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
45/* Initializing a filesystem.  */
46
47struct 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
82/* Trivial helper/accessor functions. */
83svn_node_kind_t svn_fs_fs__dag_node_kind(dag_node_t *node)
84{
85  return node->kind;
86}
87
88
89const svn_fs_id_t *
90svn_fs_fs__dag_get_id(const dag_node_t *node)
91{
92  return node->id;
93}
94
95
96const char *
97svn_fs_fs__dag_get_created_path(dag_node_t *node)
98{
99  return node->created_path;
100}
101
102
103svn_fs_t *
104svn_fs_fs__dag_get_fs(dag_node_t *node)
105{
106  return node->fs;
107}
108
109void
110svn_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. */
118static node_revision_t *
119copy_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.  */
154static svn_error_t *
155get_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
174svn_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
180svn_error_t *
181svn_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
213svn_error_t *
214svn_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
231svn_error_t *
232svn_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
243svn_error_t *
244svn_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
254svn_error_t *
255svn_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
265svn_error_t *
266svn_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
276svn_error_t *
277svn_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
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. */
306static svn_error_t *
307dir_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*/
330static svn_error_t *
331set_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 */
356static svn_error_t *
357make_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
415svn_error_t *
416svn_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
431svn_error_t *
432svn_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
450svn_error_t *
451svn_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
475/*** Proplists. ***/
476
477svn_error_t *
478svn_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
496svn_error_t *
497svn_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
521svn_error_t *
522svn_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
573svn_error_t *
574svn_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
601/*** Roots. ***/
602
603svn_error_t *
604svn_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
616svn_error_t *
617svn_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
629svn_error_t *
630svn_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
642svn_error_t *
643svn_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
718svn_error_t *
719svn_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
745svn_error_t *
746svn_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
806svn_error_t *
807svn_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
826svn_error_t *
827svn_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
867svn_error_t *
868svn_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
880svn_error_t *
881svn_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
893svn_error_t *
894svn_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
920svn_error_t *
921svn_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
949svn_error_t *
950svn_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
967svn_error_t *
968svn_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
987svn_error_t *
988svn_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
1006svn_error_t *
1007svn_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
1038svn_error_t *
1039svn_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
1059dag_node_t *
1060svn_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
1085svn_error_t *
1086svn_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
1125svn_error_t *
1126svn_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
1154svn_error_t *
1155svn_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
1183svn_error_t *
1184svn_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
1239/*** Comparison. ***/
1240
1241svn_error_t *
1242svn_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
1272svn_error_t *
1273svn_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
1288svn_error_t *
1289svn_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
1302svn_error_t *
1303svn_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
1316svn_error_t *
1317svn_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}
1339