You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2015/11/30 11:24:23 UTC

svn commit: r1717223 [15/50] - in /subversion/branches/ra-git: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ contrib/hook-scripts/ notes/ notes/api-errata/1.9/ notes/move-tracking/ subversion/ subversion/bindings/ctypes-python/...

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/fs_fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/fs_fs.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/fs_fs.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/fs_fs.c Mon Nov 30 10:24:16 2015
@@ -622,8 +622,8 @@ svn_fs_fs__write_format(svn_fs_t *fs,
     }
   else
     {
-      SVN_ERR(svn_io_write_atomic(path, sb->data, sb->len,
-                                  NULL /* copy_perms_path */, pool));
+      SVN_ERR(svn_io_write_atomic2(path, sb->data, sb->len,
+                                   NULL /* copy_perms_path */, TRUE, pool));
     }
 
   /* And set the perms to make it read only */
@@ -754,8 +754,8 @@ read_config(fs_fs_data_t *ffd,
                                    CONFIG_SECTION_PACKED_REVPROPS,
                                    CONFIG_OPTION_REVPROP_PACK_SIZE,
                                    ffd->compress_packed_revprops
-                                       ? 0x10
-                                       : 0x4));
+                                       ? 0x40
+                                       : 0x10));
 
       ffd->revprop_pack_size *= 1024;
     }
@@ -962,9 +962,9 @@ write_config(svn_fs_t *fs,
 "### latency and CPU usage reading and changing individual revprops."        NL
 "### Values smaller than 4 kByte will not improve latency any further and "  NL
 "### quickly render revprop packing ineffective."                            NL
-"### revprop-pack-size is 4 kBytes by default for non-compressed revprop"    NL
-"### pack files and 16 kBytes when compression has been enabled."            NL
-"# " CONFIG_OPTION_REVPROP_PACK_SIZE " = 4"                                  NL
+"### revprop-pack-size is 16 kBytes by default for non-compressed revprop"   NL
+"### pack files and 64 kBytes when compression has been enabled."            NL
+"# " CONFIG_OPTION_REVPROP_PACK_SIZE " = 16"                                 NL
 "###"                                                                        NL
 "### To save disk space, packed revprop files may be compressed.  Standard"  NL
 "### revprops tend to allow for very effective compression.  Reading and"    NL
@@ -1126,7 +1126,9 @@ svn_fs_fs__open(svn_fs_t *fs, const char
   /* Global configuration options. */
   SVN_ERR(read_global_config(fs));
 
-  return get_youngest(&(ffd->youngest_rev_cache), fs, pool);
+  ffd->youngest_rev_cache = 0;
+
+  return SVN_NO_ERROR;
 }
 
 /* Wrapper around svn_io_file_create which ignores EEXIST. */
@@ -1377,41 +1379,31 @@ svn_fs_fs__file_length(svn_filesize_t *l
       /* Treat "no representation" as "empty file". */
       *length = 0;
     }
-  else if (data_rep->expanded_size)
+  else
     {
-      /* Standard case: a non-empty file. */
       *length = data_rep->expanded_size;
     }
-  else
-    {
-      /* Work around a FSFS format quirk (see issue #4554).
 
-         A plain representation may specify its EXPANDED LENGTH as "0"
-         in which case, the SIZE value is what we want.
+  return SVN_NO_ERROR;
+}
+
+svn_boolean_t
+svn_fs_fs__noderev_same_rep_key(representation_t *a,
+                                representation_t *b)
+{
+  if (a == b)
+    return TRUE;
+
+  if (a == NULL || b == NULL)
+    return FALSE;
 
-         Because EXPANDED_LENGTH will also be 0 for empty files, while
-         SIZE is non-null, we need to check wether the content is
-         actually empty.  We simply compare with the MD5 checksum of
-         empty content (sha-1 is not always available).
-       */
-      svn_checksum_t *empty_md5
-        = svn_checksum_empty_checksum(svn_checksum_md5, pool);
+  if (a->item_index != b->item_index)
+    return FALSE;
 
-      if (memcmp(empty_md5->digest, data_rep->md5_digest,
-                 sizeof(data_rep->md5_digest)))
-        {
-          /* Contents is not empty, i.e. EXPANDED_LENGTH cannot be the
-             actual file length. */
-          *length = data_rep->size;
-        }
-      else
-        {
-          /* Contents is empty. */
-          *length = 0;
-        }
-    }
+  if (a->revision != b->revision)
+    return FALSE;
 
-  return SVN_NO_ERROR;
+  return memcmp(&a->uniquifier, &b->uniquifier, sizeof(a->uniquifier)) == 0;
 }
 
 svn_error_t *
@@ -1419,14 +1411,13 @@ svn_fs_fs__file_text_rep_equal(svn_boole
                                svn_fs_t *fs,
                                node_revision_t *a,
                                node_revision_t *b,
-                               svn_boolean_t strict,
                                apr_pool_t *scratch_pool)
 {
   svn_stream_t *contents_a, *contents_b;
   representation_t *rep_a = a->data_rep;
   representation_t *rep_b = b->data_rep;
-  svn_boolean_t a_empty = !rep_a || rep_a->expanded_size == 0;
-  svn_boolean_t b_empty = !rep_b || rep_b->expanded_size == 0;
+  svn_boolean_t a_empty = !rep_a;
+  svn_boolean_t b_empty = !rep_b;
 
   /* This makes sure that neither rep will be NULL later on */
   if (a_empty && b_empty)
@@ -1464,19 +1455,6 @@ svn_fs_fs__file_text_rep_equal(svn_boole
       return SVN_NO_ERROR;
     }
 
-  /* Old repositories may not have the SHA1 checksum handy.
-     This check becomes expensive.  Skip it unless explicitly required.
-
-     We already have seen that the ID is different, so produce a likely
-     false negative as allowed by the API description - even though the
-     MD5 matched, there is an extremely slim chance that the SHA1 wouldn't.
-   */
-  if (!strict)
-    {
-      *equal = FALSE;
-      return SVN_NO_ERROR;
-    }
-
   SVN_ERR(svn_fs_fs__get_contents(&contents_a, fs, rep_a, TRUE,
                                   scratch_pool));
   SVN_ERR(svn_fs_fs__get_contents(&contents_b, fs, rep_b, TRUE,
@@ -1492,7 +1470,6 @@ svn_fs_fs__prop_rep_equal(svn_boolean_t
                           svn_fs_t *fs,
                           node_revision_t *a,
                           node_revision_t *b,
-                          svn_boolean_t strict,
                           apr_pool_t *scratch_pool)
 {
   representation_t *rep_a = a->prop_rep;
@@ -1512,28 +1489,33 @@ svn_fs_fs__prop_rep_equal(svn_boolean_t
       && !svn_fs_fs__id_txn_used(&rep_a->txn_id)
       && !svn_fs_fs__id_txn_used(&rep_b->txn_id))
     {
-      /* MD5 must be given. Having the same checksum is good enough for
-         accepting the prop lists as equal. */
-      *equal = memcmp(rep_a->md5_digest, rep_b->md5_digest,
-                      sizeof(rep_a->md5_digest)) == 0;
-      return SVN_NO_ERROR;
+      /* Same representation? */
+      if (   (rep_a->revision == rep_b->revision)
+          && (rep_a->item_index == rep_b->item_index))
+        {
+          *equal = TRUE;
+          return SVN_NO_ERROR;
+        }
+
+      /* Known different content? MD5 must be given. */
+      if (memcmp(rep_a->md5_digest, rep_b->md5_digest,
+                 sizeof(rep_a->md5_digest)))
+        {
+          *equal = FALSE;
+          return SVN_NO_ERROR;
+        }
     }
 
-  /* Same path in same txn? */
+  /* Same path in same txn?
+   *
+   * For committed reps, IDs cannot be the same here b/c we already know
+   * that they point to different representations. */
   if (svn_fs_fs__id_eq(a->id, b->id))
     {
       *equal = TRUE;
       return SVN_NO_ERROR;
     }
 
-  /* Skip the expensive bits unless we are in strict mode.
-     Simply assume that there is a difference. */
-  if (!strict)
-    {
-      *equal = FALSE;
-      return SVN_NO_ERROR;
-    }
-
   /* At least one of the reps has been modified in a txn.
      Fetch and compare them. */
   SVN_ERR(svn_fs_fs__get_proplist(&proplist_a, fs, a, scratch_pool));
@@ -1884,9 +1866,9 @@ svn_fs_fs__set_uuid(svn_fs_t *fs,
 
   /* We use the permissions of the 'current' file, because the 'uuid'
      file does not exist during repository creation. */
-  SVN_ERR(svn_io_write_atomic(uuid_path, contents->data, contents->len,
-                              svn_fs_fs__path_current(fs, pool) /* perms */,
-                              pool));
+  SVN_ERR(svn_io_write_atomic2(uuid_path, contents->data, contents->len,
+                               svn_fs_fs__path_current(fs, pool) /* perms */,
+                               TRUE, pool));
 
   fs->uuid = apr_pstrdup(fs->pool, uuid);
 
@@ -1944,7 +1926,10 @@ get_node_origins_from_file(svn_fs_t *fs,
 
   stream = svn_stream_from_aprfile2(fd, FALSE, pool);
   *node_origins = apr_hash_make(pool);
-  SVN_ERR(svn_hash_read2(*node_origins, stream, SVN_HASH_TERMINATOR, pool));
+  err = svn_hash_read2(*node_origins, stream, SVN_HASH_TERMINATOR, pool);
+  if (err)
+    return svn_error_quick_wrapf(err, _("malformed node origin data in '%s'"),
+                                 node_origins_file);
   return svn_stream_close(stream);
 }
 
@@ -2034,7 +2019,7 @@ set_node_origins_for_file(svn_fs_t *fs,
   SVN_ERR(svn_stream_close(stream));
 
   /* Rename the temp file as the real destination */
-  return svn_io_file_rename(path_tmp, node_origins_path, pool);
+  return svn_io_file_rename2(path_tmp, node_origins_path, FALSE, pool);
 }
 
 
@@ -2069,14 +2054,17 @@ svn_fs_fs__revision_prop(svn_string_t **
                          svn_fs_t *fs,
                          svn_revnum_t rev,
                          const char *propname,
-                         apr_pool_t *pool)
+                         svn_boolean_t refresh,
+                         apr_pool_t *result_pool,
+                         apr_pool_t *scratch_pool)
 {
   apr_hash_t *table;
 
   SVN_ERR(svn_fs__check_fs(fs, TRUE));
-  SVN_ERR(svn_fs_fs__get_revision_proplist(&table, fs, rev, pool));
+  SVN_ERR(svn_fs_fs__get_revision_proplist(&table, fs, rev, refresh,
+                                           scratch_pool, scratch_pool));
 
-  *value_p = svn_hash_gets(table, propname);
+  *value_p = svn_string_dup(svn_hash_gets(table, propname), result_pool);
 
   return SVN_NO_ERROR;
 }
@@ -2099,13 +2087,17 @@ change_rev_prop_body(void *baton, apr_po
 {
   struct change_rev_prop_baton *cb = baton;
   apr_hash_t *table;
+  const svn_string_t *present_value;
 
-  SVN_ERR(svn_fs_fs__get_revision_proplist(&table, cb->fs, cb->rev, pool));
+  /* We always need to read the current revprops from disk.
+   * Hence, always "refresh" here. */
+  SVN_ERR(svn_fs_fs__get_revision_proplist(&table, cb->fs, cb->rev, TRUE,
+                                           pool, pool));
+  present_value = svn_hash_gets(table, cb->name);
 
   if (cb->old_value_p)
     {
       const svn_string_t *wanted_value = *cb->old_value_p;
-      const svn_string_t *present_value = svn_hash_gets(table, cb->name);
       if ((!wanted_value != !present_value)
           || (wanted_value && present_value
               && !svn_string_compare(wanted_value, present_value)))
@@ -2118,6 +2110,13 @@ change_rev_prop_body(void *baton, apr_po
         }
       /* Fall through. */
     }
+
+  /* If the prop-set is a no-op, skip the actual write. */
+  if ((!present_value && !cb->value)
+      || (present_value && cb->value
+          && svn_string_compare(present_value, cb->value)))
+    return SVN_NO_ERROR;
+
   svn_hash_sets(table, cb->name, cb->value);
 
   return svn_fs_fs__set_revision_proplist(cb->fs, cb->rev, table, pool);

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/fs_fs.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/fs_fs.h?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/fs_fs.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/fs_fs.h Mon Nov 30 10:24:16 2015
@@ -39,6 +39,15 @@ svn_error_t *svn_fs_fs__open(svn_fs_t *f
                              const char *path,
                              apr_pool_t *pool);
 
+/* Initialize parts of the FS data that are being shared across multiple
+   filesystem objects.  Use COMMON_POOL for process-wide and POOL for
+   temporary allocations.  Use COMMON_POOL_LOCK to ensure that the
+   initialization is serialized. */
+svn_error_t *svn_fs_fs__initialize_shared_data(svn_fs_t *fs,
+                                               svn_mutex__t *common_pool_lock,
+                                               apr_pool_t *pool,
+                                               apr_pool_t *common_pool);
+
 /* Upgrade the fsfs filesystem FS.  Indicate progress via the optional
  * NOTIFY_FUNC callback using NOTIFY_BATON.  The optional CANCEL_FUNC
  * will periodically be called with CANCEL_BATON to allow for preemption.
@@ -81,28 +90,29 @@ svn_error_t *svn_fs_fs__file_length(svn_
                                     node_revision_t *noderev,
                                     apr_pool_t *pool);
 
+/* Return TRUE if the representation keys in A and B both point to the
+   same representation, else return FALSE. */
+svn_boolean_t svn_fs_fs__noderev_same_rep_key(representation_t *a,
+                                              representation_t *b);
+
 /* Set *EQUAL to TRUE if the text representations in A and B within FS
-   have equal contents, else set it to FALSE.  If STRICT is not set, allow
-   for false negatives.
+   have equal contents, else set it to FALSE.
    Use SCRATCH_POOL for temporary allocations. */
 svn_error_t *
 svn_fs_fs__file_text_rep_equal(svn_boolean_t *equal,
                                svn_fs_t *fs,
                                node_revision_t *a,
                                node_revision_t *b,
-                               svn_boolean_t strict,
                                apr_pool_t *scratch_pool);
 
 /* Set *EQUAL to TRUE if the property representations in A and B within FS
-   have equal contents, else set it to FALSE.  If STRICT is not set, allow
-   for false negatives.
+   have equal contents, else set it to FALSE.
    Use SCRATCH_POOL for temporary allocations. */
 svn_error_t *
 svn_fs_fs__prop_rep_equal(svn_boolean_t *equal,
                           svn_fs_t *fs,
                           node_revision_t *a,
                           node_revision_t *b,
-                          svn_boolean_t strict,
                           apr_pool_t *scratch_pool);
 
 
@@ -215,13 +225,16 @@ svn_fs_fs__with_all_locks(svn_fs_t *fs,
                           void *baton,
                           apr_pool_t *pool);
 
-/* Find the value of the property named PROPNAME in transaction TXN.
+/* Find the value of the property named PROPNAME in revision REV.
    Return the contents in *VALUE_P.  The contents will be allocated
-   from POOL. */
+   from RESULT_POOL and SCRATCH_POOL is used for temporaries.
+   Invalidate any revprop cache is REFRESH is set. */
 svn_error_t *svn_fs_fs__revision_prop(svn_string_t **value_p, svn_fs_t *fs,
                                       svn_revnum_t rev,
                                       const char *propname,
-                                      apr_pool_t *pool);
+                                      svn_boolean_t refresh,
+                                      apr_pool_t *result_pool,
+                                      apr_pool_t *scratch_pool);
 
 /* Change, add, or delete a property on a revision REV in filesystem
    FS.  NAME gives the name of the property, and value, if non-NULL,

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/hotcopy.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/hotcopy.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/hotcopy.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/hotcopy.c Mon Nov 30 10:24:16 2015
@@ -982,6 +982,10 @@ hotcopy_body(void *baton, apr_pool_t *po
       if (kind == svn_node_file)
         {
           SVN_ERR(svn_sqlite__hotcopy(src_subdir, dst_subdir, pool));
+
+          /* The source might have r/o flags set on it - which would be
+             carried over to the copy. */
+          SVN_ERR(svn_io_set_file_read_write(dst_subdir, FALSE, pool));
           SVN_ERR(svn_fs_fs__del_rep_reference(dst_fs, src_youngest, pool));
         }
     }
@@ -991,65 +995,33 @@ hotcopy_body(void *baton, apr_pool_t *po
     SVN_ERR(svn_io_dir_file_copy(src_fs->path, dst_fs->path,
                                  PATH_TXN_CURRENT, pool));
 
-  return SVN_NO_ERROR;
-}
-
-/* Wrapper around hotcopy_body taking out all necessary source repository
- * locks.
- */
-static svn_error_t *
-hotcopy_locking_src_body(void *baton, apr_pool_t *pool)
-{
-  struct hotcopy_body_baton *hbb = baton;
-  fs_fs_data_t *src_ffd = hbb->src_fs->fsap_data;
+  /* Hotcopied FS is complete. Stamp it with a format file. */
+  SVN_ERR(svn_fs_fs__write_format(dst_fs, TRUE, pool));
 
-  return src_ffd->format >= SVN_FS_FS__MIN_PACK_LOCK_FORMAT
-    ? svn_error_trace(svn_fs_fs__with_pack_lock(hbb->src_fs, hotcopy_body,
-                                                baton, pool))
-    : hotcopy_body(baton, pool);
+  return SVN_NO_ERROR;
 }
 
-/* Create an empty filesystem at DST_FS at DST_PATH with the same
- * configuration as SRC_FS (uuid, format, and other parameters).
- * After creation DST_FS has no revisions, not even revision zero. */
-static svn_error_t *
-hotcopy_create_empty_dest(svn_fs_t *src_fs,
-                          svn_fs_t *dst_fs,
-                          const char *dst_path,
-                          apr_pool_t *pool)
+svn_error_t *
+svn_fs_fs__hotcopy(svn_fs_t *src_fs,
+                   svn_fs_t *dst_fs,
+                   const char *src_path,
+                   const char *dst_path,
+                   svn_boolean_t incremental,
+                   svn_fs_hotcopy_notify_t notify_func,
+                   void *notify_baton,
+                   svn_cancel_func_t cancel_func,
+                   void *cancel_baton,
+                   svn_mutex__t *common_pool_lock,
+                   apr_pool_t *pool,
+                   apr_pool_t *common_pool)
 {
-  fs_fs_data_t *src_ffd = src_fs->fsap_data;
+  struct hotcopy_body_baton hbb;
 
-  /* Create the DST_FS repository with the same layout as SRC_FS. */
-  SVN_ERR(svn_fs_fs__create_file_tree(dst_fs, dst_path, src_ffd->format,
-                                      src_ffd->max_files_per_dir,
-                                      src_ffd->use_log_addressing,
-                                      pool));
-
-  /* Copy the UUID.  Hotcopy destination receives a new instance ID, but
-   * has the same filesystem UUID as the source. */
-  SVN_ERR(svn_fs_fs__set_uuid(dst_fs, src_fs->uuid, NULL, pool));
-
-  /* Remove revision 0 contents.  Otherwise, it may not get overwritten
-   * due to having a newer timestamp. */
-  SVN_ERR(hotcopy_remove_file(svn_fs_fs__path_rev(dst_fs, 0, pool), pool));
-  SVN_ERR(hotcopy_remove_file(svn_fs_fs__path_revprops(dst_fs, 0, pool),
-                              pool));
-
-  /* This filesystem is ready.  Stamp it with a format number.  Fail if
-   * the 'format' file should already exist. */
-  SVN_ERR(svn_fs_fs__write_format(dst_fs, FALSE, pool));
+  if (cancel_func)
+    SVN_ERR(cancel_func(cancel_baton));
 
-  return SVN_NO_ERROR;
-}
+  SVN_ERR(svn_fs_fs__open(src_fs, src_path, pool));
 
-svn_error_t *
-svn_fs_fs__hotcopy_prepare_target(svn_fs_t *src_fs,
-                                  svn_fs_t *dst_fs,
-                                  const char *dst_path,
-                                  svn_boolean_t incremental,
-                                  apr_pool_t *pool)
-{
   if (incremental)
     {
       const char *dst_format_abspath;
@@ -1061,39 +1033,51 @@ svn_fs_fs__hotcopy_prepare_target(svn_fs
       SVN_ERR(svn_io_check_path(dst_format_abspath, &dst_format_kind, pool));
       if (dst_format_kind == svn_node_none)
         {
-          /* Destination doesn't exist yet. Perform a normal hotcopy to a
-           * empty destination using the same configuration as the source. */
-          SVN_ERR(hotcopy_create_empty_dest(src_fs, dst_fs, dst_path, pool));
-        }
-      else
-        {
-          /* Check the existing repository. */
-          SVN_ERR(svn_fs_fs__open(dst_fs, dst_path, pool));
-          SVN_ERR(hotcopy_incremental_check_preconditions(src_fs, dst_fs,
-                                                          pool));
+          /* No destination?  Fallback to a non-incremental hotcopy. */
+          incremental = FALSE;
         }
     }
+
+  if (incremental)
+    {
+      /* Check the existing repository. */
+      SVN_ERR(svn_fs_fs__open(dst_fs, dst_path, pool));
+      SVN_ERR(hotcopy_incremental_check_preconditions(src_fs, dst_fs, pool));
+
+      SVN_ERR(svn_fs_fs__initialize_shared_data(dst_fs, common_pool_lock,
+                                                pool, common_pool));
+      SVN_ERR(svn_fs_fs__initialize_caches(dst_fs, pool));
+    }
   else
     {
       /* Start out with an empty destination using the same configuration
        * as the source. */
-      SVN_ERR(hotcopy_create_empty_dest(src_fs, dst_fs, dst_path, pool));
+      fs_fs_data_t *src_ffd = src_fs->fsap_data;
+
+      /* Create the DST_FS repository with the same layout as SRC_FS. */
+      SVN_ERR(svn_fs_fs__create_file_tree(dst_fs, dst_path, src_ffd->format,
+                                          src_ffd->max_files_per_dir,
+                                          src_ffd->use_log_addressing,
+                                          pool));
+
+      /* Copy the UUID.  Hotcopy destination receives a new instance ID, but
+       * has the same filesystem UUID as the source. */
+      SVN_ERR(svn_fs_fs__set_uuid(dst_fs, src_fs->uuid, NULL, pool));
+
+      /* Remove revision 0 contents.  Otherwise, it may not get overwritten
+       * due to having a newer timestamp. */
+      SVN_ERR(hotcopy_remove_file(svn_fs_fs__path_rev(dst_fs, 0, pool),
+                                  pool));
+      SVN_ERR(hotcopy_remove_file(svn_fs_fs__path_revprops(dst_fs, 0, pool),
+                                  pool));
+
+      SVN_ERR(svn_fs_fs__initialize_shared_data(dst_fs, common_pool_lock,
+                                                pool, common_pool));
+      SVN_ERR(svn_fs_fs__initialize_caches(dst_fs, pool));
     }
 
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_fs_fs__hotcopy(svn_fs_t *src_fs,
-                   svn_fs_t *dst_fs,
-                   svn_boolean_t incremental,
-                   svn_fs_hotcopy_notify_t notify_func,
-                   void *notify_baton,
-                   svn_cancel_func_t cancel_func,
-                   void *cancel_baton,
-                   apr_pool_t *pool)
-{
-  struct hotcopy_body_baton hbb;
+  if (cancel_func)
+    SVN_ERR(cancel_func(cancel_baton));
 
   hbb.src_fs = src_fs;
   hbb.dst_fs = dst_fs;
@@ -1102,8 +1086,15 @@ svn_fs_fs__hotcopy(svn_fs_t *src_fs,
   hbb.notify_baton = notify_baton;
   hbb.cancel_func = cancel_func;
   hbb.cancel_baton = cancel_baton;
-  SVN_ERR(svn_fs_fs__with_all_locks(dst_fs, hotcopy_locking_src_body, &hbb,
-                                    pool));
+
+  /* Lock the destination in the incremental mode.  For a non-incremental
+   * hotcopy, don't take any locks.  In that case the destination cannot be
+   * opened until the hotcopy finishes, and we don't have to worry about
+   * concurrency. */
+  if (incremental)
+    SVN_ERR(svn_fs_fs__with_all_locks(dst_fs, hotcopy_body, &hbb, pool));
+  else
+    SVN_ERR(hotcopy_body(&hbb, pool));
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/hotcopy.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/hotcopy.h?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/hotcopy.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/hotcopy.h Mon Nov 30 10:24:16 2015
@@ -25,27 +25,23 @@
 
 #include "fs.h"
 
-/* Create an empty copy of the fsfs filesystem SRC_FS into a new DST_FS at
- * DST_PATH.  If INCREMENTAL is TRUE, perform a few pre-checks only if
- * a repo already exists at DST_PATH. Use POOL for temporary allocations. */
-svn_error_t *
-svn_fs_fs__hotcopy_prepare_target(svn_fs_t *src_fs,
-                                  svn_fs_t *dst_fs,
-                                  const char *dst_path,
-                                  svn_boolean_t incremental,
-                                  apr_pool_t *pool);
-
-/* Copy the fsfs filesystem SRC_FS into DST_FS. If INCREMENTAL is TRUE, do
- * not re-copy data which already exists in DST_FS.  Indicate progress via
- * the optional NOTIFY_FUNC callback using NOTIFY_BATON.  Use POOL for
- * temporary allocations. */
+/* Copy the fsfs filesystem SRC_FS at SRC_PATH into a new copy DST_FS at
+ * DST_PATH.  If INCREMENTAL is TRUE, do not re-copy data which already
+ * exists in DST_FS.  Indicate progress via the optional NOTIFY_FUNC
+ * callback using NOTIFY_BATON.  Use COMMON_POOL for process-wide and
+ * POOL for temporary allocations.  Use COMMON_POOL_LOCK to ensure
+ * that the initialization of the shared data is serialized. */
 svn_error_t * svn_fs_fs__hotcopy(svn_fs_t *src_fs,
                                  svn_fs_t *dst_fs,
+                                 const char *src_path,
+                                 const char *dst_path,
                                  svn_boolean_t incremental,
                                  svn_fs_hotcopy_notify_t notify_func,
                                  void *notify_baton,
                                  svn_cancel_func_t cancel_func,
                                  void *cancel_baton,
-                                 apr_pool_t *pool);
+                                 svn_mutex__t *common_pool_lock,
+                                 apr_pool_t *pool,
+                                 apr_pool_t *common_pool);
 
 #endif

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/id.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/id.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/id.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/id.c Mon Nov 30 10:24:16 2015
@@ -365,14 +365,21 @@ svn_fs_fs__id_check_related(const svn_fs
   if (a == b)
     return TRUE;
 
-  /* If both node_ids start with _ and they have differing transaction
-     IDs, then it is impossible for them to be related. */
-  if (id_a->private_id.node_id.revision == SVN_INVALID_REVNUM)
+  /* If both node_ids have been created within _different_ transactions
+     (and are still uncommitted), then it is impossible for them to be
+     related.
+
+     Due to our txn-local temporary IDs, however, they might have been
+     given the same temporary node ID.  We need to detect that case.
+   */
+  if (   id_a->private_id.node_id.revision == SVN_INVALID_REVNUM
+      && id_b->private_id.node_id.revision == SVN_INVALID_REVNUM)
     {
-      if (   !svn_fs_fs__id_part_eq(&id_a->private_id.txn_id,
-                                    &id_b->private_id.txn_id)
-          || !svn_fs_fs__id_txn_used(&id_a->private_id.txn_id))
+      if (!svn_fs_fs__id_part_eq(&id_a->private_id.txn_id,
+                                 &id_b->private_id.txn_id))
         return FALSE;
+
+      /* At this point, matching node_ids implies relatedness. */
     }
 
   return svn_fs_fs__id_part_eq(&id_a->private_id.node_id,
@@ -385,7 +392,7 @@ svn_fs_fs__id_compare(const svn_fs_id_t
                       const svn_fs_id_t *b)
 {
   if (svn_fs_fs__id_eq(a, b))
-    return svn_fs_node_same;
+    return svn_fs_node_unchanged;
   return (svn_fs_fs__id_check_related(a, b) ? svn_fs_node_common_ancestor
                                             : svn_fs_node_unrelated);
 }
@@ -603,7 +610,9 @@ svn_fs_fs__id_serialize(svn_temp_seriali
   if (id == NULL)
     return;
 
-  /* serialize the id data struct itself */
+  /* Serialize the id data struct itself.
+   * Note that the structure behind IN is actually larger than a mere
+   * svn_fs_id_t . */
   svn_temp_serializer__add_leaf(context,
                                 (const void * const *)in,
                                 sizeof(fs_fs__id_t));

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/index.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/index.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/index.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/index.c Mon Nov 30 10:24:16 2015
@@ -251,7 +251,7 @@ static svn_error_t *
 packed_stream_read(svn_fs_fs__packed_number_stream_t *stream)
 {
   unsigned char buffer[MAX_NUMBER_PREFETCH];
-  apr_size_t read = 0;
+  apr_size_t bytes_read = 0;
   apr_size_t i;
   value_position_pair_t *target;
   apr_off_t block_start = 0;
@@ -273,33 +273,34 @@ packed_stream_read(svn_fs_fs__packed_num
    * boundaries.  This shall prevent jumping back and forth between two
    * blocks because the extra data was not actually request _now_.
    */
-  read = sizeof(buffer);
+  bytes_read = sizeof(buffer);
   block_left = stream->block_size - (stream->next_offset - block_start);
-  if (block_left >= 10 && block_left < read)
-    read = (apr_size_t)block_left;
+  if (block_left >= 10 && block_left < bytes_read)
+    bytes_read = (apr_size_t)block_left;
 
   /* Don't read beyond the end of the file section that belongs to this
    * index / stream. */
-  read = (apr_size_t)MIN(read, stream->stream_end - stream->next_offset);
+  bytes_read = (apr_size_t)MIN(bytes_read,
+                               stream->stream_end - stream->next_offset);
 
-  err = apr_file_read(stream->file, buffer, &read);
+  err = apr_file_read(stream->file, buffer, &bytes_read);
   if (err && !APR_STATUS_IS_EOF(err))
     return stream_error_create(stream, err,
       _("Can't read index file '%s' at offset 0x%s"));
 
   /* if the last number is incomplete, trim it from the buffer */
-  while (read > 0 && buffer[read-1] >= 0x80)
-    --read;
+  while (bytes_read > 0 && buffer[bytes_read-1] >= 0x80)
+    --bytes_read;
 
   /* we call read() only if get() requires more data.  So, there must be
    * at least *one* further number. */
-  if SVN__PREDICT_FALSE(read == 0)
+  if SVN__PREDICT_FALSE(bytes_read == 0)
     return stream_error_create(stream, err,
       _("Unexpected end of index file %s at offset 0x%s"));
 
   /* parse file buffer and expand into stream buffer */
   target = stream->buffer;
-  for (i = 0; i < read;)
+  for (i = 0; i < bytes_read;)
     {
       if (buffer[i] < 0x80)
         {
@@ -558,13 +559,13 @@ read_uint64_from_proto_index(apr_file_t
                              apr_pool_t *scratch_pool)
 {
   apr_byte_t buffer[sizeof(*value_p)];
-  apr_size_t read;
+  apr_size_t bytes_read;
 
   /* Read the full 8 bytes or our 64 bit value, unless we hit EOF.
    * Assert that we never read partial values. */
   SVN_ERR(svn_io_file_read_full2(proto_index, buffer, sizeof(buffer),
-                                 &read, eof, scratch_pool));
-  SVN_ERR_ASSERT((eof && *eof) || read == sizeof(buffer));
+                                 &bytes_read, eof, scratch_pool));
+  SVN_ERR_ASSERT((eof && *eof) || bytes_read == sizeof(buffer));
 
   /* If we did not hit EOF, reconstruct the uint64 value and return it. */
   if (!eof || !*eof)
@@ -3207,18 +3208,11 @@ svn_fs_fs__l2p_index_from_p2l_entries(co
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
   int i;
   svn_revnum_t last_revision = SVN_INVALID_REVNUM;
-  svn_revnum_t revision = SVN_INVALID_REVNUM;
 
   /* L2P index must be written in revision order.
    * Sort ENTRIES accordingly. */
   svn_sort__array(entries, compare_p2l_entry_revision);
 
-  /* Find the first revision in the index
-   * (must exist since no truly empty revs are allowed). */
-  for (i = 0; i < entries->nelts && !SVN_IS_VALID_REVNUM(revision); ++i)
-    revision = APR_ARRAY_IDX(entries, i, const svn_fs_fs__p2l_entry_t *)
-               ->item.revision;
-
   /* Create the temporary proto-rev file. */
   SVN_ERR(svn_io_open_unique_file3(NULL, protoname, NULL,
                                    svn_io_file_del_on_pool_cleanup,

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/load-index.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/load-index.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/load-index.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/load-index.c Mon Nov 30 10:24:16 2015
@@ -23,53 +23,152 @@
 #include "svn_pools.h"
 
 #include "private/svn_fs_fs_private.h"
+#include "private/svn_sorts_private.h"
 
 #include "index.h"
 #include "util.h"
 #include "transaction.h"
 
+/* From the ENTRIES array of svn_fs_fs__p2l_entry_t*, sorted by offset,
+ * return the first offset behind the last item. */
+static apr_off_t
+get_max_covered(apr_array_header_t *entries)
+{
+  const svn_fs_fs__p2l_entry_t *entry;
+  if (entries->nelts == 0)
+    return -1;
+
+  entry = APR_ARRAY_IDX(entries, entries->nelts - 1,
+                        const svn_fs_fs__p2l_entry_t *);
+  return entry->offset + entry->size;
+}
+
+/* Make sure that the svn_fs_fs__p2l_entry_t* in ENTRIES are consecutive
+ * and non-overlapping.  Use SCRATCH_POOL for temporaries. */
+static svn_error_t *
+check_all_covered(apr_array_header_t *entries,
+                  apr_pool_t *scratch_pool)
+{
+  int i;
+  apr_off_t expected = 0;
+  for (i = 0; i < entries->nelts; ++i)
+    {
+      const svn_fs_fs__p2l_entry_t *entry
+        = APR_ARRAY_IDX(entries, i, const svn_fs_fs__p2l_entry_t *);
+
+      if (entry->offset < expected)
+        return svn_error_createf(SVN_ERR_INVALID_INPUT, NULL,
+                                 "Overlapping index data for offset %s",
+                                 apr_psprintf(scratch_pool,
+                                              "%" APR_UINT64_T_HEX_FMT,
+                                              (apr_uint64_t)expected));
+
+      if (entry->offset > expected)
+        return svn_error_createf(SVN_ERR_INVALID_INPUT, NULL,
+                                 "Missing index data for offset %s",
+                                 apr_psprintf(scratch_pool,
+                                              "%" APR_UINT64_T_HEX_FMT,
+                                              (apr_uint64_t)expected));
+
+      expected = entry->offset + entry->size;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* A svn_sort__array compatible comparator function, sorting the
+ * svn_fs_fs__p2l_entry_t** given in LHS, RHS by offset. */
+static int
+compare_p2l_entry_revision(const void *lhs,
+                           const void *rhs)
+{
+  const svn_fs_fs__p2l_entry_t *lhs_entry
+    =*(const svn_fs_fs__p2l_entry_t **)lhs;
+  const svn_fs_fs__p2l_entry_t *rhs_entry
+    =*(const svn_fs_fs__p2l_entry_t **)rhs;
+
+  if (lhs_entry->offset < rhs_entry->offset)
+    return -1;
+
+  return lhs_entry->offset == rhs_entry->offset ? 0 : 1;
+}
+
 svn_error_t *
 svn_fs_fs__load_index(svn_fs_t *fs,
                       svn_revnum_t revision,
                       apr_array_header_t *entries,
                       apr_pool_t *scratch_pool)
 {
-  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  apr_pool_t *subpool = svn_pool_create(scratch_pool);
 
   /* Check the FS format number. */
   if (! svn_fs_fs__use_log_addressing(fs))
     return svn_error_create(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL, NULL);
 
+  /* P2L index must be written in offset order.
+   * Sort ENTRIES accordingly. */
+  svn_sort__array(entries, compare_p2l_entry_revision);
+
   /* Treat an empty array as a no-op instead error. */
   if (entries->nelts != 0)
     {
       const char *l2p_proto_index;
       const char *p2l_proto_index;
       svn_fs_fs__revision_file_t *rev_file;
+      svn_error_t *err;
+      apr_off_t max_covered = get_max_covered(entries);
+
+      /* Ensure that the index data is complete. */
+      SVN_ERR(check_all_covered(entries, scratch_pool));
 
       /* Open rev / pack file & trim indexes + footer off it. */
       SVN_ERR(svn_fs_fs__open_pack_or_rev_file_writable(&rev_file, fs,
-                                                        revision, iterpool,
-                                                        iterpool));
-      SVN_ERR(svn_fs_fs__auto_read_footer(rev_file));
-      SVN_ERR(svn_io_file_trunc(rev_file->file, rev_file->l2p_offset,
-                                iterpool));
+                                                        revision, subpool,
+                                                        subpool));
+
+      /* Remove the existing index info. */
+      err = svn_fs_fs__auto_read_footer(rev_file);
+      if (err)
+        {
+          /* Even the index footer cannot be read, even less be trusted.
+           * Take the range of valid data from the new index data. */
+          svn_error_clear(err);
+          SVN_ERR(svn_io_file_trunc(rev_file->file, max_covered,
+                                    subpool));
+        }
+      else
+        {
+          /* We assume that the new index data covers all contents.
+           * Error out if it doesn't.  The user can always truncate
+           * the file themselves. */
+          if (max_covered != rev_file->l2p_offset)
+            return svn_error_createf(SVN_ERR_INVALID_INPUT, NULL,
+                       "New index data ends at %s, old index ended at %s",
+                       apr_psprintf(scratch_pool, "%" APR_UINT64_T_HEX_FMT,
+                                    (apr_uint64_t)max_covered),
+                       apr_psprintf(scratch_pool, "%" APR_UINT64_T_HEX_FMT,
+                                    (apr_uint64_t) rev_file->l2p_offset));
+
+          SVN_ERR(svn_io_file_trunc(rev_file->file, rev_file->l2p_offset,
+                                    subpool));
+        }
 
       /* Create proto index files for the new index data
        * (will be cleaned up automatically with iterpool). */
       SVN_ERR(svn_fs_fs__p2l_index_from_p2l_entries(&p2l_proto_index, fs,
                                                     rev_file, entries,
-                                                    iterpool, iterpool));
+                                                    subpool, subpool));
       SVN_ERR(svn_fs_fs__l2p_index_from_p2l_entries(&l2p_proto_index, fs,
-                                                    entries, iterpool,
-                                                    iterpool));
+                                                    entries, subpool,
+                                                    subpool));
 
       /* Combine rev data with new index data. */
       SVN_ERR(svn_fs_fs__add_index_data(fs, rev_file->file, l2p_proto_index,
-                                        p2l_proto_index, revision, iterpool));
+                                        p2l_proto_index,
+                                        rev_file->start_revision, subpool));
     }
 
-  svn_pool_destroy(iterpool);
+  svn_pool_destroy(subpool);
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/lock.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/lock.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/lock.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/lock.c Mon Nov 30 10:24:16 2015
@@ -222,7 +222,7 @@ write_digest_file(apr_hash_t *children,
                                  svn_io_file_del_none, pool, pool));
   if ((err = svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool)))
     {
-      svn_error_clear(svn_stream_close(stream));
+      err = svn_error_compose_create(err, svn_stream_close(stream));
       return svn_error_createf(err->apr_err,
                                err,
                                _("Cannot write lock/entries hashfile '%s'"),
@@ -230,7 +230,7 @@ write_digest_file(apr_hash_t *children,
     }
 
   SVN_ERR(svn_stream_close(stream));
-  SVN_ERR(svn_io_file_rename(tmp_path, digest_path, pool));
+  SVN_ERR(svn_io_file_rename2(tmp_path, digest_path, FALSE, pool));
   SVN_ERR(svn_io_copy_perms(perms_reference, digest_path, pool));
   return SVN_NO_ERROR;
 }
@@ -273,7 +273,7 @@ read_digest_file(apr_hash_t **children_p
   hash = apr_hash_make(pool);
   if ((err = svn_hash_read2(hash, stream, SVN_HASH_TERMINATOR, pool)))
     {
-      svn_error_clear(svn_stream_close(stream));
+      err = svn_error_compose_create(err, svn_stream_close(stream));
       return svn_error_createf(err->apr_err,
                                err,
                                _("Can't parse lock/entries hashfile '%s'"),
@@ -715,6 +715,7 @@ check_lock(svn_error_t **fs_err,
            const svn_fs_lock_target_t *target,
            struct lock_baton *lb,
            svn_fs_root_t *root,
+           svn_revnum_t youngest_rev,
            apr_pool_t *pool)
 {
   svn_node_kind_t kind;
@@ -751,6 +752,15 @@ check_lock(svn_error_t **fs_err,
   if (SVN_IS_VALID_REVNUM(target->current_rev))
     {
       svn_revnum_t created_rev;
+
+      if (target->current_rev > youngest_rev)
+        {
+          *fs_err = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
+                                      _("No such revision %ld"),
+                                      target->current_rev);
+          return SVN_NO_ERROR;
+        }
+
       SVN_ERR(svn_fs_fs__node_created_rev(&created_rev, root, path,
                                           pool));
 
@@ -837,7 +847,7 @@ lock_body(void *baton, apr_pool_t *pool)
   apr_pool_t *iterpool = svn_pool_create(pool);
 
   /* Until we implement directory locks someday, we only allow locks
-     on files or non-existent paths. */
+     on files. */
   /* Use fs->vtable->foo instead of svn_fs_foo to avoid circular
      library dependencies, which are not portable. */
   SVN_ERR(lb->fs->vtable->youngest_rev(&youngest, lb->fs, pool));
@@ -856,7 +866,7 @@ lock_body(void *baton, apr_pool_t *pool)
       info.fs_err = SVN_NO_ERROR;
 
       SVN_ERR(check_lock(&info.fs_err, info.path, item->value, lb, root,
-                         iterpool));
+                         youngest, iterpool));
 
       /* If no error occurred while pre-checking, schedule the index updates for
          this path. */
@@ -1099,6 +1109,7 @@ svn_fs_fs__lock(svn_fs_t *fs,
   apr_array_header_t *sorted_targets;
   apr_hash_t *canonical_targets = apr_hash_make(scratch_pool);
   apr_hash_index_t *hi;
+  apr_pool_t *iterpool;
   svn_error_t *err, *cb_err = SVN_NO_ERROR;
   int i;
 
@@ -1139,11 +1150,13 @@ svn_fs_fs__lock(svn_fs_t *fs,
   lb.steal_lock = steal_lock;
   lb.result_pool = result_pool;
 
-  err = svn_fs_fs__with_write_lock(fs, lock_body, &lb, scratch_pool);
+  iterpool = svn_pool_create(scratch_pool);
+  err = svn_fs_fs__with_write_lock(fs, lock_body, &lb, iterpool);
   for (i = 0; i < lb.infos->nelts; ++i)
     {
       struct lock_info_t *info = &APR_ARRAY_IDX(lb.infos, i,
                                                 struct lock_info_t);
+      svn_pool_clear(iterpool);
       if (!cb_err && lock_callback)
         {
           if (!info->lock && !info->fs_err)
@@ -1152,10 +1165,11 @@ svn_fs_fs__lock(svn_fs_t *fs,
                                              info->path);
 
           cb_err = lock_callback(lock_baton, info->path, info->lock,
-                                 info->fs_err, scratch_pool);
+                                 info->fs_err, iterpool);
         }
       svn_error_clear(info->fs_err);
     }
+  svn_pool_destroy(iterpool);
 
   if (err && cb_err)
     svn_error_compose(err, cb_err);
@@ -1195,6 +1209,7 @@ svn_fs_fs__unlock(svn_fs_t *fs,
   apr_array_header_t *sorted_targets;
   apr_hash_t *canonical_targets = apr_hash_make(scratch_pool);
   apr_hash_index_t *hi;
+  apr_pool_t *iterpool;
   svn_error_t *err, *cb_err = SVN_NO_ERROR;
   int i;
 
@@ -1229,11 +1244,13 @@ svn_fs_fs__unlock(svn_fs_t *fs,
   ub.break_lock = break_lock;
   ub.result_pool = result_pool;
 
-  err = svn_fs_fs__with_write_lock(fs, unlock_body, &ub, scratch_pool);
+  iterpool = svn_pool_create(scratch_pool);
+  err = svn_fs_fs__with_write_lock(fs, unlock_body, &ub, iterpool);
   for (i = 0; i < ub.infos->nelts; ++i)
     {
       struct unlock_info_t *info = &APR_ARRAY_IDX(ub.infos, i,
                                                   struct unlock_info_t);
+      svn_pool_clear(iterpool);
       if (!cb_err && lock_callback)
         {
           if (!info->done && !info->fs_err)
@@ -1241,10 +1258,11 @@ svn_fs_fs__unlock(svn_fs_t *fs,
                                              0, _("Failed to unlock '%s'"),
                                              info->path);
           cb_err = lock_callback(lock_baton, info->path, NULL, info->fs_err,
-                                 scratch_pool);
+                                 iterpool);
         }
       svn_error_clear(info->fs_err);
     }
+  svn_pool_destroy(iterpool);
 
   if (err && cb_err)
     svn_error_compose(err, cb_err);

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/low_level.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/low_level.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/low_level.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/low_level.c Mon Nov 30 10:24:16 2015
@@ -189,6 +189,19 @@ svn_fs_fs__unparse_revision_trailer(apr_
                                changes_offset);
 }
 
+/* If ERR is not NULL, wrap it MESSAGE.  The latter must have an %ld
+ * format parameter that will be filled with REV. */
+static svn_error_t *
+wrap_footer_error(svn_error_t *err,
+                  const char *message,
+                  svn_revnum_t rev)
+{
+  if (err)
+    return svn_error_quick_wrapf(err, message, rev);
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_fs_fs__parse_footer(apr_off_t *l2p_offset,
                         svn_checksum_t **l2p_checksum,
@@ -196,6 +209,7 @@ svn_fs_fs__parse_footer(apr_off_t *l2p_o
                         svn_checksum_t **p2l_checksum,
                         svn_stringbuf_t *footer,
                         svn_revnum_t rev,
+                        apr_off_t footer_offset,
                         apr_pool_t *result_pool)
 {
   apr_int64_t val;
@@ -204,17 +218,20 @@ svn_fs_fs__parse_footer(apr_off_t *l2p_o
   /* Get the L2P offset. */
   const char *str = svn_cstring_tokenize(" ", &last_str);
   if (str == NULL)
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Invalid revision footer"));
+    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                             "Invalid r%ld footer", rev);
 
-  SVN_ERR(svn_cstring_atoi64(&val, str));
+  SVN_ERR(wrap_footer_error(svn_cstring_strtoi64(&val, str, 0,
+                                                 footer_offset - 1, 10),
+                            "Invalid L2P offset in r%ld footer",
+                            rev));
   *l2p_offset = (apr_off_t)val;
 
   /* Get the L2P checksum. */
   str = svn_cstring_tokenize(" ", &last_str);
   if (str == NULL)
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Invalid revision footer"));
+    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                             "Invalid r%ld footer", rev);
 
   SVN_ERR(svn_checksum_parse_hex(l2p_checksum, svn_checksum_md5, str,
                                  result_pool));
@@ -222,17 +239,33 @@ svn_fs_fs__parse_footer(apr_off_t *l2p_o
   /* Get the P2L offset. */
   str = svn_cstring_tokenize(" ", &last_str);
   if (str == NULL)
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Invalid revision footer"));
+    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                             "Invalid r%ld footer", rev);
 
-  SVN_ERR(svn_cstring_atoi64(&val, str));
+  SVN_ERR(wrap_footer_error(svn_cstring_strtoi64(&val, str, 0,
+                                                 footer_offset - 1, 10),
+                            "Invalid P2L offset in r%ld footer",
+                            rev));
   *p2l_offset = (apr_off_t)val;
 
+  /* The P2L indes follows the L2P index */
+  if (*p2l_offset <= *l2p_offset)
+    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                             "P2L offset %s must be larger than L2P offset %s"
+                             " in r%ld footer",
+                             apr_psprintf(result_pool,
+                                          "%" APR_UINT64_T_HEX_FMT,
+                                          (apr_uint64_t)*p2l_offset),
+                             apr_psprintf(result_pool,
+                                          "%" APR_UINT64_T_HEX_FMT,
+                                          (apr_uint64_t)*l2p_offset),
+                             rev);
+
   /* Get the P2L checksum. */
   str = svn_cstring_tokenize(" ", &last_str);
   if (str == NULL)
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Invalid revision footer"));
+    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                             "Invalid r%ld footer", rev);
 
   SVN_ERR(svn_checksum_parse_hex(p2l_checksum, svn_checksum_md5, str,
                                  result_pool));

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/low_level.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/low_level.h?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/low_level.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/low_level.h Mon Nov 30 10:24:16 2015
@@ -67,6 +67,8 @@ svn_fs_fs__unparse_revision_trailer(apr_
  * *P2L_OFFSET, respectively.  Also, return the expected checksums in
  * in *L2P_CHECKSUM and *P2L_CHECKSUM.
  *
+ * FOOTER_OFFSET is used for validation.
+ *
  * Note that REV is only used to construct nicer error objects that
  * mention this revision.  Allocate the checksums in RESULT_POOL.
  */
@@ -77,6 +79,7 @@ svn_fs_fs__parse_footer(apr_off_t *l2p_o
                         svn_checksum_t **p2l_checksum,
                         svn_stringbuf_t *footer,
                         svn_revnum_t rev,
+                        apr_off_t footer_offset,
                         apr_pool_t *result_pool);
 
 /* Given the offset of the L2P index data in L2P_OFFSET, the content

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/pack.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/pack.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/pack.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/pack.c Mon Nov 30 10:24:16 2015
@@ -108,8 +108,8 @@ typedef struct path_order_t
   /* noderev predecessor count */
   int predecessor_count;
 
-  /* this is a directory node */
-  svn_boolean_t is_dir;
+  /* this is a node is the latest for this PATH in this rev / pack file */
+  svn_boolean_t is_head;
 
   /* length of the expanded representation content */
   apr_int64_t expanded_size;
@@ -384,6 +384,8 @@ close_pack_context(pack_context_t *conte
   SVN_ERR(svn_io_remove_file2(proto_l2p_index_path, FALSE, pool));
   SVN_ERR(svn_io_remove_file2(proto_p2l_index_path, FALSE, pool));
 
+  /* Ensure that packed file is written to disk.*/
+  SVN_ERR(svn_io_file_flush_to_disk(context->pack_file, pool));
   SVN_ERR(svn_io_file_close(context->pack_file, pool));
 
   return SVN_NO_ERROR;
@@ -572,7 +574,7 @@ copy_rep_to_temp(pack_context_t *context
   /* read & parse the representation header */
   stream = svn_stream_from_aprfile2(rev_file, TRUE, pool);
   SVN_ERR(svn_fs_fs__read_rep_header(&rep_header, stream, pool, pool));
-  svn_stream_close(stream);
+  SVN_ERR(svn_stream_close(stream));
 
   /* if the representation is a delta against some other rep, link the two */
   if (   rep_header->type == svn_fs_fs__rep_delta
@@ -608,9 +610,6 @@ compare_dir_entries_format7(const svn_so
   const svn_fs_dirent_t *lhs = (const svn_fs_dirent_t *) a->value;
   const svn_fs_dirent_t *rhs = (const svn_fs_dirent_t *) b->value;
 
-  if (lhs->kind != rhs->kind)
-    return lhs->kind == svn_node_dir ? -1 : 1;
-
   return strcmp(lhs->name, rhs->name);
 }
 
@@ -643,17 +642,18 @@ compare_dir_entries_format6(const svn_so
 apr_array_header_t *
 svn_fs_fs__order_dir_entries(svn_fs_t *fs,
                              apr_hash_t *directory,
-                             apr_pool_t *pool)
+                             apr_pool_t *result_pool,
+                             apr_pool_t *scratch_pool)
 {
   apr_array_header_t *ordered
     = svn_sort__hash(directory,
                      svn_fs_fs__use_log_addressing(fs)
                        ? compare_dir_entries_format7
                        : compare_dir_entries_format6,
-                     pool);
+                     scratch_pool);
 
   apr_array_header_t *result
-    = apr_array_make(pool, ordered->nelts, sizeof(svn_fs_dirent_t *));
+    = apr_array_make(result_pool, ordered->nelts, sizeof(svn_fs_dirent_t *));
 
   int i;
   for (i = 0; i < ordered->nelts; ++i)
@@ -714,7 +714,7 @@ copy_node_to_temp(pack_context_t *contex
   /* read & parse noderev */
   stream = svn_stream_from_aprfile2(rev_file, TRUE, pool);
   SVN_ERR(svn_fs_fs__read_noderev(&noderev, stream, pool, pool));
-  svn_stream_close(stream);
+  SVN_ERR(svn_stream_close(stream));
 
   /* create a copy of ENTRY, make it point to the copy destination and
    * store it in CONTEXT */
@@ -736,9 +736,7 @@ copy_node_to_temp(pack_context_t *contex
     {
       path_order->rep_id.revision = noderev->data_rep->revision;
       path_order->rep_id.number = noderev->data_rep->item_index;
-      path_order->expanded_size = noderev->data_rep->expanded_size
-                                ? noderev->data_rep->expanded_size
-                                : noderev->data_rep->size;
+      path_order->expanded_size = noderev->data_rep->expanded_size;
     }
 
   /* Sort path is the key used for ordering noderevs and associated reps.
@@ -748,7 +746,6 @@ copy_node_to_temp(pack_context_t *contex
   path_order->node_id = *svn_fs_fs__id_node_id(noderev->id);
   path_order->revision = svn_fs_fs__id_rev(noderev->id);
   path_order->predecessor_count = noderev->predecessor_count;
-  path_order->is_dir = noderev->kind == svn_node_dir;
   path_order->noderev_id = *svn_fs_fs__id_rev_item(noderev->id);
   APR_ARRAY_PUSH(context->path_order, path_order_t *) = path_order;
 
@@ -765,13 +762,8 @@ compare_path_order(const path_order_t *
   const path_order_t * lhs = *lhs_p;
   const path_order_t * rhs = *rhs_p;
 
-  /* cluster all directories */
-  int diff = rhs->is_dir - lhs->is_dir;
-  if (diff)
-    return diff;
-
   /* lexicographic order on path and node (i.e. latest first) */
-  diff = svn_prefix_string__compare(lhs->path, rhs->path);
+  int diff = svn_prefix_string__compare(lhs->path, rhs->path);
   if (diff)
     return diff;
 
@@ -809,15 +801,6 @@ compare_ref_to_item(const reference_t *
   return svn_fs_fs__id_part_compare(&(*lhs_p)->from, rhs_p);
 }
 
-/* implements compare_fn_t.  Finds the DIR / FILE boundary.
- */
-static int
-compare_is_dir(const path_order_t * const * lhs_p,
-               const void *unused)
-{
-  return (*lhs_p)->is_dir ? -1 : 0;
-}
-
 /* Look for the least significant bit set in VALUE and return the smallest
  * number with the same property, i.e. the largest power of 2 that is a
  * factor in VALUE. */
@@ -827,19 +810,54 @@ roundness(int value)
   return value ? value - (value & (value - 1)) : INT_MAX;
 }
 
+/* For all paths in first COUNT entries in PATH_ORDER, mark their latest
+ * node as "HEAD".  PATH_ORDER must be ordered by path, revision.
+ */
+static void
+classify_nodes(path_order_t **path_order,
+               int count)
+{
+  const svn_prefix_string__t *path;
+  int i;
+
+  /* The logic below would fail for empty ranges. */
+  if (count == 0)
+    return;
+
+  /* All entries are sorted by path, followed by revision.
+   * So, the first index is also HEAD for the first path.
+   */
+  path = path_order[0]->path;
+  path_order[0]->is_head = TRUE;
+
+  /* Since the sorting implicitly groups all entries by path and then sorts
+   * by descending revision within the group, whenever we encounter a new
+   * path, the first entry is "HEAD" for that path.
+   */
+  for (i = 1; i < count; ++i)
+    {
+      /* New path? */
+      if (svn_prefix_string__compare(path, path_order[i]->path))
+        {
+          path = path_order[i]->path;
+          path_order[i]->is_head = TRUE;
+        }
+    }
+}
+
 /* Order a range of data collected in CONTEXT such that we can place them
  * in the desired order.  The input is taken from *PATH_ORDER, offsets FIRST
  * to LAST and then written in the final order to the same range in *TEMP.
  */
 static void
 sort_reps_range(pack_context_t *context,
-                const path_order_t **path_order,
-                const path_order_t **temp,
+                path_order_t **path_order,
+                path_order_t **temp,
                 int first,
                 int last)
 {
   const svn_prefix_string__t *path;
-  int i, dest, best;
+  int i, dest;
   svn_fs_fs__id_part_t rep_id;
   fs_fs_data_t *ffd = context->fs->fsap_data;
 
@@ -856,49 +874,47 @@ sort_reps_range(pack_context_t *context,
    * We simply pick & chose from the existing path, rev order.
    */
   dest = first;
-  path = path_order[first]->path;
-  best = first;
 
-  /* (1) For each path, pick the "roundest" representation and put it in
-   * front of all other nodes in the pack file.  The "roundest" rep is
-   * the one most likely to be referenced from future pack files, i.e. we
-   * concentrate those potential "foreign link targets" in one section of
-   * the pack file.
+  /* (1) There are two classes of representations that are likely to be
+   * referenced from future shards.  These form a "hot zone" of mostly
+   * relevant data, i.e. we try to include as many reps as possible that
+   * are needed for future checkouts while trying to exclude as many as
+   * possible that are likely not needed in future checkouts.
+   *
+   * First, "very round" representations from frequently changing nodes.
+   * That excludes many in-between representations not accessed from HEAD.
    *
-   * And we only apply this to reps outside the linear deltification
-   * sections because references *into* linear deltification ranges are
-   * much less likely.
+   * The second class are infrequently changing nodes.  Because they are
+   * unlikely to change often in the future, they will remain relevant for
+   * HEAD even over long spans of revisions.  They are most likely the only
+   * thing we need from very old pack files.
    */
   for (i = first; i < last; ++i)
     {
-      /* Investigated all nodes for the current path? */
-      if (svn_prefix_string__compare(path, path_order[i]->path))
-        {
-          /* next path */
-          path = path_order[i]->path;
-
-          /* Pick roundest non-linear deltified node. */
-          if (roundness(path_order[best]->predecessor_count)
-              >= ffd->max_linear_deltification)
-            {
-              temp[dest++] = path_order[best];
-              path_order[best] = NULL;
-              best = i;
-            }
-        }
+      int round = roundness(path_order[i]->predecessor_count);
 
-      /* next entry */
-      if (  roundness(path_order[best]->predecessor_count)
-          < roundness(path_order[i]->predecessor_count))
-        best = i;
-    }
+      /* Class 1:
+       * Pretty round _and_ a significant stop in the node's delta chain.
+       * This may pick up more than one representation from the same chain
+       * but that's rare not a problem.  Prefer simple checks here. */
+      svn_boolean_t likely_target
+        =    (round >= ffd->max_linear_deltification)
+          && (4 * round >= path_order[i]->predecessor_count);
+
+      /* Class 2:
+       * Anything from short node chains.  The default of 16 is generous
+       * but we'd rather include to many than to few nodes here to keep
+       * seeks between different regions of this pack file at a minimum. */
+      svn_boolean_t likely_head
+        =   path_order[i]->predecessor_count
+          < ffd->max_linear_deltification;
 
-  /* Treat the last path the same as all others. */
-  if (roundness(path_order[best]->predecessor_count)
-      >= ffd->max_linear_deltification)
-    {
-      temp[dest++] = path_order[best];
-      path_order[best] = NULL;
+      /* Pick any node that from either class. */
+      if (likely_target || likely_head)
+        {
+          temp[dest++] = path_order[i];
+          path_order[i] = NULL;
+        }
     }
 
   /* (2) For each (remaining) path, pick the nodes along the delta chain
@@ -964,8 +980,8 @@ static void
 sort_reps(pack_context_t *context)
 {
   apr_pool_t *temp_pool;
-  const path_order_t **temp, **path_order;
-  int i, count, dir_count;
+  path_order_t **temp, **path_order;
+  int i, count;
 
   /* We will later assume that there is at least one node / path.
    */
@@ -990,13 +1006,11 @@ sort_reps(pack_context_t *context)
   temp = apr_pcalloc(temp_pool, count * sizeof(*temp));
   path_order = (void *)context->path_order->elts;
 
-  /* Find the boundary between DIR and FILE section. */
-  dir_count = svn_sort__bsearch_lower_bound(context->path_order, NULL,
-                     (int (*)(const void *, const void *))compare_is_dir);
-
-  /* Sort those sub-sections separately. */
-  sort_reps_range(context, path_order, temp, 0, dir_count);
-  sort_reps_range(context, path_order, temp, dir_count, count);
+  /* Mark nodes depending on what other nodes exist for the same path etc. */
+  classify_nodes(path_order, count);
+
+  /* Rearrange those sub-sections separately. */
+  sort_reps_range(context, path_order, temp, 0, count);
 
   /* We now know the final ordering. */
   for (i = 0; i < count; ++i)
@@ -1163,7 +1177,7 @@ copy_reps_from_temp(pack_context_t *cont
   apr_array_header_t *path_order = context->path_order;
   int i;
 
-  /* copy items in path order. */
+  /* copy items in path order.  Exclude the non-HEAD noderevs. */
   for (i = 0; i < path_order->nelts; ++i)
     {
       path_order_t *current_path;
@@ -1173,13 +1187,30 @@ copy_reps_from_temp(pack_context_t *cont
       svn_pool_clear(iterpool);
 
       current_path = APR_ARRAY_IDX(path_order, i, path_order_t *);
-      node_part = get_item(context, &current_path->noderev_id, TRUE);
+      if (current_path->is_head)
+        {
+          node_part = get_item(context, &current_path->noderev_id, TRUE);
+          if (node_part)
+            SVN_ERR(store_item(context, temp_file, node_part, iterpool));
+        }
+
       rep_part = get_item(context, &current_path->rep_id, TRUE);
+      if (rep_part)
+        SVN_ERR(store_item(context, temp_file, rep_part, iterpool));
+    }
+
+  /* copy the remaining non-head noderevs. */
+  for (i = 0; i < path_order->nelts; ++i)
+    {
+      path_order_t *current_path;
+      svn_fs_fs__p2l_entry_t *node_part;
 
+      svn_pool_clear(iterpool);
+
+      current_path = APR_ARRAY_IDX(path_order, i, path_order_t *);
+      node_part = get_item(context, &current_path->noderev_id, TRUE);
       if (node_part)
         SVN_ERR(store_item(context, temp_file, node_part, iterpool));
-      if (rep_part)
-        SVN_ERR(store_item(context, temp_file, rep_part, iterpool));
     }
 
   svn_pool_destroy(iterpool);
@@ -1655,7 +1686,9 @@ pack_phys_addressed(const char *pack_fil
                     apr_pool_t *pool)
 {
   const char *pack_file_path, *manifest_file_path;
-  svn_stream_t *pack_stream, *manifest_stream;
+  apr_file_t *pack_file;
+  apr_file_t *manifest_file;
+  svn_stream_t *manifest_stream;
   svn_revnum_t end_rev, rev;
   apr_off_t next_offset;
   apr_pool_t *iterpool;
@@ -1664,13 +1697,18 @@ pack_phys_addressed(const char *pack_fil
   pack_file_path = svn_dirent_join(pack_file_dir, PATH_PACKED, pool);
   manifest_file_path = svn_dirent_join(pack_file_dir, PATH_MANIFEST, pool);
 
-  /* Create the new directory and pack file. */
-  SVN_ERR(svn_stream_open_writable(&pack_stream, pack_file_path, pool,
-                                    pool));
+  /* Create the new directory and pack file.
+   * Use unbuffered apr_file_t since we're going to write using 16kb
+   * chunks. */
+  SVN_ERR(svn_io_file_open(&pack_file, pack_file_path,
+                           APR_WRITE | APR_CREATE | APR_EXCL,
+                           APR_OS_DEFAULT, pool));
 
   /* Create the manifest file. */
-  SVN_ERR(svn_stream_open_writable(&manifest_stream, manifest_file_path,
-                                   pool, pool));
+  SVN_ERR(svn_io_file_open(&manifest_file, manifest_file_path,
+                           APR_WRITE | APR_BUFFERED | APR_CREATE | APR_EXCL,
+                           APR_OS_DEFAULT, pool));
+  manifest_stream = svn_stream_from_aprfile2(manifest_file, TRUE, pool);
 
   end_rev = start_rev + max_files_per_dir - 1;
   next_offset = 0;
@@ -1697,16 +1735,24 @@ pack_phys_addressed(const char *pack_fil
 
       /* Copy all the bits from the rev file to the end of the pack file. */
       SVN_ERR(svn_stream_open_readonly(&rev_stream, path, iterpool, iterpool));
-      SVN_ERR(svn_stream_copy3(rev_stream, svn_stream_disown(pack_stream,
-                                                             iterpool),
+      SVN_ERR(svn_stream_copy3(rev_stream,
+                               svn_stream_from_aprfile2(pack_file, TRUE, pool),
                                cancel_func, cancel_baton, iterpool));
     }
 
-  /* disallow write access to the manifest file */
+  /* Close stream over APR file. */
   SVN_ERR(svn_stream_close(manifest_stream));
+
+  /* Ensure that pack file is written to disk. */
+  SVN_ERR(svn_io_file_flush_to_disk(manifest_file, pool));
+  SVN_ERR(svn_io_file_close(manifest_file, pool));
+
+  /* disallow write access to the manifest file */
   SVN_ERR(svn_io_set_file_read_only(manifest_file_path, FALSE, iterpool));
 
-  SVN_ERR(svn_stream_close(pack_stream));
+  /* Ensure that pack file is written to disk. */
+  SVN_ERR(svn_io_file_flush_to_disk(pack_file, pool));
+  SVN_ERR(svn_io_file_close(pack_file, pool));
 
   svn_pool_destroy(iterpool);
 
@@ -1802,6 +1848,8 @@ synced_pack_shard(void *baton,
   /* if enabled, pack the revprops in an equivalent way */
   if (pb->revsprops_dir)
     {
+      apr_int64_t pack_size_limit = 0.9 * ffd->revprop_pack_size;
+
       revprops_pack_file_dir = svn_dirent_join(pb->revsprops_dir,
                    apr_psprintf(pool,
                                 "%" APR_INT64_T_FMT PATH_EXT_PACKED_SHARD,
@@ -1815,7 +1863,7 @@ synced_pack_shard(void *baton,
                                              revprops_shard_path,
                                              pb->shard,
                                              ffd->max_files_per_dir,
-                                             (int)(0.9*ffd->revprop_pack_size),
+                                             pack_size_limit,
                                              ffd->compress_packed_revprops
                                                ? SVN__COMPRESSION_ZLIB_DEFAULT
                                                : SVN__COMPRESSION_NONE,
@@ -1915,6 +1963,34 @@ pack_shard(struct pack_baton *baton,
   return SVN_NO_ERROR;
 }
 
+/* Read the youngest rev and the first non-packed rev info for FS from disk.
+   Set *FULLY_PACKED when there is no completed unpacked shard.
+   Use SCRATCH_POOL for temporary allocations.
+ */
+static svn_error_t *
+get_pack_status(svn_boolean_t *fully_packed,
+                svn_fs_t *fs,
+                apr_pool_t *scratch_pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  apr_int64_t completed_shards;
+  svn_revnum_t youngest;
+
+  SVN_ERR(svn_fs_fs__read_min_unpacked_rev(&ffd->min_unpacked_rev, fs,
+                                           scratch_pool));
+
+  SVN_ERR(svn_fs_fs__youngest_rev(&youngest, fs, scratch_pool));
+  completed_shards = (youngest + 1) / ffd->max_files_per_dir;
+
+  /* See if we've already completed all possible shards thus far. */
+  if (ffd->min_unpacked_rev == (completed_shards * ffd->max_files_per_dir))
+    *fully_packed = TRUE;
+  else
+    *fully_packed = FALSE;
+
+  return SVN_NO_ERROR;
+}
+
 /* The work-horse for svn_fs_fs__pack, called with the FS write lock.
    This implements the svn_fs_fs__with_write_lock() 'body' callback
    type.  BATON is a 'struct pack_baton *'.
@@ -1936,30 +2012,23 @@ pack_body(void *baton,
   struct pack_baton *pb = baton;
   fs_fs_data_t *ffd = pb->fs->fsap_data;
   apr_int64_t completed_shards;
-  svn_revnum_t youngest;
   apr_pool_t *iterpool;
+  svn_boolean_t fully_packed;
 
-  /* If the repository isn't a new enough format, we don't support packing.
-     Return a friendly error to that effect. */
-  if (ffd->format < SVN_FS_FS__MIN_PACKED_FORMAT)
-    return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
-      _("FSFS format (%d) too old to pack; please upgrade the filesystem."),
-      ffd->format);
-
-  /* If we aren't using sharding, we can't do any packing, so quit. */
-  if (!ffd->max_files_per_dir)
-    return SVN_NO_ERROR;
-
-  SVN_ERR(svn_fs_fs__read_min_unpacked_rev(&ffd->min_unpacked_rev, pb->fs,
-                                           pool));
-
-  SVN_ERR(svn_fs_fs__youngest_rev(&youngest, pb->fs, pool));
-  completed_shards = (youngest + 1) / ffd->max_files_per_dir;
+  /* Since another process might have already packed the repo,
+     we need to re-read the pack status. */
+  SVN_ERR(get_pack_status(&fully_packed, pb->fs, pool));
+  if (fully_packed)
+    {
+      if (pb->notify_func)
+        (*pb->notify_func)(pb->notify_baton,
+                           ffd->min_unpacked_rev / ffd->max_files_per_dir,
+                           svn_fs_pack_notify_noop, pool);
 
-  /* See if we've already completed all possible shards thus far. */
-  if (ffd->min_unpacked_rev == (completed_shards * ffd->max_files_per_dir))
-    return SVN_NO_ERROR;
+      return SVN_NO_ERROR;
+    }
 
+  completed_shards = (ffd->youngest_rev_cache + 1) / ffd->max_files_per_dir;
   pb->revs_dir = svn_dirent_join(pb->fs->path, PATH_REVS_DIR, pool);
   if (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
     pb->revsprops_dir = svn_dirent_join(pb->fs->path, PATH_REVPROPS_DIR,
@@ -1993,7 +2062,37 @@ svn_fs_fs__pack(svn_fs_t *fs,
   struct pack_baton pb = { 0 };
   fs_fs_data_t *ffd = fs->fsap_data;
   svn_error_t *err;
+  svn_boolean_t fully_packed;
+
+  /* If the repository isn't a new enough format, we don't support packing.
+     Return a friendly error to that effect. */
+  if (ffd->format < SVN_FS_FS__MIN_PACKED_FORMAT)
+    return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+      _("FSFS format (%d) too old to pack; please upgrade the filesystem."),
+      ffd->format);
+
+  /* If we aren't using sharding, we can't do any packing, so quit. */
+  if (!ffd->max_files_per_dir)
+    {
+      if (notify_func)
+        (*notify_func)(notify_baton, -1, svn_fs_pack_notify_noop, pool);
+
+      return SVN_NO_ERROR;
+    }
+
+  /* Is there we even anything to do?. */
+  SVN_ERR(get_pack_status(&fully_packed, fs, pool));
+  if (fully_packed)
+    {
+      if (notify_func)
+        (*notify_func)(notify_baton,
+                       ffd->min_unpacked_rev / ffd->max_files_per_dir,
+                       svn_fs_pack_notify_noop, pool);
+
+      return SVN_NO_ERROR;
+    }
 
+  /* Lock the repo and start the pack process. */
   pb.fs = fs;
   pb.notify_func = notify_func;
   pb.notify_baton = notify_baton;

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/pack.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/pack.h?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/pack.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/pack.h Mon Nov 30 10:24:16 2015
@@ -51,13 +51,14 @@ svn_fs_fs__get_packed_offset(apr_off_t *
                              apr_pool_t *pool);
 
 /* Return the svn_dir_entry_t* objects of DIRECTORY in an APR array
- * allocated in POOL with entries added in storage (on-disk) order.
- * FS format will be used to pick the optimal ordering strategy.
+ * allocated in RESULT_POOL with entries added in storage (on-disk) order.
+ * FS' format will be used to pick the optimal ordering strategy.  Use
+ * SCRATCH_POOL for temporary allocations.
  */
 apr_array_header_t *
 svn_fs_fs__order_dir_entries(svn_fs_t *fs,
                              apr_hash_t *directory,
-                             apr_pool_t *pool);
-
+                             apr_pool_t *result_pool,
+                             apr_pool_t *scratch_pool);
 
 #endif

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/recovery.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/recovery.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/recovery.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/recovery.c Mon Nov 30 10:24:16 2015
@@ -160,6 +160,7 @@ recover_find_max_ids(svn_fs_t *fs,
   apr_hash_index_t *hi;
   apr_pool_t *iterpool;
   node_revision_t *noderev;
+  svn_error_t *err;
 
   baton.stream = rev_file->stream;
   SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &offset, pool));
@@ -196,16 +197,23 @@ recover_find_max_ids(svn_fs_t *fs,
      stored in the representation.  Note that this is a directory, i.e.
      represented using the hash format on disk and can never have 0 length. */
   baton.pool = pool;
-  baton.remaining = noderev->data_rep->expanded_size
-                  ? noderev->data_rep->expanded_size
-                  : noderev->data_rep->size;
+  baton.remaining = noderev->data_rep->expanded_size;
   stream = svn_stream_create(&baton, pool);
   svn_stream_set_read2(stream, NULL /* only full read support */,
                        read_handler_recover);
 
   /* Now read the entries from that stream. */
   entries = apr_hash_make(pool);
-  SVN_ERR(svn_hash_read2(entries, stream, SVN_HASH_TERMINATOR, pool));
+  err = svn_hash_read2(entries, stream, SVN_HASH_TERMINATOR, pool);
+  if (err)
+    {
+      svn_string_t *id_str = svn_fs_fs__id_unparse(noderev->id, pool);
+
+      err = svn_error_compose_create(err, svn_stream_close(stream));
+      return svn_error_quick_wrapf(err,
+                _("malformed representation for node-revision '%s'"),
+                id_str->data);
+    }
   SVN_ERR(svn_stream_close(stream));
 
   /* Now check each of the entries in our directory to find new node and

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/rep-cache-db.sql
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/rep-cache-db.sql?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/rep-cache-db.sql (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/rep-cache-db.sql Mon Nov 30 10:24:16 2015
@@ -63,3 +63,6 @@ WHERE revision > ?1
 -- STMT_LOCK_REP
 BEGIN TRANSACTION;
 INSERT INTO rep_cache VALUES ('dummy', 0, 0, 0, 0)
+
+-- STMT_UNLOCK_REP
+ROLLBACK TRANSACTION;

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/rep-cache.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/rep-cache.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/rep-cache.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/rep-cache.c Mon Nov 30 10:24:16 2015
@@ -24,6 +24,7 @@
 
 #include "svn_private_config.h"
 
+#include "cached_data.h"
 #include "fs_fs.h"
 #include "fs.h"
 #include "rep-cache.h"
@@ -273,6 +274,8 @@ svn_fs_fs__get_rep_reference(representat
       (*rep)->item_index = svn_sqlite__column_int64(stmt, 1);
       (*rep)->size = svn_sqlite__column_int64(stmt, 2);
       (*rep)->expanded_size = svn_sqlite__column_int64(stmt, 3);
+
+      SVN_ERR(svn_fs_fs__fixup_expanded_size(fs, *rep, pool));
     }
   else
     *rep = NULL;
@@ -371,9 +374,13 @@ svn_fs_fs__del_rep_reference(svn_fs_t *f
   return SVN_NO_ERROR;
 }
 
-svn_error_t *
-svn_fs_fs__lock_rep_cache(svn_fs_t *fs,
-                          apr_pool_t *pool)
+/* Start a transaction to take an SQLite reserved lock that prevents
+   other writes.
+
+   See unlock_rep_cache(). */
+static svn_error_t *
+lock_rep_cache(svn_fs_t *fs,
+               apr_pool_t *pool)
 {
   fs_fs_data_t *ffd = fs->fsap_data;
 
@@ -384,3 +391,31 @@ svn_fs_fs__lock_rep_cache(svn_fs_t *fs,
 
   return SVN_NO_ERROR;
 }
+
+/* End the transaction started by lock_rep_cache(). */
+static svn_error_t *
+unlock_rep_cache(svn_fs_t *fs,
+                 apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+
+  SVN_ERR_ASSERT(ffd->rep_cache_db); /* was opened by lock_rep_cache() */
+
+  SVN_ERR(svn_sqlite__exec_statements(ffd->rep_cache_db, STMT_UNLOCK_REP));
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__with_rep_cache_lock(svn_fs_t *fs,
+                               svn_error_t *(*body)(void *,
+                                                    apr_pool_t *),
+                               void *baton,
+                               apr_pool_t *pool)
+{
+  svn_error_t *err;
+
+  SVN_ERR(lock_rep_cache(fs, pool));
+  err = body(baton, pool);
+  return svn_error_compose_create(err, unlock_rep_cache(fs, pool));
+}

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/rep-cache.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/rep-cache.h?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/rep-cache.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/rep-cache.h Mon Nov 30 10:24:16 2015
@@ -87,10 +87,14 @@ svn_fs_fs__del_rep_reference(svn_fs_t *f
                              apr_pool_t *pool);
 
 /* Start a transaction to take an SQLite reserved lock that prevents
-   other writes. */
+   other writes, call BODY, end the transaction, and return what BODY returned.
+ */
 svn_error_t *
-svn_fs_fs__lock_rep_cache(svn_fs_t *fs,
-                          apr_pool_t *pool);
+svn_fs_fs__with_rep_cache_lock(svn_fs_t *fs,
+                               svn_error_t *(*body)(void *baton,
+                                                    apr_pool_t *pool),
+                               void *baton,
+                               apr_pool_t *pool);
 
 #ifdef __cplusplus
 }

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/rev_file.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/rev_file.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/rev_file.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/rev_file.c Mon Nov 30 10:24:16 2015
@@ -259,6 +259,7 @@ svn_fs_fs__auto_read_footer(svn_fs_fs__r
       SVN_ERR(svn_fs_fs__parse_footer(&file->l2p_offset, &file->l2p_checksum,
                                       &file->p2l_offset, &file->p2l_checksum,
                                       footer, file->start_revision,
+                                      filesize - footer_length - 1,
                                       file->pool));
       file->footer_offset = filesize - footer_length - 1;
     }