]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/libsvn_fs_x/dag.c
Update svn-1.9.7 to 1.10.0.
[FreeBSD/FreeBSD.git] / contrib / subversion / subversion / libsvn_fs_x / dag.c
1 /* dag.c : DAG-like interface filesystem
2  *
3  * ====================================================================
4  *    Licensed to the Apache Software Foundation (ASF) under one
5  *    or more contributor license agreements.  See the NOTICE file
6  *    distributed with this work for additional information
7  *    regarding copyright ownership.  The ASF licenses this file
8  *    to you under the Apache License, Version 2.0 (the
9  *    "License"); you may not use this file except in compliance
10  *    with the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  *    Unless required by applicable law or agreed to in writing,
15  *    software distributed under the License is distributed on an
16  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  *    KIND, either express or implied.  See the License for the
18  *    specific language governing permissions and limitations
19  *    under the License.
20  * ====================================================================
21  */
22
23 #include <string.h>
24
25 #include "svn_path.h"
26 #include "svn_error.h"
27 #include "svn_fs.h"
28 #include "svn_props.h"
29 #include "svn_pools.h"
30
31 #include "dag.h"
32 #include "fs.h"
33 #include "fs_x.h"
34 #include "fs_id.h"
35 #include "cached_data.h"
36 #include "transaction.h"
37
38 #include "../libsvn_fs/fs-loader.h"
39
40 #include "private/svn_fspath.h"
41 #include "svn_private_config.h"
42 #include "private/svn_temp_serializer.h"
43 #include "temp_serializer.h"
44 #include "dag_cache.h"
45
46 \f
47 /* Initializing a filesystem.  */
48
49 struct dag_node_t
50 {
51   /* The filesystem this dag node came from. */
52   svn_fs_t *fs;
53
54   /* The node's NODE-REVISION. */
55   svn_fs_x__noderev_t *node_revision;
56
57   /* The pool to allocate NODE_REVISION in. */
58   apr_pool_t *node_pool;
59
60   /* Directory entry lookup hint to speed up consecutive calls to
61      svn_fs_x__rep_contents_dir_entry(). Only used for directory nodes.
62      Any value is legal but should default to APR_SIZE_MAX. */
63   apr_size_t hint;
64 };
65
66
67 \f
68 /* Trivial helper/accessor functions. */
69 svn_node_kind_t
70 svn_fs_x__dag_node_kind(dag_node_t *node)
71 {
72   return node->node_revision->kind;
73 }
74
75 const svn_fs_x__id_t *
76 svn_fs_x__dag_get_id(const dag_node_t *node)
77 {
78   return &node->node_revision->noderev_id;
79 }
80
81
82 const char *
83 svn_fs_x__dag_get_created_path(dag_node_t *node)
84 {
85   return node->node_revision->created_path;
86 }
87
88
89 svn_fs_t *
90 svn_fs_x__dag_get_fs(dag_node_t *node)
91 {
92   return node->fs;
93 }
94
95 void
96 svn_fs_x__dag_set_fs(dag_node_t *node,
97                      svn_fs_t *fs)
98 {
99   node->fs = fs;
100 }
101
102
103 /* Dup NODEREV and all associated data into RESULT_POOL.
104    Leaves the id and is_fresh_txn_root fields as zero bytes. */
105 static svn_fs_x__noderev_t *
106 copy_node_revision(svn_fs_x__noderev_t *noderev,
107                    apr_pool_t *result_pool)
108 {
109   svn_fs_x__noderev_t *nr = apr_pmemdup(result_pool, noderev,
110                                         sizeof(*noderev));
111
112   if (noderev->copyfrom_path)
113     nr->copyfrom_path = apr_pstrdup(result_pool, noderev->copyfrom_path);
114
115   nr->copyroot_path = apr_pstrdup(result_pool, noderev->copyroot_path);
116   nr->data_rep = svn_fs_x__rep_copy(noderev->data_rep, result_pool);
117   nr->prop_rep = svn_fs_x__rep_copy(noderev->prop_rep, result_pool);
118
119   if (noderev->created_path)
120     nr->created_path = apr_pstrdup(result_pool, noderev->created_path);
121
122   return nr;
123 }
124
125
126 const svn_fs_x__id_t *
127 svn_fs_x__dag_get_node_id(dag_node_t *node)
128 {
129   return &node->node_revision->node_id;
130 }
131
132 const svn_fs_x__id_t *
133 svn_fs_x__dag_get_copy_id(dag_node_t *node)
134 {
135   return &node->node_revision->copy_id;
136 }
137
138 svn_boolean_t
139 svn_fs_x__dag_related_node(dag_node_t *lhs,
140                            dag_node_t *rhs)
141 {
142   return svn_fs_x__id_eq(&lhs->node_revision->node_id,
143                          &rhs->node_revision->node_id);
144 }
145
146 svn_boolean_t
147 svn_fs_x__dag_same_line_of_history(dag_node_t *lhs,
148                                    dag_node_t *rhs)
149 {
150   svn_fs_x__noderev_t *lhs_noderev = lhs->node_revision;
151   svn_fs_x__noderev_t *rhs_noderev = rhs->node_revision;
152
153   return svn_fs_x__id_eq(&lhs_noderev->node_id, &rhs_noderev->node_id)
154       && svn_fs_x__id_eq(&lhs_noderev->copy_id, &rhs_noderev->copy_id);
155 }
156
157 svn_boolean_t
158 svn_fs_x__dag_check_mutable(const dag_node_t *node)
159 {
160   return svn_fs_x__is_txn(svn_fs_x__dag_get_id(node)->change_set);
161 }
162
163 svn_error_t *
164 svn_fs_x__dag_get_node(dag_node_t **node,
165                        svn_fs_t *fs,
166                        const svn_fs_x__id_t *id,
167                        apr_pool_t *result_pool,
168                        apr_pool_t *scratch_pool)
169 {
170   dag_node_t *new_node;
171   svn_fs_x__noderev_t *noderev;
172
173   /* Construct the node. */
174   new_node = apr_pcalloc(result_pool, sizeof(*new_node));
175   new_node->fs = fs;
176   new_node->hint = APR_SIZE_MAX;
177
178   /* Grab the contents so we can inspect the node's kind and created path. */
179   SVN_ERR(svn_fs_x__get_node_revision(&noderev, fs, id,
180                                       result_pool, scratch_pool));
181   new_node->node_pool = result_pool;
182   new_node->node_revision = noderev;
183
184   /* Return a fresh new node */
185   *node = new_node;
186   return SVN_NO_ERROR;
187 }
188
189
190 svn_revnum_t
191 svn_fs_x__dag_get_revision(const dag_node_t *node)
192 {
193   svn_fs_x__noderev_t *noderev = node->node_revision;
194   return (  svn_fs_x__is_fresh_txn_root(noderev)
195           ? svn_fs_x__get_revnum(noderev->predecessor_id.change_set)
196           : svn_fs_x__get_revnum(noderev->noderev_id.change_set));
197 }
198
199 const svn_fs_x__id_t *
200 svn_fs_x__dag_get_predecessor_id(dag_node_t *node)
201 {
202   return &node->node_revision->predecessor_id;
203 }
204
205 int
206 svn_fs_x__dag_get_predecessor_count(dag_node_t *node)
207 {
208   return node->node_revision->predecessor_count;
209 }
210
211 apr_int64_t
212 svn_fs_x__dag_get_mergeinfo_count(dag_node_t *node)
213 {
214   return node->node_revision->mergeinfo_count;
215 }
216
217 svn_boolean_t
218 svn_fs_x__dag_has_mergeinfo(dag_node_t *node)
219 {
220   return node->node_revision->has_mergeinfo;
221 }
222
223 svn_boolean_t
224 svn_fs_x__dag_has_descendants_with_mergeinfo(dag_node_t *node)
225 {
226   svn_fs_x__noderev_t *noderev = node->node_revision;
227
228   if (noderev->kind != svn_node_dir)
229       return FALSE;
230
231   if (noderev->mergeinfo_count > 1)
232     return TRUE;
233   else if (noderev->mergeinfo_count == 1 && !noderev->has_mergeinfo)
234     return TRUE;
235
236   return FALSE;
237 }
238
239 \f
240 /*** Directory node functions ***/
241
242 /* Some of these are helpers for functions outside this section. */
243
244 /* Set *ID_P to the noderev-id for entry NAME in PARENT.  If no such
245    entry, set *ID_P to NULL but do not error. */
246 svn_error_t *
247 svn_fs_x__dir_entry_id(svn_fs_x__id_t *id_p,
248                        dag_node_t *parent,
249                        const char *name,
250                        apr_pool_t *scratch_pool)
251 {
252   svn_fs_x__dirent_t *dirent;
253   svn_fs_x__noderev_t *noderev = parent->node_revision;
254
255   if (noderev->kind != svn_node_dir)
256     return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
257                             _("Can't get entries of non-directory"));
258
259   /* Make sure that NAME is a single path component. */
260   if (! svn_path_is_single_path_component(name))
261     return svn_error_createf
262       (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
263        "Attempted to open node with an illegal name '%s'", name);
264
265   /* Get a dirent hash for this directory. */
266   SVN_ERR(svn_fs_x__rep_contents_dir_entry(&dirent, parent->fs, noderev,
267                                            name, &parent->hint,
268                                            scratch_pool, scratch_pool));
269   if (dirent)
270     *id_p = dirent->id;
271   else
272     svn_fs_x__id_reset(id_p);
273
274   return SVN_NO_ERROR;
275 }
276
277
278 /* Add or set in PARENT a directory entry NAME pointing to ID.
279    Temporary allocations are done in SCRATCH_POOL.
280
281    Assumptions:
282    - PARENT is a mutable directory.
283    - ID does not refer to an ancestor of parent
284    - NAME is a single path component
285 */
286 static svn_error_t *
287 set_entry(dag_node_t *parent,
288           const char *name,
289           const svn_fs_x__id_t *id,
290           svn_node_kind_t kind,
291           svn_fs_x__txn_id_t txn_id,
292           apr_pool_t *scratch_pool)
293 {
294   svn_fs_x__noderev_t *parent_noderev = parent->node_revision;
295
296   /* Set the new entry. */
297   SVN_ERR(svn_fs_x__set_entry(parent->fs, txn_id, parent_noderev, name, id,
298                               kind, parent->node_pool, scratch_pool));
299
300   /* Update cached data. */
301   svn_fs_x__update_dag_cache(parent);
302
303   return SVN_NO_ERROR;
304 }
305
306
307 /* Make a new entry named NAME in PARENT.  If IS_DIR is true, then the
308    node revision the new entry points to will be a directory, else it
309    will be a file.  The new node will be allocated in RESULT_POOL.  PARENT
310    must be mutable, and must not have an entry named NAME.
311
312    Use SCRATCH_POOL for all temporary allocations.
313  */
314 static svn_error_t *
315 make_entry(dag_node_t **child_p,
316            dag_node_t *parent,
317            const char *parent_path,
318            const char *name,
319            svn_boolean_t is_dir,
320            svn_fs_x__txn_id_t txn_id,
321            apr_pool_t *result_pool,
322            apr_pool_t *scratch_pool)
323 {
324   svn_fs_x__noderev_t new_noderev;
325   svn_fs_x__noderev_t *parent_noderev = parent->node_revision;
326
327   /* Make sure that NAME is a single path component. */
328   if (! svn_path_is_single_path_component(name))
329     return svn_error_createf
330       (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
331        _("Attempted to create a node with an illegal name '%s'"), name);
332
333   /* Make sure that parent is a directory */
334   if (parent_noderev->kind != svn_node_dir)
335     return svn_error_create
336       (SVN_ERR_FS_NOT_DIRECTORY, NULL,
337        _("Attempted to create entry in non-directory parent"));
338
339   /* Check that the parent is mutable. */
340   if (! svn_fs_x__dag_check_mutable(parent))
341     return svn_error_createf
342       (SVN_ERR_FS_NOT_MUTABLE, NULL,
343        _("Attempted to clone child of non-mutable node"));
344
345   /* Create the new node's NODE-REVISION */
346   memset(&new_noderev, 0, sizeof(new_noderev));
347   new_noderev.kind = is_dir ? svn_node_dir : svn_node_file;
348   new_noderev.created_path = svn_fspath__join(parent_path, name, result_pool);
349
350   new_noderev.copyroot_path = apr_pstrdup(result_pool,
351                                           parent_noderev->copyroot_path);
352   new_noderev.copyroot_rev = parent_noderev->copyroot_rev;
353   new_noderev.copyfrom_rev = SVN_INVALID_REVNUM;
354   new_noderev.copyfrom_path = NULL;
355   svn_fs_x__id_reset(&new_noderev.predecessor_id);
356
357   SVN_ERR(svn_fs_x__create_node
358           (svn_fs_x__dag_get_fs(parent), &new_noderev,
359            &parent_noderev->copy_id, txn_id, scratch_pool));
360
361   /* Create a new dag_node_t for our new node */
362   SVN_ERR(svn_fs_x__dag_get_node(child_p, svn_fs_x__dag_get_fs(parent),
363                                  &new_noderev.noderev_id, result_pool,
364                                  scratch_pool));
365
366   /* We can safely call set_entry because we already know that
367      PARENT is mutable, and we just created CHILD, so we know it has
368      no ancestors (therefore, PARENT cannot be an ancestor of CHILD) */
369   return set_entry(parent, name, &new_noderev.noderev_id,
370                    new_noderev.kind, txn_id, scratch_pool);
371 }
372
373
374 svn_error_t *
375 svn_fs_x__dag_dir_entries(apr_array_header_t **entries,
376                           dag_node_t *node,
377                           apr_pool_t *result_pool,
378                           apr_pool_t *scratch_pool)
379 {
380   svn_fs_x__noderev_t *noderev = node->node_revision;
381
382   if (noderev->kind != svn_node_dir)
383     return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
384                             _("Can't get entries of non-directory"));
385
386   return svn_fs_x__rep_contents_dir(entries, node->fs, noderev, result_pool,
387                                     scratch_pool);
388 }
389
390
391 svn_error_t *
392 svn_fs_x__dag_set_entry(dag_node_t *node,
393                         const char *entry_name,
394                         const svn_fs_x__id_t *id,
395                         svn_node_kind_t kind,
396                         svn_fs_x__txn_id_t txn_id,
397                         apr_pool_t *scratch_pool)
398 {
399   /* Check it's a directory. */
400   if (node->node_revision->kind != svn_node_dir)
401     return svn_error_create
402       (SVN_ERR_FS_NOT_DIRECTORY, NULL,
403        _("Attempted to set entry in non-directory node"));
404
405   /* Check it's mutable. */
406   if (! svn_fs_x__dag_check_mutable(node))
407     return svn_error_create
408       (SVN_ERR_FS_NOT_MUTABLE, NULL,
409        _("Attempted to set entry in immutable node"));
410
411   return set_entry(node, entry_name, id, kind, txn_id, scratch_pool);
412 }
413
414
415 \f
416 /*** Proplists. ***/
417
418 svn_error_t *
419 svn_fs_x__dag_get_proplist(apr_hash_t **proplist_p,
420                            dag_node_t *node,
421                            apr_pool_t *result_pool,
422                            apr_pool_t *scratch_pool)
423 {
424   SVN_ERR(svn_fs_x__get_proplist(proplist_p, node->fs, node->node_revision,
425                                  result_pool, scratch_pool));
426   return SVN_NO_ERROR;
427 }
428
429
430 svn_error_t *
431 svn_fs_x__dag_set_proplist(dag_node_t *node,
432                            apr_hash_t *proplist,
433                            apr_pool_t *scratch_pool)
434 {
435   /* Sanity check: this node better be mutable! */
436   if (! svn_fs_x__dag_check_mutable(node))
437     {
438       svn_string_t *idstr
439         = svn_fs_x__id_unparse(&node->node_revision->noderev_id,
440                                scratch_pool);
441       return svn_error_createf
442         (SVN_ERR_FS_NOT_MUTABLE, NULL,
443          "Can't set proplist on *immutable* node-revision %s",
444          idstr->data);
445     }
446
447   /* Set the new proplist. */
448   SVN_ERR(svn_fs_x__set_proplist(node->fs, node->node_revision, proplist,
449                                  scratch_pool));
450   svn_fs_x__update_dag_cache(node);
451
452   return SVN_NO_ERROR;
453 }
454
455 /* Write NODE's NODEREV element to disk.  Update the DAG cache.
456    Use SCRATCH_POOL for temporary allocations. */
457 static svn_error_t *
458 noderev_changed(dag_node_t *node,
459                 apr_pool_t *scratch_pool)
460 {
461   SVN_ERR(svn_fs_x__put_node_revision(node->fs, node->node_revision,
462                                       scratch_pool));
463   svn_fs_x__update_dag_cache(node);
464
465   return SVN_NO_ERROR;
466 }
467
468 svn_error_t *
469 svn_fs_x__dag_increment_mergeinfo_count(dag_node_t *node,
470                                         apr_int64_t increment,
471                                         apr_pool_t *scratch_pool)
472 {
473   svn_fs_x__noderev_t *noderev = node->node_revision;
474
475   /* Sanity check: this node better be mutable! */
476   if (! svn_fs_x__dag_check_mutable(node))
477     {
478       svn_string_t *idstr = svn_fs_x__id_unparse(&noderev->noderev_id,
479                                                  scratch_pool);
480       return svn_error_createf
481         (SVN_ERR_FS_NOT_MUTABLE, NULL,
482          "Can't increment mergeinfo count on *immutable* node-revision %s",
483          idstr->data);
484     }
485
486   if (increment == 0)
487     return SVN_NO_ERROR;
488
489   noderev->mergeinfo_count += increment;
490   if (noderev->mergeinfo_count < 0)
491     {
492       svn_string_t *idstr = svn_fs_x__id_unparse(&noderev->noderev_id,
493                                                  scratch_pool);
494       return svn_error_createf
495         (SVN_ERR_FS_CORRUPT, NULL,
496          apr_psprintf(scratch_pool,
497                       _("Can't increment mergeinfo count on node-revision %%s "
498                         "to negative value %%%s"),
499                       APR_INT64_T_FMT),
500          idstr->data, noderev->mergeinfo_count);
501     }
502   if (noderev->mergeinfo_count > 1 && noderev->kind == svn_node_file)
503     {
504       svn_string_t *idstr = svn_fs_x__id_unparse(&noderev->noderev_id,
505                                                  scratch_pool);
506       return svn_error_createf
507         (SVN_ERR_FS_CORRUPT, NULL,
508          apr_psprintf(scratch_pool,
509                       _("Can't increment mergeinfo count on *file* "
510                         "node-revision %%s to %%%s (> 1)"),
511                       APR_INT64_T_FMT),
512          idstr->data, noderev->mergeinfo_count);
513     }
514
515   /* Flush it out. */
516   return noderev_changed(node, scratch_pool);
517 }
518
519 svn_error_t *
520 svn_fs_x__dag_set_has_mergeinfo(dag_node_t *node,
521                                 svn_boolean_t has_mergeinfo,
522                                 apr_pool_t *scratch_pool)
523 {
524   /* Sanity check: this node better be mutable! */
525   if (! svn_fs_x__dag_check_mutable(node))
526     {
527       svn_string_t *idstr
528         = svn_fs_x__id_unparse(&node->node_revision->noderev_id,
529                                scratch_pool);
530       return svn_error_createf
531         (SVN_ERR_FS_NOT_MUTABLE, NULL,
532          "Can't set mergeinfo flag on *immutable* node-revision %s",
533          idstr->data);
534     }
535
536   node->node_revision->has_mergeinfo = has_mergeinfo;
537
538   /* Flush it out. */
539   return noderev_changed(node, scratch_pool);
540 }
541
542 \f
543 /*** Roots. ***/
544
545 svn_error_t *
546 svn_fs_x__dag_root(dag_node_t **node_p,
547                    svn_fs_t *fs,
548                    svn_fs_x__change_set_t change_set,
549                    apr_pool_t *result_pool,
550                    apr_pool_t *scratch_pool)
551 {
552   svn_fs_x__id_t root_id;
553   root_id.change_set = change_set;
554   root_id.number = SVN_FS_X__ITEM_INDEX_ROOT_NODE;
555
556   return svn_fs_x__dag_get_node(node_p, fs, &root_id, result_pool,
557                                 scratch_pool);
558 }
559
560
561 svn_error_t *
562 svn_fs_x__dag_clone_child(dag_node_t **child_p,
563                           dag_node_t *parent,
564                           const char *parent_path,
565                           const char *name,
566                           const svn_fs_x__id_t *copy_id,
567                           svn_fs_x__txn_id_t txn_id,
568                           svn_boolean_t is_parent_copyroot,
569                           apr_pool_t *result_pool,
570                           apr_pool_t *scratch_pool)
571 {
572   dag_node_t *cur_entry; /* parent's current entry named NAME */
573   const svn_fs_x__id_t *new_node_id; /* node id we'll put into NEW_NODE */
574   svn_fs_t *fs = svn_fs_x__dag_get_fs(parent);
575
576   /* First check that the parent is mutable. */
577   if (! svn_fs_x__dag_check_mutable(parent))
578     return svn_error_createf
579       (SVN_ERR_FS_NOT_MUTABLE, NULL,
580        "Attempted to clone child of non-mutable node");
581
582   /* Make sure that NAME is a single path component. */
583   if (! svn_path_is_single_path_component(name))
584     return svn_error_createf
585       (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
586        "Attempted to make a child clone with an illegal name '%s'", name);
587
588   /* Find the node named NAME in PARENT's entries list if it exists. */
589   SVN_ERR(svn_fs_x__dag_open(&cur_entry, parent, name, scratch_pool,
590                              scratch_pool));
591   if (! cur_entry)
592     return svn_error_createf
593       (SVN_ERR_FS_NOT_FOUND, NULL,
594        "Attempted to open non-existent child node '%s'", name);
595
596   /* Check for mutability in the node we found.  If it's mutable, we
597      don't need to clone it. */
598   if (svn_fs_x__dag_check_mutable(cur_entry))
599     {
600       /* This has already been cloned */
601       new_node_id = svn_fs_x__dag_get_id(cur_entry);
602     }
603   else
604     {
605       svn_fs_x__noderev_t *noderev = cur_entry->node_revision;
606
607       if (is_parent_copyroot)
608         {
609           svn_fs_x__noderev_t *parent_noderev = parent->node_revision;
610           noderev->copyroot_rev = parent_noderev->copyroot_rev;
611           noderev->copyroot_path = apr_pstrdup(scratch_pool,
612                                                parent_noderev->copyroot_path);
613         }
614
615       noderev->copyfrom_path = NULL;
616       noderev->copyfrom_rev = SVN_INVALID_REVNUM;
617
618       noderev->predecessor_id = noderev->noderev_id;
619       noderev->predecessor_count++;
620       noderev->created_path = svn_fspath__join(parent_path, name,
621                                                scratch_pool);
622
623       if (copy_id == NULL)
624         copy_id = &noderev->copy_id;
625
626       SVN_ERR(svn_fs_x__create_successor(fs, noderev, copy_id, txn_id,
627                                          scratch_pool));
628       new_node_id = &noderev->noderev_id;
629
630       /* Replace the ID in the parent's ENTRY list with the ID which
631          refers to the mutable clone of this child. */
632       SVN_ERR(set_entry(parent, name, new_node_id, noderev->kind, txn_id,
633                         scratch_pool));
634     }
635
636   /* Initialize the youngster. */
637   return svn_fs_x__dag_get_node(child_p, fs, new_node_id, result_pool,
638                                 scratch_pool);
639 }
640
641
642 /* Delete all mutable node revisions reachable from node ID, including
643    ID itself, from FS's `nodes' table.  Also delete any mutable
644    representations and strings associated with that node revision.
645    ID may refer to a file or directory, which may be mutable or immutable.
646
647    Use SCRATCH_POOL for temporary allocations.
648  */
649 static svn_error_t *
650 delete_if_mutable(svn_fs_t *fs,
651                   const svn_fs_x__id_t *id,
652                   apr_pool_t *scratch_pool)
653 {
654   dag_node_t *node;
655
656   /* Get the node. */
657   SVN_ERR(svn_fs_x__dag_get_node(&node, fs, id, scratch_pool, scratch_pool));
658
659   /* If immutable, do nothing and return immediately. */
660   if (! svn_fs_x__dag_check_mutable(node))
661     return SVN_NO_ERROR;
662
663   /* Else it's mutable.  Recurse on directories... */
664   if (node->node_revision->kind == svn_node_dir)
665     {
666       apr_array_header_t *entries;
667       int i;
668       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
669
670       /* Loop over directory entries */
671       SVN_ERR(svn_fs_x__dag_dir_entries(&entries, node, scratch_pool,
672                                         iterpool));
673       for (i = 0; i < entries->nelts; ++i)
674         {
675           const svn_fs_x__id_t *noderev_id
676             = &APR_ARRAY_IDX(entries, i, svn_fs_x__dirent_t *)->id;
677
678           svn_pool_clear(iterpool);
679           SVN_ERR(delete_if_mutable(fs, noderev_id, iterpool));
680         }
681
682       svn_pool_destroy(iterpool);
683     }
684
685   /* ... then delete the node itself, after deleting any mutable
686      representations and strings it points to. */
687   return svn_fs_x__delete_node_revision(fs, id, scratch_pool);
688 }
689
690
691 svn_error_t *
692 svn_fs_x__dag_delete(dag_node_t *parent,
693                      const char *name,
694                      svn_fs_x__txn_id_t txn_id,
695                      apr_pool_t *scratch_pool)
696 {
697   svn_fs_x__noderev_t *parent_noderev = parent->node_revision;
698   svn_fs_t *fs = parent->fs;
699   svn_fs_x__dirent_t *dirent;
700   apr_pool_t *subpool;
701
702   /* Make sure parent is a directory. */
703   if (parent_noderev->kind != svn_node_dir)
704     return svn_error_createf
705       (SVN_ERR_FS_NOT_DIRECTORY, NULL,
706        "Attempted to delete entry '%s' from *non*-directory node", name);
707
708   /* Make sure parent is mutable. */
709   if (! svn_fs_x__dag_check_mutable(parent))
710     return svn_error_createf
711       (SVN_ERR_FS_NOT_MUTABLE, NULL,
712        "Attempted to delete entry '%s' from immutable directory node", name);
713
714   /* Make sure that NAME is a single path component. */
715   if (! svn_path_is_single_path_component(name))
716     return svn_error_createf
717       (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL,
718        "Attempted to delete a node with an illegal name '%s'", name);
719
720   /* We allocate a few potentially heavy temporary objects (file buffers
721      and directories).  Make sure we don't keep them around for longer
722      than necessary. */
723   subpool = svn_pool_create(scratch_pool);
724
725   /* Search this directory for a dirent with that NAME. */
726   SVN_ERR(svn_fs_x__rep_contents_dir_entry(&dirent, fs, parent_noderev,
727                                            name, &parent->hint,
728                                            subpool, subpool));
729
730   /* If we never found ID in ENTRIES (perhaps because there are no
731      ENTRIES, perhaps because ID just isn't in the existing ENTRIES
732      ... it doesn't matter), return an error.  */
733   if (! dirent)
734     return svn_error_createf
735       (SVN_ERR_FS_NO_SUCH_ENTRY, NULL,
736        "Delete failed--directory has no entry '%s'", name);
737
738   /* If mutable, remove it and any mutable children from db. */
739   SVN_ERR(delete_if_mutable(parent->fs, &dirent->id, subpool));
740
741   /* Remove this entry from its parent's entries list. */
742   SVN_ERR(set_entry(parent, name, NULL, svn_node_unknown, txn_id, subpool));
743
744   svn_pool_destroy(subpool);
745   return SVN_NO_ERROR;
746 }
747
748
749 svn_error_t *
750 svn_fs_x__dag_make_file(dag_node_t **child_p,
751                         dag_node_t *parent,
752                         const char *parent_path,
753                         const char *name,
754                         svn_fs_x__txn_id_t txn_id,
755                         apr_pool_t *result_pool,
756                         apr_pool_t *scratch_pool)
757 {
758   /* Call our little helper function */
759   return make_entry(child_p, parent, parent_path, name, FALSE, txn_id,
760                     result_pool, scratch_pool);
761 }
762
763
764 svn_error_t *
765 svn_fs_x__dag_make_dir(dag_node_t **child_p,
766                        dag_node_t *parent,
767                        const char *parent_path,
768                        const char *name,
769                        svn_fs_x__txn_id_t txn_id,
770                        apr_pool_t *result_pool,
771                        apr_pool_t *scratch_pool)
772 {
773   /* Call our little helper function */
774   return make_entry(child_p, parent, parent_path, name, TRUE, txn_id,
775                     result_pool, scratch_pool);
776 }
777
778
779 svn_error_t *
780 svn_fs_x__dag_get_contents(svn_stream_t **contents_p,
781                            dag_node_t *file,
782                            apr_pool_t *result_pool)
783 {
784   /* Make sure our node is a file. */
785   if (file->node_revision->kind != svn_node_file)
786     return svn_error_createf
787       (SVN_ERR_FS_NOT_FILE, NULL,
788        "Attempted to get textual contents of a *non*-file node");
789
790   /* Get a stream to the contents. */
791   SVN_ERR(svn_fs_x__get_contents(contents_p, file->fs,
792                                  file->node_revision->data_rep, TRUE,
793                                  result_pool));
794
795   return SVN_NO_ERROR;
796 }
797
798
799 svn_error_t *
800 svn_fs_x__dag_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
801                                     dag_node_t *source,
802                                     dag_node_t *target,
803                                     apr_pool_t *result_pool,
804                                     apr_pool_t *scratch_pool)
805 {
806   svn_fs_x__noderev_t *src_noderev = source ? source->node_revision : NULL;
807   svn_fs_x__noderev_t *tgt_noderev = target->node_revision;
808
809   /* Make sure our nodes are files. */
810   if ((source && src_noderev->kind != svn_node_file)
811       || tgt_noderev->kind != svn_node_file)
812     return svn_error_createf
813       (SVN_ERR_FS_NOT_FILE, NULL,
814        "Attempted to get textual contents of a *non*-file node");
815
816   /* Get the delta stream. */
817   return svn_fs_x__get_file_delta_stream(stream_p, target->fs,
818                                          src_noderev, tgt_noderev,
819                                          result_pool, scratch_pool);
820 }
821
822
823 svn_error_t *
824 svn_fs_x__dag_try_process_file_contents(svn_boolean_t *success,
825                                         dag_node_t *node,
826                                         svn_fs_process_contents_func_t processor,
827                                         void* baton,
828                                         apr_pool_t *scratch_pool)
829 {
830   return svn_fs_x__try_process_file_contents(success, node->fs,
831                                              node->node_revision,
832                                              processor, baton, scratch_pool);
833 }
834
835
836 svn_error_t *
837 svn_fs_x__dag_file_length(svn_filesize_t *length,
838                           dag_node_t *file)
839 {
840   /* Make sure our node is a file. */
841   if (file->node_revision->kind != svn_node_file)
842     return svn_error_createf
843       (SVN_ERR_FS_NOT_FILE, NULL,
844        "Attempted to get length of a *non*-file node");
845
846   return svn_fs_x__file_length(length, file->node_revision);
847 }
848
849
850 svn_error_t *
851 svn_fs_x__dag_file_checksum(svn_checksum_t **checksum,
852                             dag_node_t *file,
853                             svn_checksum_kind_t kind,
854                             apr_pool_t *result_pool)
855 {
856   if (file->node_revision->kind != svn_node_file)
857     return svn_error_createf
858       (SVN_ERR_FS_NOT_FILE, NULL,
859        "Attempted to get checksum of a *non*-file node");
860
861   return svn_fs_x__file_checksum(checksum, file->node_revision, kind,
862                                  result_pool);
863 }
864
865
866 svn_error_t *
867 svn_fs_x__dag_get_edit_stream(svn_stream_t **contents,
868                               dag_node_t *file,
869                               apr_pool_t *result_pool)
870 {
871   /* Make sure our node is a file. */
872   if (file->node_revision->kind != svn_node_file)
873     return svn_error_createf
874       (SVN_ERR_FS_NOT_FILE, NULL,
875        "Attempted to set textual contents of a *non*-file node");
876
877   /* Make sure our node is mutable. */
878   if (! svn_fs_x__dag_check_mutable(file))
879     return svn_error_createf
880       (SVN_ERR_FS_NOT_MUTABLE, NULL,
881        "Attempted to set textual contents of an immutable node");
882
883   SVN_ERR(svn_fs_x__set_contents(contents, file->fs, file->node_revision,
884                                  result_pool));
885   return SVN_NO_ERROR;
886 }
887
888
889
890 svn_error_t *
891 svn_fs_x__dag_finalize_edits(dag_node_t *file,
892                              const svn_checksum_t *checksum,
893                              apr_pool_t *scratch_pool)
894 {
895   if (checksum)
896     {
897       svn_checksum_t *file_checksum;
898
899       SVN_ERR(svn_fs_x__dag_file_checksum(&file_checksum, file,
900                                           checksum->kind, scratch_pool));
901       if (!svn_checksum_match(checksum, file_checksum))
902         return svn_checksum_mismatch_err(checksum, file_checksum,
903                                          scratch_pool,
904                                          _("Checksum mismatch for '%s'"),
905                                          file->node_revision->created_path);
906     }
907
908   svn_fs_x__update_dag_cache(file);
909   return SVN_NO_ERROR;
910 }
911
912
913 dag_node_t *
914 svn_fs_x__dag_dup(const dag_node_t *node,
915                   apr_pool_t *result_pool)
916 {
917   /* Allocate our new node. */
918   dag_node_t *new_node = apr_pmemdup(result_pool, node, sizeof(*new_node));
919
920   /* Copy sub-structures. */
921   new_node->node_revision = copy_node_revision(node->node_revision,
922                                                result_pool);
923   new_node->node_pool = result_pool;
924
925   return new_node;
926 }
927
928
929 svn_error_t *
930 svn_fs_x__dag_open(dag_node_t **child_p,
931                    dag_node_t *parent,
932                    const char *name,
933                    apr_pool_t *result_pool,
934                    apr_pool_t *scratch_pool)
935 {
936   svn_fs_x__id_t node_id;
937
938   /* Ensure that NAME exists in PARENT's entry list. */
939   SVN_ERR(svn_fs_x__dir_entry_id(&node_id, parent, name, scratch_pool));
940   if (! svn_fs_x__id_used(&node_id))
941     {
942       *child_p = NULL;
943       return SVN_NO_ERROR;
944     }
945
946   /* Now get the node that was requested. */
947   return svn_fs_x__dag_get_node(child_p, svn_fs_x__dag_get_fs(parent),
948                                 &node_id, result_pool, scratch_pool);
949 }
950
951
952 svn_error_t *
953 svn_fs_x__dag_copy(dag_node_t *to_node,
954                    const char *entry,
955                    dag_node_t *from_node,
956                    svn_boolean_t preserve_history,
957                    svn_revnum_t from_rev,
958                    const char *from_path,
959                    svn_fs_x__txn_id_t txn_id,
960                    apr_pool_t *scratch_pool)
961 {
962   const svn_fs_x__id_t *id;
963
964   if (preserve_history)
965     {
966       svn_fs_x__noderev_t *to_noderev;
967       svn_fs_x__id_t copy_id;
968       svn_fs_t *fs = svn_fs_x__dag_get_fs(from_node);
969
970       /* Make a copy of the original node revision. */
971       to_noderev = copy_node_revision(from_node->node_revision, scratch_pool);
972
973       /* Reserve a copy ID for this new copy. */
974       SVN_ERR(svn_fs_x__reserve_copy_id(&copy_id, fs, txn_id, scratch_pool));
975
976       /* Create a successor with its predecessor pointing at the copy
977          source. */
978       to_noderev->predecessor_id = to_noderev->noderev_id;
979       to_noderev->predecessor_count++;
980       to_noderev->created_path =
981         svn_fspath__join(svn_fs_x__dag_get_created_path(to_node), entry,
982                          scratch_pool);
983       to_noderev->copyfrom_path = apr_pstrdup(scratch_pool, from_path);
984       to_noderev->copyfrom_rev = from_rev;
985
986       /* Set the copyroot equal to our own id. */
987       to_noderev->copyroot_path = NULL;
988
989       SVN_ERR(svn_fs_x__create_successor(fs, to_noderev,
990                                          &copy_id, txn_id, scratch_pool));
991       id = &to_noderev->noderev_id;
992     }
993   else  /* don't preserve history */
994     {
995       id = svn_fs_x__dag_get_id(from_node);
996     }
997
998   /* Set the entry in to_node to the new id. */
999   return svn_fs_x__dag_set_entry(to_node, entry, id,
1000                                  from_node->node_revision->kind,
1001                                  txn_id, scratch_pool);
1002 }
1003
1004
1005 \f
1006 /*** Comparison. ***/
1007
1008 svn_error_t *
1009 svn_fs_x__dag_things_different(svn_boolean_t *props_changed,
1010                                svn_boolean_t *contents_changed,
1011                                dag_node_t *node1,
1012                                dag_node_t *node2,
1013                                svn_boolean_t strict,
1014                                apr_pool_t *scratch_pool)
1015 {
1016   svn_fs_x__noderev_t *noderev1 = node1->node_revision;
1017   svn_fs_x__noderev_t *noderev2 = node2->node_revision;
1018   svn_fs_t *fs;
1019   svn_boolean_t same;
1020
1021   /* If we have no place to store our results, don't bother doing
1022      anything. */
1023   if (! props_changed && ! contents_changed)
1024     return SVN_NO_ERROR;
1025
1026   fs = svn_fs_x__dag_get_fs(node1);
1027
1028   /* Compare property keys. */
1029   if (props_changed != NULL)
1030     {
1031       SVN_ERR(svn_fs_x__prop_rep_equal(&same, fs, noderev1, noderev2,
1032                                        strict, scratch_pool));
1033       *props_changed = !same;
1034     }
1035
1036   /* Compare contents keys. */
1037   if (contents_changed != NULL)
1038     *contents_changed = !svn_fs_x__file_text_rep_equal(noderev1->data_rep,
1039                                                        noderev2->data_rep);
1040
1041   return SVN_NO_ERROR;
1042 }
1043
1044 void
1045 svn_fs_x__dag_get_copyroot(svn_revnum_t *rev,
1046                            const char **path,
1047                            dag_node_t *node)
1048 {
1049   *rev = node->node_revision->copyroot_rev;
1050   *path = node->node_revision->copyroot_path;
1051 }
1052
1053 svn_revnum_t
1054 svn_fs_x__dag_get_copyfrom_rev(dag_node_t *node)
1055 {
1056   return node->node_revision->copyfrom_rev;
1057 }
1058
1059 const char *
1060 svn_fs_x__dag_get_copyfrom_path(dag_node_t *node)
1061 {
1062   return node->node_revision->copyfrom_path;
1063 }
1064
1065 svn_error_t *
1066 svn_fs_x__dag_update_ancestry(dag_node_t *target,
1067                               dag_node_t *source,
1068                               apr_pool_t *scratch_pool)
1069 {
1070   svn_fs_x__noderev_t *source_noderev = source->node_revision;
1071   svn_fs_x__noderev_t *target_noderev = target->node_revision;
1072
1073   if (! svn_fs_x__dag_check_mutable(target))
1074     return svn_error_createf
1075       (SVN_ERR_FS_NOT_MUTABLE, NULL,
1076        _("Attempted to update ancestry of non-mutable node"));
1077
1078   target_noderev->predecessor_id = source_noderev->noderev_id;
1079   target_noderev->predecessor_count = source_noderev->predecessor_count;
1080   target_noderev->predecessor_count++;
1081
1082   return noderev_changed(target, scratch_pool);
1083 }