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