📄 tree.c
字号:
fs->path, root->rev, path);
}
static svn_error_t *
not_txn (svn_fs_root_t *root)
{
return 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)
{
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, trail->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);
}
}
/* 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)
{
if (root->is_txn_root)
return svn_fs_base__dag_clone_root (node_p, root->fs, root->txn, trail);
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)
{
const svn_fs_id_t *child_id, *parent_id;
const char *child_copy_id, *parent_copy_id;
const char *id_path = NULL;
copy_t *copy;
/* Make some assertions about the function input. */
assert (child && child->parent && txn_id);
/* 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);
/* 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)
{
*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_base__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_bdb__get_copy (©, fs, child_copy_id, trail));
if (svn_fs_base__id_compare (copy->dst_noderev_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_base__dag_get_created_path (child->node);
if (strcmp (id_path, parent_path_path (child, trail->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, 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)
{
svn_fs_t *fs = root->fs;
apr_pool_t *pool = trail->pool;
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,
trail->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));
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);
/* "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. */
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, trail));
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). */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -