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 <apr_pools.h> 42#include <apr_hash.h> 43 44#include "svn_hash.h" 45#include "svn_private_config.h" 46#include "svn_pools.h" 47#include "svn_error.h" 48#include "svn_path.h" 49#include "svn_mergeinfo.h" 50#include "svn_fs.h" 51#include "svn_props.h" 52 53#include "fs.h" 54#include "key-gen.h" 55#include "dag.h" 56#include "lock.h" 57#include "tree.h" 58#include "fs_fs.h" 59#include "id.h" 60#include "temp_serializer.h" 61 62#include "private/svn_mergeinfo_private.h" 63#include "private/svn_subr_private.h" 64#include "private/svn_fs_util.h" 65#include "private/svn_fspath.h" 66#include "../libsvn_fs/fs-loader.h" 67 68 69/* ### I believe this constant will become internal to reps-strings.c. 70 ### see the comment in window_consumer() for more information. */ 71 72/* ### the comment also seems to need tweaking: the log file stuff 73 ### is no longer an issue... */ 74/* Data written to the filesystem through the svn_fs_apply_textdelta() 75 interface is cached in memory until the end of the data stream, or 76 until a size trigger is hit. Define that trigger here (in bytes). 77 Setting the value to 0 will result in no filesystem buffering at 78 all. The value only really matters when dealing with file contents 79 bigger than the value itself. Above that point, large values here 80 allow the filesystem to buffer more data in memory before flushing 81 to the database, which increases memory usage but greatly decreases 82 the amount of disk access (and log-file generation) in database. 83 Smaller values will limit your overall memory consumption, but can 84 drastically hurt throughput by necessitating more write operations 85 to the database (which also generates more log-files). */ 86#define WRITE_BUFFER_SIZE 512000 87 88 89 90/* The root structures. 91 92 Why do they contain different data? Well, transactions are mutable 93 enough that it isn't safe to cache the DAG node for the root 94 directory or the hash of copyfrom data: somebody else might modify 95 them concurrently on disk! (Why is the DAG node cache safer than 96 the root DAG node? When cloning transaction DAG nodes in and out 97 of the cache, all of the possibly-mutable data from the 98 node_revision_t inside the dag_node_t is dropped.) Additionally, 99 revisions are immutable enough that their DAG node cache can be 100 kept in the FS object and shared among multiple revision root 101 objects. 102*/ 103typedef struct fs_rev_root_data_t 104{ 105 /* A dag node for the revision's root directory. */ 106 dag_node_t *root_dir; 107 108 /* Cache structure for mapping const char * PATH to const char 109 *COPYFROM_STRING, so that paths_changed can remember all the 110 copyfrom information in the changes file. 111 COPYFROM_STRING has the format "REV PATH", or is the empty string if 112 the path was added without history. */ 113 apr_hash_t *copyfrom_cache; 114 115} fs_rev_root_data_t; 116 117typedef struct fs_txn_root_data_t 118{ 119 const char *txn_id; 120 121 /* Cache of txn DAG nodes (without their nested noderevs, because 122 * it's mutable). Same keys/values as ffd->rev_node_cache. */ 123 svn_cache__t *txn_node_cache; 124} fs_txn_root_data_t; 125 126/* Declared here to resolve the circular dependencies. */ 127static svn_error_t * get_dag(dag_node_t **dag_node_p, 128 svn_fs_root_t *root, 129 const char *path, 130 svn_boolean_t needs_lock_cache, 131 apr_pool_t *pool); 132 133static svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev, 134 dag_node_t *root_dir, 135 apr_pool_t *pool); 136 137static svn_error_t *make_txn_root(svn_fs_root_t **root_p, 138 svn_fs_t *fs, const char *txn, 139 svn_revnum_t base_rev, apr_uint32_t flags, 140 apr_pool_t *pool); 141 142 143/*** Node Caching ***/ 144 145/* 1st level cache */ 146 147/* An entry in the first-level cache. REVISION and PATH form the key that 148 will ultimately be matched. 149 */ 150typedef struct cache_entry_t 151{ 152 /* hash value derived from PATH, REVISION. 153 Used to short-circuit failed lookups. */ 154 apr_uint32_t hash_value; 155 156 /* revision to which the NODE belongs */ 157 svn_revnum_t revision; 158 159 /* path of the NODE */ 160 char *path; 161 162 /* cached value of strlen(PATH). */ 163 apr_size_t path_len; 164 165 /* the node allocated in the cache's pool. NULL for empty entries. */ 166 dag_node_t *node; 167} cache_entry_t; 168 169/* Number of entries in the cache. Keep this low to keep pressure on the 170 CPU caches low as well. A binary value is most efficient. If we walk 171 a directory tree, we want enough entries to store nodes for all files 172 without overwriting the nodes for the parent folder. That way, there 173 will be no unnecessary misses (except for a few random ones caused by 174 hash collision). 175 176 The actual number of instances may be higher but entries that got 177 overwritten are no longer visible. 178 */ 179enum { BUCKET_COUNT = 256 }; 180 181/* Each pool that has received a DAG node, will hold at least on lock on 182 our cache to ensure that the node remains valid despite being allocated 183 in the cache's pool. This is the structure to represent the lock. 184 */ 185typedef struct cache_lock_t 186{ 187 /* pool holding the lock */ 188 apr_pool_t *pool; 189 190 /* cache being locked */ 191 fs_fs_dag_cache_t *cache; 192 193 /* next lock. NULL at EOL */ 194 struct cache_lock_t *next; 195 196 /* previous lock. NULL at list head. Only then this==cache->first_lock */ 197 struct cache_lock_t *prev; 198} cache_lock_t; 199 200/* The actual cache structure. All nodes will be allocated in POOL. 201 When the number of INSERTIONS (i.e. objects created form that pool) 202 exceeds a certain threshold, the pool will be cleared and the cache 203 with it. 204 205 To ensure that nodes returned from this structure remain valid, the 206 cache will get locked for the lifetime of the _receiving_ pools (i.e. 207 those in which we would allocate the node if there was no cache.). 208 The cache will only be cleared FIRST_LOCK is 0. 209 */ 210struct fs_fs_dag_cache_t 211{ 212 /* fixed number of (possibly empty) cache entries */ 213 cache_entry_t buckets[BUCKET_COUNT]; 214 215 /* pool used for all node allocation */ 216 apr_pool_t *pool; 217 218 /* number of entries created from POOL since the last cleanup */ 219 apr_size_t insertions; 220 221 /* Property lookups etc. have a very high locality (75% re-hit). 222 Thus, remember the last hit location for optimistic lookup. */ 223 apr_size_t last_hit; 224 225 /* List of receiving pools that are still alive. */ 226 cache_lock_t *first_lock; 227}; 228 229/* Cleanup function to be called when a receiving pool gets cleared. 230 Unlocks the cache once. 231 */ 232static apr_status_t 233unlock_cache(void *baton_void) 234{ 235 cache_lock_t *lock = baton_void; 236 237 /* remove lock from chain. Update the head */ 238 if (lock->next) 239 lock->next->prev = lock->prev; 240 if (lock->prev) 241 lock->prev->next = lock->next; 242 else 243 lock->cache->first_lock = lock->next; 244 245 return APR_SUCCESS; 246} 247 248/* Cleanup function to be called when the cache itself gets destroyed. 249 In that case, we must unregister all unlock requests. 250 */ 251static apr_status_t 252unregister_locks(void *baton_void) 253{ 254 fs_fs_dag_cache_t *cache = baton_void; 255 cache_lock_t *lock; 256 257 for (lock = cache->first_lock; lock; lock = lock->next) 258 apr_pool_cleanup_kill(lock->pool, 259 lock, 260 unlock_cache); 261 262 return APR_SUCCESS; 263} 264 265fs_fs_dag_cache_t* 266svn_fs_fs__create_dag_cache(apr_pool_t *pool) 267{ 268 fs_fs_dag_cache_t *result = apr_pcalloc(pool, sizeof(*result)); 269 result->pool = svn_pool_create(pool); 270 271 apr_pool_cleanup_register(pool, 272 result, 273 unregister_locks, 274 apr_pool_cleanup_null); 275 276 return result; 277} 278 279/* Prevent the entries in CACHE from being destroyed, for as long as the 280 POOL lives. 281 */ 282static void 283lock_cache(fs_fs_dag_cache_t* cache, apr_pool_t *pool) 284{ 285 /* we only need to lock / unlock once per pool. Since we will often ask 286 for multiple nodes with the same pool, we can reduce the overhead. 287 However, if e.g. pools are being used in an alternating pattern, 288 we may lock the cache more than once for the same pool (and register 289 just as many cleanup actions). 290 */ 291 cache_lock_t *lock = cache->first_lock; 292 293 /* try to find an existing lock for POOL. 294 But limit the time spent on chasing pointers. */ 295 int limiter = 8; 296 while (lock && --limiter) 297 if (lock->pool == pool) 298 return; 299 300 /* create a new lock and put it at the beginning of the lock chain */ 301 lock = apr_palloc(pool, sizeof(*lock)); 302 lock->cache = cache; 303 lock->pool = pool; 304 lock->next = cache->first_lock; 305 lock->prev = NULL; 306 307 if (cache->first_lock) 308 cache->first_lock->prev = lock; 309 cache->first_lock = lock; 310 311 /* instruct POOL to remove the look upon cleanup */ 312 apr_pool_cleanup_register(pool, 313 lock, 314 unlock_cache, 315 apr_pool_cleanup_null); 316} 317 318/* Clears the CACHE at regular intervals (destroying all cached nodes) 319 */ 320static void 321auto_clear_dag_cache(fs_fs_dag_cache_t* cache) 322{ 323 if (cache->first_lock == NULL && cache->insertions > BUCKET_COUNT) 324 { 325 svn_pool_clear(cache->pool); 326 327 memset(cache->buckets, 0, sizeof(cache->buckets)); 328 cache->insertions = 0; 329 } 330} 331 332/* For the given REVISION and PATH, return the respective entry in CACHE. 333 If the entry is empty, its NODE member will be NULL and the caller 334 may then set it to the corresponding DAG node allocated in CACHE->POOL. 335 */ 336static cache_entry_t * 337cache_lookup( fs_fs_dag_cache_t *cache 338 , svn_revnum_t revision 339 , const char *path) 340{ 341 apr_size_t i, bucket_index; 342 apr_size_t path_len = strlen(path); 343 apr_uint32_t hash_value = (apr_uint32_t)revision; 344 345#if SVN_UNALIGNED_ACCESS_IS_OK 346 /* "randomizing" / distributing factor used in our hash function */ 347 const apr_uint32_t factor = 0xd1f3da69; 348#endif 349 350 /* optimistic lookup: hit the same bucket again? */ 351 cache_entry_t *result = &cache->buckets[cache->last_hit]; 352 if ( (result->revision == revision) 353 && (result->path_len == path_len) 354 && !memcmp(result->path, path, path_len)) 355 { 356 return result; 357 } 358 359 /* need to do a full lookup. Calculate the hash value 360 (HASH_VALUE has been initialized to REVISION). */ 361 i = 0; 362#if SVN_UNALIGNED_ACCESS_IS_OK 363 /* We relax the dependency chain between iterations by processing 364 two chunks from the input per hash_value self-multiplication. 365 The HASH_VALUE update latency is now 1 MUL latency + 1 ADD latency 366 per 2 chunks instead of 1 chunk. 367 */ 368 for (; i + 8 <= path_len; i += 8) 369 hash_value = hash_value * factor * factor 370 + ( *(const apr_uint32_t*)(path + i) * factor 371 + *(const apr_uint32_t*)(path + i + 4)); 372#endif 373 374 for (; i < path_len; ++i) 375 /* Help GCC to minimize the HASH_VALUE update latency by splitting the 376 MUL 33 of the naive implementation: h = h * 33 + path[i]. This 377 shortens the dependency chain from 1 shift + 2 ADDs to 1 shift + 1 ADD. 378 */ 379 hash_value = hash_value * 32 + (hash_value + (unsigned char)path[i]); 380 381 bucket_index = hash_value + (hash_value >> 16); 382 bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT; 383 384 /* access the corresponding bucket and remember its location */ 385 result = &cache->buckets[bucket_index]; 386 cache->last_hit = bucket_index; 387 388 /* if it is *NOT* a match, clear the bucket, expect the caller to fill 389 in the node and count it as an insertion */ 390 if ( (result->hash_value != hash_value) 391 || (result->revision != revision) 392 || (result->path_len != path_len) 393 || memcmp(result->path, path, path_len)) 394 { 395 result->hash_value = hash_value; 396 result->revision = revision; 397 if (result->path_len < path_len) 398 result->path = apr_palloc(cache->pool, path_len + 1); 399 result->path_len = path_len; 400 memcpy(result->path, path, path_len + 1); 401 402 result->node = NULL; 403 404 cache->insertions++; 405 } 406 407 return result; 408} 409 410/* 2nd level cache */ 411 412/* Find and return the DAG node cache for ROOT and the key that 413 should be used for PATH. */ 414static void 415locate_cache(svn_cache__t **cache, 416 const char **key, 417 svn_fs_root_t *root, 418 const char *path, 419 apr_pool_t *pool) 420{ 421 if (root->is_txn_root) 422 { 423 fs_txn_root_data_t *frd = root->fsap_data; 424 if (cache) *cache = frd->txn_node_cache; 425 if (key && path) *key = path; 426 } 427 else 428 { 429 fs_fs_data_t *ffd = root->fs->fsap_data; 430 if (cache) *cache = ffd->rev_node_cache; 431 if (key && path) *key 432 = svn_fs_fs__combine_number_and_string(root->rev, path, pool); 433 } 434} 435 436/* Return NODE for PATH from ROOT's node cache, or NULL if the node 437 isn't cached; read it from the FS. *NODE remains valid until either 438 POOL or the FS gets cleared or destroyed (whichever comes first). 439 440 Since locking can be expensive and POOL may be long-living, for 441 nodes that will not need to survive the next call to this function, 442 set NEEDS_LOCK_CACHE to FALSE. */ 443static svn_error_t * 444dag_node_cache_get(dag_node_t **node_p, 445 svn_fs_root_t *root, 446 const char *path, 447 svn_boolean_t needs_lock_cache, 448 apr_pool_t *pool) 449{ 450 svn_boolean_t found; 451 dag_node_t *node = NULL; 452 svn_cache__t *cache; 453 const char *key; 454 455 SVN_ERR_ASSERT(*path == '/'); 456 457 if (!root->is_txn_root) 458 { 459 /* immutable DAG node. use the global caches for it */ 460 461 fs_fs_data_t *ffd = root->fs->fsap_data; 462 cache_entry_t *bucket; 463 464 auto_clear_dag_cache(ffd->dag_node_cache); 465 bucket = cache_lookup(ffd->dag_node_cache, root->rev, path); 466 if (bucket->node == NULL) 467 { 468 locate_cache(&cache, &key, root, path, pool); 469 SVN_ERR(svn_cache__get((void **)&node, &found, cache, key, 470 ffd->dag_node_cache->pool)); 471 if (found && node) 472 { 473 /* Patch up the FS, since this might have come from an old FS 474 * object. */ 475 svn_fs_fs__dag_set_fs(node, root->fs); 476 bucket->node = node; 477 } 478 } 479 else 480 { 481 node = bucket->node; 482 } 483 484 /* if we found a node, make sure it remains valid at least as long 485 as it would when allocated in POOL. */ 486 if (node && needs_lock_cache) 487 lock_cache(ffd->dag_node_cache, pool); 488 } 489 else 490 { 491 /* DAG is mutable / may become invalid. Use the TXN-local cache */ 492 493 locate_cache(&cache, &key, root, path, pool); 494 495 SVN_ERR(svn_cache__get((void **) &node, &found, cache, key, pool)); 496 if (found && node) 497 { 498 /* Patch up the FS, since this might have come from an old FS 499 * object. */ 500 svn_fs_fs__dag_set_fs(node, root->fs); 501 } 502 } 503 504 *node_p = node; 505 506 return SVN_NO_ERROR; 507} 508 509 510/* Add the NODE for PATH to ROOT's node cache. */ 511static svn_error_t * 512dag_node_cache_set(svn_fs_root_t *root, 513 const char *path, 514 dag_node_t *node, 515 apr_pool_t *pool) 516{ 517 svn_cache__t *cache; 518 const char *key; 519 520 SVN_ERR_ASSERT(*path == '/'); 521 522 /* Do *not* attempt to dup and put the node into L1. 523 * dup() is twice as expensive as an L2 lookup (which will set also L1). 524 */ 525 locate_cache(&cache, &key, root, path, pool); 526 527 return svn_cache__set(cache, key, node, pool); 528} 529 530 531/* Baton for find_descendents_in_cache. */ 532struct fdic_baton { 533 const char *path; 534 apr_array_header_t *list; 535 apr_pool_t *pool; 536}; 537 538/* If the given item is a descendent of BATON->PATH, push 539 * it onto BATON->LIST (copying into BATON->POOL). Implements 540 * the svn_iter_apr_hash_cb_t prototype. */ 541static svn_error_t * 542find_descendents_in_cache(void *baton, 543 const void *key, 544 apr_ssize_t klen, 545 void *val, 546 apr_pool_t *pool) 547{ 548 struct fdic_baton *b = baton; 549 const char *item_path = key; 550 551 if (svn_fspath__skip_ancestor(b->path, item_path)) 552 APR_ARRAY_PUSH(b->list, const char *) = apr_pstrdup(b->pool, item_path); 553 554 return SVN_NO_ERROR; 555} 556 557/* Invalidate cache entries for PATH and any of its children. This 558 should *only* be called on a transaction root! */ 559static svn_error_t * 560dag_node_cache_invalidate(svn_fs_root_t *root, 561 const char *path, 562 apr_pool_t *pool) 563{ 564 struct fdic_baton b; 565 svn_cache__t *cache; 566 apr_pool_t *iterpool; 567 int i; 568 569 b.path = path; 570 b.pool = svn_pool_create(pool); 571 b.list = apr_array_make(b.pool, 1, sizeof(const char *)); 572 573 SVN_ERR_ASSERT(root->is_txn_root); 574 locate_cache(&cache, NULL, root, NULL, b.pool); 575 576 577 SVN_ERR(svn_cache__iter(NULL, cache, find_descendents_in_cache, 578 &b, b.pool)); 579 580 iterpool = svn_pool_create(b.pool); 581 582 for (i = 0; i < b.list->nelts; i++) 583 { 584 const char *descendent = APR_ARRAY_IDX(b.list, i, const char *); 585 svn_pool_clear(iterpool); 586 SVN_ERR(svn_cache__set(cache, descendent, NULL, iterpool)); 587 } 588 589 svn_pool_destroy(iterpool); 590 svn_pool_destroy(b.pool); 591 return SVN_NO_ERROR; 592} 593 594 595 596/* Creating transaction and revision root nodes. */ 597 598svn_error_t * 599svn_fs_fs__txn_root(svn_fs_root_t **root_p, 600 svn_fs_txn_t *txn, 601 apr_pool_t *pool) 602{ 603 apr_uint32_t flags = 0; 604 apr_hash_t *txnprops; 605 606 /* Look for the temporary txn props representing 'flags'. */ 607 SVN_ERR(svn_fs_fs__txn_proplist(&txnprops, txn, pool)); 608 if (txnprops) 609 { 610 if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD)) 611 flags |= SVN_FS_TXN_CHECK_OOD; 612 613 if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS)) 614 flags |= SVN_FS_TXN_CHECK_LOCKS; 615 } 616 617 return make_txn_root(root_p, txn->fs, txn->id, txn->base_rev, flags, pool); 618} 619 620 621svn_error_t * 622svn_fs_fs__revision_root(svn_fs_root_t **root_p, 623 svn_fs_t *fs, 624 svn_revnum_t rev, 625 apr_pool_t *pool) 626{ 627 dag_node_t *root_dir; 628 629 SVN_ERR(svn_fs__check_fs(fs, TRUE)); 630 631 SVN_ERR(svn_fs_fs__dag_revision_root(&root_dir, fs, rev, pool)); 632 633 *root_p = make_revision_root(fs, rev, root_dir, pool); 634 635 return SVN_NO_ERROR; 636} 637 638 639 640/* Getting dag nodes for roots. */ 641 642 643/* Set *NODE_P to a freshly opened dag node referring to the root 644 directory of ROOT, allocating from POOL. */ 645static svn_error_t * 646root_node(dag_node_t **node_p, 647 svn_fs_root_t *root, 648 apr_pool_t *pool) 649{ 650 if (root->is_txn_root) 651 { 652 /* It's a transaction root. Open a fresh copy. */ 653 return svn_fs_fs__dag_txn_root(node_p, root->fs, root->txn, pool); 654 } 655 else 656 { 657 /* It's a revision root, so we already have its root directory 658 opened. */ 659 fs_rev_root_data_t *frd = root->fsap_data; 660 *node_p = svn_fs_fs__dag_dup(frd->root_dir, pool); 661 return SVN_NO_ERROR; 662 } 663} 664 665 666/* Set *NODE_P to a mutable root directory for ROOT, cloning if 667 necessary, allocating in POOL. ROOT must be a transaction root. 668 Use ERROR_PATH in error messages. */ 669static svn_error_t * 670mutable_root_node(dag_node_t **node_p, 671 svn_fs_root_t *root, 672 const char *error_path, 673 apr_pool_t *pool) 674{ 675 if (root->is_txn_root) 676 return svn_fs_fs__dag_clone_root(node_p, root->fs, root->txn, pool); 677 else 678 /* If it's not a transaction root, we can't change its contents. */ 679 return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path); 680} 681 682 683 684/* Traversing directory paths. */ 685 686typedef enum copy_id_inherit_t 687{ 688 copy_id_inherit_unknown = 0, 689 copy_id_inherit_self, 690 copy_id_inherit_parent, 691 copy_id_inherit_new 692 693} copy_id_inherit_t; 694 695/* A linked list representing the path from a node up to a root 696 directory. We use this for cloning, and for operations that need 697 to deal with both a node and its parent directory. For example, a 698 `delete' operation needs to know that the node actually exists, but 699 also needs to change the parent directory. */ 700typedef struct parent_path_t 701{ 702 703 /* A node along the path. This could be the final node, one of its 704 parents, or the root. Every parent path ends with an element for 705 the root directory. */ 706 dag_node_t *node; 707 708 /* The name NODE has in its parent directory. This is zero for the 709 root directory, which (obviously) has no name in its parent. */ 710 char *entry; 711 712 /* The parent of NODE, or zero if NODE is the root directory. */ 713 struct parent_path_t *parent; 714 715 /* The copy ID inheritance style. */ 716 copy_id_inherit_t copy_inherit; 717 718 /* If copy ID inheritance style is copy_id_inherit_new, this is the 719 path which should be implicitly copied; otherwise, this is NULL. */ 720 const char *copy_src_path; 721 722} parent_path_t; 723 724/* Return a text string describing the absolute path of parent_path 725 PARENT_PATH. It will be allocated in POOL. */ 726static const char * 727parent_path_path(parent_path_t *parent_path, 728 apr_pool_t *pool) 729{ 730 const char *path_so_far = "/"; 731 if (parent_path->parent) 732 path_so_far = parent_path_path(parent_path->parent, pool); 733 return parent_path->entry 734 ? svn_fspath__join(path_so_far, parent_path->entry, pool) 735 : path_so_far; 736} 737 738 739/* Return the FS path for the parent path chain object CHILD relative 740 to its ANCESTOR in the same chain, allocated in POOL. */ 741static const char * 742parent_path_relpath(parent_path_t *child, 743 parent_path_t *ancestor, 744 apr_pool_t *pool) 745{ 746 const char *path_so_far = ""; 747 parent_path_t *this_node = child; 748 while (this_node != ancestor) 749 { 750 assert(this_node != NULL); 751 path_so_far = svn_relpath_join(this_node->entry, path_so_far, pool); 752 this_node = this_node->parent; 753 } 754 return path_so_far; 755} 756 757 758 759/* Choose a copy ID inheritance method *INHERIT_P to be used in the 760 event that immutable node CHILD in FS needs to be made mutable. If 761 the inheritance method is copy_id_inherit_new, also return a 762 *COPY_SRC_PATH on which to base the new copy ID (else return NULL 763 for that path). CHILD must have a parent (it cannot be the root 764 node). TXN_ID is the transaction in which these items might be 765 mutable. Allocations are taken from POOL. */ 766static svn_error_t * 767get_copy_inheritance(copy_id_inherit_t *inherit_p, 768 const char **copy_src_path, 769 svn_fs_t *fs, 770 parent_path_t *child, 771 const char *txn_id, 772 apr_pool_t *pool) 773{ 774 const svn_fs_id_t *child_id, *parent_id, *copyroot_id; 775 const char *child_copy_id, *parent_copy_id; 776 const char *id_path = NULL; 777 svn_fs_root_t *copyroot_root; 778 dag_node_t *copyroot_node; 779 svn_revnum_t copyroot_rev; 780 const char *copyroot_path; 781 782 SVN_ERR_ASSERT(child && child->parent && txn_id); 783 784 /* Initialize some convenience variables. */ 785 child_id = svn_fs_fs__dag_get_id(child->node); 786 parent_id = svn_fs_fs__dag_get_id(child->parent->node); 787 child_copy_id = svn_fs_fs__id_copy_id(child_id); 788 parent_copy_id = svn_fs_fs__id_copy_id(parent_id); 789 790 /* If this child is already mutable, we have nothing to do. */ 791 if (svn_fs_fs__id_txn_id(child_id)) 792 { 793 *inherit_p = copy_id_inherit_self; 794 *copy_src_path = NULL; 795 return SVN_NO_ERROR; 796 } 797 798 /* From this point on, we'll assume that the child will just take 799 its copy ID from its parent. */ 800 *inherit_p = copy_id_inherit_parent; 801 *copy_src_path = NULL; 802 803 /* Special case: if the child's copy ID is '0', use the parent's 804 copy ID. */ 805 if (strcmp(child_copy_id, "0") == 0) 806 return SVN_NO_ERROR; 807 808 /* Compare the copy IDs of the child and its parent. If they are 809 the same, then the child is already on the same branch as the 810 parent, and should use the same mutability copy ID that the 811 parent will use. */ 812 if (svn_fs_fs__key_compare(child_copy_id, parent_copy_id) == 0) 813 return SVN_NO_ERROR; 814 815 /* If the child is on the same branch that the parent is on, the 816 child should just use the same copy ID that the parent would use. 817 Else, the child needs to generate a new copy ID to use should it 818 need to be made mutable. We will claim that child is on the same 819 branch as its parent if the child itself is not a branch point, 820 or if it is a branch point that we are accessing via its original 821 copy destination path. */ 822 SVN_ERR(svn_fs_fs__dag_get_copyroot(©root_rev, ©root_path, 823 child->node)); 824 SVN_ERR(svn_fs_fs__revision_root(©root_root, fs, copyroot_rev, pool)); 825 SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, FALSE, pool)); 826 copyroot_id = svn_fs_fs__dag_get_id(copyroot_node); 827 828 if (svn_fs_fs__id_compare(copyroot_id, child_id) == -1) 829 return SVN_NO_ERROR; 830 831 /* Determine if we are looking at the child via its original path or 832 as a subtree item of a copied tree. */ 833 id_path = svn_fs_fs__dag_get_created_path(child->node); 834 if (strcmp(id_path, parent_path_path(child, pool)) == 0) 835 { 836 *inherit_p = copy_id_inherit_self; 837 return SVN_NO_ERROR; 838 } 839 840 /* We are pretty sure that the child node is an unedited nested 841 branched node. When it needs to be made mutable, it should claim 842 a new copy ID. */ 843 *inherit_p = copy_id_inherit_new; 844 *copy_src_path = id_path; 845 return SVN_NO_ERROR; 846} 847 848/* Allocate a new parent_path_t node from POOL, referring to NODE, 849 ENTRY, PARENT, and COPY_ID. */ 850static parent_path_t * 851make_parent_path(dag_node_t *node, 852 char *entry, 853 parent_path_t *parent, 854 apr_pool_t *pool) 855{ 856 parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path)); 857 parent_path->node = node; 858 parent_path->entry = entry; 859 parent_path->parent = parent; 860 parent_path->copy_inherit = copy_id_inherit_unknown; 861 parent_path->copy_src_path = NULL; 862 return parent_path; 863} 864 865 866/* Flags for open_path. */ 867typedef enum open_path_flags_t { 868 869 /* The last component of the PATH need not exist. (All parent 870 directories must exist, as usual.) If the last component doesn't 871 exist, simply leave the `node' member of the bottom parent_path 872 component zero. */ 873 open_path_last_optional = 1, 874 875 /* When this flag is set, don't bother to lookup the DAG node in 876 our caches because we already tried this. Ignoring this flag 877 has no functional impact. */ 878 open_path_uncached = 2, 879 880 /* The caller does not care about the parent node chain but only 881 the final DAG node. */ 882 open_path_node_only = 4 883} open_path_flags_t; 884 885 886/* Open the node identified by PATH in ROOT, allocating in POOL. Set 887 *PARENT_PATH_P to a path from the node up to ROOT. The resulting 888 **PARENT_PATH_P value is guaranteed to contain at least one 889 *element, for the root directory. PATH must be in canonical form. 890 891 If resulting *PARENT_PATH_P will eventually be made mutable and 892 modified, or if copy ID inheritance information is otherwise 893 needed, TXN_ID should be the ID of the mutability transaction. If 894 TXN_ID is NULL, no copy ID inheritance information will be 895 calculated for the *PARENT_PATH_P chain. 896 897 If FLAGS & open_path_last_optional is zero, return the error 898 SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist. If 899 non-zero, require all the parent directories to exist as normal, 900 but if the final path component doesn't exist, simply return a path 901 whose bottom `node' member is zero. This option is useful for 902 callers that create new nodes --- we find the parent directory for 903 them, and tell them whether the entry exists already. 904 905 The remaining bits in FLAGS are hints that allow this function 906 to take shortcuts based on knowledge that the caller provides, 907 such as the caller is not actually being interested in PARENT_PATH_P, 908 but only in (*PARENT_PATH_P)->NODE. 909 910 NOTE: Public interfaces which only *read* from the filesystem 911 should not call this function directly, but should instead use 912 get_dag(). 913*/ 914static svn_error_t * 915open_path(parent_path_t **parent_path_p, 916 svn_fs_root_t *root, 917 const char *path, 918 int flags, 919 const char *txn_id, 920 apr_pool_t *pool) 921{ 922 svn_fs_t *fs = root->fs; 923 dag_node_t *here = NULL; /* The directory we're currently looking at. */ 924 parent_path_t *parent_path; /* The path from HERE up to the root. */ 925 const char *rest; /* The portion of PATH we haven't traversed yet. */ 926 927 /* ensure a canonical path representation */ 928 const char *path_so_far = "/"; 929 apr_pool_t *iterpool = svn_pool_create(pool); 930 931 /* callers often traverse the tree in some path-based order. That means 932 a sibling of PATH has been presently accessed. Try to start the lookup 933 directly at the parent node, if the caller did not requested the full 934 parent chain. */ 935 const char *directory; 936 assert(svn_fs__is_canonical_abspath(path)); 937 if (flags & open_path_node_only) 938 { 939 directory = svn_dirent_dirname(path, pool); 940 if (directory[1] != 0) /* root nodes are covered anyway */ 941 SVN_ERR(dag_node_cache_get(&here, root, directory, TRUE, pool)); 942 } 943 944 /* did the shortcut work? */ 945 if (here) 946 { 947 path_so_far = directory; 948 rest = path + strlen(directory) + 1; 949 } 950 else 951 { 952 /* Make a parent_path item for the root node, using its own current 953 copy id. */ 954 SVN_ERR(root_node(&here, root, pool)); 955 rest = path + 1; /* skip the leading '/', it saves in iteration */ 956 } 957 958 parent_path = make_parent_path(here, 0, 0, pool); 959 parent_path->copy_inherit = copy_id_inherit_self; 960 961 /* Whenever we are at the top of this loop: 962 - HERE is our current directory, 963 - ID is the node revision ID of HERE, 964 - REST is the path we're going to find in HERE, and 965 - PARENT_PATH includes HERE and all its parents. */ 966 for (;;) 967 { 968 const char *next; 969 char *entry; 970 dag_node_t *child; 971 972 svn_pool_clear(iterpool); 973 974 /* Parse out the next entry from the path. */ 975 entry = svn_fs__next_entry_name(&next, rest, pool); 976 977 /* Calculate the path traversed thus far. */ 978 path_so_far = svn_fspath__join(path_so_far, entry, pool); 979 980 if (*entry == '\0') 981 { 982 /* Given the behavior of svn_fs__next_entry_name(), this 983 happens when the path either starts or ends with a slash. 984 In either case, we stay put: the current directory stays 985 the same, and we add nothing to the parent path. */ 986 child = here; 987 } 988 else 989 { 990 copy_id_inherit_t inherit; 991 const char *copy_path = NULL; 992 svn_error_t *err = SVN_NO_ERROR; 993 dag_node_t *cached_node = NULL; 994 995 /* If we found a directory entry, follow it. First, we 996 check our node cache, and, failing that, we hit the DAG 997 layer. Don't bother to contact the cache for the last 998 element if we already know the lookup to fail for the 999 complete path. */ 1000 if (next || !(flags & open_path_uncached)) 1001 SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far, 1002 TRUE, pool)); 1003 if (cached_node) 1004 child = cached_node; 1005 else 1006 err = svn_fs_fs__dag_open(&child, here, entry, pool, iterpool); 1007 1008 /* "file not found" requires special handling. */ 1009 if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) 1010 { 1011 /* If this was the last path component, and the caller 1012 said it was optional, then don't return an error; 1013 just put a NULL node pointer in the path. */ 1014 1015 svn_error_clear(err); 1016 1017 if ((flags & open_path_last_optional) 1018 && (! next || *next == '\0')) 1019 { 1020 parent_path = make_parent_path(NULL, entry, parent_path, 1021 pool); 1022 break; 1023 } 1024 else 1025 { 1026 /* Build a better error message than svn_fs_fs__dag_open 1027 can provide, giving the root and full path name. */ 1028 return SVN_FS__NOT_FOUND(root, path); 1029 } 1030 } 1031 1032 /* Other errors we return normally. */ 1033 SVN_ERR(err); 1034 1035 if (flags & open_path_node_only) 1036 { 1037 /* Shortcut: the caller only wan'ts the final DAG node. */ 1038 parent_path->node = child; 1039 } 1040 else 1041 { 1042 /* Now, make a parent_path item for CHILD. */ 1043 parent_path = make_parent_path(child, entry, parent_path, pool); 1044 if (txn_id) 1045 { 1046 SVN_ERR(get_copy_inheritance(&inherit, ©_path, fs, 1047 parent_path, txn_id, iterpool)); 1048 parent_path->copy_inherit = inherit; 1049 parent_path->copy_src_path = apr_pstrdup(pool, copy_path); 1050 } 1051 } 1052 1053 /* Cache the node we found (if it wasn't already cached). */ 1054 if (! cached_node) 1055 SVN_ERR(dag_node_cache_set(root, path_so_far, child, iterpool)); 1056 } 1057 1058 /* Are we finished traversing the path? */ 1059 if (! next) 1060 break; 1061 1062 /* The path isn't finished yet; we'd better be in a directory. */ 1063 if (svn_fs_fs__dag_node_kind(child) != svn_node_dir) 1064 SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far), 1065 apr_psprintf(iterpool, _("Failure opening '%s'"), path)); 1066 1067 rest = next; 1068 here = child; 1069 } 1070 1071 svn_pool_destroy(iterpool); 1072 *parent_path_p = parent_path; 1073 return SVN_NO_ERROR; 1074} 1075 1076 1077/* Make the node referred to by PARENT_PATH mutable, if it isn't 1078 already, allocating from POOL. ROOT must be the root from which 1079 PARENT_PATH descends. Clone any parent directories as needed. 1080 Adjust the dag nodes in PARENT_PATH to refer to the clones. Use 1081 ERROR_PATH in error messages. */ 1082static svn_error_t * 1083make_path_mutable(svn_fs_root_t *root, 1084 parent_path_t *parent_path, 1085 const char *error_path, 1086 apr_pool_t *pool) 1087{ 1088 dag_node_t *clone; 1089 const char *txn_id = root->txn; 1090 1091 /* Is the node mutable already? */ 1092 if (svn_fs_fs__dag_check_mutable(parent_path->node)) 1093 return SVN_NO_ERROR; 1094 1095 /* Are we trying to clone the root, or somebody's child node? */ 1096 if (parent_path->parent) 1097 { 1098 const svn_fs_id_t *parent_id, *child_id, *copyroot_id; 1099 const char *copy_id = NULL; 1100 copy_id_inherit_t inherit = parent_path->copy_inherit; 1101 const char *clone_path, *copyroot_path; 1102 svn_revnum_t copyroot_rev; 1103 svn_boolean_t is_parent_copyroot = FALSE; 1104 svn_fs_root_t *copyroot_root; 1105 dag_node_t *copyroot_node; 1106 1107 /* We're trying to clone somebody's child. Make sure our parent 1108 is mutable. */ 1109 SVN_ERR(make_path_mutable(root, parent_path->parent, 1110 error_path, pool)); 1111 1112 switch (inherit) 1113 { 1114 case copy_id_inherit_parent: 1115 parent_id = svn_fs_fs__dag_get_id(parent_path->parent->node); 1116 copy_id = svn_fs_fs__id_copy_id(parent_id); 1117 break; 1118 1119 case copy_id_inherit_new: 1120 SVN_ERR(svn_fs_fs__reserve_copy_id(©_id, root->fs, txn_id, 1121 pool)); 1122 break; 1123 1124 case copy_id_inherit_self: 1125 copy_id = NULL; 1126 break; 1127 1128 case copy_id_inherit_unknown: 1129 default: 1130 SVN_ERR_MALFUNCTION(); /* uh-oh -- somebody didn't calculate copy-ID 1131 inheritance data. */ 1132 } 1133 1134 /* Determine what copyroot our new child node should use. */ 1135 SVN_ERR(svn_fs_fs__dag_get_copyroot(©root_rev, ©root_path, 1136 parent_path->node)); 1137 SVN_ERR(svn_fs_fs__revision_root(©root_root, root->fs, 1138 copyroot_rev, pool)); 1139 SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, 1140 FALSE, pool)); 1141 1142 child_id = svn_fs_fs__dag_get_id(parent_path->node); 1143 copyroot_id = svn_fs_fs__dag_get_id(copyroot_node); 1144 if (strcmp(svn_fs_fs__id_node_id(child_id), 1145 svn_fs_fs__id_node_id(copyroot_id)) != 0) 1146 is_parent_copyroot = TRUE; 1147 1148 /* Now make this node mutable. */ 1149 clone_path = parent_path_path(parent_path->parent, pool); 1150 SVN_ERR(svn_fs_fs__dag_clone_child(&clone, 1151 parent_path->parent->node, 1152 clone_path, 1153 parent_path->entry, 1154 copy_id, txn_id, 1155 is_parent_copyroot, 1156 pool)); 1157 1158 /* Update the path cache. */ 1159 SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool), 1160 clone, pool)); 1161 } 1162 else 1163 { 1164 /* We're trying to clone the root directory. */ 1165 SVN_ERR(mutable_root_node(&clone, root, error_path, pool)); 1166 } 1167 1168 /* Update the PARENT_PATH link to refer to the clone. */ 1169 parent_path->node = clone; 1170 1171 return SVN_NO_ERROR; 1172} 1173 1174 1175/* Open the node identified by PATH in ROOT. Set DAG_NODE_P to the 1176 node we find, allocated in POOL. Return the error 1177 SVN_ERR_FS_NOT_FOUND if this node doesn't exist. 1178 1179 Since locking can be expensive and POOL may be long-living, for 1180 nodes that will not need to survive the next call to this function, 1181 set NEEDS_LOCK_CACHE to FALSE. */ 1182static svn_error_t * 1183get_dag(dag_node_t **dag_node_p, 1184 svn_fs_root_t *root, 1185 const char *path, 1186 svn_boolean_t needs_lock_cache, 1187 apr_pool_t *pool) 1188{ 1189 parent_path_t *parent_path; 1190 dag_node_t *node = NULL; 1191 1192 /* First we look for the DAG in our cache 1193 (if the path may be canonical). */ 1194 if (*path == '/') 1195 SVN_ERR(dag_node_cache_get(&node, root, path, needs_lock_cache, pool)); 1196 1197 if (! node) 1198 { 1199 /* Canonicalize the input PATH. */ 1200 if (! svn_fs__is_canonical_abspath(path)) 1201 { 1202 path = svn_fs__canonicalize_abspath(path, pool); 1203 1204 /* Try again with the corrected path. */ 1205 SVN_ERR(dag_node_cache_get(&node, root, path, needs_lock_cache, 1206 pool)); 1207 } 1208 1209 if (! node) 1210 { 1211 /* Call open_path with no flags, as we want this to return an 1212 * error if the node for which we are searching doesn't exist. */ 1213 SVN_ERR(open_path(&parent_path, root, path, 1214 open_path_uncached | open_path_node_only, 1215 NULL, pool)); 1216 node = parent_path->node; 1217 1218 /* No need to cache our find -- open_path() will do that for us. */ 1219 } 1220 } 1221 1222 *dag_node_p = node; 1223 return SVN_NO_ERROR; 1224} 1225 1226 1227 1228/* Populating the `changes' table. */ 1229 1230/* Add a change to the changes table in FS, keyed on transaction id 1231 TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on 1232 PATH (whose node revision id is--or was, in the case of a 1233 deletion--NODEREV_ID), and optionally that TEXT_MODs or PROP_MODs 1234 occurred. If the change resulted from a copy, COPYFROM_REV and 1235 COPYFROM_PATH specify under which revision and path the node was 1236 copied from. If this was not part of a copy, COPYFROM_REV should 1237 be SVN_INVALID_REVNUM. Do all this as part of POOL. */ 1238static svn_error_t * 1239add_change(svn_fs_t *fs, 1240 const char *txn_id, 1241 const char *path, 1242 const svn_fs_id_t *noderev_id, 1243 svn_fs_path_change_kind_t change_kind, 1244 svn_boolean_t text_mod, 1245 svn_boolean_t prop_mod, 1246 svn_node_kind_t node_kind, 1247 svn_revnum_t copyfrom_rev, 1248 const char *copyfrom_path, 1249 apr_pool_t *pool) 1250{ 1251 return svn_fs_fs__add_change(fs, txn_id, 1252 svn_fs__canonicalize_abspath(path, pool), 1253 noderev_id, change_kind, text_mod, prop_mod, 1254 node_kind, copyfrom_rev, copyfrom_path, 1255 pool); 1256} 1257 1258 1259 1260/* Generic node operations. */ 1261 1262/* Get the id of a node referenced by path PATH in ROOT. Return the 1263 id in *ID_P allocated in POOL. */ 1264svn_error_t * 1265svn_fs_fs__node_id(const svn_fs_id_t **id_p, 1266 svn_fs_root_t *root, 1267 const char *path, 1268 apr_pool_t *pool) 1269{ 1270 if ((! root->is_txn_root) 1271 && (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0')))) 1272 { 1273 /* Optimize the case where we don't need any db access at all. 1274 The root directory ("" or "/") node is stored in the 1275 svn_fs_root_t object, and never changes when it's a revision 1276 root, so we can just reach in and grab it directly. */ 1277 fs_rev_root_data_t *frd = root->fsap_data; 1278 *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(frd->root_dir), pool); 1279 } 1280 else 1281 { 1282 dag_node_t *node; 1283 1284 SVN_ERR(get_dag(&node, root, path, FALSE, pool)); 1285 *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(node), pool); 1286 } 1287 return SVN_NO_ERROR; 1288} 1289 1290 1291svn_error_t * 1292svn_fs_fs__node_created_rev(svn_revnum_t *revision, 1293 svn_fs_root_t *root, 1294 const char *path, 1295 apr_pool_t *pool) 1296{ 1297 dag_node_t *node; 1298 1299 SVN_ERR(get_dag(&node, root, path, FALSE, pool)); 1300 return svn_fs_fs__dag_get_revision(revision, node, pool); 1301} 1302 1303 1304/* Set *CREATED_PATH to the path at which PATH under ROOT was created. 1305 Return a string allocated in POOL. */ 1306static svn_error_t * 1307fs_node_created_path(const char **created_path, 1308 svn_fs_root_t *root, 1309 const char *path, 1310 apr_pool_t *pool) 1311{ 1312 dag_node_t *node; 1313 1314 SVN_ERR(get_dag(&node, root, path, TRUE, pool)); 1315 *created_path = svn_fs_fs__dag_get_created_path(node); 1316 1317 return SVN_NO_ERROR; 1318} 1319 1320 1321/* Set *KIND_P to the type of node located at PATH under ROOT. 1322 Perform temporary allocations in POOL. */ 1323static svn_error_t * 1324node_kind(svn_node_kind_t *kind_p, 1325 svn_fs_root_t *root, 1326 const char *path, 1327 apr_pool_t *pool) 1328{ 1329 const svn_fs_id_t *node_id; 1330 dag_node_t *node; 1331 1332 /* Get the node id. */ 1333 SVN_ERR(svn_fs_fs__node_id(&node_id, root, path, pool)); 1334 1335 /* Use the node id to get the real kind. */ 1336 SVN_ERR(svn_fs_fs__dag_get_node(&node, root->fs, node_id, pool)); 1337 *kind_p = svn_fs_fs__dag_node_kind(node); 1338 1339 return SVN_NO_ERROR; 1340} 1341 1342 1343/* Set *KIND_P to the type of node present at PATH under ROOT. If 1344 PATH does not exist under ROOT, set *KIND_P to svn_node_none. Use 1345 POOL for temporary allocation. */ 1346svn_error_t * 1347svn_fs_fs__check_path(svn_node_kind_t *kind_p, 1348 svn_fs_root_t *root, 1349 const char *path, 1350 apr_pool_t *pool) 1351{ 1352 svn_error_t *err = node_kind(kind_p, root, path, pool); 1353 if (err && 1354 ((err->apr_err == SVN_ERR_FS_NOT_FOUND) 1355 || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY))) 1356 { 1357 svn_error_clear(err); 1358 err = SVN_NO_ERROR; 1359 *kind_p = svn_node_none; 1360 } 1361 1362 return svn_error_trace(err); 1363} 1364 1365/* Set *VALUE_P to the value of the property named PROPNAME of PATH in 1366 ROOT. If the node has no property by that name, set *VALUE_P to 1367 zero. Allocate the result in POOL. */ 1368static svn_error_t * 1369fs_node_prop(svn_string_t **value_p, 1370 svn_fs_root_t *root, 1371 const char *path, 1372 const char *propname, 1373 apr_pool_t *pool) 1374{ 1375 dag_node_t *node; 1376 apr_hash_t *proplist; 1377 1378 SVN_ERR(get_dag(&node, root, path, FALSE, pool)); 1379 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, node, pool)); 1380 *value_p = NULL; 1381 if (proplist) 1382 *value_p = svn_hash_gets(proplist, propname); 1383 1384 return SVN_NO_ERROR; 1385} 1386 1387 1388/* Set *TABLE_P to the entire property list of PATH under ROOT, as an 1389 APR hash table allocated in POOL. The resulting property table 1390 maps property names to pointers to svn_string_t objects containing 1391 the property value. */ 1392static svn_error_t * 1393fs_node_proplist(apr_hash_t **table_p, 1394 svn_fs_root_t *root, 1395 const char *path, 1396 apr_pool_t *pool) 1397{ 1398 apr_hash_t *table; 1399 dag_node_t *node; 1400 1401 SVN_ERR(get_dag(&node, root, path, FALSE, pool)); 1402 SVN_ERR(svn_fs_fs__dag_get_proplist(&table, node, pool)); 1403 *table_p = table ? table : apr_hash_make(pool); 1404 1405 return SVN_NO_ERROR; 1406} 1407 1408 1409static svn_error_t * 1410increment_mergeinfo_up_tree(parent_path_t *pp, 1411 apr_int64_t increment, 1412 apr_pool_t *pool) 1413{ 1414 for (; pp; pp = pp->parent) 1415 SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(pp->node, 1416 increment, 1417 pool)); 1418 1419 return SVN_NO_ERROR; 1420} 1421 1422/* Change, add, or delete a node's property value. The affected node 1423 is PATH under ROOT, the property value to modify is NAME, and VALUE 1424 points to either a string value to set the new contents to, or NULL 1425 if the property should be deleted. Perform temporary allocations 1426 in POOL. */ 1427static svn_error_t * 1428fs_change_node_prop(svn_fs_root_t *root, 1429 const char *path, 1430 const char *name, 1431 const svn_string_t *value, 1432 apr_pool_t *pool) 1433{ 1434 parent_path_t *parent_path; 1435 apr_hash_t *proplist; 1436 const char *txn_id; 1437 1438 if (! root->is_txn_root) 1439 return SVN_FS__NOT_TXN(root); 1440 txn_id = root->txn; 1441 1442 path = svn_fs__canonicalize_abspath(path, pool); 1443 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool)); 1444 1445 /* Check (non-recursively) to see if path is locked; if so, check 1446 that we can use it. */ 1447 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS) 1448 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE, 1449 pool)); 1450 1451 SVN_ERR(make_path_mutable(root, parent_path, path, pool)); 1452 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, parent_path->node, pool)); 1453 1454 /* If there's no proplist, but we're just deleting a property, exit now. */ 1455 if ((! proplist) && (! value)) 1456 return SVN_NO_ERROR; 1457 1458 /* Now, if there's no proplist, we know we need to make one. */ 1459 if (! proplist) 1460 proplist = apr_hash_make(pool); 1461 1462 if (svn_fs_fs__fs_supports_mergeinfo(root->fs) 1463 && strcmp (name, SVN_PROP_MERGEINFO) == 0) 1464 { 1465 apr_int64_t increment = 0; 1466 svn_boolean_t had_mergeinfo; 1467 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&had_mergeinfo, parent_path->node)); 1468 1469 if (value && !had_mergeinfo) 1470 increment = 1; 1471 else if (!value && had_mergeinfo) 1472 increment = -1; 1473 1474 if (increment != 0) 1475 { 1476 SVN_ERR(increment_mergeinfo_up_tree(parent_path, increment, pool)); 1477 SVN_ERR(svn_fs_fs__dag_set_has_mergeinfo(parent_path->node, 1478 (value != NULL), pool)); 1479 } 1480 } 1481 1482 /* Set the property. */ 1483 svn_hash_sets(proplist, name, value); 1484 1485 /* Overwrite the node's proplist. */ 1486 SVN_ERR(svn_fs_fs__dag_set_proplist(parent_path->node, proplist, 1487 pool)); 1488 1489 /* Make a record of this modification in the changes table. */ 1490 return add_change(root->fs, txn_id, path, 1491 svn_fs_fs__dag_get_id(parent_path->node), 1492 svn_fs_path_change_modify, FALSE, TRUE, 1493 svn_fs_fs__dag_node_kind(parent_path->node), 1494 SVN_INVALID_REVNUM, NULL, pool); 1495} 1496 1497 1498/* Determine if the properties of two path/root combinations are 1499 different. Set *CHANGED_P to TRUE if the properties at PATH1 under 1500 ROOT1 differ from those at PATH2 under ROOT2, or FALSE otherwise. 1501 Both roots must be in the same filesystem. */ 1502static svn_error_t * 1503fs_props_changed(svn_boolean_t *changed_p, 1504 svn_fs_root_t *root1, 1505 const char *path1, 1506 svn_fs_root_t *root2, 1507 const char *path2, 1508 apr_pool_t *pool) 1509{ 1510 dag_node_t *node1, *node2; 1511 1512 /* Check that roots are in the same fs. */ 1513 if (root1->fs != root2->fs) 1514 return svn_error_create 1515 (SVN_ERR_FS_GENERAL, NULL, 1516 _("Cannot compare property value between two different filesystems")); 1517 1518 SVN_ERR(get_dag(&node1, root1, path1, TRUE, pool)); 1519 SVN_ERR(get_dag(&node2, root2, path2, TRUE, pool)); 1520 return svn_fs_fs__dag_things_different(changed_p, NULL, 1521 node1, node2); 1522} 1523 1524 1525 1526/* Merges and commits. */ 1527 1528/* Set *NODE to the root node of ROOT. */ 1529static svn_error_t * 1530get_root(dag_node_t **node, svn_fs_root_t *root, apr_pool_t *pool) 1531{ 1532 return get_dag(node, root, "/", TRUE, pool); 1533} 1534 1535 1536/* Set the contents of CONFLICT_PATH to PATH, and return an 1537 SVN_ERR_FS_CONFLICT error that indicates that there was a conflict 1538 at PATH. Perform all allocations in POOL (except the allocation of 1539 CONFLICT_PATH, which should be handled outside this function). */ 1540static svn_error_t * 1541conflict_err(svn_stringbuf_t *conflict_path, 1542 const char *path) 1543{ 1544 svn_stringbuf_set(conflict_path, path); 1545 return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL, 1546 _("Conflict at '%s'"), path); 1547} 1548 1549 1550/* Merge changes between ANCESTOR and SOURCE into TARGET. ANCESTOR 1551 * and TARGET must be distinct node revisions. TARGET_PATH should 1552 * correspond to TARGET's full path in its filesystem, and is used for 1553 * reporting conflict location. 1554 * 1555 * SOURCE, TARGET, and ANCESTOR are generally directories; this 1556 * function recursively merges the directories' contents. If any are 1557 * files, this function simply returns an error whenever SOURCE, 1558 * TARGET, and ANCESTOR are all distinct node revisions. 1559 * 1560 * If there are differences between ANCESTOR and SOURCE that conflict 1561 * with changes between ANCESTOR and TARGET, this function returns an 1562 * SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the 1563 * conflicting node in TARGET, with TARGET_PATH prepended as a path. 1564 * 1565 * If there are no conflicting differences, CONFLICT_P is updated to 1566 * the empty string. 1567 * 1568 * CONFLICT_P must point to a valid svn_stringbuf_t. 1569 * 1570 * Do any necessary temporary allocation in POOL. 1571 */ 1572static svn_error_t * 1573merge(svn_stringbuf_t *conflict_p, 1574 const char *target_path, 1575 dag_node_t *target, 1576 dag_node_t *source, 1577 dag_node_t *ancestor, 1578 const char *txn_id, 1579 apr_int64_t *mergeinfo_increment_out, 1580 apr_pool_t *pool) 1581{ 1582 const svn_fs_id_t *source_id, *target_id, *ancestor_id; 1583 apr_hash_t *s_entries, *t_entries, *a_entries; 1584 apr_hash_index_t *hi; 1585 svn_fs_t *fs; 1586 apr_pool_t *iterpool; 1587 apr_int64_t mergeinfo_increment = 0; 1588 svn_boolean_t fs_supports_mergeinfo; 1589 1590 /* Make sure everyone comes from the same filesystem. */ 1591 fs = svn_fs_fs__dag_get_fs(ancestor); 1592 if ((fs != svn_fs_fs__dag_get_fs(source)) 1593 || (fs != svn_fs_fs__dag_get_fs(target))) 1594 { 1595 return svn_error_create 1596 (SVN_ERR_FS_CORRUPT, NULL, 1597 _("Bad merge; ancestor, source, and target not all in same fs")); 1598 } 1599 1600 /* We have the same fs, now check it. */ 1601 SVN_ERR(svn_fs__check_fs(fs, TRUE)); 1602 1603 source_id = svn_fs_fs__dag_get_id(source); 1604 target_id = svn_fs_fs__dag_get_id(target); 1605 ancestor_id = svn_fs_fs__dag_get_id(ancestor); 1606 1607 /* It's improper to call this function with ancestor == target. */ 1608 if (svn_fs_fs__id_eq(ancestor_id, target_id)) 1609 { 1610 svn_string_t *id_str = svn_fs_fs__id_unparse(target_id, pool); 1611 return svn_error_createf 1612 (SVN_ERR_FS_GENERAL, NULL, 1613 _("Bad merge; target '%s' has id '%s', same as ancestor"), 1614 target_path, id_str->data); 1615 } 1616 1617 svn_stringbuf_setempty(conflict_p); 1618 1619 /* Base cases: 1620 * Either no change made in source, or same change as made in target. 1621 * Both mean nothing to merge here. 1622 */ 1623 if (svn_fs_fs__id_eq(ancestor_id, source_id) 1624 || (svn_fs_fs__id_eq(source_id, target_id))) 1625 return SVN_NO_ERROR; 1626 1627 /* Else proceed, knowing all three are distinct node revisions. 1628 * 1629 * How to merge from this point: 1630 * 1631 * if (not all 3 are directories) 1632 * { 1633 * early exit with conflict; 1634 * } 1635 * 1636 * // Property changes may only be made to up-to-date 1637 * // directories, because once the client commits the prop 1638 * // change, it bumps the directory's revision, and therefore 1639 * // must be able to depend on there being no other changes to 1640 * // that directory in the repository. 1641 * if (target's property list differs from ancestor's) 1642 * conflict; 1643 * 1644 * For each entry NAME in the directory ANCESTOR: 1645 * 1646 * Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of 1647 * the name within ANCESTOR, SOURCE, and TARGET respectively. 1648 * (Possibly null if NAME does not exist in SOURCE or TARGET.) 1649 * 1650 * If ANCESTOR-ENTRY == SOURCE-ENTRY, then: 1651 * No changes were made to this entry while the transaction was in 1652 * progress, so do nothing to the target. 1653 * 1654 * Else if ANCESTOR-ENTRY == TARGET-ENTRY, then: 1655 * A change was made to this entry while the transaction was in 1656 * process, but the transaction did not touch this entry. Replace 1657 * TARGET-ENTRY with SOURCE-ENTRY. 1658 * 1659 * Else: 1660 * Changes were made to this entry both within the transaction and 1661 * to the repository while the transaction was in progress. They 1662 * must be merged or declared to be in conflict. 1663 * 1664 * If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a 1665 * double delete; flag a conflict. 1666 * 1667 * If any of the three entries is of type file, declare a conflict. 1668 * 1669 * If either SOURCE-ENTRY or TARGET-ENTRY is not a direct 1670 * modification of ANCESTOR-ENTRY (determine by comparing the 1671 * node-id fields), declare a conflict. A replacement is 1672 * incompatible with a modification or other replacement--even 1673 * an identical replacement. 1674 * 1675 * Direct modifications were made to the directory ANCESTOR-ENTRY 1676 * in both SOURCE and TARGET. Recursively merge these 1677 * modifications. 1678 * 1679 * For each leftover entry NAME in the directory SOURCE: 1680 * 1681 * If NAME exists in TARGET, declare a conflict. Even if SOURCE and 1682 * TARGET are adding exactly the same thing, two additions are not 1683 * auto-mergeable with each other. 1684 * 1685 * Add NAME to TARGET with the entry from SOURCE. 1686 * 1687 * Now that we are done merging the changes from SOURCE into the 1688 * directory TARGET, update TARGET's predecessor to be SOURCE. 1689 */ 1690 1691 if ((svn_fs_fs__dag_node_kind(source) != svn_node_dir) 1692 || (svn_fs_fs__dag_node_kind(target) != svn_node_dir) 1693 || (svn_fs_fs__dag_node_kind(ancestor) != svn_node_dir)) 1694 { 1695 return conflict_err(conflict_p, target_path); 1696 } 1697 1698 1699 /* Possible early merge failure: if target and ancestor have 1700 different property lists, then the merge should fail. 1701 Propchanges can *only* be committed on an up-to-date directory. 1702 ### TODO: see issue #418 about the inelegance of this. 1703 1704 Another possible, similar, early merge failure: if source and 1705 ancestor have different property lists (meaning someone else 1706 changed directory properties while our commit transaction was 1707 happening), the merge should fail. See issue #2751. 1708 */ 1709 { 1710 node_revision_t *tgt_nr, *anc_nr, *src_nr; 1711 1712 /* Get node revisions for our id's. */ 1713 SVN_ERR(svn_fs_fs__get_node_revision(&tgt_nr, fs, target_id, pool)); 1714 SVN_ERR(svn_fs_fs__get_node_revision(&anc_nr, fs, ancestor_id, pool)); 1715 SVN_ERR(svn_fs_fs__get_node_revision(&src_nr, fs, source_id, pool)); 1716 1717 /* Now compare the prop-keys of the skels. Note that just because 1718 the keys are different -doesn't- mean the proplists have 1719 different contents. But merge() isn't concerned with contents; 1720 it doesn't do a brute-force comparison on textual contents, so 1721 it won't do that here either. Checking to see if the propkey 1722 atoms are `equal' is enough. */ 1723 if (! svn_fs_fs__noderev_same_rep_key(tgt_nr->prop_rep, anc_nr->prop_rep)) 1724 return conflict_err(conflict_p, target_path); 1725 if (! svn_fs_fs__noderev_same_rep_key(src_nr->prop_rep, anc_nr->prop_rep)) 1726 return conflict_err(conflict_p, target_path); 1727 } 1728 1729 /* ### todo: it would be more efficient to simply check for a NULL 1730 entries hash where necessary below than to allocate an empty hash 1731 here, but another day, another day... */ 1732 SVN_ERR(svn_fs_fs__dag_dir_entries(&s_entries, source, pool)); 1733 SVN_ERR(svn_fs_fs__dag_dir_entries(&t_entries, target, pool)); 1734 SVN_ERR(svn_fs_fs__dag_dir_entries(&a_entries, ancestor, pool)); 1735 1736 fs_supports_mergeinfo = svn_fs_fs__fs_supports_mergeinfo(fs); 1737 1738 /* for each entry E in a_entries... */ 1739 iterpool = svn_pool_create(pool); 1740 for (hi = apr_hash_first(pool, a_entries); 1741 hi; 1742 hi = apr_hash_next(hi)) 1743 { 1744 svn_fs_dirent_t *s_entry, *t_entry, *a_entry; 1745 const char *name; 1746 apr_ssize_t klen; 1747 1748 svn_pool_clear(iterpool); 1749 1750 name = svn__apr_hash_index_key(hi); 1751 klen = svn__apr_hash_index_klen(hi); 1752 a_entry = svn__apr_hash_index_val(hi); 1753 1754 s_entry = apr_hash_get(s_entries, name, klen); 1755 t_entry = apr_hash_get(t_entries, name, klen); 1756 1757 /* No changes were made to this entry while the transaction was 1758 in progress, so do nothing to the target. */ 1759 if (s_entry && svn_fs_fs__id_eq(a_entry->id, s_entry->id)) 1760 goto end; 1761 1762 /* A change was made to this entry while the transaction was in 1763 process, but the transaction did not touch this entry. */ 1764 else if (t_entry && svn_fs_fs__id_eq(a_entry->id, t_entry->id)) 1765 { 1766 dag_node_t *t_ent_node; 1767 SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs, 1768 t_entry->id, iterpool)); 1769 if (fs_supports_mergeinfo) 1770 { 1771 apr_int64_t mergeinfo_start; 1772 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start, 1773 t_ent_node)); 1774 mergeinfo_increment -= mergeinfo_start; 1775 } 1776 1777 if (s_entry) 1778 { 1779 dag_node_t *s_ent_node; 1780 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs, 1781 s_entry->id, iterpool)); 1782 1783 if (fs_supports_mergeinfo) 1784 { 1785 apr_int64_t mergeinfo_end; 1786 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end, 1787 s_ent_node)); 1788 mergeinfo_increment += mergeinfo_end; 1789 } 1790 1791 SVN_ERR(svn_fs_fs__dag_set_entry(target, name, 1792 s_entry->id, 1793 s_entry->kind, 1794 txn_id, 1795 iterpool)); 1796 } 1797 else 1798 { 1799 SVN_ERR(svn_fs_fs__dag_delete(target, name, txn_id, iterpool)); 1800 } 1801 } 1802 1803 /* Changes were made to this entry both within the transaction 1804 and to the repository while the transaction was in progress. 1805 They must be merged or declared to be in conflict. */ 1806 else 1807 { 1808 dag_node_t *s_ent_node, *t_ent_node, *a_ent_node; 1809 const char *new_tpath; 1810 apr_int64_t sub_mergeinfo_increment; 1811 1812 /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a 1813 double delete; if one of them is null, that's a delete versus 1814 a modification. In any of these cases, flag a conflict. */ 1815 if (s_entry == NULL || t_entry == NULL) 1816 return conflict_err(conflict_p, 1817 svn_fspath__join(target_path, 1818 a_entry->name, 1819 iterpool)); 1820 1821 /* If any of the three entries is of type file, flag a conflict. */ 1822 if (s_entry->kind == svn_node_file 1823 || t_entry->kind == svn_node_file 1824 || a_entry->kind == svn_node_file) 1825 return conflict_err(conflict_p, 1826 svn_fspath__join(target_path, 1827 a_entry->name, 1828 iterpool)); 1829 1830 /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct 1831 modification of ANCESTOR-ENTRY, declare a conflict. */ 1832 if (strcmp(svn_fs_fs__id_node_id(s_entry->id), 1833 svn_fs_fs__id_node_id(a_entry->id)) != 0 1834 || strcmp(svn_fs_fs__id_copy_id(s_entry->id), 1835 svn_fs_fs__id_copy_id(a_entry->id)) != 0 1836 || strcmp(svn_fs_fs__id_node_id(t_entry->id), 1837 svn_fs_fs__id_node_id(a_entry->id)) != 0 1838 || strcmp(svn_fs_fs__id_copy_id(t_entry->id), 1839 svn_fs_fs__id_copy_id(a_entry->id)) != 0) 1840 return conflict_err(conflict_p, 1841 svn_fspath__join(target_path, 1842 a_entry->name, 1843 iterpool)); 1844 1845 /* Direct modifications were made to the directory 1846 ANCESTOR-ENTRY in both SOURCE and TARGET. Recursively 1847 merge these modifications. */ 1848 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs, 1849 s_entry->id, iterpool)); 1850 SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs, 1851 t_entry->id, iterpool)); 1852 SVN_ERR(svn_fs_fs__dag_get_node(&a_ent_node, fs, 1853 a_entry->id, iterpool)); 1854 new_tpath = svn_fspath__join(target_path, t_entry->name, iterpool); 1855 SVN_ERR(merge(conflict_p, new_tpath, 1856 t_ent_node, s_ent_node, a_ent_node, 1857 txn_id, 1858 &sub_mergeinfo_increment, 1859 iterpool)); 1860 if (fs_supports_mergeinfo) 1861 mergeinfo_increment += sub_mergeinfo_increment; 1862 } 1863 1864 /* We've taken care of any possible implications E could have. 1865 Remove it from source_entries, so it's easy later to loop 1866 over all the source entries that didn't exist in 1867 ancestor_entries. */ 1868 end: 1869 apr_hash_set(s_entries, name, klen, NULL); 1870 } 1871 1872 /* For each entry E in source but not in ancestor */ 1873 for (hi = apr_hash_first(pool, s_entries); 1874 hi; 1875 hi = apr_hash_next(hi)) 1876 { 1877 svn_fs_dirent_t *s_entry, *t_entry; 1878 const char *name = svn__apr_hash_index_key(hi); 1879 apr_ssize_t klen = svn__apr_hash_index_klen(hi); 1880 dag_node_t *s_ent_node; 1881 1882 svn_pool_clear(iterpool); 1883 1884 s_entry = svn__apr_hash_index_val(hi); 1885 t_entry = apr_hash_get(t_entries, name, klen); 1886 1887 /* If NAME exists in TARGET, declare a conflict. */ 1888 if (t_entry) 1889 return conflict_err(conflict_p, 1890 svn_fspath__join(target_path, 1891 t_entry->name, 1892 iterpool)); 1893 1894 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs, 1895 s_entry->id, iterpool)); 1896 if (fs_supports_mergeinfo) 1897 { 1898 apr_int64_t mergeinfo_s; 1899 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_s, 1900 s_ent_node)); 1901 mergeinfo_increment += mergeinfo_s; 1902 } 1903 1904 SVN_ERR(svn_fs_fs__dag_set_entry 1905 (target, s_entry->name, s_entry->id, s_entry->kind, 1906 txn_id, iterpool)); 1907 } 1908 svn_pool_destroy(iterpool); 1909 1910 SVN_ERR(svn_fs_fs__dag_update_ancestry(target, source, pool)); 1911 1912 if (fs_supports_mergeinfo) 1913 SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(target, 1914 mergeinfo_increment, 1915 pool)); 1916 1917 if (mergeinfo_increment_out) 1918 *mergeinfo_increment_out = mergeinfo_increment; 1919 1920 return SVN_NO_ERROR; 1921} 1922 1923/* Merge changes between an ancestor and SOURCE_NODE into 1924 TXN. The ancestor is either ANCESTOR_NODE, or if 1925 that is null, TXN's base node. 1926 1927 If the merge is successful, TXN's base will become 1928 SOURCE_NODE, and its root node will have a new ID, a 1929 successor of SOURCE_NODE. 1930 1931 If a conflict results, update *CONFLICT to the path in the txn that 1932 conflicted; see the CONFLICT_P parameter of merge() for details. */ 1933static svn_error_t * 1934merge_changes(dag_node_t *ancestor_node, 1935 dag_node_t *source_node, 1936 svn_fs_txn_t *txn, 1937 svn_stringbuf_t *conflict, 1938 apr_pool_t *pool) 1939{ 1940 dag_node_t *txn_root_node; 1941 svn_fs_t *fs = txn->fs; 1942 const char *txn_id = txn->id; 1943 1944 SVN_ERR(svn_fs_fs__dag_txn_root(&txn_root_node, fs, txn_id, pool)); 1945 1946 if (ancestor_node == NULL) 1947 { 1948 SVN_ERR(svn_fs_fs__dag_txn_base_root(&ancestor_node, fs, 1949 txn_id, pool)); 1950 } 1951 1952 if (svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(ancestor_node), 1953 svn_fs_fs__dag_get_id(txn_root_node))) 1954 { 1955 /* If no changes have been made in TXN since its current base, 1956 then it can't conflict with any changes since that base. 1957 The caller isn't supposed to call us in that case. */ 1958 SVN_ERR_MALFUNCTION(); 1959 } 1960 else 1961 SVN_ERR(merge(conflict, "/", txn_root_node, 1962 source_node, ancestor_node, txn_id, NULL, pool)); 1963 1964 return SVN_NO_ERROR; 1965} 1966 1967 1968svn_error_t * 1969svn_fs_fs__commit_txn(const char **conflict_p, 1970 svn_revnum_t *new_rev, 1971 svn_fs_txn_t *txn, 1972 apr_pool_t *pool) 1973{ 1974 /* How do commits work in Subversion? 1975 * 1976 * When you're ready to commit, here's what you have: 1977 * 1978 * 1. A transaction, with a mutable tree hanging off it. 1979 * 2. A base revision, against which TXN_TREE was made. 1980 * 3. A latest revision, which may be newer than the base rev. 1981 * 1982 * The problem is that if latest != base, then one can't simply 1983 * attach the txn root as the root of the new revision, because that 1984 * would lose all the changes between base and latest. It is also 1985 * not acceptable to insist that base == latest; in a busy 1986 * repository, commits happen too fast to insist that everyone keep 1987 * their entire tree up-to-date at all times. Non-overlapping 1988 * changes should not interfere with each other. 1989 * 1990 * The solution is to merge the changes between base and latest into 1991 * the txn tree [see the function merge()]. The txn tree is the 1992 * only one of the three trees that is mutable, so it has to be the 1993 * one to adjust. 1994 * 1995 * You might have to adjust it more than once, if a new latest 1996 * revision gets committed while you were merging in the previous 1997 * one. For example: 1998 * 1999 * 1. Jane starts txn T, based at revision 6. 2000 * 2. Someone commits (or already committed) revision 7. 2001 * 3. Jane's starts merging the changes between 6 and 7 into T. 2002 * 4. Meanwhile, someone commits revision 8. 2003 * 5. Jane finishes the 6-->7 merge. T could now be committed 2004 * against a latest revision of 7, if only that were still the 2005 * latest. Unfortunately, 8 is now the latest, so... 2006 * 6. Jane starts merging the changes between 7 and 8 into T. 2007 * 7. Meanwhile, no one commits any new revisions. Whew. 2008 * 8. Jane commits T, creating revision 9, whose tree is exactly 2009 * T's tree, except immutable now. 2010 * 2011 * Lather, rinse, repeat. 2012 */ 2013 2014 svn_error_t *err = SVN_NO_ERROR; 2015 svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool); 2016 svn_fs_t *fs = txn->fs; 2017 2018 /* Limit memory usage when the repository has a high commit rate and 2019 needs to run the following while loop multiple times. The memory 2020 growth without an iteration pool is very noticeable when the 2021 transaction modifies a node that has 20,000 sibling nodes. */ 2022 apr_pool_t *iterpool = svn_pool_create(pool); 2023 2024 /* Initialize output params. */ 2025 *new_rev = SVN_INVALID_REVNUM; 2026 if (conflict_p) 2027 *conflict_p = NULL; 2028 2029 while (1729) 2030 { 2031 svn_revnum_t youngish_rev; 2032 svn_fs_root_t *youngish_root; 2033 dag_node_t *youngish_root_node; 2034 2035 svn_pool_clear(iterpool); 2036 2037 /* Get the *current* youngest revision. We call it "youngish" 2038 because new revisions might get committed after we've 2039 obtained it. */ 2040 2041 SVN_ERR(svn_fs_fs__youngest_rev(&youngish_rev, fs, iterpool)); 2042 SVN_ERR(svn_fs_fs__revision_root(&youngish_root, fs, youngish_rev, 2043 iterpool)); 2044 2045 /* Get the dag node for the youngest revision. Later we'll use 2046 it as the SOURCE argument to a merge, and if the merge 2047 succeeds, this youngest root node will become the new base 2048 root for the svn txn that was the target of the merge (but 2049 note that the youngest rev may have changed by then -- that's 2050 why we're careful to get this root in its own bdb txn 2051 here). */ 2052 SVN_ERR(get_root(&youngish_root_node, youngish_root, iterpool)); 2053 2054 /* Try to merge. If the merge succeeds, the base root node of 2055 TARGET's txn will become the same as youngish_root_node, so 2056 any future merges will only be between that node and whatever 2057 the root node of the youngest rev is by then. */ 2058 err = merge_changes(NULL, youngish_root_node, txn, conflict, iterpool); 2059 if (err) 2060 { 2061 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p) 2062 *conflict_p = conflict->data; 2063 goto cleanup; 2064 } 2065 txn->base_rev = youngish_rev; 2066 2067 /* Try to commit. */ 2068 err = svn_fs_fs__commit(new_rev, fs, txn, iterpool); 2069 if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE)) 2070 { 2071 /* Did someone else finish committing a new revision while we 2072 were in mid-merge or mid-commit? If so, we'll need to 2073 loop again to merge the new changes in, then try to 2074 commit again. Or if that's not what happened, then just 2075 return the error. */ 2076 svn_revnum_t youngest_rev; 2077 SVN_ERR(svn_fs_fs__youngest_rev(&youngest_rev, fs, iterpool)); 2078 if (youngest_rev == youngish_rev) 2079 goto cleanup; 2080 else 2081 svn_error_clear(err); 2082 } 2083 else if (err) 2084 { 2085 goto cleanup; 2086 } 2087 else 2088 { 2089 err = SVN_NO_ERROR; 2090 goto cleanup; 2091 } 2092 } 2093 2094 cleanup: 2095 2096 svn_fs_fs__reset_txn_caches(fs); 2097 2098 svn_pool_destroy(iterpool); 2099 return svn_error_trace(err); 2100} 2101 2102 2103/* Merge changes between two nodes into a third node. Given nodes 2104 SOURCE_PATH under SOURCE_ROOT, TARGET_PATH under TARGET_ROOT and 2105 ANCESTOR_PATH under ANCESTOR_ROOT, modify target to contain all the 2106 changes between the ancestor and source. If there are conflicts, 2107 return SVN_ERR_FS_CONFLICT and set *CONFLICT_P to a textual 2108 description of the offending changes. Perform any temporary 2109 allocations in POOL. */ 2110static svn_error_t * 2111fs_merge(const char **conflict_p, 2112 svn_fs_root_t *source_root, 2113 const char *source_path, 2114 svn_fs_root_t *target_root, 2115 const char *target_path, 2116 svn_fs_root_t *ancestor_root, 2117 const char *ancestor_path, 2118 apr_pool_t *pool) 2119{ 2120 dag_node_t *source, *ancestor; 2121 svn_fs_txn_t *txn; 2122 svn_error_t *err; 2123 svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool); 2124 2125 if (! target_root->is_txn_root) 2126 return SVN_FS__NOT_TXN(target_root); 2127 2128 /* Paranoia. */ 2129 if ((source_root->fs != ancestor_root->fs) 2130 || (target_root->fs != ancestor_root->fs)) 2131 { 2132 return svn_error_create 2133 (SVN_ERR_FS_CORRUPT, NULL, 2134 _("Bad merge; ancestor, source, and target not all in same fs")); 2135 } 2136 2137 /* ### kff todo: is there any compelling reason to get the nodes in 2138 one db transaction? Right now we don't; txn_body_get_root() gets 2139 one node at a time. This will probably need to change: 2140 2141 Jim Blandy <jimb@zwingli.cygnus.com> writes: 2142 > svn_fs_merge needs to be a single transaction, to protect it against 2143 > people deleting parents of nodes it's working on, etc. 2144 */ 2145 2146 /* Get the ancestor node. */ 2147 SVN_ERR(get_root(&ancestor, ancestor_root, pool)); 2148 2149 /* Get the source node. */ 2150 SVN_ERR(get_root(&source, source_root, pool)); 2151 2152 /* Open a txn for the txn root into which we're merging. */ 2153 SVN_ERR(svn_fs_fs__open_txn(&txn, ancestor_root->fs, target_root->txn, 2154 pool)); 2155 2156 /* Merge changes between ANCESTOR and SOURCE into TXN. */ 2157 err = merge_changes(ancestor, source, txn, conflict, pool); 2158 if (err) 2159 { 2160 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p) 2161 *conflict_p = conflict->data; 2162 return svn_error_trace(err); 2163 } 2164 2165 return SVN_NO_ERROR; 2166} 2167 2168svn_error_t * 2169svn_fs_fs__deltify(svn_fs_t *fs, 2170 svn_revnum_t revision, 2171 apr_pool_t *pool) 2172{ 2173 /* Deltify is a no-op for fs_fs. */ 2174 2175 return SVN_NO_ERROR; 2176} 2177 2178 2179 2180/* Directories. */ 2181 2182/* Set *TABLE_P to a newly allocated APR hash table containing the 2183 entries of the directory at PATH in ROOT. The keys of the table 2184 are entry names, as byte strings, excluding the final null 2185 character; the table's values are pointers to svn_fs_dirent_t 2186 structures. Allocate the table and its contents in POOL. */ 2187static svn_error_t * 2188fs_dir_entries(apr_hash_t **table_p, 2189 svn_fs_root_t *root, 2190 const char *path, 2191 apr_pool_t *pool) 2192{ 2193 dag_node_t *node; 2194 2195 /* Get the entries for this path in the caller's pool. */ 2196 SVN_ERR(get_dag(&node, root, path, FALSE, pool)); 2197 return svn_fs_fs__dag_dir_entries(table_p, node, pool); 2198} 2199 2200/* Raise an error if PATH contains a newline because FSFS cannot handle 2201 * such paths. See issue #4340. */ 2202static svn_error_t * 2203check_newline(const char *path, apr_pool_t *pool) 2204{ 2205 char *c = strchr(path, '\n'); 2206 2207 if (c) 2208 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL, 2209 _("Invalid control character '0x%02x' in path '%s'"), 2210 (unsigned char)*c, svn_path_illegal_path_escape(path, pool)); 2211 2212 return SVN_NO_ERROR; 2213} 2214 2215/* Create a new directory named PATH in ROOT. The new directory has 2216 no entries, and no properties. ROOT must be the root of a 2217 transaction, not a revision. Do any necessary temporary allocation 2218 in POOL. */ 2219static svn_error_t * 2220fs_make_dir(svn_fs_root_t *root, 2221 const char *path, 2222 apr_pool_t *pool) 2223{ 2224 parent_path_t *parent_path; 2225 dag_node_t *sub_dir; 2226 const char *txn_id = root->txn; 2227 2228 SVN_ERR(check_newline(path, pool)); 2229 2230 path = svn_fs__canonicalize_abspath(path, pool); 2231 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional, 2232 txn_id, pool)); 2233 2234 /* Check (recursively) to see if some lock is 'reserving' a path at 2235 that location, or even some child-path; if so, check that we can 2236 use it. */ 2237 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS) 2238 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE, 2239 pool)); 2240 2241 /* If there's already a sub-directory by that name, complain. This 2242 also catches the case of trying to make a subdirectory named `/'. */ 2243 if (parent_path->node) 2244 return SVN_FS__ALREADY_EXISTS(root, path); 2245 2246 /* Create the subdirectory. */ 2247 SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool)); 2248 SVN_ERR(svn_fs_fs__dag_make_dir(&sub_dir, 2249 parent_path->parent->node, 2250 parent_path_path(parent_path->parent, 2251 pool), 2252 parent_path->entry, 2253 txn_id, 2254 pool)); 2255 2256 /* Add this directory to the path cache. */ 2257 SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool), 2258 sub_dir, pool)); 2259 2260 /* Make a record of this modification in the changes table. */ 2261 return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(sub_dir), 2262 svn_fs_path_change_add, FALSE, FALSE, svn_node_dir, 2263 SVN_INVALID_REVNUM, NULL, pool); 2264} 2265 2266 2267/* Delete the node at PATH under ROOT. ROOT must be a transaction 2268 root. Perform temporary allocations in POOL. */ 2269static svn_error_t * 2270fs_delete_node(svn_fs_root_t *root, 2271 const char *path, 2272 apr_pool_t *pool) 2273{ 2274 parent_path_t *parent_path; 2275 const char *txn_id = root->txn; 2276 apr_int64_t mergeinfo_count = 0; 2277 svn_node_kind_t kind; 2278 2279 if (! root->is_txn_root) 2280 return SVN_FS__NOT_TXN(root); 2281 2282 path = svn_fs__canonicalize_abspath(path, pool); 2283 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool)); 2284 kind = svn_fs_fs__dag_node_kind(parent_path->node); 2285 2286 /* We can't remove the root of the filesystem. */ 2287 if (! parent_path->parent) 2288 return svn_error_create(SVN_ERR_FS_ROOT_DIR, NULL, 2289 _("The root directory cannot be deleted")); 2290 2291 /* Check to see if path (or any child thereof) is locked; if so, 2292 check that we can use the existing lock(s). */ 2293 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS) 2294 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE, 2295 pool)); 2296 2297 /* Make the parent directory mutable, and do the deletion. */ 2298 SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool)); 2299 if (svn_fs_fs__fs_supports_mergeinfo(root->fs)) 2300 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count, 2301 parent_path->node)); 2302 SVN_ERR(svn_fs_fs__dag_delete(parent_path->parent->node, 2303 parent_path->entry, 2304 txn_id, pool)); 2305 2306 /* Remove this node and any children from the path cache. */ 2307 SVN_ERR(dag_node_cache_invalidate(root, parent_path_path(parent_path, pool), 2308 pool)); 2309 2310 /* Update mergeinfo counts for parents */ 2311 if (mergeinfo_count > 0) 2312 SVN_ERR(increment_mergeinfo_up_tree(parent_path->parent, 2313 -mergeinfo_count, 2314 pool)); 2315 2316 /* Make a record of this modification in the changes table. */ 2317 return add_change(root->fs, txn_id, path, 2318 svn_fs_fs__dag_get_id(parent_path->node), 2319 svn_fs_path_change_delete, FALSE, FALSE, kind, 2320 SVN_INVALID_REVNUM, NULL, pool); 2321} 2322 2323 2324/* Set *SAME_P to TRUE if FS1 and FS2 have the same UUID, else set to FALSE. 2325 Use POOL for temporary allocation only. 2326 Note: this code is duplicated between libsvn_fs_fs and libsvn_fs_base. */ 2327static svn_error_t * 2328fs_same_p(svn_boolean_t *same_p, 2329 svn_fs_t *fs1, 2330 svn_fs_t *fs2, 2331 apr_pool_t *pool) 2332{ 2333 *same_p = ! strcmp(fs1->uuid, fs2->uuid); 2334 return SVN_NO_ERROR; 2335} 2336 2337/* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under 2338 TO_ROOT. If PRESERVE_HISTORY is set, then the copy is recorded in 2339 the copies table. Perform temporary allocations in POOL. */ 2340static svn_error_t * 2341copy_helper(svn_fs_root_t *from_root, 2342 const char *from_path, 2343 svn_fs_root_t *to_root, 2344 const char *to_path, 2345 svn_boolean_t preserve_history, 2346 apr_pool_t *pool) 2347{ 2348 dag_node_t *from_node; 2349 parent_path_t *to_parent_path; 2350 const char *txn_id = to_root->txn; 2351 svn_boolean_t same_p; 2352 2353 /* Use an error check, not an assert, because even the caller cannot 2354 guarantee that a filesystem's UUID has not changed "on the fly". */ 2355 SVN_ERR(fs_same_p(&same_p, from_root->fs, to_root->fs, pool)); 2356 if (! same_p) 2357 return svn_error_createf 2358 (SVN_ERR_UNSUPPORTED_FEATURE, NULL, 2359 _("Cannot copy between two different filesystems ('%s' and '%s')"), 2360 from_root->fs->path, to_root->fs->path); 2361 2362 if (from_root->is_txn_root) 2363 return svn_error_create 2364 (SVN_ERR_UNSUPPORTED_FEATURE, NULL, 2365 _("Copy from mutable tree not currently supported")); 2366 2367 /* Get the NODE for FROM_PATH in FROM_ROOT.*/ 2368 SVN_ERR(get_dag(&from_node, from_root, from_path, TRUE, pool)); 2369 2370 /* Build up the parent path from TO_PATH in TO_ROOT. If the last 2371 component does not exist, it's not that big a deal. We'll just 2372 make one there. */ 2373 SVN_ERR(open_path(&to_parent_path, to_root, to_path, 2374 open_path_last_optional, txn_id, pool)); 2375 2376 /* Check to see if path (or any child thereof) is locked; if so, 2377 check that we can use the existing lock(s). */ 2378 if (to_root->txn_flags & SVN_FS_TXN_CHECK_LOCKS) 2379 SVN_ERR(svn_fs_fs__allow_locked_operation(to_path, to_root->fs, 2380 TRUE, FALSE, pool)); 2381 2382 /* If the destination node already exists as the same node as the 2383 source (in other words, this operation would result in nothing 2384 happening at all), just do nothing an return successfully, 2385 proud that you saved yourself from a tiresome task. */ 2386 if (to_parent_path->node && 2387 svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(from_node), 2388 svn_fs_fs__dag_get_id(to_parent_path->node))) 2389 return SVN_NO_ERROR; 2390 2391 if (! from_root->is_txn_root) 2392 { 2393 svn_fs_path_change_kind_t kind; 2394 dag_node_t *new_node; 2395 const char *from_canonpath; 2396 apr_int64_t mergeinfo_start; 2397 apr_int64_t mergeinfo_end; 2398 2399 /* If TO_PATH already existed prior to the copy, note that this 2400 operation is a replacement, not an addition. */ 2401 if (to_parent_path->node) 2402 { 2403 kind = svn_fs_path_change_replace; 2404 if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs)) 2405 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start, 2406 to_parent_path->node)); 2407 } 2408 else 2409 { 2410 kind = svn_fs_path_change_add; 2411 mergeinfo_start = 0; 2412 } 2413 2414 if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs)) 2415 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end, 2416 from_node)); 2417 2418 /* Make sure the target node's parents are mutable. */ 2419 SVN_ERR(make_path_mutable(to_root, to_parent_path->parent, 2420 to_path, pool)); 2421 2422 /* Canonicalize the copyfrom path. */ 2423 from_canonpath = svn_fs__canonicalize_abspath(from_path, pool); 2424 2425 SVN_ERR(svn_fs_fs__dag_copy(to_parent_path->parent->node, 2426 to_parent_path->entry, 2427 from_node, 2428 preserve_history, 2429 from_root->rev, 2430 from_canonpath, 2431 txn_id, pool)); 2432 2433 if (kind == svn_fs_path_change_replace) 2434 SVN_ERR(dag_node_cache_invalidate(to_root, 2435 parent_path_path(to_parent_path, 2436 pool), pool)); 2437 2438 if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs) 2439 && mergeinfo_start != mergeinfo_end) 2440 SVN_ERR(increment_mergeinfo_up_tree(to_parent_path->parent, 2441 mergeinfo_end - mergeinfo_start, 2442 pool)); 2443 2444 /* Make a record of this modification in the changes table. */ 2445 SVN_ERR(get_dag(&new_node, to_root, to_path, TRUE, pool)); 2446 SVN_ERR(add_change(to_root->fs, txn_id, to_path, 2447 svn_fs_fs__dag_get_id(new_node), kind, FALSE, FALSE, 2448 svn_fs_fs__dag_node_kind(from_node), 2449 from_root->rev, from_canonpath, pool)); 2450 } 2451 else 2452 { 2453 /* See IZ Issue #436 */ 2454 /* Copying from transaction roots not currently available. 2455 2456 ### cmpilato todo someday: make this not so. :-) Note that 2457 when copying from mutable trees, you have to make sure that 2458 you aren't creating a cyclic graph filesystem, and a simple 2459 referencing operation won't cut it. Currently, we should not 2460 be able to reach this clause, and the interface reports that 2461 this only works from immutable trees anyway, but JimB has 2462 stated that this requirement need not be necessary in the 2463 future. */ 2464 2465 SVN_ERR_MALFUNCTION(); 2466 } 2467 2468 return SVN_NO_ERROR; 2469} 2470 2471 2472/* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT. 2473 If FROM_PATH is a directory, copy it recursively. Temporary 2474 allocations are from POOL.*/ 2475static svn_error_t * 2476fs_copy(svn_fs_root_t *from_root, 2477 const char *from_path, 2478 svn_fs_root_t *to_root, 2479 const char *to_path, 2480 apr_pool_t *pool) 2481{ 2482 SVN_ERR(check_newline(to_path, pool)); 2483 2484 return svn_error_trace(copy_helper(from_root, 2485 svn_fs__canonicalize_abspath(from_path, 2486 pool), 2487 to_root, 2488 svn_fs__canonicalize_abspath(to_path, 2489 pool), 2490 TRUE, pool)); 2491} 2492 2493 2494/* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT. 2495 If FROM_PATH is a directory, copy it recursively. No history is 2496 preserved. Temporary allocations are from POOL. */ 2497static svn_error_t * 2498fs_revision_link(svn_fs_root_t *from_root, 2499 svn_fs_root_t *to_root, 2500 const char *path, 2501 apr_pool_t *pool) 2502{ 2503 if (! to_root->is_txn_root) 2504 return SVN_FS__NOT_TXN(to_root); 2505 2506 path = svn_fs__canonicalize_abspath(path, pool); 2507 return svn_error_trace(copy_helper(from_root, path, to_root, path, 2508 FALSE, pool)); 2509} 2510 2511 2512/* Discover the copy ancestry of PATH under ROOT. Return a relevant 2513 ancestor/revision combination in *PATH_P and *REV_P. Temporary 2514 allocations are in POOL. */ 2515static svn_error_t * 2516fs_copied_from(svn_revnum_t *rev_p, 2517 const char **path_p, 2518 svn_fs_root_t *root, 2519 const char *path, 2520 apr_pool_t *pool) 2521{ 2522 dag_node_t *node; 2523 const char *copyfrom_path, *copyfrom_str = NULL; 2524 svn_revnum_t copyfrom_rev; 2525 char *str, *buf; 2526 2527 /* Check to see if there is a cached version of this copyfrom 2528 entry. */ 2529 if (! root->is_txn_root) { 2530 fs_rev_root_data_t *frd = root->fsap_data; 2531 copyfrom_str = svn_hash_gets(frd->copyfrom_cache, path); 2532 } 2533 2534 if (copyfrom_str) 2535 { 2536 if (*copyfrom_str == 0) 2537 { 2538 /* We have a cached entry that says there is no copyfrom 2539 here. */ 2540 copyfrom_rev = SVN_INVALID_REVNUM; 2541 copyfrom_path = NULL; 2542 } 2543 else 2544 { 2545 /* Parse the copyfrom string for our cached entry. */ 2546 buf = apr_pstrdup(pool, copyfrom_str); 2547 str = svn_cstring_tokenize(" ", &buf); 2548 copyfrom_rev = SVN_STR_TO_REV(str); 2549 copyfrom_path = buf; 2550 } 2551 } 2552 else 2553 { 2554 /* There is no cached entry, look it up the old-fashioned 2555 way. */ 2556 SVN_ERR(get_dag(&node, root, path, TRUE, pool)); 2557 SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(©from_rev, node)); 2558 SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(©from_path, node)); 2559 } 2560 2561 *rev_p = copyfrom_rev; 2562 *path_p = copyfrom_path; 2563 2564 return SVN_NO_ERROR; 2565} 2566 2567 2568 2569/* Files. */ 2570 2571/* Create the empty file PATH under ROOT. Temporary allocations are 2572 in POOL. */ 2573static svn_error_t * 2574fs_make_file(svn_fs_root_t *root, 2575 const char *path, 2576 apr_pool_t *pool) 2577{ 2578 parent_path_t *parent_path; 2579 dag_node_t *child; 2580 const char *txn_id = root->txn; 2581 2582 SVN_ERR(check_newline(path, pool)); 2583 2584 path = svn_fs__canonicalize_abspath(path, pool); 2585 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional, 2586 txn_id, pool)); 2587 2588 /* If there's already a file by that name, complain. 2589 This also catches the case of trying to make a file named `/'. */ 2590 if (parent_path->node) 2591 return SVN_FS__ALREADY_EXISTS(root, path); 2592 2593 /* Check (non-recursively) to see if path is locked; if so, check 2594 that we can use it. */ 2595 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS) 2596 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE, 2597 pool)); 2598 2599 /* Create the file. */ 2600 SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool)); 2601 SVN_ERR(svn_fs_fs__dag_make_file(&child, 2602 parent_path->parent->node, 2603 parent_path_path(parent_path->parent, 2604 pool), 2605 parent_path->entry, 2606 txn_id, 2607 pool)); 2608 2609 /* Add this file to the path cache. */ 2610 SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool), child, 2611 pool)); 2612 2613 /* Make a record of this modification in the changes table. */ 2614 return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(child), 2615 svn_fs_path_change_add, TRUE, FALSE, svn_node_file, 2616 SVN_INVALID_REVNUM, NULL, pool); 2617} 2618 2619 2620/* Set *LENGTH_P to the size of the file PATH under ROOT. Temporary 2621 allocations are in POOL. */ 2622static svn_error_t * 2623fs_file_length(svn_filesize_t *length_p, 2624 svn_fs_root_t *root, 2625 const char *path, 2626 apr_pool_t *pool) 2627{ 2628 dag_node_t *file; 2629 2630 /* First create a dag_node_t from the root/path pair. */ 2631 SVN_ERR(get_dag(&file, root, path, FALSE, pool)); 2632 2633 /* Now fetch its length */ 2634 return svn_fs_fs__dag_file_length(length_p, file, pool); 2635} 2636 2637 2638/* Set *CHECKSUM to the checksum of type KIND for PATH under ROOT, or 2639 NULL if that information isn't available. Temporary allocations 2640 are from POOL. */ 2641static svn_error_t * 2642fs_file_checksum(svn_checksum_t **checksum, 2643 svn_checksum_kind_t kind, 2644 svn_fs_root_t *root, 2645 const char *path, 2646 apr_pool_t *pool) 2647{ 2648 dag_node_t *file; 2649 2650 SVN_ERR(get_dag(&file, root, path, FALSE, pool)); 2651 return svn_fs_fs__dag_file_checksum(checksum, file, kind, pool); 2652} 2653 2654 2655/* --- Machinery for svn_fs_file_contents() --- */ 2656 2657/* Set *CONTENTS to a readable stream that will return the contents of 2658 PATH under ROOT. The stream is allocated in POOL. */ 2659static svn_error_t * 2660fs_file_contents(svn_stream_t **contents, 2661 svn_fs_root_t *root, 2662 const char *path, 2663 apr_pool_t *pool) 2664{ 2665 dag_node_t *node; 2666 svn_stream_t *file_stream; 2667 2668 /* First create a dag_node_t from the root/path pair. */ 2669 SVN_ERR(get_dag(&node, root, path, FALSE, pool)); 2670 2671 /* Then create a readable stream from the dag_node_t. */ 2672 SVN_ERR(svn_fs_fs__dag_get_contents(&file_stream, node, pool)); 2673 2674 *contents = file_stream; 2675 return SVN_NO_ERROR; 2676} 2677 2678/* --- End machinery for svn_fs_file_contents() --- */ 2679 2680 2681/* --- Machinery for svn_fs_try_process_file_contents() --- */ 2682 2683static svn_error_t * 2684fs_try_process_file_contents(svn_boolean_t *success, 2685 svn_fs_root_t *root, 2686 const char *path, 2687 svn_fs_process_contents_func_t processor, 2688 void* baton, 2689 apr_pool_t *pool) 2690{ 2691 dag_node_t *node; 2692 SVN_ERR(get_dag(&node, root, path, FALSE, pool)); 2693 2694 return svn_fs_fs__dag_try_process_file_contents(success, node, 2695 processor, baton, pool); 2696} 2697 2698/* --- End machinery for svn_fs_try_process_file_contents() --- */ 2699 2700 2701/* --- Machinery for svn_fs_apply_textdelta() --- */ 2702 2703 2704/* Local baton type for all the helper functions below. */ 2705typedef struct txdelta_baton_t 2706{ 2707 /* This is the custom-built window consumer given to us by the delta 2708 library; it uniquely knows how to read data from our designated 2709 "source" stream, interpret the window, and write data to our 2710 designated "target" stream (in this case, our repos file.) */ 2711 svn_txdelta_window_handler_t interpreter; 2712 void *interpreter_baton; 2713 2714 /* The original file info */ 2715 svn_fs_root_t *root; 2716 const char *path; 2717 2718 /* Derived from the file info */ 2719 dag_node_t *node; 2720 2721 svn_stream_t *source_stream; 2722 svn_stream_t *target_stream; 2723 svn_stream_t *string_stream; 2724 svn_stringbuf_t *target_string; 2725 2726 /* MD5 digest for the base text against which a delta is to be 2727 applied, and for the resultant fulltext, respectively. Either or 2728 both may be null, in which case ignored. */ 2729 svn_checksum_t *base_checksum; 2730 svn_checksum_t *result_checksum; 2731 2732 /* Pool used by db txns */ 2733 apr_pool_t *pool; 2734 2735} txdelta_baton_t; 2736 2737 2738/* ### see comment in window_consumer() regarding this function. */ 2739 2740/* Helper function of generic type `svn_write_fn_t'. Implements a 2741 writable stream which appends to an svn_stringbuf_t. */ 2742static svn_error_t * 2743write_to_string(void *baton, const char *data, apr_size_t *len) 2744{ 2745 txdelta_baton_t *tb = (txdelta_baton_t *) baton; 2746 svn_stringbuf_appendbytes(tb->target_string, data, *len); 2747 return SVN_NO_ERROR; 2748} 2749 2750 2751 2752/* The main window handler returned by svn_fs_apply_textdelta. */ 2753static svn_error_t * 2754window_consumer(svn_txdelta_window_t *window, void *baton) 2755{ 2756 txdelta_baton_t *tb = (txdelta_baton_t *) baton; 2757 2758 /* Send the window right through to the custom window interpreter. 2759 In theory, the interpreter will then write more data to 2760 cb->target_string. */ 2761 SVN_ERR(tb->interpreter(window, tb->interpreter_baton)); 2762 2763 /* ### the write_to_string() callback for the txdelta's output stream 2764 ### should be doing all the flush determination logic, not here. 2765 ### in a drastic case, a window could generate a LOT more than the 2766 ### maximum buffer size. we want to flush to the underlying target 2767 ### stream much sooner (e.g. also in a streamy fashion). also, by 2768 ### moving this logic inside the stream, the stream becomes nice 2769 ### and encapsulated: it holds all the logic about buffering and 2770 ### flushing. 2771 ### 2772 ### further: I believe the buffering should be removed from tree.c 2773 ### the buffering should go into the target_stream itself, which 2774 ### is defined by reps-string.c. Specifically, I think the 2775 ### rep_write_contents() function will handle the buffering and 2776 ### the spill to the underlying DB. by locating it there, then 2777 ### anybody who gets a writable stream for FS content can take 2778 ### advantage of the buffering capability. this will be important 2779 ### when we export an FS API function for writing a fulltext into 2780 ### the FS, rather than forcing that fulltext thru apply_textdelta. 2781 */ 2782 2783 /* Check to see if we need to purge the portion of the contents that 2784 have been written thus far. */ 2785 if ((! window) || (tb->target_string->len > WRITE_BUFFER_SIZE)) 2786 { 2787 apr_size_t len = tb->target_string->len; 2788 SVN_ERR(svn_stream_write(tb->target_stream, 2789 tb->target_string->data, 2790 &len)); 2791 svn_stringbuf_setempty(tb->target_string); 2792 } 2793 2794 /* Is the window NULL? If so, we're done. */ 2795 if (! window) 2796 { 2797 /* Close the internal-use stream. ### This used to be inside of 2798 txn_body_fulltext_finalize_edits(), but that invoked a nested 2799 Berkeley DB transaction -- scandalous! */ 2800 SVN_ERR(svn_stream_close(tb->target_stream)); 2801 2802 SVN_ERR(svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum, 2803 tb->pool)); 2804 } 2805 2806 return SVN_NO_ERROR; 2807} 2808 2809/* Helper function for fs_apply_textdelta. BATON is of type 2810 txdelta_baton_t. */ 2811static svn_error_t * 2812apply_textdelta(void *baton, apr_pool_t *pool) 2813{ 2814 txdelta_baton_t *tb = (txdelta_baton_t *) baton; 2815 parent_path_t *parent_path; 2816 const char *txn_id = tb->root->txn; 2817 2818 /* Call open_path with no flags, as we want this to return an error 2819 if the node for which we are searching doesn't exist. */ 2820 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, pool)); 2821 2822 /* Check (non-recursively) to see if path is locked; if so, check 2823 that we can use it. */ 2824 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS) 2825 SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs, 2826 FALSE, FALSE, pool)); 2827 2828 /* Now, make sure this path is mutable. */ 2829 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool)); 2830 tb->node = parent_path->node; 2831 2832 if (tb->base_checksum) 2833 { 2834 svn_checksum_t *checksum; 2835 2836 /* Until we finalize the node, its data_key points to the old 2837 contents, in other words, the base text. */ 2838 SVN_ERR(svn_fs_fs__dag_file_checksum(&checksum, tb->node, 2839 tb->base_checksum->kind, pool)); 2840 if (!svn_checksum_match(tb->base_checksum, checksum)) 2841 return svn_checksum_mismatch_err(tb->base_checksum, checksum, pool, 2842 _("Base checksum mismatch on '%s'"), 2843 tb->path); 2844 } 2845 2846 /* Make a readable "source" stream out of the current contents of 2847 ROOT/PATH; obviously, this must done in the context of a db_txn. 2848 The stream is returned in tb->source_stream. */ 2849 SVN_ERR(svn_fs_fs__dag_get_contents(&(tb->source_stream), 2850 tb->node, tb->pool)); 2851 2852 /* Make a writable "target" stream */ 2853 SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->target_stream), tb->node, 2854 tb->pool)); 2855 2856 /* Make a writable "string" stream which writes data to 2857 tb->target_string. */ 2858 tb->target_string = svn_stringbuf_create_empty(tb->pool); 2859 tb->string_stream = svn_stream_create(tb, tb->pool); 2860 svn_stream_set_write(tb->string_stream, write_to_string); 2861 2862 /* Now, create a custom window handler that uses our two streams. */ 2863 svn_txdelta_apply(tb->source_stream, 2864 tb->string_stream, 2865 NULL, 2866 tb->path, 2867 tb->pool, 2868 &(tb->interpreter), 2869 &(tb->interpreter_baton)); 2870 2871 /* Make a record of this modification in the changes table. */ 2872 return add_change(tb->root->fs, txn_id, tb->path, 2873 svn_fs_fs__dag_get_id(tb->node), 2874 svn_fs_path_change_modify, TRUE, FALSE, svn_node_file, 2875 SVN_INVALID_REVNUM, NULL, pool); 2876} 2877 2878 2879/* Set *CONTENTS_P and *CONTENTS_BATON_P to a window handler and baton 2880 that will accept text delta windows to modify the contents of PATH 2881 under ROOT. Allocations are in POOL. */ 2882static svn_error_t * 2883fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p, 2884 void **contents_baton_p, 2885 svn_fs_root_t *root, 2886 const char *path, 2887 svn_checksum_t *base_checksum, 2888 svn_checksum_t *result_checksum, 2889 apr_pool_t *pool) 2890{ 2891 txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb)); 2892 2893 tb->root = root; 2894 tb->path = svn_fs__canonicalize_abspath(path, pool); 2895 tb->pool = pool; 2896 tb->base_checksum = svn_checksum_dup(base_checksum, pool); 2897 tb->result_checksum = svn_checksum_dup(result_checksum, pool); 2898 2899 SVN_ERR(apply_textdelta(tb, pool)); 2900 2901 *contents_p = window_consumer; 2902 *contents_baton_p = tb; 2903 return SVN_NO_ERROR; 2904} 2905 2906/* --- End machinery for svn_fs_apply_textdelta() --- */ 2907 2908/* --- Machinery for svn_fs_apply_text() --- */ 2909 2910/* Baton for svn_fs_apply_text(). */ 2911struct text_baton_t 2912{ 2913 /* The original file info */ 2914 svn_fs_root_t *root; 2915 const char *path; 2916 2917 /* Derived from the file info */ 2918 dag_node_t *node; 2919 2920 /* The returned stream that will accept the file's new contents. */ 2921 svn_stream_t *stream; 2922 2923 /* The actual fs stream that the returned stream will write to. */ 2924 svn_stream_t *file_stream; 2925 2926 /* MD5 digest for the final fulltext written to the file. May 2927 be null, in which case ignored. */ 2928 svn_checksum_t *result_checksum; 2929 2930 /* Pool used by db txns */ 2931 apr_pool_t *pool; 2932}; 2933 2934 2935/* A wrapper around svn_fs_fs__dag_finalize_edits, but for 2936 * fulltext data, not text deltas. Closes BATON->file_stream. 2937 * 2938 * Note: If you're confused about how this function relates to another 2939 * of similar name, think of it this way: 2940 * 2941 * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits() 2942 * svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits() 2943 */ 2944 2945/* Write function for the publically returned stream. */ 2946static svn_error_t * 2947text_stream_writer(void *baton, 2948 const char *data, 2949 apr_size_t *len) 2950{ 2951 struct text_baton_t *tb = baton; 2952 2953 /* Psst, here's some data. Pass it on to the -real- file stream. */ 2954 return svn_stream_write(tb->file_stream, data, len); 2955} 2956 2957/* Close function for the publically returned stream. */ 2958static svn_error_t * 2959text_stream_closer(void *baton) 2960{ 2961 struct text_baton_t *tb = baton; 2962 2963 /* Close the internal-use stream. ### This used to be inside of 2964 txn_body_fulltext_finalize_edits(), but that invoked a nested 2965 Berkeley DB transaction -- scandalous! */ 2966 SVN_ERR(svn_stream_close(tb->file_stream)); 2967 2968 /* Need to tell fs that we're done sending text */ 2969 return svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum, 2970 tb->pool); 2971} 2972 2973 2974/* Helper function for fs_apply_text. BATON is of type 2975 text_baton_t. */ 2976static svn_error_t * 2977apply_text(void *baton, apr_pool_t *pool) 2978{ 2979 struct text_baton_t *tb = baton; 2980 parent_path_t *parent_path; 2981 const char *txn_id = tb->root->txn; 2982 2983 /* Call open_path with no flags, as we want this to return an error 2984 if the node for which we are searching doesn't exist. */ 2985 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, pool)); 2986 2987 /* Check (non-recursively) to see if path is locked; if so, check 2988 that we can use it. */ 2989 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS) 2990 SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs, 2991 FALSE, FALSE, pool)); 2992 2993 /* Now, make sure this path is mutable. */ 2994 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool)); 2995 tb->node = parent_path->node; 2996 2997 /* Make a writable stream for replacing the file's text. */ 2998 SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->file_stream), tb->node, 2999 tb->pool)); 3000 3001 /* Create a 'returnable' stream which writes to the file_stream. */ 3002 tb->stream = svn_stream_create(tb, tb->pool); 3003 svn_stream_set_write(tb->stream, text_stream_writer); 3004 svn_stream_set_close(tb->stream, text_stream_closer); 3005 3006 /* Make a record of this modification in the changes table. */ 3007 return add_change(tb->root->fs, txn_id, tb->path, 3008 svn_fs_fs__dag_get_id(tb->node), 3009 svn_fs_path_change_modify, TRUE, FALSE, svn_node_file, 3010 SVN_INVALID_REVNUM, NULL, pool); 3011} 3012 3013 3014/* Return a writable stream that will set the contents of PATH under 3015 ROOT. RESULT_CHECKSUM is the MD5 checksum of the final result. 3016 Temporary allocations are in POOL. */ 3017static svn_error_t * 3018fs_apply_text(svn_stream_t **contents_p, 3019 svn_fs_root_t *root, 3020 const char *path, 3021 svn_checksum_t *result_checksum, 3022 apr_pool_t *pool) 3023{ 3024 struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb)); 3025 3026 tb->root = root; 3027 tb->path = svn_fs__canonicalize_abspath(path, pool); 3028 tb->pool = pool; 3029 tb->result_checksum = svn_checksum_dup(result_checksum, pool); 3030 3031 SVN_ERR(apply_text(tb, pool)); 3032 3033 *contents_p = tb->stream; 3034 return SVN_NO_ERROR; 3035} 3036 3037/* --- End machinery for svn_fs_apply_text() --- */ 3038 3039 3040/* Check if the contents of PATH1 under ROOT1 are different from the 3041 contents of PATH2 under ROOT2. If they are different set 3042 *CHANGED_P to TRUE, otherwise set it to FALSE. */ 3043static svn_error_t * 3044fs_contents_changed(svn_boolean_t *changed_p, 3045 svn_fs_root_t *root1, 3046 const char *path1, 3047 svn_fs_root_t *root2, 3048 const char *path2, 3049 apr_pool_t *pool) 3050{ 3051 dag_node_t *node1, *node2; 3052 3053 /* Check that roots are in the same fs. */ 3054 if (root1->fs != root2->fs) 3055 return svn_error_create 3056 (SVN_ERR_FS_GENERAL, NULL, 3057 _("Cannot compare file contents between two different filesystems")); 3058 3059 /* Check that both paths are files. */ 3060 { 3061 svn_node_kind_t kind; 3062 3063 SVN_ERR(svn_fs_fs__check_path(&kind, root1, path1, pool)); 3064 if (kind != svn_node_file) 3065 return svn_error_createf 3066 (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path1); 3067 3068 SVN_ERR(svn_fs_fs__check_path(&kind, root2, path2, pool)); 3069 if (kind != svn_node_file) 3070 return svn_error_createf 3071 (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2); 3072 } 3073 3074 SVN_ERR(get_dag(&node1, root1, path1, TRUE, pool)); 3075 SVN_ERR(get_dag(&node2, root2, path2, TRUE, pool)); 3076 return svn_fs_fs__dag_things_different(NULL, changed_p, 3077 node1, node2); 3078} 3079 3080 3081 3082/* Public interface to computing file text deltas. */ 3083 3084static svn_error_t * 3085fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p, 3086 svn_fs_root_t *source_root, 3087 const char *source_path, 3088 svn_fs_root_t *target_root, 3089 const char *target_path, 3090 apr_pool_t *pool) 3091{ 3092 dag_node_t *source_node, *target_node; 3093 3094 if (source_root && source_path) 3095 SVN_ERR(get_dag(&source_node, source_root, source_path, TRUE, pool)); 3096 else 3097 source_node = NULL; 3098 SVN_ERR(get_dag(&target_node, target_root, target_path, TRUE, pool)); 3099 3100 /* Create a delta stream that turns the source into the target. */ 3101 return svn_fs_fs__dag_get_file_delta_stream(stream_p, source_node, 3102 target_node, pool); 3103} 3104 3105 3106 3107/* Finding Changes */ 3108 3109/* Set *CHANGED_PATHS_P to a newly allocated hash containing 3110 descriptions of the paths changed under ROOT. The hash is keyed 3111 with const char * paths and has svn_fs_path_change2_t * values. Use 3112 POOL for all allocations. */ 3113static svn_error_t * 3114fs_paths_changed(apr_hash_t **changed_paths_p, 3115 svn_fs_root_t *root, 3116 apr_pool_t *pool) 3117{ 3118 if (root->is_txn_root) 3119 return svn_fs_fs__txn_changes_fetch(changed_paths_p, root->fs, root->txn, 3120 pool); 3121 else 3122 { 3123 fs_rev_root_data_t *frd = root->fsap_data; 3124 return svn_fs_fs__paths_changed(changed_paths_p, root->fs, root->rev, 3125 frd->copyfrom_cache, pool); 3126 } 3127} 3128 3129 3130 3131/* Our coolio opaque history object. */ 3132typedef struct fs_history_data_t 3133{ 3134 /* filesystem object */ 3135 svn_fs_t *fs; 3136 3137 /* path and revision of historical location */ 3138 const char *path; 3139 svn_revnum_t revision; 3140 3141 /* internal-use hints about where to resume the history search. */ 3142 const char *path_hint; 3143 svn_revnum_t rev_hint; 3144 3145 /* FALSE until the first call to svn_fs_history_prev(). */ 3146 svn_boolean_t is_interesting; 3147} fs_history_data_t; 3148 3149static svn_fs_history_t * 3150assemble_history(svn_fs_t *fs, 3151 const char *path, 3152 svn_revnum_t revision, 3153 svn_boolean_t is_interesting, 3154 const char *path_hint, 3155 svn_revnum_t rev_hint, 3156 apr_pool_t *pool); 3157 3158 3159/* Set *HISTORY_P to an opaque node history object which represents 3160 PATH under ROOT. ROOT must be a revision root. Use POOL for all 3161 allocations. */ 3162static svn_error_t * 3163fs_node_history(svn_fs_history_t **history_p, 3164 svn_fs_root_t *root, 3165 const char *path, 3166 apr_pool_t *pool) 3167{ 3168 svn_node_kind_t kind; 3169 3170 /* We require a revision root. */ 3171 if (root->is_txn_root) 3172 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL); 3173 3174 /* And we require that the path exist in the root. */ 3175 SVN_ERR(svn_fs_fs__check_path(&kind, root, path, pool)); 3176 if (kind == svn_node_none) 3177 return SVN_FS__NOT_FOUND(root, path); 3178 3179 /* Okay, all seems well. Build our history object and return it. */ 3180 *history_p = assemble_history(root->fs, 3181 svn_fs__canonicalize_abspath(path, pool), 3182 root->rev, FALSE, NULL, 3183 SVN_INVALID_REVNUM, pool); 3184 return SVN_NO_ERROR; 3185} 3186 3187/* Find the youngest copyroot for path PARENT_PATH or its parents in 3188 filesystem FS, and store the copyroot in *REV_P and *PATH_P. 3189 Perform all allocations in POOL. */ 3190static svn_error_t * 3191find_youngest_copyroot(svn_revnum_t *rev_p, 3192 const char **path_p, 3193 svn_fs_t *fs, 3194 parent_path_t *parent_path, 3195 apr_pool_t *pool) 3196{ 3197 svn_revnum_t rev_mine; 3198 svn_revnum_t rev_parent = SVN_INVALID_REVNUM; 3199 const char *path_mine; 3200 const char *path_parent = NULL; 3201 3202 /* First find our parent's youngest copyroot. */ 3203 if (parent_path->parent) 3204 SVN_ERR(find_youngest_copyroot(&rev_parent, &path_parent, fs, 3205 parent_path->parent, pool)); 3206 3207 /* Find our copyroot. */ 3208 SVN_ERR(svn_fs_fs__dag_get_copyroot(&rev_mine, &path_mine, 3209 parent_path->node)); 3210 3211 /* If a parent and child were copied to in the same revision, prefer 3212 the child copy target, since it is the copy relevant to the 3213 history of the child. */ 3214 if (rev_mine >= rev_parent) 3215 { 3216 *rev_p = rev_mine; 3217 *path_p = path_mine; 3218 } 3219 else 3220 { 3221 *rev_p = rev_parent; 3222 *path_p = path_parent; 3223 } 3224 3225 return SVN_NO_ERROR; 3226} 3227 3228 3229static svn_error_t *fs_closest_copy(svn_fs_root_t **root_p, 3230 const char **path_p, 3231 svn_fs_root_t *root, 3232 const char *path, 3233 apr_pool_t *pool) 3234{ 3235 svn_fs_t *fs = root->fs; 3236 parent_path_t *parent_path, *copy_dst_parent_path; 3237 svn_revnum_t copy_dst_rev, created_rev; 3238 const char *copy_dst_path; 3239 svn_fs_root_t *copy_dst_root; 3240 dag_node_t *copy_dst_node; 3241 svn_node_kind_t kind; 3242 3243 /* Initialize return values. */ 3244 *root_p = NULL; 3245 *path_p = NULL; 3246 3247 path = svn_fs__canonicalize_abspath(path, pool); 3248 SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool)); 3249 3250 /* Find the youngest copyroot in the path of this node-rev, which 3251 will indicate the target of the innermost copy affecting the 3252 node-rev. */ 3253 SVN_ERR(find_youngest_copyroot(©_dst_rev, ©_dst_path, 3254 fs, parent_path, pool)); 3255 if (copy_dst_rev == 0) /* There are no copies affecting this node-rev. */ 3256 return SVN_NO_ERROR; 3257 3258 /* It is possible that this node was created from scratch at some 3259 revision between COPY_DST_REV and REV. Make sure that PATH 3260 exists as of COPY_DST_REV and is related to this node-rev. */ 3261 SVN_ERR(svn_fs_fs__revision_root(©_dst_root, fs, copy_dst_rev, pool)); 3262 SVN_ERR(svn_fs_fs__check_path(&kind, copy_dst_root, path, pool)); 3263 if (kind == svn_node_none) 3264 return SVN_NO_ERROR; 3265 SVN_ERR(open_path(©_dst_parent_path, copy_dst_root, path, 3266 open_path_node_only, NULL, pool)); 3267 copy_dst_node = copy_dst_parent_path->node; 3268 if (! svn_fs_fs__id_check_related(svn_fs_fs__dag_get_id(copy_dst_node), 3269 svn_fs_fs__dag_get_id(parent_path->node))) 3270 return SVN_NO_ERROR; 3271 3272 /* One final check must be done here. If you copy a directory and 3273 create a new entity somewhere beneath that directory in the same 3274 txn, then we can't claim that the copy affected the new entity. 3275 For example, if you do: 3276 3277 copy dir1 dir2 3278 create dir2/new-thing 3279 commit 3280 3281 then dir2/new-thing was not affected by the copy of dir1 to dir2. 3282 We detect this situation by asking if PATH@COPY_DST_REV's 3283 created-rev is COPY_DST_REV, and that node-revision has no 3284 predecessors, then there is no relevant closest copy. 3285 */ 3286 SVN_ERR(svn_fs_fs__dag_get_revision(&created_rev, copy_dst_node, pool)); 3287 if (created_rev == copy_dst_rev) 3288 { 3289 const svn_fs_id_t *pred; 3290 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred, copy_dst_node)); 3291 if (! pred) 3292 return SVN_NO_ERROR; 3293 } 3294 3295 /* The copy destination checks out. Return it. */ 3296 *root_p = copy_dst_root; 3297 *path_p = copy_dst_path; 3298 return SVN_NO_ERROR; 3299} 3300 3301 3302/* Set *PREV_PATH and *PREV_REV to the path and revision which 3303 represent the location at which PATH in FS was located immediately 3304 prior to REVISION iff there was a copy operation (to PATH or one of 3305 its parent directories) between that previous location and 3306 PATH@REVISION. 3307 3308 If there was no such copy operation in that portion of PATH's 3309 history, set *PREV_PATH to NULL and *PREV_REV to SVN_INVALID_REVNUM. */ 3310static svn_error_t * 3311prev_location(const char **prev_path, 3312 svn_revnum_t *prev_rev, 3313 svn_fs_t *fs, 3314 svn_fs_root_t *root, 3315 const char *path, 3316 apr_pool_t *pool) 3317{ 3318 const char *copy_path, *copy_src_path, *remainder_path; 3319 svn_fs_root_t *copy_root; 3320 svn_revnum_t copy_src_rev; 3321 3322 /* Ask about the most recent copy which affected PATH@REVISION. If 3323 there was no such copy, we're done. */ 3324 SVN_ERR(fs_closest_copy(©_root, ©_path, root, path, pool)); 3325 if (! copy_root) 3326 { 3327 *prev_rev = SVN_INVALID_REVNUM; 3328 *prev_path = NULL; 3329 return SVN_NO_ERROR; 3330 } 3331 3332 /* Ultimately, it's not the path of the closest copy's source that 3333 we care about -- it's our own path's location in the copy source 3334 revision. So we'll tack the relative path that expresses the 3335 difference between the copy destination and our path in the copy 3336 revision onto the copy source path to determine this information. 3337 3338 In other words, if our path is "/branches/my-branch/foo/bar", and 3339 we know that the closest relevant copy was a copy of "/trunk" to 3340 "/branches/my-branch", then that relative path under the copy 3341 destination is "/foo/bar". Tacking that onto the copy source 3342 path tells us that our path was located at "/trunk/foo/bar" 3343 before the copy. 3344 */ 3345 SVN_ERR(fs_copied_from(©_src_rev, ©_src_path, 3346 copy_root, copy_path, pool)); 3347 remainder_path = svn_fspath__skip_ancestor(copy_path, path); 3348 *prev_path = svn_fspath__join(copy_src_path, remainder_path, pool); 3349 *prev_rev = copy_src_rev; 3350 return SVN_NO_ERROR; 3351} 3352 3353 3354static svn_error_t * 3355fs_node_origin_rev(svn_revnum_t *revision, 3356 svn_fs_root_t *root, 3357 const char *path, 3358 apr_pool_t *pool) 3359{ 3360 svn_fs_t *fs = root->fs; 3361 const svn_fs_id_t *given_noderev_id, *cached_origin_id; 3362 const char *node_id, *dash; 3363 3364 path = svn_fs__canonicalize_abspath(path, pool); 3365 3366 /* Check the cache first. */ 3367 SVN_ERR(svn_fs_fs__node_id(&given_noderev_id, root, path, pool)); 3368 node_id = svn_fs_fs__id_node_id(given_noderev_id); 3369 3370 /* Is it a brand new uncommitted node? */ 3371 if (node_id[0] == '_') 3372 { 3373 *revision = SVN_INVALID_REVNUM; 3374 return SVN_NO_ERROR; 3375 } 3376 3377 /* Maybe this is a new-style node ID that just has the revision 3378 sitting right in it. */ 3379 dash = strchr(node_id, '-'); 3380 if (dash && *(dash+1)) 3381 { 3382 *revision = SVN_STR_TO_REV(dash + 1); 3383 return SVN_NO_ERROR; 3384 } 3385 3386 /* The root node always has ID 0, created in revision 0 and will never 3387 use the new-style ID format. */ 3388 if (strcmp(node_id, "0") == 0) 3389 { 3390 *revision = 0; 3391 return SVN_NO_ERROR; 3392 } 3393 3394 /* OK, it's an old-style ID? Maybe it's cached. */ 3395 SVN_ERR(svn_fs_fs__get_node_origin(&cached_origin_id, 3396 fs, 3397 node_id, 3398 pool)); 3399 if (cached_origin_id != NULL) 3400 { 3401 *revision = svn_fs_fs__id_rev(cached_origin_id); 3402 return SVN_NO_ERROR; 3403 } 3404 3405 { 3406 /* Ah well, the answer isn't in the ID itself or in the cache. 3407 Let's actually calculate it, then. */ 3408 svn_fs_root_t *curroot = root; 3409 apr_pool_t *subpool = svn_pool_create(pool); 3410 apr_pool_t *predidpool = svn_pool_create(pool); 3411 svn_stringbuf_t *lastpath = svn_stringbuf_create(path, pool); 3412 svn_revnum_t lastrev = SVN_INVALID_REVNUM; 3413 dag_node_t *node; 3414 const svn_fs_id_t *pred_id; 3415 3416 /* Walk the closest-copy chain back to the first copy in our history. 3417 3418 NOTE: We merely *assume* that this is faster than walking the 3419 predecessor chain, because we *assume* that copies of parent 3420 directories happen less often than modifications to a given item. */ 3421 while (1) 3422 { 3423 svn_revnum_t currev; 3424 const char *curpath = lastpath->data; 3425 3426 svn_pool_clear(subpool); 3427 3428 /* Get a root pointing to LASTREV. (The first time around, 3429 LASTREV is invalid, but that's cool because CURROOT is 3430 already initialized.) */ 3431 if (SVN_IS_VALID_REVNUM(lastrev)) 3432 SVN_ERR(svn_fs_fs__revision_root(&curroot, fs, lastrev, subpool)); 3433 3434 /* Find the previous location using the closest-copy shortcut. */ 3435 SVN_ERR(prev_location(&curpath, &currev, fs, curroot, curpath, 3436 subpool)); 3437 if (! curpath) 3438 break; 3439 3440 /* Update our LASTPATH and LASTREV variables (which survive 3441 SUBPOOL). */ 3442 svn_stringbuf_set(lastpath, curpath); 3443 lastrev = currev; 3444 } 3445 3446 /* Walk the predecessor links back to origin. */ 3447 SVN_ERR(svn_fs_fs__node_id(&pred_id, curroot, lastpath->data, predidpool)); 3448 do 3449 { 3450 svn_pool_clear(subpool); 3451 SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, subpool)); 3452 3453 /* Why not just fetch the predecessor ID in PREDIDPOOL? 3454 Because svn_fs_fs__dag_get_predecessor_id() doesn't 3455 necessarily honor the passed-in pool, and might return a 3456 value cached in the node (which is allocated in 3457 SUBPOOL... maybe). */ 3458 svn_pool_clear(predidpool); 3459 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node)); 3460 pred_id = pred_id ? svn_fs_fs__id_copy(pred_id, predidpool) : NULL; 3461 } 3462 while (pred_id); 3463 3464 /* When we get here, NODE should be the first node-revision in our 3465 chain. */ 3466 SVN_ERR(svn_fs_fs__dag_get_revision(revision, node, pool)); 3467 3468 /* Wow, I don't want to have to do all that again. Let's cache 3469 the result. */ 3470 if (node_id[0] != '_') 3471 SVN_ERR(svn_fs_fs__set_node_origin(fs, node_id, 3472 svn_fs_fs__dag_get_id(node), pool)); 3473 3474 svn_pool_destroy(subpool); 3475 svn_pool_destroy(predidpool); 3476 return SVN_NO_ERROR; 3477 } 3478} 3479 3480 3481struct history_prev_args 3482{ 3483 svn_fs_history_t **prev_history_p; 3484 svn_fs_history_t *history; 3485 svn_boolean_t cross_copies; 3486 apr_pool_t *pool; 3487}; 3488 3489 3490static svn_error_t * 3491history_prev(void *baton, apr_pool_t *pool) 3492{ 3493 struct history_prev_args *args = baton; 3494 svn_fs_history_t **prev_history = args->prev_history_p; 3495 svn_fs_history_t *history = args->history; 3496 fs_history_data_t *fhd = history->fsap_data; 3497 const char *commit_path, *src_path, *path = fhd->path; 3498 svn_revnum_t commit_rev, src_rev, dst_rev; 3499 svn_revnum_t revision = fhd->revision; 3500 apr_pool_t *retpool = args->pool; 3501 svn_fs_t *fs = fhd->fs; 3502 parent_path_t *parent_path; 3503 dag_node_t *node; 3504 svn_fs_root_t *root; 3505 svn_boolean_t reported = fhd->is_interesting; 3506 svn_revnum_t copyroot_rev; 3507 const char *copyroot_path; 3508 3509 /* Initialize our return value. */ 3510 *prev_history = NULL; 3511 3512 /* If our last history report left us hints about where to pickup 3513 the chase, then our last report was on the destination of a 3514 copy. If we are crossing copies, start from those locations, 3515 otherwise, we're all done here. */ 3516 if (fhd->path_hint && SVN_IS_VALID_REVNUM(fhd->rev_hint)) 3517 { 3518 reported = FALSE; 3519 if (! args->cross_copies) 3520 return SVN_NO_ERROR; 3521 path = fhd->path_hint; 3522 revision = fhd->rev_hint; 3523 } 3524 3525 /* Construct a ROOT for the current revision. */ 3526 SVN_ERR(svn_fs_fs__revision_root(&root, fs, revision, pool)); 3527 3528 /* Open PATH/REVISION, and get its node and a bunch of other 3529 goodies. */ 3530 SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool)); 3531 node = parent_path->node; 3532 commit_path = svn_fs_fs__dag_get_created_path(node); 3533 SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, pool)); 3534 3535 /* The Subversion filesystem is written in such a way that a given 3536 line of history may have at most one interesting history point 3537 per filesystem revision. Either that node was edited (and 3538 possibly copied), or it was copied but not edited. And a copy 3539 source cannot be from the same revision as its destination. So, 3540 if our history revision matches its node's commit revision, we 3541 know that ... */ 3542 if (revision == commit_rev) 3543 { 3544 if (! reported) 3545 { 3546 /* ... we either have not yet reported on this revision (and 3547 need now to do so) ... */ 3548 *prev_history = assemble_history(fs, 3549 apr_pstrdup(retpool, commit_path), 3550 commit_rev, TRUE, NULL, 3551 SVN_INVALID_REVNUM, retpool); 3552 return SVN_NO_ERROR; 3553 } 3554 else 3555 { 3556 /* ... or we *have* reported on this revision, and must now 3557 progress toward this node's predecessor (unless there is 3558 no predecessor, in which case we're all done!). */ 3559 const svn_fs_id_t *pred_id; 3560 3561 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node)); 3562 if (! pred_id) 3563 return SVN_NO_ERROR; 3564 3565 /* Replace NODE and friends with the information from its 3566 predecessor. */ 3567 SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, pool)); 3568 commit_path = svn_fs_fs__dag_get_created_path(node); 3569 SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, pool)); 3570 } 3571 } 3572 3573 /* Find the youngest copyroot in the path of this node, including 3574 itself. */ 3575 SVN_ERR(find_youngest_copyroot(©root_rev, ©root_path, fs, 3576 parent_path, pool)); 3577 3578 /* Initialize some state variables. */ 3579 src_path = NULL; 3580 src_rev = SVN_INVALID_REVNUM; 3581 dst_rev = SVN_INVALID_REVNUM; 3582 3583 if (copyroot_rev > commit_rev) 3584 { 3585 const char *remainder_path; 3586 const char *copy_dst, *copy_src; 3587 svn_fs_root_t *copyroot_root; 3588 3589 SVN_ERR(svn_fs_fs__revision_root(©root_root, fs, copyroot_rev, 3590 pool)); 3591 SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, FALSE, pool)); 3592 copy_dst = svn_fs_fs__dag_get_created_path(node); 3593 3594 /* If our current path was the very destination of the copy, 3595 then our new current path will be the copy source. If our 3596 current path was instead the *child* of the destination of 3597 the copy, then figure out its previous location by taking its 3598 path relative to the copy destination and appending that to 3599 the copy source. Finally, if our current path doesn't meet 3600 one of these other criteria ... ### for now just fallback to 3601 the old copy hunt algorithm. */ 3602 remainder_path = svn_fspath__skip_ancestor(copy_dst, path); 3603 3604 if (remainder_path) 3605 { 3606 /* If we get here, then our current path is the destination 3607 of, or the child of the destination of, a copy. Fill 3608 in the return values and get outta here. */ 3609 SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&src_rev, node)); 3610 SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(©_src, node)); 3611 3612 dst_rev = copyroot_rev; 3613 src_path = svn_fspath__join(copy_src, remainder_path, pool); 3614 } 3615 } 3616 3617 /* If we calculated a copy source path and revision, we'll make a 3618 'copy-style' history object. */ 3619 if (src_path && SVN_IS_VALID_REVNUM(src_rev)) 3620 { 3621 svn_boolean_t retry = FALSE; 3622 3623 /* It's possible for us to find a copy location that is the same 3624 as the history point we've just reported. If that happens, 3625 we simply need to take another trip through this history 3626 search. */ 3627 if ((dst_rev == revision) && reported) 3628 retry = TRUE; 3629 3630 *prev_history = assemble_history(fs, apr_pstrdup(retpool, path), 3631 dst_rev, ! retry, 3632 src_path, src_rev, retpool); 3633 } 3634 else 3635 { 3636 *prev_history = assemble_history(fs, apr_pstrdup(retpool, commit_path), 3637 commit_rev, TRUE, NULL, 3638 SVN_INVALID_REVNUM, retpool); 3639 } 3640 3641 return SVN_NO_ERROR; 3642} 3643 3644 3645/* Implement svn_fs_history_prev, set *PREV_HISTORY_P to a new 3646 svn_fs_history_t object that represents the predecessory of 3647 HISTORY. If CROSS_COPIES is true, *PREV_HISTORY_P may be related 3648 only through a copy operation. Perform all allocations in POOL. */ 3649static svn_error_t * 3650fs_history_prev(svn_fs_history_t **prev_history_p, 3651 svn_fs_history_t *history, 3652 svn_boolean_t cross_copies, 3653 apr_pool_t *pool) 3654{ 3655 svn_fs_history_t *prev_history = NULL; 3656 fs_history_data_t *fhd = history->fsap_data; 3657 svn_fs_t *fs = fhd->fs; 3658 3659 /* Special case: the root directory changes in every single 3660 revision, no exceptions. And, the root can't be the target (or 3661 child of a target -- duh) of a copy. So, if that's our path, 3662 then we need only decrement our revision by 1, and there you go. */ 3663 if (strcmp(fhd->path, "/") == 0) 3664 { 3665 if (! fhd->is_interesting) 3666 prev_history = assemble_history(fs, "/", fhd->revision, 3667 1, NULL, SVN_INVALID_REVNUM, pool); 3668 else if (fhd->revision > 0) 3669 prev_history = assemble_history(fs, "/", fhd->revision - 1, 3670 1, NULL, SVN_INVALID_REVNUM, pool); 3671 } 3672 else 3673 { 3674 struct history_prev_args args; 3675 prev_history = history; 3676 3677 while (1) 3678 { 3679 args.prev_history_p = &prev_history; 3680 args.history = prev_history; 3681 args.cross_copies = cross_copies; 3682 args.pool = pool; 3683 SVN_ERR(history_prev(&args, pool)); 3684 3685 if (! prev_history) 3686 break; 3687 fhd = prev_history->fsap_data; 3688 if (fhd->is_interesting) 3689 break; 3690 } 3691 } 3692 3693 *prev_history_p = prev_history; 3694 return SVN_NO_ERROR; 3695} 3696 3697 3698/* Set *PATH and *REVISION to the path and revision for the HISTORY 3699 object. Use POOL for all allocations. */ 3700static svn_error_t * 3701fs_history_location(const char **path, 3702 svn_revnum_t *revision, 3703 svn_fs_history_t *history, 3704 apr_pool_t *pool) 3705{ 3706 fs_history_data_t *fhd = history->fsap_data; 3707 3708 *path = apr_pstrdup(pool, fhd->path); 3709 *revision = fhd->revision; 3710 return SVN_NO_ERROR; 3711} 3712 3713static history_vtable_t history_vtable = { 3714 fs_history_prev, 3715 fs_history_location 3716}; 3717 3718/* Return a new history object (marked as "interesting") for PATH and 3719 REVISION, allocated in POOL, and with its members set to the values 3720 of the parameters provided. Note that PATH and PATH_HINT are not 3721 duped into POOL -- it is the responsibility of the caller to ensure 3722 that this happens. */ 3723static svn_fs_history_t * 3724assemble_history(svn_fs_t *fs, 3725 const char *path, 3726 svn_revnum_t revision, 3727 svn_boolean_t is_interesting, 3728 const char *path_hint, 3729 svn_revnum_t rev_hint, 3730 apr_pool_t *pool) 3731{ 3732 svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history)); 3733 fs_history_data_t *fhd = apr_pcalloc(pool, sizeof(*fhd)); 3734 fhd->path = svn_fs__canonicalize_abspath(path, pool); 3735 fhd->revision = revision; 3736 fhd->is_interesting = is_interesting; 3737 fhd->path_hint = path_hint; 3738 fhd->rev_hint = rev_hint; 3739 fhd->fs = fs; 3740 3741 history->vtable = &history_vtable; 3742 history->fsap_data = fhd; 3743 return history; 3744} 3745 3746 3747/* mergeinfo queries */ 3748 3749 3750/* DIR_DAG is a directory DAG node which has mergeinfo in its 3751 descendants. This function iterates over its children. For each 3752 child with immediate mergeinfo, it adds its mergeinfo to 3753 RESULT_CATALOG. appropriate arguments. For each child with 3754 descendants with mergeinfo, it recurses. Note that it does *not* 3755 call the action on the path for DIR_DAG itself. 3756 3757 POOL is used for temporary allocations, including the mergeinfo 3758 hashes passed to actions; RESULT_POOL is used for the mergeinfo added 3759 to RESULT_CATALOG. 3760 */ 3761static svn_error_t * 3762crawl_directory_dag_for_mergeinfo(svn_fs_root_t *root, 3763 const char *this_path, 3764 dag_node_t *dir_dag, 3765 svn_mergeinfo_catalog_t result_catalog, 3766 apr_pool_t *result_pool, 3767 apr_pool_t *scratch_pool) 3768{ 3769 apr_hash_t *entries; 3770 apr_hash_index_t *hi; 3771 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 3772 3773 SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, dir_dag, 3774 scratch_pool)); 3775 3776 for (hi = apr_hash_first(scratch_pool, entries); 3777 hi; 3778 hi = apr_hash_next(hi)) 3779 { 3780 svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi); 3781 const char *kid_path; 3782 dag_node_t *kid_dag; 3783 svn_boolean_t has_mergeinfo, go_down; 3784 3785 svn_pool_clear(iterpool); 3786 3787 kid_path = svn_fspath__join(this_path, dirent->name, iterpool); 3788 SVN_ERR(get_dag(&kid_dag, root, kid_path, TRUE, iterpool)); 3789 3790 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, kid_dag)); 3791 SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down, kid_dag)); 3792 3793 if (has_mergeinfo) 3794 { 3795 /* Save this particular node's mergeinfo. */ 3796 apr_hash_t *proplist; 3797 svn_mergeinfo_t kid_mergeinfo; 3798 svn_string_t *mergeinfo_string; 3799 svn_error_t *err; 3800 3801 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, kid_dag, iterpool)); 3802 mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO); 3803 if (!mergeinfo_string) 3804 { 3805 svn_string_t *idstr = svn_fs_fs__id_unparse(dirent->id, iterpool); 3806 return svn_error_createf 3807 (SVN_ERR_FS_CORRUPT, NULL, 3808 _("Node-revision #'%s' claims to have mergeinfo but doesn't"), 3809 idstr->data); 3810 } 3811 3812 /* Issue #3896: If a node has syntactically invalid mergeinfo, then 3813 treat it as if no mergeinfo is present rather than raising a parse 3814 error. */ 3815 err = svn_mergeinfo_parse(&kid_mergeinfo, 3816 mergeinfo_string->data, 3817 result_pool); 3818 if (err) 3819 { 3820 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR) 3821 svn_error_clear(err); 3822 else 3823 return svn_error_trace(err); 3824 } 3825 else 3826 { 3827 svn_hash_sets(result_catalog, apr_pstrdup(result_pool, kid_path), 3828 kid_mergeinfo); 3829 } 3830 } 3831 3832 if (go_down) 3833 SVN_ERR(crawl_directory_dag_for_mergeinfo(root, 3834 kid_path, 3835 kid_dag, 3836 result_catalog, 3837 result_pool, 3838 iterpool)); 3839 } 3840 3841 svn_pool_destroy(iterpool); 3842 return SVN_NO_ERROR; 3843} 3844 3845/* Return the cache key as a combination of REV_ROOT->REV, the inheritance 3846 flags INHERIT and ADJUST_INHERITED_MERGEINFO, and the PATH. The result 3847 will be allocated in POOL.. 3848 */ 3849static const char * 3850mergeinfo_cache_key(const char *path, 3851 svn_fs_root_t *rev_root, 3852 svn_mergeinfo_inheritance_t inherit, 3853 svn_boolean_t adjust_inherited_mergeinfo, 3854 apr_pool_t *pool) 3855{ 3856 apr_int64_t number = rev_root->rev; 3857 number = number * 4 3858 + (inherit == svn_mergeinfo_nearest_ancestor ? 2 : 0) 3859 + (adjust_inherited_mergeinfo ? 1 : 0); 3860 3861 return svn_fs_fs__combine_number_and_string(number, path, pool); 3862} 3863 3864/* Calculates the mergeinfo for PATH under REV_ROOT using inheritance 3865 type INHERIT. Returns it in *MERGEINFO, or NULL if there is none. 3866 The result is allocated in RESULT_POOL; SCRATCH_POOL is 3867 used for temporary allocations. 3868 */ 3869static svn_error_t * 3870get_mergeinfo_for_path_internal(svn_mergeinfo_t *mergeinfo, 3871 svn_fs_root_t *rev_root, 3872 const char *path, 3873 svn_mergeinfo_inheritance_t inherit, 3874 svn_boolean_t adjust_inherited_mergeinfo, 3875 apr_pool_t *result_pool, 3876 apr_pool_t *scratch_pool) 3877{ 3878 parent_path_t *parent_path, *nearest_ancestor; 3879 apr_hash_t *proplist; 3880 svn_string_t *mergeinfo_string; 3881 3882 path = svn_fs__canonicalize_abspath(path, scratch_pool); 3883 3884 SVN_ERR(open_path(&parent_path, rev_root, path, 0, NULL, scratch_pool)); 3885 3886 if (inherit == svn_mergeinfo_nearest_ancestor && ! parent_path->parent) 3887 return SVN_NO_ERROR; 3888 3889 if (inherit == svn_mergeinfo_nearest_ancestor) 3890 nearest_ancestor = parent_path->parent; 3891 else 3892 nearest_ancestor = parent_path; 3893 3894 while (TRUE) 3895 { 3896 svn_boolean_t has_mergeinfo; 3897 3898 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, 3899 nearest_ancestor->node)); 3900 if (has_mergeinfo) 3901 break; 3902 3903 /* No need to loop if we're looking for explicit mergeinfo. */ 3904 if (inherit == svn_mergeinfo_explicit) 3905 { 3906 return SVN_NO_ERROR; 3907 } 3908 3909 nearest_ancestor = nearest_ancestor->parent; 3910 3911 /* Run out? There's no mergeinfo. */ 3912 if (!nearest_ancestor) 3913 { 3914 return SVN_NO_ERROR; 3915 } 3916 } 3917 3918 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, nearest_ancestor->node, 3919 scratch_pool)); 3920 mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO); 3921 if (!mergeinfo_string) 3922 return svn_error_createf 3923 (SVN_ERR_FS_CORRUPT, NULL, 3924 _("Node-revision '%s@%ld' claims to have mergeinfo but doesn't"), 3925 parent_path_path(nearest_ancestor, scratch_pool), rev_root->rev); 3926 3927 /* Parse the mergeinfo; store the result in *MERGEINFO. */ 3928 { 3929 /* Issue #3896: If a node has syntactically invalid mergeinfo, then 3930 treat it as if no mergeinfo is present rather than raising a parse 3931 error. */ 3932 svn_error_t *err = svn_mergeinfo_parse(mergeinfo, 3933 mergeinfo_string->data, 3934 result_pool); 3935 if (err) 3936 { 3937 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR) 3938 { 3939 svn_error_clear(err); 3940 err = NULL; 3941 *mergeinfo = NULL; 3942 } 3943 return svn_error_trace(err); 3944 } 3945 } 3946 3947 /* If our nearest ancestor is the very path we inquired about, we 3948 can return the mergeinfo results directly. Otherwise, we're 3949 inheriting the mergeinfo, so we need to a) remove non-inheritable 3950 ranges and b) telescope the merged-from paths. */ 3951 if (adjust_inherited_mergeinfo && (nearest_ancestor != parent_path)) 3952 { 3953 svn_mergeinfo_t tmp_mergeinfo; 3954 3955 SVN_ERR(svn_mergeinfo_inheritable2(&tmp_mergeinfo, *mergeinfo, 3956 NULL, SVN_INVALID_REVNUM, 3957 SVN_INVALID_REVNUM, TRUE, 3958 scratch_pool, scratch_pool)); 3959 SVN_ERR(svn_fs__append_to_merged_froms(mergeinfo, tmp_mergeinfo, 3960 parent_path_relpath( 3961 parent_path, nearest_ancestor, 3962 scratch_pool), 3963 result_pool)); 3964 } 3965 3966 return SVN_NO_ERROR; 3967} 3968 3969/* Caching wrapper around get_mergeinfo_for_path_internal(). 3970 */ 3971static svn_error_t * 3972get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo, 3973 svn_fs_root_t *rev_root, 3974 const char *path, 3975 svn_mergeinfo_inheritance_t inherit, 3976 svn_boolean_t adjust_inherited_mergeinfo, 3977 apr_pool_t *result_pool, 3978 apr_pool_t *scratch_pool) 3979{ 3980 fs_fs_data_t *ffd = rev_root->fs->fsap_data; 3981 const char *cache_key; 3982 svn_boolean_t found = FALSE; 3983 svn_stringbuf_t *mergeinfo_exists; 3984 3985 *mergeinfo = NULL; 3986 3987 cache_key = mergeinfo_cache_key(path, rev_root, inherit, 3988 adjust_inherited_mergeinfo, scratch_pool); 3989 if (ffd->mergeinfo_existence_cache) 3990 { 3991 SVN_ERR(svn_cache__get((void **)&mergeinfo_exists, &found, 3992 ffd->mergeinfo_existence_cache, 3993 cache_key, result_pool)); 3994 if (found && mergeinfo_exists->data[0] == '1') 3995 SVN_ERR(svn_cache__get((void **)mergeinfo, &found, 3996 ffd->mergeinfo_cache, 3997 cache_key, result_pool)); 3998 } 3999 4000 if (! found) 4001 { 4002 SVN_ERR(get_mergeinfo_for_path_internal(mergeinfo, rev_root, path, 4003 inherit, 4004 adjust_inherited_mergeinfo, 4005 result_pool, scratch_pool)); 4006 if (ffd->mergeinfo_existence_cache) 4007 { 4008 mergeinfo_exists = svn_stringbuf_create(*mergeinfo ? "1" : "0", 4009 scratch_pool); 4010 SVN_ERR(svn_cache__set(ffd->mergeinfo_existence_cache, 4011 cache_key, mergeinfo_exists, scratch_pool)); 4012 if (*mergeinfo) 4013 SVN_ERR(svn_cache__set(ffd->mergeinfo_cache, 4014 cache_key, *mergeinfo, scratch_pool)); 4015 } 4016 } 4017 4018 return SVN_NO_ERROR; 4019} 4020 4021/* Adds mergeinfo for each descendant of PATH (but not PATH itself) 4022 under ROOT to RESULT_CATALOG. Returned values are allocated in 4023 RESULT_POOL; temporary values in POOL. */ 4024static svn_error_t * 4025add_descendant_mergeinfo(svn_mergeinfo_catalog_t result_catalog, 4026 svn_fs_root_t *root, 4027 const char *path, 4028 apr_pool_t *result_pool, 4029 apr_pool_t *scratch_pool) 4030{ 4031 dag_node_t *this_dag; 4032 svn_boolean_t go_down; 4033 4034 SVN_ERR(get_dag(&this_dag, root, path, TRUE, scratch_pool)); 4035 SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down, 4036 this_dag)); 4037 if (go_down) 4038 SVN_ERR(crawl_directory_dag_for_mergeinfo(root, 4039 path, 4040 this_dag, 4041 result_catalog, 4042 result_pool, 4043 scratch_pool)); 4044 return SVN_NO_ERROR; 4045} 4046 4047 4048/* Get the mergeinfo for a set of paths, returned in 4049 *MERGEINFO_CATALOG. Returned values are allocated in 4050 POOL, while temporary values are allocated in a sub-pool. */ 4051static svn_error_t * 4052get_mergeinfos_for_paths(svn_fs_root_t *root, 4053 svn_mergeinfo_catalog_t *mergeinfo_catalog, 4054 const apr_array_header_t *paths, 4055 svn_mergeinfo_inheritance_t inherit, 4056 svn_boolean_t include_descendants, 4057 svn_boolean_t adjust_inherited_mergeinfo, 4058 apr_pool_t *result_pool, 4059 apr_pool_t *scratch_pool) 4060{ 4061 svn_mergeinfo_catalog_t result_catalog = svn_hash__make(result_pool); 4062 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 4063 int i; 4064 4065 for (i = 0; i < paths->nelts; i++) 4066 { 4067 svn_error_t *err; 4068 svn_mergeinfo_t path_mergeinfo; 4069 const char *path = APR_ARRAY_IDX(paths, i, const char *); 4070 4071 svn_pool_clear(iterpool); 4072 4073 err = get_mergeinfo_for_path(&path_mergeinfo, root, path, 4074 inherit, adjust_inherited_mergeinfo, 4075 result_pool, iterpool); 4076 if (err) 4077 { 4078 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR) 4079 { 4080 svn_error_clear(err); 4081 err = NULL; 4082 path_mergeinfo = NULL; 4083 } 4084 else 4085 { 4086 return svn_error_trace(err); 4087 } 4088 } 4089 4090 if (path_mergeinfo) 4091 svn_hash_sets(result_catalog, path, path_mergeinfo); 4092 if (include_descendants) 4093 SVN_ERR(add_descendant_mergeinfo(result_catalog, root, path, 4094 result_pool, scratch_pool)); 4095 } 4096 svn_pool_destroy(iterpool); 4097 4098 *mergeinfo_catalog = result_catalog; 4099 return SVN_NO_ERROR; 4100} 4101 4102 4103/* Implements svn_fs_get_mergeinfo. */ 4104static svn_error_t * 4105fs_get_mergeinfo(svn_mergeinfo_catalog_t *catalog, 4106 svn_fs_root_t *root, 4107 const apr_array_header_t *paths, 4108 svn_mergeinfo_inheritance_t inherit, 4109 svn_boolean_t include_descendants, 4110 svn_boolean_t adjust_inherited_mergeinfo, 4111 apr_pool_t *result_pool, 4112 apr_pool_t *scratch_pool) 4113{ 4114 fs_fs_data_t *ffd = root->fs->fsap_data; 4115 4116 /* We require a revision root. */ 4117 if (root->is_txn_root) 4118 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL); 4119 4120 /* We have to actually be able to find the mergeinfo metadata! */ 4121 if (! svn_fs_fs__fs_supports_mergeinfo(root->fs)) 4122 return svn_error_createf 4123 (SVN_ERR_UNSUPPORTED_FEATURE, NULL, 4124 _("Querying mergeinfo requires version %d of the FSFS filesystem " 4125 "schema; filesystem '%s' uses only version %d"), 4126 SVN_FS_FS__MIN_MERGEINFO_FORMAT, root->fs->path, ffd->format); 4127 4128 /* Retrieve a path -> mergeinfo hash mapping. */ 4129 return get_mergeinfos_for_paths(root, catalog, paths, 4130 inherit, 4131 include_descendants, 4132 adjust_inherited_mergeinfo, 4133 result_pool, scratch_pool); 4134} 4135 4136 4137/* The vtable associated with root objects. */ 4138static root_vtable_t root_vtable = { 4139 fs_paths_changed, 4140 svn_fs_fs__check_path, 4141 fs_node_history, 4142 svn_fs_fs__node_id, 4143 svn_fs_fs__node_created_rev, 4144 fs_node_origin_rev, 4145 fs_node_created_path, 4146 fs_delete_node, 4147 fs_copied_from, 4148 fs_closest_copy, 4149 fs_node_prop, 4150 fs_node_proplist, 4151 fs_change_node_prop, 4152 fs_props_changed, 4153 fs_dir_entries, 4154 fs_make_dir, 4155 fs_copy, 4156 fs_revision_link, 4157 fs_file_length, 4158 fs_file_checksum, 4159 fs_file_contents, 4160 fs_try_process_file_contents, 4161 fs_make_file, 4162 fs_apply_textdelta, 4163 fs_apply_text, 4164 fs_contents_changed, 4165 fs_get_file_delta_stream, 4166 fs_merge, 4167 fs_get_mergeinfo, 4168}; 4169 4170/* Construct a new root object in FS, allocated from POOL. */ 4171static svn_fs_root_t * 4172make_root(svn_fs_t *fs, 4173 apr_pool_t *pool) 4174{ 4175 svn_fs_root_t *root = apr_pcalloc(pool, sizeof(*root)); 4176 4177 root->fs = fs; 4178 root->pool = pool; 4179 root->vtable = &root_vtable; 4180 4181 return root; 4182} 4183 4184 4185/* Construct a root object referring to the root of REVISION in FS, 4186 whose root directory is ROOT_DIR. Create the new root in POOL. */ 4187static svn_fs_root_t * 4188make_revision_root(svn_fs_t *fs, 4189 svn_revnum_t rev, 4190 dag_node_t *root_dir, 4191 apr_pool_t *pool) 4192{ 4193 svn_fs_root_t *root = make_root(fs, pool); 4194 fs_rev_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd)); 4195 4196 root->is_txn_root = FALSE; 4197 root->rev = rev; 4198 4199 frd->root_dir = root_dir; 4200 frd->copyfrom_cache = svn_hash__make(root->pool); 4201 4202 root->fsap_data = frd; 4203 4204 return root; 4205} 4206 4207 4208/* Construct a root object referring to the root of the transaction 4209 named TXN and based on revision BASE_REV in FS, with FLAGS to 4210 describe transaction's behavior. Create the new root in POOL. */ 4211static svn_error_t * 4212make_txn_root(svn_fs_root_t **root_p, 4213 svn_fs_t *fs, 4214 const char *txn, 4215 svn_revnum_t base_rev, 4216 apr_uint32_t flags, 4217 apr_pool_t *pool) 4218{ 4219 svn_fs_root_t *root = make_root(fs, pool); 4220 fs_txn_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd)); 4221 4222 root->is_txn_root = TRUE; 4223 root->txn = apr_pstrdup(root->pool, txn); 4224 root->txn_flags = flags; 4225 root->rev = base_rev; 4226 4227 frd->txn_id = txn; 4228 4229 /* Because this cache actually tries to invalidate elements, keep 4230 the number of elements per page down. 4231 4232 Note that since dag_node_cache_invalidate uses svn_cache__iter, 4233 this *cannot* be a memcache-based cache. */ 4234 SVN_ERR(svn_cache__create_inprocess(&(frd->txn_node_cache), 4235 svn_fs_fs__dag_serialize, 4236 svn_fs_fs__dag_deserialize, 4237 APR_HASH_KEY_STRING, 4238 32, 20, FALSE, 4239 apr_pstrcat(pool, txn, ":TXN", 4240 (char *)NULL), 4241 root->pool)); 4242 4243 /* Initialize transaction-local caches in FS. 4244 4245 Note that we cannot put those caches in frd because that content 4246 fs root object is not available where we would need it. */ 4247 SVN_ERR(svn_fs_fs__initialize_txn_caches(fs, txn, pool)); 4248 4249 root->fsap_data = frd; 4250 4251 *root_p = root; 4252 return SVN_NO_ERROR; 4253} 4254 4255 4256 4257/* Verify. */ 4258static APR_INLINE const char * 4259stringify_node(dag_node_t *node, 4260 apr_pool_t *pool) 4261{ 4262 /* ### TODO: print some PATH@REV to it, too. */ 4263 return svn_fs_fs__id_unparse(svn_fs_fs__dag_get_id(node), pool)->data; 4264} 4265 4266/* Check metadata sanity on NODE, and on its children. Manually verify 4267 information for DAG nodes in revision REV, and trust the metadata 4268 accuracy for nodes belonging to older revisions. */ 4269static svn_error_t * 4270verify_node(dag_node_t *node, 4271 svn_revnum_t rev, 4272 apr_pool_t *pool) 4273{ 4274 svn_boolean_t has_mergeinfo; 4275 apr_int64_t mergeinfo_count; 4276 const svn_fs_id_t *pred_id; 4277 svn_fs_t *fs = svn_fs_fs__dag_get_fs(node); 4278 int pred_count; 4279 svn_node_kind_t kind; 4280 apr_pool_t *iterpool = svn_pool_create(pool); 4281 4282 /* Fetch some data. */ 4283 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, node)); 4284 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count, node)); 4285 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node)); 4286 SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_count, node)); 4287 kind = svn_fs_fs__dag_node_kind(node); 4288 4289 /* Sanity check. */ 4290 if (mergeinfo_count < 0) 4291 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 4292 "Negative mergeinfo-count %" APR_INT64_T_FMT 4293 " on node '%s'", 4294 mergeinfo_count, stringify_node(node, iterpool)); 4295 4296 /* Issue #4129. (This check will explicitly catch non-root instances too.) */ 4297 if (pred_id) 4298 { 4299 dag_node_t *pred; 4300 int pred_pred_count; 4301 SVN_ERR(svn_fs_fs__dag_get_node(&pred, fs, pred_id, iterpool)); 4302 SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_pred_count, pred)); 4303 if (pred_pred_count+1 != pred_count) 4304 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 4305 "Predecessor count mismatch: " 4306 "%s has %d, but %s has %d", 4307 stringify_node(node, iterpool), pred_count, 4308 stringify_node(pred, iterpool), 4309 pred_pred_count); 4310 } 4311 4312 /* Kind-dependent verifications. */ 4313 if (kind == svn_node_none) 4314 { 4315 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 4316 "Node '%s' has kind 'none'", 4317 stringify_node(node, iterpool)); 4318 } 4319 if (kind == svn_node_file) 4320 { 4321 if (has_mergeinfo != mergeinfo_count) /* comparing int to bool */ 4322 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 4323 "File node '%s' has inconsistent mergeinfo: " 4324 "has_mergeinfo=%d, " 4325 "mergeinfo_count=%" APR_INT64_T_FMT, 4326 stringify_node(node, iterpool), 4327 has_mergeinfo, mergeinfo_count); 4328 } 4329 if (kind == svn_node_dir) 4330 { 4331 apr_hash_t *entries; 4332 apr_hash_index_t *hi; 4333 apr_int64_t children_mergeinfo = 0; 4334 4335 SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool)); 4336 4337 /* Compute CHILDREN_MERGEINFO. */ 4338 for (hi = apr_hash_first(pool, entries); 4339 hi; 4340 hi = apr_hash_next(hi)) 4341 { 4342 svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi); 4343 dag_node_t *child; 4344 svn_revnum_t child_rev; 4345 apr_int64_t child_mergeinfo; 4346 4347 svn_pool_clear(iterpool); 4348 4349 /* Compute CHILD_REV. */ 4350 SVN_ERR(svn_fs_fs__dag_get_node(&child, fs, dirent->id, iterpool)); 4351 SVN_ERR(svn_fs_fs__dag_get_revision(&child_rev, child, iterpool)); 4352 4353 if (child_rev == rev) 4354 SVN_ERR(verify_node(child, rev, iterpool)); 4355 4356 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo, child)); 4357 children_mergeinfo += child_mergeinfo; 4358 } 4359 4360 /* Side-effect of issue #4129. */ 4361 if (children_mergeinfo+has_mergeinfo != mergeinfo_count) 4362 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 4363 "Mergeinfo-count discrepancy on '%s': " 4364 "expected %" APR_INT64_T_FMT "+%d, " 4365 "counted %" APR_INT64_T_FMT, 4366 stringify_node(node, iterpool), 4367 mergeinfo_count, has_mergeinfo, 4368 children_mergeinfo); 4369 } 4370 4371 svn_pool_destroy(iterpool); 4372 return SVN_NO_ERROR; 4373} 4374 4375svn_error_t * 4376svn_fs_fs__verify_root(svn_fs_root_t *root, 4377 apr_pool_t *pool) 4378{ 4379 svn_fs_t *fs = root->fs; 4380 dag_node_t *root_dir; 4381 4382 /* Issue #4129: bogus pred-counts and minfo-cnt's on the root node-rev 4383 (and elsewhere). This code makes more thorough checks than the 4384 commit-time checks in validate_root_noderev(). */ 4385 4386 /* Callers should disable caches by setting SVN_FS_CONFIG_FSFS_CACHE_NS; 4387 see r1462436. 4388 4389 When this code is called in the library, we want to ensure we 4390 use the on-disk data --- rather than some data that was read 4391 in the possibly-distance past and cached since. */ 4392 4393 if (root->is_txn_root) 4394 { 4395 fs_txn_root_data_t *frd = root->fsap_data; 4396 SVN_ERR(svn_fs_fs__dag_txn_root(&root_dir, fs, frd->txn_id, pool)); 4397 } 4398 else 4399 { 4400 fs_rev_root_data_t *frd = root->fsap_data; 4401 root_dir = frd->root_dir; 4402 } 4403 4404 /* Recursively verify ROOT_DIR. */ 4405 SVN_ERR(verify_node(root_dir, root->rev, pool)); 4406 4407 /* Verify explicitly the predecessor of the root. */ 4408 { 4409 const svn_fs_id_t *pred_id; 4410 4411 /* Only r0 should have no predecessor. */ 4412 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, root_dir)); 4413 if (! root->is_txn_root && !!pred_id != !!root->rev) 4414 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 4415 "r%ld's root node's predecessor is " 4416 "unexpectedly '%s'", 4417 root->rev, 4418 (pred_id 4419 ? svn_fs_fs__id_unparse(pred_id, pool)->data 4420 : "(null)")); 4421 if (root->is_txn_root && !pred_id) 4422 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 4423 "Transaction '%s''s root node's predecessor is " 4424 "unexpectedly NULL", 4425 root->txn); 4426 4427 /* Check the predecessor's revision. */ 4428 if (pred_id) 4429 { 4430 svn_revnum_t pred_rev = svn_fs_fs__id_rev(pred_id); 4431 if (! root->is_txn_root && pred_rev+1 != root->rev) 4432 /* Issue #4129. */ 4433 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 4434 "r%ld's root node's predecessor is r%ld" 4435 " but should be r%ld", 4436 root->rev, pred_rev, root->rev - 1); 4437 if (root->is_txn_root && pred_rev != root->rev) 4438 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 4439 "Transaction '%s''s root node's predecessor" 4440 " is r%ld" 4441 " but should be r%ld", 4442 root->txn, pred_rev, root->rev); 4443 } 4444 } 4445 4446 return SVN_NO_ERROR; 4447} 4448