You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2015/11/09 12:06:43 UTC

svn commit: r1713386 - in /subversion/branches/move-tracking-2: ./ subversion/ subversion/include/ subversion/include/private/ subversion/libsvn_fs_fs/ subversion/libsvn_fs_x/ subversion/libsvn_subr/ subversion/mod_dav_svn/

Author: julianfoad
Date: Mon Nov  9 11:06:42 2015
New Revision: 1713386

URL: http://svn.apache.org/viewvc?rev=1713386&view=rev
Log:
On the 'move-tracking-2' branch: catch up to trunk@1713385.

Modified:
    subversion/branches/move-tracking-2/   (props changed)
    subversion/branches/move-tracking-2/subversion/   (props changed)
    subversion/branches/move-tracking-2/subversion/include/private/svn_subr_private.h
    subversion/branches/move-tracking-2/subversion/include/svn_error_codes.h
    subversion/branches/move-tracking-2/subversion/include/svn_fs.h
    subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/fs_fs.c
    subversion/branches/move-tracking-2/subversion/libsvn_fs_x/   (props changed)
    subversion/branches/move-tracking-2/subversion/libsvn_fs_x/cached_data.c
    subversion/branches/move-tracking-2/subversion/libsvn_fs_x/caching.c
    subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs.h
    subversion/branches/move-tracking-2/subversion/libsvn_fs_x/recovery.c
    subversion/branches/move-tracking-2/subversion/libsvn_fs_x/revprops.c
    subversion/branches/move-tracking-2/subversion/libsvn_fs_x/transaction.c
    subversion/branches/move-tracking-2/subversion/libsvn_fs_x/util.c
    subversion/branches/move-tracking-2/subversion/libsvn_fs_x/util.h
    subversion/branches/move-tracking-2/subversion/libsvn_subr/compress.c
    subversion/branches/move-tracking-2/subversion/libsvn_subr/packed_data.c
    subversion/branches/move-tracking-2/subversion/mod_dav_svn/util.c

Propchange: subversion/branches/move-tracking-2/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Nov  9 11:06:42 2015
@@ -94,4 +94,4 @@
 /subversion/branches/verify-at-commit:1462039-1462408
 /subversion/branches/verify-keep-going:1439280-1546110
 /subversion/branches/wc-collate-path:1402685-1480384
-/subversion/trunk:1606692-1712079
+/subversion/trunk:1606692-1713385

Propchange: subversion/branches/move-tracking-2/subversion/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Nov  9 11:06:42 2015
@@ -82,4 +82,4 @@
 /subversion/branches/verify-at-commit/subversion:1462039-1462408
 /subversion/branches/verify-keep-going/subversion:1439280-1546110
 /subversion/branches/wc-collate-path/subversion:1402685-1480384
-/subversion/trunk/subversion:1606692-1712079
+/subversion/trunk/subversion:1606692-1713385

Modified: subversion/branches/move-tracking-2/subversion/include/private/svn_subr_private.h
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/include/private/svn_subr_private.h?rev=1713386&r1=1713385&r2=1713386&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/include/private/svn_subr_private.h (original)
+++ subversion/branches/move-tracking-2/subversion/include/private/svn_subr_private.h Mon Nov  9 11:06:42 2015
@@ -525,6 +525,16 @@ svn_version__at_least(svn_version_t *ver
 unsigned char *
 svn__encode_uint(unsigned char *p, apr_uint64_t val);
 
+/* Wrapper around svn__encode_uint using the LSB to store the sign:
+ *
+ * If VAL >= 0
+ *   UINT_VAL = 2 * VAL
+ * else
+ *   UINT_VAL = (- 2 * VAL) - 1
+ */
+unsigned char *
+svn__encode_int(unsigned char *p, apr_int64_t val);
+
 /* Decode an unsigned 7b/8b-encoded integer into *VAL and return a pointer
    to the byte after the integer.  The bytes to be decoded live in the
    range [P..END-1].  If these bytes do not contain a whole encoded
@@ -537,6 +547,14 @@ svn__decode_uint(apr_uint64_t *val,
                  const unsigned char *p,
                  const unsigned char *end);
 
+/* Wrapper around svn__decode_uint, reversing the transformation performed
+ * by svn__encode_int.
+ */
+const unsigned char *
+svn__decode_int(apr_int64_t *val,
+                const unsigned char *p,
+                const unsigned char *end);
+
 /* Compress the data from DATA with length LEN, it according to the
  * specified COMPRESSION_METHOD and write the result to OUT.
  * SVN__COMPRESSION_NONE is valid for COMPRESSION_METHOD.

Modified: subversion/branches/move-tracking-2/subversion/include/svn_error_codes.h
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/include/svn_error_codes.h?rev=1713386&r1=1713385&r2=1713386&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/include/svn_error_codes.h (original)
+++ subversion/branches/move-tracking-2/subversion/include/svn_error_codes.h Mon Nov  9 11:06:42 2015
@@ -868,6 +868,11 @@ SVN_ERROR_START
              SVN_ERR_FS_CATEGORY_START + 63,
              "Invalid generation number data.")
 
+  /** @since New in 1.10. */
+  SVN_ERRDEF(SVN_ERR_FS_CORRUPT_REVPROP_MANIFEST,
+             SVN_ERR_FS_CATEGORY_START + 64,
+             "Revprop manifest corrupt.")
+
   /* repos errors */
 
   SVN_ERRDEF(SVN_ERR_REPOS_LOCKED,

Modified: subversion/branches/move-tracking-2/subversion/include/svn_fs.h
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/include/svn_fs.h?rev=1713386&r1=1713385&r2=1713386&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/include/svn_fs.h (original)
+++ subversion/branches/move-tracking-2/subversion/include/svn_fs.h Mon Nov  9 11:06:42 2015
@@ -1081,7 +1081,7 @@ svn_fs_unparse_id(const svn_fs_id_t *id,
  * database.  Each new transaction increments the counter.  The
  * current value of the counter is not serialized into a filesystem
  * dump file, so dumping and restoring the repository will reset the
- * sequence and reuse transaction names.
+ * sequence and so may reuse transaction names.
  *
  * @defgroup svn_fs_txns Filesystem transactions
  * @{

Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/fs_fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/fs_fs.c?rev=1713386&r1=1713385&r2=1713386&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/fs_fs.c (original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/fs_fs.c Mon Nov  9 11:06:42 2015
@@ -1416,8 +1416,8 @@ svn_fs_fs__file_text_rep_equal(svn_boole
   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)

Propchange: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Nov  9 11:06:42 2015
@@ -94,4 +94,4 @@
 /subversion/branches/verify-keep-going/subversion/libsvn_fs_x:1439280-1492639,1546002-1546110
 /subversion/branches/wc-collate-path/subversion/libsvn_fs_x:1402685-1480384
 /subversion/trunk/subversion/libsvn_fs_fs:1415133-1596500,1596567,1597414,1597989,1598273,1599140,1600872,1601633,1603485-1603487,1603499,1603605,1604128,1604188,1604413-1604414,1604416-1604417,1604421,1604442,1604700,1604717,1604720,1604726,1604755,1604794,1604802,1604824,1604836,1604844,1604902-1604903,1604911,1604925,1604933,1604947,1605059-1605060,1605064-1605065,1605068,1605071-1605073,1605075,1605123,1605188-1605189,1605191,1605197,1605444,1605633,1606132,1606142,1606144,1606514,1606526,1606528,1606551,1606554,1606564,1606598-1606599,1606656,1606658,1606662,1606744,1606840,1607085,1607572,1612407,1612810,1613339,1613872,1614611,1615348,1615351-1615352,1615356,1616338-1616339,1616613,1617586,1617688,1618138,1618151,1618153,1618226,1618641,1618653,1618662,1619068,1619358,1619413,1619769,1619774,1620602,1620909,1620912,1620928,1620930,1621275,1621635,1622931,1622937,1622942,1622946,1622959-1622960,1622963,1622987,1623007,1623368,1623373,1623377,1623379,1623381,1623398,1623402,162
 4011,1624265,1624512,1626246,1626871,1626873,1626886,1627497-1627498,1627502,1627947-1627949,1627966,1628083,1628093,1628158-1628159,1628161,1628392-1628393,1628415,1628427,1628676,1628738,1628762,1628764,1629854-1629855,1629857,1629865,1629873,1629875,1629879,1630067,1630070,1631049-1631051,1631075,1631115,1631171,1631180,1631185-1631186,1631196-1631197,1631239-1631240,1631548,1631550,1631563,1631567,1631588,1631598,1632646,1632776,1632849,1632851-1632853,1632856-1632857,1632868,1632908,1632926,1633232,1633617-1633618,1634872,1634875,1634879-1634880,1634920,1636478,1636483,1636629,1636644,1637184,1637186,1637330,1637358,1637363,1637393,1639319,1639322,1639335,1639348,1639352,1639355,1639358,1639414,1639419,1639426,1639430,1639436,1639440,1639549,1640061-1640062,1640197,1640915,1640966,1641013,1643139,1643233,1645567,1646021,1646712,1646716,1647537,1647540-1647541,1647820,1647905,1648230,1648238,1648241-1648243,1648253,1648272,1648532,1648537-1648539,1648542,1648591,1648612,1649590,
 1651567,1652068,1652076,1652441,1652451,1653608,1654932,1654934,1654937,1655635,1655649,1655651,1655664,1656176,1657525,1657972,1657978,1658482,1659212,1659217,1659314,1659509,1662668,1665318,1665854,1665894,1667090,1667101,1667538,1669743,1669746,1669749,1669945,1670139,1670953,1673170,1673197,1673202,1673204,1673445,1673454,1673685,1673689,1673875,1674165,1674341,1674400,1674404,1674631,1674669,1674673,1675396,1676667,1677431,1678149,1678151,1678718,1678725,1679169,1679907,1679920-1679924,1679926,1680347,1680460,1680464,1680476,1680819,1681949,1681966,1681974,1681994,1682008,1682076,1682086,1682093,1682259,1682265,1682739,1682864,1683311,1683330,1683378,1683544,1683553,1684047,1686232,1686542,1686546,1686554,1686557,1687061,1687064,1687070-1687071,1687074,1687078-1687079,1688270,1688425,1692650,1693886,1694489,1694848,1696171,1696185,1696627-1696628,1696630,1696758,1697372,1697381,1697387,1697393,1697403,1697405,1701017,1701053,1702600,1702922,1703069,1703142,1703237,1703240,17052
 66,1705638,1705643,1705646,1705724,1705730,1705739,1706612,1706615,1706617,1706619,1706675-1706676,1706679,1706979-1706980,1707308,1707971-1707973,1707986,1707988-1707989,1708004
-/subversion/trunk/subversion/libsvn_fs_x:1414756-1509914,1606692-1712079
+/subversion/trunk/subversion/libsvn_fs_x:1414756-1509914,1606692-1713385

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=1713386&r1=1713385&r2=1713386&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 Nov  9 11:06:42 2015
@@ -2366,108 +2366,107 @@ compare_dirent_name(const void *a,
   return strcmp(lhs->name, rhs);
 }
 
-/* Into ENTRIES, read all directories entries from the key-value text in
- * STREAM.  If INCREMENTAL is TRUE, read until the end of the STREAM and
+/* Into ENTRIES, parse all directories entries from the serialized form in
+ * DATA.  If INCREMENTAL is TRUE, read until the end of the STREAM and
  * update the data.  ID is provided for nicer error messages.
+ *
+ * The contents of DATA will be shared with the items in ENTRIES, i.e. it
+ * must not be modified afterwards and must remain valid as long as ENTRIES
+ * is valid.  Use SCRATCH_POOL for temporary allocations.
  */
 static svn_error_t *
-read_dir_entries(apr_array_header_t *entries,
-                 svn_stream_t *stream,
-                 svn_boolean_t incremental,
-                 const svn_fs_x__id_t *id,
-                 apr_pool_t *result_pool,
-                 apr_pool_t *scratch_pool)
+parse_dir_entries(apr_array_header_t **entries_p,
+                  const svn_stringbuf_t *data,
+                  svn_boolean_t incremental,
+                  const svn_fs_x__id_t *id,
+                  apr_pool_t *result_pool,
+                  apr_pool_t *scratch_pool)
 {
-  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  const apr_byte_t *p = (const apr_byte_t *)data->data;
+  const apr_byte_t *end = p + data->len;
+  apr_uint64_t count;
   apr_hash_t *hash = incremental ? svn_hash__make(scratch_pool) : NULL;
-  const char *terminator = SVN_HASH_TERMINATOR;
-
-  /* Read until the terminator (non-incremental) or the end of STREAM
-     (incremental mode).  In the latter mode, we use a temporary HASH
-     to make updating and removing entries cheaper. */
-  while (1)
-    {
-      svn_hash__entry_t entry;
-      svn_fs_x__dirent_t *dirent;
-      char *str;
+  apr_array_header_t *entries;
 
-      svn_pool_clear(iterpool);
-      SVN_ERR(svn_hash__read_entry(&entry, stream, terminator,
-                                   incremental, iterpool));
-
-      /* End of directory? */
-      if (entry.key == NULL)
-        {
-          /* In incremental mode, we skip the terminator and read the
-             increments following it until the end of the stream. */
-          if (incremental && terminator)
-            terminator = NULL;
-          else
-            break;
-        }
+  /* Construct the resulting container. */
+  p = svn__decode_uint(&count, p, end);
+  if (count > INT_MAX)
+    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                             _("Directory for '%s' is too large"),
+                             svn_fs_x__id_unparse(id, scratch_pool)->data);
 
-      /* Deleted entry? */
-      if (entry.val == NULL)
-        {
-          /* We must be in incremental mode */
-          assert(hash);
-          apr_hash_set(hash, entry.key, entry.keylen, NULL);
-          continue;
-        }
+  entries = apr_array_make(result_pool, (int)count,
+                           sizeof(svn_fs_x__dirent_t *));
 
-      /* Add a new directory entry. */
+  while (p != end)
+    {
+      apr_size_t len;
+      svn_fs_x__dirent_t *dirent;
       dirent = apr_pcalloc(result_pool, sizeof(*dirent));
-      dirent->name = apr_pstrmemdup(result_pool, entry.key, entry.keylen);
 
-      str = svn_cstring_tokenize(" ", &entry.val);
-      if (str == NULL)
+      /* The part of the serialized entry that is not the name will be
+       * about 6 bytes or less.  Since APR allocates with an 8 byte
+       * alignment (4 bytes loss on average per string), simply using
+       * the name string in DATA already gives us near-optimal memory
+       * usage. */
+      dirent->name = (const char *)p;
+      len = strlen(dirent->name);
+      p += len + 1;
+      if (p == end)
         return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                      _("Directory entry corrupt in '%s'"),
-                      svn_fs_x__id_unparse(id, scratch_pool)->data);
+                            _("Directory entry missing kind in '%s'"),
+                            svn_fs_x__id_unparse(id, scratch_pool)->data);
 
-      if (strcmp(str, SVN_FS_X__KIND_FILE) == 0)
-        {
-          dirent->kind = svn_node_file;
-        }
-      else if (strcmp(str, SVN_FS_X__KIND_DIR) == 0)
-        {
-          dirent->kind = svn_node_dir;
-        }
-      else
-        {
-          return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                      _("Directory entry corrupt in '%s'"),
-                      svn_fs_x__id_unparse(id, scratch_pool)->data);
-        }
+      dirent->kind = (svn_node_kind_t)*(p++);
+      if (p == end)
+        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                            _("Directory entry missing change set in '%s'"),
+                            svn_fs_x__id_unparse(id, scratch_pool)->data);
 
-      str = svn_cstring_tokenize(" ", &entry.val);
-      if (str == NULL)
+      p = svn__decode_int(&dirent->id.change_set, p, end);
+      if (p == end)
         return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                      _("Directory entry corrupt in '%s'"),
-                      svn_fs_x__id_unparse(id, scratch_pool)->data);
+                            _("Directory entry missing item number in '%s'"),
+                            svn_fs_x__id_unparse(id, scratch_pool)->data);
 
-      SVN_ERR(svn_fs_x__id_parse(&dirent->id, str));
+      p = svn__decode_uint(&dirent->id.number, p, end);
 
       /* In incremental mode, update the hash; otherwise, write to the
        * final array. */
       if (incremental)
-        apr_hash_set(hash, dirent->name, entry.keylen, dirent);
+        {
+          /* Insertion / update or a deletion? */
+          if (svn_fs_x__id_used(&dirent->id))
+            apr_hash_set(hash, dirent->name, len, dirent);
+          else
+            apr_hash_set(hash, dirent->name, len, NULL);
+        }
       else
-        APR_ARRAY_PUSH(entries, svn_fs_x__dirent_t *) = dirent;
+        {
+          APR_ARRAY_PUSH(entries, svn_fs_x__dirent_t *) = dirent;
+        }
     }
 
-  /* Convert container to a sorted array. */
   if (incremental)
     {
+      /* Convert container into a sorted array. */
       apr_hash_index_t *hi;
-      for (hi = apr_hash_first(iterpool, hash); hi; hi = apr_hash_next(hi))
+      for (hi = apr_hash_first(scratch_pool, hash); hi; hi = apr_hash_next(hi))
         APR_ARRAY_PUSH(entries, svn_fs_x__dirent_t *) = apr_hash_this_val(hi);
-    }
 
-  if (!sorted(entries))
-    svn_sort__array(entries, compare_dirents);
+      if (!sorted(entries))
+        svn_sort__array(entries, compare_dirents);
+    }
+  else
+    {
+      /* Check that we read the expected amount of entries. */
+      if ((apr_uint64_t)entries->nelts != count)
+        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                            _("Directory length mismatch in '%s'"),
+                            svn_fs_x__id_unparse(id, scratch_pool)->data);
+    }
 
-  svn_pool_destroy(iterpool);
+ *entries_p = entries;
 
   return SVN_NO_ERROR;
 }
@@ -2515,9 +2514,11 @@ get_dir_contents(svn_fs_x__dir_data_t *d
 {
   svn_stream_t *contents;
   const svn_fs_x__id_t *id = &noderev->noderev_id;
+  apr_size_t len;
+  svn_stringbuf_t *text;
+  svn_boolean_t incremental;
 
   /* Initialize the result. */
-  dir->entries = apr_array_make(result_pool, 16, sizeof(svn_fs_x__dirent_t *));
   dir->txn_filesize = SVN_INVALID_FILESIZE;
 
   /* Read dir contents - unless there is none in which case we are done. */
@@ -2539,31 +2540,40 @@ get_dir_contents(svn_fs_x__dir_data_t *d
 
       /* Obtain txn children file size. */
       SVN_ERR(svn_io_file_size_get(&dir->txn_filesize, file, scratch_pool));
+      len = (apr_size_t)dir->txn_filesize;
 
+      /* Finally, provide stream access to FILE. */
       contents = svn_stream_from_aprfile2(file, FALSE, scratch_pool);
-      SVN_ERR(read_dir_entries(dir->entries, contents, TRUE, id,
-                               result_pool, scratch_pool));
-      SVN_ERR(svn_stream_close(contents));
+      incremental = TRUE;
     }
   else if (noderev->data_rep)
     {
-      /* Undeltify content before parsing it. Otherwise, we could only
-       * parse it byte-by-byte.
-       */
-      apr_size_t len = noderev->data_rep->expanded_size;
-      svn_stringbuf_t *text;
-
       /* The representation is immutable.  Read it normally. */
+      len = noderev->data_rep->expanded_size;
       SVN_ERR(svn_fs_x__get_contents(&contents, fs, noderev->data_rep,
                                      FALSE, scratch_pool));
-      SVN_ERR(svn_stringbuf_from_stream(&text, contents, len, scratch_pool));
-      SVN_ERR(svn_stream_close(contents));
-
-      /* de-serialize hash */
-      contents = svn_stream_from_stringbuf(text, scratch_pool);
-      SVN_ERR(read_dir_entries(dir->entries, contents, FALSE, id,
-                               result_pool, scratch_pool));
+      incremental = FALSE;
     }
+  else
+    {
+      /* Empty representation == empty directory. */
+      dir->entries = apr_array_make(result_pool, 0,
+                                    sizeof(svn_fs_x__dirent_t *));
+      return SVN_NO_ERROR;
+    }
+
+  /* Read the whole stream contents into a single buffer.
+   * Due to our LEN hint, no allocation overhead occurs.
+   *
+   * Also, a large portion of TEXT will be file / dir names which we
+   * directly reference from DIR->ENTRIES instead of copying them.
+   * Hence, we need to use the RESULT_POOL here. */
+  SVN_ERR(svn_stringbuf_from_stream(&text, contents, len, result_pool));
+  SVN_ERR(svn_stream_close(contents));
+
+  /* de-serialize hash */
+  SVN_ERR(parse_dir_entries(&dir->entries, text, incremental, id,
+                            result_pool, scratch_pool));
 
   return SVN_NO_ERROR;
 }

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=1713386&r1=1713385&r2=1713386&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 Nov  9 11:06:42 2015
@@ -370,6 +370,7 @@ svn_fs_x__initialize_caches(svn_fs_t *fs
   svn_fs_x__data_t *ffd = fs->fsap_data;
   const char *prefix = apr_pstrcat(scratch_pool,
                                    "fsx:", fs->uuid,
+                                   "--", ffd->instance_id,
                                    "/", normalize_key_part(fs->path,
                                                            scratch_pool),
                                    ":",

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=1713386&r1=1713385&r2=1713386&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 Nov  9 11:06:42 2015
@@ -60,7 +60,6 @@ extern "C" {
 #define PATH_TXNS_DIR         "transactions"     /* Directory of transactions */
 #define PATH_TXN_PROTOS_DIR   "txn-protorevs"    /* Directory of proto-revs */
 #define PATH_TXN_CURRENT      "txn-current"      /* File with next txn key */
-#define PATH_TXN_NEXT         "txn-next"         /* Will become txn-current */
 #define PATH_TXN_CURRENT_LOCK "txn-current-lock" /* Lock for txn-current */
 #define PATH_LOCKS_DIR        "locks"            /* Directory of locks */
 #define PATH_MIN_UNPACKED_REV "min-unpacked-rev" /* Oldest revision which
@@ -403,9 +402,6 @@ typedef struct svn_fs_x__data_t
   svn_error_t *(*svn_fs_open_)(svn_fs_t **, const char *, apr_hash_t *,
                                apr_pool_t *, apr_pool_t *);
 
-  /* If not 0, this is a pre-allocated transaction ID that can just be
-     used for a new txn without needing to consult 'txn-current'. */
-  apr_uint64_t next_txn_id;
 } svn_fs_x__data_t;
 
 

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=1713386&r1=1713385&r2=1713386&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 Nov  9 11:06:42 2015
@@ -22,6 +22,7 @@
 
 #include "recovery.h"
 
+#include "svn_dirent_uri.h"
 #include "svn_hash.h"
 #include "svn_pools.h"
 #include "private/svn_string_private.h"
@@ -106,6 +107,86 @@ recover_get_largest_revision(svn_fs_t *f
   return SVN_NO_ERROR;
 }
 
+/* Delete all files and sub-directories (recursively) of DIR_PATH but
+   leave DIR_PATH itself in place.  Use SCRATCH_POOL for temporaries. */
+static svn_error_t *
+clear_directory(const char *dir_path,
+                apr_pool_t *scratch_pool)
+{
+  apr_hash_t *dirents;
+  apr_hash_index_t *hi;
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+  SVN_ERR(svn_io_get_dirents3(&dirents, dir_path, TRUE, scratch_pool,
+                              scratch_pool));
+
+  for (hi = apr_hash_first(scratch_pool, dirents);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      const char *path;
+      const char *name;
+      svn_dirent_t *dirent;
+
+      svn_pool_clear(iterpool);
+      apr_hash_this(hi, (const void **)&name, NULL, (void **)&dirent);
+
+      path = svn_dirent_join(dir_path, name, iterpool);
+      if (dirent->kind == svn_node_dir)
+        SVN_ERR(svn_io_remove_dir2(path, TRUE, NULL, NULL, iterpool));
+      else
+        SVN_ERR(svn_io_remove_file2(path, TRUE, iterpool));
+    }
+
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+/* Delete all uncommitted transaction data from FS.
+   Use SCRATCH_POOL for temporaries. */
+static svn_error_t *
+discard_transactions(svn_fs_t *fs,
+                     apr_pool_t *scratch_pool)
+{
+  svn_fs_x__data_t *ffd = fs->fsap_data;
+  svn_fs_x__shared_data_t *ffsd = ffd->shared;
+
+  /* In case this FS has been opened more than once in this process,
+     we should purge their shared transaction data as well.  We do the
+     same as abort_txn would, except that we don't expect all txn files
+     to be complete on disk. */
+  while (ffsd->txns)
+    {
+      svn_fs_x__shared_txn_data_t *txn = ffsd->txns;
+      ffsd->txns = txn->next;
+
+      svn_pool_destroy(txn->pool);
+    }
+
+  /* Remove anything from the transaction folders. */
+  SVN_ERR(clear_directory(svn_fs_x__path_txns_dir(fs, scratch_pool),
+                          scratch_pool));
+  SVN_ERR(clear_directory(svn_fs_x__path_txn_proto_revs(fs, scratch_pool),
+                          scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+/* Reset txn-current in FS.  Use SCRATCH_POOL for temporaries. */
+static svn_error_t *
+reset_txn_number(svn_fs_t *fs,
+                 apr_pool_t *scratch_pool)
+{
+  const char *initial_txn = "0\n";
+  SVN_ERR(svn_io_write_atomic2(svn_fs_x__path_txn_current(fs, scratch_pool),
+                               initial_txn, strlen(initial_txn),
+                               svn_fs_x__path_uuid(fs, scratch_pool),
+                               FALSE, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
 /* Baton used for recover_body below. */
 typedef struct recover_baton_t {
   svn_fs_t *fs;
@@ -136,6 +217,12 @@ recover_body(void *baton,
      Bump the instance ID. */
   SVN_ERR(svn_fs_x__set_uuid(fs, fs->uuid, NULL, TRUE, scratch_pool));
 
+  /* Because transactions are not resilient against system crashes,
+     any existing transaction is suspect (and would probably not be
+     reopened anyway).  Get rid of those. */
+  SVN_ERR(discard_transactions(fs, scratch_pool));
+  SVN_ERR(reset_txn_number(fs, scratch_pool));
+
   /* We need to know the largest revision in the filesystem. */
   SVN_ERR(recover_get_largest_revision(fs, &max_rev, scratch_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=1713386&r1=1713385&r2=1713386&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 Nov  9 11:06:42 2015
@@ -32,6 +32,8 @@
 #include "util.h"
 #include "transaction.h"
 
+#include "private/svn_packed_data.h"
+#include "private/svn_sorts_private.h"
 #include "private/svn_subr_private.h"
 #include "private/svn_string_private.h"
 #include "../libsvn_fs/fs-loader.h"
@@ -339,6 +341,18 @@ end_revprop_change(svn_fs_t *fs,
   return SVN_NO_ERROR;
 }
 
+/* Represents an entry in the packed revprop manifest.
+ * There is one such entry per pack file. */
+typedef struct manifest_entry_t
+{
+  /* First revision in the pack file. */
+  svn_revnum_t start_rev;
+
+  /* Tag (a counter) appended to the file name to distinguish it from
+     outdated ones. */
+  apr_uint64_t tag;
+} manifest_entry_t;
+
 /* Container for all data required to access the packed revprop file
  * for a given REVISION.  This structure will be filled incrementally
  * by read_pack_revprops() its sub-routines.
@@ -356,8 +370,8 @@ typedef struct packed_revprops_t
   apr_size_t serialized_size;
 
 
-  /* name of the pack file (without folder path) */
-  const char *filename;
+  /* manifest entry describing the pack file */
+  manifest_entry_t entry;
 
   /* packed shard folder path */
   const char *folder;
@@ -365,9 +379,6 @@ typedef struct packed_revprops_t
   /* sum of values in SIZES */
   apr_size_t total_size;
 
-  /* first revision in the pack (>= MANIFEST_START) */
-  svn_revnum_t start_revision;
-
   /* size of the revprops in PACKED_REVPROPS */
   apr_array_header_t *sizes;
 
@@ -379,12 +390,8 @@ typedef struct packed_revprops_t
    * in the pack, i.e. the pack content without header and compression */
   svn_stringbuf_t *packed_revprops;
 
-  /* First revision covered by MANIFEST.
-   * Will equal the shard start revision or 1, for the 1st shard. */
-  svn_revnum_t manifest_start;
-
   /* content of the manifest.
-   * Maps long(rev - MANIFEST_START) to const char* pack file name */
+   * Sorted list of manifest_entry_t. */
   apr_array_header_t *manifest;
 } packed_revprops_t;
 
@@ -469,17 +476,159 @@ read_non_packed_revprop(apr_hash_t **pro
   return SVN_NO_ERROR;
 }
 
-/* Return the minimum length of any packed revprop file name in REVPROPS. */
-static apr_size_t
-get_min_filename_len(packed_revprops_t *revprops)
-{
-  char number_buffer[SVN_INT64_BUFFER_SIZE];
-
-  /* The revprop filenames have the format <REV>.<COUNT> - with <REV> being
-   * at least the first rev in the shard and <COUNT> having at least one
-   * digit.  Thus, the minimum is 2 + #decimal places in the start rev.
-   */
-  return svn__i64toa(number_buffer, revprops->manifest_start) + 2;
+/* Serialize the packed revprops MANIFEST into FILE.
+ * Use SCRATCH_POOL for temporary allocations.
+ */
+static svn_error_t *
+write_manifest(apr_file_t *file,
+               const apr_array_header_t *manifest,
+               apr_pool_t *scratch_pool)
+{
+  int i;
+  svn_checksum_t *checksum;
+  svn_stream_t *stream;
+  svn_packed__data_root_t *root = svn_packed__data_create_root(scratch_pool);
+
+  /* one top-level stream per struct element */
+  svn_packed__int_stream_t *start_rev_stream
+    = svn_packed__create_int_stream(root, TRUE, FALSE);
+  svn_packed__int_stream_t *tag_stream
+    = svn_packed__create_int_stream(root, FALSE, FALSE);
+
+  /* serialize ENTRIES */
+  for (i = 0; i < manifest->nelts; ++i)
+    {
+      manifest_entry_t *entry = &APR_ARRAY_IDX(manifest, i, manifest_entry_t);
+      svn_packed__add_uint(start_rev_stream, entry->start_rev);
+      svn_packed__add_uint(tag_stream, entry->tag);
+    }
+
+  /* Write to file and calculate the checksum. */
+  stream = svn_stream_from_aprfile2(file, TRUE, scratch_pool);
+  stream = svn_checksum__wrap_write_stream(&checksum, stream,
+                                           svn_checksum_fnv1a_32x4,
+                                           scratch_pool);
+  SVN_ERR(svn_packed__data_write(stream, root, scratch_pool));
+  SVN_ERR(svn_stream_close(stream));
+
+  /* Append the checksum */
+  SVN_ERR(svn_io_file_write_full(file, checksum->digest,
+                                 svn_checksum_size(checksum), NULL,
+                                 scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+/* Read the packed revprops manifest from the CONTENT buffer and return it
+ * in *MANIFEST, allocated in RESULT_POOL.  REVISION is the revision number
+ * to put into error messages.  Use SCRATCH_POOL for temporary allocations.
+ */
+static svn_error_t *
+read_manifest(apr_array_header_t **manifest,
+              svn_stringbuf_t *content,
+              svn_revnum_t revision,
+              apr_pool_t *result_pool,
+              apr_pool_t *scratch_pool)
+{
+  apr_size_t data_len;
+  apr_size_t i;
+  apr_size_t count;
+
+  svn_stream_t *stream;
+  svn_packed__data_root_t *root;
+  svn_packed__int_stream_t *start_rev_stream;
+  svn_packed__int_stream_t *tag_stream;
+  const apr_byte_t *digest;
+  svn_checksum_t *actual, *expected;
+
+  /* Verify the checksum. */
+  if (content->len < sizeof(apr_uint32_t))
+    return svn_error_createf(SVN_ERR_FS_CORRUPT_REVPROP_MANIFEST, NULL,
+                             "Revprop manifest too short for revision r%ld",
+                             revision);
+
+  data_len = content->len - sizeof(apr_uint32_t);
+  digest = (apr_byte_t *)content->data + data_len;
+
+  expected = svn_checksum__from_digest_fnv1a_32x4(digest, scratch_pool);
+  SVN_ERR(svn_checksum(&actual, svn_checksum_fnv1a_32x4, content->data,
+                       data_len, scratch_pool));
+
+  if (!svn_checksum_match(actual, expected))
+    SVN_ERR(svn_checksum_mismatch_err(expected, actual, scratch_pool, 
+                                      _("checksum mismatch in revprop "
+                                        "manifest for revision r%ld"),
+                                      revision));
+
+  /* read everything from the buffer */
+  stream = svn_stream_from_stringbuf(content, scratch_pool);
+  SVN_ERR(svn_packed__data_read(&root, stream, result_pool, scratch_pool));
+
+  /* get streams */
+  start_rev_stream = svn_packed__first_int_stream(root);
+  tag_stream = svn_packed__next_int_stream(start_rev_stream);
+
+  /* read ids array */
+  count = svn_packed__int_count(start_rev_stream);
+  *manifest = apr_array_make(result_pool, (int)count,
+                            sizeof(manifest_entry_t));
+
+  for (i = 0; i < count; ++i)
+    {
+      manifest_entry_t *entry = apr_array_push(*manifest);
+      entry->start_rev = (svn_revnum_t)svn_packed__get_int(start_rev_stream);
+      entry->tag = svn_packed__get_uint(tag_stream);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Implements the standard comparison function signature comparing the
+ * manifest_entry_t(lhs).start_rev to svn_revnum_t(rhs). */
+static int
+compare_entry_revision(const void *lhs,
+                       const void *rhs)
+{
+  const manifest_entry_t *entry = lhs;
+  const svn_revnum_t *revision = rhs;
+
+  if (entry->start_rev < *revision)
+    return -1;
+
+  return entry->start_rev == *revision ? 0 : 1;
+}
+
+/* Return the index in MANIFEST that has the info for the pack file
+ * containing REVISION. */
+static int
+get_entry(apr_array_header_t *manifest,
+          svn_revnum_t revision)
+{
+  manifest_entry_t *entry;
+  int idx = svn_sort__bsearch_lower_bound(manifest, &revision,
+                                          compare_entry_revision);
+
+  assert(manifest->nelts > 0);
+  if (idx >= manifest->nelts)
+    return idx - 1;
+
+  entry = &APR_ARRAY_IDX(manifest, idx, manifest_entry_t);
+  if (entry->start_rev > revision && idx > 0)
+    return idx - 1;
+
+  return idx;
+}
+
+/* Return the full path of the revprop pack file given by ENTRY within
+ * REVPROPS.  Allocate the result in RESULT_POOL. */
+static const char *
+get_revprop_pack_filepath(packed_revprops_t *revprops,
+                          manifest_entry_t *entry,
+                          apr_pool_t *result_pool)
+{
+  const char *filename = apr_psprintf(result_pool, "%ld.%" APR_UINT64_T_FMT,
+                                      entry->start_rev, entry->tag);
+  return svn_dirent_join(revprops->folder, filename, result_pool);
 }
 
 /* Given FS and REVPROPS->REVISION, fill the FILENAME, FOLDER and MANIFEST
@@ -495,98 +644,59 @@ get_revprop_packname(svn_fs_t *fs,
   svn_fs_x__data_t *ffd = fs->fsap_data;
   svn_stringbuf_t *content = NULL;
   const char *manifest_file_path;
-  int idx, rev_count;
-  char *buffer, *buffer_end;
-  const char **filenames, **filenames_end;
-  apr_size_t min_filename_len;
+  int idx;
+  svn_revnum_t previous_start_rev;
+  int i;
 
   /* Determine the dimensions. Rev 0 is excluded from the first shard. */
-  rev_count = ffd->max_files_per_dir;
-  revprops->manifest_start
+  int rev_count = ffd->max_files_per_dir;
+  svn_revnum_t manifest_start
     = revprops->revision - (revprops->revision % rev_count);
-  if (revprops->manifest_start == 0)
+  if (manifest_start == 0)
     {
-      ++revprops->manifest_start;
+      ++manifest_start;
       --rev_count;
     }
 
-  revprops->manifest = apr_array_make(result_pool, rev_count,
-                                      sizeof(const char*));
-
-  /* No line in the file can be less than this number of chars long. */
-  min_filename_len = get_min_filename_len(revprops);
-
   /* Read the content of the manifest file */
   revprops->folder = svn_fs_x__path_pack_shard(fs, revprops->revision,
                                                result_pool);
   manifest_file_path = svn_dirent_join(revprops->folder, PATH_MANIFEST,
                                        result_pool);
-
   SVN_ERR(svn_fs_x__read_content(&content, manifest_file_path, result_pool));
+  SVN_ERR(read_manifest(&revprops->manifest, content, revprops->revision,
+                        result_pool, scratch_pool));
 
-  /* There CONTENT must have a certain minimal size and there no
-   * unterminated lines at the end of the file.  Both guarantees also
-   * simplify the parser loop below.
-   */
-  if (   content->len < rev_count * (min_filename_len + 1)
-      || content->data[content->len - 1] != '\n')
-    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                             _("Packed revprop manifest for r%ld not "
-                               "properly terminated"), revprops->revision);
-
-  /* Chop (parse) the manifest CONTENT into filenames, one per line.
-   * We only have to replace all newlines with NUL and add all line
-   * starts to REVPROPS->MANIFEST.
-   *
-   * There must be exactly REV_COUNT lines and that is the number of
-   * lines we parse from BUFFER to FILENAMES.  Set the end pointer for
-   * the source BUFFER such that BUFFER+MIN_FILENAME_LEN is still valid
-   * BUFFER_END is always valid due to CONTENT->LEN > MIN_FILENAME_LEN.
-   *
-   * Please note that this loop is performance critical for e.g. 'svn log'.
-   * It is run 1000x per revprop access, i.e. per revision and about
-   * 50 million times per sec (and CPU core).
-   */
-  for (filenames = (const char **)revprops->manifest->elts,
-       filenames_end = filenames + rev_count,
-       buffer = content->data,
-       buffer_end = buffer + content->len - min_filename_len;
-       (filenames < filenames_end) && (buffer < buffer_end);
-       ++filenames)
-    {
-      /* BUFFER always points to the start of the next line / filename. */
-      *filenames = buffer;
-
-      /* Find the next EOL.  This is guaranteed to stay within the CONTENT
-       * buffer because we left enough room after BUFFER_END and we know
-       * we will always see a newline as the last non-NUL char. */
-      buffer += min_filename_len;
-      while (*buffer != '\n')
-        ++buffer;
-
-      /* Found EOL.  Turn it into the filename terminator and move BUFFER
-       * to the start of the next line or CONTENT buffer end. */
-      *buffer = '\0';
-      ++buffer;
-    }
-
-  /* We must have reached the end of both buffers. */
-  if (buffer < content->data + content->len)
-    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                             _("Packed revprop manifest for r%ld "
-                               "has too many entries"), revprops->revision);
-
-  if (filenames < filenames_end)
-    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                             _("Packed revprop manifest for r%ld "
-                               "has too few entries"), revprops->revision);
-
-  /* The target array has now exactly one entry per revision. */
-  revprops->manifest->nelts = rev_count;
-
-  /* Now get the file name */
-  idx = (int)(revprops->revision - revprops->manifest_start);
-  revprops->filename = APR_ARRAY_IDX(revprops->manifest, idx, const char*);
+  /* Verify the manifest data. */
+  if (revprops->manifest->nelts == 0)
+    return svn_error_createf(SVN_ERR_FS_CORRUPT_REVPROP_MANIFEST, NULL,
+                             "Revprop manifest for r%ld is empty",
+                             revprops->revision);
+
+  previous_start_rev = 0;
+  for (i = 0; i < revprops->manifest->nelts; ++i)
+    {
+      svn_revnum_t start_rev = APR_ARRAY_IDX(revprops->manifest, i,
+                                             manifest_entry_t).start_rev;
+      if (   start_rev < manifest_start
+          || start_rev >= manifest_start + rev_count)
+        return svn_error_createf(SVN_ERR_FS_CORRUPT_REVPROP_MANIFEST, NULL,
+                                 "Revprop manifest for r%ld contains "
+                                 "out-of-range revision r%ld",
+                                 revprops->revision, start_rev);
+
+      if (start_rev < previous_start_rev)
+        return svn_error_createf(SVN_ERR_FS_CORRUPT_REVPROP_MANIFEST, NULL,
+                                 "Entries in revprop manifest for r%ld "
+                                 "are not ordered", revprops->revision);
+
+      previous_start_rev = start_rev;
+    }
+
+  /* Now get the pack file description */
+  idx = get_entry(revprops->manifest, revprops->revision);
+  revprops->entry = APR_ARRAY_IDX(revprops->manifest, idx,
+                                  manifest_entry_t);
 
   return SVN_NO_ERROR;
 }
@@ -602,10 +712,9 @@ same_shard(svn_fs_t *fs,
   return (r1 / ffd->max_files_per_dir) == (r2 / ffd->max_files_per_dir);
 }
 
-/* Given FS and the full packed file content in REVPROPS->PACKED_REVPROPS,
- * fill the START_REVISION member, and make PACKED_REVPROPS point to the
- * first serialized revprop.  If READ_ALL is set, initialize the SIZES
- * and OFFSETS members as well.
+/* Given FS and the full packed file content in REVPROPS->PACKED_REVPROPS
+ * and make PACKED_REVPROPS point to the first serialized revprop.  If
+ * READ_ALL is set, initialize the SIZES and OFFSETS members as well.
  *
  * Parse the revprops for REVPROPS->REVISION and set the PROPERTIES as
  * well as the SERIALIZED_SIZE member.  If revprop caching has been
@@ -673,7 +782,7 @@ parse_packed_revprops(svn_fs_t *fs,
   revprops->packed_revprops->blocksize = (apr_size_t)(uncompressed->blocksize - offset);
 
   /* STREAM still points to the first entry in the sizes list. */
-  revprops->start_revision = (svn_revnum_t)first_rev;
+  SVN_ERR_ASSERT(revprops->entry.start_rev = (svn_revnum_t)first_rev);
   if (read_all)
     {
       /* Init / construct REVPROPS members. */
@@ -782,9 +891,8 @@ read_pack_revprop(packed_revprops_t **re
        * Re-read the manifest and the pack file.
        */
       SVN_ERR(get_revprop_packname(fs, result, result_pool, iterpool));
-      file_path  = svn_dirent_join(result->folder,
-                                   result->filename,
-                                   iterpool);
+      file_path = get_revprop_pack_filepath(result, &result->entry,
+                                            iterpool);
       SVN_ERR(svn_fs_x__try_stringbuf_from_file(&result->packed_revprops,
                                 &missing,
                                 file_path,
@@ -1052,7 +1160,8 @@ repack_revprops(svn_fs_t *fs,
   stream = svn_stream_from_stringbuf(uncompressed, scratch_pool);
 
   /* write the header*/
-  SVN_ERR(serialize_revprops_header(stream, revprops->start_revision + start,
+  SVN_ERR(serialize_revprops_header(stream,
+                                    revprops->entry.start_rev + start,
                                     revprops->sizes, start, end,
                                     scratch_pool));
 
@@ -1093,9 +1202,8 @@ repack_revprops(svn_fs_t *fs,
   return SVN_NO_ERROR;
 }
 
-/* Allocate a new pack file name for revisions
- *     [REVPROPS->START_REVISION + START, REVPROPS->START_REVISION + END - 1]
- * of REVPROPS->MANIFEST.  Add the name of old file to FILES_TO_DELETE,
+/* Allocate a new pack file name for revisions starting at START_REV in
+ * REVPROPS->MANIFEST.  Add the name of old file to FILES_TO_DELETE,
  * auto-create that array if necessary.  Return an open file *FILE that is
  * allocated in RESULT_POOL.  Allocate the paths in *FILES_TO_DELETE from
  * the same pool that contains the array itself.  Schedule necessary fsync
@@ -1107,55 +1215,43 @@ static svn_error_t *
 repack_file_open(apr_file_t **file,
                  svn_fs_t *fs,
                  packed_revprops_t *revprops,
-                 int start,
-                 int end,
+                 svn_revnum_t start_rev,
                  apr_array_header_t **files_to_delete,
                  svn_fs_x__batch_fsync_t *batch,
                  apr_pool_t *result_pool,
                  apr_pool_t *scratch_pool)
 {
-  apr_int64_t tag;
-  const char *tag_string;
-  svn_string_t *new_filename;
-  int i;
-  int manifest_offset
-    = (int)(revprops->start_revision - revprops->manifest_start);
-
-  /* get the old (= current) file name and enlist it for later deletion */
-  const char *old_filename = APR_ARRAY_IDX(revprops->manifest,
-                                           start + manifest_offset,
-                                           const char*);
+  manifest_entry_t new_entry;
+  const char *new_path;
+  int idx;
+
+  /* We always replace whole pack files - possibly by more than one new file.
+   * When we create the file for the first part of the pack, enlist the old
+   * one for later deletion */
+  SVN_ERR_ASSERT(start_rev >= revprops->entry.start_rev);
 
   if (*files_to_delete == NULL)
     *files_to_delete = apr_array_make(result_pool, 3, sizeof(const char*));
 
-  APR_ARRAY_PUSH(*files_to_delete, const char*)
-    = svn_dirent_join(revprops->folder, old_filename,
-                      (*files_to_delete)->pool);
-
-  /* increase the tag part, i.e. the counter after the dot */
-  tag_string = strchr(old_filename, '.');
-  if (tag_string == NULL)
-    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                             _("Packed file '%s' misses a tag"),
-                             old_filename);
-
-  SVN_ERR(svn_cstring_atoi64(&tag, tag_string + 1));
-  new_filename = svn_string_createf((*files_to_delete)->pool,
-                                    "%ld.%" APR_INT64_T_FMT,
-                                    revprops->start_revision + start,
-                                    ++tag);
+  if (revprops->entry.start_rev == start_rev)
+    APR_ARRAY_PUSH(*files_to_delete, const char*)
+      = get_revprop_pack_filepath(revprops, &revprops->entry,
+                                  (*files_to_delete)->pool);
+
+  /* Initialize the new manifest entry. Bump the tag part. */
+  new_entry.start_rev = start_rev;
+  new_entry.tag = revprops->entry.tag + 1;
 
   /* update the manifest to point to the new file */
-  for (i = start; i < end; ++i)
-    APR_ARRAY_IDX(revprops->manifest, i + manifest_offset, const char*)
-      = new_filename->data;
+  idx = get_entry(revprops->manifest, start_rev);
+  if (revprops->entry.start_rev == start_rev)
+    APR_ARRAY_IDX(revprops->manifest, idx, manifest_entry_t) = new_entry;
+  else
+    svn_sort__array_insert(revprops->manifest, &new_path, idx + 1);
 
   /* open the file */
-  SVN_ERR(svn_fs_x__batch_fsync_open_file(file, batch,
-                                          svn_dirent_join(revprops->folder,
-                                                          new_filename->data,
-                                                          scratch_pool),
+  new_path = get_revprop_pack_filepath(revprops, &new_entry, scratch_pool);
+  SVN_ERR(svn_fs_x__batch_fsync_open_file(file, batch, new_path,
                                           scratch_pool));
 
   return SVN_NO_ERROR;
@@ -1205,7 +1301,7 @@ write_packed_revprop(const char **final_
   SVN_ERR(svn_stream_close(stream));
 
   /* calculate the size of the new data */
-  changed_index = (int)(rev - revprops->start_revision);
+  changed_index = (int)(rev - revprops->entry.start_rev);
   new_total_size = revprops->total_size - revprops->serialized_size
                  + serialized->len
                  + (revprops->offsets->nelts + 2) * SVN_INT64_BUFFER_SIZE;
@@ -1219,8 +1315,8 @@ write_packed_revprop(const char **final_
       /* simply replace the old pack file with new content as we do it
        * in the non-packed case */
 
-      *final_path = svn_dirent_join(revprops->folder, revprops->filename,
-                                    result_pool);
+      *final_path = get_revprop_pack_filepath(revprops, &revprops->entry,
+                                              result_pool);
       *tmp_path = apr_pstrcat(result_pool, *final_path, ".tmp", SVN_VA_NULL);
       SVN_ERR(svn_fs_x__batch_fsync_open_file(&file, batch, *tmp_path,
                                               scratch_pool));
@@ -1231,7 +1327,7 @@ write_packed_revprop(const char **final_
   else
     {
       /* split the pack file into two of roughly equal size */
-      int right_count, left_count, i;
+      int right_count, left_count;
 
       int left = 0;
       int right = revprops->sizes->nelts - 1;
@@ -1278,8 +1374,9 @@ write_packed_revprop(const char **final_
       /* write the new, split files */
       if (left_count)
         {
-          SVN_ERR(repack_file_open(&file, fs, revprops, 0,
-                                   left_count, files_to_delete, batch,
+          SVN_ERR(repack_file_open(&file, fs, revprops,
+                                   revprops->entry.start_rev,
+                                   files_to_delete, batch,
                                    scratch_pool, scratch_pool));
           SVN_ERR(repack_revprops(fs, revprops, 0, left_count,
                                   changed_index, serialized, new_total_size,
@@ -1288,9 +1385,9 @@ write_packed_revprop(const char **final_
 
       if (left_count + right_count < revprops->sizes->nelts)
         {
-          SVN_ERR(repack_file_open(&file, fs, revprops, changed_index,
-                                   changed_index + 1, files_to_delete,
-                                   batch, scratch_pool, scratch_pool));
+          SVN_ERR(repack_file_open(&file, fs, revprops, rev,
+                                   files_to_delete, batch,
+                                   scratch_pool, scratch_pool));
           SVN_ERR(repack_revprops(fs, revprops, changed_index,
                                   changed_index + 1,
                                   changed_index, serialized, new_total_size,
@@ -1299,9 +1396,7 @@ write_packed_revprop(const char **final_
 
       if (right_count)
         {
-          SVN_ERR(repack_file_open(&file, fs, revprops,
-                                   revprops->sizes->nelts - right_count,
-                                   revprops->sizes->nelts,
+          SVN_ERR(repack_file_open(&file, fs, revprops, rev + 1,
                                    files_to_delete,  batch,
                                    scratch_pool, scratch_pool));
           SVN_ERR(repack_revprops(fs, revprops,
@@ -1317,15 +1412,7 @@ write_packed_revprop(const char **final_
       *tmp_path = apr_pstrcat(result_pool, *final_path, ".tmp", SVN_VA_NULL);
       SVN_ERR(svn_fs_x__batch_fsync_open_file(&file, batch, *tmp_path,
                                               scratch_pool));
-
-      stream = svn_stream_from_aprfile2(file, TRUE, scratch_pool);
-      for (i = 0; i < revprops->manifest->nelts; ++i)
-        {
-          const char *filename = APR_ARRAY_IDX(revprops->manifest, i,
-                                               const char*);
-          SVN_ERR(svn_stream_printf(stream, scratch_pool, "%s\n", filename));
-        }
-      SVN_ERR(svn_stream_close(stream));
+      SVN_ERR(write_manifest(file, revprops->manifest, scratch_pool));
     }
 
   return SVN_NO_ERROR;
@@ -1406,19 +1493,14 @@ svn_fs_x__packed_revprop_available(svn_b
                                    svn_revnum_t revision,
                                    apr_pool_t *scratch_pool)
 {
-  svn_fs_x__data_t *ffd = fs->fsap_data;
-  svn_stringbuf_t *content = NULL;
+  svn_node_kind_t kind;
+  packed_revprops_t *revprops;
+  svn_error_t *err;
 
   /* try to read the manifest file */
-  const char *folder = svn_fs_x__path_pack_shard(fs, revision, scratch_pool);
-  const char *manifest_path = svn_dirent_join(folder, PATH_MANIFEST,
-                                              scratch_pool);
-
-  svn_error_t *err = svn_fs_x__try_stringbuf_from_file(&content,
-                                                       missing,
-                                                       manifest_path,
-                                                       FALSE,
-                                                       scratch_pool);
+  revprops = apr_pcalloc(scratch_pool, sizeof(*revprops));
+  revprops->revision = revision;
+  err = get_revprop_packname(fs, revprops, scratch_pool, scratch_pool);
 
   /* if the manifest cannot be read, consider the pack files inaccessible
    * even if the file itself exists. */
@@ -1428,44 +1510,19 @@ svn_fs_x__packed_revprop_available(svn_b
       return FALSE;
     }
 
-  if (*missing)
-    return FALSE;
-
-  /* parse manifest content until we find the entry for REVISION.
-   * Revision 0 is never packed. */
-  revision = revision < ffd->max_files_per_dir
-           ? revision - 1
-           : revision % ffd->max_files_per_dir;
-  while (content->data)
+  /* the respective pack file must exist (and be a file) */
+  err = svn_io_check_path(get_revprop_pack_filepath(revprops,
+                                                    &revprops->entry,
+                                                    scratch_pool),
+                          &kind, scratch_pool);
+  if (err)
     {
-      char *next = strchr(content->data, '\n');
-      if (next)
-        {
-          *next = 0;
-          ++next;
-        }
-
-      if (revision-- == 0)
-        {
-          /* the respective pack file must exist (and be a file) */
-          svn_node_kind_t kind;
-          err = svn_io_check_path(svn_dirent_join(folder, content->data,
-                                                  scratch_pool),
-                                  &kind, scratch_pool);
-          if (err)
-            {
-              svn_error_clear(err);
-              return FALSE;
-            }
-
-          *missing = kind == svn_node_none;
-          return kind == svn_node_file;
-        }
-
-      content->data = next;
+      svn_error_clear(err);
+      return FALSE;
     }
 
-  return FALSE;
+  *missing = kind == svn_node_none;
+  return kind == svn_node_file;
 }
 
 
@@ -1573,21 +1630,19 @@ svn_fs_x__pack_revprops_shard(svn_fs_t *
 {
   const char *manifest_file_path, *pack_filename = NULL;
   apr_file_t *manifest_file;
-  svn_stream_t *manifest_stream;
   svn_revnum_t start_rev, end_rev, rev;
   apr_off_t total_size;
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
   apr_array_header_t *sizes;
+  apr_array_header_t *manifest;
 
   /* Some useful paths. */
   manifest_file_path = svn_dirent_join(pack_file_dir, PATH_MANIFEST,
                                        scratch_pool);
 
-  /* Create the manifest file stream. */
+  /* Create the manifest file. */
   SVN_ERR(svn_fs_x__batch_fsync_open_file(&manifest_file, batch,
                                           manifest_file_path, scratch_pool));
-  manifest_stream = svn_stream_from_aprfile2(manifest_file, TRUE,
-                                             scratch_pool);
 
   /* revisions to handle. Special case: revision 0 */
   start_rev = (svn_revnum_t) (shard * max_files_per_dir);
@@ -1611,6 +1666,8 @@ svn_fs_x__pack_revprops_shard(svn_fs_t *
   sizes = apr_array_make(scratch_pool, max_files_per_dir, sizeof(apr_off_t));
   total_size = 2 * SVN_INT64_BUFFER_SIZE;
 
+  manifest = apr_array_make(scratch_pool, 4, sizeof(manifest_entry_t));
+
   /* Iterate over the revisions in this shard, determine their size and
    * squashing them together into pack files. */
   for (rev = start_rev; rev <= end_rev; rev++)
@@ -1644,10 +1701,13 @@ svn_fs_x__pack_revprops_shard(svn_fs_t *
       /* Update the manifest. Allocate a file name for the current pack
        * file if it is a new one */
       if (sizes->nelts == 0)
-        pack_filename = apr_psprintf(scratch_pool, "%ld.0", rev);
+        {
+          manifest_entry_t *entry = apr_array_push(manifest);
+          entry->start_rev = rev;
+          entry->tag = 0;
 
-      SVN_ERR(svn_stream_printf(manifest_stream, iterpool, "%s\n",
-                                pack_filename));
+          pack_filename = apr_psprintf(scratch_pool, "%ld.0", rev);
+        }
 
       /* add to list of files to put into the current pack file */
       APR_ARRAY_PUSH(sizes, apr_off_t) = finfo.size;
@@ -1661,8 +1721,9 @@ svn_fs_x__pack_revprops_shard(svn_fs_t *
                           (apr_size_t)total_size, compression_level,
                           batch, cancel_func, cancel_baton, iterpool));
 
+  SVN_ERR(write_manifest(manifest_file, manifest, iterpool));
+
   /* flush all data to disk and update permissions */
-  SVN_ERR(svn_stream_close(manifest_stream));
   SVN_ERR(svn_io_copy_perms(shard_path, pack_file_dir, iterpool));
   svn_pool_destroy(iterpool);
 

Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/transaction.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/transaction.c?rev=1713386&r1=1713385&r2=1713386&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/transaction.c (original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/transaction.c Mon Nov  9 11:06:42 2015
@@ -877,61 +877,33 @@ unparse_dir_entry(svn_fs_x__dirent_t *di
                   apr_pool_t *scratch_pool)
 {
   apr_size_t to_write;
-  svn_string_t *id_str = svn_fs_x__id_unparse(&dirent->id, scratch_pool);
   apr_size_t name_len = strlen(dirent->name);
 
-  /* Note that sizeof == len + 1, i.e. accounts for the space between
-   * type and ID. */
-  apr_size_t type_len = (dirent->kind == svn_node_file)
-                      ? sizeof(SVN_FS_X__KIND_FILE)
-                      : sizeof(SVN_FS_X__KIND_DIR);
-  apr_size_t value_len = type_len + id_str->len;
-
   /* A buffer with sufficient space for 
-   * - both string lines
-   * - 4 newlines
-   * - 2 lines K/V lines containing a number each
+   * - entry name + 1 terminating NUL
+   * - 1 byte for the node kind
+   * - 2 numbers in 7b/8b encoding for the noderev-id
    */
-  char *buffer = apr_palloc(scratch_pool,   name_len + value_len
-                                          + 4
-                                          + 2 * (2 + SVN_INT64_BUFFER_SIZE));
+  apr_byte_t *buffer = apr_palloc(scratch_pool,
+                                  name_len + 2 + 2 * SVN__MAX_ENCODED_UINT_LEN);
 
   /* Now construct the value. */
-  char *p = buffer;
-
-  /* The "K length(name)\n" line. */
-  p[0] = 'K';
-  p[1] = ' ';
-  p += 2;
-  p += svn__i64toa(p, name_len);
-  *(p++) = '\n';
+  apr_byte_t *p = buffer;
 
-  /* The line with the key, i.e. dir entry name. */
-  memcpy(p, dirent->name, name_len);
-  p += name_len;
-  *(p++) = '\n';
-
-  /* The "V length(type+id)\n" line. */
-  p[0] = 'V';
-  p[1] = ' ';
-  p += 2;
-  p += svn__i64toa(p, value_len);
-  *(p++) = '\n';
-
-  /* The line with the type and ID. */
-  memcpy(p,
-         (dirent->kind == svn_node_file) ? SVN_FS_X__KIND_FILE
-                                         : SVN_FS_X__KIND_DIR,
-         type_len - 1);
-  p += type_len - 1;
-  *(p++) = ' ';
-  memcpy(p, id_str->data, id_str->len);
-  p+=id_str->len;
-  *(p++) = '\n';
+  /* The entry name, terminated by NUL. */
+  memcpy(p, dirent->name, name_len + 1);
+  p += name_len + 1;
+
+  /* The entry type. */
+  p = svn__encode_uint(p, dirent->kind);
+
+  /* The ID. */
+  p = svn__encode_int(p, dirent->id.change_set);
+  p = svn__encode_uint(p, dirent->id.number);
 
   /* Add the entry to the output stream. */
   to_write = p - buffer;
-  SVN_ERR(svn_stream_write(stream, buffer, &to_write));
+  SVN_ERR(svn_stream_write(stream, (const char *)buffer, &to_write));
 
   return SVN_NO_ERROR;
 }
@@ -943,8 +915,15 @@ unparse_dir_entries(apr_array_header_t *
                     svn_stream_t *stream,
                     apr_pool_t *scratch_pool)
 {
+  apr_byte_t buffer[SVN__MAX_ENCODED_UINT_LEN];
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
   int i;
+
+  /* Write the number of entries. */
+  apr_size_t to_write = svn__encode_uint(buffer, entries->nelts) - buffer;
+  SVN_ERR(svn_stream_write(stream, (const char *)buffer, &to_write));
+
+  /* Write all entries */
   for (i = 0; i < entries->nelts; ++i)
     {
       svn_fs_x__dirent_t *dirent;
@@ -954,9 +933,6 @@ unparse_dir_entries(apr_array_header_t *
       SVN_ERR(unparse_dir_entry(dirent, stream, iterpool));
     }
 
-  SVN_ERR(svn_stream_printf(stream, scratch_pool, "%s\n",
-                            SVN_HASH_TERMINATOR));
-
   svn_pool_destroy(iterpool);
   return SVN_NO_ERROR;
 }
@@ -1240,60 +1216,6 @@ create_new_txn_noderev_from_rev(svn_fs_t
   return svn_fs_x__put_node_revision(fs, noderev, scratch_pool);
 }
 
-/* Read 'txn-current', return it in *TXN_NUMBER and write the next value
-   into 'txn-next' for FS.  Schedule fsyncs in BATCH.  Use SCRATCH_POOL
-   for temporaries. */
-static svn_error_t *
-get_and_txn_key(apr_uint64_t *txn_number,
-                svn_fs_t *fs,
-                svn_fs_x__batch_fsync_t *batch,
-                apr_pool_t *scratch_pool)
-{
-  const char *txn_current_path = svn_fs_x__path_txn_current(fs, scratch_pool);
-  const char *txn_next_path = svn_fs_x__path_txn_next(fs, scratch_pool);
-
-  apr_file_t *file;
-  char new_id_str[SVN_INT64_BUFFER_SIZE];
-
-  svn_stringbuf_t *buf;
-  SVN_ERR(svn_fs_x__read_content(&buf, txn_current_path, scratch_pool));
-
-  /* remove trailing newlines */
-  *txn_number = svn__base36toui64(NULL, buf->data);
-  if (*txn_number == 0)
-    ++(*txn_number);
-
-  /* Increment the key and add a trailing \n to the string so the
-     txn-current file has a newline in it. */
-  SVN_ERR(svn_fs_x__batch_fsync_open_file(&file, batch, txn_next_path,
-                                          scratch_pool));
-  SVN_ERR(svn_io_file_write_full(file, new_id_str,
-                                 svn__ui64tobase36(new_id_str, *txn_number+1),
-                                 NULL, scratch_pool));
-  SVN_ERR(svn_io_copy_perms(txn_current_path, txn_next_path, scratch_pool));
-
-  return SVN_NO_ERROR;
-}
-
-/* Move 'txn-next' into place as 'txn-current' for FS.  Schedule fsyncs
-   in BATCH.  Use SCRATCH_POOL for temporaries. */
-static svn_error_t *
-bump_txn_key(svn_fs_t *fs,
-             svn_fs_x__batch_fsync_t *batch,
-             apr_pool_t *scratch_pool)
-{
-  const char *txn_current_path = svn_fs_x__path_txn_current(fs, scratch_pool);
-  const char *txn_next_path = svn_fs_x__path_txn_next(fs, scratch_pool);
-
-  /* Increment the key and add a trailing \n to the string so the
-     txn-current file has a newline in it. */
-  SVN_ERR(svn_fs_x__move_into_place(txn_next_path, txn_current_path,
-                                    txn_current_path, batch,
-                                    scratch_pool));
-
-  return SVN_NO_ERROR;
-}
-
 /* A structure used by get_and_increment_txn_key_body(). */
 typedef struct get_and_increment_txn_key_baton_t
 {
@@ -1309,14 +1231,57 @@ get_and_increment_txn_key_body(void *bat
                                apr_pool_t *scratch_pool)
 {
   get_and_increment_txn_key_baton_t *cb = baton;
-  svn_fs_x__batch_fsync_t *batch;
+  svn_fs_t *fs = cb->fs;
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  const char *txn_current_path = svn_fs_x__path_txn_current(fs, scratch_pool);
+  char new_id_str[SVN_INT64_BUFFER_SIZE];
 
-  SVN_ERR(svn_fs_x__batch_fsync_create(&batch, scratch_pool));
-  SVN_ERR(get_and_txn_key(&cb->txn_number, cb->fs, batch, scratch_pool));
-  SVN_ERR(svn_fs_x__batch_fsync_run(batch, scratch_pool));
+  svn_stringbuf_t *buf;
+  SVN_ERR(svn_fs_x__read_content(&buf, txn_current_path, scratch_pool));
 
-  SVN_ERR(bump_txn_key(cb->fs, batch, scratch_pool));
-  SVN_ERR(svn_fs_x__batch_fsync_run(batch, scratch_pool));
+  /* Parse the txn number, stopping at the next non-digit.
+   *
+   * Note that an empty string is being interpreted as "0".
+   * This gives us implicit recovery if the file contents should be lost
+   * due to e.g. power failure.
+   */
+  cb->txn_number = svn__base36toui64(NULL, buf->data);
+  if (cb->txn_number == 0)
+    ++cb->txn_number;
+
+  /* Check for conflicts.  Those might happen if the server crashed and we
+   * had 'svnadmin recover' reset the txn counter.
+   *
+   * Once we found an unused txn id, claim it by creating the respective
+   * txn directory.
+   *
+   * Note that this is not racy because we hold the txn-current-lock.
+   */
+  while (TRUE)
+    {
+      const char *txn_dir;
+      svn_node_kind_t kind;
+      svn_pool_clear(iterpool);
+
+      txn_dir = svn_fs_x__path_txn_dir(fs, cb->txn_number, iterpool);
+      SVN_ERR(svn_io_check_path(txn_dir, &kind, iterpool));
+      if (kind == svn_node_none)
+        {
+          svn_io_dir_make(txn_dir, APR_OS_DEFAULT, iterpool);
+          break;
+        }
+
+      ++cb->txn_number;
+    }
+
+  /* Increment the key and add a trailing \n to the string so the
+     txn-current file has a newline in it. */
+  SVN_ERR(svn_io_write_atomic2(txn_current_path, new_id_str,
+                               svn__ui64tobase36(new_id_str,
+                                                 cb->txn_number + 1),
+                               txn_current_path, FALSE, scratch_pool));
+
+  svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;
 }
@@ -1332,38 +1297,21 @@ create_txn_dir(const char **id_p,
                apr_pool_t *result_pool,
                apr_pool_t *scratch_pool)
 {
-  const char *txn_dir;
-  svn_fs_x__data_t *ffd = fs->fsap_data;
-
-  /* If we recently committed a revision through FS, we will have a
-     pre-allocated txn-ID that we can just use. */
-  if (ffd->next_txn_id)
-    {
-      *txn_id = ffd->next_txn_id;
-
-      /* Not pre-allocated anymore. */
-      ffd->next_txn_id = 0;
-    }
-  else
-    {
-      get_and_increment_txn_key_baton_t cb;
-
-      /* Get the current transaction sequence value, which is a base-36
-        number, from the txn-current file, and write an
-        incremented value back out to the file.  Place the revision
-        number the transaction is based off into the transaction id. */
-      cb.fs = fs;
-      SVN_ERR(svn_fs_x__with_txn_current_lock(fs,
-                                              get_and_increment_txn_key_body,
-                                              &cb,
-                                              scratch_pool));
-      *txn_id = cb.txn_number;
-    }
+  get_and_increment_txn_key_baton_t cb;
 
+  /* Get the current transaction sequence value, which is a base-36
+    number, from the txn-current file, and write an
+    incremented value back out to the file.  Place the revision
+    number the transaction is based off into the transaction id. */
+  cb.fs = fs;
+  SVN_ERR(svn_fs_x__with_txn_current_lock(fs,
+                                          get_and_increment_txn_key_body,
+                                          &cb,
+                                          scratch_pool));
+  *txn_id = cb.txn_number;
   *id_p = svn_fs_x__txn_name(*txn_id, result_pool);
-  txn_dir = svn_fs_x__path_txn_dir(fs, *txn_id, scratch_pool);
 
-  return svn_io_dir_make(txn_dir, APR_OS_DEFAULT, scratch_pool);
+  return SVN_NO_ERROR;
 }
 
 /* Create a new transaction in filesystem FS, based on revision REV,
@@ -1860,6 +1808,7 @@ svn_fs_x__set_entry(svn_fs_t *fs,
   svn_fs_x__data_t *ffd = fs->fsap_data;
   apr_pool_t *subpool = svn_pool_create(scratch_pool);
   const svn_fs_x__id_t *key = &(parent_noderev->noderev_id);
+  svn_fs_x__dirent_t entry;
 
   if (!rep || !svn_fs_x__is_txn(rep->id.change_set))
     {
@@ -1940,21 +1889,17 @@ svn_fs_x__set_entry(svn_fs_t *fs,
         }
     }
 
-  /* Append an incremental hash entry for the entry change. */
+  /* Append an incremental hash entry for the entry change.
+     A deletion is represented by an "unused" noderev-id. */
   if (id)
-    {
-      svn_fs_x__dirent_t entry;
-      entry.name = name;
-      entry.id = *id;
-      entry.kind = kind;
-
-      SVN_ERR(unparse_dir_entry(&entry, out, subpool));
-    }
+    entry.id = *id;
   else
-    {
-      SVN_ERR(svn_stream_printf(out, subpool, "D %" APR_SIZE_T_FMT "\n%s\n",
-                                strlen(name), name));
-    }
+    svn_fs_x__id_reset(&entry.id);
+
+  entry.name = name;
+  entry.kind = kind;
+
+  SVN_ERR(unparse_dir_entry(&entry, out, subpool));
 
   /* Flush APR buffers. */
   SVN_ERR(svn_io_file_flush(file, subpool));
@@ -3068,6 +3013,10 @@ get_final_id(svn_fs_x__id_t *part,
    node-revision.  It is only controls additional sanity checking
    logic.
 
+   CHANGED_PATHS is the changed paths hash for the new revision.
+   The noderev-ids in it will be updated as soon as the respective
+   nodesrevs got their final IDs assigned.
+
    Temporary allocations are also from SCRATCH_POOL. */
 static svn_error_t *
 write_final_rev(svn_fs_x__id_t *new_id_p,
@@ -3081,6 +3030,7 @@ write_final_rev(svn_fs_x__id_t *new_id_p
                 apr_hash_t *reps_hash,
                 apr_pool_t *reps_pool,
                 svn_boolean_t at_root,
+                apr_hash_t *changed_paths,
                 apr_pool_t *scratch_pool)
 {
   svn_fs_x__noderev_t *noderev;
@@ -3093,6 +3043,7 @@ write_final_rev(svn_fs_x__id_t *new_id_p
   svn_fs_x__change_set_t change_set = svn_fs_x__change_set_by_rev(rev);
   svn_stream_t *file_stream;
   apr_pool_t *subpool;
+  svn_fs_x__change_t *change;
 
   /* Check to see if this is a transaction node. */
   if (txn_id == SVN_FS_X__INVALID_TXN_ID)
@@ -3123,9 +3074,8 @@ write_final_rev(svn_fs_x__id_t *new_id_p
           SVN_ERR(write_final_rev(&new_id, file, rev, fs, &dirent->id,
                                   initial_offset, directory_ids,
                                   reps_to_cache, reps_hash,
-                                  reps_pool, FALSE, subpool));
-          if (   svn_fs_x__id_used(&new_id)
-              && (svn_fs_x__get_revnum(new_id.change_set) == rev))
+                                  reps_pool, FALSE, changed_paths, subpool));
+          if (new_id.change_set == change_set)
             dirent->id = new_id;
         }
 
@@ -3271,12 +3221,38 @@ write_final_rev(svn_fs_x__id_t *new_id_p
 
   SVN_ERR(store_p2l_index_entry(fs, txn_id, &entry, scratch_pool));
 
+  /* Update the ID within the changed paths list. */
+  change = svn_hash_gets(changed_paths, noderev->created_path);
+  if (change)
+    change->noderev_id = noderev->noderev_id;
+
   /* Return our ID that references the revision file. */
   *new_id_p = new_id;
 
   return SVN_NO_ERROR;
 }
 
+/* Reset all in-transaction noderev-IDs in CHANGED_PATHS.  They should
+   belong to deleted nodes only.  At any rate, these IDs become invalid
+   as soon as transaction got committed.
+   Perform temporary allocations in SCRATCH_POOL. */
+static svn_error_t *
+sanitize_changed_path_info(apr_hash_t *changed_paths,
+                           apr_pool_t *scratch_pool)
+{
+  apr_hash_index_t *hi;
+  for (hi = apr_hash_first(scratch_pool, changed_paths);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      svn_fs_x__change_t *change = apr_hash_this_val(hi);
+      if (svn_fs_x__is_txn(change->noderev_id.change_set))
+        svn_fs_x__id_reset(&change->noderev_id);
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /* Write the changed path info CHANGED_PATHS from transaction TXN_ID to the
    permanent rev-file FILE representing NEW_REV in filesystem FS.  *OFFSET_P
    is set the to offset in the file of the beginning of this information.
@@ -3675,15 +3651,11 @@ bump_ids(void *baton,
          apr_pool_t *scratch_pool)
 {
   bump_ids_baton_t *b = baton;
-  svn_fs_x__data_t *ffd = b->fs->fsap_data;
   const char *current_filename;
 
   /* Write the 'next' file. */
   SVN_ERR(write_next_file(b->fs, b->new_rev, b->batch, scratch_pool));
 
-  /* Allocate a new txn id. */
-  SVN_ERR(get_and_txn_key(&ffd->next_txn_id, b->fs, b->batch, scratch_pool));
-
   /* Commit all changes to disk. */
   SVN_ERR(svn_fs_x__batch_fsync_run(b->batch, scratch_pool));
 
@@ -3693,9 +3665,6 @@ bump_ids(void *baton,
                                     current_filename, current_filename,
                                     b->batch, scratch_pool));
 
-  /* Bump txn id. */
-  SVN_ERR(bump_txn_key(b->fs, b->batch, scratch_pool));
-
   /* Make the new revision permanently visible. */
   SVN_ERR(svn_fs_x__batch_fsync_run(b->batch, scratch_pool));
 
@@ -3830,10 +3799,12 @@ commit_body(void *baton,
   svn_fs_x__init_txn_root(&root_id, txn_id);
   SVN_ERR(write_final_rev(&new_root_id, proto_file, new_rev, cb->fs, &root_id,
                           initial_offset, directory_ids, cb->reps_to_cache,
-                          cb->reps_hash, cb->reps_pool, TRUE, subpool));
+                          cb->reps_hash, cb->reps_pool, TRUE, changed_paths,
+                          subpool));
   svn_pool_clear(subpool);
 
   /* Write the changed-path information. */
+  SVN_ERR(sanitize_changed_path_info(changed_paths, subpool));
   SVN_ERR(write_final_changed_path_info(&changed_path_offset, proto_file,
                                         cb->fs, txn_id, changed_paths,
                                         new_rev, subpool));

Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/util.c?rev=1713386&r1=1713385&r2=1713386&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/util.c (original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/util.c Mon Nov  9 11:06:42 2015
@@ -124,13 +124,6 @@ svn_fs_x__path_txn_current(svn_fs_t *fs,
 }
 
 const char *
-svn_fs_x__path_txn_next(svn_fs_t *fs,
-                           apr_pool_t *result_pool)
-{
-  return svn_dirent_join(fs->path, PATH_TXN_NEXT, result_pool);
-}
-
-const char *
 svn_fs_x__path_txn_current_lock(svn_fs_t *fs,
                                 apr_pool_t *result_pool)
 {

Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/util.h
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/util.h?rev=1713386&r1=1713385&r2=1713386&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/util.h (original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/util.h Mon Nov  9 11:06:42 2015
@@ -123,13 +123,6 @@ const char *
 svn_fs_x__path_txn_current(svn_fs_t *fs,
                            apr_pool_t *result_pool);
 
-/* Return the full path of the "txn-next" file in FS.
- * The result will be allocated in RESULT_POOL.
- */
-const char *
-svn_fs_x__path_txn_next(svn_fs_t *fs,
-                        apr_pool_t *result_pool);
-
 /* Return the full path of the "txn-current-lock" file in FS.
  * The result will be allocated in RESULT_POOL.
  */

Modified: subversion/branches/move-tracking-2/subversion/libsvn_subr/compress.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_subr/compress.c?rev=1713386&r1=1713385&r2=1713386&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_subr/compress.c (original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_subr/compress.c Mon Nov  9 11:06:42 2015
@@ -82,6 +82,17 @@ svn__encode_uint(unsigned char *p, apr_u
   return p;
 }
 
+unsigned char *
+svn__encode_int(unsigned char *p, apr_int64_t val)
+{
+  apr_uint64_t value = val;
+  value = value & APR_UINT64_C(0x8000000000000000)
+        ? APR_UINT64_MAX - (2 * value)
+        : 2 * value;
+
+  return svn__encode_uint(p, value);
+}
+
 const unsigned char *
 svn__decode_uint(apr_uint64_t *val,
                  const unsigned char *p,
@@ -111,6 +122,22 @@ svn__decode_uint(apr_uint64_t *val,
   return NULL;
 }
 
+const unsigned char *
+svn__decode_int(apr_int64_t *val,
+                const unsigned char *p,
+                const unsigned char *end)
+{
+  apr_uint64_t value;
+  const unsigned char *result = svn__decode_uint(&value, p, end);
+
+  value = value & 1
+        ? (APR_UINT64_MAX - value / 2)
+        : value / 2;
+  *val = (apr_int64_t)value;
+
+  return result;
+}
+
 /* If IN is a string that is >= MIN_COMPRESS_SIZE and the COMPRESSION_LEVEL
    is not SVN_DELTA_COMPRESSION_LEVEL_NONE, zlib compress it and places the
    result in OUT, with an integer prepended specifying the original size.

Modified: subversion/branches/move-tracking-2/subversion/libsvn_subr/packed_data.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_subr/packed_data.c?rev=1713386&r1=1713385&r2=1713386&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/libsvn_subr/packed_data.c (original)
+++ subversion/branches/move-tracking-2/subversion/libsvn_subr/packed_data.c Mon Nov  9 11:06:42 2015
@@ -308,7 +308,7 @@ unmap_uint(apr_uint64_t value)
  * are no sub-streams).
  */
 static void
-svn_packed__data_flush_buffer(svn_packed__int_stream_t *stream)
+data_flush_buffer(svn_packed__int_stream_t *stream)
 {
   packed_int_private_t *private_data = stream->private_data;
   apr_size_t i;
@@ -382,7 +382,7 @@ svn_packed__add_uint(svn_packed__int_str
 {
   stream->buffer[stream->buffer_used] = value;
   if (++stream->buffer_used == SVN__PACKED_DATA_BUFFER_SIZE)
-    svn_packed__data_flush_buffer(stream);
+    data_flush_buffer(stream);
 }
 
 void
@@ -435,7 +435,7 @@ write_int_stream_structure(svn_stringbuf
                                    + (private_data->is_signed ? 2 : 0));
 
       /* store item count and length their of packed representation */
-      svn_packed__data_flush_buffer(stream);
+      data_flush_buffer(stream);
 
       write_packed_uint(tree_struct, private_data->item_count);
       write_packed_uint(tree_struct, private_data->packed

Modified: subversion/branches/move-tracking-2/subversion/mod_dav_svn/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/mod_dav_svn/util.c?rev=1713386&r1=1713385&r2=1713386&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/mod_dav_svn/util.c (original)
+++ subversion/branches/move-tracking-2/subversion/mod_dav_svn/util.c Mon Nov  9 11:06:42 2015
@@ -620,7 +620,7 @@ dav_svn__make_base64_output_stream(apr_b
   wb->output = output;
   svn_stream_set_write(stream, brigade_write_fn);
 
-  return svn_base64_encode2(stream, TRUE, pool);
+  return svn_base64_encode2(stream, FALSE, pool);
 }
 
 void