1251881Speter/* dag.c : DAG-like interface filesystem, private to libsvn_fs 2251881Speter * 3251881Speter * ==================================================================== 4251881Speter * Licensed to the Apache Software Foundation (ASF) under one 5251881Speter * or more contributor license agreements. See the NOTICE file 6251881Speter * distributed with this work for additional information 7251881Speter * regarding copyright ownership. The ASF licenses this file 8251881Speter * to you under the Apache License, Version 2.0 (the 9251881Speter * "License"); you may not use this file except in compliance 10251881Speter * with the License. You may obtain a copy of the License at 11251881Speter * 12251881Speter * http://www.apache.org/licenses/LICENSE-2.0 13251881Speter * 14251881Speter * Unless required by applicable law or agreed to in writing, 15251881Speter * software distributed under the License is distributed on an 16251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17251881Speter * KIND, either express or implied. See the License for the 18251881Speter * specific language governing permissions and limitations 19251881Speter * under the License. 20251881Speter * ==================================================================== 21251881Speter */ 22251881Speter 23251881Speter#include <string.h> 24251881Speter 25251881Speter#include "svn_path.h" 26251881Speter#include "svn_error.h" 27251881Speter#include "svn_fs.h" 28251881Speter#include "svn_props.h" 29251881Speter#include "svn_pools.h" 30251881Speter 31299742Sdim#include "cached_data.h" 32251881Speter#include "dag.h" 33251881Speter#include "fs.h" 34251881Speter#include "fs_fs.h" 35251881Speter#include "id.h" 36299742Sdim#include "transaction.h" 37251881Speter 38251881Speter#include "../libsvn_fs/fs-loader.h" 39251881Speter 40251881Speter#include "private/svn_fspath.h" 41251881Speter#include "svn_private_config.h" 42251881Speter#include "private/svn_temp_serializer.h" 43251881Speter#include "temp_serializer.h" 44251881Speter 45251881Speter 46251881Speter/* Initializing a filesystem. */ 47251881Speter 48251881Speterstruct dag_node_t 49251881Speter{ 50251881Speter /* The filesystem this dag node came from. */ 51251881Speter svn_fs_t *fs; 52251881Speter 53251881Speter /* The node revision ID for this dag node, allocated in POOL. */ 54251881Speter svn_fs_id_t *id; 55251881Speter 56251881Speter /* In the special case that this node is the root of a transaction 57251881Speter that has not yet been modified, the node revision ID for this dag 58251881Speter node's predecessor; otherwise NULL. (Used in 59251881Speter svn_fs_node_created_rev.) */ 60251881Speter const svn_fs_id_t *fresh_root_predecessor_id; 61251881Speter 62251881Speter /* The node's type (file, dir, etc.) */ 63251881Speter svn_node_kind_t kind; 64251881Speter 65251881Speter /* The node's NODE-REVISION, or NULL if we haven't read it in yet. 66251881Speter This is allocated in this node's POOL. 67251881Speter 68251881Speter If you're willing to respect all the rules above, you can munge 69251881Speter this yourself, but you're probably better off just calling 70251881Speter `get_node_revision' and `set_node_revision', which take care of 71251881Speter things for you. */ 72251881Speter node_revision_t *node_revision; 73251881Speter 74251881Speter /* The pool to allocate NODE_REVISION in. */ 75251881Speter apr_pool_t *node_pool; 76251881Speter 77251881Speter /* the path at which this node was created. */ 78251881Speter const char *created_path; 79251881Speter}; 80251881Speter 81251881Speter 82251881Speter 83251881Speter/* Trivial helper/accessor functions. */ 84251881Spetersvn_node_kind_t svn_fs_fs__dag_node_kind(dag_node_t *node) 85251881Speter{ 86251881Speter return node->kind; 87251881Speter} 88251881Speter 89251881Speter 90251881Speterconst svn_fs_id_t * 91251881Spetersvn_fs_fs__dag_get_id(const dag_node_t *node) 92251881Speter{ 93251881Speter return node->id; 94251881Speter} 95251881Speter 96251881Speter 97251881Speterconst char * 98251881Spetersvn_fs_fs__dag_get_created_path(dag_node_t *node) 99251881Speter{ 100251881Speter return node->created_path; 101251881Speter} 102251881Speter 103251881Speter 104251881Spetersvn_fs_t * 105251881Spetersvn_fs_fs__dag_get_fs(dag_node_t *node) 106251881Speter{ 107251881Speter return node->fs; 108251881Speter} 109251881Speter 110251881Spetervoid 111251881Spetersvn_fs_fs__dag_set_fs(dag_node_t *node, svn_fs_t *fs) 112251881Speter{ 113251881Speter node->fs = fs; 114251881Speter} 115251881Speter 116251881Speter 117251881Speter/* Dup NODEREV and all associated data into POOL. 118251881Speter Leaves the id and is_fresh_txn_root fields as zero bytes. */ 119251881Speterstatic node_revision_t * 120251881Spetercopy_node_revision(node_revision_t *noderev, 121251881Speter apr_pool_t *pool) 122251881Speter{ 123251881Speter node_revision_t *nr = apr_pcalloc(pool, sizeof(*nr)); 124251881Speter nr->kind = noderev->kind; 125251881Speter if (noderev->predecessor_id) 126251881Speter nr->predecessor_id = svn_fs_fs__id_copy(noderev->predecessor_id, pool); 127251881Speter nr->predecessor_count = noderev->predecessor_count; 128251881Speter if (noderev->copyfrom_path) 129251881Speter nr->copyfrom_path = apr_pstrdup(pool, noderev->copyfrom_path); 130251881Speter nr->copyfrom_rev = noderev->copyfrom_rev; 131251881Speter nr->copyroot_path = apr_pstrdup(pool, noderev->copyroot_path); 132251881Speter nr->copyroot_rev = noderev->copyroot_rev; 133251881Speter nr->data_rep = svn_fs_fs__rep_copy(noderev->data_rep, pool); 134251881Speter nr->prop_rep = svn_fs_fs__rep_copy(noderev->prop_rep, pool); 135251881Speter nr->mergeinfo_count = noderev->mergeinfo_count; 136251881Speter nr->has_mergeinfo = noderev->has_mergeinfo; 137251881Speter 138251881Speter if (noderev->created_path) 139251881Speter nr->created_path = apr_pstrdup(pool, noderev->created_path); 140251881Speter return nr; 141251881Speter} 142251881Speter 143251881Speter 144251881Speter/* Set *NODEREV_P to the cached node-revision for NODE. 145251881Speter If the node-revision was not already cached in NODE, read it in, 146251881Speter allocating the cache in NODE->NODE_POOL. 147251881Speter 148251881Speter If you plan to change the contents of NODE, be careful! We're 149251881Speter handing you a pointer directly to our cached node-revision, not 150251881Speter your own copy. If you change it as part of some operation, but 151251881Speter then some Berkeley DB function deadlocks or gets an error, you'll 152251881Speter need to back out your changes, or else the cache will reflect 153251881Speter changes that never got committed. It's probably best not to change 154251881Speter the structure at all. */ 155251881Speterstatic svn_error_t * 156251881Speterget_node_revision(node_revision_t **noderev_p, 157251881Speter dag_node_t *node) 158251881Speter{ 159251881Speter /* If we've already got a copy, there's no need to read it in. */ 160251881Speter if (! node->node_revision) 161251881Speter { 162251881Speter node_revision_t *noderev; 163299742Sdim apr_pool_t *scratch_pool = svn_pool_create(node->node_pool); 164251881Speter 165251881Speter SVN_ERR(svn_fs_fs__get_node_revision(&noderev, node->fs, 166299742Sdim node->id, node->node_pool, 167299742Sdim scratch_pool)); 168251881Speter node->node_revision = noderev; 169299742Sdim svn_pool_destroy(scratch_pool); 170251881Speter } 171251881Speter 172251881Speter /* Now NODE->node_revision is set. */ 173251881Speter *noderev_p = node->node_revision; 174251881Speter return SVN_NO_ERROR; 175251881Speter} 176251881Speter 177251881Speter 178251881Spetersvn_boolean_t svn_fs_fs__dag_check_mutable(const dag_node_t *node) 179251881Speter{ 180299742Sdim return svn_fs_fs__id_is_txn(svn_fs_fs__dag_get_id(node)); 181251881Speter} 182251881Speter 183251881Speter 184251881Spetersvn_error_t * 185251881Spetersvn_fs_fs__dag_get_node(dag_node_t **node, 186251881Speter svn_fs_t *fs, 187251881Speter const svn_fs_id_t *id, 188251881Speter apr_pool_t *pool) 189251881Speter{ 190251881Speter dag_node_t *new_node; 191251881Speter node_revision_t *noderev; 192251881Speter 193251881Speter /* Construct the node. */ 194251881Speter new_node = apr_pcalloc(pool, sizeof(*new_node)); 195251881Speter new_node->fs = fs; 196251881Speter new_node->id = svn_fs_fs__id_copy(id, pool); 197251881Speter 198251881Speter /* Grab the contents so we can inspect the node's kind and created path. */ 199251881Speter new_node->node_pool = pool; 200251881Speter SVN_ERR(get_node_revision(&noderev, new_node)); 201251881Speter 202251881Speter /* Initialize the KIND and CREATED_PATH attributes */ 203251881Speter new_node->kind = noderev->kind; 204251881Speter new_node->created_path = apr_pstrdup(pool, noderev->created_path); 205251881Speter 206251881Speter if (noderev->is_fresh_txn_root) 207251881Speter new_node->fresh_root_predecessor_id = noderev->predecessor_id; 208251881Speter else 209251881Speter new_node->fresh_root_predecessor_id = NULL; 210251881Speter 211251881Speter /* Return a fresh new node */ 212251881Speter *node = new_node; 213251881Speter return SVN_NO_ERROR; 214251881Speter} 215251881Speter 216251881Speter 217251881Spetersvn_error_t * 218251881Spetersvn_fs_fs__dag_get_revision(svn_revnum_t *rev, 219251881Speter dag_node_t *node, 220251881Speter apr_pool_t *pool) 221251881Speter{ 222251881Speter /* In the special case that this is an unmodified transaction root, 223251881Speter we need to actually get the revision of the noderev's predecessor 224251881Speter (the revision root); see Issue #2608. */ 225251881Speter const svn_fs_id_t *correct_id = node->fresh_root_predecessor_id 226251881Speter ? node->fresh_root_predecessor_id : node->id; 227251881Speter 228251881Speter /* Look up the committed revision from the Node-ID. */ 229251881Speter *rev = svn_fs_fs__id_rev(correct_id); 230251881Speter 231251881Speter return SVN_NO_ERROR; 232251881Speter} 233251881Speter 234251881Speter 235251881Spetersvn_error_t * 236251881Spetersvn_fs_fs__dag_get_predecessor_id(const svn_fs_id_t **id_p, 237251881Speter dag_node_t *node) 238251881Speter{ 239251881Speter node_revision_t *noderev; 240251881Speter 241251881Speter SVN_ERR(get_node_revision(&noderev, node)); 242251881Speter *id_p = noderev->predecessor_id; 243251881Speter return SVN_NO_ERROR; 244251881Speter} 245251881Speter 246251881Speter 247251881Spetersvn_error_t * 248251881Spetersvn_fs_fs__dag_get_predecessor_count(int *count, 249251881Speter dag_node_t *node) 250251881Speter{ 251251881Speter node_revision_t *noderev; 252251881Speter 253251881Speter SVN_ERR(get_node_revision(&noderev, node)); 254251881Speter *count = noderev->predecessor_count; 255251881Speter return SVN_NO_ERROR; 256251881Speter} 257251881Speter 258251881Spetersvn_error_t * 259251881Spetersvn_fs_fs__dag_get_mergeinfo_count(apr_int64_t *count, 260251881Speter dag_node_t *node) 261251881Speter{ 262251881Speter node_revision_t *noderev; 263251881Speter 264251881Speter SVN_ERR(get_node_revision(&noderev, node)); 265251881Speter *count = noderev->mergeinfo_count; 266251881Speter return SVN_NO_ERROR; 267251881Speter} 268251881Speter 269251881Spetersvn_error_t * 270251881Spetersvn_fs_fs__dag_has_mergeinfo(svn_boolean_t *has_mergeinfo, 271251881Speter dag_node_t *node) 272251881Speter{ 273251881Speter node_revision_t *noderev; 274251881Speter 275251881Speter SVN_ERR(get_node_revision(&noderev, node)); 276251881Speter *has_mergeinfo = noderev->has_mergeinfo; 277251881Speter return SVN_NO_ERROR; 278251881Speter} 279251881Speter 280251881Spetersvn_error_t * 281251881Spetersvn_fs_fs__dag_has_descendants_with_mergeinfo(svn_boolean_t *do_they, 282251881Speter dag_node_t *node) 283251881Speter{ 284251881Speter node_revision_t *noderev; 285251881Speter 286251881Speter if (node->kind != svn_node_dir) 287251881Speter { 288251881Speter *do_they = FALSE; 289251881Speter return SVN_NO_ERROR; 290251881Speter } 291251881Speter 292251881Speter SVN_ERR(get_node_revision(&noderev, node)); 293251881Speter if (noderev->mergeinfo_count > 1) 294251881Speter *do_they = TRUE; 295251881Speter else if (noderev->mergeinfo_count == 1 && !noderev->has_mergeinfo) 296251881Speter *do_they = TRUE; 297251881Speter else 298251881Speter *do_they = FALSE; 299251881Speter return SVN_NO_ERROR; 300251881Speter} 301251881Speter 302251881Speter 303251881Speter/*** Directory node functions ***/ 304251881Speter 305251881Speter/* Some of these are helpers for functions outside this section. */ 306251881Speter 307251881Speter/* Set *ID_P to the node-id for entry NAME in PARENT. If no such 308251881Speter entry, set *ID_P to NULL but do not error. The node-id is 309251881Speter allocated in POOL. */ 310251881Speterstatic svn_error_t * 311251881Speterdir_entry_id_from_node(const svn_fs_id_t **id_p, 312251881Speter dag_node_t *parent, 313251881Speter const char *name, 314251881Speter apr_pool_t *result_pool, 315251881Speter apr_pool_t *scratch_pool) 316251881Speter{ 317251881Speter svn_fs_dirent_t *dirent; 318251881Speter 319299742Sdim SVN_ERR(svn_fs_fs__dag_dir_entry(&dirent, parent, name, result_pool, 320299742Sdim scratch_pool)); 321299742Sdim *id_p = dirent ? dirent->id : NULL; 322251881Speter 323251881Speter return SVN_NO_ERROR; 324251881Speter} 325251881Speter 326251881Speter 327251881Speter/* Add or set in PARENT a directory entry NAME pointing to ID. 328251881Speter Allocations are done in POOL. 329251881Speter 330251881Speter Assumptions: 331251881Speter - PARENT is a mutable directory. 332251881Speter - ID does not refer to an ancestor of parent 333251881Speter - NAME is a single path component 334251881Speter*/ 335251881Speterstatic svn_error_t * 336251881Speterset_entry(dag_node_t *parent, 337251881Speter const char *name, 338251881Speter const svn_fs_id_t *id, 339251881Speter svn_node_kind_t kind, 340299742Sdim const svn_fs_fs__id_part_t *txn_id, 341251881Speter apr_pool_t *pool) 342251881Speter{ 343251881Speter node_revision_t *parent_noderev; 344251881Speter 345251881Speter /* Get the parent's node-revision. */ 346251881Speter SVN_ERR(get_node_revision(&parent_noderev, parent)); 347251881Speter 348251881Speter /* Set the new entry. */ 349251881Speter return svn_fs_fs__set_entry(parent->fs, txn_id, parent_noderev, name, id, 350251881Speter kind, pool); 351251881Speter} 352251881Speter 353251881Speter 354251881Speter/* Make a new entry named NAME in PARENT. If IS_DIR is true, then the 355251881Speter node revision the new entry points to will be a directory, else it 356251881Speter will be a file. The new node will be allocated in POOL. PARENT 357251881Speter must be mutable, and must not have an entry named NAME. 358251881Speter 359251881Speter Use POOL for all allocations, except caching the node_revision in PARENT. 360251881Speter */ 361251881Speterstatic svn_error_t * 362251881Spetermake_entry(dag_node_t **child_p, 363251881Speter dag_node_t *parent, 364251881Speter const char *parent_path, 365251881Speter const char *name, 366251881Speter svn_boolean_t is_dir, 367299742Sdim const svn_fs_fs__id_part_t *txn_id, 368251881Speter apr_pool_t *pool) 369251881Speter{ 370251881Speter const svn_fs_id_t *new_node_id; 371251881Speter node_revision_t new_noderev, *parent_noderev; 372251881Speter 373251881Speter /* Make sure that NAME is a single path component. */ 374251881Speter if (! svn_path_is_single_path_component(name)) 375251881Speter return svn_error_createf 376251881Speter (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL, 377251881Speter _("Attempted to create a node with an illegal name '%s'"), name); 378251881Speter 379251881Speter /* Make sure that parent is a directory */ 380251881Speter if (parent->kind != svn_node_dir) 381251881Speter return svn_error_create 382251881Speter (SVN_ERR_FS_NOT_DIRECTORY, NULL, 383251881Speter _("Attempted to create entry in non-directory parent")); 384251881Speter 385251881Speter /* Check that the parent is mutable. */ 386251881Speter if (! svn_fs_fs__dag_check_mutable(parent)) 387251881Speter return svn_error_createf 388251881Speter (SVN_ERR_FS_NOT_MUTABLE, NULL, 389251881Speter _("Attempted to clone child of non-mutable node")); 390251881Speter 391251881Speter /* Create the new node's NODE-REVISION */ 392251881Speter memset(&new_noderev, 0, sizeof(new_noderev)); 393251881Speter new_noderev.kind = is_dir ? svn_node_dir : svn_node_file; 394251881Speter new_noderev.created_path = svn_fspath__join(parent_path, name, pool); 395251881Speter 396251881Speter SVN_ERR(get_node_revision(&parent_noderev, parent)); 397251881Speter new_noderev.copyroot_path = apr_pstrdup(pool, 398251881Speter parent_noderev->copyroot_path); 399251881Speter new_noderev.copyroot_rev = parent_noderev->copyroot_rev; 400251881Speter new_noderev.copyfrom_rev = SVN_INVALID_REVNUM; 401251881Speter new_noderev.copyfrom_path = NULL; 402251881Speter 403251881Speter SVN_ERR(svn_fs_fs__create_node 404251881Speter (&new_node_id, svn_fs_fs__dag_get_fs(parent), &new_noderev, 405251881Speter svn_fs_fs__id_copy_id(svn_fs_fs__dag_get_id(parent)), 406251881Speter txn_id, pool)); 407251881Speter 408251881Speter /* Create a new dag_node_t for our new node */ 409251881Speter SVN_ERR(svn_fs_fs__dag_get_node(child_p, svn_fs_fs__dag_get_fs(parent), 410251881Speter new_node_id, pool)); 411251881Speter 412251881Speter /* We can safely call set_entry because we already know that 413251881Speter PARENT is mutable, and we just created CHILD, so we know it has 414251881Speter no ancestors (therefore, PARENT cannot be an ancestor of CHILD) */ 415251881Speter return set_entry(parent, name, svn_fs_fs__dag_get_id(*child_p), 416251881Speter new_noderev.kind, txn_id, pool); 417251881Speter} 418251881Speter 419251881Speter 420251881Spetersvn_error_t * 421299742Sdimsvn_fs_fs__dag_dir_entries(apr_array_header_t **entries, 422251881Speter dag_node_t *node, 423251881Speter apr_pool_t *pool) 424251881Speter{ 425251881Speter node_revision_t *noderev; 426251881Speter 427251881Speter SVN_ERR(get_node_revision(&noderev, node)); 428251881Speter 429251881Speter if (noderev->kind != svn_node_dir) 430251881Speter return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, 431251881Speter _("Can't get entries of non-directory")); 432251881Speter 433299742Sdim return svn_fs_fs__rep_contents_dir(entries, node->fs, noderev, pool, pool); 434251881Speter} 435251881Speter 436251881Spetersvn_error_t * 437251881Spetersvn_fs_fs__dag_dir_entry(svn_fs_dirent_t **dirent, 438251881Speter dag_node_t *node, 439251881Speter const char* name, 440299742Sdim apr_pool_t *result_pool, 441299742Sdim apr_pool_t *scratch_pool) 442251881Speter{ 443251881Speter node_revision_t *noderev; 444251881Speter SVN_ERR(get_node_revision(&noderev, node)); 445251881Speter 446251881Speter if (noderev->kind != svn_node_dir) 447251881Speter return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, 448251881Speter _("Can't get entries of non-directory")); 449251881Speter 450251881Speter /* Get a dirent hash for this directory. */ 451299742Sdim return svn_fs_fs__rep_contents_dir_entry(dirent, node->fs, noderev, name, 452299742Sdim result_pool, scratch_pool); 453251881Speter} 454251881Speter 455251881Speter 456251881Spetersvn_error_t * 457251881Spetersvn_fs_fs__dag_set_entry(dag_node_t *node, 458251881Speter const char *entry_name, 459251881Speter const svn_fs_id_t *id, 460251881Speter svn_node_kind_t kind, 461299742Sdim const svn_fs_fs__id_part_t *txn_id, 462251881Speter apr_pool_t *pool) 463251881Speter{ 464251881Speter /* Check it's a directory. */ 465251881Speter if (node->kind != svn_node_dir) 466251881Speter return svn_error_create 467251881Speter (SVN_ERR_FS_NOT_DIRECTORY, NULL, 468251881Speter _("Attempted to set entry in non-directory node")); 469251881Speter 470251881Speter /* Check it's mutable. */ 471251881Speter if (! svn_fs_fs__dag_check_mutable(node)) 472251881Speter return svn_error_create 473251881Speter (SVN_ERR_FS_NOT_MUTABLE, NULL, 474251881Speter _("Attempted to set entry in immutable node")); 475251881Speter 476251881Speter return set_entry(node, entry_name, id, kind, txn_id, pool); 477251881Speter} 478251881Speter 479251881Speter 480251881Speter 481251881Speter/*** Proplists. ***/ 482251881Speter 483251881Spetersvn_error_t * 484251881Spetersvn_fs_fs__dag_get_proplist(apr_hash_t **proplist_p, 485251881Speter dag_node_t *node, 486251881Speter apr_pool_t *pool) 487251881Speter{ 488251881Speter node_revision_t *noderev; 489251881Speter apr_hash_t *proplist = NULL; 490251881Speter 491251881Speter SVN_ERR(get_node_revision(&noderev, node)); 492251881Speter 493251881Speter SVN_ERR(svn_fs_fs__get_proplist(&proplist, node->fs, 494251881Speter noderev, pool)); 495251881Speter 496251881Speter *proplist_p = proplist; 497251881Speter 498251881Speter return SVN_NO_ERROR; 499251881Speter} 500251881Speter 501299742Sdimsvn_error_t * 502299742Sdimsvn_fs_fs__dag_has_props(svn_boolean_t *has_props, 503299742Sdim dag_node_t *node, 504299742Sdim apr_pool_t *scratch_pool) 505299742Sdim{ 506299742Sdim node_revision_t *noderev; 507251881Speter 508299742Sdim SVN_ERR(get_node_revision(&noderev, node)); 509299742Sdim 510299742Sdim if (! noderev->prop_rep) 511299742Sdim { 512299742Sdim *has_props = FALSE; /* Easy out */ 513299742Sdim return SVN_NO_ERROR; 514299742Sdim } 515299742Sdim 516299742Sdim if (svn_fs_fs__id_txn_used(&noderev->prop_rep->txn_id)) 517299742Sdim { 518299742Sdim /* We are in a commit or something. Check actual properties */ 519299742Sdim apr_hash_t *proplist; 520299742Sdim 521299742Sdim SVN_ERR(svn_fs_fs__get_proplist(&proplist, node->fs, 522299742Sdim noderev, scratch_pool)); 523299742Sdim 524299742Sdim *has_props = proplist ? (0 < apr_hash_count(proplist)) : FALSE; 525299742Sdim } 526299742Sdim else 527299742Sdim { 528299742Sdim /* Properties are stored as a standard hash stream, 529299742Sdim always ending with "END\n" (4 bytes) */ 530299742Sdim *has_props = (noderev->prop_rep->expanded_size > 4 531299742Sdim || (noderev->prop_rep->expanded_size == 0 532299742Sdim && noderev->prop_rep->size > 4)); 533299742Sdim } 534299742Sdim 535299742Sdim return SVN_NO_ERROR; 536299742Sdim} 537299742Sdim 538251881Spetersvn_error_t * 539251881Spetersvn_fs_fs__dag_set_proplist(dag_node_t *node, 540251881Speter apr_hash_t *proplist, 541251881Speter apr_pool_t *pool) 542251881Speter{ 543251881Speter node_revision_t *noderev; 544251881Speter 545251881Speter /* Sanity check: this node better be mutable! */ 546251881Speter if (! svn_fs_fs__dag_check_mutable(node)) 547251881Speter { 548251881Speter svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool); 549251881Speter return svn_error_createf 550251881Speter (SVN_ERR_FS_NOT_MUTABLE, NULL, 551251881Speter "Can't set proplist on *immutable* node-revision %s", 552251881Speter idstr->data); 553251881Speter } 554251881Speter 555251881Speter /* Go get a fresh NODE-REVISION for this node. */ 556251881Speter SVN_ERR(get_node_revision(&noderev, node)); 557251881Speter 558251881Speter /* Set the new proplist. */ 559251881Speter return svn_fs_fs__set_proplist(node->fs, noderev, proplist, pool); 560251881Speter} 561251881Speter 562251881Speter 563251881Spetersvn_error_t * 564251881Spetersvn_fs_fs__dag_increment_mergeinfo_count(dag_node_t *node, 565251881Speter apr_int64_t increment, 566251881Speter apr_pool_t *pool) 567251881Speter{ 568251881Speter node_revision_t *noderev; 569251881Speter 570251881Speter /* Sanity check: this node better be mutable! */ 571251881Speter if (! svn_fs_fs__dag_check_mutable(node)) 572251881Speter { 573251881Speter svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool); 574251881Speter return svn_error_createf 575251881Speter (SVN_ERR_FS_NOT_MUTABLE, NULL, 576251881Speter "Can't increment mergeinfo count on *immutable* node-revision %s", 577251881Speter idstr->data); 578251881Speter } 579251881Speter 580251881Speter if (increment == 0) 581251881Speter return SVN_NO_ERROR; 582251881Speter 583251881Speter /* Go get a fresh NODE-REVISION for this node. */ 584251881Speter SVN_ERR(get_node_revision(&noderev, node)); 585251881Speter 586251881Speter noderev->mergeinfo_count += increment; 587251881Speter if (noderev->mergeinfo_count < 0) 588251881Speter { 589251881Speter svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool); 590251881Speter return svn_error_createf 591251881Speter (SVN_ERR_FS_CORRUPT, NULL, 592251881Speter apr_psprintf(pool, 593251881Speter _("Can't increment mergeinfo count on node-revision %%s " 594251881Speter "to negative value %%%s"), 595251881Speter APR_INT64_T_FMT), 596251881Speter idstr->data, noderev->mergeinfo_count); 597251881Speter } 598251881Speter if (noderev->mergeinfo_count > 1 && noderev->kind == svn_node_file) 599251881Speter { 600251881Speter svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool); 601251881Speter return svn_error_createf 602251881Speter (SVN_ERR_FS_CORRUPT, NULL, 603251881Speter apr_psprintf(pool, 604251881Speter _("Can't increment mergeinfo count on *file* " 605251881Speter "node-revision %%s to %%%s (> 1)"), 606251881Speter APR_INT64_T_FMT), 607251881Speter idstr->data, noderev->mergeinfo_count); 608251881Speter } 609251881Speter 610251881Speter /* Flush it out. */ 611251881Speter return svn_fs_fs__put_node_revision(node->fs, noderev->id, 612251881Speter noderev, FALSE, pool); 613251881Speter} 614251881Speter 615251881Spetersvn_error_t * 616251881Spetersvn_fs_fs__dag_set_has_mergeinfo(dag_node_t *node, 617251881Speter svn_boolean_t has_mergeinfo, 618251881Speter apr_pool_t *pool) 619251881Speter{ 620251881Speter node_revision_t *noderev; 621251881Speter 622251881Speter /* Sanity check: this node better be mutable! */ 623251881Speter if (! svn_fs_fs__dag_check_mutable(node)) 624251881Speter { 625251881Speter svn_string_t *idstr = svn_fs_fs__id_unparse(node->id, pool); 626251881Speter return svn_error_createf 627251881Speter (SVN_ERR_FS_NOT_MUTABLE, NULL, 628251881Speter "Can't set mergeinfo flag on *immutable* node-revision %s", 629251881Speter idstr->data); 630251881Speter } 631251881Speter 632251881Speter /* Go get a fresh NODE-REVISION for this node. */ 633251881Speter SVN_ERR(get_node_revision(&noderev, node)); 634251881Speter 635251881Speter noderev->has_mergeinfo = has_mergeinfo; 636251881Speter 637251881Speter /* Flush it out. */ 638251881Speter return svn_fs_fs__put_node_revision(node->fs, noderev->id, 639251881Speter noderev, FALSE, pool); 640251881Speter} 641251881Speter 642251881Speter 643251881Speter/*** Roots. ***/ 644251881Speter 645251881Spetersvn_error_t * 646251881Spetersvn_fs_fs__dag_revision_root(dag_node_t **node_p, 647251881Speter svn_fs_t *fs, 648251881Speter svn_revnum_t rev, 649251881Speter apr_pool_t *pool) 650251881Speter{ 651299742Sdim dag_node_t *new_node; 652251881Speter 653299742Sdim /* Construct the node. */ 654299742Sdim new_node = apr_pcalloc(pool, sizeof(*new_node)); 655299742Sdim new_node->fs = fs; 656299742Sdim SVN_ERR(svn_fs_fs__rev_get_root(&new_node->id, fs, rev, pool, pool)); 657299742Sdim 658299742Sdim /* Grab the contents so we can inspect the node's kind and created path. */ 659299742Sdim new_node->node_pool = pool; 660299742Sdim 661299742Sdim /* Initialize the KIND and CREATED_PATH attributes */ 662299742Sdim new_node->kind = svn_node_dir; 663299742Sdim new_node->created_path = "/"; 664299742Sdim new_node->fresh_root_predecessor_id = NULL; 665299742Sdim 666299742Sdim /* Return a fresh new node */ 667299742Sdim *node_p = new_node; 668299742Sdim return SVN_NO_ERROR; 669251881Speter} 670251881Speter 671251881Speter 672251881Spetersvn_error_t * 673251881Spetersvn_fs_fs__dag_txn_root(dag_node_t **node_p, 674251881Speter svn_fs_t *fs, 675299742Sdim const svn_fs_fs__id_part_t *txn_id, 676251881Speter apr_pool_t *pool) 677251881Speter{ 678251881Speter const svn_fs_id_t *root_id, *ignored; 679251881Speter 680251881Speter SVN_ERR(svn_fs_fs__get_txn_ids(&root_id, &ignored, fs, txn_id, pool)); 681251881Speter return svn_fs_fs__dag_get_node(node_p, fs, root_id, pool); 682251881Speter} 683251881Speter 684251881Speter 685251881Spetersvn_error_t * 686251881Spetersvn_fs_fs__dag_txn_base_root(dag_node_t **node_p, 687251881Speter svn_fs_t *fs, 688299742Sdim const svn_fs_fs__id_part_t *txn_id, 689251881Speter apr_pool_t *pool) 690251881Speter{ 691251881Speter const svn_fs_id_t *base_root_id, *ignored; 692251881Speter 693251881Speter SVN_ERR(svn_fs_fs__get_txn_ids(&ignored, &base_root_id, fs, txn_id, pool)); 694251881Speter return svn_fs_fs__dag_get_node(node_p, fs, base_root_id, pool); 695251881Speter} 696251881Speter 697251881Speter 698251881Spetersvn_error_t * 699251881Spetersvn_fs_fs__dag_clone_child(dag_node_t **child_p, 700251881Speter dag_node_t *parent, 701251881Speter const char *parent_path, 702251881Speter const char *name, 703299742Sdim const svn_fs_fs__id_part_t *copy_id, 704299742Sdim const svn_fs_fs__id_part_t *txn_id, 705251881Speter svn_boolean_t is_parent_copyroot, 706251881Speter apr_pool_t *pool) 707251881Speter{ 708251881Speter dag_node_t *cur_entry; /* parent's current entry named NAME */ 709251881Speter const svn_fs_id_t *new_node_id; /* node id we'll put into NEW_NODE */ 710251881Speter svn_fs_t *fs = svn_fs_fs__dag_get_fs(parent); 711251881Speter apr_pool_t *subpool = svn_pool_create(pool); 712251881Speter 713251881Speter /* First check that the parent is mutable. */ 714251881Speter if (! svn_fs_fs__dag_check_mutable(parent)) 715251881Speter return svn_error_createf 716251881Speter (SVN_ERR_FS_NOT_MUTABLE, NULL, 717251881Speter "Attempted to clone child of non-mutable node"); 718251881Speter 719251881Speter /* Make sure that NAME is a single path component. */ 720251881Speter if (! svn_path_is_single_path_component(name)) 721251881Speter return svn_error_createf 722251881Speter (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL, 723251881Speter "Attempted to make a child clone with an illegal name '%s'", name); 724251881Speter 725251881Speter /* Find the node named NAME in PARENT's entries list if it exists. */ 726251881Speter SVN_ERR(svn_fs_fs__dag_open(&cur_entry, parent, name, pool, subpool)); 727299742Sdim if (! cur_entry) 728299742Sdim return svn_error_createf 729299742Sdim (SVN_ERR_FS_NOT_FOUND, NULL, 730299742Sdim "Attempted to open non-existent child node '%s'", name); 731251881Speter 732251881Speter /* Check for mutability in the node we found. If it's mutable, we 733251881Speter don't need to clone it. */ 734251881Speter if (svn_fs_fs__dag_check_mutable(cur_entry)) 735251881Speter { 736251881Speter /* This has already been cloned */ 737251881Speter new_node_id = cur_entry->id; 738251881Speter } 739251881Speter else 740251881Speter { 741251881Speter node_revision_t *noderev, *parent_noderev; 742251881Speter 743251881Speter /* Go get a fresh NODE-REVISION for current child node. */ 744251881Speter SVN_ERR(get_node_revision(&noderev, cur_entry)); 745251881Speter 746251881Speter if (is_parent_copyroot) 747251881Speter { 748251881Speter SVN_ERR(get_node_revision(&parent_noderev, parent)); 749251881Speter noderev->copyroot_rev = parent_noderev->copyroot_rev; 750251881Speter noderev->copyroot_path = apr_pstrdup(pool, 751251881Speter parent_noderev->copyroot_path); 752251881Speter } 753251881Speter 754251881Speter noderev->copyfrom_path = NULL; 755251881Speter noderev->copyfrom_rev = SVN_INVALID_REVNUM; 756251881Speter 757251881Speter noderev->predecessor_id = svn_fs_fs__id_copy(cur_entry->id, pool); 758251881Speter if (noderev->predecessor_count != -1) 759251881Speter noderev->predecessor_count++; 760251881Speter noderev->created_path = svn_fspath__join(parent_path, name, pool); 761251881Speter 762251881Speter SVN_ERR(svn_fs_fs__create_successor(&new_node_id, fs, cur_entry->id, 763251881Speter noderev, copy_id, txn_id, pool)); 764251881Speter 765251881Speter /* Replace the ID in the parent's ENTRY list with the ID which 766251881Speter refers to the mutable clone of this child. */ 767251881Speter SVN_ERR(set_entry(parent, name, new_node_id, noderev->kind, txn_id, 768251881Speter pool)); 769251881Speter } 770251881Speter 771251881Speter /* Initialize the youngster. */ 772251881Speter svn_pool_destroy(subpool); 773251881Speter return svn_fs_fs__dag_get_node(child_p, fs, new_node_id, pool); 774251881Speter} 775251881Speter 776251881Speter 777251881Speter 778251881Spetersvn_error_t * 779251881Spetersvn_fs_fs__dag_clone_root(dag_node_t **root_p, 780251881Speter svn_fs_t *fs, 781299742Sdim const svn_fs_fs__id_part_t *txn_id, 782251881Speter apr_pool_t *pool) 783251881Speter{ 784251881Speter const svn_fs_id_t *base_root_id, *root_id; 785251881Speter 786251881Speter /* Get the node ID's of the root directories of the transaction and 787251881Speter its base revision. */ 788251881Speter SVN_ERR(svn_fs_fs__get_txn_ids(&root_id, &base_root_id, fs, txn_id, pool)); 789251881Speter 790251881Speter /* Oh, give me a clone... 791251881Speter (If they're the same, we haven't cloned the transaction's root 792251881Speter directory yet.) */ 793251881Speter SVN_ERR_ASSERT(!svn_fs_fs__id_eq(root_id, base_root_id)); 794251881Speter 795251881Speter /* 796251881Speter * (Sung to the tune of "Home, Home on the Range", with thanks to 797251881Speter * Randall Garrett and Isaac Asimov.) 798251881Speter */ 799251881Speter 800251881Speter /* One way or another, root_id now identifies a cloned root node. */ 801251881Speter return svn_fs_fs__dag_get_node(root_p, fs, root_id, pool); 802251881Speter} 803251881Speter 804251881Speter 805251881Spetersvn_error_t * 806251881Spetersvn_fs_fs__dag_delete(dag_node_t *parent, 807251881Speter const char *name, 808299742Sdim const svn_fs_fs__id_part_t *txn_id, 809251881Speter apr_pool_t *pool) 810251881Speter{ 811251881Speter node_revision_t *parent_noderev; 812251881Speter svn_fs_t *fs = parent->fs; 813251881Speter svn_fs_dirent_t *dirent; 814251881Speter svn_fs_id_t *id; 815251881Speter apr_pool_t *subpool; 816251881Speter 817251881Speter /* Make sure parent is a directory. */ 818251881Speter if (parent->kind != svn_node_dir) 819251881Speter return svn_error_createf 820251881Speter (SVN_ERR_FS_NOT_DIRECTORY, NULL, 821251881Speter "Attempted to delete entry '%s' from *non*-directory node", name); 822251881Speter 823251881Speter /* Make sure parent is mutable. */ 824251881Speter if (! svn_fs_fs__dag_check_mutable(parent)) 825251881Speter return svn_error_createf 826251881Speter (SVN_ERR_FS_NOT_MUTABLE, NULL, 827251881Speter "Attempted to delete entry '%s' from immutable directory node", name); 828251881Speter 829251881Speter /* Make sure that NAME is a single path component. */ 830251881Speter if (! svn_path_is_single_path_component(name)) 831251881Speter return svn_error_createf 832251881Speter (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL, 833251881Speter "Attempted to delete a node with an illegal name '%s'", name); 834251881Speter 835251881Speter /* Get a fresh NODE-REVISION for the parent node. */ 836251881Speter SVN_ERR(get_node_revision(&parent_noderev, parent)); 837251881Speter 838251881Speter subpool = svn_pool_create(pool); 839251881Speter 840251881Speter /* Search this directory for a dirent with that NAME. */ 841251881Speter SVN_ERR(svn_fs_fs__rep_contents_dir_entry(&dirent, fs, parent_noderev, 842251881Speter name, subpool, subpool)); 843251881Speter 844251881Speter /* If we never found ID in ENTRIES (perhaps because there are no 845251881Speter ENTRIES, perhaps because ID just isn't in the existing ENTRIES 846251881Speter ... it doesn't matter), return an error. */ 847251881Speter if (! dirent) 848251881Speter return svn_error_createf 849251881Speter (SVN_ERR_FS_NO_SUCH_ENTRY, NULL, 850251881Speter "Delete failed--directory has no entry '%s'", name); 851251881Speter 852251881Speter /* Copy the ID out of the subpool and release the rest of the 853251881Speter directory listing. */ 854251881Speter id = svn_fs_fs__id_copy(dirent->id, pool); 855251881Speter svn_pool_destroy(subpool); 856251881Speter 857251881Speter /* If mutable, remove it and any mutable children from db. */ 858251881Speter SVN_ERR(svn_fs_fs__dag_delete_if_mutable(parent->fs, id, pool)); 859251881Speter 860251881Speter /* Remove this entry from its parent's entries list. */ 861251881Speter return svn_fs_fs__set_entry(parent->fs, txn_id, parent_noderev, name, 862251881Speter NULL, svn_node_unknown, pool); 863251881Speter} 864251881Speter 865251881Speter 866251881Spetersvn_error_t * 867251881Spetersvn_fs_fs__dag_remove_node(svn_fs_t *fs, 868251881Speter const svn_fs_id_t *id, 869251881Speter apr_pool_t *pool) 870251881Speter{ 871251881Speter dag_node_t *node; 872251881Speter 873251881Speter /* Fetch the node. */ 874251881Speter SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, id, pool)); 875251881Speter 876251881Speter /* If immutable, do nothing and return immediately. */ 877251881Speter if (! svn_fs_fs__dag_check_mutable(node)) 878251881Speter return svn_error_createf(SVN_ERR_FS_NOT_MUTABLE, NULL, 879251881Speter "Attempted removal of immutable node"); 880251881Speter 881251881Speter /* Delete the node revision. */ 882251881Speter return svn_fs_fs__delete_node_revision(fs, id, pool); 883251881Speter} 884251881Speter 885251881Speter 886251881Spetersvn_error_t * 887251881Spetersvn_fs_fs__dag_delete_if_mutable(svn_fs_t *fs, 888251881Speter const svn_fs_id_t *id, 889251881Speter apr_pool_t *pool) 890251881Speter{ 891251881Speter dag_node_t *node; 892251881Speter 893251881Speter /* Get the node. */ 894251881Speter SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, id, pool)); 895251881Speter 896251881Speter /* If immutable, do nothing and return immediately. */ 897251881Speter if (! svn_fs_fs__dag_check_mutable(node)) 898251881Speter return SVN_NO_ERROR; 899251881Speter 900251881Speter /* Else it's mutable. Recurse on directories... */ 901251881Speter if (node->kind == svn_node_dir) 902251881Speter { 903299742Sdim apr_array_header_t *entries; 904299742Sdim int i; 905299742Sdim apr_pool_t *iterpool = svn_pool_create(pool); 906251881Speter 907299742Sdim /* Loop over directory entries */ 908251881Speter SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool)); 909251881Speter if (entries) 910299742Sdim for (i = 0; i < entries->nelts; ++i) 911299742Sdim { 912299742Sdim svn_pool_clear(iterpool); 913299742Sdim SVN_ERR(svn_fs_fs__dag_delete_if_mutable(fs, 914299742Sdim APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *)->id, 915299742Sdim iterpool)); 916299742Sdim } 917251881Speter 918299742Sdim svn_pool_destroy(iterpool); 919251881Speter } 920251881Speter 921251881Speter /* ... then delete the node itself, after deleting any mutable 922251881Speter representations and strings it points to. */ 923251881Speter return svn_fs_fs__dag_remove_node(fs, id, pool); 924251881Speter} 925251881Speter 926251881Spetersvn_error_t * 927251881Spetersvn_fs_fs__dag_make_file(dag_node_t **child_p, 928251881Speter dag_node_t *parent, 929251881Speter const char *parent_path, 930251881Speter const char *name, 931299742Sdim const svn_fs_fs__id_part_t *txn_id, 932251881Speter apr_pool_t *pool) 933251881Speter{ 934251881Speter /* Call our little helper function */ 935251881Speter return make_entry(child_p, parent, parent_path, name, FALSE, txn_id, pool); 936251881Speter} 937251881Speter 938251881Speter 939251881Spetersvn_error_t * 940251881Spetersvn_fs_fs__dag_make_dir(dag_node_t **child_p, 941251881Speter dag_node_t *parent, 942251881Speter const char *parent_path, 943251881Speter const char *name, 944299742Sdim const svn_fs_fs__id_part_t *txn_id, 945251881Speter apr_pool_t *pool) 946251881Speter{ 947251881Speter /* Call our little helper function */ 948251881Speter return make_entry(child_p, parent, parent_path, name, TRUE, txn_id, pool); 949251881Speter} 950251881Speter 951251881Speter 952251881Spetersvn_error_t * 953251881Spetersvn_fs_fs__dag_get_contents(svn_stream_t **contents_p, 954251881Speter dag_node_t *file, 955251881Speter apr_pool_t *pool) 956251881Speter{ 957251881Speter node_revision_t *noderev; 958251881Speter svn_stream_t *contents; 959251881Speter 960251881Speter /* Make sure our node is a file. */ 961251881Speter if (file->kind != svn_node_file) 962251881Speter return svn_error_createf 963251881Speter (SVN_ERR_FS_NOT_FILE, NULL, 964251881Speter "Attempted to get textual contents of a *non*-file node"); 965251881Speter 966251881Speter /* Go get a fresh node-revision for FILE. */ 967251881Speter SVN_ERR(get_node_revision(&noderev, file)); 968251881Speter 969251881Speter /* Get a stream to the contents. */ 970251881Speter SVN_ERR(svn_fs_fs__get_contents(&contents, file->fs, 971299742Sdim noderev->data_rep, TRUE, pool)); 972251881Speter 973251881Speter *contents_p = contents; 974251881Speter 975251881Speter return SVN_NO_ERROR; 976251881Speter} 977251881Speter 978251881Speter 979251881Spetersvn_error_t * 980251881Spetersvn_fs_fs__dag_get_file_delta_stream(svn_txdelta_stream_t **stream_p, 981251881Speter dag_node_t *source, 982251881Speter dag_node_t *target, 983251881Speter apr_pool_t *pool) 984251881Speter{ 985251881Speter node_revision_t *src_noderev; 986251881Speter node_revision_t *tgt_noderev; 987251881Speter 988251881Speter /* Make sure our nodes are files. */ 989251881Speter if ((source && source->kind != svn_node_file) 990251881Speter || target->kind != svn_node_file) 991251881Speter return svn_error_createf 992251881Speter (SVN_ERR_FS_NOT_FILE, NULL, 993251881Speter "Attempted to get textual contents of a *non*-file node"); 994251881Speter 995251881Speter /* Go get fresh node-revisions for the nodes. */ 996251881Speter if (source) 997251881Speter SVN_ERR(get_node_revision(&src_noderev, source)); 998251881Speter else 999251881Speter src_noderev = NULL; 1000251881Speter SVN_ERR(get_node_revision(&tgt_noderev, target)); 1001251881Speter 1002251881Speter /* Get the delta stream. */ 1003251881Speter return svn_fs_fs__get_file_delta_stream(stream_p, target->fs, 1004251881Speter src_noderev, tgt_noderev, pool); 1005251881Speter} 1006251881Speter 1007251881Speter 1008251881Spetersvn_error_t * 1009251881Spetersvn_fs_fs__dag_try_process_file_contents(svn_boolean_t *success, 1010251881Speter dag_node_t *node, 1011251881Speter svn_fs_process_contents_func_t processor, 1012251881Speter void* baton, 1013251881Speter apr_pool_t *pool) 1014251881Speter{ 1015251881Speter node_revision_t *noderev; 1016251881Speter 1017251881Speter /* Go get fresh node-revisions for the nodes. */ 1018251881Speter SVN_ERR(get_node_revision(&noderev, node)); 1019251881Speter 1020251881Speter return svn_fs_fs__try_process_file_contents(success, node->fs, 1021251881Speter noderev, 1022251881Speter processor, baton, pool); 1023251881Speter} 1024251881Speter 1025251881Speter 1026251881Spetersvn_error_t * 1027251881Spetersvn_fs_fs__dag_file_length(svn_filesize_t *length, 1028251881Speter dag_node_t *file, 1029251881Speter apr_pool_t *pool) 1030251881Speter{ 1031251881Speter node_revision_t *noderev; 1032251881Speter 1033251881Speter /* Make sure our node is a file. */ 1034251881Speter if (file->kind != svn_node_file) 1035251881Speter return svn_error_createf 1036251881Speter (SVN_ERR_FS_NOT_FILE, NULL, 1037251881Speter "Attempted to get length of a *non*-file node"); 1038251881Speter 1039251881Speter /* Go get a fresh node-revision for FILE, and . */ 1040251881Speter SVN_ERR(get_node_revision(&noderev, file)); 1041251881Speter 1042251881Speter return svn_fs_fs__file_length(length, noderev, pool); 1043251881Speter} 1044251881Speter 1045251881Speter 1046251881Spetersvn_error_t * 1047251881Spetersvn_fs_fs__dag_file_checksum(svn_checksum_t **checksum, 1048251881Speter dag_node_t *file, 1049251881Speter svn_checksum_kind_t kind, 1050251881Speter apr_pool_t *pool) 1051251881Speter{ 1052251881Speter node_revision_t *noderev; 1053251881Speter 1054251881Speter if (file->kind != svn_node_file) 1055251881Speter return svn_error_createf 1056251881Speter (SVN_ERR_FS_NOT_FILE, NULL, 1057251881Speter "Attempted to get checksum of a *non*-file node"); 1058251881Speter 1059251881Speter SVN_ERR(get_node_revision(&noderev, file)); 1060251881Speter 1061251881Speter return svn_fs_fs__file_checksum(checksum, noderev, kind, pool); 1062251881Speter} 1063251881Speter 1064251881Speter 1065251881Spetersvn_error_t * 1066251881Spetersvn_fs_fs__dag_get_edit_stream(svn_stream_t **contents, 1067251881Speter dag_node_t *file, 1068251881Speter apr_pool_t *pool) 1069251881Speter{ 1070251881Speter node_revision_t *noderev; 1071251881Speter svn_stream_t *ws; 1072251881Speter 1073251881Speter /* Make sure our node is a file. */ 1074251881Speter if (file->kind != svn_node_file) 1075251881Speter return svn_error_createf 1076251881Speter (SVN_ERR_FS_NOT_FILE, NULL, 1077251881Speter "Attempted to set textual contents of a *non*-file node"); 1078251881Speter 1079251881Speter /* Make sure our node is mutable. */ 1080251881Speter if (! svn_fs_fs__dag_check_mutable(file)) 1081251881Speter return svn_error_createf 1082251881Speter (SVN_ERR_FS_NOT_MUTABLE, NULL, 1083251881Speter "Attempted to set textual contents of an immutable node"); 1084251881Speter 1085251881Speter /* Get the node revision. */ 1086251881Speter SVN_ERR(get_node_revision(&noderev, file)); 1087251881Speter 1088251881Speter SVN_ERR(svn_fs_fs__set_contents(&ws, file->fs, noderev, pool)); 1089251881Speter 1090251881Speter *contents = ws; 1091251881Speter 1092251881Speter return SVN_NO_ERROR; 1093251881Speter} 1094251881Speter 1095251881Speter 1096251881Speter 1097251881Spetersvn_error_t * 1098251881Spetersvn_fs_fs__dag_finalize_edits(dag_node_t *file, 1099251881Speter const svn_checksum_t *checksum, 1100251881Speter apr_pool_t *pool) 1101251881Speter{ 1102251881Speter if (checksum) 1103251881Speter { 1104251881Speter svn_checksum_t *file_checksum; 1105251881Speter 1106251881Speter SVN_ERR(svn_fs_fs__dag_file_checksum(&file_checksum, file, 1107251881Speter checksum->kind, pool)); 1108251881Speter if (!svn_checksum_match(checksum, file_checksum)) 1109251881Speter return svn_checksum_mismatch_err(checksum, file_checksum, pool, 1110251881Speter _("Checksum mismatch for '%s'"), 1111251881Speter file->created_path); 1112251881Speter } 1113251881Speter 1114251881Speter return SVN_NO_ERROR; 1115251881Speter} 1116251881Speter 1117251881Speter 1118251881Speterdag_node_t * 1119251881Spetersvn_fs_fs__dag_dup(const dag_node_t *node, 1120251881Speter apr_pool_t *pool) 1121251881Speter{ 1122251881Speter /* Allocate our new node. */ 1123251881Speter dag_node_t *new_node = apr_pcalloc(pool, sizeof(*new_node)); 1124251881Speter 1125251881Speter new_node->fs = node->fs; 1126251881Speter new_node->id = svn_fs_fs__id_copy(node->id, pool); 1127251881Speter new_node->kind = node->kind; 1128251881Speter new_node->created_path = apr_pstrdup(pool, node->created_path); 1129251881Speter 1130251881Speter /* Only copy cached node_revision_t for immutable nodes. */ 1131251881Speter if (node->node_revision && !svn_fs_fs__dag_check_mutable(node)) 1132251881Speter { 1133251881Speter new_node->node_revision = copy_node_revision(node->node_revision, pool); 1134251881Speter new_node->node_revision->id = 1135251881Speter svn_fs_fs__id_copy(node->node_revision->id, pool); 1136251881Speter new_node->node_revision->is_fresh_txn_root = 1137251881Speter node->node_revision->is_fresh_txn_root; 1138251881Speter } 1139251881Speter new_node->node_pool = pool; 1140251881Speter 1141251881Speter return new_node; 1142251881Speter} 1143251881Speter 1144251881Spetersvn_error_t * 1145251881Spetersvn_fs_fs__dag_serialize(void **data, 1146251881Speter apr_size_t *data_len, 1147251881Speter void *in, 1148251881Speter apr_pool_t *pool) 1149251881Speter{ 1150251881Speter dag_node_t *node = in; 1151251881Speter svn_stringbuf_t *serialized; 1152251881Speter 1153251881Speter /* create an serialization context and serialize the dag node as root */ 1154251881Speter svn_temp_serializer__context_t *context = 1155251881Speter svn_temp_serializer__init(node, 1156251881Speter sizeof(*node), 1157251881Speter 1024 - SVN_TEMP_SERIALIZER__OVERHEAD, 1158251881Speter pool); 1159251881Speter 1160251881Speter /* for mutable nodes, we will _never_ cache the noderev */ 1161251881Speter if (node->node_revision && !svn_fs_fs__dag_check_mutable(node)) 1162251881Speter svn_fs_fs__noderev_serialize(context, &node->node_revision); 1163251881Speter else 1164251881Speter svn_temp_serializer__set_null(context, 1165251881Speter (const void * const *)&node->node_revision); 1166251881Speter 1167251881Speter /* The deserializer will use its own pool. */ 1168251881Speter svn_temp_serializer__set_null(context, 1169299742Sdim (const void * const *)&node->node_pool); 1170251881Speter 1171251881Speter /* serialize other sub-structures */ 1172251881Speter svn_fs_fs__id_serialize(context, (const svn_fs_id_t **)&node->id); 1173251881Speter svn_fs_fs__id_serialize(context, &node->fresh_root_predecessor_id); 1174251881Speter svn_temp_serializer__add_string(context, &node->created_path); 1175251881Speter 1176251881Speter /* return serialized data */ 1177251881Speter serialized = svn_temp_serializer__get(context); 1178251881Speter *data = serialized->data; 1179251881Speter *data_len = serialized->len; 1180251881Speter 1181251881Speter return SVN_NO_ERROR; 1182251881Speter} 1183251881Speter 1184251881Spetersvn_error_t * 1185251881Spetersvn_fs_fs__dag_deserialize(void **out, 1186251881Speter void *data, 1187251881Speter apr_size_t data_len, 1188251881Speter apr_pool_t *pool) 1189251881Speter{ 1190251881Speter dag_node_t *node = (dag_node_t *)data; 1191251881Speter if (data_len == 0) 1192251881Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 1193251881Speter _("Empty noderev in cache")); 1194251881Speter 1195251881Speter /* Copy the _full_ buffer as it also contains the sub-structures. */ 1196251881Speter node->fs = NULL; 1197251881Speter 1198251881Speter /* fixup all references to sub-structures */ 1199251881Speter svn_fs_fs__id_deserialize(node, &node->id); 1200251881Speter svn_fs_fs__id_deserialize(node, 1201251881Speter (svn_fs_id_t **)&node->fresh_root_predecessor_id); 1202251881Speter svn_fs_fs__noderev_deserialize(node, &node->node_revision); 1203251881Speter node->node_pool = pool; 1204251881Speter 1205251881Speter svn_temp_deserializer__resolve(node, (void**)&node->created_path); 1206251881Speter 1207251881Speter /* return result */ 1208251881Speter *out = node; 1209251881Speter 1210251881Speter return SVN_NO_ERROR; 1211251881Speter} 1212251881Speter 1213251881Spetersvn_error_t * 1214251881Spetersvn_fs_fs__dag_open(dag_node_t **child_p, 1215251881Speter dag_node_t *parent, 1216251881Speter const char *name, 1217251881Speter apr_pool_t *result_pool, 1218251881Speter apr_pool_t *scratch_pool) 1219251881Speter{ 1220251881Speter const svn_fs_id_t *node_id; 1221251881Speter 1222251881Speter /* Ensure that NAME exists in PARENT's entry list. */ 1223251881Speter SVN_ERR(dir_entry_id_from_node(&node_id, parent, name, 1224251881Speter scratch_pool, scratch_pool)); 1225251881Speter if (! node_id) 1226299742Sdim { 1227299742Sdim *child_p = NULL; 1228299742Sdim return SVN_NO_ERROR; 1229299742Sdim } 1230251881Speter 1231251881Speter /* Make sure that NAME is a single path component. */ 1232251881Speter if (! svn_path_is_single_path_component(name)) 1233251881Speter return svn_error_createf 1234251881Speter (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL, 1235251881Speter "Attempted to open node with an illegal name '%s'", name); 1236251881Speter 1237251881Speter /* Now get the node that was requested. */ 1238251881Speter return svn_fs_fs__dag_get_node(child_p, svn_fs_fs__dag_get_fs(parent), 1239251881Speter node_id, result_pool); 1240251881Speter} 1241251881Speter 1242251881Speter 1243251881Spetersvn_error_t * 1244251881Spetersvn_fs_fs__dag_copy(dag_node_t *to_node, 1245251881Speter const char *entry, 1246251881Speter dag_node_t *from_node, 1247251881Speter svn_boolean_t preserve_history, 1248251881Speter svn_revnum_t from_rev, 1249251881Speter const char *from_path, 1250299742Sdim const svn_fs_fs__id_part_t *txn_id, 1251251881Speter apr_pool_t *pool) 1252251881Speter{ 1253251881Speter const svn_fs_id_t *id; 1254251881Speter 1255251881Speter if (preserve_history) 1256251881Speter { 1257251881Speter node_revision_t *from_noderev, *to_noderev; 1258299742Sdim svn_fs_fs__id_part_t copy_id; 1259251881Speter const svn_fs_id_t *src_id = svn_fs_fs__dag_get_id(from_node); 1260251881Speter svn_fs_t *fs = svn_fs_fs__dag_get_fs(from_node); 1261251881Speter 1262251881Speter /* Make a copy of the original node revision. */ 1263251881Speter SVN_ERR(get_node_revision(&from_noderev, from_node)); 1264251881Speter to_noderev = copy_node_revision(from_noderev, pool); 1265251881Speter 1266251881Speter /* Reserve a copy ID for this new copy. */ 1267251881Speter SVN_ERR(svn_fs_fs__reserve_copy_id(©_id, fs, txn_id, pool)); 1268251881Speter 1269251881Speter /* Create a successor with its predecessor pointing at the copy 1270251881Speter source. */ 1271251881Speter to_noderev->predecessor_id = svn_fs_fs__id_copy(src_id, pool); 1272251881Speter if (to_noderev->predecessor_count != -1) 1273251881Speter to_noderev->predecessor_count++; 1274251881Speter to_noderev->created_path = 1275251881Speter svn_fspath__join(svn_fs_fs__dag_get_created_path(to_node), entry, 1276251881Speter pool); 1277251881Speter to_noderev->copyfrom_path = apr_pstrdup(pool, from_path); 1278251881Speter to_noderev->copyfrom_rev = from_rev; 1279251881Speter 1280251881Speter /* Set the copyroot equal to our own id. */ 1281251881Speter to_noderev->copyroot_path = NULL; 1282251881Speter 1283251881Speter SVN_ERR(svn_fs_fs__create_successor(&id, fs, src_id, to_noderev, 1284299742Sdim ©_id, txn_id, pool)); 1285251881Speter 1286251881Speter } 1287251881Speter else /* don't preserve history */ 1288251881Speter { 1289251881Speter id = svn_fs_fs__dag_get_id(from_node); 1290251881Speter } 1291251881Speter 1292251881Speter /* Set the entry in to_node to the new id. */ 1293251881Speter return svn_fs_fs__dag_set_entry(to_node, entry, id, from_node->kind, 1294251881Speter txn_id, pool); 1295251881Speter} 1296251881Speter 1297251881Speter 1298251881Speter 1299251881Speter/*** Comparison. ***/ 1300251881Speter 1301251881Spetersvn_error_t * 1302251881Spetersvn_fs_fs__dag_things_different(svn_boolean_t *props_changed, 1303251881Speter svn_boolean_t *contents_changed, 1304251881Speter dag_node_t *node1, 1305299742Sdim dag_node_t *node2, 1306299742Sdim svn_boolean_t strict, 1307299742Sdim apr_pool_t *pool) 1308251881Speter{ 1309251881Speter node_revision_t *noderev1, *noderev2; 1310251881Speter 1311251881Speter /* If we have no place to store our results, don't bother doing 1312251881Speter anything. */ 1313251881Speter if (! props_changed && ! contents_changed) 1314251881Speter return SVN_NO_ERROR; 1315251881Speter 1316251881Speter /* The node revision skels for these two nodes. */ 1317251881Speter SVN_ERR(get_node_revision(&noderev1, node1)); 1318251881Speter SVN_ERR(get_node_revision(&noderev2, node2)); 1319251881Speter 1320299742Sdim if (strict) 1321299742Sdim { 1322299742Sdim /* In strict mode, compare text and property representations in the 1323299742Sdim svn_fs_contents_different() / svn_fs_props_different() manner. 1324251881Speter 1325299742Sdim See the "No-op changes no longer dumped by 'svnadmin dump' in 1.9" 1326299742Sdim discussion (http://svn.haxx.se/dev/archive-2015-09/0269.shtml) and 1327299742Sdim issue #4598 (https://issues.apache.org/jira/browse/SVN-4598). */ 1328299742Sdim svn_fs_t *fs = svn_fs_fs__dag_get_fs(node1); 1329299742Sdim svn_boolean_t same; 1330251881Speter 1331299742Sdim /* Compare property keys. */ 1332299742Sdim if (props_changed != NULL) 1333299742Sdim { 1334299742Sdim SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, noderev1, 1335299742Sdim noderev2, pool)); 1336299742Sdim *props_changed = !same; 1337299742Sdim } 1338299742Sdim 1339299742Sdim /* Compare contents keys. */ 1340299742Sdim if (contents_changed != NULL) 1341299742Sdim { 1342299742Sdim SVN_ERR(svn_fs_fs__file_text_rep_equal(&same, fs, noderev1, 1343299742Sdim noderev2, pool)); 1344299742Sdim *contents_changed = !same; 1345299742Sdim } 1346299742Sdim } 1347299742Sdim else 1348299742Sdim { 1349299742Sdim /* Otherwise, compare representation keys -- as in Subversion 1.8. */ 1350299742Sdim 1351299742Sdim /* Compare property keys. */ 1352299742Sdim if (props_changed != NULL) 1353299742Sdim *props_changed = 1354299742Sdim !svn_fs_fs__noderev_same_rep_key(noderev1->prop_rep, 1355299742Sdim noderev2->prop_rep); 1356299742Sdim 1357299742Sdim /* Compare contents keys. */ 1358299742Sdim if (contents_changed != NULL) 1359299742Sdim *contents_changed = 1360299742Sdim !svn_fs_fs__noderev_same_rep_key(noderev1->data_rep, 1361299742Sdim noderev2->data_rep); 1362299742Sdim } 1363299742Sdim 1364251881Speter return SVN_NO_ERROR; 1365251881Speter} 1366251881Speter 1367251881Spetersvn_error_t * 1368251881Spetersvn_fs_fs__dag_get_copyroot(svn_revnum_t *rev, 1369251881Speter const char **path, 1370251881Speter dag_node_t *node) 1371251881Speter{ 1372251881Speter node_revision_t *noderev; 1373251881Speter 1374251881Speter /* Go get a fresh node-revision for NODE. */ 1375251881Speter SVN_ERR(get_node_revision(&noderev, node)); 1376251881Speter 1377251881Speter *rev = noderev->copyroot_rev; 1378251881Speter *path = noderev->copyroot_path; 1379251881Speter 1380251881Speter return SVN_NO_ERROR; 1381251881Speter} 1382251881Speter 1383251881Spetersvn_error_t * 1384251881Spetersvn_fs_fs__dag_get_copyfrom_rev(svn_revnum_t *rev, 1385251881Speter dag_node_t *node) 1386251881Speter{ 1387251881Speter node_revision_t *noderev; 1388251881Speter 1389251881Speter /* Go get a fresh node-revision for NODE. */ 1390251881Speter SVN_ERR(get_node_revision(&noderev, node)); 1391251881Speter 1392251881Speter *rev = noderev->copyfrom_rev; 1393251881Speter 1394251881Speter return SVN_NO_ERROR; 1395251881Speter} 1396251881Speter 1397251881Spetersvn_error_t * 1398251881Spetersvn_fs_fs__dag_get_copyfrom_path(const char **path, 1399251881Speter dag_node_t *node) 1400251881Speter{ 1401251881Speter node_revision_t *noderev; 1402251881Speter 1403251881Speter /* Go get a fresh node-revision for NODE. */ 1404251881Speter SVN_ERR(get_node_revision(&noderev, node)); 1405251881Speter 1406251881Speter *path = noderev->copyfrom_path; 1407251881Speter 1408251881Speter return SVN_NO_ERROR; 1409251881Speter} 1410251881Speter 1411251881Spetersvn_error_t * 1412251881Spetersvn_fs_fs__dag_update_ancestry(dag_node_t *target, 1413251881Speter dag_node_t *source, 1414251881Speter apr_pool_t *pool) 1415251881Speter{ 1416251881Speter node_revision_t *source_noderev, *target_noderev; 1417251881Speter 1418251881Speter if (! svn_fs_fs__dag_check_mutable(target)) 1419251881Speter return svn_error_createf 1420251881Speter (SVN_ERR_FS_NOT_MUTABLE, NULL, 1421251881Speter _("Attempted to update ancestry of non-mutable node")); 1422251881Speter 1423251881Speter SVN_ERR(get_node_revision(&source_noderev, source)); 1424251881Speter SVN_ERR(get_node_revision(&target_noderev, target)); 1425251881Speter 1426251881Speter target_noderev->predecessor_id = source->id; 1427251881Speter target_noderev->predecessor_count = source_noderev->predecessor_count; 1428251881Speter if (target_noderev->predecessor_count != -1) 1429251881Speter target_noderev->predecessor_count++; 1430251881Speter 1431251881Speter return svn_fs_fs__put_node_revision(target->fs, target->id, target_noderev, 1432251881Speter FALSE, pool); 1433251881Speter} 1434