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