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_time.h" 27251881Speter#include "svn_error.h" 28251881Speter#include "svn_fs.h" 29251881Speter#include "svn_hash.h" 30251881Speter#include "svn_props.h" 31251881Speter#include "svn_pools.h" 32251881Speter 33251881Speter#include "dag.h" 34251881Speter#include "err.h" 35251881Speter#include "fs.h" 36251881Speter#include "key-gen.h" 37251881Speter#include "node-rev.h" 38251881Speter#include "trail.h" 39251881Speter#include "reps-strings.h" 40251881Speter#include "revs-txns.h" 41251881Speter#include "id.h" 42251881Speter 43251881Speter#include "util/fs_skels.h" 44251881Speter 45251881Speter#include "bdb/txn-table.h" 46251881Speter#include "bdb/rev-table.h" 47251881Speter#include "bdb/nodes-table.h" 48251881Speter#include "bdb/copies-table.h" 49251881Speter#include "bdb/reps-table.h" 50251881Speter#include "bdb/strings-table.h" 51251881Speter#include "bdb/checksum-reps-table.h" 52251881Speter#include "bdb/changes-table.h" 53251881Speter#include "bdb/node-origins-table.h" 54251881Speter 55251881Speter#include "private/svn_skel.h" 56251881Speter#include "private/svn_fs_util.h" 57251881Speter#include "private/svn_fspath.h" 58251881Speter#include "../libsvn_fs/fs-loader.h" 59251881Speter 60251881Speter#include "svn_private_config.h" 61251881Speter 62251881Speter 63251881Speter/* Initializing a filesystem. */ 64251881Speter 65251881Speterstruct dag_node_t 66251881Speter{ 67251881Speter /*** NOTE: Keeping in-memory representations of disk data that can 68251881Speter be changed by other accessors is a nasty business. Such 69251881Speter representations are basically a cache with some pretty complex 70251881Speter invalidation rules. For example, the "node revision" 71251881Speter associated with a DAG node ID can look completely different to 72251881Speter a process that has modified that information as part of a 73251881Speter Berkeley DB transaction than it does to some other process. 74251881Speter That said, there are some aspects of a "node revision" which 75251881Speter never change, like its 'id' or 'kind'. Our best bet is to 76251881Speter limit ourselves to exposing outside of this interface only 77251881Speter those immutable aspects of a DAG node representation. ***/ 78251881Speter 79251881Speter /* The filesystem this dag node came from. */ 80251881Speter svn_fs_t *fs; 81251881Speter 82251881Speter /* The node revision ID for this dag node. */ 83251881Speter svn_fs_id_t *id; 84251881Speter 85251881Speter /* The node's type (file, dir, etc.) */ 86251881Speter svn_node_kind_t kind; 87251881Speter 88251881Speter /* the path at which this node was created. */ 89251881Speter const char *created_path; 90251881Speter}; 91251881Speter 92251881Speter 93251881Speter 94251881Speter/* Trivial helper/accessor functions. */ 95251881Spetersvn_node_kind_t svn_fs_base__dag_node_kind(dag_node_t *node) 96251881Speter{ 97251881Speter return node->kind; 98251881Speter} 99251881Speter 100251881Speter 101251881Speterconst svn_fs_id_t * 102251881Spetersvn_fs_base__dag_get_id(dag_node_t *node) 103251881Speter{ 104251881Speter return node->id; 105251881Speter} 106251881Speter 107251881Speter 108251881Speterconst char * 109251881Spetersvn_fs_base__dag_get_created_path(dag_node_t *node) 110251881Speter{ 111251881Speter return node->created_path; 112251881Speter} 113251881Speter 114251881Speter 115251881Spetersvn_fs_t * 116251881Spetersvn_fs_base__dag_get_fs(dag_node_t *node) 117251881Speter{ 118251881Speter return node->fs; 119251881Speter} 120251881Speter 121251881Speter 122251881Spetersvn_boolean_t svn_fs_base__dag_check_mutable(dag_node_t *node, 123251881Speter const char *txn_id) 124251881Speter{ 125251881Speter return (strcmp(svn_fs_base__id_txn_id(svn_fs_base__dag_get_id(node)), 126251881Speter txn_id) == 0); 127251881Speter} 128251881Speter 129251881Speter 130251881Spetersvn_error_t * 131251881Spetersvn_fs_base__dag_get_node(dag_node_t **node, 132251881Speter svn_fs_t *fs, 133251881Speter const svn_fs_id_t *id, 134251881Speter trail_t *trail, 135251881Speter apr_pool_t *pool) 136251881Speter{ 137251881Speter dag_node_t *new_node; 138251881Speter node_revision_t *noderev; 139251881Speter 140251881Speter /* Construct the node. */ 141251881Speter new_node = apr_pcalloc(pool, sizeof(*new_node)); 142251881Speter new_node->fs = fs; 143251881Speter new_node->id = svn_fs_base__id_copy(id, pool); 144251881Speter 145251881Speter /* Grab the contents so we can cache some of the immutable parts of it. */ 146251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, id, trail, pool)); 147251881Speter 148251881Speter /* Initialize the KIND and CREATED_PATH attributes */ 149251881Speter new_node->kind = noderev->kind; 150251881Speter new_node->created_path = noderev->created_path; 151251881Speter 152251881Speter /* Return a fresh new node */ 153251881Speter *node = new_node; 154251881Speter return SVN_NO_ERROR; 155251881Speter} 156251881Speter 157251881Speter 158251881Spetersvn_error_t * 159251881Spetersvn_fs_base__dag_get_revision(svn_revnum_t *rev, 160251881Speter dag_node_t *node, 161251881Speter trail_t *trail, 162251881Speter apr_pool_t *pool) 163251881Speter{ 164251881Speter /* Use the txn ID from the NODE's id to look up the transaction and 165251881Speter get its revision number. */ 166251881Speter return svn_fs_base__txn_get_revision 167251881Speter (rev, svn_fs_base__dag_get_fs(node), 168251881Speter svn_fs_base__id_txn_id(svn_fs_base__dag_get_id(node)), trail, pool); 169251881Speter} 170251881Speter 171251881Speter 172251881Spetersvn_error_t * 173251881Spetersvn_fs_base__dag_get_predecessor_id(const svn_fs_id_t **id_p, 174251881Speter dag_node_t *node, 175251881Speter trail_t *trail, 176251881Speter apr_pool_t *pool) 177251881Speter{ 178251881Speter node_revision_t *noderev; 179251881Speter 180251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, node->fs, node->id, 181251881Speter trail, pool)); 182251881Speter *id_p = noderev->predecessor_id; 183251881Speter return SVN_NO_ERROR; 184251881Speter} 185251881Speter 186251881Speter 187251881Spetersvn_error_t * 188251881Spetersvn_fs_base__dag_get_predecessor_count(int *count, 189251881Speter dag_node_t *node, 190251881Speter trail_t *trail, 191251881Speter apr_pool_t *pool) 192251881Speter{ 193251881Speter node_revision_t *noderev; 194251881Speter 195251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, node->fs, node->id, 196251881Speter trail, pool)); 197251881Speter *count = noderev->predecessor_count; 198251881Speter return SVN_NO_ERROR; 199251881Speter} 200251881Speter 201251881Speter 202251881Speter/* Trail body for svn_fs_base__dag_init_fs. */ 203251881Speterstatic svn_error_t * 204251881Spetertxn_body_dag_init_fs(void *baton, 205251881Speter trail_t *trail) 206251881Speter{ 207251881Speter node_revision_t noderev; 208251881Speter revision_t revision; 209251881Speter svn_revnum_t rev = SVN_INVALID_REVNUM; 210251881Speter svn_fs_t *fs = trail->fs; 211251881Speter svn_string_t date; 212251881Speter const char *txn_id; 213251881Speter const char *copy_id; 214251881Speter svn_fs_id_t *root_id = svn_fs_base__id_create("0", "0", "0", trail->pool); 215251881Speter 216251881Speter /* Create empty root directory with node revision 0.0.0. */ 217251881Speter memset(&noderev, 0, sizeof(noderev)); 218251881Speter noderev.kind = svn_node_dir; 219251881Speter noderev.created_path = "/"; 220251881Speter SVN_ERR(svn_fs_bdb__put_node_revision(fs, root_id, &noderev, 221251881Speter trail, trail->pool)); 222251881Speter 223251881Speter /* Create a new transaction (better have an id of "0") */ 224251881Speter SVN_ERR(svn_fs_bdb__create_txn(&txn_id, fs, root_id, trail, trail->pool)); 225251881Speter if (strcmp(txn_id, "0")) 226251881Speter return svn_error_createf 227251881Speter (SVN_ERR_FS_CORRUPT, 0, 228251881Speter _("Corrupt DB: initial transaction id not '0' in filesystem '%s'"), 229251881Speter fs->path); 230251881Speter 231251881Speter /* Create a default copy (better have an id of "0") */ 232251881Speter SVN_ERR(svn_fs_bdb__reserve_copy_id(©_id, fs, trail, trail->pool)); 233251881Speter if (strcmp(copy_id, "0")) 234251881Speter return svn_error_createf 235251881Speter (SVN_ERR_FS_CORRUPT, 0, 236251881Speter _("Corrupt DB: initial copy id not '0' in filesystem '%s'"), fs->path); 237251881Speter SVN_ERR(svn_fs_bdb__create_copy(fs, copy_id, NULL, NULL, root_id, 238251881Speter copy_kind_real, trail, trail->pool)); 239251881Speter 240251881Speter /* Link it into filesystem revision 0. */ 241251881Speter revision.txn_id = txn_id; 242251881Speter SVN_ERR(svn_fs_bdb__put_rev(&rev, fs, &revision, trail, trail->pool)); 243251881Speter if (rev != 0) 244251881Speter return svn_error_createf(SVN_ERR_FS_CORRUPT, 0, 245251881Speter _("Corrupt DB: initial revision number " 246251881Speter "is not '0' in filesystem '%s'"), fs->path); 247251881Speter 248251881Speter /* Promote our transaction to a "committed" transaction. */ 249251881Speter SVN_ERR(svn_fs_base__txn_make_committed(fs, txn_id, rev, 250251881Speter trail, trail->pool)); 251251881Speter 252251881Speter /* Set a date on revision 0. */ 253251881Speter date.data = svn_time_to_cstring(apr_time_now(), trail->pool); 254251881Speter date.len = strlen(date.data); 255251881Speter return svn_fs_base__set_rev_prop(fs, 0, SVN_PROP_REVISION_DATE, NULL, &date, 256251881Speter trail, trail->pool); 257251881Speter} 258251881Speter 259251881Speter 260251881Spetersvn_error_t * 261251881Spetersvn_fs_base__dag_init_fs(svn_fs_t *fs) 262251881Speter{ 263251881Speter return svn_fs_base__retry_txn(fs, txn_body_dag_init_fs, NULL, 264251881Speter TRUE, fs->pool); 265251881Speter} 266251881Speter 267251881Speter 268251881Speter 269251881Speter/*** Directory node functions ***/ 270251881Speter 271251881Speter/* Some of these are helpers for functions outside this section. */ 272251881Speter 273251881Speter/* Given directory NODEREV in FS, set *ENTRIES_P to its entries list 274251881Speter hash, as part of TRAIL, or to NULL if NODEREV has no entries. The 275251881Speter entries list will be allocated in POOL, and the entries in that 276251881Speter list will not have interesting value in their 'kind' fields. If 277251881Speter NODEREV is not a directory, return the error SVN_ERR_FS_NOT_DIRECTORY. */ 278251881Speterstatic svn_error_t * 279251881Speterget_dir_entries(apr_hash_t **entries_p, 280251881Speter svn_fs_t *fs, 281251881Speter node_revision_t *noderev, 282251881Speter trail_t *trail, 283251881Speter apr_pool_t *pool) 284251881Speter{ 285251881Speter apr_hash_t *entries = NULL; 286251881Speter apr_hash_index_t *hi; 287251881Speter svn_string_t entries_raw; 288251881Speter svn_skel_t *entries_skel; 289251881Speter 290251881Speter /* Error if this is not a directory. */ 291251881Speter if (noderev->kind != svn_node_dir) 292251881Speter return svn_error_create 293251881Speter (SVN_ERR_FS_NOT_DIRECTORY, NULL, 294251881Speter _("Attempted to get entries of a non-directory node")); 295251881Speter 296251881Speter /* If there's a DATA-KEY, there might be entries to fetch. */ 297251881Speter if (noderev->data_key) 298251881Speter { 299251881Speter /* Now we have a rep, follow through to get the entries. */ 300251881Speter SVN_ERR(svn_fs_base__rep_contents(&entries_raw, fs, noderev->data_key, 301251881Speter trail, pool)); 302251881Speter entries_skel = svn_skel__parse(entries_raw.data, entries_raw.len, pool); 303251881Speter 304251881Speter /* Were there entries? Make a hash from them. */ 305251881Speter if (entries_skel) 306251881Speter SVN_ERR(svn_fs_base__parse_entries_skel(&entries, entries_skel, 307251881Speter pool)); 308251881Speter } 309251881Speter 310251881Speter /* No hash? No problem. */ 311251881Speter *entries_p = NULL; 312251881Speter if (! entries) 313251881Speter return SVN_NO_ERROR; 314251881Speter 315251881Speter /* Else, convert the hash from a name->id mapping to a name->dirent one. */ 316251881Speter *entries_p = apr_hash_make(pool); 317251881Speter for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) 318251881Speter { 319251881Speter const void *key; 320251881Speter apr_ssize_t klen; 321251881Speter void *val; 322251881Speter svn_fs_dirent_t *dirent = apr_palloc(pool, sizeof(*dirent)); 323251881Speter 324251881Speter /* KEY will be the entry name in ancestor, VAL the id. */ 325251881Speter apr_hash_this(hi, &key, &klen, &val); 326251881Speter dirent->name = key; 327251881Speter dirent->id = val; 328251881Speter dirent->kind = svn_node_unknown; 329251881Speter apr_hash_set(*entries_p, key, klen, dirent); 330251881Speter } 331251881Speter 332251881Speter /* Return our findings. */ 333251881Speter return SVN_NO_ERROR; 334251881Speter} 335251881Speter 336251881Speter 337251881Speter/* Set *ID_P to the node-id for entry NAME in PARENT, as part of 338251881Speter TRAIL. If no such entry, set *ID_P to NULL but do not error. The 339251881Speter entry is allocated in POOL or in the same pool as PARENT; 340251881Speter the caller should copy if it cares. */ 341251881Speterstatic svn_error_t * 342251881Speterdir_entry_id_from_node(const svn_fs_id_t **id_p, 343251881Speter dag_node_t *parent, 344251881Speter const char *name, 345251881Speter trail_t *trail, 346251881Speter apr_pool_t *pool) 347251881Speter{ 348251881Speter apr_hash_t *entries; 349251881Speter svn_fs_dirent_t *dirent; 350251881Speter 351251881Speter SVN_ERR(svn_fs_base__dag_dir_entries(&entries, parent, trail, pool)); 352251881Speter if (entries) 353251881Speter dirent = svn_hash_gets(entries, name); 354251881Speter else 355251881Speter dirent = NULL; 356251881Speter 357251881Speter *id_p = dirent ? dirent->id : NULL; 358251881Speter return SVN_NO_ERROR; 359251881Speter} 360251881Speter 361251881Speter 362251881Speter/* Add or set in PARENT a directory entry NAME pointing to ID. 363251881Speter Allocations are done in TRAIL. 364251881Speter 365251881Speter Assumptions: 366251881Speter - PARENT is a mutable directory. 367251881Speter - ID does not refer to an ancestor of parent 368251881Speter - NAME is a single path component 369251881Speter*/ 370251881Speterstatic svn_error_t * 371251881Speterset_entry(dag_node_t *parent, 372251881Speter const char *name, 373251881Speter const svn_fs_id_t *id, 374251881Speter const char *txn_id, 375251881Speter trail_t *trail, 376251881Speter apr_pool_t *pool) 377251881Speter{ 378251881Speter node_revision_t *parent_noderev; 379251881Speter const char *rep_key, *mutable_rep_key; 380251881Speter apr_hash_t *entries = NULL; 381251881Speter svn_stream_t *wstream; 382251881Speter apr_size_t len; 383251881Speter svn_string_t raw_entries; 384251881Speter svn_stringbuf_t *raw_entries_buf; 385251881Speter svn_skel_t *entries_skel; 386251881Speter svn_fs_t *fs = svn_fs_base__dag_get_fs(parent); 387251881Speter 388251881Speter /* Get the parent's node-revision. */ 389251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&parent_noderev, fs, parent->id, 390251881Speter trail, pool)); 391251881Speter rep_key = parent_noderev->data_key; 392251881Speter SVN_ERR(svn_fs_base__get_mutable_rep(&mutable_rep_key, rep_key, 393251881Speter fs, txn_id, trail, pool)); 394251881Speter 395251881Speter /* If the parent node already pointed at a mutable representation, 396251881Speter we don't need to do anything. But if it didn't, either because 397251881Speter the parent didn't refer to any rep yet or because it referred to 398251881Speter an immutable one, we must make the parent refer to the mutable 399251881Speter rep we just created. */ 400251881Speter if (! svn_fs_base__same_keys(rep_key, mutable_rep_key)) 401251881Speter { 402251881Speter parent_noderev->data_key = mutable_rep_key; 403251881Speter SVN_ERR(svn_fs_bdb__put_node_revision(fs, parent->id, parent_noderev, 404251881Speter trail, pool)); 405251881Speter } 406251881Speter 407251881Speter /* If the new representation inherited nothing, start a new entries 408251881Speter list for it. Else, go read its existing entries list. */ 409251881Speter if (rep_key) 410251881Speter { 411251881Speter SVN_ERR(svn_fs_base__rep_contents(&raw_entries, fs, rep_key, 412251881Speter trail, pool)); 413251881Speter entries_skel = svn_skel__parse(raw_entries.data, raw_entries.len, pool); 414251881Speter if (entries_skel) 415251881Speter SVN_ERR(svn_fs_base__parse_entries_skel(&entries, entries_skel, 416251881Speter pool)); 417251881Speter } 418251881Speter 419251881Speter /* If we still have no ENTRIES hash, make one here. */ 420251881Speter if (! entries) 421251881Speter entries = apr_hash_make(pool); 422251881Speter 423251881Speter /* Now, add our new entry to the entries list. */ 424251881Speter svn_hash_sets(entries, name, id); 425251881Speter 426251881Speter /* Finally, replace the old entries list with the new one. */ 427251881Speter SVN_ERR(svn_fs_base__unparse_entries_skel(&entries_skel, entries, 428251881Speter pool)); 429251881Speter raw_entries_buf = svn_skel__unparse(entries_skel, pool); 430251881Speter SVN_ERR(svn_fs_base__rep_contents_write_stream(&wstream, fs, 431251881Speter mutable_rep_key, txn_id, 432251881Speter TRUE, trail, pool)); 433251881Speter len = raw_entries_buf->len; 434251881Speter SVN_ERR(svn_stream_write(wstream, raw_entries_buf->data, &len)); 435251881Speter return svn_stream_close(wstream); 436251881Speter} 437251881Speter 438251881Speter 439251881Speter/* Make a new entry named NAME in PARENT, as part of TRAIL. If IS_DIR 440251881Speter is true, then the node revision the new entry points to will be a 441251881Speter directory, else it will be a file. The new node will be allocated 442251881Speter in POOL. PARENT must be mutable, and must not have an entry 443251881Speter named NAME. */ 444251881Speterstatic svn_error_t * 445251881Spetermake_entry(dag_node_t **child_p, 446251881Speter dag_node_t *parent, 447251881Speter const char *parent_path, 448251881Speter const char *name, 449251881Speter svn_boolean_t is_dir, 450251881Speter const char *txn_id, 451251881Speter trail_t *trail, 452251881Speter apr_pool_t *pool) 453251881Speter{ 454251881Speter const svn_fs_id_t *new_node_id; 455251881Speter node_revision_t new_noderev; 456251881Speter 457251881Speter /* Make sure that NAME is a single path component. */ 458251881Speter if (! svn_path_is_single_path_component(name)) 459251881Speter return svn_error_createf 460251881Speter (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL, 461251881Speter _("Attempted to create a node with an illegal name '%s'"), name); 462251881Speter 463251881Speter /* Make sure that parent is a directory */ 464251881Speter if (parent->kind != svn_node_dir) 465251881Speter return svn_error_create 466251881Speter (SVN_ERR_FS_NOT_DIRECTORY, NULL, 467251881Speter _("Attempted to create entry in non-directory parent")); 468251881Speter 469251881Speter /* Check that the parent is mutable. */ 470251881Speter if (! svn_fs_base__dag_check_mutable(parent, txn_id)) 471251881Speter return svn_error_createf 472251881Speter (SVN_ERR_FS_NOT_MUTABLE, NULL, 473251881Speter _("Attempted to clone child of non-mutable node")); 474251881Speter 475251881Speter /* Check that parent does not already have an entry named NAME. */ 476251881Speter SVN_ERR(dir_entry_id_from_node(&new_node_id, parent, name, trail, pool)); 477251881Speter if (new_node_id) 478251881Speter return svn_error_createf 479251881Speter (SVN_ERR_FS_ALREADY_EXISTS, NULL, 480251881Speter _("Attempted to create entry that already exists")); 481251881Speter 482251881Speter /* Create the new node's NODE-REVISION */ 483251881Speter memset(&new_noderev, 0, sizeof(new_noderev)); 484251881Speter new_noderev.kind = is_dir ? svn_node_dir : svn_node_file; 485251881Speter new_noderev.created_path = svn_fspath__join(parent_path, name, pool); 486251881Speter SVN_ERR(svn_fs_base__create_node 487251881Speter (&new_node_id, svn_fs_base__dag_get_fs(parent), &new_noderev, 488251881Speter svn_fs_base__id_copy_id(svn_fs_base__dag_get_id(parent)), 489251881Speter txn_id, trail, pool)); 490251881Speter 491251881Speter /* Create a new dag_node_t for our new node */ 492251881Speter SVN_ERR(svn_fs_base__dag_get_node(child_p, 493251881Speter svn_fs_base__dag_get_fs(parent), 494251881Speter new_node_id, trail, pool)); 495251881Speter 496251881Speter /* We can safely call set_entry because we already know that 497251881Speter PARENT is mutable, and we just created CHILD, so we know it has 498251881Speter no ancestors (therefore, PARENT cannot be an ancestor of CHILD) */ 499251881Speter return set_entry(parent, name, svn_fs_base__dag_get_id(*child_p), 500251881Speter txn_id, trail, pool); 501251881Speter} 502251881Speter 503251881Speter 504251881Spetersvn_error_t * 505251881Spetersvn_fs_base__dag_dir_entries(apr_hash_t **entries, 506251881Speter dag_node_t *node, 507251881Speter trail_t *trail, 508251881Speter apr_pool_t *pool) 509251881Speter{ 510251881Speter node_revision_t *noderev; 511251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, node->fs, node->id, 512251881Speter trail, pool)); 513251881Speter return get_dir_entries(entries, node->fs, noderev, trail, pool); 514251881Speter} 515251881Speter 516251881Speter 517251881Spetersvn_error_t * 518251881Spetersvn_fs_base__dag_set_entry(dag_node_t *node, 519251881Speter const char *entry_name, 520251881Speter const svn_fs_id_t *id, 521251881Speter const char *txn_id, 522251881Speter trail_t *trail, 523251881Speter apr_pool_t *pool) 524251881Speter{ 525251881Speter /* Check it's a directory. */ 526251881Speter if (node->kind != svn_node_dir) 527251881Speter return svn_error_create 528251881Speter (SVN_ERR_FS_NOT_DIRECTORY, NULL, 529251881Speter _("Attempted to set entry in non-directory node")); 530251881Speter 531251881Speter /* Check it's mutable. */ 532251881Speter if (! svn_fs_base__dag_check_mutable(node, txn_id)) 533251881Speter return svn_error_create 534251881Speter (SVN_ERR_FS_NOT_MUTABLE, NULL, 535251881Speter _("Attempted to set entry in immutable node")); 536251881Speter 537251881Speter return set_entry(node, entry_name, id, txn_id, trail, pool); 538251881Speter} 539251881Speter 540251881Speter 541251881Speter 542251881Speter/*** Proplists. ***/ 543251881Speter 544251881Spetersvn_error_t * 545251881Spetersvn_fs_base__dag_get_proplist(apr_hash_t **proplist_p, 546251881Speter dag_node_t *node, 547251881Speter trail_t *trail, 548251881Speter apr_pool_t *pool) 549251881Speter{ 550251881Speter node_revision_t *noderev; 551251881Speter apr_hash_t *proplist = NULL; 552251881Speter svn_string_t raw_proplist; 553251881Speter svn_skel_t *proplist_skel; 554251881Speter 555251881Speter /* Go get a fresh NODE-REVISION for this node. */ 556251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, node->fs, node->id, 557251881Speter trail, pool)); 558251881Speter 559251881Speter /* Get property key (returning early if there isn't one) . */ 560251881Speter if (! noderev->prop_key) 561251881Speter { 562251881Speter *proplist_p = NULL; 563251881Speter return SVN_NO_ERROR; 564251881Speter } 565251881Speter 566251881Speter /* Get the string associated with the property rep, parsing it as a 567251881Speter skel, and then attempt to parse *that* into a property hash. */ 568251881Speter SVN_ERR(svn_fs_base__rep_contents(&raw_proplist, 569251881Speter svn_fs_base__dag_get_fs(node), 570251881Speter noderev->prop_key, trail, pool)); 571251881Speter proplist_skel = svn_skel__parse(raw_proplist.data, raw_proplist.len, pool); 572251881Speter if (proplist_skel) 573251881Speter SVN_ERR(svn_skel__parse_proplist(&proplist, proplist_skel, pool)); 574251881Speter 575251881Speter *proplist_p = proplist; 576251881Speter return SVN_NO_ERROR; 577251881Speter} 578251881Speter 579251881Speter 580251881Spetersvn_error_t * 581251881Spetersvn_fs_base__dag_set_proplist(dag_node_t *node, 582251881Speter const apr_hash_t *proplist, 583251881Speter const char *txn_id, 584251881Speter trail_t *trail, 585251881Speter apr_pool_t *pool) 586251881Speter{ 587251881Speter node_revision_t *noderev; 588251881Speter const char *rep_key, *mutable_rep_key; 589251881Speter svn_fs_t *fs = svn_fs_base__dag_get_fs(node); 590251881Speter svn_stream_t *wstream; 591251881Speter apr_size_t len; 592251881Speter svn_skel_t *proplist_skel; 593251881Speter svn_stringbuf_t *raw_proplist_buf; 594251881Speter base_fs_data_t *bfd = fs->fsap_data; 595251881Speter 596251881Speter /* Sanity check: this node better be mutable! */ 597251881Speter if (! svn_fs_base__dag_check_mutable(node, txn_id)) 598251881Speter { 599251881Speter svn_string_t *idstr = svn_fs_base__id_unparse(node->id, pool); 600251881Speter return svn_error_createf 601251881Speter (SVN_ERR_FS_NOT_MUTABLE, NULL, 602251881Speter _("Can't set proplist on *immutable* node-revision %s"), 603251881Speter idstr->data); 604251881Speter } 605251881Speter 606251881Speter /* Go get a fresh NODE-REVISION for this node. */ 607251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, node->id, 608251881Speter trail, pool)); 609251881Speter rep_key = noderev->prop_key; 610251881Speter 611251881Speter /* Flatten the proplist into a string. */ 612251881Speter SVN_ERR(svn_skel__unparse_proplist(&proplist_skel, proplist, pool)); 613251881Speter raw_proplist_buf = svn_skel__unparse(proplist_skel, pool); 614251881Speter 615251881Speter /* If this repository supports representation sharing, and the 616251881Speter resulting property list is exactly the same as another string in 617251881Speter the database, just use the previously existing string and get 618251881Speter outta here. */ 619251881Speter if (bfd->format >= SVN_FS_BASE__MIN_REP_SHARING_FORMAT) 620251881Speter { 621251881Speter svn_error_t *err; 622251881Speter const char *dup_rep_key; 623251881Speter svn_checksum_t *checksum; 624251881Speter 625251881Speter SVN_ERR(svn_checksum(&checksum, svn_checksum_sha1, raw_proplist_buf->data, 626251881Speter raw_proplist_buf->len, pool)); 627251881Speter 628251881Speter err = svn_fs_bdb__get_checksum_rep(&dup_rep_key, fs, checksum, 629251881Speter trail, pool); 630251881Speter if (! err) 631251881Speter { 632251881Speter if (noderev->prop_key) 633251881Speter SVN_ERR(svn_fs_base__delete_rep_if_mutable(fs, noderev->prop_key, 634251881Speter txn_id, trail, pool)); 635251881Speter noderev->prop_key = dup_rep_key; 636251881Speter return svn_fs_bdb__put_node_revision(fs, node->id, noderev, 637251881Speter trail, pool); 638251881Speter } 639251881Speter else if (err) 640251881Speter { 641251881Speter if (err->apr_err != SVN_ERR_FS_NO_SUCH_CHECKSUM_REP) 642251881Speter return svn_error_trace(err); 643251881Speter 644251881Speter svn_error_clear(err); 645251881Speter err = SVN_NO_ERROR; 646251881Speter } 647251881Speter } 648251881Speter 649251881Speter /* Get a mutable version of this rep (updating the node revision if 650251881Speter this isn't a NOOP) */ 651251881Speter SVN_ERR(svn_fs_base__get_mutable_rep(&mutable_rep_key, rep_key, 652251881Speter fs, txn_id, trail, pool)); 653251881Speter if (! svn_fs_base__same_keys(mutable_rep_key, rep_key)) 654251881Speter { 655251881Speter noderev->prop_key = mutable_rep_key; 656251881Speter SVN_ERR(svn_fs_bdb__put_node_revision(fs, node->id, noderev, 657251881Speter trail, pool)); 658251881Speter } 659251881Speter 660251881Speter /* Replace the old property list with the new one. */ 661251881Speter SVN_ERR(svn_fs_base__rep_contents_write_stream(&wstream, fs, 662251881Speter mutable_rep_key, txn_id, 663251881Speter TRUE, trail, pool)); 664251881Speter len = raw_proplist_buf->len; 665251881Speter SVN_ERR(svn_stream_write(wstream, raw_proplist_buf->data, &len)); 666251881Speter SVN_ERR(svn_stream_close(wstream)); 667251881Speter 668251881Speter return SVN_NO_ERROR; 669251881Speter} 670251881Speter 671251881Speter 672251881Speter 673251881Speter/*** Roots. ***/ 674251881Speter 675251881Spetersvn_error_t * 676251881Spetersvn_fs_base__dag_revision_root(dag_node_t **node_p, 677251881Speter svn_fs_t *fs, 678251881Speter svn_revnum_t rev, 679251881Speter trail_t *trail, 680251881Speter apr_pool_t *pool) 681251881Speter{ 682251881Speter const svn_fs_id_t *root_id; 683251881Speter 684251881Speter SVN_ERR(svn_fs_base__rev_get_root(&root_id, fs, rev, trail, pool)); 685251881Speter return svn_fs_base__dag_get_node(node_p, fs, root_id, trail, pool); 686251881Speter} 687251881Speter 688251881Speter 689251881Spetersvn_error_t * 690251881Spetersvn_fs_base__dag_txn_root(dag_node_t **node_p, 691251881Speter svn_fs_t *fs, 692251881Speter const char *txn_id, 693251881Speter trail_t *trail, 694251881Speter apr_pool_t *pool) 695251881Speter{ 696251881Speter const svn_fs_id_t *root_id, *ignored; 697251881Speter 698251881Speter SVN_ERR(svn_fs_base__get_txn_ids(&root_id, &ignored, fs, txn_id, 699251881Speter trail, pool)); 700251881Speter return svn_fs_base__dag_get_node(node_p, fs, root_id, trail, pool); 701251881Speter} 702251881Speter 703251881Speter 704251881Spetersvn_error_t * 705251881Spetersvn_fs_base__dag_txn_base_root(dag_node_t **node_p, 706251881Speter svn_fs_t *fs, 707251881Speter const char *txn_id, 708251881Speter trail_t *trail, 709251881Speter apr_pool_t *pool) 710251881Speter{ 711251881Speter const svn_fs_id_t *base_root_id, *ignored; 712251881Speter 713251881Speter SVN_ERR(svn_fs_base__get_txn_ids(&ignored, &base_root_id, fs, txn_id, 714251881Speter trail, pool)); 715251881Speter return svn_fs_base__dag_get_node(node_p, fs, base_root_id, trail, pool); 716251881Speter} 717251881Speter 718251881Speter 719251881Spetersvn_error_t * 720251881Spetersvn_fs_base__dag_clone_child(dag_node_t **child_p, 721251881Speter dag_node_t *parent, 722251881Speter const char *parent_path, 723251881Speter const char *name, 724251881Speter const char *copy_id, 725251881Speter const char *txn_id, 726251881Speter trail_t *trail, 727251881Speter apr_pool_t *pool) 728251881Speter{ 729251881Speter dag_node_t *cur_entry; /* parent's current entry named NAME */ 730251881Speter const svn_fs_id_t *new_node_id; /* node id we'll put into NEW_NODE */ 731251881Speter svn_fs_t *fs = svn_fs_base__dag_get_fs(parent); 732251881Speter 733251881Speter /* First check that the parent is mutable. */ 734251881Speter if (! svn_fs_base__dag_check_mutable(parent, txn_id)) 735251881Speter return svn_error_createf 736251881Speter (SVN_ERR_FS_NOT_MUTABLE, NULL, 737251881Speter _("Attempted to clone child of non-mutable node")); 738251881Speter 739251881Speter /* Make sure that NAME is a single path component. */ 740251881Speter if (! svn_path_is_single_path_component(name)) 741251881Speter return svn_error_createf 742251881Speter (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL, 743251881Speter _("Attempted to make a child clone with an illegal name '%s'"), name); 744251881Speter 745251881Speter /* Find the node named NAME in PARENT's entries list if it exists. */ 746251881Speter SVN_ERR(svn_fs_base__dag_open(&cur_entry, parent, name, trail, pool)); 747251881Speter 748251881Speter /* Check for mutability in the node we found. If it's mutable, we 749251881Speter don't need to clone it. */ 750251881Speter if (svn_fs_base__dag_check_mutable(cur_entry, txn_id)) 751251881Speter { 752251881Speter /* This has already been cloned */ 753251881Speter new_node_id = cur_entry->id; 754251881Speter } 755251881Speter else 756251881Speter { 757251881Speter node_revision_t *noderev; 758251881Speter 759251881Speter /* Go get a fresh NODE-REVISION for current child node. */ 760251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, cur_entry->id, 761251881Speter trail, pool)); 762251881Speter 763251881Speter /* Do the clone thingy here. */ 764251881Speter noderev->predecessor_id = cur_entry->id; 765251881Speter if (noderev->predecessor_count != -1) 766251881Speter noderev->predecessor_count++; 767251881Speter noderev->created_path = svn_fspath__join(parent_path, name, pool); 768251881Speter SVN_ERR(svn_fs_base__create_successor(&new_node_id, fs, cur_entry->id, 769251881Speter noderev, copy_id, txn_id, 770251881Speter trail, pool)); 771251881Speter 772251881Speter /* Replace the ID in the parent's ENTRY list with the ID which 773251881Speter refers to the mutable clone of this child. */ 774251881Speter SVN_ERR(set_entry(parent, name, new_node_id, txn_id, trail, pool)); 775251881Speter } 776251881Speter 777251881Speter /* Initialize the youngster. */ 778251881Speter return svn_fs_base__dag_get_node(child_p, fs, new_node_id, trail, pool); 779251881Speter} 780251881Speter 781251881Speter 782251881Speter 783251881Spetersvn_error_t * 784251881Spetersvn_fs_base__dag_clone_root(dag_node_t **root_p, 785251881Speter svn_fs_t *fs, 786251881Speter const char *txn_id, 787251881Speter trail_t *trail, 788251881Speter apr_pool_t *pool) 789251881Speter{ 790251881Speter const svn_fs_id_t *base_root_id, *root_id; 791251881Speter node_revision_t *noderev; 792251881Speter 793251881Speter /* Get the node ID's of the root directories of the transaction and 794251881Speter its base revision. */ 795251881Speter SVN_ERR(svn_fs_base__get_txn_ids(&root_id, &base_root_id, fs, txn_id, 796251881Speter trail, pool)); 797251881Speter 798251881Speter /* Oh, give me a clone... 799251881Speter (If they're the same, we haven't cloned the transaction's root 800251881Speter directory yet.) */ 801251881Speter if (svn_fs_base__id_eq(root_id, base_root_id)) 802251881Speter { 803251881Speter const char *base_copy_id = svn_fs_base__id_copy_id(base_root_id); 804251881Speter 805251881Speter /* Of my own flesh and bone... 806251881Speter (Get the NODE-REVISION for the base node, and then write 807251881Speter it back out as the clone.) */ 808251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, base_root_id, 809251881Speter trail, pool)); 810251881Speter 811251881Speter /* With its Y-chromosome changed to X... 812251881Speter (Store it with an updated predecessor count.) */ 813251881Speter /* ### TODO: Does it even makes sense to have a different copy id for 814251881Speter the root node? That is, does this function need a copy_id 815251881Speter passed in? */ 816251881Speter noderev->predecessor_id = svn_fs_base__id_copy(base_root_id, pool); 817251881Speter if (noderev->predecessor_count != -1) 818251881Speter noderev->predecessor_count++; 819251881Speter SVN_ERR(svn_fs_base__create_successor(&root_id, fs, base_root_id, 820251881Speter noderev, base_copy_id, 821251881Speter txn_id, trail, pool)); 822251881Speter 823251881Speter /* ... And when it is grown 824251881Speter * Then my own little clone 825251881Speter * Will be of the opposite sex! 826251881Speter */ 827251881Speter SVN_ERR(svn_fs_base__set_txn_root(fs, txn_id, root_id, trail, pool)); 828251881Speter } 829251881Speter 830251881Speter /* 831251881Speter * (Sung to the tune of "Home, Home on the Range", with thanks to 832251881Speter * Randall Garrett and Isaac Asimov.) 833251881Speter */ 834251881Speter 835251881Speter /* One way or another, root_id now identifies a cloned root node. */ 836251881Speter return svn_fs_base__dag_get_node(root_p, fs, root_id, trail, pool); 837251881Speter} 838251881Speter 839251881Speter 840251881Spetersvn_error_t * 841251881Spetersvn_fs_base__dag_delete(dag_node_t *parent, 842251881Speter const char *name, 843251881Speter const char *txn_id, 844251881Speter trail_t *trail, 845251881Speter apr_pool_t *pool) 846251881Speter{ 847251881Speter node_revision_t *parent_noderev; 848251881Speter const char *rep_key, *mutable_rep_key; 849251881Speter apr_hash_t *entries = NULL; 850251881Speter svn_skel_t *entries_skel; 851251881Speter svn_fs_t *fs = parent->fs; 852251881Speter svn_string_t str; 853251881Speter svn_fs_id_t *id = NULL; 854251881Speter dag_node_t *node; 855251881Speter 856251881Speter /* Make sure parent is a directory. */ 857251881Speter if (parent->kind != svn_node_dir) 858251881Speter return svn_error_createf 859251881Speter (SVN_ERR_FS_NOT_DIRECTORY, NULL, 860251881Speter _("Attempted to delete entry '%s' from *non*-directory node"), name); 861251881Speter 862251881Speter /* Make sure parent is mutable. */ 863251881Speter if (! svn_fs_base__dag_check_mutable(parent, txn_id)) 864251881Speter return svn_error_createf 865251881Speter (SVN_ERR_FS_NOT_MUTABLE, NULL, 866251881Speter _("Attempted to delete entry '%s' from immutable directory node"), 867251881Speter name); 868251881Speter 869251881Speter /* Make sure that NAME is a single path component. */ 870251881Speter if (! svn_path_is_single_path_component(name)) 871251881Speter return svn_error_createf 872251881Speter (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL, 873251881Speter _("Attempted to delete a node with an illegal name '%s'"), name); 874251881Speter 875251881Speter /* Get a fresh NODE-REVISION for the parent node. */ 876251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&parent_noderev, fs, parent->id, 877251881Speter trail, pool)); 878251881Speter 879251881Speter /* Get the key for the parent's entries list (data) representation. */ 880251881Speter rep_key = parent_noderev->data_key; 881251881Speter 882251881Speter /* No REP_KEY means no representation, and no representation means 883251881Speter no data, and no data means no entries...there's nothing here to 884251881Speter delete! */ 885251881Speter if (! rep_key) 886251881Speter return svn_error_createf 887251881Speter (SVN_ERR_FS_NO_SUCH_ENTRY, NULL, 888251881Speter _("Delete failed: directory has no entry '%s'"), name); 889251881Speter 890251881Speter /* Ensure we have a key to a mutable representation of the entries 891251881Speter list. We'll have to update the NODE-REVISION if it points to an 892251881Speter immutable version. */ 893251881Speter SVN_ERR(svn_fs_base__get_mutable_rep(&mutable_rep_key, rep_key, 894251881Speter fs, txn_id, trail, pool)); 895251881Speter if (! svn_fs_base__same_keys(mutable_rep_key, rep_key)) 896251881Speter { 897251881Speter parent_noderev->data_key = mutable_rep_key; 898251881Speter SVN_ERR(svn_fs_bdb__put_node_revision(fs, parent->id, parent_noderev, 899251881Speter trail, pool)); 900251881Speter } 901251881Speter 902251881Speter /* Read the representation, then use it to get the string that holds 903251881Speter the entries list. Parse that list into a skel, and parse *that* 904251881Speter into a hash. */ 905251881Speter 906251881Speter SVN_ERR(svn_fs_base__rep_contents(&str, fs, rep_key, trail, pool)); 907251881Speter entries_skel = svn_skel__parse(str.data, str.len, pool); 908251881Speter if (entries_skel) 909251881Speter SVN_ERR(svn_fs_base__parse_entries_skel(&entries, entries_skel, pool)); 910251881Speter 911251881Speter /* Find NAME in the ENTRIES skel. */ 912251881Speter if (entries) 913251881Speter id = svn_hash_gets(entries, name); 914251881Speter 915251881Speter /* If we never found ID in ENTRIES (perhaps because there are no 916251881Speter ENTRIES, perhaps because ID just isn't in the existing ENTRIES 917251881Speter ... it doesn't matter), return an error. */ 918251881Speter if (! id) 919251881Speter return svn_error_createf 920251881Speter (SVN_ERR_FS_NO_SUCH_ENTRY, NULL, 921251881Speter _("Delete failed: directory has no entry '%s'"), name); 922251881Speter 923251881Speter /* Use the ID of this ENTRY to get the entry's node. */ 924251881Speter SVN_ERR(svn_fs_base__dag_get_node(&node, svn_fs_base__dag_get_fs(parent), 925251881Speter id, trail, pool)); 926251881Speter 927251881Speter /* If mutable, remove it and any mutable children from db. */ 928251881Speter SVN_ERR(svn_fs_base__dag_delete_if_mutable(parent->fs, id, txn_id, 929251881Speter trail, pool)); 930251881Speter 931251881Speter /* Remove this entry from its parent's entries list. */ 932251881Speter svn_hash_sets(entries, name, NULL); 933251881Speter 934251881Speter /* Replace the old entries list with the new one. */ 935251881Speter { 936251881Speter svn_stream_t *ws; 937251881Speter svn_stringbuf_t *unparsed_entries; 938251881Speter apr_size_t len; 939251881Speter 940251881Speter SVN_ERR(svn_fs_base__unparse_entries_skel(&entries_skel, entries, pool)); 941251881Speter unparsed_entries = svn_skel__unparse(entries_skel, pool); 942251881Speter SVN_ERR(svn_fs_base__rep_contents_write_stream(&ws, fs, mutable_rep_key, 943251881Speter txn_id, TRUE, trail, 944251881Speter pool)); 945251881Speter len = unparsed_entries->len; 946251881Speter SVN_ERR(svn_stream_write(ws, unparsed_entries->data, &len)); 947251881Speter SVN_ERR(svn_stream_close(ws)); 948251881Speter } 949251881Speter 950251881Speter return SVN_NO_ERROR; 951251881Speter} 952251881Speter 953251881Speter 954251881Spetersvn_error_t * 955251881Spetersvn_fs_base__dag_remove_node(svn_fs_t *fs, 956251881Speter const svn_fs_id_t *id, 957251881Speter const char *txn_id, 958251881Speter trail_t *trail, 959251881Speter apr_pool_t *pool) 960251881Speter{ 961251881Speter dag_node_t *node; 962251881Speter node_revision_t *noderev; 963251881Speter 964251881Speter /* Fetch the node. */ 965251881Speter SVN_ERR(svn_fs_base__dag_get_node(&node, fs, id, trail, pool)); 966251881Speter 967251881Speter /* If immutable, do nothing and return immediately. */ 968251881Speter if (! svn_fs_base__dag_check_mutable(node, txn_id)) 969251881Speter return svn_error_createf(SVN_ERR_FS_NOT_MUTABLE, NULL, 970251881Speter _("Attempted removal of immutable node")); 971251881Speter 972251881Speter /* Get a fresh node-revision. */ 973251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, id, trail, pool)); 974251881Speter 975251881Speter /* Delete any mutable property representation. */ 976251881Speter if (noderev->prop_key) 977251881Speter SVN_ERR(svn_fs_base__delete_rep_if_mutable(fs, noderev->prop_key, 978251881Speter txn_id, trail, pool)); 979251881Speter 980251881Speter /* Delete any mutable data representation. */ 981251881Speter if (noderev->data_key) 982251881Speter SVN_ERR(svn_fs_base__delete_rep_if_mutable(fs, noderev->data_key, 983251881Speter txn_id, trail, pool)); 984251881Speter 985251881Speter /* Delete any mutable edit representation (files only). */ 986251881Speter if (noderev->edit_key) 987251881Speter SVN_ERR(svn_fs_base__delete_rep_if_mutable(fs, noderev->edit_key, 988251881Speter txn_id, trail, pool)); 989251881Speter 990251881Speter /* Delete the node revision itself. */ 991251881Speter return svn_fs_base__delete_node_revision(fs, id, 992251881Speter noderev->predecessor_id == NULL, 993251881Speter trail, pool); 994251881Speter} 995251881Speter 996251881Speter 997251881Spetersvn_error_t * 998251881Spetersvn_fs_base__dag_delete_if_mutable(svn_fs_t *fs, 999251881Speter const svn_fs_id_t *id, 1000251881Speter const char *txn_id, 1001251881Speter trail_t *trail, 1002251881Speter apr_pool_t *pool) 1003251881Speter{ 1004251881Speter dag_node_t *node; 1005251881Speter 1006251881Speter /* Get the node. */ 1007251881Speter SVN_ERR(svn_fs_base__dag_get_node(&node, fs, id, trail, pool)); 1008251881Speter 1009251881Speter /* If immutable, do nothing and return immediately. */ 1010251881Speter if (! svn_fs_base__dag_check_mutable(node, txn_id)) 1011251881Speter return SVN_NO_ERROR; 1012251881Speter 1013251881Speter /* Else it's mutable. Recurse on directories... */ 1014251881Speter if (node->kind == svn_node_dir) 1015251881Speter { 1016251881Speter apr_hash_t *entries; 1017251881Speter apr_hash_index_t *hi; 1018251881Speter 1019251881Speter /* Loop over hash entries */ 1020251881Speter SVN_ERR(svn_fs_base__dag_dir_entries(&entries, node, trail, pool)); 1021251881Speter if (entries) 1022251881Speter { 1023251881Speter apr_pool_t *subpool = svn_pool_create(pool); 1024251881Speter for (hi = apr_hash_first(pool, entries); 1025251881Speter hi; 1026251881Speter hi = apr_hash_next(hi)) 1027251881Speter { 1028251881Speter void *val; 1029251881Speter svn_fs_dirent_t *dirent; 1030251881Speter 1031299742Sdim svn_pool_clear(subpool); 1032251881Speter apr_hash_this(hi, NULL, NULL, &val); 1033251881Speter dirent = val; 1034251881Speter SVN_ERR(svn_fs_base__dag_delete_if_mutable(fs, dirent->id, 1035251881Speter txn_id, trail, 1036251881Speter subpool)); 1037251881Speter } 1038299742Sdim svn_pool_destroy(subpool); 1039251881Speter } 1040251881Speter } 1041251881Speter 1042251881Speter /* ... then delete the node itself, any mutable representations and 1043251881Speter strings it points to, and possibly its node-origins record. */ 1044251881Speter return svn_fs_base__dag_remove_node(fs, id, txn_id, trail, pool); 1045251881Speter} 1046251881Speter 1047251881Speter 1048251881Spetersvn_error_t * 1049251881Spetersvn_fs_base__dag_make_file(dag_node_t **child_p, 1050251881Speter dag_node_t *parent, 1051251881Speter const char *parent_path, 1052251881Speter const char *name, 1053251881Speter const char *txn_id, 1054251881Speter trail_t *trail, 1055251881Speter apr_pool_t *pool) 1056251881Speter{ 1057251881Speter /* Call our little helper function */ 1058251881Speter return make_entry(child_p, parent, parent_path, name, FALSE, 1059251881Speter txn_id, trail, pool); 1060251881Speter} 1061251881Speter 1062251881Speter 1063251881Spetersvn_error_t * 1064251881Spetersvn_fs_base__dag_make_dir(dag_node_t **child_p, 1065251881Speter dag_node_t *parent, 1066251881Speter const char *parent_path, 1067251881Speter const char *name, 1068251881Speter const char *txn_id, 1069251881Speter trail_t *trail, 1070251881Speter apr_pool_t *pool) 1071251881Speter{ 1072251881Speter /* Call our little helper function */ 1073251881Speter return make_entry(child_p, parent, parent_path, name, TRUE, 1074251881Speter txn_id, trail, pool); 1075251881Speter} 1076251881Speter 1077251881Speter 1078251881Spetersvn_error_t * 1079251881Spetersvn_fs_base__dag_get_contents(svn_stream_t **contents, 1080251881Speter dag_node_t *file, 1081251881Speter trail_t *trail, 1082251881Speter apr_pool_t *pool) 1083251881Speter{ 1084251881Speter node_revision_t *noderev; 1085251881Speter 1086251881Speter /* Make sure our node is a file. */ 1087251881Speter if (file->kind != svn_node_file) 1088251881Speter return svn_error_createf 1089251881Speter (SVN_ERR_FS_NOT_FILE, NULL, 1090251881Speter _("Attempted to get textual contents of a *non*-file node")); 1091251881Speter 1092251881Speter /* Go get a fresh node-revision for FILE. */ 1093251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, file->fs, file->id, 1094251881Speter trail, pool)); 1095251881Speter 1096251881Speter /* Our job is to _return_ a stream on the file's contents, so the 1097251881Speter stream has to be trail-independent. Here, we pass NULL to tell 1098251881Speter the stream that we're not providing it a trail that lives across 1099251881Speter reads. This means the stream will do each read in a one-off, 1100251881Speter temporary trail. */ 1101251881Speter return svn_fs_base__rep_contents_read_stream(contents, file->fs, 1102251881Speter noderev->data_key, 1103251881Speter FALSE, trail, pool); 1104251881Speter 1105251881Speter /* Note that we're not registering any `close' func, because there's 1106251881Speter nothing to cleanup outside of our trail. When the trail is 1107251881Speter freed, the stream/baton will be too. */ 1108251881Speter} 1109251881Speter 1110251881Speter 1111251881Spetersvn_error_t * 1112251881Spetersvn_fs_base__dag_file_length(svn_filesize_t *length, 1113251881Speter dag_node_t *file, 1114251881Speter trail_t *trail, 1115251881Speter apr_pool_t *pool) 1116251881Speter{ 1117251881Speter node_revision_t *noderev; 1118251881Speter 1119251881Speter /* Make sure our node is a file. */ 1120251881Speter if (file->kind != svn_node_file) 1121251881Speter return svn_error_createf 1122251881Speter (SVN_ERR_FS_NOT_FILE, NULL, 1123251881Speter _("Attempted to get length of a *non*-file node")); 1124251881Speter 1125251881Speter /* Go get a fresh node-revision for FILE, and . */ 1126251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, file->fs, file->id, 1127251881Speter trail, pool)); 1128251881Speter if (noderev->data_key) 1129251881Speter SVN_ERR(svn_fs_base__rep_contents_size(length, file->fs, 1130251881Speter noderev->data_key, trail, pool)); 1131251881Speter else 1132251881Speter *length = 0; 1133251881Speter 1134251881Speter return SVN_NO_ERROR; 1135251881Speter} 1136251881Speter 1137251881Speter 1138251881Spetersvn_error_t * 1139251881Spetersvn_fs_base__dag_file_checksum(svn_checksum_t **checksum, 1140251881Speter svn_checksum_kind_t checksum_kind, 1141251881Speter dag_node_t *file, 1142251881Speter trail_t *trail, 1143251881Speter apr_pool_t *pool) 1144251881Speter{ 1145251881Speter node_revision_t *noderev; 1146251881Speter 1147251881Speter if (file->kind != svn_node_file) 1148251881Speter return svn_error_createf 1149251881Speter (SVN_ERR_FS_NOT_FILE, NULL, 1150251881Speter _("Attempted to get checksum of a *non*-file node")); 1151251881Speter 1152251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, file->fs, file->id, 1153251881Speter trail, pool)); 1154251881Speter if (! noderev->data_key) 1155251881Speter { 1156251881Speter *checksum = NULL; 1157251881Speter return SVN_NO_ERROR; 1158251881Speter } 1159251881Speter 1160251881Speter if (checksum_kind == svn_checksum_md5) 1161251881Speter return svn_fs_base__rep_contents_checksums(checksum, NULL, file->fs, 1162251881Speter noderev->data_key, 1163251881Speter trail, pool); 1164251881Speter else if (checksum_kind == svn_checksum_sha1) 1165251881Speter return svn_fs_base__rep_contents_checksums(NULL, checksum, file->fs, 1166251881Speter noderev->data_key, 1167251881Speter trail, pool); 1168251881Speter else 1169251881Speter return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL); 1170251881Speter} 1171251881Speter 1172251881Speter 1173251881Spetersvn_error_t * 1174251881Spetersvn_fs_base__dag_get_edit_stream(svn_stream_t **contents, 1175251881Speter dag_node_t *file, 1176251881Speter const char *txn_id, 1177251881Speter trail_t *trail, 1178251881Speter apr_pool_t *pool) 1179251881Speter{ 1180251881Speter svn_fs_t *fs = file->fs; /* just for nicer indentation */ 1181251881Speter node_revision_t *noderev; 1182251881Speter const char *mutable_rep_key; 1183251881Speter svn_stream_t *ws; 1184251881Speter 1185251881Speter /* Make sure our node is a file. */ 1186251881Speter if (file->kind != svn_node_file) 1187251881Speter return svn_error_createf 1188251881Speter (SVN_ERR_FS_NOT_FILE, NULL, 1189251881Speter _("Attempted to set textual contents of a *non*-file node")); 1190251881Speter 1191251881Speter /* Make sure our node is mutable. */ 1192251881Speter if (! svn_fs_base__dag_check_mutable(file, txn_id)) 1193251881Speter return svn_error_createf 1194251881Speter (SVN_ERR_FS_NOT_MUTABLE, NULL, 1195251881Speter _("Attempted to set textual contents of an immutable node")); 1196251881Speter 1197251881Speter /* Get the node revision. */ 1198251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, file->id, 1199251881Speter trail, pool)); 1200251881Speter 1201251881Speter /* If this node already has an EDIT-DATA-KEY, destroy the data 1202251881Speter associated with that key. */ 1203251881Speter if (noderev->edit_key) 1204251881Speter SVN_ERR(svn_fs_base__delete_rep_if_mutable(fs, noderev->edit_key, 1205251881Speter txn_id, trail, pool)); 1206251881Speter 1207251881Speter /* Now, let's ensure that we have a new EDIT-DATA-KEY available for 1208251881Speter use. */ 1209251881Speter SVN_ERR(svn_fs_base__get_mutable_rep(&mutable_rep_key, NULL, fs, 1210251881Speter txn_id, trail, pool)); 1211251881Speter 1212251881Speter /* We made a new rep, so update the node revision. */ 1213251881Speter noderev->edit_key = mutable_rep_key; 1214251881Speter SVN_ERR(svn_fs_bdb__put_node_revision(fs, file->id, noderev, 1215251881Speter trail, pool)); 1216251881Speter 1217251881Speter /* Return a writable stream with which to set new contents. */ 1218251881Speter SVN_ERR(svn_fs_base__rep_contents_write_stream(&ws, fs, mutable_rep_key, 1219251881Speter txn_id, FALSE, trail, 1220251881Speter pool)); 1221251881Speter *contents = ws; 1222251881Speter 1223251881Speter return SVN_NO_ERROR; 1224251881Speter} 1225251881Speter 1226251881Speter 1227251881Speter 1228251881Spetersvn_error_t * 1229251881Spetersvn_fs_base__dag_finalize_edits(dag_node_t *file, 1230251881Speter const svn_checksum_t *checksum, 1231251881Speter const char *txn_id, 1232251881Speter trail_t *trail, 1233251881Speter apr_pool_t *pool) 1234251881Speter{ 1235251881Speter svn_fs_t *fs = file->fs; /* just for nicer indentation */ 1236251881Speter node_revision_t *noderev; 1237251881Speter const char *old_data_key, *new_data_key, *useless_data_key = NULL; 1238251881Speter const char *data_key_uniquifier = NULL; 1239251881Speter svn_checksum_t *md5_checksum, *sha1_checksum; 1240251881Speter base_fs_data_t *bfd = fs->fsap_data; 1241251881Speter 1242251881Speter /* Make sure our node is a file. */ 1243251881Speter if (file->kind != svn_node_file) 1244251881Speter return svn_error_createf 1245251881Speter (SVN_ERR_FS_NOT_FILE, NULL, 1246251881Speter _("Attempted to set textual contents of a *non*-file node")); 1247251881Speter 1248251881Speter /* Make sure our node is mutable. */ 1249251881Speter if (! svn_fs_base__dag_check_mutable(file, txn_id)) 1250251881Speter return svn_error_createf 1251251881Speter (SVN_ERR_FS_NOT_MUTABLE, NULL, 1252251881Speter _("Attempted to set textual contents of an immutable node")); 1253251881Speter 1254251881Speter /* Get the node revision. */ 1255251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, file->id, 1256251881Speter trail, pool)); 1257251881Speter 1258251881Speter /* If this node has no EDIT-DATA-KEY, this is a no-op. */ 1259251881Speter if (! noderev->edit_key) 1260251881Speter return SVN_NO_ERROR; 1261251881Speter 1262251881Speter /* Get our representation's checksums. */ 1263251881Speter SVN_ERR(svn_fs_base__rep_contents_checksums(&md5_checksum, &sha1_checksum, 1264251881Speter fs, noderev->edit_key, 1265251881Speter trail, pool)); 1266251881Speter 1267251881Speter /* If our caller provided a checksum of the right kind to compare, do so. */ 1268251881Speter if (checksum) 1269251881Speter { 1270251881Speter svn_checksum_t *test_checksum; 1271251881Speter 1272251881Speter if (checksum->kind == svn_checksum_md5) 1273251881Speter test_checksum = md5_checksum; 1274251881Speter else if (checksum->kind == svn_checksum_sha1) 1275251881Speter test_checksum = sha1_checksum; 1276251881Speter else 1277251881Speter return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL); 1278251881Speter 1279251881Speter if (! svn_checksum_match(checksum, test_checksum)) 1280251881Speter return svn_checksum_mismatch_err(checksum, test_checksum, pool, 1281251881Speter _("Checksum mismatch on representation '%s'"), 1282251881Speter noderev->edit_key); 1283251881Speter } 1284251881Speter 1285251881Speter /* Now, we want to delete the old representation and replace it with 1286251881Speter the new. Of course, we don't actually delete anything until 1287251881Speter everything is being properly referred to by the node-revision 1288251881Speter skel. 1289251881Speter 1290251881Speter Now, if the result of all this editing is that we've created a 1291251881Speter representation that describes content already represented 1292251881Speter immutably in our database, we don't even need to keep these edits. 1293251881Speter We can simply point our data_key at that pre-existing 1294251881Speter representation and throw away our work! In this situation, 1295251881Speter though, we'll need a unique ID to help other code distinguish 1296251881Speter between "the contents weren't touched" and "the contents were 1297251881Speter touched but still look the same" (to state it oversimply). */ 1298251881Speter old_data_key = noderev->data_key; 1299251881Speter if (sha1_checksum && bfd->format >= SVN_FS_BASE__MIN_REP_SHARING_FORMAT) 1300251881Speter { 1301251881Speter svn_error_t *err = svn_fs_bdb__get_checksum_rep(&new_data_key, fs, 1302251881Speter sha1_checksum, 1303251881Speter trail, pool); 1304251881Speter if (! err) 1305251881Speter { 1306251881Speter useless_data_key = noderev->edit_key; 1307251881Speter err = svn_fs_bdb__reserve_rep_reuse_id(&data_key_uniquifier, 1308251881Speter trail->fs, trail, pool); 1309251881Speter } 1310251881Speter else if (err && (err->apr_err == SVN_ERR_FS_NO_SUCH_CHECKSUM_REP)) 1311251881Speter { 1312251881Speter svn_error_clear(err); 1313251881Speter err = SVN_NO_ERROR; 1314251881Speter new_data_key = noderev->edit_key; 1315251881Speter } 1316251881Speter SVN_ERR(err); 1317251881Speter } 1318251881Speter else 1319251881Speter { 1320251881Speter new_data_key = noderev->edit_key; 1321251881Speter } 1322251881Speter 1323251881Speter noderev->data_key = new_data_key; 1324251881Speter noderev->data_key_uniquifier = data_key_uniquifier; 1325251881Speter noderev->edit_key = NULL; 1326251881Speter 1327251881Speter SVN_ERR(svn_fs_bdb__put_node_revision(fs, file->id, noderev, trail, pool)); 1328251881Speter 1329251881Speter /* Only *now* can we safely destroy the old representation (if it 1330251881Speter even existed in the first place). */ 1331251881Speter if (old_data_key) 1332251881Speter SVN_ERR(svn_fs_base__delete_rep_if_mutable(fs, old_data_key, txn_id, 1333251881Speter trail, pool)); 1334251881Speter 1335251881Speter /* If we've got a discardable rep (probably because we ended up 1336251881Speter re-using a preexisting one), throw out the discardable rep. */ 1337251881Speter if (useless_data_key) 1338251881Speter SVN_ERR(svn_fs_base__delete_rep_if_mutable(fs, useless_data_key, 1339251881Speter txn_id, trail, pool)); 1340251881Speter 1341251881Speter return SVN_NO_ERROR; 1342251881Speter} 1343251881Speter 1344251881Speter 1345251881Speter 1346251881Speterdag_node_t * 1347299742Sdimsvn_fs_base__dag_dup(const dag_node_t *node, 1348251881Speter apr_pool_t *pool) 1349251881Speter{ 1350251881Speter /* Allocate our new node. */ 1351251881Speter dag_node_t *new_node = apr_pcalloc(pool, sizeof(*new_node)); 1352251881Speter 1353251881Speter new_node->fs = node->fs; 1354251881Speter new_node->id = svn_fs_base__id_copy(node->id, pool); 1355251881Speter new_node->kind = node->kind; 1356251881Speter new_node->created_path = apr_pstrdup(pool, node->created_path); 1357251881Speter return new_node; 1358251881Speter} 1359251881Speter 1360251881Speter 1361251881Spetersvn_error_t * 1362251881Spetersvn_fs_base__dag_open(dag_node_t **child_p, 1363251881Speter dag_node_t *parent, 1364251881Speter const char *name, 1365251881Speter trail_t *trail, 1366251881Speter apr_pool_t *pool) 1367251881Speter{ 1368251881Speter const svn_fs_id_t *node_id; 1369251881Speter 1370251881Speter /* Ensure that NAME exists in PARENT's entry list. */ 1371251881Speter SVN_ERR(dir_entry_id_from_node(&node_id, parent, name, trail, pool)); 1372251881Speter if (! node_id) 1373251881Speter return svn_error_createf 1374251881Speter (SVN_ERR_FS_NOT_FOUND, NULL, 1375251881Speter _("Attempted to open non-existent child node '%s'"), name); 1376251881Speter 1377251881Speter /* Make sure that NAME is a single path component. */ 1378251881Speter if (! svn_path_is_single_path_component(name)) 1379251881Speter return svn_error_createf 1380251881Speter (SVN_ERR_FS_NOT_SINGLE_PATH_COMPONENT, NULL, 1381251881Speter _("Attempted to open node with an illegal name '%s'"), name); 1382251881Speter 1383251881Speter /* Now get the node that was requested. */ 1384251881Speter return svn_fs_base__dag_get_node(child_p, svn_fs_base__dag_get_fs(parent), 1385251881Speter node_id, trail, pool); 1386251881Speter} 1387251881Speter 1388251881Speter 1389251881Spetersvn_error_t * 1390251881Spetersvn_fs_base__dag_copy(dag_node_t *to_node, 1391251881Speter const char *entry, 1392251881Speter dag_node_t *from_node, 1393251881Speter svn_boolean_t preserve_history, 1394251881Speter svn_revnum_t from_rev, 1395251881Speter const char *from_path, 1396251881Speter const char *txn_id, 1397251881Speter trail_t *trail, 1398251881Speter apr_pool_t *pool) 1399251881Speter{ 1400251881Speter const svn_fs_id_t *id; 1401251881Speter 1402251881Speter if (preserve_history) 1403251881Speter { 1404251881Speter node_revision_t *noderev; 1405251881Speter const char *copy_id; 1406251881Speter svn_fs_t *fs = svn_fs_base__dag_get_fs(from_node); 1407251881Speter const svn_fs_id_t *src_id = svn_fs_base__dag_get_id(from_node); 1408251881Speter const char *from_txn_id = NULL; 1409251881Speter 1410251881Speter /* Make a copy of the original node revision. */ 1411251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, from_node->id, 1412251881Speter trail, pool)); 1413251881Speter 1414251881Speter /* Reserve a copy ID for this new copy. */ 1415251881Speter SVN_ERR(svn_fs_bdb__reserve_copy_id(©_id, fs, trail, pool)); 1416251881Speter 1417251881Speter /* Create a successor with its predecessor pointing at the copy 1418251881Speter source. */ 1419251881Speter noderev->predecessor_id = svn_fs_base__id_copy(src_id, pool); 1420251881Speter if (noderev->predecessor_count != -1) 1421251881Speter noderev->predecessor_count++; 1422251881Speter noderev->created_path = svn_fspath__join 1423251881Speter (svn_fs_base__dag_get_created_path(to_node), entry, pool); 1424251881Speter SVN_ERR(svn_fs_base__create_successor(&id, fs, src_id, noderev, 1425251881Speter copy_id, txn_id, trail, pool)); 1426251881Speter 1427251881Speter /* Translate FROM_REV into a transaction ID. */ 1428251881Speter SVN_ERR(svn_fs_base__rev_get_txn_id(&from_txn_id, fs, from_rev, 1429251881Speter trail, pool)); 1430251881Speter 1431251881Speter /* Now that we've done the copy, we need to add the information 1432251881Speter about the copy to the `copies' table, using the COPY_ID we 1433251881Speter reserved above. */ 1434251881Speter SVN_ERR(svn_fs_bdb__create_copy 1435251881Speter (fs, copy_id, 1436251881Speter svn_fs__canonicalize_abspath(from_path, pool), 1437251881Speter from_txn_id, id, copy_kind_real, trail, pool)); 1438251881Speter 1439251881Speter /* Finally, add the COPY_ID to the transaction's list of copies 1440251881Speter so that, if this transaction is aborted, the `copies' table 1441251881Speter entry we added above will be cleaned up. */ 1442251881Speter SVN_ERR(svn_fs_base__add_txn_copy(fs, txn_id, copy_id, trail, pool)); 1443251881Speter } 1444251881Speter else /* don't preserve history */ 1445251881Speter { 1446251881Speter id = svn_fs_base__dag_get_id(from_node); 1447251881Speter } 1448251881Speter 1449251881Speter /* Set the entry in to_node to the new id. */ 1450251881Speter return svn_fs_base__dag_set_entry(to_node, entry, id, txn_id, 1451251881Speter trail, pool); 1452251881Speter} 1453251881Speter 1454251881Speter 1455251881Speter 1456251881Speter/*** Deltification ***/ 1457251881Speter 1458251881Speter/* Maybe change the representation identified by TARGET_REP_KEY to be 1459251881Speter a delta against the representation identified by SOURCE_REP_KEY. 1460251881Speter Some reasons why we wouldn't include: 1461251881Speter 1462251881Speter - TARGET_REP_KEY and SOURCE_REP_KEY are the same key. 1463251881Speter 1464251881Speter - TARGET_REP_KEY's representation isn't mutable in TXN_ID (if 1465251881Speter TXN_ID is non-NULL). 1466251881Speter 1467251881Speter - The delta provides less space savings that a fulltext (this is 1468251881Speter a detail handled by lower logic layers, not this function). 1469251881Speter 1470251881Speter Do this work in TRAIL, using POOL for necessary allocations. 1471251881Speter*/ 1472251881Speterstatic svn_error_t * 1473251881Spetermaybe_deltify_mutable_rep(const char *target_rep_key, 1474251881Speter const char *source_rep_key, 1475251881Speter const char *txn_id, 1476251881Speter trail_t *trail, 1477251881Speter apr_pool_t *pool) 1478251881Speter{ 1479251881Speter if (! (target_rep_key && source_rep_key 1480251881Speter && (strcmp(target_rep_key, source_rep_key) != 0))) 1481251881Speter return SVN_NO_ERROR; 1482251881Speter 1483251881Speter if (txn_id) 1484251881Speter { 1485251881Speter representation_t *target_rep; 1486251881Speter SVN_ERR(svn_fs_bdb__read_rep(&target_rep, trail->fs, target_rep_key, 1487251881Speter trail, pool)); 1488251881Speter if (strcmp(target_rep->txn_id, txn_id) != 0) 1489251881Speter return SVN_NO_ERROR; 1490251881Speter } 1491251881Speter 1492251881Speter return svn_fs_base__rep_deltify(trail->fs, target_rep_key, source_rep_key, 1493251881Speter trail, pool); 1494251881Speter} 1495251881Speter 1496251881Speter 1497251881Spetersvn_error_t * 1498251881Spetersvn_fs_base__dag_deltify(dag_node_t *target, 1499251881Speter dag_node_t *source, 1500251881Speter svn_boolean_t props_only, 1501251881Speter const char *txn_id, 1502251881Speter trail_t *trail, 1503251881Speter apr_pool_t *pool) 1504251881Speter{ 1505251881Speter node_revision_t *source_nr, *target_nr; 1506251881Speter svn_fs_t *fs = svn_fs_base__dag_get_fs(target); 1507251881Speter 1508251881Speter /* Get node revisions for the two nodes. */ 1509251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&target_nr, fs, target->id, 1510251881Speter trail, pool)); 1511251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&source_nr, fs, source->id, 1512251881Speter trail, pool)); 1513251881Speter 1514251881Speter /* If TARGET and SOURCE both have properties, and are not sharing a 1515251881Speter property key, deltify TARGET's properties. */ 1516251881Speter SVN_ERR(maybe_deltify_mutable_rep(target_nr->prop_key, source_nr->prop_key, 1517251881Speter txn_id, trail, pool)); 1518251881Speter 1519251881Speter /* If we are not only attending to properties, and if TARGET and 1520251881Speter SOURCE both have data, and are not sharing a data key, deltify 1521251881Speter TARGET's data. */ 1522251881Speter if (! props_only) 1523251881Speter SVN_ERR(maybe_deltify_mutable_rep(target_nr->data_key, source_nr->data_key, 1524251881Speter txn_id, trail, pool)); 1525251881Speter 1526251881Speter return SVN_NO_ERROR; 1527251881Speter} 1528251881Speter 1529251881Speter/* Maybe store a `checksum-reps' index record for the representation whose 1530251881Speter key is REP. (If there's already a rep for this checksum, we don't 1531251881Speter bother overwriting it.) */ 1532251881Speterstatic svn_error_t * 1533251881Spetermaybe_store_checksum_rep(const char *rep, 1534251881Speter trail_t *trail, 1535251881Speter apr_pool_t *pool) 1536251881Speter{ 1537251881Speter svn_error_t *err = SVN_NO_ERROR; 1538251881Speter svn_fs_t *fs = trail->fs; 1539251881Speter svn_checksum_t *sha1_checksum; 1540251881Speter 1541251881Speter /* We want the SHA1 checksum, if any. */ 1542251881Speter SVN_ERR(svn_fs_base__rep_contents_checksums(NULL, &sha1_checksum, 1543251881Speter fs, rep, trail, pool)); 1544251881Speter if (sha1_checksum) 1545251881Speter { 1546251881Speter err = svn_fs_bdb__set_checksum_rep(fs, sha1_checksum, rep, trail, pool); 1547251881Speter if (err && (err->apr_err == SVN_ERR_FS_ALREADY_EXISTS)) 1548251881Speter { 1549251881Speter svn_error_clear(err); 1550251881Speter err = SVN_NO_ERROR; 1551251881Speter } 1552251881Speter } 1553251881Speter return svn_error_trace(err); 1554251881Speter} 1555251881Speter 1556251881Spetersvn_error_t * 1557251881Spetersvn_fs_base__dag_index_checksums(dag_node_t *node, 1558251881Speter trail_t *trail, 1559251881Speter apr_pool_t *pool) 1560251881Speter{ 1561251881Speter node_revision_t *node_rev; 1562251881Speter 1563251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&node_rev, trail->fs, node->id, 1564251881Speter trail, pool)); 1565251881Speter if ((node_rev->kind == svn_node_file) && node_rev->data_key) 1566251881Speter SVN_ERR(maybe_store_checksum_rep(node_rev->data_key, trail, pool)); 1567251881Speter if (node_rev->prop_key) 1568251881Speter SVN_ERR(maybe_store_checksum_rep(node_rev->prop_key, trail, pool)); 1569251881Speter 1570251881Speter return SVN_NO_ERROR; 1571251881Speter} 1572251881Speter 1573251881Speter 1574251881Speter 1575251881Speter/*** Committing ***/ 1576251881Speter 1577251881Spetersvn_error_t * 1578251881Spetersvn_fs_base__dag_commit_txn(svn_revnum_t *new_rev, 1579251881Speter svn_fs_txn_t *txn, 1580251881Speter trail_t *trail, 1581251881Speter apr_pool_t *pool) 1582251881Speter{ 1583251881Speter revision_t revision; 1584251881Speter apr_hash_t *txnprops; 1585251881Speter svn_fs_t *fs = txn->fs; 1586251881Speter const char *txn_id = txn->id; 1587299742Sdim const svn_string_t *client_date; 1588251881Speter 1589251881Speter /* Remove any temporary transaction properties initially created by 1590251881Speter begin_txn(). */ 1591251881Speter SVN_ERR(svn_fs_base__txn_proplist_in_trail(&txnprops, txn_id, trail)); 1592251881Speter 1593251881Speter /* Add new revision entry to `revisions' table. */ 1594251881Speter revision.txn_id = txn_id; 1595251881Speter *new_rev = SVN_INVALID_REVNUM; 1596251881Speter SVN_ERR(svn_fs_bdb__put_rev(new_rev, fs, &revision, trail, pool)); 1597251881Speter 1598251881Speter if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD)) 1599251881Speter SVN_ERR(svn_fs_base__set_txn_prop 1600251881Speter (fs, txn_id, SVN_FS__PROP_TXN_CHECK_OOD, NULL, trail, pool)); 1601251881Speter 1602251881Speter if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS)) 1603251881Speter SVN_ERR(svn_fs_base__set_txn_prop 1604251881Speter (fs, txn_id, SVN_FS__PROP_TXN_CHECK_LOCKS, NULL, trail, pool)); 1605251881Speter 1606299742Sdim client_date = svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CLIENT_DATE); 1607299742Sdim if (client_date) 1608299742Sdim SVN_ERR(svn_fs_base__set_txn_prop 1609299742Sdim (fs, txn_id, SVN_FS__PROP_TXN_CLIENT_DATE, NULL, trail, pool)); 1610299742Sdim 1611251881Speter /* Promote the unfinished transaction to a committed one. */ 1612251881Speter SVN_ERR(svn_fs_base__txn_make_committed(fs, txn_id, *new_rev, 1613251881Speter trail, pool)); 1614251881Speter 1615299742Sdim if (!client_date || strcmp(client_date->data, "1")) 1616299742Sdim { 1617299742Sdim /* Set a date on the commit if requested. We wait until now to fetch the 1618299742Sdim date, so it's definitely newer than any previous revision's date. */ 1619299742Sdim svn_string_t date; 1620299742Sdim date.data = svn_time_to_cstring(apr_time_now(), pool); 1621299742Sdim date.len = strlen(date.data); 1622299742Sdim SVN_ERR(svn_fs_base__set_rev_prop(fs, *new_rev, SVN_PROP_REVISION_DATE, 1623299742Sdim NULL, &date, trail, pool)); 1624299742Sdim } 1625299742Sdim 1626299742Sdim return SVN_NO_ERROR; 1627251881Speter} 1628251881Speter 1629251881Speter 1630251881Speter/*** Comparison. ***/ 1631251881Speter 1632251881Spetersvn_error_t * 1633251881Spetersvn_fs_base__things_different(svn_boolean_t *props_changed, 1634251881Speter svn_boolean_t *contents_changed, 1635251881Speter dag_node_t *node1, 1636251881Speter dag_node_t *node2, 1637251881Speter trail_t *trail, 1638251881Speter apr_pool_t *pool) 1639251881Speter{ 1640251881Speter node_revision_t *noderev1, *noderev2; 1641251881Speter 1642251881Speter /* If we have no place to store our results, don't bother doing 1643251881Speter anything. */ 1644251881Speter if (! props_changed && ! contents_changed) 1645251881Speter return SVN_NO_ERROR; 1646251881Speter 1647251881Speter /* The node revision skels for these two nodes. */ 1648251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&noderev1, node1->fs, node1->id, 1649251881Speter trail, pool)); 1650251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&noderev2, node2->fs, node2->id, 1651251881Speter trail, pool)); 1652251881Speter 1653251881Speter /* Compare property keys. */ 1654251881Speter if (props_changed != NULL) 1655251881Speter *props_changed = (! svn_fs_base__same_keys(noderev1->prop_key, 1656251881Speter noderev2->prop_key)); 1657251881Speter 1658251881Speter /* Compare contents keys and their (optional) uniquifiers. */ 1659251881Speter if (contents_changed != NULL) 1660251881Speter *contents_changed = 1661251881Speter (! (svn_fs_base__same_keys(noderev1->data_key, 1662251881Speter noderev2->data_key) 1663251881Speter /* Technically, these uniquifiers aren't used and "keys", 1664251881Speter but keys are base-36 stringified numbers, so we'll take 1665251881Speter this liberty. */ 1666251881Speter && (svn_fs_base__same_keys(noderev1->data_key_uniquifier, 1667251881Speter noderev2->data_key_uniquifier)))); 1668251881Speter 1669251881Speter return SVN_NO_ERROR; 1670251881Speter} 1671251881Speter 1672251881Speter 1673251881Speter 1674251881Speter/*** Mergeinfo tracking stuff ***/ 1675251881Speter 1676251881Spetersvn_error_t * 1677251881Spetersvn_fs_base__dag_get_mergeinfo_stats(svn_boolean_t *has_mergeinfo, 1678251881Speter apr_int64_t *count, 1679251881Speter dag_node_t *node, 1680251881Speter trail_t *trail, 1681251881Speter apr_pool_t *pool) 1682251881Speter{ 1683251881Speter node_revision_t *node_rev; 1684251881Speter svn_fs_t *fs = svn_fs_base__dag_get_fs(node); 1685251881Speter const svn_fs_id_t *id = svn_fs_base__dag_get_id(node); 1686251881Speter 1687251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&node_rev, fs, id, trail, pool)); 1688251881Speter if (has_mergeinfo) 1689251881Speter *has_mergeinfo = node_rev->has_mergeinfo; 1690251881Speter if (count) 1691251881Speter *count = node_rev->mergeinfo_count; 1692251881Speter return SVN_NO_ERROR; 1693251881Speter} 1694251881Speter 1695251881Speter 1696251881Spetersvn_error_t * 1697251881Spetersvn_fs_base__dag_set_has_mergeinfo(dag_node_t *node, 1698251881Speter svn_boolean_t has_mergeinfo, 1699251881Speter svn_boolean_t *had_mergeinfo, 1700251881Speter const char *txn_id, 1701251881Speter trail_t *trail, 1702251881Speter apr_pool_t *pool) 1703251881Speter{ 1704251881Speter node_revision_t *node_rev; 1705251881Speter svn_fs_t *fs = svn_fs_base__dag_get_fs(node); 1706251881Speter const svn_fs_id_t *id = svn_fs_base__dag_get_id(node); 1707251881Speter 1708251881Speter SVN_ERR(svn_fs_base__test_required_feature_format 1709251881Speter (trail->fs, "mergeinfo", SVN_FS_BASE__MIN_MERGEINFO_FORMAT)); 1710251881Speter 1711251881Speter if (! svn_fs_base__dag_check_mutable(node, txn_id)) 1712251881Speter return svn_error_createf(SVN_ERR_FS_NOT_MUTABLE, NULL, 1713251881Speter _("Attempted merge tracking info change on " 1714251881Speter "immutable node")); 1715251881Speter 1716251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&node_rev, fs, id, trail, pool)); 1717251881Speter *had_mergeinfo = node_rev->has_mergeinfo; 1718251881Speter 1719251881Speter /* Are we changing the node? */ 1720251881Speter if ((! has_mergeinfo) != (! *had_mergeinfo)) 1721251881Speter { 1722251881Speter /* Note the new has-mergeinfo state. */ 1723251881Speter node_rev->has_mergeinfo = has_mergeinfo; 1724251881Speter 1725251881Speter /* Increment or decrement the mergeinfo count as necessary. */ 1726251881Speter if (has_mergeinfo) 1727251881Speter node_rev->mergeinfo_count++; 1728251881Speter else 1729251881Speter node_rev->mergeinfo_count--; 1730251881Speter 1731251881Speter SVN_ERR(svn_fs_bdb__put_node_revision(fs, id, node_rev, trail, pool)); 1732251881Speter } 1733251881Speter return SVN_NO_ERROR; 1734251881Speter} 1735251881Speter 1736251881Speter 1737251881Spetersvn_error_t * 1738251881Spetersvn_fs_base__dag_adjust_mergeinfo_count(dag_node_t *node, 1739251881Speter apr_int64_t count_delta, 1740251881Speter const char *txn_id, 1741251881Speter trail_t *trail, 1742251881Speter apr_pool_t *pool) 1743251881Speter{ 1744251881Speter node_revision_t *node_rev; 1745251881Speter svn_fs_t *fs = svn_fs_base__dag_get_fs(node); 1746251881Speter const svn_fs_id_t *id = svn_fs_base__dag_get_id(node); 1747251881Speter 1748251881Speter SVN_ERR(svn_fs_base__test_required_feature_format 1749251881Speter (trail->fs, "mergeinfo", SVN_FS_BASE__MIN_MERGEINFO_FORMAT)); 1750251881Speter 1751251881Speter if (! svn_fs_base__dag_check_mutable(node, txn_id)) 1752251881Speter return svn_error_createf(SVN_ERR_FS_NOT_MUTABLE, NULL, 1753251881Speter _("Attempted mergeinfo count change on " 1754251881Speter "immutable node")); 1755251881Speter 1756251881Speter if (count_delta == 0) 1757251881Speter return SVN_NO_ERROR; 1758251881Speter 1759251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&node_rev, fs, id, trail, pool)); 1760251881Speter node_rev->mergeinfo_count = node_rev->mergeinfo_count + count_delta; 1761251881Speter if ((node_rev->mergeinfo_count < 0) 1762251881Speter || ((node->kind == svn_node_file) && (node_rev->mergeinfo_count > 1))) 1763251881Speter return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 1764251881Speter apr_psprintf(pool, 1765251881Speter _("Invalid value (%%%s) for node " 1766251881Speter "revision mergeinfo count"), 1767251881Speter APR_INT64_T_FMT), 1768251881Speter node_rev->mergeinfo_count); 1769251881Speter 1770251881Speter return svn_fs_bdb__put_node_revision(fs, id, node_rev, trail, pool); 1771251881Speter} 1772