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 
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 
83 /* Trivial helper/accessor functions. */
svn_fs_fs__dag_node_kind(dag_node_t * node)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 *
svn_fs_fs__dag_get_id(const dag_node_t * node)91 svn_fs_fs__dag_get_id(const dag_node_t *node)
92 {
93   return node->id;
94 }
95 
96 
97 const char *
svn_fs_fs__dag_get_created_path(dag_node_t * node)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 *
svn_fs_fs__dag_get_fs(dag_node_t * node)105 svn_fs_fs__dag_get_fs(dag_node_t *node)
106 {
107   return node->fs;
108 }
109 
110 void
svn_fs_fs__dag_set_fs(dag_node_t * node,svn_fs_t * fs)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 *
copy_node_revision(node_revision_t * noderev,apr_pool_t * pool)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 *
get_node_revision(node_revision_t ** noderev_p,dag_node_t * node)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 
svn_fs_fs__dag_check_mutable(const dag_node_t * node)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 *
svn_fs_fs__dag_get_node(dag_node_t ** node,svn_fs_t * fs,const svn_fs_id_t * id,apr_pool_t * pool)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 *
svn_fs_fs__dag_get_revision(svn_revnum_t * rev,dag_node_t * node,apr_pool_t * pool)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 *
svn_fs_fs__dag_get_predecessor_id(const svn_fs_id_t ** id_p,dag_node_t * node)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 *
svn_fs_fs__dag_get_predecessor_count(int * count,dag_node_t * node)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 *
svn_fs_fs__dag_get_mergeinfo_count(apr_int64_t * count,dag_node_t * node)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 *
svn_fs_fs__dag_has_mergeinfo(svn_boolean_t * has_mergeinfo,dag_node_t * node)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 *
svn_fs_fs__dag_has_descendants_with_mergeinfo(svn_boolean_t * do_they,dag_node_t * node)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 
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 *
dir_entry_id_from_node(const svn_fs_id_t ** id_p,dag_node_t * parent,const char * name,apr_pool_t * result_pool,apr_pool_t * scratch_pool)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 *
set_entry(dag_node_t * parent,const char * name,const svn_fs_id_t * id,svn_node_kind_t kind,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)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 *
make_entry(dag_node_t ** child_p,dag_node_t * parent,const char * parent_path,const char * name,svn_boolean_t is_dir,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)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 *
svn_fs_fs__dag_dir_entries(apr_array_header_t ** entries,dag_node_t * node,apr_pool_t * pool)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 *
svn_fs_fs__dag_dir_entry(svn_fs_dirent_t ** dirent,dag_node_t * node,const char * name,apr_pool_t * result_pool,apr_pool_t * scratch_pool)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 *
svn_fs_fs__dag_set_entry(dag_node_t * node,const char * entry_name,const svn_fs_id_t * id,svn_node_kind_t kind,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)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 
481 /*** Proplists. ***/
482 
483 svn_error_t *
svn_fs_fs__dag_get_proplist(apr_hash_t ** proplist_p,dag_node_t * node,apr_pool_t * pool)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 *
svn_fs_fs__dag_has_props(svn_boolean_t * has_props,dag_node_t * node,apr_pool_t * scratch_pool)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     }
532 
533   return SVN_NO_ERROR;
534 }
535 
536 svn_error_t *
svn_fs_fs__dag_set_proplist(dag_node_t * node,apr_hash_t * proplist,apr_pool_t * pool)537 svn_fs_fs__dag_set_proplist(dag_node_t *node,
538                             apr_hash_t *proplist,
539                             apr_pool_t *pool)
540 {
541   node_revision_t *noderev;
542 
543   /* Sanity check: this node better be mutable! */
544   if (! svn_fs_fs__dag_check_mutable(node))
545     {
546       svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
547       return svn_error_createf
548         (SVN_ERR_FS_NOT_MUTABLE, NULL,
549          "Can't set proplist on *immutable* node-revision %s",
550          idstr->data);
551     }
552 
553   /* Go get a fresh NODE-REVISION for this node. */
554   SVN_ERR(get_node_revision(&noderev, node));
555 
556   /* Set the new proplist. */
557   return svn_fs_fs__set_proplist(node->fs, noderev, proplist, pool);
558 }
559 
560 
561 svn_error_t *
svn_fs_fs__dag_increment_mergeinfo_count(dag_node_t * node,apr_int64_t increment,apr_pool_t * pool)562 svn_fs_fs__dag_increment_mergeinfo_count(dag_node_t *node,
563                                          apr_int64_t increment,
564                                          apr_pool_t *pool)
565 {
566   node_revision_t *noderev;
567 
568   /* Sanity check: this node better be mutable! */
569   if (! svn_fs_fs__dag_check_mutable(node))
570     {
571       svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
572       return svn_error_createf
573         (SVN_ERR_FS_NOT_MUTABLE, NULL,
574          "Can't increment mergeinfo count on *immutable* node-revision %s",
575          idstr->data);
576     }
577 
578   if (increment == 0)
579     return SVN_NO_ERROR;
580 
581   /* Go get a fresh NODE-REVISION for this node. */
582   SVN_ERR(get_node_revision(&noderev, node));
583 
584   noderev->mergeinfo_count += increment;
585   if (noderev->mergeinfo_count < 0)
586     {
587       svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
588       return svn_error_createf
589         (SVN_ERR_FS_CORRUPT, NULL,
590          apr_psprintf(pool,
591                       _("Can't increment mergeinfo count on node-revision %%s "
592                         "to negative value %%%s"),
593                       APR_INT64_T_FMT),
594          idstr->data, noderev->mergeinfo_count);
595     }
596   if (noderev->mergeinfo_count > 1 && noderev->kind == svn_node_file)
597     {
598       svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
599       return svn_error_createf
600         (SVN_ERR_FS_CORRUPT, NULL,
601          apr_psprintf(pool,
602                       _("Can't increment mergeinfo count on *file* "
603                         "node-revision %%s to %%%s (> 1)"),
604                       APR_INT64_T_FMT),
605          idstr->data, noderev->mergeinfo_count);
606     }
607 
608   /* Flush it out. */
609   return svn_fs_fs__put_node_revision(node->fs, noderev->id,
610                                       noderev, FALSE, pool);
611 }
612 
613 svn_error_t *
svn_fs_fs__dag_set_has_mergeinfo(dag_node_t * node,svn_boolean_t has_mergeinfo,apr_pool_t * pool)614 svn_fs_fs__dag_set_has_mergeinfo(dag_node_t *node,
615                                  svn_boolean_t has_mergeinfo,
616                                  apr_pool_t *pool)
617 {
618   node_revision_t *noderev;
619 
620   /* Sanity check: this node better be mutable! */
621   if (! svn_fs_fs__dag_check_mutable(node))
622     {
623       svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool);
624       return svn_error_createf
625         (SVN_ERR_FS_NOT_MUTABLE, NULL,
626          "Can't set mergeinfo flag on *immutable* node-revision %s",
627          idstr->data);
628     }
629 
630   /* Go get a fresh NODE-REVISION for this node. */
631   SVN_ERR(get_node_revision(&noderev, node));
632 
633   noderev->has_mergeinfo = has_mergeinfo;
634 
635   /* Flush it out. */
636   return svn_fs_fs__put_node_revision(node->fs, noderev->id,
637                                       noderev, FALSE, pool);
638 }
639 
640 
641 /*** Roots. ***/
642 
643 svn_error_t *
svn_fs_fs__dag_revision_root(dag_node_t ** node_p,svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)644 svn_fs_fs__dag_revision_root(dag_node_t **node_p,
645                              svn_fs_t *fs,
646                              svn_revnum_t rev,
647                              apr_pool_t *pool)
648 {
649   dag_node_t *new_node;
650 
651   /* Construct the node. */
652   new_node = apr_pcalloc(pool, sizeof(*new_node));
653   new_node->fs = fs;
654   SVN_ERR(svn_fs_fs__rev_get_root(&new_node->id, fs, rev, pool, pool));
655 
656   /* Grab the contents so we can inspect the node's kind and created path. */
657   new_node->node_pool = pool;
658 
659   /* Initialize the KIND and CREATED_PATH attributes */
660   new_node->kind = svn_node_dir;
661   new_node->created_path = "/";
662   new_node->fresh_root_predecessor_id = NULL;
663 
664   /* Return a fresh new node */
665   *node_p = new_node;
666   return SVN_NO_ERROR;
667 }
668 
669 
670 svn_error_t *
svn_fs_fs__dag_txn_root(dag_node_t ** node_p,svn_fs_t * fs,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)671 svn_fs_fs__dag_txn_root(dag_node_t **node_p,
672                         svn_fs_t *fs,
673                         const svn_fs_fs__id_part_t *txn_id,
674                         apr_pool_t *pool)
675 {
676   const svn_fs_id_t *root_id, *ignored;
677 
678   SVN_ERR(svn_fs_fs__get_txn_ids(&root_id, &ignored, fs, txn_id, pool));
679   return svn_fs_fs__dag_get_node(node_p, fs, root_id, pool);
680 }
681 
682 
683 svn_error_t *
svn_fs_fs__dag_txn_base_root(dag_node_t ** node_p,svn_fs_t * fs,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)684 svn_fs_fs__dag_txn_base_root(dag_node_t **node_p,
685                              svn_fs_t *fs,
686                              const svn_fs_fs__id_part_t *txn_id,
687                              apr_pool_t *pool)
688 {
689   const svn_fs_id_t *base_root_id, *ignored;
690 
691   SVN_ERR(svn_fs_fs__get_txn_ids(&ignored, &base_root_id, fs, txn_id, pool));
692   return svn_fs_fs__dag_get_node(node_p, fs, base_root_id, pool);
693 }
694 
695 
696 svn_error_t *
svn_fs_fs__dag_clone_child(dag_node_t ** child_p,dag_node_t * parent,const char * parent_path,const char * name,const svn_fs_fs__id_part_t * copy_id,const svn_fs_fs__id_part_t * txn_id,svn_boolean_t is_parent_copyroot,apr_pool_t * pool)697 svn_fs_fs__dag_clone_child(dag_node_t **child_p,
698                            dag_node_t *parent,
699                            const char *parent_path,
700                            const char *name,
701                            const svn_fs_fs__id_part_t *copy_id,
702                            const svn_fs_fs__id_part_t *txn_id,
703                            svn_boolean_t is_parent_copyroot,
704                            apr_pool_t *pool)
705 {
706   dag_node_t *cur_entry; /* parent's current entry named NAME */
707   const svn_fs_id_t *new_node_id; /* node id we'll put into NEW_NODE */
708   svn_fs_t *fs = svn_fs_fs__dag_get_fs(parent);
709   apr_pool_t *subpool = svn_pool_create(pool);
710 
711   /* First check that the parent is mutable. */
712   if (! svn_fs_fs__dag_check_mutable(parent))
713     return svn_error_createf
714       (SVN_ERR_FS_NOT_MUTABLE, NULL,
715        "Attempted to clone child of non-mutable node");
716 
717   /* Make sure that NAME is a single path component. */
718   if (! svn_path_is_single_path_component(name))
719     return svn_error_createf
720       (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
721        "Attempted to make a child clone with an illegal name '%s'", name);
722 
723   /* Find the node named NAME in PARENT's entries list if it exists. */
724   SVN_ERR(svn_fs_fs__dag_open(&cur_entry, parent, name, pool, subpool));
725   if (! cur_entry)
726     return svn_error_createf
727       (SVN_ERR_FS_NOT_FOUND, NULL,
728        "Attempted to open non-existent child node '%s'", name);
729 
730   /* Check for mutability in the node we found.  If it's mutable, we
731      don't need to clone it. */
732   if (svn_fs_fs__dag_check_mutable(cur_entry))
733     {
734       /* This has already been cloned */
735       new_node_id = cur_entry->id;
736     }
737   else
738     {
739       node_revision_t *noderev, *parent_noderev;
740 
741       /* Go get a fresh NODE-REVISION for current child node. */
742       SVN_ERR(get_node_revision(&noderev, cur_entry));
743 
744       if (is_parent_copyroot)
745         {
746           SVN_ERR(get_node_revision(&parent_noderev, parent));
747           noderev->copyroot_rev = parent_noderev->copyroot_rev;
748           noderev->copyroot_path = apr_pstrdup(pool,
749                                                parent_noderev->copyroot_path);
750         }
751 
752       noderev->copyfrom_path = NULL;
753       noderev->copyfrom_rev = SVN_INVALID_REVNUM;
754 
755       noderev->predecessor_id = svn_fs_fs__id_copy(cur_entry->id, pool);
756       noderev->predecessor_count++;
757       noderev->created_path = svn_fspath__join(parent_path, name, pool);
758 
759       SVN_ERR(svn_fs_fs__create_successor(&new_node_id, fs, cur_entry->id,
760                                           noderev, copy_id, txn_id, pool));
761 
762       /* Replace the ID in the parent's ENTRY list with the ID which
763          refers to the mutable clone of this child. */
764       SVN_ERR(set_entry(parent, name, new_node_id, noderev->kind, txn_id,
765                         pool));
766     }
767 
768   /* Initialize the youngster. */
769   svn_pool_destroy(subpool);
770   return svn_fs_fs__dag_get_node(child_p, fs, new_node_id, pool);
771 }
772 
773 
774 
775 svn_error_t *
svn_fs_fs__dag_clone_root(dag_node_t ** root_p,svn_fs_t * fs,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)776 svn_fs_fs__dag_clone_root(dag_node_t **root_p,
777                           svn_fs_t *fs,
778                           const svn_fs_fs__id_part_t *txn_id,
779                           apr_pool_t *pool)
780 {
781   const svn_fs_id_t *base_root_id, *root_id;
782 
783   /* Get the node ID's of the root directories of the transaction and
784      its base revision.  */
785   SVN_ERR(svn_fs_fs__get_txn_ids(&root_id, &base_root_id, fs, txn_id, pool));
786 
787   /* Oh, give me a clone...
788      (If they're the same, we haven't cloned the transaction's root
789      directory yet.)  */
790   SVN_ERR_ASSERT(!svn_fs_fs__id_eq(root_id, base_root_id));
791 
792   /*
793    * (Sung to the tune of "Home, Home on the Range", with thanks to
794    * Randall Garrett and Isaac Asimov.)
795    */
796 
797   /* One way or another, root_id now identifies a cloned root node. */
798   return svn_fs_fs__dag_get_node(root_p, fs, root_id, pool);
799 }
800 
801 
802 svn_error_t *
svn_fs_fs__dag_delete(dag_node_t * parent,const char * name,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)803 svn_fs_fs__dag_delete(dag_node_t *parent,
804                       const char *name,
805                       const svn_fs_fs__id_part_t *txn_id,
806                       apr_pool_t *pool)
807 {
808   node_revision_t *parent_noderev;
809   svn_fs_t *fs = parent->fs;
810   svn_fs_dirent_t *dirent;
811   svn_fs_id_t *id;
812   apr_pool_t *subpool;
813 
814   /* Make sure parent is a directory. */
815   if (parent->kind != svn_node_dir)
816     return svn_error_createf
817       (SVN_ERR_FS_NOT_DIRECTORY, NULL,
818        "Attempted to delete entry '%s' from *non*-directory node", name);
819 
820   /* Make sure parent is mutable. */
821   if (! svn_fs_fs__dag_check_mutable(parent))
822     return svn_error_createf
823       (SVN_ERR_FS_NOT_MUTABLE, NULL,
824        "Attempted to delete entry '%s' from immutable directory node", name);
825 
826   /* Make sure that NAME is a single path component. */
827   if (! svn_path_is_single_path_component(name))
828     return svn_error_createf
829       (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
830        "Attempted to delete a node with an illegal name '%s'", name);
831 
832   /* Get a fresh NODE-REVISION for the parent node. */
833   SVN_ERR(get_node_revision(&parent_noderev, parent));
834 
835   subpool = svn_pool_create(pool);
836 
837   /* Search this directory for a dirent with that NAME. */
838   SVN_ERR(svn_fs_fs__rep_contents_dir_entry(&dirent, fs, parent_noderev,
839                                             name, subpool, subpool));
840 
841   /* If we never found ID in ENTRIES (perhaps because there are no
842      ENTRIES, perhaps because ID just isn't in the existing ENTRIES
843      ... it doesn't matter), return an error.  */
844   if (! dirent)
845     return svn_error_createf
846       (SVN_ERR_FS_NO_SUCH_ENTRY, NULL,
847        "Delete failed--directory has no entry '%s'", name);
848 
849   /* Copy the ID out of the subpool and release the rest of the
850      directory listing. */
851   id = svn_fs_fs__id_copy(dirent->id, pool);
852   svn_pool_destroy(subpool);
853 
854   /* If mutable, remove it and any mutable children from db. */
855   SVN_ERR(svn_fs_fs__dag_delete_if_mutable(parent->fs, id, pool));
856 
857   /* Remove this entry from its parent's entries list. */
858   return svn_fs_fs__set_entry(parent->fs, txn_id, parent_noderev, name,
859                               NULL, svn_node_unknown, pool);
860 }
861 
862 
863 svn_error_t *
svn_fs_fs__dag_remove_node(svn_fs_t * fs,const svn_fs_id_t * id,apr_pool_t * pool)864 svn_fs_fs__dag_remove_node(svn_fs_t *fs,
865                            const svn_fs_id_t *id,
866                            apr_pool_t *pool)
867 {
868   dag_node_t *node;
869 
870   /* Fetch the node. */
871   SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, id, pool));
872 
873   /* If immutable, do nothing and return immediately. */
874   if (! svn_fs_fs__dag_check_mutable(node))
875     return svn_error_createf(SVN_ERR_FS_NOT_MUTABLE, NULL,
876                              "Attempted removal of immutable node");
877 
878   /* Delete the node revision. */
879   return svn_fs_fs__delete_node_revision(fs, id, pool);
880 }
881 
882 
883 svn_error_t *
svn_fs_fs__dag_delete_if_mutable(svn_fs_t * fs,const svn_fs_id_t * id,apr_pool_t * pool)884 svn_fs_fs__dag_delete_if_mutable(svn_fs_t *fs,
885                                  const svn_fs_id_t *id,
886                                  apr_pool_t *pool)
887 {
888   dag_node_t *node;
889 
890   /* Get the node. */
891   SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, id, pool));
892 
893   /* If immutable, do nothing and return immediately. */
894   if (! svn_fs_fs__dag_check_mutable(node))
895     return SVN_NO_ERROR;
896 
897   /* Else it's mutable.  Recurse on directories... */
898   if (node->kind == svn_node_dir)
899     {
900       apr_array_header_t *entries;
901       int i;
902       apr_pool_t *iterpool = svn_pool_create(pool);
903 
904       /* Loop over directory entries */
905       SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
906       if (entries)
907         for (i = 0; i < entries->nelts; ++i)
908           {
909             svn_pool_clear(iterpool);
910             SVN_ERR(svn_fs_fs__dag_delete_if_mutable(fs,
911                           APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *)->id,
912                           iterpool));
913           }
914 
915       svn_pool_destroy(iterpool);
916     }
917 
918   /* ... then delete the node itself, after deleting any mutable
919      representations and strings it points to. */
920   return svn_fs_fs__dag_remove_node(fs, id, pool);
921 }
922 
923 svn_error_t *
svn_fs_fs__dag_make_file(dag_node_t ** child_p,dag_node_t * parent,const char * parent_path,const char * name,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)924 svn_fs_fs__dag_make_file(dag_node_t **child_p,
925                          dag_node_t *parent,
926                          const char *parent_path,
927                          const char *name,
928                          const svn_fs_fs__id_part_t *txn_id,
929                          apr_pool_t *pool)
930 {
931   /* Call our little helper function */
932   return make_entry(child_p, parent, parent_path, name, FALSE, txn_id, pool);
933 }
934 
935 
936 svn_error_t *
svn_fs_fs__dag_make_dir(dag_node_t ** child_p,dag_node_t * parent,const char * parent_path,const char * name,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)937 svn_fs_fs__dag_make_dir(dag_node_t **child_p,
938                         dag_node_t *parent,
939                         const char *parent_path,
940                         const char *name,
941                         const svn_fs_fs__id_part_t *txn_id,
942                         apr_pool_t *pool)
943 {
944   /* Call our little helper function */
945   return make_entry(child_p, parent, parent_path, name, TRUE, txn_id, pool);
946 }
947 
948 
949 svn_error_t *
svn_fs_fs__dag_get_contents(svn_stream_t ** contents_p,dag_node_t * file,apr_pool_t * pool)950 svn_fs_fs__dag_get_contents(svn_stream_t **contents_p,
951                             dag_node_t *file,
952                             apr_pool_t *pool)
953 {
954   node_revision_t *noderev;
955   svn_stream_t *contents;
956 
957   /* Make sure our node is a file. */
958   if (file->kind != svn_node_file)
959     return svn_error_createf
960       (SVN_ERR_FS_NOT_FILE, NULL,
961        "Attempted to get textual contents of a *non*-file node");
962 
963   /* Go get a fresh node-revision for FILE. */
964   SVN_ERR(get_node_revision(&noderev, file));
965 
966   /* Get a stream to the contents. */
967   SVN_ERR(svn_fs_fs__get_contents(&contents, file->fs,
968                                   noderev->data_rep, TRUE, pool));
969 
970   *contents_p = contents;
971 
972   return SVN_NO_ERROR;
973 }
974 
975 
976 svn_error_t *
svn_fs_fs__dag_get_file_delta_stream(svn_txdelta_stream_t ** stream_p,dag_node_t * source,dag_node_t * target,apr_pool_t * pool)977 svn_fs_fs__dag_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
978                                      dag_node_t *source,
979                                      dag_node_t *target,
980                                      apr_pool_t *pool)
981 {
982   node_revision_t *src_noderev;
983   node_revision_t *tgt_noderev;
984 
985   /* Make sure our nodes are files. */
986   if ((source && source->kind != svn_node_file)
987       || target->kind != svn_node_file)
988     return svn_error_createf
989       (SVN_ERR_FS_NOT_FILE, NULL,
990        "Attempted to get textual contents of a *non*-file node");
991 
992   /* Go get fresh node-revisions for the nodes. */
993   if (source)
994     SVN_ERR(get_node_revision(&src_noderev, source));
995   else
996     src_noderev = NULL;
997   SVN_ERR(get_node_revision(&tgt_noderev, target));
998 
999   /* Get the delta stream. */
1000   return svn_fs_fs__get_file_delta_stream(stream_p, target->fs,
1001                                           src_noderev, tgt_noderev, pool);
1002 }
1003 
1004 
1005 svn_error_t *
svn_fs_fs__dag_try_process_file_contents(svn_boolean_t * success,dag_node_t * node,svn_fs_process_contents_func_t processor,void * baton,apr_pool_t * pool)1006 svn_fs_fs__dag_try_process_file_contents(svn_boolean_t *success,
1007                                          dag_node_t *node,
1008                                          svn_fs_process_contents_func_t processor,
1009                                          void* baton,
1010                                          apr_pool_t *pool)
1011 {
1012   node_revision_t *noderev;
1013 
1014   /* Go get fresh node-revisions for the nodes. */
1015   SVN_ERR(get_node_revision(&noderev, node));
1016 
1017   return svn_fs_fs__try_process_file_contents(success, node->fs,
1018                                               noderev,
1019                                               processor, baton, pool);
1020 }
1021 
1022 
1023 svn_error_t *
svn_fs_fs__dag_file_length(svn_filesize_t * length,dag_node_t * file,apr_pool_t * pool)1024 svn_fs_fs__dag_file_length(svn_filesize_t *length,
1025                            dag_node_t *file,
1026                            apr_pool_t *pool)
1027 {
1028   node_revision_t *noderev;
1029 
1030   /* Make sure our node is a file. */
1031   if (file->kind != svn_node_file)
1032     return svn_error_createf
1033       (SVN_ERR_FS_NOT_FILE, NULL,
1034        "Attempted to get length of a *non*-file node");
1035 
1036   /* Go get a fresh node-revision for FILE, and . */
1037   SVN_ERR(get_node_revision(&noderev, file));
1038 
1039   return svn_fs_fs__file_length(length, noderev, pool);
1040 }
1041 
1042 
1043 svn_error_t *
svn_fs_fs__dag_file_checksum(svn_checksum_t ** checksum,dag_node_t * file,svn_checksum_kind_t kind,apr_pool_t * pool)1044 svn_fs_fs__dag_file_checksum(svn_checksum_t **checksum,
1045                              dag_node_t *file,
1046                              svn_checksum_kind_t kind,
1047                              apr_pool_t *pool)
1048 {
1049   node_revision_t *noderev;
1050 
1051   if (file->kind != svn_node_file)
1052     return svn_error_createf
1053       (SVN_ERR_FS_NOT_FILE, NULL,
1054        "Attempted to get checksum of a *non*-file node");
1055 
1056   SVN_ERR(get_node_revision(&noderev, file));
1057 
1058   return svn_fs_fs__file_checksum(checksum, noderev, kind, pool);
1059 }
1060 
1061 
1062 svn_error_t *
svn_fs_fs__dag_get_edit_stream(svn_stream_t ** contents,dag_node_t * file,apr_pool_t * pool)1063 svn_fs_fs__dag_get_edit_stream(svn_stream_t **contents,
1064                                dag_node_t *file,
1065                                apr_pool_t *pool)
1066 {
1067   node_revision_t *noderev;
1068   svn_stream_t *ws;
1069 
1070   /* Make sure our node is a file. */
1071   if (file->kind != svn_node_file)
1072     return svn_error_createf
1073       (SVN_ERR_FS_NOT_FILE, NULL,
1074        "Attempted to set textual contents of a *non*-file node");
1075 
1076   /* Make sure our node is mutable. */
1077   if (! svn_fs_fs__dag_check_mutable(file))
1078     return svn_error_createf
1079       (SVN_ERR_FS_NOT_MUTABLE, NULL,
1080        "Attempted to set textual contents of an immutable node");
1081 
1082   /* Get the node revision. */
1083   SVN_ERR(get_node_revision(&noderev, file));
1084 
1085   SVN_ERR(svn_fs_fs__set_contents(&ws, file->fs, noderev, pool));
1086 
1087   *contents = ws;
1088 
1089   return SVN_NO_ERROR;
1090 }
1091 
1092 
1093 
1094 svn_error_t *
svn_fs_fs__dag_finalize_edits(dag_node_t * file,const svn_checksum_t * checksum,apr_pool_t * pool)1095 svn_fs_fs__dag_finalize_edits(dag_node_t *file,
1096                               const svn_checksum_t *checksum,
1097                               apr_pool_t *pool)
1098 {
1099   if (checksum)
1100     {
1101       svn_checksum_t *file_checksum;
1102 
1103       SVN_ERR(svn_fs_fs__dag_file_checksum(&file_checksum, file,
1104                                            checksum->kind, pool));
1105       if (!svn_checksum_match(checksum, file_checksum))
1106         return svn_checksum_mismatch_err(checksum, file_checksum, pool,
1107                                          _("Checksum mismatch for '%s'"),
1108                                          file->created_path);
1109     }
1110 
1111   return SVN_NO_ERROR;
1112 }
1113 
1114 
1115 dag_node_t *
svn_fs_fs__dag_dup(const dag_node_t * node,apr_pool_t * pool)1116 svn_fs_fs__dag_dup(const dag_node_t *node,
1117                    apr_pool_t *pool)
1118 {
1119   /* Allocate our new node. */
1120   dag_node_t *new_node = apr_pcalloc(pool, sizeof(*new_node));
1121 
1122   new_node->fs = node->fs;
1123   new_node->id = svn_fs_fs__id_copy(node->id, pool);
1124   new_node->kind = node->kind;
1125   new_node->created_path = apr_pstrdup(pool, node->created_path);
1126 
1127   /* Only copy cached node_revision_t for immutable nodes. */
1128   if (node->node_revision && !svn_fs_fs__dag_check_mutable(node))
1129     {
1130       new_node->node_revision = copy_node_revision(node->node_revision, pool);
1131       new_node->node_revision->id =
1132           svn_fs_fs__id_copy(node->node_revision->id, pool);
1133       new_node->node_revision->is_fresh_txn_root =
1134           node->node_revision->is_fresh_txn_root;
1135     }
1136   new_node->node_pool = pool;
1137 
1138   return new_node;
1139 }
1140 
1141 svn_error_t *
svn_fs_fs__dag_serialize(void ** data,apr_size_t * data_len,void * in,apr_pool_t * pool)1142 svn_fs_fs__dag_serialize(void **data,
1143                          apr_size_t *data_len,
1144                          void *in,
1145                          apr_pool_t *pool)
1146 {
1147   dag_node_t *node = in;
1148   svn_stringbuf_t *serialized;
1149 
1150   /* create an serialization context and serialize the dag node as root */
1151   svn_temp_serializer__context_t *context =
1152       svn_temp_serializer__init(node,
1153                                 sizeof(*node),
1154                                 1024 - SVN_TEMP_SERIALIZER__OVERHEAD,
1155                                 pool);
1156 
1157   /* for mutable nodes, we will _never_ cache the noderev */
1158   if (node->node_revision && !svn_fs_fs__dag_check_mutable(node))
1159     svn_fs_fs__noderev_serialize(context, &node->node_revision);
1160   else
1161     svn_temp_serializer__set_null(context,
1162                                   (const void * const *)&node->node_revision);
1163 
1164   /* The deserializer will use its own pool. */
1165   svn_temp_serializer__set_null(context,
1166                                 (const void * const *)&node->node_pool);
1167 
1168   /* serialize other sub-structures */
1169   svn_fs_fs__id_serialize(context, (const svn_fs_id_t *const *)&node->id);
1170   svn_fs_fs__id_serialize(context, &node->fresh_root_predecessor_id);
1171   svn_temp_serializer__add_string(context, &node->created_path);
1172 
1173   /* return serialized data */
1174   serialized = svn_temp_serializer__get(context);
1175   *data = serialized->data;
1176   *data_len = serialized->len;
1177 
1178   return SVN_NO_ERROR;
1179 }
1180 
1181 svn_error_t *
svn_fs_fs__dag_deserialize(void ** out,void * data,apr_size_t data_len,apr_pool_t * pool)1182 svn_fs_fs__dag_deserialize(void **out,
1183                            void *data,
1184                            apr_size_t data_len,
1185                            apr_pool_t *pool)
1186 {
1187   dag_node_t *node = (dag_node_t *)data;
1188   if (data_len == 0)
1189     return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
1190                             _("Empty noderev in cache"));
1191 
1192   /* Copy the _full_ buffer as it also contains the sub-structures. */
1193   node->fs = NULL;
1194 
1195   /* fixup all references to sub-structures */
1196   svn_fs_fs__id_deserialize(node, &node->id);
1197   svn_fs_fs__id_deserialize(node,
1198                             (svn_fs_id_t **)&node->fresh_root_predecessor_id);
1199   svn_fs_fs__noderev_deserialize(node, &node->node_revision);
1200   node->node_pool = pool;
1201 
1202   svn_temp_deserializer__resolve(node, (void**)&node->created_path);
1203 
1204   /* return result */
1205   *out = node;
1206 
1207   return SVN_NO_ERROR;
1208 }
1209 
1210 svn_error_t *
svn_fs_fs__dag_open(dag_node_t ** child_p,dag_node_t * parent,const char * name,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1211 svn_fs_fs__dag_open(dag_node_t **child_p,
1212                     dag_node_t *parent,
1213                     const char *name,
1214                     apr_pool_t *result_pool,
1215                     apr_pool_t *scratch_pool)
1216 {
1217   const svn_fs_id_t *node_id;
1218 
1219   /* Ensure that NAME exists in PARENT's entry list. */
1220   SVN_ERR(dir_entry_id_from_node(&node_id, parent, name,
1221                                  scratch_pool, scratch_pool));
1222   if (! node_id)
1223     {
1224       *child_p = NULL;
1225       return SVN_NO_ERROR;
1226     }
1227 
1228   /* Make sure that NAME is a single path component. */
1229   if (! svn_path_is_single_path_component(name))
1230     return svn_error_createf
1231       (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
1232        "Attempted to open node with an illegal name '%s'", name);
1233 
1234   /* Now get the node that was requested. */
1235   return svn_fs_fs__dag_get_node(child_p, svn_fs_fs__dag_get_fs(parent),
1236                                  node_id, result_pool);
1237 }
1238 
1239 
1240 svn_error_t *
svn_fs_fs__dag_copy(dag_node_t * to_node,const char * entry,dag_node_t * from_node,svn_boolean_t preserve_history,svn_revnum_t from_rev,const char * from_path,const svn_fs_fs__id_part_t * txn_id,apr_pool_t * pool)1241 svn_fs_fs__dag_copy(dag_node_t *to_node,
1242                     const char *entry,
1243                     dag_node_t *from_node,
1244                     svn_boolean_t preserve_history,
1245                     svn_revnum_t from_rev,
1246                     const char *from_path,
1247                     const svn_fs_fs__id_part_t *txn_id,
1248                     apr_pool_t *pool)
1249 {
1250   const svn_fs_id_t *id;
1251 
1252   if (preserve_history)
1253     {
1254       node_revision_t *from_noderev, *to_noderev;
1255       svn_fs_fs__id_part_t copy_id;
1256       const svn_fs_id_t *src_id = svn_fs_fs__dag_get_id(from_node);
1257       svn_fs_t *fs = svn_fs_fs__dag_get_fs(from_node);
1258 
1259       /* Make a copy of the original node revision. */
1260       SVN_ERR(get_node_revision(&from_noderev, from_node));
1261       to_noderev = copy_node_revision(from_noderev, pool);
1262 
1263       /* Reserve a copy ID for this new copy. */
1264       SVN_ERR(svn_fs_fs__reserve_copy_id(&copy_id, fs, txn_id, pool));
1265 
1266       /* Create a successor with its predecessor pointing at the copy
1267          source. */
1268       to_noderev->predecessor_id = svn_fs_fs__id_copy(src_id, pool);
1269       to_noderev->predecessor_count++;
1270       to_noderev->created_path =
1271         svn_fspath__join(svn_fs_fs__dag_get_created_path(to_node), entry,
1272                      pool);
1273       to_noderev->copyfrom_path = apr_pstrdup(pool, from_path);
1274       to_noderev->copyfrom_rev = from_rev;
1275 
1276       /* Set the copyroot equal to our own id. */
1277       to_noderev->copyroot_path = NULL;
1278 
1279       SVN_ERR(svn_fs_fs__create_successor(&id, fs, src_id, to_noderev,
1280                                           &copy_id, txn_id, pool));
1281 
1282     }
1283   else  /* don't preserve history */
1284     {
1285       id = svn_fs_fs__dag_get_id(from_node);
1286     }
1287 
1288   /* Set the entry in to_node to the new id. */
1289   return svn_fs_fs__dag_set_entry(to_node, entry, id, from_node->kind,
1290                                   txn_id, pool);
1291 }
1292 
1293 
1294 
1295 /*** Comparison. ***/
1296 
1297 svn_error_t *
svn_fs_fs__dag_things_different(svn_boolean_t * props_changed,svn_boolean_t * contents_changed,dag_node_t * node1,dag_node_t * node2,svn_boolean_t strict,apr_pool_t * pool)1298 svn_fs_fs__dag_things_different(svn_boolean_t *props_changed,
1299                                 svn_boolean_t *contents_changed,
1300                                 dag_node_t *node1,
1301                                 dag_node_t *node2,
1302                                 svn_boolean_t strict,
1303                                 apr_pool_t *pool)
1304 {
1305   node_revision_t *noderev1, *noderev2;
1306 
1307   /* If we have no place to store our results, don't bother doing
1308      anything. */
1309   if (! props_changed && ! contents_changed)
1310     return SVN_NO_ERROR;
1311 
1312   /* The node revision skels for these two nodes. */
1313   SVN_ERR(get_node_revision(&noderev1, node1));
1314   SVN_ERR(get_node_revision(&noderev2, node2));
1315 
1316   if (strict)
1317     {
1318       /* In strict mode, compare text and property representations in the
1319          svn_fs_contents_different() / svn_fs_props_different() manner.
1320 
1321          See the "No-op changes no longer dumped by 'svnadmin dump' in 1.9"
1322          discussion (http://svn.haxx.se/dev/archive-2015-09/0269.shtml) and
1323          issue #4598 (https://issues.apache.org/jira/browse/SVN-4598). */
1324       svn_fs_t *fs = svn_fs_fs__dag_get_fs(node1);
1325       svn_boolean_t same;
1326 
1327       /* Compare property keys. */
1328       if (props_changed != NULL)
1329         {
1330           SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, noderev1,
1331                                             noderev2, pool));
1332           *props_changed = !same;
1333         }
1334 
1335       /* Compare contents keys. */
1336       if (contents_changed != NULL)
1337         {
1338           SVN_ERR(svn_fs_fs__file_text_rep_equal(&same, fs, noderev1,
1339                                                  noderev2, pool));
1340           *contents_changed = !same;
1341         }
1342     }
1343   else
1344     {
1345       /* Otherwise, compare representation keys -- as in Subversion 1.8. */
1346 
1347       /* Compare property keys. */
1348       if (props_changed != NULL)
1349         *props_changed =
1350           !svn_fs_fs__noderev_same_rep_key(noderev1->prop_rep,
1351                                            noderev2->prop_rep);
1352 
1353       /* Compare contents keys. */
1354       if (contents_changed != NULL)
1355         *contents_changed =
1356           !svn_fs_fs__noderev_same_rep_key(noderev1->data_rep,
1357                                            noderev2->data_rep);
1358     }
1359 
1360   return SVN_NO_ERROR;
1361 }
1362 
1363 svn_error_t *
svn_fs_fs__dag_get_copyroot(svn_revnum_t * rev,const char ** path,dag_node_t * node)1364 svn_fs_fs__dag_get_copyroot(svn_revnum_t *rev,
1365                             const char **path,
1366                             dag_node_t *node)
1367 {
1368   node_revision_t *noderev;
1369 
1370   /* Go get a fresh node-revision for NODE. */
1371   SVN_ERR(get_node_revision(&noderev, node));
1372 
1373   *rev = noderev->copyroot_rev;
1374   *path = noderev->copyroot_path;
1375 
1376   return SVN_NO_ERROR;
1377 }
1378 
1379 svn_error_t *
svn_fs_fs__dag_get_copyfrom_rev(svn_revnum_t * rev,dag_node_t * node)1380 svn_fs_fs__dag_get_copyfrom_rev(svn_revnum_t *rev,
1381                                 dag_node_t *node)
1382 {
1383   node_revision_t *noderev;
1384 
1385   /* Go get a fresh node-revision for NODE. */
1386   SVN_ERR(get_node_revision(&noderev, node));
1387 
1388   *rev = noderev->copyfrom_rev;
1389 
1390   return SVN_NO_ERROR;
1391 }
1392 
1393 svn_error_t *
svn_fs_fs__dag_get_copyfrom_path(const char ** path,dag_node_t * node)1394 svn_fs_fs__dag_get_copyfrom_path(const char **path,
1395                                  dag_node_t *node)
1396 {
1397   node_revision_t *noderev;
1398 
1399   /* Go get a fresh node-revision for NODE. */
1400   SVN_ERR(get_node_revision(&noderev, node));
1401 
1402   *path = noderev->copyfrom_path;
1403 
1404   return SVN_NO_ERROR;
1405 }
1406 
1407 svn_error_t *
svn_fs_fs__dag_update_ancestry(dag_node_t * target,dag_node_t * source,apr_pool_t * pool)1408 svn_fs_fs__dag_update_ancestry(dag_node_t *target,
1409                                dag_node_t *source,
1410                                apr_pool_t *pool)
1411 {
1412   node_revision_t *source_noderev, *target_noderev;
1413 
1414   if (! svn_fs_fs__dag_check_mutable(target))
1415     return svn_error_createf
1416       (SVN_ERR_FS_NOT_MUTABLE, NULL,
1417        _("Attempted to update ancestry of non-mutable node"));
1418 
1419   SVN_ERR(get_node_revision(&source_noderev, source));
1420   SVN_ERR(get_node_revision(&target_noderev, target));
1421 
1422   target_noderev->predecessor_id = source->id;
1423   target_noderev->predecessor_count = source_noderev->predecessor_count;
1424   target_noderev->predecessor_count++;
1425 
1426   return svn_fs_fs__put_node_revision(target->fs, target->id, target_noderev,
1427                                       FALSE, pool);
1428 }
1429