]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_fs_x/dag.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_fs_x / dag.c
1 /* dag.c : DAG-like interface filesystem, private to libsvn_fs
2  *
3  * ====================================================================
4  *    Licensed to the Apache Software Foundation (ASF) under one
5  *    or more contributor license agreements.  See the NOTICE file
6  *    distributed with this work for additional information
7  *    regarding copyright ownership.  The ASF licenses this file
8  *    to you under the Apache License, Version 2.0 (the
9  *    "License"); you may not use this file except in compliance
10  *    with the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  *    Unless required by applicable law or agreed to in writing,
15  *    software distributed under the License is distributed on an
16  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  *    KIND, either express or implied.  See the License for the
18  *    specific language governing permissions and limitations
19  *    under the License.
20  * ====================================================================
21  */
22
23 #include <string.h>
24
25 #include "svn_path.h"
26 #include "svn_error.h"
27 #include "svn_fs.h"
28 #include "svn_props.h"
29 #include "svn_pools.h"
30
31 #include "dag.h"
32 #include "fs.h"
33 #include "fs_x.h"
34 #include "fs_id.h"
35 #include "cached_data.h"
36 #include "transaction.h"
37
38 #include "../libsvn_fs/fs-loader.h"
39
40 #include "private/svn_fspath.h"
41 #include "svn_private_config.h"
42 #include "private/svn_temp_serializer.h"
43 #include "temp_serializer.h"
44
45 \f
46 /* Initializing a filesystem.  */
47
48 struct dag_node_t
49 {
50   /* The filesystem this dag node came from. */
51   svn_fs_t *fs;
52
53   /* The node revision ID for this dag node.  */
54   svn_fs_x__id_t id;
55
56   /* In the special case that this node is the root of a transaction
57      that has not yet been modified, the revision of this node is the
58      respective txn's base rev.  Otherwise, this is SVN_INVALID_REVNUM
59      for txn nodes and the respective crev for committed nodes.
60      (Used in svn_fs_node_created_rev.) */
61   svn_revnum_t revision;
62
63   /* The node's type (file, dir, etc.) */
64   svn_node_kind_t kind;
65
66   /* The node's NODE-REVISION, or NULL if we haven't read it in yet.
67      This is allocated in this node's POOL.
68
69      If you're willing to respect all the rules above, you can munge
70      this yourself, but you're probably better off just calling
71      `get_node_revision' and `set_node_revision', which take care of
72      things for you.  */
73   svn_fs_x__noderev_t *node_revision;
74
75   /* The pool to allocate NODE_REVISION in. */
76   apr_pool_t *node_pool;
77
78   /* the path at which this node was created. */
79   const char *created_path;
80
81   /* Directory entry lookup hint to speed up consecutive calls to
82      svn_fs_x__rep_contents_dir_entry(). Only used for directory nodes.
83      Any value is legal but should default to APR_SIZE_MAX. */
84   apr_size_t hint;
85 };
86
87
88 \f
89 /* Trivial helper/accessor functions. */
90 svn_node_kind_t
91 svn_fs_x__dag_node_kind(dag_node_t *node)
92 {
93   return node->kind;
94 }
95
96 const svn_fs_x__id_t *
97 svn_fs_x__dag_get_id(const dag_node_t *node)
98 {
99   return &node->id;
100 }
101
102
103 const char *
104 svn_fs_x__dag_get_created_path(dag_node_t *node)
105 {
106   return node->created_path;
107 }
108
109
110 svn_fs_t *
111 svn_fs_x__dag_get_fs(dag_node_t *node)
112 {
113   return node->fs;
114 }
115
116 void
117 svn_fs_x__dag_set_fs(dag_node_t *node,
118                      svn_fs_t *fs)
119 {
120   node->fs = fs;
121 }
122
123
124 /* Dup NODEREV and all associated data into RESULT_POOL.
125    Leaves the id and is_fresh_txn_root fields as zero bytes. */
126 static svn_fs_x__noderev_t *
127 copy_node_revision(svn_fs_x__noderev_t *noderev,
128                    apr_pool_t *result_pool)
129 {
130   svn_fs_x__noderev_t *nr = apr_pmemdup(result_pool, noderev,
131                                         sizeof(*noderev));
132
133   if (noderev->copyfrom_path)
134     nr->copyfrom_path = apr_pstrdup(result_pool, noderev->copyfrom_path);
135
136   nr->copyroot_path = apr_pstrdup(result_pool, noderev->copyroot_path);
137   nr->data_rep = svn_fs_x__rep_copy(noderev->data_rep, result_pool);
138   nr->prop_rep = svn_fs_x__rep_copy(noderev->prop_rep, result_pool);
139
140   if (noderev->created_path)
141     nr->created_path = apr_pstrdup(result_pool, noderev->created_path);
142
143   return nr;
144 }
145
146
147 /* Set *NODEREV_P to the cached node-revision for NODE.
148    If the node-revision was not already cached in NODE, read it in,
149    allocating the cache in NODE->NODE_POOL.
150
151    If you plan to change the contents of NODE, be careful!  We're
152    handing you a pointer directly to our cached node-revision, not
153    your own copy.  If you change it as part of some operation, but
154    then some Berkeley DB function deadlocks or gets an error, you'll
155    need to back out your changes, or else the cache will reflect
156    changes that never got committed.  It's probably best not to change
157    the structure at all.  */
158 static svn_error_t *
159 get_node_revision(svn_fs_x__noderev_t **noderev_p,
160                   dag_node_t *node)
161 {
162   /* If we've already got a copy, there's no need to read it in.  */
163   if (! node->node_revision)
164     {
165       svn_fs_x__noderev_t *noderev;
166       apr_pool_t *scratch_pool = svn_pool_create(node->node_pool);
167
168       SVN_ERR(svn_fs_x__get_node_revision(&noderev, node->fs, &node->id,
169                                           node->node_pool, scratch_pool));
170       node->node_revision = noderev;
171       svn_pool_destroy(scratch_pool);
172     }
173
174   /* Now NODE->node_revision is set.  */
175   *noderev_p = node->node_revision;
176   return SVN_NO_ERROR;
177 }
178
179 /* Return the node revision ID of NODE.  The value returned is shared
180    with NODE, and will be deallocated when NODE is.  */
181 svn_error_t *
182 svn_fs_x__dag_get_node_id(svn_fs_x__id_t *node_id,
183                           dag_node_t *node)
184 {
185   svn_fs_x__noderev_t *noderev;
186   SVN_ERR(get_node_revision(&noderev, node));
187
188   *node_id = noderev->node_id;
189   return SVN_NO_ERROR;
190 }
191
192 /* Return the node revision ID of NODE.  The value returned is shared
193    with NODE, and will be deallocated when NODE is.  */
194 svn_error_t *
195 svn_fs_x__dag_get_copy_id(svn_fs_x__id_t *copy_id,
196                           dag_node_t *node)
197 {
198   svn_fs_x__noderev_t *noderev;
199   SVN_ERR(get_node_revision(&noderev, node));
200
201   *copy_id = noderev->copy_id;
202   return SVN_NO_ERROR;
203 }
204
205 /* Return the node ID of NODE.  The value returned is shared with NODE,
206    and will be deallocated when NODE is.  */
207 svn_error_t *
208 svn_fs_x__dag_related_node(svn_boolean_t *same,
209                            dag_node_t *lhs,
210                            dag_node_t *rhs)
211 {
212   svn_fs_x__id_t lhs_node, rhs_node;
213
214   SVN_ERR(svn_fs_x__dag_get_node_id(&lhs_node, lhs));
215   SVN_ERR(svn_fs_x__dag_get_node_id(&rhs_node, rhs));
216   *same = svn_fs_x__id_eq(&lhs_node, &rhs_node);
217
218   return SVN_NO_ERROR;
219 }
220
221 svn_error_t *
222 svn_fs_x__dag_same_line_of_history(svn_boolean_t *same,
223                                    dag_node_t *lhs,
224                                    dag_node_t *rhs)
225 {
226   svn_fs_x__noderev_t *lhs_noderev, *rhs_noderev;
227
228   SVN_ERR(get_node_revision(&lhs_noderev, lhs));
229   SVN_ERR(get_node_revision(&rhs_noderev, rhs));
230
231   *same = svn_fs_x__id_eq(&lhs_noderev->node_id, &rhs_noderev->node_id)
232        && svn_fs_x__id_eq(&lhs_noderev->copy_id, &rhs_noderev->copy_id);
233
234   return SVN_NO_ERROR;
235 }
236
237 svn_boolean_t
238 svn_fs_x__dag_check_mutable(const dag_node_t *node)
239 {
240   return svn_fs_x__is_txn(svn_fs_x__dag_get_id(node)->change_set);
241 }
242
243
244 svn_error_t *
245 svn_fs_x__dag_get_node(dag_node_t **node,
246                        svn_fs_t *fs,
247                        const svn_fs_x__id_t *id,
248                        apr_pool_t *result_pool,
249                        apr_pool_t *scratch_pool)
250 {
251   dag_node_t *new_node;
252   svn_fs_x__noderev_t *noderev;
253
254   /* Construct the node. */
255   new_node = apr_pcalloc(result_pool, sizeof(*new_node));
256   new_node->fs = fs;
257   new_node->id = *id;
258   new_node->hint = APR_SIZE_MAX;
259
260   /* Grab the contents so we can inspect the node's kind and created path. */
261   SVN_ERR(svn_fs_x__get_node_revision(&noderev, fs, id,
262                                       result_pool, scratch_pool));
263   new_node->node_pool = result_pool;
264   new_node->node_revision = noderev;
265
266   /* Initialize the KIND and CREATED_PATH attributes */
267   new_node->kind = noderev->kind;
268   new_node->created_path = noderev->created_path;
269
270   /* Support our quirky svn_fs_node_created_rev API.
271      Untouched txn roots report the base rev as theirs. */
272   new_node->revision
273     = (  svn_fs_x__is_fresh_txn_root(noderev)
274        ? svn_fs_x__get_revnum(noderev->predecessor_id.change_set)
275        : svn_fs_x__get_revnum(id->change_set));
276
277   /* Return a fresh new node */
278   *node = new_node;
279   return SVN_NO_ERROR;
280 }
281
282
283 svn_revnum_t
284 svn_fs_x__dag_get_revision(const dag_node_t *node)
285 {
286   return node->revision;
287 }
288
289
290 svn_error_t *
291 svn_fs_x__dag_get_predecessor_id(svn_fs_x__id_t *id_p,
292                                  dag_node_t *node)
293 {
294   svn_fs_x__noderev_t *noderev;
295
296   SVN_ERR(get_node_revision(&noderev, node));
297   *id_p = noderev->predecessor_id;
298
299   return SVN_NO_ERROR;
300 }
301
302
303 svn_error_t *
304 svn_fs_x__dag_get_predecessor_count(int *count,
305                                     dag_node_t *node)
306 {
307   svn_fs_x__noderev_t *noderev;
308
309   SVN_ERR(get_node_revision(&noderev, node));
310   *count = noderev->predecessor_count;
311   return SVN_NO_ERROR;
312 }
313
314 svn_error_t *
315 svn_fs_x__dag_get_mergeinfo_count(apr_int64_t *count,
316                                   dag_node_t *node)
317 {
318   svn_fs_x__noderev_t *noderev;
319
320   SVN_ERR(get_node_revision(&noderev, node));
321   *count = noderev->mergeinfo_count;
322   return SVN_NO_ERROR;
323 }
324
325 svn_error_t *
326 svn_fs_x__dag_has_mergeinfo(svn_boolean_t *has_mergeinfo,
327                             dag_node_t *node)
328 {
329   svn_fs_x__noderev_t *noderev;
330
331   SVN_ERR(get_node_revision(&noderev, node));
332   *has_mergeinfo = noderev->has_mergeinfo;
333   return SVN_NO_ERROR;
334 }
335
336 svn_error_t *
337 svn_fs_x__dag_has_descendants_with_mergeinfo(svn_boolean_t *do_they,
338                                              dag_node_t *node)
339 {
340   svn_fs_x__noderev_t *noderev;
341
342   if (node->kind != svn_node_dir)
343     {
344       *do_they = FALSE;
345       return SVN_NO_ERROR;
346     }
347
348   SVN_ERR(get_node_revision(&noderev, node));
349   if (noderev->mergeinfo_count > 1)
350     *do_they = TRUE;
351   else if (noderev->mergeinfo_count == 1 && !noderev->has_mergeinfo)
352     *do_they = TRUE;
353   else
354     *do_they = FALSE;
355   return SVN_NO_ERROR;
356 }
357
358 \f
359 /*** Directory node functions ***/
360
361 /* Some of these are helpers for functions outside this section. */
362
363 /* Set *ID_P to the noderev-id for entry NAME in PARENT.  If no such
364    entry, set *ID_P to NULL but do not error. */
365 static svn_error_t *
366 dir_entry_id_from_node(svn_fs_x__id_t *id_p,
367                        dag_node_t *parent,
368                        const char *name,
369                        apr_pool_t *scratch_pool)
370 {
371   svn_fs_x__dirent_t *dirent;
372   svn_fs_x__noderev_t *noderev;
373
374   SVN_ERR(get_node_revision(&noderev, parent));
375   if (noderev->kind != svn_node_dir)
376     return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
377                             _("Can't get entries of non-directory"));
378
379   /* Make sure that NAME is a single path component. */
380   if (! svn_path_is_single_path_component(name))
381     return svn_error_createf
382       (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
383        "Attempted to open node with an illegal name '%s'", name);
384
385   /* Get a dirent hash for this directory. */
386   SVN_ERR(svn_fs_x__rep_contents_dir_entry(&dirent, parent->fs, noderev,
387                                            name, &parent->hint,
388                                            scratch_pool, scratch_pool));
389   if (dirent)
390     *id_p = dirent->id;
391   else
392     svn_fs_x__id_reset(id_p);
393
394   return SVN_NO_ERROR;
395 }
396
397
398 /* Add or set in PARENT a directory entry NAME pointing to ID.
399    Temporary allocations are done in SCRATCH_POOL.
400
401    Assumptions:
402    - PARENT is a mutable directory.
403    - ID does not refer to an ancestor of parent
404    - NAME is a single path component
405 */
406 static svn_error_t *
407 set_entry(dag_node_t *parent,
408           const char *name,
409           const svn_fs_x__id_t *id,
410           svn_node_kind_t kind,
411           svn_fs_x__txn_id_t txn_id,
412           apr_pool_t *scratch_pool)
413 {
414   svn_fs_x__noderev_t *parent_noderev;
415
416   /* Get the parent's node-revision. */
417   SVN_ERR(get_node_revision(&parent_noderev, parent));
418
419   /* Set the new entry. */
420   return svn_fs_x__set_entry(parent->fs, txn_id, parent_noderev, name, id,
421                              kind, parent->node_pool, scratch_pool);
422 }
423
424
425 /* Make a new entry named NAME in PARENT.  If IS_DIR is true, then the
426    node revision the new entry points to will be a directory, else it
427    will be a file.  The new node will be allocated in RESULT_POOL.  PARENT
428    must be mutable, and must not have an entry named NAME.
429
430    Use SCRATCH_POOL for all temporary allocations.
431  */
432 static svn_error_t *
433 make_entry(dag_node_t **child_p,
434            dag_node_t *parent,
435            const char *parent_path,
436            const char *name,
437            svn_boolean_t is_dir,
438            svn_fs_x__txn_id_t txn_id,
439            apr_pool_t *result_pool,
440            apr_pool_t *scratch_pool)
441 {
442   svn_fs_x__noderev_t new_noderev, *parent_noderev;
443
444   /* Make sure that NAME is a single path component. */
445   if (! svn_path_is_single_path_component(name))
446     return svn_error_createf
447       (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
448        _("Attempted to create a node with an illegal name '%s'"), name);
449
450   /* Make sure that parent is a directory */
451   if (parent->kind != svn_node_dir)
452     return svn_error_create
453       (SVN_ERR_FS_NOT_DIRECTORY, NULL,
454        _("Attempted to create entry in non-directory parent"));
455
456   /* Check that the parent is mutable. */
457   if (! svn_fs_x__dag_check_mutable(parent))
458     return svn_error_createf
459       (SVN_ERR_FS_NOT_MUTABLE, NULL,
460        _("Attempted to clone child of non-mutable node"));
461
462   /* Create the new node's NODE-REVISION */
463   memset(&new_noderev, 0, sizeof(new_noderev));
464   new_noderev.kind = is_dir ? svn_node_dir : svn_node_file;
465   new_noderev.created_path = svn_fspath__join(parent_path, name, result_pool);
466
467   SVN_ERR(get_node_revision(&parent_noderev, parent));
468   new_noderev.copyroot_path = apr_pstrdup(result_pool,
469                                           parent_noderev->copyroot_path);
470   new_noderev.copyroot_rev = parent_noderev->copyroot_rev;
471   new_noderev.copyfrom_rev = SVN_INVALID_REVNUM;
472   new_noderev.copyfrom_path = NULL;
473   svn_fs_x__id_reset(&new_noderev.predecessor_id);
474
475   SVN_ERR(svn_fs_x__create_node
476           (svn_fs_x__dag_get_fs(parent), &new_noderev,
477            &parent_noderev->copy_id, txn_id, scratch_pool));
478
479   /* Create a new dag_node_t for our new node */
480   SVN_ERR(svn_fs_x__dag_get_node(child_p, svn_fs_x__dag_get_fs(parent),
481                                  &new_noderev.noderev_id, result_pool,
482                                  scratch_pool));
483
484   /* We can safely call set_entry because we already know that
485      PARENT is mutable, and we just created CHILD, so we know it has
486      no ancestors (therefore, PARENT cannot be an ancestor of CHILD) */
487   return set_entry(parent, name, &new_noderev.noderev_id,
488                    new_noderev.kind, txn_id, scratch_pool);
489 }
490
491
492 svn_error_t *
493 svn_fs_x__dag_dir_entries(apr_array_header_t **entries,
494                           dag_node_t *node,
495                           apr_pool_t *result_pool,
496                           apr_pool_t *scratch_pool)
497 {
498   svn_fs_x__noderev_t *noderev;
499
500   SVN_ERR(get_node_revision(&noderev, node));
501
502   if (noderev->kind != svn_node_dir)
503     return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
504                             _("Can't get entries of non-directory"));
505
506   return svn_fs_x__rep_contents_dir(entries, node->fs, noderev, result_pool,
507                                     scratch_pool);
508 }
509
510
511 svn_error_t *
512 svn_fs_x__dag_set_entry(dag_node_t *node,
513                         const char *entry_name,
514                         const svn_fs_x__id_t *id,
515                         svn_node_kind_t kind,
516                         svn_fs_x__txn_id_t txn_id,
517                         apr_pool_t *scratch_pool)
518 {
519   /* Check it's a directory. */
520   if (node->kind != svn_node_dir)
521     return svn_error_create
522       (SVN_ERR_FS_NOT_DIRECTORY, NULL,
523        _("Attempted to set entry in non-directory node"));
524
525   /* Check it's mutable. */
526   if (! svn_fs_x__dag_check_mutable(node))
527     return svn_error_create
528       (SVN_ERR_FS_NOT_MUTABLE, NULL,
529        _("Attempted to set entry in immutable node"));
530
531   return set_entry(node, entry_name, id, kind, txn_id, scratch_pool);
532 }
533
534
535 \f
536 /*** Proplists. ***/
537
538 svn_error_t *
539 svn_fs_x__dag_get_proplist(apr_hash_t **proplist_p,
540                            dag_node_t *node,
541                            apr_pool_t *result_pool,
542                            apr_pool_t *scratch_pool)
543 {
544   svn_fs_x__noderev_t *noderev;
545   apr_hash_t *proplist = NULL;
546
547   SVN_ERR(get_node_revision(&noderev, node));
548
549   SVN_ERR(svn_fs_x__get_proplist(&proplist, node->fs, noderev, result_pool,
550                                  scratch_pool));
551
552   *proplist_p = proplist;
553
554   return SVN_NO_ERROR;
555 }
556
557
558 svn_error_t *
559 svn_fs_x__dag_set_proplist(dag_node_t *node,
560                            apr_hash_t *proplist,
561                            apr_pool_t *scratch_pool)
562 {
563   svn_fs_x__noderev_t *noderev;
564
565   /* Sanity check: this node better be mutable! */
566   if (! svn_fs_x__dag_check_mutable(node))
567     {
568       svn_string_t *idstr = svn_fs_x__id_unparse(&node->id, scratch_pool);
569       return svn_error_createf
570         (SVN_ERR_FS_NOT_MUTABLE, NULL,
571          "Can't set proplist on *immutable* node-revision %s",
572          idstr->data);
573     }
574
575   /* Go get a fresh NODE-REVISION for this node. */
576   SVN_ERR(get_node_revision(&noderev, node));
577
578   /* Set the new proplist. */
579   return svn_fs_x__set_proplist(node->fs, noderev, proplist, scratch_pool);
580 }
581
582
583 svn_error_t *
584 svn_fs_x__dag_increment_mergeinfo_count(dag_node_t *node,
585                                         apr_int64_t increment,
586                                         apr_pool_t *scratch_pool)
587 {
588   svn_fs_x__noderev_t *noderev;
589
590   /* Sanity check: this node better be mutable! */
591   if (! svn_fs_x__dag_check_mutable(node))
592     {
593       svn_string_t *idstr = svn_fs_x__id_unparse(&node->id, scratch_pool);
594       return svn_error_createf
595         (SVN_ERR_FS_NOT_MUTABLE, NULL,
596          "Can't increment mergeinfo count on *immutable* node-revision %s",
597          idstr->data);
598     }
599
600   if (increment == 0)
601     return SVN_NO_ERROR;
602
603   /* Go get a fresh NODE-REVISION for this node. */
604   SVN_ERR(get_node_revision(&noderev, node));
605
606   noderev->mergeinfo_count += increment;
607   if (noderev->mergeinfo_count < 0)
608     {
609       svn_string_t *idstr = svn_fs_x__id_unparse(&node->id, scratch_pool);
610       return svn_error_createf
611         (SVN_ERR_FS_CORRUPT, NULL,
612          apr_psprintf(scratch_pool,
613                       _("Can't increment mergeinfo count on node-revision %%s "
614                         "to negative value %%%s"),
615                       APR_INT64_T_FMT),
616          idstr->data, noderev->mergeinfo_count);
617     }
618   if (noderev->mergeinfo_count > 1 && noderev->kind == svn_node_file)
619     {
620       svn_string_t *idstr = svn_fs_x__id_unparse(&node->id, scratch_pool);
621       return svn_error_createf
622         (SVN_ERR_FS_CORRUPT, NULL,
623          apr_psprintf(scratch_pool,
624                       _("Can't increment mergeinfo count on *file* "
625                         "node-revision %%s to %%%s (> 1)"),
626                       APR_INT64_T_FMT),
627          idstr->data, noderev->mergeinfo_count);
628     }
629
630   /* Flush it out. */
631   return svn_fs_x__put_node_revision(node->fs, noderev, scratch_pool);
632 }
633
634 svn_error_t *
635 svn_fs_x__dag_set_has_mergeinfo(dag_node_t *node,
636                                 svn_boolean_t has_mergeinfo,
637                                 apr_pool_t *scratch_pool)
638 {
639   svn_fs_x__noderev_t *noderev;
640
641   /* Sanity check: this node better be mutable! */
642   if (! svn_fs_x__dag_check_mutable(node))
643     {
644       svn_string_t *idstr = svn_fs_x__id_unparse(&node->id, scratch_pool);
645       return svn_error_createf
646         (SVN_ERR_FS_NOT_MUTABLE, NULL,
647          "Can't set mergeinfo flag on *immutable* node-revision %s",
648          idstr->data);
649     }
650
651   /* Go get a fresh NODE-REVISION for this node. */
652   SVN_ERR(get_node_revision(&noderev, node));
653
654   noderev->has_mergeinfo = has_mergeinfo;
655
656   /* Flush it out. */
657   return svn_fs_x__put_node_revision(node->fs, noderev, scratch_pool);
658 }
659
660 \f
661 /*** Roots. ***/
662
663 svn_error_t *
664 svn_fs_x__dag_revision_root(dag_node_t **node_p,
665                             svn_fs_t *fs,
666                             svn_revnum_t rev,
667                             apr_pool_t *result_pool,
668                             apr_pool_t *scratch_pool)
669 {
670   svn_fs_x__id_t root_id;
671
672   svn_fs_x__init_rev_root(&root_id, rev);
673   return svn_fs_x__dag_get_node(node_p, fs, &root_id, result_pool,
674                                 scratch_pool);
675 }
676
677
678 svn_error_t *
679 svn_fs_x__dag_txn_root(dag_node_t **node_p,
680                        svn_fs_t *fs,
681                        svn_fs_x__txn_id_t txn_id,
682                        apr_pool_t *result_pool,
683                        apr_pool_t *scratch_pool)
684 {
685   svn_fs_x__id_t root_id;
686
687   svn_fs_x__init_txn_root(&root_id, txn_id);
688   return svn_fs_x__dag_get_node(node_p, fs, &root_id, result_pool,
689                                 scratch_pool);
690 }
691
692
693 svn_error_t *
694 svn_fs_x__dag_clone_child(dag_node_t **child_p,
695                           dag_node_t *parent,
696                           const char *parent_path,
697                           const char *name,
698                           const svn_fs_x__id_t *copy_id,
699                           svn_fs_x__txn_id_t txn_id,
700                           svn_boolean_t is_parent_copyroot,
701                           apr_pool_t *result_pool,
702                           apr_pool_t *scratch_pool)
703 {
704   dag_node_t *cur_entry; /* parent's current entry named NAME */
705   const svn_fs_x__id_t *new_node_id; /* node id we'll put into NEW_NODE */
706   svn_fs_t *fs = svn_fs_x__dag_get_fs(parent);
707
708   /* First check that the parent is mutable. */
709   if (! svn_fs_x__dag_check_mutable(parent))
710     return svn_error_createf
711       (SVN_ERR_FS_NOT_MUTABLE, NULL,
712        "Attempted to clone child of non-mutable node");
713
714   /* Make sure that NAME is a single path component. */
715   if (! svn_path_is_single_path_component(name))
716     return svn_error_createf
717       (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
718        "Attempted to make a child clone with an illegal name '%s'", name);
719
720   /* Find the node named NAME in PARENT's entries list if it exists. */
721   SVN_ERR(svn_fs_x__dag_open(&cur_entry, parent, name, scratch_pool,
722                              scratch_pool));
723   if (! cur_entry)
724     return svn_error_createf
725       (SVN_ERR_FS_NOT_FOUND, NULL,
726        "Attempted to open non-existent child node '%s'", name);
727
728   /* Check for mutability in the node we found.  If it's mutable, we
729      don't need to clone it. */
730   if (svn_fs_x__dag_check_mutable(cur_entry))
731     {
732       /* This has already been cloned */
733       new_node_id = svn_fs_x__dag_get_id(cur_entry);
734     }
735   else
736     {
737       svn_fs_x__noderev_t *noderev, *parent_noderev;
738
739       /* Go get a fresh NODE-REVISION for current child node. */
740       SVN_ERR(get_node_revision(&noderev, cur_entry));
741
742       if (is_parent_copyroot)
743         {
744           SVN_ERR(get_node_revision(&parent_noderev, parent));
745           noderev->copyroot_rev = parent_noderev->copyroot_rev;
746           noderev->copyroot_path = apr_pstrdup(scratch_pool,
747                                                parent_noderev->copyroot_path);
748         }
749
750       noderev->copyfrom_path = NULL;
751       noderev->copyfrom_rev = SVN_INVALID_REVNUM;
752
753       noderev->predecessor_id = noderev->noderev_id;
754       noderev->predecessor_count++;
755       noderev->created_path = svn_fspath__join(parent_path, name,
756                                                scratch_pool);
757
758       if (copy_id == NULL)
759         copy_id = &noderev->copy_id;
760
761       SVN_ERR(svn_fs_x__create_successor(fs, noderev, copy_id, txn_id,
762                                          scratch_pool));
763       new_node_id = &noderev->noderev_id;
764
765       /* Replace the ID in the parent's ENTRY list with the ID which
766          refers to the mutable clone of this child. */
767       SVN_ERR(set_entry(parent, name, new_node_id, noderev->kind, txn_id,
768                         scratch_pool));
769     }
770
771   /* Initialize the youngster. */
772   return svn_fs_x__dag_get_node(child_p, fs, new_node_id, result_pool,
773                                 scratch_pool);
774 }
775
776
777 /* Delete all mutable node revisions reachable from node ID, including
778    ID itself, from FS's `nodes' table.  Also delete any mutable
779    representations and strings associated with that node revision.
780    ID may refer to a file or directory, which may be mutable or immutable.
781
782    Use SCRATCH_POOL for temporary allocations.
783  */
784 static svn_error_t *
785 delete_if_mutable(svn_fs_t *fs,
786                   const svn_fs_x__id_t *id,
787                   apr_pool_t *scratch_pool)
788 {
789   dag_node_t *node;
790
791   /* Get the node. */
792   SVN_ERR(svn_fs_x__dag_get_node(&node, fs, id, scratch_pool, scratch_pool));
793
794   /* If immutable, do nothing and return immediately. */
795   if (! svn_fs_x__dag_check_mutable(node))
796     return SVN_NO_ERROR;
797
798   /* Else it's mutable.  Recurse on directories... */
799   if (node->kind == svn_node_dir)
800     {
801       apr_array_header_t *entries;
802       int i;
803       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
804
805       /* Loop over directory entries */
806       SVN_ERR(svn_fs_x__dag_dir_entries(&entries, node, scratch_pool,
807                                         iterpool));
808       for (i = 0; i < entries->nelts; ++i)
809         {
810           const svn_fs_x__id_t *noderev_id
811             = &APR_ARRAY_IDX(entries, i, svn_fs_x__dirent_t *)->id;
812
813           svn_pool_clear(iterpool);
814           SVN_ERR(delete_if_mutable(fs, noderev_id, iterpool));
815         }
816
817       svn_pool_destroy(iterpool);
818     }
819
820   /* ... then delete the node itself, after deleting any mutable
821      representations and strings it points to. */
822   return svn_fs_x__delete_node_revision(fs, id, scratch_pool);
823 }
824
825
826 svn_error_t *
827 svn_fs_x__dag_delete(dag_node_t *parent,
828                      const char *name,
829                      svn_fs_x__txn_id_t txn_id,
830                      apr_pool_t *scratch_pool)
831 {
832   svn_fs_x__noderev_t *parent_noderev;
833   svn_fs_t *fs = parent->fs;
834   svn_fs_x__dirent_t *dirent;
835   apr_pool_t *subpool;
836
837   /* Make sure parent is a directory. */
838   if (parent->kind != svn_node_dir)
839     return svn_error_createf
840       (SVN_ERR_FS_NOT_DIRECTORY, NULL,
841        "Attempted to delete entry '%s' from *non*-directory node", name);
842
843   /* Make sure parent is mutable. */
844   if (! svn_fs_x__dag_check_mutable(parent))
845     return svn_error_createf
846       (SVN_ERR_FS_NOT_MUTABLE, NULL,
847        "Attempted to delete entry '%s' from immutable directory node", name);
848
849   /* Make sure that NAME is a single path component. */
850   if (! svn_path_is_single_path_component(name))
851     return svn_error_createf
852       (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
853        "Attempted to delete a node with an illegal name '%s'", name);
854
855   /* Get a fresh NODE-REVISION for the parent node. */
856   SVN_ERR(get_node_revision(&parent_noderev, parent));
857
858   subpool = svn_pool_create(scratch_pool);
859
860   /* Search this directory for a dirent with that NAME. */
861   SVN_ERR(svn_fs_x__rep_contents_dir_entry(&dirent, fs, parent_noderev,
862                                            name, &parent->hint,
863                                            subpool, subpool));
864
865   /* If we never found ID in ENTRIES (perhaps because there are no
866      ENTRIES, perhaps because ID just isn't in the existing ENTRIES
867      ... it doesn't matter), return an error.  */
868   if (! dirent)
869     return svn_error_createf
870       (SVN_ERR_FS_NO_SUCH_ENTRY, NULL,
871        "Delete failed--directory has no entry '%s'", name);
872
873   /* If mutable, remove it and any mutable children from db. */
874   SVN_ERR(delete_if_mutable(parent->fs, &dirent->id, scratch_pool));
875   svn_pool_destroy(subpool);
876
877   /* Remove this entry from its parent's entries list. */
878   return svn_fs_x__set_entry(parent->fs, txn_id, parent_noderev, name,
879                              NULL, svn_node_unknown, parent->node_pool,
880                              scratch_pool);
881 }
882
883
884 svn_error_t *
885 svn_fs_x__dag_make_file(dag_node_t **child_p,
886                         dag_node_t *parent,
887                         const char *parent_path,
888                         const char *name,
889                         svn_fs_x__txn_id_t txn_id,
890                         apr_pool_t *result_pool,
891                         apr_pool_t *scratch_pool)
892 {
893   /* Call our little helper function */
894   return make_entry(child_p, parent, parent_path, name, FALSE, txn_id,
895                     result_pool, scratch_pool);
896 }
897
898
899 svn_error_t *
900 svn_fs_x__dag_make_dir(dag_node_t **child_p,
901                        dag_node_t *parent,
902                        const char *parent_path,
903                        const char *name,
904                        svn_fs_x__txn_id_t txn_id,
905                        apr_pool_t *result_pool,
906                        apr_pool_t *scratch_pool)
907 {
908   /* Call our little helper function */
909   return make_entry(child_p, parent, parent_path, name, TRUE, txn_id,
910                     result_pool, scratch_pool);
911 }
912
913
914 svn_error_t *
915 svn_fs_x__dag_get_contents(svn_stream_t **contents_p,
916                            dag_node_t *file,
917                            apr_pool_t *result_pool)
918 {
919   svn_fs_x__noderev_t *noderev;
920   svn_stream_t *contents;
921
922   /* Make sure our node is a file. */
923   if (file->kind != svn_node_file)
924     return svn_error_createf
925       (SVN_ERR_FS_NOT_FILE, NULL,
926        "Attempted to get textual contents of a *non*-file node");
927
928   /* Go get a fresh node-revision for FILE. */
929   SVN_ERR(get_node_revision(&noderev, file));
930
931   /* Get a stream to the contents. */
932   SVN_ERR(svn_fs_x__get_contents(&contents, file->fs,
933                                  noderev->data_rep, TRUE, result_pool));
934
935   *contents_p = contents;
936
937   return SVN_NO_ERROR;
938 }
939
940
941 svn_error_t *
942 svn_fs_x__dag_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
943                                     dag_node_t *source,
944                                     dag_node_t *target,
945                                     apr_pool_t *result_pool,
946                                     apr_pool_t *scratch_pool)
947 {
948   svn_fs_x__noderev_t *src_noderev;
949   svn_fs_x__noderev_t *tgt_noderev;
950
951   /* Make sure our nodes are files. */
952   if ((source && source->kind != svn_node_file)
953       || target->kind != svn_node_file)
954     return svn_error_createf
955       (SVN_ERR_FS_NOT_FILE, NULL,
956        "Attempted to get textual contents of a *non*-file node");
957
958   /* Go get fresh node-revisions for the nodes. */
959   if (source)
960     SVN_ERR(get_node_revision(&src_noderev, source));
961   else
962     src_noderev = NULL;
963   SVN_ERR(get_node_revision(&tgt_noderev, target));
964
965   /* Get the delta stream. */
966   return svn_fs_x__get_file_delta_stream(stream_p, target->fs,
967                                          src_noderev, tgt_noderev,
968                                          result_pool, scratch_pool);
969 }
970
971
972 svn_error_t *
973 svn_fs_x__dag_try_process_file_contents(svn_boolean_t *success,
974                                         dag_node_t *node,
975                                         svn_fs_process_contents_func_t processor,
976                                         void* baton,
977                                         apr_pool_t *scratch_pool)
978 {
979   svn_fs_x__noderev_t *noderev;
980
981   /* Go get fresh node-revisions for the nodes. */
982   SVN_ERR(get_node_revision(&noderev, node));
983
984   return svn_fs_x__try_process_file_contents(success, node->fs,
985                                              noderev,
986                                              processor, baton, scratch_pool);
987 }
988
989
990 svn_error_t *
991 svn_fs_x__dag_file_length(svn_filesize_t *length,
992                           dag_node_t *file)
993 {
994   svn_fs_x__noderev_t *noderev;
995
996   /* Make sure our node is a file. */
997   if (file->kind != svn_node_file)
998     return svn_error_createf
999       (SVN_ERR_FS_NOT_FILE, NULL,
1000        "Attempted to get length of a *non*-file node");
1001
1002   /* Go get a fresh node-revision for FILE, and . */
1003   SVN_ERR(get_node_revision(&noderev, file));
1004
1005   return svn_fs_x__file_length(length, noderev);
1006 }
1007
1008
1009 svn_error_t *
1010 svn_fs_x__dag_file_checksum(svn_checksum_t **checksum,
1011                             dag_node_t *file,
1012                             svn_checksum_kind_t kind,
1013                             apr_pool_t *result_pool)
1014 {
1015   svn_fs_x__noderev_t *noderev;
1016
1017   if (file->kind != svn_node_file)
1018     return svn_error_createf
1019       (SVN_ERR_FS_NOT_FILE, NULL,
1020        "Attempted to get checksum of a *non*-file node");
1021
1022   SVN_ERR(get_node_revision(&noderev, file));
1023
1024   return svn_fs_x__file_checksum(checksum, noderev, kind, result_pool);
1025 }
1026
1027
1028 svn_error_t *
1029 svn_fs_x__dag_get_edit_stream(svn_stream_t **contents,
1030                               dag_node_t *file,
1031                               apr_pool_t *result_pool)
1032 {
1033   svn_fs_x__noderev_t *noderev;
1034   svn_stream_t *ws;
1035
1036   /* Make sure our node is a file. */
1037   if (file->kind != svn_node_file)
1038     return svn_error_createf
1039       (SVN_ERR_FS_NOT_FILE, NULL,
1040        "Attempted to set textual contents of a *non*-file node");
1041
1042   /* Make sure our node is mutable. */
1043   if (! svn_fs_x__dag_check_mutable(file))
1044     return svn_error_createf
1045       (SVN_ERR_FS_NOT_MUTABLE, NULL,
1046        "Attempted to set textual contents of an immutable node");
1047
1048   /* Get the node revision. */
1049   SVN_ERR(get_node_revision(&noderev, file));
1050
1051   SVN_ERR(svn_fs_x__set_contents(&ws, file->fs, noderev, result_pool));
1052
1053   *contents = ws;
1054
1055   return SVN_NO_ERROR;
1056 }
1057
1058
1059
1060 svn_error_t *
1061 svn_fs_x__dag_finalize_edits(dag_node_t *file,
1062                              const svn_checksum_t *checksum,
1063                              apr_pool_t *scratch_pool)
1064 {
1065   if (checksum)
1066     {
1067       svn_checksum_t *file_checksum;
1068
1069       SVN_ERR(svn_fs_x__dag_file_checksum(&file_checksum, file,
1070                                           checksum->kind, scratch_pool));
1071       if (!svn_checksum_match(checksum, file_checksum))
1072         return svn_checksum_mismatch_err(checksum, file_checksum,
1073                                          scratch_pool,
1074                                          _("Checksum mismatch for '%s'"),
1075                                          file->created_path);
1076     }
1077
1078   return SVN_NO_ERROR;
1079 }
1080
1081
1082 dag_node_t *
1083 svn_fs_x__dag_dup(const dag_node_t *node,
1084                   apr_pool_t *result_pool)
1085 {
1086   /* Allocate our new node. */
1087   dag_node_t *new_node = apr_pmemdup(result_pool, node, sizeof(*new_node));
1088
1089   /* Only copy cached svn_fs_x__noderev_t for immutable nodes. */
1090   if (node->node_revision && !svn_fs_x__dag_check_mutable(node))
1091     {
1092       new_node->node_revision = copy_node_revision(node->node_revision,
1093                                                    result_pool);
1094       new_node->created_path = new_node->node_revision->created_path;
1095     }
1096   else
1097     {
1098       new_node->node_revision = NULL;
1099       new_node->created_path = apr_pstrdup(result_pool, node->created_path);
1100     }
1101
1102   new_node->node_pool = result_pool;
1103
1104   return new_node;
1105 }
1106
1107 dag_node_t *
1108 svn_fs_x__dag_copy_into_pool(dag_node_t *node,
1109                              apr_pool_t *result_pool)
1110 {
1111   return (node->node_pool == result_pool
1112             ? node
1113             : svn_fs_x__dag_dup(node, result_pool));
1114 }
1115
1116 svn_error_t *
1117 svn_fs_x__dag_serialize(void **data,
1118                         apr_size_t *data_len,
1119                         void *in,
1120                         apr_pool_t *pool)
1121 {
1122   dag_node_t *node = in;
1123   svn_stringbuf_t *serialized;
1124
1125   /* create an serialization context and serialize the dag node as root */
1126   svn_temp_serializer__context_t *context =
1127       svn_temp_serializer__init(node,
1128                                 sizeof(*node),
1129                                 1024 - SVN_TEMP_SERIALIZER__OVERHEAD,
1130                                 pool);
1131
1132   /* for mutable nodes, we will _never_ cache the noderev */
1133   if (node->node_revision && !svn_fs_x__dag_check_mutable(node))
1134     {
1135       svn_fs_x__noderev_serialize(context, &node->node_revision);
1136     }
1137   else
1138     {
1139       svn_temp_serializer__set_null(context,
1140                                     (const void * const *)&node->node_revision);
1141       svn_temp_serializer__add_string(context, &node->created_path);
1142     }
1143
1144   /* The deserializer will use its own pool. */
1145   svn_temp_serializer__set_null(context,
1146                                 (const void * const *)&node->node_pool);
1147
1148   /* return serialized data */
1149   serialized = svn_temp_serializer__get(context);
1150   *data = serialized->data;
1151   *data_len = serialized->len;
1152
1153   return SVN_NO_ERROR;
1154 }
1155
1156 svn_error_t *
1157 svn_fs_x__dag_deserialize(void **out,
1158                           void *data,
1159                           apr_size_t data_len,
1160                           apr_pool_t *pool)
1161 {
1162   dag_node_t *node = (dag_node_t *)data;
1163   if (data_len == 0)
1164     return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
1165                             _("Empty noderev in cache"));
1166
1167   /* Copy the _full_ buffer as it also contains the sub-structures. */
1168   node->fs = NULL;
1169
1170   /* fixup all references to sub-structures */
1171   svn_fs_x__noderev_deserialize(node, &node->node_revision, pool);
1172   node->node_pool = pool;
1173
1174   if (node->node_revision)
1175     node->created_path = node->node_revision->created_path;
1176   else
1177     svn_temp_deserializer__resolve(node, (void**)&node->created_path);
1178
1179   /* return result */
1180   *out = node;
1181
1182   return SVN_NO_ERROR;
1183 }
1184
1185 svn_error_t *
1186 svn_fs_x__dag_open(dag_node_t **child_p,
1187                    dag_node_t *parent,
1188                    const char *name,
1189                    apr_pool_t *result_pool,
1190                    apr_pool_t *scratch_pool)
1191 {
1192   svn_fs_x__id_t node_id;
1193
1194   /* Ensure that NAME exists in PARENT's entry list. */
1195   SVN_ERR(dir_entry_id_from_node(&node_id, parent, name, scratch_pool));
1196   if (! svn_fs_x__id_used(&node_id))
1197     {
1198       *child_p = NULL;
1199       return SVN_NO_ERROR;
1200     }
1201
1202   /* Now get the node that was requested. */
1203   return svn_fs_x__dag_get_node(child_p, svn_fs_x__dag_get_fs(parent),
1204                                 &node_id, result_pool, scratch_pool);
1205 }
1206
1207
1208 svn_error_t *
1209 svn_fs_x__dag_copy(dag_node_t *to_node,
1210                    const char *entry,
1211                    dag_node_t *from_node,
1212                    svn_boolean_t preserve_history,
1213                    svn_revnum_t from_rev,
1214                    const char *from_path,
1215                    svn_fs_x__txn_id_t txn_id,
1216                    apr_pool_t *scratch_pool)
1217 {
1218   const svn_fs_x__id_t *id;
1219
1220   if (preserve_history)
1221     {
1222       svn_fs_x__noderev_t *from_noderev, *to_noderev;
1223       svn_fs_x__id_t copy_id;
1224       svn_fs_t *fs = svn_fs_x__dag_get_fs(from_node);
1225
1226       /* Make a copy of the original node revision. */
1227       SVN_ERR(get_node_revision(&from_noderev, from_node));
1228       to_noderev = copy_node_revision(from_noderev, scratch_pool);
1229
1230       /* Reserve a copy ID for this new copy. */
1231       SVN_ERR(svn_fs_x__reserve_copy_id(&copy_id, fs, txn_id, scratch_pool));
1232
1233       /* Create a successor with its predecessor pointing at the copy
1234          source. */
1235       to_noderev->predecessor_id = to_noderev->noderev_id;
1236       to_noderev->predecessor_count++;
1237       to_noderev->created_path =
1238         svn_fspath__join(svn_fs_x__dag_get_created_path(to_node), entry,
1239                          scratch_pool);
1240       to_noderev->copyfrom_path = apr_pstrdup(scratch_pool, from_path);
1241       to_noderev->copyfrom_rev = from_rev;
1242
1243       /* Set the copyroot equal to our own id. */
1244       to_noderev->copyroot_path = NULL;
1245
1246       SVN_ERR(svn_fs_x__create_successor(fs, to_noderev,
1247                                          &copy_id, txn_id, scratch_pool));
1248       id = &to_noderev->noderev_id;
1249     }
1250   else  /* don't preserve history */
1251     {
1252       id = svn_fs_x__dag_get_id(from_node);
1253     }
1254
1255   /* Set the entry in to_node to the new id. */
1256   return svn_fs_x__dag_set_entry(to_node, entry, id, from_node->kind,
1257                                  txn_id, scratch_pool);
1258 }
1259
1260
1261 \f
1262 /*** Comparison. ***/
1263
1264 svn_error_t *
1265 svn_fs_x__dag_things_different(svn_boolean_t *props_changed,
1266                                svn_boolean_t *contents_changed,
1267                                dag_node_t *node1,
1268                                dag_node_t *node2,
1269                                svn_boolean_t strict,
1270                                apr_pool_t *scratch_pool)
1271 {
1272   svn_fs_x__noderev_t *noderev1, *noderev2;
1273   svn_fs_t *fs;
1274   svn_boolean_t same;
1275
1276   /* If we have no place to store our results, don't bother doing
1277      anything. */
1278   if (! props_changed && ! contents_changed)
1279     return SVN_NO_ERROR;
1280
1281   fs = svn_fs_x__dag_get_fs(node1);
1282
1283   /* The node revision skels for these two nodes. */
1284   SVN_ERR(get_node_revision(&noderev1, node1));
1285   SVN_ERR(get_node_revision(&noderev2, node2));
1286
1287   /* Compare property keys. */
1288   if (props_changed != NULL)
1289     {
1290       SVN_ERR(svn_fs_x__prop_rep_equal(&same, fs, noderev1, noderev2,
1291                                        strict, scratch_pool));
1292       *props_changed = !same;
1293     }
1294
1295   /* Compare contents keys. */
1296   if (contents_changed != NULL)
1297     *contents_changed = !svn_fs_x__file_text_rep_equal(noderev1->data_rep,
1298                                                        noderev2->data_rep);
1299
1300   return SVN_NO_ERROR;
1301 }
1302
1303 svn_error_t *
1304 svn_fs_x__dag_get_copyroot(svn_revnum_t *rev,
1305                            const char **path,
1306                            dag_node_t *node)
1307 {
1308   svn_fs_x__noderev_t *noderev;
1309
1310   /* Go get a fresh node-revision for NODE. */
1311   SVN_ERR(get_node_revision(&noderev, node));
1312
1313   *rev = noderev->copyroot_rev;
1314   *path = noderev->copyroot_path;
1315
1316   return SVN_NO_ERROR;
1317 }
1318
1319 svn_error_t *
1320 svn_fs_x__dag_get_copyfrom_rev(svn_revnum_t *rev,
1321                                dag_node_t *node)
1322 {
1323   svn_fs_x__noderev_t *noderev;
1324
1325   /* Go get a fresh node-revision for NODE. */
1326   SVN_ERR(get_node_revision(&noderev, node));
1327
1328   *rev = noderev->copyfrom_rev;
1329
1330   return SVN_NO_ERROR;
1331 }
1332
1333 svn_error_t *
1334 svn_fs_x__dag_get_copyfrom_path(const char **path,
1335                                 dag_node_t *node)
1336 {
1337   svn_fs_x__noderev_t *noderev;
1338
1339   /* Go get a fresh node-revision for NODE. */
1340   SVN_ERR(get_node_revision(&noderev, node));
1341
1342   *path = noderev->copyfrom_path;
1343
1344   return SVN_NO_ERROR;
1345 }
1346
1347 svn_error_t *
1348 svn_fs_x__dag_update_ancestry(dag_node_t *target,
1349                               dag_node_t *source,
1350                               apr_pool_t *scratch_pool)
1351 {
1352   svn_fs_x__noderev_t *source_noderev, *target_noderev;
1353
1354   if (! svn_fs_x__dag_check_mutable(target))
1355     return svn_error_createf
1356       (SVN_ERR_FS_NOT_MUTABLE, NULL,
1357        _("Attempted to update ancestry of non-mutable node"));
1358
1359   SVN_ERR(get_node_revision(&source_noderev, source));
1360   SVN_ERR(get_node_revision(&target_noderev, target));
1361
1362   target_noderev->predecessor_id = source_noderev->noderev_id;
1363   target_noderev->predecessor_count = source_noderev->predecessor_count;
1364   target_noderev->predecessor_count++;
1365
1366   return svn_fs_x__put_node_revision(target->fs, target_noderev,
1367                                      scratch_pool);
1368 }