]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_fs_fs/dag.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_fs_fs / 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 "cached_data.h"
32 #include "dag.h"
33 #include "fs.h"
34 #include "fs_fs.h"
35 #include "id.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, allocated in POOL.  */
54   svn_fs_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 node revision ID for this dag
58      node's predecessor; otherwise NULL. (Used in
59      svn_fs_node_created_rev.) */
60   const svn_fs_id_t *fresh_root_predecessor_id;
61
62   /* The node's type (file, dir, etc.) */
63   svn_node_kind_t kind;
64
65   /* The node's NODE-REVISION, or NULL if we haven't read it in yet.
66      This is allocated in this node's POOL.
67
68      If you're willing to respect all the rules above, you can munge
69      this yourself, but you're probably better off just calling
70      `get_node_revision' and `set_node_revision', which take care of
71      things for you.  */
72   node_revision_t *node_revision;
73
74   /* The pool to allocate NODE_REVISION in. */
75   apr_pool_t *node_pool;
76
77   /* the path at which this node was created. */
78   const char *created_path;
79 };
80
81
82 \f
83 /* Trivial helper/accessor functions. */
84 svn_node_kind_t svn_fs_fs__dag_node_kind(dag_node_t *node)
85 {
86   return node->kind;
87 }
88
89
90 const svn_fs_id_t *
91 svn_fs_fs__dag_get_id(const dag_node_t *node)
92 {
93   return node->id;
94 }
95
96
97 const char *
98 svn_fs_fs__dag_get_created_path(dag_node_t *node)
99 {
100   return node->created_path;
101 }
102
103
104 svn_fs_t *
105 svn_fs_fs__dag_get_fs(dag_node_t *node)
106 {
107   return node->fs;
108 }
109
110 void
111 svn_fs_fs__dag_set_fs(dag_node_t *node, svn_fs_t *fs)
112 {
113   node->fs = fs;
114 }
115
116
117 /* Dup NODEREV and all associated data into POOL.
118    Leaves the id and is_fresh_txn_root fields as zero bytes. */
119 static node_revision_t *
120 copy_node_revision(node_revision_t *noderev,
121                    apr_pool_t *pool)
122 {
123   node_revision_t *nr = apr_pcalloc(pool, sizeof(*nr));
124   nr->kind = noderev->kind;
125   if (noderev->predecessor_id)
126     nr->predecessor_id = svn_fs_fs__id_copy(noderev->predecessor_id, pool);
127   nr->predecessor_count = noderev->predecessor_count;
128   if (noderev->copyfrom_path)
129     nr->copyfrom_path = apr_pstrdup(pool, noderev->copyfrom_path);
130   nr->copyfrom_rev = noderev->copyfrom_rev;
131   nr->copyroot_path = apr_pstrdup(pool, noderev->copyroot_path);
132   nr->copyroot_rev = noderev->copyroot_rev;
133   nr->data_rep = svn_fs_fs__rep_copy(noderev->data_rep, pool);
134   nr->prop_rep = svn_fs_fs__rep_copy(noderev->prop_rep, pool);
135   nr->mergeinfo_count = noderev->mergeinfo_count;
136   nr->has_mergeinfo = noderev->has_mergeinfo;
137
138   if (noderev->created_path)
139     nr->created_path = apr_pstrdup(pool, noderev->created_path);
140   return nr;
141 }
142
143
144 /* Set *NODEREV_P to the cached node-revision for NODE.
145    If the node-revision was not already cached in NODE, read it in,
146    allocating the cache in NODE->NODE_POOL.
147
148    If you plan to change the contents of NODE, be careful!  We're
149    handing you a pointer directly to our cached node-revision, not
150    your own copy.  If you change it as part of some operation, but
151    then some Berkeley DB function deadlocks or gets an error, you'll
152    need to back out your changes, or else the cache will reflect
153    changes that never got committed.  It's probably best not to change
154    the structure at all.  */
155 static svn_error_t *
156 get_node_revision(node_revision_t **noderev_p,
157                   dag_node_t *node)
158 {
159   /* If we've already got a copy, there's no need to read it in.  */
160   if (! node->node_revision)
161     {
162       node_revision_t *noderev;
163       apr_pool_t *scratch_pool = svn_pool_create(node->node_pool);
164
165       SVN_ERR(svn_fs_fs__get_node_revision(&noderev, node->fs,
166                                            node->id, node->node_pool,
167                                            scratch_pool));
168       node->node_revision = noderev;
169       svn_pool_destroy(scratch_pool);
170     }
171
172   /* Now NODE->node_revision is set.  */
173   *noderev_p = node->node_revision;
174   return SVN_NO_ERROR;
175 }
176
177
178 svn_boolean_t svn_fs_fs__dag_check_mutable(const dag_node_t *node)
179 {
180   return svn_fs_fs__id_is_txn(svn_fs_fs__dag_get_id(node));
181 }
182
183
184 svn_error_t *
185 svn_fs_fs__dag_get_node(dag_node_t **node,
186                         svn_fs_t *fs,
187                         const svn_fs_id_t *id,
188                         apr_pool_t *pool)
189 {
190   dag_node_t *new_node;
191   node_revision_t *noderev;
192
193   /* Construct the node. */
194   new_node = apr_pcalloc(pool, sizeof(*new_node));
195   new_node->fs = fs;
196   new_node->id = svn_fs_fs__id_copy(id, pool);
197
198   /* Grab the contents so we can inspect the node's kind and created path. */
199   new_node->node_pool = pool;
200   SVN_ERR(get_node_revision(&noderev, new_node));
201
202   /* Initialize the KIND and CREATED_PATH attributes */
203   new_node->kind = noderev->kind;
204   new_node->created_path = apr_pstrdup(pool, noderev->created_path);
205
206   if (noderev->is_fresh_txn_root)
207     new_node->fresh_root_predecessor_id = noderev->predecessor_id;
208   else
209     new_node->fresh_root_predecessor_id = NULL;
210
211   /* Return a fresh new node */
212   *node = new_node;
213   return SVN_NO_ERROR;
214 }
215
216
217 svn_error_t *
218 svn_fs_fs__dag_get_revision(svn_revnum_t *rev,
219                             dag_node_t *node,
220                             apr_pool_t *pool)
221 {
222   /* In the special case that this is an unmodified transaction root,
223      we need to actually get the revision of the noderev's predecessor
224      (the revision root); see Issue #2608. */
225   const svn_fs_id_t *correct_id = node->fresh_root_predecessor_id
226     ? node->fresh_root_predecessor_id : node->id;
227
228   /* Look up the committed revision from the Node-ID. */
229   *rev = svn_fs_fs__id_rev(correct_id);
230
231   return SVN_NO_ERROR;
232 }
233
234
235 svn_error_t *
236 svn_fs_fs__dag_get_predecessor_id(const svn_fs_id_t **id_p,
237                                   dag_node_t *node)
238 {
239   node_revision_t *noderev;
240
241   SVN_ERR(get_node_revision(&noderev, node));
242   *id_p = noderev->predecessor_id;
243   return SVN_NO_ERROR;
244 }
245
246
247 svn_error_t *
248 svn_fs_fs__dag_get_predecessor_count(int *count,
249                                      dag_node_t *node)
250 {
251   node_revision_t *noderev;
252
253   SVN_ERR(get_node_revision(&noderev, node));
254   *count = noderev->predecessor_count;
255   return SVN_NO_ERROR;
256 }
257
258 svn_error_t *
259 svn_fs_fs__dag_get_mergeinfo_count(apr_int64_t *count,
260                                    dag_node_t *node)
261 {
262   node_revision_t *noderev;
263
264   SVN_ERR(get_node_revision(&noderev, node));
265   *count = noderev->mergeinfo_count;
266   return SVN_NO_ERROR;
267 }
268
269 svn_error_t *
270 svn_fs_fs__dag_has_mergeinfo(svn_boolean_t *has_mergeinfo,
271                              dag_node_t *node)
272 {
273   node_revision_t *noderev;
274
275   SVN_ERR(get_node_revision(&noderev, node));
276   *has_mergeinfo = noderev->has_mergeinfo;
277   return SVN_NO_ERROR;
278 }
279
280 svn_error_t *
281 svn_fs_fs__dag_has_descendants_with_mergeinfo(svn_boolean_t *do_they,
282                                               dag_node_t *node)
283 {
284   node_revision_t *noderev;
285
286   if (node->kind != svn_node_dir)
287     {
288       *do_they = FALSE;
289       return SVN_NO_ERROR;
290     }
291
292   SVN_ERR(get_node_revision(&noderev, node));
293   if (noderev->mergeinfo_count > 1)
294     *do_they = TRUE;
295   else if (noderev->mergeinfo_count == 1 && !noderev->has_mergeinfo)
296     *do_they = TRUE;
297   else
298     *do_they = FALSE;
299   return SVN_NO_ERROR;
300 }
301
302 \f
303 /*** Directory node functions ***/
304
305 /* Some of these are helpers for functions outside this section. */
306
307 /* Set *ID_P to the node-id for entry NAME in PARENT.  If no such
308    entry, set *ID_P to NULL but do not error.  The node-id is
309    allocated in POOL. */
310 static svn_error_t *
311 dir_entry_id_from_node(const svn_fs_id_t **id_p,
312                        dag_node_t *parent,
313                        const char *name,
314                        apr_pool_t *result_pool,
315                        apr_pool_t *scratch_pool)
316 {
317   svn_fs_dirent_t *dirent;
318
319   SVN_ERR(svn_fs_fs__dag_dir_entry(&dirent, parent, name, result_pool,
320                                    scratch_pool));
321   *id_p = dirent ? dirent->id : NULL;
322
323   return SVN_NO_ERROR;
324 }
325
326
327 /* Add or set in PARENT a directory entry NAME pointing to ID.
328    Allocations are done in POOL.
329
330    Assumptions:
331    - PARENT is a mutable directory.
332    - ID does not refer to an ancestor of parent
333    - NAME is a single path component
334 */
335 static svn_error_t *
336 set_entry(dag_node_t *parent,
337           const char *name,
338           const svn_fs_id_t *id,
339           svn_node_kind_t kind,
340           const svn_fs_fs__id_part_t *txn_id,
341           apr_pool_t *pool)
342 {
343   node_revision_t *parent_noderev;
344
345   /* Get the parent's node-revision. */
346   SVN_ERR(get_node_revision(&parent_noderev, parent));
347
348   /* Set the new entry. */
349   return svn_fs_fs__set_entry(parent->fs, txn_id, parent_noderev, name, id,
350                               kind, pool);
351 }
352
353
354 /* Make a new entry named NAME in PARENT.  If IS_DIR is true, then the
355    node revision the new entry points to will be a directory, else it
356    will be a file.  The new node will be allocated in POOL.  PARENT
357    must be mutable, and must not have an entry named NAME.
358
359    Use POOL for all allocations, except caching the node_revision in PARENT.
360  */
361 static svn_error_t *
362 make_entry(dag_node_t **child_p,
363            dag_node_t *parent,
364            const char *parent_path,
365            const char *name,
366            svn_boolean_t is_dir,
367            const svn_fs_fs__id_part_t *txn_id,
368            apr_pool_t *pool)
369 {
370   const svn_fs_id_t *new_node_id;
371   node_revision_t new_noderev, *parent_noderev;
372
373   /* Make sure that NAME is a single path component. */
374   if (! svn_path_is_single_path_component(name))
375     return svn_error_createf
376       (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
377        _("Attempted to create a node with an illegal name '%s'"), name);
378
379   /* Make sure that parent is a directory */
380   if (parent->kind != svn_node_dir)
381     return svn_error_create
382       (SVN_ERR_FS_NOT_DIRECTORY, NULL,
383        _("Attempted to create entry in non-directory parent"));
384
385   /* Check that the parent is mutable. */
386   if (! svn_fs_fs__dag_check_mutable(parent))
387     return svn_error_createf
388       (SVN_ERR_FS_NOT_MUTABLE, NULL,
389        _("Attempted to clone child of non-mutable node"));
390
391   /* Create the new node's NODE-REVISION */
392   memset(&new_noderev, 0, sizeof(new_noderev));
393   new_noderev.kind = is_dir ? svn_node_dir : svn_node_file;
394   new_noderev.created_path = svn_fspath__join(parent_path, name, pool);
395
396   SVN_ERR(get_node_revision(&parent_noderev, parent));
397   new_noderev.copyroot_path = apr_pstrdup(pool,
398                                           parent_noderev->copyroot_path);
399   new_noderev.copyroot_rev = parent_noderev->copyroot_rev;
400   new_noderev.copyfrom_rev = SVN_INVALID_REVNUM;
401   new_noderev.copyfrom_path = NULL;
402
403   SVN_ERR(svn_fs_fs__create_node
404           (&new_node_id, svn_fs_fs__dag_get_fs(parent), &new_noderev,
405            svn_fs_fs__id_copy_id(svn_fs_fs__dag_get_id(parent)),
406            txn_id, pool));
407
408   /* Create a new dag_node_t for our new node */
409   SVN_ERR(svn_fs_fs__dag_get_node(child_p, svn_fs_fs__dag_get_fs(parent),
410                                   new_node_id, pool));
411
412   /* We can safely call set_entry because we already know that
413      PARENT is mutable, and we just created CHILD, so we know it has
414      no ancestors (therefore, PARENT cannot be an ancestor of CHILD) */
415   return set_entry(parent, name, svn_fs_fs__dag_get_id(*child_p),
416                    new_noderev.kind, txn_id, pool);
417 }
418
419
420 svn_error_t *
421 svn_fs_fs__dag_dir_entries(apr_array_header_t **entries,
422                            dag_node_t *node,
423                            apr_pool_t *pool)
424 {
425   node_revision_t *noderev;
426
427   SVN_ERR(get_node_revision(&noderev, node));
428
429   if (noderev->kind != svn_node_dir)
430     return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
431                             _("Can't get entries of non-directory"));
432
433   return svn_fs_fs__rep_contents_dir(entries, node->fs, noderev, pool, pool);
434 }
435
436 svn_error_t *
437 svn_fs_fs__dag_dir_entry(svn_fs_dirent_t **dirent,
438                          dag_node_t *node,
439                          const char* name,
440                          apr_pool_t *result_pool,
441                          apr_pool_t *scratch_pool)
442 {
443   node_revision_t *noderev;
444   SVN_ERR(get_node_revision(&noderev, node));
445
446   if (noderev->kind != svn_node_dir)
447     return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
448                             _("Can't get entries of non-directory"));
449
450   /* Get a dirent hash for this directory. */
451   return svn_fs_fs__rep_contents_dir_entry(dirent, node->fs, noderev, name,
452                                            result_pool, scratch_pool);
453 }
454
455
456 svn_error_t *
457 svn_fs_fs__dag_set_entry(dag_node_t *node,
458                          const char *entry_name,
459                          const svn_fs_id_t *id,
460                          svn_node_kind_t kind,
461                          const svn_fs_fs__id_part_t *txn_id,
462                          apr_pool_t *pool)
463 {
464   /* Check it's a directory. */
465   if (node->kind != svn_node_dir)
466     return svn_error_create
467       (SVN_ERR_FS_NOT_DIRECTORY, NULL,
468        _("Attempted to set entry in non-directory node"));
469
470   /* Check it's mutable. */
471   if (! svn_fs_fs__dag_check_mutable(node))
472     return svn_error_create
473       (SVN_ERR_FS_NOT_MUTABLE, NULL,
474        _("Attempted to set entry in immutable node"));
475
476   return set_entry(node, entry_name, id, kind, txn_id, pool);
477 }
478
479
480 \f
481 /*** Proplists. ***/
482
483 svn_error_t *
484 svn_fs_fs__dag_get_proplist(apr_hash_t **proplist_p,
485                             dag_node_t *node,
486                             apr_pool_t *pool)
487 {
488   node_revision_t *noderev;
489   apr_hash_t *proplist = NULL;
490
491   SVN_ERR(get_node_revision(&noderev, node));
492
493   SVN_ERR(svn_fs_fs__get_proplist(&proplist, node->fs,
494                                   noderev, pool));
495
496   *proplist_p = proplist;
497
498   return SVN_NO_ERROR;
499 }
500
501 svn_error_t *
502 svn_fs_fs__dag_has_props(svn_boolean_t *has_props,
503                          dag_node_t *node,
504                          apr_pool_t *scratch_pool)
505 {
506   node_revision_t *noderev;
507
508   SVN_ERR(get_node_revision(&noderev, node));
509
510   if (! noderev->prop_rep)
511     {
512       *has_props = FALSE; /* Easy out */
513       return SVN_NO_ERROR;
514     }
515
516   if (svn_fs_fs__id_txn_used(&noderev->prop_rep->txn_id))
517     {
518       /* We are in a commit or something. Check actual properties */
519       apr_hash_t *proplist;
520
521       SVN_ERR(svn_fs_fs__get_proplist(&proplist, node->fs,
522                                       noderev, scratch_pool));
523
524       *has_props = proplist ? (0 < apr_hash_count(proplist)) : FALSE;
525     }
526   else
527     {
528       /* Properties are stored as a standard hash stream,
529          always ending with "END\n" (4 bytes) */
530       *has_props = (noderev->prop_rep->expanded_size > 4
531                     || (noderev->prop_rep->expanded_size == 0
532                         && noderev->prop_rep->size > 4));
533     }
534
535   return SVN_NO_ERROR;
536 }
537
538 svn_error_t *
539 svn_fs_fs__dag_set_proplist(dag_node_t *node,
540                             apr_hash_t *proplist,
541                             apr_pool_t *pool)
542 {
543   node_revision_t *noderev;
544
545   /* Sanity check: this node better be mutable! */
546   if (! svn_fs_fs__dag_check_mutable(node))
547     {
548       svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
549       return svn_error_createf
550         (SVN_ERR_FS_NOT_MUTABLE, NULL,
551          "Can't set proplist on *immutable* node-revision %s",
552          idstr->data);
553     }
554
555   /* Go get a fresh NODE-REVISION for this node. */
556   SVN_ERR(get_node_revision(&noderev, node));
557
558   /* Set the new proplist. */
559   return svn_fs_fs__set_proplist(node->fs, noderev, proplist, pool);
560 }
561
562
563 svn_error_t *
564 svn_fs_fs__dag_increment_mergeinfo_count(dag_node_t *node,
565                                          apr_int64_t increment,
566                                          apr_pool_t *pool)
567 {
568   node_revision_t *noderev;
569
570   /* Sanity check: this node better be mutable! */
571   if (! svn_fs_fs__dag_check_mutable(node))
572     {
573       svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
574       return svn_error_createf
575         (SVN_ERR_FS_NOT_MUTABLE, NULL,
576          "Can't increment mergeinfo count on *immutable* node-revision %s",
577          idstr->data);
578     }
579
580   if (increment == 0)
581     return SVN_NO_ERROR;
582
583   /* Go get a fresh NODE-REVISION for this node. */
584   SVN_ERR(get_node_revision(&noderev, node));
585
586   noderev->mergeinfo_count += increment;
587   if (noderev->mergeinfo_count < 0)
588     {
589       svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
590       return svn_error_createf
591         (SVN_ERR_FS_CORRUPT, NULL,
592          apr_psprintf(pool,
593                       _("Can't increment mergeinfo count on node-revision %%s "
594                         "to negative value %%%s"),
595                       APR_INT64_T_FMT),
596          idstr->data, noderev->mergeinfo_count);
597     }
598   if (noderev->mergeinfo_count > 1 && noderev->kind == svn_node_file)
599     {
600       svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
601       return svn_error_createf
602         (SVN_ERR_FS_CORRUPT, NULL,
603          apr_psprintf(pool,
604                       _("Can't increment mergeinfo count on *file* "
605                         "node-revision %%s to %%%s (> 1)"),
606                       APR_INT64_T_FMT),
607          idstr->data, noderev->mergeinfo_count);
608     }
609
610   /* Flush it out. */
611   return svn_fs_fs__put_node_revision(node->fs, noderev->id,
612                                       noderev, FALSE, pool);
613 }
614
615 svn_error_t *
616 svn_fs_fs__dag_set_has_mergeinfo(dag_node_t *node,
617                                  svn_boolean_t has_mergeinfo,
618                                  apr_pool_t *pool)
619 {
620   node_revision_t *noderev;
621
622   /* Sanity check: this node better be mutable! */
623   if (! svn_fs_fs__dag_check_mutable(node))
624     {
625       svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
626       return svn_error_createf
627         (SVN_ERR_FS_NOT_MUTABLE, NULL,
628          "Can't set mergeinfo flag on *immutable* node-revision %s",
629          idstr->data);
630     }
631
632   /* Go get a fresh NODE-REVISION for this node. */
633   SVN_ERR(get_node_revision(&noderev, node));
634
635   noderev->has_mergeinfo = has_mergeinfo;
636
637   /* Flush it out. */
638   return svn_fs_fs__put_node_revision(node->fs, noderev->id,
639                                       noderev, FALSE, pool);
640 }
641
642 \f
643 /*** Roots. ***/
644
645 svn_error_t *
646 svn_fs_fs__dag_revision_root(dag_node_t **node_p,
647                              svn_fs_t *fs,
648                              svn_revnum_t rev,
649                              apr_pool_t *pool)
650 {
651   dag_node_t *new_node;
652
653   /* Construct the node. */
654   new_node = apr_pcalloc(pool, sizeof(*new_node));
655   new_node->fs = fs;
656   SVN_ERR(svn_fs_fs__rev_get_root(&new_node->id, fs, rev, pool, pool));
657
658   /* Grab the contents so we can inspect the node's kind and created path. */
659   new_node->node_pool = pool;
660
661   /* Initialize the KIND and CREATED_PATH attributes */
662   new_node->kind = svn_node_dir;
663   new_node->created_path = "/";
664   new_node->fresh_root_predecessor_id = NULL;
665
666   /* Return a fresh new node */
667   *node_p = new_node;
668   return SVN_NO_ERROR;
669 }
670
671
672 svn_error_t *
673 svn_fs_fs__dag_txn_root(dag_node_t **node_p,
674                         svn_fs_t *fs,
675                         const svn_fs_fs__id_part_t *txn_id,
676                         apr_pool_t *pool)
677 {
678   const svn_fs_id_t *root_id, *ignored;
679
680   SVN_ERR(svn_fs_fs__get_txn_ids(&root_id, &ignored, fs, txn_id, pool));
681   return svn_fs_fs__dag_get_node(node_p, fs, root_id, pool);
682 }
683
684
685 svn_error_t *
686 svn_fs_fs__dag_txn_base_root(dag_node_t **node_p,
687                              svn_fs_t *fs,
688                              const svn_fs_fs__id_part_t *txn_id,
689                              apr_pool_t *pool)
690 {
691   const svn_fs_id_t *base_root_id, *ignored;
692
693   SVN_ERR(svn_fs_fs__get_txn_ids(&ignored, &base_root_id, fs, txn_id, pool));
694   return svn_fs_fs__dag_get_node(node_p, fs, base_root_id, pool);
695 }
696
697
698 svn_error_t *
699 svn_fs_fs__dag_clone_child(dag_node_t **child_p,
700                            dag_node_t *parent,
701                            const char *parent_path,
702                            const char *name,
703                            const svn_fs_fs__id_part_t *copy_id,
704                            const svn_fs_fs__id_part_t *txn_id,
705                            svn_boolean_t is_parent_copyroot,
706                            apr_pool_t *pool)
707 {
708   dag_node_t *cur_entry; /* parent's current entry named NAME */
709   const svn_fs_id_t *new_node_id; /* node id we'll put into NEW_NODE */
710   svn_fs_t *fs = svn_fs_fs__dag_get_fs(parent);
711   apr_pool_t *subpool = svn_pool_create(pool);
712
713   /* First check that the parent is mutable. */
714   if (! svn_fs_fs__dag_check_mutable(parent))
715     return svn_error_createf
716       (SVN_ERR_FS_NOT_MUTABLE, NULL,
717        "Attempted to clone child of non-mutable node");
718
719   /* Make sure that NAME is a single path component. */
720   if (! svn_path_is_single_path_component(name))
721     return svn_error_createf
722       (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
723        "Attempted to make a child clone with an illegal name '%s'", name);
724
725   /* Find the node named NAME in PARENT's entries list if it exists. */
726   SVN_ERR(svn_fs_fs__dag_open(&cur_entry, parent, name, pool, subpool));
727   if (! cur_entry)
728     return svn_error_createf
729       (SVN_ERR_FS_NOT_FOUND, NULL,
730        "Attempted to open non-existent child node '%s'", name);
731
732   /* Check for mutability in the node we found.  If it's mutable, we
733      don't need to clone it. */
734   if (svn_fs_fs__dag_check_mutable(cur_entry))
735     {
736       /* This has already been cloned */
737       new_node_id = cur_entry->id;
738     }
739   else
740     {
741       node_revision_t *noderev, *parent_noderev;
742
743       /* Go get a fresh NODE-REVISION for current child node. */
744       SVN_ERR(get_node_revision(&noderev, cur_entry));
745
746       if (is_parent_copyroot)
747         {
748           SVN_ERR(get_node_revision(&parent_noderev, parent));
749           noderev->copyroot_rev = parent_noderev->copyroot_rev;
750           noderev->copyroot_path = apr_pstrdup(pool,
751                                                parent_noderev->copyroot_path);
752         }
753
754       noderev->copyfrom_path = NULL;
755       noderev->copyfrom_rev = SVN_INVALID_REVNUM;
756
757       noderev->predecessor_id = svn_fs_fs__id_copy(cur_entry->id, pool);
758       if (noderev->predecessor_count != -1)
759         noderev->predecessor_count++;
760       noderev->created_path = svn_fspath__join(parent_path, name, pool);
761
762       SVN_ERR(svn_fs_fs__create_successor(&new_node_id, fs, cur_entry->id,
763                                           noderev, copy_id, txn_id, pool));
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                         pool));
769     }
770
771   /* Initialize the youngster. */
772   svn_pool_destroy(subpool);
773   return svn_fs_fs__dag_get_node(child_p, fs, new_node_id, pool);
774 }
775
776
777
778 svn_error_t *
779 svn_fs_fs__dag_clone_root(dag_node_t **root_p,
780                           svn_fs_t *fs,
781                           const svn_fs_fs__id_part_t *txn_id,
782                           apr_pool_t *pool)
783 {
784   const svn_fs_id_t *base_root_id, *root_id;
785
786   /* Get the node ID's of the root directories of the transaction and
787      its base revision.  */
788   SVN_ERR(svn_fs_fs__get_txn_ids(&root_id, &base_root_id, fs, txn_id, pool));
789
790   /* Oh, give me a clone...
791      (If they're the same, we haven't cloned the transaction's root
792      directory yet.)  */
793   SVN_ERR_ASSERT(!svn_fs_fs__id_eq(root_id, base_root_id));
794
795   /*
796    * (Sung to the tune of "Home, Home on the Range", with thanks to
797    * Randall Garrett and Isaac Asimov.)
798    */
799
800   /* One way or another, root_id now identifies a cloned root node. */
801   return svn_fs_fs__dag_get_node(root_p, fs, root_id, pool);
802 }
803
804
805 svn_error_t *
806 svn_fs_fs__dag_delete(dag_node_t *parent,
807                       const char *name,
808                       const svn_fs_fs__id_part_t *txn_id,
809                       apr_pool_t *pool)
810 {
811   node_revision_t *parent_noderev;
812   svn_fs_t *fs = parent->fs;
813   svn_fs_dirent_t *dirent;
814   svn_fs_id_t *id;
815   apr_pool_t *subpool;
816
817   /* Make sure parent is a directory. */
818   if (parent->kind != svn_node_dir)
819     return svn_error_createf
820       (SVN_ERR_FS_NOT_DIRECTORY, NULL,
821        "Attempted to delete entry '%s' from *non*-directory node", name);
822
823   /* Make sure parent is mutable. */
824   if (! svn_fs_fs__dag_check_mutable(parent))
825     return svn_error_createf
826       (SVN_ERR_FS_NOT_MUTABLE, NULL,
827        "Attempted to delete entry '%s' from immutable directory node", name);
828
829   /* Make sure that NAME is a single path component. */
830   if (! svn_path_is_single_path_component(name))
831     return svn_error_createf
832       (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
833        "Attempted to delete a node with an illegal name '%s'", name);
834
835   /* Get a fresh NODE-REVISION for the parent node. */
836   SVN_ERR(get_node_revision(&parent_noderev, parent));
837
838   subpool = svn_pool_create(pool);
839
840   /* Search this directory for a dirent with that NAME. */
841   SVN_ERR(svn_fs_fs__rep_contents_dir_entry(&dirent, fs, parent_noderev,
842                                             name, subpool, subpool));
843
844   /* If we never found ID in ENTRIES (perhaps because there are no
845      ENTRIES, perhaps because ID just isn't in the existing ENTRIES
846      ... it doesn't matter), return an error.  */
847   if (! dirent)
848     return svn_error_createf
849       (SVN_ERR_FS_NO_SUCH_ENTRY, NULL,
850        "Delete failed--directory has no entry '%s'", name);
851
852   /* Copy the ID out of the subpool and release the rest of the
853      directory listing. */
854   id = svn_fs_fs__id_copy(dirent->id, pool);
855   svn_pool_destroy(subpool);
856
857   /* If mutable, remove it and any mutable children from db. */
858   SVN_ERR(svn_fs_fs__dag_delete_if_mutable(parent->fs, id, pool));
859
860   /* Remove this entry from its parent's entries list. */
861   return svn_fs_fs__set_entry(parent->fs, txn_id, parent_noderev, name,
862                               NULL, svn_node_unknown, pool);
863 }
864
865
866 svn_error_t *
867 svn_fs_fs__dag_remove_node(svn_fs_t *fs,
868                            const svn_fs_id_t *id,
869                            apr_pool_t *pool)
870 {
871   dag_node_t *node;
872
873   /* Fetch the node. */
874   SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, id, pool));
875
876   /* If immutable, do nothing and return immediately. */
877   if (! svn_fs_fs__dag_check_mutable(node))
878     return svn_error_createf(SVN_ERR_FS_NOT_MUTABLE, NULL,
879                              "Attempted removal of immutable node");
880
881   /* Delete the node revision. */
882   return svn_fs_fs__delete_node_revision(fs, id, pool);
883 }
884
885
886 svn_error_t *
887 svn_fs_fs__dag_delete_if_mutable(svn_fs_t *fs,
888                                  const svn_fs_id_t *id,
889                                  apr_pool_t *pool)
890 {
891   dag_node_t *node;
892
893   /* Get the node. */
894   SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, id, pool));
895
896   /* If immutable, do nothing and return immediately. */
897   if (! svn_fs_fs__dag_check_mutable(node))
898     return SVN_NO_ERROR;
899
900   /* Else it's mutable.  Recurse on directories... */
901   if (node->kind == svn_node_dir)
902     {
903       apr_array_header_t *entries;
904       int i;
905       apr_pool_t *iterpool = svn_pool_create(pool);
906
907       /* Loop over directory entries */
908       SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
909       if (entries)
910         for (i = 0; i < entries->nelts; ++i)
911           {
912             svn_pool_clear(iterpool);
913             SVN_ERR(svn_fs_fs__dag_delete_if_mutable(fs,
914                           APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *)->id,
915                           iterpool));
916           }
917
918       svn_pool_destroy(iterpool);
919     }
920
921   /* ... then delete the node itself, after deleting any mutable
922      representations and strings it points to. */
923   return svn_fs_fs__dag_remove_node(fs, id, pool);
924 }
925
926 svn_error_t *
927 svn_fs_fs__dag_make_file(dag_node_t **child_p,
928                          dag_node_t *parent,
929                          const char *parent_path,
930                          const char *name,
931                          const svn_fs_fs__id_part_t *txn_id,
932                          apr_pool_t *pool)
933 {
934   /* Call our little helper function */
935   return make_entry(child_p, parent, parent_path, name, FALSE, txn_id, pool);
936 }
937
938
939 svn_error_t *
940 svn_fs_fs__dag_make_dir(dag_node_t **child_p,
941                         dag_node_t *parent,
942                         const char *parent_path,
943                         const char *name,
944                         const svn_fs_fs__id_part_t *txn_id,
945                         apr_pool_t *pool)
946 {
947   /* Call our little helper function */
948   return make_entry(child_p, parent, parent_path, name, TRUE, txn_id, pool);
949 }
950
951
952 svn_error_t *
953 svn_fs_fs__dag_get_contents(svn_stream_t **contents_p,
954                             dag_node_t *file,
955                             apr_pool_t *pool)
956 {
957   node_revision_t *noderev;
958   svn_stream_t *contents;
959
960   /* Make sure our node is a file. */
961   if (file->kind != svn_node_file)
962     return svn_error_createf
963       (SVN_ERR_FS_NOT_FILE, NULL,
964        "Attempted to get textual contents of a *non*-file node");
965
966   /* Go get a fresh node-revision for FILE. */
967   SVN_ERR(get_node_revision(&noderev, file));
968
969   /* Get a stream to the contents. */
970   SVN_ERR(svn_fs_fs__get_contents(&contents, file->fs,
971                                   noderev->data_rep, TRUE, pool));
972
973   *contents_p = contents;
974
975   return SVN_NO_ERROR;
976 }
977
978
979 svn_error_t *
980 svn_fs_fs__dag_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
981                                      dag_node_t *source,
982                                      dag_node_t *target,
983                                      apr_pool_t *pool)
984 {
985   node_revision_t *src_noderev;
986   node_revision_t *tgt_noderev;
987
988   /* Make sure our nodes are files. */
989   if ((source && source->kind != svn_node_file)
990       || target->kind != svn_node_file)
991     return svn_error_createf
992       (SVN_ERR_FS_NOT_FILE, NULL,
993        "Attempted to get textual contents of a *non*-file node");
994
995   /* Go get fresh node-revisions for the nodes. */
996   if (source)
997     SVN_ERR(get_node_revision(&src_noderev, source));
998   else
999     src_noderev = NULL;
1000   SVN_ERR(get_node_revision(&tgt_noderev, target));
1001
1002   /* Get the delta stream. */
1003   return svn_fs_fs__get_file_delta_stream(stream_p, target->fs,
1004                                           src_noderev, tgt_noderev, pool);
1005 }
1006
1007
1008 svn_error_t *
1009 svn_fs_fs__dag_try_process_file_contents(svn_boolean_t *success,
1010                                          dag_node_t *node,
1011                                          svn_fs_process_contents_func_t processor,
1012                                          void* baton,
1013                                          apr_pool_t *pool)
1014 {
1015   node_revision_t *noderev;
1016
1017   /* Go get fresh node-revisions for the nodes. */
1018   SVN_ERR(get_node_revision(&noderev, node));
1019
1020   return svn_fs_fs__try_process_file_contents(success, node->fs,
1021                                               noderev,
1022                                               processor, baton, pool);
1023 }
1024
1025
1026 svn_error_t *
1027 svn_fs_fs__dag_file_length(svn_filesize_t *length,
1028                            dag_node_t *file,
1029                            apr_pool_t *pool)
1030 {
1031   node_revision_t *noderev;
1032
1033   /* Make sure our node is a file. */
1034   if (file->kind != svn_node_file)
1035     return svn_error_createf
1036       (SVN_ERR_FS_NOT_FILE, NULL,
1037        "Attempted to get length of a *non*-file node");
1038
1039   /* Go get a fresh node-revision for FILE, and . */
1040   SVN_ERR(get_node_revision(&noderev, file));
1041
1042   return svn_fs_fs__file_length(length, noderev, pool);
1043 }
1044
1045
1046 svn_error_t *
1047 svn_fs_fs__dag_file_checksum(svn_checksum_t **checksum,
1048                              dag_node_t *file,
1049                              svn_checksum_kind_t kind,
1050                              apr_pool_t *pool)
1051 {
1052   node_revision_t *noderev;
1053
1054   if (file->kind != svn_node_file)
1055     return svn_error_createf
1056       (SVN_ERR_FS_NOT_FILE, NULL,
1057        "Attempted to get checksum of a *non*-file node");
1058
1059   SVN_ERR(get_node_revision(&noderev, file));
1060
1061   return svn_fs_fs__file_checksum(checksum, noderev, kind, pool);
1062 }
1063
1064
1065 svn_error_t *
1066 svn_fs_fs__dag_get_edit_stream(svn_stream_t **contents,
1067                                dag_node_t *file,
1068                                apr_pool_t *pool)
1069 {
1070   node_revision_t *noderev;
1071   svn_stream_t *ws;
1072
1073   /* Make sure our node is a file. */
1074   if (file->kind != svn_node_file)
1075     return svn_error_createf
1076       (SVN_ERR_FS_NOT_FILE, NULL,
1077        "Attempted to set textual contents of a *non*-file node");
1078
1079   /* Make sure our node is mutable. */
1080   if (! svn_fs_fs__dag_check_mutable(file))
1081     return svn_error_createf
1082       (SVN_ERR_FS_NOT_MUTABLE, NULL,
1083        "Attempted to set textual contents of an immutable node");
1084
1085   /* Get the node revision. */
1086   SVN_ERR(get_node_revision(&noderev, file));
1087
1088   SVN_ERR(svn_fs_fs__set_contents(&ws, file->fs, noderev, pool));
1089
1090   *contents = ws;
1091
1092   return SVN_NO_ERROR;
1093 }
1094
1095
1096
1097 svn_error_t *
1098 svn_fs_fs__dag_finalize_edits(dag_node_t *file,
1099                               const svn_checksum_t *checksum,
1100                               apr_pool_t *pool)
1101 {
1102   if (checksum)
1103     {
1104       svn_checksum_t *file_checksum;
1105
1106       SVN_ERR(svn_fs_fs__dag_file_checksum(&file_checksum, file,
1107                                            checksum->kind, pool));
1108       if (!svn_checksum_match(checksum, file_checksum))
1109         return svn_checksum_mismatch_err(checksum, file_checksum, pool,
1110                                          _("Checksum mismatch for '%s'"),
1111                                          file->created_path);
1112     }
1113
1114   return SVN_NO_ERROR;
1115 }
1116
1117
1118 dag_node_t *
1119 svn_fs_fs__dag_dup(const dag_node_t *node,
1120                    apr_pool_t *pool)
1121 {
1122   /* Allocate our new node. */
1123   dag_node_t *new_node = apr_pcalloc(pool, sizeof(*new_node));
1124
1125   new_node->fs = node->fs;
1126   new_node->id = svn_fs_fs__id_copy(node->id, pool);
1127   new_node->kind = node->kind;
1128   new_node->created_path = apr_pstrdup(pool, node->created_path);
1129
1130   /* Only copy cached node_revision_t for immutable nodes. */
1131   if (node->node_revision && !svn_fs_fs__dag_check_mutable(node))
1132     {
1133       new_node->node_revision = copy_node_revision(node->node_revision, pool);
1134       new_node->node_revision->id =
1135           svn_fs_fs__id_copy(node->node_revision->id, pool);
1136       new_node->node_revision->is_fresh_txn_root =
1137           node->node_revision->is_fresh_txn_root;
1138     }
1139   new_node->node_pool = pool;
1140
1141   return new_node;
1142 }
1143
1144 svn_error_t *
1145 svn_fs_fs__dag_serialize(void **data,
1146                          apr_size_t *data_len,
1147                          void *in,
1148                          apr_pool_t *pool)
1149 {
1150   dag_node_t *node = in;
1151   svn_stringbuf_t *serialized;
1152
1153   /* create an serialization context and serialize the dag node as root */
1154   svn_temp_serializer__context_t *context =
1155       svn_temp_serializer__init(node,
1156                                 sizeof(*node),
1157                                 1024 - SVN_TEMP_SERIALIZER__OVERHEAD,
1158                                 pool);
1159
1160   /* for mutable nodes, we will _never_ cache the noderev */
1161   if (node->node_revision && !svn_fs_fs__dag_check_mutable(node))
1162     svn_fs_fs__noderev_serialize(context, &node->node_revision);
1163   else
1164     svn_temp_serializer__set_null(context,
1165                                   (const void * const *)&node->node_revision);
1166
1167   /* The deserializer will use its own pool. */
1168   svn_temp_serializer__set_null(context,
1169                                 (const void * const *)&node->node_pool);
1170
1171   /* serialize other sub-structures */
1172   svn_fs_fs__id_serialize(context, (const svn_fs_id_t **)&node->id);
1173   svn_fs_fs__id_serialize(context, &node->fresh_root_predecessor_id);
1174   svn_temp_serializer__add_string(context, &node->created_path);
1175
1176   /* return serialized data */
1177   serialized = svn_temp_serializer__get(context);
1178   *data = serialized->data;
1179   *data_len = serialized->len;
1180
1181   return SVN_NO_ERROR;
1182 }
1183
1184 svn_error_t *
1185 svn_fs_fs__dag_deserialize(void **out,
1186                            void *data,
1187                            apr_size_t data_len,
1188                            apr_pool_t *pool)
1189 {
1190   dag_node_t *node = (dag_node_t *)data;
1191   if (data_len == 0)
1192     return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
1193                             _("Empty noderev in cache"));
1194
1195   /* Copy the _full_ buffer as it also contains the sub-structures. */
1196   node->fs = NULL;
1197
1198   /* fixup all references to sub-structures */
1199   svn_fs_fs__id_deserialize(node, &node->id);
1200   svn_fs_fs__id_deserialize(node,
1201                             (svn_fs_id_t **)&node->fresh_root_predecessor_id);
1202   svn_fs_fs__noderev_deserialize(node, &node->node_revision);
1203   node->node_pool = pool;
1204
1205   svn_temp_deserializer__resolve(node, (void**)&node->created_path);
1206
1207   /* return result */
1208   *out = node;
1209
1210   return SVN_NO_ERROR;
1211 }
1212
1213 svn_error_t *
1214 svn_fs_fs__dag_open(dag_node_t **child_p,
1215                     dag_node_t *parent,
1216                     const char *name,
1217                     apr_pool_t *result_pool,
1218                     apr_pool_t *scratch_pool)
1219 {
1220   const svn_fs_id_t *node_id;
1221
1222   /* Ensure that NAME exists in PARENT's entry list. */
1223   SVN_ERR(dir_entry_id_from_node(&node_id, parent, name,
1224                                  scratch_pool, scratch_pool));
1225   if (! node_id)
1226     {
1227       *child_p = NULL;
1228       return SVN_NO_ERROR;
1229     }
1230
1231   /* Make sure that NAME is a single path component. */
1232   if (! svn_path_is_single_path_component(name))
1233     return svn_error_createf
1234       (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
1235        "Attempted to open node with an illegal name '%s'", name);
1236
1237   /* Now get the node that was requested. */
1238   return svn_fs_fs__dag_get_node(child_p, svn_fs_fs__dag_get_fs(parent),
1239                                  node_id, result_pool);
1240 }
1241
1242
1243 svn_error_t *
1244 svn_fs_fs__dag_copy(dag_node_t *to_node,
1245                     const char *entry,
1246                     dag_node_t *from_node,
1247                     svn_boolean_t preserve_history,
1248                     svn_revnum_t from_rev,
1249                     const char *from_path,
1250                     const svn_fs_fs__id_part_t *txn_id,
1251                     apr_pool_t *pool)
1252 {
1253   const svn_fs_id_t *id;
1254
1255   if (preserve_history)
1256     {
1257       node_revision_t *from_noderev, *to_noderev;
1258       svn_fs_fs__id_part_t copy_id;
1259       const svn_fs_id_t *src_id = svn_fs_fs__dag_get_id(from_node);
1260       svn_fs_t *fs = svn_fs_fs__dag_get_fs(from_node);
1261
1262       /* Make a copy of the original node revision. */
1263       SVN_ERR(get_node_revision(&from_noderev, from_node));
1264       to_noderev = copy_node_revision(from_noderev, pool);
1265
1266       /* Reserve a copy ID for this new copy. */
1267       SVN_ERR(svn_fs_fs__reserve_copy_id(&copy_id, fs, txn_id, pool));
1268
1269       /* Create a successor with its predecessor pointing at the copy
1270          source. */
1271       to_noderev->predecessor_id = svn_fs_fs__id_copy(src_id, pool);
1272       if (to_noderev->predecessor_count != -1)
1273         to_noderev->predecessor_count++;
1274       to_noderev->created_path =
1275         svn_fspath__join(svn_fs_fs__dag_get_created_path(to_node), entry,
1276                      pool);
1277       to_noderev->copyfrom_path = apr_pstrdup(pool, from_path);
1278       to_noderev->copyfrom_rev = from_rev;
1279
1280       /* Set the copyroot equal to our own id. */
1281       to_noderev->copyroot_path = NULL;
1282
1283       SVN_ERR(svn_fs_fs__create_successor(&id, fs, src_id, to_noderev,
1284                                           &copy_id, txn_id, pool));
1285
1286     }
1287   else  /* don't preserve history */
1288     {
1289       id = svn_fs_fs__dag_get_id(from_node);
1290     }
1291
1292   /* Set the entry in to_node to the new id. */
1293   return svn_fs_fs__dag_set_entry(to_node, entry, id, from_node->kind,
1294                                   txn_id, pool);
1295 }
1296
1297
1298 \f
1299 /*** Comparison. ***/
1300
1301 svn_error_t *
1302 svn_fs_fs__dag_things_different(svn_boolean_t *props_changed,
1303                                 svn_boolean_t *contents_changed,
1304                                 dag_node_t *node1,
1305                                 dag_node_t *node2,
1306                                 svn_boolean_t strict,
1307                                 apr_pool_t *pool)
1308 {
1309   node_revision_t *noderev1, *noderev2;
1310
1311   /* If we have no place to store our results, don't bother doing
1312      anything. */
1313   if (! props_changed && ! contents_changed)
1314     return SVN_NO_ERROR;
1315
1316   /* The node revision skels for these two nodes. */
1317   SVN_ERR(get_node_revision(&noderev1, node1));
1318   SVN_ERR(get_node_revision(&noderev2, node2));
1319
1320   if (strict)
1321     {
1322       /* In strict mode, compare text and property representations in the
1323          svn_fs_contents_different() / svn_fs_props_different() manner.
1324
1325          See the "No-op changes no longer dumped by 'svnadmin dump' in 1.9"
1326          discussion (http://svn.haxx.se/dev/archive-2015-09/0269.shtml) and
1327          issue #4598 (https://issues.apache.org/jira/browse/SVN-4598). */
1328       svn_fs_t *fs = svn_fs_fs__dag_get_fs(node1);
1329       svn_boolean_t same;
1330
1331       /* Compare property keys. */
1332       if (props_changed != NULL)
1333         {
1334           SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, noderev1,
1335                                             noderev2, pool));
1336           *props_changed = !same;
1337         }
1338
1339       /* Compare contents keys. */
1340       if (contents_changed != NULL)
1341         {
1342           SVN_ERR(svn_fs_fs__file_text_rep_equal(&same, fs, noderev1,
1343                                                  noderev2, pool));
1344           *contents_changed = !same;
1345         }
1346     }
1347   else
1348     {
1349       /* Otherwise, compare representation keys -- as in Subversion 1.8. */
1350
1351       /* Compare property keys. */
1352       if (props_changed != NULL)
1353         *props_changed =
1354           !svn_fs_fs__noderev_same_rep_key(noderev1->prop_rep,
1355                                            noderev2->prop_rep);
1356
1357       /* Compare contents keys. */
1358       if (contents_changed != NULL)
1359         *contents_changed =
1360           !svn_fs_fs__noderev_same_rep_key(noderev1->data_rep,
1361                                            noderev2->data_rep);
1362     }
1363
1364   return SVN_NO_ERROR;
1365 }
1366
1367 svn_error_t *
1368 svn_fs_fs__dag_get_copyroot(svn_revnum_t *rev,
1369                             const char **path,
1370                             dag_node_t *node)
1371 {
1372   node_revision_t *noderev;
1373
1374   /* Go get a fresh node-revision for NODE. */
1375   SVN_ERR(get_node_revision(&noderev, node));
1376
1377   *rev = noderev->copyroot_rev;
1378   *path = noderev->copyroot_path;
1379
1380   return SVN_NO_ERROR;
1381 }
1382
1383 svn_error_t *
1384 svn_fs_fs__dag_get_copyfrom_rev(svn_revnum_t *rev,
1385                                 dag_node_t *node)
1386 {
1387   node_revision_t *noderev;
1388
1389   /* Go get a fresh node-revision for NODE. */
1390   SVN_ERR(get_node_revision(&noderev, node));
1391
1392   *rev = noderev->copyfrom_rev;
1393
1394   return SVN_NO_ERROR;
1395 }
1396
1397 svn_error_t *
1398 svn_fs_fs__dag_get_copyfrom_path(const char **path,
1399                                  dag_node_t *node)
1400 {
1401   node_revision_t *noderev;
1402
1403   /* Go get a fresh node-revision for NODE. */
1404   SVN_ERR(get_node_revision(&noderev, node));
1405
1406   *path = noderev->copyfrom_path;
1407
1408   return SVN_NO_ERROR;
1409 }
1410
1411 svn_error_t *
1412 svn_fs_fs__dag_update_ancestry(dag_node_t *target,
1413                                dag_node_t *source,
1414                                apr_pool_t *pool)
1415 {
1416   node_revision_t *source_noderev, *target_noderev;
1417
1418   if (! svn_fs_fs__dag_check_mutable(target))
1419     return svn_error_createf
1420       (SVN_ERR_FS_NOT_MUTABLE, NULL,
1421        _("Attempted to update ancestry of non-mutable node"));
1422
1423   SVN_ERR(get_node_revision(&source_noderev, source));
1424   SVN_ERR(get_node_revision(&target_noderev, target));
1425
1426   target_noderev->predecessor_id = source->id;
1427   target_noderev->predecessor_count = source_noderev->predecessor_count;
1428   if (target_noderev->predecessor_count != -1)
1429     target_noderev->predecessor_count++;
1430
1431   return svn_fs_fs__put_node_revision(target->fs, target->id, target_noderev,
1432                                       FALSE, pool);
1433 }