Backport branch for issue #2608:
  * r21406-21407, r21540
    Fix issue #2608: Can set directory props on the root of an FSFS
    filesystem from an out-of-date repository.
    Justification:
      Minor data loss bug.  Has been in trunk for a year (so probably
      safe).  Now reported by a user
      (http://svn.haxx.se/dev/archive-2007-09/0441.shtml).  Has unit
      and end-to-end tests.
    Votes:
      +1: glasser, dlr

This produced by:

$ svnmerge merge -r21406-21407,21540

and then:

* subversion/tests/cmdline/prop_tests.py
  (is_this_fsfs): Remove helper function added in r21407, moved
   elsewhere in a later commit, and made irrelevant by r21540.
  (test_list): Resolve trivial conflict.


git-svn-id: https://svn.apache.org/repos/asf/subversion/branches/1.4.x-issue-2608@866974 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/subversion/libsvn_fs_fs/dag.c b/subversion/libsvn_fs_fs/dag.c
index 80206f9..e37f3e5 100644
--- a/subversion/libsvn_fs_fs/dag.c
+++ b/subversion/libsvn_fs_fs/dag.c
@@ -53,6 +53,12 @@
   /* The node revision ID for this dag node, allocated in POOL.  */
   svn_fs_id_t *id;
 
+  /* In the special case that this node is the root of a transaction
+     that has not yet been modified, the node revision ID for this dag
+     node's predecessor; otherwise NULL. (Used in
+     svn_fs_node_created_rev.) */
+  const svn_fs_id_t *fresh_root_predecessor_id;
+
   /* The node's type (file, dir, etc.) */
   svn_node_kind_t kind;
 
@@ -185,6 +191,11 @@
   new_node->kind = noderev->kind;
   new_node->created_path = apr_pstrdup(pool, noderev->created_path);
 
+  if (noderev->is_fresh_txn_root)
+    new_node->fresh_root_predecessor_id = noderev->predecessor_id;
+  else
+    new_node->fresh_root_predecessor_id = NULL;
+
   /* Return a fresh new node */
   *node = new_node;
   return SVN_NO_ERROR;
@@ -196,8 +207,14 @@
                             dag_node_t *node,
                             apr_pool_t *pool)
 {
+  /* In the special case that this is an unmodified transaction root,
+     we need to actually get the revision of the noderev's predecessor
+     (the revision root); see Issue #2608. */
+  const svn_fs_id_t *correct_id = node->fresh_root_predecessor_id
+    ? node->fresh_root_predecessor_id : node->id;
+
   /* Look up the committed revision from the Node-ID. */
-  *rev = svn_fs_fs__id_rev(node->id);
+  *rev = svn_fs_fs__id_rev(correct_id);
 
   return SVN_NO_ERROR;
 }
diff --git a/subversion/libsvn_fs_fs/fs.h b/subversion/libsvn_fs_fs/fs.h
index 7c83e28..3daa47f 100644
--- a/subversion/libsvn_fs_fs/fs.h
+++ b/subversion/libsvn_fs_fs/fs.h
@@ -236,6 +236,9 @@
   /* path at which this node first came into existence.  */
   const char *created_path;
 
+  /* is this the unmodified root of a transaction? */
+  svn_boolean_t is_fresh_txn_root;
+
 } node_revision_t;
 
 
diff --git a/subversion/libsvn_fs_fs/fs_fs.c b/subversion/libsvn_fs_fs/fs_fs.c
index 45af607..811aa0b 100644
--- a/subversion/libsvn_fs_fs/fs_fs.c
+++ b/subversion/libsvn_fs_fs/fs_fs.c
@@ -83,6 +83,7 @@
 #define HEADER_PRED        "pred"
 #define HEADER_COPYFROM    "copyfrom"
 #define HEADER_COPYROOT    "copyroot"
+#define HEADER_FRESHTXNRT  "is-fresh-txn-root"
 
 /* Kinds that a change can be. */
 #define ACTION_MODIFY      "modify"
@@ -1134,6 +1135,10 @@
       noderev->copyfrom_path = apr_pstrdup(pool, last_str);
     }
 
+  /* Get whether this is a fresh txn root. */
+  value = apr_hash_get(headers, HEADER_FRESHTXNRT, APR_HASH_KEY_STRING);
+  noderev->is_fresh_txn_root = (value != NULL);
+
   *noderev_p = noderev;
   
   return SVN_NO_ERROR;
@@ -1213,6 +1218,9 @@
                               noderev->copyroot_rev,
                               noderev->copyroot_path));
 
+  if (noderev->is_fresh_txn_root)
+    SVN_ERR(svn_stream_printf(outfile, pool, HEADER_FRESHTXNRT ": y\n"));
+
   SVN_ERR(svn_stream_printf(outfile, pool, "\n"));
 
   return SVN_NO_ERROR;
@@ -1222,11 +1230,14 @@
 svn_fs_fs__put_node_revision(svn_fs_t *fs,
                              const svn_fs_id_t *id,
                              node_revision_t *noderev,
+                             svn_boolean_t fresh_txn_root,
                              apr_pool_t *pool)
 {
   apr_file_t *noderev_file;
   const char *txn_id = svn_fs_fs__id_txn_id(id);
 
+  noderev->is_fresh_txn_root = fresh_txn_root;
+
   if (! txn_id)
     return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
                             _("Attempted to write to non-transaction"));
@@ -2768,7 +2779,8 @@
 }
 
 /* Copy a revision node-rev SRC into the current transaction TXN_ID in
-   the filesystem FS.  Allocations are from POOL.  */
+   the filesystem FS.  This is only used to create the root of a transaction.
+   Allocations are from POOL.  */
 static svn_error_t *
 create_new_txn_noderev_from_rev(svn_fs_t *fs,
                                 const char *txn_id,
@@ -2795,7 +2807,7 @@
   copy_id = svn_fs_fs__id_copy_id(noderev->id);
   noderev->id = svn_fs_fs__id_txn_create(node_id, copy_id, txn_id, pool);
 
-  SVN_ERR(svn_fs_fs__put_node_revision(fs, noderev->id, noderev, pool));
+  SVN_ERR(svn_fs_fs__put_node_revision(fs, noderev->id, noderev, TRUE, pool));
 
   return SVN_NO_ERROR;
 }
@@ -3081,7 +3093,7 @@
 
   noderev->id = id;
 
-  SVN_ERR(svn_fs_fs__put_node_revision(fs, noderev->id, noderev, pool));
+  SVN_ERR(svn_fs_fs__put_node_revision(fs, noderev->id, noderev, FALSE, pool));
 
   *id_p = id;
 
@@ -3200,7 +3212,7 @@
       rep->txn_id = txn_id;
       parent_noderev->data_rep = rep;
       SVN_ERR(svn_fs_fs__put_node_revision(fs, parent_noderev->id,
-                                           parent_noderev, pool));
+                                           parent_noderev, FALSE, pool));
     }
   else
     {
@@ -3564,7 +3576,7 @@
   b->noderev->data_rep = rep;
 
   /* Write out the new node-rev information. */
-  SVN_ERR(svn_fs_fs__put_node_revision(b->fs, b->noderev->id, b->noderev,
+  SVN_ERR(svn_fs_fs__put_node_revision(b->fs, b->noderev->id, b->noderev, FALSE,
                                        b->pool));
 
   SVN_ERR(svn_io_file_close(b->file, b->pool));
@@ -3638,7 +3650,7 @@
       new_noderev->copyroot_rev = svn_fs_fs__id_rev(new_noderev->id);
     }
 
-  SVN_ERR(svn_fs_fs__put_node_revision(fs, new_noderev->id, new_noderev,
+  SVN_ERR(svn_fs_fs__put_node_revision(fs, new_noderev->id, new_noderev, FALSE,
                                        pool));
 
   *new_id_p = id;
@@ -3669,7 +3681,7 @@
     {
       noderev->prop_rep = apr_pcalloc(pool, sizeof(*noderev->prop_rep));
       noderev->prop_rep->txn_id = svn_fs_fs__id_txn_id(noderev->id);
-      SVN_ERR(svn_fs_fs__put_node_revision(fs, noderev->id, noderev, pool));
+      SVN_ERR(svn_fs_fs__put_node_revision(fs, noderev->id, noderev, FALSE, pool));
     }
   
   return SVN_NO_ERROR;
@@ -3908,7 +3920,7 @@
   /* Write out our new node-revision. */
   SVN_ERR(write_noderev_txn(file, noderev, pool));
 
-  SVN_ERR(svn_fs_fs__put_node_revision(fs, id, noderev, pool));
+  SVN_ERR(svn_fs_fs__put_node_revision(fs, id, noderev, FALSE, pool));
 
   /* Return our ID that references the revision file. */
   *new_id_p = noderev->id;
diff --git a/subversion/libsvn_fs_fs/fs_fs.h b/subversion/libsvn_fs_fs/fs_fs.h
index 186c112..21d509b 100644
--- a/subversion/libsvn_fs_fs/fs_fs.h
+++ b/subversion/libsvn_fs_fs/fs_fs.h
@@ -40,10 +40,12 @@
                                           apr_pool_t *pool);
 
 /* Store NODEREV as the node-revision for the node whose id is ID in
-   FS.  Do any necessary temporary allocation in POOL. */
+   FS, after setting its is_fresh_txn_root to FRESH_TXN_ROOT.  Do any
+   necessary temporary allocation in POOL. */
 svn_error_t *svn_fs_fs__put_node_revision(svn_fs_t *fs,
                                           const svn_fs_id_t *id,
                                           node_revision_t *noderev,
+                                          svn_boolean_t fresh_txn_root,
                                           apr_pool_t *pool);
 
 /* Set *YOUNGEST to the youngest revision in filesystem FS.  Do any
diff --git a/subversion/libsvn_fs_fs/tree.c b/subversion/libsvn_fs_fs/tree.c
index 5a42b43..66e8e49 100644
--- a/subversion/libsvn_fs_fs/tree.c
+++ b/subversion/libsvn_fs_fs/tree.c
@@ -1195,7 +1195,7 @@
   noderev->predecessor_count = source_pred_count;
   if (noderev->predecessor_count != -1)
     noderev->predecessor_count++;
-  SVN_ERR(svn_fs_fs__put_node_revision(fs, target_id, noderev, pool));
+  SVN_ERR(svn_fs_fs__put_node_revision(fs, target_id, noderev, FALSE, pool));
 
   return SVN_NO_ERROR;
 }
diff --git a/subversion/tests/cmdline/prop_tests.py b/subversion/tests/cmdline/prop_tests.py
index 5994b7c..ebe0d23 100755
--- a/subversion/tests/cmdline/prop_tests.py
+++ b/subversion/tests/cmdline/prop_tests.py
@@ -355,6 +355,33 @@
   svntest.actions.run_and_verify_status(wc_dir, expected_status)
 
 #----------------------------------------------------------------------
+def commit_conflict_dirprops(sbox):
+  "commit with conflicting dirprops"
+  
+  # Issue #2608: failure to see conflicting dirprops on root of
+  # repository.
+
+  # Bootstrap
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  svntest.main.run_svn(None, 'propset', 'foo', 'bar', wc_dir)
+
+  # Commit the file and directory
+  svntest.main.run_svn(None, 'ci', '-m', 'r2', wc_dir)
+
+  # Update to rev 1
+  svntest.main.run_svn(None, 'up', '-r', '1', wc_dir)
+
+  # Add conflicting properties
+  svntest.main.run_svn(None, 'propset', 'foo', 'eek', wc_dir)
+
+  svntest.actions.run_and_verify_commit(wc_dir, None, None,
+                                        "Out of date: '' in transaction",
+                                        None, None, None, None,
+                                        wc_dir)
+
+#----------------------------------------------------------------------
 
 # Issue #742: we used to screw up when committing a file replacement
 # that also had properties.  It was fixed by teaching
@@ -1138,6 +1165,7 @@
               downdate_props,
               remove_props,
               update_conflict_props,
+              commit_conflict_dirprops,
               commit_replacement_props,
               revert_replacement_props,
               inappropriate_props,
diff --git a/subversion/tests/libsvn_repos/repos-test.c b/subversion/tests/libsvn_repos/repos-test.c
index dbc2f37..3bce7b6 100644
--- a/subversion/tests/libsvn_repos/repos-test.c
+++ b/subversion/tests/libsvn_repos/repos-test.c
@@ -705,6 +705,58 @@
   return SVN_NO_ERROR;
 }
 
+/* See Issue #2608. */
+
+static svn_error_t *
+created_rev_root(const char **msg,
+                 svn_boolean_t msg_only,
+                 svn_test_opts_t *opts,
+                 apr_pool_t *pool)
+{ 
+  apr_pool_t *spool = svn_pool_create(pool);
+  svn_repos_t *repos;
+  svn_fs_t *fs;
+  svn_fs_txn_t *txn;
+  svn_fs_root_t *txn_root;
+  svn_revnum_t youngest_rev = 0, rev;
+  
+  *msg = "test svn_fs_node_created_rev on root";
+
+  if (msg_only)
+    return SVN_NO_ERROR;
+
+  /* Create a filesystem and repository. */
+  SVN_ERR(svn_test__create_repos(&repos, "test-repo-created-rev-root", 
+                                 opts->fs_type, pool));
+  fs = svn_repos_fs(repos);
+
+  /* Created the greek tree in revision 1. */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, spool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
+  SVN_ERR(svn_test__create_greek_tree(txn_root, spool));
+  SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, spool));
+  svn_pool_clear(spool);
+
+  SVN_ERR(svn_repos_fs_begin_txn_for_commit(&txn,
+                                            repos,
+                                            1,
+                                            "someuser", "log",
+                                            spool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool));
+  SVN_ERR(svn_fs_node_created_rev(&rev, txn_root, "", spool));
+  svn_pool_clear(spool);
+
+  if (rev == SVN_INVALID_REVNUM)
+    return svn_error_createf
+      (SVN_ERR_FS_GENERAL, NULL, "Root has invalid created revnum");
+
+  /* Destroy the subpool. */
+  svn_pool_destroy(spool);
+
+  return SVN_NO_ERROR;
+}
+
+
 
 
 struct locations_info
@@ -1759,6 +1811,7 @@
     SVN_TEST_PASS(dir_deltas),
     SVN_TEST_PASS(node_tree_delete_under_copy),
     SVN_TEST_PASS(revisions_changed),
+    SVN_TEST_PASS(created_rev_root),
     SVN_TEST_PASS(node_locations),
     SVN_TEST_PASS(node_locations2),
     SVN_TEST_PASS(rmlocks),