You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by br...@apache.org on 2015/01/03 15:00:44 UTC

svn commit: r1649205 [7/30] - in /subversion/branches/authzperf: ./ build/ build/ac-macros/ notes/ subversion/bindings/ctypes-python/ subversion/bindings/cxxhl/ subversion/bindings/javahl/tests/org/apache/subversion/javahl/ subversion/bindings/swig/ su...

Modified: subversion/branches/authzperf/subversion/libsvn_fs_fs/transaction.c
URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_fs/transaction.c?rev=1649205&r1=1649204&r2=1649205&view=diff
==============================================================================
--- subversion/branches/authzperf/subversion/libsvn_fs_fs/transaction.c (original)
+++ subversion/branches/authzperf/subversion/libsvn_fs_fs/transaction.c Sat Jan  3 14:00:41 2015
@@ -391,7 +391,7 @@ auto_truncate_proto_rev(svn_fs_t *fs,
                         apr_pool_t *pool)
 {
   /* Only relevant for newer FSFS formats. */
-  if (svn_fs_fs__use_log_addressing(fs, txn_id->revision))
+  if (svn_fs_fs__use_log_addressing(fs))
     {
       /* Determine file range covered by the proto-index so far.  Note that
          we always append to both file, i.e. the last index entry also
@@ -409,7 +409,7 @@ auto_truncate_proto_rev(svn_fs_t *fs,
       if (indexed_length < actual_length)
         SVN_ERR(svn_io_file_trunc(proto_rev, indexed_length, pool));
       else if (indexed_length > actual_length)
-        return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_INCONSISTENT,
+        return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT,
                                  NULL,
                                  _("p2l proto index offset %s beyond proto"
                                    "rev file size %s for TXN %s"),
@@ -621,40 +621,29 @@ unparse_dir_entries(apr_array_header_t *
   return SVN_NO_ERROR;
 }
 
-/* Copy the contents of NEW_CHANGE into OLD_CHANGE assuming that both
-   belong to the same path.  Allocate copies in POOL.
+/* Return a deep copy of SOURCE and allocate it in RESULT_POOL.
  */
-static void
-replace_change(svn_fs_path_change2_t *old_change,
-               const svn_fs_path_change2_t *new_change,
-               apr_pool_t *pool)
-{
-  /* An add at this point must be following a previous delete,
-      so treat it just like a replace. */
-  old_change->node_kind = new_change->node_kind;
-  old_change->node_rev_id = svn_fs_fs__id_copy(new_change->node_rev_id,
-                                               pool);
-  old_change->text_mod = new_change->text_mod;
-  old_change->prop_mod = new_change->prop_mod;
-  old_change->mergeinfo_mod = new_change->mergeinfo_mod;
-  if (new_change->copyfrom_rev == SVN_INVALID_REVNUM)
-    {
-      old_change->copyfrom_rev = SVN_INVALID_REVNUM;
-      old_change->copyfrom_path = NULL;
-    }
-  else
-    {
-      old_change->copyfrom_rev = new_change->copyfrom_rev;
-      old_change->copyfrom_path = apr_pstrdup(pool,
-                                              new_change->copyfrom_path);
-    }
+static svn_fs_path_change2_t *
+path_change_dup(const svn_fs_path_change2_t *source,
+                apr_pool_t *result_pool)
+{
+  svn_fs_path_change2_t *result = apr_pmemdup(result_pool, source,
+                                              sizeof(*source));
+  result->node_rev_id = svn_fs_fs__id_copy(source->node_rev_id, result_pool);
+  if (source->copyfrom_path)
+    result->copyfrom_path = apr_pstrdup(result_pool, source->copyfrom_path);
+
+  return result;
 }
 
 /* Merge the internal-use-only CHANGE into a hash of public-FS
    svn_fs_path_change2_t CHANGED_PATHS, collapsing multiple changes into a
-   single summarical (is that real word?) change per path.  */
+   single summarical (is that real word?) change per path.  DELETIONS is
+   also a path->svn_fs_path_change2_t hash and contains all the deletions
+   that got turned into a replacement. */
 static svn_error_t *
 fold_change(apr_hash_t *changed_paths,
+            apr_hash_t *deletions,
             const change_t *change)
 {
   apr_pool_t *pool = apr_hash_pool_get(changed_paths);
@@ -686,7 +675,7 @@ fold_change(apr_hash_t *changed_paths,
            _("Invalid change ordering: new node revision ID "
              "without delete"));
 
-      /* Sanity check: an add, replacement, move, or reset must be the first
+      /* Sanity check: an add, replacement, or reset must be the first
          thing to follow a deletion. */
       if ((old_change->change_kind == svn_fs_path_change_delete)
           && (! ((info->change_kind == svn_fs_path_change_replace)
@@ -711,7 +700,7 @@ fold_change(apr_hash_t *changed_paths,
         case svn_fs_path_change_reset:
           /* A reset here will simply remove the path change from the
              hash. */
-          old_change = NULL;
+          apr_hash_set(changed_paths, path->data, path->len, NULL);
           break;
 
         case svn_fs_path_change_delete:
@@ -719,31 +708,48 @@ fold_change(apr_hash_t *changed_paths,
             {
               /* If the path was introduced in this transaction via an
                  add, and we are deleting it, just remove the path
-                 altogether. */
-              old_change = NULL;
+                 altogether.  (The caller will delete any child paths.) */
+              apr_hash_set(changed_paths, path->data, path->len, NULL);
+            }
+          else if (old_change->change_kind == svn_fs_path_change_replace)
+            {
+              /* A deleting a 'replace' restore the original deletion. */
+              new_change = apr_hash_get(deletions, path->data, path->len);
+              SVN_ERR_ASSERT(new_change);
+              apr_hash_set(changed_paths, path->data, path->len, new_change);
             }
           else
             {
-              /* A deletion overrules all previous changes. */
-              old_change->change_kind = svn_fs_path_change_delete;
-              old_change->text_mod = info->text_mod;
-              old_change->prop_mod = info->prop_mod;
-              old_change->mergeinfo_mod = info->mergeinfo_mod;
-              old_change->copyfrom_rev = SVN_INVALID_REVNUM;
-              old_change->copyfrom_path = NULL;
+              /* A deletion overrules a previous change (modify). */
+              new_change = path_change_dup(info, pool);
+              apr_hash_set(changed_paths, path->data, path->len, new_change);
             }
           break;
 
         case svn_fs_path_change_add:
         case svn_fs_path_change_replace:
           /* An add at this point must be following a previous delete,
-             so treat it just like a replace. */
-          replace_change(old_change, info, pool);
-          old_change->change_kind = svn_fs_path_change_replace;
+             so treat it just like a replace.  Remember the original
+             deletion such that we are able to delete this path again
+             (the replacement may have changed node kind and id). */
+          new_change = path_change_dup(info, pool);
+          new_change->change_kind = svn_fs_path_change_replace;
+
+          apr_hash_set(changed_paths, path->data, path->len, new_change);
+
+          /* Remember the original change.
+           * Make sure to allocate the hash key in a durable pool. */
+          apr_hash_set(deletions,
+                       apr_pstrmemdup(apr_hash_pool_get(deletions),
+                                      path->data, path->len),
+                       path->len, old_change);
           break;
 
         case svn_fs_path_change_modify:
         default:
+          /* If the new change modifies some attribute of the node, set
+             the corresponding flag, whether it already was set or not.
+             Note: We do not reset a flag to FALSE if a change is undone. */
           if (info->text_mod)
             old_change->text_mod = TRUE;
           if (info->prop_mod)
@@ -752,44 +758,44 @@ fold_change(apr_hash_t *changed_paths,
             old_change->mergeinfo_mod = svn_tristate_true;
           break;
         }
-
-      /* remove old_change from the cache if it is no longer needed. */
-      if (old_change == NULL)
-        apr_hash_set(changed_paths, path->data, path->len, NULL);
     }
   else
     {
-      /* This change is new to the hash, so make a new public change
-         structure from the internal one (in the hash's pool), and dup
-         the path into the hash's pool, too. */
-      new_change = apr_pmemdup(pool, info, sizeof(*new_change));
-      new_change->node_rev_id = svn_fs_fs__id_copy(info->node_rev_id, pool);
-      if (info->copyfrom_path)
-        new_change->copyfrom_path = apr_pstrdup(pool, info->copyfrom_path);
-
       /* Add this path.  The API makes no guarantees that this (new) key
-        will not be retained.  Thus, we copy the key into the target pool
-        to ensure a proper lifetime.  */
+         will not be retained.  Thus, we copy the key into the target pool
+         to ensure a proper lifetime.  */
       apr_hash_set(changed_paths,
                    apr_pstrmemdup(pool, path->data, path->len), path->len,
-                   new_change);
+                   path_change_dup(info, pool));
     }
 
   return SVN_NO_ERROR;
 }
 
+/* Baton type to be used with process_changes(). */
+typedef struct process_changes_baton_t
+{
+  /* Folded list of path changes. */
+  apr_hash_t *changed_paths;
+
+  /* Path changes that are deletions and have been turned into
+     replacements.  If those replacements get deleted again, this
+     container contains the record that we have to revert to. */
+  apr_hash_t *deletions;
+} process_changes_baton_t;
+
 /* An implementation of svn_fs_fs__change_receiver_t.
    Examine all the changed path entries in CHANGES and store them in
    *CHANGED_PATHS.  Folding is done to remove redundant or unnecessary
    data. Do all allocations in POOL. */
 static svn_error_t *
-process_changes(void *baton,
+process_changes(void *baton_p,
                 change_t *change,
                 apr_pool_t *scratch_pool)
 {
-  apr_hash_t *changed_paths = baton;
+  process_changes_baton_t *baton = baton_p;
 
-  SVN_ERR(fold_change(changed_paths, change));
+  SVN_ERR(fold_change(baton->changed_paths, baton->deletions, change));
 
   /* Now, if our change was a deletion or replacement, we have to
      blow away any changes thus far on paths that are (or, were)
@@ -820,14 +826,15 @@ process_changes(void *baton,
          The number of changes to process may be >> 1000.
          Therefore, keep the inner loop as tight as possible.
       */
-      for (hi = apr_hash_first(scratch_pool, changed_paths);
+      for (hi = apr_hash_first(scratch_pool, baton->changed_paths);
            hi;
            hi = apr_hash_next(hi))
         {
           /* KEY is the path. */
           const void *path;
           apr_ssize_t klen;
-          apr_hash_this(hi, &path, &klen, NULL);
+          svn_fs_path_change2_t *old_change;
+          apr_hash_this(hi, &path, &klen, (void**)&old_change);
 
           /* If we come across a child of our path, remove it.
              Call svn_fspath__skip_ancestor only if there is a chance that
@@ -839,7 +846,9 @@ process_changes(void *baton,
 
               child = svn_fspath__skip_ancestor(change->path.data, path);
               if (child && child[0] != '\0')
-                apr_hash_set(changed_paths, path, klen, NULL);
+                {
+                  apr_hash_set(baton->changed_paths, path, klen, NULL);
+                }
             }
         }
     }
@@ -856,6 +865,10 @@ svn_fs_fs__txn_changes_fetch(apr_hash_t
   apr_file_t *file;
   apr_hash_t *changed_paths = apr_hash_make(pool);
   apr_pool_t *scratch_pool = svn_pool_create(pool);
+  process_changes_baton_t baton;
+
+  baton.changed_paths = changed_paths;
+  baton.deletions = apr_hash_make(scratch_pool);
 
   SVN_ERR(svn_io_file_open(&file,
                            path_txn_changes(fs, txn_id, scratch_pool),
@@ -865,7 +878,7 @@ svn_fs_fs__txn_changes_fetch(apr_hash_t
   SVN_ERR(svn_fs_fs__read_changes_incrementally(
                                   svn_stream_from_aprfile2(file, TRUE,
                                                            scratch_pool),
-                                  process_changes, changed_paths,
+                                  process_changes, &baton,
                                   scratch_pool));
   svn_pool_destroy(scratch_pool);
 
@@ -998,12 +1011,7 @@ create_txn_dir(const char **id_p,
   txn_id->number = cb.txn_number;
 
   *id_p = svn_fs_fs__id_txn_unparse(txn_id, pool);
-  txn_dir = svn_dirent_join_many(pool,
-                                 fs->path,
-                                 PATH_TXNS_DIR,
-                                 apr_pstrcat(pool, *id_p, PATH_EXT_TXN,
-                                             SVN_VA_NULL),
-                                 SVN_VA_NULL);
+  txn_dir = svn_fs_fs__path_txn_dir(fs, txn_id, pool);
 
   return svn_io_dir_make(txn_dir, APR_OS_DEFAULT, pool);
 }
@@ -1026,8 +1034,8 @@ create_txn_dir_pre_1_5(const char **id_p
   const char *unique_path, *prefix;
 
   /* Try to create directories named "<txndir>/<rev>-<uniqueifier>.txn". */
-  prefix = svn_dirent_join_many(pool, fs->path, PATH_TXNS_DIR,
-                                apr_psprintf(pool, "%ld", rev), SVN_VA_NULL);
+  prefix = svn_dirent_join(svn_fs_fs__path_txns_dir(fs, pool),
+                           apr_psprintf(pool, "%ld", rev), pool);
 
   subpool = svn_pool_create(pool);
   for (i = 1; i <= 99999; i++)
@@ -1590,8 +1598,10 @@ svn_fs_fs__add_change(svn_fs_t *fs,
                         ? svn_tristate_true
                         : svn_tristate_false;
   change->node_kind = node_kind;
+  change->copyfrom_known = TRUE;
   change->copyfrom_rev = copyfrom_rev;
-  change->copyfrom_path = apr_pstrdup(pool, copyfrom_path);
+  if (copyfrom_path)
+    change->copyfrom_path = apr_pstrdup(pool, copyfrom_path);
 
   svn_hash_sets(changes, path, change);
   SVN_ERR(svn_fs_fs__write_changes(svn_stream_from_aprfile2(file, TRUE, pool),
@@ -1602,21 +1612,16 @@ svn_fs_fs__add_change(svn_fs_t *fs,
 
 /* If the transaction TXN_ID in FS uses logical addressing, store the
  * (ITEM_INDEX, OFFSET) pair in the txn's log-to-phys proto index file.
- * If FINAL_REVISION is not SVN_INVALID_REVNUM, use it to determine whether
- * to actually write to the proto-index.  Use POOL for allocations.
+ * Use POOL for allocations.
  */
 static svn_error_t *
 store_l2p_index_entry(svn_fs_t *fs,
                       const svn_fs_fs__id_part_t *txn_id,
-                      svn_revnum_t final_revision,
                       apr_off_t offset,
                       apr_uint64_t item_index,
                       apr_pool_t *pool)
 {
-  if (final_revision == SVN_INVALID_REVNUM)
-    final_revision = txn_id->revision + 1;
-
-  if (svn_fs_fs__use_log_addressing(fs, final_revision))
+  if (svn_fs_fs__use_log_addressing(fs))
     {
       const char *path = svn_fs_fs__path_l2p_proto_index(fs, txn_id, pool);
       apr_file_t *file;
@@ -1631,20 +1636,15 @@ store_l2p_index_entry(svn_fs_t *fs,
 
 /* If the transaction TXN_ID in FS uses logical addressing, store ENTRY
  * in the phys-to-log proto index file of transaction TXN_ID.
- * If FINAL_REVISION is not SVN_INVALID_REVNUM, use it to determine whether
- * to actually write to the proto-index.  Use POOL for allocations.
+ * Use POOL for allocations.
  */
 static svn_error_t *
 store_p2l_index_entry(svn_fs_t *fs,
                       const svn_fs_fs__id_part_t *txn_id,
-                      svn_revnum_t final_revision,
                       svn_fs_fs__p2l_entry_t *entry,
                       apr_pool_t *pool)
 {
-  if (final_revision == SVN_INVALID_REVNUM)
-    final_revision = txn_id->revision + 1;
-
-  if (svn_fs_fs__use_log_addressing(fs, final_revision))
+  if (svn_fs_fs__use_log_addressing(fs))
     {
       const char *path = svn_fs_fs__path_p2l_proto_index(fs, txn_id, pool);
       apr_file_t *file;
@@ -1660,22 +1660,16 @@ store_p2l_index_entry(svn_fs_t *fs,
  * of file system FS and return it in *ITEM_INDEX.  For old formats, it
  * will simply return the offset as item index; in new formats, it will
  * increment the txn's item index counter file and store the mapping in
- * the proto index file.  If FINAL_REVISION is not SVN_INVALID_REVNUM, use
- * it to determine whether to actually write to the proto-index.
- * Use POOL for allocations.
+ * the proto index file.  Use POOL for allocations.
  */
 static svn_error_t *
 allocate_item_index(apr_uint64_t *item_index,
                     svn_fs_t *fs,
                     const svn_fs_fs__id_part_t *txn_id,
-                    svn_revnum_t final_revision,
                     apr_off_t my_offset,
                     apr_pool_t *pool)
 {
-  if (final_revision == SVN_INVALID_REVNUM)
-    final_revision = txn_id->revision + 1;
-
-  if (svn_fs_fs__use_log_addressing(fs, final_revision))
+  if (svn_fs_fs__use_log_addressing(fs))
     {
       apr_file_t *file;
       char buffer[SVN_INT64_BUFFER_SIZE] = { 0 };
@@ -1697,13 +1691,12 @@ allocate_item_index(apr_uint64_t *item_i
         *item_index = SVN_FS_FS__ITEM_INDEX_FIRST_USER;
 
       to_write = svn__ui64toa(buffer, *item_index + 1);
-      SVN_ERR(svn_io_file_seek(file, SEEK_SET, &offset, pool));
+      SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, pool));
       SVN_ERR(svn_io_file_write_full(file, buffer, to_write, NULL, pool));
       SVN_ERR(svn_io_file_close(file, pool));
 
       /* write log-to-phys index */
-      SVN_ERR(store_l2p_index_entry(fs, txn_id, final_revision,
-                                    my_offset, *item_index, pool));
+      SVN_ERR(store_l2p_index_entry(fs, txn_id, my_offset, *item_index, pool));
     }
   else
     {
@@ -2299,8 +2292,7 @@ rep_write_contents_close(void *baton)
       /* Write out our cosmetic end marker. */
       SVN_ERR(svn_stream_puts(b->rep_stream, "ENDREP\n"));
       SVN_ERR(allocate_item_index(&rep->item_index, b->fs, &rep->txn_id,
-                                  SVN_INVALID_REVNUM, b->rep_offset,
-                                  b->scratch_pool));
+                                  b->rep_offset, b->scratch_pool));
 
       b->noderev->data_rep = rep;
     }
@@ -2326,8 +2318,8 @@ rep_write_contents_close(void *baton)
                                       b->scratch_pool));
 
       SVN_ERR(store_sha1_rep_mapping(b->fs, b->noderev, b->scratch_pool));
-      SVN_ERR(store_p2l_index_entry(b->fs, &rep->txn_id, SVN_INVALID_REVNUM,
-                                    &entry, b->scratch_pool));
+      SVN_ERR(store_p2l_index_entry(b->fs, &rep->txn_id, &entry,
+                                    b->scratch_pool));
     }
 
   SVN_ERR(svn_io_file_close(b->file, b->scratch_pool));
@@ -2509,9 +2501,7 @@ write_directory_to_stream(svn_stream_t *
    is not NULL, it will be used in addition to the on-disk cache to find
    earlier reps with the same content.  When such existing reps can be
    found, we will truncate the one just written from the file and return
-   the existing rep.  If FINAL_REVISION is not SVN_INVALID_REVNUM, use it
-   to determine whether to write to the proto-index files.
-   Perform temporary allocations in SCRATCH_POOL. */
+   the existing rep.  Perform temporary allocations in SCRATCH_POOL. */
 static svn_error_t *
 write_container_rep(representation_t *rep,
                     apr_file_t *file,
@@ -2519,8 +2509,7 @@ write_container_rep(representation_t *re
                     collection_writer_t writer,
                     svn_fs_t *fs,
                     apr_hash_t *reps_hash,
-                    int item_type,
-                    svn_revnum_t final_revision,
+                    apr_uint32_t item_type,
                     apr_pool_t *scratch_pool)
 {
   svn_stream_t *stream;
@@ -2572,7 +2561,7 @@ write_container_rep(representation_t *re
       SVN_ERR(svn_stream_puts(whb->stream, "ENDREP\n"));
 
       SVN_ERR(allocate_item_index(&rep->item_index, fs, &rep->txn_id,
-                                  final_revision, offset, scratch_pool));
+                                  offset, scratch_pool));
 
       entry.offset = offset;
       SVN_ERR(svn_fs_fs__get_file_offset(&offset, file, scratch_pool));
@@ -2584,8 +2573,7 @@ write_container_rep(representation_t *re
                                       fnv1a_checksum_ctx,
                                       scratch_pool));
 
-      SVN_ERR(store_p2l_index_entry(fs, &rep->txn_id, final_revision,
-                                    &entry, scratch_pool));
+      SVN_ERR(store_p2l_index_entry(fs, &rep->txn_id, &entry, scratch_pool));
 
       /* update the representation */
       rep->size = whb->size;
@@ -2606,8 +2594,6 @@ write_container_rep(representation_t *re
 
    If ITEM_TYPE is IS_PROPS equals SVN_FS_FS__ITEM_TYPE_*_PROPS, assume
    that we want to a props representation as the base for our delta.
-   If FINAL_REVISION is not SVN_INVALID_REVNUM, use it to determine whether
-   to write to the proto-index files.
    Perform temporary allocations in SCRATCH_POOL.
  */
 static svn_error_t *
@@ -2618,8 +2604,7 @@ write_container_delta_rep(representation
                           svn_fs_t *fs,
                           node_revision_t *noderev,
                           apr_hash_t *reps_hash,
-                          int item_type,
-                          svn_revnum_t final_revision,
+                          apr_uint32_t item_type,
                           apr_pool_t *scratch_pool)
 {
   svn_txdelta_window_handler_t diff_wh;
@@ -2716,7 +2701,7 @@ write_container_delta_rep(representation
       SVN_ERR(svn_stream_puts(file_stream, "ENDREP\n"));
 
       SVN_ERR(allocate_item_index(&rep->item_index, fs, &rep->txn_id,
-                                  final_revision, offset, scratch_pool));
+                                  offset, scratch_pool));
 
       entry.offset = offset;
       SVN_ERR(svn_fs_fs__get_file_offset(&offset, file, scratch_pool));
@@ -2728,8 +2713,7 @@ write_container_delta_rep(representation
                                       fnv1a_checksum_ctx,
                                       scratch_pool));
 
-      SVN_ERR(store_p2l_index_entry(fs, &rep->txn_id, final_revision,
-                                    &entry, scratch_pool));
+      SVN_ERR(store_p2l_index_entry(fs, &rep->txn_id, &entry, scratch_pool));
 
       /* update the representation */
       rep->expanded_size = whb->size;
@@ -2916,12 +2900,11 @@ write_final_rev(const svn_fs_id_t **new_
                                               write_directory_to_stream,
                                               fs, noderev, NULL,
                                               SVN_FS_FS__ITEM_TYPE_DIR_REP,
-                                              rev, pool));
+                                              pool));
           else
             SVN_ERR(write_container_rep(noderev->data_rep, file, entries,
                                         write_directory_to_stream, fs, NULL,
-                                        SVN_FS_FS__ITEM_TYPE_DIR_REP, rev,
-                                        pool));
+                                        SVN_FS_FS__ITEM_TYPE_DIR_REP, pool));
 
           reset_txn_in_rep(noderev->data_rep);
         }
@@ -2937,7 +2920,7 @@ write_final_rev(const svn_fs_id_t **new_
           reset_txn_in_rep(noderev->data_rep);
           noderev->data_rep->revision = rev;
 
-          if (!svn_fs_fs__use_log_addressing(fs, rev))
+          if (!svn_fs_fs__use_log_addressing(fs))
             {
               /* See issue 3845.  Some unknown mechanism caused the
                  protorev file to get truncated, so check for that
@@ -2956,9 +2939,9 @@ write_final_rev(const svn_fs_id_t **new_
   if (noderev->prop_rep && is_txn_rep(noderev->prop_rep))
     {
       apr_hash_t *proplist;
-      int item_type = noderev->kind == svn_node_dir
-                    ? SVN_FS_FS__ITEM_TYPE_DIR_PROPS
-                    : SVN_FS_FS__ITEM_TYPE_FILE_PROPS;
+      apr_uint32_t item_type = noderev->kind == svn_node_dir
+                             ? SVN_FS_FS__ITEM_TYPE_DIR_PROPS
+                             : SVN_FS_FS__ITEM_TYPE_FILE_PROPS;
       SVN_ERR(svn_fs_fs__get_proplist(&proplist, fs, noderev, pool));
 
       noderev->prop_rep->revision = rev;
@@ -2966,11 +2949,11 @@ write_final_rev(const svn_fs_id_t **new_
       if (ffd->deltify_properties)
         SVN_ERR(write_container_delta_rep(noderev->prop_rep, file, proplist,
                                           write_hash_to_stream, fs, noderev,
-                                          reps_hash, item_type, rev, pool));
+                                          reps_hash, item_type, pool));
       else
         SVN_ERR(write_container_rep(noderev->prop_rep, file, proplist,
                                     write_hash_to_stream, fs, reps_hash,
-                                    item_type, rev, pool));
+                                    item_type, pool));
 
       reset_txn_in_rep(noderev->prop_rep);
     }
@@ -2986,15 +2969,15 @@ write_final_rev(const svn_fs_id_t **new_
 
   /* root nodes have a fixed ID in log addressing mode */
   SVN_ERR(svn_fs_fs__get_file_offset(&my_offset, file, pool));
-  if (svn_fs_fs__use_log_addressing(fs, rev) && at_root)
+  if (svn_fs_fs__use_log_addressing(fs) && at_root)
     {
       /* reference the root noderev from the log-to-phys index */
       rev_item.number = SVN_FS_FS__ITEM_INDEX_ROOT_NODE;
-      SVN_ERR(store_l2p_index_entry(fs, txn_id, rev, my_offset,
+      SVN_ERR(store_l2p_index_entry(fs, txn_id, my_offset,
                                     rev_item.number, pool));
     }
   else
-    SVN_ERR(allocate_item_index(&rev_item.number, fs, txn_id, rev,
+    SVN_ERR(allocate_item_index(&rev_item.number, fs, txn_id,
                                 my_offset, pool));
 
   rev_item.revision = rev;
@@ -3052,7 +3035,7 @@ write_final_rev(const svn_fs_id_t **new_
                                    pool));
 
   /* reference the root noderev from the log-to-phys index */
-  if (svn_fs_fs__use_log_addressing(fs, rev))
+  if (svn_fs_fs__use_log_addressing(fs))
     {
       svn_fs_fs__p2l_entry_t entry;
       rev_item.revision = SVN_INVALID_REVNUM;
@@ -3066,7 +3049,7 @@ write_final_rev(const svn_fs_id_t **new_
                                       fnv1a_checksum_ctx,
                                       pool));
 
-      SVN_ERR(store_p2l_index_entry(fs, txn_id, rev, &entry, pool));
+      SVN_ERR(store_p2l_index_entry(fs, txn_id, &entry, pool));
     }
 
   /* Return our ID that references the revision file. */
@@ -3076,17 +3059,15 @@ write_final_rev(const svn_fs_id_t **new_
 }
 
 /* Write the changed path info CHANGED_PATHS from transaction TXN_ID to the
-   permanent rev-file FILE representing NEW_REV in filesystem FS.  *OFFSET_P
-   is set the to offset in the file of the beginning of this information.
-   NEW_REV is the revision currently being committed.
-   Perform temporary allocations in POOL. */
+   permanent rev-file FILE in filesystem FS.  *OFFSET_P is set the to offset
+   in the file of the beginning of this information.  Perform temporary
+   allocations in POOL. */
 static svn_error_t *
 write_final_changed_path_info(apr_off_t *offset_p,
                               apr_file_t *file,
                               svn_fs_t *fs,
                               const svn_fs_fs__id_part_t *txn_id,
                               apr_hash_t *changed_paths,
-                              svn_revnum_t new_rev,
                               apr_pool_t *pool)
 {
   apr_off_t offset;
@@ -3104,7 +3085,7 @@ write_final_changed_path_info(apr_off_t
   *offset_p = offset;
 
   /* reference changes from the indexes */
-  if (svn_fs_fs__use_log_addressing(fs, new_rev))
+  if (svn_fs_fs__use_log_addressing(fs))
     {
       svn_fs_fs__p2l_entry_t entry;
 
@@ -3118,8 +3099,8 @@ write_final_changed_path_info(apr_off_t
                                       fnv1a_checksum_ctx,
                                       pool));
 
-      SVN_ERR(store_p2l_index_entry(fs, txn_id, new_rev, &entry, pool));
-      SVN_ERR(store_l2p_index_entry(fs, txn_id, new_rev, entry.offset,
+      SVN_ERR(store_p2l_index_entry(fs, txn_id, &entry, pool));
+      SVN_ERR(store_l2p_index_entry(fs, txn_id, entry.offset,
                                     SVN_FS_FS__ITEM_INDEX_CHANGES, pool));
     }
 
@@ -3210,44 +3191,41 @@ verify_locks(svn_fs_t *fs,
              apr_hash_t *changed_paths,
              apr_pool_t *pool)
 {
-  apr_pool_t *subpool = svn_pool_create(pool);
-  apr_hash_index_t *hi;
+  apr_pool_t *iterpool;
   apr_array_header_t *changed_paths_sorted;
   svn_stringbuf_t *last_recursed = NULL;
   int i;
 
   /* Make an array of the changed paths, and sort them depth-first-ily.  */
-  changed_paths_sorted = apr_array_make(pool,
-                                        apr_hash_count(changed_paths) + 1,
-                                        sizeof(const char *));
-  for (hi = apr_hash_first(pool, changed_paths); hi; hi = apr_hash_next(hi))
-    {
-      APR_ARRAY_PUSH(changed_paths_sorted, const char *) =
-        apr_hash_this_key(hi);
-    }
-  svn_sort__array(changed_paths_sorted, svn_sort_compare_paths);
+  changed_paths_sorted = svn_sort__hash(changed_paths,
+                                        svn_sort_compare_items_as_paths,
+                                        pool);
 
   /* Now, traverse the array of changed paths, verify locks.  Note
      that if we need to do a recursive verification a path, we'll skip
      over children of that path when we get to them. */
+  iterpool = svn_pool_create(pool);
   for (i = 0; i < changed_paths_sorted->nelts; i++)
     {
+      const svn_sort__item_t *item;
       const char *path;
       svn_fs_path_change2_t *change;
       svn_boolean_t recurse = TRUE;
 
-      svn_pool_clear(subpool);
-      path = APR_ARRAY_IDX(changed_paths_sorted, i, const char *);
+      svn_pool_clear(iterpool);
+
+      item = &APR_ARRAY_IDX(changed_paths_sorted, i, svn_sort__item_t);
+
+      /* Fetch the change associated with our path.  */
+      path = item->key;
+      change = item->value;
 
       /* If this path has already been verified as part of a recursive
          check of one of its parents, no need to do it again.  */
       if (last_recursed
-          && svn_dirent_is_child(last_recursed->data, path, subpool))
+          && svn_fspath__skip_ancestor(last_recursed->data, path))
         continue;
 
-      /* Fetch the change associated with our path.  */
-      change = svn_hash_gets(changed_paths, path);
-
       /* What does it mean to succeed at lock verification for a given
          path?  For an existing file or directory getting modified
          (text, props), it means we hold the lock on the file or
@@ -3260,7 +3238,7 @@ verify_locks(svn_fs_t *fs,
       if (change->change_kind == svn_fs_path_change_modify)
         recurse = FALSE;
       SVN_ERR(svn_fs_fs__allow_locked_operation(path, fs, recurse, TRUE,
-                                                subpool));
+                                                iterpool));
 
       /* If we just did a recursive check, remember the path we
          checked (so children can be skipped).  */
@@ -3272,243 +3250,7 @@ verify_locks(svn_fs_t *fs,
             svn_stringbuf_set(last_recursed, path);
         }
     }
-  svn_pool_destroy(subpool);
-  return SVN_NO_ERROR;
-}
-
-/* Return TRUE, if transaction TXN_ID in FS definitively uses logical
- * addressing mode.  Use POOL for temporary allocations.
- */
-static svn_boolean_t
-using_log_addressing(svn_fs_t *fs,
-                     const svn_fs_fs__id_part_t *txn_id,
-                     apr_pool_t *pool)
-{
-  /* As long as we don't write new data representations, we won't allocate
-     IDs and there is no difference between log & phys mode.
-
-     After the first ID got allocated, it is logical mode and the proto-
-     index file does exist.
-   */
-  svn_node_kind_t kind;
-  const char *path = svn_fs_fs__path_l2p_proto_index(fs, txn_id, pool);
-
-  svn_error_t *err = svn_io_check_path(path, &kind, pool);
-  if (err)
-    {
-      /* We couldn't check for the presence of the index file.
-
-         So, we probably won't be able to access it during later stages
-         of the commit.
-       */
-      svn_error_clear(err);
-      return FALSE;
-    }
-
-  return kind == svn_node_file;
-}
-
-/* Return TRUE, if the file with FILENAME contains a node revision.
- */
-static svn_boolean_t
-is_noderev_file(const char *filename)
-{
-  apr_size_t dot_count = 0;
-
-  /* all interesting files start with "node." */
-  if (strncmp(filename, PATH_PREFIX_NODE, strlen(PATH_PREFIX_NODE)))
-    return FALSE;
-
-  for (; *filename; ++filename)
-    if (*filename == '.')
-      ++dot_count;
-
-  return dot_count == 2;
-}
-
-/* Determine the checksum for the SIZE bytes of data starting at START
- * in FILE and return the result in *FNV1_CHECKSUM.
- * Use POOL for tempoary allocations.
- */
-static svn_error_t *
-fnv1a_checksum_on_file_range(apr_uint32_t *fnv1_checksum,
-                             apr_file_t *file,
-                             apr_off_t start,
-                             apr_off_t size,
-                             apr_pool_t *pool)
-{
-  char *buffer = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
-
-  svn_checksum_ctx_t *checksum_ctx
-    = svn_checksum_ctx_create(svn_checksum_fnv1a_32x4, pool);
-
-  SVN_ERR(svn_io_file_seek(file, APR_SET, &start, pool));
-  while (size > 0)
-    {
-      apr_size_t to_read = MIN(size, sizeof(buffer));
-
-      SVN_ERR(svn_io_file_read_full2(file, buffer, to_read, &to_read,
-                                     NULL, pool));
-      SVN_ERR(svn_checksum_update(checksum_ctx, buffer, to_read));
-      size -= to_read;
-    }
-  SVN_ERR(fnv1a_checksum_finalize(fnv1_checksum, checksum_ctx, pool));
-
-  return SVN_NO_ERROR;
-}
-
-/* qsort()-compatible comparison function sorting svn_fs_fs__p2l_entry_t
- * by offset.
- */
-static int
-compare_sort_p2l_entry(const void *a,
-                       const void *b)
-{
-  apr_off_t lhs = ((const svn_fs_fs__p2l_entry_t *)a)->offset;
-  apr_off_t rhs = ((const svn_fs_fs__p2l_entry_t *)b)->offset;
-
-  return lhs < rhs ? -1 : rhs == lhs ? 0 : 1;
-}
-
-
-/* Upgrade the transaction TXN_ID in FS from physical addressing mode
- * to logical addressing mode.  FINAL_REVISION is the revision that this
- * txn is being committed to.  Use POOL for temporary allocations.
- */
-static svn_error_t *
-upgrade_transaction(svn_fs_t *fs,
-                    const svn_fs_fs__id_part_t *txn_id,
-                    svn_revnum_t final_revision,
-                    apr_file_t *proto_file,
-                    apr_pool_t *pool)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-  apr_hash_t *dirents;
-  int i;
-  apr_hash_index_t* hi;
-
-  /* we allocate large temporary data and want to release it asap */
-
-  apr_pool_t *subpool = svn_pool_create(pool);
-  apr_pool_t *iterpool = svn_pool_create(pool);
-
-  apr_hash_t *id_map = apr_hash_make(subpool);
-  const char *txn_dir = svn_fs_fs__path_txn_dir(fs, txn_id, subpool);
-  apr_array_header_t *p2l_entries
-    = apr_array_make(subpool, 16, sizeof(svn_fs_fs__p2l_entry_t));
-
-  /* scan the txn directory for noderev files and patch them up */
-
-  SVN_ERR(svn_io_get_dirents3(&dirents, txn_dir, TRUE, subpool, iterpool));
-  for (hi = apr_hash_first(subpool, dirents); hi; hi = apr_hash_next(hi))
-    {
-      apr_file_t *file;
-      const char *filename;
-      node_revision_t *noderev;
-      svn_stream_t *stream;
-      const char *name;
-      apr_uint64_t *old_index, *item_index, *new_index;
-
-      svn_pool_clear(iterpool);
-
-      /* We are only interested in file data reps of this txns.
-         Older IDs remain valid because they are already committed.
-         Other IDs (noderevs and their usage in directories) will only be
-         assigned later anyways. */
-
-      name = apr_hash_this_key(hi);
-      if (!is_noderev_file(name))
-        continue;
-
-      filename = svn_dirent_join(txn_dir, name, iterpool);
-      SVN_ERR(svn_io_file_open(&file, filename,
-                               APR_READ | APR_WRITE | APR_BUFFERED,
-                               APR_OS_DEFAULT,
-                               iterpool));
-      stream = svn_stream_from_aprfile2(file, TRUE, iterpool);
-      SVN_ERR(svn_fs_fs__read_noderev(&noderev, stream, iterpool, iterpool));
-      if (   noderev->data_rep == NULL
-          || noderev->data_rep->revision != SVN_INVALID_REVNUM
-          || noderev->kind != svn_node_file)
-        continue;
-
-      /* We need to assign an id.
-         We might already have one because of rep sharing. */
-
-      item_index = &noderev->data_rep->item_index;
-      new_index = apr_hash_get(id_map, item_index, sizeof(*item_index));
-
-      if (new_index)
-        {
-          *item_index = *new_index;
-        }
-      else
-        {
-          svn_fs_fs__rep_header_t *header;
-          svn_fs_fs__p2l_entry_t *entry;
-
-          /* assign a logical ID and write the L2P proto-index */
-
-          old_index = apr_pmemdup(subpool, item_index, sizeof(*item_index));
-          SVN_ERR(allocate_item_index(item_index, fs, txn_id, final_revision,
-                                      *old_index, iterpool));
-
-          new_index = apr_pmemdup(subpool, item_index, sizeof(*item_index));
-          apr_hash_set(id_map, old_index, sizeof(*old_index), new_index);
-
-          /* we need to know the length of the representation header
-             because it is not accounted for by the representation length */
-
-          entry = apr_array_push(p2l_entries);
-          entry->offset = *old_index;
-          SVN_ERR(svn_io_file_seek(proto_file, APR_SET, &entry->offset,
-                                   iterpool));
-          SVN_ERR(svn_fs_fs__read_rep_header(&header,
-                    svn_stream_from_aprfile2(proto_file, TRUE, iterpool),
-                    iterpool, iterpool));
-
-          /* Create the corresponding entry for the P2L proto-index.
-
-             We need to write that proto-index in strict offset order but
-             we have no control over the order in which we traverse the
-             data reps.   Thus, we collect the entries in an array. */
-
-          entry->size = noderev->data_rep->size + header->header_size + 7;
-                        /* 7 for the "ENDREP\n" */
-          entry->type = SVN_FS_FS__ITEM_TYPE_FILE_REP;
-          entry->item.revision = SVN_INVALID_REVNUM;
-          entry->item.number = *new_index;
-          SVN_ERR(fnv1a_checksum_on_file_range(&entry->fnv1_checksum,
-                                               proto_file,
-                                               entry->offset, entry->size,
-                                               iterpool));
-        }
-
-      /* write the updated noderev to disk */
-
-      SVN_ERR(svn_io_file_trunc(file, 0, iterpool));
-      SVN_ERR(svn_fs_fs__write_noderev(stream, noderev, ffd->format,
-                                       TRUE, iterpool));
-    }
-
-  /* Finally, write all P2L proto-index entries ordered by item offset. */
-
-  qsort(p2l_entries->elts, p2l_entries->nelts, p2l_entries->elt_size,
-        compare_sort_p2l_entry);
-  for (i = 0; i < p2l_entries->nelts; ++i)
-    {
-      svn_fs_fs__p2l_entry_t *entry;
-
-      svn_pool_clear(iterpool);
-
-      entry = &APR_ARRAY_IDX(p2l_entries, i, svn_fs_fs__p2l_entry_t);
-      SVN_ERR(store_p2l_index_entry(fs, txn_id, final_revision,
-                                    entry, iterpool));
-    }
-
-  svn_pool_clear(iterpool);
-  svn_pool_clear(subpool);
-
+  svn_pool_destroy(iterpool);
   return SVN_NO_ERROR;
 }
 
@@ -3584,20 +3326,25 @@ svn_fs_fs__add_index_data(svn_fs_t *fs,
   apr_off_t p2l_offset;
   svn_stringbuf_t *footer;
   unsigned char footer_length;
+  svn_checksum_t *l2p_checksum;
+  svn_checksum_t *p2l_checksum;
 
   /* Append the actual index data to the pack file. */
   l2p_offset = 0;
   SVN_ERR(svn_io_file_seek(file, APR_END, &l2p_offset, pool));
-  SVN_ERR(svn_fs_fs__l2p_index_append(fs, file, l2p_proto_index, revision,
-                                      pool));
+  SVN_ERR(svn_fs_fs__l2p_index_append(&l2p_checksum, fs, file,
+                                      l2p_proto_index, revision,
+                                      pool, pool));
 
   p2l_offset = 0;
   SVN_ERR(svn_io_file_seek(file, APR_END, &p2l_offset, pool));
-  SVN_ERR(svn_fs_fs__p2l_index_append(fs, file, p2l_proto_index, revision,
-                                      pool));
+  SVN_ERR(svn_fs_fs__p2l_index_append(&p2l_checksum, fs, file,
+                                      p2l_proto_index, revision,
+                                      pool, pool));
 
   /* Append footer. */
-  footer = svn_fs_fs__unparse_footer(l2p_offset, p2l_offset, pool);
+  footer = svn_fs_fs__unparse_footer(l2p_offset, l2p_checksum, 
+                                     p2l_offset, p2l_checksum, pool, pool);
   SVN_ERR(svn_io_file_write_full(file, footer->data, footer->len, NULL,
                                  pool));
 
@@ -3638,6 +3385,22 @@ commit_body(void *baton, apr_pool_t *poo
   const svn_fs_fs__id_part_t *txn_id = svn_fs_fs__txn_get_id(cb->txn);
   apr_hash_t *changed_paths;
 
+  /* Re-Read the current repository format.  All our repo upgrade and
+     config evaluation strategies are such that existing information in
+     FS and FFD remains valid.
+
+     Although we don't recommend upgrading hot repositories, people may
+     still do it and we must make sure to either handle them gracefully
+     or to error out.
+
+     Committing pre-format 3 txns will fail after upgrade to format 3+
+     because the proto-rev cannot be found; no further action needed.
+     Upgrades from pre-f7 to f7+ means a potential change in addressing
+     mode for the final rev.  We must be sure to detect that cause because
+     the failure would only manifest once the new revision got committed.
+   */
+  SVN_ERR(svn_fs_fs__read_format_file(cb->fs, pool));
+
   /* Read the current youngest revision and, possibly, the next available
      node id and copy id (for old format filesystems).  Update the cached
      value for the youngest revision, because we have just checked it. */
@@ -3670,18 +3433,6 @@ commit_body(void *baton, apr_pool_t *poo
                                  cb->fs, txn_id, pool));
   SVN_ERR(svn_fs_fs__get_file_offset(&initial_offset, proto_file, pool));
 
-  /* Make sure that we don't try to commit an old txn that used physical
-     addressing but will be committed into the revision range that requires
-     logical addressing.
-     */
-  if (svn_fs_fs__use_log_addressing(cb->fs, new_rev)
-      && !svn_fs_fs__use_log_addressing(cb->fs, txn_id->revision)
-      && !using_log_addressing(cb->fs, txn_id, pool))
-    {
-      SVN_ERR(upgrade_transaction(cb->fs, txn_id, new_rev, proto_file, pool));
-      SVN_ERR(svn_io_file_seek(proto_file, APR_SET, &initial_offset, pool));
-    }
-
   /* Write out all the node-revisions and directory contents. */
   root_id = svn_fs_fs__id_txn_create_root(txn_id, pool);
   SVN_ERR(write_final_rev(&new_root_id, proto_file, new_rev, cb->fs, root_id,
@@ -3692,9 +3443,17 @@ commit_body(void *baton, apr_pool_t *poo
   /* Write the changed-path information. */
   SVN_ERR(write_final_changed_path_info(&changed_path_offset, proto_file,
                                         cb->fs, txn_id, changed_paths,
-                                        new_rev, pool));
+                                        pool));
 
-  if (!svn_fs_fs__use_log_addressing(cb->fs, new_rev))
+  if (svn_fs_fs__use_log_addressing(cb->fs))
+    {
+      /* Append the index data to the rev file. */
+      SVN_ERR(svn_fs_fs__add_index_data(cb->fs, proto_file,
+                      svn_fs_fs__path_l2p_proto_index(cb->fs, txn_id, pool),
+                      svn_fs_fs__path_p2l_proto_index(cb->fs, txn_id, pool),
+                      new_rev, pool));
+    }
+  else
     {
       /* Write the final line. */
 
@@ -3706,15 +3465,6 @@ commit_body(void *baton, apr_pool_t *poo
       SVN_ERR(svn_io_file_write_full(proto_file, trailer->data, trailer->len,
                                      NULL, pool));
     }
-  else
-    {
-      /* Append the index data to the rev file. */
-      SVN_ERR(svn_fs_fs__add_index_data(cb->fs, proto_file,
-                      svn_fs_fs__path_l2p_proto_index(cb->fs, txn_id, pool),
-                      svn_fs_fs__path_p2l_proto_index(cb->fs, txn_id, pool),
-                      new_rev, pool));
-    }
-
 
   SVN_ERR(svn_io_file_flush_to_disk(proto_file, pool));
   SVN_ERR(svn_io_file_close(proto_file, pool));
@@ -3889,7 +3639,7 @@ svn_fs_fs__list_transactions(apr_array_h
   names = apr_array_make(pool, 1, sizeof(const char *));
 
   /* Get the transactions directory. */
-  txn_dir = svn_dirent_join(fs->path, PATH_TXNS_DIR, pool);
+  txn_dir = svn_fs_fs__path_txns_dir(fs, pool);
 
   /* Now find a listing of this directory. */
   SVN_ERR(svn_io_get_dirents3(&dirents, txn_dir, TRUE, pool, pool));

Modified: subversion/branches/authzperf/subversion/libsvn_fs_fs/tree.c
URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_fs/tree.c?rev=1649205&r1=1649204&r2=1649205&view=diff
==============================================================================
--- subversion/branches/authzperf/subversion/libsvn_fs_fs/tree.c (original)
+++ subversion/branches/authzperf/subversion/libsvn_fs_fs/tree.c Sat Jan  3 14:00:41 2015
@@ -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,34 +158,10 @@ 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_fs_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_fs_dag_cache_t
 {
@@ -203,109 +178,29 @@ struct fs_fs_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_fs_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_fs_dag_cache_t*
 svn_fs_fs__create_dag_cache(apr_pool_t *pool)
 {
   fs_fs_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_fs_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_fs_dag_cache_t* cache)
 {
-  if (cache->first_lock == NULL && cache->insertions > BUCKET_COUNT)
+  if (cache->insertions > BUCKET_COUNT)
     {
       svn_pool_clear(cache->pool);
 
@@ -338,6 +233,10 @@ cache_lookup( fs_fs_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;
     }
 
@@ -409,10 +308,38 @@ cache_lookup( fs_fs_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(fs_fs_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
@@ -449,15 +376,11 @@ locate_cache(svn_cache__t **cache,
 /* 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;
@@ -493,11 +416,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
     {
@@ -848,7 +766,7 @@ get_copy_inheritance(copy_id_inherit_t *
   SVN_ERR(svn_fs_fs__dag_get_copyroot(&copyroot_rev, &copyroot_path,
                                       child->node));
   SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, fs, copyroot_rev, pool));
-  SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path, FALSE, pool));
+  SVN_ERR(get_dag(&copyroot_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) == svn_fs_node_unrelated)
@@ -880,7 +798,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_fs__dag_copy_into_pool(node, pool);
   parent_path->entry = entry;
   parent_path->parent = parent;
   parent_path->copy_inherit = copy_id_inherit_unknown;
@@ -912,6 +831,56 @@ 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)
+{
+  fs_fs_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_fs__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_fs__dag_get_created_path(node);
+      svn_revnum_t revision;
+      SVN_ERR(svn_fs_fs__dag_get_revision(&revision, node, scratch_pool));
+
+      /* 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
@@ -959,20 +928,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);
@@ -1016,15 +1020,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;
@@ -1037,7 +1038,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
@@ -1073,7 +1074,7 @@ open_path(parent_path_t **parent_path_p,
           if (flags & open_path_node_only)
             {
               /* Shortcut: the caller only wants the final DAG node. */
-              parent_path->node = child;
+              parent_path->node = svn_fs_fs__dag_copy_into_pool(child, pool);
             }
           else
             {
@@ -1104,7 +1105,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);
@@ -1176,8 +1180,7 @@ make_path_mutable(svn_fs_root_t *root,
                                           parent_path->node));
       SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, root->fs,
                                        copyroot_rev, pool));
-      SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path,
-                      FALSE, pool));
+      SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path, pool));
 
       child_id = svn_fs_fs__dag_get_id(parent_path->node);
       copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
@@ -1215,15 +1218,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;
@@ -1232,7 +1231,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)
     {
@@ -1242,8 +1241,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)
         {
@@ -1258,7 +1256,7 @@ get_dag(dag_node_t **dag_node_p,
         }
     }
 
-  *dag_node_p = node;
+  *dag_node_p = svn_fs_fs__dag_copy_into_pool(node, pool);
   return SVN_NO_ERROR;
 }
 
@@ -1322,7 +1320,7 @@ svn_fs_fs__node_id(const svn_fs_id_t **i
     {
       dag_node_t *node;
 
-      SVN_ERR(get_dag(&node, root, path, FALSE, pool));
+      SVN_ERR(get_dag(&node, root, path, pool));
       *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(node), pool);
     }
   return SVN_NO_ERROR;
@@ -1371,12 +1369,12 @@ fs_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));
+  SVN_ERR(get_dag(&node, root_a, path_a, pool));
   id = svn_fs_fs__dag_get_id(node);
   rev_item_a = *svn_fs_fs__id_rev_item(id);
   node_id_a = *svn_fs_fs__id_node_id(id);
 
-  SVN_ERR(get_dag(&node, root_b, path_b, FALSE, pool));
+  SVN_ERR(get_dag(&node, root_b, path_b,  pool));
   id = svn_fs_fs__dag_get_id(node);
   rev_item_b = *svn_fs_fs__id_rev_item(id);
   node_id_b = *svn_fs_fs__id_node_id(id);
@@ -1399,7 +1397,7 @@ svn_fs_fs__node_created_rev(svn_revnum_t
 {
   dag_node_t *node;
 
-  SVN_ERR(get_dag(&node, root, path, FALSE, pool));
+  SVN_ERR(get_dag(&node, root, path, pool));
   return svn_fs_fs__dag_get_revision(revision, node, pool);
 }
 
@@ -1414,7 +1412,7 @@ fs_node_created_path(const char **create
 {
   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_fs__dag_get_created_path(node);
 
   return SVN_NO_ERROR;
@@ -1478,7 +1476,7 @@ fs_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_fs__dag_get_proplist(&proplist, node, pool));
   *value_p = NULL;
   if (proplist)
@@ -1501,7 +1499,7 @@ fs_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_fs__dag_get_proplist(&table, node, pool));
   *table_p = table ? table : apr_hash_make(pool);
 
@@ -1537,7 +1535,7 @@ fs_change_node_prop(svn_fs_root_t *root,
   parent_path_t *parent_path;
   apr_hash_t *proplist;
   const svn_fs_fs__id_part_t *txn_id;
-  svn_boolean_t modeinfo_mod = FALSE;
+  svn_boolean_t mergeinfo_mod = FALSE;
 
   if (! root->is_txn_root)
     return SVN_FS__NOT_TXN(root);
@@ -1582,7 +1580,7 @@ fs_change_node_prop(svn_fs_root_t *root,
                                                    (value != NULL), pool));
         }
 
-      modeinfo_mod = TRUE;
+      mergeinfo_mod = TRUE;
     }
 
   /* Set the property. */
@@ -1595,7 +1593,7 @@ fs_change_node_prop(svn_fs_root_t *root,
   /* Make a record of this modification in the changes table. */
   return add_change(root->fs, txn_id, path,
                     svn_fs_fs__dag_get_id(parent_path->node),
-                    svn_fs_path_change_modify, FALSE, TRUE, modeinfo_mod,
+                    svn_fs_path_change_modify, FALSE, TRUE, mergeinfo_mod,
                     svn_fs_fs__dag_node_kind(parent_path->node),
                     SVN_INVALID_REVNUM, NULL, pool);
 }
@@ -1622,8 +1620,8 @@ fs_props_changed(svn_boolean_t *changed_
       (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));
+  SVN_ERR(get_dag(&node1, root1, path1, pool));
+  SVN_ERR(get_dag(&node2, root2, path2, pool));
   return svn_fs_fs__dag_things_different(changed_p, NULL,
                                          node1, node2, strict, pool);
 }
@@ -1636,7 +1634,7 @@ fs_props_changed(svn_boolean_t *changed_
 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);
 }
 
 
@@ -2367,7 +2365,7 @@ fs_dir_entries(apr_hash_t **table_p,
   int i;
 
   /* 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_fs__dag_dir_entries(&table, node, pool));
 
   /* Convert directory array to hash. */
@@ -2387,10 +2385,7 @@ fs_dir_optimal_order(apr_array_header_t
                      apr_hash_t *entries,
                      apr_pool_t *pool)
 {
-  *ordered_p
-    = svn_fs_fs__order_dir_entries(root->fs, entries,
-                                   root->rev,
-                                   pool);
+  *ordered_p = svn_fs_fs__order_dir_entries(root->fs, entries, pool);
 
   return SVN_NO_ERROR;
 }
@@ -2570,7 +2565,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
@@ -2647,7 +2642,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_fs__dag_get_id(new_node), kind, FALSE,
                          FALSE, FALSE, svn_fs_fs__dag_node_kind(from_node),
@@ -2728,7 +2723,7 @@ fs_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_fs__dag_get_copyfrom_rev(rev_p, node));
   SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(path_p, node));
 
@@ -2799,7 +2794,7 @@ fs_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_fs__dag_file_length(length_p, file, pool);
@@ -2818,7 +2813,7 @@ fs_file_checksum(svn_checksum_t **checks
 {
   dag_node_t *file;
 
-  SVN_ERR(get_dag(&file, root, path, FALSE, pool));
+  SVN_ERR(get_dag(&file, root, path, pool));
   return svn_fs_fs__dag_file_checksum(checksum, file, kind, pool);
 }
 
@@ -2837,7 +2832,7 @@ fs_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_fs__dag_get_contents(&file_stream, node, pool));
@@ -2860,7 +2855,7 @@ fs_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_fs__dag_try_process_file_contents(success, node,
                                                   processor, baton, pool);
@@ -3184,8 +3179,8 @@ fs_contents_changed(svn_boolean_t *chang
         (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));
+  SVN_ERR(get_dag(&node1, root1, path1, pool));
+  SVN_ERR(get_dag(&node2, root2, path2, pool));
   return svn_fs_fs__dag_things_different(NULL, changed_p,
                                          node1, node2, strict, pool);
 }
@@ -3205,10 +3200,10 @@ fs_get_file_delta_stream(svn_txdelta_str
   dag_node_t *source_node, *target_node;
 
   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_fs__dag_get_file_delta_stream(stream_p, source_node,
@@ -3672,7 +3667,7 @@ history_prev(svn_fs_history_t **prev_his
 
       SVN_ERR(svn_fs_fs__revision_root(&copyroot_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_fs__dag_get_created_path(node);
 
       /* If our current path was the very destination of the copy,
@@ -3756,12 +3751,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;
@@ -3769,6 +3766,8 @@ fs_history_prev(svn_fs_history_t **prev_
           if (fhd->is_interesting)
             break;
         }
+
+      svn_pool_destroy(iterpool);
     }
 
   *prev_history_p = prev_history;
@@ -3862,7 +3861,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_fs__dag_has_mergeinfo(&has_mergeinfo, kid_dag));
       SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down, kid_dag));
@@ -4108,7 +4107,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_fs__dag_has_descendants_with_mergeinfo(&go_down,
                                                         this_dag));
   if (go_down)
@@ -4338,10 +4337,12 @@ 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;
@@ -4351,6 +4352,18 @@ verify_node(dag_node_t *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_fs__id_eq(svn_fs_fs__dag_get_id(parent),
+                           svn_fs_fs__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_fs__dag_has_mergeinfo(&has_mergeinfo, node));
@@ -4402,8 +4415,8 @@ 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_fs__dag_dir_entries(&entries, node, pool));
 
@@ -4422,7 +4435,7 @@ verify_node(dag_node_t *node,
             {
               SVN_ERR(svn_fs_fs__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_fs__dag_get_mergeinfo_count(&child_mergeinfo,
                                                          child));
             }
@@ -4447,6 +4460,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);
@@ -4459,6 +4476,7 @@ svn_fs_fs__verify_root(svn_fs_root_t *ro
 {
   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
@@ -4482,7 +4500,8 @@ svn_fs_fs__verify_root(svn_fs_root_t *ro
     }
 
   /* 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. */
   {

Modified: subversion/branches/authzperf/subversion/libsvn_fs_fs/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_fs/util.c?rev=1649205&r1=1649204&r2=1649205&view=diff
==============================================================================
--- subversion/branches/authzperf/subversion/libsvn_fs_fs/util.c (original)
+++ subversion/branches/authzperf/subversion/libsvn_fs_fs/util.c Sat Jan  3 14:00:41 2015
@@ -55,6 +55,16 @@ svn_fs_fs__is_packed_revprop(svn_fs_t *f
       && (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT);
 }
 
+svn_revnum_t
+svn_fs_fs__packed_base_rev(svn_fs_t *fs,
+                           svn_revnum_t revision)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  return (revision < ffd->min_unpacked_rev)
+       ? (revision - (revision % ffd->max_files_per_dir))
+       : revision;
+}
+
 const char *
 svn_fs_fs__path_txn_current(svn_fs_t *fs,
                             apr_pool_t *pool)
@@ -224,15 +234,21 @@ combine_txn_id_string(const svn_fs_fs__i
 }
 
 const char *
+svn_fs_fs__path_txns_dir(svn_fs_t *fs,
+                         apr_pool_t *pool)
+{
+  return svn_dirent_join(fs->path, PATH_TXNS_DIR, pool);
+}
+
+const char *
 svn_fs_fs__path_txn_dir(svn_fs_t *fs,
                         const svn_fs_fs__id_part_t *txn_id,
                         apr_pool_t *pool)
 {
   SVN_ERR_ASSERT_NO_RETURN(txn_id != NULL);
-  return svn_dirent_join_many(pool, fs->path, PATH_TXNS_DIR,
-                              combine_txn_id_string(txn_id, PATH_EXT_TXN,
-                                                    pool),
-                              SVN_VA_NULL);
+  return svn_dirent_join(svn_fs_fs__path_txns_dir(fs, pool),
+                         combine_txn_id_string(txn_id, PATH_EXT_TXN, pool),
+                         pool);
 }
 
 const char*
@@ -263,16 +279,22 @@ svn_fs_fs__path_txn_item_index(svn_fs_t
 }
 
 const char *
+svn_fs_fs__path_txn_proto_revs(svn_fs_t *fs,
+                               apr_pool_t *pool)
+{
+  return svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, pool);
+}
+
+const char *
 svn_fs_fs__path_txn_proto_rev(svn_fs_t *fs,
                               const svn_fs_fs__id_part_t *txn_id,
                               apr_pool_t *pool)
 {
   fs_fs_data_t *ffd = fs->fsap_data;
   if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
-    return svn_dirent_join_many(pool, fs->path, PATH_TXN_PROTOS_DIR,
-                                combine_txn_id_string(txn_id, PATH_EXT_REV,
-                                                      pool),
-                                SVN_VA_NULL);
+    return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool),
+                           combine_txn_id_string(txn_id, PATH_EXT_REV, pool),
+                           pool);
   else
     return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
                            PATH_REV, pool);
@@ -286,11 +308,10 @@ svn_fs_fs__path_txn_proto_rev_lock(svn_f
 {
   fs_fs_data_t *ffd = fs->fsap_data;
   if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
-    return svn_dirent_join_many(pool, fs->path, PATH_TXN_PROTOS_DIR,
-                                combine_txn_id_string(txn_id,
-                                                      PATH_EXT_REV_LOCK,
-                                                      pool),
-                                SVN_VA_NULL);
+    return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool),
+                           combine_txn_id_string(txn_id, PATH_EXT_REV_LOCK,
+                                                 pool),
+                           pool);
   else
     return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
                            PATH_REV_LOCK, pool);
@@ -387,7 +408,7 @@ svn_fs_fs__read_min_unpacked_rev(svn_rev
   SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
   SVN_ERR(svn_io_file_close(file, pool));
 
-  *min_unpacked_rev = SVN_STR_TO_REV(buf);
+  SVN_ERR(svn_revnum_parse(min_unpacked_rev, buf, NULL));
   return SVN_NO_ERROR;
 }
 
@@ -429,7 +450,6 @@ svn_fs_fs__read_current(svn_revnum_t *re
 {
   fs_fs_data_t *ffd = fs->fsap_data;
   svn_stringbuf_t *content;
-  const char *str;
 
   SVN_ERR(svn_fs_fs__read_content(&content,
                                   svn_fs_fs__path_current(fs, pool),
@@ -437,16 +457,20 @@ svn_fs_fs__read_current(svn_revnum_t *re
 
   if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
     {
-      SVN_ERR(svn_revnum_parse(rev, content->data, &str));
-      if (*str != '\n')
-        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                                _("Corrupt 'current' file"));
+      /* When format 1 and 2 filesystems are upgraded, the 'current' file is
+         left intact.  As a consequence, there is a window when a filesystem
+         has a new format, but this file still contains the IDs left from an
+         old format, i.e. looks like "359 j5 v\n".  Do not be too strict here
+         and only expect a parseable revision number. */
+      SVN_ERR(svn_revnum_parse(rev, content->data, NULL));
 
       *next_node_id = 0;
       *next_copy_id = 0;
     }
   else
     {
+      const char *str;
+
       SVN_ERR(svn_revnum_parse(rev, content->data, &str));
       if (*str != ' ')
         return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
@@ -663,10 +687,8 @@ svn_fs_fs__move_into_place(const char *o
 }
 
 svn_boolean_t
-svn_fs_fs__use_log_addressing(svn_fs_t *fs,
-                              svn_revnum_t rev)
+svn_fs_fs__use_log_addressing(svn_fs_t *fs)
 {
   fs_fs_data_t *ffd = fs->fsap_data;
-  return ffd->min_log_addressing_rev != SVN_INVALID_REVNUM
-      && ffd->min_log_addressing_rev <= rev;
+  return ffd->use_log_addressing;
 }

Modified: subversion/branches/authzperf/subversion/libsvn_fs_fs/util.h
URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/libsvn_fs_fs/util.h?rev=1649205&r1=1649204&r2=1649205&view=diff
==============================================================================
--- subversion/branches/authzperf/subversion/libsvn_fs_fs/util.h (original)
+++ subversion/branches/authzperf/subversion/libsvn_fs_fs/util.h Sat Jan  3 14:00:41 2015
@@ -76,6 +76,12 @@ svn_boolean_t
 svn_fs_fs__is_packed_revprop(svn_fs_t *fs,
                              svn_revnum_t rev);
 
+/* Return the first revision in the pack / rev file containing REVISION in
+ * filesystem FS.  For non-packed revs, this will simply be REVISION. */
+svn_revnum_t
+svn_fs_fs__packed_base_rev(svn_fs_t *fs,
+                           svn_revnum_t revision);
+
 /* Return the full path of the rev shard directory that will contain
  * revision REV in FS.  Allocate the result in POOL.
  */
@@ -183,6 +189,13 @@ const char *
 svn_fs_fs__path_min_unpacked_rev(svn_fs_t *fs,
                                  apr_pool_t *pool);
 
+/* Return the path of the 'transactions' directory in FS.
+ * The result will be allocated in POOL.
+ */
+const char *
+svn_fs_fs__path_txns_dir(svn_fs_t *fs,
+                         apr_pool_t *pool);
+
 /* Return the path of the directory containing the transaction TXN_ID in FS.
  * The result will be allocated in POOL.
  */
@@ -191,6 +204,13 @@ svn_fs_fs__path_txn_dir(svn_fs_t *fs,
                         const svn_fs_fs__id_part_t *txn_id,
                         apr_pool_t *pool);
 
+/* Return the path of the 'txn-protorevs' directory in FS, even if that
+ * folder may not exist in FS.  The result will be allocated in POOL.
+ */
+const char *
+svn_fs_fs__path_txn_proto_revs(svn_fs_t *fs,
+                               apr_pool_t *pool);
+
 /* Return the path of the proto-revision file for transaction TXN_ID in FS.
  * The result will be allocated in POOL.
  */
@@ -381,9 +401,8 @@ svn_fs_fs__move_into_place(const char *o
                            const char *perms_reference,
                            apr_pool_t *pool);
 
-/* Return TRUE, iff revision REV in FS requires logical addressing. */
+/* Return TRUE, iff FS uses logical addressing. */
 svn_boolean_t
-svn_fs_fs__use_log_addressing(svn_fs_t *fs,
-                              svn_revnum_t rev);
+svn_fs_fs__use_log_addressing(svn_fs_t *fs);
 
 #endif