tree.c revision 362181
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. */ 101struct 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 109typedef 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 136static 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 140static 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. */ 149static dag_node_t * 150dag_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. */ 177static void 178dag_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 285struct txn_root_args 286{ 287 svn_fs_root_t **root_p; 288 svn_fs_txn_t *txn; 289}; 290 291 292static svn_error_t * 293txn_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 326svn_error_t * 327svn_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 344struct revision_root_args 345{ 346 svn_fs_root_t **root_p; 347 svn_revnum_t rev; 348}; 349 350 351static svn_error_t * 352txn_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 368svn_error_t * 369svn_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. */ 395static svn_error_t * 396root_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. */ 422static svn_error_t * 423mutable_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 441typedef 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. */ 455typedef 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. */ 482static const char * 483parent_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. */ 497static const char * 498parent_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. */ 521static svn_error_t * 522get_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. */ 608static parent_path_t * 609make_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. */ 625typedef 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*/ 659static svn_error_t * 660open_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. */ 790static svn_error_t * 791make_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. */ 882static svn_error_t * 883adjust_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. */ 914static svn_error_t * 915get_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. */ 952static svn_error_t * 953add_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 977struct node_id_args { 978 const svn_fs_id_t **id_p; 979 svn_fs_root_t *root; 980 const char *path; 981}; 982 983 984static svn_error_t * 985txn_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 998static svn_error_t * 999base_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 1032static svn_error_t * 1033base_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 1057struct node_created_rev_args { 1058 svn_revnum_t revision; 1059 svn_fs_root_t *root; 1060 const char *path; 1061}; 1062 1063 1064static svn_error_t * 1065txn_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 1076static svn_error_t * 1077base_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 1094struct node_created_path_args { 1095 const char **created_path; 1096 svn_fs_root_t *root; 1097 const char *path; 1098}; 1099 1100 1101static svn_error_t * 1102txn_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 1113static svn_error_t * 1114base_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 1135struct node_kind_args { 1136 const svn_fs_id_t *id; 1137 svn_node_kind_t kind; /* OUT parameter */ 1138}; 1139 1140 1141static svn_error_t * 1142txn_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 1155static svn_error_t * 1156node_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 1177static svn_error_t * 1178base_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 1197struct 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 1206static svn_error_t * 1207txn_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 1224static svn_error_t * 1225base_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 1247struct node_proplist_args { 1248 apr_hash_t **table_p; 1249 svn_fs_root_t *root; 1250 const char *path; 1251}; 1252 1253 1254static svn_error_t * 1255txn_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 1269static svn_error_t * 1270base_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 1289static svn_error_t * 1290base_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 1305struct 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 1313static svn_error_t * 1314txn_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 1385static svn_error_t * 1386base_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 1406struct 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 1418static svn_error_t * 1419txn_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 1446static svn_error_t * 1447base_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 1479struct miscellaneous_set_args 1480{ 1481 const char *key; 1482 const char *val; 1483}; 1484 1485static svn_error_t * 1486txn_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 1494svn_error_t * 1495svn_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 1508struct miscellaneous_get_args 1509{ 1510 const char *key; 1511 const char **val; 1512}; 1513 1514static svn_error_t * 1515txn_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 1522svn_error_t * 1523svn_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 1546struct 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 */ 1555static svn_error_t * 1556txn_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 1574static svn_error_t * 1575base_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 1623static svn_error_t * 1624base_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 1647struct 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 1655struct 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 1678static svn_error_t * 1679txn_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 1705struct txn_pred_count_args 1706{ 1707 const svn_fs_id_t *id; 1708 int pred_count; 1709}; 1710 1711 1712static svn_error_t * 1713txn_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 1725struct 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 1733static svn_error_t * 1734txn_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. */ 1758static svn_error_t * 1759deltify_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 2021struct 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. */ 2029static svn_error_t * 2030txn_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 2038static svn_error_t * 2039update_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). */ 2069static svn_error_t * 2070conflict_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 */ 2101static svn_error_t * 2102merge(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 2463struct 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. */ 2492static svn_error_t * 2493txn_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. */ 2559static svn_error_t * 2560verify_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 2636struct 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 */ 2653static svn_error_t * 2654txn_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. */ 2718svn_error_t * 2719svn_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. */ 2866static svn_error_t * 2867base_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 <jimb@zwingli.cygnus.com> 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 2936struct rev_get_txn_id_args 2937{ 2938 const char **txn_id; 2939 svn_revnum_t revision; 2940}; 2941 2942 2943static svn_error_t * 2944txn_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 2952svn_error_t * 2953svn_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 2993struct make_dir_args 2994{ 2995 svn_fs_root_t *root; 2996 const char *path; 2997}; 2998 2999 3000static svn_error_t * 3001txn_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 3047static svn_error_t * 3048base_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 3064struct 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. */ 3074static svn_error_t * 3075txn_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 3135static svn_error_t * 3136base_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 3149struct 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 3159static svn_error_t * 3160txn_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. */ 3278static svn_error_t * 3279fs_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. */ 3291static svn_error_t * 3292copy_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 3329static svn_error_t * 3330base_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 3340static svn_error_t * 3341base_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 3350struct 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 3362static svn_error_t * 3363txn_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 3409static svn_error_t * 3410base_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 3437struct make_file_args 3438{ 3439 svn_fs_root_t *root; 3440 const char *path; 3441}; 3442 3443 3444static svn_error_t * 3445txn_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 3491static svn_error_t * 3492base_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 3506struct file_length_args 3507{ 3508 svn_fs_root_t *root; 3509 const char *path; 3510 svn_filesize_t length; /* OUT parameter */ 3511}; 3512 3513static svn_error_t * 3514txn_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 3528static svn_error_t * 3529base_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 3546struct 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 3554static svn_error_t * 3555txn_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 3567static svn_error_t * 3568base_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. */ 3593typedef 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). */ 3614static svn_error_t * 3615txn_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 3629static svn_error_t * 3630base_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. */ 3656typedef 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 */ 3698static svn_error_t * 3699txn_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. */ 3719static svn_error_t * 3720write_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. */ 3730static svn_error_t * 3731window_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 3789static svn_error_t * 3790txn_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 3860static svn_error_t * 3861base_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(). */ 3890struct 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 */ 3923static svn_error_t * 3924txn_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. */ 3940static svn_error_t * 3941text_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. */ 3952static svn_error_t * 3953text_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 3969static svn_error_t * 3970txn_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 4004static svn_error_t * 4005base_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 4031static svn_error_t * 4032txn_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. */ 4088static svn_error_t * 4089base_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. */ 4138static svn_error_t * 4139base_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 4169struct paths_changed_args 4170{ 4171 apr_hash_t *changes; 4172 svn_fs_root_t *root; 4173}; 4174 4175 4176static svn_error_t * 4177txn_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 4199static svn_error_t * 4200base_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. */ 4216typedef 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 4234static 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 4242static svn_error_t * 4243base_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*/ 4275static svn_error_t * 4276examine_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 4317struct 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 4326static svn_error_t * 4327txn_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 4511static svn_error_t * 4512base_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 4565static svn_error_t * 4566base_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 4579static history_vtable_t history_vtable = { 4580 base_history_prev, 4581 base_history_location 4582}; 4583 4584 4585 4586struct 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 4596static svn_error_t * 4597txn_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 4711static svn_error_t * 4712base_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. */ 4741static svn_fs_history_t * 4742assemble_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 4764svn_error_t * 4765svn_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 4799svn_error_t * 4800svn_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. */ 4851static svn_error_t * 4852prev_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 4895struct id_created_rev_args { 4896 svn_revnum_t revision; 4897 const svn_fs_id_t *id; 4898 const char *path; 4899}; 4900 4901 4902static svn_error_t * 4903txn_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 4915struct get_set_node_origin_args { 4916 const svn_fs_id_t *origin_id; 4917 const char *node_id; 4918}; 4919 4920 4921static svn_error_t * 4922txn_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 4929static svn_error_t * 4930txn_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 4937static svn_error_t * 4938base_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 5090struct 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 5099static svn_error_t * 5100txn_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 5205static svn_error_t * 5206crawl_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 5258struct 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 5268static svn_error_t * 5269txn_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 5381struct 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 5390static svn_error_t * 5391txn_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. */ 5409static svn_error_t * 5410get_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. */ 5480static svn_error_t * 5481base_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 5511static 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. */ 5549static svn_fs_root_t * 5550make_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. */ 5571static svn_fs_root_t * 5572make_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. */ 5591static svn_fs_root_t * 5592make_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