Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.c?rev=1634609&r1=1634608&r2=1634609&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.c 
(original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.c Mon Oct 
27 17:28:13 2014
@@ -55,6 +55,16 @@ svn_fs_fs__is_packed_revprop(svn_fs_t *f
       && (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT);
 }
 
+svn_revnum_t
+svn_fs_fs__packed_base_rev(svn_fs_t *fs,
+                           svn_revnum_t revision)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  return (revision < ffd->min_unpacked_rev)
+       ? (revision - (revision % ffd->max_files_per_dir))
+       : revision;
+}
+
 const char *
 svn_fs_fs__path_txn_current(svn_fs_t *fs,
                             apr_pool_t *pool)
@@ -224,15 +234,25 @@ combine_txn_id_string(const svn_fs_fs__i
 }
 
 const char *
+svn_fs_fs__path_txns_dir(svn_fs_t *fs,
+                         apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+
+  return ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT
+       ? svn_dirent_join(fs->path, PATH_TXNS_LA_DIR, pool)
+       : svn_dirent_join(fs->path, PATH_TXNS_DIR, pool);
+}
+
+const char *
 svn_fs_fs__path_txn_dir(svn_fs_t *fs,
                         const svn_fs_fs__id_part_t *txn_id,
                         apr_pool_t *pool)
 {
   SVN_ERR_ASSERT_NO_RETURN(txn_id != NULL);
-  return svn_dirent_join_many(pool, fs->path, PATH_TXNS_DIR,
-                              combine_txn_id_string(txn_id, PATH_EXT_TXN,
-                                                    pool),
-                              SVN_VA_NULL);
+  return svn_dirent_join(svn_fs_fs__path_txns_dir(fs, pool),
+                         combine_txn_id_string(txn_id, PATH_EXT_TXN, pool),
+                         pool);
 }
 
 const char*
@@ -263,16 +283,22 @@ svn_fs_fs__path_txn_item_index(svn_fs_t 
 }
 
 const char *
+svn_fs_fs__path_txn_proto_revs(svn_fs_t *fs,
+                               apr_pool_t *pool)
+{
+  return svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, pool);
+}
+
+const char *
 svn_fs_fs__path_txn_proto_rev(svn_fs_t *fs,
                               const svn_fs_fs__id_part_t *txn_id,
                               apr_pool_t *pool)
 {
   fs_fs_data_t *ffd = fs->fsap_data;
   if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
-    return svn_dirent_join_many(pool, fs->path, PATH_TXN_PROTOS_DIR,
-                                combine_txn_id_string(txn_id, PATH_EXT_REV,
-                                                      pool),
-                                SVN_VA_NULL);
+    return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool),
+                           combine_txn_id_string(txn_id, PATH_EXT_REV, pool),
+                           pool);
   else
     return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
                            PATH_REV, pool);
@@ -286,11 +312,10 @@ svn_fs_fs__path_txn_proto_rev_lock(svn_f
 {
   fs_fs_data_t *ffd = fs->fsap_data;
   if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
-    return svn_dirent_join_many(pool, fs->path, PATH_TXN_PROTOS_DIR,
-                                combine_txn_id_string(txn_id,
-                                                      PATH_EXT_REV_LOCK,
-                                                      pool),
-                                SVN_VA_NULL);
+    return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool),
+                           combine_txn_id_string(txn_id, PATH_EXT_REV_LOCK,
+                                                 pool),
+                           pool);
   else
     return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
                            PATH_REV_LOCK, pool);
@@ -387,7 +412,7 @@ svn_fs_fs__read_min_unpacked_rev(svn_rev
   SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
   SVN_ERR(svn_io_file_close(file, pool));
 
-  *min_unpacked_rev = SVN_STR_TO_REV(buf);
+  SVN_ERR(svn_revnum_parse(min_unpacked_rev, buf, NULL));
   return SVN_NO_ERROR;
 }
 
@@ -429,7 +454,6 @@ svn_fs_fs__read_current(svn_revnum_t *re
 {
   fs_fs_data_t *ffd = fs->fsap_data;
   svn_stringbuf_t *content;
-  const char *str;
 
   SVN_ERR(svn_fs_fs__read_content(&content,
                                   svn_fs_fs__path_current(fs, pool),
@@ -437,16 +461,20 @@ svn_fs_fs__read_current(svn_revnum_t *re
 
   if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT)
     {
-      SVN_ERR(svn_revnum_parse(rev, content->data, &str));
-      if (*str != '\n')
-        return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                                _("Corrupt 'current' file"));
+      /* When format 1 and 2 filesystems are upgraded, the 'current' file is
+         left intact.  As a consequence, there is a window when a filesystem
+         has a new format, but this file still contains the IDs left from an
+         old format, i.e. looks like "359 j5 v\n".  Do not be too strict here
+         and only expect a parseable revision number. */
+      SVN_ERR(svn_revnum_parse(rev, content->data, NULL));
 
       *next_node_id = 0;
       *next_copy_id = 0;
     }
   else
     {
+      const char *str;
+
       SVN_ERR(svn_revnum_parse(rev, content->data, &str));
       if (*str != ' ')
         return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,

Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.h?rev=1634609&r1=1634608&r2=1634609&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.h 
(original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.h Mon Oct 
27 17:28:13 2014
@@ -76,6 +76,12 @@ svn_boolean_t
 svn_fs_fs__is_packed_revprop(svn_fs_t *fs,
                              svn_revnum_t rev);
 
+/* Return the first revision in the pack / rev file containing REVISION in
+ * filesystem FS.  For non-packed revs, this will simply be REVISION. */
+svn_revnum_t
+svn_fs_fs__packed_base_rev(svn_fs_t *fs,
+                           svn_revnum_t revision);
+
 /* Return the full path of the rev shard directory that will contain
  * revision REV in FS.  Allocate the result in POOL.
  */
@@ -183,6 +189,13 @@ const char *
 svn_fs_fs__path_min_unpacked_rev(svn_fs_t *fs,
                                  apr_pool_t *pool);
 
+/* Return the path of the 'transactions' directory in FS.
+ * The result will be allocated in POOL.
+ */
+const char *
+svn_fs_fs__path_txns_dir(svn_fs_t *fs,
+                         apr_pool_t *pool);
+
 /* Return the path of the directory containing the transaction TXN_ID in FS.
  * The result will be allocated in POOL.
  */
@@ -191,6 +204,13 @@ svn_fs_fs__path_txn_dir(svn_fs_t *fs,
                         const svn_fs_fs__id_part_t *txn_id,
                         apr_pool_t *pool);
 
+/* Return the path of the 'txn-protorevs' directory in FS, even if that
+ * folder may not exist in FS.  The result will be allocated in POOL.
+ */
+const char *
+svn_fs_fs__path_txn_proto_revs(svn_fs_t *fs,
+                               apr_pool_t *pool);
+
 /* Return the path of the proto-revision file for transaction TXN_ID in FS.
  * The result will be allocated in POOL.
  */

Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/verify.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/verify.c?rev=1634609&r1=1634608&r2=1634609&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/verify.c 
(original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/verify.c Mon 
Oct 27 17:28:13 2014
@@ -159,6 +159,93 @@ verify_rep_cache(svn_fs_t *fs,
   return SVN_NO_ERROR;
 }
 
+/* Verify that the MD5 checksum of the data between offsets START and END
+ * in FILE matches the EXPECTED checksum.  If there is a mismatch use the
+ * indedx NAME in the error message.  Supports cancellation with CANCEL_FUNC
+ * and CANCEL_BATON.  SCRATCH_POOL is for temporary allocations. */
+static svn_error_t *
+verify_index_checksum(apr_file_t *file,
+                      const char *name,
+                      apr_off_t start,
+                      apr_off_t end,
+                      svn_checksum_t *expected,
+                      svn_cancel_func_t cancel_func,
+                      void *cancel_baton,
+                      apr_pool_t *scratch_pool)
+{
+  unsigned char buffer[SVN__STREAM_CHUNK_SIZE];
+  apr_off_t size = end - start;
+  svn_checksum_t *actual;
+  svn_checksum_ctx_t *context
+    = svn_checksum_ctx_create(svn_checksum_md5, scratch_pool);
+
+  /* Calculate the index checksum. */
+  SVN_ERR(svn_io_file_seek(file, APR_SET, &start, scratch_pool));
+  while (size > 0)
+    {
+      apr_size_t to_read = size > sizeof(buffer)
+                         ? sizeof(buffer)
+                         : (apr_size_t)size;
+      SVN_ERR(svn_io_file_read_full2(file, buffer, to_read, NULL, NULL,
+                                     scratch_pool));
+      SVN_ERR(svn_checksum_update(context, buffer, to_read));
+      size -= to_read;
+
+      if (cancel_func)
+        SVN_ERR(cancel_func(cancel_baton));
+    }
+
+  SVN_ERR(svn_checksum_final(&actual, context, scratch_pool));
+
+  /* Verify that it matches the expected checksum. */
+  if (!svn_checksum_match(expected, actual))
+    {
+      const char *file_name;
+
+      SVN_ERR(svn_io_file_name_get(&file_name, file, scratch_pool));
+      SVN_ERR(svn_checksum_mismatch_err(expected, actual, scratch_pool, 
+                                        _("%s checksum mismatch in file %s"),
+                                        name, file_name));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Verify the MD5 checksums of the index data in the rev / pack file
+ * containing revision START in FS.  If given, invoke CANCEL_FUNC with
+ * CANCEL_BATON at regular intervals.  Use SCRATCH_POOL for temporary
+ * allocations.
+ */
+static svn_error_t *
+verify_index_checksums(svn_fs_t *fs,
+                       svn_revnum_t start,
+                       svn_cancel_func_t cancel_func,
+                       void *cancel_baton,
+                       apr_pool_t *scratch_pool)
+{
+  svn_fs_fs__revision_file_t *rev_file;
+
+  /* Open the rev / pack file and read the footer */
+  SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, start,
+                                           scratch_pool, scratch_pool));
+  SVN_ERR(svn_fs_fs__auto_read_footer(rev_file));
+
+  /* Verify the index contents against the checksum from the footer. */
+  SVN_ERR(verify_index_checksum(rev_file->file, "L2P index",
+                                rev_file->l2p_offset, rev_file->p2l_offset,
+                                rev_file->l2p_checksum,
+                                cancel_func, cancel_baton, scratch_pool));
+  SVN_ERR(verify_index_checksum(rev_file->file, "P2L index",
+                                rev_file->p2l_offset, rev_file->footer_offset,
+                                rev_file->p2l_checksum,
+                                cancel_func, cancel_baton, scratch_pool));
+
+  /* Done. */
+  SVN_ERR(svn_fs_fs__close_revision_file(rev_file));
+
+  return SVN_NO_ERROR;
+}
+
 /* Verify that for all log-to-phys index entries for revisions START to
  * START + COUNT-1 in FS there is a consistent entry in the phys-to-log
  * index.  If given, invoke CANCEL_FUNC with CANCEL_BATON at regular
@@ -182,7 +269,8 @@ compare_l2p_to_p2l_index(svn_fs_t *fs,
                                            iterpool));
 
   /* determine the range of items to check for each revision */
-  SVN_ERR(svn_fs_fs__l2p_get_max_ids(&max_ids, fs, start, count, pool));
+  SVN_ERR(svn_fs_fs__l2p_get_max_ids(&max_ids, fs, start, count, pool,
+                                     iterpool));
 
   /* check all items in all revisions if the given range */
   for (i = 0; i < max_ids->nelts; ++i)
@@ -205,10 +293,11 @@ compare_l2p_to_p2l_index(svn_fs_t *fs,
 
           /* find the corresponding P2L entry */
           SVN_ERR(svn_fs_fs__p2l_entry_lookup(&p2l_entry, fs, rev_file,
-                                              revision, offset, iterpool));
+                                              revision, offset, iterpool,
+                                              iterpool));
 
           if (p2l_entry == NULL)
-            return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_INCONSISTENT,
+            return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT,
                                      NULL,
                                      _("p2l index entry not found for "
                                        "PHYS %s returned by "
@@ -218,7 +307,7 @@ compare_l2p_to_p2l_index(svn_fs_t *fs,
 
           if (   p2l_entry->item.number != k
               || p2l_entry->item.revision != revision)
-            return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_INCONSISTENT,
+            return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT,
                                      NULL,
                                      _("p2l index info LOG r%ld:i%ld"
                                        " does not match "
@@ -282,9 +371,9 @@ compare_p2l_to_l2p_index(svn_fs_t *fs,
       /* get all entries for the current block */
       SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, fs, rev_file, start,
                                           offset, ffd->p2l_page_size,
-                                          iterpool));
+                                          iterpool, iterpool));
       if (entries->nelts == 0)
-        return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_CORRUPTION,
+        return svn_error_createf(SVN_ERR_FS_INDEX_CORRUPTION,
                                  NULL,
                                  _("p2l does not cover offset %s"
                                    " for revision %ld"),
@@ -301,7 +390,24 @@ compare_p2l_to_l2p_index(svn_fs_t *fs,
             = &APR_ARRAY_IDX(entries, i, svn_fs_fs__p2l_entry_t);
 
           /* check all sub-items for consist entries in the L2P index */
-          if (entry->type != SVN_FS_FS__ITEM_TYPE_UNUSED)
+          if (entry->type == SVN_FS_FS__ITEM_TYPE_UNUSED)
+            {
+              /* There is no L2P entry for unused rev file sections.
+               * And its P2L index data is hardly ever used.  But we
+               * should still check whether someone tempered with it. */
+              if (   entry->item.revision != SVN_INVALID_REVNUM
+                  && (   entry->item.revision < start
+                      || entry->item.revision >= start + count))
+                return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT,
+                                         NULL,
+                                         _("Empty P2L entry for PHYS %s "
+                                           "refers to revision %ld outside "
+                                           "the rev / pack file (%ld-%ld)"),
+                                         apr_off_t_toa(pool, entry->offset),
+                                         entry->item.number,
+                                         start, start + count - 1);
+            }
+          else
             {
               apr_off_t l2p_offset;
               SVN_ERR(svn_fs_fs__item_offset(&l2p_offset, fs, rev_file,
@@ -309,7 +415,7 @@ compare_p2l_to_l2p_index(svn_fs_t *fs,
                                              entry->item.number, iterpool));
 
               if (l2p_offset != entry->offset)
-                return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_INCONSISTENT,
+                return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT,
                                          NULL,
                                          _("l2p index entry PHYS %s"
                                            "does not match p2l index value "
@@ -416,7 +522,7 @@ expected_checksum(apr_file_t *file,
 
       SVN_ERR(svn_io_file_name_get(&file_name, file, pool));
       return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                               _("Checksum mismatch item at offset %s of "
+                               _("Checksum mismatch in item at offset %s of "
                                  "length %s bytes in file %s"),
                                apr_off_t_toa(pool, entry->offset),
                                apr_off_t_toa(pool, entry->size), file_name);
@@ -511,7 +617,7 @@ compare_p2l_to_rev(svn_fs_t *fs,
                                         pool));
 
   if (rev_file->l2p_offset != max_offset)
-    return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_INCONSISTENT, NULL,
+    return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, NULL,
                              _("File size of %s for revision r%ld does "
                                "not match p2l index size of %s"),
                              apr_off_t_toa(pool, rev_file->l2p_offset), start,
@@ -532,7 +638,12 @@ compare_p2l_to_rev(svn_fs_t *fs,
       /* get all entries for the current block */
       SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, fs, rev_file, start,
                                           offset, ffd->p2l_page_size,
-                                          iterpool));
+                                          iterpool, iterpool));
+
+      /* The above might have moved the file pointer.
+       * Ensure we actually start reading at OFFSET.  */
+      SVN_ERR(svn_io_file_aligned_seek(rev_file->file, ffd->block_size,
+                                       NULL, offset, iterpool));
 
       /* process all entries (and later continue with the next block) */
       for (i = 0; i < entries->nelts; ++i)
@@ -550,7 +661,7 @@ compare_p2l_to_rev(svn_fs_t *fs,
 
           /* p2l index must cover all rev / pack file offsets exactly once */
           if (entry->offset != offset)
-            return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_INCONSISTENT,
+            return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT,
                                      NULL,
                                      _("p2l index entry for revision r%ld"
                                        " is non-contiguous between offsets "
@@ -566,7 +677,7 @@ compare_p2l_to_rev(svn_fs_t *fs,
               if (entry->offset != max_offset)
                 SVN_ERR(read_all_nul(rev_file->file, entry->size, pool));
             }
-          else if (entry->fnv1_checksum)
+          else
             {
               if (entry->size < STREAM_THRESHOLD)
                 SVN_ERR(expected_buffered_checksum(rev_file->file, entry,
@@ -592,16 +703,6 @@ compare_p2l_to_rev(svn_fs_t *fs,
 }
 
 static svn_revnum_t
-packed_base_rev(svn_fs_t *fs, svn_revnum_t rev)
-{
-  fs_fs_data_t *ffd = fs->fsap_data;
-
-  return rev < ffd->min_unpacked_rev
-       ? rev - (rev % ffd->max_files_per_dir)
-       : rev;
-}
-
-static svn_revnum_t
 pack_size(svn_fs_t *fs, svn_revnum_t rev)
 {
   fs_fs_data_t *ffd = fs->fsap_data;
@@ -635,7 +736,7 @@ verify_index_consistency(svn_fs_t *fs,
       svn_error_t *err = SVN_NO_ERROR;
 
       svn_revnum_t count = pack_size(fs, revision);
-      svn_revnum_t pack_start = packed_base_rev(fs, revision);
+      svn_revnum_t pack_start = svn_fs_fs__packed_base_rev(fs, revision);
       svn_revnum_t pack_end = pack_start + count;
 
       svn_pool_clear(iterpool);
@@ -643,9 +744,14 @@ verify_index_consistency(svn_fs_t *fs,
       if (notify_func && (pack_start % ffd->max_files_per_dir == 0))
         notify_func(pack_start, notify_baton, iterpool);
 
+      /* Check for external corruption to the indexes. */
+      err = verify_index_checksums(fs, pack_start, cancel_func,
+                                   cancel_baton, iterpool);
+ 
       /* two-way index check */
-      err = compare_l2p_to_p2l_index(fs, pack_start, pack_end - pack_start,
-                                     cancel_func, cancel_baton, iterpool);
+      if (!err)
+        err = compare_l2p_to_p2l_index(fs, pack_start, pack_end - pack_start,
+                                       cancel_func, cancel_baton, iterpool);
       if (!err)
         err = compare_p2l_to_l2p_index(fs, pack_start, pack_end - pack_start,
                                        cancel_func, cancel_baton, iterpool);
@@ -668,7 +774,7 @@ verify_index_consistency(svn_fs_t *fs,
 
           /* We could simply assign revision here but the code below is
              more intuitive to maintainers. */
-          next_revision = packed_base_rev(fs, revision);
+          next_revision = svn_fs_fs__packed_base_rev(fs, revision);
         }
       else
         {

Propchange: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/
------------------------------------------------------------------------------
  Merged 
/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs:r1619782-1620595
  Merged /subversion/trunk/subversion/libsvn_fs_fs:r1631115,1631171,1631180
  Merged /subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x:r1620597
  Merged /subversion/trunk/subversion/libsvn_fs_x:r1622194-1634606

Modified: 
subversion/branches/move-tracking-2/subversion/libsvn_fs_x/cached_data.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/cached_data.c?rev=1634609&r1=1634608&r2=1634609&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/cached_data.c 
(original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/cached_data.c 
Mon Oct 27 17:28:13 2014
@@ -772,13 +772,18 @@ svn_fs_x__rep_chain_length(int *chain_le
   svn_revnum_t shard_size = ffd->max_files_per_dir
                           ? ffd->max_files_per_dir
                           : 1;
-  apr_pool_t *sub_pool = svn_pool_create(pool);
   svn_boolean_t is_delta = FALSE;
   int count = 0;
   int shards = 1;
   svn_revnum_t revision = svn_fs_x__get_revnum(rep->id.change_set);
   svn_revnum_t last_shard = revision / shard_size;
-  
+
+  /* Note that this iteration pool will be used in a non-standard way.
+   * To reuse open file handles between iterations (e.g. while within the
+   * same pack file), we only clear this pool once in a while instead of
+   * at the start of each iteration. */
+  apr_pool_t *iterpool = svn_pool_create(pool);
+
   /* Check whether the length of the deltification chain is acceptable.
    * Otherwise, shared reps may form a non-skipping delta chain in
    * extreme cases. */
@@ -806,7 +811,7 @@ svn_fs_x__rep_chain_length(int *chain_le
                                     &file_hint,
                                     &base_rep,
                                     fs,
-                                    sub_pool));
+                                    iterpool));
 
       base_rep.id.change_set
         = svn_fs_x__change_set_by_rev(header->base_revision);
@@ -814,18 +819,28 @@ svn_fs_x__rep_chain_length(int *chain_le
       base_rep.size = header->base_length;
       is_delta = header->type == svn_fs_x__rep_delta;
 
+      /* Clear it the ITERPOOL once in a while.  Doing it too frequently
+       * renders the FILE_HINT ineffective.  Doing too infrequently, may
+       * leave us with too many open file handles.
+       *
+       * Note that this is mostly about efficiency, with larger values
+       * being more efficient, and any non-zero value is legal here.  When
+       * reading deltified contents, we may keep 10s of rev files open at
+       * the same time and the system has to cope with that.  Thus, the
+       * limit of 16 chosen below is in the same ballpark.
+       */
       ++count;
       if (count % 16 == 0)
         {
           file_hint = NULL;
-          svn_pool_clear(sub_pool);
+          svn_pool_clear(iterpool);
         }
     }
   while (is_delta && base_rep.id.change_set);
 
   *chain_length = count;
   *shard_count = shards;
-  svn_pool_destroy(sub_pool);
+  svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;
 }
@@ -3116,7 +3131,7 @@ block_read(void **result,
               key.revision = svn_fs_x__get_revnum(entry->items[0].change_set);
               key.second = entry->items[0].number;
 
-              SVN_ERR(svn_io_file_seek(revision_file, SEEK_SET,
+              SVN_ERR(svn_io_file_seek(revision_file, APR_SET,
                                        &entry->offset, iterpool));
               switch (entry->type)
                 {

Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/caching.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/caching.c?rev=1634609&r1=1634608&r2=1634609&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/caching.c 
(original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/caching.c Mon 
Oct 27 17:28:13 2014
@@ -126,19 +126,17 @@ read_config(const char **cache_namespace
    * Revprop caching significantly speeds up operations like
    * svn ls -v. However, it requires synchronization that may
    * not be available or efficient in the current server setup.
-   *
-   * If the caller chose option "2", enable revprop caching if
-   * the required API support is there to make it efficient.
+   * Option "2" is equivalent to "1".
    */
   if (strcmp(svn_hash__get_cstring(fs->config,
                                    SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
                                    ""), "2"))
     *cache_revprops
       = svn_hash__get_bool(fs->config,
-                           SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
-                           FALSE);
+                          SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
+                          FALSE);
   else
-    *cache_revprops = svn_named_atomic__is_efficient();
+    *cache_revprops = TRUE;
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs.c?rev=1634609&r1=1634608&r2=1634609&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs.c (original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs.c Mon Oct 27 
17:28:13 2014
@@ -190,13 +190,26 @@ x_info(const void **fsx_info,
   return SVN_NO_ERROR;
 }
 
+/* Wrapper around svn_fs_x__get_revision_proplist() adapting between function
+   signatures. */
+static svn_error_t *
+x_revision_proplist(apr_hash_t **proplist_p,
+                    svn_fs_t *fs,
+                    svn_revnum_t rev,
+                    apr_pool_t *pool)
+{
+  /* No need to bypass the caches for r/o access to revprops. */
+  return svn_error_trace(svn_fs_x__get_revision_proplist(proplist_p, fs,
+                                                         rev, FALSE, pool));
+}
+
 
 
 /* The vtable associated with a specific open filesystem. */
 static fs_vtable_t fs_vtable = {
   svn_fs_x__youngest_rev,
   svn_fs_x__revision_prop,
-  svn_fs_x__revision_proplist,
+  x_revision_proplist,
   svn_fs_x__change_rev_prop,
   svn_fs_x__set_uuid,
   svn_fs_x__revision_root,

Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs.h?rev=1634609&r1=1634608&r2=1634609&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs.h (original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs.h Mon Oct 27 
17:28:13 2014
@@ -36,7 +36,6 @@
 #include "private/svn_fs_private.h"
 #include "private/svn_sqlite.h"
 #include "private/svn_mutex.h"
-#include "private/svn_named_atomic.h"
 
 #include "id.h"
 
@@ -288,17 +287,9 @@ typedef struct fs_x_data_t
      rep key (revision/offset) to svn_stringbuf_t. */
   svn_cache__t *fulltext_cache;
 
-  /* Access object to the atomics namespace used by revprop caching.
-     Will be NULL until the first access. */
-  svn_atomic_namespace__t *revprop_namespace;
-
   /* Access object to the revprop "generation". Will be NULL until
-     the first access. */
-  svn_named_atomic__t *revprop_generation;
-
-  /* Access object to the revprop update timeout. Will be NULL until
-     the first access. */
-  svn_named_atomic__t *revprop_timeout;
+     the first access.  May be also get closed and set to NULL again. */
+  apr_file_t *revprop_generation_file;
 
   /* Revision property cache.  Maps from (rev,generation) to apr_hash_t. */
   svn_cache__t *revprop_cache;

Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs_x.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs_x.c?rev=1634609&r1=1634608&r2=1634609&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs_x.c (original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs_x.c Mon Oct 
27 17:28:13 2014
@@ -664,7 +664,7 @@ svn_fs_x__revision_proplist(apr_hash_t *
                             svn_revnum_t rev,
                             apr_pool_t *pool)
 {
-  SVN_ERR(svn_fs_x__get_revision_proplist(proplist_p, fs, rev, pool));
+  SVN_ERR(svn_fs_x__get_revision_proplist(proplist_p, fs, rev, FALSE, pool));
 
   return SVN_NO_ERROR;
 }
@@ -960,6 +960,9 @@ svn_fs_x__create(svn_fs_t *fs,
   SVN_ERR(svn_io_file_create_empty(svn_fs_x__path_txn_current_lock(fs, pool),
                                    pool));
 
+  /* Initialize the revprop caching info. */
+  SVN_ERR(svn_fs_x__reset_revprop_generation_file(fs, pool));
+
   /* This filesystem is ready.  Stamp it with a format number. */
   SVN_ERR(svn_fs_x__write_format(fs, FALSE, pool));
 

Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/hotcopy.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/hotcopy.c?rev=1634609&r1=1634608&r2=1634609&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/hotcopy.c 
(original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/hotcopy.c Mon 
Oct 27 17:28:13 2014
@@ -891,12 +891,7 @@ hotcopy_body(void *baton, apr_pool_t *po
    * reset it to zero (since this is on a different path, it will not
    * overlap with data already in cache).  Also, clean up stale files
    * used for the named atomics implementation. */
-  SVN_ERR(svn_io_check_path(svn_fs_x__path_revprop_generation(src_fs, pool),
-                            &kind, pool));
-  if (kind == svn_node_file)
-    SVN_ERR(svn_fs_x__write_revprop_generation_file(dst_fs, 0, pool));
-
-  SVN_ERR(svn_fs_x__cleanup_revprop_namespace(dst_fs));
+  SVN_ERR(svn_fs_x__reset_revprop_generation_file(dst_fs, pool));
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/index.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/index.c?rev=1634609&r1=1634608&r2=1634609&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/index.c 
(original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/index.c Mon Oct 
27 17:28:13 2014
@@ -200,7 +200,7 @@ stream_error_create(packed_number_stream
   apr_off_t offset = 0;
   SVN_ERR(svn_io_file_name_get(&file_name, stream->file,
                                stream->pool));
-  SVN_ERR(svn_io_file_seek(stream->file, SEEK_CUR, &offset, stream->pool));
+  SVN_ERR(svn_io_file_seek(stream->file, APR_CUR, &offset, stream->pool));
 
   return svn_error_createf(err, NULL, message, file_name,
                            (apr_uint64_t)offset);
@@ -292,7 +292,7 @@ packed_stream_read(packed_number_stream_
           /* let's catch corrupted data early.  It would surely cause
            * havoc further down the line. */
           if SVN__PREDICT_FALSE(shift > 8 * sizeof(value))
-            return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_CORRUPTION, NULL,
+            return svn_error_createf(SVN_ERR_FS_INDEX_CORRUPTION, NULL,
                                      _("Corrupt index: number too large"));
        }
     }
@@ -718,7 +718,7 @@ svn_fs_x__l2p_index_create(svn_fs_t *fs,
   /* Paranoia check that makes later casting to int32 safe.
    * The current implementation is limited to 2G entries per page. */
   if (ffd->l2p_page_size > APR_INT32_MAX)
-    return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_OVERFLOW , NULL,
+    return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL,
                             _("L2P index page size  %s" 
                               " exceeds current limit of 2G entries"),
                             apr_psprintf(local_pool, "%" APR_UINT64_T_FMT,
@@ -781,7 +781,7 @@ svn_fs_x__l2p_index_create(svn_fs_t *fs,
           l2p_page_entry_t page_entry = { 0 };
 
           if (proto_entry.item_index > APR_INT32_MAX)
-            return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_OVERFLOW , NULL,
+            return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL,
                                     _("Item index %s too large "
                                       "in l2p proto index for revision %ld"),
                                     apr_psprintf(local_pool,
@@ -808,7 +808,7 @@ svn_fs_x__l2p_index_create(svn_fs_t *fs,
   /* Paranoia check that makes later casting to int32 safe.
    * The current implementation is limited to 2G pages per index. */
   if (page_counts->nelts > APR_INT32_MAX)
-    return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_OVERFLOW , NULL,
+    return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL,
                             _("L2P index page count  %d"
                               " exceeds current limit of 2G pages"),
                             page_counts->nelts);
@@ -915,7 +915,7 @@ l2p_header_copy(l2p_page_info_baton_t *b
   /* revision offset within the index file */
   apr_size_t rel_revision = baton->revision - header->first_revision;
   if (rel_revision >= header->revision_count)
-    return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_REVISION , NULL,
+    return svn_error_createf(SVN_ERR_FS_INDEX_REVISION , NULL,
                              _("Revision %ld not covered by item index"),
                              baton->revision);
 
@@ -941,7 +941,7 @@ l2p_header_copy(l2p_page_info_baton_t *b
       max_item_index =   (apr_uint64_t)header->page_size
                        * (last_entry - first_entry);
       if (baton->item_index >= max_item_index)
-        return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_OVERFLOW , NULL,
+        return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL,
                                 _("Item index %s exceeds l2p limit "
                                   "of %s for revision %ld"),
                                 apr_psprintf(scratch_pool,
@@ -1070,7 +1070,7 @@ get_l2p_header_body(l2p_header_t **heade
 
   if (result->first_revision > revision
       || result->first_revision + result->revision_count <= revision)
-    return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_CORRUPTION, NULL,
+    return svn_error_createf(SVN_ERR_FS_INDEX_CORRUPTION, NULL,
                       _("Corrupt L2P index for r%ld only covers r%ld:%ld"),
                       revision, result->first_revision,
                       result->first_revision + result->revision_count);
@@ -1292,7 +1292,7 @@ l2p_page_get_offset(l2p_page_baton_t *ba
 {
   /* overflow check */
   if (page->entry_count <= baton->page_offset)
-    return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_OVERFLOW , NULL,
+    return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL,
                              _("Item index %s too large in"
                                " revision %ld"),
                              apr_psprintf(pool, "%" APR_UINT64_T_FMT,
@@ -1843,7 +1843,7 @@ svn_fs_x__p2l_index_create(svn_fs_t *fs,
      = svn_spillbuf__create(0x10000, 0x1000000, local_pool);
 
   /* for loop temps ... */
-  apr_pool_t *iter_pool = svn_pool_create(pool);
+  apr_pool_t *iterpool = svn_pool_create(pool);
 
   /* start at the beginning of the source file */
   SVN_ERR(svn_io_file_open(&proto_index, proto_file_name,
@@ -1861,20 +1861,20 @@ svn_fs_x__p2l_index_create(svn_fs_t *fs,
       svn_revnum_t last_revision = revision;
       apr_uint64_t last_number = 0;
 
-      svn_pool_clear(iter_pool);
+      svn_pool_clear(iterpool);
 
       /* (attempt to) read the next entry from the source */
       SVN_ERR(svn_io_file_read_full2(proto_index, &entry, sizeof(entry),
-                                     &read, &eof, iter_pool));
+                                     &read, &eof, iterpool));
       SVN_ERR_ASSERT(eof || read == sizeof(entry));
 
       if (entry.item_count && !eof)
         {
           to_read = entry.item_count * sizeof(*entry.items);
-          entry.items = apr_palloc(iter_pool, to_read);
+          entry.items = apr_palloc(iterpool, to_read);
 
           SVN_ERR(svn_io_file_read_full2(proto_index, entry.items, to_read,
-                                         &read, &eof, iter_pool));
+                                         &read, &eof, iterpool));
           SVN_ERR_ASSERT(eof || read == to_read);
         }
 
@@ -1884,7 +1884,7 @@ svn_fs_x__p2l_index_create(svn_fs_t *fs,
           apr_size_t entry_size;
           to_read = sizeof(entry_size);
           SVN_ERR(svn_io_file_read_full2(proto_index, &entry_size, to_read,
-                                         &read, &eof, iter_pool));
+                                         &read, &eof, iterpool));
           SVN_ERR_ASSERT(eof || read == to_read);
         }
 
@@ -1925,20 +1925,20 @@ svn_fs_x__p2l_index_create(svn_fs_t *fs,
         {
           SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded,
                                       encode_uint(encoded, entry.offset),
-                                      iter_pool));
+                                      iterpool));
           last_revision = revision;
         }
 
       /* write simple item / container entry */
       SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded,
                                   encode_uint(encoded, entry.size),
-                                  iter_pool));
+                                  iterpool));
       SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded,
                                   encode_uint(encoded, entry.type + 
entry.item_count * 16),
-                                  iter_pool));
+                                  iterpool));
       SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded,
                                   encode_uint(encoded, entry.fnv1_checksum),
-                                  iter_pool));
+                                  iterpool));
 
       /* container contents (only one for non-container items) */
       for (sub_item = 0; sub_item < entry.item_count; ++sub_item)
@@ -1948,7 +1948,7 @@ svn_fs_x__p2l_index_create(svn_fs_t *fs,
           apr_int64_t diff = item_rev - last_revision;
           SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded,
                                       encode_int(encoded, diff),
-                                      iter_pool));
+                                      iterpool));
           last_revision = item_rev;
         }
 
@@ -1957,7 +1957,7 @@ svn_fs_x__p2l_index_create(svn_fs_t *fs,
           apr_int64_t diff = entry.items[sub_item].number - last_number;
           SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded,
                                       encode_int(encoded, diff),
-                                      iter_pool));
+                                      iterpool));
           last_number = entry.items[sub_item].number;
         }
 
@@ -2007,7 +2007,7 @@ svn_fs_x__p2l_index_create(svn_fs_t *fs,
   SVN_ERR(svn_io_file_close(index_file, local_pool));
   SVN_ERR(svn_io_set_file_read_only(file_name, FALSE, local_pool));
 
-  svn_pool_destroy(iter_pool);
+  svn_pool_destroy(iterpool);
   svn_pool_destroy(local_pool);
 
   return SVN_NO_ERROR;
@@ -2438,7 +2438,7 @@ get_p2l_keys(p2l_page_info_baton_t *page
   if (page_info.page_count <= page_info.page_no)
     {
       SVN_ERR(packed_stream_close(*stream));
-      return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_OVERFLOW , NULL,
+      return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL,
                                _("Offset %s too large in revision %ld"),
                                apr_off_t_toa(pool, offset), revision);
     }

Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/noderevs.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/noderevs.c?rev=1634609&r1=1634608&r2=1634609&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/noderevs.c 
(original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/noderevs.c Mon 
Oct 27 17:28:13 2014
@@ -60,8 +60,6 @@ typedef struct binary_id_t
 } binary_id_t;
 
 /* Our internal representation of an representation.
- * We simply omit the uniquifier, which allows us to share instances of
- * binary_representation_t and uniquify them in a shared_representation_t.
  */
 typedef struct binary_representation_t
 {

Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/pack.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/pack.c?rev=1634609&r1=1634608&r2=1634609&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/pack.c (original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/pack.c Mon Oct 
27 17:28:13 2014
@@ -483,7 +483,7 @@ copy_item_to_temp(pack_context_t *contex
   svn_fs_x__p2l_entry_t *new_entry
     = svn_fs_x__p2l_entry_dup(entry, context->info_pool);
   new_entry->offset = 0;
-  SVN_ERR(svn_io_file_seek(temp_file, SEEK_CUR, &new_entry->offset, pool));
+  SVN_ERR(svn_io_file_seek(temp_file, APR_CUR, &new_entry->offset, pool));
   APR_ARRAY_PUSH(entries, svn_fs_x__p2l_entry_t *) = new_entry;
   
   SVN_ERR(copy_file_data(context, temp_file, rev_file, entry->size, pool));
@@ -573,7 +573,7 @@ copy_rep_to_temp(pack_context_t *context
    * store it in CONTEXT */
   entry = svn_fs_x__p2l_entry_dup(entry, context->info_pool);
   entry->offset = 0;
-  SVN_ERR(svn_io_file_seek(context->reps_file, SEEK_CUR, &entry->offset,
+  SVN_ERR(svn_io_file_seek(context->reps_file, APR_CUR, &entry->offset,
                            pool));
   add_item_rep_mapping(context, entry);
 
@@ -596,7 +596,7 @@ copy_rep_to_temp(pack_context_t *context
     }
 
   /* copy the whole rep (including header!) to our temp file */
-  SVN_ERR(svn_io_file_seek(rev_file, SEEK_SET, &source_offset, pool));
+  SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &source_offset, pool));
   SVN_ERR(copy_file_data(context, context->reps_file, rev_file, entry->size,
                          pool));
 
@@ -699,12 +699,12 @@ copy_node_to_temp(pack_context_t *contex
    * store it in CONTEXT */
   entry = svn_fs_x__p2l_entry_dup(entry, context->info_pool);
   entry->offset = 0;
-  SVN_ERR(svn_io_file_seek(context->reps_file, SEEK_CUR,
+  SVN_ERR(svn_io_file_seek(context->reps_file, APR_CUR,
                            &entry->offset, pool));
   add_item_rep_mapping(context, entry);
 
   /* copy the noderev to our temp file */
-  SVN_ERR(svn_io_file_seek(rev_file, SEEK_SET, &source_offset, pool));
+  SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &source_offset, pool));
   SVN_ERR(copy_file_data(context, context->reps_file, rev_file, entry->size,
                          pool));
 
@@ -1245,7 +1245,7 @@ write_reps_container(pack_context_t *con
 
   SVN_ERR(svn_fs_x__write_reps_container(pack_stream, container, pool));
   SVN_ERR(svn_stream_close(pack_stream));
-  SVN_ERR(svn_io_file_seek(context->pack_file, SEEK_CUR, &offset, pool));
+  SVN_ERR(svn_io_file_seek(context->pack_file, APR_CUR, &offset, pool));
 
   container_entry.offset = context->pack_offset;
   container_entry.size = offset - container_entry.offset;
@@ -1327,7 +1327,7 @@ write_reps_containers(pack_context_t *co
 
       /* select the change list in the source file, parse it and add it to
        * the container */
-      SVN_ERR(svn_io_file_seek(temp_file, SEEK_SET, &entry->offset,
+      SVN_ERR(svn_io_file_seek(temp_file, APR_SET, &entry->offset,
                                iterpool));
       SVN_ERR(svn_fs_x__get_representation_length(&representation.size,
                                              &representation.expanded_size,
@@ -1414,7 +1414,7 @@ store_items(pack_context_t *context,
 
       /* select the item in the source file and copy it into the target
        * pack file */
-      SVN_ERR(svn_io_file_seek(temp_file, SEEK_SET, &entry->offset,
+      SVN_ERR(svn_io_file_seek(temp_file, APR_SET, &entry->offset,
                                iterpool));
       SVN_ERR(copy_file_data(context, context->pack_file, temp_file,
                              entry->size, iterpool));
@@ -1558,7 +1558,7 @@ write_changes_container(pack_context_t *
                                              container,
                                              pool));
   SVN_ERR(svn_stream_close(pack_stream));
-  SVN_ERR(svn_io_file_seek(context->pack_file, SEEK_CUR, &offset, pool));
+  SVN_ERR(svn_io_file_seek(context->pack_file, APR_CUR, &offset, pool));
 
   container_entry.offset = context->pack_offset;
   container_entry.size = offset - container_entry.offset;
@@ -1654,7 +1654,7 @@ write_changes_containers(pack_context_t 
 
       /* select the change list in the source file, parse it and add it to
        * the container */
-      SVN_ERR(svn_io_file_seek(temp_file, SEEK_SET, &entry->offset,
+      SVN_ERR(svn_io_file_seek(temp_file, APR_SET, &entry->offset,
                                iterpool));
       SVN_ERR(svn_fs_x__read_changes(&changes, temp_stream, iterpool));
       SVN_ERR(svn_fs_x__changes_append_list(&list_index, container, changes));
@@ -1880,7 +1880,7 @@ pack_range(pack_context_t *context,
               offset = entry->offset;
               if (offset < finfo.size)
                 {
-                  SVN_ERR(svn_io_file_seek(rev_file, SEEK_SET, &offset,
+                  SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &offset,
                                            iterpool));
 
                   if (entry->type == SVN_FS_X__ITEM_TYPE_CHANGES)

Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/recovery.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/recovery.c?rev=1634609&r1=1634608&r2=1634609&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/recovery.c 
(original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/recovery.c Mon 
Oct 27 17:28:13 2014
@@ -122,7 +122,7 @@ recover_body(void *baton, apr_pool_t *po
   svn_node_kind_t youngest_revprops_kind;
 
   /* Lose potentially corrupted data in temp files */
-  SVN_ERR(svn_fs_x__cleanup_revprop_namespace(fs));
+  SVN_ERR(svn_fs_x__reset_revprop_generation_file(fs, pool));
 
   /* We need to know the largest revision in the filesystem. */
   SVN_ERR(recover_get_largest_revision(fs, &max_rev, pool));

Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/revprops.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/revprops.c?rev=1634609&r1=1634608&r2=1634609&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/revprops.c 
(original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/revprops.c Mon 
Oct 27 17:28:13 2014
@@ -21,6 +21,7 @@
  */
 
 #include <assert.h>
+#include <apr_md5.h>
 
 #include "svn_pools.h"
 #include "svn_hash.h"
@@ -42,11 +43,15 @@
    process got aborted and that we have re-read revprops. */
 #define REVPROP_CHANGE_TIMEOUT (10 * 1000000)
 
-/* The following are names of atomics that will be used to communicate
- * revprop updates across all processes on this machine. */
-#define ATOMIC_REVPROP_GENERATION "rev-prop-generation"
-#define ATOMIC_REVPROP_TIMEOUT    "rev-prop-timeout"
-#define ATOMIC_REVPROP_NAMESPACE  "rev-prop-atomics"
+/* In case of an inconsistent read, close the generation file, yield,
+   re-open and re-read.  This is the number of times we try this before
+   giving up. */
+#define GENERATION_READ_RETRY_COUNT 100
+
+/* Maximum size of the generation number file contents (including NUL). */
+#define CHECKSUMMED_NUMBER_BUFFER_LEN \
+           (SVN_INT64_BUFFER_SIZE + 3 + APR_MD5_DIGESTSIZE * 2)
+
 
 svn_error_t *
 svn_fs_x__upgrade_pack_revprops(svn_fs_t *fs,
@@ -147,179 +152,251 @@ svn_fs_x__upgrade_cleanup_pack_revprops(
  *
  * Revprop caching needs to be activated and will be deactivated for the
  * respective FS instance if the necessary infrastructure could not be
- * initialized.  In deactivated mode, there is almost no runtime overhead
- * associated with revprop caching.  As long as no revprops are being read
- * or changed, revprop caching imposes no overhead.
+ * initialized.  As long as no revprops are being read or changed, revprop
+ * caching imposes no overhead.
  *
  * When activated, we cache revprops using (revision, generation) pairs
  * as keys with the generation being incremented upon every revprop change.
  * Since the cache is process-local, the generation needs to be tracked
  * for at least as long as the process lives but may be reset afterwards.
  *
- * To track the revprop generation, we use two-layer approach. On the lower
- * level, we use named atomics to have a system-wide consistent value for
- * the current revprop generation.  However, those named atomics will only
- * remain valid for as long as at least one process / thread in the system
- * accesses revprops in the respective repository.  The underlying shared
- * memory gets cleaned up afterwards.
- *
- * On the second level, we will use a persistent file to track the latest
- * revprop generation.  It will be written upon each revprop change but
- * only be read if we are the first process to initialize the named atomics
- * with that value.
- *
- * The overhead for the second and following accesses to revprops is
- * almost zero on most systems.
- *
- *
- * Tech aspects:
- * -------------
- *
- * A problem is that we need to provide a globally available file name to
- * back the SHM implementation on OSes that need it.  We can only assume
- * write access to some file within the respective repositories.  Because
- * a given server process may access thousands of repositories during its
- * lifetime, keeping the SHM data alive for all of them is also not an
- * option.
- *
- * So, we store the new revprop generation on disk as part of each
- * setrevprop call, i.e. this write will be serialized and the write order
- * be guaranteed by the repository write lock.
- *
- * The only racy situation occurs when the data is being read again by two
- * processes concurrently but in that situation, the first process to
- * finish that procedure is guaranteed to be the only one that initializes
- * the SHM data.  Since even writers will first go through that
- * initialization phase, they will never operate on stale data.
+ * We track the revprop generation in a persistent, unbuffered file that
+ * we may keep open for the lifetime of the svn_fs_t.  It is the OS'
+ * responsibility to provide us with the latest contents upon read.  To
+ * detect incomplete updates due to non-atomic reads, we put a MD5 checksum
+ * next to the actual generation number and verify that it matches.
+ *
+ * Since we cannot guarantee that the OS will provide us with up-to-date
+ * data buffers for open files, we re-open and re-read the file before
+ * modifying it.  This will prevent lost updates.
+ *
+ * A race condition exists between switching to the modified revprop data
+ * and bumping the generation number.  In particular, the process may crash
+ * just after switching to the new revprop data and before bumping the
+ * generation.  To be able to detect this scenario, we bump the generation
+ * twice per revprop change: once immediately before (creating an odd number)
+ * and once after the atomic switch (even generation).
+ *
+ * A writer holding the write lock can immediately assume a crashed writer
+ * in case of an odd generation or they would not have been able to acquire
+ * the lock.  A reader detecting an odd generation will use that number and
+ * be forced to re-read any revprop data - usually getting the new revprops
+ * already.  If the generation file modification timestamp is too old, the
+ * reader will assume a crashed writer, acquire the write lock and bump
+ * the generation if it is still odd.  So, for about REVPROP_CHANGE_TIMEOUT
+ * after the crash, reader caches may be stale.
  */
 
-/* Read revprop generation as stored on disk for repository FS. The result
- * is returned in *CURRENT. Default to 2 if no such file is available.
+/* If the revprop generation file in FS is open, close it.  This is a no-op
+ * if the file is not open.
  */
 static svn_error_t *
-read_revprop_generation_file(apr_int64_t *current,
-                             svn_fs_t *fs,
-                             apr_pool_t *pool)
+close_revprop_generation_file(svn_fs_t *fs,
+                              apr_pool_t *scratch_pool)
 {
-  svn_error_t *err;
-  apr_file_t *file;
-  char buf[80];
-  apr_size_t len;
-  const char *path = svn_fs_x__path_revprop_generation(fs, pool);
-
-  err = svn_io_file_open(&file, path,
-                         APR_READ | APR_BUFFERED,
-                         APR_OS_DEFAULT, pool);
-  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
+  fs_x_data_t *ffd = fs->fsap_data;
+  if (ffd->revprop_generation_file)
     {
-      svn_error_clear(err);
-      *current = 2;
-
-      return SVN_NO_ERROR;
+      SVN_ERR(svn_io_file_close(ffd->revprop_generation_file, scratch_pool));
+      ffd->revprop_generation_file = NULL;
     }
-  SVN_ERR(err);
-
-  len = sizeof(buf);
-  SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
 
-  /* Check that the first line contains only digits. */
-  SVN_ERR(svn_fs_x__check_file_buffer_numeric(buf, 0, path,
-                                              "Revprop Generation", pool));
-  SVN_ERR(svn_cstring_atoi64(current, buf));
-
-  return svn_io_file_close(file, pool);
+  return SVN_NO_ERROR;
 }
 
-/* Write the CURRENT revprop generation to disk for repository FS.
+/* Make sure the revprop_generation member in FS is set.  If READ_ONLY is
+ * set, open the file w/o write permission if the file is not open yet.
+ * The file is kept open if it has sufficient rights (or more) but will be
+ * closed and re-opened if it provided insufficient access rights.
+ *
+ * Call only for repos that support revprop caching.
  */
-svn_error_t *
-svn_fs_x__write_revprop_generation_file(svn_fs_t *fs,
-                                        apr_int64_t current,
-                                        apr_pool_t *pool)
+static svn_error_t *
+open_revprop_generation_file(svn_fs_t *fs,
+                             svn_boolean_t read_only,
+                             apr_pool_t *scratch_pool)
 {
-  char buf[SVN_INT64_BUFFER_SIZE];
-  apr_size_t len = svn__i64toa(buf, current);
-  buf[len] = '\n';
+  fs_x_data_t *ffd = fs->fsap_data;
+  apr_int32_t flags = read_only ? APR_READ : (APR_READ | APR_WRITE);
 
-  SVN_ERR(svn_io_write_atomic(svn_fs_x__path_revprop_generation(fs, pool),
-                              buf, len + 1,
-                              NULL /* copy_perms */, pool));
+  /* Close the current file handle if it has insufficient rights. */
+  if (   ffd->revprop_generation_file
+      && (apr_file_flags_get(ffd->revprop_generation_file) & flags) != flags)
+    SVN_ERR(close_revprop_generation_file(fs, scratch_pool));
+
+  /* If not open already, open with sufficient rights. */
+  if (ffd->revprop_generation_file == NULL)
+    {
+      const char *path = svn_fs_x__path_revprop_generation(fs, scratch_pool);
+      SVN_ERR(svn_io_file_open(&ffd->revprop_generation_file, path,
+                               flags, APR_OS_DEFAULT, fs->pool));
+    }
 
   return SVN_NO_ERROR;
 }
 
-/* Make sure the revprop_namespace member in FS is set. */
+/* Return the textual representation of NUMBER and its checksum in *BUFFER.
+ */
 static svn_error_t *
-ensure_revprop_namespace(svn_fs_t *fs)
+checkedsummed_number(svn_stringbuf_t **buffer,
+                     apr_int64_t number,
+                     apr_pool_t *result_pool,
+                     apr_pool_t *scratch_pool)
 {
-  fs_x_data_t *ffd = fs->fsap_data;
+  svn_checksum_t *checksum;
+  const char *digest;
+
+  char str[SVN_INT64_BUFFER_SIZE];
+  apr_size_t len = svn__i64toa(str, number);
+  str[len] = 0;
 
-  return ffd->revprop_namespace == NULL
-    ? svn_atomic_namespace__create(&ffd->revprop_namespace,
-                                   svn_dirent_join(fs->path,
-                                                   ATOMIC_REVPROP_NAMESPACE,
-                                                   fs->pool),
-                                   fs->pool)
-    : SVN_NO_ERROR;
+  SVN_ERR(svn_checksum(&checksum, svn_checksum_md5, str, len, scratch_pool));
+  digest = svn_checksum_to_cstring_display(checksum, scratch_pool);
+
+  *buffer = svn_stringbuf_createf(result_pool, "%s %s\n", digest, str);
+
+  return SVN_NO_ERROR;
 }
 
-svn_error_t *
-svn_fs_x__cleanup_revprop_namespace(svn_fs_t *fs)
+/* Extract the generation number from the text BUFFER of LEN bytes and
+ * verify it against the checksum in the same BUFFER.  If they match, return
+ * the generation in *NUMBER.  Otherwise, return an error.
+ * BUFFER does not need to be NUL-terminated.
+ */
+static svn_error_t *
+verify_extract_number(apr_int64_t *number,
+                      const char *buffer,
+                      apr_size_t len,
+                      apr_pool_t *scratch_pool)
 {
-  const char *name = svn_dirent_join(fs->path,
-                                     ATOMIC_REVPROP_NAMESPACE,
-                                     fs->pool);
-  return svn_error_trace(svn_atomic_namespace__cleanup(name, fs->pool));
+  const char *digest_end = strchr(buffer, ' ');
+
+  /* Does the buffer even contain checksum _and_ number? */
+  if (digest_end != NULL)
+    {
+      svn_checksum_t *expected;
+      svn_checksum_t *actual;
+
+      SVN_ERR(svn_checksum_parse_hex(&expected, svn_checksum_md5, buffer,
+                                     scratch_pool));
+      SVN_ERR(svn_checksum(&actual, svn_checksum_md5, digest_end + 1,
+                           (buffer + len) - (digest_end + 1), scratch_pool));
+
+      if (svn_checksum_match(expected, actual))
+        return svn_error_trace(svn_cstring_atoi64(number, digest_end + 1));
+    }
+
+  /* Incomplete buffer or not a match. */
+  return svn_error_create(SVN_ERR_FS_INVALID_GENERATION, NULL,
+                          _("Invalid generation number data."));
 }
 
-/* Make sure the revprop_generation member in FS is set and, if necessary,
- * initialized with the latest value stored on disk.
+/* Read revprop generation as stored on disk for repository FS. The result is
+ * returned in *CURRENT.  Call only for repos that support revprop caching.
  */
 static svn_error_t *
-ensure_revprop_generation(svn_fs_t *fs, apr_pool_t *pool)
+read_revprop_generation_file(apr_int64_t *current,
+                             svn_fs_t *fs,
+                             apr_pool_t *scratch_pool)
 {
   fs_x_data_t *ffd = fs->fsap_data;
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  char buf[CHECKSUMMED_NUMBER_BUFFER_LEN];
+  apr_size_t len;
+  apr_off_t offset = 0;
+  int i;
+  svn_error_t *err = SVN_NO_ERROR;
 
-  SVN_ERR(ensure_revprop_namespace(fs));
-  if (ffd->revprop_generation == NULL)
+  /* Retry in case of incomplete file buffer updates. */
+  for (i = 0; i < GENERATION_READ_RETRY_COUNT; ++i)
     {
-      apr_int64_t current;
+      svn_error_clear(err);
+      svn_pool_clear(iterpool);
 
-      SVN_ERR(svn_named_atomic__get(&ffd->revprop_generation,
-                                    ffd->revprop_namespace,
-                                    ATOMIC_REVPROP_GENERATION,
-                                    TRUE));
-
-      /* If the generation is at 0, we just created a new namespace
-       * (it would be at least 2 otherwise). Read the latest generation
-       * from disk and if we are the first one to initialize the atomic
-       * (i.e. is still 0), set it to the value just gotten.
+      /* If we can't even access the data, things are very wrong.
+       * Don't retry in that case.
        */
-      SVN_ERR(svn_named_atomic__read(&current, ffd->revprop_generation));
-      if (current == 0)
-        {
-          SVN_ERR(read_revprop_generation_file(&current, fs, pool));
-          SVN_ERR(svn_named_atomic__cmpxchg(NULL, current, 0,
-                                            ffd->revprop_generation));
-        }
+      SVN_ERR(open_revprop_generation_file(fs, TRUE, iterpool));
+      SVN_ERR(svn_io_file_seek(ffd->revprop_generation_file, APR_SET, &offset,
+                              iterpool));
+
+      len = sizeof(buf);
+      SVN_ERR(svn_io_read_length_line(ffd->revprop_generation_file, buf, &len,
+                                      iterpool));
+
+      /* Some data has been read.  It will most likely be complete and
+       * consistent.  Extract and verify anyway. */
+      err = verify_extract_number(current, buf, len, iterpool);
+      if (!err)
+        break;
+
+      /* Got unlucky and data was invalid.  Retry. */
+      SVN_ERR(close_revprop_generation_file(fs, iterpool));
+
+#if APR_HAS_THREADS
+      apr_thread_yield();
+#else
+      apr_sleep(0);
+#endif
     }
 
-  return SVN_NO_ERROR;
+  svn_pool_destroy(iterpool);
+
+  /* If we had to give up, propagate the error. */
+  return svn_error_trace(err);
 }
 
-/* Make sure the revprop_timeout member in FS is set. */
+/* Write the CURRENT revprop generation to disk for repository FS.
+ * Call only for repos that support revprop caching.
+ */
 static svn_error_t *
-ensure_revprop_timeout(svn_fs_t *fs)
+write_revprop_generation_file(svn_fs_t *fs,
+                              apr_int64_t current,
+                              apr_pool_t *scratch_pool)
 {
   fs_x_data_t *ffd = fs->fsap_data;
+  svn_stringbuf_t *buffer;
+  apr_off_t offset = 0;
+
+  SVN_ERR(checkedsummed_number(&buffer, current, scratch_pool, scratch_pool));
+
+  SVN_ERR(open_revprop_generation_file(fs, FALSE, scratch_pool));
+  SVN_ERR(svn_io_file_seek(ffd->revprop_generation_file, APR_SET, &offset,
+                           scratch_pool));
+  SVN_ERR(svn_io_file_write_full(ffd->revprop_generation_file, buffer->data,
+                                 buffer->len, NULL, scratch_pool));
+  SVN_ERR(svn_io_file_flush_to_disk(ffd->revprop_generation_file,
+                                    scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_x__reset_revprop_generation_file(svn_fs_t *fs,
+                                        apr_pool_t *scratch_pool)
+{
+  const char *path = svn_fs_x__path_revprop_generation(fs, scratch_pool);
+  svn_stringbuf_t *buffer;
 
-  SVN_ERR(ensure_revprop_namespace(fs));
-  return ffd->revprop_timeout == NULL
-    ? svn_named_atomic__get(&ffd->revprop_timeout,
-                            ffd->revprop_namespace,
-                            ATOMIC_REVPROP_TIMEOUT,
-                            TRUE)
-    : SVN_NO_ERROR;
+  /* Unconditionally close the revprop generation file.
+   * Don't care about FS formats. This ensures consistent internal state. */
+  SVN_ERR(close_revprop_generation_file(fs, scratch_pool));
+
+  /* Unconditionally remove any old revprop generation file.
+   * Don't care about FS formats.  This ensures consistent on-disk state
+   * for old format repositories. */
+  SVN_ERR(svn_io_remove_file2(path, TRUE, scratch_pool));
+
+  /* Write the initial revprop generation file contents, if supported by
+   * the current format.  This ensures consistent on-disk state for new
+   * format repositories. */
+  SVN_ERR(checkedsummed_number(&buffer, 0, scratch_pool, scratch_pool));
+  SVN_ERR(svn_io_write_atomic(path, buffer->data, buffer->len, NULL,
+                              scratch_pool));
+
+  /* ffd->revprop_generation_file will be re-opened on demand. */
+
+  return SVN_NO_ERROR;
 }
 
 /* Create an error object with the given MESSAGE and pass it to the
@@ -344,7 +421,8 @@ log_revprop_cache_init_warning(svn_fs_t 
 /* Test whether revprop cache and necessary infrastructure are
    available in FS. */
 static svn_boolean_t
-has_revprop_cache(svn_fs_t *fs, apr_pool_t *pool)
+has_revprop_cache(svn_fs_t *fs,
+                  apr_pool_t *scratch_pool)
 {
   fs_x_data_t *ffd = fs->fsap_data;
   svn_error_t *error;
@@ -353,23 +431,8 @@ has_revprop_cache(svn_fs_t *fs, apr_pool
   if (ffd->revprop_cache == NULL)
     return FALSE;
 
-  /* is it efficient? */
-  if (!svn_named_atomic__is_efficient())
-    {
-      /* access to it would be quite slow
-       * -> disable the revprop cache for good
-       */
-      ffd->revprop_cache = NULL;
-      log_revprop_cache_init_warning(fs, NULL,
-                                     "Revprop caching for '%s' disabled"
-                                     " because it would be inefficient.",
-                                     pool);
-
-      return FALSE;
-    }
-
-  /* try to access our SHM-backed infrastructure */
-  error = ensure_revprop_generation(fs, pool);
+  /* try initialize our file-backed infrastructure */
+  error = open_revprop_generation_file(fs, TRUE, scratch_pool);
   if (error)
     {
       /* failure -> disable revprop cache for good */
@@ -377,9 +440,9 @@ has_revprop_cache(svn_fs_t *fs, apr_pool
       ffd->revprop_cache = NULL;
       log_revprop_cache_init_warning(fs, error,
                                      "Revprop caching for '%s' disabled "
-                                     "because SHM infrastructure for revprop "
+                                     "because infrastructure for revprop "
                                      "caching failed to initialize.",
-                                     pool);
+                                     scratch_pool);
 
       return FALSE;
     }
@@ -393,8 +456,8 @@ typedef struct revprop_generation_fixup_
   /* revprop generation to read */
   apr_int64_t *generation;
 
-  /* containing the revprop_generation member to query */
-  fs_x_data_t *ffd;
+  /* file system context */
+  svn_fs_t *fs;
 } revprop_generation_upgrade_t;
 
 /* If the revprop generation has an odd value, it means the original writer
@@ -406,23 +469,31 @@ typedef struct revprop_generation_fixup_
  */
 static svn_error_t *
 revprop_generation_fixup(void *void_baton,
-                         apr_pool_t *pool)
+                         apr_pool_t *scratch_pool)
 {
   revprop_generation_upgrade_t *baton = void_baton;
-  assert(baton->ffd->has_write_lock);
+  fs_x_data_t *ffd = baton->fs->fsap_data;
+  assert(ffd->has_write_lock);
+
+  /* Make sure we don't operate on stale OS buffers. */
+  SVN_ERR(close_revprop_generation_file(baton->fs, scratch_pool));
 
   /* Maybe, either the original revprop writer or some other reader has
      already corrected / bumped the revprop generation.  Thus, we need
-     to read it again. */
-  SVN_ERR(svn_named_atomic__read(baton->generation,
-                                 baton->ffd->revprop_generation));
+     to read it again.  However, we will now be the only ones changing
+     the file contents due to us holding the write lock. */
+  SVN_ERR(read_revprop_generation_file(baton->generation, baton->fs,
+                                       scratch_pool));
 
   /* Cause everyone to re-read revprops upon their next access, if the
      last revprop write did not complete properly. */
-  while (*baton->generation % 2)
-    SVN_ERR(svn_named_atomic__add(baton->generation,
-                                  1,
-                                  baton->ffd->revprop_generation));
+  if (*baton->generation % 2)
+    {
+      ++*baton->generation;
+      SVN_ERR(write_revprop_generation_file(baton->fs,
+                                            *baton->generation,
+                                            scratch_pool));
+    }
 
   return SVN_NO_ERROR;
 }
@@ -433,42 +504,46 @@ revprop_generation_fixup(void *void_bato
 static svn_error_t *
 read_revprop_generation(apr_int64_t *generation,
                         svn_fs_t *fs,
-                        apr_pool_t *pool)
+                        apr_pool_t *scratch_pool)
 {
   apr_int64_t current = 0;
   fs_x_data_t *ffd = fs->fsap_data;
 
   /* read the current revprop generation number */
-  SVN_ERR(ensure_revprop_generation(fs, pool));
-  SVN_ERR(svn_named_atomic__read(&current, ffd->revprop_generation));
+  SVN_ERR(read_revprop_generation_file(&current, fs, scratch_pool));
 
   /* is an unfinished revprop write under the way? */
   if (current % 2)
     {
-      apr_int64_t timeout = 0;
+      svn_boolean_t timeout = FALSE;
 
-      /* read timeout for the write operation */
-      SVN_ERR(ensure_revprop_timeout(fs));
-      SVN_ERR(svn_named_atomic__read(&timeout, ffd->revprop_timeout));
-
-      /* has the writer process been aborted,
-       * i.e. has the timeout been reached?
+      /* Has the writer process been aborted?
+       * Either by timeout or by us being the writer now.
        */
-      if (apr_time_now() > timeout)
+      if (!ffd->has_write_lock)
+        {
+          apr_time_t mtime;
+          SVN_ERR(svn_io_file_affected_time(&mtime,
+                        svn_fs_x__path_revprop_generation(fs, scratch_pool),
+                        scratch_pool));
+          timeout = apr_time_now() > mtime + REVPROP_CHANGE_TIMEOUT;
+        }
+
+      if (ffd->has_write_lock || timeout)
         {
           revprop_generation_upgrade_t baton;
           baton.generation = &current;
-          baton.ffd = ffd;
+          baton.fs = fs;
 
           /* Ensure that the original writer process no longer exists by
            * acquiring the write lock to this repository.  Then, fix up
            * the revprop generation.
            */
           if (ffd->has_write_lock)
-            SVN_ERR(revprop_generation_fixup(&baton, pool));
+            SVN_ERR(revprop_generation_fixup(&baton, scratch_pool));
           else
             SVN_ERR(svn_fs_x__with_write_lock(fs, revprop_generation_fixup,
-                                              &baton, pool));
+                                              &baton, scratch_pool));
         }
     }
 
@@ -477,64 +552,54 @@ read_revprop_generation(apr_int64_t *gen
   return SVN_NO_ERROR;
 }
 
-/* Set the revprop generation to the next odd number to indicate that
-   there is a revprop write process under way. If that times out,
-   readers shall recover from that state & re-read revprops.
-   Use the access object in FS to set the shared mem value. */
+/* Set the revprop generation in FS to the next odd number to indicate
+   that there is a revprop write process under way.  Return that value
+   in *GENERATION.  If the change times out, readers shall recover from
+   that state & re-read revprops.
+   This is a no-op for repo formats that don't support revprop caching. */
 static svn_error_t *
-begin_revprop_change(svn_fs_t *fs, apr_pool_t *pool)
+begin_revprop_change(apr_int64_t *generation,
+                     svn_fs_t *fs,
+                     apr_pool_t *scratch_pool)
 {
-  apr_int64_t current;
   fs_x_data_t *ffd = fs->fsap_data;
+  SVN_ERR_ASSERT(ffd->has_write_lock);
 
-  /* set the timeout for the write operation */
-  SVN_ERR(ensure_revprop_timeout(fs));
-  SVN_ERR(svn_named_atomic__write(NULL,
-                                  apr_time_now() + REVPROP_CHANGE_TIMEOUT,
-                                  ffd->revprop_timeout));
+  /* Close and re-open to make sure we read the latest data. */
+  SVN_ERR(close_revprop_generation_file(fs, scratch_pool));
+  SVN_ERR(open_revprop_generation_file(fs, FALSE, scratch_pool));
 
-  /* set the revprop generation to an odd value to indicate
-   * that a write is in progress
+  /* Set the revprop generation to an odd value to indicate
+   * that a write is in progress.
    */
-  SVN_ERR(ensure_revprop_generation(fs, pool));
-  do
-    {
-      SVN_ERR(svn_named_atomic__add(&current,
-                                    1,
-                                    ffd->revprop_generation));
-    }
-  while (current % 2 == 0);
+  SVN_ERR(read_revprop_generation(generation, fs, scratch_pool));
+  ++*generation;
+  SVN_ERR(write_revprop_generation_file(fs, *generation, scratch_pool));
 
   return SVN_NO_ERROR;
 }
 
-/* Set the revprop generation to the next even number to indicate that
+/* Set the revprop generation in FS to the next even generation after
+   the odd value in GENERATION to indicate that
    a) readers shall re-read revprops, and
-   b) the write process has been completed (no recovery required)
-   Use the access object in FS to set the shared mem value. */
+   b) the write process has been completed (no recovery required).
+   This is a no-op for repo formats that don't support revprop caching. */
 static svn_error_t *
-end_revprop_change(svn_fs_t *fs, apr_pool_t *pool)
+end_revprop_change(svn_fs_t *fs,
+                   apr_int64_t generation,
+                   apr_pool_t *scratch_pool)
 {
-  apr_int64_t current = 1;
   fs_x_data_t *ffd = fs->fsap_data;
+  SVN_ERR_ASSERT(ffd->has_write_lock);
+  SVN_ERR_ASSERT(generation % 2);
 
-  /* set the revprop generation to an even value to indicate
-   * that a write has been completed
+  /* Set the revprop generation to an even value to indicate
+   * that a write has been completed.  Since we held the write
+   * lock, nobody else could have updated the file contents.
    */
-  SVN_ERR(ensure_revprop_generation(fs, pool));
-  do
-    {
-      SVN_ERR(svn_named_atomic__add(&current,
-                                    1,
-                                    ffd->revprop_generation));
-    }
-  while (current % 2);
-
-  /* Save the latest generation to disk. FS is currently in a "locked"
-   * state such that we can be sure the be the only ones to write that
-   * file.
-   */
-  return svn_fs_x__write_revprop_generation_file(fs, current, pool);
+  SVN_ERR(write_revprop_generation_file(fs, generation + 1, scratch_pool));
+
+  return SVN_NO_ERROR;
 }
 
 /* Container for all data required to access the packed revprop file
@@ -943,6 +1008,7 @@ svn_error_t *
 svn_fs_x__get_revision_proplist(apr_hash_t **proplist_p,
                                 svn_fs_t *fs,
                                 svn_revnum_t rev,
+                                svn_boolean_t bypass_cache,
                                 apr_pool_t *pool)
 {
   fs_x_data_t *ffd = fs->fsap_data;
@@ -955,7 +1021,7 @@ svn_fs_x__get_revision_proplist(apr_hash
   SVN_ERR(svn_fs_x__ensure_revision_exists(rev, fs, pool));
 
   /* Try cache lookup first. */
-  if (has_revprop_cache(fs, pool))
+  if (!bypass_cache && has_revprop_cache(fs, pool))
     {
       svn_boolean_t is_cached;
       pair_cache_key_t key = { 0 };
@@ -1054,17 +1120,19 @@ switch_to_new_revprop(svn_fs_t *fs,
                       svn_boolean_t bump_generation,
                       apr_pool_t *pool)
 {
+  apr_int64_t generation;
+
   /* Now, we may actually be replacing revprops. Make sure that all other
      threads and processes will know about this. */
   if (bump_generation)
-    SVN_ERR(begin_revprop_change(fs, pool));
+    SVN_ERR(begin_revprop_change(&generation, fs, pool));
 
   SVN_ERR(svn_fs_x__move_into_place(tmp_path, final_path, perms_reference,
                                     pool));
 
   /* Indicate that the update (if relevant) has been completed. */
   if (bump_generation)
-    SVN_ERR(end_revprop_change(fs, pool));
+    SVN_ERR(end_revprop_change(fs, generation, pool));
 
   /* Clean up temporary files, if necessary. */
   if (files_to_delete)
@@ -1433,20 +1501,18 @@ svn_fs_x__set_revision_proplist(svn_fs_t
   is_packed = svn_fs_x__is_packed_revprop(fs, rev);
 
   /* Test whether revprops already exist for this revision.
-   * Only then will we need to bump the revprop generation. */
-  if (has_revprop_cache(fs, pool))
+   * Only then will we need to bump the revprop generation.
+   * The fact that they did not yet exist is never cached. */
+  if (is_packed)
     {
-      if (is_packed)
-        {
-          bump_generation = TRUE;
-        }
-      else
-        {
-          svn_node_kind_t kind;
-          SVN_ERR(svn_io_check_path(svn_fs_x__path_revprops(fs, rev, pool),
-                                    &kind, pool));
-          bump_generation = kind != svn_node_none;
-        }
+      bump_generation = TRUE;
+    }
+  else
+    {
+      svn_node_kind_t kind;
+      SVN_ERR(svn_io_check_path(svn_fs_x__path_revprops(fs, rev, pool),
+                                &kind, pool));
+      bump_generation = kind != svn_node_none;
     }
 
   /* Serialize the new revprop data */

Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/revprops.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/revprops.h?rev=1634609&r1=1634608&r2=1634609&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/revprops.h 
(original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/revprops.h Mon 
Oct 27 17:28:13 2014
@@ -29,17 +29,14 @@
 extern "C" {
 #endif /* __cplusplus */
 
-/* Write the CURRENT revprop generation to disk for repository FS.
+/* Auto-create / replace the revprop generation file in FS with its
+ * initial contents.  In any case, FS will not hold an open handle to
+ * it after this function succeeds.
  */
 svn_error_t *
-svn_fs_x__write_revprop_generation_file(svn_fs_t *fs,
-                                        apr_int64_t current,
+svn_fs_x__reset_revprop_generation_file(svn_fs_t *fs,
                                         apr_pool_t *pool);
 
-/* Make sure the revprop_namespace member in FS is set. */
-svn_error_t *
-svn_fs_x__cleanup_revprop_namespace(svn_fs_t *fs);
-
 /* In the filesystem FS, pack all revprop shards up to min_unpacked_rev.
  * 
  * NOTE: Keep the old non-packed shards around until after the format bump.
@@ -77,6 +74,7 @@ svn_fs_x__upgrade_cleanup_pack_revprops(
                                         apr_pool_t *scratch_pool);
 
 /* Read the revprops for revision REV in FS and return them in *PROPERTIES_P.
+ * If BYPASS_CACHE is set, don't consult the disks but always read from disk.
  *
  * Allocations will be done in POOL.
  */
@@ -84,6 +82,7 @@ svn_error_t *
 svn_fs_x__get_revision_proplist(apr_hash_t **proplist_p,
                                 svn_fs_t *fs,
                                 svn_revnum_t rev,
+                                svn_boolean_t bypass_cache,
                                 apr_pool_t *pool);
 
 /* Set the revision property list of revision REV in filesystem FS to


Reply via email to