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