dag.c revision 362181
1/* dag.c : DAG-like interface filesystem, private to libsvn_fs 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#include <string.h> 24 25#include "svn_path.h" 26#include "svn_error.h" 27#include "svn_fs.h" 28#include "svn_props.h" 29#include "svn_pools.h" 30 31#include "cached_data.h" 32#include "dag.h" 33#include "fs.h" 34#include "fs_fs.h" 35#include "id.h" 36#include "transaction.h" 37 38#include "../libsvn_fs/fs-loader.h" 39 40#include "private/svn_fspath.h" 41#include "svn_private_config.h" 42#include "private/svn_temp_serializer.h" 43#include "temp_serializer.h" 44 45 46/* Initializing a filesystem. */ 47 48struct dag_node_t 49{ 50 /* The filesystem this dag node came from. */ 51 svn_fs_t *fs; 52 53 /* The node revision ID for this dag node, allocated in POOL. */ 54 svn_fs_id_t *id; 55 56 /* In the special case that this node is the root of a transaction 57 that has not yet been modified, the node revision ID for this dag 58 node's predecessor; otherwise NULL. (Used in 59 svn_fs_node_created_rev.) */ 60 const svn_fs_id_t *fresh_root_predecessor_id; 61 62 /* The node's type (file, dir, etc.) */ 63 svn_node_kind_t kind; 64 65 /* The node's NODE-REVISION, or NULL if we haven't read it in yet. 66 This is allocated in this node's POOL. 67 68 If you're willing to respect all the rules above, you can munge 69 this yourself, but you're probably better off just calling 70 `get_node_revision' and `set_node_revision', which take care of 71 things for you. */ 72 node_revision_t *node_revision; 73 74 /* The pool to allocate NODE_REVISION in. */ 75 apr_pool_t *node_pool; 76 77 /* the path at which this node was created. */ 78 const char *created_path; 79}; 80 81 82 83/* Trivial helper/accessor functions. */ 84svn_node_kind_t svn_fs_fs__dag_node_kind(dag_node_t *node) 85{ 86 return node->kind; 87} 88 89 90const svn_fs_id_t * 91svn_fs_fs__dag_get_id(const dag_node_t *node) 92{ 93 return node->id; 94} 95 96 97const char * 98svn_fs_fs__dag_get_created_path(dag_node_t *node) 99{ 100 return node->created_path; 101} 102 103 104svn_fs_t * 105svn_fs_fs__dag_get_fs(dag_node_t *node) 106{ 107 return node->fs; 108} 109 110void 111svn_fs_fs__dag_set_fs(dag_node_t *node, svn_fs_t *fs) 112{ 113 node->fs = fs; 114} 115 116 117/* Dup NODEREV and all associated data into POOL. 118 Leaves the id and is_fresh_txn_root fields as zero bytes. */ 119static node_revision_t * 120copy_node_revision(node_revision_t *noderev, 121 apr_pool_t *pool) 122{ 123 node_revision_t *nr = apr_pcalloc(pool, sizeof(*nr)); 124 nr->kind = noderev->kind; 125 if (noderev->predecessor_id) 126 nr->predecessor_id = svn_fs_fs__id_copy(noderev->predecessor_id, pool); 127 nr->predecessor_count = noderev->predecessor_count; 128 if (noderev->copyfrom_path) 129 nr->copyfrom_path = apr_pstrdup(pool, noderev->copyfrom_path); 130 nr->copyfrom_rev = noderev->copyfrom_rev; 131 nr->copyroot_path = apr_pstrdup(pool, noderev->copyroot_path); 132 nr->copyroot_rev = noderev->copyroot_rev; 133 nr->data_rep = svn_fs_fs__rep_copy(noderev->data_rep, pool); 134 nr->prop_rep = svn_fs_fs__rep_copy(noderev->prop_rep, pool); 135 nr->mergeinfo_count = noderev->mergeinfo_count; 136 nr->has_mergeinfo = noderev->has_mergeinfo; 137 138 if (noderev->created_path) 139 nr->created_path = apr_pstrdup(pool, noderev->created_path); 140 return nr; 141} 142 143 144/* Set *NODEREV_P to the cached node-revision for NODE. 145 If the node-revision was not already cached in NODE, read it in, 146 allocating the cache in NODE->NODE_POOL. 147 148 If you plan to change the contents of NODE, be careful! We're 149 handing you a pointer directly to our cached node-revision, not 150 your own copy. If you change it as part of some operation, but 151 then some Berkeley DB function deadlocks or gets an error, you'll 152 need to back out your changes, or else the cache will reflect 153 changes that never got committed. It's probably best not to change 154 the structure at all. */ 155static svn_error_t * 156get_node_revision(node_revision_t **noderev_p, 157 dag_node_t *node) 158{ 159 /* If we've already got a copy, there's no need to read it in. */ 160 if (! node->node_revision) 161 { 162 node_revision_t *noderev; 163 apr_pool_t *scratch_pool = svn_pool_create(node->node_pool); 164 165 SVN_ERR(svn_fs_fs__get_node_revision(&noderev, node->fs, 166 node->id, node->node_pool, 167 scratch_pool)); 168 node->node_revision = noderev; 169 svn_pool_destroy(scratch_pool); 170 } 171 172 /* Now NODE->node_revision is set. */ 173 *noderev_p = node->node_revision; 174 return SVN_NO_ERROR; 175} 176 177 178svn_boolean_t svn_fs_fs__dag_check_mutable(const dag_node_t *node) 179{ 180 return svn_fs_fs__id_is_txn(svn_fs_fs__dag_get_id(node)); 181} 182 183 184svn_error_t * 185svn_fs_fs__dag_get_node(dag_node_t **node, 186 svn_fs_t *fs, 187 const svn_fs_id_t *id, 188 apr_pool_t *pool) 189{ 190 dag_node_t *new_node; 191 node_revision_t *noderev; 192 193 /* Construct the node. */ 194 new_node = apr_pcalloc(pool, sizeof(*new_node)); 195 new_node->fs = fs; 196 new_node->id = svn_fs_fs__id_copy(id, pool); 197 198 /* Grab the contents so we can inspect the node's kind and created path. */ 199 new_node->node_pool = pool; 200 SVN_ERR(get_node_revision(&noderev, new_node)); 201 202 /* Initialize the KIND and CREATED_PATH attributes */ 203 new_node->kind = noderev->kind; 204 new_node->created_path = apr_pstrdup(pool, noderev->created_path); 205 206 if (noderev->is_fresh_txn_root) 207 new_node->fresh_root_predecessor_id = noderev->predecessor_id; 208 else 209 new_node->fresh_root_predecessor_id = NULL; 210 211 /* Return a fresh new node */ 212 *node = new_node; 213 return SVN_NO_ERROR; 214} 215 216 217svn_error_t * 218svn_fs_fs__dag_get_revision(svn_revnum_t *rev, 219 dag_node_t *node, 220 apr_pool_t *pool) 221{ 222 /* In the special case that this is an unmodified transaction root, 223 we need to actually get the revision of the noderev's predecessor 224 (the revision root); see Issue #2608. */ 225 const svn_fs_id_t *correct_id = node->fresh_root_predecessor_id 226 ? node->fresh_root_predecessor_id : node->id; 227 228 /* Look up the committed revision from the Node-ID. */ 229 *rev = svn_fs_fs__id_rev(correct_id); 230 231 return SVN_NO_ERROR; 232} 233 234 235svn_error_t * 236svn_fs_fs__dag_get_predecessor_id(const svn_fs_id_t **id_p, 237 dag_node_t *node) 238{ 239 node_revision_t *noderev; 240 241 SVN_ERR(get_node_revision(&noderev, node)); 242 *id_p = noderev->predecessor_id; 243 return SVN_NO_ERROR; 244} 245 246 247svn_error_t * 248svn_fs_fs__dag_get_predecessor_count(int *count, 249 dag_node_t *node) 250{ 251 node_revision_t *noderev; 252 253 SVN_ERR(get_node_revision(&noderev, node)); 254 *count = noderev->predecessor_count; 255 return SVN_NO_ERROR; 256} 257 258svn_error_t * 259svn_fs_fs__dag_get_mergeinfo_count(apr_int64_t *count, 260 dag_node_t *node) 261{ 262 node_revision_t *noderev; 263 264 SVN_ERR(get_node_revision(&noderev, node)); 265 *count = noderev->mergeinfo_count; 266 return SVN_NO_ERROR; 267} 268 269svn_error_t * 270svn_fs_fs__dag_has_mergeinfo(svn_boolean_t *has_mergeinfo, 271 dag_node_t *node) 272{ 273 node_revision_t *noderev; 274 275 SVN_ERR(get_node_revision(&noderev, node)); 276 *has_mergeinfo = noderev->has_mergeinfo; 277 return SVN_NO_ERROR; 278} 279 280svn_error_t * 281svn_fs_fs__dag_has_descendants_with_mergeinfo(svn_boolean_t *do_they, 282 dag_node_t *node) 283{ 284 node_revision_t *noderev; 285 286 if (node->kind != svn_node_dir) 287 { 288 *do_they = FALSE; 289 return SVN_NO_ERROR; 290 } 291 292 SVN_ERR(get_node_revision(&noderev, node)); 293 if (noderev->mergeinfo_count > 1) 294 *do_they = TRUE; 295 else if (noderev->mergeinfo_count == 1 && !noderev->has_mergeinfo) 296 *do_they = TRUE; 297 else 298 *do_they = FALSE; 299 return SVN_NO_ERROR; 300} 301 302 303/*** Directory node functions ***/ 304 305/* Some of these are helpers for functions outside this section. */ 306 307/* Set *ID_P to the node-id for entry NAME in PARENT. If no such 308 entry, set *ID_P to NULL but do not error. The node-id is 309 allocated in POOL. */ 310static svn_error_t * 311dir_entry_id_from_node(const svn_fs_id_t **id_p, 312 dag_node_t *parent, 313 const char *name, 314 apr_pool_t *result_pool, 315 apr_pool_t *scratch_pool) 316{ 317 svn_fs_dirent_t *dirent; 318 319 SVN_ERR(svn_fs_fs__dag_dir_entry(&dirent, parent, name, result_pool, 320 scratch_pool)); 321 *id_p = dirent ? dirent->id : NULL; 322 323 return SVN_NO_ERROR; 324} 325 326 327/* Add or set in PARENT a directory entry NAME pointing to ID. 328 Allocations are done in POOL. 329 330 Assumptions: 331 - PARENT is a mutable directory. 332 - ID does not refer to an ancestor of parent 333 - NAME is a single path component 334*/ 335static svn_error_t * 336set_entry(dag_node_t *parent, 337 const char *name, 338 const svn_fs_id_t *id, 339 svn_node_kind_t kind, 340 const svn_fs_fs__id_part_t *txn_id, 341 apr_pool_t *pool) 342{ 343 node_revision_t *parent_noderev; 344 345 /* Get the parent's node-revision. */ 346 SVN_ERR(get_node_revision(&parent_noderev, parent)); 347 348 /* Set the new entry. */ 349 return svn_fs_fs__set_entry(parent->fs, txn_id, parent_noderev, name, id, 350 kind, pool); 351} 352 353 354/* Make a new entry named NAME in PARENT. If IS_DIR is true, then the 355 node revision the new entry points to will be a directory, else it 356 will be a file. The new node will be allocated in POOL. PARENT 357 must be mutable, and must not have an entry named NAME. 358 359 Use POOL for all allocations, except caching the node_revision in PARENT. 360 */ 361static svn_error_t * 362make_entry(dag_node_t **child_p, 363 dag_node_t *parent, 364 const char *parent_path, 365 const char *name, 366 svn_boolean_t is_dir, 367 const svn_fs_fs__id_part_t *txn_id, 368 apr_pool_t *pool) 369{ 370 const svn_fs_id_t *new_node_id; 371 node_revision_t new_noderev, *parent_noderev; 372 373 /* Make sure that NAME is a single path component. */ 374 if (! svn_path_is_single_path_component(name)) 375 return svn_error_createf 376 (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL, 377 _("Attempted to create a node with an illegal name '%s'"), name); 378 379 /* Make sure that parent is a directory */ 380 if (parent->kind != svn_node_dir) 381 return svn_error_create 382 (SVN_ERR_FS_NOT_DIRECTORY, NULL, 383 _("Attempted to create entry in non-directory parent")); 384 385 /* Check that the parent is mutable. */ 386 if (! svn_fs_fs__dag_check_mutable(parent)) 387 return svn_error_createf 388 (SVN_ERR_FS_NOT_MUTABLE, NULL, 389 _("Attempted to clone child of non-mutable node")); 390 391 /* Create the new node's NODE-REVISION */ 392 memset(&new_noderev, 0, sizeof(new_noderev)); 393 new_noderev.kind = is_dir ? svn_node_dir : svn_node_file; 394 new_noderev.created_path = svn_fspath__join(parent_path, name, pool); 395 396 SVN_ERR(get_node_revision(&parent_noderev, parent)); 397 new_noderev.copyroot_path = apr_pstrdup(pool, 398 parent_noderev->copyroot_path); 399 new_noderev.copyroot_rev = parent_noderev->copyroot_rev; 400 new_noderev.copyfrom_rev = SVN_INVALID_REVNUM; 401 new_noderev.copyfrom_path = NULL; 402 403 SVN_ERR(svn_fs_fs__create_node 404 (&new_node_id, svn_fs_fs__dag_get_fs(parent), &new_noderev, 405 svn_fs_fs__id_copy_id(svn_fs_fs__dag_get_id(parent)), 406 txn_id, pool)); 407 408 /* Create a new dag_node_t for our new node */ 409 SVN_ERR(svn_fs_fs__dag_get_node(child_p, svn_fs_fs__dag_get_fs(parent), 410 new_node_id, pool)); 411 412 /* We can safely call set_entry because we already know that 413 PARENT is mutable, and we just created CHILD, so we know it has 414 no ancestors (therefore, PARENT cannot be an ancestor of CHILD) */ 415 return set_entry(parent, name, svn_fs_fs__dag_get_id(*child_p), 416 new_noderev.kind, txn_id, pool); 417} 418 419 420svn_error_t * 421svn_fs_fs__dag_dir_entries(apr_array_header_t **entries, 422 dag_node_t *node, 423 apr_pool_t *pool) 424{ 425 node_revision_t *noderev; 426 427 SVN_ERR(get_node_revision(&noderev, node)); 428 429 if (noderev->kind != svn_node_dir) 430 return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, 431 _("Can't get entries of non-directory")); 432 433 return svn_fs_fs__rep_contents_dir(entries, node->fs, noderev, pool, pool); 434} 435 436svn_error_t * 437svn_fs_fs__dag_dir_entry(svn_fs_dirent_t **dirent, 438 dag_node_t *node, 439 const char* name, 440 apr_pool_t *result_pool, 441 apr_pool_t *scratch_pool) 442{ 443 node_revision_t *noderev; 444 SVN_ERR(get_node_revision(&noderev, node)); 445 446 if (noderev->kind != svn_node_dir) 447 return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, 448 _("Can't get entries of non-directory")); 449 450 /* Get a dirent hash for this directory. */ 451 return svn_fs_fs__rep_contents_dir_entry(dirent, node->fs, noderev, name, 452 result_pool, scratch_pool); 453} 454 455 456svn_error_t * 457svn_fs_fs__dag_set_entry(dag_node_t *node, 458 const char *entry_name, 459 const svn_fs_id_t *id, 460 svn_node_kind_t kind, 461 const svn_fs_fs__id_part_t *txn_id, 462 apr_pool_t *pool) 463{ 464 /* Check it's a directory. */ 465 if (node->kind != svn_node_dir) 466 return svn_error_create 467 (SVN_ERR_FS_NOT_DIRECTORY, NULL, 468 _("Attempted to set entry in non-directory node")); 469 470 /* Check it's mutable. */ 471 if (! svn_fs_fs__dag_check_mutable(node)) 472 return svn_error_create 473 (SVN_ERR_FS_NOT_MUTABLE, NULL, 474 _("Attempted to set entry in immutable node")); 475 476 return set_entry(node, entry_name, id, kind, txn_id, pool); 477} 478 479 480 481/*** Proplists. ***/ 482 483svn_error_t * 484svn_fs_fs__dag_get_proplist(apr_hash_t **proplist_p, 485 dag_node_t *node, 486 apr_pool_t *pool) 487{ 488 node_revision_t *noderev; 489 apr_hash_t *proplist = NULL; 490 491 SVN_ERR(get_node_revision(&noderev, node)); 492 493 SVN_ERR(svn_fs_fs__get_proplist(&proplist, node->fs, 494 noderev, pool)); 495 496 *proplist_p = proplist; 497 498 return SVN_NO_ERROR; 499} 500 501svn_error_t * 502svn_fs_fs__dag_has_props(svn_boolean_t *has_props, 503 dag_node_t *node, 504 apr_pool_t *scratch_pool) 505{ 506 node_revision_t *noderev; 507 508 SVN_ERR(get_node_revision(&noderev, node)); 509 510 if (! noderev->prop_rep) 511 { 512 *has_props = FALSE; /* Easy out */ 513 return SVN_NO_ERROR; 514 } 515 516 if (svn_fs_fs__id_txn_used(&noderev->prop_rep->txn_id)) 517 { 518 /* We are in a commit or something. Check actual properties */ 519 apr_hash_t *proplist; 520 521 SVN_ERR(svn_fs_fs__get_proplist(&proplist, node->fs, 522 noderev, scratch_pool)); 523 524 *has_props = proplist ? (0 < apr_hash_count(proplist)) : FALSE; 525 } 526 else 527 { 528 /* Properties are stored as a standard hash stream, 529 always ending with "END\n" (4 bytes) */ 530 *has_props = noderev->prop_rep->expanded_size > 4; 531 } 532 533 return SVN_NO_ERROR; 534} 535 536svn_error_t * 537svn_fs_fs__dag_set_proplist(dag_node_t *node, 538 apr_hash_t *proplist, 539 apr_pool_t *pool) 540{ 541 node_revision_t *noderev; 542 543 /* Sanity check: this node better be mutable! */ 544 if (! svn_fs_fs__dag_check_mutable(node)) 545 { 546 svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool); 547 return svn_error_createf 548 (SVN_ERR_FS_NOT_MUTABLE, NULL, 549 "Can't set proplist on *immutable* node-revision %s", 550 idstr->data); 551 } 552 553 /* Go get a fresh NODE-REVISION for this node. */ 554 SVN_ERR(get_node_revision(&noderev, node)); 555 556 /* Set the new proplist. */ 557 return svn_fs_fs__set_proplist(node->fs, noderev, proplist, pool); 558} 559 560 561svn_error_t * 562svn_fs_fs__dag_increment_mergeinfo_count(dag_node_t *node, 563 apr_int64_t increment, 564 apr_pool_t *pool) 565{ 566 node_revision_t *noderev; 567 568 /* Sanity check: this node better be mutable! */ 569 if (! svn_fs_fs__dag_check_mutable(node)) 570 { 571 svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool); 572 return svn_error_createf 573 (SVN_ERR_FS_NOT_MUTABLE, NULL, 574 "Can't increment mergeinfo count on *immutable* node-revision %s", 575 idstr->data); 576 } 577 578 if (increment == 0) 579 return SVN_NO_ERROR; 580 581 /* Go get a fresh NODE-REVISION for this node. */ 582 SVN_ERR(get_node_revision(&noderev, node)); 583 584 noderev->mergeinfo_count += increment; 585 if (noderev->mergeinfo_count < 0) 586 { 587 svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool); 588 return svn_error_createf 589 (SVN_ERR_FS_CORRUPT, NULL, 590 apr_psprintf(pool, 591 _("Can't increment mergeinfo count on node-revision %%s " 592 "to negative value %%%s"), 593 APR_INT64_T_FMT), 594 idstr->data, noderev->mergeinfo_count); 595 } 596 if (noderev->mergeinfo_count > 1 && noderev->kind == svn_node_file) 597 { 598 svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool); 599 return svn_error_createf 600 (SVN_ERR_FS_CORRUPT, NULL, 601 apr_psprintf(pool, 602 _("Can't increment mergeinfo count on *file* " 603 "node-revision %%s to %%%s (> 1)"), 604 APR_INT64_T_FMT), 605 idstr->data, noderev->mergeinfo_count); 606 } 607 608 /* Flush it out. */ 609 return svn_fs_fs__put_node_revision(node->fs, noderev->id, 610 noderev, FALSE, pool); 611} 612 613svn_error_t * 614svn_fs_fs__dag_set_has_mergeinfo(dag_node_t *node, 615 svn_boolean_t has_mergeinfo, 616 apr_pool_t *pool) 617{ 618 node_revision_t *noderev; 619 620 /* Sanity check: this node better be mutable! */ 621 if (! svn_fs_fs__dag_check_mutable(node)) 622 { 623 svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool); 624 return svn_error_createf 625 (SVN_ERR_FS_NOT_MUTABLE, NULL, 626 "Can't set mergeinfo flag on *immutable* node-revision %s", 627 idstr->data); 628 } 629 630 /* Go get a fresh NODE-REVISION for this node. */ 631 SVN_ERR(get_node_revision(&noderev, node)); 632 633 noderev->has_mergeinfo = has_mergeinfo; 634 635 /* Flush it out. */ 636 return svn_fs_fs__put_node_revision(node->fs, noderev->id, 637 noderev, FALSE, pool); 638} 639 640 641/*** Roots. ***/ 642 643svn_error_t * 644svn_fs_fs__dag_revision_root(dag_node_t **node_p, 645 svn_fs_t *fs, 646 svn_revnum_t rev, 647 apr_pool_t *pool) 648{ 649 dag_node_t *new_node; 650 651 /* Construct the node. */ 652 new_node = apr_pcalloc(pool, sizeof(*new_node)); 653 new_node->fs = fs; 654 SVN_ERR(svn_fs_fs__rev_get_root(&new_node->id, fs, rev, pool, pool)); 655 656 /* Grab the contents so we can inspect the node's kind and created path. */ 657 new_node->node_pool = pool; 658 659 /* Initialize the KIND and CREATED_PATH attributes */ 660 new_node->kind = svn_node_dir; 661 new_node->created_path = "/"; 662 new_node->fresh_root_predecessor_id = NULL; 663 664 /* Return a fresh new node */ 665 *node_p = new_node; 666 return SVN_NO_ERROR; 667} 668 669 670svn_error_t * 671svn_fs_fs__dag_txn_root(dag_node_t **node_p, 672 svn_fs_t *fs, 673 const svn_fs_fs__id_part_t *txn_id, 674 apr_pool_t *pool) 675{ 676 const svn_fs_id_t *root_id, *ignored; 677 678 SVN_ERR(svn_fs_fs__get_txn_ids(&root_id, &ignored, fs, txn_id, pool)); 679 return svn_fs_fs__dag_get_node(node_p, fs, root_id, pool); 680} 681 682 683svn_error_t * 684svn_fs_fs__dag_txn_base_root(dag_node_t **node_p, 685 svn_fs_t *fs, 686 const svn_fs_fs__id_part_t *txn_id, 687 apr_pool_t *pool) 688{ 689 const svn_fs_id_t *base_root_id, *ignored; 690 691 SVN_ERR(svn_fs_fs__get_txn_ids(&ignored, &base_root_id, fs, txn_id, pool)); 692 return svn_fs_fs__dag_get_node(node_p, fs, base_root_id, pool); 693} 694 695 696svn_error_t * 697svn_fs_fs__dag_clone_child(dag_node_t **child_p, 698 dag_node_t *parent, 699 const char *parent_path, 700 const char *name, 701 const svn_fs_fs__id_part_t *copy_id, 702 const svn_fs_fs__id_part_t *txn_id, 703 svn_boolean_t is_parent_copyroot, 704 apr_pool_t *pool) 705{ 706 dag_node_t *cur_entry; /* parent's current entry named NAME */ 707 const svn_fs_id_t *new_node_id; /* node id we'll put into NEW_NODE */ 708 svn_fs_t *fs = svn_fs_fs__dag_get_fs(parent); 709 apr_pool_t *subpool = svn_pool_create(pool); 710 711 /* First check that the parent is mutable. */ 712 if (! svn_fs_fs__dag_check_mutable(parent)) 713 return svn_error_createf 714 (SVN_ERR_FS_NOT_MUTABLE, NULL, 715 "Attempted to clone child of non-mutable node"); 716 717 /* Make sure that NAME is a single path component. */ 718 if (! svn_path_is_single_path_component(name)) 719 return svn_error_createf 720 (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL, 721 "Attempted to make a child clone with an illegal name '%s'", name); 722 723 /* Find the node named NAME in PARENT's entries list if it exists. */ 724 SVN_ERR(svn_fs_fs__dag_open(&cur_entry, parent, name, pool, subpool)); 725 if (! cur_entry) 726 return svn_error_createf 727 (SVN_ERR_FS_NOT_FOUND, NULL, 728 "Attempted to open non-existent child node '%s'", name); 729 730 /* Check for mutability in the node we found. If it's mutable, we 731 don't need to clone it. */ 732 if (svn_fs_fs__dag_check_mutable(cur_entry)) 733 { 734 /* This has already been cloned */ 735 new_node_id = cur_entry->id; 736 } 737 else 738 { 739 node_revision_t *noderev, *parent_noderev; 740 741 /* Go get a fresh NODE-REVISION for current child node. */ 742 SVN_ERR(get_node_revision(&noderev, cur_entry)); 743 744 if (is_parent_copyroot) 745 { 746 SVN_ERR(get_node_revision(&parent_noderev, parent)); 747 noderev->copyroot_rev = parent_noderev->copyroot_rev; 748 noderev->copyroot_path = apr_pstrdup(pool, 749 parent_noderev->copyroot_path); 750 } 751 752 noderev->copyfrom_path = NULL; 753 noderev->copyfrom_rev = SVN_INVALID_REVNUM; 754 755 noderev->predecessor_id = svn_fs_fs__id_copy(cur_entry->id, pool); 756 noderev->predecessor_count++; 757 noderev->created_path = svn_fspath__join(parent_path, name, pool); 758 759 SVN_ERR(svn_fs_fs__create_successor(&new_node_id, fs, cur_entry->id, 760 noderev, copy_id, txn_id, pool)); 761 762 /* Replace the ID in the parent's ENTRY list with the ID which 763 refers to the mutable clone of this child. */ 764 SVN_ERR(set_entry(parent, name, new_node_id, noderev->kind, txn_id, 765 pool)); 766 } 767 768 /* Initialize the youngster. */ 769 svn_pool_destroy(subpool); 770 return svn_fs_fs__dag_get_node(child_p, fs, new_node_id, pool); 771} 772 773 774 775svn_error_t * 776svn_fs_fs__dag_clone_root(dag_node_t **root_p, 777 svn_fs_t *fs, 778 const svn_fs_fs__id_part_t *txn_id, 779 apr_pool_t *pool) 780{ 781 const svn_fs_id_t *base_root_id, *root_id; 782 783 /* Get the node ID's of the root directories of the transaction and 784 its base revision. */ 785 SVN_ERR(svn_fs_fs__get_txn_ids(&root_id, &base_root_id, fs, txn_id, pool)); 786 787 /* Oh, give me a clone... 788 (If they're the same, we haven't cloned the transaction's root 789 directory yet.) */ 790 SVN_ERR_ASSERT(!svn_fs_fs__id_eq(root_id, base_root_id)); 791 792 /* 793 * (Sung to the tune of "Home, Home on the Range", with thanks to 794 * Randall Garrett and Isaac Asimov.) 795 */ 796 797 /* One way or another, root_id now identifies a cloned root node. */ 798 return svn_fs_fs__dag_get_node(root_p, fs, root_id, pool); 799} 800 801 802svn_error_t * 803svn_fs_fs__dag_delete(dag_node_t *parent, 804 const char *name, 805 const svn_fs_fs__id_part_t *txn_id, 806 apr_pool_t *pool) 807{ 808 node_revision_t *parent_noderev; 809 svn_fs_t *fs = parent->fs; 810 svn_fs_dirent_t *dirent; 811 svn_fs_id_t *id; 812 apr_pool_t *subpool; 813 814 /* Make sure parent is a directory. */ 815 if (parent->kind != svn_node_dir) 816 return svn_error_createf 817 (SVN_ERR_FS_NOT_DIRECTORY, NULL, 818 "Attempted to delete entry '%s' from *non*-directory node", name); 819 820 /* Make sure parent is mutable. */ 821 if (! svn_fs_fs__dag_check_mutable(parent)) 822 return svn_error_createf 823 (SVN_ERR_FS_NOT_MUTABLE, NULL, 824 "Attempted to delete entry '%s' from immutable directory node", name); 825 826 /* Make sure that NAME is a single path component. */ 827 if (! svn_path_is_single_path_component(name)) 828 return svn_error_createf 829 (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL, 830 "Attempted to delete a node with an illegal name '%s'", name); 831 832 /* Get a fresh NODE-REVISION for the parent node. */ 833 SVN_ERR(get_node_revision(&parent_noderev, parent)); 834 835 subpool = svn_pool_create(pool); 836 837 /* Search this directory for a dirent with that NAME. */ 838 SVN_ERR(svn_fs_fs__rep_contents_dir_entry(&dirent, fs, parent_noderev, 839 name, subpool, subpool)); 840 841 /* If we never found ID in ENTRIES (perhaps because there are no 842 ENTRIES, perhaps because ID just isn't in the existing ENTRIES 843 ... it doesn't matter), return an error. */ 844 if (! dirent) 845 return svn_error_createf 846 (SVN_ERR_FS_NO_SUCH_ENTRY, NULL, 847 "Delete failed--directory has no entry '%s'", name); 848 849 /* Copy the ID out of the subpool and release the rest of the 850 directory listing. */ 851 id = svn_fs_fs__id_copy(dirent->id, pool); 852 svn_pool_destroy(subpool); 853 854 /* If mutable, remove it and any mutable children from db. */ 855 SVN_ERR(svn_fs_fs__dag_delete_if_mutable(parent->fs, id, pool)); 856 857 /* Remove this entry from its parent's entries list. */ 858 return svn_fs_fs__set_entry(parent->fs, txn_id, parent_noderev, name, 859 NULL, svn_node_unknown, pool); 860} 861 862 863svn_error_t * 864svn_fs_fs__dag_remove_node(svn_fs_t *fs, 865 const svn_fs_id_t *id, 866 apr_pool_t *pool) 867{ 868 dag_node_t *node; 869 870 /* Fetch the node. */ 871 SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, id, pool)); 872 873 /* If immutable, do nothing and return immediately. */ 874 if (! svn_fs_fs__dag_check_mutable(node)) 875 return svn_error_createf(SVN_ERR_FS_NOT_MUTABLE, NULL, 876 "Attempted removal of immutable node"); 877 878 /* Delete the node revision. */ 879 return svn_fs_fs__delete_node_revision(fs, id, pool); 880} 881 882 883svn_error_t * 884svn_fs_fs__dag_delete_if_mutable(svn_fs_t *fs, 885 const svn_fs_id_t *id, 886 apr_pool_t *pool) 887{ 888 dag_node_t *node; 889 890 /* Get the node. */ 891 SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, id, pool)); 892 893 /* If immutable, do nothing and return immediately. */ 894 if (! svn_fs_fs__dag_check_mutable(node)) 895 return SVN_NO_ERROR; 896 897 /* Else it's mutable. Recurse on directories... */ 898 if (node->kind == svn_node_dir) 899 { 900 apr_array_header_t *entries; 901 int i; 902 apr_pool_t *iterpool = svn_pool_create(pool); 903 904 /* Loop over directory entries */ 905 SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool)); 906 if (entries) 907 for (i = 0; i < entries->nelts; ++i) 908 { 909 svn_pool_clear(iterpool); 910 SVN_ERR(svn_fs_fs__dag_delete_if_mutable(fs, 911 APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *)->id, 912 iterpool)); 913 } 914 915 svn_pool_destroy(iterpool); 916 } 917 918 /* ... then delete the node itself, after deleting any mutable 919 representations and strings it points to. */ 920 return svn_fs_fs__dag_remove_node(fs, id, pool); 921} 922 923svn_error_t * 924svn_fs_fs__dag_make_file(dag_node_t **child_p, 925 dag_node_t *parent, 926 const char *parent_path, 927 const char *name, 928 const svn_fs_fs__id_part_t *txn_id, 929 apr_pool_t *pool) 930{ 931 /* Call our little helper function */ 932 return make_entry(child_p, parent, parent_path, name, FALSE, txn_id, pool); 933} 934 935 936svn_error_t * 937svn_fs_fs__dag_make_dir(dag_node_t **child_p, 938 dag_node_t *parent, 939 const char *parent_path, 940 const char *name, 941 const svn_fs_fs__id_part_t *txn_id, 942 apr_pool_t *pool) 943{ 944 /* Call our little helper function */ 945 return make_entry(child_p, parent, parent_path, name, TRUE, txn_id, pool); 946} 947 948 949svn_error_t * 950svn_fs_fs__dag_get_contents(svn_stream_t **contents_p, 951 dag_node_t *file, 952 apr_pool_t *pool) 953{ 954 node_revision_t *noderev; 955 svn_stream_t *contents; 956 957 /* Make sure our node is a file. */ 958 if (file->kind != svn_node_file) 959 return svn_error_createf 960 (SVN_ERR_FS_NOT_FILE, NULL, 961 "Attempted to get textual contents of a *non*-file node"); 962 963 /* Go get a fresh node-revision for FILE. */ 964 SVN_ERR(get_node_revision(&noderev, file)); 965 966 /* Get a stream to the contents. */ 967 SVN_ERR(svn_fs_fs__get_contents(&contents, file->fs, 968 noderev->data_rep, TRUE, pool)); 969 970 *contents_p = contents; 971 972 return SVN_NO_ERROR; 973} 974 975 976svn_error_t * 977svn_fs_fs__dag_get_file_delta_stream(svn_txdelta_stream_t **stream_p, 978 dag_node_t *source, 979 dag_node_t *target, 980 apr_pool_t *pool) 981{ 982 node_revision_t *src_noderev; 983 node_revision_t *tgt_noderev; 984 985 /* Make sure our nodes are files. */ 986 if ((source && source->kind != svn_node_file) 987 || target->kind != svn_node_file) 988 return svn_error_createf 989 (SVN_ERR_FS_NOT_FILE, NULL, 990 "Attempted to get textual contents of a *non*-file node"); 991 992 /* Go get fresh node-revisions for the nodes. */ 993 if (source) 994 SVN_ERR(get_node_revision(&src_noderev, source)); 995 else 996 src_noderev = NULL; 997 SVN_ERR(get_node_revision(&tgt_noderev, target)); 998 999 /* Get the delta stream. */ 1000 return svn_fs_fs__get_file_delta_stream(stream_p, target->fs, 1001 src_noderev, tgt_noderev, pool); 1002} 1003 1004 1005svn_error_t * 1006svn_fs_fs__dag_try_process_file_contents(svn_boolean_t *success, 1007 dag_node_t *node, 1008 svn_fs_process_contents_func_t processor, 1009 void* baton, 1010 apr_pool_t *pool) 1011{ 1012 node_revision_t *noderev; 1013 1014 /* Go get fresh node-revisions for the nodes. */ 1015 SVN_ERR(get_node_revision(&noderev, node)); 1016 1017 return svn_fs_fs__try_process_file_contents(success, node->fs, 1018 noderev, 1019 processor, baton, pool); 1020} 1021 1022 1023svn_error_t * 1024svn_fs_fs__dag_file_length(svn_filesize_t *length, 1025 dag_node_t *file, 1026 apr_pool_t *pool) 1027{ 1028 node_revision_t *noderev; 1029 1030 /* Make sure our node is a file. */ 1031 if (file->kind != svn_node_file) 1032 return svn_error_createf 1033 (SVN_ERR_FS_NOT_FILE, NULL, 1034 "Attempted to get length of a *non*-file node"); 1035 1036 /* Go get a fresh node-revision for FILE, and . */ 1037 SVN_ERR(get_node_revision(&noderev, file)); 1038 1039 return svn_fs_fs__file_length(length, noderev, pool); 1040} 1041 1042 1043svn_error_t * 1044svn_fs_fs__dag_file_checksum(svn_checksum_t **checksum, 1045 dag_node_t *file, 1046 svn_checksum_kind_t kind, 1047 apr_pool_t *pool) 1048{ 1049 node_revision_t *noderev; 1050 1051 if (file->kind != svn_node_file) 1052 return svn_error_createf 1053 (SVN_ERR_FS_NOT_FILE, NULL, 1054 "Attempted to get checksum of a *non*-file node"); 1055 1056 SVN_ERR(get_node_revision(&noderev, file)); 1057 1058 return svn_fs_fs__file_checksum(checksum, noderev, kind, pool); 1059} 1060 1061 1062svn_error_t * 1063svn_fs_fs__dag_get_edit_stream(svn_stream_t **contents, 1064 dag_node_t *file, 1065 apr_pool_t *pool) 1066{ 1067 node_revision_t *noderev; 1068 svn_stream_t *ws; 1069 1070 /* Make sure our node is a file. */ 1071 if (file->kind != svn_node_file) 1072 return svn_error_createf 1073 (SVN_ERR_FS_NOT_FILE, NULL, 1074 "Attempted to set textual contents of a *non*-file node"); 1075 1076 /* Make sure our node is mutable. */ 1077 if (! svn_fs_fs__dag_check_mutable(file)) 1078 return svn_error_createf 1079 (SVN_ERR_FS_NOT_MUTABLE, NULL, 1080 "Attempted to set textual contents of an immutable node"); 1081 1082 /* Get the node revision. */ 1083 SVN_ERR(get_node_revision(&noderev, file)); 1084 1085 SVN_ERR(svn_fs_fs__set_contents(&ws, file->fs, noderev, pool)); 1086 1087 *contents = ws; 1088 1089 return SVN_NO_ERROR; 1090} 1091 1092 1093 1094svn_error_t * 1095svn_fs_fs__dag_finalize_edits(dag_node_t *file, 1096 const svn_checksum_t *checksum, 1097 apr_pool_t *pool) 1098{ 1099 if (checksum) 1100 { 1101 svn_checksum_t *file_checksum; 1102 1103 SVN_ERR(svn_fs_fs__dag_file_checksum(&file_checksum, file, 1104 checksum->kind, pool)); 1105 if (!svn_checksum_match(checksum, file_checksum)) 1106 return svn_checksum_mismatch_err(checksum, file_checksum, pool, 1107 _("Checksum mismatch for '%s'"), 1108 file->created_path); 1109 } 1110 1111 return SVN_NO_ERROR; 1112} 1113 1114 1115dag_node_t * 1116svn_fs_fs__dag_dup(const dag_node_t *node, 1117 apr_pool_t *pool) 1118{ 1119 /* Allocate our new node. */ 1120 dag_node_t *new_node = apr_pcalloc(pool, sizeof(*new_node)); 1121 1122 new_node->fs = node->fs; 1123 new_node->id = svn_fs_fs__id_copy(node->id, pool); 1124 new_node->kind = node->kind; 1125 new_node->created_path = apr_pstrdup(pool, node->created_path); 1126 1127 /* Only copy cached node_revision_t for immutable nodes. */ 1128 if (node->node_revision && !svn_fs_fs__dag_check_mutable(node)) 1129 { 1130 new_node->node_revision = copy_node_revision(node->node_revision, pool); 1131 new_node->node_revision->id = 1132 svn_fs_fs__id_copy(node->node_revision->id, pool); 1133 new_node->node_revision->is_fresh_txn_root = 1134 node->node_revision->is_fresh_txn_root; 1135 } 1136 new_node->node_pool = pool; 1137 1138 return new_node; 1139} 1140 1141svn_error_t * 1142svn_fs_fs__dag_serialize(void **data, 1143 apr_size_t *data_len, 1144 void *in, 1145 apr_pool_t *pool) 1146{ 1147 dag_node_t *node = in; 1148 svn_stringbuf_t *serialized; 1149 1150 /* create an serialization context and serialize the dag node as root */ 1151 svn_temp_serializer__context_t *context = 1152 svn_temp_serializer__init(node, 1153 sizeof(*node), 1154 1024 - SVN_TEMP_SERIALIZER__OVERHEAD, 1155 pool); 1156 1157 /* for mutable nodes, we will _never_ cache the noderev */ 1158 if (node->node_revision && !svn_fs_fs__dag_check_mutable(node)) 1159 svn_fs_fs__noderev_serialize(context, &node->node_revision); 1160 else 1161 svn_temp_serializer__set_null(context, 1162 (const void * const *)&node->node_revision); 1163 1164 /* The deserializer will use its own pool. */ 1165 svn_temp_serializer__set_null(context, 1166 (const void * const *)&node->node_pool); 1167 1168 /* serialize other sub-structures */ 1169 svn_fs_fs__id_serialize(context, (const svn_fs_id_t *const *)&node->id); 1170 svn_fs_fs__id_serialize(context, &node->fresh_root_predecessor_id); 1171 svn_temp_serializer__add_string(context, &node->created_path); 1172 1173 /* return serialized data */ 1174 serialized = svn_temp_serializer__get(context); 1175 *data = serialized->data; 1176 *data_len = serialized->len; 1177 1178 return SVN_NO_ERROR; 1179} 1180 1181svn_error_t * 1182svn_fs_fs__dag_deserialize(void **out, 1183 void *data, 1184 apr_size_t data_len, 1185 apr_pool_t *pool) 1186{ 1187 dag_node_t *node = (dag_node_t *)data; 1188 if (data_len == 0) 1189 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 1190 _("Empty noderev in cache")); 1191 1192 /* Copy the _full_ buffer as it also contains the sub-structures. */ 1193 node->fs = NULL; 1194 1195 /* fixup all references to sub-structures */ 1196 svn_fs_fs__id_deserialize(node, &node->id); 1197 svn_fs_fs__id_deserialize(node, 1198 (svn_fs_id_t **)&node->fresh_root_predecessor_id); 1199 svn_fs_fs__noderev_deserialize(node, &node->node_revision); 1200 node->node_pool = pool; 1201 1202 svn_temp_deserializer__resolve(node, (void**)&node->created_path); 1203 1204 /* return result */ 1205 *out = node; 1206 1207 return SVN_NO_ERROR; 1208} 1209 1210svn_error_t * 1211svn_fs_fs__dag_open(dag_node_t **child_p, 1212 dag_node_t *parent, 1213 const char *name, 1214 apr_pool_t *result_pool, 1215 apr_pool_t *scratch_pool) 1216{ 1217 const svn_fs_id_t *node_id; 1218 1219 /* Ensure that NAME exists in PARENT's entry list. */ 1220 SVN_ERR(dir_entry_id_from_node(&node_id, parent, name, 1221 scratch_pool, scratch_pool)); 1222 if (! node_id) 1223 { 1224 *child_p = NULL; 1225 return SVN_NO_ERROR; 1226 } 1227 1228 /* Make sure that NAME is a single path component. */ 1229 if (! svn_path_is_single_path_component(name)) 1230 return svn_error_createf 1231 (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL, 1232 "Attempted to open node with an illegal name '%s'", name); 1233 1234 /* Now get the node that was requested. */ 1235 return svn_fs_fs__dag_get_node(child_p, svn_fs_fs__dag_get_fs(parent), 1236 node_id, result_pool); 1237} 1238 1239 1240svn_error_t * 1241svn_fs_fs__dag_copy(dag_node_t *to_node, 1242 const char *entry, 1243 dag_node_t *from_node, 1244 svn_boolean_t preserve_history, 1245 svn_revnum_t from_rev, 1246 const char *from_path, 1247 const svn_fs_fs__id_part_t *txn_id, 1248 apr_pool_t *pool) 1249{ 1250 const svn_fs_id_t *id; 1251 1252 if (preserve_history) 1253 { 1254 node_revision_t *from_noderev, *to_noderev; 1255 svn_fs_fs__id_part_t copy_id; 1256 const svn_fs_id_t *src_id = svn_fs_fs__dag_get_id(from_node); 1257 svn_fs_t *fs = svn_fs_fs__dag_get_fs(from_node); 1258 1259 /* Make a copy of the original node revision. */ 1260 SVN_ERR(get_node_revision(&from_noderev, from_node)); 1261 to_noderev = copy_node_revision(from_noderev, pool); 1262 1263 /* Reserve a copy ID for this new copy. */ 1264 SVN_ERR(svn_fs_fs__reserve_copy_id(©_id, fs, txn_id, pool)); 1265 1266 /* Create a successor with its predecessor pointing at the copy 1267 source. */ 1268 to_noderev->predecessor_id = svn_fs_fs__id_copy(src_id, pool); 1269 to_noderev->predecessor_count++; 1270 to_noderev->created_path = 1271 svn_fspath__join(svn_fs_fs__dag_get_created_path(to_node), entry, 1272 pool); 1273 to_noderev->copyfrom_path = apr_pstrdup(pool, from_path); 1274 to_noderev->copyfrom_rev = from_rev; 1275 1276 /* Set the copyroot equal to our own id. */ 1277 to_noderev->copyroot_path = NULL; 1278 1279 SVN_ERR(svn_fs_fs__create_successor(&id, fs, src_id, to_noderev, 1280 ©_id, txn_id, pool)); 1281 1282 } 1283 else /* don't preserve history */ 1284 { 1285 id = svn_fs_fs__dag_get_id(from_node); 1286 } 1287 1288 /* Set the entry in to_node to the new id. */ 1289 return svn_fs_fs__dag_set_entry(to_node, entry, id, from_node->kind, 1290 txn_id, pool); 1291} 1292 1293 1294 1295/*** Comparison. ***/ 1296 1297svn_error_t * 1298svn_fs_fs__dag_things_different(svn_boolean_t *props_changed, 1299 svn_boolean_t *contents_changed, 1300 dag_node_t *node1, 1301 dag_node_t *node2, 1302 svn_boolean_t strict, 1303 apr_pool_t *pool) 1304{ 1305 node_revision_t *noderev1, *noderev2; 1306 1307 /* If we have no place to store our results, don't bother doing 1308 anything. */ 1309 if (! props_changed && ! contents_changed) 1310 return SVN_NO_ERROR; 1311 1312 /* The node revision skels for these two nodes. */ 1313 SVN_ERR(get_node_revision(&noderev1, node1)); 1314 SVN_ERR(get_node_revision(&noderev2, node2)); 1315 1316 if (strict) 1317 { 1318 /* In strict mode, compare text and property representations in the 1319 svn_fs_contents_different() / svn_fs_props_different() manner. 1320 1321 See the "No-op changes no longer dumped by 'svnadmin dump' in 1.9" 1322 discussion (http://svn.haxx.se/dev/archive-2015-09/0269.shtml) and 1323 issue #4598 (https://issues.apache.org/jira/browse/SVN-4598). */ 1324 svn_fs_t *fs = svn_fs_fs__dag_get_fs(node1); 1325 svn_boolean_t same; 1326 1327 /* Compare property keys. */ 1328 if (props_changed != NULL) 1329 { 1330 SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, noderev1, 1331 noderev2, pool)); 1332 *props_changed = !same; 1333 } 1334 1335 /* Compare contents keys. */ 1336 if (contents_changed != NULL) 1337 { 1338 SVN_ERR(svn_fs_fs__file_text_rep_equal(&same, fs, noderev1, 1339 noderev2, pool)); 1340 *contents_changed = !same; 1341 } 1342 } 1343 else 1344 { 1345 /* Otherwise, compare representation keys -- as in Subversion 1.8. */ 1346 1347 /* Compare property keys. */ 1348 if (props_changed != NULL) 1349 *props_changed = 1350 !svn_fs_fs__noderev_same_rep_key(noderev1->prop_rep, 1351 noderev2->prop_rep); 1352 1353 /* Compare contents keys. */ 1354 if (contents_changed != NULL) 1355 *contents_changed = 1356 !svn_fs_fs__noderev_same_rep_key(noderev1->data_rep, 1357 noderev2->data_rep); 1358 } 1359 1360 return SVN_NO_ERROR; 1361} 1362 1363svn_error_t * 1364svn_fs_fs__dag_get_copyroot(svn_revnum_t *rev, 1365 const char **path, 1366 dag_node_t *node) 1367{ 1368 node_revision_t *noderev; 1369 1370 /* Go get a fresh node-revision for NODE. */ 1371 SVN_ERR(get_node_revision(&noderev, node)); 1372 1373 *rev = noderev->copyroot_rev; 1374 *path = noderev->copyroot_path; 1375 1376 return SVN_NO_ERROR; 1377} 1378 1379svn_error_t * 1380svn_fs_fs__dag_get_copyfrom_rev(svn_revnum_t *rev, 1381 dag_node_t *node) 1382{ 1383 node_revision_t *noderev; 1384 1385 /* Go get a fresh node-revision for NODE. */ 1386 SVN_ERR(get_node_revision(&noderev, node)); 1387 1388 *rev = noderev->copyfrom_rev; 1389 1390 return SVN_NO_ERROR; 1391} 1392 1393svn_error_t * 1394svn_fs_fs__dag_get_copyfrom_path(const char **path, 1395 dag_node_t *node) 1396{ 1397 node_revision_t *noderev; 1398 1399 /* Go get a fresh node-revision for NODE. */ 1400 SVN_ERR(get_node_revision(&noderev, node)); 1401 1402 *path = noderev->copyfrom_path; 1403 1404 return SVN_NO_ERROR; 1405} 1406 1407svn_error_t * 1408svn_fs_fs__dag_update_ancestry(dag_node_t *target, 1409 dag_node_t *source, 1410 apr_pool_t *pool) 1411{ 1412 node_revision_t *source_noderev, *target_noderev; 1413 1414 if (! svn_fs_fs__dag_check_mutable(target)) 1415 return svn_error_createf 1416 (SVN_ERR_FS_NOT_MUTABLE, NULL, 1417 _("Attempted to update ancestry of non-mutable node")); 1418 1419 SVN_ERR(get_node_revision(&source_noderev, source)); 1420 SVN_ERR(get_node_revision(&target_noderev, target)); 1421 1422 target_noderev->predecessor_id = source->id; 1423 target_noderev->predecessor_count = source_noderev->predecessor_count; 1424 target_noderev->predecessor_count++; 1425 1426 return svn_fs_fs__put_node_revision(target->fs, target->id, target_noderev, 1427 FALSE, pool); 1428} 1429