1 /* tree.c : tree-like filesystem, built on DAG 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
24 /* The job of this layer is to take a filesystem with lots of node
25 sharing going on --- the real DAG filesystem as it appears in the
26 database --- and make it look and act like an ordinary tree
27 filesystem, with no sharing.
28
29 We do just-in-time cloning: you can walk from some unfinished
30 transaction's root down into directories and files shared with
31 committed revisions; as soon as you try to change something, the
32 appropriate nodes get cloned (and parent directory entries updated)
33 invisibly, behind your back. Any other references you have to
34 nodes that have been cloned by other changes, even made by other
35 processes, are automatically updated to point to the right clones. */
36
37
38 #include <stdlib.h>
39 #include <string.h>
40 #include <assert.h>
41 #include "svn_private_config.h"
42 #include "svn_hash.h"
43 #include "svn_pools.h"
44 #include "svn_error.h"
45 #include "svn_path.h"
46 #include "svn_mergeinfo.h"
47 #include "svn_fs.h"
48 #include "svn_sorts.h"
49 #include "svn_checksum.h"
50 #include "fs.h"
51 #include "err.h"
52 #include "trail.h"
53 #include "node-rev.h"
54 #include "key-gen.h"
55 #include "dag.h"
56 #include "tree.h"
57 #include "lock.h"
58 #include "revs-txns.h"
59 #include "id.h"
60 #include "bdb/txn-table.h"
61 #include "bdb/rev-table.h"
62 #include "bdb/nodes-table.h"
63 #include "bdb/changes-table.h"
64 #include "bdb/copies-table.h"
65 #include "bdb/node-origins-table.h"
66 #include "bdb/miscellaneous-table.h"
67 #include "../libsvn_fs/fs-loader.h"
68 #include "private/svn_fspath.h"
69 #include "private/svn_fs_util.h"
70 #include "private/svn_mergeinfo_private.h"
71 #include "private/svn_sorts_private.h"
72
73
74 /* ### I believe this constant will become internal to reps-strings.c.
75 ### see the comment in window_consumer() for more information. */
76
77 /* ### the comment also seems to need tweaking: the log file stuff
78 ### is no longer an issue... */
79 /* Data written to the filesystem through the svn_fs_apply_textdelta()
80 interface is cached in memory until the end of the data stream, or
81 until a size trigger is hit. Define that trigger here (in bytes).
82 Setting the value to 0 will result in no filesystem buffering at
83 all. The value only really matters when dealing with file contents
84 bigger than the value itself. Above that point, large values here
85 allow the filesystem to buffer more data in memory before flushing
86 to the database, which increases memory usage but greatly decreases
87 the amount of disk access (and log-file generation) in database.
88 Smaller values will limit your overall memory consumption, but can
89 drastically hurt throughput by necessitating more write operations
90 to the database (which also generates more log-files). */
91 #define WRITE_BUFFER_SIZE 512000
92
93 /* The maximum number of cache items to maintain in the node cache. */
94 #define NODE_CACHE_MAX_KEYS 32
95
96
97
98 /* The root structure. */
99
100 /* Structure for svn_fs_root_t's node_cache hash values. */
101 struct dag_node_cache_t
102 {
103 dag_node_t *node; /* NODE to be cached. */
104 int idx; /* Index into the keys array for this cache item's key. */
105 apr_pool_t *pool; /* Pool in which NODE is allocated. */
106 };
107
108
109 typedef struct base_root_data_t
110 {
111
112 /* For revision roots, this is a dag node for the revision's root
113 directory. For transaction roots, we open the root directory
114 afresh every time, since the root may have been cloned, or
115 the transaction may have disappeared altogether. */
116 dag_node_t *root_dir;
117
118 /* Cache structures, for mapping const char * PATH to const
119 struct dag_node_cache_t * structures.
120
121 ### Currently this is only used for revision roots. To be safe
122 for transaction roots, you must have the guarantee that there is
123 never more than a single transaction root per Subversion
124 transaction ever open at a given time -- having two roots open to
125 the same Subversion transaction would be a request for pain.
126 Also, you have to ensure that if a 'make_path_mutable()' fails for
127 any reason, you don't leave cached nodes for the portion of that
128 function that succeeded. In other words, this cache must never,
129 ever, lie. */
130 apr_hash_t *node_cache;
131 const char *node_cache_keys[NODE_CACHE_MAX_KEYS];
132 int node_cache_idx;
133 } base_root_data_t;
134
135
136 static svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev,
137 dag_node_t *root_dir,
138 apr_pool_t *pool);
139
140 static svn_fs_root_t *make_txn_root(svn_fs_t *fs, const char *txn,
141 svn_revnum_t base_rev, apr_uint32_t flags,
142 apr_pool_t *pool);
143
144
145 /*** Node Caching in the Roots. ***/
146
147 /* Return NODE for PATH from ROOT's node cache, or NULL if the node
148 isn't cached. */
149 static dag_node_t *
dag_node_cache_get(svn_fs_root_t * root,const char * path,apr_pool_t * pool)150 dag_node_cache_get(svn_fs_root_t *root,
151 const char *path,
152 apr_pool_t *pool)
153 {
154 base_root_data_t *brd = root->fsap_data;
155 struct dag_node_cache_t *cache_item;
156
157 /* Assert valid input. */
158 assert(*path == '/');
159
160 /* Only allow revision roots. */
161 if (root->is_txn_root)
162 return NULL;
163
164 /* Look in the cache for our desired item. */
165 cache_item = svn_hash_gets(brd->node_cache, path);
166 if (cache_item)
167 return svn_fs_base__dag_dup(cache_item->node, pool);
168
169 return NULL;
170 }
171
172
173 /* Add the NODE for PATH to ROOT's node cache. Callers should *NOT*
174 call this unless they are adding a currently un-cached item to the
175 cache, or are replacing the NODE for PATH with a new (different)
176 one. */
177 static void
dag_node_cache_set(svn_fs_root_t * root,const char * path,dag_node_t * node)178 dag_node_cache_set(svn_fs_root_t *root,
179 const char *path,
180 dag_node_t *node)
181 {
182 base_root_data_t *brd = root->fsap_data;
183 const char *cache_path;
184 apr_pool_t *cache_pool;
185 struct dag_node_cache_t *cache_item;
186 int num_keys = apr_hash_count(brd->node_cache);
187
188 /* What? No POOL passed to this function?
189
190 To ensure that our cache values live as long as the svn_fs_root_t
191 in which they are ultimately stored, and to allow us to free()
192 them individually without harming the rest, they are each
193 allocated from a subpool of ROOT's pool. We'll keep one subpool
194 around for each cache slot -- as we start expiring stuff
195 to make room for more entries, we'll re-use the expired thing's
196 pool. */
197
198 /* Assert valid input and state. */
199 assert(*path == '/');
200 assert((brd->node_cache_idx <= num_keys)
201 && (num_keys <= NODE_CACHE_MAX_KEYS));
202
203 /* Only allow revision roots. */
204 if (root->is_txn_root)
205 return;
206
207 /* Special case: the caller wants us to replace an existing cached
208 node with a new one. If the callers aren't mindless, this should
209 only happen when a node is made mutable under a transaction
210 root, and that only happens once under that root. So, we'll be a
211 little bit sloppy here, and count on callers doing the right
212 thing. */
213 cache_item = svn_hash_gets(brd->node_cache, path);
214 if (cache_item)
215 {
216 /* ### This section is somehow broken. I don't know how, but it
217 ### is. And I don't want to spend any more time on it. So,
218 ### callers, use only revision root and don't try to update
219 ### an already-cached thing. -- cmpilato */
220 SVN_ERR_MALFUNCTION_NO_RETURN();
221
222 #if 0
223 int cache_index = cache_item->idx;
224 cache_path = brd->node_cache_keys[cache_index];
225 cache_pool = cache_item->pool;
226 cache_item->node = svn_fs_base__dag_dup(node, cache_pool);
227
228 /* Now, move the cache key reference to the end of the keys in
229 the keys array (unless it's already at the end). ### Yes,
230 it's a memmove(), but we're not talking about pages of memory
231 here. */
232 if (cache_index != (num_keys - 1))
233 {
234 int move_num = NODE_CACHE_MAX_KEYS - cache_index - 1;
235 memmove(brd->node_cache_keys + cache_index,
236 brd->node_cache_keys + cache_index + 1,
237 move_num * sizeof(const char *));
238 cache_index = num_keys - 1;
239 brd->node_cache_keys[cache_index] = cache_path;
240 }
241
242 /* Advance the cache pointers. */
243 cache_item->idx = cache_index;
244 brd->node_cache_idx = (cache_index + 1) % NODE_CACHE_MAX_KEYS;
245 return;
246 #endif
247 }
248
249 /* We're adding a new cache item. First, see if we have room for it
250 (otherwise, make some room). */
251 if (apr_hash_count(brd->node_cache) == NODE_CACHE_MAX_KEYS)
252 {
253 /* No room. Expire the oldest thing. */
254 cache_path = brd->node_cache_keys[brd->node_cache_idx];
255 cache_item = svn_hash_gets(brd->node_cache, cache_path);
256 svn_hash_sets(brd->node_cache, cache_path, NULL);
257 cache_pool = cache_item->pool;
258 svn_pool_clear(cache_pool);
259 }
260 else
261 {
262 cache_pool = svn_pool_create(root->pool);
263 }
264
265 /* Make the cache item, allocated in its own pool. */
266 cache_item = apr_palloc(cache_pool, sizeof(*cache_item));
267 cache_item->node = svn_fs_base__dag_dup(node, cache_pool);
268 cache_item->idx = brd->node_cache_idx;
269 cache_item->pool = cache_pool;
270
271 /* Now add it to the cache. */
272 cache_path = apr_pstrdup(cache_pool, path);
273 svn_hash_sets(brd->node_cache, cache_path, cache_item);
274 brd->node_cache_keys[brd->node_cache_idx] = cache_path;
275
276 /* Advance the cache pointer. */
277 brd->node_cache_idx = (brd->node_cache_idx + 1) % NODE_CACHE_MAX_KEYS;
278 }
279
280
281
282
283 /* Creating transaction and revision root nodes. */
284
285 struct txn_root_args
286 {
287 svn_fs_root_t **root_p;
288 svn_fs_txn_t *txn;
289 };
290
291
292 static svn_error_t *
txn_body_txn_root(void * baton,trail_t * trail)293 txn_body_txn_root(void *baton,
294 trail_t *trail)
295 {
296 struct txn_root_args *args = baton;
297 svn_fs_root_t **root_p = args->root_p;
298 svn_fs_txn_t *txn = args->txn;
299 svn_fs_t *fs = txn->fs;
300 const char *svn_txn_id = txn->id;
301 const svn_fs_id_t *root_id, *base_root_id;
302 svn_fs_root_t *root;
303 apr_hash_t *txnprops;
304 apr_uint32_t flags = 0;
305
306 /* Verify that the transaction actually exists. */
307 SVN_ERR(svn_fs_base__get_txn_ids(&root_id, &base_root_id, fs,
308 svn_txn_id, trail, trail->pool));
309
310 /* Look for special txn props that represent the 'flags' behavior of
311 the transaction. */
312 SVN_ERR(svn_fs_base__txn_proplist_in_trail(&txnprops, svn_txn_id, trail));
313 if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
314 flags |= SVN_FS_TXN_CHECK_OOD;
315
316 if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
317 flags |= SVN_FS_TXN_CHECK_LOCKS;
318
319 root = make_txn_root(fs, svn_txn_id, txn->base_rev, flags, trail->pool);
320
321 *root_p = root;
322 return SVN_NO_ERROR;
323 }
324
325
326 svn_error_t *
svn_fs_base__txn_root(svn_fs_root_t ** root_p,svn_fs_txn_t * txn,apr_pool_t * pool)327 svn_fs_base__txn_root(svn_fs_root_t **root_p,
328 svn_fs_txn_t *txn,
329 apr_pool_t *pool)
330 {
331 svn_fs_root_t *root;
332 struct txn_root_args args;
333
334 args.root_p = &root;
335 args.txn = txn;
336 SVN_ERR(svn_fs_base__retry_txn(txn->fs, txn_body_txn_root, &args,
337 FALSE, pool));
338
339 *root_p = root;
340 return SVN_NO_ERROR;
341 }
342
343
344 struct revision_root_args
345 {
346 svn_fs_root_t **root_p;
347 svn_revnum_t rev;
348 };
349
350
351 static svn_error_t *
txn_body_revision_root(void * baton,trail_t * trail)352 txn_body_revision_root(void *baton,
353 trail_t *trail)
354 {
355 struct revision_root_args *args = baton;
356 dag_node_t *root_dir;
357 svn_fs_root_t *root;
358
359 SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, args->rev,
360 trail, trail->pool));
361 root = make_revision_root(trail->fs, args->rev, root_dir, trail->pool);
362
363 *args->root_p = root;
364 return SVN_NO_ERROR;
365 }
366
367
368 svn_error_t *
svn_fs_base__revision_root(svn_fs_root_t ** root_p,svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)369 svn_fs_base__revision_root(svn_fs_root_t **root_p,
370 svn_fs_t *fs,
371 svn_revnum_t rev,
372 apr_pool_t *pool)
373 {
374 struct revision_root_args args;
375 svn_fs_root_t *root;
376
377 SVN_ERR(svn_fs__check_fs(fs, TRUE));
378
379 args.root_p = &root;
380 args.rev = rev;
381 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_revision_root, &args,
382 FALSE, pool));
383
384 *root_p = root;
385 return SVN_NO_ERROR;
386 }
387
388
389
390 /* Getting dag nodes for roots. */
391
392
393 /* Set *NODE_P to a freshly opened dag node referring to the root
394 directory of ROOT, as part of TRAIL. */
395 static svn_error_t *
root_node(dag_node_t ** node_p,svn_fs_root_t * root,trail_t * trail,apr_pool_t * pool)396 root_node(dag_node_t **node_p,
397 svn_fs_root_t *root,
398 trail_t *trail,
399 apr_pool_t *pool)
400 {
401 base_root_data_t *brd = root->fsap_data;
402
403 if (! root->is_txn_root)
404 {
405 /* It's a revision root, so we already have its root directory
406 opened. */
407 *node_p = svn_fs_base__dag_dup(brd->root_dir, pool);
408 return SVN_NO_ERROR;
409 }
410 else
411 {
412 /* It's a transaction root. Open a fresh copy. */
413 return svn_fs_base__dag_txn_root(node_p, root->fs, root->txn,
414 trail, pool);
415 }
416 }
417
418
419 /* Set *NODE_P to a mutable root directory for ROOT, cloning if
420 necessary, as part of TRAIL. ROOT must be a transaction root. Use
421 ERROR_PATH in error messages. */
422 static svn_error_t *
mutable_root_node(dag_node_t ** node_p,svn_fs_root_t * root,const char * error_path,trail_t * trail,apr_pool_t * pool)423 mutable_root_node(dag_node_t **node_p,
424 svn_fs_root_t *root,
425 const char *error_path,
426 trail_t *trail,
427 apr_pool_t *pool)
428 {
429 if (root->is_txn_root)
430 return svn_fs_base__dag_clone_root(node_p, root->fs, root->txn,
431 trail, pool);
432 else
433 /* If it's not a transaction root, we can't change its contents. */
434 return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
435 }
436
437
438
439 /* Traversing directory paths. */
440
441 typedef enum copy_id_inherit_t
442 {
443 copy_id_inherit_unknown = 0,
444 copy_id_inherit_self,
445 copy_id_inherit_parent,
446 copy_id_inherit_new
447
448 } copy_id_inherit_t;
449
450 /* A linked list representing the path from a node up to a root
451 directory. We use this for cloning, and for operations that need
452 to deal with both a node and its parent directory. For example, a
453 `delete' operation needs to know that the node actually exists, but
454 also needs to change the parent directory. */
455 typedef struct parent_path_t
456 {
457
458 /* A node along the path. This could be the final node, one of its
459 parents, or the root. Every parent path ends with an element for
460 the root directory. */
461 dag_node_t *node;
462
463 /* The name NODE has in its parent directory. This is zero for the
464 root directory, which (obviously) has no name in its parent. */
465 char *entry;
466
467 /* The parent of NODE, or zero if NODE is the root directory. */
468 struct parent_path_t *parent;
469
470 /* The copy ID inheritance style. */
471 copy_id_inherit_t copy_inherit;
472
473 /* If copy ID inheritance style is copy_id_inherit_new, this is the
474 path which should be implicitly copied; otherwise, this is NULL. */
475 const char *copy_src_path;
476
477 } parent_path_t;
478
479
480 /* Return the FS path for the parent path chain object PARENT_PATH,
481 allocated in POOL. */
482 static const char *
parent_path_path(parent_path_t * parent_path,apr_pool_t * pool)483 parent_path_path(parent_path_t *parent_path,
484 apr_pool_t *pool)
485 {
486 const char *path_so_far = "/";
487 if (parent_path->parent)
488 path_so_far = parent_path_path(parent_path->parent, pool);
489 return parent_path->entry
490 ? svn_fspath__join(path_so_far, parent_path->entry, pool)
491 : path_so_far;
492 }
493
494
495 /* Return the FS path for the parent path chain object CHILD relative
496 to its ANCESTOR in the same chain, allocated in POOL. */
497 static const char *
parent_path_relpath(parent_path_t * child,parent_path_t * ancestor,apr_pool_t * pool)498 parent_path_relpath(parent_path_t *child,
499 parent_path_t *ancestor,
500 apr_pool_t *pool)
501 {
502 const char *path_so_far = "";
503 parent_path_t *this_node = child;
504 while (this_node != ancestor)
505 {
506 assert(this_node != NULL);
507 path_so_far = svn_relpath_join(this_node->entry, path_so_far, pool);
508 this_node = this_node->parent;
509 }
510 return path_so_far;
511 }
512
513
514 /* Choose a copy ID inheritance method *INHERIT_P to be used in the
515 event that immutable node CHILD in FS needs to be made mutable. If
516 the inheritance method is copy_id_inherit_new, also return a
517 *COPY_SRC_PATH on which to base the new copy ID (else return NULL
518 for that path). CHILD must have a parent (it cannot be the root
519 node). TXN_ID is the transaction in which these items might be
520 mutable. */
521 static svn_error_t *
get_copy_inheritance(copy_id_inherit_t * inherit_p,const char ** copy_src_path,svn_fs_t * fs,parent_path_t * child,const char * txn_id,trail_t * trail,apr_pool_t * pool)522 get_copy_inheritance(copy_id_inherit_t *inherit_p,
523 const char **copy_src_path,
524 svn_fs_t *fs,
525 parent_path_t *child,
526 const char *txn_id,
527 trail_t *trail,
528 apr_pool_t *pool)
529 {
530 const svn_fs_id_t *child_id, *parent_id;
531 const char *child_copy_id, *parent_copy_id;
532 const char *id_path = NULL;
533
534 SVN_ERR_ASSERT(child && child->parent && txn_id);
535
536 /* Initialize our return variables (default: self-inheritance). */
537 *inherit_p = copy_id_inherit_self;
538 *copy_src_path = NULL;
539
540 /* Initialize some convenience variables. */
541 child_id = svn_fs_base__dag_get_id(child->node);
542 parent_id = svn_fs_base__dag_get_id(child->parent->node);
543 child_copy_id = svn_fs_base__id_copy_id(child_id);
544 parent_copy_id = svn_fs_base__id_copy_id(parent_id);
545
546 /* Easy out: if this child is already mutable, we have nothing to do. */
547 if (strcmp(svn_fs_base__id_txn_id(child_id), txn_id) == 0)
548 return SVN_NO_ERROR;
549
550 /* If the child and its parent are on the same branch, then the
551 child will inherit the copy ID of its parent when made mutable.
552 This is trivially detectable when the child and its parent have
553 the same copy ID. But that's not the sole indicator of
554 same-branchness. It might be the case that the parent was the
555 result of a copy, but the child has not yet been cloned for
556 mutability since that copy. Detection of this latter case
557 basically means making sure the copy IDs don't differ for some
558 other reason, such as that the child was the direct target of the
559 copy whose ID it has. There is a special case here, too -- if
560 the child's copy ID is the special ID "0", it can't have been the
561 target of any copy, and therefore must be on the same branch as
562 its parent. */
563 if ((strcmp(child_copy_id, "0") == 0)
564 || (strcmp(child_copy_id, parent_copy_id) == 0))
565 {
566 *inherit_p = copy_id_inherit_parent;
567 return SVN_NO_ERROR;
568 }
569 else
570 {
571 copy_t *copy;
572 SVN_ERR(svn_fs_bdb__get_copy(©, fs, child_copy_id, trail, pool));
573 if ( svn_fs_base__id_compare(copy->dst_noderev_id, child_id)
574 == svn_fs_node_unrelated)
575 {
576 *inherit_p = copy_id_inherit_parent;
577 return SVN_NO_ERROR;
578 }
579 }
580
581 /* If we get here, the child and its parent are not on speaking
582 terms -- there will be no parental inheritance handed down in
583 *this* generation. */
584
585 /* If the child was created at a different path than the one we are
586 expecting its clone to live, one of its parents must have been
587 created via a copy since the child was created. The child isn't
588 on the same branch as its parent (we caught those cases early);
589 it can't keep its current copy ID because there's been an
590 affecting copy (its clone won't be on the same branch as the
591 child is). That leaves only one course of action -- to assign
592 the child a brand new "soft" copy ID. */
593 id_path = svn_fs_base__dag_get_created_path(child->node);
594 if (strcmp(id_path, parent_path_path(child, pool)) != 0)
595 {
596 *inherit_p = copy_id_inherit_new;
597 *copy_src_path = id_path;
598 return SVN_NO_ERROR;
599 }
600
601 /* The node gets to keep its own ID. */
602 return SVN_NO_ERROR;
603 }
604
605
606 /* Allocate a new parent_path_t node from POOL, referring to NODE,
607 ENTRY, PARENT, and COPY_ID. */
608 static parent_path_t *
make_parent_path(dag_node_t * node,char * entry,parent_path_t * parent,apr_pool_t * pool)609 make_parent_path(dag_node_t *node,
610 char *entry,
611 parent_path_t *parent,
612 apr_pool_t *pool)
613 {
614 parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path));
615 parent_path->node = node;
616 parent_path->entry = entry;
617 parent_path->parent = parent;
618 parent_path->copy_inherit = copy_id_inherit_unknown;
619 parent_path->copy_src_path = NULL;
620 return parent_path;
621 }
622
623
624 /* Flags for open_path. */
625 typedef enum open_path_flags_t {
626
627 /* The last component of the PATH need not exist. (All parent
628 directories must exist, as usual.) If the last component doesn't
629 exist, simply leave the `node' member of the bottom parent_path
630 component zero. */
631 open_path_last_optional = 1
632
633 } open_path_flags_t;
634
635
636 /* Open the node identified by PATH in ROOT, as part of TRAIL. Set
637 *PARENT_PATH_P to a path from the node up to ROOT, allocated in
638 TRAIL->pool. The resulting *PARENT_PATH_P value is guaranteed to
639 contain at least one element, for the root directory.
640
641 If resulting *PARENT_PATH_P will eventually be made mutable and
642 modified, or if copy ID inheritance information is otherwise
643 needed, TXN_ID should be the ID of the mutability transaction. If
644 TXN_ID is NULL, no copy ID in heritance information will be
645 calculated for the *PARENT_PATH_P chain.
646
647 If FLAGS & open_path_last_optional is zero, return the error
648 SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist. If
649 non-zero, require all the parent directories to exist as normal,
650 but if the final path component doesn't exist, simply return a path
651 whose bottom `node' member is zero. This option is useful for
652 callers that create new nodes --- we find the parent directory for
653 them, and tell them whether the entry exists already.
654
655 NOTE: Public interfaces which only *read* from the filesystem
656 should not call this function directly, but should instead use
657 get_dag().
658 */
659 static svn_error_t *
open_path(parent_path_t ** parent_path_p,svn_fs_root_t * root,const char * path,int flags,const char * txn_id,trail_t * trail,apr_pool_t * pool)660 open_path(parent_path_t **parent_path_p,
661 svn_fs_root_t *root,
662 const char *path,
663 int flags,
664 const char *txn_id,
665 trail_t *trail,
666 apr_pool_t *pool)
667 {
668 svn_fs_t *fs = root->fs;
669 dag_node_t *here; /* The directory we're currently looking at. */
670 parent_path_t *parent_path; /* The path from HERE up to the root. */
671 const char *rest; /* The portion of PATH we haven't traversed yet. */
672 const char *canon_path = svn_fs__canonicalize_abspath(path, pool);
673 const char *path_so_far = "/";
674
675 /* Make a parent_path item for the root node, using its own current
676 copy id. */
677 SVN_ERR(root_node(&here, root, trail, pool));
678 parent_path = make_parent_path(here, 0, 0, pool);
679 parent_path->copy_inherit = copy_id_inherit_self;
680
681 rest = canon_path + 1; /* skip the leading '/', it saves in iteration */
682
683 /* Whenever we are at the top of this loop:
684 - HERE is our current directory,
685 - ID is the node revision ID of HERE,
686 - REST is the path we're going to find in HERE, and
687 - PARENT_PATH includes HERE and all its parents. */
688 for (;;)
689 {
690 const char *next;
691 char *entry;
692 dag_node_t *child;
693
694 /* Parse out the next entry from the path. */
695 entry = svn_fs__next_entry_name(&next, rest, pool);
696
697 /* Calculate the path traversed thus far. */
698 path_so_far = svn_fspath__join(path_so_far, entry, pool);
699
700 if (*entry == '\0')
701 {
702 /* Given the behavior of svn_fs__next_entry_name(), this
703 happens when the path either starts or ends with a slash.
704 In either case, we stay put: the current directory stays
705 the same, and we add nothing to the parent path. */
706 child = here;
707 }
708 else
709 {
710 copy_id_inherit_t inherit;
711 const char *copy_path = NULL;
712 svn_error_t *err = SVN_NO_ERROR;
713 dag_node_t *cached_node;
714
715 /* If we found a directory entry, follow it. First, we
716 check our node cache, and, failing that, we hit the DAG
717 layer. */
718 cached_node = dag_node_cache_get(root, path_so_far, pool);
719 if (cached_node)
720 child = cached_node;
721 else
722 err = svn_fs_base__dag_open(&child, here, entry, trail, pool);
723
724 /* "file not found" requires special handling. */
725 if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
726 {
727 /* If this was the last path component, and the caller
728 said it was optional, then don't return an error;
729 just put a NULL node pointer in the path. */
730
731 svn_error_clear(err);
732
733 if ((flags & open_path_last_optional)
734 && (! next || *next == '\0'))
735 {
736 parent_path = make_parent_path(NULL, entry, parent_path,
737 pool);
738 break;
739 }
740 else
741 {
742 /* Build a better error message than svn_fs_base__dag_open
743 can provide, giving the root and full path name. */
744 return SVN_FS__NOT_FOUND(root, path);
745 }
746 }
747
748 /* Other errors we return normally. */
749 SVN_ERR(err);
750
751 /* Now, make a parent_path item for CHILD. */
752 parent_path = make_parent_path(child, entry, parent_path, pool);
753 if (txn_id)
754 {
755 SVN_ERR(get_copy_inheritance(&inherit, ©_path,
756 fs, parent_path, txn_id,
757 trail, pool));
758 parent_path->copy_inherit = inherit;
759 parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
760 }
761
762 /* Cache the node we found (if it wasn't already cached). */
763 if (! cached_node)
764 dag_node_cache_set(root, path_so_far, child);
765 }
766
767 /* Are we finished traversing the path? */
768 if (! next)
769 break;
770
771 /* The path isn't finished yet; we'd better be in a directory. */
772 if (svn_fs_base__dag_node_kind(child) != svn_node_dir)
773 SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far),
774 apr_psprintf(pool, _("Failure opening '%s'"), path));
775
776 rest = next;
777 here = child;
778 }
779
780 *parent_path_p = parent_path;
781 return SVN_NO_ERROR;
782 }
783
784
785 /* Make the node referred to by PARENT_PATH mutable, if it isn't
786 already, as part of TRAIL. ROOT must be the root from which
787 PARENT_PATH descends. Clone any parent directories as needed.
788 Adjust the dag nodes in PARENT_PATH to refer to the clones. Use
789 ERROR_PATH in error messages. */
790 static svn_error_t *
make_path_mutable(svn_fs_root_t * root,parent_path_t * parent_path,const char * error_path,trail_t * trail,apr_pool_t * pool)791 make_path_mutable(svn_fs_root_t *root,
792 parent_path_t *parent_path,
793 const char *error_path,
794 trail_t *trail,
795 apr_pool_t *pool)
796 {
797 dag_node_t *cloned_node;
798 const char *txn_id = root->txn;
799 svn_fs_t *fs = root->fs;
800
801 /* Is the node mutable already? */
802 if (svn_fs_base__dag_check_mutable(parent_path->node, txn_id))
803 return SVN_NO_ERROR;
804
805 /* Are we trying to clone the root, or somebody's child node? */
806 if (parent_path->parent)
807 {
808 const svn_fs_id_t *parent_id;
809 const svn_fs_id_t *node_id = svn_fs_base__dag_get_id(parent_path->node);
810 const char *copy_id = NULL;
811 const char *copy_src_path = parent_path->copy_src_path;
812 copy_id_inherit_t inherit = parent_path->copy_inherit;
813 const char *clone_path;
814
815 /* We're trying to clone somebody's child. Make sure our parent
816 is mutable. */
817 SVN_ERR(make_path_mutable(root, parent_path->parent,
818 error_path, trail, pool));
819
820 switch (inherit)
821 {
822 case copy_id_inherit_parent:
823 parent_id = svn_fs_base__dag_get_id(parent_path->parent->node);
824 copy_id = svn_fs_base__id_copy_id(parent_id);
825 break;
826
827 case copy_id_inherit_new:
828 SVN_ERR(svn_fs_bdb__reserve_copy_id(©_id, fs, trail, pool));
829 break;
830
831 case copy_id_inherit_self:
832 copy_id = NULL;
833 break;
834
835 case copy_id_inherit_unknown:
836 default:
837 SVN_ERR_MALFUNCTION(); /* uh-oh -- somebody didn't calculate copy-ID
838 inheritance data. */
839 }
840
841 /* Now make this node mutable. */
842 clone_path = parent_path_path(parent_path->parent, pool);
843 SVN_ERR(svn_fs_base__dag_clone_child(&cloned_node,
844 parent_path->parent->node,
845 clone_path,
846 parent_path->entry,
847 copy_id, txn_id,
848 trail, pool));
849
850 /* If we just created a brand new copy ID, we need to store a
851 `copies' table entry for it, as well as a notation in the
852 transaction that should this transaction be terminated, our
853 new copy needs to be removed. */
854 if (inherit == copy_id_inherit_new)
855 {
856 const svn_fs_id_t *new_node_id =
857 svn_fs_base__dag_get_id(cloned_node);
858 SVN_ERR(svn_fs_bdb__create_copy(fs, copy_id, copy_src_path,
859 svn_fs_base__id_txn_id(node_id),
860 new_node_id,
861 copy_kind_soft, trail, pool));
862 SVN_ERR(svn_fs_base__add_txn_copy(fs, txn_id, copy_id,
863 trail, pool));
864 }
865 }
866 else
867 {
868 /* We're trying to clone the root directory. */
869 SVN_ERR(mutable_root_node(&cloned_node, root, error_path, trail, pool));
870 }
871
872 /* Update the PARENT_PATH link to refer to the clone. */
873 parent_path->node = cloned_node;
874
875 return SVN_NO_ERROR;
876 }
877
878
879 /* Walk up PARENT_PATH to the root of the tree, adjusting each node's
880 mergeinfo count by COUNT_DELTA as part of Subversion transaction
881 TXN_ID and TRAIL. Use POOL for allocations. */
882 static svn_error_t *
adjust_parent_mergeinfo_counts(parent_path_t * parent_path,apr_int64_t count_delta,const char * txn_id,trail_t * trail,apr_pool_t * pool)883 adjust_parent_mergeinfo_counts(parent_path_t *parent_path,
884 apr_int64_t count_delta,
885 const char *txn_id,
886 trail_t *trail,
887 apr_pool_t *pool)
888 {
889 apr_pool_t *iterpool;
890 parent_path_t *pp = parent_path;
891
892 if (count_delta == 0)
893 return SVN_NO_ERROR;
894
895 iterpool = svn_pool_create(pool);
896
897 while (pp)
898 {
899 svn_pool_clear(iterpool);
900 SVN_ERR(svn_fs_base__dag_adjust_mergeinfo_count(pp->node, count_delta,
901 txn_id, trail,
902 iterpool));
903 pp = pp->parent;
904 }
905 svn_pool_destroy(iterpool);
906
907 return SVN_NO_ERROR;
908 }
909
910
911 /* Open the node identified by PATH in ROOT, as part of TRAIL. Set
912 *DAG_NODE_P to the node we find, allocated in TRAIL->pool. Return
913 the error SVN_ERR_FS_NOT_FOUND if this node doesn't exist. */
914 static svn_error_t *
get_dag(dag_node_t ** dag_node_p,svn_fs_root_t * root,const char * path,trail_t * trail,apr_pool_t * pool)915 get_dag(dag_node_t **dag_node_p,
916 svn_fs_root_t *root,
917 const char *path,
918 trail_t *trail,
919 apr_pool_t *pool)
920 {
921 parent_path_t *parent_path;
922 dag_node_t *node = NULL;
923
924 /* Canonicalize the input PATH. */
925 path = svn_fs__canonicalize_abspath(path, pool);
926
927 /* If ROOT is a revision root, we'll look for the DAG in our cache. */
928 node = dag_node_cache_get(root, path, pool);
929 if (! node)
930 {
931 /* Call open_path with no flags, as we want this to return an error
932 if the node for which we are searching doesn't exist. */
933 SVN_ERR(open_path(&parent_path, root, path, 0, NULL, trail, pool));
934 node = parent_path->node;
935
936 /* No need to cache our find -- open_path() will do that for us. */
937 }
938
939 *dag_node_p = node;
940 return SVN_NO_ERROR;
941 }
942
943
944
945 /* Populating the `changes' table. */
946
947 /* Add a change to the changes table in FS, keyed on transaction id
948 TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on
949 PATH (whose node revision id is--or was, in the case of a
950 deletion--NODEREV_ID), and optionally that TEXT_MODs or PROP_MODs
951 occurred. Do all this as part of TRAIL. */
952 static svn_error_t *
add_change(svn_fs_t * fs,const char * txn_id,const char * path,const svn_fs_id_t * noderev_id,svn_fs_path_change_kind_t change_kind,svn_boolean_t text_mod,svn_boolean_t prop_mod,trail_t * trail,apr_pool_t * pool)953 add_change(svn_fs_t *fs,
954 const char *txn_id,
955 const char *path,
956 const svn_fs_id_t *noderev_id,
957 svn_fs_path_change_kind_t change_kind,
958 svn_boolean_t text_mod,
959 svn_boolean_t prop_mod,
960 trail_t *trail,
961 apr_pool_t *pool)
962 {
963 change_t change;
964 change.path = svn_fs__canonicalize_abspath(path, pool);
965 change.noderev_id = noderev_id;
966 change.kind = change_kind;
967 change.text_mod = text_mod;
968 change.prop_mod = prop_mod;
969 return svn_fs_bdb__changes_add(fs, txn_id, &change, trail, pool);
970 }
971
972
973
974 /* Generic node operations. */
975
976
977 struct node_id_args {
978 const svn_fs_id_t **id_p;
979 svn_fs_root_t *root;
980 const char *path;
981 };
982
983
984 static svn_error_t *
txn_body_node_id(void * baton,trail_t * trail)985 txn_body_node_id(void *baton, trail_t *trail)
986 {
987 struct node_id_args *args = baton;
988 dag_node_t *node;
989
990 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
991 *args->id_p = svn_fs_base__id_copy(svn_fs_base__dag_get_id(node),
992 trail->pool);
993
994 return SVN_NO_ERROR;
995 }
996
997
998 static svn_error_t *
base_node_id(const svn_fs_id_t ** id_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)999 base_node_id(const svn_fs_id_t **id_p,
1000 svn_fs_root_t *root,
1001 const char *path,
1002 apr_pool_t *pool)
1003 {
1004 base_root_data_t *brd = root->fsap_data;
1005
1006 if (! root->is_txn_root
1007 && (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0'))))
1008 {
1009 /* Optimize the case where we don't need any db access at all.
1010 The root directory ("" or "/") node is stored in the
1011 svn_fs_root_t object, and never changes when it's a revision
1012 root, so we can just reach in and grab it directly. */
1013 *id_p = svn_fs_base__id_copy(svn_fs_base__dag_get_id(brd->root_dir),
1014 pool);
1015 }
1016 else
1017 {
1018 const svn_fs_id_t *id;
1019 struct node_id_args args;
1020
1021 args.id_p = &id;
1022 args.root = root;
1023 args.path = path;
1024
1025 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_id, &args,
1026 FALSE, pool));
1027 *id_p = id;
1028 }
1029 return SVN_NO_ERROR;
1030 }
1031
1032 static svn_error_t *
base_node_relation(svn_fs_node_relation_t * relation,svn_fs_root_t * root_a,const char * path_a,svn_fs_root_t * root_b,const char * path_b,apr_pool_t * pool)1033 base_node_relation(svn_fs_node_relation_t *relation,
1034 svn_fs_root_t *root_a, const char *path_a,
1035 svn_fs_root_t *root_b, const char *path_b,
1036 apr_pool_t *pool)
1037 {
1038 const svn_fs_id_t *id_a, *id_b;
1039
1040 /* Paths from different repository are never related. */
1041 if (root_a->fs != root_b->fs)
1042 {
1043 *relation = svn_fs_node_unrelated;
1044 return SVN_NO_ERROR;
1045 }
1046
1047 /* Naive implementation. */
1048 SVN_ERR(base_node_id(&id_a, root_a, path_a, pool));
1049 SVN_ERR(base_node_id(&id_b, root_b, path_b, pool));
1050
1051 *relation = svn_fs_base__id_compare(id_a, id_b);
1052
1053 return SVN_NO_ERROR;
1054 }
1055
1056
1057 struct node_created_rev_args {
1058 svn_revnum_t revision;
1059 svn_fs_root_t *root;
1060 const char *path;
1061 };
1062
1063
1064 static svn_error_t *
txn_body_node_created_rev(void * baton,trail_t * trail)1065 txn_body_node_created_rev(void *baton, trail_t *trail)
1066 {
1067 struct node_created_rev_args *args = baton;
1068 dag_node_t *node;
1069
1070 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1071 return svn_fs_base__dag_get_revision(&(args->revision), node,
1072 trail, trail->pool);
1073 }
1074
1075
1076 static svn_error_t *
base_node_created_rev(svn_revnum_t * revision,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1077 base_node_created_rev(svn_revnum_t *revision,
1078 svn_fs_root_t *root,
1079 const char *path,
1080 apr_pool_t *pool)
1081 {
1082 struct node_created_rev_args args;
1083
1084 args.revision = SVN_INVALID_REVNUM;
1085 args.root = root;
1086 args.path = path;
1087 SVN_ERR(svn_fs_base__retry_txn
1088 (root->fs, txn_body_node_created_rev, &args, TRUE, pool));
1089 *revision = args.revision;
1090 return SVN_NO_ERROR;
1091 }
1092
1093
1094 struct node_created_path_args {
1095 const char **created_path;
1096 svn_fs_root_t *root;
1097 const char *path;
1098 };
1099
1100
1101 static svn_error_t *
txn_body_node_created_path(void * baton,trail_t * trail)1102 txn_body_node_created_path(void *baton, trail_t *trail)
1103 {
1104 struct node_created_path_args *args = baton;
1105 dag_node_t *node;
1106
1107 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1108 *args->created_path = svn_fs_base__dag_get_created_path(node);
1109 return SVN_NO_ERROR;
1110 }
1111
1112
1113 static svn_error_t *
base_node_created_path(const char ** created_path,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1114 base_node_created_path(const char **created_path,
1115 svn_fs_root_t *root,
1116 const char *path,
1117 apr_pool_t *pool)
1118 {
1119 struct node_created_path_args args;
1120 apr_pool_t *scratch_pool = svn_pool_create(pool);
1121
1122 args.created_path = created_path;
1123 args.root = root;
1124 args.path = path;
1125
1126 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_created_path, &args,
1127 FALSE, scratch_pool));
1128 if (*created_path)
1129 *created_path = apr_pstrdup(pool, *created_path);
1130 svn_pool_destroy(scratch_pool);
1131 return SVN_NO_ERROR;
1132 }
1133
1134
1135 struct node_kind_args {
1136 const svn_fs_id_t *id;
1137 svn_node_kind_t kind; /* OUT parameter */
1138 };
1139
1140
1141 static svn_error_t *
txn_body_node_kind(void * baton,trail_t * trail)1142 txn_body_node_kind(void *baton, trail_t *trail)
1143 {
1144 struct node_kind_args *args = baton;
1145 dag_node_t *node;
1146
1147 SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id,
1148 trail, trail->pool));
1149 args->kind = svn_fs_base__dag_node_kind(node);
1150
1151 return SVN_NO_ERROR;
1152 }
1153
1154
1155 static svn_error_t *
node_kind(svn_node_kind_t * kind_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1156 node_kind(svn_node_kind_t *kind_p,
1157 svn_fs_root_t *root,
1158 const char *path,
1159 apr_pool_t *pool)
1160 {
1161 struct node_kind_args args;
1162 const svn_fs_id_t *node_id;
1163
1164 /* Get the node id. */
1165 SVN_ERR(base_node_id(&node_id, root, path, pool));
1166
1167 /* Use the node id to get the real kind. */
1168 args.id = node_id;
1169 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_kind, &args,
1170 TRUE, pool));
1171
1172 *kind_p = args.kind;
1173 return SVN_NO_ERROR;
1174 }
1175
1176
1177 static svn_error_t *
base_check_path(svn_node_kind_t * kind_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1178 base_check_path(svn_node_kind_t *kind_p,
1179 svn_fs_root_t *root,
1180 const char *path,
1181 apr_pool_t *pool)
1182 {
1183 svn_error_t *err = node_kind(kind_p, root, path, pool);
1184 if (err &&
1185 ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
1186 || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY)))
1187 {
1188 svn_error_clear(err);
1189 err = SVN_NO_ERROR;
1190 *kind_p = svn_node_none;
1191 }
1192
1193 return svn_error_trace(err);
1194 }
1195
1196
1197 struct node_prop_args
1198 {
1199 svn_string_t **value_p;
1200 svn_fs_root_t *root;
1201 const char *path;
1202 const char *propname;
1203 };
1204
1205
1206 static svn_error_t *
txn_body_node_prop(void * baton,trail_t * trail)1207 txn_body_node_prop(void *baton,
1208 trail_t *trail)
1209 {
1210 struct node_prop_args *args = baton;
1211 dag_node_t *node;
1212 apr_hash_t *proplist;
1213
1214 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1215 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node,
1216 trail, trail->pool));
1217 *(args->value_p) = NULL;
1218 if (proplist)
1219 *(args->value_p) = svn_hash_gets(proplist, args->propname);
1220 return SVN_NO_ERROR;
1221 }
1222
1223
1224 static svn_error_t *
base_node_prop(svn_string_t ** value_p,svn_fs_root_t * root,const char * path,const char * propname,apr_pool_t * pool)1225 base_node_prop(svn_string_t **value_p,
1226 svn_fs_root_t *root,
1227 const char *path,
1228 const char *propname,
1229 apr_pool_t *pool)
1230 {
1231 struct node_prop_args args;
1232 svn_string_t *value;
1233 apr_pool_t *scratch_pool = svn_pool_create(pool);
1234
1235 args.value_p = &value;
1236 args.root = root;
1237 args.path = path;
1238 args.propname = propname;
1239 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_prop, &args,
1240 FALSE, scratch_pool));
1241 *value_p = svn_string_dup(value, pool);
1242 svn_pool_destroy(scratch_pool);
1243 return SVN_NO_ERROR;
1244 }
1245
1246
1247 struct node_proplist_args {
1248 apr_hash_t **table_p;
1249 svn_fs_root_t *root;
1250 const char *path;
1251 };
1252
1253
1254 static svn_error_t *
txn_body_node_proplist(void * baton,trail_t * trail)1255 txn_body_node_proplist(void *baton, trail_t *trail)
1256 {
1257 struct node_proplist_args *args = baton;
1258 dag_node_t *node;
1259 apr_hash_t *proplist;
1260
1261 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1262 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node,
1263 trail, trail->pool));
1264 *args->table_p = proplist ? proplist : apr_hash_make(trail->pool);
1265 return SVN_NO_ERROR;
1266 }
1267
1268
1269 static svn_error_t *
base_node_proplist(apr_hash_t ** table_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1270 base_node_proplist(apr_hash_t **table_p,
1271 svn_fs_root_t *root,
1272 const char *path,
1273 apr_pool_t *pool)
1274 {
1275 apr_hash_t *table;
1276 struct node_proplist_args args;
1277
1278 args.table_p = &table;
1279 args.root = root;
1280 args.path = path;
1281
1282 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_proplist, &args,
1283 FALSE, pool));
1284
1285 *table_p = table;
1286 return SVN_NO_ERROR;
1287 }
1288
1289 static svn_error_t *
base_node_has_props(svn_boolean_t * has_props,svn_fs_root_t * root,const char * path,apr_pool_t * scratch_pool)1290 base_node_has_props(svn_boolean_t *has_props,
1291 svn_fs_root_t *root,
1292 const char *path,
1293 apr_pool_t *scratch_pool)
1294 {
1295 apr_hash_t *props;
1296
1297 SVN_ERR(base_node_proplist(&props, root, path, scratch_pool));
1298
1299 *has_props = (0 < apr_hash_count(props));
1300
1301 return SVN_NO_ERROR;
1302 }
1303
1304
1305 struct change_node_prop_args {
1306 svn_fs_root_t *root;
1307 const char *path;
1308 const char *name;
1309 const svn_string_t *value;
1310 };
1311
1312
1313 static svn_error_t *
txn_body_change_node_prop(void * baton,trail_t * trail)1314 txn_body_change_node_prop(void *baton,
1315 trail_t *trail)
1316 {
1317 struct change_node_prop_args *args = baton;
1318 parent_path_t *parent_path;
1319 apr_hash_t *proplist;
1320 const char *txn_id = args->root->txn;
1321 base_fs_data_t *bfd = trail->fs->fsap_data;
1322
1323 SVN_ERR(open_path(&parent_path, args->root, args->path, 0, txn_id,
1324 trail, trail->pool));
1325
1326 /* Check to see if path is locked; if so, check that we can use it.
1327 Notice that we're doing this non-recursively, regardless of node kind. */
1328 if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
1329 SVN_ERR(svn_fs_base__allow_locked_operation
1330 (args->path, FALSE, trail, trail->pool));
1331
1332 SVN_ERR(make_path_mutable(args->root, parent_path, args->path,
1333 trail, trail->pool));
1334 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, parent_path->node,
1335 trail, trail->pool));
1336
1337 /* If there's no proplist, but we're just deleting a property, exit now. */
1338 if ((! proplist) && (! args->value))
1339 return SVN_NO_ERROR;
1340
1341 /* Now, if there's no proplist, we know we need to make one. */
1342 if (! proplist)
1343 proplist = apr_hash_make(trail->pool);
1344
1345 /* Set the property. */
1346 svn_hash_sets(proplist, args->name, args->value);
1347
1348 /* Overwrite the node's proplist. */
1349 SVN_ERR(svn_fs_base__dag_set_proplist(parent_path->node, proplist,
1350 txn_id, trail, trail->pool));
1351
1352 /* If this was a change to the mergeinfo property, and our version
1353 of the filesystem cares, we have some extra recording to do.
1354
1355 ### If the format *doesn't* support mergeinfo recording, should
1356 ### we fuss about attempts to change the svn:mergeinfo property
1357 ### in any way save to delete it? */
1358 if ((bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
1359 && (strcmp(args->name, SVN_PROP_MERGEINFO) == 0))
1360 {
1361 svn_boolean_t had_mergeinfo, has_mergeinfo = args->value != NULL;
1362
1363 /* First, note on our node that it has mergeinfo. */
1364 SVN_ERR(svn_fs_base__dag_set_has_mergeinfo(parent_path->node,
1365 has_mergeinfo,
1366 &had_mergeinfo, txn_id,
1367 trail, trail->pool));
1368
1369 /* If this is a change from the old state, we need to update our
1370 node's parents' mergeinfo counts by a factor of 1. */
1371 if (parent_path->parent && ((! had_mergeinfo) != (! has_mergeinfo)))
1372 SVN_ERR(adjust_parent_mergeinfo_counts(parent_path->parent,
1373 has_mergeinfo ? 1 : -1,
1374 txn_id, trail, trail->pool));
1375 }
1376
1377 /* Make a record of this modification in the changes table. */
1378 return add_change(args->root->fs, txn_id,
1379 args->path, svn_fs_base__dag_get_id(parent_path->node),
1380 svn_fs_path_change_modify, FALSE, TRUE, trail,
1381 trail->pool);
1382 }
1383
1384
1385 static svn_error_t *
base_change_node_prop(svn_fs_root_t * root,const char * path,const char * name,const svn_string_t * value,apr_pool_t * pool)1386 base_change_node_prop(svn_fs_root_t *root,
1387 const char *path,
1388 const char *name,
1389 const svn_string_t *value,
1390 apr_pool_t *pool)
1391 {
1392 struct change_node_prop_args args;
1393
1394 if (! root->is_txn_root)
1395 return SVN_FS__NOT_TXN(root);
1396
1397 args.root = root;
1398 args.path = path;
1399 args.name = name;
1400 args.value = value;
1401 return svn_fs_base__retry_txn(root->fs, txn_body_change_node_prop, &args,
1402 TRUE, pool);
1403 }
1404
1405
1406 struct things_changed_args
1407 {
1408 svn_boolean_t *changed_p;
1409 svn_fs_root_t *root1;
1410 svn_fs_root_t *root2;
1411 const char *path1;
1412 const char *path2;
1413 svn_boolean_t strict;
1414 apr_pool_t *pool;
1415 };
1416
1417
1418 static svn_error_t *
txn_body_props_changed(void * baton,trail_t * trail)1419 txn_body_props_changed(void *baton, trail_t *trail)
1420 {
1421 struct things_changed_args *args = baton;
1422 dag_node_t *node1, *node2;
1423 apr_hash_t *proplist1, *proplist2;
1424
1425 SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool));
1426 SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool));
1427 SVN_ERR(svn_fs_base__things_different(args->changed_p, NULL,
1428 node1, node2, trail, trail->pool));
1429
1430 /* Is there a potential false positive and do we want to correct it? */
1431 if (!args->strict || !*args->changed_p)
1432 return SVN_NO_ERROR;
1433
1434 /* Different representations. They might still have equal contents. */
1435 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist1, node1,
1436 trail, trail->pool));
1437 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist2, node2,
1438 trail, trail->pool));
1439
1440 *args->changed_p = !svn_fs__prop_lists_equal(proplist1, proplist2,
1441 trail->pool);
1442 return SVN_NO_ERROR;
1443 }
1444
1445
1446 static svn_error_t *
base_props_changed(svn_boolean_t * changed_p,svn_fs_root_t * root1,const char * path1,svn_fs_root_t * root2,const char * path2,svn_boolean_t strict,apr_pool_t * pool)1447 base_props_changed(svn_boolean_t *changed_p,
1448 svn_fs_root_t *root1,
1449 const char *path1,
1450 svn_fs_root_t *root2,
1451 const char *path2,
1452 svn_boolean_t strict,
1453 apr_pool_t *pool)
1454 {
1455 struct things_changed_args args;
1456
1457 /* Check that roots are in the same fs. */
1458 if (root1->fs != root2->fs)
1459 return svn_error_create
1460 (SVN_ERR_FS_GENERAL, NULL,
1461 _("Cannot compare property value between two different filesystems"));
1462
1463 args.root1 = root1;
1464 args.root2 = root2;
1465 args.path1 = path1;
1466 args.path2 = path2;
1467 args.changed_p = changed_p;
1468 args.pool = pool;
1469 args.strict = strict;
1470
1471 return svn_fs_base__retry_txn(root1->fs, txn_body_props_changed, &args,
1472 TRUE, pool);
1473 }
1474
1475
1476
1477 /* Miscellaneous table handling */
1478
1479 struct miscellaneous_set_args
1480 {
1481 const char *key;
1482 const char *val;
1483 };
1484
1485 static svn_error_t *
txn_body_miscellaneous_set(void * baton,trail_t * trail)1486 txn_body_miscellaneous_set(void *baton, trail_t *trail)
1487 {
1488 struct miscellaneous_set_args *msa = baton;
1489
1490 return svn_fs_bdb__miscellaneous_set(trail->fs, msa->key, msa->val, trail,
1491 trail->pool);
1492 }
1493
1494 svn_error_t *
svn_fs_base__miscellaneous_set(svn_fs_t * fs,const char * key,const char * val,apr_pool_t * pool)1495 svn_fs_base__miscellaneous_set(svn_fs_t *fs,
1496 const char *key,
1497 const char *val,
1498 apr_pool_t *pool)
1499 {
1500 struct miscellaneous_set_args msa;
1501 msa.key = key;
1502 msa.val = val;
1503
1504 return svn_fs_base__retry_txn(fs, txn_body_miscellaneous_set, &msa,
1505 TRUE, pool);
1506 }
1507
1508 struct miscellaneous_get_args
1509 {
1510 const char *key;
1511 const char **val;
1512 };
1513
1514 static svn_error_t *
txn_body_miscellaneous_get(void * baton,trail_t * trail)1515 txn_body_miscellaneous_get(void *baton, trail_t *trail)
1516 {
1517 struct miscellaneous_get_args *mga = baton;
1518 return svn_fs_bdb__miscellaneous_get(mga->val, trail->fs, mga->key, trail,
1519 trail->pool);
1520 }
1521
1522 svn_error_t *
svn_fs_base__miscellaneous_get(const char ** val,svn_fs_t * fs,const char * key,apr_pool_t * pool)1523 svn_fs_base__miscellaneous_get(const char **val,
1524 svn_fs_t *fs,
1525 const char *key,
1526 apr_pool_t *pool)
1527 {
1528 struct miscellaneous_get_args mga;
1529 apr_pool_t *scratch_pool = svn_pool_create(pool);
1530
1531 mga.key = key;
1532 mga.val = val;
1533 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_miscellaneous_get, &mga,
1534 FALSE, scratch_pool));
1535 if (*val)
1536 *val = apr_pstrdup(pool, *val);
1537 svn_pool_destroy(scratch_pool);
1538 return SVN_NO_ERROR;
1539 }
1540
1541
1542
1543 /* Getting a directory's entries */
1544
1545
1546 struct dir_entries_args
1547 {
1548 apr_hash_t **table_p;
1549 svn_fs_root_t *root;
1550 const char *path;
1551 };
1552
1553
1554 /* *(BATON->table_p) will never be NULL on successful return */
1555 static svn_error_t *
txn_body_dir_entries(void * baton,trail_t * trail)1556 txn_body_dir_entries(void *baton,
1557 trail_t *trail)
1558 {
1559 struct dir_entries_args *args = baton;
1560 dag_node_t *node;
1561 apr_hash_t *entries;
1562
1563 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1564
1565 /* Get the entries for PARENT_PATH. */
1566 SVN_ERR(svn_fs_base__dag_dir_entries(&entries, node, trail, trail->pool));
1567
1568 /* Potentially initialize the return value to an empty hash. */
1569 *args->table_p = entries ? entries : apr_hash_make(trail->pool);
1570 return SVN_NO_ERROR;
1571 }
1572
1573
1574 static svn_error_t *
base_dir_entries(apr_hash_t ** table_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1575 base_dir_entries(apr_hash_t **table_p,
1576 svn_fs_root_t *root,
1577 const char *path,
1578 apr_pool_t *pool)
1579 {
1580 struct dir_entries_args args;
1581 apr_pool_t *iterpool;
1582 apr_hash_t *table;
1583 svn_fs_t *fs = root->fs;
1584 apr_hash_index_t *hi;
1585
1586 args.table_p = &table;
1587 args.root = root;
1588 args.path = path;
1589 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_dir_entries, &args,
1590 FALSE, pool));
1591
1592 iterpool = svn_pool_create(pool);
1593
1594 /* Add in the kind data. */
1595 for (hi = apr_hash_first(pool, table); hi; hi = apr_hash_next(hi))
1596 {
1597 svn_fs_dirent_t *entry;
1598 struct node_kind_args nk_args;
1599 void *val;
1600
1601 svn_pool_clear(iterpool);
1602
1603 /* KEY will be the entry name in ancestor (about which we
1604 simply don't care), VAL the dirent. */
1605 apr_hash_this(hi, NULL, NULL, &val);
1606 entry = val;
1607 nk_args.id = entry->id;
1608
1609 /* We don't need to have the retry function destroy the trail
1610 pool because we're already doing that via the use of an
1611 iteration pool. */
1612 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_node_kind, &nk_args,
1613 FALSE, iterpool));
1614 entry->kind = nk_args.kind;
1615 }
1616
1617 svn_pool_destroy(iterpool);
1618
1619 *table_p = table;
1620 return SVN_NO_ERROR;
1621 }
1622
1623 static svn_error_t *
base_dir_optimal_order(apr_array_header_t ** ordered_p,svn_fs_root_t * root,apr_hash_t * entries,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1624 base_dir_optimal_order(apr_array_header_t **ordered_p,
1625 svn_fs_root_t *root,
1626 apr_hash_t *entries,
1627 apr_pool_t *result_pool,
1628 apr_pool_t *scratch_pool)
1629 {
1630 /* 1:1 copy of entries with no differnce in ordering */
1631 apr_hash_index_t *hi;
1632 apr_array_header_t *result
1633 = apr_array_make(result_pool, apr_hash_count(entries),
1634 sizeof(svn_fs_dirent_t *));
1635 for (hi = apr_hash_first(scratch_pool, entries); hi; hi = apr_hash_next(hi))
1636 APR_ARRAY_PUSH(result, svn_fs_dirent_t *) = apr_hash_this_val(hi);
1637
1638 *ordered_p = result;
1639 return SVN_NO_ERROR;
1640 }
1641
1642
1643
1644 /* Merges and commits. */
1645
1646
1647 struct deltify_committed_args
1648 {
1649 svn_fs_t *fs; /* the filesystem */
1650 svn_revnum_t rev; /* revision just committed */
1651 const char *txn_id; /* transaction just committed */
1652 };
1653
1654
1655 struct txn_deltify_args
1656 {
1657 /* The transaction ID whose nodes are being deltified. */
1658 const char *txn_id;
1659
1660 /* The target is what we're deltifying. */
1661 const svn_fs_id_t *tgt_id;
1662
1663 /* The base is what we're deltifying against. It's not necessarily
1664 the "next" revision of the node; skip deltas mean we sometimes
1665 deltify against a successor many generations away. This may be
1666 NULL, in which case we'll avoid deltification and simply index
1667 TGT_ID's data checksum. */
1668 const svn_fs_id_t *base_id;
1669
1670 /* We only deltify props for directories.
1671 ### Didn't we try removing this horrid little optimization once?
1672 ### What was the result? I would have thought that skip deltas
1673 ### mean directory undeltification is cheap enough now. */
1674 svn_boolean_t is_dir;
1675 };
1676
1677
1678 static svn_error_t *
txn_body_txn_deltify(void * baton,trail_t * trail)1679 txn_body_txn_deltify(void *baton, trail_t *trail)
1680 {
1681 struct txn_deltify_args *args = baton;
1682 dag_node_t *tgt_node, *base_node;
1683 base_fs_data_t *bfd = trail->fs->fsap_data;
1684
1685 SVN_ERR(svn_fs_base__dag_get_node(&tgt_node, trail->fs, args->tgt_id,
1686 trail, trail->pool));
1687 /* If we have something to deltify against, do so. */
1688 if (args->base_id)
1689 {
1690 SVN_ERR(svn_fs_base__dag_get_node(&base_node, trail->fs, args->base_id,
1691 trail, trail->pool));
1692 SVN_ERR(svn_fs_base__dag_deltify(tgt_node, base_node, args->is_dir,
1693 args->txn_id, trail, trail->pool));
1694 }
1695
1696 /* If we support rep sharing, and this isn't a directory, record a
1697 mapping of TGT_NODE's data checksum to its representation key. */
1698 if (bfd->format >= SVN_FS_BASE__MIN_REP_SHARING_FORMAT)
1699 SVN_ERR(svn_fs_base__dag_index_checksums(tgt_node, trail, trail->pool));
1700
1701 return SVN_NO_ERROR;
1702 }
1703
1704
1705 struct txn_pred_count_args
1706 {
1707 const svn_fs_id_t *id;
1708 int pred_count;
1709 };
1710
1711
1712 static svn_error_t *
txn_body_pred_count(void * baton,trail_t * trail)1713 txn_body_pred_count(void *baton, trail_t *trail)
1714 {
1715 node_revision_t *noderev;
1716 struct txn_pred_count_args *args = baton;
1717
1718 SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, trail->fs,
1719 args->id, trail, trail->pool));
1720 args->pred_count = noderev->predecessor_count;
1721 return SVN_NO_ERROR;
1722 }
1723
1724
1725 struct txn_pred_id_args
1726 {
1727 const svn_fs_id_t *id; /* The node id whose predecessor we want. */
1728 const svn_fs_id_t *pred_id; /* The returned predecessor id. */
1729 apr_pool_t *pool; /* The pool in which to allocate pred_id. */
1730 };
1731
1732
1733 static svn_error_t *
txn_body_pred_id(void * baton,trail_t * trail)1734 txn_body_pred_id(void *baton, trail_t *trail)
1735 {
1736 node_revision_t *nr;
1737 struct txn_pred_id_args *args = baton;
1738
1739 SVN_ERR(svn_fs_bdb__get_node_revision(&nr, trail->fs, args->id,
1740 trail, trail->pool));
1741 if (nr->predecessor_id)
1742 args->pred_id = svn_fs_base__id_copy(nr->predecessor_id, args->pool);
1743 else
1744 args->pred_id = NULL;
1745
1746 return SVN_NO_ERROR;
1747 }
1748
1749
1750 /* Deltify PATH in ROOT's predecessor iff PATH is mutable under TXN_ID
1751 in FS. If PATH is a mutable directory, recurse.
1752
1753 NODE_ID is the node revision ID for PATH in ROOT, or NULL if that
1754 value isn't known. KIND is the node kind for PATH in ROOT, or
1755 svn_node_unknown is the kind isn't known.
1756
1757 Use POOL for necessary allocations. */
1758 static svn_error_t *
deltify_mutable(svn_fs_t * fs,svn_fs_root_t * root,const char * path,const svn_fs_id_t * node_id,svn_node_kind_t kind,const char * txn_id,apr_pool_t * pool)1759 deltify_mutable(svn_fs_t *fs,
1760 svn_fs_root_t *root,
1761 const char *path,
1762 const svn_fs_id_t *node_id,
1763 svn_node_kind_t kind,
1764 const char *txn_id,
1765 apr_pool_t *pool)
1766 {
1767 const svn_fs_id_t *id = node_id;
1768 apr_hash_t *entries = NULL;
1769 struct txn_deltify_args td_args;
1770 base_fs_data_t *bfd = fs->fsap_data;
1771
1772 /* Get the ID for PATH under ROOT if it wasn't provided. */
1773 if (! node_id)
1774 SVN_ERR(base_node_id(&id, root, path, pool));
1775
1776 /* Check for mutability. Not mutable? Go no further. This is safe
1777 to do because for items in the tree to be mutable, their parent
1778 dirs must also be mutable. Therefore, if a directory is not
1779 mutable under TXN_ID, its children cannot be. */
1780 if (strcmp(svn_fs_base__id_txn_id(id), txn_id))
1781 return SVN_NO_ERROR;
1782
1783 /* Is this a directory? */
1784 if (kind == svn_node_unknown)
1785 SVN_ERR(base_check_path(&kind, root, path, pool));
1786
1787 /* If this is a directory, read its entries. */
1788 if (kind == svn_node_dir)
1789 SVN_ERR(base_dir_entries(&entries, root, path, pool));
1790
1791 /* If there are entries, recurse on 'em. */
1792 if (entries)
1793 {
1794 apr_pool_t *subpool = svn_pool_create(pool);
1795 apr_hash_index_t *hi;
1796
1797 for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
1798 {
1799 /* KEY will be the entry name, VAL the dirent */
1800 const void *key;
1801 void *val;
1802 svn_fs_dirent_t *entry;
1803 svn_pool_clear(subpool);
1804 apr_hash_this(hi, &key, NULL, &val);
1805 entry = val;
1806 SVN_ERR(deltify_mutable(fs, root,
1807 svn_fspath__join(path, key, subpool),
1808 entry->id, entry->kind, txn_id, subpool));
1809 }
1810
1811 svn_pool_destroy(subpool);
1812 }
1813
1814 /* Index ID's data checksum. */
1815 td_args.txn_id = txn_id;
1816 td_args.tgt_id = id;
1817 td_args.base_id = NULL;
1818 td_args.is_dir = (kind == svn_node_dir);
1819 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args,
1820 TRUE, pool));
1821
1822 /* Finally, deltify old data against this node. */
1823 {
1824 /* Prior to 1.6, we use the following algorithm to deltify nodes:
1825
1826 Redeltify predecessor node-revisions of the one we added. The
1827 idea is to require at most 2*lg(N) deltas to be applied to get
1828 to any node-revision in a chain of N predecessors. We do this
1829 using a technique derived from skip lists:
1830
1831 - Always redeltify the immediate parent
1832
1833 - If the number of predecessors is divisible by 2,
1834 redeltify the revision two predecessors back
1835
1836 - If the number of predecessors is divisible by 4,
1837 redeltify the revision four predecessors back
1838
1839 ... and so on.
1840
1841 That's the theory, anyway. Unfortunately, if we strictly
1842 follow that theory we get a bunch of overhead up front and no
1843 great benefit until the number of predecessors gets large. So,
1844 stop at redeltifying the parent if the number of predecessors
1845 is less than 32, and also skip the second level (redeltifying
1846 two predecessors back), since that doesn't help much. Also,
1847 don't redeltify the oldest node-revision; it's potentially
1848 expensive and doesn't help retrieve any other revision.
1849 (Retrieving the oldest node-revision will still be fast, just
1850 not as blindingly so.)
1851
1852 For 1.6 and beyond, we just deltify the current node against its
1853 predecessors, using skip deltas similar to the way FSFS does. */
1854
1855 int pred_count;
1856 const svn_fs_id_t *pred_id;
1857 struct txn_pred_count_args tpc_args;
1858 apr_pool_t *subpools[2];
1859 int active_subpool = 0;
1860 svn_revnum_t forward_delta_rev = 0;
1861
1862 tpc_args.id = id;
1863 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_count, &tpc_args,
1864 TRUE, pool));
1865 pred_count = tpc_args.pred_count;
1866
1867 /* If nothing to deltify, then we're done. */
1868 if (pred_count == 0)
1869 return SVN_NO_ERROR;
1870
1871 subpools[0] = svn_pool_create(pool);
1872 subpools[1] = svn_pool_create(pool);
1873
1874 /* If we support the 'miscellaneous' table, check it to see if
1875 there is a point in time before which we don't want to do
1876 deltification. */
1877 /* ### FIXME: I think this is an unnecessary restriction. We
1878 ### should be able to do something meaningful for most
1879 ### deltification requests -- what that is depends on the
1880 ### directory of the deltas for that revision, though. */
1881 if (bfd->format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT)
1882 {
1883 const char *val;
1884 SVN_ERR(svn_fs_base__miscellaneous_get
1885 (&val, fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE, pool));
1886 if (val)
1887 SVN_ERR(svn_revnum_parse(&forward_delta_rev, val, NULL));
1888 }
1889
1890 if (bfd->format >= SVN_FS_BASE__MIN_FORWARD_DELTAS_FORMAT
1891 && forward_delta_rev <= root->rev)
1892 {
1893 /**** FORWARD DELTA STORAGE ****/
1894
1895 /* Decide which predecessor to deltify against. Flip the rightmost '1'
1896 bit of the predecessor count to determine which file rev (counting
1897 from 0) we want to use. (To see why count & (count - 1) unsets the
1898 rightmost set bit, think about how you decrement a binary number. */
1899 pred_count = pred_count & (pred_count - 1);
1900
1901 /* Walk back a number of predecessors equal to the difference between
1902 pred_count and the original predecessor count. (For example, if
1903 the node has ten predecessors and we want the eighth node, walk back
1904 two predecessors. */
1905 pred_id = id;
1906
1907 /* We need to use two alternating pools because the id used in the
1908 call to txn_body_pred_id is allocated by the previous inner
1909 loop iteration. If we would clear the pool each iteration we
1910 would free the previous result. */
1911 while ((pred_count++) < tpc_args.pred_count)
1912 {
1913 struct txn_pred_id_args tpi_args;
1914
1915 active_subpool = !active_subpool;
1916 svn_pool_clear(subpools[active_subpool]);
1917
1918 tpi_args.id = pred_id;
1919 tpi_args.pool = subpools[active_subpool];
1920 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id, &tpi_args,
1921 FALSE, subpools[active_subpool]));
1922 pred_id = tpi_args.pred_id;
1923
1924 if (pred_id == NULL)
1925 return svn_error_create
1926 (SVN_ERR_FS_CORRUPT, 0,
1927 _("Corrupt DB: faulty predecessor count"));
1928
1929 }
1930
1931 /* Finally, do the deltification. */
1932 td_args.txn_id = txn_id;
1933 td_args.tgt_id = id;
1934 td_args.base_id = pred_id;
1935 td_args.is_dir = (kind == svn_node_dir);
1936 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args,
1937 TRUE, subpools[active_subpool]));
1938 }
1939 else
1940 {
1941 int nlevels, lev, count;
1942
1943 /**** REVERSE DELTA STORAGE ****/
1944
1945 /* Decide how many predecessors to redeltify. To save overhead,
1946 don't redeltify anything but the immediate predecessor if there
1947 are less than 32 predecessors. */
1948 nlevels = 1;
1949 if (pred_count >= 32)
1950 {
1951 while (pred_count % 2 == 0)
1952 {
1953 pred_count /= 2;
1954 nlevels++;
1955 }
1956
1957 /* Don't redeltify the oldest revision. */
1958 if (1 << (nlevels - 1) == pred_count)
1959 nlevels--;
1960 }
1961
1962 /* Redeltify the desired number of predecessors. */
1963 count = 0;
1964 pred_id = id;
1965
1966 /* We need to use two alternating pools because the id used in the
1967 call to txn_body_pred_id is allocated by the previous inner
1968 loop iteration. If we would clear the pool each iteration we
1969 would free the previous result. */
1970 for (lev = 0; lev < nlevels; lev++)
1971 {
1972 /* To save overhead, skip the second level (that is, never
1973 redeltify the node-revision two predecessors back). */
1974 if (lev == 1)
1975 continue;
1976
1977 /* Note that COUNT is not reset between levels, and neither is
1978 PREDNODE; we just keep counting from where we were up to
1979 where we're supposed to get. */
1980 while (count < (1 << lev))
1981 {
1982 struct txn_pred_id_args tpi_args;
1983
1984 active_subpool = !active_subpool;
1985 svn_pool_clear(subpools[active_subpool]);
1986
1987 tpi_args.id = pred_id;
1988 tpi_args.pool = subpools[active_subpool];
1989 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id,
1990 &tpi_args, FALSE,
1991 subpools[active_subpool]));
1992 pred_id = tpi_args.pred_id;
1993
1994 if (pred_id == NULL)
1995 return svn_error_create
1996 (SVN_ERR_FS_CORRUPT, 0,
1997 _("Corrupt DB: faulty predecessor count"));
1998
1999 count++;
2000 }
2001
2002 /* Finally, do the deltification. */
2003 td_args.txn_id = NULL; /* Don't require mutable reps */
2004 td_args.tgt_id = pred_id;
2005 td_args.base_id = id;
2006 td_args.is_dir = (kind == svn_node_dir);
2007 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args,
2008 TRUE, subpools[active_subpool]));
2009
2010 }
2011 }
2012
2013 svn_pool_destroy(subpools[0]);
2014 svn_pool_destroy(subpools[1]);
2015 }
2016
2017 return SVN_NO_ERROR;
2018 }
2019
2020
2021 struct get_root_args
2022 {
2023 svn_fs_root_t *root;
2024 dag_node_t *node;
2025 };
2026
2027
2028 /* Set ARGS->node to the root node of ARGS->root. */
2029 static svn_error_t *
txn_body_get_root(void * baton,trail_t * trail)2030 txn_body_get_root(void *baton, trail_t *trail)
2031 {
2032 struct get_root_args *args = baton;
2033 return get_dag(&(args->node), args->root, "", trail, trail->pool);
2034 }
2035
2036
2037
2038 static svn_error_t *
update_ancestry(svn_fs_t * fs,const svn_fs_id_t * source_id,const svn_fs_id_t * target_id,const char * txn_id,const char * target_path,int source_pred_count,trail_t * trail,apr_pool_t * pool)2039 update_ancestry(svn_fs_t *fs,
2040 const svn_fs_id_t *source_id,
2041 const svn_fs_id_t *target_id,
2042 const char *txn_id,
2043 const char *target_path,
2044 int source_pred_count,
2045 trail_t *trail,
2046 apr_pool_t *pool)
2047 {
2048 node_revision_t *noderev;
2049
2050 /* Set target's predecessor-id to source_id. */
2051 if (strcmp(svn_fs_base__id_txn_id(target_id), txn_id))
2052 return svn_error_createf
2053 (SVN_ERR_FS_NOT_MUTABLE, NULL,
2054 _("Unexpected immutable node at '%s'"), target_path);
2055 SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, target_id,
2056 trail, pool));
2057 noderev->predecessor_id = source_id;
2058 noderev->predecessor_count = source_pred_count;
2059 if (noderev->predecessor_count != -1)
2060 noderev->predecessor_count++;
2061 return svn_fs_bdb__put_node_revision(fs, target_id, noderev, trail, pool);
2062 }
2063
2064
2065 /* Set the contents of CONFLICT_PATH to PATH, and return an
2066 SVN_ERR_FS_CONFLICT error that indicates that there was a conflict
2067 at PATH. Perform all allocations in POOL (except the allocation of
2068 CONFLICT_PATH, which should be handled outside this function). */
2069 static svn_error_t *
conflict_err(svn_stringbuf_t * conflict_path,const char * path)2070 conflict_err(svn_stringbuf_t *conflict_path,
2071 const char *path)
2072 {
2073 svn_stringbuf_set(conflict_path, path);
2074 return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
2075 _("Conflict at '%s'"), path);
2076 }
2077
2078
2079 /* Merge changes between ANCESTOR and SOURCE into TARGET as part of
2080 * TRAIL. ANCESTOR and TARGET must be distinct node revisions.
2081 * TARGET_PATH should correspond to TARGET's full path in its
2082 * filesystem, and is used for reporting conflict location.
2083 *
2084 * SOURCE, TARGET, and ANCESTOR are generally directories; this
2085 * function recursively merges the directories' contents. If any are
2086 * files, this function simply returns an error whenever SOURCE,
2087 * TARGET, and ANCESTOR are all distinct node revisions.
2088 *
2089 * If there are differences between ANCESTOR and SOURCE that conflict
2090 * with changes between ANCESTOR and TARGET, this function returns an
2091 * SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the
2092 * conflicting node in TARGET, with TARGET_PATH prepended as a path.
2093 *
2094 * If there are no conflicting differences, CONFLICT_P is updated to
2095 * the empty string.
2096 *
2097 * CONFLICT_P must point to a valid svn_stringbuf_t.
2098 *
2099 * Do any necessary temporary allocation in POOL.
2100 */
2101 static svn_error_t *
merge(svn_stringbuf_t * conflict_p,const char * target_path,dag_node_t * target,dag_node_t * source,dag_node_t * ancestor,const char * txn_id,apr_int64_t * mergeinfo_increment_out,trail_t * trail,apr_pool_t * pool)2102 merge(svn_stringbuf_t *conflict_p,
2103 const char *target_path,
2104 dag_node_t *target,
2105 dag_node_t *source,
2106 dag_node_t *ancestor,
2107 const char *txn_id,
2108 apr_int64_t *mergeinfo_increment_out,
2109 trail_t *trail,
2110 apr_pool_t *pool)
2111 {
2112 const svn_fs_id_t *source_id, *target_id, *ancestor_id;
2113 apr_hash_t *s_entries, *t_entries, *a_entries;
2114 apr_hash_index_t *hi;
2115 apr_pool_t *iterpool;
2116 svn_fs_t *fs;
2117 int pred_count;
2118 apr_int64_t mergeinfo_increment = 0;
2119 base_fs_data_t *bfd = trail->fs->fsap_data;
2120
2121 /* Make sure everyone comes from the same filesystem. */
2122 fs = svn_fs_base__dag_get_fs(ancestor);
2123 if ((fs != svn_fs_base__dag_get_fs(source))
2124 || (fs != svn_fs_base__dag_get_fs(target)))
2125 {
2126 return svn_error_create
2127 (SVN_ERR_FS_CORRUPT, NULL,
2128 _("Bad merge; ancestor, source, and target not all in same fs"));
2129 }
2130
2131 /* We have the same fs, now check it. */
2132 SVN_ERR(svn_fs__check_fs(fs, TRUE));
2133
2134 source_id = svn_fs_base__dag_get_id(source);
2135 target_id = svn_fs_base__dag_get_id(target);
2136 ancestor_id = svn_fs_base__dag_get_id(ancestor);
2137
2138 /* It's improper to call this function with ancestor == target. */
2139 if (svn_fs_base__id_eq(ancestor_id, target_id))
2140 {
2141 svn_string_t *id_str = svn_fs_base__id_unparse(target_id, pool);
2142 return svn_error_createf
2143 (SVN_ERR_FS_GENERAL, NULL,
2144 _("Bad merge; target '%s' has id '%s', same as ancestor"),
2145 target_path, id_str->data);
2146 }
2147
2148 svn_stringbuf_setempty(conflict_p);
2149
2150 /* Base cases:
2151 * Either no change made in source, or same change as made in target.
2152 * Both mean nothing to merge here.
2153 */
2154 if (svn_fs_base__id_eq(ancestor_id, source_id)
2155 || (svn_fs_base__id_eq(source_id, target_id)))
2156 return SVN_NO_ERROR;
2157
2158 /* Else proceed, knowing all three are distinct node revisions.
2159 *
2160 * How to merge from this point:
2161 *
2162 * if (not all 3 are directories)
2163 * {
2164 * early exit with conflict;
2165 * }
2166 *
2167 * // Property changes may only be made to up-to-date
2168 * // directories, because once the client commits the prop
2169 * // change, it bumps the directory's revision, and therefore
2170 * // must be able to depend on there being no other changes to
2171 * // that directory in the repository.
2172 * if (target's property list differs from ancestor's)
2173 * conflict;
2174 *
2175 * For each entry NAME in the directory ANCESTOR:
2176 *
2177 * Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of
2178 * the name within ANCESTOR, SOURCE, and TARGET respectively.
2179 * (Possibly null if NAME does not exist in SOURCE or TARGET.)
2180 *
2181 * If ANCESTOR-ENTRY == SOURCE-ENTRY, then:
2182 * No changes were made to this entry while the transaction was in
2183 * progress, so do nothing to the target.
2184 *
2185 * Else if ANCESTOR-ENTRY == TARGET-ENTRY, then:
2186 * A change was made to this entry while the transaction was in
2187 * process, but the transaction did not touch this entry. Replace
2188 * TARGET-ENTRY with SOURCE-ENTRY.
2189 *
2190 * Else:
2191 * Changes were made to this entry both within the transaction and
2192 * to the repository while the transaction was in progress. They
2193 * must be merged or declared to be in conflict.
2194 *
2195 * If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
2196 * double delete; flag a conflict.
2197 *
2198 * If any of the three entries is of type file, declare a conflict.
2199 *
2200 * If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
2201 * modification of ANCESTOR-ENTRY (determine by comparing the
2202 * node-id fields), declare a conflict. A replacement is
2203 * incompatible with a modification or other replacement--even
2204 * an identical replacement.
2205 *
2206 * Direct modifications were made to the directory ANCESTOR-ENTRY
2207 * in both SOURCE and TARGET. Recursively merge these
2208 * modifications.
2209 *
2210 * For each leftover entry NAME in the directory SOURCE:
2211 *
2212 * If NAME exists in TARGET, declare a conflict. Even if SOURCE and
2213 * TARGET are adding exactly the same thing, two additions are not
2214 * auto-mergeable with each other.
2215 *
2216 * Add NAME to TARGET with the entry from SOURCE.
2217 *
2218 * Now that we are done merging the changes from SOURCE into the
2219 * directory TARGET, update TARGET's predecessor to be SOURCE.
2220 */
2221
2222 if ((svn_fs_base__dag_node_kind(source) != svn_node_dir)
2223 || (svn_fs_base__dag_node_kind(target) != svn_node_dir)
2224 || (svn_fs_base__dag_node_kind(ancestor) != svn_node_dir))
2225 {
2226 return conflict_err(conflict_p, target_path);
2227 }
2228
2229
2230 /* Possible early merge failure: if target and ancestor have
2231 different property lists, then the merge should fail.
2232 Propchanges can *only* be committed on an up-to-date directory.
2233 ### TODO: see issue #418 about the inelegance of this.
2234
2235 Another possible, similar, early merge failure: if source and
2236 ancestor have different property lists (meaning someone else
2237 changed directory properties while our commit transaction was
2238 happening), the merge should fail. See issue #2751.
2239 */
2240 {
2241 node_revision_t *tgt_nr, *anc_nr, *src_nr;
2242
2243 /* Get node revisions for our id's. */
2244 SVN_ERR(svn_fs_bdb__get_node_revision(&tgt_nr, fs, target_id,
2245 trail, pool));
2246 SVN_ERR(svn_fs_bdb__get_node_revision(&anc_nr, fs, ancestor_id,
2247 trail, pool));
2248 SVN_ERR(svn_fs_bdb__get_node_revision(&src_nr, fs, source_id,
2249 trail, pool));
2250
2251 /* Now compare the prop-keys of the skels. Note that just because
2252 the keys are different -doesn't- mean the proplists have
2253 different contents. But merge() isn't concerned with contents;
2254 it doesn't do a brute-force comparison on textual contents, so
2255 it won't do that here either. Checking to see if the propkey
2256 atoms are `equal' is enough. */
2257 if (! svn_fs_base__same_keys(tgt_nr->prop_key, anc_nr->prop_key))
2258 return conflict_err(conflict_p, target_path);
2259 if (! svn_fs_base__same_keys(src_nr->prop_key, anc_nr->prop_key))
2260 return conflict_err(conflict_p, target_path);
2261 }
2262
2263 /* ### todo: it would be more efficient to simply check for a NULL
2264 entries hash where necessary below than to allocate an empty hash
2265 here, but another day, another day... */
2266 SVN_ERR(svn_fs_base__dag_dir_entries(&s_entries, source, trail, pool));
2267 if (! s_entries)
2268 s_entries = apr_hash_make(pool);
2269 SVN_ERR(svn_fs_base__dag_dir_entries(&t_entries, target, trail, pool));
2270 if (! t_entries)
2271 t_entries = apr_hash_make(pool);
2272 SVN_ERR(svn_fs_base__dag_dir_entries(&a_entries, ancestor, trail, pool));
2273 if (! a_entries)
2274 a_entries = apr_hash_make(pool);
2275
2276 /* for each entry E in a_entries... */
2277 iterpool = svn_pool_create(pool);
2278 for (hi = apr_hash_first(pool, a_entries);
2279 hi;
2280 hi = apr_hash_next(hi))
2281 {
2282 svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
2283
2284 const void *key;
2285 void *val;
2286 apr_ssize_t klen;
2287
2288 svn_pool_clear(iterpool);
2289
2290 /* KEY will be the entry name in ancestor, VAL the dirent */
2291 apr_hash_this(hi, &key, &klen, &val);
2292 a_entry = val;
2293
2294 s_entry = apr_hash_get(s_entries, key, klen);
2295 t_entry = apr_hash_get(t_entries, key, klen);
2296
2297 /* No changes were made to this entry while the transaction was
2298 in progress, so do nothing to the target. */
2299 if (s_entry && svn_fs_base__id_eq(a_entry->id, s_entry->id))
2300 goto end;
2301
2302 /* A change was made to this entry while the transaction was in
2303 process, but the transaction did not touch this entry. */
2304 else if (t_entry && svn_fs_base__id_eq(a_entry->id, t_entry->id))
2305 {
2306 dag_node_t *t_ent_node;
2307 apr_int64_t mergeinfo_start;
2308 SVN_ERR(svn_fs_base__dag_get_node(&t_ent_node, fs,
2309 t_entry->id, trail, iterpool));
2310 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_start,
2311 t_ent_node, trail,
2312 iterpool));
2313 mergeinfo_increment -= mergeinfo_start;
2314
2315 if (s_entry)
2316 {
2317 dag_node_t *s_ent_node;
2318 apr_int64_t mergeinfo_end;
2319 SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2320 s_entry->id, trail,
2321 iterpool));
2322 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
2323 &mergeinfo_end,
2324 s_ent_node, trail,
2325 iterpool));
2326 mergeinfo_increment += mergeinfo_end;
2327 SVN_ERR(svn_fs_base__dag_set_entry(target, key, s_entry->id,
2328 txn_id, trail, iterpool));
2329 }
2330 else
2331 {
2332 SVN_ERR(svn_fs_base__dag_delete(target, key, txn_id,
2333 trail, iterpool));
2334 }
2335 }
2336
2337 /* Changes were made to this entry both within the transaction
2338 and to the repository while the transaction was in progress.
2339 They must be merged or declared to be in conflict. */
2340 else
2341 {
2342 dag_node_t *s_ent_node, *t_ent_node, *a_ent_node;
2343 const char *new_tpath;
2344 apr_int64_t sub_mergeinfo_increment;
2345
2346 /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
2347 double delete; if one of them is null, that's a delete versus
2348 a modification. In any of these cases, flag a conflict. */
2349 if (s_entry == NULL || t_entry == NULL)
2350 return conflict_err(conflict_p,
2351 svn_fspath__join(target_path,
2352 a_entry->name,
2353 iterpool));
2354
2355 /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
2356 modification of ANCESTOR-ENTRY, declare a conflict. */
2357 if (strcmp(svn_fs_base__id_node_id(s_entry->id),
2358 svn_fs_base__id_node_id(a_entry->id)) != 0
2359 || strcmp(svn_fs_base__id_copy_id(s_entry->id),
2360 svn_fs_base__id_copy_id(a_entry->id)) != 0
2361 || strcmp(svn_fs_base__id_node_id(t_entry->id),
2362 svn_fs_base__id_node_id(a_entry->id)) != 0
2363 || strcmp(svn_fs_base__id_copy_id(t_entry->id),
2364 svn_fs_base__id_copy_id(a_entry->id)) != 0)
2365 return conflict_err(conflict_p,
2366 svn_fspath__join(target_path,
2367 a_entry->name,
2368 iterpool));
2369
2370 /* Fetch the nodes for our entries. */
2371 SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2372 s_entry->id, trail, iterpool));
2373 SVN_ERR(svn_fs_base__dag_get_node(&t_ent_node, fs,
2374 t_entry->id, trail, iterpool));
2375 SVN_ERR(svn_fs_base__dag_get_node(&a_ent_node, fs,
2376 a_entry->id, trail, iterpool));
2377
2378 /* If any of the three entries is of type file, flag a conflict. */
2379 if ((svn_fs_base__dag_node_kind(s_ent_node) == svn_node_file)
2380 || (svn_fs_base__dag_node_kind(t_ent_node) == svn_node_file)
2381 || (svn_fs_base__dag_node_kind(a_ent_node) == svn_node_file))
2382 return conflict_err(conflict_p,
2383 svn_fspath__join(target_path,
2384 a_entry->name,
2385 iterpool));
2386
2387 /* Direct modifications were made to the directory
2388 ANCESTOR-ENTRY in both SOURCE and TARGET. Recursively
2389 merge these modifications. */
2390 new_tpath = svn_fspath__join(target_path, t_entry->name, iterpool);
2391 SVN_ERR(merge(conflict_p, new_tpath,
2392 t_ent_node, s_ent_node, a_ent_node,
2393 txn_id, &sub_mergeinfo_increment, trail, iterpool));
2394 mergeinfo_increment += sub_mergeinfo_increment;
2395 }
2396
2397 /* We've taken care of any possible implications E could have.
2398 Remove it from source_entries, so it's easy later to loop
2399 over all the source entries that didn't exist in
2400 ancestor_entries. */
2401 end:
2402 apr_hash_set(s_entries, key, klen, NULL);
2403 }
2404
2405 /* For each entry E in source but not in ancestor */
2406 for (hi = apr_hash_first(pool, s_entries);
2407 hi;
2408 hi = apr_hash_next(hi))
2409 {
2410 svn_fs_dirent_t *s_entry, *t_entry;
2411 const void *key;
2412 void *val;
2413 apr_ssize_t klen;
2414 dag_node_t *s_ent_node;
2415 apr_int64_t mergeinfo_s;
2416
2417 svn_pool_clear(iterpool);
2418
2419 apr_hash_this(hi, &key, &klen, &val);
2420 s_entry = val;
2421 t_entry = apr_hash_get(t_entries, key, klen);
2422
2423 /* If NAME exists in TARGET, declare a conflict. */
2424 if (t_entry)
2425 return conflict_err(conflict_p,
2426 svn_fspath__join(target_path,
2427 t_entry->name,
2428 iterpool));
2429
2430 SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2431 s_entry->id, trail, iterpool));
2432 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_s,
2433 s_ent_node, trail,
2434 iterpool));
2435 mergeinfo_increment += mergeinfo_s;
2436 SVN_ERR(svn_fs_base__dag_set_entry
2437 (target, s_entry->name, s_entry->id, txn_id, trail, iterpool));
2438 }
2439 svn_pool_destroy(iterpool);
2440
2441 /* Now that TARGET has absorbed all of the history between ANCESTOR
2442 and SOURCE, we can update its predecessor to point to SOURCE. */
2443 SVN_ERR(svn_fs_base__dag_get_predecessor_count(&pred_count, source,
2444 trail, pool));
2445 SVN_ERR(update_ancestry(fs, source_id, target_id, txn_id, target_path,
2446 pred_count, trail, pool));
2447
2448 /* Tweak mergeinfo data if our format supports it. */
2449 if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
2450 {
2451 SVN_ERR(svn_fs_base__dag_adjust_mergeinfo_count(target,
2452 mergeinfo_increment,
2453 txn_id, trail, pool));
2454 }
2455
2456 if (mergeinfo_increment_out)
2457 *mergeinfo_increment_out = mergeinfo_increment;
2458
2459 return SVN_NO_ERROR;
2460 }
2461
2462
2463 struct merge_args
2464 {
2465 /* The ancestor for the merge. If this is null, then TXN's base is
2466 used as the ancestor for the merge. */
2467 dag_node_t *ancestor_node;
2468
2469 /* This is the SOURCE node for the merge. It may not be null. */
2470 dag_node_t *source_node;
2471
2472 /* This is the TARGET of the merge. It may not be null. If
2473 ancestor_node above is null, then this txn's base is used as the
2474 ancestor for the merge. */
2475 svn_fs_txn_t *txn;
2476
2477 /* If a conflict results, this is updated to the path in the txn that
2478 conflicted. It must point to a valid svn_stringbuf_t before calling
2479 svn_fs_base__retry_txn, as this determines the pool used to allocate any
2480 required memory. */
2481 svn_stringbuf_t *conflict;
2482 };
2483
2484
2485 /* Merge changes between an ancestor and BATON->source_node into
2486 BATON->txn. The ancestor is either BATON->ancestor_node, or if
2487 that is null, BATON->txn's base node.
2488
2489 If the merge is successful, BATON->txn's base will become
2490 BATON->source_node, and its root node will have a new ID, a
2491 successor of BATON->source_node. */
2492 static svn_error_t *
txn_body_merge(void * baton,trail_t * trail)2493 txn_body_merge(void *baton, trail_t *trail)
2494 {
2495 struct merge_args *args = baton;
2496 dag_node_t *source_node, *txn_root_node, *ancestor_node;
2497 const svn_fs_id_t *source_id;
2498 svn_fs_t *fs = args->txn->fs;
2499 const char *txn_id = args->txn->id;
2500
2501 source_node = args->source_node;
2502 ancestor_node = args->ancestor_node;
2503 source_id = svn_fs_base__dag_get_id(source_node);
2504
2505 SVN_ERR(svn_fs_base__dag_txn_root(&txn_root_node, fs, txn_id,
2506 trail, trail->pool));
2507
2508 if (ancestor_node == NULL)
2509 {
2510 SVN_ERR(svn_fs_base__dag_txn_base_root(&ancestor_node, fs,
2511 txn_id, trail, trail->pool));
2512 }
2513
2514 if (svn_fs_base__id_eq(svn_fs_base__dag_get_id(ancestor_node),
2515 svn_fs_base__dag_get_id(txn_root_node)))
2516 {
2517 /* If no changes have been made in TXN since its current base,
2518 then it can't conflict with any changes since that base. So
2519 we just set *both* its base and root to source, making TXN
2520 in effect a repeat of source. */
2521
2522 /* ### kff todo: this would, of course, be a mighty silly thing
2523 for the caller to do, and we might want to consider whether
2524 this response is really appropriate. */
2525
2526 SVN_ERR(svn_fs_base__set_txn_base(fs, txn_id, source_id,
2527 trail, trail->pool));
2528 SVN_ERR(svn_fs_base__set_txn_root(fs, txn_id, source_id,
2529 trail, trail->pool));
2530 }
2531 else
2532 {
2533 int pred_count;
2534
2535 SVN_ERR(merge(args->conflict, "/", txn_root_node, source_node,
2536 ancestor_node, txn_id, NULL, trail, trail->pool));
2537
2538 SVN_ERR(svn_fs_base__dag_get_predecessor_count(&pred_count,
2539 source_node, trail,
2540 trail->pool));
2541
2542 /* After the merge, txn's new "ancestor" is now really the node
2543 at source_id, so record that fact. Think of this as
2544 ratcheting the txn forward in time, so it can't backslide and
2545 forget the merging work that's already been done. */
2546 SVN_ERR(update_ancestry(fs, source_id,
2547 svn_fs_base__dag_get_id(txn_root_node),
2548 txn_id, "/", pred_count, trail, trail->pool));
2549 SVN_ERR(svn_fs_base__set_txn_base(fs, txn_id, source_id,
2550 trail, trail->pool));
2551 }
2552
2553 return SVN_NO_ERROR;
2554 }
2555
2556
2557 /* Verify that there are registered with TRAIL->fs all the locks
2558 necessary to permit all the changes associated with TXN_NAME. */
2559 static svn_error_t *
verify_locks(const char * txn_name,trail_t * trail,apr_pool_t * pool)2560 verify_locks(const char *txn_name,
2561 trail_t *trail,
2562 apr_pool_t *pool)
2563 {
2564 apr_pool_t *subpool = svn_pool_create(pool);
2565 apr_hash_t *changes;
2566 apr_hash_index_t *hi;
2567 apr_array_header_t *changed_paths;
2568 svn_stringbuf_t *last_recursed = NULL;
2569 int i;
2570
2571 /* Fetch the changes for this transaction. */
2572 SVN_ERR(svn_fs_bdb__changes_fetch(&changes, trail->fs, txn_name,
2573 trail, pool));
2574
2575 /* Make an array of the changed paths, and sort them depth-first-ily. */
2576 changed_paths = apr_array_make(pool, apr_hash_count(changes) + 1,
2577 sizeof(const char *));
2578 for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
2579 {
2580 const void *key;
2581 apr_hash_this(hi, &key, NULL, NULL);
2582 APR_ARRAY_PUSH(changed_paths, const char *) = key;
2583 }
2584 svn_sort__array(changed_paths, svn_sort_compare_paths);
2585
2586 /* Now, traverse the array of changed paths, verify locks. Note
2587 that if we need to do a recursive verification a path, we'll skip
2588 over children of that path when we get to them. */
2589 for (i = 0; i < changed_paths->nelts; i++)
2590 {
2591 const char *path;
2592 svn_fs_path_change2_t *change;
2593 svn_boolean_t recurse = TRUE;
2594
2595 svn_pool_clear(subpool);
2596 path = APR_ARRAY_IDX(changed_paths, i, const char *);
2597
2598 /* If this path has already been verified as part of a recursive
2599 check of one of its parents, no need to do it again. */
2600 if (last_recursed
2601 && svn_fspath__skip_ancestor(last_recursed->data, path))
2602 continue;
2603
2604 /* Fetch the change associated with our path. */
2605 change = svn_hash_gets(changes, path);
2606
2607 /* What does it mean to succeed at lock verification for a given
2608 path? For an existing file or directory getting modified
2609 (text, props), it means we hold the lock on the file or
2610 directory. For paths being added or removed, we need to hold
2611 the locks for that path and any children of that path.
2612
2613 WHEW! We have no reliable way to determine the node kind of
2614 deleted items, but fortunately we are going to do a recursive
2615 check on deleted paths regardless of their kind. */
2616 if (change->change_kind == svn_fs_path_change_modify)
2617 recurse = FALSE;
2618 SVN_ERR(svn_fs_base__allow_locked_operation(path, recurse,
2619 trail, subpool));
2620
2621 /* If we just did a recursive check, remember the path we
2622 checked (so children can be skipped). */
2623 if (recurse)
2624 {
2625 if (! last_recursed)
2626 last_recursed = svn_stringbuf_create(path, pool);
2627 else
2628 svn_stringbuf_set(last_recursed, path);
2629 }
2630 }
2631 svn_pool_destroy(subpool);
2632 return SVN_NO_ERROR;
2633 }
2634
2635
2636 struct commit_args
2637 {
2638 svn_fs_txn_t *txn;
2639 svn_revnum_t new_rev;
2640 };
2641
2642
2643 /* Commit ARGS->txn, setting ARGS->new_rev to the resulting new
2644 * revision, if ARGS->txn is up-to-date with respect to the repository.
2645 *
2646 * Up-to-date means that ARGS->txn's base root is the same as the root
2647 * of the youngest revision. If ARGS->txn is not up-to-date, the
2648 * error SVN_ERR_FS_TXN_OUT_OF_DATE is returned, and the commit fails: no
2649 * new revision is created, and ARGS->new_rev is not touched.
2650 *
2651 * If the commit succeeds, ARGS->txn is destroyed.
2652 */
2653 static svn_error_t *
txn_body_commit(void * baton,trail_t * trail)2654 txn_body_commit(void *baton, trail_t *trail)
2655 {
2656 struct commit_args *args = baton;
2657
2658 svn_fs_txn_t *txn = args->txn;
2659 svn_fs_t *fs = txn->fs;
2660 const char *txn_name = txn->id;
2661
2662 svn_revnum_t youngest_rev;
2663 const svn_fs_id_t *y_rev_root_id;
2664 dag_node_t *txn_base_root_node, *txn_root_node;
2665
2666 /* Getting the youngest revision locks the revisions table until
2667 this trail is done. */
2668 SVN_ERR(svn_fs_bdb__youngest_rev(&youngest_rev, fs, trail, trail->pool));
2669
2670 /* If the root of the youngest revision is the same as txn's base,
2671 then no further merging is necessary and we can commit. */
2672 SVN_ERR(svn_fs_base__rev_get_root(&y_rev_root_id, fs, youngest_rev,
2673 trail, trail->pool));
2674 SVN_ERR(svn_fs_base__dag_txn_base_root(&txn_base_root_node, fs, txn_name,
2675 trail, trail->pool));
2676 /* ### kff todo: it seems weird to grab the ID for one, and the node
2677 for the other. We can certainly do the comparison we need, but
2678 it would be nice to grab the same type of information from the
2679 start, instead of having to transform one of them. */
2680 if (! svn_fs_base__id_eq(y_rev_root_id,
2681 svn_fs_base__dag_get_id(txn_base_root_node)))
2682 {
2683 svn_string_t *id_str = svn_fs_base__id_unparse(y_rev_root_id,
2684 trail->pool);
2685 return svn_error_createf
2686 (SVN_ERR_FS_TXN_OUT_OF_DATE, NULL,
2687 _("Transaction '%s' out-of-date with respect to revision '%s'"),
2688 txn_name, id_str->data);
2689 }
2690
2691 /* Locks may have been added (or stolen) between the calling of
2692 previous svn_fs.h functions and svn_fs_commit_txn(), so we need
2693 to re-examine every changed-path in the txn and re-verify all
2694 discovered locks. */
2695 SVN_ERR(verify_locks(txn_name, trail, trail->pool));
2696
2697 /* Ensure every txn has a mutable root as then the new revision will
2698 have a distinct root node-revision-id. This is necessary as
2699 future transactions use the root node-revision-id as a proxy for
2700 the transaction base revision. */
2701 SVN_ERR(svn_fs_base__dag_txn_root(&txn_root_node, fs, txn_name,
2702 trail, trail->pool));
2703 if (!svn_fs_base__dag_check_mutable(txn_root_node, txn->id))
2704 {
2705 dag_node_t *clone;
2706 SVN_ERR(svn_fs_base__dag_clone_root(&clone, fs, txn->id,
2707 trail, trail->pool));
2708 }
2709
2710 /* Else, commit the txn. */
2711 return svn_fs_base__dag_commit_txn(&(args->new_rev), txn, trail,
2712 trail->pool);
2713 }
2714
2715
2716 /* Note: it is acceptable for this function to call back into
2717 top-level FS interfaces because it does not itself use trails. */
2718 svn_error_t *
svn_fs_base__commit_txn(const char ** conflict_p,svn_revnum_t * new_rev,svn_fs_txn_t * txn,apr_pool_t * pool)2719 svn_fs_base__commit_txn(const char **conflict_p,
2720 svn_revnum_t *new_rev,
2721 svn_fs_txn_t *txn,
2722 apr_pool_t *pool)
2723 {
2724 /* How do commits work in Subversion?
2725 *
2726 * When you're ready to commit, here's what you have:
2727 *
2728 * 1. A transaction, with a mutable tree hanging off it.
2729 * 2. A base revision, against which TXN_TREE was made.
2730 * 3. A latest revision, which may be newer than the base rev.
2731 *
2732 * The problem is that if latest != base, then one can't simply
2733 * attach the txn root as the root of the new revision, because that
2734 * would lose all the changes between base and latest. It is also
2735 * not acceptable to insist that base == latest; in a busy
2736 * repository, commits happen too fast to insist that everyone keep
2737 * their entire tree up-to-date at all times. Non-overlapping
2738 * changes should not interfere with each other.
2739 *
2740 * The solution is to merge the changes between base and latest into
2741 * the txn tree [see the function merge()]. The txn tree is the
2742 * only one of the three trees that is mutable, so it has to be the
2743 * one to adjust.
2744 *
2745 * You might have to adjust it more than once, if a new latest
2746 * revision gets committed while you were merging in the previous
2747 * one. For example:
2748 *
2749 * 1. Jane starts txn T, based at revision 6.
2750 * 2. Someone commits (or already committed) revision 7.
2751 * 3. Jane's starts merging the changes between 6 and 7 into T.
2752 * 4. Meanwhile, someone commits revision 8.
2753 * 5. Jane finishes the 6-->7 merge. T could now be committed
2754 * against a latest revision of 7, if only that were still the
2755 * latest. Unfortunately, 8 is now the latest, so...
2756 * 6. Jane starts merging the changes between 7 and 8 into T.
2757 * 7. Meanwhile, no one commits any new revisions. Whew.
2758 * 8. Jane commits T, creating revision 9, whose tree is exactly
2759 * T's tree, except immutable now.
2760 *
2761 * Lather, rinse, repeat.
2762 */
2763
2764 svn_error_t *err;
2765 svn_fs_t *fs = txn->fs;
2766 apr_pool_t *subpool = svn_pool_create(pool);
2767
2768 /* Initialize output params. */
2769 *new_rev = SVN_INVALID_REVNUM;
2770 if (conflict_p)
2771 *conflict_p = NULL;
2772
2773 while (1729)
2774 {
2775 struct get_root_args get_root_args;
2776 struct merge_args merge_args;
2777 struct commit_args commit_args;
2778 svn_revnum_t youngish_rev;
2779 svn_fs_root_t *youngish_root;
2780 dag_node_t *youngish_root_node;
2781
2782 svn_pool_clear(subpool);
2783
2784 /* Get the *current* youngest revision, in one short-lived
2785 Berkeley transaction. (We don't want the revisions table
2786 locked while we do the main merge.) We call it "youngish"
2787 because new revisions might get committed after we've
2788 obtained it. */
2789
2790 SVN_ERR(svn_fs_base__youngest_rev(&youngish_rev, fs, subpool));
2791 SVN_ERR(svn_fs_base__revision_root(&youngish_root, fs, youngish_rev,
2792 subpool));
2793
2794 /* Get the dag node for the youngest revision, also in one
2795 Berkeley transaction. Later we'll use it as the SOURCE
2796 argument to a merge, and if the merge succeeds, this youngest
2797 root node will become the new base root for the svn txn that
2798 was the target of the merge (but note that the youngest rev
2799 may have changed by then -- that's why we're careful to get
2800 this root in its own bdb txn here). */
2801 get_root_args.root = youngish_root;
2802 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2803 FALSE, subpool));
2804 youngish_root_node = get_root_args.node;
2805
2806 /* Try to merge. If the merge succeeds, the base root node of
2807 TARGET's txn will become the same as youngish_root_node, so
2808 any future merges will only be between that node and whatever
2809 the root node of the youngest rev is by then. */
2810 merge_args.ancestor_node = NULL;
2811 merge_args.source_node = youngish_root_node;
2812 merge_args.txn = txn;
2813 merge_args.conflict = svn_stringbuf_create_empty(pool); /* use pool */
2814 err = svn_fs_base__retry_txn(fs, txn_body_merge, &merge_args,
2815 FALSE, subpool);
2816 if (err)
2817 {
2818 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2819 *conflict_p = merge_args.conflict->data;
2820 return svn_error_trace(err);
2821 }
2822
2823 /* Try to commit. */
2824 commit_args.txn = txn;
2825 err = svn_fs_base__retry_txn(fs, txn_body_commit, &commit_args,
2826 FALSE, subpool);
2827 if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
2828 {
2829 /* Did someone else finish committing a new revision while we
2830 were in mid-merge or mid-commit? If so, we'll need to
2831 loop again to merge the new changes in, then try to
2832 commit again. Or if that's not what happened, then just
2833 return the error. */
2834 svn_revnum_t youngest_rev;
2835 svn_error_t *err2 = svn_fs_base__youngest_rev(&youngest_rev, fs,
2836 subpool);
2837 if (err2)
2838 {
2839 svn_error_clear(err);
2840 return svn_error_trace(err2); /* err2 is bad,
2841 it should not occur */
2842 }
2843 else if (youngest_rev == youngish_rev)
2844 return svn_error_trace(err);
2845 else
2846 svn_error_clear(err);
2847 }
2848 else if (err)
2849 {
2850 return svn_error_trace(err);
2851 }
2852 else
2853 {
2854 /* Set the return value -- our brand spankin' new revision! */
2855 *new_rev = commit_args.new_rev;
2856 break;
2857 }
2858 }
2859
2860 svn_pool_destroy(subpool);
2861 return SVN_NO_ERROR;
2862 }
2863
2864 /* Note: it is acceptable for this function to call back into
2865 public FS API interfaces because it does not itself use trails. */
2866 static svn_error_t *
base_merge(const char ** conflict_p,svn_fs_root_t * source_root,const char * source_path,svn_fs_root_t * target_root,const char * target_path,svn_fs_root_t * ancestor_root,const char * ancestor_path,apr_pool_t * pool)2867 base_merge(const char **conflict_p,
2868 svn_fs_root_t *source_root,
2869 const char *source_path,
2870 svn_fs_root_t *target_root,
2871 const char *target_path,
2872 svn_fs_root_t *ancestor_root,
2873 const char *ancestor_path,
2874 apr_pool_t *pool)
2875 {
2876 dag_node_t *source, *ancestor;
2877 struct get_root_args get_root_args;
2878 struct merge_args merge_args;
2879 svn_fs_txn_t *txn;
2880 svn_error_t *err;
2881 svn_fs_t *fs;
2882
2883 if (! target_root->is_txn_root)
2884 return SVN_FS__NOT_TXN(target_root);
2885
2886 /* Paranoia. */
2887 fs = ancestor_root->fs;
2888 if ((source_root->fs != fs) || (target_root->fs != fs))
2889 {
2890 return svn_error_create
2891 (SVN_ERR_FS_CORRUPT, NULL,
2892 _("Bad merge; ancestor, source, and target not all in same fs"));
2893 }
2894
2895 /* ### kff todo: is there any compelling reason to get the nodes in
2896 one db transaction? Right now we don't; txn_body_get_root() gets
2897 one node at a time. This will probably need to change:
2898
2899 Jim Blandy <[email protected]> writes:
2900 > svn_fs_merge needs to be a single transaction, to protect it against
2901 > people deleting parents of nodes it's working on, etc.
2902 */
2903
2904 /* Get the ancestor node. */
2905 get_root_args.root = ancestor_root;
2906 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2907 FALSE, pool));
2908 ancestor = get_root_args.node;
2909
2910 /* Get the source node. */
2911 get_root_args.root = source_root;
2912 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2913 FALSE, pool));
2914 source = get_root_args.node;
2915
2916 /* Open a txn for the txn root into which we're merging. */
2917 SVN_ERR(svn_fs_base__open_txn(&txn, fs, target_root->txn, pool));
2918
2919 /* Merge changes between ANCESTOR and SOURCE into TXN. */
2920 merge_args.source_node = source;
2921 merge_args.ancestor_node = ancestor;
2922 merge_args.txn = txn;
2923 merge_args.conflict = svn_stringbuf_create_empty(pool);
2924 err = svn_fs_base__retry_txn(fs, txn_body_merge, &merge_args, FALSE, pool);
2925 if (err)
2926 {
2927 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2928 *conflict_p = merge_args.conflict->data;
2929 return svn_error_trace(err);
2930 }
2931
2932 return SVN_NO_ERROR;
2933 }
2934
2935
2936 struct rev_get_txn_id_args
2937 {
2938 const char **txn_id;
2939 svn_revnum_t revision;
2940 };
2941
2942
2943 static svn_error_t *
txn_body_rev_get_txn_id(void * baton,trail_t * trail)2944 txn_body_rev_get_txn_id(void *baton, trail_t *trail)
2945 {
2946 struct rev_get_txn_id_args *args = baton;
2947 return svn_fs_base__rev_get_txn_id(args->txn_id, trail->fs,
2948 args->revision, trail, trail->pool);
2949 }
2950
2951
2952 svn_error_t *
svn_fs_base__deltify(svn_fs_t * fs,svn_revnum_t revision,apr_pool_t * pool)2953 svn_fs_base__deltify(svn_fs_t *fs,
2954 svn_revnum_t revision,
2955 apr_pool_t *pool)
2956 {
2957 svn_fs_root_t *root;
2958 const char *txn_id;
2959 struct rev_get_txn_id_args args;
2960 base_fs_data_t *bfd = fs->fsap_data;
2961
2962 if (bfd->format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT)
2963 {
2964 const char *val;
2965 svn_revnum_t forward_delta_rev = 0;
2966
2967 SVN_ERR(svn_fs_base__miscellaneous_get
2968 (&val, fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE, pool));
2969 if (val)
2970 SVN_ERR(svn_revnum_parse(&forward_delta_rev, val, NULL));
2971
2972 /* ### FIXME: Unnecessarily harsh requirement? (cmpilato). */
2973 if (revision <= forward_delta_rev)
2974 return svn_error_createf
2975 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2976 _("Cannot deltify revisions prior to r%ld"), forward_delta_rev+1);
2977 }
2978
2979 SVN_ERR(svn_fs_base__revision_root(&root, fs, revision, pool));
2980
2981 args.txn_id = &txn_id;
2982 args.revision = revision;
2983 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_rev_get_txn_id, &args,
2984 FALSE, pool));
2985
2986 return deltify_mutable(fs, root, "/", NULL, svn_node_dir, txn_id, pool);
2987 }
2988
2989
2990 /* Modifying directories */
2991
2992
2993 struct make_dir_args
2994 {
2995 svn_fs_root_t *root;
2996 const char *path;
2997 };
2998
2999
3000 static svn_error_t *
txn_body_make_dir(void * baton,trail_t * trail)3001 txn_body_make_dir(void *baton,
3002 trail_t *trail)
3003 {
3004 struct make_dir_args *args = baton;
3005 svn_fs_root_t *root = args->root;
3006 const char *path = args->path;
3007 parent_path_t *parent_path;
3008 dag_node_t *sub_dir;
3009 const char *txn_id = root->txn;
3010
3011 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
3012 txn_id, trail, trail->pool));
3013
3014 /* If there's already a sub-directory by that name, complain. This
3015 also catches the case of trying to make a subdirectory named `/'. */
3016 if (parent_path->node)
3017 return SVN_FS__ALREADY_EXISTS(root, path);
3018
3019 /* Check to see if some lock is 'reserving' a file-path or dir-path
3020 at that location, or even some child-path; if so, check that we
3021 can use it. */
3022 if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3023 {
3024 SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
3025 trail, trail->pool));
3026 }
3027
3028 /* Create the subdirectory. */
3029 SVN_ERR(make_path_mutable(root, parent_path->parent, path,
3030 trail, trail->pool));
3031 SVN_ERR(svn_fs_base__dag_make_dir(&sub_dir,
3032 parent_path->parent->node,
3033 parent_path_path(parent_path->parent,
3034 trail->pool),
3035 parent_path->entry,
3036 txn_id,
3037 trail, trail->pool));
3038
3039 /* Make a record of this modification in the changes table. */
3040 return add_change(root->fs, txn_id, path,
3041 svn_fs_base__dag_get_id(sub_dir),
3042 svn_fs_path_change_add, FALSE, FALSE,
3043 trail, trail->pool);
3044 }
3045
3046
3047 static svn_error_t *
base_make_dir(svn_fs_root_t * root,const char * path,apr_pool_t * pool)3048 base_make_dir(svn_fs_root_t *root,
3049 const char *path,
3050 apr_pool_t *pool)
3051 {
3052 struct make_dir_args args;
3053
3054 if (! root->is_txn_root)
3055 return SVN_FS__NOT_TXN(root);
3056
3057 args.root = root;
3058 args.path = path;
3059 return svn_fs_base__retry_txn(root->fs, txn_body_make_dir, &args,
3060 TRUE, pool);
3061 }
3062
3063
3064 struct delete_args
3065 {
3066 svn_fs_root_t *root;
3067 const char *path;
3068 };
3069
3070
3071 /* If this returns SVN_ERR_FS_NO_SUCH_ENTRY, it means that the
3072 basename of PATH is missing from its parent, that is, the final
3073 target of the deletion is missing. */
3074 static svn_error_t *
txn_body_delete(void * baton,trail_t * trail)3075 txn_body_delete(void *baton,
3076 trail_t *trail)
3077 {
3078 struct delete_args *args = baton;
3079 svn_fs_root_t *root = args->root;
3080 const char *path = args->path;
3081 parent_path_t *parent_path;
3082 const char *txn_id = root->txn;
3083 base_fs_data_t *bfd = trail->fs->fsap_data;
3084
3085 if (! root->is_txn_root)
3086 return SVN_FS__NOT_TXN(root);
3087
3088 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
3089 trail, trail->pool));
3090
3091 /* We can't remove the root of the filesystem. */
3092 if (! parent_path->parent)
3093 return svn_error_create(SVN_ERR_FS_ROOT_DIR, NULL,
3094 _("The root directory cannot be deleted"));
3095
3096 /* Check to see if path (or any child thereof) is locked; if so,
3097 check that we can use the existing lock(s). */
3098 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3099 {
3100 SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
3101 trail, trail->pool));
3102 }
3103
3104 /* Make the parent directory mutable. */
3105 SVN_ERR(make_path_mutable(root, parent_path->parent, path,
3106 trail, trail->pool));
3107
3108 /* Decrement mergeinfo counts on the parents of this node by the
3109 count it previously carried, if our format supports it. */
3110 if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
3111 {
3112 apr_int64_t mergeinfo_count;
3113 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_count,
3114 parent_path->node,
3115 trail, trail->pool));
3116 SVN_ERR(adjust_parent_mergeinfo_counts(parent_path->parent,
3117 -mergeinfo_count, txn_id,
3118 trail, trail->pool));
3119 }
3120
3121 /* Do the deletion. */
3122 SVN_ERR(svn_fs_base__dag_delete(parent_path->parent->node,
3123 parent_path->entry,
3124 txn_id, trail, trail->pool));
3125
3126
3127 /* Make a record of this modification in the changes table. */
3128 return add_change(root->fs, txn_id, path,
3129 svn_fs_base__dag_get_id(parent_path->node),
3130 svn_fs_path_change_delete, FALSE, FALSE, trail,
3131 trail->pool);
3132 }
3133
3134
3135 static svn_error_t *
base_delete_node(svn_fs_root_t * root,const char * path,apr_pool_t * pool)3136 base_delete_node(svn_fs_root_t *root,
3137 const char *path,
3138 apr_pool_t *pool)
3139 {
3140 struct delete_args args;
3141
3142 args.root = root;
3143 args.path = path;
3144 return svn_fs_base__retry_txn(root->fs, txn_body_delete, &args,
3145 TRUE, pool);
3146 }
3147
3148
3149 struct copy_args
3150 {
3151 svn_fs_root_t *from_root;
3152 const char *from_path;
3153 svn_fs_root_t *to_root;
3154 const char *to_path;
3155 svn_boolean_t preserve_history;
3156 };
3157
3158
3159 static svn_error_t *
txn_body_copy(void * baton,trail_t * trail)3160 txn_body_copy(void *baton,
3161 trail_t *trail)
3162 {
3163 struct copy_args *args = baton;
3164 svn_fs_root_t *from_root = args->from_root;
3165 const char *from_path = args->from_path;
3166 svn_fs_root_t *to_root = args->to_root;
3167 const char *to_path = args->to_path;
3168 dag_node_t *from_node;
3169 parent_path_t *to_parent_path;
3170 const char *txn_id = to_root->txn;
3171
3172 /* Get the NODE for FROM_PATH in FROM_ROOT.*/
3173 SVN_ERR(get_dag(&from_node, from_root, from_path, trail, trail->pool));
3174
3175 /* Build up the parent path from TO_PATH in TO_ROOT. If the last
3176 component does not exist, it's not that big a deal. We'll just
3177 make one there. */
3178 SVN_ERR(open_path(&to_parent_path, to_root, to_path,
3179 open_path_last_optional, txn_id, trail, trail->pool));
3180
3181 /* Check to see if to-path (or any child thereof) is locked, or at
3182 least 'reserved', whether it exists or not; if so, check that we
3183 can use the existing lock(s). */
3184 if (to_root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3185 {
3186 SVN_ERR(svn_fs_base__allow_locked_operation(to_path, TRUE,
3187 trail, trail->pool));
3188 }
3189
3190 /* If the destination node already exists as the same node as the
3191 source (in other words, this operation would result in nothing
3192 happening at all), just do nothing an return successfully,
3193 proud that you saved yourself from a tiresome task. */
3194 if ((to_parent_path->node)
3195 && (svn_fs_base__id_compare(svn_fs_base__dag_get_id(from_node),
3196 svn_fs_base__dag_get_id
3197 (to_parent_path->node))
3198 == svn_fs_node_unchanged))
3199 return SVN_NO_ERROR;
3200
3201 if (! from_root->is_txn_root)
3202 {
3203 svn_fs_path_change_kind_t kind;
3204 dag_node_t *new_node;
3205 apr_int64_t old_mergeinfo_count = 0, mergeinfo_count;
3206 base_fs_data_t *bfd = trail->fs->fsap_data;
3207
3208 /* If TO_PATH already existed prior to the copy, note that this
3209 operation is a replacement, not an addition. */
3210 if (to_parent_path->node)
3211 kind = svn_fs_path_change_replace;
3212 else
3213 kind = svn_fs_path_change_add;
3214
3215 /* Make sure the target node's parents are mutable. */
3216 SVN_ERR(make_path_mutable(to_root, to_parent_path->parent,
3217 to_path, trail, trail->pool));
3218
3219 /* If this is a replacement operation, we need to know the old
3220 node's mergeinfo count. */
3221 if (to_parent_path->node)
3222 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
3223 &old_mergeinfo_count,
3224 to_parent_path->node,
3225 trail, trail->pool));
3226 /* Do the copy. */
3227 SVN_ERR(svn_fs_base__dag_copy(to_parent_path->parent->node,
3228 to_parent_path->entry,
3229 from_node,
3230 args->preserve_history,
3231 from_root->rev,
3232 from_path, txn_id, trail, trail->pool));
3233
3234 /* Adjust the mergeinfo counts of the destination's parents if
3235 our format supports it. */
3236 if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
3237 {
3238 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
3239 &mergeinfo_count,
3240 from_node, trail,
3241 trail->pool));
3242 SVN_ERR(adjust_parent_mergeinfo_counts
3243 (to_parent_path->parent,
3244 mergeinfo_count - old_mergeinfo_count,
3245 txn_id, trail, trail->pool));
3246 }
3247
3248 /* Make a record of this modification in the changes table. */
3249 SVN_ERR(get_dag(&new_node, to_root, to_path, trail, trail->pool));
3250 SVN_ERR(add_change(to_root->fs, txn_id, to_path,
3251 svn_fs_base__dag_get_id(new_node),
3252 kind, FALSE, FALSE, trail, trail->pool));
3253 }
3254 else
3255 {
3256 /* See IZ Issue #436 */
3257 /* Copying from transaction roots not currently available.
3258
3259 ### cmpilato todo someday: make this not so. :-) Note that
3260 when copying from mutable trees, you have to make sure that
3261 you aren't creating a cyclic graph filesystem, and a simple
3262 referencing operation won't cut it. Currently, we should not
3263 be able to reach this clause, and the interface reports that
3264 this only works from immutable trees anyway, but JimB has
3265 stated that this requirement need not be necessary in the
3266 future. */
3267
3268 SVN_ERR_MALFUNCTION();
3269 }
3270
3271 return SVN_NO_ERROR;
3272 }
3273
3274
3275 /* Set *SAME_P to TRUE if FS1 and FS2 have the same UUID, else set to FALSE.
3276 Use POOL for temporary allocation only.
3277 Note: this code is duplicated between libsvn_fs_fs and libsvn_fs_base. */
3278 static svn_error_t *
fs_same_p(svn_boolean_t * same_p,svn_fs_t * fs1,svn_fs_t * fs2,apr_pool_t * pool)3279 fs_same_p(svn_boolean_t *same_p,
3280 svn_fs_t *fs1,
3281 svn_fs_t *fs2,
3282 apr_pool_t *pool)
3283 {
3284 *same_p = ! strcmp(fs1->uuid, fs2->uuid);
3285 return SVN_NO_ERROR;
3286 }
3287
3288 /* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under
3289 TO_ROOT. If PRESERVE_HISTORY is set, then the copy is recorded in
3290 the copies table. Perform temporary allocations in POOL. */
3291 static svn_error_t *
copy_helper(svn_fs_root_t * from_root,const char * from_path,svn_fs_root_t * to_root,const char * to_path,svn_boolean_t preserve_history,apr_pool_t * pool)3292 copy_helper(svn_fs_root_t *from_root,
3293 const char *from_path,
3294 svn_fs_root_t *to_root,
3295 const char *to_path,
3296 svn_boolean_t preserve_history,
3297 apr_pool_t *pool)
3298 {
3299 struct copy_args args;
3300 svn_boolean_t same_p;
3301
3302 /* Use an error check, not an assert, because even the caller cannot
3303 guarantee that a filesystem's UUID has not changed "on the fly". */
3304 SVN_ERR(fs_same_p(&same_p, from_root->fs, to_root->fs, pool));
3305 if (! same_p)
3306 return svn_error_createf
3307 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
3308 _("Cannot copy between two different filesystems ('%s' and '%s')"),
3309 from_root->fs->path, to_root->fs->path);
3310
3311 if (! to_root->is_txn_root)
3312 return SVN_FS__NOT_TXN(to_root);
3313
3314 if (from_root->is_txn_root)
3315 return svn_error_create
3316 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
3317 _("Copy from mutable tree not currently supported"));
3318
3319 args.from_root = from_root;
3320 args.from_path = from_path;
3321 args.to_root = to_root;
3322 args.to_path = to_path;
3323 args.preserve_history = preserve_history;
3324
3325 return svn_fs_base__retry_txn(to_root->fs, txn_body_copy, &args,
3326 TRUE, pool);
3327 }
3328
3329 static svn_error_t *
base_copy(svn_fs_root_t * from_root,const char * from_path,svn_fs_root_t * to_root,const char * to_path,apr_pool_t * pool)3330 base_copy(svn_fs_root_t *from_root,
3331 const char *from_path,
3332 svn_fs_root_t *to_root,
3333 const char *to_path,
3334 apr_pool_t *pool)
3335 {
3336 return copy_helper(from_root, from_path, to_root, to_path, TRUE, pool);
3337 }
3338
3339
3340 static svn_error_t *
base_revision_link(svn_fs_root_t * from_root,svn_fs_root_t * to_root,const char * path,apr_pool_t * pool)3341 base_revision_link(svn_fs_root_t *from_root,
3342 svn_fs_root_t *to_root,
3343 const char *path,
3344 apr_pool_t *pool)
3345 {
3346 return copy_helper(from_root, path, to_root, path, FALSE, pool);
3347 }
3348
3349
3350 struct copied_from_args
3351 {
3352 svn_fs_root_t *root; /* Root for the node whose ancestry we seek. */
3353 const char *path; /* Path for the node whose ancestry we seek. */
3354
3355 svn_revnum_t result_rev; /* Revision, if any, of the ancestor. */
3356 const char *result_path; /* Path, if any, of the ancestor. */
3357
3358 apr_pool_t *pool; /* Allocate `result_path' here. */
3359 };
3360
3361
3362 static svn_error_t *
txn_body_copied_from(void * baton,trail_t * trail)3363 txn_body_copied_from(void *baton, trail_t *trail)
3364 {
3365 struct copied_from_args *args = baton;
3366 const svn_fs_id_t *node_id, *pred_id;
3367 dag_node_t *node;
3368 svn_fs_t *fs = args->root->fs;
3369
3370 /* Clear the return variables. */
3371 args->result_path = NULL;
3372 args->result_rev = SVN_INVALID_REVNUM;
3373
3374 /* Fetch the NODE in question. */
3375 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
3376 node_id = svn_fs_base__dag_get_id(node);
3377
3378 /* Check the node's predecessor-ID. If it doesn't have one, it
3379 isn't a copy. */
3380 SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id, node,
3381 trail, trail->pool));
3382 if (! pred_id)
3383 return SVN_NO_ERROR;
3384
3385 /* If NODE's copy-ID is the same as that of its predecessor... */
3386 if (strcmp(svn_fs_base__id_copy_id(node_id),
3387 svn_fs_base__id_copy_id(pred_id)) != 0)
3388 {
3389 /* ... then NODE was either the target of a copy operation,
3390 a copied subtree item. We examine the actual copy record
3391 to determine which is the case. */
3392 copy_t *copy;
3393 SVN_ERR(svn_fs_bdb__get_copy(©, fs,
3394 svn_fs_base__id_copy_id(node_id),
3395 trail, trail->pool));
3396 if ((copy->kind == copy_kind_real)
3397 && svn_fs_base__id_eq(copy->dst_noderev_id, node_id))
3398 {
3399 args->result_path = copy->src_path;
3400 SVN_ERR(svn_fs_base__txn_get_revision(&(args->result_rev), fs,
3401 copy->src_txn_id,
3402 trail, trail->pool));
3403 }
3404 }
3405 return SVN_NO_ERROR;
3406 }
3407
3408
3409 static svn_error_t *
base_copied_from(svn_revnum_t * rev_p,const char ** path_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)3410 base_copied_from(svn_revnum_t *rev_p,
3411 const char **path_p,
3412 svn_fs_root_t *root,
3413 const char *path,
3414 apr_pool_t *pool)
3415 {
3416 struct copied_from_args args;
3417 apr_pool_t *scratch_pool = svn_pool_create(pool);
3418 args.root = root;
3419 args.path = path;
3420 args.pool = pool;
3421
3422 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_copied_from, &args,
3423 FALSE, scratch_pool));
3424
3425 *rev_p = args.result_rev;
3426 *path_p = args.result_path ? apr_pstrdup(pool, args.result_path) : NULL;
3427
3428 svn_pool_destroy(scratch_pool);
3429 return SVN_NO_ERROR;
3430 }
3431
3432
3433
3434 /* Files. */
3435
3436
3437 struct make_file_args
3438 {
3439 svn_fs_root_t *root;
3440 const char *path;
3441 };
3442
3443
3444 static svn_error_t *
txn_body_make_file(void * baton,trail_t * trail)3445 txn_body_make_file(void *baton,
3446 trail_t *trail)
3447 {
3448 struct make_file_args *args = baton;
3449 svn_fs_root_t *root = args->root;
3450 const char *path = args->path;
3451 parent_path_t *parent_path;
3452 dag_node_t *child;
3453 const char *txn_id = root->txn;
3454
3455 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
3456 txn_id, trail, trail->pool));
3457
3458 /* If there's already a file by that name, complain.
3459 This also catches the case of trying to make a file named `/'. */
3460 if (parent_path->node)
3461 return SVN_FS__ALREADY_EXISTS(root, path);
3462
3463 /* Check to see if some lock is 'reserving' a file-path or dir-path
3464 at that location, or even some child-path; if so, check that we
3465 can use it. */
3466 if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3467 {
3468 SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
3469 trail, trail->pool));
3470 }
3471
3472 /* Create the file. */
3473 SVN_ERR(make_path_mutable(root, parent_path->parent, path,
3474 trail, trail->pool));
3475 SVN_ERR(svn_fs_base__dag_make_file(&child,
3476 parent_path->parent->node,
3477 parent_path_path(parent_path->parent,
3478 trail->pool),
3479 parent_path->entry,
3480 txn_id,
3481 trail, trail->pool));
3482
3483 /* Make a record of this modification in the changes table. */
3484 return add_change(root->fs, txn_id, path,
3485 svn_fs_base__dag_get_id(child),
3486 svn_fs_path_change_add, TRUE, FALSE,
3487 trail, trail->pool);
3488 }
3489
3490
3491 static svn_error_t *
base_make_file(svn_fs_root_t * root,const char * path,apr_pool_t * pool)3492 base_make_file(svn_fs_root_t *root,
3493 const char *path,
3494 apr_pool_t *pool)
3495 {
3496 struct make_file_args args;
3497
3498 args.root = root;
3499 args.path = path;
3500 return svn_fs_base__retry_txn(root->fs, txn_body_make_file, &args,
3501 TRUE, pool);
3502 }
3503
3504
3505
3506 struct file_length_args
3507 {
3508 svn_fs_root_t *root;
3509 const char *path;
3510 svn_filesize_t length; /* OUT parameter */
3511 };
3512
3513 static svn_error_t *
txn_body_file_length(void * baton,trail_t * trail)3514 txn_body_file_length(void *baton,
3515 trail_t *trail)
3516 {
3517 struct file_length_args *args = baton;
3518 dag_node_t *file;
3519
3520 /* First create a dag_node_t from the root/path pair. */
3521 SVN_ERR(get_dag(&file, args->root, args->path, trail, trail->pool));
3522
3523 /* Now fetch its length */
3524 return svn_fs_base__dag_file_length(&args->length, file,
3525 trail, trail->pool);
3526 }
3527
3528 static svn_error_t *
base_file_length(svn_filesize_t * length_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)3529 base_file_length(svn_filesize_t *length_p,
3530 svn_fs_root_t *root,
3531 const char *path,
3532 apr_pool_t *pool)
3533 {
3534 struct file_length_args args;
3535
3536 args.root = root;
3537 args.path = path;
3538 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_file_length, &args,
3539 TRUE, pool));
3540
3541 *length_p = args.length;
3542 return SVN_NO_ERROR;
3543 }
3544
3545
3546 struct file_checksum_args
3547 {
3548 svn_fs_root_t *root;
3549 const char *path;
3550 svn_checksum_kind_t kind;
3551 svn_checksum_t **checksum; /* OUT parameter */
3552 };
3553
3554 static svn_error_t *
txn_body_file_checksum(void * baton,trail_t * trail)3555 txn_body_file_checksum(void *baton,
3556 trail_t *trail)
3557 {
3558 struct file_checksum_args *args = baton;
3559 dag_node_t *file;
3560
3561 SVN_ERR(get_dag(&file, args->root, args->path, trail, trail->pool));
3562
3563 return svn_fs_base__dag_file_checksum(args->checksum, args->kind, file,
3564 trail, trail->pool);
3565 }
3566
3567 static svn_error_t *
base_file_checksum(svn_checksum_t ** checksum,svn_checksum_kind_t kind,svn_fs_root_t * root,const char * path,apr_pool_t * pool)3568 base_file_checksum(svn_checksum_t **checksum,
3569 svn_checksum_kind_t kind,
3570 svn_fs_root_t *root,
3571 const char *path,
3572 apr_pool_t *pool)
3573 {
3574 struct file_checksum_args args;
3575 apr_pool_t *scratch_pool = svn_pool_create(pool);
3576
3577 args.root = root;
3578 args.path = path;
3579 args.kind = kind;
3580 args.checksum = checksum;
3581 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_file_checksum, &args,
3582 FALSE, scratch_pool));
3583 *checksum = svn_checksum_dup(*checksum, pool);
3584 svn_pool_destroy(scratch_pool);
3585 return SVN_NO_ERROR;
3586 }
3587
3588
3589 /* --- Machinery for svn_fs_file_contents() --- */
3590
3591
3592 /* Local baton type for txn_body_get_file_contents. */
3593 typedef struct file_contents_baton_t
3594 {
3595 /* The file we want to read. */
3596 svn_fs_root_t *root;
3597 const char *path;
3598
3599 /* The dag_node that will be made from the above. */
3600 dag_node_t *node;
3601
3602 /* The pool in which `file_stream' (below) is allocated. */
3603 apr_pool_t *pool;
3604
3605 /* The readable file stream that will be made from the
3606 dag_node. (And returned to the caller.) */
3607 svn_stream_t *file_stream;
3608
3609 } file_contents_baton_t;
3610
3611
3612 /* Main body of svn_fs_file_contents; converts a root/path pair into
3613 a readable file stream (in the context of a db txn). */
3614 static svn_error_t *
txn_body_get_file_contents(void * baton,trail_t * trail)3615 txn_body_get_file_contents(void *baton, trail_t *trail)
3616 {
3617 file_contents_baton_t *fb = (file_contents_baton_t *) baton;
3618
3619 /* First create a dag_node_t from the root/path pair. */
3620 SVN_ERR(get_dag(&(fb->node), fb->root, fb->path, trail, trail->pool));
3621
3622 /* Then create a readable stream from the dag_node_t. */
3623 return svn_fs_base__dag_get_contents(&(fb->file_stream),
3624 fb->node, trail, fb->pool);
3625 }
3626
3627
3628
3629 static svn_error_t *
base_file_contents(svn_stream_t ** contents,svn_fs_root_t * root,const char * path,apr_pool_t * pool)3630 base_file_contents(svn_stream_t **contents,
3631 svn_fs_root_t *root,
3632 const char *path,
3633 apr_pool_t *pool)
3634 {
3635 file_contents_baton_t *fb = apr_pcalloc(pool, sizeof(*fb));
3636 fb->root = root;
3637 fb->path = path;
3638 fb->pool = pool;
3639
3640 /* Create the readable stream in the context of a db txn. */
3641 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_get_file_contents, fb,
3642 FALSE, pool));
3643
3644 *contents = fb->file_stream;
3645 return SVN_NO_ERROR;
3646 }
3647
3648 /* --- End machinery for svn_fs_file_contents() --- */
3649
3650
3651
3652 /* --- Machinery for svn_fs_apply_textdelta() --- */
3653
3654
3655 /* Local baton type for all the helper functions below. */
3656 typedef struct txdelta_baton_t
3657 {
3658 /* This is the custom-built window consumer given to us by the delta
3659 library; it uniquely knows how to read data from our designated
3660 "source" stream, interpret the window, and write data to our
3661 designated "target" stream (in this case, our repos file.) */
3662 svn_txdelta_window_handler_t interpreter;
3663 void *interpreter_baton;
3664
3665 /* The original file info */
3666 svn_fs_root_t *root;
3667 const char *path;
3668
3669 /* Derived from the file info */
3670 dag_node_t *node;
3671
3672 svn_stream_t *source_stream;
3673 svn_stream_t *target_stream;
3674 svn_stream_t *string_stream;
3675 svn_stringbuf_t *target_string;
3676
3677 /* Checksums for the base text against which a delta is to be
3678 applied, and for the resultant fulltext, respectively. Either or
3679 both may be null, in which case ignored. */
3680 svn_checksum_t *base_checksum;
3681 svn_checksum_t *result_checksum;
3682
3683 /* Pool used by db txns */
3684 apr_pool_t *pool;
3685
3686 } txdelta_baton_t;
3687
3688
3689 /* A trail-ready wrapper around svn_fs_base__dag_finalize_edits.
3690 * This closes BATON->target_stream.
3691 *
3692 * Note: If you're confused about how this function relates to another
3693 * of similar name, think of it this way:
3694 *
3695 * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
3696 * svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits()
3697 */
3698 static svn_error_t *
txn_body_txdelta_finalize_edits(void * baton,trail_t * trail)3699 txn_body_txdelta_finalize_edits(void *baton, trail_t *trail)
3700 {
3701 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3702 SVN_ERR(svn_fs_base__dag_finalize_edits(tb->node,
3703 tb->result_checksum,
3704 tb->root->txn,
3705 trail, trail->pool));
3706
3707 /* Make a record of this modification in the changes table. */
3708 return add_change(tb->root->fs, tb->root->txn, tb->path,
3709 svn_fs_base__dag_get_id(tb->node),
3710 svn_fs_path_change_modify, TRUE, FALSE, trail,
3711 trail->pool);
3712 }
3713
3714
3715 /* ### see comment in window_consumer() regarding this function. */
3716
3717 /* Helper function of generic type `svn_write_fn_t'. Implements a
3718 writable stream which appends to an svn_stringbuf_t. */
3719 static svn_error_t *
write_to_string(void * baton,const char * data,apr_size_t * len)3720 write_to_string(void *baton, const char *data, apr_size_t *len)
3721 {
3722 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3723 svn_stringbuf_appendbytes(tb->target_string, data, *len);
3724 return SVN_NO_ERROR;
3725 }
3726
3727
3728
3729 /* The main window handler returned by svn_fs_apply_textdelta. */
3730 static svn_error_t *
window_consumer(svn_txdelta_window_t * window,void * baton)3731 window_consumer(svn_txdelta_window_t *window, void *baton)
3732 {
3733 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3734
3735 /* Send the window right through to the custom window interpreter.
3736 In theory, the interpreter will then write more data to
3737 cb->target_string. */
3738 SVN_ERR(tb->interpreter(window, tb->interpreter_baton));
3739
3740 /* ### the write_to_string() callback for the txdelta's output stream
3741 ### should be doing all the flush determination logic, not here.
3742 ### in a drastic case, a window could generate a LOT more than the
3743 ### maximum buffer size. we want to flush to the underlying target
3744 ### stream much sooner (e.g. also in a streamy fashion). also, by
3745 ### moving this logic inside the stream, the stream becomes nice
3746 ### and encapsulated: it holds all the logic about buffering and
3747 ### flushing.
3748 ###
3749 ### further: I believe the buffering should be removed from tree.c
3750 ### the buffering should go into the target_stream itself, which
3751 ### is defined by reps-string.c. Specifically, I think the
3752 ### rep_write_contents() function will handle the buffering and
3753 ### the spill to the underlying DB. by locating it there, then
3754 ### anybody who gets a writable stream for FS content can take
3755 ### advantage of the buffering capability. this will be important
3756 ### when we export an FS API function for writing a fulltext into
3757 ### the FS, rather than forcing that fulltext thru apply_textdelta.
3758 */
3759
3760 /* Check to see if we need to purge the portion of the contents that
3761 have been written thus far. */
3762 if ((! window) || (tb->target_string->len > WRITE_BUFFER_SIZE))
3763 {
3764 apr_size_t len = tb->target_string->len;
3765 SVN_ERR(svn_stream_write(tb->target_stream,
3766 tb->target_string->data,
3767 &len));
3768 svn_stringbuf_setempty(tb->target_string);
3769 }
3770
3771 /* Is the window NULL? If so, we're done. */
3772 if (! window)
3773 {
3774 /* Close the internal-use stream. ### This used to be inside of
3775 txn_body_fulltext_finalize_edits(), but that invoked a nested
3776 Berkeley DB transaction -- scandalous! */
3777 SVN_ERR(svn_stream_close(tb->target_stream));
3778
3779 /* Tell the dag subsystem that we're finished with our edits. */
3780 SVN_ERR(svn_fs_base__retry_txn(tb->root->fs,
3781 txn_body_txdelta_finalize_edits, tb,
3782 FALSE, tb->pool));
3783 }
3784
3785 return SVN_NO_ERROR;
3786 }
3787
3788
3789 static svn_error_t *
txn_body_apply_textdelta(void * baton,trail_t * trail)3790 txn_body_apply_textdelta(void *baton, trail_t *trail)
3791 {
3792 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3793 parent_path_t *parent_path;
3794 const char *txn_id = tb->root->txn;
3795
3796 /* Call open_path with no flags, as we want this to return an error
3797 if the node for which we are searching doesn't exist. */
3798 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id,
3799 trail, trail->pool));
3800
3801 /* Check to see if path is locked; if so, check that we can use it. */
3802 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3803 SVN_ERR(svn_fs_base__allow_locked_operation(tb->path, FALSE,
3804 trail, trail->pool));
3805
3806 /* Now, make sure this path is mutable. */
3807 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path,
3808 trail, trail->pool));
3809 tb->node = parent_path->node;
3810
3811 if (tb->base_checksum)
3812 {
3813 svn_checksum_t *checksum;
3814
3815 /* Until we finalize the node, its data_key points to the old
3816 contents, in other words, the base text. */
3817 SVN_ERR(svn_fs_base__dag_file_checksum(&checksum,
3818 tb->base_checksum->kind,
3819 tb->node, trail, trail->pool));
3820 /* TODO: This only compares checksums if they are the same kind, but
3821 we're calculating both SHA1 and MD5 checksums somewhere in
3822 reps-strings.c. Could we keep them both around somehow so this
3823 check could be more comprehensive? */
3824 if (!svn_checksum_match(tb->base_checksum, checksum))
3825 return svn_checksum_mismatch_err(tb->base_checksum, checksum,
3826 trail->pool,
3827 _("Base checksum mismatch on '%s'"),
3828 tb->path);
3829 }
3830
3831 /* Make a readable "source" stream out of the current contents of
3832 ROOT/PATH; obviously, this must done in the context of a db_txn.
3833 The stream is returned in tb->source_stream. */
3834 SVN_ERR(svn_fs_base__dag_get_contents(&(tb->source_stream),
3835 tb->node, trail, tb->pool));
3836
3837 /* Make a writable "target" stream */
3838 SVN_ERR(svn_fs_base__dag_get_edit_stream(&(tb->target_stream), tb->node,
3839 txn_id, trail, tb->pool));
3840
3841 /* Make a writable "string" stream which writes data to
3842 tb->target_string. */
3843 tb->target_string = svn_stringbuf_create_empty(tb->pool);
3844 tb->string_stream = svn_stream_create(tb, tb->pool);
3845 svn_stream_set_write(tb->string_stream, write_to_string);
3846
3847 /* Now, create a custom window handler that uses our two streams. */
3848 svn_txdelta_apply(tb->source_stream,
3849 tb->string_stream,
3850 NULL,
3851 tb->path,
3852 tb->pool,
3853 &(tb->interpreter),
3854 &(tb->interpreter_baton));
3855
3856 return SVN_NO_ERROR;
3857 }
3858
3859
3860 static svn_error_t *
base_apply_textdelta(svn_txdelta_window_handler_t * contents_p,void ** contents_baton_p,svn_fs_root_t * root,const char * path,svn_checksum_t * base_checksum,svn_checksum_t * result_checksum,apr_pool_t * pool)3861 base_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
3862 void **contents_baton_p,
3863 svn_fs_root_t *root,
3864 const char *path,
3865 svn_checksum_t *base_checksum,
3866 svn_checksum_t *result_checksum,
3867 apr_pool_t *pool)
3868 {
3869 txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
3870
3871 tb->root = root;
3872 tb->path = path;
3873 tb->pool = pool;
3874 tb->base_checksum = svn_checksum_dup(base_checksum, pool);
3875 tb->result_checksum = svn_checksum_dup(result_checksum, pool);
3876
3877 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_apply_textdelta, tb,
3878 FALSE, pool));
3879
3880 *contents_p = window_consumer;
3881 *contents_baton_p = tb;
3882 return SVN_NO_ERROR;
3883 }
3884
3885 /* --- End machinery for svn_fs_apply_textdelta() --- */
3886
3887 /* --- Machinery for svn_fs_apply_text() --- */
3888
3889 /* Baton for svn_fs_apply_text(). */
3890 struct text_baton_t
3891 {
3892 /* The original file info */
3893 svn_fs_root_t *root;
3894 const char *path;
3895
3896 /* Derived from the file info */
3897 dag_node_t *node;
3898
3899 /* The returned stream that will accept the file's new contents. */
3900 svn_stream_t *stream;
3901
3902 /* The actual fs stream that the returned stream will write to. */
3903 svn_stream_t *file_stream;
3904
3905 /* Checksum for the final fulltext written to the file. May
3906 be null, in which case ignored. */
3907 svn_checksum_t *result_checksum;
3908
3909 /* Pool used by db txns */
3910 apr_pool_t *pool;
3911 };
3912
3913
3914 /* A trail-ready wrapper around svn_fs_base__dag_finalize_edits, but for
3915 * fulltext data, not text deltas. Closes BATON->file_stream.
3916 *
3917 * Note: If you're confused about how this function relates to another
3918 * of similar name, think of it this way:
3919 *
3920 * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
3921 * svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits()
3922 */
3923 static svn_error_t *
txn_body_fulltext_finalize_edits(void * baton,trail_t * trail)3924 txn_body_fulltext_finalize_edits(void *baton, trail_t *trail)
3925 {
3926 struct text_baton_t *tb = baton;
3927 SVN_ERR(svn_fs_base__dag_finalize_edits(tb->node,
3928 tb->result_checksum,
3929 tb->root->txn,
3930 trail, trail->pool));
3931
3932 /* Make a record of this modification in the changes table. */
3933 return add_change(tb->root->fs, tb->root->txn, tb->path,
3934 svn_fs_base__dag_get_id(tb->node),
3935 svn_fs_path_change_modify, TRUE, FALSE, trail,
3936 trail->pool);
3937 }
3938
3939 /* Write function for the publically returned stream. */
3940 static svn_error_t *
text_stream_writer(void * baton,const char * data,apr_size_t * len)3941 text_stream_writer(void *baton,
3942 const char *data,
3943 apr_size_t *len)
3944 {
3945 struct text_baton_t *tb = baton;
3946
3947 /* Psst, here's some data. Pass it on to the -real- file stream. */
3948 return svn_stream_write(tb->file_stream, data, len);
3949 }
3950
3951 /* Close function for the publically returned stream. */
3952 static svn_error_t *
text_stream_closer(void * baton)3953 text_stream_closer(void *baton)
3954 {
3955 struct text_baton_t *tb = baton;
3956
3957 /* Close the internal-use stream. ### This used to be inside of
3958 txn_body_fulltext_finalize_edits(), but that invoked a nested
3959 Berkeley DB transaction -- scandalous! */
3960 SVN_ERR(svn_stream_close(tb->file_stream));
3961
3962 /* Need to tell fs that we're done sending text */
3963 return svn_fs_base__retry_txn(tb->root->fs,
3964 txn_body_fulltext_finalize_edits, tb,
3965 FALSE, tb->pool);
3966 }
3967
3968
3969 static svn_error_t *
txn_body_apply_text(void * baton,trail_t * trail)3970 txn_body_apply_text(void *baton, trail_t *trail)
3971 {
3972 struct text_baton_t *tb = baton;
3973 parent_path_t *parent_path;
3974 const char *txn_id = tb->root->txn;
3975
3976 /* Call open_path with no flags, as we want this to return an error
3977 if the node for which we are searching doesn't exist. */
3978 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id,
3979 trail, trail->pool));
3980
3981 /* Check to see if path is locked; if so, check that we can use it. */
3982 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3983 SVN_ERR(svn_fs_base__allow_locked_operation(tb->path, FALSE,
3984 trail, trail->pool));
3985
3986 /* Now, make sure this path is mutable. */
3987 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path,
3988 trail, trail->pool));
3989 tb->node = parent_path->node;
3990
3991 /* Make a writable stream for replacing the file's text. */
3992 SVN_ERR(svn_fs_base__dag_get_edit_stream(&(tb->file_stream), tb->node,
3993 txn_id, trail, tb->pool));
3994
3995 /* Create a 'returnable' stream which writes to the file_stream. */
3996 tb->stream = svn_stream_create(tb, tb->pool);
3997 svn_stream_set_write(tb->stream, text_stream_writer);
3998 svn_stream_set_close(tb->stream, text_stream_closer);
3999
4000 return SVN_NO_ERROR;
4001 }
4002
4003
4004 static svn_error_t *
base_apply_text(svn_stream_t ** contents_p,svn_fs_root_t * root,const char * path,svn_checksum_t * result_checksum,apr_pool_t * pool)4005 base_apply_text(svn_stream_t **contents_p,
4006 svn_fs_root_t *root,
4007 const char *path,
4008 svn_checksum_t *result_checksum,
4009 apr_pool_t *pool)
4010 {
4011 struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
4012
4013 tb->root = root;
4014 tb->path = path;
4015 tb->pool = pool;
4016 tb->result_checksum = svn_checksum_dup(result_checksum, pool);
4017
4018 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_apply_text, tb,
4019 FALSE, pool));
4020
4021 *contents_p = tb->stream;
4022 return SVN_NO_ERROR;
4023 }
4024
4025 /* --- End machinery for svn_fs_apply_text() --- */
4026
4027
4028 /* Note: we're sharing the `things_changed_args' struct with
4029 svn_fs_props_changed(). */
4030
4031 static svn_error_t *
txn_body_contents_changed(void * baton,trail_t * trail)4032 txn_body_contents_changed(void *baton, trail_t *trail)
4033 {
4034 struct things_changed_args *args = baton;
4035 dag_node_t *node1, *node2;
4036 svn_checksum_t *checksum1, *checksum2;
4037 svn_stream_t *stream1, *stream2;
4038 svn_boolean_t same;
4039
4040 SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool));
4041 SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool));
4042 SVN_ERR(svn_fs_base__things_different(NULL, args->changed_p,
4043 node1, node2, trail, trail->pool));
4044
4045 /* Is there a potential false positive and do we want to correct it? */
4046 if (!args->strict || !*args->changed_p)
4047 return SVN_NO_ERROR;
4048
4049 /* Different representations. They might still have equal contents. */
4050
4051 /* Compare MD5 checksums. These should be readily accessible. */
4052 SVN_ERR(svn_fs_base__dag_file_checksum(&checksum1, svn_checksum_md5,
4053 node1, trail, trail->pool));
4054 SVN_ERR(svn_fs_base__dag_file_checksum(&checksum2, svn_checksum_md5,
4055 node2, trail, trail->pool));
4056
4057 /* Different MD5 checksums -> different contents */
4058 if (!svn_checksum_match(checksum1, checksum2))
4059 return SVN_NO_ERROR;
4060
4061 /* Paranoia. Compare SHA1 checksums because that's the level of
4062 confidence we require for e.g. the working copy. */
4063 SVN_ERR(svn_fs_base__dag_file_checksum(&checksum1, svn_checksum_sha1,
4064 node1, trail, trail->pool));
4065 SVN_ERR(svn_fs_base__dag_file_checksum(&checksum2, svn_checksum_sha1,
4066 node2, trail, trail->pool));
4067
4068 /* Different SHA1 checksums -> different contents */
4069 if (checksum1 && checksum2)
4070 {
4071 *args->changed_p = !svn_checksum_match(checksum1, checksum2);
4072 return SVN_NO_ERROR;
4073 }
4074
4075 /* SHA1 checksums are not available for very old reps / repositories. */
4076 SVN_ERR(svn_fs_base__dag_get_contents(&stream1, node1, trail, trail->pool));
4077 SVN_ERR(svn_fs_base__dag_get_contents(&stream2, node2, trail, trail->pool));
4078 SVN_ERR(svn_stream_contents_same2(&same, stream1, stream2, trail->pool));
4079
4080 /* Now, it's definitive. */
4081 *args->changed_p = !same;
4082 return SVN_NO_ERROR;
4083 }
4084
4085
4086 /* Note: it is acceptable for this function to call back into
4087 top-level interfaces because it does not itself use trails. */
4088 static svn_error_t *
base_contents_changed(svn_boolean_t * changed_p,svn_fs_root_t * root1,const char * path1,svn_fs_root_t * root2,const char * path2,svn_boolean_t strict,apr_pool_t * pool)4089 base_contents_changed(svn_boolean_t *changed_p,
4090 svn_fs_root_t *root1,
4091 const char *path1,
4092 svn_fs_root_t *root2,
4093 const char *path2,
4094 svn_boolean_t strict,
4095 apr_pool_t *pool)
4096 {
4097 struct things_changed_args args;
4098
4099 /* Check that roots are in the same fs. */
4100 if (root1->fs != root2->fs)
4101 return svn_error_create
4102 (SVN_ERR_FS_GENERAL, NULL,
4103 _("Cannot compare file contents between two different filesystems"));
4104
4105 /* Check that both paths are files. */
4106 {
4107 svn_node_kind_t kind;
4108
4109 SVN_ERR(base_check_path(&kind, root1, path1, pool));
4110 if (kind != svn_node_file)
4111 return svn_error_createf
4112 (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path1);
4113
4114 SVN_ERR(base_check_path(&kind, root2, path2, pool));
4115 if (kind != svn_node_file)
4116 return svn_error_createf
4117 (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2);
4118 }
4119
4120 args.root1 = root1;
4121 args.root2 = root2;
4122 args.path1 = path1;
4123 args.path2 = path2;
4124 args.changed_p = changed_p;
4125 args.pool = pool;
4126 args.strict = strict;
4127
4128 return svn_fs_base__retry_txn(root1->fs, txn_body_contents_changed, &args,
4129 TRUE, pool);
4130 }
4131
4132
4133
4134 /* Public interface to computing file text deltas. */
4135
4136 /* Note: it is acceptable for this function to call back into
4137 public FS API interfaces because it does not itself use trails. */
4138 static svn_error_t *
base_get_file_delta_stream(svn_txdelta_stream_t ** stream_p,svn_fs_root_t * source_root,const char * source_path,svn_fs_root_t * target_root,const char * target_path,apr_pool_t * pool)4139 base_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
4140 svn_fs_root_t *source_root,
4141 const char *source_path,
4142 svn_fs_root_t *target_root,
4143 const char *target_path,
4144 apr_pool_t *pool)
4145 {
4146 svn_stream_t *source, *target;
4147 svn_txdelta_stream_t *delta_stream;
4148
4149 /* Get read functions for the source file contents. */
4150 if (source_root && source_path)
4151 SVN_ERR(base_file_contents(&source, source_root, source_path, pool));
4152 else
4153 source = svn_stream_empty(pool);
4154
4155 /* Get read functions for the target file contents. */
4156 SVN_ERR(base_file_contents(&target, target_root, target_path, pool));
4157
4158 /* Create a delta stream that turns the ancestor into the target. */
4159 svn_txdelta2(&delta_stream, source, target, TRUE, pool);
4160
4161 *stream_p = delta_stream;
4162 return SVN_NO_ERROR;
4163 }
4164
4165
4166
4167 /* Finding Changes */
4168
4169 struct paths_changed_args
4170 {
4171 apr_hash_t *changes;
4172 svn_fs_root_t *root;
4173 };
4174
4175
4176 static svn_error_t *
txn_body_paths_changed(void * baton,trail_t * trail)4177 txn_body_paths_changed(void *baton,
4178 trail_t *trail)
4179 {
4180 /* WARNING: This is called *without* the protection of a Berkeley DB
4181 transaction. If you modify this function, keep that in mind. */
4182
4183 struct paths_changed_args *args = baton;
4184 const char *txn_id;
4185 svn_fs_t *fs = args->root->fs;
4186
4187 /* Get the transaction ID from ROOT. */
4188 if (! args->root->is_txn_root)
4189 SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, args->root->rev,
4190 trail, trail->pool));
4191 else
4192 txn_id = args->root->txn;
4193
4194 return svn_fs_bdb__changes_fetch(&(args->changes), fs, txn_id,
4195 trail, trail->pool);
4196 }
4197
4198
4199 static svn_error_t *
base_paths_changed(apr_hash_t ** changed_paths_p,svn_fs_root_t * root,apr_pool_t * pool)4200 base_paths_changed(apr_hash_t **changed_paths_p,
4201 svn_fs_root_t *root,
4202 apr_pool_t *pool)
4203 {
4204 struct paths_changed_args args;
4205 args.root = root;
4206 args.changes = NULL;
4207 SVN_ERR(svn_fs_base__retry(root->fs, txn_body_paths_changed, &args,
4208 FALSE, pool));
4209 *changed_paths_p = args.changes;
4210 return SVN_NO_ERROR;
4211 }
4212
4213
4214
4215 /* Our coolio opaque history object. */
4216 typedef struct base_history_data_t
4217 {
4218 /* filesystem object */
4219 svn_fs_t *fs;
4220
4221 /* path and revision of historical location */
4222 const char *path;
4223 svn_revnum_t revision;
4224
4225 /* internal-use hints about where to resume the history search. */
4226 const char *path_hint;
4227 svn_revnum_t rev_hint;
4228
4229 /* FALSE until the first call to svn_fs_history_prev(). */
4230 svn_boolean_t is_interesting;
4231 } base_history_data_t;
4232
4233
4234 static svn_fs_history_t *assemble_history(svn_fs_t *fs, const char *path,
4235 svn_revnum_t revision,
4236 svn_boolean_t is_interesting,
4237 const char *path_hint,
4238 svn_revnum_t rev_hint,
4239 apr_pool_t *pool);
4240
4241
4242 static svn_error_t *
base_node_history(svn_fs_history_t ** history_p,svn_fs_root_t * root,const char * path,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4243 base_node_history(svn_fs_history_t **history_p,
4244 svn_fs_root_t *root,
4245 const char *path,
4246 apr_pool_t *result_pool,
4247 apr_pool_t *scratch_pool)
4248 {
4249 svn_node_kind_t kind;
4250
4251 /* We require a revision root. */
4252 if (root->is_txn_root)
4253 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
4254
4255 /* And we require that the path exist in the root. */
4256 SVN_ERR(base_check_path(&kind, root, path, scratch_pool));
4257 if (kind == svn_node_none)
4258 return SVN_FS__NOT_FOUND(root, path);
4259
4260 /* Okay, all seems well. Build our history object and return it. */
4261 *history_p = assemble_history(root->fs,
4262 svn_fs__canonicalize_abspath(path,
4263 result_pool),
4264 root->rev, FALSE, NULL,
4265 SVN_INVALID_REVNUM, result_pool);
4266 return SVN_NO_ERROR;
4267 }
4268
4269
4270 /* Examine the PARENT_PATH structure chain to determine how copy IDs
4271 would be doled out in the event that PARENT_PATH was made mutable.
4272 Return the ID of the copy that last affected PARENT_PATH (and the
4273 COPY itself, if we've already fetched it).
4274 */
4275 static svn_error_t *
examine_copy_inheritance(const char ** copy_id,copy_t ** copy,svn_fs_t * fs,parent_path_t * parent_path,trail_t * trail,apr_pool_t * pool)4276 examine_copy_inheritance(const char **copy_id,
4277 copy_t **copy,
4278 svn_fs_t *fs,
4279 parent_path_t *parent_path,
4280 trail_t *trail,
4281 apr_pool_t *pool)
4282 {
4283 /* The default response -- our current copy ID, and no fetched COPY. */
4284 *copy_id = svn_fs_base__id_copy_id
4285 (svn_fs_base__dag_get_id(parent_path->node));
4286 *copy = NULL;
4287
4288 /* If we have no parent (we are looking at the root node), or if
4289 this node is supposed to inherit from itself, return that fact. */
4290 if (! parent_path->parent)
4291 return SVN_NO_ERROR;
4292
4293 /* We could be a branch destination (which would answer our question
4294 altogether)! But then, again, we might just have been modified
4295 in this revision, so all bets are off. */
4296 if (parent_path->copy_inherit == copy_id_inherit_self)
4297 {
4298 /* A copy ID of "0" means we've never been branched. Therefore,
4299 there are no copies relevant to our history. */
4300 if (((*copy_id)[0] == '0') && ((*copy_id)[1] == '\0'))
4301 return SVN_NO_ERROR;
4302
4303 /* Get the COPY record. If it was a real copy (not an implicit
4304 one), we have our answer. Otherwise, we fall through to the
4305 recursive case. */
4306 SVN_ERR(svn_fs_bdb__get_copy(copy, fs, *copy_id, trail, pool));
4307 if ((*copy)->kind != copy_kind_soft)
4308 return SVN_NO_ERROR;
4309 }
4310
4311 /* Otherwise, our answer is dependent upon our parent. */
4312 return examine_copy_inheritance(copy_id, copy, fs,
4313 parent_path->parent, trail, pool);
4314 }
4315
4316
4317 struct history_prev_args
4318 {
4319 svn_fs_history_t **prev_history_p;
4320 svn_fs_history_t *history;
4321 svn_boolean_t cross_copies;
4322 apr_pool_t *pool;
4323 };
4324
4325
4326 static svn_error_t *
txn_body_history_prev(void * baton,trail_t * trail)4327 txn_body_history_prev(void *baton, trail_t *trail)
4328 {
4329 struct history_prev_args *args = baton;
4330 svn_fs_history_t **prev_history = args->prev_history_p;
4331 svn_fs_history_t *history = args->history;
4332 base_history_data_t *bhd = history->fsap_data;
4333 const char *commit_path, *src_path, *path = bhd->path;
4334 svn_revnum_t commit_rev, src_rev, dst_rev, revision = bhd->revision;
4335 apr_pool_t *retpool = args->pool;
4336 svn_fs_t *fs = bhd->fs;
4337 parent_path_t *parent_path;
4338 dag_node_t *node;
4339 svn_fs_root_t *root;
4340 const svn_fs_id_t *node_id;
4341 const char *end_copy_id = NULL;
4342 struct revision_root_args rr_args;
4343 svn_boolean_t reported = bhd->is_interesting;
4344 const char *txn_id;
4345 copy_t *copy = NULL;
4346 svn_boolean_t retry = FALSE;
4347
4348 /* Initialize our return value. */
4349 *prev_history = NULL;
4350
4351 /* If our last history report left us hints about where to pickup
4352 the chase, then our last report was on the destination of a
4353 copy. If we are crossing copies, start from those locations,
4354 otherwise, we're all done here. */
4355 if (bhd->path_hint && SVN_IS_VALID_REVNUM(bhd->rev_hint))
4356 {
4357 reported = FALSE;
4358 if (! args->cross_copies)
4359 return SVN_NO_ERROR;
4360 path = bhd->path_hint;
4361 revision = bhd->rev_hint;
4362 }
4363
4364 /* Construct a ROOT for the current revision. */
4365 rr_args.root_p = &root;
4366 rr_args.rev = revision;
4367 SVN_ERR(txn_body_revision_root(&rr_args, trail));
4368
4369 /* Open PATH/REVISION, and get its node and a bunch of other
4370 goodies. */
4371 SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, revision, trail,
4372 trail->pool));
4373 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
4374 trail, trail->pool));
4375 node = parent_path->node;
4376 node_id = svn_fs_base__dag_get_id(node);
4377 commit_path = svn_fs_base__dag_get_created_path(node);
4378 SVN_ERR(svn_fs_base__dag_get_revision(&commit_rev, node,
4379 trail, trail->pool));
4380
4381 /* The Subversion filesystem is written in such a way that a given
4382 line of history may have at most one interesting history point
4383 per filesystem revision. Either that node was edited (and
4384 possibly copied), or it was copied but not edited. And a copy
4385 source cannot be from the same revision as its destination. So,
4386 if our history revision matches its node's commit revision, we
4387 know that ... */
4388 if (revision == commit_rev)
4389 {
4390 if (! reported)
4391 {
4392 /* ... we either have not yet reported on this revision (and
4393 need now to do so) ... */
4394 *prev_history = assemble_history(fs,
4395 apr_pstrdup(retpool, commit_path),
4396 commit_rev, TRUE, NULL,
4397 SVN_INVALID_REVNUM, retpool);
4398 return SVN_NO_ERROR;
4399 }
4400 else
4401 {
4402 /* ... or we *have* reported on this revision, and must now
4403 progress toward this node's predecessor (unless there is
4404 no predecessor, in which case we're all done!). */
4405 const svn_fs_id_t *pred_id;
4406
4407 SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id, node,
4408 trail, trail->pool));
4409 if (! pred_id)
4410 return SVN_NO_ERROR;
4411
4412 /* Replace NODE and friends with the information from its
4413 predecessor. */
4414 SVN_ERR(svn_fs_base__dag_get_node(&node, fs, pred_id,
4415 trail, trail->pool));
4416 node_id = svn_fs_base__dag_get_id(node);
4417 commit_path = svn_fs_base__dag_get_created_path(node);
4418 SVN_ERR(svn_fs_base__dag_get_revision(&commit_rev, node,
4419 trail, trail->pool));
4420 }
4421 }
4422
4423 /* Calculate a possibly relevant copy ID. */
4424 SVN_ERR(examine_copy_inheritance(&end_copy_id, ©, fs,
4425 parent_path, trail, trail->pool));
4426
4427 /* Initialize some state variables. */
4428 src_path = NULL;
4429 src_rev = SVN_INVALID_REVNUM;
4430 dst_rev = SVN_INVALID_REVNUM;
4431
4432 /* If our current copy ID (which is either the real copy ID of our
4433 node, or the last copy ID which would affect our node if it were
4434 to be made mutable) diffs at all from that of its predecessor
4435 (which is either a real predecessor, or is the node itself
4436 playing the predecessor role to an imaginary mutable successor),
4437 then we need to report a copy. */
4438 if (strcmp(svn_fs_base__id_copy_id(node_id), end_copy_id) != 0)
4439 {
4440 const char *remainder;
4441 dag_node_t *dst_node;
4442 const char *copy_dst;
4443
4444 /* Get the COPY record if we haven't already fetched it. */
4445 if (! copy)
4446 SVN_ERR(svn_fs_bdb__get_copy(©, fs, end_copy_id, trail,
4447 trail->pool));
4448
4449 /* Figure out the destination path of the copy operation. */
4450 SVN_ERR(svn_fs_base__dag_get_node(&dst_node, fs,
4451 copy->dst_noderev_id,
4452 trail, trail->pool));
4453 copy_dst = svn_fs_base__dag_get_created_path(dst_node);
4454
4455 /* If our current path was the very destination of the copy,
4456 then our new current path will be the copy source. If our
4457 current path was instead the *child* of the destination of
4458 the copy, then figure out its previous location by taking its
4459 path relative to the copy destination and appending that to
4460 the copy source. Finally, if our current path doesn't meet
4461 one of these other criteria ... ### for now just fallback to
4462 the old copy hunt algorithm. */
4463 remainder = svn_fspath__skip_ancestor(copy_dst, path);
4464
4465 if (remainder)
4466 {
4467 /* If we get here, then our current path is the destination
4468 of, or the child of the destination of, a copy. Fill
4469 in the return values and get outta here. */
4470 SVN_ERR(svn_fs_base__txn_get_revision
4471 (&src_rev, fs, copy->src_txn_id, trail, trail->pool));
4472 SVN_ERR(svn_fs_base__txn_get_revision
4473 (&dst_rev, fs,
4474 svn_fs_base__id_txn_id(copy->dst_noderev_id),
4475 trail, trail->pool));
4476 src_path = svn_fspath__join(copy->src_path, remainder,
4477 trail->pool);
4478 if (copy->kind == copy_kind_soft)
4479 retry = TRUE;
4480 }
4481 }
4482
4483 /* If we calculated a copy source path and revision, and the
4484 copy source revision doesn't pre-date a revision in which we
4485 *know* our node was modified, we'll make a 'copy-style' history
4486 object. */
4487 if (src_path && SVN_IS_VALID_REVNUM(src_rev) && (src_rev >= commit_rev))
4488 {
4489 /* It's possible for us to find a copy location that is the same
4490 as the history point we've just reported. If that happens,
4491 we simply need to take another trip through this history
4492 search. */
4493 if ((dst_rev == revision) && reported)
4494 retry = TRUE;
4495
4496 *prev_history = assemble_history(fs, apr_pstrdup(retpool, path),
4497 dst_rev, ! retry,
4498 src_path, src_rev, retpool);
4499 }
4500 else
4501 {
4502 *prev_history = assemble_history(fs, apr_pstrdup(retpool, commit_path),
4503 commit_rev, TRUE, NULL,
4504 SVN_INVALID_REVNUM, retpool);
4505 }
4506
4507 return SVN_NO_ERROR;
4508 }
4509
4510
4511 static svn_error_t *
base_history_prev(svn_fs_history_t ** prev_history_p,svn_fs_history_t * history,svn_boolean_t cross_copies,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4512 base_history_prev(svn_fs_history_t **prev_history_p,
4513 svn_fs_history_t *history,
4514 svn_boolean_t cross_copies,
4515 apr_pool_t *result_pool,
4516 apr_pool_t *scratch_pool)
4517 {
4518 svn_fs_history_t *prev_history = NULL;
4519 base_history_data_t *bhd = history->fsap_data;
4520 svn_fs_t *fs = bhd->fs;
4521
4522 /* Special case: the root directory changes in every single
4523 revision, no exceptions. And, the root can't be the target (or
4524 child of a target -- duh) of a copy. So, if that's our path,
4525 then we need only decrement our revision by 1, and there you go. */
4526 if (strcmp(bhd->path, "/") == 0)
4527 {
4528 if (! bhd->is_interesting)
4529 prev_history = assemble_history(fs, "/", bhd->revision,
4530 1, NULL, SVN_INVALID_REVNUM,
4531 result_pool);
4532 else if (bhd->revision > 0)
4533 prev_history = assemble_history(fs, "/", bhd->revision - 1,
4534 1, NULL, SVN_INVALID_REVNUM,
4535 result_pool);
4536 }
4537 else
4538 {
4539 struct history_prev_args args;
4540 prev_history = history;
4541
4542 while (1)
4543 {
4544 /* Get a trail, and get to work. */
4545
4546 args.prev_history_p = &prev_history;
4547 args.history = prev_history;
4548 args.cross_copies = cross_copies;
4549 args.pool = result_pool;
4550 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_history_prev, &args,
4551 FALSE, result_pool));
4552 if (! prev_history)
4553 break;
4554 bhd = prev_history->fsap_data;
4555 if (bhd->is_interesting)
4556 break;
4557 }
4558 }
4559
4560 *prev_history_p = prev_history;
4561 return SVN_NO_ERROR;
4562 }
4563
4564
4565 static svn_error_t *
base_history_location(const char ** path,svn_revnum_t * revision,svn_fs_history_t * history,apr_pool_t * pool)4566 base_history_location(const char **path,
4567 svn_revnum_t *revision,
4568 svn_fs_history_t *history,
4569 apr_pool_t *pool)
4570 {
4571 base_history_data_t *bhd = history->fsap_data;
4572
4573 *path = apr_pstrdup(pool, bhd->path);
4574 *revision = bhd->revision;
4575 return SVN_NO_ERROR;
4576 }
4577
4578
4579 static history_vtable_t history_vtable = {
4580 base_history_prev,
4581 base_history_location
4582 };
4583
4584
4585
4586 struct closest_copy_args
4587 {
4588 svn_fs_root_t **root_p;
4589 const char **path_p;
4590 svn_fs_root_t *root;
4591 const char *path;
4592 apr_pool_t *pool;
4593 };
4594
4595
4596 static svn_error_t *
txn_body_closest_copy(void * baton,trail_t * trail)4597 txn_body_closest_copy(void *baton, trail_t *trail)
4598 {
4599 struct closest_copy_args *args = baton;
4600 svn_fs_root_t *root = args->root;
4601 const char *path = args->path;
4602 svn_fs_t *fs = root->fs;
4603 parent_path_t *parent_path;
4604 const svn_fs_id_t *node_id;
4605 const char *txn_id, *copy_id;
4606 copy_t *copy = NULL;
4607 svn_fs_root_t *copy_dst_root;
4608 dag_node_t *path_node_in_copy_dst, *copy_dst_node, *copy_dst_root_node;
4609 const char *copy_dst_path;
4610 svn_revnum_t copy_dst_rev, created_rev;
4611 svn_error_t *err;
4612
4613 *(args->path_p) = NULL;
4614 *(args->root_p) = NULL;
4615
4616 /* Get the transaction ID associated with our root. */
4617 if (root->is_txn_root)
4618 txn_id = root->txn;
4619 else
4620 SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, root->rev,
4621 trail, trail->pool));
4622
4623 /* Open PATH in ROOT -- it must exist. */
4624 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
4625 trail, trail->pool));
4626 node_id = svn_fs_base__dag_get_id(parent_path->node);
4627
4628 /* Now, examine the copy inheritance rules in play should our path
4629 be made mutable in the future (if it isn't already). This will
4630 tell us about the youngest affecting copy. */
4631 SVN_ERR(examine_copy_inheritance(©_id, ©, fs, parent_path,
4632 trail, trail->pool));
4633
4634 /* Easy out: if the copy ID is 0, there's nothing of interest here. */
4635 if (((copy_id)[0] == '0') && ((copy_id)[1] == '\0'))
4636 return SVN_NO_ERROR;
4637
4638 /* Fetch our copy if examine_copy_inheritance() didn't do it for us. */
4639 if (! copy)
4640 SVN_ERR(svn_fs_bdb__get_copy(©, fs, copy_id, trail, trail->pool));
4641
4642 /* Figure out the destination path and revision of the copy operation. */
4643 SVN_ERR(svn_fs_base__dag_get_node(©_dst_node, fs, copy->dst_noderev_id,
4644 trail, trail->pool));
4645 copy_dst_path = svn_fs_base__dag_get_created_path(copy_dst_node);
4646 SVN_ERR(svn_fs_base__dag_get_revision(©_dst_rev, copy_dst_node,
4647 trail, trail->pool));
4648
4649 /* Turn that revision into a revision root. */
4650 SVN_ERR(svn_fs_base__dag_revision_root(©_dst_root_node, fs,
4651 copy_dst_rev, trail, args->pool));
4652 copy_dst_root = make_revision_root(fs, copy_dst_rev,
4653 copy_dst_root_node, args->pool);
4654
4655 /* It is possible that this node was created from scratch at some
4656 revision between COPY_DST_REV and the transaction associated with
4657 our ROOT. Make sure that PATH exists as of COPY_DST_REV and is
4658 related to this node-rev. */
4659 err = get_dag(&path_node_in_copy_dst, copy_dst_root, path,
4660 trail, trail->pool);
4661 if (err)
4662 {
4663 if ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
4664 || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY))
4665 {
4666 svn_error_clear(err);
4667 return SVN_NO_ERROR;
4668 }
4669 return svn_error_trace(err);
4670 }
4671 if ((svn_fs_base__dag_node_kind(path_node_in_copy_dst) == svn_node_none)
4672 || (! (svn_fs_base__id_check_related
4673 (node_id, svn_fs_base__dag_get_id(path_node_in_copy_dst)))))
4674 {
4675 return SVN_NO_ERROR;
4676 }
4677
4678 /* One final check must be done here. If you copy a directory and
4679 create a new entity somewhere beneath that directory in the same
4680 txn, then we can't claim that the copy affected the new entity.
4681 For example, if you do:
4682
4683 copy dir1 dir2
4684 create dir2/new-thing
4685 commit
4686
4687 then dir2/new-thing was not affected by the copy of dir1 to dir2.
4688 We detect this situation by asking if PATH@COPY_DST_REV's
4689 created-rev is COPY_DST_REV, and that node-revision has no
4690 predecessors, then there is no relevant closest copy.
4691 */
4692 SVN_ERR(svn_fs_base__dag_get_revision(&created_rev, path_node_in_copy_dst,
4693 trail, trail->pool));
4694 if (created_rev == copy_dst_rev)
4695 {
4696 const svn_fs_id_t *pred_id;
4697 SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id,
4698 path_node_in_copy_dst,
4699 trail, trail->pool));
4700 if (! pred_id)
4701 return SVN_NO_ERROR;
4702 }
4703
4704 *(args->path_p) = apr_pstrdup(args->pool, copy_dst_path);
4705 *(args->root_p) = copy_dst_root;
4706
4707 return SVN_NO_ERROR;
4708 }
4709
4710
4711 static svn_error_t *
base_closest_copy(svn_fs_root_t ** root_p,const char ** path_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)4712 base_closest_copy(svn_fs_root_t **root_p,
4713 const char **path_p,
4714 svn_fs_root_t *root,
4715 const char *path,
4716 apr_pool_t *pool)
4717 {
4718 struct closest_copy_args args;
4719 svn_fs_t *fs = root->fs;
4720 svn_fs_root_t *closest_root = NULL;
4721 const char *closest_path = NULL;
4722
4723 args.root_p = &closest_root;
4724 args.path_p = &closest_path;
4725 args.root = root;
4726 args.path = path;
4727 args.pool = pool;
4728 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_closest_copy, &args,
4729 FALSE, pool));
4730 *root_p = closest_root;
4731 *path_p = closest_path;
4732 return SVN_NO_ERROR;
4733 }
4734
4735
4736 /* Return a new history object (marked as "interesting") for PATH and
4737 REVISION, allocated in POOL, and with its members set to the values
4738 of the parameters provided. Note that PATH and PATH_HINT are not
4739 duped into POOL -- it is the responsibility of the caller to ensure
4740 that this happens. */
4741 static svn_fs_history_t *
assemble_history(svn_fs_t * fs,const char * path,svn_revnum_t revision,svn_boolean_t is_interesting,const char * path_hint,svn_revnum_t rev_hint,apr_pool_t * pool)4742 assemble_history(svn_fs_t *fs,
4743 const char *path,
4744 svn_revnum_t revision,
4745 svn_boolean_t is_interesting,
4746 const char *path_hint,
4747 svn_revnum_t rev_hint,
4748 apr_pool_t *pool)
4749 {
4750 svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history));
4751 base_history_data_t *bhd = apr_pcalloc(pool, sizeof(*bhd));
4752 bhd->path = path;
4753 bhd->revision = revision;
4754 bhd->is_interesting = is_interesting;
4755 bhd->path_hint = path_hint;
4756 bhd->rev_hint = rev_hint;
4757 bhd->fs = fs;
4758 history->vtable = &history_vtable;
4759 history->fsap_data = bhd;
4760 return history;
4761 }
4762
4763
4764 svn_error_t *
svn_fs_base__get_path_kind(svn_node_kind_t * kind,const char * path,trail_t * trail,apr_pool_t * pool)4765 svn_fs_base__get_path_kind(svn_node_kind_t *kind,
4766 const char *path,
4767 trail_t *trail,
4768 apr_pool_t *pool)
4769 {
4770 svn_revnum_t head_rev;
4771 svn_fs_root_t *root;
4772 dag_node_t *root_dir, *path_node;
4773 svn_error_t *err;
4774
4775 /* Get HEAD revision, */
4776 SVN_ERR(svn_fs_bdb__youngest_rev(&head_rev, trail->fs, trail, pool));
4777
4778 /* Then convert it into a root_t, */
4779 SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, head_rev,
4780 trail, pool));
4781 root = make_revision_root(trail->fs, head_rev, root_dir, pool);
4782
4783 /* And get the dag_node for path in the root_t. */
4784 err = get_dag(&path_node, root, path, trail, pool);
4785 if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND))
4786 {
4787 svn_error_clear(err);
4788 *kind = svn_node_none;
4789 return SVN_NO_ERROR;
4790 }
4791 else if (err)
4792 return svn_error_trace(err);
4793
4794 *kind = svn_fs_base__dag_node_kind(path_node);
4795 return SVN_NO_ERROR;
4796 }
4797
4798
4799 svn_error_t *
svn_fs_base__get_path_created_rev(svn_revnum_t * rev,const char * path,trail_t * trail,apr_pool_t * pool)4800 svn_fs_base__get_path_created_rev(svn_revnum_t *rev,
4801 const char *path,
4802 trail_t *trail,
4803 apr_pool_t *pool)
4804 {
4805 svn_revnum_t head_rev, created_rev;
4806 svn_fs_root_t *root;
4807 dag_node_t *root_dir, *path_node;
4808 svn_error_t *err;
4809
4810 /* Get HEAD revision, */
4811 SVN_ERR(svn_fs_bdb__youngest_rev(&head_rev, trail->fs, trail, pool));
4812
4813 /* Then convert it into a root_t, */
4814 SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, head_rev,
4815 trail, pool));
4816 root = make_revision_root(trail->fs, head_rev, root_dir, pool);
4817
4818 /* And get the dag_node for path in the root_t. */
4819 err = get_dag(&path_node, root, path, trail, pool);
4820 if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND))
4821 {
4822 svn_error_clear(err);
4823 *rev = SVN_INVALID_REVNUM;
4824 return SVN_NO_ERROR;
4825 }
4826 else if (err)
4827 return svn_error_trace(err);
4828
4829 /* Find the created_rev of the dag_node. */
4830 SVN_ERR(svn_fs_base__dag_get_revision(&created_rev, path_node,
4831 trail, pool));
4832
4833 *rev = created_rev;
4834 return SVN_NO_ERROR;
4835 }
4836
4837
4838
4839 /*** Finding the Origin of a Line of History ***/
4840
4841 /* Set *PREV_PATH and *PREV_REV to the path and revision which
4842 represent the location at which PATH in FS was located immediately
4843 prior to REVISION iff there was a copy operation (to PATH or one of
4844 its parent directories) between that previous location and
4845 PATH@REVISION.
4846
4847 If there was no such copy operation in that portion of PATH's
4848 history, set *PREV_PATH to NULL and *PREV_REV to SVN_INVALID_REVNUM.
4849
4850 WARNING: Do *not* call this from inside a trail. */
4851 static svn_error_t *
prev_location(const char ** prev_path,svn_revnum_t * prev_rev,svn_fs_t * fs,svn_fs_root_t * root,const char * path,apr_pool_t * pool)4852 prev_location(const char **prev_path,
4853 svn_revnum_t *prev_rev,
4854 svn_fs_t *fs,
4855 svn_fs_root_t *root,
4856 const char *path,
4857 apr_pool_t *pool)
4858 {
4859 const char *copy_path, *copy_src_path, *remainder;
4860 svn_fs_root_t *copy_root;
4861 svn_revnum_t copy_src_rev;
4862
4863 /* Ask about the most recent copy which affected PATH@REVISION. If
4864 there was no such copy, we're done. */
4865 SVN_ERR(base_closest_copy(©_root, ©_path, root, path, pool));
4866 if (! copy_root)
4867 {
4868 *prev_rev = SVN_INVALID_REVNUM;
4869 *prev_path = NULL;
4870 return SVN_NO_ERROR;
4871 }
4872
4873 /* Ultimately, it's not the path of the closest copy's source that
4874 we care about -- it's our own path's location in the copy source
4875 revision. So we'll tack the relative path that expresses the
4876 difference between the copy destination and our path in the copy
4877 revision onto the copy source path to determine this information.
4878
4879 In other words, if our path is "/branches/my-branch/foo/bar", and
4880 we know that the closest relevant copy was a copy of "/trunk" to
4881 "/branches/my-branch", then that relative path under the copy
4882 destination is "/foo/bar". Tacking that onto the copy source
4883 path tells us that our path was located at "/trunk/foo/bar"
4884 before the copy.
4885 */
4886 SVN_ERR(base_copied_from(©_src_rev, ©_src_path,
4887 copy_root, copy_path, pool));
4888 remainder = svn_fspath__skip_ancestor(copy_path, path);
4889 *prev_path = svn_fspath__join(copy_src_path, remainder, pool);
4890 *prev_rev = copy_src_rev;
4891 return SVN_NO_ERROR;
4892 }
4893
4894
4895 struct id_created_rev_args {
4896 svn_revnum_t revision;
4897 const svn_fs_id_t *id;
4898 const char *path;
4899 };
4900
4901
4902 static svn_error_t *
txn_body_id_created_rev(void * baton,trail_t * trail)4903 txn_body_id_created_rev(void *baton, trail_t *trail)
4904 {
4905 struct id_created_rev_args *args = baton;
4906 dag_node_t *node;
4907
4908 SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id,
4909 trail, trail->pool));
4910 return svn_fs_base__dag_get_revision(&(args->revision), node,
4911 trail, trail->pool);
4912 }
4913
4914
4915 struct get_set_node_origin_args {
4916 const svn_fs_id_t *origin_id;
4917 const char *node_id;
4918 };
4919
4920
4921 static svn_error_t *
txn_body_get_node_origin(void * baton,trail_t * trail)4922 txn_body_get_node_origin(void *baton, trail_t *trail)
4923 {
4924 struct get_set_node_origin_args *args = baton;
4925 return svn_fs_bdb__get_node_origin(&(args->origin_id), trail->fs,
4926 args->node_id, trail, trail->pool);
4927 }
4928
4929 static svn_error_t *
txn_body_set_node_origin(void * baton,trail_t * trail)4930 txn_body_set_node_origin(void *baton, trail_t *trail)
4931 {
4932 struct get_set_node_origin_args *args = baton;
4933 return svn_fs_bdb__set_node_origin(trail->fs, args->node_id,
4934 args->origin_id, trail, trail->pool);
4935 }
4936
4937 static svn_error_t *
base_node_origin_rev(svn_revnum_t * revision,svn_fs_root_t * root,const char * path,apr_pool_t * pool)4938 base_node_origin_rev(svn_revnum_t *revision,
4939 svn_fs_root_t *root,
4940 const char *path,
4941 apr_pool_t *pool)
4942 {
4943 svn_fs_t *fs = root->fs;
4944 base_fs_data_t *bfd = fs->fsap_data;
4945 struct get_set_node_origin_args args;
4946 const svn_fs_id_t *origin_id = NULL;
4947 struct id_created_rev_args icr_args;
4948
4949 /* Canonicalize the input path so that the path-math that
4950 prev_location() does below will work. */
4951 path = svn_fs__canonicalize_abspath(path, pool);
4952
4953 /* Special-case the root node (for performance reasons) */
4954 if (strcmp(path, "/") == 0)
4955 {
4956 *revision = 0;
4957 return SVN_NO_ERROR;
4958 }
4959
4960 /* If we have support for the node-origins table, we'll try to use
4961 it. */
4962 if (bfd->format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT)
4963 {
4964 const svn_fs_id_t *id;
4965 svn_error_t *err;
4966
4967 SVN_ERR(base_node_id(&id, root, path, pool));
4968 args.node_id = svn_fs_base__id_node_id(id);
4969 err = svn_fs_base__retry_txn(root->fs, txn_body_get_node_origin, &args,
4970 FALSE, pool);
4971
4972 /* If we got a value for the origin node-revision-ID, that's
4973 great. If we didn't, that's sad but non-fatal -- we'll just
4974 figure it out the hard way, then record it so we don't have
4975 suffer again the next time. */
4976 if (! err)
4977 {
4978 origin_id = args.origin_id;
4979 }
4980 else if (err->apr_err == SVN_ERR_FS_NO_SUCH_NODE_ORIGIN)
4981 {
4982 svn_error_clear(err);
4983 err = SVN_NO_ERROR;
4984 }
4985 SVN_ERR(err);
4986 }
4987
4988 /* If we haven't yet found a node origin ID, we'll go spelunking for one. */
4989 if (! origin_id)
4990 {
4991 svn_fs_root_t *curroot = root;
4992 apr_pool_t *subpool = svn_pool_create(pool);
4993 apr_pool_t *predidpool = svn_pool_create(pool);
4994 svn_stringbuf_t *lastpath =
4995 svn_stringbuf_create(path, pool);
4996 svn_revnum_t lastrev = SVN_INVALID_REVNUM;
4997 const svn_fs_id_t *pred_id;
4998
4999 /* Walk the closest-copy chain back to the first copy in our history.
5000
5001 NOTE: We merely *assume* that this is faster than walking the
5002 predecessor chain, because we *assume* that copies of parent
5003 directories happen less often than modifications to a given item. */
5004 while (1)
5005 {
5006 svn_revnum_t currev;
5007 const char *curpath = lastpath->data;
5008
5009 /* Get a root pointing to LASTREV. (The first time around,
5010 LASTREV is invalid, but that's cool because CURROOT is
5011 already initialized.) */
5012 if (SVN_IS_VALID_REVNUM(lastrev))
5013 SVN_ERR(svn_fs_base__revision_root(&curroot, fs,
5014 lastrev, subpool));
5015
5016 /* Find the previous location using the closest-copy shortcut. */
5017 SVN_ERR(prev_location(&curpath, &currev, fs, curroot,
5018 curpath, subpool));
5019 if (! curpath)
5020 break;
5021
5022 /* Update our LASTPATH and LASTREV variables (which survive
5023 SUBPOOL). */
5024 svn_stringbuf_set(lastpath, curpath);
5025 lastrev = currev;
5026 }
5027
5028 /* Walk the predecessor links back to origin. */
5029 SVN_ERR(base_node_id(&pred_id, curroot, lastpath->data, pool));
5030 while (1)
5031 {
5032 struct txn_pred_id_args pid_args;
5033 svn_pool_clear(subpool);
5034 pid_args.id = pred_id;
5035 pid_args.pred_id = NULL;
5036 pid_args.pool = subpool;
5037 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id, &pid_args,
5038 FALSE, subpool));
5039 if (! pid_args.pred_id)
5040 break;
5041 svn_pool_clear(predidpool);
5042 pred_id = svn_fs_base__id_copy(pid_args.pred_id, predidpool);
5043 }
5044
5045 /* Okay. PRED_ID should hold our origin ID now. */
5046 origin_id = svn_fs_base__id_copy(pred_id, pool);
5047
5048 /* If our filesystem version supports it, let's remember this
5049 value from now on. */
5050 if (bfd->format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT)
5051 {
5052 args.origin_id = origin_id;
5053 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_set_node_origin,
5054 &args, TRUE, subpool));
5055 }
5056
5057 svn_pool_destroy(predidpool);
5058 svn_pool_destroy(subpool);
5059 }
5060
5061 /* Okay. We have an origin node-revision-ID. Let's get a created
5062 revision from it. */
5063 icr_args.id = origin_id;
5064 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_id_created_rev, &icr_args,
5065 TRUE, pool));
5066 *revision = icr_args.revision;
5067 return SVN_NO_ERROR;
5068 }
5069
5070
5071
5072 /* Mergeinfo Queries */
5073
5074
5075 /* Examine directory NODE's immediately children for mergeinfo.
5076
5077 For those which have explicit mergeinfo, invoke RECEIVER with
5078 RECEIVER_BATON.
5079
5080 For those which don't, but sit atop trees which contain mergeinfo
5081 somewhere deeper, add them to *CHILDREN_ATOP_MERGEINFO_TREES, a
5082 hash mapping dirent names to dag_node_t * objects, allocated
5083 from that hash's pool.
5084
5085 For those which neither have explicit mergeinfo nor sit atop trees
5086 which contain mergeinfo, ignore them.
5087
5088 Use TRAIL->pool for temporary allocations. */
5089
5090 struct get_mergeinfo_data_and_entries_baton
5091 {
5092 apr_hash_t *children_atop_mergeinfo_trees;
5093 dag_node_t *node;
5094 const char *node_path;
5095 svn_fs_mergeinfo_receiver_t receiver;
5096 void *receiver_baton;
5097 };
5098
5099 static svn_error_t *
txn_body_get_mergeinfo_data_and_entries(void * baton,trail_t * trail)5100 txn_body_get_mergeinfo_data_and_entries(void *baton, trail_t *trail)
5101 {
5102 struct get_mergeinfo_data_and_entries_baton *args = baton;
5103 dag_node_t *node = args->node;
5104 apr_hash_t *entries;
5105 apr_hash_index_t *hi;
5106 apr_pool_t *iterpool = svn_pool_create(trail->pool);
5107 apr_pool_t *children_pool =
5108 apr_hash_pool_get(args->children_atop_mergeinfo_trees);
5109
5110 SVN_ERR_ASSERT(svn_fs_base__dag_node_kind(node) == svn_node_dir);
5111
5112 SVN_ERR(svn_fs_base__dag_dir_entries(&entries, node, trail, trail->pool));
5113 for (hi = apr_hash_first(trail->pool, entries); hi; hi = apr_hash_next(hi))
5114 {
5115 void *val;
5116 svn_fs_dirent_t *dirent;
5117 const svn_fs_id_t *child_id;
5118 dag_node_t *child_node;
5119 svn_boolean_t has_mergeinfo;
5120 apr_int64_t kid_count;
5121
5122 svn_pool_clear(iterpool);
5123 apr_hash_this(hi, NULL, NULL, &val);
5124 dirent = val;
5125 child_id = dirent->id;
5126
5127 /* Get the node for this child. */
5128 SVN_ERR(svn_fs_base__dag_get_node(&child_node, trail->fs, child_id,
5129 trail, iterpool));
5130
5131 /* Query the child node's mergeinfo stats. */
5132 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(&has_mergeinfo, &kid_count,
5133 child_node, trail,
5134 iterpool));
5135
5136 /* If the child has mergeinfo, add it to the result catalog. */
5137 if (has_mergeinfo)
5138 {
5139 apr_hash_t *plist;
5140 svn_mergeinfo_t child_mergeinfo;
5141 svn_string_t *pval;
5142 svn_error_t *err;
5143
5144 SVN_ERR(svn_fs_base__dag_get_proplist(&plist, child_node,
5145 trail, iterpool));
5146 pval = svn_hash_gets(plist, SVN_PROP_MERGEINFO);
5147 if (! pval)
5148 {
5149 svn_string_t *id_str = svn_fs_base__id_unparse(child_id,
5150 iterpool);
5151 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
5152 _("Node-revision '%s' claims to have "
5153 "mergeinfo but doesn't"),
5154 id_str->data);
5155 }
5156 /* Issue #3896: If syntactically invalid mergeinfo is present on
5157 CHILD_NODE then treat it as if no mergeinfo is present rather
5158 than raising a parse error. */
5159 err = svn_mergeinfo_parse(&child_mergeinfo, pval->data,
5160 iterpool);
5161 if (err)
5162 {
5163 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
5164 svn_error_clear(err);
5165 else
5166 return svn_error_trace(err);
5167 }
5168 else
5169 {
5170 SVN_ERR(args->receiver(svn_fspath__join(args->node_path,
5171 dirent->name,
5172 iterpool),
5173 child_mergeinfo,
5174 args->receiver_baton,
5175 iterpool));
5176 }
5177 }
5178
5179 /* If the child has descendants with mergeinfo -- that is, if
5180 the count of descendants beneath it carrying mergeinfo, not
5181 including itself, is non-zero -- then add it to the
5182 children_atop_mergeinfo_trees hash to be crawled later. */
5183 if ((kid_count - (has_mergeinfo ? 1 : 0)) > 0)
5184 {
5185 if (svn_fs_base__dag_node_kind(child_node) != svn_node_dir)
5186 {
5187 svn_string_t *id_str = svn_fs_base__id_unparse(child_id,
5188 iterpool);
5189 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
5190 _("Node-revision '%s' claims to sit "
5191 "atop a tree containing mergeinfo "
5192 "but is not a directory"),
5193 id_str->data);
5194 }
5195 svn_hash_sets(args->children_atop_mergeinfo_trees,
5196 apr_pstrdup(children_pool, dirent->name),
5197 svn_fs_base__dag_dup(child_node, children_pool));
5198 }
5199 }
5200
5201 svn_pool_destroy(iterpool);
5202 return SVN_NO_ERROR;
5203 }
5204
5205 static svn_error_t *
crawl_directory_for_mergeinfo(svn_fs_t * fs,dag_node_t * node,const char * node_path,svn_fs_mergeinfo_receiver_t receiver,void * baton,apr_pool_t * pool)5206 crawl_directory_for_mergeinfo(svn_fs_t *fs,
5207 dag_node_t *node,
5208 const char *node_path,
5209 svn_fs_mergeinfo_receiver_t receiver,
5210 void *baton,
5211 apr_pool_t *pool)
5212 {
5213 struct get_mergeinfo_data_and_entries_baton gmdae_args;
5214 apr_hash_t *children_atop_mergeinfo_trees = apr_hash_make(pool);
5215 apr_hash_index_t *hi;
5216 apr_pool_t *iterpool;
5217
5218 /* Add mergeinfo for immediate children that have it, and fetch
5219 immediate children that *don't* have it but sit atop trees that do. */
5220 gmdae_args.children_atop_mergeinfo_trees = children_atop_mergeinfo_trees;
5221 gmdae_args.node = node;
5222 gmdae_args.node_path = node_path;
5223 gmdae_args.receiver = receiver;
5224 gmdae_args.receiver_baton = baton;
5225 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_mergeinfo_data_and_entries,
5226 &gmdae_args, FALSE, pool));
5227
5228 /* If no children sit atop trees with mergeinfo, we're done.
5229 Otherwise, recurse on those children. */
5230
5231 if (apr_hash_count(children_atop_mergeinfo_trees) == 0)
5232 return SVN_NO_ERROR;
5233
5234 iterpool = svn_pool_create(pool);
5235 for (hi = apr_hash_first(pool, children_atop_mergeinfo_trees);
5236 hi;
5237 hi = apr_hash_next(hi))
5238 {
5239 const void *key;
5240 void *val;
5241 svn_pool_clear(iterpool);
5242 apr_hash_this(hi, &key, NULL, &val);
5243 SVN_ERR(crawl_directory_for_mergeinfo(fs, val,
5244 svn_fspath__join(node_path, key,
5245 iterpool),
5246 receiver, baton, iterpool));
5247 }
5248 svn_pool_destroy(iterpool);
5249 return SVN_NO_ERROR;
5250 }
5251
5252
5253 /* Calculate the mergeinfo for PATH under revision ROOT using
5254 inheritance type INHERIT. Set *MERGEINFO to the mergeinfo, or to
5255 NULL if there is none. Results are allocated in POOL; TRAIL->pool
5256 is used for temporary allocations. */
5257
5258 struct get_mergeinfo_for_path_baton
5259 {
5260 svn_mergeinfo_t *mergeinfo;
5261 svn_fs_root_t *root;
5262 const char *path;
5263 svn_mergeinfo_inheritance_t inherit;
5264 svn_boolean_t adjust_inherited_mergeinfo;
5265 apr_pool_t *pool;
5266 };
5267
5268 static svn_error_t *
txn_body_get_mergeinfo_for_path(void * baton,trail_t * trail)5269 txn_body_get_mergeinfo_for_path(void *baton, trail_t *trail)
5270 {
5271 struct get_mergeinfo_for_path_baton *args = baton;
5272 parent_path_t *parent_path, *nearest_ancestor;
5273 apr_hash_t *proplist;
5274 svn_string_t *mergeinfo_string;
5275 apr_pool_t *iterpool;
5276 dag_node_t *node = NULL;
5277
5278 *(args->mergeinfo) = NULL;
5279
5280 SVN_ERR(open_path(&parent_path, args->root, args->path, 0,
5281 NULL, trail, trail->pool));
5282
5283 /* Init the nearest ancestor. */
5284 nearest_ancestor = parent_path;
5285 if (args->inherit == svn_mergeinfo_nearest_ancestor)
5286 {
5287 if (! parent_path->parent)
5288 return SVN_NO_ERROR;
5289 nearest_ancestor = parent_path->parent;
5290 }
5291
5292 iterpool = svn_pool_create(trail->pool);
5293 while (TRUE)
5294 {
5295 svn_boolean_t has_mergeinfo;
5296 apr_int64_t count;
5297
5298 svn_pool_clear(iterpool);
5299
5300 node = nearest_ancestor->node;
5301 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(&has_mergeinfo, &count,
5302 node, trail, iterpool));
5303 if (has_mergeinfo)
5304 break;
5305
5306 /* No need to loop if we're looking for explicit mergeinfo. */
5307 if (args->inherit == svn_mergeinfo_explicit)
5308 {
5309 svn_pool_destroy(iterpool);
5310 return SVN_NO_ERROR;
5311 }
5312
5313 nearest_ancestor = nearest_ancestor->parent;
5314
5315 /* Run out? There's no mergeinfo. */
5316 if (! nearest_ancestor)
5317 {
5318 svn_pool_destroy(iterpool);
5319 return SVN_NO_ERROR;
5320 }
5321 }
5322 svn_pool_destroy(iterpool);
5323
5324 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node, trail, trail->pool));
5325 mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
5326 if (! mergeinfo_string)
5327 {
5328 svn_string_t *id_str =
5329 svn_fs_base__id_unparse(svn_fs_base__dag_get_id(node), trail->pool);
5330 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
5331 _("Node-revision '%s' claims to have "
5332 "mergeinfo but doesn't"), id_str->data);
5333 }
5334
5335 /* Parse the mergeinfo; store the result in ARGS->MERGEINFO. */
5336 {
5337 /* Issue #3896: If a node has syntactically invalid mergeinfo, then
5338 treat it as if no mergeinfo is present rather than raising a parse
5339 error. */
5340 svn_error_t *err = svn_mergeinfo_parse(args->mergeinfo,
5341 mergeinfo_string->data,
5342 args->pool);
5343 if (err)
5344 {
5345 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
5346 {
5347 svn_error_clear(err);
5348 err = NULL;
5349 args->mergeinfo = NULL;
5350 }
5351 return svn_error_trace(err);
5352 }
5353 }
5354
5355 /* If our nearest ancestor is the very path we inquired about, we
5356 can return the mergeinfo results directly. Otherwise, we're
5357 inheriting the mergeinfo, so we need to a) remove non-inheritable
5358 ranges and b) telescope the merged-from paths. */
5359 if (args->adjust_inherited_mergeinfo && (nearest_ancestor != parent_path))
5360 {
5361 svn_mergeinfo_t tmp_mergeinfo;
5362
5363 SVN_ERR(svn_mergeinfo_inheritable2(&tmp_mergeinfo, *args->mergeinfo,
5364 NULL, SVN_INVALID_REVNUM,
5365 SVN_INVALID_REVNUM, TRUE,
5366 trail->pool, trail->pool));
5367 SVN_ERR(svn_fs__append_to_merged_froms(args->mergeinfo, tmp_mergeinfo,
5368 parent_path_relpath(
5369 parent_path, nearest_ancestor,
5370 trail->pool),
5371 args->pool));
5372 }
5373
5374 return SVN_NO_ERROR;
5375 }
5376
5377 /* Set **NODE to the dag node for PATH in ROOT (allocated in POOL),
5378 and query its mergeinfo stats, setting HAS_MERGEINFO and
5379 CHILD_MERGEINFO_COUNT appropriately. */
5380
5381 struct get_node_mergeinfo_stats_baton
5382 {
5383 dag_node_t *node;
5384 svn_boolean_t has_mergeinfo;
5385 apr_int64_t child_mergeinfo_count;
5386 svn_fs_root_t *root;
5387 const char *path;
5388 };
5389
5390 static svn_error_t *
txn_body_get_node_mergeinfo_stats(void * baton,trail_t * trail)5391 txn_body_get_node_mergeinfo_stats(void *baton, trail_t *trail)
5392 {
5393 struct get_node_mergeinfo_stats_baton *args = baton;
5394
5395 SVN_ERR(get_dag(&(args->node), args->root, args->path,
5396 trail, trail->pool));
5397 return svn_fs_base__dag_get_mergeinfo_stats(&(args->has_mergeinfo),
5398 &(args->child_mergeinfo_count),
5399 args->node, trail,
5400 trail->pool);
5401 }
5402
5403
5404 /* Find all the mergeinfo for a set of PATHS under ROOT and report it
5405 through RECEIVER with BATON. INHERITED, INCLUDE_DESCENDANTS and
5406 ADJUST_INHERITED_MERGEINFO are the same as in the FS API.
5407
5408 Allocate temporary values are allocated in SCRATCH_POOL. */
5409 static svn_error_t *
get_mergeinfos_for_paths(svn_fs_root_t * root,const apr_array_header_t * paths,svn_mergeinfo_inheritance_t inherit,svn_boolean_t include_descendants,svn_boolean_t adjust_inherited_mergeinfo,svn_fs_mergeinfo_receiver_t receiver,void * baton,apr_pool_t * scratch_pool)5410 get_mergeinfos_for_paths(svn_fs_root_t *root,
5411 const apr_array_header_t *paths,
5412 svn_mergeinfo_inheritance_t inherit,
5413 svn_boolean_t include_descendants,
5414 svn_boolean_t adjust_inherited_mergeinfo,
5415 svn_fs_mergeinfo_receiver_t receiver,
5416 void *baton,
5417 apr_pool_t *scratch_pool)
5418 {
5419 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5420 int i;
5421
5422 for (i = 0; i < paths->nelts; i++)
5423 {
5424 svn_mergeinfo_t path_mergeinfo;
5425 struct get_mergeinfo_for_path_baton gmfp_args;
5426 const char *path = APR_ARRAY_IDX(paths, i, const char *);
5427
5428 svn_pool_clear(iterpool);
5429
5430 path = svn_fs__canonicalize_abspath(path, iterpool);
5431
5432 /* Get the mergeinfo for PATH itself. */
5433 gmfp_args.mergeinfo = &path_mergeinfo;
5434 gmfp_args.root = root;
5435 gmfp_args.path = path;
5436 gmfp_args.inherit = inherit;
5437 gmfp_args.pool = iterpool;
5438 gmfp_args.adjust_inherited_mergeinfo = adjust_inherited_mergeinfo;
5439 SVN_ERR(svn_fs_base__retry_txn(root->fs,
5440 txn_body_get_mergeinfo_for_path,
5441 &gmfp_args, FALSE, iterpool));
5442 if (path_mergeinfo)
5443 SVN_ERR(receiver(path, path_mergeinfo, baton, iterpool));
5444
5445 /* If we're including descendants, do so. */
5446 if (include_descendants)
5447 {
5448 svn_boolean_t do_crawl;
5449 struct get_node_mergeinfo_stats_baton gnms_args;
5450
5451 /* Query the node and its mergeinfo stats. */
5452 gnms_args.root = root;
5453 gnms_args.path = path;
5454 SVN_ERR(svn_fs_base__retry_txn(root->fs,
5455 txn_body_get_node_mergeinfo_stats,
5456 &gnms_args, FALSE, iterpool));
5457
5458 /* Determine if there's anything worth crawling here. */
5459 if (svn_fs_base__dag_node_kind(gnms_args.node) != svn_node_dir)
5460 do_crawl = FALSE;
5461 else
5462 do_crawl = ((gnms_args.child_mergeinfo_count > 1)
5463 || ((gnms_args.child_mergeinfo_count == 1)
5464 && (! gnms_args.has_mergeinfo)));
5465
5466 /* If it's worth crawling, crawl. */
5467 if (do_crawl)
5468 SVN_ERR(crawl_directory_for_mergeinfo(root->fs, gnms_args.node,
5469 path, receiver, baton,
5470 iterpool));
5471 }
5472 }
5473 svn_pool_destroy(iterpool);
5474
5475 return SVN_NO_ERROR;
5476 }
5477
5478
5479 /* Implements svn_fs_get_mergeinfo. */
5480 static svn_error_t *
base_get_mergeinfo(svn_fs_root_t * root,const apr_array_header_t * paths,svn_mergeinfo_inheritance_t inherit,svn_boolean_t include_descendants,svn_boolean_t adjust_inherited_mergeinfo,svn_fs_mergeinfo_receiver_t receiver,void * baton,apr_pool_t * scratch_pool)5481 base_get_mergeinfo(svn_fs_root_t *root,
5482 const apr_array_header_t *paths,
5483 svn_mergeinfo_inheritance_t inherit,
5484 svn_boolean_t include_descendants,
5485 svn_boolean_t adjust_inherited_mergeinfo,
5486 svn_fs_mergeinfo_receiver_t receiver,
5487 void *baton,
5488 apr_pool_t *scratch_pool)
5489 {
5490 /* Verify that our filesystem version supports mergeinfo stuff. */
5491 SVN_ERR(svn_fs_base__test_required_feature_format
5492 (root->fs, "mergeinfo", SVN_FS_BASE__MIN_MERGEINFO_FORMAT));
5493
5494 /* We require a revision root. */
5495 if (root->is_txn_root)
5496 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
5497
5498 /* Retrieve a path -> mergeinfo mapping. */
5499 return get_mergeinfos_for_paths(root, paths,
5500 inherit, include_descendants,
5501 adjust_inherited_mergeinfo,
5502 receiver, baton,
5503 scratch_pool);
5504 }
5505
5506
5507
5508 /* Creating root objects. */
5509
5510
5511 static root_vtable_t root_vtable = {
5512 base_paths_changed,
5513 NULL,
5514 base_check_path,
5515 base_node_history,
5516 base_node_id,
5517 base_node_relation,
5518 base_node_created_rev,
5519 base_node_origin_rev,
5520 base_node_created_path,
5521 base_delete_node,
5522 base_copy,
5523 base_revision_link,
5524 base_copied_from,
5525 base_closest_copy,
5526 base_node_prop,
5527 base_node_proplist,
5528 base_node_has_props,
5529 base_change_node_prop,
5530 base_props_changed,
5531 base_dir_entries,
5532 base_dir_optimal_order,
5533 base_make_dir,
5534 base_file_length,
5535 base_file_checksum,
5536 base_file_contents,
5537 NULL,
5538 base_make_file,
5539 base_apply_textdelta,
5540 base_apply_text,
5541 base_contents_changed,
5542 base_get_file_delta_stream,
5543 base_merge,
5544 base_get_mergeinfo,
5545 };
5546
5547
5548 /* Construct a new root object in FS, allocated from POOL. */
5549 static svn_fs_root_t *
make_root(svn_fs_t * fs,apr_pool_t * pool)5550 make_root(svn_fs_t *fs,
5551 apr_pool_t *pool)
5552 {
5553 svn_fs_root_t *root = apr_pcalloc(pool, sizeof(*root));
5554 base_root_data_t *brd = apr_palloc(pool, sizeof(*brd));
5555
5556 root->fs = fs;
5557 root->pool = pool;
5558
5559 /* Init the node ID cache. */
5560 brd->node_cache = apr_hash_make(pool);
5561 brd->node_cache_idx = 0;
5562 root->vtable = &root_vtable;
5563 root->fsap_data = brd;
5564
5565 return root;
5566 }
5567
5568
5569 /* Construct a root object referring to the root of REVISION in FS,
5570 whose root directory is ROOT_DIR. Create the new root in POOL. */
5571 static svn_fs_root_t *
make_revision_root(svn_fs_t * fs,svn_revnum_t rev,dag_node_t * root_dir,apr_pool_t * pool)5572 make_revision_root(svn_fs_t *fs,
5573 svn_revnum_t rev,
5574 dag_node_t *root_dir,
5575 apr_pool_t *pool)
5576 {
5577 svn_fs_root_t *root = make_root(fs, pool);
5578 base_root_data_t *brd = root->fsap_data;
5579
5580 root->is_txn_root = FALSE;
5581 root->rev = rev;
5582 brd->root_dir = root_dir;
5583
5584 return root;
5585 }
5586
5587
5588 /* Construct a root object referring to the root of the transaction
5589 named TXN and based on revision BASE_REV in FS. FLAGS represents
5590 the behavior of the transaction. Create the new root in POOL. */
5591 static svn_fs_root_t *
make_txn_root(svn_fs_t * fs,const char * txn,svn_revnum_t base_rev,apr_uint32_t flags,apr_pool_t * pool)5592 make_txn_root(svn_fs_t *fs,
5593 const char *txn,
5594 svn_revnum_t base_rev,
5595 apr_uint32_t flags,
5596 apr_pool_t *pool)
5597 {
5598 svn_fs_root_t *root = make_root(fs, pool);
5599 root->is_txn_root = TRUE;
5600 root->txn = apr_pstrdup(root->pool, txn);
5601 root->txn_flags = flags;
5602 root->rev = base_rev;
5603
5604 return root;
5605 }
5606