Modified: subversion/branches/authzperf/subversion/libsvn_fs_x/tree.c URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_x/tree.c?rev=1649205&r1=1649204&r2=1649205&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_fs_x/tree.c (original) +++ subversion/branches/authzperf/subversion/libsvn_fs_x/tree.c Sat Jan 3 14:00:41 2015 @@ -56,7 +56,7 @@ #include "lock.h" #include "tree.h" #include "fs_x.h" -#include "id.h" +#include "fs_id.h" #include "temp_serializer.h" #include "cached_data.h" #include "transaction.h" @@ -79,7 +79,7 @@ them concurrently on disk! (Why is the DAG node cache safer than the root DAG node? When cloning transaction DAG nodes in and out of the cache, all of the possibly-mutable data from the - node_revision_t inside the dag_node_t is dropped.) Additionally, + svn_fs_x__noderev_t inside the dag_node_t is dropped.) Additionally, revisions are immutable enough that their DAG node cache can be kept in the FS object and shared among multiple revision root objects. @@ -100,7 +100,6 @@ typedef struct fs_txn_root_data_t static svn_error_t * get_dag(dag_node_t **dag_node_p, svn_fs_root_t *root, const char *path, - svn_boolean_t needs_lock_cache, apr_pool_t *pool); static svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev, @@ -159,36 +158,12 @@ typedef struct cache_entry_t */ enum { BUCKET_COUNT = 256 }; -/* Each pool that has received a DAG node, will hold at least on lock on - our cache to ensure that the node remains valid despite being allocated - in the cache's pool. This is the structure to represent the lock. - */ -typedef struct cache_lock_t -{ - /* pool holding the lock */ - apr_pool_t *pool; - - /* cache being locked */ - fs_x_dag_cache_t *cache; - - /* next lock. NULL at EOL */ - struct cache_lock_t *next; - - /* previous lock. NULL at list head. Only then this==cache->first_lock */ - struct cache_lock_t *prev; -} cache_lock_t; - /* The actual cache structure. All nodes will be allocated in POOL. When the number of INSERTIONS (i.e. objects created form that pool) exceeds a certain threshold, the pool will be cleared and the cache with it. - - To ensure that nodes returned from this structure remain valid, the - cache will get locked for the lifetime of the _receiving_ pools (i.e. - those in which we would allocate the node if there was no cache.). - The cache will only be cleared FIRST_LOCK is 0. */ -struct fs_x_dag_cache_t +struct svn_fs_x__dag_cache_t { /* fixed number of (possibly empty) cache entries */ cache_entry_t buckets[BUCKET_COUNT]; @@ -203,109 +178,29 @@ struct fs_x_dag_cache_t Thus, remember the last hit location for optimistic lookup. */ apr_size_t last_hit; - /* List of receiving pools that are still alive. */ - cache_lock_t *first_lock; + /* Position of the last bucket hit that actually had a DAG node in it. + LAST_HIT may refer to a bucket that matches path@rev but has not + its NODE element set, yet. + This value is a mere hint for optimistic lookup and any value is + valid (as long as it is < BUCKET_COUNT). */ + apr_size_t last_non_empty; }; -/* Cleanup function to be called when a receiving pool gets cleared. - Unlocks the cache once. - */ -static apr_status_t -unlock_cache(void *baton_void) -{ - cache_lock_t *lock = baton_void; - - /* remove lock from chain. Update the head */ - if (lock->next) - lock->next->prev = lock->prev; - if (lock->prev) - lock->prev->next = lock->next; - else - lock->cache->first_lock = lock->next; - - return APR_SUCCESS; -} - -/* Cleanup function to be called when the cache itself gets destroyed. - In that case, we must unregister all unlock requests. - */ -static apr_status_t -unregister_locks(void *baton_void) -{ - fs_x_dag_cache_t *cache = baton_void; - cache_lock_t *lock; - - for (lock = cache->first_lock; lock; lock = lock->next) - apr_pool_cleanup_kill(lock->pool, - lock, - unlock_cache); - - return APR_SUCCESS; -} - -fs_x_dag_cache_t* +svn_fs_x__dag_cache_t* svn_fs_x__create_dag_cache(apr_pool_t *pool) { - fs_x_dag_cache_t *result = apr_pcalloc(pool, sizeof(*result)); + svn_fs_x__dag_cache_t *result = apr_pcalloc(pool, sizeof(*result)); result->pool = svn_pool_create(pool); - apr_pool_cleanup_register(pool, - result, - unregister_locks, - apr_pool_cleanup_null); - return result; } -/* Prevent the entries in CACHE from being destroyed, for as long as the - POOL lives. - */ -static void -lock_cache(fs_x_dag_cache_t* cache, apr_pool_t *pool) -{ - /* we only need to lock / unlock once per pool. Since we will often ask - for multiple nodes with the same pool, we can reduce the overhead. - However, if e.g. pools are being used in an alternating pattern, - we may lock the cache more than once for the same pool (and register - just as many cleanup actions). - */ - cache_lock_t *lock = cache->first_lock; - - /* try to find an existing lock for POOL. - But limit the time spent on chasing pointers. */ - int limiter = 8; - while (lock && --limiter) - { - if (lock->pool == pool) - return; - - lock = lock->next; - } - - /* create a new lock and put it at the beginning of the lock chain */ - lock = apr_palloc(pool, sizeof(*lock)); - lock->cache = cache; - lock->pool = pool; - lock->next = cache->first_lock; - lock->prev = NULL; - - if (cache->first_lock) - cache->first_lock->prev = lock; - cache->first_lock = lock; - - /* instruct POOL to remove the look upon cleanup */ - apr_pool_cleanup_register(pool, - lock, - unlock_cache, - apr_pool_cleanup_null); -} - /* Clears the CACHE at regular intervals (destroying all cached nodes) */ static void -auto_clear_dag_cache(fs_x_dag_cache_t* cache) +auto_clear_dag_cache(svn_fs_x__dag_cache_t* cache) { - if (cache->first_lock == NULL && cache->insertions > BUCKET_COUNT) + if (cache->insertions > BUCKET_COUNT) { svn_pool_clear(cache->pool); @@ -319,7 +214,7 @@ auto_clear_dag_cache(fs_x_dag_cache_t* c may then set it to the corresponding DAG node allocated in CACHE->POOL. */ static cache_entry_t * -cache_lookup( fs_x_dag_cache_t *cache +cache_lookup( svn_fs_x__dag_cache_t *cache , svn_revnum_t revision , const char *path) { @@ -338,6 +233,10 @@ cache_lookup( fs_x_dag_cache_t *cache && (result->path_len == path_len) && !memcmp(result->path, path, path_len)) { + /* Remember the position of the last node we found in this cache. */ + if (result->node) + cache->last_non_empty = cache->last_hit; + return result; } @@ -388,14 +287,44 @@ cache_lookup( fs_x_dag_cache_t *cache cache->insertions++; } + else if (result->node) + { + /* This bucket is valid & has a suitable DAG node in it. + Remember its location. */ + cache->last_non_empty = bucket_index; + } return result; } +/* Optimistic lookup using the last seen non-empty location in CACHE. + Return the node of that entry, if it is still in use and matches PATH. + Return NULL otherwise. Since the caller usually already knows the path + length, provide it in PATH_LEN. */ +static dag_node_t * +cache_lookup_last_path(svn_fs_x__dag_cache_t *cache, + const char *path, + apr_size_t path_len) +{ + cache_entry_t *result = &cache->buckets[cache->last_non_empty]; + assert(strlen(path) == path_len); + + if ( result->node + && (result->path_len == path_len) + && !memcmp(result->path, path, path_len)) + { + return result->node; + } + + return NULL; +} + /* 2nd level cache */ /* Find and return the DAG node cache for ROOT and the key that - should be used for PATH. */ + should be used for PATH. + + Pool will only be used for allocating a new keys if necessary */ static void locate_cache(svn_cache__t **cache, const char **key, @@ -406,30 +335,31 @@ locate_cache(svn_cache__t **cache, if (root->is_txn_root) { fs_txn_root_data_t *frd = root->fsap_data; - if (cache) *cache = frd->txn_node_cache; - if (key && path) *key = path; + + if (cache) + *cache = frd->txn_node_cache; + if (key && path) + *key = path; } else { - fs_x_data_t *ffd = root->fs->fsap_data; - if (cache) *cache = ffd->rev_node_cache; - if (key && path) *key - = svn_fs_x__combine_number_and_string(root->rev, path, pool); + svn_fs_x__data_t *ffd = root->fs->fsap_data; + + if (cache) + *cache = ffd->rev_node_cache; + if (key && path) + *key = svn_fs_x__combine_number_and_string(root->rev, path, pool); } } /* Return NODE for PATH from ROOT's node cache, or NULL if the node isn't cached; read it from the FS. *NODE remains valid until either POOL or the FS gets cleared or destroyed (whichever comes first). - - Since locking can be expensive and POOL may be long-living, for - nodes that will not need to survive the next call to this function, - set NEEDS_LOCK_CACHE to FALSE. */ + */ static svn_error_t * dag_node_cache_get(dag_node_t **node_p, svn_fs_root_t *root, const char *path, - svn_boolean_t needs_lock_cache, apr_pool_t *pool) { svn_boolean_t found; @@ -443,7 +373,7 @@ dag_node_cache_get(dag_node_t **node_p, { /* immutable DAG node. use the global caches for it */ - fs_x_data_t *ffd = root->fs->fsap_data; + svn_fs_x__data_t *ffd = root->fs->fsap_data; cache_entry_t *bucket; auto_clear_dag_cache(ffd->dag_node_cache); @@ -465,11 +395,6 @@ dag_node_cache_get(dag_node_t **node_p, { node = bucket->node; } - - /* if we found a node, make sure it remains valid at least as long - as it would when allocated in POOL. */ - if (node && needs_lock_cache) - lock_cache(ffd->dag_node_cache, pool); } else { @@ -769,8 +694,8 @@ get_copy_inheritance(copy_id_inherit_t * parent_path_t *child, apr_pool_t *pool) { - const svn_fs_id_t *child_id, *parent_id, *copyroot_id; - const svn_fs_x__id_part_t *child_copy_id, *parent_copy_id; + svn_fs_x__id_t child_copy_id, parent_copy_id; + svn_boolean_t related; const char *id_path = NULL; svn_fs_root_t *copyroot_root; dag_node_t *copyroot_node; @@ -780,13 +705,11 @@ get_copy_inheritance(copy_id_inherit_t * SVN_ERR_ASSERT(child && child->parent); /* Initialize some convenience variables. */ - child_id = svn_fs_x__dag_get_id(child->node); - parent_id = svn_fs_x__dag_get_id(child->parent->node); - child_copy_id = svn_fs_x__id_copy_id(child_id); - parent_copy_id = svn_fs_x__id_copy_id(parent_id); + SVN_ERR(svn_fs_x__dag_get_copy_id(&child_copy_id, child->node)); + SVN_ERR(svn_fs_x__dag_get_copy_id(&parent_copy_id, child->parent->node)); /* If this child is already mutable, we have nothing to do. */ - if (svn_fs_x__id_is_txn(child_id)) + if (svn_fs_x__dag_check_mutable(child->node)) { *inherit_p = copy_id_inherit_self; *copy_src_path = NULL; @@ -800,14 +723,14 @@ get_copy_inheritance(copy_id_inherit_t * /* Special case: if the child's copy ID is '0', use the parent's copy ID. */ - if (svn_fs_x__id_part_is_root(child_copy_id)) + if (svn_fs_x__id_is_root(&child_copy_id)) 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_x__id_part_eq(child_copy_id, parent_copy_id)) + if (svn_fs_x__id_eq(&child_copy_id, &parent_copy_id)) return SVN_NO_ERROR; /* If the child is on the same branch that the parent is on, the @@ -818,12 +741,12 @@ get_copy_inheritance(copy_id_inherit_t * or if it is a branch point that we are accessing via its original copy destination path. */ SVN_ERR(svn_fs_x__dag_get_copyroot(©root_rev, ©root_path, - child->node)); + child->node)); SVN_ERR(svn_fs_x__revision_root(©root_root, fs, copyroot_rev, pool)); - SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, FALSE, pool)); - copyroot_id = svn_fs_x__dag_get_id(copyroot_node); + SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, pool)); - if (svn_fs_x__id_compare(copyroot_id, child_id) == svn_fs_node_unrelated) + SVN_ERR(svn_fs_x__dag_related_node(&related, copyroot_node, child->node)); + if (!related) return SVN_NO_ERROR; /* Determine if we are looking at the child via its original path or @@ -852,7 +775,8 @@ make_parent_path(dag_node_t *node, apr_pool_t *pool) { parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path)); - parent_path->node = node; + if (node) + parent_path->node = svn_fs_x__dag_copy_into_pool(node, pool); parent_path->entry = entry; parent_path->parent = parent; parent_path->copy_inherit = copy_id_inherit_unknown; @@ -884,6 +808,55 @@ typedef enum open_path_flags_t { open_path_allow_null = 8 } open_path_flags_t; +/* Try a short-cut for the open_path() function using the last node accessed. + * If that ROOT is that nodes's "created rev" and PATH of PATH_LEN chars is + * its "created path", return the node in *NODE_P. Set it to NULL otherwise. + * + * This function is used to support ra_serf-style access patterns where we + * are first asked for path@rev and then for path@c_rev of the same node. + * The shortcut works by ignoring the "rev" part of the cache key and then + * checking whether we got lucky. Lookup and verification are both quick + * plus there are many early outs for common types of mismatch. + */ +static svn_error_t * +try_match_last_node(dag_node_t **node_p, + svn_fs_root_t *root, + const char *path, + apr_size_t path_len, + apr_pool_t *scratch_pool) +{ + svn_fs_x__data_t *ffd = root->fs->fsap_data; + + /* Optimistic lookup: if the last node returned from the cache applied to + the same PATH, return it in NODE. */ + dag_node_t *node + = cache_lookup_last_path(ffd->dag_node_cache, path, path_len); + + /* Did we get a bucket with a committed node? */ + if (node && !svn_fs_x__dag_check_mutable(node)) + { + /* Get the path&rev pair at which this node was created. + This is repository location for which this node is _known_ to be + the right lookup result irrespective of how we found it. */ + const char *created_path + = svn_fs_x__dag_get_created_path(node); + svn_revnum_t revision = svn_fs_x__dag_get_revision(node); + + /* Is it an exact match? */ + if (revision == root->rev && strcmp(created_path, path) == 0) + { + /* Cache it under its full path@rev access path. */ + SVN_ERR(dag_node_cache_set(root, path, node, scratch_pool)); + + *node_p = node; + return SVN_NO_ERROR; + } + } + + *node_p = NULL; + return SVN_NO_ERROR; +} + /* 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 @@ -931,20 +904,55 @@ open_path(parent_path_t **parent_path_p, at the respective position and replacing that with a '/' in the next iteration. This is correct as we assert() PATH to be canonical. */ svn_stringbuf_t *path_so_far = svn_stringbuf_create(path, pool); + apr_size_t path_len = path_so_far->len; - /* callers often traverse the tree in some path-based order. That means - a sibling of PATH has been presently accessed. Try to start the lookup - directly at the parent node, if the caller did not requested the full - parent chain. */ + /* Callers often traverse the DAG in some path-based order or along the + history segments. That allows us to try a few guesses about where to + find the next item. This is only useful if the caller didn't request + the full parent chain. */ assert(svn_fs__is_canonical_abspath(path)); path_so_far->len = 0; /* "" */ if (flags & open_path_node_only) { - const char *directory = svn_dirent_dirname(path, pool); + const char *directory; + + /* First attempt: Assume that we access the DAG for the same path as + in the last lookup but for a different revision that happens to be + the last revision that touched the respective node. This is a + common pattern when e.g. checking out over ra_serf. Note that this + will only work for committed data as the revision info for nodes in + txns is bogus. + + This shortcut is quick and will exit this function upon success. + So, try it first. */ + if (!root->is_txn_root) + { + dag_node_t *node; + SVN_ERR(try_match_last_node(&node, root, path, path_len, iterpool)); + + /* Did the shortcut work? */ + if (node) + { + /* Construct and return the result. */ + svn_pool_destroy(iterpool); + + parent_path = make_parent_path(node, 0, 0, pool); + parent_path->copy_inherit = copy_id_inherit_self; + *parent_path_p = parent_path; + + return SVN_NO_ERROR; + } + } + + /* Second attempt: Try starting the lookup immediately at the parent + node. We will often have recently accessed either a sibling or + said parent DIRECTORY itself for the same revision. */ + directory = svn_dirent_dirname(path, pool); if (directory[1] != 0) /* root nodes are covered anyway */ { - SVN_ERR(dag_node_cache_get(&here, root, directory, TRUE, pool)); - /* did the shortcut work? */ + SVN_ERR(dag_node_cache_get(&here, root, directory, pool)); + + /* Did the shortcut work? */ if (here) { apr_size_t dirname_len = strlen(directory); @@ -988,15 +996,12 @@ open_path(parent_path_t **parent_path_p, path_so_far->len += strlen(entry) + 1; path_so_far->data[path_so_far->len] = '\0'; - if (*entry == '\0') - { - /* Given the behavior of svn_fs__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 + /* Given the behavior of svn_fs__next_entry_name(), ENTRY may be an + empty string 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. We only need to + process non-empty path segments. */ + if (*entry != '\0') { copy_id_inherit_t inherit; const char *copy_path = NULL; @@ -1009,7 +1014,7 @@ open_path(parent_path_t **parent_path_p, complete path. */ if (next || !(flags & open_path_uncached)) SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far->data, - TRUE, pool)); + pool)); if (cached_node) child = cached_node; else @@ -1044,8 +1049,8 @@ open_path(parent_path_t **parent_path_p, if (flags & open_path_node_only) { - /* Shortcut: the caller only wan'ts the final DAG node. */ - parent_path->node = child; + /* Shortcut: the caller only wants the final DAG node. */ + parent_path->node = svn_fs_x__dag_copy_into_pool(child, pool); } else { @@ -1076,7 +1081,10 @@ open_path(parent_path_t **parent_path_p, apr_psprintf(iterpool, _("Failure opening '%s'"), path)); rest = next; - here = child; + + /* The NODE in PARENT_PATH equals CHILD but lives in POOL, i.e. + * it will survive the cleanup of ITERPOOL.*/ + here = parent_path->node; } svn_pool_destroy(iterpool); @@ -1106,15 +1114,15 @@ make_path_mutable(svn_fs_root_t *root, /* Are we trying to clone the root, or somebody's child node? */ if (parent_path->parent) { - const svn_fs_id_t *parent_id, *child_id, *copyroot_id; - svn_fs_x__id_part_t copy_id = { SVN_INVALID_REVNUM, 0 }; - svn_fs_x__id_part_t *copy_id_ptr = ©_id; + svn_fs_x__id_t copy_id = { SVN_INVALID_REVNUM, 0 }; + svn_fs_x__id_t *copy_id_ptr = ©_id; copy_id_inherit_t inherit = parent_path->copy_inherit; const char *clone_path, *copyroot_path; svn_revnum_t copyroot_rev; svn_boolean_t is_parent_copyroot = FALSE; svn_fs_root_t *copyroot_root; dag_node_t *copyroot_node; + svn_boolean_t related; /* We're trying to clone somebody's child. Make sure our parent is mutable. */ @@ -1124,8 +1132,8 @@ make_path_mutable(svn_fs_root_t *root, switch (inherit) { case copy_id_inherit_parent: - parent_id = svn_fs_x__dag_get_id(parent_path->parent->node); - copy_id = *svn_fs_x__id_copy_id(parent_id); + SVN_ERR(svn_fs_x__dag_get_copy_id(©_id, + parent_path->parent->node)); break; case copy_id_inherit_new: @@ -1148,13 +1156,11 @@ make_path_mutable(svn_fs_root_t *root, parent_path->node)); SVN_ERR(svn_fs_x__revision_root(©root_root, root->fs, copyroot_rev, pool)); - SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, - FALSE, pool)); + SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, pool)); - child_id = svn_fs_x__dag_get_id(parent_path->node); - copyroot_id = svn_fs_x__dag_get_id(copyroot_node); - if (!svn_fs_x__id_part_eq(svn_fs_x__id_node_id(child_id), - svn_fs_x__id_node_id(copyroot_id))) + SVN_ERR(svn_fs_x__dag_related_node(&related, copyroot_node, + parent_path->node)); + if (!related) is_parent_copyroot = TRUE; /* Now make this node mutable. */ @@ -1187,15 +1193,11 @@ make_path_mutable(svn_fs_root_t *root, /* Open the node identified by PATH in ROOT. Set DAG_NODE_P to the node we find, allocated in POOL. Return the error SVN_ERR_FS_NOT_FOUND if this node doesn't exist. - - Since locking can be expensive and POOL may be long-living, for - nodes that will not need to survive the next call to this function, - set NEEDS_LOCK_CACHE to FALSE. */ + */ static svn_error_t * get_dag(dag_node_t **dag_node_p, svn_fs_root_t *root, const char *path, - svn_boolean_t needs_lock_cache, apr_pool_t *pool) { parent_path_t *parent_path; @@ -1204,7 +1206,7 @@ get_dag(dag_node_t **dag_node_p, /* First we look for the DAG in our cache (if the path may be canonical). */ if (*path == '/') - SVN_ERR(dag_node_cache_get(&node, root, path, needs_lock_cache, pool)); + SVN_ERR(dag_node_cache_get(&node, root, path, pool)); if (! node) { @@ -1214,8 +1216,7 @@ get_dag(dag_node_t **dag_node_p, * performance benefit over previously checking path for being * canonical. */ path = svn_fs__canonicalize_abspath(path, pool); - SVN_ERR(dag_node_cache_get(&node, root, path, needs_lock_cache, - pool)); + SVN_ERR(dag_node_cache_get(&node, root, path, pool)); if (! node) { @@ -1230,7 +1231,7 @@ get_dag(dag_node_t **dag_node_p, } } - *dag_node_p = node; + *dag_node_p = svn_fs_x__dag_copy_into_pool(node, pool); return SVN_NO_ERROR; } @@ -1250,7 +1251,7 @@ static svn_error_t * add_change(svn_fs_t *fs, svn_fs_x__txn_id_t txn_id, const char *path, - const svn_fs_id_t *noderev_id, + const svn_fs_x__id_t *noderev_id, svn_fs_path_change_kind_t change_kind, svn_boolean_t text_mod, svn_boolean_t prop_mod, @@ -1274,12 +1275,14 @@ add_change(svn_fs_t *fs, /* Get the id of a node referenced by path PATH in ROOT. Return the id in *ID_P allocated in POOL. */ -svn_error_t * -svn_fs_x__node_id(const svn_fs_id_t **id_p, - svn_fs_root_t *root, - const char *path, - apr_pool_t *pool) +static svn_error_t * +x_node_id(const svn_fs_id_t **id_p, + svn_fs_root_t *root, + const char *path, + apr_pool_t *pool) { + const svn_fs_x__id_t *noderev_id; + if ((! root->is_txn_root) && (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0')))) { @@ -1288,15 +1291,19 @@ svn_fs_x__node_id(const svn_fs_id_t **id svn_fs_root_t object, and never changes when it's a revision root, so we can just reach in and grab it directly. */ dag_node_t *root_dir = root->fsap_data; - *id_p = svn_fs_x__id_copy(svn_fs_x__dag_get_id(root_dir), pool); + noderev_id = svn_fs_x__dag_get_id(root_dir); } else { dag_node_t *node; - SVN_ERR(get_dag(&node, root, path, FALSE, pool)); - *id_p = svn_fs_x__id_copy(svn_fs_x__dag_get_id(node), pool); + SVN_ERR(get_dag(&node, root, path, pool)); + noderev_id = svn_fs_x__dag_get_id(node); } + + *id_p = svn_fs_x__id_create(svn_fs_x__id_create_context(root->fs, pool), + noderev_id, pool); + return SVN_NO_ERROR; } @@ -1307,8 +1314,7 @@ x_node_relation(svn_fs_node_relation_t * apr_pool_t *pool) { dag_node_t *node; - const svn_fs_id_t *id; - svn_fs_x__id_part_t noderev_id_a, noderev_id_b, node_id_a, node_id_b; + svn_fs_x__id_t noderev_id_a, noderev_id_b, node_id_a, node_id_b; /* Root paths are a common special case. */ svn_boolean_t a_is_root_dir @@ -1343,19 +1349,17 @@ x_node_relation(svn_fs_node_relation_t * /* We checked for all separations between ID spaces (repos, txn). * Now, we can simply test for the ID values themselves. */ - SVN_ERR(get_dag(&node, root_a, path_a, FALSE, pool)); - id = svn_fs_x__dag_get_id(node); - noderev_id_a = *svn_fs_x__id_noderev_id(id); - node_id_a = *svn_fs_x__id_node_id(id); - - SVN_ERR(get_dag(&node, root_b, path_b, FALSE, pool)); - id = svn_fs_x__dag_get_id(node); - noderev_id_b = *svn_fs_x__id_noderev_id(id); - node_id_b = *svn_fs_x__id_node_id(id); + SVN_ERR(get_dag(&node, root_a, path_a, pool)); + noderev_id_a = *svn_fs_x__dag_get_id(node); + SVN_ERR(svn_fs_x__dag_get_node_id(&node_id_a, node)); + + SVN_ERR(get_dag(&node, root_b, path_b, pool)); + noderev_id_b = *svn_fs_x__dag_get_id(node); + SVN_ERR(svn_fs_x__dag_get_node_id(&node_id_b, node)); - if (svn_fs_x__id_part_eq(&noderev_id_a, &noderev_id_b)) + if (svn_fs_x__id_eq(&noderev_id_a, &noderev_id_b)) *relation = svn_fs_node_same; - else if (svn_fs_x__id_part_eq(&node_id_a, &node_id_b)) + else if (svn_fs_x__id_eq(&node_id_a, &node_id_b)) *relation = svn_fs_node_common_ancestor; else *relation = svn_fs_node_unrelated; @@ -1371,8 +1375,10 @@ svn_fs_x__node_created_rev(svn_revnum_t { dag_node_t *node; - SVN_ERR(get_dag(&node, root, path, FALSE, pool)); - return svn_fs_x__dag_get_revision(revision, node, pool); + SVN_ERR(get_dag(&node, root, path, pool)); + *revision = svn_fs_x__dag_get_revision(node); + + return SVN_NO_ERROR; } @@ -1386,7 +1392,7 @@ x_node_created_path(const char **created { dag_node_t *node; - SVN_ERR(get_dag(&node, root, path, TRUE, pool)); + SVN_ERR(get_dag(&node, root, path, pool)); *created_path = svn_fs_x__dag_get_created_path(node); return SVN_NO_ERROR; @@ -1401,14 +1407,12 @@ node_kind(svn_node_kind_t *kind_p, const char *path, apr_pool_t *pool) { - const svn_fs_id_t *node_id; dag_node_t *node; /* Get the node id. */ - SVN_ERR(svn_fs_x__node_id(&node_id, root, path, pool)); + SVN_ERR(get_dag(&node, root, path, pool)); /* Use the node id to get the real kind. */ - SVN_ERR(svn_fs_x__dag_get_node(&node, root->fs, node_id, pool)); *kind_p = svn_fs_x__dag_node_kind(node); return SVN_NO_ERROR; @@ -1450,7 +1454,7 @@ x_node_prop(svn_string_t **value_p, dag_node_t *node; apr_hash_t *proplist; - SVN_ERR(get_dag(&node, root, path, FALSE, pool)); + SVN_ERR(get_dag(&node, root, path, pool)); SVN_ERR(svn_fs_x__dag_get_proplist(&proplist, node, pool)); *value_p = NULL; if (proplist) @@ -1473,7 +1477,7 @@ x_node_proplist(apr_hash_t **table_p, apr_hash_t *table; dag_node_t *node; - SVN_ERR(get_dag(&node, root, path, FALSE, pool)); + SVN_ERR(get_dag(&node, root, path, pool)); SVN_ERR(svn_fs_x__dag_get_proplist(&table, node, pool)); *table_p = table ? table : apr_hash_make(pool); @@ -1509,23 +1513,24 @@ x_change_node_prop(svn_fs_root_t *root, parent_path_t *parent_path; apr_hash_t *proplist; svn_fs_x__txn_id_t txn_id; - svn_boolean_t modeinfo_mod = FALSE; + svn_boolean_t mergeinfo_mod = FALSE; + apr_pool_t *subpool = svn_pool_create(pool); if (! root->is_txn_root) return SVN_FS__NOT_TXN(root); txn_id = root_txn_id(root); - path = svn_fs__canonicalize_abspath(path, pool); - SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool)); + path = svn_fs__canonicalize_abspath(path, subpool); + SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, subpool)); /* Check (non-recursively) to see if path is locked; if so, check that we can use it. */ if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS) SVN_ERR(svn_fs_x__allow_locked_operation(path, root->fs, FALSE, FALSE, - pool)); + subpool)); - SVN_ERR(make_path_mutable(root, parent_path, path, pool)); - SVN_ERR(svn_fs_x__dag_get_proplist(&proplist, parent_path->node, pool)); + SVN_ERR(make_path_mutable(root, parent_path, path, subpool)); + SVN_ERR(svn_fs_x__dag_get_proplist(&proplist, parent_path->node, subpool)); /* If there's no proplist, but we're just deleting a property, exit now. */ if ((! proplist) && (! value)) @@ -1533,7 +1538,7 @@ x_change_node_prop(svn_fs_root_t *root, /* Now, if there's no proplist, we know we need to make one. */ if (! proplist) - proplist = apr_hash_make(pool); + proplist = apr_hash_make(subpool); if (strcmp(name, SVN_PROP_MERGEINFO) == 0) { @@ -1548,12 +1553,12 @@ x_change_node_prop(svn_fs_root_t *root, if (increment != 0) { - SVN_ERR(increment_mergeinfo_up_tree(parent_path, increment, pool)); + SVN_ERR(increment_mergeinfo_up_tree(parent_path, increment, subpool)); SVN_ERR(svn_fs_x__dag_set_has_mergeinfo(parent_path->node, - (value != NULL), pool)); + (value != NULL), subpool)); } - modeinfo_mod = TRUE; + mergeinfo_mod = TRUE; } /* Set the property. */ @@ -1561,14 +1566,17 @@ x_change_node_prop(svn_fs_root_t *root, /* Overwrite the node's proplist. */ SVN_ERR(svn_fs_x__dag_set_proplist(parent_path->node, proplist, - pool)); + subpool)); /* Make a record of this modification in the changes table. */ - return add_change(root->fs, txn_id, path, - svn_fs_x__dag_get_id(parent_path->node), - svn_fs_path_change_modify, FALSE, TRUE, modeinfo_mod, - svn_fs_x__dag_node_kind(parent_path->node), - SVN_INVALID_REVNUM, NULL, pool); + SVN_ERR(add_change(root->fs, txn_id, path, + svn_fs_x__dag_get_id(parent_path->node), + svn_fs_path_change_modify, FALSE, TRUE, mergeinfo_mod, + svn_fs_x__dag_node_kind(parent_path->node), + SVN_INVALID_REVNUM, NULL, subpool)); + + svn_pool_destroy(subpool); + return SVN_NO_ERROR; } @@ -1586,6 +1594,7 @@ x_props_changed(svn_boolean_t *changed_p apr_pool_t *pool) { dag_node_t *node1, *node2; + apr_pool_t *subpool = svn_pool_create(pool); /* Check that roots are in the same fs. */ if (root1->fs != root2->fs) @@ -1593,10 +1602,13 @@ x_props_changed(svn_boolean_t *changed_p (SVN_ERR_FS_GENERAL, NULL, _("Cannot compare property value between two different filesystems")); - SVN_ERR(get_dag(&node1, root1, path1, TRUE, pool)); - SVN_ERR(get_dag(&node2, root2, path2, TRUE, pool)); - return svn_fs_x__dag_things_different(changed_p, NULL, node1, node2, - strict, pool); + SVN_ERR(get_dag(&node1, root1, path1, subpool)); + SVN_ERR(get_dag(&node2, root2, path2, subpool)); + SVN_ERR(svn_fs_x__dag_things_different(changed_p, NULL, node1, node2, + strict, subpool)); + svn_pool_destroy(subpool); + + return SVN_NO_ERROR; } @@ -1607,7 +1619,7 @@ x_props_changed(svn_boolean_t *changed_p static svn_error_t * get_root(dag_node_t **node, svn_fs_root_t *root, apr_pool_t *pool) { - return get_dag(node, root, "/", TRUE, pool); + return get_dag(node, root, "/", pool); } @@ -1624,12 +1636,13 @@ conflict_err(svn_stringbuf_t *conflict_p _("Conflict at '%s'"), path); } -/* Compare the directory representations at nodes LHS and RHS and set +/* Compare the directory representations at nodes LHS and RHS in FS and set * *CHANGED to TRUE, if at least one entry has been added or removed them. * Use POOL for temporary allocations. */ static svn_error_t * compare_dir_structure(svn_boolean_t *changed, + svn_fs_t *fs, dag_node_t *lhs, dag_node_t *rhs, apr_pool_t *pool) @@ -1637,6 +1650,7 @@ compare_dir_structure(svn_boolean_t *cha apr_array_header_t *lhs_entries; apr_array_header_t *rhs_entries; int i; + apr_pool_t *iterpool = svn_pool_create(pool); SVN_ERR(svn_fs_x__dag_dir_entries(&lhs_entries, lhs, pool)); SVN_ERR(svn_fs_x__dag_dir_entries(&rhs_entries, rhs, pool)); @@ -1645,23 +1659,42 @@ compare_dir_structure(svn_boolean_t *cha entries one-by-one without binary lookup etc. */ for (i = 0; i < lhs_entries->nelts; ++i) { - svn_fs_dirent_t *lhs_entry - = APR_ARRAY_IDX(lhs_entries, i, svn_fs_dirent_t *); - svn_fs_dirent_t *rhs_entry - = APR_ARRAY_IDX(rhs_entries, i, svn_fs_dirent_t *); - - if (strcmp(lhs_entry->name, rhs_entry->name) - || !svn_fs_x__id_part_eq(svn_fs_x__id_node_id(lhs_entry->id), - svn_fs_x__id_node_id(rhs_entry->id)) - || !svn_fs_x__id_part_eq(svn_fs_x__id_copy_id(lhs_entry->id), - svn_fs_x__id_copy_id(rhs_entry->id))) + svn_fs_x__dirent_t *lhs_entry + = APR_ARRAY_IDX(lhs_entries, i, svn_fs_x__dirent_t *); + svn_fs_x__dirent_t *rhs_entry + = APR_ARRAY_IDX(rhs_entries, i, svn_fs_x__dirent_t *); + + if (strcmp(lhs_entry->name, rhs_entry->name) == 0) { - *changed = TRUE; - return SVN_NO_ERROR; + svn_boolean_t same_history; + dag_node_t *lhs_node, *rhs_node; + + /* Unchanged entry? */ + if (!svn_fs_x__id_eq(&lhs_entry->id, &rhs_entry->id)) + continue; + + /* We get here rarely. */ + svn_pool_clear(iterpool); + + /* Modified but not copied / replaced or anything? */ + SVN_ERR(svn_fs_x__dag_get_node(&lhs_node, fs, &lhs_entry->id, + iterpool)); + SVN_ERR(svn_fs_x__dag_get_node(&rhs_node, fs, &rhs_entry->id, + iterpool)); + SVN_ERR(svn_fs_x__dag_same_line_of_history(&same_history, + lhs_node, rhs_node)); + if (same_history) + continue; } + + /* This is a different entry. */ + *changed = TRUE; + break; } + svn_pool_destroy(iterpool); *changed = FALSE; + return SVN_NO_ERROR; } @@ -1697,7 +1730,7 @@ merge(svn_stringbuf_t *conflict_p, apr_int64_t *mergeinfo_increment_out, apr_pool_t *pool) { - const svn_fs_id_t *source_id, *target_id, *ancestor_id; + const svn_fs_x__id_t *source_id, *target_id, *ancestor_id; apr_array_header_t *s_entries, *t_entries, *a_entries; int i, s_idx = -1, t_idx = -1; svn_fs_t *fs; @@ -1824,13 +1857,21 @@ merge(svn_stringbuf_t *conflict_p, happening), the merge should fail. See issue #2751. */ { - node_revision_t *tgt_nr, *anc_nr, *src_nr; + svn_fs_x__noderev_t *tgt_nr, *anc_nr, *src_nr; svn_boolean_t same; + apr_pool_t *scratch_pool; /* Get node revisions for our id's. */ - SVN_ERR(svn_fs_x__get_node_revision(&tgt_nr, fs, target_id, pool)); - SVN_ERR(svn_fs_x__get_node_revision(&anc_nr, fs, ancestor_id, pool)); - SVN_ERR(svn_fs_x__get_node_revision(&src_nr, fs, source_id, pool)); + scratch_pool = svn_pool_create(pool); + SVN_ERR(svn_fs_x__get_node_revision(&tgt_nr, fs, target_id, + pool, scratch_pool)); + svn_pool_clear(scratch_pool); + SVN_ERR(svn_fs_x__get_node_revision(&anc_nr, fs, ancestor_id, + pool, scratch_pool)); + svn_pool_clear(scratch_pool); + SVN_ERR(svn_fs_x__get_node_revision(&src_nr, fs, source_id, + pool, scratch_pool)); + svn_pool_destroy(scratch_pool); /* Now compare the prop-keys of the skels. Note that just because the keys are different -doesn't- mean the proplists have @@ -1849,7 +1890,7 @@ merge(svn_stringbuf_t *conflict_p, to its entries, i.e. there were no additions or removals. Those could cause update problems to the working copy. */ svn_boolean_t changed; - SVN_ERR(compare_dir_structure(&changed, source, ancestor, pool)); + SVN_ERR(compare_dir_structure(&changed, fs, source, ancestor, pool)); if (changed) return conflict_err(conflict_p, target_path); @@ -1867,44 +1908,44 @@ merge(svn_stringbuf_t *conflict_p, iterpool = svn_pool_create(pool); for (i = 0; i < a_entries->nelts; ++i) { - svn_fs_dirent_t *s_entry, *t_entry, *a_entry; + svn_fs_x__dirent_t *s_entry, *t_entry, *a_entry; svn_pool_clear(iterpool); - a_entry = APR_ARRAY_IDX(a_entries, i, svn_fs_dirent_t *); + a_entry = APR_ARRAY_IDX(a_entries, i, svn_fs_x__dirent_t *); s_entry = svn_fs_x__find_dir_entry(s_entries, a_entry->name, &s_idx); t_entry = svn_fs_x__find_dir_entry(t_entries, a_entry->name, &t_idx); /* No changes were made to this entry while the transaction was in progress, so do nothing to the target. */ - if (s_entry && svn_fs_x__id_eq(a_entry->id, s_entry->id)) + if (s_entry && svn_fs_x__id_eq(&a_entry->id, &s_entry->id)) continue; /* A change was made to this entry while the transaction was in process, but the transaction did not touch this entry. */ - else if (t_entry && svn_fs_x__id_eq(a_entry->id, t_entry->id)) + else if (t_entry && svn_fs_x__id_eq(&a_entry->id, &t_entry->id)) { apr_int64_t mergeinfo_start; apr_int64_t mergeinfo_end; dag_node_t *t_ent_node; - SVN_ERR(svn_fs_x__dag_get_node(&t_ent_node, fs, - t_entry->id, iterpool)); + SVN_ERR(svn_fs_x__dag_get_node(&t_ent_node, fs, &t_entry->id, + iterpool)); SVN_ERR(svn_fs_x__dag_get_mergeinfo_count(&mergeinfo_start, - t_ent_node)); + t_ent_node)); mergeinfo_increment -= mergeinfo_start; if (s_entry) { dag_node_t *s_ent_node; - SVN_ERR(svn_fs_x__dag_get_node(&s_ent_node, fs, - s_entry->id, iterpool)); + SVN_ERR(svn_fs_x__dag_get_node(&s_ent_node, fs, &s_entry->id, + iterpool)); SVN_ERR(svn_fs_x__dag_get_mergeinfo_count(&mergeinfo_end, s_ent_node)); mergeinfo_increment += mergeinfo_end; SVN_ERR(svn_fs_x__dag_set_entry(target, a_entry->name, - s_entry->id, + &s_entry->id, s_entry->kind, txn_id, iterpool)); @@ -1924,6 +1965,7 @@ merge(svn_stringbuf_t *conflict_p, dag_node_t *s_ent_node, *t_ent_node, *a_ent_node; const char *new_tpath; apr_int64_t sub_mergeinfo_increment; + svn_boolean_t s_a_same, t_a_same; /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a double delete; if one of them is null, that's a delete versus @@ -1943,16 +1985,21 @@ merge(svn_stringbuf_t *conflict_p, a_entry->name, iterpool)); + /* Fetch DAG nodes to efficiently access ID parts. */ + SVN_ERR(svn_fs_x__dag_get_node(&s_ent_node, fs, &s_entry->id, + iterpool)); + SVN_ERR(svn_fs_x__dag_get_node(&t_ent_node, fs, &t_entry->id, + iterpool)); + SVN_ERR(svn_fs_x__dag_get_node(&a_ent_node, fs, &a_entry->id, + iterpool)); + /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct modification of ANCESTOR-ENTRY, declare a conflict. */ - if (!svn_fs_x__id_part_eq(svn_fs_x__id_node_id(s_entry->id), - svn_fs_x__id_node_id(a_entry->id)) - || !svn_fs_x__id_part_eq(svn_fs_x__id_copy_id(s_entry->id), - svn_fs_x__id_copy_id(a_entry->id)) - || !svn_fs_x__id_part_eq(svn_fs_x__id_node_id(t_entry->id), - svn_fs_x__id_node_id(a_entry->id)) - || !svn_fs_x__id_part_eq(svn_fs_x__id_copy_id(t_entry->id), - svn_fs_x__id_copy_id(a_entry->id))) + SVN_ERR(svn_fs_x__dag_same_line_of_history(&s_a_same, s_ent_node, + a_ent_node)); + SVN_ERR(svn_fs_x__dag_same_line_of_history(&t_a_same, t_ent_node, + a_ent_node)); + if (!s_a_same || !t_a_same) return conflict_err(conflict_p, svn_fspath__join(target_path, a_entry->name, @@ -1961,12 +2008,6 @@ merge(svn_stringbuf_t *conflict_p, /* Direct modifications were made to the directory ANCESTOR-ENTRY in both SOURCE and TARGET. Recursively merge these modifications. */ - SVN_ERR(svn_fs_x__dag_get_node(&s_ent_node, fs, - s_entry->id, iterpool)); - SVN_ERR(svn_fs_x__dag_get_node(&t_ent_node, fs, - t_entry->id, iterpool)); - SVN_ERR(svn_fs_x__dag_get_node(&a_ent_node, fs, - a_entry->id, iterpool)); new_tpath = svn_fspath__join(target_path, t_entry->name, iterpool); SVN_ERR(merge(conflict_p, new_tpath, t_ent_node, s_ent_node, a_ent_node, @@ -1980,13 +2021,13 @@ merge(svn_stringbuf_t *conflict_p, /* For each entry E in source but not in ancestor */ for (i = 0; i < s_entries->nelts; ++i) { - svn_fs_dirent_t *a_entry, *s_entry, *t_entry; + svn_fs_x__dirent_t *a_entry, *s_entry, *t_entry; dag_node_t *s_ent_node; apr_int64_t mergeinfo_s; svn_pool_clear(iterpool); - s_entry = APR_ARRAY_IDX(s_entries, i, svn_fs_dirent_t *); + s_entry = APR_ARRAY_IDX(s_entries, i, svn_fs_x__dirent_t *); a_entry = svn_fs_x__find_dir_entry(a_entries, s_entry->name, &s_idx); t_entry = svn_fs_x__find_dir_entry(t_entries, s_entry->name, &t_idx); @@ -2001,13 +2042,13 @@ merge(svn_stringbuf_t *conflict_p, t_entry->name, iterpool)); - SVN_ERR(svn_fs_x__dag_get_node(&s_ent_node, fs, - s_entry->id, iterpool)); + SVN_ERR(svn_fs_x__dag_get_node(&s_ent_node, fs, &s_entry->id, + iterpool)); SVN_ERR(svn_fs_x__dag_get_mergeinfo_count(&mergeinfo_s, s_ent_node)); mergeinfo_increment += mergeinfo_s; SVN_ERR(svn_fs_x__dag_set_entry - (target, s_entry->name, s_entry->id, s_entry->kind, + (target, s_entry->name, &s_entry->id, s_entry->kind, txn_id, iterpool)); } svn_pool_destroy(iterpool); @@ -2044,6 +2085,7 @@ merge_changes(dag_node_t *ancestor_node, dag_node_t *txn_root_node; svn_fs_t *fs = txn->fs; svn_fs_x__txn_id_t txn_id = svn_fs_x__txn_get_id(txn); + svn_boolean_t related; SVN_ERR(svn_fs_x__dag_txn_root(&txn_root_node, fs, txn_id, pool)); @@ -2053,8 +2095,8 @@ merge_changes(dag_node_t *ancestor_node, txn_id, pool)); } - if (svn_fs_x__id_eq(svn_fs_x__dag_get_id(ancestor_node), - svn_fs_x__dag_get_id(txn_root_node))) + SVN_ERR(svn_fs_x__dag_related_node(&related, ancestor_node, txn_root_node)); + if (!related) { /* If no changes have been made in TXN since its current base, then it can't conflict with any changes since that base. @@ -2118,7 +2160,7 @@ svn_fs_x__commit_txn(const char **confli svn_error_t *err = SVN_NO_ERROR; svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool); svn_fs_t *fs = txn->fs; - fs_x_data_t *ffd = fs->fsap_data; + svn_fs_x__data_t *ffd = fs->fsap_data; /* Limit memory usage when the repository has a high commit rate and needs to run the following while loop multiple times. The memory @@ -2293,7 +2335,7 @@ svn_fs_x__deltify(svn_fs_t *fs, /* Set *TABLE_P to a newly allocated APR hash table containing the entries of the directory at PATH in ROOT. The keys of the table are entry names, as byte strings, excluding the final null - character; the table's values are pointers to svn_fs_dirent_t + character; the table's values are pointers to svn_fs_svn_fs_x__dirent_t structures. Allocate the table and its contents in POOL. */ static svn_error_t * x_dir_entries(apr_hash_t **table_p, @@ -2305,16 +2347,27 @@ x_dir_entries(apr_hash_t **table_p, apr_hash_t *hash = svn_hash__make(pool); apr_array_header_t *table; int i; + svn_fs_x__id_context_t *context = NULL; /* Get the entries for this path in the caller's pool. */ - SVN_ERR(get_dag(&node, root, path, FALSE, pool)); + SVN_ERR(get_dag(&node, root, path, pool)); SVN_ERR(svn_fs_x__dag_dir_entries(&table, node, pool)); + if (table->nelts) + context = svn_fs_x__id_create_context(root->fs, pool); + /* Convert directory array to hash. */ for (i = 0; i < table->nelts; ++i) { - svn_fs_dirent_t *entry = APR_ARRAY_IDX(table, i, svn_fs_dirent_t *); - svn_hash_sets(hash, entry->name, entry); + svn_fs_x__dirent_t *entry + = APR_ARRAY_IDX(table, i, svn_fs_x__dirent_t *); + + svn_fs_dirent_t *api_dirent = apr_pcalloc(pool, sizeof(*api_dirent)); + api_dirent->name = entry->name; + api_dirent->kind = entry->kind; + api_dirent->id = svn_fs_x__id_create(context, &entry->id, pool); + + svn_hash_sets(hash, api_dirent->name, api_dirent); } *table_p = hash; @@ -2344,17 +2397,18 @@ x_make_dir(svn_fs_root_t *root, parent_path_t *parent_path; dag_node_t *sub_dir; svn_fs_x__txn_id_t txn_id = root_txn_id(root); + apr_pool_t *subpool = svn_pool_create(pool); - path = svn_fs__canonicalize_abspath(path, pool); + path = svn_fs__canonicalize_abspath(path, subpool); SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional, - TRUE, pool)); + TRUE, subpool)); /* Check (recursively) to see if some lock is 'reserving' a path at that location, or even some child-path; if so, check that we can use it. */ if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS) SVN_ERR(svn_fs_x__allow_locked_operation(path, root->fs, TRUE, FALSE, - pool)); + subpool)); /* If there's already a sub-directory by that name, complain. This also catches the case of trying to make a subdirectory named `/'. */ @@ -2362,23 +2416,26 @@ x_make_dir(svn_fs_root_t *root, return SVN_FS__ALREADY_EXISTS(root, path); /* Create the subdirectory. */ - SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool)); + SVN_ERR(make_path_mutable(root, parent_path->parent, path, subpool)); SVN_ERR(svn_fs_x__dag_make_dir(&sub_dir, parent_path->parent->node, parent_path_path(parent_path->parent, - pool), + subpool), parent_path->entry, txn_id, - pool)); + subpool)); /* Add this directory to the path cache. */ - SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool), - sub_dir, pool)); + SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, subpool), + sub_dir, subpool)); /* Make a record of this modification in the changes table. */ - return add_change(root->fs, txn_id, path, svn_fs_x__dag_get_id(sub_dir), - svn_fs_path_change_add, FALSE, FALSE, FALSE, - svn_node_dir, SVN_INVALID_REVNUM, NULL, pool); + SVN_ERR(add_change(root->fs, txn_id, path, svn_fs_x__dag_get_id(sub_dir), + svn_fs_path_change_add, FALSE, FALSE, FALSE, + svn_node_dir, SVN_INVALID_REVNUM, NULL, subpool)); + + svn_pool_destroy(subpool); + return SVN_NO_ERROR; } @@ -2393,13 +2450,14 @@ x_delete_node(svn_fs_root_t *root, svn_fs_x__txn_id_t txn_id; apr_int64_t mergeinfo_count = 0; svn_node_kind_t kind; + apr_pool_t *subpool = svn_pool_create(pool); if (! root->is_txn_root) return SVN_FS__NOT_TXN(root); txn_id = root_txn_id(root); - path = svn_fs__canonicalize_abspath(path, pool); - SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool)); + path = svn_fs__canonicalize_abspath(path, subpool); + SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, subpool)); kind = svn_fs_x__dag_node_kind(parent_path->node); /* We can't remove the root of the filesystem. */ @@ -2411,31 +2469,35 @@ x_delete_node(svn_fs_root_t *root, check that we can use the existing lock(s). */ if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS) SVN_ERR(svn_fs_x__allow_locked_operation(path, root->fs, TRUE, FALSE, - pool)); + subpool)); /* Make the parent directory mutable, and do the deletion. */ - SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool)); + SVN_ERR(make_path_mutable(root, parent_path->parent, path, subpool)); SVN_ERR(svn_fs_x__dag_get_mergeinfo_count(&mergeinfo_count, parent_path->node)); SVN_ERR(svn_fs_x__dag_delete(parent_path->parent->node, parent_path->entry, - txn_id, pool)); + txn_id, subpool)); /* Remove this node and any children from the path cache. */ - SVN_ERR(dag_node_cache_invalidate(root, parent_path_path(parent_path, pool), - pool)); + SVN_ERR(dag_node_cache_invalidate(root, parent_path_path(parent_path, + subpool), + subpool)); /* Update mergeinfo counts for parents */ if (mergeinfo_count > 0) SVN_ERR(increment_mergeinfo_up_tree(parent_path->parent, -mergeinfo_count, - pool)); + subpool)); /* Make a record of this modification in the changes table. */ - return add_change(root->fs, txn_id, path, - svn_fs_x__dag_get_id(parent_path->node), - svn_fs_path_change_delete, FALSE, FALSE, FALSE, kind, - SVN_INVALID_REVNUM, NULL, pool); + SVN_ERR(add_change(root->fs, txn_id, path, + svn_fs_x__dag_get_id(parent_path->node), + svn_fs_path_change_delete, FALSE, FALSE, FALSE, kind, + SVN_INVALID_REVNUM, NULL, subpool)); + + svn_pool_destroy(subpool); + return SVN_NO_ERROR; } @@ -2452,27 +2514,15 @@ x_same_p(svn_boolean_t *same_p, return SVN_NO_ERROR; } -/* Type to select the various behavioral modes of copy_helper. - */ -typedef enum copy_type_t -{ - /* add without history */ - copy_type_plain_add, - - /* add with history */ - copy_type_add_with_history -} copy_type_t; - /* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under - TO_ROOT. COPY_TYPE determines whether then the copy is recorded in - the copies table and whether it is being marked as a move. - Perform temporary allocations in POOL. */ + TO_ROOT. If PRESERVE_HISTORY is set, then the copy is recorded in + the copies table. Perform temporary allocations in POOL. */ static svn_error_t * copy_helper(svn_fs_root_t *from_root, const char *from_path, svn_fs_root_t *to_root, const char *to_path, - copy_type_t copy_type, + svn_boolean_t preserve_history, apr_pool_t *pool) { dag_node_t *from_node; @@ -2501,7 +2551,7 @@ copy_helper(svn_fs_root_t *from_root, _("Copy immutable tree not supported")); /* Get the NODE for FROM_PATH in FROM_ROOT.*/ - SVN_ERR(get_dag(&from_node, from_root, from_path, TRUE, pool)); + SVN_ERR(get_dag(&from_node, from_root, from_path, pool)); /* Build up the parent path from TO_PATH in TO_ROOT. If the last component does not exist, it's not that big a deal. We'll just @@ -2558,7 +2608,7 @@ copy_helper(svn_fs_root_t *from_root, SVN_ERR(svn_fs_x__dag_copy(to_parent_path->parent->node, to_parent_path->entry, from_node, - copy_type != copy_type_plain_add, + preserve_history, from_root->rev, from_canonpath, txn_id, pool)); @@ -2574,7 +2624,7 @@ copy_helper(svn_fs_root_t *from_root, pool)); /* Make a record of this modification in the changes table. */ - SVN_ERR(get_dag(&new_node, to_root, to_path, TRUE, pool)); + SVN_ERR(get_dag(&new_node, to_root, to_path, pool)); SVN_ERR(add_change(to_root->fs, txn_id, to_path, svn_fs_x__dag_get_id(new_node), kind, FALSE, FALSE, FALSE, svn_fs_x__dag_node_kind(from_node), @@ -2611,13 +2661,17 @@ x_copy(svn_fs_root_t *from_root, const char *to_path, apr_pool_t *pool) { - return svn_error_trace(copy_helper(from_root, - svn_fs__canonicalize_abspath(from_path, - pool), - to_root, - svn_fs__canonicalize_abspath(to_path, - pool), - copy_type_add_with_history, pool)); + apr_pool_t *subpool = svn_pool_create(pool); + + SVN_ERR(copy_helper(from_root, + svn_fs__canonicalize_abspath(from_path, subpool), + to_root, + svn_fs__canonicalize_abspath(to_path, subpool), + TRUE, subpool)); + + svn_pool_destroy(subpool); + + return SVN_NO_ERROR; } @@ -2630,12 +2684,19 @@ x_revision_link(svn_fs_root_t *from_root const char *path, apr_pool_t *pool) { + apr_pool_t *subpool; + if (! to_root->is_txn_root) return SVN_FS__NOT_TXN(to_root); - path = svn_fs__canonicalize_abspath(path, pool); - return svn_error_trace(copy_helper(from_root, path, to_root, path, - copy_type_plain_add, pool)); + subpool = svn_pool_create(pool); + + path = svn_fs__canonicalize_abspath(path, subpool); + SVN_ERR(copy_helper(from_root, path, to_root, path, FALSE, subpool)); + + svn_pool_destroy(subpool); + + return SVN_NO_ERROR; } @@ -2653,7 +2714,7 @@ x_copied_from(svn_revnum_t *rev_p, /* There is no cached entry, look it up the old-fashioned way. */ - SVN_ERR(get_dag(&node, root, path, TRUE, pool)); + SVN_ERR(get_dag(&node, root, path, pool)); SVN_ERR(svn_fs_x__dag_get_copyfrom_rev(rev_p, node)); SVN_ERR(svn_fs_x__dag_get_copyfrom_path(path_p, node)); @@ -2674,10 +2735,11 @@ x_make_file(svn_fs_root_t *root, parent_path_t *parent_path; dag_node_t *child; svn_fs_x__txn_id_t txn_id = root_txn_id(root); + apr_pool_t *subpool = svn_pool_create(pool); - path = svn_fs__canonicalize_abspath(path, pool); + path = svn_fs__canonicalize_abspath(path, subpool); SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional, - TRUE, pool)); + TRUE, subpool)); /* If there's already a file by that name, complain. This also catches the case of trying to make a file named `/'. */ @@ -2688,26 +2750,29 @@ x_make_file(svn_fs_root_t *root, that we can use it. */ if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS) SVN_ERR(svn_fs_x__allow_locked_operation(path, root->fs, FALSE, FALSE, - pool)); + subpool)); /* Create the file. */ - SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool)); + SVN_ERR(make_path_mutable(root, parent_path->parent, path, subpool)); SVN_ERR(svn_fs_x__dag_make_file(&child, parent_path->parent->node, parent_path_path(parent_path->parent, - pool), + subpool), parent_path->entry, txn_id, - pool)); + subpool)); /* Add this file to the path cache. */ - SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool), child, - pool)); + SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, subpool), + child, subpool)); /* Make a record of this modification in the changes table. */ - return add_change(root->fs, txn_id, path, svn_fs_x__dag_get_id(child), - svn_fs_path_change_add, TRUE, FALSE, FALSE, - svn_node_file, SVN_INVALID_REVNUM, NULL, pool); + SVN_ERR(add_change(root->fs, txn_id, path, svn_fs_x__dag_get_id(child), + svn_fs_path_change_add, TRUE, FALSE, FALSE, + svn_node_file, SVN_INVALID_REVNUM, NULL, subpool)); + + svn_pool_destroy(subpool); + return SVN_NO_ERROR; } @@ -2722,7 +2787,7 @@ x_file_length(svn_filesize_t *length_p, dag_node_t *file; /* First create a dag_node_t from the root/path pair. */ - SVN_ERR(get_dag(&file, root, path, FALSE, pool)); + SVN_ERR(get_dag(&file, root, path, pool)); /* Now fetch its length */ return svn_fs_x__dag_file_length(length_p, file, pool); @@ -2741,7 +2806,7 @@ x_file_checksum(svn_checksum_t **checksu { dag_node_t *file; - SVN_ERR(get_dag(&file, root, path, FALSE, pool)); + SVN_ERR(get_dag(&file, root, path, pool)); return svn_fs_x__dag_file_checksum(checksum, file, kind, pool); } @@ -2760,7 +2825,7 @@ x_file_contents(svn_stream_t **contents, svn_stream_t *file_stream; /* First create a dag_node_t from the root/path pair. */ - SVN_ERR(get_dag(&node, root, path, FALSE, pool)); + SVN_ERR(get_dag(&node, root, path, pool)); /* Then create a readable stream from the dag_node_t. */ SVN_ERR(svn_fs_x__dag_get_contents(&file_stream, node, pool)); @@ -2783,7 +2848,7 @@ x_try_process_file_contents(svn_boolean_ apr_pool_t *pool) { dag_node_t *node; - SVN_ERR(get_dag(&node, root, path, FALSE, pool)); + SVN_ERR(get_dag(&node, root, path, pool)); return svn_fs_x__dag_try_process_file_contents(success, node, processor, baton, pool); @@ -2868,7 +2933,7 @@ apply_textdelta(void *baton, apr_pool_t /* Now, make sure this path is mutable. */ SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool)); - tb->node = parent_path->node; + tb->node = svn_fs_x__dag_dup(parent_path->node, tb->pool); if (tb->base_checksum) { @@ -2923,6 +2988,7 @@ x_apply_textdelta(svn_txdelta_window_han svn_checksum_t *result_checksum, apr_pool_t *pool) { + apr_pool_t *subpool = svn_pool_create(pool); txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb)); tb->root = root; @@ -2931,10 +2997,12 @@ x_apply_textdelta(svn_txdelta_window_han tb->base_checksum = svn_checksum_dup(base_checksum, pool); tb->result_checksum = svn_checksum_dup(result_checksum, pool); - SVN_ERR(apply_textdelta(tb, pool)); + SVN_ERR(apply_textdelta(tb, subpool)); *contents_p = window_consumer; *contents_baton_p = tb; + + svn_pool_destroy(subpool); return SVN_NO_ERROR; } @@ -3027,11 +3095,11 @@ apply_text(void *baton, apr_pool_t *pool /* Now, make sure this path is mutable. */ SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool)); - tb->node = parent_path->node; + tb->node = svn_fs_x__dag_dup(parent_path->node, tb->pool); /* Make a writable stream for replacing the file's text. */ SVN_ERR(svn_fs_x__dag_get_edit_stream(&(tb->file_stream), tb->node, - tb->pool)); + tb->pool)); /* Create a 'returnable' stream which writes to the file_stream. */ tb->stream = svn_stream_create(tb, tb->pool); @@ -3056,6 +3124,7 @@ x_apply_text(svn_stream_t **contents_p, svn_checksum_t *result_checksum, apr_pool_t *pool) { + apr_pool_t *subpool = svn_pool_create(pool); struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb)); tb->root = root; @@ -3063,9 +3132,11 @@ x_apply_text(svn_stream_t **contents_p, tb->pool = pool; tb->result_checksum = svn_checksum_dup(result_checksum, pool); - SVN_ERR(apply_text(tb, pool)); + SVN_ERR(apply_text(tb, subpool)); *contents_p = tb->stream; + + svn_pool_destroy(subpool); return SVN_NO_ERROR; } @@ -3085,6 +3156,7 @@ x_contents_changed(svn_boolean_t *change apr_pool_t *pool) { dag_node_t *node1, *node2; + apr_pool_t *subpool = svn_pool_create(pool); /* Check that roots are in the same fs. */ if (root1->fs != root2->fs) @@ -3096,21 +3168,24 @@ x_contents_changed(svn_boolean_t *change { svn_node_kind_t kind; - SVN_ERR(svn_fs_x__check_path(&kind, root1, path1, pool)); + SVN_ERR(svn_fs_x__check_path(&kind, root1, path1, subpool)); if (kind != svn_node_file) return svn_error_createf (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path1); - SVN_ERR(svn_fs_x__check_path(&kind, root2, path2, pool)); + SVN_ERR(svn_fs_x__check_path(&kind, root2, path2, subpool)); if (kind != svn_node_file) return svn_error_createf (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2); } - SVN_ERR(get_dag(&node1, root1, path1, TRUE, pool)); - SVN_ERR(get_dag(&node2, root2, path2, TRUE, pool)); - return svn_fs_x__dag_things_different(NULL, changed_p, node1, node2, - strict, pool); + SVN_ERR(get_dag(&node1, root1, path1, subpool)); + SVN_ERR(get_dag(&node2, root2, path2, subpool)); + SVN_ERR(svn_fs_x__dag_things_different(NULL, changed_p, node1, node2, + strict, subpool)); + + svn_pool_destroy(subpool); + return SVN_NO_ERROR; } @@ -3126,22 +3201,55 @@ x_get_file_delta_stream(svn_txdelta_stre apr_pool_t *pool) { dag_node_t *source_node, *target_node; + apr_pool_t *subpool = svn_pool_create(pool); if (source_root && source_path) - SVN_ERR(get_dag(&source_node, source_root, source_path, TRUE, pool)); + SVN_ERR(get_dag(&source_node, source_root, source_path, pool)); else source_node = NULL; - SVN_ERR(get_dag(&target_node, target_root, target_path, TRUE, pool)); + SVN_ERR(get_dag(&target_node, target_root, target_path, pool)); /* Create a delta stream that turns the source into the target. */ - return svn_fs_x__dag_get_file_delta_stream(stream_p, source_node, - target_node, pool); + SVN_ERR(svn_fs_x__dag_get_file_delta_stream(stream_p, source_node, + target_node, pool)); + + svn_pool_destroy(subpool); + return SVN_NO_ERROR; } /* Finding Changes */ +/* Copy CHANGE into a FS API object allocated in RESULT_POOL and return + it in *RESULT_P. Pass CONTEXT to the ID API object being created. */ +static svn_error_t * +construct_fs_path_change(svn_fs_path_change2_t **result_p, + svn_fs_x__id_context_t *context, + svn_fs_x__change_t *change, + apr_pool_t *result_pool) +{ + const svn_fs_id_t *id + = svn_fs_x__id_create(context, &change->noderev_id, result_pool); + svn_fs_path_change2_t *result + = svn_fs__path_change_create_internal(id, change->change_kind, + result_pool); + + result->text_mod = change->text_mod; + result->prop_mod = change->prop_mod; + result->node_kind = change->node_kind; + + result->copyfrom_known = change->copyfrom_known; + result->copyfrom_rev = change->copyfrom_rev; + result->copyfrom_path = change->copyfrom_path; + + result->mergeinfo_mod = change->mergeinfo_mod; + + *result_p = result; + + return SVN_NO_ERROR; +} + /* Set *CHANGED_PATHS_P to a newly allocated hash containing descriptions of the paths changed under ROOT. The hash is keyed with const char * paths and has svn_fs_path_change2_t * values. Use @@ -3151,12 +3259,50 @@ x_paths_changed(apr_hash_t **changed_pat svn_fs_root_t *root, apr_pool_t *pool) { + apr_hash_t *changed_paths; + svn_fs_path_change2_t *path_change; + svn_fs_x__id_context_t *context + = svn_fs_x__id_create_context(root->fs, pool); + if (root->is_txn_root) - return svn_fs_x__txn_changes_fetch(changed_paths_p, root->fs, - root_txn_id(root), pool); + { + apr_hash_index_t *hi; + SVN_ERR(svn_fs_x__txn_changes_fetch(&changed_paths, root->fs, + root_txn_id(root), pool)); + for (hi = apr_hash_first(pool, changed_paths); + hi; + hi = apr_hash_next(hi)) + { + svn_fs_x__change_t *change = apr_hash_this_val(hi); + SVN_ERR(construct_fs_path_change(&path_change, context, change, + pool)); + apr_hash_set(changed_paths, + apr_hash_this_key(hi), apr_hash_this_key_len(hi), + path_change); + } + } else - return svn_fs_x__paths_changed(changed_paths_p, root->fs, root->rev, - pool); + { + apr_array_header_t *changes; + int i; + + SVN_ERR(svn_fs_x__get_changes(&changes, root->fs, root->rev, pool)); + + changed_paths = svn_hash__make(pool); + for (i = 0; i < changes->nelts; ++i) + { + svn_fs_x__change_t *change = APR_ARRAY_IDX(changes, i, + svn_fs_x__change_t *); + SVN_ERR(construct_fs_path_change(&path_change, context, change, + pool)); + apr_hash_set(changed_paths, change->path.data, change->path.len, + path_change); + } + } + + *changed_paths_p = changed_paths; + + return SVN_NO_ERROR; } @@ -3218,7 +3364,10 @@ x_node_history(svn_fs_history_t **histor /* Find the youngest copyroot for path PARENT_PATH or its parents in filesystem FS, and store the copyroot in *REV_P and *PATH_P. - Perform all allocations in POOL. */ + Perform all allocations in POOL. + + Note that *PATH_P will not be allocated in POOL but will be taken from + a DAG node in PARENT_PATH. */ static svn_error_t * find_youngest_copyroot(svn_revnum_t *rev_p, const char **path_p, @@ -3271,35 +3420,48 @@ svn_error_t *x_closest_copy(svn_fs_root_ const char *copy_dst_path; svn_fs_root_t *copy_dst_root; dag_node_t *copy_dst_node; + svn_boolean_t related; + apr_pool_t *subpool = svn_pool_create(pool); /* Initialize return values. */ *root_p = NULL; *path_p = NULL; - path = svn_fs__canonicalize_abspath(path, pool); - SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, pool)); + path = svn_fs__canonicalize_abspath(path, subpool); + SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, subpool)); /* Find the youngest copyroot in the path of this node-rev, which will indicate the target of the innermost copy affecting the node-rev. */ SVN_ERR(find_youngest_copyroot(©_dst_rev, ©_dst_path, - fs, parent_path, pool)); + fs, parent_path, subpool)); if (copy_dst_rev == 0) /* There are no copies affecting this node-rev. */ - return SVN_NO_ERROR; + { + svn_pool_destroy(subpool); + return SVN_NO_ERROR; + } /* It is possible that this node was created from scratch at some revision between COPY_DST_REV and REV. Make sure that PATH exists as of COPY_DST_REV and is related to this node-rev. */ SVN_ERR(svn_fs_x__revision_root(©_dst_root, fs, copy_dst_rev, pool)); SVN_ERR(open_path(©_dst_parent_path, copy_dst_root, path, - open_path_node_only | open_path_allow_null, FALSE, pool)); + open_path_node_only | open_path_allow_null, FALSE, + subpool)); if (copy_dst_parent_path == NULL) - return SVN_NO_ERROR; + { + svn_pool_destroy(subpool); + return SVN_NO_ERROR; + } copy_dst_node = copy_dst_parent_path->node; - if (! svn_fs_x__id_check_related(svn_fs_x__dag_get_id(copy_dst_node), - svn_fs_x__dag_get_id(parent_path->node))) - return SVN_NO_ERROR; + SVN_ERR(svn_fs_x__dag_related_node(&related, copy_dst_node, + parent_path->node)); + if (!related) + { + svn_pool_destroy(subpool); + return SVN_NO_ERROR; + } /* One final check must be done here. If you copy a directory and create a new entity somewhere beneath that directory in the same @@ -3315,18 +3477,23 @@ svn_error_t *x_closest_copy(svn_fs_root_ created-rev is COPY_DST_REV, and that node-revision has no predecessors, then there is no relevant closest copy. */ - SVN_ERR(svn_fs_x__dag_get_revision(&created_rev, copy_dst_node, pool)); + created_rev = svn_fs_x__dag_get_revision(copy_dst_node); if (created_rev == copy_dst_rev) { - const svn_fs_id_t *pred; + svn_fs_x__id_t pred; SVN_ERR(svn_fs_x__dag_get_predecessor_id(&pred, copy_dst_node)); - if (! pred) - return SVN_NO_ERROR; + if (!svn_fs_x__id_used(&pred)) + { + svn_pool_destroy(subpool); + return SVN_NO_ERROR; + } } /* The copy destination checks out. Return it. */ *root_p = copy_dst_root; - *path_p = copy_dst_path; + *path_p = apr_pstrdup(pool, copy_dst_path); + + svn_pool_destroy(subpool); return SVN_NO_ERROR; } @@ -3337,14 +3504,15 @@ x_node_origin_rev(svn_revnum_t *revision const char *path, apr_pool_t *pool) { - const svn_fs_id_t *given_noderev_id; - const svn_fs_x__id_part_t *node_id; + svn_fs_x__id_t node_id; + dag_node_t *node; path = svn_fs__canonicalize_abspath(path, pool); - SVN_ERR(svn_fs_x__node_id(&given_noderev_id, root, path, pool)); - node_id = svn_fs_x__id_node_id(given_noderev_id); - *revision = svn_fs_x__get_revnum(node_id->change_set); + SVN_ERR(get_dag(&node, root, path, pool)); + SVN_ERR(svn_fs_x__dag_get_node_id(&node_id, node)); + + *revision = svn_fs_x__get_revnum(node_id.change_set); return SVN_NO_ERROR; } @@ -3393,7 +3561,7 @@ history_prev(svn_fs_history_t **prev_his SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, scratch_pool)); node = parent_path->node; commit_path = svn_fs_x__dag_get_created_path(node); - SVN_ERR(svn_fs_x__dag_get_revision(&commit_rev, node, scratch_pool)); + commit_rev = svn_fs_x__dag_get_revision(node); /* The Subversion filesystem is written in such a way that a given line of history may have at most one interesting history point @@ -3418,17 +3586,17 @@ history_prev(svn_fs_history_t **prev_his /* ... or we *have* reported on this revision, and must now progress toward this node's predecessor (unless there is no predecessor, in which case we're all done!). */ - const svn_fs_id_t *pred_id; + svn_fs_x__id_t pred_id; SVN_ERR(svn_fs_x__dag_get_predecessor_id(&pred_id, node)); - if (! pred_id) + if (!svn_fs_x__id_used(&pred_id)) return SVN_NO_ERROR; /* Replace NODE and friends with the information from its predecessor. */ - SVN_ERR(svn_fs_x__dag_get_node(&node, fs, pred_id, scratch_pool)); + SVN_ERR(svn_fs_x__dag_get_node(&node, fs, &pred_id, scratch_pool)); commit_path = svn_fs_x__dag_get_created_path(node); - SVN_ERR(svn_fs_x__dag_get_revision(&commit_rev, node, scratch_pool)); + commit_rev = svn_fs_x__dag_get_revision(node); } } @@ -3450,7 +3618,7 @@ history_prev(svn_fs_history_t **prev_his SVN_ERR(svn_fs_x__revision_root(©root_root, fs, copyroot_rev, scratch_pool)); - SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, FALSE, scratch_pool)); + SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, scratch_pool)); copy_dst = svn_fs_x__dag_get_created_path(node); /* If our current path was the very destination of the copy, @@ -3534,12 +3702,14 @@ fs_history_prev(svn_fs_history_t **prev_ } else { + apr_pool_t *iterpool = svn_pool_create(scratch_pool); prev_history = history; while (1) { + svn_pool_clear(iterpool); SVN_ERR(history_prev(&prev_history, prev_history, cross_copies, - result_pool, scratch_pool)); + result_pool, iterpool)); if (! prev_history) break; @@ -3547,6 +3717,8 @@ fs_history_prev(svn_fs_history_t **prev_ if (fhd->is_interesting) break; } + + svn_pool_destroy(iterpool); } *prev_history_p = prev_history; @@ -3632,7 +3804,7 @@ crawl_directory_dag_for_mergeinfo(svn_fs SVN_ERR(svn_fs_x__dag_dir_entries(&entries, dir_dag, scratch_pool)); for (i = 0; i < entries->nelts; ++i) { - svn_fs_dirent_t *dirent = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *); + svn_fs_x__dirent_t *dirent = APR_ARRAY_IDX(entries, i, svn_fs_x__dirent_t *); const char *kid_path; dag_node_t *kid_dag; svn_boolean_t has_mergeinfo, go_down; @@ -3640,7 +3812,7 @@ crawl_directory_dag_for_mergeinfo(svn_fs svn_pool_clear(iterpool); kid_path = svn_fspath__join(this_path, dirent->name, iterpool); - SVN_ERR(get_dag(&kid_dag, root, kid_path, TRUE, iterpool)); + SVN_ERR(get_dag(&kid_dag, root, kid_path, iterpool)); SVN_ERR(svn_fs_x__dag_has_mergeinfo(&has_mergeinfo, kid_dag)); SVN_ERR(svn_fs_x__dag_has_descendants_with_mergeinfo(&go_down, kid_dag)); @@ -3657,7 +3829,8 @@ crawl_directory_dag_for_mergeinfo(svn_fs mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO); if (!mergeinfo_string) { - svn_string_t *idstr = svn_fs_x__id_unparse(dirent->id, iterpool); + svn_string_t *idstr + = svn_fs_x__id_unparse(&dirent->id, iterpool); return svn_error_createf (SVN_ERR_FS_CORRUPT, NULL, _("Node-revision #'%s' claims to have mergeinfo but doesn't"), @@ -3832,7 +4005,7 @@ get_mergeinfo_for_path(svn_mergeinfo_t * apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - fs_x_data_t *ffd = rev_root->fs->fsap_data; + svn_fs_x__data_t *ffd = rev_root->fs->fsap_data; const char *cache_key; svn_boolean_t found = FALSE; svn_stringbuf_t *mergeinfo_exists; @@ -3886,7 +4059,7 @@ add_descendant_mergeinfo(svn_mergeinfo_c dag_node_t *this_dag; svn_boolean_t go_down; - SVN_ERR(get_dag(&this_dag, root, path, TRUE, scratch_pool)); + SVN_ERR(get_dag(&this_dag, root, path, scratch_pool)); SVN_ERR(svn_fs_x__dag_has_descendants_with_mergeinfo(&go_down, this_dag)); if (go_down) @@ -3984,7 +4157,7 @@ static root_vtable_t root_vtable = { x_paths_changed, svn_fs_x__check_path, x_node_history, - svn_fs_x__node_id, + x_node_id, x_node_relation, svn_fs_x__node_created_rev, x_node_origin_rev, @@ -4100,19 +4273,33 @@ stringify_node(dag_node_t *node, /* Check metadata sanity on NODE, and on its children. Manually verify information for DAG nodes in revision REV, and trust the metadata - accuracy for nodes belonging to older revisions. */ + accuracy for nodes belonging to older revisions. To detect cycles, + provide all parent dag_node_t * in PARENT_NODES. */ static svn_error_t * verify_node(dag_node_t *node, svn_revnum_t rev, + apr_array_header_t *parent_nodes, apr_pool_t *pool) { svn_boolean_t has_mergeinfo; apr_int64_t mergeinfo_count; - const svn_fs_id_t *pred_id; + svn_fs_x__id_t pred_id; svn_fs_t *fs = svn_fs_x__dag_get_fs(node); int pred_count; svn_node_kind_t kind; apr_pool_t *iterpool = svn_pool_create(pool); + int i; + + /* Detect (non-)DAG cycles. */ + for (i = 0; i < parent_nodes->nelts; ++i) + { + dag_node_t *parent = APR_ARRAY_IDX(parent_nodes, i, dag_node_t *); + if (svn_fs_x__id_eq(svn_fs_x__dag_get_id(parent), + svn_fs_x__dag_get_id(node))) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + "Node is its own direct or indirect parent '%s'", + stringify_node(node, iterpool)); + } /* Fetch some data. */ SVN_ERR(svn_fs_x__dag_has_mergeinfo(&has_mergeinfo, node)); @@ -4129,11 +4316,11 @@ verify_node(dag_node_t *node, mergeinfo_count, stringify_node(node, iterpool)); /* Issue #4129. (This check will explicitly catch non-root instances too.) */ - if (pred_id) + if (svn_fs_x__id_used(&pred_id)) { dag_node_t *pred; int pred_pred_count; - SVN_ERR(svn_fs_x__dag_get_node(&pred, fs, pred_id, iterpool)); + SVN_ERR(svn_fs_x__dag_get_node(&pred, fs, &pred_id, iterpool)); SVN_ERR(svn_fs_x__dag_get_predecessor_count(&pred_pred_count, pred)); if (pred_pred_count+1 != pred_count) return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, @@ -4164,34 +4351,33 @@ verify_node(dag_node_t *node, if (kind == svn_node_dir) { apr_array_header_t *entries; - int i; apr_int64_t children_mergeinfo = 0; + APR_ARRAY_PUSH(parent_nodes, dag_node_t*) = node; SVN_ERR(svn_fs_x__dag_dir_entries(&entries, node, pool)); /* Compute CHILDREN_MERGEINFO. */ for (i = 0; i < entries->nelts; ++i) { - svn_fs_dirent_t *dirent - = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *); + svn_fs_x__dirent_t *dirent = APR_ARRAY_IDX(entries, i, svn_fs_x__dirent_t *); dag_node_t *child; apr_int64_t child_mergeinfo; svn_pool_clear(iterpool); /* Compute CHILD_REV. */ - if (svn_fs_x__id_rev(dirent->id) == rev) + if (svn_fs_x__get_revnum(dirent->id.change_set) == rev) { - SVN_ERR(svn_fs_x__dag_get_node(&child, fs, dirent->id, + SVN_ERR(svn_fs_x__dag_get_node(&child, fs, &dirent->id, iterpool)); - SVN_ERR(verify_node(child, rev, iterpool)); + SVN_ERR(verify_node(child, rev, parent_nodes, iterpool)); SVN_ERR(svn_fs_x__dag_get_mergeinfo_count(&child_mergeinfo, child)); } else { SVN_ERR(svn_fs_x__get_mergeinfo_count(&child_mergeinfo, fs, - dirent->id, iterpool)); + &dirent->id, iterpool)); } children_mergeinfo += child_mergeinfo; @@ -4206,6 +4392,10 @@ verify_node(dag_node_t *node, stringify_node(node, iterpool), mergeinfo_count, has_mergeinfo, children_mergeinfo); + + /* If we don't make it here, there was an error / corruption. + * In that case, nobody will need PARENT_NODES anymore. */ + apr_array_pop(parent_nodes); } svn_pool_destroy(iterpool); @@ -4218,6 +4408,7 @@ svn_fs_x__verify_root(svn_fs_root_t *roo { svn_fs_t *fs = root->fs; dag_node_t *root_dir; + apr_array_header_t *parent_nodes; /* Issue #4129: bogus pred-counts and minfo-cnt's on the root node-rev (and elsewhere). This code makes more thorough checks than the @@ -4241,32 +4432,35 @@ svn_fs_x__verify_root(svn_fs_root_t *roo } /* Recursively verify ROOT_DIR. */ - SVN_ERR(verify_node(root_dir, root->rev, pool)); + parent_nodes = apr_array_make(pool, 16, sizeof(dag_node_t *)); + SVN_ERR(verify_node(root_dir, root->rev, parent_nodes, pool)); /* Verify explicitly the predecessor of the root. */ { - const svn_fs_id_t *pred_id; + svn_fs_x__id_t pred_id; + svn_boolean_t has_predecessor; /* Only r0 should have no predecessor. */ SVN_ERR(svn_fs_x__dag_get_predecessor_id(&pred_id, root_dir)); - if (! root->is_txn_root && !!pred_id != !!root->rev) + has_predecessor = svn_fs_x__id_used(&pred_id); + if (!root->is_txn_root && has_predecessor != !!root->rev) return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, "r%ld's root node's predecessor is " "unexpectedly '%s'", root->rev, - (pred_id - ? svn_fs_x__id_unparse(pred_id, pool)->data - : "(null)")); - if (root->is_txn_root && !pred_id) + (has_predecessor + ? svn_fs_x__id_unparse(&pred_id, pool)->data + : "(null)")); + if (root->is_txn_root && !has_predecessor) return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, "Transaction '%s''s root node's predecessor is " "unexpectedly NULL", root->txn); /* Check the predecessor's revision. */ - if (pred_id) + if (has_predecessor) { - svn_revnum_t pred_rev = svn_fs_x__id_rev(pred_id); + svn_revnum_t pred_rev = svn_fs_x__get_revnum(pred_id.change_set); if (! root->is_txn_root && pred_rev+1 != root->rev) /* Issue #4129. */ return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
Modified: subversion/branches/authzperf/subversion/libsvn_fs_x/tree.h URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_x/tree.h?rev=1649205&r1=1649204&r2=1649205&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/libsvn_fs_x/tree.h (original) +++ subversion/branches/authzperf/subversion/libsvn_fs_x/tree.h Sat Jan 3 14:00:41 2015 @@ -33,7 +33,7 @@ extern "C" { /* In POOL, create an instance of a DAG node 1st level cache. The POOL will be cleared at regular intervals. */ -fs_x_dag_cache_t* +svn_fs_x__dag_cache_t* svn_fs_x__create_dag_cache(apr_pool_t *pool); /* Set *ROOT_P to the root directory of revision REV in filesystem FS. @@ -67,13 +67,6 @@ svn_fs_x__check_path(svn_node_kind_t *ki const char *path, apr_pool_t *pool); -/* Implement root_vtable_t.node_id(). */ -svn_error_t * -svn_fs_x__node_id(const svn_fs_id_t **id_p, - svn_fs_root_t *root, - const char *path, - apr_pool_t *pool); - /* Set *REVISION to the revision in which PATH under ROOT was created. Use POOL for any temporary allocations. If PATH is in an uncommitted transaction, *REVISION will be set to
