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