📄 tree.c
字号:
: \ svn_error_createf \ (SVN_ERR_FS_ALREADY_EXISTS, 0, \ _("File already exists: filesystem '%s', revision %ld, path '%s'"), \ r->fs->path, r->rev, p) \ )#define NOT_TXN(r) \ svn_error_create \ (SVN_ERR_FS_NOT_TXN_ROOT, NULL, \ _("Root object must be a transaction root"))/* Getting dag nodes for roots. *//* Set *NODE_P to a freshly opened dag node referring to the root directory of ROOT, as part of TRAIL. */static svn_error_t *root_node(dag_node_t **node_p, svn_fs_root_t *root, trail_t *trail, apr_pool_t *pool){ base_root_data_t *brd = root->fsap_data; if (! root->is_txn_root) { /* It's a revision root, so we already have its root directory opened. */ *node_p = svn_fs_base__dag_dup(brd->root_dir, pool); return SVN_NO_ERROR; } else { /* It's a transaction root. Open a fresh copy. */ return svn_fs_base__dag_txn_root(node_p, root->fs, root->txn, trail, pool); }}/* Set *NODE_P to a mutable root directory for ROOT, cloning if necessary, as part of TRAIL. ROOT must be a transaction root. Use ERROR_PATH in error messages. */static svn_error_t *mutable_root_node(dag_node_t **node_p, svn_fs_root_t *root, const char *error_path, trail_t *trail, apr_pool_t *pool){ if (root->is_txn_root) return svn_fs_base__dag_clone_root(node_p, root->fs, root->txn, trail, pool); else /* If it's not a transaction root, we can't change its contents. */ return svn_fs_base__err_not_mutable(root->fs, root->rev, error_path);}/* Traversing directory paths. */typedef enum copy_id_inherit_t{ copy_id_inherit_unknown = 0, copy_id_inherit_self, copy_id_inherit_parent, copy_id_inherit_new} copy_id_inherit_t;/* A linked list representing the path from a node up to a root directory. We use this for cloning, and for operations that need to deal with both a node and its parent directory. For example, a `delete' operation needs to know that the node actually exists, but also needs to change the parent directory. */typedef struct parent_path_t{ /* A node along the path. This could be the final node, one of its parents, or the root. Every parent path ends with an element for the root directory. */ dag_node_t *node; /* The name NODE has in its parent directory. This is zero for the root directory, which (obviously) has no name in its parent. */ char *entry; /* The parent of NODE, or zero if NODE is the root directory. */ struct parent_path_t *parent; /* The copy ID inheritence style. */ copy_id_inherit_t copy_inherit; /* If copy ID inheritence style is copy_id_inherit_new, this is the path which should be implicitly copied; otherwise, this is NULL. */ const char *copy_src_path;} parent_path_t;static const char *parent_path_path(parent_path_t *parent_path, apr_pool_t *pool){ const char *path_so_far = "/"; if (parent_path->parent) path_so_far = parent_path_path(parent_path->parent, pool); return parent_path->entry ? svn_path_join(path_so_far, parent_path->entry, pool) : path_so_far;}/* Choose a copy ID inheritance method *INHERIT_P to be used in the event that immutable node CHILD in FS needs to be made mutable. If the inheritance method is copy_id_inherit_new, also return a *COPY_SRC_PATH on which to base the new copy ID (else return NULL for that path). CHILD must have a parent (it cannot be the root node). TXN_ID is the transaction in which these items might be mutable. */static svn_error_t *get_copy_inheritance(copy_id_inherit_t *inherit_p, const char **copy_src_path, svn_fs_t *fs, parent_path_t *child, const char *txn_id, trail_t *trail, apr_pool_t *pool){ const svn_fs_id_t *child_id, *parent_id; const char *child_copy_id, *parent_copy_id; const char *id_path = NULL; /* Make some assertions about the function input. */ assert(child && child->parent && txn_id); /* Initialize our return variables (default: self-inheritance). */ *inherit_p = copy_id_inherit_self; *copy_src_path = NULL; /* Initialize some convenience variables. */ child_id = svn_fs_base__dag_get_id(child->node); parent_id = svn_fs_base__dag_get_id(child->parent->node); child_copy_id = svn_fs_base__id_copy_id(child_id); parent_copy_id = svn_fs_base__id_copy_id(parent_id); /* Easy out: if this child is already mutable, we have nothing to do. */ if (svn_fs_base__key_compare(svn_fs_base__id_txn_id(child_id), txn_id) == 0) return SVN_NO_ERROR; /* If the child and its parent are on the same branch, then the child will inherit the copy ID of its parent when made mutable. This is trivially detectable when the child and its parent have the same copy ID. But that's not the sole indicator of same-branchness. It might be the case that the parent was the result of a copy, but the child has not yet been cloned for mutability since that copy. Detection of this latter case basically means making sure the copy IDs don't differ for some other reason, such as that the child was the direct target of the copy whose ID it has. There is a special case here, too -- if the child's copy ID is the special ID "0", it can't have been the target of any copy, and therefore must be on the same branch as its parent. */ if ((strcmp(child_copy_id, "0") == 0) || (svn_fs_base__key_compare(child_copy_id, parent_copy_id) == 0)) { *inherit_p = copy_id_inherit_parent; return SVN_NO_ERROR; } else { copy_t *copy; SVN_ERR(svn_fs_bdb__get_copy(©, fs, child_copy_id, trail, pool)); if (svn_fs_base__id_compare(copy->dst_noderev_id, child_id) == -1) { *inherit_p = copy_id_inherit_parent; return SVN_NO_ERROR; } } /* If we get here, the child and its parent are not on speaking terms -- there will be no parental inheritence handed down in *this* generation. */ /* If the child was created at a different path than the one we are expecting its clone to live, one of its parents must have been created via a copy since the child was created. The child isn't on the same branch as its parent (we caught those cases early); it can't keep its current copy ID because there's been an affecting copy (its clone won't be on the same branch as the child is). That leaves only one course of action -- to assign the child a brand new "soft" copy ID. */ id_path = svn_fs_base__dag_get_created_path(child->node); if (strcmp(id_path, parent_path_path(child, pool)) != 0) { *inherit_p = copy_id_inherit_new; *copy_src_path = id_path; return SVN_NO_ERROR; } /* The node gets to keep its own ID. */ return SVN_NO_ERROR;}/* Allocate a new parent_path_t node from POOL, referring to NODE, ENTRY, PARENT, and COPY_ID. */static parent_path_t *make_parent_path(dag_node_t *node, char *entry, parent_path_t *parent, apr_pool_t *pool){ parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path)); parent_path->node = node; parent_path->entry = entry; parent_path->parent = parent; parent_path->copy_inherit = copy_id_inherit_unknown; parent_path->copy_src_path = NULL; return parent_path;}/* Return a null-terminated copy of the first component of PATH, allocated in POOL. If path is empty, or consists entirely of slashes, return the empty string. If the component is followed by one or more slashes, we set *NEXT_P to point after the slashes. If the component ends PATH, we set *NEXT_P to zero. This means: - If *NEXT_P is zero, then the component ends the PATH, and there are no trailing slashes in the path. - If *NEXT_P points at PATH's terminating null character, then the component returned was the last, and PATH ends with one or more slash characters. - Otherwise, *NEXT_P points to the beginning of the next component of PATH. You can pass this value to next_entry_name to extract the next component. */static char *next_entry_name(const char **next_p, const char *path, apr_pool_t *pool){ const char *end; /* Find the end of the current component. */ end = strchr(path, '/'); if (! end) { /* The path contains only one component, with no trailing slashes. */ *next_p = 0; return apr_pstrdup(pool, path); } else { /* There's a slash after the first component. Skip over an arbitrary number of slashes to find the next one. */ const char *next = end; while (*next == '/') next++; *next_p = next; return apr_pstrndup(pool, path, end - path); }}/* Flags for open_path. */typedef enum open_path_flags_t { /* The last component of the PATH need not exist. (All parent directories must exist, as usual.) If the last component doesn't exist, simply leave the `node' member of the bottom parent_path component zero. */ open_path_last_optional = 1} open_path_flags_t;/* Open the node identified by PATH in ROOT, as part of TRAIL. Set *PARENT_PATH_P to a path from the node up to ROOT, allocated in TRAIL->pool. The resulting *PARENT_PATH_P value is guaranteed to contain at least one element, for the root directory. If resulting *PARENT_PATH_P will eventually be made mutable and modified, or if copy ID inheritance information is otherwise needed, TXN_ID should be the ID of the mutability transaction. If TXN_ID is NULL, no copy ID in heritance information will be calculated for the *PARENT_PATH_P chain. If FLAGS & open_path_last_optional is zero, return the error SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist. If non-zero, require all the parent directories to exist as normal, but if the final path component doesn't exist, simply return a path whose bottom `node' member is zero. This option is useful for callers that create new nodes --- we find the parent directory for them, and tell them whether the entry exists already. NOTE: Public interfaces which only *read* from the filesystem should not call this function directly, but should instead use get_dag().*/static svn_error_t *open_path(parent_path_t **parent_path_p, svn_fs_root_t *root, const char *path, int flags, const char *txn_id, trail_t *trail, apr_pool_t *pool){ svn_fs_t *fs = root->fs; const svn_fs_id_t *id; dag_node_t *here; /* The directory we're currently looking at. */ parent_path_t *parent_path; /* The path from HERE up to the root. */ const char *rest; /* The portion of PATH we haven't traversed yet. */ const char *canon_path = svn_fs_base__canonicalize_abspath(path, pool); const char *path_so_far = "/"; /* Make a parent_path item for the root node, using its own current copy id. */ SVN_ERR(root_node(&here, root, trail, pool)); id = svn_fs_base__dag_get_id(here); parent_path = make_parent_path(here, 0, 0, pool); parent_path->copy_inherit = copy_id_inherit_self; rest = canon_path + 1; /* skip the leading '/', it saves in iteration */ /* Whenever we are at the top of this loop: - HERE is our current directory, - ID is the node revision ID of HERE, - REST is the path we're going to find in HERE, and - PARENT_PATH includes HERE and all its parents. */ for (;;) { const char *next; char *entry; dag_node_t *child; /* Parse out the next entry from the path. */ entry = next_entry_name(&next, rest, pool); /* Calculate the path traversed thus far. */ path_so_far = svn_path_join(path_so_far, entry, pool); if (*entry == '\0') { /* Given the behavior of next_entry_name, this happens when the path either starts or ends with a slash. In either case, we stay put: the current directory stays the same, and we add nothing to the parent path. */ child = here; } else { copy_id_inherit_t inherit; const char *copy_path = NULL; svn_error_t *err = SVN_NO_ERROR; dag_node_t *cached_node; /* If we found a directory entry, follow it. First, we check our node cache, and, failing that, we hit the DAG layer. */ cached_node = dag_node_cache_get(root, path_so_far, pool); if (cached_node) child = cached_node; else err = svn_fs_base__dag_open(&child, here, entry, trail, pool); /* "file not found" requires special handling. */ if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) { /* If this was the last path component, and the caller said it was optional, then don't return an error; just put a NULL node pointer in the path. */ svn_error_clear(err); if ((flags & open_path_last_optional) && (! next || *next == '\0')) { parent_path = make_parent_path(NULL, entry, parent_path, pool); break; } else { /* Build a better error message than svn_fs_base__dag_open can provide, giving the root and full path name. */ return NOT_FOUND(root, path); } } /* Other errors we return normally. */ SVN_ERR(err); /* Now, make a parent_path item for CHILD. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -