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