📄 tree.c
字号:
/* Set *NODE_P to a mutable root directory for ROOT, cloning if necessary, allocating in POOL. 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, apr_pool_t *pool){ if (root->is_txn_root) return svn_fs_fs__dag_clone_root(node_p, root->fs, root->txn, pool); else /* If it's not a transaction root, we can't change its contents. */ return svn_fs_fs__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;/* Return a text string describing the absolute path of parent_path PARENT_PATH. It will be allocated in POOL. */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. Allocations are taken from POOL. */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, apr_pool_t *pool){ const svn_fs_id_t *child_id, *parent_id, *copyroot_id; const char *child_copy_id, *parent_copy_id; const char *id_path = NULL; svn_fs_root_t *copyroot_root; dag_node_t *copyroot_node; svn_revnum_t copyroot_rev; const char *copyroot_path; /* Make some assertions about the function input. */ assert(child && child->parent && txn_id); /* Initialize some convenience variables. */ child_id = svn_fs_fs__dag_get_id(child->node); parent_id = svn_fs_fs__dag_get_id(child->parent->node); child_copy_id = svn_fs_fs__id_copy_id(child_id); parent_copy_id = svn_fs_fs__id_copy_id(parent_id); /* If this child is already mutable, we have nothing to do. */ if (svn_fs_fs__id_txn_id(child_id)) { *inherit_p = copy_id_inherit_self; *copy_src_path = NULL; return SVN_NO_ERROR; } /* From this point on, we'll assume that the child will just take its copy ID from its parent. */ *inherit_p = copy_id_inherit_parent; *copy_src_path = NULL; /* Special case: if the child's copy ID is '0', use the parent's copy ID. */ if (strcmp(child_copy_id, "0") == 0) return SVN_NO_ERROR; /* Compare the copy IDs of the child and its parent. If they are the same, then the child is already on the same branch as the parent, and should use the same mutability copy ID that the parent will use. */ if (svn_fs_fs__key_compare(child_copy_id, parent_copy_id) == 0) return SVN_NO_ERROR; /* If the child is on the same branch that the parent is on, the child should just use the same copy ID that the parent would use. Else, the child needs to generate a new copy ID to use should it need to be made mutable. We will claim that child is on the same branch as its parent if the child itself is not a branch point, or if it is a branch point that we are accessing via its original copy destination path. */ SVN_ERR(svn_fs_fs__dag_get_copyroot(©root_rev, ©root_path, child->node,pool)); SVN_ERR(svn_fs_fs__revision_root(©root_root, fs, copyroot_rev, pool)); SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, pool)); copyroot_id = svn_fs_fs__dag_get_id(copyroot_node); if (svn_fs_fs__id_compare(copyroot_id, child_id) == -1) return SVN_NO_ERROR; /* Determine if we are looking at the child via its original path or as a subtree item of a copied tree. */ id_path = svn_fs_fs__dag_get_created_path(child->node); if (strcmp(id_path, parent_path_path(child, pool)) == 0) { *inherit_p = copy_id_inherit_self; return SVN_NO_ERROR; } /* We are pretty sure that the child node is an unedited nested branched node. When it needs to be made mutable, it should claim a new copy ID. */ *inherit_p = copy_id_inherit_new; *copy_src_path = id_path; 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, allocating in POOL. Set *PARENT_PATH_P to a path from the node up to ROOT. 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, 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_fs__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, pool)); id = svn_fs_fs__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_fs__dag_open(&child, here, entry, 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_fs__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. */ parent_path = make_parent_path(child, entry, parent_path, pool); if (txn_id) { SVN_ERR(get_copy_inheritance(&inherit, ©_path, fs, parent_path, txn_id, pool)); parent_path->copy_inherit = inherit; parent_path->copy_src_path = apr_pstrdup(pool, copy_path); } /* Cache the node we found (if it wasn't already cached). */ if (! cached_node) dag_node_cache_set(root, path_so_far, child); } /* Are we finished traversing the path? */ if (! next) break; /* The path isn't finished yet; we'd better be in a directory. */ if (svn_fs_fs__dag_node_kind(child) != svn_node_dir) SVN_ERR_W(svn_fs_fs__err_not_directory(fs, path_so_far),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -