Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/cached_data.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/cached_data.c?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/cached_data.c 
(original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/cached_data.c Sun Jun 
14 20:58:10 2015
@@ -286,6 +286,114 @@ use_block_read(svn_fs_t *fs)
   return svn_fs_fs__use_log_addressing(fs) && ffd->use_block_read;
 }
 
+svn_error_t *
+svn_fs_fs__fixup_expanded_size(svn_fs_t *fs,
+                               representation_t *rep,
+                               apr_pool_t *scratch_pool)
+{
+  svn_checksum_t checksum;
+  svn_checksum_t *empty_md5;
+  svn_fs_fs__revision_file_t *revision_file;
+  svn_fs_fs__rep_header_t *rep_header;
+
+  /* Anything to do at all?
+   *
+   * Note that a 0 SIZE is only possible for PLAIN reps due to the SVN\1
+   * magic prefix in any DELTA rep. */
+  if (!rep || rep->expanded_size != 0 || rep->size == 0)
+    return SVN_NO_ERROR;
+
+  /* This function may only be called for committed data. */
+  assert(!svn_fs_fs__id_txn_used(&rep->txn_id));
+
+  /* EXPANDED_SIZE is 0. If the MD5 does not match the one for empty
+   * contents, we know that EXPANDED_SIZE == 0 is wrong and needs to
+   * be set to the actual value given by SIZE.
+   *
+   * Using svn_checksum_match() will also accept all-zero values for
+   * the MD5 digest and only report a mismatch if the MD5 has actually
+   * been given. */
+  empty_md5 = svn_checksum_empty_checksum(svn_checksum_md5, scratch_pool);
+
+  checksum.digest = rep->md5_digest;
+  checksum.kind = svn_checksum_md5;
+  if (!svn_checksum_match(empty_md5, &checksum))
+    {
+      rep->expanded_size = rep->size;
+      return SVN_NO_ERROR;
+    }
+
+  /* Data in the rep-cache.db does not have MD5 checksums (all zero) on it.
+   * Compare SHA1 instead. */
+  if (rep->has_sha1)
+    {
+      svn_checksum_t *empty_sha1
+        = svn_checksum_empty_checksum(svn_checksum_sha1, scratch_pool);
+
+      checksum.digest = rep->sha1_digest;
+      checksum.kind = svn_checksum_sha1;
+      if (!svn_checksum_match(empty_sha1, &checksum))
+        {
+          rep->expanded_size = rep->size;
+          return SVN_NO_ERROR;
+        }
+    }
+
+  /* Only two cases are left here.
+   * (1) A non-empty PLAIN rep with a MD5 collision on EMPTY_MD5.
+   * (2) A DELTA rep with zero-length output. */
+
+  /* SVN always stores a DELTA rep with zero-length output as an empty
+   * sequence of txdelta windows, i.e. as "SVN\1".  In that case, SIZE is
+   * 4 bytes.  There is no other possible DELTA rep of that size and any
+   * PLAIN rep of 4 bytes would produce a different MD5.  Hence, if SIZE is
+   * actually 4 here, we know that this is an empty DELTA rep.
+   *
+   * Note that it is technically legal to have DELTA reps with a 0 length
+   * output window.  Their on-disk size would be longer.  We handle that
+   * case later together with the equally unlikely MD5 collision. */
+  if (rep->size == 4)
+    {
+      /* EXPANDED_SIZE is already 0. */
+      return SVN_NO_ERROR;
+    }
+
+  /* We still have the two options, PLAIN or DELTA rep.  At this point, we
+   * are in an extremely unlikely case and can spend some time to figure it
+   * out.  So, let's just look at the representation header. */
+  SVN_ERR(open_and_seek_revision(&revision_file, fs, rep->revision,
+                                 rep->item_index, scratch_pool));
+  SVN_ERR(svn_fs_fs__read_rep_header(&rep_header, revision_file->stream,
+                                     scratch_pool, scratch_pool));
+  SVN_ERR(svn_fs_fs__close_revision_file(revision_file));
+
+  /* Only for PLAIN reps do we have to correct EXPANDED_SIZE. */
+  if (rep_header->type == svn_fs_fs__rep_plain)
+    rep->expanded_size = rep->size;
+
+  return SVN_NO_ERROR;
+}
+
+/* Correct known issues with committed NODEREV in FS.
+ * Uses SCRATCH_POOL for temporaries.
+ */
+static svn_error_t *
+fixup_node_revision(svn_fs_t *fs,
+                    node_revision_t *noderev,
+                    apr_pool_t *scratch_pool)
+{
+  /* Workaround issue #4031: is-fresh-txn-root in revision files. */
+  noderev->is_fresh_txn_root = FALSE;
+
+  /* Make sure EXPANDED_SIZE has the correct value for every rep. */
+  SVN_ERR(svn_fs_fs__fixup_expanded_size(fs, noderev->data_rep,
+                                         scratch_pool));
+  SVN_ERR(svn_fs_fs__fixup_expanded_size(fs, noderev->prop_rep,
+                                         scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
 /* Get the node-revision for the node ID in FS.
    Set *NODEREV_P to the new node-revision structure, allocated in POOL.
    See svn_fs_fs__get_node_revision, which wraps this and adds another
@@ -376,9 +484,7 @@ get_node_revision_body(node_revision_t *
                                           revision_file->stream,
                                           result_pool,
                                           scratch_pool));
-
-          /* Workaround issue #4031: is-fresh-txn-root in revision files. */
-          (*noderev_p)->is_fresh_txn_root = FALSE;
+          SVN_ERR(fixup_node_revision(fs, *noderev_p, scratch_pool));
 
           /* The noderev is not in cache, yet. Add it, if caching has been 
enabled. */
           if (ffd->node_revision_cache)
@@ -631,7 +737,7 @@ typedef struct rep_state_t
                        -1 if the offset is yet unknown. */
   apr_off_t current;/* The current offset relative to START. */
   apr_off_t size;   /* The on-disk size of the representation. */
-  int ver;          /* If a delta, what svndiff version? 
+  int ver;          /* If a delta, what svndiff version?
                        -1 for unknown delta version. */
   int chunk_index;  /* number of the window to read */
 } rep_state_t;
@@ -765,9 +871,7 @@ create_rep_state_body(rep_state_t **rep_
      Since we don't know the depth of the delta chain, let's assume, the
      whole contents get rewritten 3 times.
    */
-  estimated_window_storage
-    = 4 * (  (rep->expanded_size ? rep->expanded_size : rep->size)
-           + SVN_DELTA_WINDOW_SIZE);
+  estimated_window_storage = 4 * (rep->expanded_size + SVN_DELTA_WINDOW_SIZE);
   estimated_window_storage = MIN(estimated_window_storage, APR_SIZE_MAX);
 
   rs->window_cache =    ffd->txdelta_window_cache
@@ -952,7 +1056,7 @@ svn_fs_fs__check_rep(representation_t *r
       if (   entry == NULL
           || entry->type < SVN_FS_FS__ITEM_TYPE_FILE_REP
           || entry->type > SVN_FS_FS__ITEM_TYPE_DIR_PROPS)
-        return svn_error_createf(SVN_ERR_REPOS_CORRUPTED, NULL,
+        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
                                  _("No representation found at offset %s "
                                    "for item %s in revision %ld"),
                                  apr_off_t_toa(scratch_pool, offset),
@@ -991,7 +1095,7 @@ svn_fs_fs__rep_chain_length(int *chain_l
   int count = 0;
   int shards = 1;
   svn_revnum_t last_shard = rep->revision / shard_size;
-  
+
   /* Check whether the length of the deltification chain is acceptable.
    * Otherwise, shared reps may form a non-skipping delta chain in
    * extreme cases. */
@@ -1104,7 +1208,7 @@ struct rep_read_baton
   /* The text we've been reading, if we're going to cache it. */
   svn_stringbuf_t *current_fulltext;
 
-  /* If not NULL, attempt to read the data from this cache. 
+  /* If not NULL, attempt to read the data from this cache.
      Once that lookup fails, reset it to NULL. */
   svn_cache__t *fulltext_cache;
 
@@ -1322,15 +1426,11 @@ set_cached_combined_window(svn_stringbuf
    ID, and representation REP.
    Also, set *WINDOW_P to the base window content for *LIST, if it
    could be found in cache. Otherwise, *LIST will contain the base
-   representation for the whole delta chain.
-   Finally, return the expanded size of the representation in
-   *EXPANDED_SIZE. It will take care of cases where only the on-disk
-   size is known.  */
+   representation for the whole delta chain. */
 static svn_error_t *
 build_rep_list(apr_array_header_t **list,
                svn_stringbuf_t **window_p,
                rep_state_t **src_state,
-               svn_filesize_t *expanded_size,
                svn_fs_t *fs,
                representation_t *first_rep,
                apr_pool_t *pool)
@@ -1345,24 +1445,9 @@ build_rep_list(apr_array_header_t **list
   *list = apr_array_make(pool, 1, sizeof(rep_state_t *));
   rep = *first_rep;
 
-  /* The value as stored in the data struct.
-     0 is either for unknown length or actually zero length. */
-  *expanded_size = first_rep->expanded_size;
-
   /* for the top-level rep, we need the rep_args */
   SVN_ERR(create_rep_state(&rs, &rep_header, &shared_file, &rep, fs, pool,
                            iterpool));
-
-  /* Unknown size or empty representation?
-     That implies the this being the first iteration.
-     Usually size equals on-disk size, except for empty,
-     compressed representations (delta, size = 4).
-     Please note that for all non-empty deltas have
-     a 4-byte header _plus_ some data. */
-  if (*expanded_size == 0)
-    if (rep_header->type == svn_fs_fs__rep_plain || first_rep->size != 4)
-      *expanded_size = first_rep->size;
-
   while (1)
     {
       svn_pool_clear(iterpool);
@@ -1550,7 +1635,7 @@ read_plain_window(svn_stringbuf_t **nwin
                   apr_pool_t *scratch_pool)
 {
   apr_off_t offset;
-  
+
   /* RS->FILE may be shared between RS instances -> make sure we point
    * to the right data. */
   SVN_ERR(auto_open_shared_file(rs->sfile));
@@ -1624,9 +1709,11 @@ get_combined_window(svn_stringbuf_t **re
       /* Maybe, we've got a PLAIN start representation.  If we do, read
          as much data from it as the needed for the txdelta window's source
          view.
-         Note that BUF / SOURCE may only be NULL in the first iteration. */
+         Note that BUF / SOURCE may only be NULL in the first iteration.
+         Also note that we may have short-cut reading the delta chain --
+         in which case SRC_OPS is 0 and it might not be a PLAIN rep. */
       source = buf;
-      if (source == NULL && rb->src_state != NULL)
+      if (source == NULL && rb->src_state != NULL && window->src_ops)
         SVN_ERR(read_plain_window(&source, rb->src_state, window->sview_len,
                                   pool, iterpool));
 
@@ -1664,7 +1751,7 @@ get_combined_window(svn_stringbuf_t **re
 }
 
 /* Returns whether or not the expanded fulltext of the file is cachable
- * based on its size SIZE.  The decision depends on the cache used by RB.
+ * based on its size SIZE.  The decision depends on the cache used by FFD.
  */
 static svn_boolean_t
 fulltext_size_is_cachable(fs_fs_data_t *ffd, svn_filesize_t size)
@@ -1997,11 +2084,12 @@ rep_read_contents(void *baton,
   if (!rb->rs_list)
     {
       /* Window stream not initialized, yet.  Do it now. */
+      rb->len = rb->rep.expanded_size;
       SVN_ERR(build_rep_list(&rb->rs_list, &rb->base_window,
-                             &rb->src_state, &rb->len, rb->fs, &rb->rep,
+                             &rb->src_state, rb->fs, &rb->rep,
                              rb->filehandle_pool));
 
-      /* In case we did read from the fulltext cache before, make the 
+      /* In case we did read from the fulltext cache before, make the
        * window stream catch up.  Also, initialize the fulltext buffer
        * if we want to cache the fulltext at the end. */
       SVN_ERR(skip_contents(rb, rb->fulltext_delivered));
@@ -2065,7 +2153,6 @@ svn_fs_fs__get_contents(svn_stream_t **c
   else
     {
       fs_fs_data_t *ffd = fs->fsap_data;
-      svn_filesize_t len = rep->expanded_size ? rep->expanded_size : rep->size;
       struct rep_read_baton *rb;
 
       pair_cache_key_t fulltext_cache_key = { 0 };
@@ -2081,7 +2168,7 @@ svn_fs_fs__get_contents(svn_stream_t **c
        * cache it. */
       if (ffd->fulltext_cache && cache_fulltext
           && SVN_IS_VALID_REVNUM(rep->revision)
-          && fulltext_size_is_cachable(ffd, len))
+          && fulltext_size_is_cachable(ffd, rep->expanded_size))
         {
           rb->fulltext_cache = ffd->fulltext_cache;
         }
@@ -2437,11 +2524,41 @@ read_dir_entries(apr_array_header_t *ent
   return SVN_NO_ERROR;
 }
 
-/* Fetch the contents of a directory into ENTRIES.  Values are stored
+/* For directory NODEREV in FS, return the *FILESIZE of its in-txn
+ * representation.  If the directory representation is comitted data,
+ * set *FILESIZE to SVN_INVALID_FILESIZE. Use SCRATCH_POOL for temporaries.
+ */
+static svn_error_t *
+get_txn_dir_info(svn_filesize_t *filesize,
+                 svn_fs_t *fs,
+                 node_revision_t *noderev,
+                 apr_pool_t *scratch_pool)
+{
+  if (noderev->data_rep && svn_fs_fs__id_txn_used(&noderev->data_rep->txn_id))
+    {
+      const svn_io_dirent2_t *dirent;
+      const char *filename;
+
+      filename = svn_fs_fs__path_txn_node_children(fs, noderev->id,
+                                                   scratch_pool);
+
+      SVN_ERR(svn_io_stat_dirent2(&dirent, filename, FALSE, FALSE,
+                                  scratch_pool, scratch_pool));
+      *filesize = dirent->filesize;
+    }
+  else
+    {
+      *filesize = SVN_INVALID_FILESIZE;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Fetch the contents of a directory into DIR.  Values are stored
    as filename to string mappings; further conversion is necessary to
    convert them into svn_fs_dirent_t values. */
 static svn_error_t *
-get_dir_contents(apr_array_header_t **entries,
+get_dir_contents(svn_fs_fs__dir_data_t *dir,
                  svn_fs_t *fs,
                  node_revision_t *noderev,
                  apr_pool_t *result_pool,
@@ -2449,18 +2566,31 @@ get_dir_contents(apr_array_header_t **en
 {
   svn_stream_t *contents;
 
-  *entries = apr_array_make(result_pool, 16, sizeof(svn_fs_dirent_t *));
+  /* Initialize the result. */
+  dir->entries = apr_array_make(result_pool, 16, sizeof(svn_fs_dirent_t *));
+  dir->txn_filesize = SVN_INVALID_FILESIZE;
+
+  /* Read dir contents - unless there is none in which case we are done. */
   if (noderev->data_rep && svn_fs_fs__id_txn_used(&noderev->data_rep->txn_id))
     {
-      const char *filename
-        = svn_fs_fs__path_txn_node_children(fs, noderev->id, scratch_pool);
+      /* Get location & current size of the directory representation. */
+      const char *filename;
+      apr_file_t *file;
+
+      filename = svn_fs_fs__path_txn_node_children(fs, noderev->id,
+                                                   scratch_pool);
 
       /* The representation is mutable.  Read the old directory
          contents from the mutable children file, followed by the
          changes we've made in this transaction. */
-      SVN_ERR(svn_stream_open_readonly(&contents, filename, scratch_pool,
-                                       scratch_pool));
-      SVN_ERR(read_dir_entries(*entries, contents, TRUE, noderev->id,
+      SVN_ERR(svn_io_file_open(&file, filename, APR_READ | APR_BUFFERED,
+                               APR_OS_DEFAULT, scratch_pool));
+
+      /* Obtain txn children file size. */
+      SVN_ERR(svn_io_file_size_get(&dir->txn_filesize, file, scratch_pool));
+
+      contents = svn_stream_from_aprfile2(file, FALSE, scratch_pool);
+      SVN_ERR(read_dir_entries(dir->entries, contents, TRUE, noderev->id,
                                result_pool, scratch_pool));
       SVN_ERR(svn_stream_close(contents));
     }
@@ -2469,9 +2599,7 @@ get_dir_contents(apr_array_header_t **en
       /* Undeltify content before parsing it. Otherwise, we could only
        * parse it byte-by-byte.
        */
-      apr_size_t len = noderev->data_rep->expanded_size
-                     ? (apr_size_t)noderev->data_rep->expanded_size
-                     : (apr_size_t)noderev->data_rep->size;
+      apr_size_t len = noderev->data_rep->expanded_size;
       svn_stringbuf_t *text;
 
       /* The representation is immutable.  Read it normally. */
@@ -2482,7 +2610,7 @@ get_dir_contents(apr_array_header_t **en
 
       /* de-serialize hash */
       contents = svn_stream_from_stringbuf(text, scratch_pool);
-      SVN_ERR(read_dir_entries(*entries, contents, FALSE,  noderev->id,
+      SVN_ERR(read_dir_entries(dir->entries, contents, FALSE, noderev->id,
                                result_pool, scratch_pool));
     }
 
@@ -2538,6 +2666,7 @@ svn_fs_fs__rep_contents_dir(apr_array_he
 {
   pair_cache_key_t pair_key = { 0 };
   const void *key;
+  svn_fs_fs__dir_data_t *dir;
 
   /* find the cache we may use */
   svn_cache__t *cache = locate_dir_cache(fs, &key, &pair_key, noderev,
@@ -2546,19 +2675,32 @@ svn_fs_fs__rep_contents_dir(apr_array_he
     {
       svn_boolean_t found;
 
-      SVN_ERR(svn_cache__get((void **)entries_p, &found, cache, key,
+      SVN_ERR(svn_cache__get((void **)&dir, &found, cache, key,
                              result_pool));
       if (found)
-        return SVN_NO_ERROR;
+        {
+          /* Verify that the cached dir info is not stale
+           * (no-op for committed data). */
+          svn_filesize_t filesize;
+          SVN_ERR(get_txn_dir_info(&filesize, fs, noderev, scratch_pool));
+
+          if (filesize == dir->txn_filesize)
+            {
+              /* Still valid. Done. */
+              *entries_p = dir->entries;
+              return SVN_NO_ERROR;
+            }
+        }
     }
 
   /* Read in the directory contents. */
-  SVN_ERR(get_dir_contents(entries_p, fs, noderev, result_pool,
-                           scratch_pool));
+  dir = apr_pcalloc(scratch_pool, sizeof(*dir));
+  SVN_ERR(get_dir_contents(dir, fs, noderev, result_pool, scratch_pool));
+  *entries_p = dir->entries;
 
   /* Update the cache, if we are to use one. */
   if (cache)
-    SVN_ERR(svn_cache__set(cache, key, *entries_p, scratch_pool));
+    SVN_ERR(svn_cache__set(cache, key, dir, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -2590,30 +2732,40 @@ svn_fs_fs__rep_contents_dir_entry(svn_fs
                                          scratch_pool);
   if (cache)
     {
+      extract_dir_entry_baton_t baton;
+
+      svn_filesize_t filesize;
+      SVN_ERR(get_txn_dir_info(&filesize, fs, noderev, scratch_pool));
+
       /* Cache lookup. */
+      baton.txn_filesize = filesize;
+      baton.name = name;
       SVN_ERR(svn_cache__get_partial((void **)dirent,
                                      &found,
                                      cache,
                                      key,
                                      svn_fs_fs__extract_dir_entry,
-                                     (void*)name,
+                                     &baton,
                                      result_pool));
     }
 
   /* fetch data from disk if we did not find it in the cache */
   if (! found)
     {
-      apr_array_header_t *entries;
       svn_fs_dirent_t *entry;
       svn_fs_dirent_t *entry_copy = NULL;
+      svn_fs_fs__dir_data_t dir;
 
-      /* read the dir from the file system. It will probably be put it
-         into the cache for faster lookup in future calls. */
-      SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, noderev,
-                                          scratch_pool, scratch_pool));
+      /* Read in the directory contents. */
+      SVN_ERR(get_dir_contents(&dir, fs, noderev, scratch_pool,
+                               scratch_pool));
+
+      /* Update the cache, if we are to use one. */
+      if (cache)
+        SVN_ERR(svn_cache__set(cache, key, &dir, scratch_pool));
 
       /* find desired entry and return a copy in POOL, if found */
-      entry = svn_fs_fs__find_dir_entry(entries, name, NULL);
+      entry = svn_fs_fs__find_dir_entry(dir.entries, name, NULL);
       if (entry)
         {
           entry_copy = apr_palloc(result_pool, sizeof(*entry_copy));
@@ -2639,16 +2791,27 @@ svn_fs_fs__get_proplist(apr_hash_t **pro
 
   if (noderev->prop_rep && svn_fs_fs__id_txn_used(&noderev->prop_rep->txn_id))
     {
+      svn_error_t *err;
       const char *filename
         = svn_fs_fs__path_txn_node_props(fs, noderev->id, pool);
       proplist = apr_hash_make(pool);
 
       SVN_ERR(svn_stream_open_readonly(&stream, filename, pool, pool));
-      SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, pool));
+      err = svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, pool);
+      if (err)
+        {
+          svn_string_t *id_str = svn_fs_fs__id_unparse(noderev->id, pool);
+
+          err = svn_error_compose_create(err, svn_stream_close(stream));
+          return svn_error_quick_wrapf(err,
+                   _("malformed property list for node-revision '%s' in '%s'"),
+                   id_str->data, filename);
+        }
       SVN_ERR(svn_stream_close(stream));
     }
   else if (noderev->prop_rep)
     {
+      svn_error_t *err;
       fs_fs_data_t *ffd = fs->fsap_data;
       representation_t *rep = noderev->prop_rep;
       pair_cache_key_t key = { 0 };
@@ -2667,7 +2830,16 @@ svn_fs_fs__get_proplist(apr_hash_t **pro
       proplist = apr_hash_make(pool);
       SVN_ERR(svn_fs_fs__get_contents(&stream, fs, noderev->prop_rep, FALSE,
                                       pool));
-      SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, pool));
+      err = svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, pool);
+      if (err)
+        {
+          svn_string_t *id_str = svn_fs_fs__id_unparse(noderev->id, pool);
+
+          err = svn_error_compose_create(err, svn_stream_close(stream));
+          return svn_error_quick_wrapf(err,
+                   _("malformed property list for node-revision '%s'"),
+                   id_str->data);
+        }
       SVN_ERR(svn_stream_close(stream));
 
       if (ffd->properties_cache && SVN_IS_VALID_REVNUM(rep->revision))
@@ -2788,7 +2960,7 @@ init_rep_state(rep_state_t *rs,
   /* this function does not apply to representation containers */
   SVN_ERR_ASSERT(entry->type >= SVN_FS_FS__ITEM_TYPE_FILE_REP
                  && entry->type <= SVN_FS_FS__ITEM_TYPE_DIR_PROPS);
-  
+
   shared_file->rfile = file;
   shared_file->fs = fs;
   shared_file->revision = entry->item.revision;
@@ -2850,7 +3022,7 @@ get_raw_window_end(void **out,
  * at or beyond that offset.  Use POOL for temporary allocations.
  *
  * This function requires RS->RAW_WINDOW_CACHE and RS->WINDOW_CACHE to
- * be non-NULL. 
+ * be non-NULL.
  */
 static svn_error_t *
 cache_windows(svn_fs_t *fs,
@@ -2965,7 +3137,7 @@ block_read_windows(svn_fs_fs__rep_header
 
   SVN_ERR(init_rep_state(&rs, rep_header, fs, rev_file, entry,
                          result_pool));
-  
+
   /* RS->FILE may be shared between RS instances -> make sure we point
    * to the right data. */
   offset = rs.start + rs.current;
@@ -3018,7 +3190,7 @@ read_rep_header(svn_fs_fs__rep_header_t
 {
   fs_fs_data_t *ffd = fs->fsap_data;
   svn_boolean_t is_cached = FALSE;
-  
+
   if (ffd->rep_header_cache)
     {
       SVN_ERR(svn_cache__get((void**)rep_header, &is_cached,
@@ -3200,9 +3372,7 @@ block_read_noderev(node_revision_t **nod
   /* read node rev from revision file */
   SVN_ERR(svn_fs_fs__read_noderev(noderev_p, stream,
                                   result_pool, scratch_pool));
-
-  /* Workaround issue #4031: is-fresh-txn-root in revision files. */
-  (*noderev_p)->is_fresh_txn_root = FALSE;
+  SVN_ERR(fixup_node_revision(fs, *noderev_p, scratch_pool));
 
   if (ffd->node_revision_cache)
     SVN_ERR(svn_cache__set(ffd->node_revision_cache, &key, *noderev_p,
@@ -3246,7 +3416,7 @@ block_read(void **result,
 
   /* don't try this on transaction protorev files */
   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
-  
+
   /* index lookup: find the OFFSET of the item we *must* read plus (in the
    * "do-while" block) the list of items in the same block. */
   SVN_ERR(svn_fs_fs__item_offset(&wanted_offset, fs, revision_file,

Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/cached_data.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/cached_data.h?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/cached_data.h 
(original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/cached_data.h Sun Jun 
14 20:58:10 2015
@@ -30,6 +30,18 @@
 
 
 
+/* Resolve a FSFS quirk: if REP in FS is a "PLAIN" representation, its
+ * EXPANDED_SIZE element may be 0, in which case its value has to be taken
+ * from SIZE.
+ *
+ * This function ensures that EXPANDED_SIZE in REP always contains the
+ * actual value. No-op if REP is NULL.  Uses SCRATCH_POOL for temporaries.
+ */
+svn_error_t *
+svn_fs_fs__fixup_expanded_size(svn_fs_t *fs,
+                               representation_t *rep,
+                               apr_pool_t *scratch_pool);
+
 /* Set *NODEREV_P to the node-revision for the node ID in FS.  Do any
    allocations in POOL. */
 svn_error_t *

Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/dag.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/dag.c?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/dag.c (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/dag.c Sun Jun 14 
20:58:10 2015
@@ -498,6 +498,40 @@ svn_fs_fs__dag_get_proplist(apr_hash_t *
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_fs_fs__dag_has_props(svn_boolean_t *has_props,
+                         dag_node_t *node,
+                         apr_pool_t *scratch_pool)
+{
+  node_revision_t *noderev;
+
+  SVN_ERR(get_node_revision(&noderev, node));
+
+  if (! noderev->prop_rep)
+    {
+      *has_props = FALSE; /* Easy out */
+      return SVN_NO_ERROR;
+    }
+
+  if (svn_fs_fs__id_txn_used(&noderev->prop_rep->txn_id))
+    {
+      /* We are in a commit or something. Check actual properties */
+      apr_hash_t *proplist;
+
+      SVN_ERR(svn_fs_fs__get_proplist(&proplist, node->fs,
+                                      noderev, scratch_pool));
+
+      *has_props = proplist ? (0 < apr_hash_count(proplist)) : FALSE;
+    }
+  else
+    {
+      /* Properties are stored as a standard hash stream,
+         always ending with "END\n" (4 bytes) */
+      *has_props = noderev->prop_rep->expanded_size > 4;
+    }
+
+  return SVN_NO_ERROR;
+}
 
 svn_error_t *
 svn_fs_fs__dag_set_proplist(dag_node_t *node,

Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/dag.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/dag.h?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/dag.h (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/dag.h Sun Jun 14 
20:58:10 2015
@@ -177,6 +177,12 @@ svn_error_t *svn_fs_fs__dag_get_proplist
                                          dag_node_t *node,
                                          apr_pool_t *pool);
 
+/* Set *HAS_PROPS to TRUE if NODE has properties. Use SCRATCH_POOL
+   for temporary allocations */
+svn_error_t *svn_fs_fs__dag_has_props(svn_boolean_t *has_props,
+                                      dag_node_t *node,
+                                      apr_pool_t *scratch_pool);
+
 /* Set the property list of NODE to PROPLIST, allocating from POOL.
    The node being changed must be mutable.
 

Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs.c?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs.c (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs.c Sun Jun 14 
20:58:10 2015
@@ -27,7 +27,6 @@
 #include <apr_general.h>
 #include <apr_pools.h>
 #include <apr_file_io.h>
-#include <apr_thread_mutex.h>
 
 #include "svn_fs.h"
 #include "svn_delta.h"
@@ -163,9 +162,11 @@ fs_freeze_body(void *baton,
 
   SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, b->fs, pool));
   if (exists)
-    SVN_ERR(svn_fs_fs__lock_rep_cache(b->fs, pool));
-
-  SVN_ERR(b->freeze_func(b->freeze_baton, pool));
+    SVN_ERR(svn_fs_fs__with_rep_cache_lock(b->fs,
+                                           b->freeze_func, b->freeze_baton,
+                                           pool));
+  else
+    SVN_ERR(b->freeze_func(b->freeze_baton, pool));
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs.h?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs.h (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs.h Sun Jun 14 
20:58:10 2015
@@ -527,7 +527,14 @@ typedef struct representation_t
   svn_filesize_t size;
 
   /* The size of the fulltext of the representation. If this is 0,
-   * the fulltext size is equal to representation size in the rev file, */
+   * for a plain rep, the real fulltext size is equal to the SIZE field.
+   * For a delta rep, this field is always the real fulltext size.
+   *
+   * Note that svn_fs_fs__fixup_expanded_size() checks for these special
+   * cases and ensures that this field contains the actual value.  We call
+   * it early after reading a representation struct, so most code does not
+   * have to worry about it.
+   */
   svn_filesize_t expanded_size;
 
   /* Is this a representation (still) within a transaction? */
@@ -616,6 +623,18 @@ typedef struct change_t
   svn_fs_path_change2_t info;
 } change_t;
 
+
+/*** Directory (only used at the cache interface) ***/
+typedef struct svn_fs_fs__dir_data_t
+{
+  /* Contents, i.e. all directory entries, sorted by name. */
+  apr_array_header_t *entries;
+
+  /* SVN_INVALID_FILESIZE for committed data, otherwise the length of the
+   * in-txn on-disk representation of that directory. */
+  svn_filesize_t txn_filesize;
+} svn_fs_fs__dir_data_t;
+
 
 #ifdef __cplusplus
 }

Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs_fs.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs_fs.c?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs_fs.c (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs_fs.c Sun Jun 14 
20:58:10 2015
@@ -401,7 +401,7 @@ svn_fs_fs__with_all_locks(svn_fs_t *fs,
   fs_fs_data_t *ffd = fs->fsap_data;
 
   /* Be sure to use the correct lock ordering as documented in
-     fs_fs_shared_data_t.  The lock chain is being created in 
+     fs_fs_shared_data_t.  The lock chain is being created in
      innermost (last to acquire) -> outermost (first to acquire) order. */
   with_lock_baton_t *lock_baton
     = create_lock_baton(fs, write_lock, body, baton, pool);
@@ -1126,7 +1126,9 @@ svn_fs_fs__open(svn_fs_t *fs, const char
   /* Global configuration options. */
   SVN_ERR(read_global_config(fs));
 
-  return get_youngest(&(ffd->youngest_rev_cache), fs, pool);
+  ffd->youngest_rev_cache = 0;
+
+  return SVN_NO_ERROR;
 }
 
 /* Wrapper around svn_io_file_create which ignores EEXIST. */
@@ -1144,7 +1146,7 @@ create_file_ignore_eexist(const char *fi
   return svn_error_trace(err);
 }
 
-/* Baton type bridging svn_fs_fs__upgrade and upgrade_body carrying 
+/* Baton type bridging svn_fs_fs__upgrade and upgrade_body carrying
  * parameters over between them. */
 struct upgrade_baton_t
 {
@@ -1287,7 +1289,7 @@ svn_fs_fs__upgrade(svn_fs_t *fs,
   baton.notify_baton = notify_baton;
   baton.cancel_func = cancel_func;
   baton.cancel_baton = cancel_baton;
-  
+
   return svn_fs_fs__with_all_locks(fs, upgrade_body, (void *)&baton, pool);
 }
 
@@ -1377,38 +1379,9 @@ svn_fs_fs__file_length(svn_filesize_t *l
       /* Treat "no representation" as "empty file". */
       *length = 0;
     }
-  else if (data_rep->expanded_size)
-    {
-      /* Standard case: a non-empty file. */
-      *length = data_rep->expanded_size;
-    }
   else
     {
-      /* Work around a FSFS format quirk (see issue #4554).
-
-         A plain representation may specify its EXPANDED LENGTH as "0"
-         in which case, the SIZE value is what we want.
-
-         Because EXPANDED_LENGTH will also be 0 for empty files, while
-         SIZE is non-null, we need to check wether the content is
-         actually empty.  We simply compare with the MD5 checksum of
-         empty content (sha-1 is not always available).
-       */
-      svn_checksum_t *empty_md5
-        = svn_checksum_empty_checksum(svn_checksum_md5, pool);
-
-      if (memcmp(empty_md5->digest, data_rep->md5_digest,
-                 sizeof(data_rep->md5_digest)))
-        {
-          /* Contents is not empty, i.e. EXPANDED_LENGTH cannot be the
-             actual file length. */
-          *length = data_rep->size;
-        }
-      else
-        {
-          /* Contents is empty. */
-          *length = 0;
-        }
+      *length = data_rep->expanded_size;
     }
 
   return SVN_NO_ERROR;
@@ -1556,7 +1529,7 @@ svn_fs_fs__file_checksum(svn_checksum_t
     {
       svn_checksum_t temp;
       temp.kind = kind;
-      
+
       switch(kind)
         {
           case svn_checksum_md5:
@@ -1944,7 +1917,10 @@ get_node_origins_from_file(svn_fs_t *fs,
 
   stream = svn_stream_from_aprfile2(fd, FALSE, pool);
   *node_origins = apr_hash_make(pool);
-  SVN_ERR(svn_hash_read2(*node_origins, stream, SVN_HASH_TERMINATOR, pool));
+  err = svn_hash_read2(*node_origins, stream, SVN_HASH_TERMINATOR, pool);
+  if (err)
+    return svn_error_quick_wrapf(err, _("malformed node origin data in '%s'"),
+                                 node_origins_file);
   return svn_stream_close(stream);
 }
 

Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/hotcopy.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/hotcopy.c?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/hotcopy.c (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/hotcopy.c Sun Jun 14 
20:58:10 2015
@@ -369,7 +369,7 @@ hotcopy_copy_packed_shard(svn_boolean_t
   return SVN_NO_ERROR;
 }
 
-/* Remove file PATH, if it exists - even if it is read-only. 
+/* Remove file PATH, if it exists - even if it is read-only.
  * Use POOL for temporary allocations. */
 static svn_error_t *
 hotcopy_remove_file(const char *path,
@@ -855,7 +855,6 @@ hotcopy_body(void *baton, apr_pool_t *po
                * ### so we have no way of just printing a warning via
                * ### the fs->warning() callback. */
 
-              const char *msg;
               const char *src_abspath;
               const char *dst_abspath;
               const char *config_relpath;
@@ -874,13 +873,12 @@ hotcopy_body(void *baton, apr_pool_t *po
               src_abspath = svn_dirent_dirname(src_abspath, pool);
               dst_abspath = svn_dirent_dirname(dst_abspath, pool);
 
-              msg = apr_psprintf(pool,
+              return svn_error_quick_wrapf(err,
                                  _("Failed to create hotcopy at '%s'. "
                                    "The file '%s' is missing from the source "
                                    "repository. Please create this file, for "
                                    "instance by running 'svnadmin upgrade 
%s'"),
                                  dst_abspath, config_relpath, src_abspath);
-              return svn_error_quick_wrap(err, msg);
             }
           else
             return svn_error_trace(err);

Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/id.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/id.c?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/id.c (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/id.c Sun Jun 14 
20:58:10 2015
@@ -232,7 +232,7 @@ svn_fs_fs__id_txn_unparse(const svn_fs_f
 {
   char string[2 * SVN_INT64_BUFFER_SIZE + 1];
   char *p = string;
-  
+
   p += svn__i64toa(p, txn_id->revision);
   *(p++) = '-';
   p += svn__ui64tobase36(p, txn_id->number);
@@ -365,14 +365,21 @@ svn_fs_fs__id_check_related(const svn_fs
   if (a == b)
     return TRUE;
 
-  /* If both node_ids start with _ and they have differing transaction
-     IDs, then it is impossible for them to be related. */
-  if (id_a->private_id.node_id.revision == SVN_INVALID_REVNUM)
+  /* If both node_ids have been created within _different_ transactions
+     (and are still uncommitted), then it is impossible for them to be
+     related.
+
+     Due to our txn-local temporary IDs, however, they might have been
+     given the same temporary node ID.  We need to detect that case.
+   */
+  if (   id_a->private_id.node_id.revision == SVN_INVALID_REVNUM
+      && id_b->private_id.node_id.revision == SVN_INVALID_REVNUM)
     {
-      if (   !svn_fs_fs__id_part_eq(&id_a->private_id.txn_id,
-                                    &id_b->private_id.txn_id)
-          || !svn_fs_fs__id_txn_used(&id_a->private_id.txn_id))
+      if (!svn_fs_fs__id_part_eq(&id_a->private_id.txn_id,
+                                 &id_b->private_id.txn_id))
         return FALSE;
+
+      /* At this point, matching node_ids implies relatedness. */
     }
 
   return svn_fs_fs__id_part_eq(&id_a->private_id.node_id,
@@ -385,7 +392,7 @@ svn_fs_fs__id_compare(const svn_fs_id_t
                       const svn_fs_id_t *b)
 {
   if (svn_fs_fs__id_eq(a, b))
-    return svn_fs_node_same;
+    return svn_fs_node_unchanged;
   return (svn_fs_fs__id_check_related(a, b) ? svn_fs_node_common_ancestor
                                             : svn_fs_node_unrelated);
 }
@@ -418,7 +425,7 @@ svn_fs_fs__id_txn_create_root(const svn_
   fs_fs__id_t *id = apr_pcalloc(pool, sizeof(*id));
 
   /* node ID and copy ID are "0" */
-  
+
   id->private_id.txn_id = *txn_id;
   id->private_id.rev_item.revision = SVN_INVALID_REVNUM;
 
@@ -598,7 +605,7 @@ svn_fs_fs__id_serialize(svn_temp_seriali
                         const svn_fs_id_t * const *in)
 {
   const fs_fs__id_t *id = (const fs_fs__id_t *)*in;
-  
+
   /* nothing to do for NULL ids */
   if (id == NULL)
     return;

Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/index.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/index.c?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/index.c (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/index.c Sun Jun 14 
20:58:10 2015
@@ -87,7 +87,7 @@ typedef struct l2p_page_table_entry_t
 
 /* Master run-time data structure of an log-to-phys index.  It contains
  * the page tables of every revision covered by that index - but not the
- * pages themselves. 
+ * pages themselves.
  */
 typedef struct l2p_header_t
 {
@@ -498,7 +498,7 @@ stream_write_encoded(svn_stream_t *strea
                      apr_uint64_t value)
 {
   unsigned char encoded[ENCODED_INT_LENGTH];
- 
+
   apr_size_t len = encode_uint(encoded, value);
   return svn_error_trace(svn_stream_write(stream, (char *)encoded, &len));
 }
@@ -806,7 +806,7 @@ svn_fs_fs__l2p_index_append(svn_checksum
    * 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_INDEX_OVERFLOW , NULL,
-                            _("L2P index page size  %s" 
+                            _("L2P index page size  %s"
                               " exceeds current limit of 2G entries"),
                             apr_psprintf(local_pool, "%" APR_UINT64_T_FMT,
                                          ffd->l2p_page_size));
@@ -869,7 +869,7 @@ svn_fs_fs__l2p_index_append(svn_checksum
             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, "%" 
APR_UINT64_T_FMT, 
+                                    apr_psprintf(local_pool, "%" 
APR_UINT64_T_FMT,
                                                  proto_entry.item_index),
                                     revision + page_counts->nelts);
 
@@ -1277,7 +1277,7 @@ l2p_page_table_access_func(void **out,
 
   /* set output as a courtesy to the caller */
   *out = table_baton->pages;
-  
+
   return SVN_NO_ERROR;
 }
 
@@ -1669,7 +1669,7 @@ l2p_proto_index_lookup(apr_off_t *offset
     }
 
   SVN_ERR(svn_io_file_close(file, scratch_pool));
-  
+
   return SVN_NO_ERROR;
 }
 
@@ -2188,7 +2188,7 @@ get_p2l_header(p2l_header_t **header,
 
   /* allocate result data structure */
   result = apr_pcalloc(result_pool, sizeof(*result));
-  
+
   /* Read table sizes, check them for plausibility and allocate page array. */
   SVN_ERR(packed_stream_get(&value, rev_file->p2l_stream));
   result->first_revision = (svn_revnum_t)value;
@@ -2282,7 +2282,7 @@ p2l_page_info_copy(p2l_page_info_baton_t
                    const p2l_header_t *header,
                    const apr_off_t *offsets)
 {
-  /* if the requested offset is out of bounds, return info for 
+  /* if the requested offset is out of bounds, return info for
    * a zero-sized empty page right behind the last page.
    */
   if (baton->offset / header->page_size < header->page_count)
@@ -2595,7 +2595,7 @@ get_p2l_keys(p2l_page_info_baton_t *page
              apr_pool_t *scratch_pool)
 {
   p2l_page_info_baton_t page_info;
-  
+
   /* request info for the index pages that describes the pack / rev file
    * contents at pack / rev file position OFFSET. */
   page_info.offset = offset;
@@ -2611,7 +2611,7 @@ get_p2l_keys(p2l_page_info_baton_t *page
   /* return results */
   if (page_info_p)
     *page_info_p = page_info;
-  
+
   /* construct cache key */
   if (key_p)
     {
@@ -2621,7 +2621,7 @@ get_p2l_keys(p2l_page_info_baton_t *page
       key.is_packed = rev_file->is_packed;
       key.page = page_info.page_no;
 
-      *key_p = key;  
+      *key_p = key;
     }
 
   return SVN_NO_ERROR;

Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/index.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/index.h?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/index.h (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/index.h Sun Jun 14 
20:58:10 2015
@@ -71,7 +71,7 @@ svn_fs_fs__l2p_proto_index_add_revision(
  * Not all possible index values need to be used.  OFFSET may be -1 to
  * mark 'invalid' item indexes but that is already implied for all item
  * indexes not explicitly given a mapping.
- * 
+ *
  * Use SCRATCH_POOL for temporary allocations.
  */
 svn_error_t *
@@ -83,7 +83,7 @@ svn_fs_fs__l2p_proto_index_add_entry(apr
 /* Use the proto index file stored at PROTO_FILE_NAME, construct the final
  * log-to-phys index and append it to INDEX_FILE.  The first revision will
  * be REVISION, entries to the next revision will be assigned to REVISION+1
- * and so forth.  
+ * and so forth.
  *
  * Return the MD5 checksum of the on-disk index data in *CHECKSUM, allocated
  * in RESULT_POOL.  Use SCRATCH_POOL for temporary allocations.

Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/lock.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/lock.c?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/lock.c (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/lock.c Sun Jun 14 
20:58:10 2015
@@ -222,7 +222,7 @@ write_digest_file(apr_hash_t *children,
                                  svn_io_file_del_none, pool, pool));
   if ((err = svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool)))
     {
-      svn_error_clear(svn_stream_close(stream));
+      err = svn_error_compose_create(err, svn_stream_close(stream));
       return svn_error_createf(err->apr_err,
                                err,
                                _("Cannot write lock/entries hashfile '%s'"),
@@ -273,7 +273,7 @@ read_digest_file(apr_hash_t **children_p
   hash = apr_hash_make(pool);
   if ((err = svn_hash_read2(hash, stream, SVN_HASH_TERMINATOR, pool)))
     {
-      svn_error_clear(svn_stream_close(stream));
+      err = svn_error_compose_create(err, svn_stream_close(stream));
       return svn_error_createf(err->apr_err,
                                err,
                                _("Can't parse lock/entries hashfile '%s'"),
@@ -355,7 +355,7 @@ set_lock(const char *fs_path,
      always come back empty. */
   SVN_ERR(read_digest_file(&children, NULL, fs_path, digest_path, pool));
 
-  SVN_ERR(write_digest_file(children, lock, fs_path, digest_path, 
+  SVN_ERR(write_digest_file(children, lock, fs_path, digest_path,
                             perms_reference, pool));
 
   return SVN_NO_ERROR;
@@ -405,7 +405,7 @@ add_to_digest(const char *fs_path,
     }
 
   if (apr_hash_count(children) != original_count)
-    SVN_ERR(write_digest_file(children, lock, fs_path, index_digest_path, 
+    SVN_ERR(write_digest_file(children, lock, fs_path, index_digest_path,
                               perms_reference, pool));
 
   return SVN_NO_ERROR;
@@ -438,7 +438,7 @@ delete_from_digest(const char *fs_path,
     }
 
   if (apr_hash_count(children) || lock)
-    SVN_ERR(write_digest_file(children, lock, fs_path, index_digest_path, 
+    SVN_ERR(write_digest_file(children, lock, fs_path, index_digest_path,
                               perms_reference, pool));
   else
     SVN_ERR(svn_io_remove_file2(index_digest_path, TRUE, pool));
@@ -715,6 +715,7 @@ check_lock(svn_error_t **fs_err,
            const svn_fs_lock_target_t *target,
            struct lock_baton *lb,
            svn_fs_root_t *root,
+           svn_revnum_t youngest_rev,
            apr_pool_t *pool)
 {
   svn_node_kind_t kind;
@@ -751,6 +752,15 @@ check_lock(svn_error_t **fs_err,
   if (SVN_IS_VALID_REVNUM(target->current_rev))
     {
       svn_revnum_t created_rev;
+
+      if (target->current_rev > youngest_rev)
+        {
+          *fs_err = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
+                                      _("No such revision %ld"),
+                                      target->current_rev);
+          return SVN_NO_ERROR;
+        }
+
       SVN_ERR(svn_fs_fs__node_created_rev(&created_rev, root, path,
                                           pool));
 
@@ -856,7 +866,7 @@ lock_body(void *baton, apr_pool_t *pool)
       info.fs_err = SVN_NO_ERROR;
 
       SVN_ERR(check_lock(&info.fs_err, info.path, item->value, lb, root,
-                         iterpool));
+                         youngest, iterpool));
 
       /* If no error occurred while pre-checking, schedule the index updates 
for
          this path. */
@@ -900,14 +910,17 @@ lock_body(void *baton, apr_pool_t *pool)
         {
           info->lock = svn_lock_create(lb->result_pool);
           if (target->token)
-            info->lock->token = target->token;
+            info->lock->token = apr_pstrdup(lb->result_pool, target->token);
           else
             SVN_ERR(svn_fs_fs__generate_lock_token(&(info->lock->token), 
lb->fs,
                                                    lb->result_pool));
 
+          /* The INFO->PATH is already allocated in LB->RESULT_POOL as a result
+             of svn_fspath__canonicalize() (see svn_fs_fs__lock()). */
           info->lock->path = info->path;
-          info->lock->owner = lb->fs->access_ctx->username;
-          info->lock->comment = lb->comment;
+          info->lock->owner = apr_pstrdup(lb->result_pool,
+                                          lb->fs->access_ctx->username);
+          info->lock->comment = apr_pstrdup(lb->result_pool, lb->comment);
           info->lock->is_dav_comment = lb->is_dav_comment;
           info->lock->creation_date = apr_time_now();
           info->lock->expiration_date = lb->expiration_date;
@@ -1096,6 +1109,7 @@ svn_fs_fs__lock(svn_fs_t *fs,
   apr_array_header_t *sorted_targets;
   apr_hash_t *canonical_targets = apr_hash_make(scratch_pool);
   apr_hash_index_t *hi;
+  apr_pool_t *iterpool;
   svn_error_t *err, *cb_err = SVN_NO_ERROR;
   int i;
 
@@ -1136,23 +1150,26 @@ svn_fs_fs__lock(svn_fs_t *fs,
   lb.steal_lock = steal_lock;
   lb.result_pool = result_pool;
 
-  err = svn_fs_fs__with_write_lock(fs, lock_body, &lb, scratch_pool);
+  iterpool = svn_pool_create(scratch_pool);
+  err = svn_fs_fs__with_write_lock(fs, lock_body, &lb, iterpool);
   for (i = 0; i < lb.infos->nelts; ++i)
     {
       struct lock_info_t *info = &APR_ARRAY_IDX(lb.infos, i,
                                                 struct lock_info_t);
+      svn_pool_clear(iterpool);
       if (!cb_err && lock_callback)
         {
           if (!info->lock && !info->fs_err)
             info->fs_err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED,
                                              0, _("Failed to lock '%s'"),
                                              info->path);
-                                             
+
           cb_err = lock_callback(lock_baton, info->path, info->lock,
-                                 info->fs_err, scratch_pool);
+                                 info->fs_err, iterpool);
         }
       svn_error_clear(info->fs_err);
     }
+  svn_pool_destroy(iterpool);
 
   if (err && cb_err)
     svn_error_compose(err, cb_err);
@@ -1192,6 +1209,7 @@ svn_fs_fs__unlock(svn_fs_t *fs,
   apr_array_header_t *sorted_targets;
   apr_hash_t *canonical_targets = apr_hash_make(scratch_pool);
   apr_hash_index_t *hi;
+  apr_pool_t *iterpool;
   svn_error_t *err, *cb_err = SVN_NO_ERROR;
   int i;
 
@@ -1226,11 +1244,13 @@ svn_fs_fs__unlock(svn_fs_t *fs,
   ub.break_lock = break_lock;
   ub.result_pool = result_pool;
 
-  err = svn_fs_fs__with_write_lock(fs, unlock_body, &ub, scratch_pool);
+  iterpool = svn_pool_create(scratch_pool);
+  err = svn_fs_fs__with_write_lock(fs, unlock_body, &ub, iterpool);
   for (i = 0; i < ub.infos->nelts; ++i)
     {
       struct unlock_info_t *info = &APR_ARRAY_IDX(ub.infos, i,
                                                   struct unlock_info_t);
+      svn_pool_clear(iterpool);
       if (!cb_err && lock_callback)
         {
           if (!info->done && !info->fs_err)
@@ -1238,10 +1258,11 @@ svn_fs_fs__unlock(svn_fs_t *fs,
                                              0, _("Failed to unlock '%s'"),
                                              info->path);
           cb_err = lock_callback(lock_baton, info->path, NULL, info->fs_err,
-                                 scratch_pool);
+                                 iterpool);
         }
       svn_error_clear(info->fs_err);
     }
+  svn_pool_destroy(iterpool);
 
   if (err && cb_err)
     svn_error_compose(err, cb_err);

Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/low_level.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/low_level.c?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/low_level.c (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/low_level.c Sun Jun 14 
20:58:10 2015
@@ -189,6 +189,19 @@ svn_fs_fs__unparse_revision_trailer(apr_
                                changes_offset);
 }
 
+/* If ERR is not NULL, wrap it MESSAGE.  The latter must have an %ld
+ * format parameter that will be filled with REV. */
+static svn_error_t *
+wrap_footer_error(svn_error_t *err,
+                  const char *message,
+                  svn_revnum_t rev)
+{
+  if (err)
+    return svn_error_quick_wrapf(err, message, rev);
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_fs_fs__parse_footer(apr_off_t *l2p_offset,
                         svn_checksum_t **l2p_checksum,
@@ -196,6 +209,7 @@ svn_fs_fs__parse_footer(apr_off_t *l2p_o
                         svn_checksum_t **p2l_checksum,
                         svn_stringbuf_t *footer,
                         svn_revnum_t rev,
+                        apr_off_t footer_offset,
                         apr_pool_t *result_pool)
 {
   apr_int64_t val;
@@ -204,17 +218,20 @@ svn_fs_fs__parse_footer(apr_off_t *l2p_o
   /* Get the L2P offset. */
   const char *str = svn_cstring_tokenize(" ", &last_str);
   if (str == NULL)
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Invalid revision footer"));
+    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                             "Invalid r%ld footer", rev);
 
-  SVN_ERR(svn_cstring_atoi64(&val, str));
+  SVN_ERR(wrap_footer_error(svn_cstring_strtoi64(&val, str, 0,
+                                                 footer_offset - 1, 10),
+                            "Invalid L2P offset in r%ld footer",
+                            rev));
   *l2p_offset = (apr_off_t)val;
 
   /* Get the L2P checksum. */
   str = svn_cstring_tokenize(" ", &last_str);
   if (str == NULL)
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Invalid revision footer"));
+    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                             "Invalid r%ld footer", rev);
 
   SVN_ERR(svn_checksum_parse_hex(l2p_checksum, svn_checksum_md5, str,
                                  result_pool));
@@ -222,17 +239,33 @@ svn_fs_fs__parse_footer(apr_off_t *l2p_o
   /* Get the P2L offset. */
   str = svn_cstring_tokenize(" ", &last_str);
   if (str == NULL)
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Invalid revision footer"));
+    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                             "Invalid r%ld footer", rev);
 
-  SVN_ERR(svn_cstring_atoi64(&val, str));
+  SVN_ERR(wrap_footer_error(svn_cstring_strtoi64(&val, str, 0,
+                                                 footer_offset - 1, 10),
+                            "Invalid P2L offset in r%ld footer",
+                            rev));
   *p2l_offset = (apr_off_t)val;
 
+  /* The P2L indes follows the L2P index */
+  if (*p2l_offset <= *l2p_offset)
+    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                             "P2L offset %s must be larger than L2P offset %s"
+                             " in r%ld footer",
+                             apr_psprintf(result_pool,
+                                          "%" APR_UINT64_T_HEX_FMT,
+                                          (apr_uint64_t)*p2l_offset),
+                             apr_psprintf(result_pool,
+                                          "%" APR_UINT64_T_HEX_FMT,
+                                          (apr_uint64_t)*l2p_offset),
+                             rev);
+
   /* Get the P2L checksum. */
   str = svn_cstring_tokenize(" ", &last_str);
   if (str == NULL)
-    return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
-                            _("Invalid revision footer"));
+    return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                             "Invalid r%ld footer", rev);
 
   SVN_ERR(svn_checksum_parse_hex(p2l_checksum, svn_checksum_md5, str,
                                  result_pool));
@@ -499,7 +532,7 @@ svn_fs_fs__read_changes_incrementally(sv
     }
   while (change);
   svn_pool_destroy(iterpool);
-  
+
   return SVN_NO_ERROR;
 }
 
@@ -725,7 +758,7 @@ svn_fs_fs__parse_representation(represen
 
   /* initialize transaction info (never stored) */
   svn_fs_fs__id_txn_reset(&rep->txn_id);
-  
+
   /* while in transactions, it is legal to simply write "-1" */
   str = svn_cstring_tokenize(" ", &string);
   if (str == NULL)
@@ -998,7 +1031,7 @@ format_digest(const unsigned char *diges
   svn_checksum_t checksum;
   checksum.digest = digest;
   checksum.kind = kind;
-  
+
   if (is_null)
     return "(null)";
 
@@ -1174,7 +1207,7 @@ svn_fs_fs__write_rep_header(svn_fs_fs__r
                             apr_pool_t *scratch_pool)
 {
   const char *text;
-  
+
   switch (header->type)
     {
       case svn_fs_fs__rep_plain:

Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/low_level.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/low_level.h?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/low_level.h (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/low_level.h Sun Jun 14 
20:58:10 2015
@@ -44,7 +44,7 @@
  * in *ROOT_OFFSET and the offset of the changed paths list in
  * *CHANGES_OFFSET.  Offsets are relative to the revision's start offset.
  * ROOT_OFFSET and / or CHANGES_OFFSET may be NULL.
- * 
+ *
  * Note that REV is only used to construct nicer error objects.
  */
 svn_error_t *
@@ -66,7 +66,9 @@ svn_fs_fs__unparse_revision_trailer(apr_
  * and return the start offsets of the index data in *L2P_OFFSET and
  * *P2L_OFFSET, respectively.  Also, return the expected checksums in
  * in *L2P_CHECKSUM and *P2L_CHECKSUM.
- * 
+ *
+ * FOOTER_OFFSET is used for validation.
+ *
  * Note that REV is only used to construct nicer error objects that
  * mention this revision.  Allocate the checksums in RESULT_POOL.
  */
@@ -77,6 +79,7 @@ svn_fs_fs__parse_footer(apr_off_t *l2p_o
                         svn_checksum_t **p2l_checksum,
                         svn_stringbuf_t *footer,
                         svn_revnum_t rev,
+                        apr_off_t footer_offset,
                         apr_pool_t *result_pool);
 
 /* Given the offset of the L2P index data in L2P_OFFSET, the content

Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/pack.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/pack.c?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/pack.c (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/pack.c Sun Jun 14 
20:58:10 2015
@@ -69,7 +69,7 @@
  * each of the 4 buckets separately.  The first three will simply order
  * their items by revision, starting with the newest once.  Placing rep
  * and noderev items is a more elaborate process documented in the code.
- * 
+ *
  * In short, we store items in the following order:
  * - changed paths lists
  * - node property
@@ -256,7 +256,7 @@ initialize_pack_context(pack_context_t *
   fs_fs_data_t *ffd = fs->fsap_data;
   const char *temp_dir;
   int max_revs = MIN(ffd->max_files_per_dir, max_items);
-  
+
   SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT);
   SVN_ERR_ASSERT(shard_rev % ffd->max_files_per_dir == 0);
 
@@ -272,7 +272,7 @@ initialize_pack_context(pack_context_t *
   context->start_rev = shard_rev;
   context->end_rev = shard_rev;
   context->shard_end_rev = shard_rev + ffd->max_files_per_dir;
-  
+
   /* Create the new directory and pack file. */
   context->shard_dir = shard_dir;
   context->pack_file_dir = pack_file_dir;
@@ -349,7 +349,7 @@ reset_pack_context(pack_context_t *conte
   SVN_ERR(svn_io_file_trunc(context->reps_file, 0, pool));
 
   svn_pool_clear(context->info_pool);
-  
+
   return SVN_NO_ERROR;
 }
 
@@ -377,13 +377,15 @@ close_pack_context(pack_context_t *conte
   SVN_ERR(svn_fs_fs__add_index_data(context->fs, context->pack_file,
                                     proto_l2p_index_path,
                                     proto_p2l_index_path,
-                                    context->shard_rev, 
+                                    context->shard_rev,
                                     pool));
 
   /* remove proto index files */
   SVN_ERR(svn_io_remove_file2(proto_l2p_index_path, FALSE, pool));
   SVN_ERR(svn_io_remove_file2(proto_p2l_index_path, FALSE, pool));
 
+  /* Ensure that packed file is written to disk.*/
+  SVN_ERR(svn_io_file_flush_to_disk(context->pack_file, pool));
   SVN_ERR(svn_io_file_close(context->pack_file, pool));
 
   return SVN_NO_ERROR;
@@ -402,7 +404,7 @@ copy_file_data(pack_context_t *context,
   /* most non-representation items will be small.  Minimize the buffer
    * and infrastructure overhead in that case. */
   enum { STACK_BUFFER_SIZE = 1024 };
- 
+
   if (size < STACK_BUFFER_SIZE)
     {
       /* copy small data using a fixed-size buffer on stack */
@@ -481,9 +483,9 @@ copy_item_to_temp(pack_context_t *contex
 
   SVN_ERR(svn_fs_fs__get_file_offset(&new_entry->offset, temp_file, pool));
   APR_ARRAY_PUSH(entries, svn_fs_fs__p2l_entry_t *) = new_entry;
-  
+
   SVN_ERR(copy_file_data(context, temp_file, rev_file, entry->size, pool));
-  
+
   return SVN_NO_ERROR;
 }
 
@@ -572,7 +574,7 @@ copy_rep_to_temp(pack_context_t *context
   /* read & parse the representation header */
   stream = svn_stream_from_aprfile2(rev_file, TRUE, pool);
   SVN_ERR(svn_fs_fs__read_rep_header(&rep_header, stream, pool, pool));
-  svn_stream_close(stream);
+  SVN_ERR(svn_stream_close(stream));
 
   /* if the representation is a delta against some other rep, link the two */
   if (   rep_header->type == svn_fs_fs__rep_delta
@@ -643,17 +645,18 @@ compare_dir_entries_format6(const svn_so
 apr_array_header_t *
 svn_fs_fs__order_dir_entries(svn_fs_t *fs,
                              apr_hash_t *directory,
-                             apr_pool_t *pool)
+                             apr_pool_t *result_pool,
+                             apr_pool_t *scratch_pool)
 {
   apr_array_header_t *ordered
     = svn_sort__hash(directory,
                      svn_fs_fs__use_log_addressing(fs)
                        ? compare_dir_entries_format7
                        : compare_dir_entries_format6,
-                     pool);
+                     scratch_pool);
 
   apr_array_header_t *result
-    = apr_array_make(pool, ordered->nelts, sizeof(svn_fs_dirent_t *));
+    = apr_array_make(result_pool, ordered->nelts, sizeof(svn_fs_dirent_t *));
 
   int i;
   for (i = 0; i < ordered->nelts; ++i)
@@ -714,7 +717,7 @@ copy_node_to_temp(pack_context_t *contex
   /* read & parse noderev */
   stream = svn_stream_from_aprfile2(rev_file, TRUE, pool);
   SVN_ERR(svn_fs_fs__read_noderev(&noderev, stream, pool, pool));
-  svn_stream_close(stream);
+  SVN_ERR(svn_stream_close(stream));
 
   /* create a copy of ENTRY, make it point to the copy destination and
    * store it in CONTEXT */
@@ -736,9 +739,7 @@ copy_node_to_temp(pack_context_t *contex
     {
       path_order->rep_id.revision = noderev->data_rep->revision;
       path_order->rep_id.number = noderev->data_rep->item_index;
-      path_order->expanded_size = noderev->data_rep->expanded_size
-                                ? noderev->data_rep->expanded_size
-                                : noderev->data_rep->size;
+      path_order->expanded_size = noderev->data_rep->expanded_size;
     }
 
   /* Sort path is the key used for ordering noderevs and associated reps.
@@ -1001,7 +1002,7 @@ sort_reps(pack_context_t *context)
   /* We now know the final ordering. */
   for (i = 0; i < count; ++i)
     path_order[i] = temp[i];
-  
+
   svn_pool_destroy(temp_pool);
 }
 
@@ -1290,7 +1291,7 @@ pack_range(pack_context_t *context,
 
       /* store the indirect array index */
       APR_ARRAY_PUSH(context->rev_offsets, int) = context->reps->nelts;
-  
+
       /* read the phys-to-log index file until we covered the whole rev file.
        * That index contains enough info to build both target indexes from it. 
*/
       while (offset < rev_file->l2p_offset)
@@ -1391,7 +1392,7 @@ pack_range(pack_context_t *context,
   SVN_ERR(write_l2p_index(context, revpool));
 
   svn_pool_destroy(revpool);
-  
+
   return SVN_NO_ERROR;
 }
 
@@ -1639,7 +1640,7 @@ svn_fs_fs__get_packed_offset(apr_off_t *
 
 /* Packing logic for physical addresssing mode:
  * Simply concatenate all revision contents.
- * 
+ *
  * Pack the revision shard starting at SHARD_REV containing exactly
  * MAX_FILES_PER_DIR revisions from SHARD_PATH into the PACK_FILE_DIR,
  * using POOL for allocations.  CANCEL_FUNC and CANCEL_BATON are what you
@@ -1655,7 +1656,9 @@ pack_phys_addressed(const char *pack_fil
                     apr_pool_t *pool)
 {
   const char *pack_file_path, *manifest_file_path;
-  svn_stream_t *pack_stream, *manifest_stream;
+  apr_file_t *pack_file;
+  apr_file_t *manifest_file;
+  svn_stream_t *manifest_stream;
   svn_revnum_t end_rev, rev;
   apr_off_t next_offset;
   apr_pool_t *iterpool;
@@ -1664,13 +1667,18 @@ pack_phys_addressed(const char *pack_fil
   pack_file_path = svn_dirent_join(pack_file_dir, PATH_PACKED, pool);
   manifest_file_path = svn_dirent_join(pack_file_dir, PATH_MANIFEST, pool);
 
-  /* Create the new directory and pack file. */
-  SVN_ERR(svn_stream_open_writable(&pack_stream, pack_file_path, pool,
-                                    pool));
+  /* Create the new directory and pack file.
+   * Use unbuffered apr_file_t since we're going to write using 16kb
+   * chunks. */
+  SVN_ERR(svn_io_file_open(&pack_file, pack_file_path,
+                           APR_WRITE | APR_CREATE | APR_EXCL,
+                           APR_OS_DEFAULT, pool));
 
   /* Create the manifest file. */
-  SVN_ERR(svn_stream_open_writable(&manifest_stream, manifest_file_path,
-                                   pool, pool));
+  SVN_ERR(svn_io_file_open(&manifest_file, manifest_file_path,
+                           APR_WRITE | APR_BUFFERED | APR_CREATE | APR_EXCL,
+                           APR_OS_DEFAULT, pool));
+  manifest_stream = svn_stream_from_aprfile2(manifest_file, TRUE, pool);
 
   end_rev = start_rev + max_files_per_dir - 1;
   next_offset = 0;
@@ -1697,16 +1705,24 @@ pack_phys_addressed(const char *pack_fil
 
       /* Copy all the bits from the rev file to the end of the pack file. */
       SVN_ERR(svn_stream_open_readonly(&rev_stream, path, iterpool, iterpool));
-      SVN_ERR(svn_stream_copy3(rev_stream, svn_stream_disown(pack_stream,
-                                                             iterpool),
+      SVN_ERR(svn_stream_copy3(rev_stream,
+                               svn_stream_from_aprfile2(pack_file, TRUE, pool),
                                cancel_func, cancel_baton, iterpool));
     }
 
-  /* disallow write access to the manifest file */
+  /* Close stream over APR file. */
   SVN_ERR(svn_stream_close(manifest_stream));
+
+  /* Ensure that pack file is written to disk. */
+  SVN_ERR(svn_io_file_flush_to_disk(manifest_file, pool));
+  SVN_ERR(svn_io_file_close(manifest_file, pool));
+
+  /* disallow write access to the manifest file */
   SVN_ERR(svn_io_set_file_read_only(manifest_file_path, FALSE, iterpool));
 
-  SVN_ERR(svn_stream_close(pack_stream));
+  /* Ensure that pack file is written to disk. */
+  SVN_ERR(svn_io_file_flush_to_disk(pack_file, pool));
+  SVN_ERR(svn_io_file_close(pack_file, pool));
 
   svn_pool_destroy(iterpool);
 
@@ -1756,7 +1772,7 @@ pack_rev_shard(svn_fs_t *fs,
     SVN_ERR(pack_phys_addressed(pack_file_dir, shard_path, shard_rev,
                                 max_files_per_dir, cancel_func,
                                 cancel_baton, pool));
-  
+
   SVN_ERR(svn_io_copy_perms(shard_path, pack_file_dir, pool));
   SVN_ERR(svn_io_set_file_read_only(pack_file_path, FALSE, pool));
 

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

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

Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rep-cache-db.sql
URL: 
http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rep-cache-db.sql?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rep-cache-db.sql 
(original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rep-cache-db.sql Sun 
Jun 14 20:58:10 2015
@@ -63,3 +63,6 @@ WHERE revision > ?1
 -- STMT_LOCK_REP
 BEGIN TRANSACTION;
 INSERT INTO rep_cache VALUES ('dummy', 0, 0, 0, 0)
+
+-- STMT_UNLOCK_REP
+ROLLBACK TRANSACTION;

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

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

Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rev_file.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rev_file.c?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rev_file.c (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rev_file.c Sun Jun 14 
20:58:10 2015
@@ -146,7 +146,7 @@ open_pack_or_rev_file(svn_fs_fs__revisio
 
       /* We may have to *temporarily* enable write access. */
       err = writable ? auto_make_writable(path, result_pool, scratch_pool)
-                     : SVN_NO_ERROR; 
+                     : SVN_NO_ERROR;
 
       /* open the revision file in buffered r/o or r/w mode */
       if (!err)
@@ -259,6 +259,7 @@ svn_fs_fs__auto_read_footer(svn_fs_fs__r
       SVN_ERR(svn_fs_fs__parse_footer(&file->l2p_offset, &file->l2p_checksum,
                                       &file->p2l_offset, &file->p2l_checksum,
                                       footer, file->start_revision,
+                                      filesize - footer_length - 1,
                                       file->pool));
       file->footer_offset = filesize - footer_length - 1;
     }


Reply via email to