You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2013/07/17 14:39:49 UTC

svn commit: r1504100 [2/5] - in /subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs: dag.c fs.c fs_fs.c fs_fs.h pack.c transaction.c transaction.h tree.c

Modified: subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/fs_fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/fs_fs.c?rev=1504100&r1=1504099&r2=1504100&view=diff
==============================================================================
--- subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/fs_fs.c (original)
+++ subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs/fs_fs.c Wed Jul 17 12:39:48 2013
@@ -64,6 +64,7 @@
 #include "rep-cache.h"
 #include "revprops.h"
 #include "temp_serializer.h"
+#include "transaction.h"
 #include "util.h"
 
 #include "private/svn_string_private.h"
@@ -110,17 +111,6 @@ are likely some errors because of that.
 
 */
 
-/* The vtable associated with an open transaction object. */
-static txn_vtable_t txn_vtable = {
-  svn_fs_fs__commit_txn,
-  svn_fs_fs__abort_txn,
-  svn_fs_fs__txn_prop,
-  svn_fs_fs__txn_proplist,
-  svn_fs_fs__change_txn_prop,
-  svn_fs_fs__txn_root,
-  svn_fs_fs__change_txn_props
-};
-
 /* Declarations. */
 
 static svn_error_t *
@@ -152,53 +142,6 @@ path_lock(svn_fs_t *fs, apr_pool_t *pool
   return svn_dirent_join(fs->path, PATH_LOCK_FILE, pool);
 }
 
-/* Return the name of the sha1->rep mapping file in transaction TXN_ID
- * within FS for the given SHA1 checksum.  Use POOL for allocations.
- */
-static APR_INLINE const char *
-path_txn_sha1(svn_fs_t *fs, const char *txn_id, svn_checksum_t *sha1,
-              apr_pool_t *pool)
-{
-  return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
-                         svn_checksum_to_cstring(sha1, pool),
-                         pool);
-}
-
-static APR_INLINE const char *
-path_txn_changes(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
-{
-  return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
-                         PATH_CHANGES, pool);
-}
-
-static APR_INLINE const char *
-path_txn_props(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
-{
-  return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
-                         PATH_TXN_PROPS, pool);
-}
-
-static APR_INLINE const char *
-path_txn_next_ids(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
-{
-  return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
-                         PATH_NEXT_IDS, pool);
-}
-
-static APR_INLINE const char *
-path_txn_proto_rev_lock(svn_fs_t *fs, const char *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,
-                                apr_pstrcat(pool, txn_id, PATH_EXT_REV_LOCK,
-                                            (char *)NULL),
-                                NULL);
-  else
-    return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
-                           PATH_REV_LOCK, pool);
-}
-
 static APR_INLINE const char *
 path_node_origin(svn_fs_t *fs, const char *node_id, apr_pool_t *pool)
 {
@@ -209,127 +152,8 @@ path_node_origin(svn_fs_t *fs, const cha
                               node_id_minus_last_char, NULL);
 }
 
-static APR_INLINE const char *
-path_and_offset_of(apr_file_t *file, apr_pool_t *pool)
-{
-  const char *path;
-  apr_off_t offset = 0;
-
-  if (apr_file_name_get(&path, file) != APR_SUCCESS)
-    path = "(unknown)";
-
-  if (apr_file_seek(file, APR_CUR, &offset) != APR_SUCCESS)
-    offset = -1;
-
-  return apr_psprintf(pool, "%s:%" APR_OFF_T_FMT, path, offset);
-}
-
-
 
 
-/* Functions for working with shared transaction data. */
-
-/* Return the transaction object for transaction TXN_ID from the
-   transaction list of filesystem FS (which must already be locked via the
-   txn_list_lock mutex).  If the transaction does not exist in the list,
-   then create a new transaction object and return it (if CREATE_NEW is
-   true) or return NULL (otherwise). */
-static fs_fs_shared_txn_data_t *
-get_shared_txn(svn_fs_t *fs, const char *txn_id, svn_boolean_t create_new)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-  fs_fs_shared_data_t *ffsd = ffd->shared;
-  fs_fs_shared_txn_data_t *txn;
-
-  for (txn = ffsd->txns; txn; txn = txn->next)
-    if (strcmp(txn->txn_id, txn_id) == 0)
-      break;
-
-  if (txn || !create_new)
-    return txn;
-
-  /* Use the transaction object from the (single-object) freelist,
-     if one is available, or otherwise create a new object. */
-  if (ffsd->free_txn)
-    {
-      txn = ffsd->free_txn;
-      ffsd->free_txn = NULL;
-    }
-  else
-    {
-      apr_pool_t *subpool = svn_pool_create(ffsd->common_pool);
-      txn = apr_palloc(subpool, sizeof(*txn));
-      txn->pool = subpool;
-    }
-
-  assert(strlen(txn_id) < sizeof(txn->txn_id));
-  apr_cpystrn(txn->txn_id, txn_id, sizeof(txn->txn_id));
-  txn->being_written = FALSE;
-
-  /* Link this transaction into the head of the list.  We will typically
-     be dealing with only one active transaction at a time, so it makes
-     sense for searches through the transaction list to look at the
-     newest transactions first.  */
-  txn->next = ffsd->txns;
-  ffsd->txns = txn;
-
-  return txn;
-}
-
-/* Free the transaction object for transaction TXN_ID, and remove it
-   from the transaction list of filesystem FS (which must already be
-   locked via the txn_list_lock mutex).  Do nothing if the transaction
-   does not exist. */
-static void
-free_shared_txn(svn_fs_t *fs, const char *txn_id)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-  fs_fs_shared_data_t *ffsd = ffd->shared;
-  fs_fs_shared_txn_data_t *txn, *prev = NULL;
-
-  for (txn = ffsd->txns; txn; prev = txn, txn = txn->next)
-    if (strcmp(txn->txn_id, txn_id) == 0)
-      break;
-
-  if (!txn)
-    return;
-
-  if (prev)
-    prev->next = txn->next;
-  else
-    ffsd->txns = txn->next;
-
-  /* As we typically will be dealing with one transaction after another,
-     we will maintain a single-object free list so that we can hopefully
-     keep reusing the same transaction object. */
-  if (!ffsd->free_txn)
-    ffsd->free_txn = txn;
-  else
-    svn_pool_destroy(txn->pool);
-}
-
-
-/* Obtain a lock on the transaction list of filesystem FS, call BODY
-   with FS, BATON, and POOL, and then unlock the transaction list.
-   Return what BODY returned. */
-static svn_error_t *
-with_txnlist_lock(svn_fs_t *fs,
-                  svn_error_t *(*body)(svn_fs_t *fs,
-                                       const void *baton,
-                                       apr_pool_t *pool),
-                  const void *baton,
-                  apr_pool_t *pool)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-  fs_fs_shared_data_t *ffsd = ffd->shared;
-
-  SVN_MUTEX__WITH_LOCK(ffsd->txn_list_lock,
-                       body(fs, baton, pool));
-
-  return SVN_NO_ERROR;
-}
-
-
 /* Get a lock on empty file LOCK_FILENAME, creating it in POOL. */
 static svn_error_t *
 get_lock_on_filesystem(const char *lock_filename,
@@ -447,233 +271,6 @@ svn_fs_fs__with_txn_current_lock(svn_fs_
   return SVN_NO_ERROR;
 }
 
-/* A structure used by unlock_proto_rev() and unlock_proto_rev_body(),
-   which see. */
-struct unlock_proto_rev_baton
-{
-  const char *txn_id;
-  void *lockcookie;
-};
-
-/* Callback used in the implementation of unlock_proto_rev(). */
-static svn_error_t *
-unlock_proto_rev_body(svn_fs_t *fs, const void *baton, apr_pool_t *pool)
-{
-  const struct unlock_proto_rev_baton *b = baton;
-  const char *txn_id = b->txn_id;
-  apr_file_t *lockfile = b->lockcookie;
-  fs_fs_shared_txn_data_t *txn = get_shared_txn(fs, txn_id, FALSE);
-  apr_status_t apr_err;
-
-  if (!txn)
-    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                             _("Can't unlock unknown transaction '%s'"),
-                             txn_id);
-  if (!txn->being_written)
-    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                             _("Can't unlock nonlocked transaction '%s'"),
-                             txn_id);
-
-  apr_err = apr_file_unlock(lockfile);
-  if (apr_err)
-    return svn_error_wrap_apr
-      (apr_err,
-       _("Can't unlock prototype revision lockfile for transaction '%s'"),
-       txn_id);
-  apr_err = apr_file_close(lockfile);
-  if (apr_err)
-    return svn_error_wrap_apr
-      (apr_err,
-       _("Can't close prototype revision lockfile for transaction '%s'"),
-       txn_id);
-
-  txn->being_written = FALSE;
-
-  return SVN_NO_ERROR;
-}
-
-/* Unlock the prototype revision file for transaction TXN_ID in filesystem
-   FS using cookie LOCKCOOKIE.  The original prototype revision file must
-   have been closed _before_ calling this function.
-
-   Perform temporary allocations in POOL. */
-static svn_error_t *
-unlock_proto_rev(svn_fs_t *fs, const char *txn_id, void *lockcookie,
-                 apr_pool_t *pool)
-{
-  struct unlock_proto_rev_baton b;
-
-  b.txn_id = txn_id;
-  b.lockcookie = lockcookie;
-  return with_txnlist_lock(fs, unlock_proto_rev_body, &b, pool);
-}
-
-/* Same as unlock_proto_rev(), but requires that the transaction list
-   lock is already held. */
-static svn_error_t *
-unlock_proto_rev_list_locked(svn_fs_t *fs, const char *txn_id,
-                             void *lockcookie,
-                             apr_pool_t *pool)
-{
-  struct unlock_proto_rev_baton b;
-
-  b.txn_id = txn_id;
-  b.lockcookie = lockcookie;
-  return unlock_proto_rev_body(fs, &b, pool);
-}
-
-/* A structure used by get_writable_proto_rev() and
-   get_writable_proto_rev_body(), which see. */
-struct get_writable_proto_rev_baton
-{
-  apr_file_t **file;
-  void **lockcookie;
-  const char *txn_id;
-};
-
-/* Callback used in the implementation of get_writable_proto_rev(). */
-static svn_error_t *
-get_writable_proto_rev_body(svn_fs_t *fs, const void *baton, apr_pool_t *pool)
-{
-  const struct get_writable_proto_rev_baton *b = baton;
-  apr_file_t **file = b->file;
-  void **lockcookie = b->lockcookie;
-  const char *txn_id = b->txn_id;
-  svn_error_t *err;
-  fs_fs_shared_txn_data_t *txn = get_shared_txn(fs, txn_id, TRUE);
-
-  /* First, ensure that no thread in this process (including this one)
-     is currently writing to this transaction's proto-rev file. */
-  if (txn->being_written)
-    return svn_error_createf(SVN_ERR_FS_REP_BEING_WRITTEN, NULL,
-                             _("Cannot write to the prototype revision file "
-                               "of transaction '%s' because a previous "
-                               "representation is currently being written by "
-                               "this process"),
-                             txn_id);
-
-
-  /* We know that no thread in this process is writing to the proto-rev
-     file, and by extension, that no thread in this process is holding a
-     lock on the prototype revision lock file.  It is therefore safe
-     for us to attempt to lock this file, to see if any other process
-     is holding a lock. */
-
-  {
-    apr_file_t *lockfile;
-    apr_status_t apr_err;
-    const char *lockfile_path = path_txn_proto_rev_lock(fs, txn_id, pool);
-
-    /* Open the proto-rev lockfile, creating it if necessary, as it may
-       not exist if the transaction dates from before the lockfiles were
-       introduced.
-
-       ### We'd also like to use something like svn_io_file_lock2(), but
-           that forces us to create a subpool just to be able to unlock
-           the file, which seems a waste. */
-    SVN_ERR(svn_io_file_open(&lockfile, lockfile_path,
-                             APR_WRITE | APR_CREATE, APR_OS_DEFAULT, pool));
-
-    apr_err = apr_file_lock(lockfile,
-                            APR_FLOCK_EXCLUSIVE | APR_FLOCK_NONBLOCK);
-    if (apr_err)
-      {
-        svn_error_clear(svn_io_file_close(lockfile, pool));
-
-        if (APR_STATUS_IS_EAGAIN(apr_err))
-          return svn_error_createf(SVN_ERR_FS_REP_BEING_WRITTEN, NULL,
-                                   _("Cannot write to the prototype revision "
-                                     "file of transaction '%s' because a "
-                                     "previous representation is currently "
-                                     "being written by another process"),
-                                   txn_id);
-
-        return svn_error_wrap_apr(apr_err,
-                                  _("Can't get exclusive lock on file '%s'"),
-                                  svn_dirent_local_style(lockfile_path, pool));
-      }
-
-    *lockcookie = lockfile;
-  }
-
-  /* We've successfully locked the transaction; mark it as such. */
-  txn->being_written = TRUE;
-
-
-  /* Now open the prototype revision file and seek to the end. */
-  err = svn_io_file_open(file,
-                         svn_fs_fs__path_txn_proto_rev(fs, txn_id, pool),
-                         APR_WRITE | APR_BUFFERED, APR_OS_DEFAULT, pool);
-
-  /* You might expect that we could dispense with the following seek
-     and achieve the same thing by opening the file using APR_APPEND.
-     Unfortunately, APR's buffered file implementation unconditionally
-     places its initial file pointer at the start of the file (even for
-     files opened with APR_APPEND), so we need this seek to reconcile
-     the APR file pointer to the OS file pointer (since we need to be
-     able to read the current file position later). */
-  if (!err)
-    {
-      apr_off_t offset = 0;
-      err = svn_io_file_seek(*file, APR_END, &offset, pool);
-    }
-
-  if (err)
-    {
-      err = svn_error_compose_create(
-              err,
-              unlock_proto_rev_list_locked(fs, txn_id, *lockcookie, pool));
-
-      *lockcookie = NULL;
-    }
-
-  return svn_error_trace(err);
-}
-
-/* Get a handle to the prototype revision file for transaction TXN_ID in
-   filesystem FS, and lock it for writing.  Return FILE, a file handle
-   positioned at the end of the file, and LOCKCOOKIE, a cookie that
-   should be passed to unlock_proto_rev() to unlock the file once FILE
-   has been closed.
-
-   If the prototype revision file is already locked, return error
-   SVN_ERR_FS_REP_BEING_WRITTEN.
-
-   Perform all allocations in POOL. */
-static svn_error_t *
-get_writable_proto_rev(apr_file_t **file,
-                       void **lockcookie,
-                       svn_fs_t *fs, const char *txn_id,
-                       apr_pool_t *pool)
-{
-  struct get_writable_proto_rev_baton b;
-
-  b.file = file;
-  b.lockcookie = lockcookie;
-  b.txn_id = txn_id;
-
-  return with_txnlist_lock(fs, get_writable_proto_rev_body, &b, pool);
-}
-
-/* Callback used in the implementation of purge_shared_txn(). */
-static svn_error_t *
-purge_shared_txn_body(svn_fs_t *fs, const void *baton, apr_pool_t *pool)
-{
-  const char *txn_id = baton;
-
-  free_shared_txn(fs, txn_id);
-  svn_fs_fs__reset_txn_caches(fs);
-
-  return SVN_NO_ERROR;
-}
-
-/* Purge the shared data for transaction TXN_ID in filesystem FS.
-   Perform all allocations in POOL. */
-static svn_error_t *
-purge_shared_txn(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
-{
-  return with_txnlist_lock(fs, purge_shared_txn_body, txn_id, pool);
-}
 
 
 
@@ -1335,128 +932,6 @@ svn_fs_fs__revision_exists(svn_revnum_t 
 }
 
 svn_error_t *
-svn_fs_fs__put_node_revision(svn_fs_t *fs,
-                             const svn_fs_id_t *id,
-                             node_revision_t *noderev,
-                             svn_boolean_t fresh_txn_root,
-                             apr_pool_t *pool)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-  apr_file_t *noderev_file;
-  const char *txn_id = svn_fs_fs__id_txn_id(id);
-
-  noderev->is_fresh_txn_root = fresh_txn_root;
-
-  if (! txn_id)
-    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                             _("Attempted to write to non-transaction '%s'"),
-                             svn_fs_fs__id_unparse(id, pool)->data);
-
-  SVN_ERR(svn_io_file_open(&noderev_file,
-                           svn_fs_fs__path_txn_node_rev(fs, id, pool),
-                           APR_WRITE | APR_CREATE | APR_TRUNCATE
-                           | APR_BUFFERED, APR_OS_DEFAULT, pool));
-
-  SVN_ERR(svn_fs_fs__write_noderev(svn_stream_from_aprfile2(noderev_file, TRUE,
-                                                            pool),
-                                   noderev, ffd->format,
-                                   svn_fs_fs__fs_supports_mergeinfo(fs),
-                                   pool));
-
-  SVN_ERR(svn_io_file_close(noderev_file, pool));
-
-  return SVN_NO_ERROR;
-}
-
-/* For the in-transaction NODEREV within FS, write the sha1->rep mapping
- * file in the respective transaction, if rep sharing has been enabled etc.
- * Use POOL for temporary allocations.
- */
-static svn_error_t *
-store_sha1_rep_mapping(svn_fs_t *fs,
-                       node_revision_t *noderev,
-                       apr_pool_t *pool)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-
-  /* if rep sharing has been enabled and the noderev has a data rep and
-   * its SHA-1 is known, store the rep struct under its SHA1. */
-  if (   ffd->rep_sharing_allowed
-      && noderev->data_rep
-      && noderev->data_rep->sha1_checksum)
-    {
-      apr_file_t *rep_file;
-      const char *file_name = path_txn_sha1(fs,
-                                            svn_fs_fs__id_txn_id(noderev->id),
-                                            noderev->data_rep->sha1_checksum,
-                                            pool);
-      svn_stringbuf_t *rep_string
-        = svn_fs_fs__unparse_representation(noderev->data_rep,
-                                            ffd->format,
-                                            (noderev->kind == svn_node_dir),
-                                            FALSE,
-                                            pool);
-      SVN_ERR(svn_io_file_open(&rep_file, file_name,
-                               APR_WRITE | APR_CREATE | APR_TRUNCATE
-                               | APR_BUFFERED, APR_OS_DEFAULT, pool));
-
-      SVN_ERR(svn_io_file_write_full(rep_file, rep_string->data,
-                                     rep_string->len, NULL, pool));
-
-      SVN_ERR(svn_io_file_close(rep_file, pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-static const char *
-unparse_dir_entry(svn_node_kind_t kind, const svn_fs_id_t *id,
-                  apr_pool_t *pool)
-{
-  return apr_psprintf(pool, "%s %s",
-                      (kind == svn_node_file) ? SVN_FS_FS__KIND_FILE
-                                              : SVN_FS_FS__KIND_DIR,
-                      svn_fs_fs__id_unparse(id, pool)->data);
-}
-
-/* Given a hash ENTRIES of dirent structions, return a hash in
-   *STR_ENTRIES_P, that has svn_string_t as the values in the format
-   specified by the fs_fs directory contents file.  Perform
-   allocations in POOL. */
-static svn_error_t *
-unparse_dir_entries(apr_hash_t **str_entries_p,
-                    apr_hash_t *entries,
-                    apr_pool_t *pool)
-{
-  apr_hash_index_t *hi;
-
-  /* For now, we use a our own hash function to ensure that we get a
-   * (largely) stable order when serializing the data.  It also gives
-   * us some performance improvement.
-   *
-   * ### TODO ###
-   * Use some sorted or other fixed order data container.
-   */
-  *str_entries_p = svn_hash__make(pool);
-
-  for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
-    {
-      const void *key;
-      apr_ssize_t klen;
-      svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
-      const char *new_val;
-
-      apr_hash_this(hi, &key, &klen, NULL);
-      new_val = unparse_dir_entry(dirent->kind, dirent->id, pool);
-      apr_hash_set(*str_entries_p, key, klen,
-                   svn_string_create(new_val, pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-
-svn_error_t *
 svn_fs_fs__file_length(svn_filesize_t *length,
                        node_revision_t *noderev,
                        apr_pool_t *pool)
@@ -1541,2567 +1016,71 @@ svn_fs_fs__rep_copy(representation_t *re
   return rep_new;
 }
 
-/* Merge the internal-use-only CHANGE into a hash of public-FS
-   svn_fs_path_change2_t CHANGES, collapsing multiple changes into a
-   single summarical (is that real word?) change per path.  Also keep
-   the COPYFROM_CACHE up to date with new adds and replaces.  */
-static svn_error_t *
-fold_change(apr_hash_t *changes,
-            const change_t *change,
-            apr_hash_t *copyfrom_cache)
-{
-  apr_pool_t *pool = apr_hash_pool_get(changes);
-  svn_fs_path_change2_t *old_change, *new_change;
-  const char *path;
-  apr_size_t path_len = strlen(change->path);
-
-  if ((old_change = apr_hash_get(changes, change->path, path_len)))
-    {
-      /* This path already exists in the hash, so we have to merge
-         this change into the already existing one. */
-
-      /* Sanity check:  only allow NULL node revision ID in the
-         `reset' case. */
-      if ((! change->noderev_id) && (change->kind != svn_fs_path_change_reset))
-        return svn_error_create
-          (SVN_ERR_FS_CORRUPT, NULL,
-           _("Missing required node revision ID"));
-
-      /* Sanity check: we should be talking about the same node
-         revision ID as our last change except where the last change
-         was a deletion. */
-      if (change->noderev_id
-          && (! svn_fs_fs__id_eq(old_change->node_rev_id, change->noderev_id))
-          && (old_change->change_kind != svn_fs_path_change_delete))
-        return svn_error_create
-          (SVN_ERR_FS_CORRUPT, NULL,
-           _("Invalid change ordering: new node revision ID "
-             "without delete"));
-
-      /* 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)
-          && (! ((change->kind == svn_fs_path_change_replace)
-                 || (change->kind == svn_fs_path_change_reset)
-                 || (change->kind == svn_fs_path_change_add))))
-        return svn_error_create
-          (SVN_ERR_FS_CORRUPT, NULL,
-           _("Invalid change ordering: non-add change on deleted path"));
-
-      /* Sanity check: an add can't follow anything except
-         a delete or reset.  */
-      if ((change->kind == svn_fs_path_change_add)
-          && (old_change->change_kind != svn_fs_path_change_delete)
-          && (old_change->change_kind != svn_fs_path_change_reset))
-        return svn_error_create
-          (SVN_ERR_FS_CORRUPT, NULL,
-           _("Invalid change ordering: add change on preexisting path"));
 
-      /* Now, merge that change in. */
-      switch (change->kind)
-        {
-        case svn_fs_path_change_reset:
-          /* A reset here will simply remove the path change from the
-             hash. */
-          old_change = NULL;
-          break;
+/* Write out the zeroth revision for filesystem FS. */
+static svn_error_t *
+write_revision_zero(svn_fs_t *fs)
+{
+  const char *path_revision_zero = svn_fs_fs__path_rev(fs, 0, fs->pool);
+  apr_hash_t *proplist;
+  svn_string_t date;
 
-        case svn_fs_path_change_delete:
-          if (old_change->change_kind == svn_fs_path_change_add)
-            {
-              /* If the path was introduced in this transaction via an
-                 add, and we are deleting it, just remove the path
-                 altogether. */
-              old_change = NULL;
-            }
-          else
-            {
-              /* A deletion overrules all previous changes. */
-              old_change->change_kind = svn_fs_path_change_delete;
-              old_change->text_mod = change->text_mod;
-              old_change->prop_mod = change->prop_mod;
-              old_change->copyfrom_rev = SVN_INVALID_REVNUM;
-              old_change->copyfrom_path = NULL;
-            }
-          break;
+  /* Write out a rev file for revision 0. */
+  SVN_ERR(svn_io_file_create(path_revision_zero,
+                             "PLAIN\nEND\nENDREP\n"
+                             "id: 0.0.r0/17\n"
+                             "type: dir\n"
+                             "count: 0\n"
+                             "text: 0 0 4 4 "
+                             "2d2977d1c96f487abe4a1e202dd03b4e\n"
+                             "cpath: /\n"
+                             "\n\n17 107\n", fs->pool));
+  SVN_ERR(svn_io_set_file_read_only(path_revision_zero, FALSE, fs->pool));
 
-        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. */
-          old_change->change_kind = svn_fs_path_change_replace;
-          old_change->node_rev_id = svn_fs_fs__id_copy(change->noderev_id,
-                                                       pool);
-          old_change->text_mod = change->text_mod;
-          old_change->prop_mod = change->prop_mod;
-          if (change->copyfrom_rev == SVN_INVALID_REVNUM)
-            {
-              old_change->copyfrom_rev = SVN_INVALID_REVNUM;
-              old_change->copyfrom_path = NULL;
-            }
-          else
-            {
-              old_change->copyfrom_rev = change->copyfrom_rev;
-              old_change->copyfrom_path = apr_pstrdup(pool,
-                                                      change->copyfrom_path);
-            }
-          break;
+  /* Set a date on revision 0. */
+  date.data = svn_time_to_cstring(apr_time_now(), fs->pool);
+  date.len = strlen(date.data);
+  proplist = apr_hash_make(fs->pool);
+  svn_hash_sets(proplist, SVN_PROP_REVISION_DATE, &date);
+  return svn_fs_fs__set_revision_proplist(fs, 0, proplist, fs->pool);
+}
 
-        case svn_fs_path_change_modify:
-        default:
-          if (change->text_mod)
-            old_change->text_mod = TRUE;
-          if (change->prop_mod)
-            old_change->prop_mod = TRUE;
-          break;
-        }
+svn_error_t *
+svn_fs_fs__create(svn_fs_t *fs,
+                  const char *path,
+                  apr_pool_t *pool)
+{
+  int format = SVN_FS_FS__FORMAT_NUMBER;
+  fs_fs_data_t *ffd = fs->fsap_data;
 
-      /* Point our new_change to our (possibly modified) old_change. */
-      new_change = old_change;
-    }
-  else
+  fs->path = apr_pstrdup(pool, path);
+  /* See if compatibility with older versions was explicitly requested. */
+  if (fs->config)
     {
-      /* 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_pcalloc(pool, sizeof(*new_change));
-      new_change->node_rev_id = svn_fs_fs__id_copy(change->noderev_id, pool);
-      new_change->change_kind = change->kind;
-      new_change->text_mod = change->text_mod;
-      new_change->prop_mod = change->prop_mod;
-      /* In FSFS, copyfrom_known is *always* true, since we've always
-       * stored copyfroms in changed paths lists. */
-      new_change->copyfrom_known = TRUE;
-      if (change->copyfrom_rev != SVN_INVALID_REVNUM)
-        {
-          new_change->copyfrom_rev = change->copyfrom_rev;
-          new_change->copyfrom_path = apr_pstrdup(pool, change->copyfrom_path);
-        }
-      else
-        {
-          new_change->copyfrom_rev = SVN_INVALID_REVNUM;
-          new_change->copyfrom_path = NULL;
-        }
+      if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE))
+        format = 1;
+      else if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE))
+        format = 2;
+      else if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE))
+        format = 3;
+      else if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_8_COMPATIBLE))
+        format = 4;
     }
+  ffd->format = format;
 
-  if (new_change)
-    new_change->node_kind = change->node_kind;
+  /* Override the default linear layout if this is a new-enough format. */
+  if (format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT)
+    ffd->max_files_per_dir = SVN_FS_FS_DEFAULT_MAX_FILES_PER_DIR;
 
-  /* Add (or update) this path.
-
-     Note: this key might already be present, and it would be nice to
-     re-use its value, but there is no way to fetch it. The API makes no
-     guarantees that this (new) key will not be retained. Thus, we (again)
-     copy the key into the target pool to ensure a proper lifetime.  */
-  path = apr_pstrmemdup(pool, change->path, path_len);
-  apr_hash_set(changes, path, path_len, new_change);
-
-  /* Update the copyfrom cache, if any. */
-  if (copyfrom_cache)
-    {
-      apr_pool_t *copyfrom_pool = apr_hash_pool_get(copyfrom_cache);
-      const char *copyfrom_string = NULL, *copyfrom_key = path;
-      if (new_change)
-        {
-          if (SVN_IS_VALID_REVNUM(new_change->copyfrom_rev))
-            copyfrom_string = apr_psprintf(copyfrom_pool, "%ld %s",
-                                           new_change->copyfrom_rev,
-                                           new_change->copyfrom_path);
-          else
-            copyfrom_string = "";
-        }
-      /* We need to allocate a copy of the key in the copyfrom_pool if
-       * we're not doing a deletion and if it isn't already there. */
-      if (   copyfrom_string
-          && (   ! apr_hash_count(copyfrom_cache)
-              || ! apr_hash_get(copyfrom_cache, copyfrom_key, path_len)))
-        copyfrom_key = apr_pstrmemdup(copyfrom_pool, copyfrom_key, path_len);
-
-      apr_hash_set(copyfrom_cache, copyfrom_key, path_len,
-                   copyfrom_string);
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Examine all the changed path entries in CHANGES and store them in
-   *CHANGED_PATHS.  Folding is done to remove redundant or unnecessary
-   *data.  Store a hash of paths to copyfrom "REV PATH" strings in
-   COPYFROM_HASH if it is non-NULL.  If PREFOLDED is true, assume that
-   the changed-path entries have already been folded (by
-   write_final_changed_path_info) and may be out of order, so we shouldn't
-   remove children of replaced or deleted directories.  Do all
-   allocations in POOL. */
-static svn_error_t *
-process_changes(apr_hash_t *changed_paths,
-                apr_hash_t *copyfrom_cache,
-                apr_array_header_t *changes,
-                svn_boolean_t prefolded,
-                apr_pool_t *pool)
-{
-  apr_pool_t *iterpool = svn_pool_create(pool);
-  int i;
-
-  /* Read in the changes one by one, folding them into our local hash
-     as necessary. */
-
-  for (i = 0; i < changes->nelts; ++i)
-    {
-      change_t *change = APR_ARRAY_IDX(changes, i, change_t *);
-
-      SVN_ERR(fold_change(changed_paths, change, copyfrom_cache));
-
-      /* Now, if our change was a deletion or replacement, we have to
-         blow away any changes thus far on paths that are (or, were)
-         children of this path.
-         ### i won't bother with another iteration pool here -- at
-         most we talking about a few extra dups of paths into what
-         is already a temporary subpool.
-      */
-
-      if (((change->kind == svn_fs_path_change_delete)
-           || (change->kind == svn_fs_path_change_replace))
-          && ! prefolded)
-        {
-          apr_hash_index_t *hi;
-
-          /* a potential child path must contain at least 2 more chars
-             (the path separator plus at least one char for the name).
-             Also, we should not assume that all paths have been normalized
-             i.e. some might have trailing path separators.
-          */
-          apr_ssize_t change_path_len = strlen(change->path);
-          apr_ssize_t min_child_len = change_path_len == 0
-                                    ? 1
-                                    : change->path[change_path_len-1] == '/'
-                                        ? change_path_len + 1
-                                        : change_path_len + 2;
-
-          /* CAUTION: This is the inner loop of an O(n^2) algorithm.
-             The number of changes to process may be >> 1000.
-             Therefore, keep the inner loop as tight as possible.
-          */
-          for (hi = apr_hash_first(iterpool, 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);
-
-              /* If we come across a child of our path, remove it.
-                 Call svn_dirent_is_child only if there is a chance that
-                 this is actually a sub-path.
-               */
-              if (   klen >= min_child_len
-                  && svn_dirent_is_child(change->path, path, iterpool))
-                apr_hash_set(changed_paths, path, klen, NULL);
-            }
-        }
-
-      /* Clear the per-iteration subpool. */
-      svn_pool_clear(iterpool);
-    }
-
-  /* Destroy the per-iteration subpool. */
-  svn_pool_destroy(iterpool);
-
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_fs_fs__txn_changes_fetch(apr_hash_t **changed_paths_p,
-                             svn_fs_t *fs,
-                             const char *txn_id,
-                             apr_pool_t *pool)
-{
-  apr_file_t *file;
-  apr_hash_t *changed_paths = apr_hash_make(pool);
-  apr_array_header_t *changes;
-  apr_pool_t *scratch_pool = svn_pool_create(pool);
-
-  SVN_ERR(svn_io_file_open(&file, path_txn_changes(fs, txn_id, pool),
-                           APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool));
-
-  SVN_ERR(svn_fs_fs__read_changes(&changes,
-                                  svn_stream_from_aprfile2(file, TRUE,
-                                                           scratch_pool),
-                                  scratch_pool));
-  SVN_ERR(process_changes(changed_paths, NULL, changes, FALSE, pool));
-  svn_pool_destroy(scratch_pool);
-
-  SVN_ERR(svn_io_file_close(file, pool));
-
-  *changed_paths_p = changed_paths;
-
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_fs_fs__paths_changed(apr_hash_t **changed_paths_p,
-                         svn_fs_t *fs,
-                         svn_revnum_t rev,
-                         apr_hash_t *copyfrom_cache,
-                         apr_pool_t *pool)
-{
-  apr_hash_t *changed_paths;
-  apr_array_header_t *changes;
-  apr_pool_t *scratch_pool = svn_pool_create(pool);
-
-  SVN_ERR(svn_fs_fs__get_changes(&changes, fs, rev, scratch_pool));
-
-  changed_paths = svn_hash__make(pool);
-
-  SVN_ERR(process_changes(changed_paths, copyfrom_cache, changes,
-                          TRUE, pool));
-  svn_pool_destroy(scratch_pool);
-
-  *changed_paths_p = changed_paths;
-
-  return SVN_NO_ERROR;
-}
-
-/* Copy a revision node-rev SRC into the current transaction TXN_ID in
-   the filesystem FS.  This is only used to create the root of a transaction.
-   Allocations are from POOL.  */
-static svn_error_t *
-create_new_txn_noderev_from_rev(svn_fs_t *fs,
-                                const char *txn_id,
-                                svn_fs_id_t *src,
-                                apr_pool_t *pool)
-{
-  node_revision_t *noderev;
-  const char *node_id, *copy_id;
-
-  SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, src, pool));
-
-  if (svn_fs_fs__id_txn_id(noderev->id))
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Copying from transactions not allowed"));
-
-  noderev->predecessor_id = noderev->id;
-  noderev->predecessor_count++;
-  noderev->copyfrom_path = NULL;
-  noderev->copyfrom_rev = SVN_INVALID_REVNUM;
-
-  /* For the transaction root, the copyroot never changes. */
-
-  node_id = svn_fs_fs__id_node_id(noderev->id);
-  copy_id = svn_fs_fs__id_copy_id(noderev->id);
-  noderev->id = svn_fs_fs__id_txn_create(node_id, copy_id, txn_id, pool);
-
-  return svn_fs_fs__put_node_revision(fs, noderev->id, noderev, TRUE, pool);
-}
-
-/* A structure used by get_and_increment_txn_key_body(). */
-struct get_and_increment_txn_key_baton {
-  svn_fs_t *fs;
-  char *txn_id;
-  apr_pool_t *pool;
-};
-
-/* Callback used in the implementation of create_txn_dir().  This gets
-   the current base 36 value in PATH_TXN_CURRENT and increments it.
-   It returns the original value by the baton. */
-static svn_error_t *
-get_and_increment_txn_key_body(void *baton, apr_pool_t *pool)
-{
-  struct get_and_increment_txn_key_baton *cb = baton;
-  const char *txn_current_filename
-    = svn_fs_fs__path_txn_current(cb->fs, pool);
-  char next_txn_id[MAX_KEY_SIZE+3];
-  apr_size_t len;
-
-  svn_stringbuf_t *buf;
-  SVN_ERR(svn_fs_fs__read_content(&buf, txn_current_filename, cb->pool));
-
-  /* remove trailing newlines */
-  svn_stringbuf_strip_whitespace(buf);
-  cb->txn_id = buf->data;
-  len = buf->len;
-
-  /* Increment the key and add a trailing \n to the string so the
-     txn-current file has a newline in it. */
-  svn_fs_fs__next_key(cb->txn_id, &len, next_txn_id);
-  next_txn_id[len] = '\n';
-  ++len;
-  next_txn_id[len] = '\0';
-
-  SVN_ERR(svn_io_write_atomic(txn_current_filename, next_txn_id, len,
-                              txn_current_filename /* copy_perms path */, pool));
-
-  return SVN_NO_ERROR;
-}
-
-/* Create a unique directory for a transaction in FS based on revision
-   REV.  Return the ID for this transaction in *ID_P.  Use a sequence
-   value in the transaction ID to prevent reuse of transaction IDs. */
-static svn_error_t *
-create_txn_dir(const char **id_p, svn_fs_t *fs, svn_revnum_t rev,
-               apr_pool_t *pool)
-{
-  struct get_and_increment_txn_key_baton cb;
-  const char *txn_dir;
-
-  /* Get the current transaction sequence value, which is a base-36
-     number, from the txn-current file, and write an
-     incremented value back out to the file.  Place the revision
-     number the transaction is based off into the transaction id. */
-  cb.pool = pool;
-  cb.fs = fs;
-  SVN_ERR(svn_fs_fs__with_txn_current_lock(fs,
-                                           get_and_increment_txn_key_body,
-                                           &cb,
-                                           pool));
-  *id_p = apr_psprintf(pool, "%ld-%s", rev, cb.txn_id);
-
-  txn_dir = svn_dirent_join_many(pool,
-                                 fs->path,
-                                 PATH_TXNS_DIR,
-                                 apr_pstrcat(pool, *id_p, PATH_EXT_TXN,
-                                             (char *)NULL),
-                                 NULL);
-
-  return svn_io_dir_make(txn_dir, APR_OS_DEFAULT, pool);
-}
-
-/* Create a unique directory for a transaction in FS based on revision
-   REV.  Return the ID for this transaction in *ID_P.  This
-   implementation is used in svn 1.4 and earlier repositories and is
-   kept in 1.5 and greater to support the --pre-1.4-compatible and
-   --pre-1.5-compatible repository creation options.  Reused
-   transaction IDs are possible with this implementation. */
-static svn_error_t *
-create_txn_dir_pre_1_5(const char **id_p, svn_fs_t *fs, svn_revnum_t rev,
-                       apr_pool_t *pool)
-{
-  unsigned int i;
-  apr_pool_t *subpool;
-  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), NULL);
-
-  subpool = svn_pool_create(pool);
-  for (i = 1; i <= 99999; i++)
-    {
-      svn_error_t *err;
-
-      svn_pool_clear(subpool);
-      unique_path = apr_psprintf(subpool, "%s-%u" PATH_EXT_TXN, prefix, i);
-      err = svn_io_dir_make(unique_path, APR_OS_DEFAULT, subpool);
-      if (! err)
-        {
-          /* We succeeded.  Return the basename minus the ".txn" extension. */
-          const char *name = svn_dirent_basename(unique_path, subpool);
-          *id_p = apr_pstrndup(pool, name,
-                               strlen(name) - strlen(PATH_EXT_TXN));
-          svn_pool_destroy(subpool);
-          return SVN_NO_ERROR;
-        }
-      if (! APR_STATUS_IS_EEXIST(err->apr_err))
-        return svn_error_trace(err);
-      svn_error_clear(err);
-    }
-
-  return svn_error_createf(SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
-                           NULL,
-                           _("Unable to create transaction directory "
-                             "in '%s' for revision %ld"),
-                           svn_dirent_local_style(fs->path, pool),
-                           rev);
-}
-
-svn_error_t *
-svn_fs_fs__create_txn(svn_fs_txn_t **txn_p,
-                      svn_fs_t *fs,
-                      svn_revnum_t rev,
-                      apr_pool_t *pool)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-  svn_fs_txn_t *txn;
-  svn_fs_id_t *root_id;
-
-  txn = apr_pcalloc(pool, sizeof(*txn));
-
-  /* Get the txn_id. */
-  if (ffd->format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
-    SVN_ERR(create_txn_dir(&txn->id, fs, rev, pool));
-  else
-    SVN_ERR(create_txn_dir_pre_1_5(&txn->id, fs, rev, pool));
-
-  txn->fs = fs;
-  txn->base_rev = rev;
-
-  txn->vtable = &txn_vtable;
-  *txn_p = txn;
-
-  /* Create a new root node for this transaction. */
-  SVN_ERR(svn_fs_fs__rev_get_root(&root_id, fs, rev, pool));
-  SVN_ERR(create_new_txn_noderev_from_rev(fs, txn->id, root_id, pool));
-
-  /* Create an empty rev file. */
-  SVN_ERR(svn_io_file_create(svn_fs_fs__path_txn_proto_rev(fs, txn->id, pool),
-                             "", pool));
-
-  /* Create an empty rev-lock file. */
-  SVN_ERR(svn_io_file_create(path_txn_proto_rev_lock(fs, txn->id, pool), "",
-                             pool));
-
-  /* Create an empty changes file. */
-  SVN_ERR(svn_io_file_create(path_txn_changes(fs, txn->id, pool), "",
-                             pool));
-
-  /* Create the next-ids file. */
-  return svn_io_file_create(path_txn_next_ids(fs, txn->id, pool), "0 0\n",
-                            pool);
-}
-
-/* Store the property list for transaction TXN_ID in PROPLIST.
-   Perform temporary allocations in POOL. */
-static svn_error_t *
-get_txn_proplist(apr_hash_t *proplist,
-                 svn_fs_t *fs,
-                 const char *txn_id,
-                 apr_pool_t *pool)
-{
-  svn_stream_t *stream;
-
-  /* Check for issue #3696. (When we find and fix the cause, we can change
-   * this to an assertion.) */
-  if (txn_id == NULL)
-    return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
-                            _("Internal error: a null transaction id was "
-                              "passed to get_txn_proplist()"));
-
-  /* Open the transaction properties file. */
-  SVN_ERR(svn_stream_open_readonly(&stream, path_txn_props(fs, txn_id, pool),
-                                   pool, pool));
-
-  /* Read in the property list. */
-  SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, pool));
-
-  return svn_stream_close(stream);
-}
-
-svn_error_t *
-svn_fs_fs__change_txn_prop(svn_fs_txn_t *txn,
-                           const char *name,
-                           const svn_string_t *value,
-                           apr_pool_t *pool)
-{
-  apr_array_header_t *props = apr_array_make(pool, 1, sizeof(svn_prop_t));
-  svn_prop_t prop;
-
-  prop.name = name;
-  prop.value = value;
-  APR_ARRAY_PUSH(props, svn_prop_t) = prop;
-
-  return svn_fs_fs__change_txn_props(txn, props, pool);
-}
-
-svn_error_t *
-svn_fs_fs__change_txn_props(svn_fs_txn_t *txn,
-                            const apr_array_header_t *props,
-                            apr_pool_t *pool)
-{
-  svn_stringbuf_t *buf;
-  svn_stream_t *stream;
-  apr_hash_t *txn_prop = apr_hash_make(pool);
-  int i;
-  svn_error_t *err;
-
-  err = get_txn_proplist(txn_prop, txn->fs, txn->id, pool);
-  /* Here - and here only - we need to deal with the possibility that the
-     transaction property file doesn't yet exist.  The rest of the
-     implementation assumes that the file exists, but we're called to set the
-     initial transaction properties as the transaction is being created. */
-  if (err && (APR_STATUS_IS_ENOENT(err->apr_err)))
-    svn_error_clear(err);
-  else if (err)
-    return svn_error_trace(err);
-
-  for (i = 0; i < props->nelts; i++)
-    {
-      svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
-
-      svn_hash_sets(txn_prop, prop->name, prop->value);
-    }
-
-  /* Create a new version of the file and write out the new props. */
-  /* Open the transaction properties file. */
-  buf = svn_stringbuf_create_ensure(1024, pool);
-  stream = svn_stream_from_stringbuf(buf, pool);
-  SVN_ERR(svn_hash_write2(txn_prop, stream, SVN_HASH_TERMINATOR, pool));
-  SVN_ERR(svn_stream_close(stream));
-  SVN_ERR(svn_io_write_atomic(path_txn_props(txn->fs, txn->id, pool),
-                              buf->data, buf->len,
-                              NULL /* copy_perms_path */, pool));
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_fs_fs__get_txn(transaction_t **txn_p,
-                   svn_fs_t *fs,
-                   const char *txn_id,
-                   apr_pool_t *pool)
-{
-  transaction_t *txn;
-  node_revision_t *noderev;
-  svn_fs_id_t *root_id;
-
-  txn = apr_pcalloc(pool, sizeof(*txn));
-  txn->proplist = apr_hash_make(pool);
-
-  SVN_ERR(get_txn_proplist(txn->proplist, fs, txn_id, pool));
-  root_id = svn_fs_fs__id_txn_create("0", "0", txn_id, pool);
-
-  SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, root_id, pool));
-
-  txn->root_id = svn_fs_fs__id_copy(noderev->id, pool);
-  txn->base_id = svn_fs_fs__id_copy(noderev->predecessor_id, pool);
-  txn->copies = NULL;
-
-  *txn_p = txn;
-
-  return SVN_NO_ERROR;
-}
-
-/* Write out the currently available next node_id NODE_ID and copy_id
-   COPY_ID for transaction TXN_ID in filesystem FS.  The next node-id is
-   used both for creating new unique nodes for the given transaction, as
-   well as uniquifying representations.  Perform temporary allocations in
-   POOL. */
-static svn_error_t *
-write_next_ids(svn_fs_t *fs,
-               const char *txn_id,
-               const char *node_id,
-               const char *copy_id,
-               apr_pool_t *pool)
-{
-  apr_file_t *file;
-  svn_stream_t *out_stream;
-
-  SVN_ERR(svn_io_file_open(&file, path_txn_next_ids(fs, txn_id, pool),
-                           APR_WRITE | APR_TRUNCATE,
-                           APR_OS_DEFAULT, pool));
-
-  out_stream = svn_stream_from_aprfile2(file, TRUE, pool);
-
-  SVN_ERR(svn_stream_printf(out_stream, pool, "%s %s\n", node_id, copy_id));
-
-  SVN_ERR(svn_stream_close(out_stream));
-  return svn_io_file_close(file, pool);
-}
-
-/* Find out what the next unique node-id and copy-id are for
-   transaction TXN_ID in filesystem FS.  Store the results in *NODE_ID
-   and *COPY_ID.  The next node-id is used both for creating new unique
-   nodes for the given transaction, as well as uniquifying representations.
-   Perform all allocations in POOL. */
-static svn_error_t *
-read_next_ids(const char **node_id,
-              const char **copy_id,
-              svn_fs_t *fs,
-              const char *txn_id,
-              apr_pool_t *pool)
-{
-  apr_file_t *file;
-  char buf[MAX_KEY_SIZE*2+3];
-  apr_size_t limit;
-  char *str, *last_str = buf;
-
-  SVN_ERR(svn_io_file_open(&file, path_txn_next_ids(fs, txn_id, pool),
-                           APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool));
-
-  limit = sizeof(buf);
-  SVN_ERR(svn_io_read_length_line(file, buf, &limit, pool));
-
-  SVN_ERR(svn_io_file_close(file, pool));
-
-  /* Parse this into two separate strings. */
-
-  str = svn_cstring_tokenize(" ", &last_str);
-  if (! str)
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("next-id file corrupt"));
-
-  *node_id = apr_pstrdup(pool, str);
-
-  str = svn_cstring_tokenize(" ", &last_str);
-  if (! str)
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("next-id file corrupt"));
-
-  *copy_id = apr_pstrdup(pool, str);
-
-  return SVN_NO_ERROR;
-}
-
-/* Get a new and unique to this transaction node-id for transaction
-   TXN_ID in filesystem FS.  Store the new node-id in *NODE_ID_P.
-   Node-ids are guaranteed to be unique to this transction, but may
-   not necessarily be sequential.  Perform all allocations in POOL. */
-static svn_error_t *
-get_new_txn_node_id(const char **node_id_p,
-                    svn_fs_t *fs,
-                    const char *txn_id,
-                    apr_pool_t *pool)
-{
-  const char *cur_node_id, *cur_copy_id;
-  char *node_id;
-  apr_size_t len;
-
-  /* First read in the current next-ids file. */
-  SVN_ERR(read_next_ids(&cur_node_id, &cur_copy_id, fs, txn_id, pool));
-
-  node_id = apr_pcalloc(pool, strlen(cur_node_id) + 2);
-
-  len = strlen(cur_node_id);
-  svn_fs_fs__next_key(cur_node_id, &len, node_id);
-
-  SVN_ERR(write_next_ids(fs, txn_id, node_id, cur_copy_id, pool));
-
-  *node_id_p = apr_pstrcat(pool, "_", cur_node_id, (char *)NULL);
-
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_fs_fs__create_node(const svn_fs_id_t **id_p,
-                       svn_fs_t *fs,
-                       node_revision_t *noderev,
-                       const char *copy_id,
-                       const char *txn_id,
-                       apr_pool_t *pool)
-{
-  const char *node_id;
-  const svn_fs_id_t *id;
-
-  /* Get a new node-id for this node. */
-  SVN_ERR(get_new_txn_node_id(&node_id, fs, txn_id, pool));
-
-  id = svn_fs_fs__id_txn_create(node_id, copy_id, txn_id, pool);
-
-  noderev->id = id;
-
-  SVN_ERR(svn_fs_fs__put_node_revision(fs, noderev->id, noderev, FALSE, pool));
-
-  *id_p = id;
-
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_fs_fs__purge_txn(svn_fs_t *fs,
-                     const char *txn_id,
-                     apr_pool_t *pool)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-
-  /* Remove the shared transaction object associated with this transaction. */
-  SVN_ERR(purge_shared_txn(fs, txn_id, pool));
-  /* Remove the directory associated with this transaction. */
-  SVN_ERR(svn_io_remove_dir2(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
-                             FALSE, NULL, NULL, pool));
-  if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
-    {
-      /* Delete protorev and its lock, which aren't in the txn
-         directory.  It's OK if they don't exist (for example, if this
-         is post-commit and the proto-rev has been moved into
-         place). */
-      SVN_ERR(svn_io_remove_file2(
-                  svn_fs_fs__path_txn_proto_rev(fs, txn_id, pool),
-                  TRUE, pool));
-      SVN_ERR(svn_io_remove_file2(path_txn_proto_rev_lock(fs, txn_id, pool),
-                                  TRUE, pool));
-    }
-  return SVN_NO_ERROR;
-}
-
-
-svn_error_t *
-svn_fs_fs__abort_txn(svn_fs_txn_t *txn,
-                     apr_pool_t *pool)
-{
-  SVN_ERR(svn_fs__check_fs(txn->fs, TRUE));
-
-  /* Now, purge the transaction. */
-  SVN_ERR_W(svn_fs_fs__purge_txn(txn->fs, txn->id, pool),
-            apr_psprintf(pool, _("Transaction '%s' cleanup failed"),
-                         txn->id));
-
-  return SVN_NO_ERROR;
-}
-
-
-svn_error_t *
-svn_fs_fs__set_entry(svn_fs_t *fs,
-                     const char *txn_id,
-                     node_revision_t *parent_noderev,
-                     const char *name,
-                     const svn_fs_id_t *id,
-                     svn_node_kind_t kind,
-                     apr_pool_t *pool)
-{
-  representation_t *rep = parent_noderev->data_rep;
-  const char *filename
-    = svn_fs_fs__path_txn_node_children(fs, parent_noderev->id, pool);
-  apr_file_t *file;
-  svn_stream_t *out;
-  fs_fs_data_t *ffd = fs->fsap_data;
-  apr_pool_t *subpool = svn_pool_create(pool);
-
-  if (!rep || !rep->txn_id)
-    {
-      const char *unique_suffix;
-      apr_hash_t *entries;
-
-      /* Before we can modify the directory, we need to dump its old
-         contents into a mutable representation file. */
-      SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, parent_noderev,
-                                          subpool));
-      SVN_ERR(unparse_dir_entries(&entries, entries, subpool));
-      SVN_ERR(svn_io_file_open(&file, filename,
-                               APR_WRITE | APR_CREATE | APR_BUFFERED,
-                               APR_OS_DEFAULT, pool));
-      out = svn_stream_from_aprfile2(file, TRUE, pool);
-      SVN_ERR(svn_hash_write2(entries, out, SVN_HASH_TERMINATOR, subpool));
-
-      svn_pool_clear(subpool);
-
-      /* Mark the node-rev's data rep as mutable. */
-      rep = apr_pcalloc(pool, sizeof(*rep));
-      rep->revision = SVN_INVALID_REVNUM;
-      rep->txn_id = txn_id;
-      SVN_ERR(get_new_txn_node_id(&unique_suffix, fs, txn_id, pool));
-      rep->uniquifier = apr_psprintf(pool, "%s/%s", txn_id, unique_suffix);
-      parent_noderev->data_rep = rep;
-      SVN_ERR(svn_fs_fs__put_node_revision(fs, parent_noderev->id,
-                                           parent_noderev, FALSE, pool));
-    }
-  else
-    {
-      /* The directory rep is already mutable, so just open it for append. */
-      SVN_ERR(svn_io_file_open(&file, filename, APR_WRITE | APR_APPEND,
-                               APR_OS_DEFAULT, pool));
-      out = svn_stream_from_aprfile2(file, TRUE, pool);
-    }
-
-  /* if we have a directory cache for this transaction, update it */
-  if (ffd->txn_dir_cache)
-    {
-      /* build parameters: (name, new entry) pair */
-      const char *key =
-          svn_fs_fs__id_unparse(parent_noderev->id, subpool)->data;
-      replace_baton_t baton;
-
-      baton.name = name;
-      baton.new_entry = NULL;
-
-      if (id)
-        {
-          baton.new_entry = apr_pcalloc(subpool, sizeof(*baton.new_entry));
-          baton.new_entry->name = name;
-          baton.new_entry->kind = kind;
-          baton.new_entry->id = id;
-        }
-
-      /* actually update the cached directory (if cached) */
-      SVN_ERR(svn_cache__set_partial(ffd->txn_dir_cache, key,
-                                     svn_fs_fs__replace_dir_entry, &baton,
-                                     subpool));
-    }
-  svn_pool_clear(subpool);
-
-  /* Append an incremental hash entry for the entry change. */
-  if (id)
-    {
-      const char *val = unparse_dir_entry(kind, id, subpool);
-
-      SVN_ERR(svn_stream_printf(out, subpool, "K %" APR_SIZE_T_FMT "\n%s\n"
-                                "V %" APR_SIZE_T_FMT "\n%s\n",
-                                strlen(name), name,
-                                strlen(val), val));
-    }
-  else
-    {
-      SVN_ERR(svn_stream_printf(out, subpool, "D %" APR_SIZE_T_FMT "\n%s\n",
-                                strlen(name), name));
-    }
-
-  SVN_ERR(svn_io_file_close(file, subpool));
-  svn_pool_destroy(subpool);
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_fs_fs__add_change(svn_fs_t *fs,
-                      const char *txn_id,
-                      const char *path,
-                      const svn_fs_id_t *id,
-                      svn_fs_path_change_kind_t change_kind,
-                      svn_boolean_t text_mod,
-                      svn_boolean_t prop_mod,
-                      svn_node_kind_t node_kind,
-                      svn_revnum_t copyfrom_rev,
-                      const char *copyfrom_path,
-                      apr_pool_t *pool)
-{
-  apr_file_t *file;
-  svn_fs_path_change2_t *change;
-  apr_hash_t *changes = apr_hash_make(pool);
-
-  SVN_ERR(svn_io_file_open(&file, path_txn_changes(fs, txn_id, pool),
-                           APR_APPEND | APR_WRITE | APR_CREATE
-                           | APR_BUFFERED, APR_OS_DEFAULT, pool));
-
-  change = svn_fs__path_change_create_internal(id, change_kind, pool);
-  change->text_mod = text_mod;
-  change->prop_mod = prop_mod;
-  change->node_kind = node_kind;
-  change->copyfrom_rev = copyfrom_rev;
-  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),
-                                   fs, changes, FALSE, pool));
-
-  return svn_io_file_close(file, pool);
-}
-
-/* This baton is used by the representation writing streams.  It keeps
-   track of the checksum information as well as the total size of the
-   representation so far. */
-struct rep_write_baton
-{
-  /* The FS we are writing to. */
-  svn_fs_t *fs;
-
-  /* Actual file to which we are writing. */
-  svn_stream_t *rep_stream;
-
-  /* A stream from the delta combiner.  Data written here gets
-     deltified, then eventually written to rep_stream. */
-  svn_stream_t *delta_stream;
-
-  /* Where is this representation header stored. */
-  apr_off_t rep_offset;
-
-  /* Start of the actual data. */
-  apr_off_t delta_start;
-
-  /* How many bytes have been written to this rep already. */
-  svn_filesize_t rep_size;
-
-  /* The node revision for which we're writing out info. */
-  node_revision_t *noderev;
-
-  /* Actual output file. */
-  apr_file_t *file;
-  /* Lock 'cookie' used to unlock the output file once we've finished
-     writing to it. */
-  void *lockcookie;
-
-  svn_checksum_ctx_t *md5_checksum_ctx;
-  svn_checksum_ctx_t *sha1_checksum_ctx;
-
-  apr_pool_t *pool;
-
-  apr_pool_t *parent_pool;
-};
-
-/* Handler for the write method of the representation writable stream.
-   BATON is a rep_write_baton, DATA is the data to write, and *LEN is
-   the length of this data. */
-static svn_error_t *
-rep_write_contents(void *baton,
-                   const char *data,
-                   apr_size_t *len)
-{
-  struct rep_write_baton *b = baton;
-
-  SVN_ERR(svn_checksum_update(b->md5_checksum_ctx, data, *len));
-  SVN_ERR(svn_checksum_update(b->sha1_checksum_ctx, data, *len));
-  b->rep_size += *len;
-
-  /* If we are writing a delta, use that stream. */
-  if (b->delta_stream)
-    return svn_stream_write(b->delta_stream, data, len);
-  else
-    return svn_stream_write(b->rep_stream, data, len);
-}
-
-/* Given a node-revision NODEREV in filesystem FS, return the
-   representation in *REP to use as the base for a text representation
-   delta if PROPS is FALSE.  If PROPS has been set, a suitable props
-   base representation will be returned.  Perform temporary allocations
-   in *POOL. */
-static svn_error_t *
-choose_delta_base(representation_t **rep,
-                  svn_fs_t *fs,
-                  node_revision_t *noderev,
-                  svn_boolean_t props,
-                  apr_pool_t *pool)
-{
-  /* The zero-based index (counting from the "oldest" end), along NODEREVs line
-   * predecessors, of the node-rev we will use as delta base. */
-  int count;
-  /* The length of the linear part of a delta chain.  (Delta chains use
-   * skip-delta bits for the high-order bits and are linear in the low-order
-   * bits.) */
-  int walk;
-  node_revision_t *base;
-  fs_fs_data_t *ffd = fs->fsap_data;
-  svn_boolean_t maybe_shared_rep = FALSE;
-
-  /* If we have no predecessors, then use the empty stream as a
-     base. */
-  if (! noderev->predecessor_count)
-    {
-      *rep = NULL;
-      return SVN_NO_ERROR;
-    }
-
-  /* Flip the rightmost '1' bit of the predecessor count to determine
-     which file rev (counting from 0) we want to use.  (To see why
-     count & (count - 1) unsets the rightmost set bit, think about how
-     you decrement a binary number.) */
-  count = noderev->predecessor_count;
-  count = count & (count - 1);
-
-  /* We use skip delta for limiting the number of delta operations
-     along very long node histories.  Close to HEAD however, we create
-     a linear history to minimize delta size.  */
-  walk = noderev->predecessor_count - count;
-  if (walk < (int)ffd->max_linear_deltification)
-    count = noderev->predecessor_count - 1;
-
-  /* Finding the delta base over a very long distance can become extremely
-     expensive for very deep histories, possibly causing client timeouts etc.
-     OTOH, this is a rare operation and its gains are minimal. Lets simply
-     start deltification anew close every other 1000 changes or so.  */
-  if (walk > (int)ffd->max_deltification_walk)
-    {
-      *rep = NULL;
-      return SVN_NO_ERROR;
-    }
-
-  /* Walk back a number of predecessors equal to the difference
-     between count and the original predecessor count.  (For example,
-     if noderev has ten predecessors and we want the eighth file rev,
-     walk back two predecessors.) */
-  base = noderev;
-  while ((count++) < noderev->predecessor_count)
-    {
-      SVN_ERR(svn_fs_fs__get_node_revision(&base, fs,
-                                           base->predecessor_id, pool));
-
-      /* If there is a shared rep along the way, we need to limit the
-       * length of the deltification chain.
-       *
-       * Please note that copied nodes - such as branch directories - will
-       * look the same (false positive) while reps shared within the same
-       * revision will not be caught (false negative).
-       *
-       * Message-ID: <CA...@mail.gmail.com>
-       */
-      if (props)
-        {
-          if (   base->prop_rep
-              && svn_fs_fs__id_rev(base->id) > base->prop_rep->revision)
-            maybe_shared_rep = TRUE;
-        }
-      else
-        {
-          if (   base->data_rep
-              && svn_fs_fs__id_rev(base->id) > base->data_rep->revision)
-            maybe_shared_rep = TRUE;
-        }
-    }
-
-  /* return a suitable base representation */
-  *rep = props ? base->prop_rep : base->data_rep;
-
-  /* if we encountered a shared rep, its parent chain may be different
-   * from the node-rev parent chain. */
-  if (*rep && maybe_shared_rep)
-    {
-      /* Check whether the length of the deltification chain is acceptable.
-       * Otherwise, shared reps may form a non-skipping delta chain in
-       * extreme cases. */
-      int chain_length = 0;
-      SVN_ERR(svn_fs_fs__rep_chain_length(&chain_length, *rep, fs, pool));
-
-      /* Some reasonable limit, depending on how acceptable longer linear
-       * chains are in this repo.  Also, allow for some minimal chain. */
-      if (chain_length >= 2 * (int)ffd->max_linear_deltification + 2)
-        *rep = NULL;
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Something went wrong and the pool for the rep write is being
-   cleared before we've finished writing the rep.  So we need
-   to remove the rep from the protorevfile and we need to unlock
-   the protorevfile. */
-static apr_status_t
-rep_write_cleanup(void *data)
-{
-  struct rep_write_baton *b = data;
-  const char *txn_id = svn_fs_fs__id_txn_id(b->noderev->id);
-  svn_error_t *err;
-
-  /* Truncate and close the protorevfile. */
-  err = svn_io_file_trunc(b->file, b->rep_offset, b->pool);
-  err = svn_error_compose_create(err, svn_io_file_close(b->file, b->pool));
-
-  /* Remove our lock regardless of any preceeding errors so that the
-     being_written flag is always removed and stays consistent with the
-     file lock which will be removed no matter what since the pool is
-     going away. */
-  err = svn_error_compose_create(err, unlock_proto_rev(b->fs, txn_id,
-                                                       b->lockcookie, b->pool));
-  if (err)
-    {
-      apr_status_t rc = err->apr_err;
-      svn_error_clear(err);
-      return rc;
-    }
-
-  return APR_SUCCESS;
-}
-
-
-/* Get a rep_write_baton and store it in *WB_P for the representation
-   indicated by NODEREV in filesystem FS.  Perform allocations in
-   POOL.  Only appropriate for file contents, not for props or
-   directory contents. */
-static svn_error_t *
-rep_write_get_baton(struct rep_write_baton **wb_p,
-                    svn_fs_t *fs,
-                    node_revision_t *noderev,
-                    apr_pool_t *pool)
-{
-  struct rep_write_baton *b;
-  apr_file_t *file;
-  representation_t *base_rep;
-  svn_stream_t *source;
-  svn_txdelta_window_handler_t wh;
-  void *whb;
-  fs_fs_data_t *ffd = fs->fsap_data;
-  int diff_version = ffd->format >= SVN_FS_FS__MIN_SVNDIFF1_FORMAT ? 1 : 0;
-  svn_fs_fs__rep_header_t header = { 0 };
-
-  b = apr_pcalloc(pool, sizeof(*b));
-
-  b->sha1_checksum_ctx = svn_checksum_ctx_create(svn_checksum_sha1, pool);
-  b->md5_checksum_ctx = svn_checksum_ctx_create(svn_checksum_md5, pool);
-
-  b->fs = fs;
-  b->parent_pool = pool;
-  b->pool = svn_pool_create(pool);
-  b->rep_size = 0;
-  b->noderev = noderev;
-
-  /* Open the prototype rev file and seek to its end. */
-  SVN_ERR(get_writable_proto_rev(&file, &b->lockcookie,
-                                 fs, svn_fs_fs__id_txn_id(noderev->id),
-                                 b->pool));
-
-  b->file = file;
-  b->rep_stream = svn_stream_from_aprfile2(file, TRUE, b->pool);
-
-  SVN_ERR(svn_fs_fs__get_file_offset(&b->rep_offset, file, b->pool));
-
-  /* Get the base for this delta. */
-  SVN_ERR(choose_delta_base(&base_rep, fs, noderev, FALSE, b->pool));
-  SVN_ERR(svn_fs_fs__get_contents(&source, fs, base_rep, b->pool));
-
-  /* Write out the rep header. */
-  if (base_rep)
-    {
-      header.base_revision = base_rep->revision;
-      header.base_offset = base_rep->offset;
-      header.base_length = base_rep->size;
-      header.type = svn_fs_fs__rep_delta;
-    }
-  else
-    {
-      header.type = svn_fs_fs__rep_self_delta;
-    }
-  SVN_ERR(svn_fs_fs__write_rep_header(&header, b->rep_stream, b->pool));
-
-  /* Now determine the offset of the actual svndiff data. */
-  SVN_ERR(svn_fs_fs__get_file_offset(&b->delta_start, file, b->pool));
-
-  /* Cleanup in case something goes wrong. */
-  apr_pool_cleanup_register(b->pool, b, rep_write_cleanup,
-                            apr_pool_cleanup_null);
-
-  /* Prepare to write the svndiff data. */
-  svn_txdelta_to_svndiff3(&wh,
-                          &whb,
-                          b->rep_stream,
-                          diff_version,
-                          SVN_DELTA_COMPRESSION_LEVEL_DEFAULT,
-                          pool);
-
-  b->delta_stream = svn_txdelta_target_push(wh, whb, source, b->pool);
-
-  *wb_p = b;
-
-  return SVN_NO_ERROR;
-}
-
-/* For REP->SHA1_CHECKSUM, try to find an already existing representation
-   in FS and return it in *OUT_REP.  If no such representation exists or
-   if rep sharing has been disabled for FS, NULL will be returned.  Since
-   there may be new duplicate representations within the same uncommitted
-   revision, those can be passed in REPS_HASH (maps a sha1 digest onto
-   representation_t*), otherwise pass in NULL for REPS_HASH.
-   POOL will be used for allocations. The lifetime of the returned rep is
-   limited by both, POOL and REP lifetime.
- */
-static svn_error_t *
-get_shared_rep(representation_t **old_rep,
-               svn_fs_t *fs,
-               representation_t *rep,
-               apr_hash_t *reps_hash,
-               apr_pool_t *pool)
-{
-  svn_error_t *err;
-  fs_fs_data_t *ffd = fs->fsap_data;
-
-  /* Return NULL, if rep sharing has been disabled. */
-  *old_rep = NULL;
-  if (!ffd->rep_sharing_allowed)
-    return SVN_NO_ERROR;
-
-  /* Check and see if we already have a representation somewhere that's
-     identical to the one we just wrote out.  Start with the hash lookup
-     because it is cheepest. */
-  if (reps_hash)
-    *old_rep = apr_hash_get(reps_hash,
-                            rep->sha1_checksum->digest,
-                            APR_SHA1_DIGESTSIZE);
-
-  /* If we haven't found anything yet, try harder and consult our DB. */
-  if (*old_rep == NULL)
-    {
-      err = svn_fs_fs__get_rep_reference(old_rep, fs, rep->sha1_checksum,
-                                         pool);
-      /* ### Other error codes that we shouldn't mask out? */
-      if (err == SVN_NO_ERROR)
-        {
-          if (*old_rep)
-            SVN_ERR(svn_fs_fs__check_rep(*old_rep, fs, NULL, NULL, pool));
-        }
-      else if (err->apr_err == SVN_ERR_FS_CORRUPT
-               || SVN_ERROR_IN_CATEGORY(err->apr_err,
-                                        SVN_ERR_MALFUNC_CATEGORY_START))
-        {
-          /* Fatal error; don't mask it.
-
-             In particular, this block is triggered when the rep-cache refers
-             to revisions in the future.  We signal that as a corruption situation
-             since, once those revisions are less than youngest (because of more
-             commits), the rep-cache would be invalid.
-           */
-          SVN_ERR(err);
-        }
-      else
-        {
-          /* Something's wrong with the rep-sharing index.  We can continue
-             without rep-sharing, but warn.
-           */
-          (fs->warning)(fs->warning_baton, err);
-          svn_error_clear(err);
-          *old_rep = NULL;
-        }
-    }
-
-  /* look for intra-revision matches (usually data reps but not limited
-     to them in case props happen to look like some data rep)
-   */
-  if (*old_rep == NULL && rep->txn_id)
-    {
-      svn_node_kind_t kind;
-      const char *file_name
-        = path_txn_sha1(fs, rep->txn_id, rep->sha1_checksum, pool);
-
-      /* in our txn, is there a rep file named with the wanted SHA1?
-         If so, read it and use that rep.
-       */
-      SVN_ERR(svn_io_check_path(file_name, &kind, pool));
-      if (kind == svn_node_file)
-        {
-          svn_stringbuf_t *rep_string;
-          SVN_ERR(svn_stringbuf_from_file2(&rep_string, file_name, pool));
-          SVN_ERR(svn_fs_fs__parse_representation(old_rep, rep_string, pool));
-        }
-    }
-
-  /* Add information that is missing in the cached data. */
-  if (*old_rep)
-    {
-      /* Use the old rep for this content. */
-      (*old_rep)->md5_checksum = rep->md5_checksum;
-      (*old_rep)->uniquifier = rep->uniquifier;
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Close handler for the representation write stream.  BATON is a
-   rep_write_baton.  Writes out a new node-rev that correctly
-   references the representation we just finished writing. */
-static svn_error_t *
-rep_write_contents_close(void *baton)
-{
-  struct rep_write_baton *b = baton;
-  const char *unique_suffix;
-  representation_t *rep;
-  representation_t *old_rep;
-  apr_off_t offset;
-
-  rep = apr_pcalloc(b->parent_pool, sizeof(*rep));
-  rep->offset = b->rep_offset;
-
-  /* Close our delta stream so the last bits of svndiff are written
-     out. */
-  if (b->delta_stream)
-    SVN_ERR(svn_stream_close(b->delta_stream));
-
-  /* Determine the length of the svndiff data. */
-  SVN_ERR(svn_fs_fs__get_file_offset(&offset, b->file, b->pool));
-  rep->size = offset - b->delta_start;
-
-  /* Fill in the rest of the representation field. */
-  rep->expanded_size = b->rep_size;
-  rep->txn_id = svn_fs_fs__id_txn_id(b->noderev->id);
-  SVN_ERR(get_new_txn_node_id(&unique_suffix, b->fs, rep->txn_id, b->pool));
-  rep->uniquifier = apr_psprintf(b->parent_pool, "%s/%s", rep->txn_id,
-                                 unique_suffix);
-  rep->revision = SVN_INVALID_REVNUM;
-
-  /* Finalize the checksum. */
-  SVN_ERR(svn_checksum_final(&rep->md5_checksum, b->md5_checksum_ctx,
-                              b->parent_pool));
-  SVN_ERR(svn_checksum_final(&rep->sha1_checksum, b->sha1_checksum_ctx,
-                              b->parent_pool));
-
-  /* Check and see if we already have a representation somewhere that's
-     identical to the one we just wrote out. */
-  SVN_ERR(get_shared_rep(&old_rep, b->fs, rep, NULL, b->parent_pool));
-
-  if (old_rep)
-    {
-      /* We need to erase from the protorev the data we just wrote. */
-      SVN_ERR(svn_io_file_trunc(b->file, b->rep_offset, b->pool));
-
-      /* Use the old rep for this content. */
-      b->noderev->data_rep = old_rep;
-    }
-  else
-    {
-      /* Write out our cosmetic end marker. */
-      SVN_ERR(svn_stream_puts(b->rep_stream, "ENDREP\n"));
-
-      b->noderev->data_rep = rep;
-    }
-
-  /* Remove cleanup callback. */
-  apr_pool_cleanup_kill(b->pool, b, rep_write_cleanup);
-
-  /* Write out the new node-rev information. */
-  SVN_ERR(svn_fs_fs__put_node_revision(b->fs, b->noderev->id, b->noderev, FALSE,
-                                       b->pool));
-  if (!old_rep)
-    SVN_ERR(store_sha1_rep_mapping(b->fs, b->noderev, b->pool));
-
-  SVN_ERR(svn_io_file_close(b->file, b->pool));
-  SVN_ERR(unlock_proto_rev(b->fs, rep->txn_id, b->lockcookie, b->pool));
-  svn_pool_destroy(b->pool);
-
-  return SVN_NO_ERROR;
-}
-
-/* Store a writable stream in *CONTENTS_P that will receive all data
-   written and store it as the file data representation referenced by
-   NODEREV in filesystem FS.  Perform temporary allocations in
-   POOL.  Only appropriate for file data, not props or directory
-   contents. */
-static svn_error_t *
-set_representation(svn_stream_t **contents_p,
-                   svn_fs_t *fs,
-                   node_revision_t *noderev,
-                   apr_pool_t *pool)
-{
-  struct rep_write_baton *wb;
-
-  if (! svn_fs_fs__id_txn_id(noderev->id))
-    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                             _("Attempted to write to non-transaction '%s'"),
-                             svn_fs_fs__id_unparse(noderev->id, pool)->data);
-
-  SVN_ERR(rep_write_get_baton(&wb, fs, noderev, pool));
-
-  *contents_p = svn_stream_create(wb, pool);
-  svn_stream_set_write(*contents_p, rep_write_contents);
-  svn_stream_set_close(*contents_p, rep_write_contents_close);
-
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_fs_fs__set_contents(svn_stream_t **stream,
-                        svn_fs_t *fs,
-                        node_revision_t *noderev,
-                        apr_pool_t *pool)
-{
-  if (noderev->kind != svn_node_file)
-    return svn_error_create(SVN_ERR_FS_NOT_FILE, NULL,
-                            _("Can't set text contents of a directory"));
-
-  return set_representation(stream, fs, noderev, pool);
-}
-
-svn_error_t *
-svn_fs_fs__create_successor(const svn_fs_id_t **new_id_p,
-                            svn_fs_t *fs,
-                            const svn_fs_id_t *old_idp,
-                            node_revision_t *new_noderev,
-                            const char *copy_id,
-                            const char *txn_id,
-                            apr_pool_t *pool)
-{
-  const svn_fs_id_t *id;
-
-  if (! copy_id)
-    copy_id = svn_fs_fs__id_copy_id(old_idp);
-  id = svn_fs_fs__id_txn_create(svn_fs_fs__id_node_id(old_idp), copy_id,
-                                txn_id, pool);
-
-  new_noderev->id = id;
-
-  if (! new_noderev->copyroot_path)
-    {
-      new_noderev->copyroot_path = apr_pstrdup(pool,
-                                               new_noderev->created_path);
-      new_noderev->copyroot_rev = svn_fs_fs__id_rev(new_noderev->id);
-    }
-
-  SVN_ERR(svn_fs_fs__put_node_revision(fs, new_noderev->id, new_noderev, FALSE,
-                                       pool));
-
-  *new_id_p = id;
-
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_fs_fs__set_proplist(svn_fs_t *fs,
-                        node_revision_t *noderev,
-                        apr_hash_t *proplist,
-                        apr_pool_t *pool)
-{
-  const char *filename
-    = svn_fs_fs__path_txn_node_props(fs, noderev->id, pool);
-  apr_file_t *file;
-  svn_stream_t *out;
-
-  /* Dump the property list to the mutable property file. */
-  SVN_ERR(svn_io_file_open(&file, filename,
-                           APR_WRITE | APR_CREATE | APR_TRUNCATE
-                           | APR_BUFFERED, APR_OS_DEFAULT, pool));
-  out = svn_stream_from_aprfile2(file, TRUE, pool);
-  SVN_ERR(svn_hash_write2(proplist, out, SVN_HASH_TERMINATOR, pool));
-  SVN_ERR(svn_io_file_close(file, pool));
-
-  /* Mark the node-rev's prop rep as mutable, if not already done. */
-  if (!noderev->prop_rep || !noderev->prop_rep->txn_id)
-    {
-      noderev->prop_rep = apr_pcalloc(pool, sizeof(*noderev->prop_rep));
-      noderev->prop_rep->txn_id = svn_fs_fs__id_txn_id(noderev->id);
-      SVN_ERR(svn_fs_fs__put_node_revision(fs, noderev->id, noderev, FALSE, pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Read the 'current' file for filesystem FS and store the next
-   available node id in *NODE_ID, and the next available copy id in
-   *COPY_ID.  Allocations are performed from POOL. */
-static svn_error_t *
-get_next_revision_ids(const char **node_id,
-                      const char **copy_id,
-                      svn_fs_t *fs,
-                      apr_pool_t *pool)
-{
-  char *buf;
-  char *str;
-  svn_stringbuf_t *content;
-
-  SVN_ERR(svn_fs_fs__read_content(&content,
-                                  svn_fs_fs__path_current(fs, pool),
-                                  pool));
-  buf = content->data;
-
-  str = svn_cstring_tokenize(" ", &buf);
-  if (! str)
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Corrupt 'current' file"));
-
-  str = svn_cstring_tokenize(" ", &buf);
-  if (! str)
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Corrupt 'current' file"));
-
-  *node_id = apr_pstrdup(pool, str);
-
-  str = svn_cstring_tokenize(" \n", &buf);
-  if (! str)
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Corrupt 'current' file"));
-
-  *copy_id = apr_pstrdup(pool, str);
-
-  return SVN_NO_ERROR;
-}
-
-/* This baton is used by the stream created for write_hash_rep. */
-struct write_hash_baton
-{
-  svn_stream_t *stream;
-
-  apr_size_t size;
-
-  svn_checksum_ctx_t *md5_ctx;
-  svn_checksum_ctx_t *sha1_ctx;
-};
-
-/* The handler for the write_hash_rep stream.  BATON is a
-   write_hash_baton, DATA has the data to write and *LEN is the number
-   of bytes to write. */
-static svn_error_t *
-write_hash_handler(void *baton,
-                   const char *data,
-                   apr_size_t *len)
-{
-  struct write_hash_baton *whb = baton;
-
-  SVN_ERR(svn_checksum_update(whb->md5_ctx, data, *len));
-  SVN_ERR(svn_checksum_update(whb->sha1_ctx, data, *len));
-
-  SVN_ERR(svn_stream_write(whb->stream, data, len));
-  whb->size += *len;
-
-  return SVN_NO_ERROR;
-}
-
-/* Write out the hash HASH as a text representation to file FILE.  In
-   the process, record position, the total size of the dump and MD5 as
-   well as SHA1 in REP.   If rep sharing has been enabled and REPS_HASH
-   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.  Perform temporary allocations in POOL. */
-static svn_error_t *
-write_hash_rep(representation_t *rep,
-               apr_file_t *file,
-               apr_hash_t *hash,
-               svn_fs_t *fs,
-               apr_hash_t *reps_hash,
-               apr_pool_t *pool)
-{
-  svn_stream_t *stream;
-  struct write_hash_baton *whb;
-  representation_t *old_rep;
-
-  SVN_ERR(svn_fs_fs__get_file_offset(&rep->offset, file, pool));
-
-  whb = apr_pcalloc(pool, sizeof(*whb));
-
-  whb->stream = svn_stream_from_aprfile2(file, TRUE, pool);
-  whb->size = 0;
-  whb->md5_ctx = svn_checksum_ctx_create(svn_checksum_md5, pool);
-  whb->sha1_ctx = svn_checksum_ctx_create(svn_checksum_sha1, pool);
-
-  stream = svn_stream_create(whb, pool);
-  svn_stream_set_write(stream, write_hash_handler);
-
-  SVN_ERR(svn_stream_puts(whb->stream, "PLAIN\n"));
-
-  SVN_ERR(svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool));
-
-  /* Store the results. */
-  SVN_ERR(svn_checksum_final(&rep->md5_checksum, whb->md5_ctx, pool));
-  SVN_ERR(svn_checksum_final(&rep->sha1_checksum, whb->sha1_ctx, pool));
-
-  /* Check and see if we already have a representation somewhere that's
-     identical to the one we just wrote out. */
-  SVN_ERR(get_shared_rep(&old_rep, fs, rep, reps_hash, pool));
-
-  if (old_rep)
-    {
-      /* We need to erase from the protorev the data we just wrote. */
-      SVN_ERR(svn_io_file_trunc(file, rep->offset, pool));
-
-      /* Use the old rep for this content. */
-      memcpy(rep, old_rep, sizeof (*rep));
-    }
-  else
-    {
-      /* Write out our cosmetic end marker. */
-      SVN_ERR(svn_stream_puts(whb->stream, "ENDREP\n"));
-
-      /* update the representation */
-      rep->size = whb->size;
-      rep->expanded_size = 0;
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Write out the hash HASH pertaining to the NODEREV in FS as a deltified
-   text representation to file FILE.  In the process, record the total size
-   and the md5 digest in REP.  If rep sharing has been enabled and REPS_HASH
-   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 PROPS is set, assume that we want to a props representation as
-   the base for our delta.  Perform temporary allocations in POOL. */
-static svn_error_t *
-write_hash_delta_rep(representation_t *rep,
-                     apr_file_t *file,
-                     apr_hash_t *hash,
-                     svn_fs_t *fs,
-                     node_revision_t *noderev,
-                     apr_hash_t *reps_hash,
-                     svn_boolean_t props,
-                     apr_pool_t *pool)
-{
-  svn_txdelta_window_handler_t diff_wh;
-  void *diff_whb;
-
-  svn_stream_t *file_stream;
-  svn_stream_t *stream;
-  representation_t *base_rep;
-  representation_t *old_rep;
-  svn_stream_t *source;
-  svn_fs_fs__rep_header_t header = { 0 };
-
-  apr_off_t rep_end = 0;
-  apr_off_t delta_start = 0;
-
-  struct write_hash_baton *whb;
-  fs_fs_data_t *ffd = fs->fsap_data;
-  int diff_version = ffd->format >= SVN_FS_FS__MIN_SVNDIFF1_FORMAT ? 1 : 0;
-
-  /* Get the base for this delta. */
-  SVN_ERR(choose_delta_base(&base_rep, fs, noderev, props, pool));
-  SVN_ERR(svn_fs_fs__get_contents(&source, fs, base_rep, pool));
-
-  SVN_ERR(svn_fs_fs__get_file_offset(&rep->offset, file, pool));
-
-  /* Write out the rep header. */
-  if (base_rep)
-    {
-      header.base_revision = base_rep->revision;
-      header.base_offset = base_rep->offset;
-      header.base_length = base_rep->size;
-      header.type = svn_fs_fs__rep_delta;
-    }
-  else
-    {
-      header.type = svn_fs_fs__rep_self_delta;
-    }
-
-  file_stream = svn_stream_from_aprfile2(file, TRUE, pool);
-  SVN_ERR(svn_fs_fs__write_rep_header(&header, file_stream, pool));
-  SVN_ERR(svn_fs_fs__get_file_offset(&delta_start, file, pool));
-
-  /* Prepare to write the svndiff data. */
-  svn_txdelta_to_svndiff3(&diff_wh,
-                          &diff_whb,
-                          file_stream,
-                          diff_version,
-                          SVN_DELTA_COMPRESSION_LEVEL_DEFAULT,
-                          pool);
-
-  whb = apr_pcalloc(pool, sizeof(*whb));
-  whb->stream = svn_txdelta_target_push(diff_wh, diff_whb, source, pool);
-  whb->size = 0;
-  whb->md5_ctx = svn_checksum_ctx_create(svn_checksum_md5, pool);
-  whb->sha1_ctx = svn_checksum_ctx_create(svn_checksum_sha1, pool);
-
-  /* serialize the hash */
-  stream = svn_stream_create(whb, pool);
-  svn_stream_set_write(stream, write_hash_handler);
-
-  SVN_ERR(svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool));
-  SVN_ERR(svn_stream_close(whb->stream));
-
-  /* Store the results. */
-  SVN_ERR(svn_checksum_final(&rep->md5_checksum, whb->md5_ctx, pool));
-  SVN_ERR(svn_checksum_final(&rep->sha1_checksum, whb->sha1_ctx, pool));
-
-  /* Check and see if we already have a representation somewhere that's
-     identical to the one we just wrote out. */
-  SVN_ERR(get_shared_rep(&old_rep, fs, rep, reps_hash, pool));
-
-  if (old_rep)
-    {
-      /* We need to erase from the protorev the data we just wrote. */
-      SVN_ERR(svn_io_file_trunc(file, rep->offset, pool));
-
-      /* Use the old rep for this content. */
-      memcpy(rep, old_rep, sizeof (*rep));
-    }
-  else
-    {
-      /* Write out our cosmetic end marker. */
-      SVN_ERR(svn_fs_fs__get_file_offset(&rep_end, file, pool));
-      SVN_ERR(svn_stream_puts(file_stream, "ENDREP\n"));
-
-      /* update the representation */
-      rep->expanded_size = whb->size;
-      rep->size = rep_end - delta_start;
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Sanity check ROOT_NODEREV, a candidate for being the root node-revision
-   of (not yet committed) revision REV in FS.  Use POOL for temporary
-   allocations.
-
-   If you change this function, consider updating svn_fs_fs__verify() too.
- */
-static svn_error_t *
-validate_root_noderev(svn_fs_t *fs,
-                      node_revision_t *root_noderev,
-                      svn_revnum_t rev,
-                      apr_pool_t *pool)
-{
-  svn_revnum_t head_revnum = rev-1;
-  int head_predecessor_count;
-
-  SVN_ERR_ASSERT(rev > 0);
-
-  /* Compute HEAD_PREDECESSOR_COUNT. */
-  {
-    svn_fs_root_t *head_revision;
-    const svn_fs_id_t *head_root_id;
-    node_revision_t *head_root_noderev;
-
-    /* Get /@HEAD's noderev. */
-    SVN_ERR(svn_fs_fs__revision_root(&head_revision, fs, head_revnum, pool));
-    SVN_ERR(svn_fs_fs__node_id(&head_root_id, head_revision, "/", pool));
-    SVN_ERR(svn_fs_fs__get_node_revision(&head_root_noderev, fs, head_root_id,
-                                         pool));
-
-    head_predecessor_count = head_root_noderev->predecessor_count;
-  }
-
-  /* Check that the root noderev's predecessor count equals REV.
-
-     This kind of corruption was seen on svn.apache.org (both on
-     the root noderev and on other fspaths' noderevs); see
-     issue #4129.
-
-     Normally (rev == root_noderev->predecessor_count), but here we
-     use a more roundabout check that should only trigger on new instances
-     of the corruption, rather then trigger on each and every new commit
-     to a repository that has triggered the bug somewhere in its root
-     noderev's history.
-   */
-  if (root_noderev->predecessor_count != -1
-      && (root_noderev->predecessor_count - head_predecessor_count)
-         != (rev - head_revnum))
-    {
-      return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                               _("predecessor count for "
-                                 "the root node-revision is wrong: "
-                                 "found (%d+%ld != %d), committing r%ld"),
-                                 head_predecessor_count,
-                                 rev - head_revnum, /* This is equal to 1. */
-                                 root_noderev->predecessor_count,
-                                 rev);
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Copy a node-revision specified by id ID in fileystem FS from a
-   transaction into the proto-rev-file FILE.  Set *NEW_ID_P to a
-   pointer to the new node-id which will be allocated in POOL.
-   If this is a directory, copy all children as well.
-
-   START_NODE_ID and START_COPY_ID are
-   the first available node and copy ids for this filesystem, for older
-   FS formats.
-
-   REV is the revision number that this proto-rev-file will represent.
-
-   INITIAL_OFFSET is the offset of the proto-rev-file on entry to
-   commit_body.
-
-   If REPS_TO_CACHE is not NULL, append to it a copy (allocated in
-   REPS_POOL) of each data rep that is new in this revision.
-
-   If REPS_HASH is not NULL, append copies (allocated in REPS_POOL)
-   of the representations of each property rep that is new in this
-   revision.
-
-   AT_ROOT is true if the node revision being written is the root
-   node-revision.  It is only controls additional sanity checking
-   logic.
-
-   Temporary allocations are also from POOL. */
-static svn_error_t *
-write_final_rev(const svn_fs_id_t **new_id_p,
-                apr_file_t *file,
-                svn_revnum_t rev,
-                svn_fs_t *fs,
-                const svn_fs_id_t *id,
-                const char *start_node_id,
-                const char *start_copy_id,
-                apr_off_t initial_offset,
-                apr_array_header_t *reps_to_cache,
-                apr_hash_t *reps_hash,
-                apr_pool_t *reps_pool,
-                svn_boolean_t at_root,
-                apr_pool_t *pool)
-{
-  node_revision_t *noderev;
-  apr_off_t my_offset;
-  char my_node_id_buf[MAX_KEY_SIZE + 2];
-  char my_copy_id_buf[MAX_KEY_SIZE + 2];
-  const svn_fs_id_t *new_id;
-  const char *node_id, *copy_id, *my_node_id, *my_copy_id;
-  fs_fs_data_t *ffd = fs->fsap_data;
-
-  *new_id_p = NULL;
-
-  /* Check to see if this is a transaction node. */
-  if (! svn_fs_fs__id_txn_id(id))
-    return SVN_NO_ERROR;
-
-  SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, id, pool));
-
-  if (noderev->kind == svn_node_dir)
-    {
-      apr_pool_t *subpool;
-      apr_hash_t *entries, *str_entries;
-      apr_array_header_t *sorted_entries;
-      int i;
-
-      /* This is a directory.  Write out all the children first. */
-      subpool = svn_pool_create(pool);
-
-      SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, noderev, pool));
-      /* For the sake of the repository administrator sort the entries
-         so that the final file is deterministic and repeatable,
-         however the rest of the FSFS code doesn't require any
-         particular order here. */
-      sorted_entries = svn_sort__hash(entries, svn_sort_compare_items_lexically,
-                                      pool);
-      for (i = 0; i < sorted_entries->nelts; ++i)
-        {
-          svn_fs_dirent_t *dirent = APR_ARRAY_IDX(sorted_entries, i,
-                                                  svn_sort__item_t).value;
-
-          svn_pool_clear(subpool);
-          SVN_ERR(write_final_rev(&new_id, file, rev, fs, dirent->id,
-                                  start_node_id, start_copy_id, initial_offset,

[... 919 lines stripped ...]