1251881Speter/* tree.c : tree-like filesystem, built on DAG filesystem 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 24251881Speter/* The job of this layer is to take a filesystem with lots of node 25251881Speter sharing going on --- the real DAG filesystem as it appears in the 26251881Speter database --- and make it look and act like an ordinary tree 27251881Speter filesystem, with no sharing. 28251881Speter 29251881Speter We do just-in-time cloning: you can walk from some unfinished 30251881Speter transaction's root down into directories and files shared with 31251881Speter committed revisions; as soon as you try to change something, the 32251881Speter appropriate nodes get cloned (and parent directory entries updated) 33251881Speter invisibly, behind your back. Any other references you have to 34251881Speter nodes that have been cloned by other changes, even made by other 35251881Speter processes, are automatically updated to point to the right clones. */ 36251881Speter 37251881Speter 38251881Speter#include <stdlib.h> 39251881Speter#include <string.h> 40251881Speter#include <assert.h> 41251881Speter#include "svn_private_config.h" 42251881Speter#include "svn_hash.h" 43251881Speter#include "svn_pools.h" 44251881Speter#include "svn_error.h" 45251881Speter#include "svn_path.h" 46251881Speter#include "svn_mergeinfo.h" 47251881Speter#include "svn_fs.h" 48251881Speter#include "svn_sorts.h" 49251881Speter#include "svn_checksum.h" 50251881Speter#include "fs.h" 51251881Speter#include "err.h" 52251881Speter#include "trail.h" 53251881Speter#include "node-rev.h" 54251881Speter#include "key-gen.h" 55251881Speter#include "dag.h" 56251881Speter#include "tree.h" 57251881Speter#include "lock.h" 58251881Speter#include "revs-txns.h" 59251881Speter#include "id.h" 60251881Speter#include "bdb/txn-table.h" 61251881Speter#include "bdb/rev-table.h" 62251881Speter#include "bdb/nodes-table.h" 63251881Speter#include "bdb/changes-table.h" 64251881Speter#include "bdb/copies-table.h" 65251881Speter#include "bdb/node-origins-table.h" 66251881Speter#include "bdb/miscellaneous-table.h" 67251881Speter#include "../libsvn_fs/fs-loader.h" 68251881Speter#include "private/svn_fspath.h" 69251881Speter#include "private/svn_fs_util.h" 70251881Speter#include "private/svn_mergeinfo_private.h" 71251881Speter 72251881Speter 73251881Speter/* ### I believe this constant will become internal to reps-strings.c. 74251881Speter ### see the comment in window_consumer() for more information. */ 75251881Speter 76251881Speter/* ### the comment also seems to need tweaking: the log file stuff 77251881Speter ### is no longer an issue... */ 78251881Speter/* Data written to the filesystem through the svn_fs_apply_textdelta() 79251881Speter interface is cached in memory until the end of the data stream, or 80251881Speter until a size trigger is hit. Define that trigger here (in bytes). 81251881Speter Setting the value to 0 will result in no filesystem buffering at 82251881Speter all. The value only really matters when dealing with file contents 83251881Speter bigger than the value itself. Above that point, large values here 84251881Speter allow the filesystem to buffer more data in memory before flushing 85251881Speter to the database, which increases memory usage but greatly decreases 86251881Speter the amount of disk access (and log-file generation) in database. 87251881Speter Smaller values will limit your overall memory consumption, but can 88251881Speter drastically hurt throughput by necessitating more write operations 89251881Speter to the database (which also generates more log-files). */ 90251881Speter#define WRITE_BUFFER_SIZE 512000 91251881Speter 92251881Speter/* The maximum number of cache items to maintain in the node cache. */ 93251881Speter#define NODE_CACHE_MAX_KEYS 32 94251881Speter 95251881Speter 96251881Speter 97251881Speter/* The root structure. */ 98251881Speter 99251881Speter/* Structure for svn_fs_root_t's node_cache hash values. */ 100251881Speterstruct dag_node_cache_t 101251881Speter{ 102251881Speter dag_node_t *node; /* NODE to be cached. */ 103251881Speter int idx; /* Index into the keys array for this cache item's key. */ 104251881Speter apr_pool_t *pool; /* Pool in which NODE is allocated. */ 105251881Speter}; 106251881Speter 107251881Speter 108251881Spetertypedef struct base_root_data_t 109251881Speter{ 110251881Speter 111251881Speter /* For revision roots, this is a dag node for the revision's root 112251881Speter directory. For transaction roots, we open the root directory 113251881Speter afresh every time, since the root may have been cloned, or 114251881Speter the transaction may have disappeared altogether. */ 115251881Speter dag_node_t *root_dir; 116251881Speter 117251881Speter /* Cache structures, for mapping const char * PATH to const 118251881Speter struct dag_node_cache_t * structures. 119251881Speter 120251881Speter ### Currently this is only used for revision roots. To be safe 121251881Speter for transaction roots, you must have the guarantee that there is 122251881Speter never more than a single transaction root per Subversion 123251881Speter transaction ever open at a given time -- having two roots open to 124251881Speter the same Subversion transaction would be a request for pain. 125251881Speter Also, you have to ensure that if a 'make_path_mutable()' fails for 126251881Speter any reason, you don't leave cached nodes for the portion of that 127251881Speter function that succeeded. In other words, this cache must never, 128251881Speter ever, lie. */ 129251881Speter apr_hash_t *node_cache; 130251881Speter const char *node_cache_keys[NODE_CACHE_MAX_KEYS]; 131251881Speter int node_cache_idx; 132251881Speter} base_root_data_t; 133251881Speter 134251881Speter 135251881Speterstatic svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev, 136251881Speter dag_node_t *root_dir, 137251881Speter apr_pool_t *pool); 138251881Speter 139251881Speterstatic svn_fs_root_t *make_txn_root(svn_fs_t *fs, const char *txn, 140251881Speter svn_revnum_t base_rev, apr_uint32_t flags, 141251881Speter apr_pool_t *pool); 142251881Speter 143251881Speter 144251881Speter/*** Node Caching in the Roots. ***/ 145251881Speter 146251881Speter/* Return NODE for PATH from ROOT's node cache, or NULL if the node 147251881Speter isn't cached. */ 148251881Speterstatic dag_node_t * 149251881Speterdag_node_cache_get(svn_fs_root_t *root, 150251881Speter const char *path, 151251881Speter apr_pool_t *pool) 152251881Speter{ 153251881Speter base_root_data_t *brd = root->fsap_data; 154251881Speter struct dag_node_cache_t *cache_item; 155251881Speter 156251881Speter /* Assert valid input. */ 157251881Speter assert(*path == '/'); 158251881Speter 159251881Speter /* Only allow revision roots. */ 160251881Speter if (root->is_txn_root) 161251881Speter return NULL; 162251881Speter 163251881Speter /* Look in the cache for our desired item. */ 164251881Speter cache_item = svn_hash_gets(brd->node_cache, path); 165251881Speter if (cache_item) 166251881Speter return svn_fs_base__dag_dup(cache_item->node, pool); 167251881Speter 168251881Speter return NULL; 169251881Speter} 170251881Speter 171251881Speter 172251881Speter/* Add the NODE for PATH to ROOT's node cache. Callers should *NOT* 173251881Speter call this unless they are adding a currently un-cached item to the 174251881Speter cache, or are replacing the NODE for PATH with a new (different) 175251881Speter one. */ 176251881Speterstatic void 177251881Speterdag_node_cache_set(svn_fs_root_t *root, 178251881Speter const char *path, 179251881Speter dag_node_t *node) 180251881Speter{ 181251881Speter base_root_data_t *brd = root->fsap_data; 182251881Speter const char *cache_path; 183251881Speter apr_pool_t *cache_pool; 184251881Speter struct dag_node_cache_t *cache_item; 185251881Speter int num_keys = apr_hash_count(brd->node_cache); 186251881Speter 187251881Speter /* What? No POOL passed to this function? 188251881Speter 189251881Speter To ensure that our cache values live as long as the svn_fs_root_t 190251881Speter in which they are ultimately stored, and to allow us to free() 191251881Speter them individually without harming the rest, they are each 192251881Speter allocated from a subpool of ROOT's pool. We'll keep one subpool 193251881Speter around for each cache slot -- as we start expiring stuff 194251881Speter to make room for more entries, we'll re-use the expired thing's 195251881Speter pool. */ 196251881Speter 197251881Speter /* Assert valid input and state. */ 198251881Speter assert(*path == '/'); 199251881Speter assert((brd->node_cache_idx <= num_keys) 200251881Speter && (num_keys <= NODE_CACHE_MAX_KEYS)); 201251881Speter 202251881Speter /* Only allow revision roots. */ 203251881Speter if (root->is_txn_root) 204251881Speter return; 205251881Speter 206251881Speter /* Special case: the caller wants us to replace an existing cached 207251881Speter node with a new one. If the callers aren't mindless, this should 208251881Speter only happen when a node is made mutable under a transaction 209251881Speter root, and that only happens once under that root. So, we'll be a 210251881Speter little bit sloppy here, and count on callers doing the right 211251881Speter thing. */ 212251881Speter cache_item = svn_hash_gets(brd->node_cache, path); 213251881Speter if (cache_item) 214251881Speter { 215251881Speter /* ### This section is somehow broken. I don't know how, but it 216251881Speter ### is. And I don't want to spend any more time on it. So, 217251881Speter ### callers, use only revision root and don't try to update 218251881Speter ### an already-cached thing. -- cmpilato */ 219251881Speter SVN_ERR_MALFUNCTION_NO_RETURN(); 220251881Speter 221251881Speter#if 0 222251881Speter int cache_index = cache_item->idx; 223251881Speter cache_path = brd->node_cache_keys[cache_index]; 224251881Speter cache_pool = cache_item->pool; 225251881Speter cache_item->node = svn_fs_base__dag_dup(node, cache_pool); 226251881Speter 227251881Speter /* Now, move the cache key reference to the end of the keys in 228251881Speter the keys array (unless it's already at the end). ### Yes, 229251881Speter it's a memmove(), but we're not talking about pages of memory 230251881Speter here. */ 231251881Speter if (cache_index != (num_keys - 1)) 232251881Speter { 233251881Speter int move_num = NODE_CACHE_MAX_KEYS - cache_index - 1; 234251881Speter memmove(brd->node_cache_keys + cache_index, 235251881Speter brd->node_cache_keys + cache_index + 1, 236251881Speter move_num * sizeof(const char *)); 237251881Speter cache_index = num_keys - 1; 238251881Speter brd->node_cache_keys[cache_index] = cache_path; 239251881Speter } 240251881Speter 241251881Speter /* Advance the cache pointers. */ 242251881Speter cache_item->idx = cache_index; 243251881Speter brd->node_cache_idx = (cache_index + 1) % NODE_CACHE_MAX_KEYS; 244251881Speter return; 245251881Speter#endif 246251881Speter } 247251881Speter 248251881Speter /* We're adding a new cache item. First, see if we have room for it 249251881Speter (otherwise, make some room). */ 250251881Speter if (apr_hash_count(brd->node_cache) == NODE_CACHE_MAX_KEYS) 251251881Speter { 252251881Speter /* No room. Expire the oldest thing. */ 253251881Speter cache_path = brd->node_cache_keys[brd->node_cache_idx]; 254251881Speter cache_item = svn_hash_gets(brd->node_cache, cache_path); 255251881Speter svn_hash_sets(brd->node_cache, cache_path, NULL); 256251881Speter cache_pool = cache_item->pool; 257251881Speter svn_pool_clear(cache_pool); 258251881Speter } 259251881Speter else 260251881Speter { 261251881Speter cache_pool = svn_pool_create(root->pool); 262251881Speter } 263251881Speter 264251881Speter /* Make the cache item, allocated in its own pool. */ 265251881Speter cache_item = apr_palloc(cache_pool, sizeof(*cache_item)); 266251881Speter cache_item->node = svn_fs_base__dag_dup(node, cache_pool); 267251881Speter cache_item->idx = brd->node_cache_idx; 268251881Speter cache_item->pool = cache_pool; 269251881Speter 270251881Speter /* Now add it to the cache. */ 271251881Speter cache_path = apr_pstrdup(cache_pool, path); 272251881Speter svn_hash_sets(brd->node_cache, cache_path, cache_item); 273251881Speter brd->node_cache_keys[brd->node_cache_idx] = cache_path; 274251881Speter 275251881Speter /* Advance the cache pointer. */ 276251881Speter brd->node_cache_idx = (brd->node_cache_idx + 1) % NODE_CACHE_MAX_KEYS; 277251881Speter} 278251881Speter 279251881Speter 280251881Speter 281251881Speter 282251881Speter/* Creating transaction and revision root nodes. */ 283251881Speter 284251881Speterstruct txn_root_args 285251881Speter{ 286251881Speter svn_fs_root_t **root_p; 287251881Speter svn_fs_txn_t *txn; 288251881Speter}; 289251881Speter 290251881Speter 291251881Speterstatic svn_error_t * 292251881Spetertxn_body_txn_root(void *baton, 293251881Speter trail_t *trail) 294251881Speter{ 295251881Speter struct txn_root_args *args = baton; 296251881Speter svn_fs_root_t **root_p = args->root_p; 297251881Speter svn_fs_txn_t *txn = args->txn; 298251881Speter svn_fs_t *fs = txn->fs; 299251881Speter const char *svn_txn_id = txn->id; 300251881Speter const svn_fs_id_t *root_id, *base_root_id; 301251881Speter svn_fs_root_t *root; 302251881Speter apr_hash_t *txnprops; 303251881Speter apr_uint32_t flags = 0; 304251881Speter 305251881Speter /* Verify that the transaction actually exists. */ 306251881Speter SVN_ERR(svn_fs_base__get_txn_ids(&root_id, &base_root_id, fs, 307251881Speter svn_txn_id, trail, trail->pool)); 308251881Speter 309251881Speter /* Look for special txn props that represent the 'flags' behavior of 310251881Speter the transaction. */ 311251881Speter SVN_ERR(svn_fs_base__txn_proplist_in_trail(&txnprops, svn_txn_id, trail)); 312251881Speter if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD)) 313251881Speter flags |= SVN_FS_TXN_CHECK_OOD; 314251881Speter 315251881Speter if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS)) 316251881Speter flags |= SVN_FS_TXN_CHECK_LOCKS; 317251881Speter 318251881Speter root = make_txn_root(fs, svn_txn_id, txn->base_rev, flags, trail->pool); 319251881Speter 320251881Speter *root_p = root; 321251881Speter return SVN_NO_ERROR; 322251881Speter} 323251881Speter 324251881Speter 325251881Spetersvn_error_t * 326251881Spetersvn_fs_base__txn_root(svn_fs_root_t **root_p, 327251881Speter svn_fs_txn_t *txn, 328251881Speter apr_pool_t *pool) 329251881Speter{ 330251881Speter svn_fs_root_t *root; 331251881Speter struct txn_root_args args; 332251881Speter 333251881Speter args.root_p = &root; 334251881Speter args.txn = txn; 335251881Speter SVN_ERR(svn_fs_base__retry_txn(txn->fs, txn_body_txn_root, &args, 336251881Speter FALSE, pool)); 337251881Speter 338251881Speter *root_p = root; 339251881Speter return SVN_NO_ERROR; 340251881Speter} 341251881Speter 342251881Speter 343251881Speterstruct revision_root_args 344251881Speter{ 345251881Speter svn_fs_root_t **root_p; 346251881Speter svn_revnum_t rev; 347251881Speter}; 348251881Speter 349251881Speter 350251881Speterstatic svn_error_t * 351251881Spetertxn_body_revision_root(void *baton, 352251881Speter trail_t *trail) 353251881Speter{ 354251881Speter struct revision_root_args *args = baton; 355251881Speter dag_node_t *root_dir; 356251881Speter svn_fs_root_t *root; 357251881Speter 358251881Speter SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, args->rev, 359251881Speter trail, trail->pool)); 360251881Speter root = make_revision_root(trail->fs, args->rev, root_dir, trail->pool); 361251881Speter 362251881Speter *args->root_p = root; 363251881Speter return SVN_NO_ERROR; 364251881Speter} 365251881Speter 366251881Speter 367251881Spetersvn_error_t * 368251881Spetersvn_fs_base__revision_root(svn_fs_root_t **root_p, 369251881Speter svn_fs_t *fs, 370251881Speter svn_revnum_t rev, 371251881Speter apr_pool_t *pool) 372251881Speter{ 373251881Speter struct revision_root_args args; 374251881Speter svn_fs_root_t *root; 375251881Speter 376251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 377251881Speter 378251881Speter args.root_p = &root; 379251881Speter args.rev = rev; 380251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_revision_root, &args, 381251881Speter FALSE, pool)); 382251881Speter 383251881Speter *root_p = root; 384251881Speter return SVN_NO_ERROR; 385251881Speter} 386251881Speter 387251881Speter 388251881Speter 389251881Speter/* Getting dag nodes for roots. */ 390251881Speter 391251881Speter 392251881Speter/* Set *NODE_P to a freshly opened dag node referring to the root 393251881Speter directory of ROOT, as part of TRAIL. */ 394251881Speterstatic svn_error_t * 395251881Speterroot_node(dag_node_t **node_p, 396251881Speter svn_fs_root_t *root, 397251881Speter trail_t *trail, 398251881Speter apr_pool_t *pool) 399251881Speter{ 400251881Speter base_root_data_t *brd = root->fsap_data; 401251881Speter 402251881Speter if (! root->is_txn_root) 403251881Speter { 404251881Speter /* It's a revision root, so we already have its root directory 405251881Speter opened. */ 406251881Speter *node_p = svn_fs_base__dag_dup(brd->root_dir, pool); 407251881Speter return SVN_NO_ERROR; 408251881Speter } 409251881Speter else 410251881Speter { 411251881Speter /* It's a transaction root. Open a fresh copy. */ 412251881Speter return svn_fs_base__dag_txn_root(node_p, root->fs, root->txn, 413251881Speter trail, pool); 414251881Speter } 415251881Speter} 416251881Speter 417251881Speter 418251881Speter/* Set *NODE_P to a mutable root directory for ROOT, cloning if 419251881Speter necessary, as part of TRAIL. ROOT must be a transaction root. Use 420251881Speter ERROR_PATH in error messages. */ 421251881Speterstatic svn_error_t * 422251881Spetermutable_root_node(dag_node_t **node_p, 423251881Speter svn_fs_root_t *root, 424251881Speter const char *error_path, 425251881Speter trail_t *trail, 426251881Speter apr_pool_t *pool) 427251881Speter{ 428251881Speter if (root->is_txn_root) 429251881Speter return svn_fs_base__dag_clone_root(node_p, root->fs, root->txn, 430251881Speter trail, pool); 431251881Speter else 432251881Speter /* If it's not a transaction root, we can't change its contents. */ 433251881Speter return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path); 434251881Speter} 435251881Speter 436251881Speter 437251881Speter 438251881Speter/* Traversing directory paths. */ 439251881Speter 440251881Spetertypedef enum copy_id_inherit_t 441251881Speter{ 442251881Speter copy_id_inherit_unknown = 0, 443251881Speter copy_id_inherit_self, 444251881Speter copy_id_inherit_parent, 445251881Speter copy_id_inherit_new 446251881Speter 447251881Speter} copy_id_inherit_t; 448251881Speter 449251881Speter/* A linked list representing the path from a node up to a root 450251881Speter directory. We use this for cloning, and for operations that need 451251881Speter to deal with both a node and its parent directory. For example, a 452251881Speter `delete' operation needs to know that the node actually exists, but 453251881Speter also needs to change the parent directory. */ 454251881Spetertypedef struct parent_path_t 455251881Speter{ 456251881Speter 457251881Speter /* A node along the path. This could be the final node, one of its 458251881Speter parents, or the root. Every parent path ends with an element for 459251881Speter the root directory. */ 460251881Speter dag_node_t *node; 461251881Speter 462251881Speter /* The name NODE has in its parent directory. This is zero for the 463251881Speter root directory, which (obviously) has no name in its parent. */ 464251881Speter char *entry; 465251881Speter 466251881Speter /* The parent of NODE, or zero if NODE is the root directory. */ 467251881Speter struct parent_path_t *parent; 468251881Speter 469251881Speter /* The copy ID inheritance style. */ 470251881Speter copy_id_inherit_t copy_inherit; 471251881Speter 472251881Speter /* If copy ID inheritance style is copy_id_inherit_new, this is the 473251881Speter path which should be implicitly copied; otherwise, this is NULL. */ 474251881Speter const char *copy_src_path; 475251881Speter 476251881Speter} parent_path_t; 477251881Speter 478251881Speter 479251881Speter/* Return the FS path for the parent path chain object PARENT_PATH, 480251881Speter allocated in POOL. */ 481251881Speterstatic const char * 482251881Speterparent_path_path(parent_path_t *parent_path, 483251881Speter apr_pool_t *pool) 484251881Speter{ 485251881Speter const char *path_so_far = "/"; 486251881Speter if (parent_path->parent) 487251881Speter path_so_far = parent_path_path(parent_path->parent, pool); 488251881Speter return parent_path->entry 489251881Speter ? svn_fspath__join(path_so_far, parent_path->entry, pool) 490251881Speter : path_so_far; 491251881Speter} 492251881Speter 493251881Speter 494251881Speter/* Return the FS path for the parent path chain object CHILD relative 495251881Speter to its ANCESTOR in the same chain, allocated in POOL. */ 496251881Speterstatic const char * 497251881Speterparent_path_relpath(parent_path_t *child, 498251881Speter parent_path_t *ancestor, 499251881Speter apr_pool_t *pool) 500251881Speter{ 501251881Speter const char *path_so_far = ""; 502251881Speter parent_path_t *this_node = child; 503251881Speter while (this_node != ancestor) 504251881Speter { 505251881Speter assert(this_node != NULL); 506251881Speter path_so_far = svn_relpath_join(this_node->entry, path_so_far, pool); 507251881Speter this_node = this_node->parent; 508251881Speter } 509251881Speter return path_so_far; 510251881Speter} 511251881Speter 512251881Speter 513251881Speter/* Choose a copy ID inheritance method *INHERIT_P to be used in the 514251881Speter event that immutable node CHILD in FS needs to be made mutable. If 515251881Speter the inheritance method is copy_id_inherit_new, also return a 516251881Speter *COPY_SRC_PATH on which to base the new copy ID (else return NULL 517251881Speter for that path). CHILD must have a parent (it cannot be the root 518251881Speter node). TXN_ID is the transaction in which these items might be 519251881Speter mutable. */ 520251881Speterstatic svn_error_t * 521251881Speterget_copy_inheritance(copy_id_inherit_t *inherit_p, 522251881Speter const char **copy_src_path, 523251881Speter svn_fs_t *fs, 524251881Speter parent_path_t *child, 525251881Speter const char *txn_id, 526251881Speter trail_t *trail, 527251881Speter apr_pool_t *pool) 528251881Speter{ 529251881Speter const svn_fs_id_t *child_id, *parent_id; 530251881Speter const char *child_copy_id, *parent_copy_id; 531251881Speter const char *id_path = NULL; 532251881Speter 533251881Speter SVN_ERR_ASSERT(child && child->parent && txn_id); 534251881Speter 535251881Speter /* Initialize our return variables (default: self-inheritance). */ 536251881Speter *inherit_p = copy_id_inherit_self; 537251881Speter *copy_src_path = NULL; 538251881Speter 539251881Speter /* Initialize some convenience variables. */ 540251881Speter child_id = svn_fs_base__dag_get_id(child->node); 541251881Speter parent_id = svn_fs_base__dag_get_id(child->parent->node); 542251881Speter child_copy_id = svn_fs_base__id_copy_id(child_id); 543251881Speter parent_copy_id = svn_fs_base__id_copy_id(parent_id); 544251881Speter 545251881Speter /* Easy out: if this child is already mutable, we have nothing to do. */ 546251881Speter if (svn_fs_base__key_compare(svn_fs_base__id_txn_id(child_id), txn_id) == 0) 547251881Speter return SVN_NO_ERROR; 548251881Speter 549251881Speter /* If the child and its parent are on the same branch, then the 550251881Speter child will inherit the copy ID of its parent when made mutable. 551251881Speter This is trivially detectable when the child and its parent have 552251881Speter the same copy ID. But that's not the sole indicator of 553251881Speter same-branchness. It might be the case that the parent was the 554251881Speter result of a copy, but the child has not yet been cloned for 555251881Speter mutability since that copy. Detection of this latter case 556251881Speter basically means making sure the copy IDs don't differ for some 557251881Speter other reason, such as that the child was the direct target of the 558251881Speter copy whose ID it has. There is a special case here, too -- if 559251881Speter the child's copy ID is the special ID "0", it can't have been the 560251881Speter target of any copy, and therefore must be on the same branch as 561251881Speter its parent. */ 562251881Speter if ((strcmp(child_copy_id, "0") == 0) 563251881Speter || (svn_fs_base__key_compare(child_copy_id, parent_copy_id) == 0)) 564251881Speter { 565251881Speter *inherit_p = copy_id_inherit_parent; 566251881Speter return SVN_NO_ERROR; 567251881Speter } 568251881Speter else 569251881Speter { 570251881Speter copy_t *copy; 571251881Speter SVN_ERR(svn_fs_bdb__get_copy(©, fs, child_copy_id, trail, pool)); 572251881Speter if (svn_fs_base__id_compare(copy->dst_noderev_id, child_id) == -1) 573251881Speter { 574251881Speter *inherit_p = copy_id_inherit_parent; 575251881Speter return SVN_NO_ERROR; 576251881Speter } 577251881Speter } 578251881Speter 579251881Speter /* If we get here, the child and its parent are not on speaking 580251881Speter terms -- there will be no parental inheritance handed down in 581251881Speter *this* generation. */ 582251881Speter 583251881Speter /* If the child was created at a different path than the one we are 584251881Speter expecting its clone to live, one of its parents must have been 585251881Speter created via a copy since the child was created. The child isn't 586251881Speter on the same branch as its parent (we caught those cases early); 587251881Speter it can't keep its current copy ID because there's been an 588251881Speter affecting copy (its clone won't be on the same branch as the 589251881Speter child is). That leaves only one course of action -- to assign 590251881Speter the child a brand new "soft" copy ID. */ 591251881Speter id_path = svn_fs_base__dag_get_created_path(child->node); 592251881Speter if (strcmp(id_path, parent_path_path(child, pool)) != 0) 593251881Speter { 594251881Speter *inherit_p = copy_id_inherit_new; 595251881Speter *copy_src_path = id_path; 596251881Speter return SVN_NO_ERROR; 597251881Speter } 598251881Speter 599251881Speter /* The node gets to keep its own ID. */ 600251881Speter return SVN_NO_ERROR; 601251881Speter} 602251881Speter 603251881Speter 604251881Speter/* Allocate a new parent_path_t node from POOL, referring to NODE, 605251881Speter ENTRY, PARENT, and COPY_ID. */ 606251881Speterstatic parent_path_t * 607251881Spetermake_parent_path(dag_node_t *node, 608251881Speter char *entry, 609251881Speter parent_path_t *parent, 610251881Speter apr_pool_t *pool) 611251881Speter{ 612251881Speter parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path)); 613251881Speter parent_path->node = node; 614251881Speter parent_path->entry = entry; 615251881Speter parent_path->parent = parent; 616251881Speter parent_path->copy_inherit = copy_id_inherit_unknown; 617251881Speter parent_path->copy_src_path = NULL; 618251881Speter return parent_path; 619251881Speter} 620251881Speter 621251881Speter 622251881Speter/* Flags for open_path. */ 623251881Spetertypedef enum open_path_flags_t { 624251881Speter 625251881Speter /* The last component of the PATH need not exist. (All parent 626251881Speter directories must exist, as usual.) If the last component doesn't 627251881Speter exist, simply leave the `node' member of the bottom parent_path 628251881Speter component zero. */ 629251881Speter open_path_last_optional = 1 630251881Speter 631251881Speter} open_path_flags_t; 632251881Speter 633251881Speter 634251881Speter/* Open the node identified by PATH in ROOT, as part of TRAIL. Set 635251881Speter *PARENT_PATH_P to a path from the node up to ROOT, allocated in 636251881Speter TRAIL->pool. The resulting *PARENT_PATH_P value is guaranteed to 637251881Speter contain at least one element, for the root directory. 638251881Speter 639251881Speter If resulting *PARENT_PATH_P will eventually be made mutable and 640251881Speter modified, or if copy ID inheritance information is otherwise 641251881Speter needed, TXN_ID should be the ID of the mutability transaction. If 642251881Speter TXN_ID is NULL, no copy ID in heritance information will be 643251881Speter calculated for the *PARENT_PATH_P chain. 644251881Speter 645251881Speter If FLAGS & open_path_last_optional is zero, return the error 646251881Speter SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist. If 647251881Speter non-zero, require all the parent directories to exist as normal, 648251881Speter but if the final path component doesn't exist, simply return a path 649251881Speter whose bottom `node' member is zero. This option is useful for 650251881Speter callers that create new nodes --- we find the parent directory for 651251881Speter them, and tell them whether the entry exists already. 652251881Speter 653251881Speter NOTE: Public interfaces which only *read* from the filesystem 654251881Speter should not call this function directly, but should instead use 655251881Speter get_dag(). 656251881Speter*/ 657251881Speterstatic svn_error_t * 658251881Speteropen_path(parent_path_t **parent_path_p, 659251881Speter svn_fs_root_t *root, 660251881Speter const char *path, 661251881Speter int flags, 662251881Speter const char *txn_id, 663251881Speter trail_t *trail, 664251881Speter apr_pool_t *pool) 665251881Speter{ 666251881Speter svn_fs_t *fs = root->fs; 667251881Speter dag_node_t *here; /* The directory we're currently looking at. */ 668251881Speter parent_path_t *parent_path; /* The path from HERE up to the root. */ 669251881Speter const char *rest; /* The portion of PATH we haven't traversed yet. */ 670251881Speter const char *canon_path = svn_fs__canonicalize_abspath(path, pool); 671251881Speter const char *path_so_far = "/"; 672251881Speter 673251881Speter /* Make a parent_path item for the root node, using its own current 674251881Speter copy id. */ 675251881Speter SVN_ERR(root_node(&here, root, trail, pool)); 676251881Speter parent_path = make_parent_path(here, 0, 0, pool); 677251881Speter parent_path->copy_inherit = copy_id_inherit_self; 678251881Speter 679251881Speter rest = canon_path + 1; /* skip the leading '/', it saves in iteration */ 680251881Speter 681251881Speter /* Whenever we are at the top of this loop: 682251881Speter - HERE is our current directory, 683251881Speter - ID is the node revision ID of HERE, 684251881Speter - REST is the path we're going to find in HERE, and 685251881Speter - PARENT_PATH includes HERE and all its parents. */ 686251881Speter for (;;) 687251881Speter { 688251881Speter const char *next; 689251881Speter char *entry; 690251881Speter dag_node_t *child; 691251881Speter 692251881Speter /* Parse out the next entry from the path. */ 693251881Speter entry = svn_fs__next_entry_name(&next, rest, pool); 694251881Speter 695251881Speter /* Calculate the path traversed thus far. */ 696251881Speter path_so_far = svn_fspath__join(path_so_far, entry, pool); 697251881Speter 698251881Speter if (*entry == '\0') 699251881Speter { 700251881Speter /* Given the behavior of svn_fs__next_entry_name(), this 701251881Speter happens when the path either starts or ends with a slash. 702251881Speter In either case, we stay put: the current directory stays 703251881Speter the same, and we add nothing to the parent path. */ 704251881Speter child = here; 705251881Speter } 706251881Speter else 707251881Speter { 708251881Speter copy_id_inherit_t inherit; 709251881Speter const char *copy_path = NULL; 710251881Speter svn_error_t *err = SVN_NO_ERROR; 711251881Speter dag_node_t *cached_node; 712251881Speter 713251881Speter /* If we found a directory entry, follow it. First, we 714251881Speter check our node cache, and, failing that, we hit the DAG 715251881Speter layer. */ 716251881Speter cached_node = dag_node_cache_get(root, path_so_far, pool); 717251881Speter if (cached_node) 718251881Speter child = cached_node; 719251881Speter else 720251881Speter err = svn_fs_base__dag_open(&child, here, entry, trail, pool); 721251881Speter 722251881Speter /* "file not found" requires special handling. */ 723251881Speter if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) 724251881Speter { 725251881Speter /* If this was the last path component, and the caller 726251881Speter said it was optional, then don't return an error; 727251881Speter just put a NULL node pointer in the path. */ 728251881Speter 729251881Speter svn_error_clear(err); 730251881Speter 731251881Speter if ((flags & open_path_last_optional) 732251881Speter && (! next || *next == '\0')) 733251881Speter { 734251881Speter parent_path = make_parent_path(NULL, entry, parent_path, 735251881Speter pool); 736251881Speter break; 737251881Speter } 738251881Speter else 739251881Speter { 740251881Speter /* Build a better error message than svn_fs_base__dag_open 741251881Speter can provide, giving the root and full path name. */ 742251881Speter return SVN_FS__NOT_FOUND(root, path); 743251881Speter } 744251881Speter } 745251881Speter 746251881Speter /* Other errors we return normally. */ 747251881Speter SVN_ERR(err); 748251881Speter 749251881Speter /* Now, make a parent_path item for CHILD. */ 750251881Speter parent_path = make_parent_path(child, entry, parent_path, pool); 751251881Speter if (txn_id) 752251881Speter { 753251881Speter SVN_ERR(get_copy_inheritance(&inherit, ©_path, 754251881Speter fs, parent_path, txn_id, 755251881Speter trail, pool)); 756251881Speter parent_path->copy_inherit = inherit; 757251881Speter parent_path->copy_src_path = apr_pstrdup(pool, copy_path); 758251881Speter } 759251881Speter 760251881Speter /* Cache the node we found (if it wasn't already cached). */ 761251881Speter if (! cached_node) 762251881Speter dag_node_cache_set(root, path_so_far, child); 763251881Speter } 764251881Speter 765251881Speter /* Are we finished traversing the path? */ 766251881Speter if (! next) 767251881Speter break; 768251881Speter 769251881Speter /* The path isn't finished yet; we'd better be in a directory. */ 770251881Speter if (svn_fs_base__dag_node_kind(child) != svn_node_dir) 771251881Speter SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far), 772251881Speter apr_psprintf(pool, _("Failure opening '%s'"), path)); 773251881Speter 774251881Speter rest = next; 775251881Speter here = child; 776251881Speter } 777251881Speter 778251881Speter *parent_path_p = parent_path; 779251881Speter return SVN_NO_ERROR; 780251881Speter} 781251881Speter 782251881Speter 783251881Speter/* Make the node referred to by PARENT_PATH mutable, if it isn't 784251881Speter already, as part of TRAIL. ROOT must be the root from which 785251881Speter PARENT_PATH descends. Clone any parent directories as needed. 786251881Speter Adjust the dag nodes in PARENT_PATH to refer to the clones. Use 787251881Speter ERROR_PATH in error messages. */ 788251881Speterstatic svn_error_t * 789251881Spetermake_path_mutable(svn_fs_root_t *root, 790251881Speter parent_path_t *parent_path, 791251881Speter const char *error_path, 792251881Speter trail_t *trail, 793251881Speter apr_pool_t *pool) 794251881Speter{ 795251881Speter dag_node_t *cloned_node; 796251881Speter const char *txn_id = root->txn; 797251881Speter svn_fs_t *fs = root->fs; 798251881Speter 799251881Speter /* Is the node mutable already? */ 800251881Speter if (svn_fs_base__dag_check_mutable(parent_path->node, txn_id)) 801251881Speter return SVN_NO_ERROR; 802251881Speter 803251881Speter /* Are we trying to clone the root, or somebody's child node? */ 804251881Speter if (parent_path->parent) 805251881Speter { 806251881Speter const svn_fs_id_t *parent_id; 807251881Speter const svn_fs_id_t *node_id = svn_fs_base__dag_get_id(parent_path->node); 808251881Speter const char *copy_id = NULL; 809251881Speter const char *copy_src_path = parent_path->copy_src_path; 810251881Speter copy_id_inherit_t inherit = parent_path->copy_inherit; 811251881Speter const char *clone_path; 812251881Speter 813251881Speter /* We're trying to clone somebody's child. Make sure our parent 814251881Speter is mutable. */ 815251881Speter SVN_ERR(make_path_mutable(root, parent_path->parent, 816251881Speter error_path, trail, pool)); 817251881Speter 818251881Speter switch (inherit) 819251881Speter { 820251881Speter case copy_id_inherit_parent: 821251881Speter parent_id = svn_fs_base__dag_get_id(parent_path->parent->node); 822251881Speter copy_id = svn_fs_base__id_copy_id(parent_id); 823251881Speter break; 824251881Speter 825251881Speter case copy_id_inherit_new: 826251881Speter SVN_ERR(svn_fs_bdb__reserve_copy_id(©_id, fs, trail, pool)); 827251881Speter break; 828251881Speter 829251881Speter case copy_id_inherit_self: 830251881Speter copy_id = NULL; 831251881Speter break; 832251881Speter 833251881Speter case copy_id_inherit_unknown: 834251881Speter default: 835251881Speter SVN_ERR_MALFUNCTION(); /* uh-oh -- somebody didn't calculate copy-ID 836251881Speter inheritance data. */ 837251881Speter } 838251881Speter 839251881Speter /* Now make this node mutable. */ 840251881Speter clone_path = parent_path_path(parent_path->parent, pool); 841251881Speter SVN_ERR(svn_fs_base__dag_clone_child(&cloned_node, 842251881Speter parent_path->parent->node, 843251881Speter clone_path, 844251881Speter parent_path->entry, 845251881Speter copy_id, txn_id, 846251881Speter trail, pool)); 847251881Speter 848251881Speter /* If we just created a brand new copy ID, we need to store a 849251881Speter `copies' table entry for it, as well as a notation in the 850251881Speter transaction that should this transaction be terminated, our 851251881Speter new copy needs to be removed. */ 852251881Speter if (inherit == copy_id_inherit_new) 853251881Speter { 854251881Speter const svn_fs_id_t *new_node_id = 855251881Speter svn_fs_base__dag_get_id(cloned_node); 856251881Speter SVN_ERR(svn_fs_bdb__create_copy(fs, copy_id, copy_src_path, 857251881Speter svn_fs_base__id_txn_id(node_id), 858251881Speter new_node_id, 859251881Speter copy_kind_soft, trail, pool)); 860251881Speter SVN_ERR(svn_fs_base__add_txn_copy(fs, txn_id, copy_id, 861251881Speter trail, pool)); 862251881Speter } 863251881Speter } 864251881Speter else 865251881Speter { 866251881Speter /* We're trying to clone the root directory. */ 867251881Speter SVN_ERR(mutable_root_node(&cloned_node, root, error_path, trail, pool)); 868251881Speter } 869251881Speter 870251881Speter /* Update the PARENT_PATH link to refer to the clone. */ 871251881Speter parent_path->node = cloned_node; 872251881Speter 873251881Speter return SVN_NO_ERROR; 874251881Speter} 875251881Speter 876251881Speter 877251881Speter/* Walk up PARENT_PATH to the root of the tree, adjusting each node's 878251881Speter mergeinfo count by COUNT_DELTA as part of Subversion transaction 879251881Speter TXN_ID and TRAIL. Use POOL for allocations. */ 880251881Speterstatic svn_error_t * 881251881Speteradjust_parent_mergeinfo_counts(parent_path_t *parent_path, 882251881Speter apr_int64_t count_delta, 883251881Speter const char *txn_id, 884251881Speter trail_t *trail, 885251881Speter apr_pool_t *pool) 886251881Speter{ 887251881Speter apr_pool_t *iterpool; 888251881Speter parent_path_t *pp = parent_path; 889251881Speter 890251881Speter if (count_delta == 0) 891251881Speter return SVN_NO_ERROR; 892251881Speter 893251881Speter iterpool = svn_pool_create(pool); 894251881Speter 895251881Speter while (pp) 896251881Speter { 897251881Speter svn_pool_clear(iterpool); 898251881Speter SVN_ERR(svn_fs_base__dag_adjust_mergeinfo_count(pp->node, count_delta, 899251881Speter txn_id, trail, 900251881Speter iterpool)); 901251881Speter pp = pp->parent; 902251881Speter } 903251881Speter svn_pool_destroy(iterpool); 904251881Speter 905251881Speter return SVN_NO_ERROR; 906251881Speter} 907251881Speter 908251881Speter 909251881Speter/* Open the node identified by PATH in ROOT, as part of TRAIL. Set 910251881Speter *DAG_NODE_P to the node we find, allocated in TRAIL->pool. Return 911251881Speter the error SVN_ERR_FS_NOT_FOUND if this node doesn't exist. */ 912251881Speterstatic svn_error_t * 913251881Speterget_dag(dag_node_t **dag_node_p, 914251881Speter svn_fs_root_t *root, 915251881Speter const char *path, 916251881Speter trail_t *trail, 917251881Speter apr_pool_t *pool) 918251881Speter{ 919251881Speter parent_path_t *parent_path; 920251881Speter dag_node_t *node = NULL; 921251881Speter 922251881Speter /* Canonicalize the input PATH. */ 923251881Speter path = svn_fs__canonicalize_abspath(path, pool); 924251881Speter 925251881Speter /* If ROOT is a revision root, we'll look for the DAG in our cache. */ 926251881Speter node = dag_node_cache_get(root, path, pool); 927251881Speter if (! node) 928251881Speter { 929251881Speter /* Call open_path with no flags, as we want this to return an error 930251881Speter if the node for which we are searching doesn't exist. */ 931251881Speter SVN_ERR(open_path(&parent_path, root, path, 0, NULL, trail, pool)); 932251881Speter node = parent_path->node; 933251881Speter 934251881Speter /* No need to cache our find -- open_path() will do that for us. */ 935251881Speter } 936251881Speter 937251881Speter *dag_node_p = node; 938251881Speter return SVN_NO_ERROR; 939251881Speter} 940251881Speter 941251881Speter 942251881Speter 943251881Speter/* Populating the `changes' table. */ 944251881Speter 945251881Speter/* Add a change to the changes table in FS, keyed on transaction id 946251881Speter TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on 947251881Speter PATH (whose node revision id is--or was, in the case of a 948251881Speter deletion--NODEREV_ID), and optionally that TEXT_MODs or PROP_MODs 949251881Speter occurred. Do all this as part of TRAIL. */ 950251881Speterstatic svn_error_t * 951251881Speteradd_change(svn_fs_t *fs, 952251881Speter const char *txn_id, 953251881Speter const char *path, 954251881Speter const svn_fs_id_t *noderev_id, 955251881Speter svn_fs_path_change_kind_t change_kind, 956251881Speter svn_boolean_t text_mod, 957251881Speter svn_boolean_t prop_mod, 958251881Speter trail_t *trail, 959251881Speter apr_pool_t *pool) 960251881Speter{ 961251881Speter change_t change; 962251881Speter change.path = svn_fs__canonicalize_abspath(path, pool); 963251881Speter change.noderev_id = noderev_id; 964251881Speter change.kind = change_kind; 965251881Speter change.text_mod = text_mod; 966251881Speter change.prop_mod = prop_mod; 967251881Speter return svn_fs_bdb__changes_add(fs, txn_id, &change, trail, pool); 968251881Speter} 969251881Speter 970251881Speter 971251881Speter 972251881Speter/* Generic node operations. */ 973251881Speter 974251881Speter 975251881Speterstruct node_id_args { 976251881Speter const svn_fs_id_t **id_p; 977251881Speter svn_fs_root_t *root; 978251881Speter const char *path; 979251881Speter}; 980251881Speter 981251881Speter 982251881Speterstatic svn_error_t * 983251881Spetertxn_body_node_id(void *baton, trail_t *trail) 984251881Speter{ 985251881Speter struct node_id_args *args = baton; 986251881Speter dag_node_t *node; 987251881Speter 988251881Speter SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool)); 989251881Speter *args->id_p = svn_fs_base__id_copy(svn_fs_base__dag_get_id(node), 990251881Speter trail->pool); 991251881Speter 992251881Speter return SVN_NO_ERROR; 993251881Speter} 994251881Speter 995251881Speter 996251881Speterstatic svn_error_t * 997251881Speterbase_node_id(const svn_fs_id_t **id_p, 998251881Speter svn_fs_root_t *root, 999251881Speter const char *path, 1000251881Speter apr_pool_t *pool) 1001251881Speter{ 1002251881Speter base_root_data_t *brd = root->fsap_data; 1003251881Speter 1004251881Speter if (! root->is_txn_root 1005251881Speter && (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0')))) 1006251881Speter { 1007251881Speter /* Optimize the case where we don't need any db access at all. 1008251881Speter The root directory ("" or "/") node is stored in the 1009251881Speter svn_fs_root_t object, and never changes when it's a revision 1010251881Speter root, so we can just reach in and grab it directly. */ 1011251881Speter *id_p = svn_fs_base__id_copy(svn_fs_base__dag_get_id(brd->root_dir), 1012251881Speter pool); 1013251881Speter } 1014251881Speter else 1015251881Speter { 1016251881Speter const svn_fs_id_t *id; 1017251881Speter struct node_id_args args; 1018251881Speter 1019251881Speter args.id_p = &id; 1020251881Speter args.root = root; 1021251881Speter args.path = path; 1022251881Speter 1023251881Speter SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_id, &args, 1024251881Speter FALSE, pool)); 1025251881Speter *id_p = id; 1026251881Speter } 1027251881Speter return SVN_NO_ERROR; 1028251881Speter} 1029251881Speter 1030251881Speter 1031251881Speterstruct node_created_rev_args { 1032251881Speter svn_revnum_t revision; 1033251881Speter svn_fs_root_t *root; 1034251881Speter const char *path; 1035251881Speter}; 1036251881Speter 1037251881Speter 1038251881Speterstatic svn_error_t * 1039251881Spetertxn_body_node_created_rev(void *baton, trail_t *trail) 1040251881Speter{ 1041251881Speter struct node_created_rev_args *args = baton; 1042251881Speter dag_node_t *node; 1043251881Speter 1044251881Speter SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool)); 1045251881Speter return svn_fs_base__dag_get_revision(&(args->revision), node, 1046251881Speter trail, trail->pool); 1047251881Speter} 1048251881Speter 1049251881Speter 1050251881Speterstatic svn_error_t * 1051251881Speterbase_node_created_rev(svn_revnum_t *revision, 1052251881Speter svn_fs_root_t *root, 1053251881Speter const char *path, 1054251881Speter apr_pool_t *pool) 1055251881Speter{ 1056251881Speter struct node_created_rev_args args; 1057251881Speter 1058251881Speter args.revision = SVN_INVALID_REVNUM; 1059251881Speter args.root = root; 1060251881Speter args.path = path; 1061251881Speter SVN_ERR(svn_fs_base__retry_txn 1062251881Speter (root->fs, txn_body_node_created_rev, &args, TRUE, pool)); 1063251881Speter *revision = args.revision; 1064251881Speter return SVN_NO_ERROR; 1065251881Speter} 1066251881Speter 1067251881Speter 1068251881Speterstruct node_created_path_args { 1069251881Speter const char **created_path; 1070251881Speter svn_fs_root_t *root; 1071251881Speter const char *path; 1072251881Speter}; 1073251881Speter 1074251881Speter 1075251881Speterstatic svn_error_t * 1076251881Spetertxn_body_node_created_path(void *baton, trail_t *trail) 1077251881Speter{ 1078251881Speter struct node_created_path_args *args = baton; 1079251881Speter dag_node_t *node; 1080251881Speter 1081251881Speter SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool)); 1082251881Speter *args->created_path = svn_fs_base__dag_get_created_path(node); 1083251881Speter return SVN_NO_ERROR; 1084251881Speter} 1085251881Speter 1086251881Speter 1087251881Speterstatic svn_error_t * 1088251881Speterbase_node_created_path(const char **created_path, 1089251881Speter svn_fs_root_t *root, 1090251881Speter const char *path, 1091251881Speter apr_pool_t *pool) 1092251881Speter{ 1093251881Speter struct node_created_path_args args; 1094251881Speter apr_pool_t *scratch_pool = svn_pool_create(pool); 1095251881Speter 1096251881Speter args.created_path = created_path; 1097251881Speter args.root = root; 1098251881Speter args.path = path; 1099251881Speter 1100251881Speter SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_created_path, &args, 1101251881Speter FALSE, scratch_pool)); 1102251881Speter if (*created_path) 1103251881Speter *created_path = apr_pstrdup(pool, *created_path); 1104251881Speter svn_pool_destroy(scratch_pool); 1105251881Speter return SVN_NO_ERROR; 1106251881Speter} 1107251881Speter 1108251881Speter 1109251881Speterstruct node_kind_args { 1110251881Speter const svn_fs_id_t *id; 1111251881Speter svn_node_kind_t kind; /* OUT parameter */ 1112251881Speter}; 1113251881Speter 1114251881Speter 1115251881Speterstatic svn_error_t * 1116251881Spetertxn_body_node_kind(void *baton, trail_t *trail) 1117251881Speter{ 1118251881Speter struct node_kind_args *args = baton; 1119251881Speter dag_node_t *node; 1120251881Speter 1121251881Speter SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id, 1122251881Speter trail, trail->pool)); 1123251881Speter args->kind = svn_fs_base__dag_node_kind(node); 1124251881Speter 1125251881Speter return SVN_NO_ERROR; 1126251881Speter} 1127251881Speter 1128251881Speter 1129251881Speterstatic svn_error_t * 1130251881Speternode_kind(svn_node_kind_t *kind_p, 1131251881Speter svn_fs_root_t *root, 1132251881Speter const char *path, 1133251881Speter apr_pool_t *pool) 1134251881Speter{ 1135251881Speter struct node_kind_args args; 1136251881Speter const svn_fs_id_t *node_id; 1137251881Speter 1138251881Speter /* Get the node id. */ 1139251881Speter SVN_ERR(base_node_id(&node_id, root, path, pool)); 1140251881Speter 1141251881Speter /* Use the node id to get the real kind. */ 1142251881Speter args.id = node_id; 1143251881Speter SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_kind, &args, 1144251881Speter TRUE, pool)); 1145251881Speter 1146251881Speter *kind_p = args.kind; 1147251881Speter return SVN_NO_ERROR; 1148251881Speter} 1149251881Speter 1150251881Speter 1151251881Speterstatic svn_error_t * 1152251881Speterbase_check_path(svn_node_kind_t *kind_p, 1153251881Speter svn_fs_root_t *root, 1154251881Speter const char *path, 1155251881Speter apr_pool_t *pool) 1156251881Speter{ 1157251881Speter svn_error_t *err = node_kind(kind_p, root, path, pool); 1158251881Speter if (err && 1159251881Speter ((err->apr_err == SVN_ERR_FS_NOT_FOUND) 1160251881Speter || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY))) 1161251881Speter { 1162251881Speter svn_error_clear(err); 1163251881Speter err = SVN_NO_ERROR; 1164251881Speter *kind_p = svn_node_none; 1165251881Speter } 1166251881Speter 1167251881Speter return svn_error_trace(err); 1168251881Speter} 1169251881Speter 1170251881Speter 1171251881Speterstruct node_prop_args 1172251881Speter{ 1173251881Speter svn_string_t **value_p; 1174251881Speter svn_fs_root_t *root; 1175251881Speter const char *path; 1176251881Speter const char *propname; 1177251881Speter}; 1178251881Speter 1179251881Speter 1180251881Speterstatic svn_error_t * 1181251881Spetertxn_body_node_prop(void *baton, 1182251881Speter trail_t *trail) 1183251881Speter{ 1184251881Speter struct node_prop_args *args = baton; 1185251881Speter dag_node_t *node; 1186251881Speter apr_hash_t *proplist; 1187251881Speter 1188251881Speter SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool)); 1189251881Speter SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node, 1190251881Speter trail, trail->pool)); 1191251881Speter *(args->value_p) = NULL; 1192251881Speter if (proplist) 1193251881Speter *(args->value_p) = svn_hash_gets(proplist, args->propname); 1194251881Speter return SVN_NO_ERROR; 1195251881Speter} 1196251881Speter 1197251881Speter 1198251881Speterstatic svn_error_t * 1199251881Speterbase_node_prop(svn_string_t **value_p, 1200251881Speter svn_fs_root_t *root, 1201251881Speter const char *path, 1202251881Speter const char *propname, 1203251881Speter apr_pool_t *pool) 1204251881Speter{ 1205251881Speter struct node_prop_args args; 1206251881Speter svn_string_t *value; 1207251881Speter apr_pool_t *scratch_pool = svn_pool_create(pool); 1208251881Speter 1209251881Speter args.value_p = &value; 1210251881Speter args.root = root; 1211251881Speter args.path = path; 1212251881Speter args.propname = propname; 1213251881Speter SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_prop, &args, 1214251881Speter FALSE, scratch_pool)); 1215251881Speter *value_p = value ? svn_string_dup(value, pool) : NULL; 1216251881Speter svn_pool_destroy(scratch_pool); 1217251881Speter return SVN_NO_ERROR; 1218251881Speter} 1219251881Speter 1220251881Speter 1221251881Speterstruct node_proplist_args { 1222251881Speter apr_hash_t **table_p; 1223251881Speter svn_fs_root_t *root; 1224251881Speter const char *path; 1225251881Speter}; 1226251881Speter 1227251881Speter 1228251881Speterstatic svn_error_t * 1229251881Spetertxn_body_node_proplist(void *baton, trail_t *trail) 1230251881Speter{ 1231251881Speter struct node_proplist_args *args = baton; 1232251881Speter dag_node_t *node; 1233251881Speter apr_hash_t *proplist; 1234251881Speter 1235251881Speter SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool)); 1236251881Speter SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node, 1237251881Speter trail, trail->pool)); 1238251881Speter *args->table_p = proplist ? proplist : apr_hash_make(trail->pool); 1239251881Speter return SVN_NO_ERROR; 1240251881Speter} 1241251881Speter 1242251881Speter 1243251881Speterstatic svn_error_t * 1244251881Speterbase_node_proplist(apr_hash_t **table_p, 1245251881Speter svn_fs_root_t *root, 1246251881Speter const char *path, 1247251881Speter apr_pool_t *pool) 1248251881Speter{ 1249251881Speter apr_hash_t *table; 1250251881Speter struct node_proplist_args args; 1251251881Speter 1252251881Speter args.table_p = &table; 1253251881Speter args.root = root; 1254251881Speter args.path = path; 1255251881Speter 1256251881Speter SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_proplist, &args, 1257251881Speter FALSE, pool)); 1258251881Speter 1259251881Speter *table_p = table; 1260251881Speter return SVN_NO_ERROR; 1261251881Speter} 1262251881Speter 1263251881Speter 1264251881Speterstruct change_node_prop_args { 1265251881Speter svn_fs_root_t *root; 1266251881Speter const char *path; 1267251881Speter const char *name; 1268251881Speter const svn_string_t *value; 1269251881Speter}; 1270251881Speter 1271251881Speter 1272251881Speterstatic svn_error_t * 1273251881Spetertxn_body_change_node_prop(void *baton, 1274251881Speter trail_t *trail) 1275251881Speter{ 1276251881Speter struct change_node_prop_args *args = baton; 1277251881Speter parent_path_t *parent_path; 1278251881Speter apr_hash_t *proplist; 1279251881Speter const char *txn_id = args->root->txn; 1280251881Speter base_fs_data_t *bfd = trail->fs->fsap_data; 1281251881Speter 1282251881Speter SVN_ERR(open_path(&parent_path, args->root, args->path, 0, txn_id, 1283251881Speter trail, trail->pool)); 1284251881Speter 1285251881Speter /* Check to see if path is locked; if so, check that we can use it. 1286251881Speter Notice that we're doing this non-recursively, regardless of node kind. */ 1287251881Speter if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS) 1288251881Speter SVN_ERR(svn_fs_base__allow_locked_operation 1289251881Speter (args->path, FALSE, trail, trail->pool)); 1290251881Speter 1291251881Speter SVN_ERR(make_path_mutable(args->root, parent_path, args->path, 1292251881Speter trail, trail->pool)); 1293251881Speter SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, parent_path->node, 1294251881Speter trail, trail->pool)); 1295251881Speter 1296251881Speter /* If there's no proplist, but we're just deleting a property, exit now. */ 1297251881Speter if ((! proplist) && (! args->value)) 1298251881Speter return SVN_NO_ERROR; 1299251881Speter 1300251881Speter /* Now, if there's no proplist, we know we need to make one. */ 1301251881Speter if (! proplist) 1302251881Speter proplist = apr_hash_make(trail->pool); 1303251881Speter 1304251881Speter /* Set the property. */ 1305251881Speter svn_hash_sets(proplist, args->name, args->value); 1306251881Speter 1307251881Speter /* Overwrite the node's proplist. */ 1308251881Speter SVN_ERR(svn_fs_base__dag_set_proplist(parent_path->node, proplist, 1309251881Speter txn_id, trail, trail->pool)); 1310251881Speter 1311251881Speter /* If this was a change to the mergeinfo property, and our version 1312251881Speter of the filesystem cares, we have some extra recording to do. 1313251881Speter 1314251881Speter ### If the format *doesn't* support mergeinfo recording, should 1315251881Speter ### we fuss about attempts to change the svn:mergeinfo property 1316251881Speter ### in any way save to delete it? */ 1317251881Speter if ((bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT) 1318251881Speter && (strcmp(args->name, SVN_PROP_MERGEINFO) == 0)) 1319251881Speter { 1320251881Speter svn_boolean_t had_mergeinfo, has_mergeinfo = args->value != NULL; 1321251881Speter 1322251881Speter /* First, note on our node that it has mergeinfo. */ 1323251881Speter SVN_ERR(svn_fs_base__dag_set_has_mergeinfo(parent_path->node, 1324251881Speter has_mergeinfo, 1325251881Speter &had_mergeinfo, txn_id, 1326251881Speter trail, trail->pool)); 1327251881Speter 1328251881Speter /* If this is a change from the old state, we need to update our 1329251881Speter node's parents' mergeinfo counts by a factor of 1. */ 1330251881Speter if (parent_path->parent && ((! had_mergeinfo) != (! has_mergeinfo))) 1331251881Speter SVN_ERR(adjust_parent_mergeinfo_counts(parent_path->parent, 1332251881Speter has_mergeinfo ? 1 : -1, 1333251881Speter txn_id, trail, trail->pool)); 1334251881Speter } 1335251881Speter 1336251881Speter /* Make a record of this modification in the changes table. */ 1337251881Speter return add_change(args->root->fs, txn_id, 1338251881Speter args->path, svn_fs_base__dag_get_id(parent_path->node), 1339251881Speter svn_fs_path_change_modify, FALSE, TRUE, trail, 1340251881Speter trail->pool); 1341251881Speter} 1342251881Speter 1343251881Speter 1344251881Speterstatic svn_error_t * 1345251881Speterbase_change_node_prop(svn_fs_root_t *root, 1346251881Speter const char *path, 1347251881Speter const char *name, 1348251881Speter const svn_string_t *value, 1349251881Speter apr_pool_t *pool) 1350251881Speter{ 1351251881Speter struct change_node_prop_args args; 1352251881Speter 1353251881Speter if (! root->is_txn_root) 1354251881Speter return SVN_FS__NOT_TXN(root); 1355251881Speter 1356251881Speter args.root = root; 1357251881Speter args.path = path; 1358251881Speter args.name = name; 1359251881Speter args.value = value; 1360251881Speter return svn_fs_base__retry_txn(root->fs, txn_body_change_node_prop, &args, 1361251881Speter TRUE, pool); 1362251881Speter} 1363251881Speter 1364251881Speter 1365251881Speterstruct things_changed_args 1366251881Speter{ 1367251881Speter svn_boolean_t *changed_p; 1368251881Speter svn_fs_root_t *root1; 1369251881Speter svn_fs_root_t *root2; 1370251881Speter const char *path1; 1371251881Speter const char *path2; 1372251881Speter apr_pool_t *pool; 1373251881Speter}; 1374251881Speter 1375251881Speter 1376251881Speterstatic svn_error_t * 1377251881Spetertxn_body_props_changed(void *baton, trail_t *trail) 1378251881Speter{ 1379251881Speter struct things_changed_args *args = baton; 1380251881Speter dag_node_t *node1, *node2; 1381251881Speter 1382251881Speter SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool)); 1383251881Speter SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool)); 1384251881Speter return svn_fs_base__things_different(args->changed_p, NULL, 1385251881Speter node1, node2, trail, trail->pool); 1386251881Speter} 1387251881Speter 1388251881Speter 1389251881Speterstatic svn_error_t * 1390251881Speterbase_props_changed(svn_boolean_t *changed_p, 1391251881Speter svn_fs_root_t *root1, 1392251881Speter const char *path1, 1393251881Speter svn_fs_root_t *root2, 1394251881Speter const char *path2, 1395251881Speter apr_pool_t *pool) 1396251881Speter{ 1397251881Speter struct things_changed_args args; 1398251881Speter 1399251881Speter /* Check that roots are in the same fs. */ 1400251881Speter if (root1->fs != root2->fs) 1401251881Speter return svn_error_create 1402251881Speter (SVN_ERR_FS_GENERAL, NULL, 1403251881Speter _("Cannot compare property value between two different filesystems")); 1404251881Speter 1405251881Speter args.root1 = root1; 1406251881Speter args.root2 = root2; 1407251881Speter args.path1 = path1; 1408251881Speter args.path2 = path2; 1409251881Speter args.changed_p = changed_p; 1410251881Speter args.pool = pool; 1411251881Speter 1412251881Speter return svn_fs_base__retry_txn(root1->fs, txn_body_props_changed, &args, 1413251881Speter TRUE, pool); 1414251881Speter} 1415251881Speter 1416251881Speter 1417251881Speter 1418251881Speter/* Miscellaneous table handling */ 1419251881Speter 1420251881Speterstruct miscellaneous_set_args 1421251881Speter{ 1422251881Speter const char *key; 1423251881Speter const char *val; 1424251881Speter}; 1425251881Speter 1426251881Speterstatic svn_error_t * 1427251881Spetertxn_body_miscellaneous_set(void *baton, trail_t *trail) 1428251881Speter{ 1429251881Speter struct miscellaneous_set_args *msa = baton; 1430251881Speter 1431251881Speter return svn_fs_bdb__miscellaneous_set(trail->fs, msa->key, msa->val, trail, 1432251881Speter trail->pool); 1433251881Speter} 1434251881Speter 1435251881Spetersvn_error_t * 1436251881Spetersvn_fs_base__miscellaneous_set(svn_fs_t *fs, 1437251881Speter const char *key, 1438251881Speter const char *val, 1439251881Speter apr_pool_t *pool) 1440251881Speter{ 1441251881Speter struct miscellaneous_set_args msa; 1442251881Speter msa.key = key; 1443251881Speter msa.val = val; 1444251881Speter 1445251881Speter return svn_fs_base__retry_txn(fs, txn_body_miscellaneous_set, &msa, 1446251881Speter TRUE, pool); 1447251881Speter} 1448251881Speter 1449251881Speterstruct miscellaneous_get_args 1450251881Speter{ 1451251881Speter const char *key; 1452251881Speter const char **val; 1453251881Speter}; 1454251881Speter 1455251881Speterstatic svn_error_t * 1456251881Spetertxn_body_miscellaneous_get(void *baton, trail_t *trail) 1457251881Speter{ 1458251881Speter struct miscellaneous_get_args *mga = baton; 1459251881Speter return svn_fs_bdb__miscellaneous_get(mga->val, trail->fs, mga->key, trail, 1460251881Speter trail->pool); 1461251881Speter} 1462251881Speter 1463251881Spetersvn_error_t * 1464251881Spetersvn_fs_base__miscellaneous_get(const char **val, 1465251881Speter svn_fs_t *fs, 1466251881Speter const char *key, 1467251881Speter apr_pool_t *pool) 1468251881Speter{ 1469251881Speter struct miscellaneous_get_args mga; 1470251881Speter apr_pool_t *scratch_pool = svn_pool_create(pool); 1471251881Speter 1472251881Speter mga.key = key; 1473251881Speter mga.val = val; 1474251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_miscellaneous_get, &mga, 1475251881Speter FALSE, scratch_pool)); 1476251881Speter if (*val) 1477251881Speter *val = apr_pstrdup(pool, *val); 1478251881Speter svn_pool_destroy(scratch_pool); 1479251881Speter return SVN_NO_ERROR; 1480251881Speter} 1481251881Speter 1482251881Speter 1483251881Speter 1484251881Speter/* Getting a directory's entries */ 1485251881Speter 1486251881Speter 1487251881Speterstruct dir_entries_args 1488251881Speter{ 1489251881Speter apr_hash_t **table_p; 1490251881Speter svn_fs_root_t *root; 1491251881Speter const char *path; 1492251881Speter}; 1493251881Speter 1494251881Speter 1495251881Speter/* *(BATON->table_p) will never be NULL on successful return */ 1496251881Speterstatic svn_error_t * 1497251881Spetertxn_body_dir_entries(void *baton, 1498251881Speter trail_t *trail) 1499251881Speter{ 1500251881Speter struct dir_entries_args *args = baton; 1501251881Speter dag_node_t *node; 1502251881Speter apr_hash_t *entries; 1503251881Speter 1504251881Speter SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool)); 1505251881Speter 1506251881Speter /* Get the entries for PARENT_PATH. */ 1507251881Speter SVN_ERR(svn_fs_base__dag_dir_entries(&entries, node, trail, trail->pool)); 1508251881Speter 1509251881Speter /* Potentially initialize the return value to an empty hash. */ 1510251881Speter *args->table_p = entries ? entries : apr_hash_make(trail->pool); 1511251881Speter return SVN_NO_ERROR; 1512251881Speter} 1513251881Speter 1514251881Speter 1515251881Speterstatic svn_error_t * 1516251881Speterbase_dir_entries(apr_hash_t **table_p, 1517251881Speter svn_fs_root_t *root, 1518251881Speter const char *path, 1519251881Speter apr_pool_t *pool) 1520251881Speter{ 1521251881Speter struct dir_entries_args args; 1522251881Speter apr_pool_t *iterpool; 1523251881Speter apr_hash_t *table; 1524251881Speter svn_fs_t *fs = root->fs; 1525251881Speter apr_hash_index_t *hi; 1526251881Speter 1527251881Speter args.table_p = &table; 1528251881Speter args.root = root; 1529251881Speter args.path = path; 1530251881Speter SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_dir_entries, &args, 1531251881Speter FALSE, pool)); 1532251881Speter 1533251881Speter iterpool = svn_pool_create(pool); 1534251881Speter 1535251881Speter /* Add in the kind data. */ 1536251881Speter for (hi = apr_hash_first(pool, table); hi; hi = apr_hash_next(hi)) 1537251881Speter { 1538251881Speter svn_fs_dirent_t *entry; 1539251881Speter struct node_kind_args nk_args; 1540251881Speter void *val; 1541251881Speter 1542251881Speter svn_pool_clear(iterpool); 1543251881Speter 1544251881Speter /* KEY will be the entry name in ancestor (about which we 1545251881Speter simply don't care), VAL the dirent. */ 1546251881Speter apr_hash_this(hi, NULL, NULL, &val); 1547251881Speter entry = val; 1548251881Speter nk_args.id = entry->id; 1549251881Speter 1550251881Speter /* We don't need to have the retry function destroy the trail 1551251881Speter pool because we're already doing that via the use of an 1552251881Speter iteration pool. */ 1553251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_node_kind, &nk_args, 1554251881Speter FALSE, iterpool)); 1555251881Speter entry->kind = nk_args.kind; 1556251881Speter } 1557251881Speter 1558251881Speter svn_pool_destroy(iterpool); 1559251881Speter 1560251881Speter *table_p = table; 1561251881Speter return SVN_NO_ERROR; 1562251881Speter} 1563251881Speter 1564251881Speter 1565251881Speter 1566251881Speter/* Merges and commits. */ 1567251881Speter 1568251881Speter 1569251881Speterstruct deltify_committed_args 1570251881Speter{ 1571251881Speter svn_fs_t *fs; /* the filesystem */ 1572251881Speter svn_revnum_t rev; /* revision just committed */ 1573251881Speter const char *txn_id; /* transaction just committed */ 1574251881Speter}; 1575251881Speter 1576251881Speter 1577251881Speterstruct txn_deltify_args 1578251881Speter{ 1579251881Speter /* The transaction ID whose nodes are being deltified. */ 1580251881Speter const char *txn_id; 1581251881Speter 1582251881Speter /* The target is what we're deltifying. */ 1583251881Speter const svn_fs_id_t *tgt_id; 1584251881Speter 1585251881Speter /* The base is what we're deltifying against. It's not necessarily 1586251881Speter the "next" revision of the node; skip deltas mean we sometimes 1587251881Speter deltify against a successor many generations away. This may be 1588251881Speter NULL, in which case we'll avoid deltification and simply index 1589251881Speter TGT_ID's data checksum. */ 1590251881Speter const svn_fs_id_t *base_id; 1591251881Speter 1592251881Speter /* We only deltify props for directories. 1593251881Speter ### Didn't we try removing this horrid little optimization once? 1594251881Speter ### What was the result? I would have thought that skip deltas 1595251881Speter ### mean directory undeltification is cheap enough now. */ 1596251881Speter svn_boolean_t is_dir; 1597251881Speter}; 1598251881Speter 1599251881Speter 1600251881Speterstatic svn_error_t * 1601251881Spetertxn_body_txn_deltify(void *baton, trail_t *trail) 1602251881Speter{ 1603251881Speter struct txn_deltify_args *args = baton; 1604251881Speter dag_node_t *tgt_node, *base_node; 1605251881Speter base_fs_data_t *bfd = trail->fs->fsap_data; 1606251881Speter 1607251881Speter SVN_ERR(svn_fs_base__dag_get_node(&tgt_node, trail->fs, args->tgt_id, 1608251881Speter trail, trail->pool)); 1609251881Speter /* If we have something to deltify against, do so. */ 1610251881Speter if (args->base_id) 1611251881Speter { 1612251881Speter SVN_ERR(svn_fs_base__dag_get_node(&base_node, trail->fs, args->base_id, 1613251881Speter trail, trail->pool)); 1614251881Speter SVN_ERR(svn_fs_base__dag_deltify(tgt_node, base_node, args->is_dir, 1615251881Speter args->txn_id, trail, trail->pool)); 1616251881Speter } 1617251881Speter 1618251881Speter /* If we support rep sharing, and this isn't a directory, record a 1619251881Speter mapping of TGT_NODE's data checksum to its representation key. */ 1620251881Speter if (bfd->format >= SVN_FS_BASE__MIN_REP_SHARING_FORMAT) 1621251881Speter SVN_ERR(svn_fs_base__dag_index_checksums(tgt_node, trail, trail->pool)); 1622251881Speter 1623251881Speter return SVN_NO_ERROR; 1624251881Speter} 1625251881Speter 1626251881Speter 1627251881Speterstruct txn_pred_count_args 1628251881Speter{ 1629251881Speter const svn_fs_id_t *id; 1630251881Speter int pred_count; 1631251881Speter}; 1632251881Speter 1633251881Speter 1634251881Speterstatic svn_error_t * 1635251881Spetertxn_body_pred_count(void *baton, trail_t *trail) 1636251881Speter{ 1637251881Speter node_revision_t *noderev; 1638251881Speter struct txn_pred_count_args *args = baton; 1639251881Speter 1640251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, trail->fs, 1641251881Speter args->id, trail, trail->pool)); 1642251881Speter args->pred_count = noderev->predecessor_count; 1643251881Speter return SVN_NO_ERROR; 1644251881Speter} 1645251881Speter 1646251881Speter 1647251881Speterstruct txn_pred_id_args 1648251881Speter{ 1649251881Speter const svn_fs_id_t *id; /* The node id whose predecessor we want. */ 1650251881Speter const svn_fs_id_t *pred_id; /* The returned predecessor id. */ 1651251881Speter apr_pool_t *pool; /* The pool in which to allocate pred_id. */ 1652251881Speter}; 1653251881Speter 1654251881Speter 1655251881Speterstatic svn_error_t * 1656251881Spetertxn_body_pred_id(void *baton, trail_t *trail) 1657251881Speter{ 1658251881Speter node_revision_t *nr; 1659251881Speter struct txn_pred_id_args *args = baton; 1660251881Speter 1661251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&nr, trail->fs, args->id, 1662251881Speter trail, trail->pool)); 1663251881Speter if (nr->predecessor_id) 1664251881Speter args->pred_id = svn_fs_base__id_copy(nr->predecessor_id, args->pool); 1665251881Speter else 1666251881Speter args->pred_id = NULL; 1667251881Speter 1668251881Speter return SVN_NO_ERROR; 1669251881Speter} 1670251881Speter 1671251881Speter 1672251881Speter/* Deltify PATH in ROOT's predecessor iff PATH is mutable under TXN_ID 1673251881Speter in FS. If PATH is a mutable directory, recurse. 1674251881Speter 1675251881Speter NODE_ID is the node revision ID for PATH in ROOT, or NULL if that 1676251881Speter value isn't known. KIND is the node kind for PATH in ROOT, or 1677251881Speter svn_node_unknown is the kind isn't known. 1678251881Speter 1679251881Speter Use POOL for necessary allocations. */ 1680251881Speterstatic svn_error_t * 1681251881Speterdeltify_mutable(svn_fs_t *fs, 1682251881Speter svn_fs_root_t *root, 1683251881Speter const char *path, 1684251881Speter const svn_fs_id_t *node_id, 1685251881Speter svn_node_kind_t kind, 1686251881Speter const char *txn_id, 1687251881Speter apr_pool_t *pool) 1688251881Speter{ 1689251881Speter const svn_fs_id_t *id = node_id; 1690251881Speter apr_hash_t *entries = NULL; 1691251881Speter struct txn_deltify_args td_args; 1692251881Speter base_fs_data_t *bfd = fs->fsap_data; 1693251881Speter 1694251881Speter /* Get the ID for PATH under ROOT if it wasn't provided. */ 1695251881Speter if (! node_id) 1696251881Speter SVN_ERR(base_node_id(&id, root, path, pool)); 1697251881Speter 1698251881Speter /* Check for mutability. Not mutable? Go no further. This is safe 1699251881Speter to do because for items in the tree to be mutable, their parent 1700251881Speter dirs must also be mutable. Therefore, if a directory is not 1701251881Speter mutable under TXN_ID, its children cannot be. */ 1702251881Speter if (strcmp(svn_fs_base__id_txn_id(id), txn_id)) 1703251881Speter return SVN_NO_ERROR; 1704251881Speter 1705251881Speter /* Is this a directory? */ 1706251881Speter if (kind == svn_node_unknown) 1707251881Speter SVN_ERR(base_check_path(&kind, root, path, pool)); 1708251881Speter 1709251881Speter /* If this is a directory, read its entries. */ 1710251881Speter if (kind == svn_node_dir) 1711251881Speter SVN_ERR(base_dir_entries(&entries, root, path, pool)); 1712251881Speter 1713251881Speter /* If there are entries, recurse on 'em. */ 1714251881Speter if (entries) 1715251881Speter { 1716251881Speter apr_pool_t *subpool = svn_pool_create(pool); 1717251881Speter apr_hash_index_t *hi; 1718251881Speter 1719251881Speter for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) 1720251881Speter { 1721251881Speter /* KEY will be the entry name, VAL the dirent */ 1722251881Speter const void *key; 1723251881Speter void *val; 1724251881Speter svn_fs_dirent_t *entry; 1725251881Speter svn_pool_clear(subpool); 1726251881Speter apr_hash_this(hi, &key, NULL, &val); 1727251881Speter entry = val; 1728251881Speter SVN_ERR(deltify_mutable(fs, root, 1729251881Speter svn_fspath__join(path, key, subpool), 1730251881Speter entry->id, entry->kind, txn_id, subpool)); 1731251881Speter } 1732251881Speter 1733251881Speter svn_pool_destroy(subpool); 1734251881Speter } 1735251881Speter 1736251881Speter /* Index ID's data checksum. */ 1737251881Speter td_args.txn_id = txn_id; 1738251881Speter td_args.tgt_id = id; 1739251881Speter td_args.base_id = NULL; 1740251881Speter td_args.is_dir = (kind == svn_node_dir); 1741251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args, 1742251881Speter TRUE, pool)); 1743251881Speter 1744251881Speter /* Finally, deltify old data against this node. */ 1745251881Speter { 1746251881Speter /* Prior to 1.6, we use the following algorithm to deltify nodes: 1747251881Speter 1748251881Speter Redeltify predecessor node-revisions of the one we added. The 1749251881Speter idea is to require at most 2*lg(N) deltas to be applied to get 1750251881Speter to any node-revision in a chain of N predecessors. We do this 1751251881Speter using a technique derived from skip lists: 1752251881Speter 1753251881Speter - Always redeltify the immediate parent 1754251881Speter 1755251881Speter - If the number of predecessors is divisible by 2, 1756251881Speter redeltify the revision two predecessors back 1757251881Speter 1758251881Speter - If the number of predecessors is divisible by 4, 1759251881Speter redeltify the revision four predecessors back 1760251881Speter 1761251881Speter ... and so on. 1762251881Speter 1763251881Speter That's the theory, anyway. Unfortunately, if we strictly 1764251881Speter follow that theory we get a bunch of overhead up front and no 1765251881Speter great benefit until the number of predecessors gets large. So, 1766251881Speter stop at redeltifying the parent if the number of predecessors 1767251881Speter is less than 32, and also skip the second level (redeltifying 1768251881Speter two predecessors back), since that doesn't help much. Also, 1769251881Speter don't redeltify the oldest node-revision; it's potentially 1770251881Speter expensive and doesn't help retrieve any other revision. 1771251881Speter (Retrieving the oldest node-revision will still be fast, just 1772251881Speter not as blindingly so.) 1773251881Speter 1774251881Speter For 1.6 and beyond, we just deltify the current node against its 1775251881Speter predecessors, using skip deltas similar to the way FSFS does. */ 1776251881Speter 1777251881Speter int pred_count; 1778251881Speter const svn_fs_id_t *pred_id; 1779251881Speter struct txn_pred_count_args tpc_args; 1780251881Speter apr_pool_t *subpools[2]; 1781251881Speter int active_subpool = 0; 1782251881Speter svn_revnum_t forward_delta_rev = 0; 1783251881Speter 1784251881Speter tpc_args.id = id; 1785251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_count, &tpc_args, 1786251881Speter TRUE, pool)); 1787251881Speter pred_count = tpc_args.pred_count; 1788251881Speter 1789251881Speter /* If nothing to deltify, then we're done. */ 1790251881Speter if (pred_count == 0) 1791251881Speter return SVN_NO_ERROR; 1792251881Speter 1793251881Speter subpools[0] = svn_pool_create(pool); 1794251881Speter subpools[1] = svn_pool_create(pool); 1795251881Speter 1796251881Speter /* If we support the 'miscellaneous' table, check it to see if 1797251881Speter there is a point in time before which we don't want to do 1798251881Speter deltification. */ 1799251881Speter /* ### FIXME: I think this is an unnecessary restriction. We 1800251881Speter ### should be able to do something meaningful for most 1801251881Speter ### deltification requests -- what that is depends on the 1802251881Speter ### directory of the deltas for that revision, though. */ 1803251881Speter if (bfd->format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT) 1804251881Speter { 1805251881Speter const char *val; 1806251881Speter SVN_ERR(svn_fs_base__miscellaneous_get 1807251881Speter (&val, fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE, pool)); 1808251881Speter if (val) 1809251881Speter SVN_ERR(svn_revnum_parse(&forward_delta_rev, val, NULL)); 1810251881Speter } 1811251881Speter 1812251881Speter if (bfd->format >= SVN_FS_BASE__MIN_FORWARD_DELTAS_FORMAT 1813251881Speter && forward_delta_rev <= root->rev) 1814251881Speter { 1815251881Speter /**** FORWARD DELTA STORAGE ****/ 1816251881Speter 1817251881Speter /* Decide which predecessor to deltify against. Flip the rightmost '1' 1818251881Speter bit of the predecessor count to determine which file rev (counting 1819251881Speter from 0) we want to use. (To see why count & (count - 1) unsets the 1820251881Speter rightmost set bit, think about how you decrement a binary number. */ 1821251881Speter pred_count = pred_count & (pred_count - 1); 1822251881Speter 1823251881Speter /* Walk back a number of predecessors equal to the difference between 1824251881Speter pred_count and the original predecessor count. (For example, if 1825251881Speter the node has ten predecessors and we want the eighth node, walk back 1826251881Speter two predecessors. */ 1827251881Speter pred_id = id; 1828251881Speter 1829251881Speter /* We need to use two alternating pools because the id used in the 1830251881Speter call to txn_body_pred_id is allocated by the previous inner 1831251881Speter loop iteration. If we would clear the pool each iteration we 1832251881Speter would free the previous result. */ 1833251881Speter while ((pred_count++) < tpc_args.pred_count) 1834251881Speter { 1835251881Speter struct txn_pred_id_args tpi_args; 1836251881Speter 1837251881Speter active_subpool = !active_subpool; 1838251881Speter svn_pool_clear(subpools[active_subpool]); 1839251881Speter 1840251881Speter tpi_args.id = pred_id; 1841251881Speter tpi_args.pool = subpools[active_subpool]; 1842251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id, &tpi_args, 1843251881Speter FALSE, subpools[active_subpool])); 1844251881Speter pred_id = tpi_args.pred_id; 1845251881Speter 1846251881Speter if (pred_id == NULL) 1847251881Speter return svn_error_create 1848251881Speter (SVN_ERR_FS_CORRUPT, 0, 1849251881Speter _("Corrupt DB: faulty predecessor count")); 1850251881Speter 1851251881Speter } 1852251881Speter 1853251881Speter /* Finally, do the deltification. */ 1854251881Speter td_args.txn_id = txn_id; 1855251881Speter td_args.tgt_id = id; 1856251881Speter td_args.base_id = pred_id; 1857251881Speter td_args.is_dir = (kind == svn_node_dir); 1858251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args, 1859251881Speter TRUE, subpools[active_subpool])); 1860251881Speter } 1861251881Speter else 1862251881Speter { 1863251881Speter int nlevels, lev, count; 1864251881Speter 1865251881Speter /**** REVERSE DELTA STORAGE ****/ 1866251881Speter 1867251881Speter /* Decide how many predecessors to redeltify. To save overhead, 1868251881Speter don't redeltify anything but the immediate predecessor if there 1869251881Speter are less than 32 predecessors. */ 1870251881Speter nlevels = 1; 1871251881Speter if (pred_count >= 32) 1872251881Speter { 1873251881Speter while (pred_count % 2 == 0) 1874251881Speter { 1875251881Speter pred_count /= 2; 1876251881Speter nlevels++; 1877251881Speter } 1878251881Speter 1879251881Speter /* Don't redeltify the oldest revision. */ 1880251881Speter if (1 << (nlevels - 1) == pred_count) 1881251881Speter nlevels--; 1882251881Speter } 1883251881Speter 1884251881Speter /* Redeltify the desired number of predecessors. */ 1885251881Speter count = 0; 1886251881Speter pred_id = id; 1887251881Speter 1888251881Speter /* We need to use two alternating pools because the id used in the 1889251881Speter call to txn_body_pred_id is allocated by the previous inner 1890251881Speter loop iteration. If we would clear the pool each iteration we 1891251881Speter would free the previous result. */ 1892251881Speter for (lev = 0; lev < nlevels; lev++) 1893251881Speter { 1894251881Speter /* To save overhead, skip the second level (that is, never 1895251881Speter redeltify the node-revision two predecessors back). */ 1896251881Speter if (lev == 1) 1897251881Speter continue; 1898251881Speter 1899251881Speter /* Note that COUNT is not reset between levels, and neither is 1900251881Speter PREDNODE; we just keep counting from where we were up to 1901251881Speter where we're supposed to get. */ 1902251881Speter while (count < (1 << lev)) 1903251881Speter { 1904251881Speter struct txn_pred_id_args tpi_args; 1905251881Speter 1906251881Speter active_subpool = !active_subpool; 1907251881Speter svn_pool_clear(subpools[active_subpool]); 1908251881Speter 1909251881Speter tpi_args.id = pred_id; 1910251881Speter tpi_args.pool = subpools[active_subpool]; 1911251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id, 1912251881Speter &tpi_args, FALSE, 1913251881Speter subpools[active_subpool])); 1914251881Speter pred_id = tpi_args.pred_id; 1915251881Speter 1916251881Speter if (pred_id == NULL) 1917251881Speter return svn_error_create 1918251881Speter (SVN_ERR_FS_CORRUPT, 0, 1919251881Speter _("Corrupt DB: faulty predecessor count")); 1920251881Speter 1921251881Speter count++; 1922251881Speter } 1923251881Speter 1924251881Speter /* Finally, do the deltification. */ 1925251881Speter td_args.txn_id = NULL; /* Don't require mutable reps */ 1926251881Speter td_args.tgt_id = pred_id; 1927251881Speter td_args.base_id = id; 1928251881Speter td_args.is_dir = (kind == svn_node_dir); 1929251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args, 1930251881Speter TRUE, subpools[active_subpool])); 1931251881Speter 1932251881Speter } 1933251881Speter } 1934251881Speter 1935251881Speter svn_pool_destroy(subpools[0]); 1936251881Speter svn_pool_destroy(subpools[1]); 1937251881Speter } 1938251881Speter 1939251881Speter return SVN_NO_ERROR; 1940251881Speter} 1941251881Speter 1942251881Speter 1943251881Speterstruct get_root_args 1944251881Speter{ 1945251881Speter svn_fs_root_t *root; 1946251881Speter dag_node_t *node; 1947251881Speter}; 1948251881Speter 1949251881Speter 1950251881Speter/* Set ARGS->node to the root node of ARGS->root. */ 1951251881Speterstatic svn_error_t * 1952251881Spetertxn_body_get_root(void *baton, trail_t *trail) 1953251881Speter{ 1954251881Speter struct get_root_args *args = baton; 1955251881Speter return get_dag(&(args->node), args->root, "", trail, trail->pool); 1956251881Speter} 1957251881Speter 1958251881Speter 1959251881Speter 1960251881Speterstatic svn_error_t * 1961251881Speterupdate_ancestry(svn_fs_t *fs, 1962251881Speter const svn_fs_id_t *source_id, 1963251881Speter const svn_fs_id_t *target_id, 1964251881Speter const char *txn_id, 1965251881Speter const char *target_path, 1966251881Speter int source_pred_count, 1967251881Speter trail_t *trail, 1968251881Speter apr_pool_t *pool) 1969251881Speter{ 1970251881Speter node_revision_t *noderev; 1971251881Speter 1972251881Speter /* Set target's predecessor-id to source_id. */ 1973251881Speter if (strcmp(svn_fs_base__id_txn_id(target_id), txn_id)) 1974251881Speter return svn_error_createf 1975251881Speter (SVN_ERR_FS_NOT_MUTABLE, NULL, 1976251881Speter _("Unexpected immutable node at '%s'"), target_path); 1977251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, target_id, 1978251881Speter trail, pool)); 1979251881Speter noderev->predecessor_id = source_id; 1980251881Speter noderev->predecessor_count = source_pred_count; 1981251881Speter if (noderev->predecessor_count != -1) 1982251881Speter noderev->predecessor_count++; 1983251881Speter return svn_fs_bdb__put_node_revision(fs, target_id, noderev, trail, pool); 1984251881Speter} 1985251881Speter 1986251881Speter 1987251881Speter/* Set the contents of CONFLICT_PATH to PATH, and return an 1988251881Speter SVN_ERR_FS_CONFLICT error that indicates that there was a conflict 1989251881Speter at PATH. Perform all allocations in POOL (except the allocation of 1990251881Speter CONFLICT_PATH, which should be handled outside this function). */ 1991251881Speterstatic svn_error_t * 1992251881Speterconflict_err(svn_stringbuf_t *conflict_path, 1993251881Speter const char *path) 1994251881Speter{ 1995251881Speter svn_stringbuf_set(conflict_path, path); 1996251881Speter return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL, 1997251881Speter _("Conflict at '%s'"), path); 1998251881Speter} 1999251881Speter 2000251881Speter 2001251881Speter/* Merge changes between ANCESTOR and SOURCE into TARGET as part of 2002251881Speter * TRAIL. ANCESTOR and TARGET must be distinct node revisions. 2003251881Speter * TARGET_PATH should correspond to TARGET's full path in its 2004251881Speter * filesystem, and is used for reporting conflict location. 2005251881Speter * 2006251881Speter * SOURCE, TARGET, and ANCESTOR are generally directories; this 2007251881Speter * function recursively merges the directories' contents. If any are 2008251881Speter * files, this function simply returns an error whenever SOURCE, 2009251881Speter * TARGET, and ANCESTOR are all distinct node revisions. 2010251881Speter * 2011251881Speter * If there are differences between ANCESTOR and SOURCE that conflict 2012251881Speter * with changes between ANCESTOR and TARGET, this function returns an 2013251881Speter * SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the 2014251881Speter * conflicting node in TARGET, with TARGET_PATH prepended as a path. 2015251881Speter * 2016251881Speter * If there are no conflicting differences, CONFLICT_P is updated to 2017251881Speter * the empty string. 2018251881Speter * 2019251881Speter * CONFLICT_P must point to a valid svn_stringbuf_t. 2020251881Speter * 2021251881Speter * Do any necessary temporary allocation in POOL. 2022251881Speter */ 2023251881Speterstatic svn_error_t * 2024251881Spetermerge(svn_stringbuf_t *conflict_p, 2025251881Speter const char *target_path, 2026251881Speter dag_node_t *target, 2027251881Speter dag_node_t *source, 2028251881Speter dag_node_t *ancestor, 2029251881Speter const char *txn_id, 2030251881Speter apr_int64_t *mergeinfo_increment_out, 2031251881Speter trail_t *trail, 2032251881Speter apr_pool_t *pool) 2033251881Speter{ 2034251881Speter const svn_fs_id_t *source_id, *target_id, *ancestor_id; 2035251881Speter apr_hash_t *s_entries, *t_entries, *a_entries; 2036251881Speter apr_hash_index_t *hi; 2037251881Speter apr_pool_t *iterpool; 2038251881Speter svn_fs_t *fs; 2039251881Speter int pred_count; 2040251881Speter apr_int64_t mergeinfo_increment = 0; 2041251881Speter base_fs_data_t *bfd = trail->fs->fsap_data; 2042251881Speter 2043251881Speter /* Make sure everyone comes from the same filesystem. */ 2044251881Speter fs = svn_fs_base__dag_get_fs(ancestor); 2045251881Speter if ((fs != svn_fs_base__dag_get_fs(source)) 2046251881Speter || (fs != svn_fs_base__dag_get_fs(target))) 2047251881Speter { 2048251881Speter return svn_error_create 2049251881Speter (SVN_ERR_FS_CORRUPT, NULL, 2050251881Speter _("Bad merge; ancestor, source, and target not all in same fs")); 2051251881Speter } 2052251881Speter 2053251881Speter /* We have the same fs, now check it. */ 2054251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 2055251881Speter 2056251881Speter source_id = svn_fs_base__dag_get_id(source); 2057251881Speter target_id = svn_fs_base__dag_get_id(target); 2058251881Speter ancestor_id = svn_fs_base__dag_get_id(ancestor); 2059251881Speter 2060251881Speter /* It's improper to call this function with ancestor == target. */ 2061251881Speter if (svn_fs_base__id_eq(ancestor_id, target_id)) 2062251881Speter { 2063251881Speter svn_string_t *id_str = svn_fs_base__id_unparse(target_id, pool); 2064251881Speter return svn_error_createf 2065251881Speter (SVN_ERR_FS_GENERAL, NULL, 2066251881Speter _("Bad merge; target '%s' has id '%s', same as ancestor"), 2067251881Speter target_path, id_str->data); 2068251881Speter } 2069251881Speter 2070251881Speter svn_stringbuf_setempty(conflict_p); 2071251881Speter 2072251881Speter /* Base cases: 2073251881Speter * Either no change made in source, or same change as made in target. 2074251881Speter * Both mean nothing to merge here. 2075251881Speter */ 2076251881Speter if (svn_fs_base__id_eq(ancestor_id, source_id) 2077251881Speter || (svn_fs_base__id_eq(source_id, target_id))) 2078251881Speter return SVN_NO_ERROR; 2079251881Speter 2080251881Speter /* Else proceed, knowing all three are distinct node revisions. 2081251881Speter * 2082251881Speter * How to merge from this point: 2083251881Speter * 2084251881Speter * if (not all 3 are directories) 2085251881Speter * { 2086251881Speter * early exit with conflict; 2087251881Speter * } 2088251881Speter * 2089251881Speter * // Property changes may only be made to up-to-date 2090251881Speter * // directories, because once the client commits the prop 2091251881Speter * // change, it bumps the directory's revision, and therefore 2092251881Speter * // must be able to depend on there being no other changes to 2093251881Speter * // that directory in the repository. 2094251881Speter * if (target's property list differs from ancestor's) 2095251881Speter * conflict; 2096251881Speter * 2097251881Speter * For each entry NAME in the directory ANCESTOR: 2098251881Speter * 2099251881Speter * Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of 2100251881Speter * the name within ANCESTOR, SOURCE, and TARGET respectively. 2101251881Speter * (Possibly null if NAME does not exist in SOURCE or TARGET.) 2102251881Speter * 2103251881Speter * If ANCESTOR-ENTRY == SOURCE-ENTRY, then: 2104251881Speter * No changes were made to this entry while the transaction was in 2105251881Speter * progress, so do nothing to the target. 2106251881Speter * 2107251881Speter * Else if ANCESTOR-ENTRY == TARGET-ENTRY, then: 2108251881Speter * A change was made to this entry while the transaction was in 2109251881Speter * process, but the transaction did not touch this entry. Replace 2110251881Speter * TARGET-ENTRY with SOURCE-ENTRY. 2111251881Speter * 2112251881Speter * Else: 2113251881Speter * Changes were made to this entry both within the transaction and 2114251881Speter * to the repository while the transaction was in progress. They 2115251881Speter * must be merged or declared to be in conflict. 2116251881Speter * 2117251881Speter * If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a 2118251881Speter * double delete; flag a conflict. 2119251881Speter * 2120251881Speter * If any of the three entries is of type file, declare a conflict. 2121251881Speter * 2122251881Speter * If either SOURCE-ENTRY or TARGET-ENTRY is not a direct 2123251881Speter * modification of ANCESTOR-ENTRY (determine by comparing the 2124251881Speter * node-id fields), declare a conflict. A replacement is 2125251881Speter * incompatible with a modification or other replacement--even 2126251881Speter * an identical replacement. 2127251881Speter * 2128251881Speter * Direct modifications were made to the directory ANCESTOR-ENTRY 2129251881Speter * in both SOURCE and TARGET. Recursively merge these 2130251881Speter * modifications. 2131251881Speter * 2132251881Speter * For each leftover entry NAME in the directory SOURCE: 2133251881Speter * 2134251881Speter * If NAME exists in TARGET, declare a conflict. Even if SOURCE and 2135251881Speter * TARGET are adding exactly the same thing, two additions are not 2136251881Speter * auto-mergeable with each other. 2137251881Speter * 2138251881Speter * Add NAME to TARGET with the entry from SOURCE. 2139251881Speter * 2140251881Speter * Now that we are done merging the changes from SOURCE into the 2141251881Speter * directory TARGET, update TARGET's predecessor to be SOURCE. 2142251881Speter */ 2143251881Speter 2144251881Speter if ((svn_fs_base__dag_node_kind(source) != svn_node_dir) 2145251881Speter || (svn_fs_base__dag_node_kind(target) != svn_node_dir) 2146251881Speter || (svn_fs_base__dag_node_kind(ancestor) != svn_node_dir)) 2147251881Speter { 2148251881Speter return conflict_err(conflict_p, target_path); 2149251881Speter } 2150251881Speter 2151251881Speter 2152251881Speter /* Possible early merge failure: if target and ancestor have 2153251881Speter different property lists, then the merge should fail. 2154251881Speter Propchanges can *only* be committed on an up-to-date directory. 2155251881Speter ### TODO: see issue #418 about the inelegance of this. 2156251881Speter 2157251881Speter Another possible, similar, early merge failure: if source and 2158251881Speter ancestor have different property lists (meaning someone else 2159251881Speter changed directory properties while our commit transaction was 2160251881Speter happening), the merge should fail. See issue #2751. 2161251881Speter */ 2162251881Speter { 2163251881Speter node_revision_t *tgt_nr, *anc_nr, *src_nr; 2164251881Speter 2165251881Speter /* Get node revisions for our id's. */ 2166251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&tgt_nr, fs, target_id, 2167251881Speter trail, pool)); 2168251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&anc_nr, fs, ancestor_id, 2169251881Speter trail, pool)); 2170251881Speter SVN_ERR(svn_fs_bdb__get_node_revision(&src_nr, fs, source_id, 2171251881Speter trail, pool)); 2172251881Speter 2173251881Speter /* Now compare the prop-keys of the skels. Note that just because 2174251881Speter the keys are different -doesn't- mean the proplists have 2175251881Speter different contents. But merge() isn't concerned with contents; 2176251881Speter it doesn't do a brute-force comparison on textual contents, so 2177251881Speter it won't do that here either. Checking to see if the propkey 2178251881Speter atoms are `equal' is enough. */ 2179251881Speter if (! svn_fs_base__same_keys(tgt_nr->prop_key, anc_nr->prop_key)) 2180251881Speter return conflict_err(conflict_p, target_path); 2181251881Speter if (! svn_fs_base__same_keys(src_nr->prop_key, anc_nr->prop_key)) 2182251881Speter return conflict_err(conflict_p, target_path); 2183251881Speter } 2184251881Speter 2185251881Speter /* ### todo: it would be more efficient to simply check for a NULL 2186251881Speter entries hash where necessary below than to allocate an empty hash 2187251881Speter here, but another day, another day... */ 2188251881Speter SVN_ERR(svn_fs_base__dag_dir_entries(&s_entries, source, trail, pool)); 2189251881Speter if (! s_entries) 2190251881Speter s_entries = apr_hash_make(pool); 2191251881Speter SVN_ERR(svn_fs_base__dag_dir_entries(&t_entries, target, trail, pool)); 2192251881Speter if (! t_entries) 2193251881Speter t_entries = apr_hash_make(pool); 2194251881Speter SVN_ERR(svn_fs_base__dag_dir_entries(&a_entries, ancestor, trail, pool)); 2195251881Speter if (! a_entries) 2196251881Speter a_entries = apr_hash_make(pool); 2197251881Speter 2198251881Speter /* for each entry E in a_entries... */ 2199251881Speter iterpool = svn_pool_create(pool); 2200251881Speter for (hi = apr_hash_first(pool, a_entries); 2201251881Speter hi; 2202251881Speter hi = apr_hash_next(hi)) 2203251881Speter { 2204251881Speter svn_fs_dirent_t *s_entry, *t_entry, *a_entry; 2205251881Speter 2206251881Speter const void *key; 2207251881Speter void *val; 2208251881Speter apr_ssize_t klen; 2209251881Speter 2210251881Speter svn_pool_clear(iterpool); 2211251881Speter 2212251881Speter /* KEY will be the entry name in ancestor, VAL the dirent */ 2213251881Speter apr_hash_this(hi, &key, &klen, &val); 2214251881Speter a_entry = val; 2215251881Speter 2216251881Speter s_entry = apr_hash_get(s_entries, key, klen); 2217251881Speter t_entry = apr_hash_get(t_entries, key, klen); 2218251881Speter 2219251881Speter /* No changes were made to this entry while the transaction was 2220251881Speter in progress, so do nothing to the target. */ 2221251881Speter if (s_entry && svn_fs_base__id_eq(a_entry->id, s_entry->id)) 2222251881Speter goto end; 2223251881Speter 2224251881Speter /* A change was made to this entry while the transaction was in 2225251881Speter process, but the transaction did not touch this entry. */ 2226251881Speter else if (t_entry && svn_fs_base__id_eq(a_entry->id, t_entry->id)) 2227251881Speter { 2228251881Speter dag_node_t *t_ent_node; 2229251881Speter apr_int64_t mergeinfo_start; 2230251881Speter SVN_ERR(svn_fs_base__dag_get_node(&t_ent_node, fs, 2231251881Speter t_entry->id, trail, iterpool)); 2232251881Speter SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_start, 2233251881Speter t_ent_node, trail, 2234251881Speter iterpool)); 2235251881Speter mergeinfo_increment -= mergeinfo_start; 2236251881Speter 2237251881Speter if (s_entry) 2238251881Speter { 2239251881Speter dag_node_t *s_ent_node; 2240251881Speter apr_int64_t mergeinfo_end; 2241251881Speter SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs, 2242251881Speter s_entry->id, trail, 2243251881Speter iterpool)); 2244251881Speter SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, 2245251881Speter &mergeinfo_end, 2246251881Speter s_ent_node, trail, 2247251881Speter iterpool)); 2248251881Speter mergeinfo_increment += mergeinfo_end; 2249251881Speter SVN_ERR(svn_fs_base__dag_set_entry(target, key, s_entry->id, 2250251881Speter txn_id, trail, iterpool)); 2251251881Speter } 2252251881Speter else 2253251881Speter { 2254251881Speter SVN_ERR(svn_fs_base__dag_delete(target, key, txn_id, 2255251881Speter trail, iterpool)); 2256251881Speter } 2257251881Speter } 2258251881Speter 2259251881Speter /* Changes were made to this entry both within the transaction 2260251881Speter and to the repository while the transaction was in progress. 2261251881Speter They must be merged or declared to be in conflict. */ 2262251881Speter else 2263251881Speter { 2264251881Speter dag_node_t *s_ent_node, *t_ent_node, *a_ent_node; 2265251881Speter const char *new_tpath; 2266251881Speter apr_int64_t sub_mergeinfo_increment; 2267251881Speter 2268251881Speter /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a 2269251881Speter double delete; if one of them is null, that's a delete versus 2270251881Speter a modification. In any of these cases, flag a conflict. */ 2271251881Speter if (s_entry == NULL || t_entry == NULL) 2272251881Speter return conflict_err(conflict_p, 2273251881Speter svn_fspath__join(target_path, 2274251881Speter a_entry->name, 2275251881Speter iterpool)); 2276251881Speter 2277251881Speter /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct 2278251881Speter modification of ANCESTOR-ENTRY, declare a conflict. */ 2279251881Speter if (strcmp(svn_fs_base__id_node_id(s_entry->id), 2280251881Speter svn_fs_base__id_node_id(a_entry->id)) != 0 2281251881Speter || strcmp(svn_fs_base__id_copy_id(s_entry->id), 2282251881Speter svn_fs_base__id_copy_id(a_entry->id)) != 0 2283251881Speter || strcmp(svn_fs_base__id_node_id(t_entry->id), 2284251881Speter svn_fs_base__id_node_id(a_entry->id)) != 0 2285251881Speter || strcmp(svn_fs_base__id_copy_id(t_entry->id), 2286251881Speter svn_fs_base__id_copy_id(a_entry->id)) != 0) 2287251881Speter return conflict_err(conflict_p, 2288251881Speter svn_fspath__join(target_path, 2289251881Speter a_entry->name, 2290251881Speter iterpool)); 2291251881Speter 2292251881Speter /* Fetch the nodes for our entries. */ 2293251881Speter SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs, 2294251881Speter s_entry->id, trail, iterpool)); 2295251881Speter SVN_ERR(svn_fs_base__dag_get_node(&t_ent_node, fs, 2296251881Speter t_entry->id, trail, iterpool)); 2297251881Speter SVN_ERR(svn_fs_base__dag_get_node(&a_ent_node, fs, 2298251881Speter a_entry->id, trail, iterpool)); 2299251881Speter 2300251881Speter /* If any of the three entries is of type file, flag a conflict. */ 2301251881Speter if ((svn_fs_base__dag_node_kind(s_ent_node) == svn_node_file) 2302251881Speter || (svn_fs_base__dag_node_kind(t_ent_node) == svn_node_file) 2303251881Speter || (svn_fs_base__dag_node_kind(a_ent_node) == svn_node_file)) 2304251881Speter return conflict_err(conflict_p, 2305251881Speter svn_fspath__join(target_path, 2306251881Speter a_entry->name, 2307251881Speter iterpool)); 2308251881Speter 2309251881Speter /* Direct modifications were made to the directory 2310251881Speter ANCESTOR-ENTRY in both SOURCE and TARGET. Recursively 2311251881Speter merge these modifications. */ 2312251881Speter new_tpath = svn_fspath__join(target_path, t_entry->name, iterpool); 2313251881Speter SVN_ERR(merge(conflict_p, new_tpath, 2314251881Speter t_ent_node, s_ent_node, a_ent_node, 2315251881Speter txn_id, &sub_mergeinfo_increment, trail, iterpool)); 2316251881Speter mergeinfo_increment += sub_mergeinfo_increment; 2317251881Speter } 2318251881Speter 2319251881Speter /* We've taken care of any possible implications E could have. 2320251881Speter Remove it from source_entries, so it's easy later to loop 2321251881Speter over all the source entries that didn't exist in 2322251881Speter ancestor_entries. */ 2323251881Speter end: 2324251881Speter apr_hash_set(s_entries, key, klen, NULL); 2325251881Speter } 2326251881Speter 2327251881Speter /* For each entry E in source but not in ancestor */ 2328251881Speter for (hi = apr_hash_first(pool, s_entries); 2329251881Speter hi; 2330251881Speter hi = apr_hash_next(hi)) 2331251881Speter { 2332251881Speter svn_fs_dirent_t *s_entry, *t_entry; 2333251881Speter const void *key; 2334251881Speter void *val; 2335251881Speter apr_ssize_t klen; 2336251881Speter dag_node_t *s_ent_node; 2337251881Speter apr_int64_t mergeinfo_s; 2338251881Speter 2339251881Speter svn_pool_clear(iterpool); 2340251881Speter 2341251881Speter apr_hash_this(hi, &key, &klen, &val); 2342251881Speter s_entry = val; 2343251881Speter t_entry = apr_hash_get(t_entries, key, klen); 2344251881Speter 2345251881Speter /* If NAME exists in TARGET, declare a conflict. */ 2346251881Speter if (t_entry) 2347251881Speter return conflict_err(conflict_p, 2348251881Speter svn_fspath__join(target_path, 2349251881Speter t_entry->name, 2350251881Speter iterpool)); 2351251881Speter 2352251881Speter SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs, 2353251881Speter s_entry->id, trail, iterpool)); 2354251881Speter SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_s, 2355251881Speter s_ent_node, trail, 2356251881Speter iterpool)); 2357251881Speter mergeinfo_increment += mergeinfo_s; 2358251881Speter SVN_ERR(svn_fs_base__dag_set_entry 2359251881Speter (target, s_entry->name, s_entry->id, txn_id, trail, iterpool)); 2360251881Speter } 2361251881Speter svn_pool_destroy(iterpool); 2362251881Speter 2363251881Speter /* Now that TARGET has absorbed all of the history between ANCESTOR 2364251881Speter and SOURCE, we can update its predecessor to point to SOURCE. */ 2365251881Speter SVN_ERR(svn_fs_base__dag_get_predecessor_count(&pred_count, source, 2366251881Speter trail, pool)); 2367251881Speter SVN_ERR(update_ancestry(fs, source_id, target_id, txn_id, target_path, 2368251881Speter pred_count, trail, pool)); 2369251881Speter 2370251881Speter /* Tweak mergeinfo data if our format supports it. */ 2371251881Speter if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT) 2372251881Speter { 2373251881Speter SVN_ERR(svn_fs_base__dag_adjust_mergeinfo_count(target, 2374251881Speter mergeinfo_increment, 2375251881Speter txn_id, trail, pool)); 2376251881Speter } 2377251881Speter 2378251881Speter if (mergeinfo_increment_out) 2379251881Speter *mergeinfo_increment_out = mergeinfo_increment; 2380251881Speter 2381251881Speter return SVN_NO_ERROR; 2382251881Speter} 2383251881Speter 2384251881Speter 2385251881Speterstruct merge_args 2386251881Speter{ 2387251881Speter /* The ancestor for the merge. If this is null, then TXN's base is 2388251881Speter used as the ancestor for the merge. */ 2389251881Speter dag_node_t *ancestor_node; 2390251881Speter 2391251881Speter /* This is the SOURCE node for the merge. It may not be null. */ 2392251881Speter dag_node_t *source_node; 2393251881Speter 2394251881Speter /* This is the TARGET of the merge. It may not be null. If 2395251881Speter ancestor_node above is null, then this txn's base is used as the 2396251881Speter ancestor for the merge. */ 2397251881Speter svn_fs_txn_t *txn; 2398251881Speter 2399251881Speter /* If a conflict results, this is updated to the path in the txn that 2400251881Speter conflicted. It must point to a valid svn_stringbuf_t before calling 2401251881Speter svn_fs_base__retry_txn, as this determines the pool used to allocate any 2402251881Speter required memory. */ 2403251881Speter svn_stringbuf_t *conflict; 2404251881Speter}; 2405251881Speter 2406251881Speter 2407251881Speter/* Merge changes between an ancestor and BATON->source_node into 2408251881Speter BATON->txn. The ancestor is either BATON->ancestor_node, or if 2409251881Speter that is null, BATON->txn's base node. 2410251881Speter 2411251881Speter If the merge is successful, BATON->txn's base will become 2412251881Speter BATON->source_node, and its root node will have a new ID, a 2413251881Speter successor of BATON->source_node. */ 2414251881Speterstatic svn_error_t * 2415251881Spetertxn_body_merge(void *baton, trail_t *trail) 2416251881Speter{ 2417251881Speter struct merge_args *args = baton; 2418251881Speter dag_node_t *source_node, *txn_root_node, *ancestor_node; 2419251881Speter const svn_fs_id_t *source_id; 2420251881Speter svn_fs_t *fs = args->txn->fs; 2421251881Speter const char *txn_id = args->txn->id; 2422251881Speter 2423251881Speter source_node = args->source_node; 2424251881Speter ancestor_node = args->ancestor_node; 2425251881Speter source_id = svn_fs_base__dag_get_id(source_node); 2426251881Speter 2427251881Speter SVN_ERR(svn_fs_base__dag_txn_root(&txn_root_node, fs, txn_id, 2428251881Speter trail, trail->pool)); 2429251881Speter 2430251881Speter if (ancestor_node == NULL) 2431251881Speter { 2432251881Speter SVN_ERR(svn_fs_base__dag_txn_base_root(&ancestor_node, fs, 2433251881Speter txn_id, trail, trail->pool)); 2434251881Speter } 2435251881Speter 2436251881Speter if (svn_fs_base__id_eq(svn_fs_base__dag_get_id(ancestor_node), 2437251881Speter svn_fs_base__dag_get_id(txn_root_node))) 2438251881Speter { 2439251881Speter /* If no changes have been made in TXN since its current base, 2440251881Speter then it can't conflict with any changes since that base. So 2441251881Speter we just set *both* its base and root to source, making TXN 2442251881Speter in effect a repeat of source. */ 2443251881Speter 2444251881Speter /* ### kff todo: this would, of course, be a mighty silly thing 2445251881Speter for the caller to do, and we might want to consider whether 2446251881Speter this response is really appropriate. */ 2447251881Speter 2448251881Speter SVN_ERR(svn_fs_base__set_txn_base(fs, txn_id, source_id, 2449251881Speter trail, trail->pool)); 2450251881Speter SVN_ERR(svn_fs_base__set_txn_root(fs, txn_id, source_id, 2451251881Speter trail, trail->pool)); 2452251881Speter } 2453251881Speter else 2454251881Speter { 2455251881Speter int pred_count; 2456251881Speter 2457251881Speter SVN_ERR(merge(args->conflict, "/", txn_root_node, source_node, 2458251881Speter ancestor_node, txn_id, NULL, trail, trail->pool)); 2459251881Speter 2460251881Speter SVN_ERR(svn_fs_base__dag_get_predecessor_count(&pred_count, 2461251881Speter source_node, trail, 2462251881Speter trail->pool)); 2463251881Speter 2464251881Speter /* After the merge, txn's new "ancestor" is now really the node 2465251881Speter at source_id, so record that fact. Think of this as 2466251881Speter ratcheting the txn forward in time, so it can't backslide and 2467251881Speter forget the merging work that's already been done. */ 2468251881Speter SVN_ERR(update_ancestry(fs, source_id, 2469251881Speter svn_fs_base__dag_get_id(txn_root_node), 2470251881Speter txn_id, "/", pred_count, trail, trail->pool)); 2471251881Speter SVN_ERR(svn_fs_base__set_txn_base(fs, txn_id, source_id, 2472251881Speter trail, trail->pool)); 2473251881Speter } 2474251881Speter 2475251881Speter return SVN_NO_ERROR; 2476251881Speter} 2477251881Speter 2478251881Speter 2479251881Speter/* Verify that there are registered with TRAIL->fs all the locks 2480251881Speter necessary to permit all the changes associated with TXN_NAME. */ 2481251881Speterstatic svn_error_t * 2482251881Speterverify_locks(const char *txn_name, 2483251881Speter trail_t *trail, 2484251881Speter apr_pool_t *pool) 2485251881Speter{ 2486251881Speter apr_pool_t *subpool = svn_pool_create(pool); 2487251881Speter apr_hash_t *changes; 2488251881Speter apr_hash_index_t *hi; 2489251881Speter apr_array_header_t *changed_paths; 2490251881Speter svn_stringbuf_t *last_recursed = NULL; 2491251881Speter int i; 2492251881Speter 2493251881Speter /* Fetch the changes for this transaction. */ 2494251881Speter SVN_ERR(svn_fs_bdb__changes_fetch(&changes, trail->fs, txn_name, 2495251881Speter trail, pool)); 2496251881Speter 2497251881Speter /* Make an array of the changed paths, and sort them depth-first-ily. */ 2498251881Speter changed_paths = apr_array_make(pool, apr_hash_count(changes) + 1, 2499251881Speter sizeof(const char *)); 2500251881Speter for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi)) 2501251881Speter { 2502251881Speter const void *key; 2503251881Speter apr_hash_this(hi, &key, NULL, NULL); 2504251881Speter APR_ARRAY_PUSH(changed_paths, const char *) = key; 2505251881Speter } 2506251881Speter qsort(changed_paths->elts, changed_paths->nelts, 2507251881Speter changed_paths->elt_size, svn_sort_compare_paths); 2508251881Speter 2509251881Speter /* Now, traverse the array of changed paths, verify locks. Note 2510251881Speter that if we need to do a recursive verification a path, we'll skip 2511251881Speter over children of that path when we get to them. */ 2512251881Speter for (i = 0; i < changed_paths->nelts; i++) 2513251881Speter { 2514251881Speter const char *path; 2515251881Speter svn_fs_path_change2_t *change; 2516251881Speter svn_boolean_t recurse = TRUE; 2517251881Speter 2518251881Speter svn_pool_clear(subpool); 2519251881Speter path = APR_ARRAY_IDX(changed_paths, i, const char *); 2520251881Speter 2521251881Speter /* If this path has already been verified as part of a recursive 2522251881Speter check of one of its parents, no need to do it again. */ 2523251881Speter if (last_recursed 2524251881Speter && svn_fspath__skip_ancestor(last_recursed->data, path)) 2525251881Speter continue; 2526251881Speter 2527251881Speter /* Fetch the change associated with our path. */ 2528251881Speter change = svn_hash_gets(changes, path); 2529251881Speter 2530251881Speter /* What does it mean to succeed at lock verification for a given 2531251881Speter path? For an existing file or directory getting modified 2532251881Speter (text, props), it means we hold the lock on the file or 2533251881Speter directory. For paths being added or removed, we need to hold 2534251881Speter the locks for that path and any children of that path. 2535251881Speter 2536251881Speter WHEW! We have no reliable way to determine the node kind of 2537251881Speter deleted items, but fortunately we are going to do a recursive 2538251881Speter check on deleted paths regardless of their kind. */ 2539251881Speter if (change->change_kind == svn_fs_path_change_modify) 2540251881Speter recurse = FALSE; 2541251881Speter SVN_ERR(svn_fs_base__allow_locked_operation(path, recurse, 2542251881Speter trail, subpool)); 2543251881Speter 2544251881Speter /* If we just did a recursive check, remember the path we 2545251881Speter checked (so children can be skipped). */ 2546251881Speter if (recurse) 2547251881Speter { 2548251881Speter if (! last_recursed) 2549251881Speter last_recursed = svn_stringbuf_create(path, pool); 2550251881Speter else 2551251881Speter svn_stringbuf_set(last_recursed, path); 2552251881Speter } 2553251881Speter } 2554251881Speter svn_pool_destroy(subpool); 2555251881Speter return SVN_NO_ERROR; 2556251881Speter} 2557251881Speter 2558251881Speter 2559251881Speterstruct commit_args 2560251881Speter{ 2561251881Speter svn_fs_txn_t *txn; 2562251881Speter svn_revnum_t new_rev; 2563251881Speter}; 2564251881Speter 2565251881Speter 2566251881Speter/* Commit ARGS->txn, setting ARGS->new_rev to the resulting new 2567251881Speter * revision, if ARGS->txn is up-to-date with respect to the repository. 2568251881Speter * 2569251881Speter * Up-to-date means that ARGS->txn's base root is the same as the root 2570251881Speter * of the youngest revision. If ARGS->txn is not up-to-date, the 2571251881Speter * error SVN_ERR_FS_TXN_OUT_OF_DATE is returned, and the commit fails: no 2572251881Speter * new revision is created, and ARGS->new_rev is not touched. 2573251881Speter * 2574251881Speter * If the commit succeeds, ARGS->txn is destroyed. 2575251881Speter */ 2576251881Speterstatic svn_error_t * 2577251881Spetertxn_body_commit(void *baton, trail_t *trail) 2578251881Speter{ 2579251881Speter struct commit_args *args = baton; 2580251881Speter 2581251881Speter svn_fs_txn_t *txn = args->txn; 2582251881Speter svn_fs_t *fs = txn->fs; 2583251881Speter const char *txn_name = txn->id; 2584251881Speter 2585251881Speter svn_revnum_t youngest_rev; 2586251881Speter const svn_fs_id_t *y_rev_root_id; 2587251881Speter dag_node_t *txn_base_root_node; 2588251881Speter 2589251881Speter /* Getting the youngest revision locks the revisions table until 2590251881Speter this trail is done. */ 2591251881Speter SVN_ERR(svn_fs_bdb__youngest_rev(&youngest_rev, fs, trail, trail->pool)); 2592251881Speter 2593251881Speter /* If the root of the youngest revision is the same as txn's base, 2594251881Speter then no further merging is necessary and we can commit. */ 2595251881Speter SVN_ERR(svn_fs_base__rev_get_root(&y_rev_root_id, fs, youngest_rev, 2596251881Speter trail, trail->pool)); 2597251881Speter SVN_ERR(svn_fs_base__dag_txn_base_root(&txn_base_root_node, fs, txn_name, 2598251881Speter trail, trail->pool)); 2599251881Speter /* ### kff todo: it seems weird to grab the ID for one, and the node 2600251881Speter for the other. We can certainly do the comparison we need, but 2601251881Speter it would be nice to grab the same type of information from the 2602251881Speter start, instead of having to transform one of them. */ 2603251881Speter if (! svn_fs_base__id_eq(y_rev_root_id, 2604251881Speter svn_fs_base__dag_get_id(txn_base_root_node))) 2605251881Speter { 2606251881Speter svn_string_t *id_str = svn_fs_base__id_unparse(y_rev_root_id, 2607251881Speter trail->pool); 2608251881Speter return svn_error_createf 2609251881Speter (SVN_ERR_FS_TXN_OUT_OF_DATE, NULL, 2610251881Speter _("Transaction '%s' out-of-date with respect to revision '%s'"), 2611251881Speter txn_name, id_str->data); 2612251881Speter } 2613251881Speter 2614251881Speter /* Locks may have been added (or stolen) between the calling of 2615251881Speter previous svn_fs.h functions and svn_fs_commit_txn(), so we need 2616251881Speter to re-examine every changed-path in the txn and re-verify all 2617251881Speter discovered locks. */ 2618251881Speter SVN_ERR(verify_locks(txn_name, trail, trail->pool)); 2619251881Speter 2620251881Speter /* Else, commit the txn. */ 2621251881Speter return svn_fs_base__dag_commit_txn(&(args->new_rev), txn, trail, 2622251881Speter trail->pool); 2623251881Speter} 2624251881Speter 2625251881Speter 2626251881Speter/* Note: it is acceptable for this function to call back into 2627251881Speter top-level FS interfaces because it does not itself use trails. */ 2628251881Spetersvn_error_t * 2629251881Spetersvn_fs_base__commit_txn(const char **conflict_p, 2630251881Speter svn_revnum_t *new_rev, 2631251881Speter svn_fs_txn_t *txn, 2632251881Speter apr_pool_t *pool) 2633251881Speter{ 2634251881Speter /* How do commits work in Subversion? 2635251881Speter * 2636251881Speter * When you're ready to commit, here's what you have: 2637251881Speter * 2638251881Speter * 1. A transaction, with a mutable tree hanging off it. 2639251881Speter * 2. A base revision, against which TXN_TREE was made. 2640251881Speter * 3. A latest revision, which may be newer than the base rev. 2641251881Speter * 2642251881Speter * The problem is that if latest != base, then one can't simply 2643251881Speter * attach the txn root as the root of the new revision, because that 2644251881Speter * would lose all the changes between base and latest. It is also 2645251881Speter * not acceptable to insist that base == latest; in a busy 2646251881Speter * repository, commits happen too fast to insist that everyone keep 2647251881Speter * their entire tree up-to-date at all times. Non-overlapping 2648251881Speter * changes should not interfere with each other. 2649251881Speter * 2650251881Speter * The solution is to merge the changes between base and latest into 2651251881Speter * the txn tree [see the function merge()]. The txn tree is the 2652251881Speter * only one of the three trees that is mutable, so it has to be the 2653251881Speter * one to adjust. 2654251881Speter * 2655251881Speter * You might have to adjust it more than once, if a new latest 2656251881Speter * revision gets committed while you were merging in the previous 2657251881Speter * one. For example: 2658251881Speter * 2659251881Speter * 1. Jane starts txn T, based at revision 6. 2660251881Speter * 2. Someone commits (or already committed) revision 7. 2661251881Speter * 3. Jane's starts merging the changes between 6 and 7 into T. 2662251881Speter * 4. Meanwhile, someone commits revision 8. 2663251881Speter * 5. Jane finishes the 6-->7 merge. T could now be committed 2664251881Speter * against a latest revision of 7, if only that were still the 2665251881Speter * latest. Unfortunately, 8 is now the latest, so... 2666251881Speter * 6. Jane starts merging the changes between 7 and 8 into T. 2667251881Speter * 7. Meanwhile, no one commits any new revisions. Whew. 2668251881Speter * 8. Jane commits T, creating revision 9, whose tree is exactly 2669251881Speter * T's tree, except immutable now. 2670251881Speter * 2671251881Speter * Lather, rinse, repeat. 2672251881Speter */ 2673251881Speter 2674251881Speter svn_error_t *err; 2675251881Speter svn_fs_t *fs = txn->fs; 2676251881Speter apr_pool_t *subpool = svn_pool_create(pool); 2677251881Speter 2678251881Speter /* Initialize output params. */ 2679251881Speter *new_rev = SVN_INVALID_REVNUM; 2680251881Speter if (conflict_p) 2681251881Speter *conflict_p = NULL; 2682251881Speter 2683251881Speter while (1729) 2684251881Speter { 2685251881Speter struct get_root_args get_root_args; 2686251881Speter struct merge_args merge_args; 2687251881Speter struct commit_args commit_args; 2688251881Speter svn_revnum_t youngish_rev; 2689251881Speter svn_fs_root_t *youngish_root; 2690251881Speter dag_node_t *youngish_root_node; 2691251881Speter 2692251881Speter svn_pool_clear(subpool); 2693251881Speter 2694251881Speter /* Get the *current* youngest revision, in one short-lived 2695251881Speter Berkeley transaction. (We don't want the revisions table 2696251881Speter locked while we do the main merge.) We call it "youngish" 2697251881Speter because new revisions might get committed after we've 2698251881Speter obtained it. */ 2699251881Speter 2700251881Speter SVN_ERR(svn_fs_base__youngest_rev(&youngish_rev, fs, subpool)); 2701251881Speter SVN_ERR(svn_fs_base__revision_root(&youngish_root, fs, youngish_rev, 2702251881Speter subpool)); 2703251881Speter 2704251881Speter /* Get the dag node for the youngest revision, also in one 2705251881Speter Berkeley transaction. Later we'll use it as the SOURCE 2706251881Speter argument to a merge, and if the merge succeeds, this youngest 2707251881Speter root node will become the new base root for the svn txn that 2708251881Speter was the target of the merge (but note that the youngest rev 2709251881Speter may have changed by then -- that's why we're careful to get 2710251881Speter this root in its own bdb txn here). */ 2711251881Speter get_root_args.root = youngish_root; 2712251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args, 2713251881Speter FALSE, subpool)); 2714251881Speter youngish_root_node = get_root_args.node; 2715251881Speter 2716251881Speter /* Try to merge. If the merge succeeds, the base root node of 2717251881Speter TARGET's txn will become the same as youngish_root_node, so 2718251881Speter any future merges will only be between that node and whatever 2719251881Speter the root node of the youngest rev is by then. */ 2720251881Speter merge_args.ancestor_node = NULL; 2721251881Speter merge_args.source_node = youngish_root_node; 2722251881Speter merge_args.txn = txn; 2723251881Speter merge_args.conflict = svn_stringbuf_create_empty(pool); /* use pool */ 2724251881Speter err = svn_fs_base__retry_txn(fs, txn_body_merge, &merge_args, 2725251881Speter FALSE, subpool); 2726251881Speter if (err) 2727251881Speter { 2728251881Speter if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p) 2729251881Speter *conflict_p = merge_args.conflict->data; 2730251881Speter return svn_error_trace(err); 2731251881Speter } 2732251881Speter 2733251881Speter /* Try to commit. */ 2734251881Speter commit_args.txn = txn; 2735251881Speter err = svn_fs_base__retry_txn(fs, txn_body_commit, &commit_args, 2736251881Speter FALSE, subpool); 2737251881Speter if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE)) 2738251881Speter { 2739251881Speter /* Did someone else finish committing a new revision while we 2740251881Speter were in mid-merge or mid-commit? If so, we'll need to 2741251881Speter loop again to merge the new changes in, then try to 2742251881Speter commit again. Or if that's not what happened, then just 2743251881Speter return the error. */ 2744251881Speter svn_revnum_t youngest_rev; 2745251881Speter svn_error_t *err2 = svn_fs_base__youngest_rev(&youngest_rev, fs, 2746251881Speter subpool); 2747251881Speter if (err2) 2748251881Speter { 2749251881Speter svn_error_clear(err); 2750251881Speter return svn_error_trace(err2); /* err2 is bad, 2751251881Speter it should not occur */ 2752251881Speter } 2753251881Speter else if (youngest_rev == youngish_rev) 2754251881Speter return svn_error_trace(err); 2755251881Speter else 2756251881Speter svn_error_clear(err); 2757251881Speter } 2758251881Speter else if (err) 2759251881Speter { 2760251881Speter return svn_error_trace(err); 2761251881Speter } 2762251881Speter else 2763251881Speter { 2764251881Speter /* Set the return value -- our brand spankin' new revision! */ 2765251881Speter *new_rev = commit_args.new_rev; 2766251881Speter break; 2767251881Speter } 2768251881Speter } 2769251881Speter 2770251881Speter svn_pool_destroy(subpool); 2771251881Speter return SVN_NO_ERROR; 2772251881Speter} 2773251881Speter 2774251881Speter/* Note: it is acceptable for this function to call back into 2775251881Speter public FS API interfaces because it does not itself use trails. */ 2776251881Speterstatic svn_error_t * 2777251881Speterbase_merge(const char **conflict_p, 2778251881Speter svn_fs_root_t *source_root, 2779251881Speter const char *source_path, 2780251881Speter svn_fs_root_t *target_root, 2781251881Speter const char *target_path, 2782251881Speter svn_fs_root_t *ancestor_root, 2783251881Speter const char *ancestor_path, 2784251881Speter apr_pool_t *pool) 2785251881Speter{ 2786251881Speter dag_node_t *source, *ancestor; 2787251881Speter struct get_root_args get_root_args; 2788251881Speter struct merge_args merge_args; 2789251881Speter svn_fs_txn_t *txn; 2790251881Speter svn_error_t *err; 2791251881Speter svn_fs_t *fs; 2792251881Speter 2793251881Speter if (! target_root->is_txn_root) 2794251881Speter return SVN_FS__NOT_TXN(target_root); 2795251881Speter 2796251881Speter /* Paranoia. */ 2797251881Speter fs = ancestor_root->fs; 2798251881Speter if ((source_root->fs != fs) || (target_root->fs != fs)) 2799251881Speter { 2800251881Speter return svn_error_create 2801251881Speter (SVN_ERR_FS_CORRUPT, NULL, 2802251881Speter _("Bad merge; ancestor, source, and target not all in same fs")); 2803251881Speter } 2804251881Speter 2805251881Speter /* ### kff todo: is there any compelling reason to get the nodes in 2806251881Speter one db transaction? Right now we don't; txn_body_get_root() gets 2807251881Speter one node at a time. This will probably need to change: 2808251881Speter 2809251881Speter Jim Blandy <jimb@zwingli.cygnus.com> writes: 2810251881Speter > svn_fs_merge needs to be a single transaction, to protect it against 2811251881Speter > people deleting parents of nodes it's working on, etc. 2812251881Speter */ 2813251881Speter 2814251881Speter /* Get the ancestor node. */ 2815251881Speter get_root_args.root = ancestor_root; 2816251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args, 2817251881Speter FALSE, pool)); 2818251881Speter ancestor = get_root_args.node; 2819251881Speter 2820251881Speter /* Get the source node. */ 2821251881Speter get_root_args.root = source_root; 2822251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args, 2823251881Speter FALSE, pool)); 2824251881Speter source = get_root_args.node; 2825251881Speter 2826251881Speter /* Open a txn for the txn root into which we're merging. */ 2827251881Speter SVN_ERR(svn_fs_base__open_txn(&txn, fs, target_root->txn, pool)); 2828251881Speter 2829251881Speter /* Merge changes between ANCESTOR and SOURCE into TXN. */ 2830251881Speter merge_args.source_node = source; 2831251881Speter merge_args.ancestor_node = ancestor; 2832251881Speter merge_args.txn = txn; 2833251881Speter merge_args.conflict = svn_stringbuf_create_empty(pool); 2834251881Speter err = svn_fs_base__retry_txn(fs, txn_body_merge, &merge_args, FALSE, pool); 2835251881Speter if (err) 2836251881Speter { 2837251881Speter if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p) 2838251881Speter *conflict_p = merge_args.conflict->data; 2839251881Speter return svn_error_trace(err); 2840251881Speter } 2841251881Speter 2842251881Speter return SVN_NO_ERROR; 2843251881Speter} 2844251881Speter 2845251881Speter 2846251881Speterstruct rev_get_txn_id_args 2847251881Speter{ 2848251881Speter const char **txn_id; 2849251881Speter svn_revnum_t revision; 2850251881Speter}; 2851251881Speter 2852251881Speter 2853251881Speterstatic svn_error_t * 2854251881Spetertxn_body_rev_get_txn_id(void *baton, trail_t *trail) 2855251881Speter{ 2856251881Speter struct rev_get_txn_id_args *args = baton; 2857251881Speter return svn_fs_base__rev_get_txn_id(args->txn_id, trail->fs, 2858251881Speter args->revision, trail, trail->pool); 2859251881Speter} 2860251881Speter 2861251881Speter 2862251881Spetersvn_error_t * 2863251881Spetersvn_fs_base__deltify(svn_fs_t *fs, 2864251881Speter svn_revnum_t revision, 2865251881Speter apr_pool_t *pool) 2866251881Speter{ 2867251881Speter svn_fs_root_t *root; 2868251881Speter const char *txn_id; 2869251881Speter struct rev_get_txn_id_args args; 2870251881Speter base_fs_data_t *bfd = fs->fsap_data; 2871251881Speter 2872251881Speter if (bfd->format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT) 2873251881Speter { 2874251881Speter const char *val; 2875251881Speter svn_revnum_t forward_delta_rev = 0; 2876251881Speter 2877251881Speter SVN_ERR(svn_fs_base__miscellaneous_get 2878251881Speter (&val, fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE, pool)); 2879251881Speter if (val) 2880251881Speter SVN_ERR(svn_revnum_parse(&forward_delta_rev, val, NULL)); 2881251881Speter 2882251881Speter /* ### FIXME: Unnecessarily harsh requirement? (cmpilato). */ 2883251881Speter if (revision <= forward_delta_rev) 2884251881Speter return svn_error_createf 2885251881Speter (SVN_ERR_UNSUPPORTED_FEATURE, NULL, 2886251881Speter _("Cannot deltify revisions prior to r%ld"), forward_delta_rev+1); 2887251881Speter } 2888251881Speter 2889251881Speter SVN_ERR(svn_fs_base__revision_root(&root, fs, revision, pool)); 2890251881Speter 2891251881Speter args.txn_id = &txn_id; 2892251881Speter args.revision = revision; 2893251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_rev_get_txn_id, &args, 2894251881Speter FALSE, pool)); 2895251881Speter 2896251881Speter return deltify_mutable(fs, root, "/", NULL, svn_node_dir, txn_id, pool); 2897251881Speter} 2898251881Speter 2899251881Speter 2900251881Speter/* Modifying directories */ 2901251881Speter 2902251881Speter 2903251881Speterstruct make_dir_args 2904251881Speter{ 2905251881Speter svn_fs_root_t *root; 2906251881Speter const char *path; 2907251881Speter}; 2908251881Speter 2909251881Speter 2910251881Speterstatic svn_error_t * 2911251881Spetertxn_body_make_dir(void *baton, 2912251881Speter trail_t *trail) 2913251881Speter{ 2914251881Speter struct make_dir_args *args = baton; 2915251881Speter svn_fs_root_t *root = args->root; 2916251881Speter const char *path = args->path; 2917251881Speter parent_path_t *parent_path; 2918251881Speter dag_node_t *sub_dir; 2919251881Speter const char *txn_id = root->txn; 2920251881Speter 2921251881Speter SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional, 2922251881Speter txn_id, trail, trail->pool)); 2923251881Speter 2924251881Speter /* If there's already a sub-directory by that name, complain. This 2925251881Speter also catches the case of trying to make a subdirectory named `/'. */ 2926251881Speter if (parent_path->node) 2927251881Speter return SVN_FS__ALREADY_EXISTS(root, path); 2928251881Speter 2929251881Speter /* Check to see if some lock is 'reserving' a file-path or dir-path 2930251881Speter at that location, or even some child-path; if so, check that we 2931251881Speter can use it. */ 2932251881Speter if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS) 2933251881Speter { 2934251881Speter SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE, 2935251881Speter trail, trail->pool)); 2936251881Speter } 2937251881Speter 2938251881Speter /* Create the subdirectory. */ 2939251881Speter SVN_ERR(make_path_mutable(root, parent_path->parent, path, 2940251881Speter trail, trail->pool)); 2941251881Speter SVN_ERR(svn_fs_base__dag_make_dir(&sub_dir, 2942251881Speter parent_path->parent->node, 2943251881Speter parent_path_path(parent_path->parent, 2944251881Speter trail->pool), 2945251881Speter parent_path->entry, 2946251881Speter txn_id, 2947251881Speter trail, trail->pool)); 2948251881Speter 2949251881Speter /* Make a record of this modification in the changes table. */ 2950251881Speter return add_change(root->fs, txn_id, path, 2951251881Speter svn_fs_base__dag_get_id(sub_dir), 2952251881Speter svn_fs_path_change_add, FALSE, FALSE, 2953251881Speter trail, trail->pool); 2954251881Speter} 2955251881Speter 2956251881Speter 2957251881Speterstatic svn_error_t * 2958251881Speterbase_make_dir(svn_fs_root_t *root, 2959251881Speter const char *path, 2960251881Speter apr_pool_t *pool) 2961251881Speter{ 2962251881Speter struct make_dir_args args; 2963251881Speter 2964251881Speter if (! root->is_txn_root) 2965251881Speter return SVN_FS__NOT_TXN(root); 2966251881Speter 2967251881Speter args.root = root; 2968251881Speter args.path = path; 2969251881Speter return svn_fs_base__retry_txn(root->fs, txn_body_make_dir, &args, 2970251881Speter TRUE, pool); 2971251881Speter} 2972251881Speter 2973251881Speter 2974251881Speterstruct delete_args 2975251881Speter{ 2976251881Speter svn_fs_root_t *root; 2977251881Speter const char *path; 2978251881Speter}; 2979251881Speter 2980251881Speter 2981251881Speter/* If this returns SVN_ERR_FS_NO_SUCH_ENTRY, it means that the 2982251881Speter basename of PATH is missing from its parent, that is, the final 2983251881Speter target of the deletion is missing. */ 2984251881Speterstatic svn_error_t * 2985251881Spetertxn_body_delete(void *baton, 2986251881Speter trail_t *trail) 2987251881Speter{ 2988251881Speter struct delete_args *args = baton; 2989251881Speter svn_fs_root_t *root = args->root; 2990251881Speter const char *path = args->path; 2991251881Speter parent_path_t *parent_path; 2992251881Speter const char *txn_id = root->txn; 2993251881Speter base_fs_data_t *bfd = trail->fs->fsap_data; 2994251881Speter 2995251881Speter if (! root->is_txn_root) 2996251881Speter return SVN_FS__NOT_TXN(root); 2997251881Speter 2998251881Speter SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, 2999251881Speter trail, trail->pool)); 3000251881Speter 3001251881Speter /* We can't remove the root of the filesystem. */ 3002251881Speter if (! parent_path->parent) 3003251881Speter return svn_error_create(SVN_ERR_FS_ROOT_DIR, NULL, 3004251881Speter _("The root directory cannot be deleted")); 3005251881Speter 3006251881Speter /* Check to see if path (or any child thereof) is locked; if so, 3007251881Speter check that we can use the existing lock(s). */ 3008251881Speter if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS) 3009251881Speter { 3010251881Speter SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE, 3011251881Speter trail, trail->pool)); 3012251881Speter } 3013251881Speter 3014251881Speter /* Make the parent directory mutable. */ 3015251881Speter SVN_ERR(make_path_mutable(root, parent_path->parent, path, 3016251881Speter trail, trail->pool)); 3017251881Speter 3018251881Speter /* Decrement mergeinfo counts on the parents of this node by the 3019251881Speter count it previously carried, if our format supports it. */ 3020251881Speter if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT) 3021251881Speter { 3022251881Speter apr_int64_t mergeinfo_count; 3023251881Speter SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_count, 3024251881Speter parent_path->node, 3025251881Speter trail, trail->pool)); 3026251881Speter SVN_ERR(adjust_parent_mergeinfo_counts(parent_path->parent, 3027251881Speter -mergeinfo_count, txn_id, 3028251881Speter trail, trail->pool)); 3029251881Speter } 3030251881Speter 3031251881Speter /* Do the deletion. */ 3032251881Speter SVN_ERR(svn_fs_base__dag_delete(parent_path->parent->node, 3033251881Speter parent_path->entry, 3034251881Speter txn_id, trail, trail->pool)); 3035251881Speter 3036251881Speter 3037251881Speter /* Make a record of this modification in the changes table. */ 3038251881Speter return add_change(root->fs, txn_id, path, 3039251881Speter svn_fs_base__dag_get_id(parent_path->node), 3040251881Speter svn_fs_path_change_delete, FALSE, FALSE, trail, 3041251881Speter trail->pool); 3042251881Speter} 3043251881Speter 3044251881Speter 3045251881Speterstatic svn_error_t * 3046251881Speterbase_delete_node(svn_fs_root_t *root, 3047251881Speter const char *path, 3048251881Speter apr_pool_t *pool) 3049251881Speter{ 3050251881Speter struct delete_args args; 3051251881Speter 3052251881Speter args.root = root; 3053251881Speter args.path = path; 3054251881Speter return svn_fs_base__retry_txn(root->fs, txn_body_delete, &args, 3055251881Speter TRUE, pool); 3056251881Speter} 3057251881Speter 3058251881Speter 3059251881Speterstruct copy_args 3060251881Speter{ 3061251881Speter svn_fs_root_t *from_root; 3062251881Speter const char *from_path; 3063251881Speter svn_fs_root_t *to_root; 3064251881Speter const char *to_path; 3065251881Speter svn_boolean_t preserve_history; 3066251881Speter}; 3067251881Speter 3068251881Speter 3069251881Speterstatic svn_error_t * 3070251881Spetertxn_body_copy(void *baton, 3071251881Speter trail_t *trail) 3072251881Speter{ 3073251881Speter struct copy_args *args = baton; 3074251881Speter svn_fs_root_t *from_root = args->from_root; 3075251881Speter const char *from_path = args->from_path; 3076251881Speter svn_fs_root_t *to_root = args->to_root; 3077251881Speter const char *to_path = args->to_path; 3078251881Speter dag_node_t *from_node; 3079251881Speter parent_path_t *to_parent_path; 3080251881Speter const char *txn_id = to_root->txn; 3081251881Speter 3082251881Speter /* Get the NODE for FROM_PATH in FROM_ROOT.*/ 3083251881Speter SVN_ERR(get_dag(&from_node, from_root, from_path, trail, trail->pool)); 3084251881Speter 3085251881Speter /* Build up the parent path from TO_PATH in TO_ROOT. If the last 3086251881Speter component does not exist, it's not that big a deal. We'll just 3087251881Speter make one there. */ 3088251881Speter SVN_ERR(open_path(&to_parent_path, to_root, to_path, 3089251881Speter open_path_last_optional, txn_id, trail, trail->pool)); 3090251881Speter 3091251881Speter /* Check to see if to-path (or any child thereof) is locked, or at 3092251881Speter least 'reserved', whether it exists or not; if so, check that we 3093251881Speter can use the existing lock(s). */ 3094251881Speter if (to_root->txn_flags & SVN_FS_TXN_CHECK_LOCKS) 3095251881Speter { 3096251881Speter SVN_ERR(svn_fs_base__allow_locked_operation(to_path, TRUE, 3097251881Speter trail, trail->pool)); 3098251881Speter } 3099251881Speter 3100251881Speter /* If the destination node already exists as the same node as the 3101251881Speter source (in other words, this operation would result in nothing 3102251881Speter happening at all), just do nothing an return successfully, 3103251881Speter proud that you saved yourself from a tiresome task. */ 3104251881Speter if ((to_parent_path->node) 3105251881Speter && (svn_fs_base__id_compare(svn_fs_base__dag_get_id(from_node), 3106251881Speter svn_fs_base__dag_get_id 3107251881Speter (to_parent_path->node)) == 0)) 3108251881Speter return SVN_NO_ERROR; 3109251881Speter 3110251881Speter if (! from_root->is_txn_root) 3111251881Speter { 3112251881Speter svn_fs_path_change_kind_t kind; 3113251881Speter dag_node_t *new_node; 3114251881Speter apr_int64_t old_mergeinfo_count = 0, mergeinfo_count; 3115251881Speter base_fs_data_t *bfd = trail->fs->fsap_data; 3116251881Speter 3117251881Speter /* If TO_PATH already existed prior to the copy, note that this 3118251881Speter operation is a replacement, not an addition. */ 3119251881Speter if (to_parent_path->node) 3120251881Speter kind = svn_fs_path_change_replace; 3121251881Speter else 3122251881Speter kind = svn_fs_path_change_add; 3123251881Speter 3124251881Speter /* Make sure the target node's parents are mutable. */ 3125251881Speter SVN_ERR(make_path_mutable(to_root, to_parent_path->parent, 3126251881Speter to_path, trail, trail->pool)); 3127251881Speter 3128251881Speter /* If this is a replacement operation, we need to know the old 3129251881Speter node's mergeinfo count. */ 3130251881Speter if (to_parent_path->node) 3131251881Speter SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, 3132251881Speter &old_mergeinfo_count, 3133251881Speter to_parent_path->node, 3134251881Speter trail, trail->pool)); 3135251881Speter /* Do the copy. */ 3136251881Speter SVN_ERR(svn_fs_base__dag_copy(to_parent_path->parent->node, 3137251881Speter to_parent_path->entry, 3138251881Speter from_node, 3139251881Speter args->preserve_history, 3140251881Speter from_root->rev, 3141251881Speter from_path, txn_id, trail, trail->pool)); 3142251881Speter 3143251881Speter /* Adjust the mergeinfo counts of the destination's parents if 3144251881Speter our format supports it. */ 3145251881Speter if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT) 3146251881Speter { 3147251881Speter SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, 3148251881Speter &mergeinfo_count, 3149251881Speter from_node, trail, 3150251881Speter trail->pool)); 3151251881Speter SVN_ERR(adjust_parent_mergeinfo_counts 3152251881Speter (to_parent_path->parent, 3153251881Speter mergeinfo_count - old_mergeinfo_count, 3154251881Speter txn_id, trail, trail->pool)); 3155251881Speter } 3156251881Speter 3157251881Speter /* Make a record of this modification in the changes table. */ 3158251881Speter SVN_ERR(get_dag(&new_node, to_root, to_path, trail, trail->pool)); 3159251881Speter SVN_ERR(add_change(to_root->fs, txn_id, to_path, 3160251881Speter svn_fs_base__dag_get_id(new_node), 3161251881Speter kind, FALSE, FALSE, trail, trail->pool)); 3162251881Speter } 3163251881Speter else 3164251881Speter { 3165251881Speter /* See IZ Issue #436 */ 3166251881Speter /* Copying from transaction roots not currently available. 3167251881Speter 3168251881Speter ### cmpilato todo someday: make this not so. :-) Note that 3169251881Speter when copying from mutable trees, you have to make sure that 3170251881Speter you aren't creating a cyclic graph filesystem, and a simple 3171251881Speter referencing operation won't cut it. Currently, we should not 3172251881Speter be able to reach this clause, and the interface reports that 3173251881Speter this only works from immutable trees anyway, but JimB has 3174251881Speter stated that this requirement need not be necessary in the 3175251881Speter future. */ 3176251881Speter 3177251881Speter SVN_ERR_MALFUNCTION(); 3178251881Speter } 3179251881Speter 3180251881Speter return SVN_NO_ERROR; 3181251881Speter} 3182251881Speter 3183251881Speter 3184251881Speter/* Set *SAME_P to TRUE if FS1 and FS2 have the same UUID, else set to FALSE. 3185251881Speter Use POOL for temporary allocation only. 3186251881Speter Note: this code is duplicated between libsvn_fs_fs and libsvn_fs_base. */ 3187251881Speterstatic svn_error_t * 3188251881Speterfs_same_p(svn_boolean_t *same_p, 3189251881Speter svn_fs_t *fs1, 3190251881Speter svn_fs_t *fs2, 3191251881Speter apr_pool_t *pool) 3192251881Speter{ 3193251881Speter *same_p = ! strcmp(fs1->uuid, fs2->uuid); 3194251881Speter return SVN_NO_ERROR; 3195251881Speter} 3196251881Speter 3197251881Speter/* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under 3198251881Speter TO_ROOT. If PRESERVE_HISTORY is set, then the copy is recorded in 3199251881Speter the copies table. Perform temporary allocations in POOL. */ 3200251881Speterstatic svn_error_t * 3201251881Spetercopy_helper(svn_fs_root_t *from_root, 3202251881Speter const char *from_path, 3203251881Speter svn_fs_root_t *to_root, 3204251881Speter const char *to_path, 3205251881Speter svn_boolean_t preserve_history, 3206251881Speter apr_pool_t *pool) 3207251881Speter{ 3208251881Speter struct copy_args args; 3209251881Speter svn_boolean_t same_p; 3210251881Speter 3211251881Speter /* Use an error check, not an assert, because even the caller cannot 3212251881Speter guarantee that a filesystem's UUID has not changed "on the fly". */ 3213251881Speter SVN_ERR(fs_same_p(&same_p, from_root->fs, to_root->fs, pool)); 3214251881Speter if (! same_p) 3215251881Speter return svn_error_createf 3216251881Speter (SVN_ERR_UNSUPPORTED_FEATURE, NULL, 3217251881Speter _("Cannot copy between two different filesystems ('%s' and '%s')"), 3218251881Speter from_root->fs->path, to_root->fs->path); 3219251881Speter 3220251881Speter if (! to_root->is_txn_root) 3221251881Speter return SVN_FS__NOT_TXN(to_root); 3222251881Speter 3223251881Speter if (from_root->is_txn_root) 3224251881Speter return svn_error_create 3225251881Speter (SVN_ERR_UNSUPPORTED_FEATURE, NULL, 3226251881Speter _("Copy from mutable tree not currently supported")); 3227251881Speter 3228251881Speter args.from_root = from_root; 3229251881Speter args.from_path = from_path; 3230251881Speter args.to_root = to_root; 3231251881Speter args.to_path = to_path; 3232251881Speter args.preserve_history = preserve_history; 3233251881Speter 3234251881Speter return svn_fs_base__retry_txn(to_root->fs, txn_body_copy, &args, 3235251881Speter TRUE, pool); 3236251881Speter} 3237251881Speter 3238251881Speterstatic svn_error_t * 3239251881Speterbase_copy(svn_fs_root_t *from_root, 3240251881Speter const char *from_path, 3241251881Speter svn_fs_root_t *to_root, 3242251881Speter const char *to_path, 3243251881Speter apr_pool_t *pool) 3244251881Speter{ 3245251881Speter return copy_helper(from_root, from_path, to_root, to_path, TRUE, pool); 3246251881Speter} 3247251881Speter 3248251881Speter 3249251881Speterstatic svn_error_t * 3250251881Speterbase_revision_link(svn_fs_root_t *from_root, 3251251881Speter svn_fs_root_t *to_root, 3252251881Speter const char *path, 3253251881Speter apr_pool_t *pool) 3254251881Speter{ 3255251881Speter return copy_helper(from_root, path, to_root, path, FALSE, pool); 3256251881Speter} 3257251881Speter 3258251881Speter 3259251881Speterstruct copied_from_args 3260251881Speter{ 3261251881Speter svn_fs_root_t *root; /* Root for the node whose ancestry we seek. */ 3262251881Speter const char *path; /* Path for the node whose ancestry we seek. */ 3263251881Speter 3264251881Speter svn_revnum_t result_rev; /* Revision, if any, of the ancestor. */ 3265251881Speter const char *result_path; /* Path, if any, of the ancestor. */ 3266251881Speter 3267251881Speter apr_pool_t *pool; /* Allocate `result_path' here. */ 3268251881Speter}; 3269251881Speter 3270251881Speter 3271251881Speterstatic svn_error_t * 3272251881Spetertxn_body_copied_from(void *baton, trail_t *trail) 3273251881Speter{ 3274251881Speter struct copied_from_args *args = baton; 3275251881Speter const svn_fs_id_t *node_id, *pred_id; 3276251881Speter dag_node_t *node; 3277251881Speter svn_fs_t *fs = args->root->fs; 3278251881Speter 3279251881Speter /* Clear the return variables. */ 3280251881Speter args->result_path = NULL; 3281251881Speter args->result_rev = SVN_INVALID_REVNUM; 3282251881Speter 3283251881Speter /* Fetch the NODE in question. */ 3284251881Speter SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool)); 3285251881Speter node_id = svn_fs_base__dag_get_id(node); 3286251881Speter 3287251881Speter /* Check the node's predecessor-ID. If it doesn't have one, it 3288251881Speter isn't a copy. */ 3289251881Speter SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id, node, 3290251881Speter trail, trail->pool)); 3291251881Speter if (! pred_id) 3292251881Speter return SVN_NO_ERROR; 3293251881Speter 3294251881Speter /* If NODE's copy-ID is the same as that of its predecessor... */ 3295251881Speter if (svn_fs_base__key_compare(svn_fs_base__id_copy_id(node_id), 3296251881Speter svn_fs_base__id_copy_id(pred_id)) != 0) 3297251881Speter { 3298251881Speter /* ... then NODE was either the target of a copy operation, 3299251881Speter a copied subtree item. We examine the actual copy record 3300251881Speter to determine which is the case. */ 3301251881Speter copy_t *copy; 3302251881Speter SVN_ERR(svn_fs_bdb__get_copy(©, fs, 3303251881Speter svn_fs_base__id_copy_id(node_id), 3304251881Speter trail, trail->pool)); 3305251881Speter if ((copy->kind == copy_kind_real) 3306251881Speter && svn_fs_base__id_eq(copy->dst_noderev_id, node_id)) 3307251881Speter { 3308251881Speter args->result_path = copy->src_path; 3309251881Speter SVN_ERR(svn_fs_base__txn_get_revision(&(args->result_rev), fs, 3310251881Speter copy->src_txn_id, 3311251881Speter trail, trail->pool)); 3312251881Speter } 3313251881Speter } 3314251881Speter return SVN_NO_ERROR; 3315251881Speter} 3316251881Speter 3317251881Speter 3318251881Speterstatic svn_error_t * 3319251881Speterbase_copied_from(svn_revnum_t *rev_p, 3320251881Speter const char **path_p, 3321251881Speter svn_fs_root_t *root, 3322251881Speter const char *path, 3323251881Speter apr_pool_t *pool) 3324251881Speter{ 3325251881Speter struct copied_from_args args; 3326251881Speter apr_pool_t *scratch_pool = svn_pool_create(pool); 3327251881Speter args.root = root; 3328251881Speter args.path = path; 3329251881Speter args.pool = pool; 3330251881Speter 3331251881Speter SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_copied_from, &args, 3332251881Speter FALSE, scratch_pool)); 3333251881Speter 3334251881Speter *rev_p = args.result_rev; 3335251881Speter *path_p = args.result_path ? apr_pstrdup(pool, args.result_path) : NULL; 3336251881Speter 3337251881Speter svn_pool_destroy(scratch_pool); 3338251881Speter return SVN_NO_ERROR; 3339251881Speter} 3340251881Speter 3341251881Speter 3342251881Speter 3343251881Speter/* Files. */ 3344251881Speter 3345251881Speter 3346251881Speterstruct make_file_args 3347251881Speter{ 3348251881Speter svn_fs_root_t *root; 3349251881Speter const char *path; 3350251881Speter}; 3351251881Speter 3352251881Speter 3353251881Speterstatic svn_error_t * 3354251881Spetertxn_body_make_file(void *baton, 3355251881Speter trail_t *trail) 3356251881Speter{ 3357251881Speter struct make_file_args *args = baton; 3358251881Speter svn_fs_root_t *root = args->root; 3359251881Speter const char *path = args->path; 3360251881Speter parent_path_t *parent_path; 3361251881Speter dag_node_t *child; 3362251881Speter const char *txn_id = root->txn; 3363251881Speter 3364251881Speter SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional, 3365251881Speter txn_id, trail, trail->pool)); 3366251881Speter 3367251881Speter /* If there's already a file by that name, complain. 3368251881Speter This also catches the case of trying to make a file named `/'. */ 3369251881Speter if (parent_path->node) 3370251881Speter return SVN_FS__ALREADY_EXISTS(root, path); 3371251881Speter 3372251881Speter /* Check to see if some lock is 'reserving' a file-path or dir-path 3373251881Speter at that location, or even some child-path; if so, check that we 3374251881Speter can use it. */ 3375251881Speter if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS) 3376251881Speter { 3377251881Speter SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE, 3378251881Speter trail, trail->pool)); 3379251881Speter } 3380251881Speter 3381251881Speter /* Create the file. */ 3382251881Speter SVN_ERR(make_path_mutable(root, parent_path->parent, path, 3383251881Speter trail, trail->pool)); 3384251881Speter SVN_ERR(svn_fs_base__dag_make_file(&child, 3385251881Speter parent_path->parent->node, 3386251881Speter parent_path_path(parent_path->parent, 3387251881Speter trail->pool), 3388251881Speter parent_path->entry, 3389251881Speter txn_id, 3390251881Speter trail, trail->pool)); 3391251881Speter 3392251881Speter /* Make a record of this modification in the changes table. */ 3393251881Speter return add_change(root->fs, txn_id, path, 3394251881Speter svn_fs_base__dag_get_id(child), 3395251881Speter svn_fs_path_change_add, TRUE, FALSE, 3396251881Speter trail, trail->pool); 3397251881Speter} 3398251881Speter 3399251881Speter 3400251881Speterstatic svn_error_t * 3401251881Speterbase_make_file(svn_fs_root_t *root, 3402251881Speter const char *path, 3403251881Speter apr_pool_t *pool) 3404251881Speter{ 3405251881Speter struct make_file_args args; 3406251881Speter 3407251881Speter args.root = root; 3408251881Speter args.path = path; 3409251881Speter return svn_fs_base__retry_txn(root->fs, txn_body_make_file, &args, 3410251881Speter TRUE, pool); 3411251881Speter} 3412251881Speter 3413251881Speter 3414251881Speter 3415251881Speterstruct file_length_args 3416251881Speter{ 3417251881Speter svn_fs_root_t *root; 3418251881Speter const char *path; 3419251881Speter svn_filesize_t length; /* OUT parameter */ 3420251881Speter}; 3421251881Speter 3422251881Speterstatic svn_error_t * 3423251881Spetertxn_body_file_length(void *baton, 3424251881Speter trail_t *trail) 3425251881Speter{ 3426251881Speter struct file_length_args *args = baton; 3427251881Speter dag_node_t *file; 3428251881Speter 3429251881Speter /* First create a dag_node_t from the root/path pair. */ 3430251881Speter SVN_ERR(get_dag(&file, args->root, args->path, trail, trail->pool)); 3431251881Speter 3432251881Speter /* Now fetch its length */ 3433251881Speter return svn_fs_base__dag_file_length(&args->length, file, 3434251881Speter trail, trail->pool); 3435251881Speter} 3436251881Speter 3437251881Speterstatic svn_error_t * 3438251881Speterbase_file_length(svn_filesize_t *length_p, 3439251881Speter svn_fs_root_t *root, 3440251881Speter const char *path, 3441251881Speter apr_pool_t *pool) 3442251881Speter{ 3443251881Speter struct file_length_args args; 3444251881Speter 3445251881Speter args.root = root; 3446251881Speter args.path = path; 3447251881Speter SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_file_length, &args, 3448251881Speter TRUE, pool)); 3449251881Speter 3450251881Speter *length_p = args.length; 3451251881Speter return SVN_NO_ERROR; 3452251881Speter} 3453251881Speter 3454251881Speter 3455251881Speterstruct file_checksum_args 3456251881Speter{ 3457251881Speter svn_fs_root_t *root; 3458251881Speter const char *path; 3459251881Speter svn_checksum_kind_t kind; 3460251881Speter svn_checksum_t **checksum; /* OUT parameter */ 3461251881Speter}; 3462251881Speter 3463251881Speterstatic svn_error_t * 3464251881Spetertxn_body_file_checksum(void *baton, 3465251881Speter trail_t *trail) 3466251881Speter{ 3467251881Speter struct file_checksum_args *args = baton; 3468251881Speter dag_node_t *file; 3469251881Speter 3470251881Speter SVN_ERR(get_dag(&file, args->root, args->path, trail, trail->pool)); 3471251881Speter 3472251881Speter return svn_fs_base__dag_file_checksum(args->checksum, args->kind, file, 3473251881Speter trail, trail->pool); 3474251881Speter} 3475251881Speter 3476251881Speterstatic svn_error_t * 3477251881Speterbase_file_checksum(svn_checksum_t **checksum, 3478251881Speter svn_checksum_kind_t kind, 3479251881Speter svn_fs_root_t *root, 3480251881Speter const char *path, 3481251881Speter apr_pool_t *pool) 3482251881Speter{ 3483251881Speter struct file_checksum_args args; 3484251881Speter apr_pool_t *scratch_pool = svn_pool_create(pool); 3485251881Speter 3486251881Speter args.root = root; 3487251881Speter args.path = path; 3488251881Speter args.kind = kind; 3489251881Speter args.checksum = checksum; 3490251881Speter SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_file_checksum, &args, 3491251881Speter FALSE, scratch_pool)); 3492251881Speter *checksum = svn_checksum_dup(*checksum, pool); 3493251881Speter svn_pool_destroy(scratch_pool); 3494251881Speter return SVN_NO_ERROR; 3495251881Speter} 3496251881Speter 3497251881Speter 3498251881Speter/* --- Machinery for svn_fs_file_contents() --- */ 3499251881Speter 3500251881Speter 3501251881Speter/* Local baton type for txn_body_get_file_contents. */ 3502251881Spetertypedef struct file_contents_baton_t 3503251881Speter{ 3504251881Speter /* The file we want to read. */ 3505251881Speter svn_fs_root_t *root; 3506251881Speter const char *path; 3507251881Speter 3508251881Speter /* The dag_node that will be made from the above. */ 3509251881Speter dag_node_t *node; 3510251881Speter 3511251881Speter /* The pool in which `file_stream' (below) is allocated. */ 3512251881Speter apr_pool_t *pool; 3513251881Speter 3514251881Speter /* The readable file stream that will be made from the 3515251881Speter dag_node. (And returned to the caller.) */ 3516251881Speter svn_stream_t *file_stream; 3517251881Speter 3518251881Speter} file_contents_baton_t; 3519251881Speter 3520251881Speter 3521251881Speter/* Main body of svn_fs_file_contents; converts a root/path pair into 3522251881Speter a readable file stream (in the context of a db txn). */ 3523251881Speterstatic svn_error_t * 3524251881Spetertxn_body_get_file_contents(void *baton, trail_t *trail) 3525251881Speter{ 3526251881Speter file_contents_baton_t *fb = (file_contents_baton_t *) baton; 3527251881Speter 3528251881Speter /* First create a dag_node_t from the root/path pair. */ 3529251881Speter SVN_ERR(get_dag(&(fb->node), fb->root, fb->path, trail, trail->pool)); 3530251881Speter 3531251881Speter /* Then create a readable stream from the dag_node_t. */ 3532251881Speter return svn_fs_base__dag_get_contents(&(fb->file_stream), 3533251881Speter fb->node, trail, fb->pool); 3534251881Speter} 3535251881Speter 3536251881Speter 3537251881Speter 3538251881Speterstatic svn_error_t * 3539251881Speterbase_file_contents(svn_stream_t **contents, 3540251881Speter svn_fs_root_t *root, 3541251881Speter const char *path, 3542251881Speter apr_pool_t *pool) 3543251881Speter{ 3544251881Speter file_contents_baton_t *fb = apr_pcalloc(pool, sizeof(*fb)); 3545251881Speter fb->root = root; 3546251881Speter fb->path = path; 3547251881Speter fb->pool = pool; 3548251881Speter 3549251881Speter /* Create the readable stream in the context of a db txn. */ 3550251881Speter SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_get_file_contents, fb, 3551251881Speter FALSE, pool)); 3552251881Speter 3553251881Speter *contents = fb->file_stream; 3554251881Speter return SVN_NO_ERROR; 3555251881Speter} 3556251881Speter 3557251881Speter/* --- End machinery for svn_fs_file_contents() --- */ 3558251881Speter 3559251881Speter 3560251881Speter 3561251881Speter/* --- Machinery for svn_fs_apply_textdelta() --- */ 3562251881Speter 3563251881Speter 3564251881Speter/* Local baton type for all the helper functions below. */ 3565251881Spetertypedef struct txdelta_baton_t 3566251881Speter{ 3567251881Speter /* This is the custom-built window consumer given to us by the delta 3568251881Speter library; it uniquely knows how to read data from our designated 3569251881Speter "source" stream, interpret the window, and write data to our 3570251881Speter designated "target" stream (in this case, our repos file.) */ 3571251881Speter svn_txdelta_window_handler_t interpreter; 3572251881Speter void *interpreter_baton; 3573251881Speter 3574251881Speter /* The original file info */ 3575251881Speter svn_fs_root_t *root; 3576251881Speter const char *path; 3577251881Speter 3578251881Speter /* Derived from the file info */ 3579251881Speter dag_node_t *node; 3580251881Speter 3581251881Speter svn_stream_t *source_stream; 3582251881Speter svn_stream_t *target_stream; 3583251881Speter svn_stream_t *string_stream; 3584251881Speter svn_stringbuf_t *target_string; 3585251881Speter 3586251881Speter /* Checksums for the base text against which a delta is to be 3587251881Speter applied, and for the resultant fulltext, respectively. Either or 3588251881Speter both may be null, in which case ignored. */ 3589251881Speter svn_checksum_t *base_checksum; 3590251881Speter svn_checksum_t *result_checksum; 3591251881Speter 3592251881Speter /* Pool used by db txns */ 3593251881Speter apr_pool_t *pool; 3594251881Speter 3595251881Speter} txdelta_baton_t; 3596251881Speter 3597251881Speter 3598251881Speter/* A trail-ready wrapper around svn_fs_base__dag_finalize_edits. 3599251881Speter * This closes BATON->target_stream. 3600251881Speter * 3601251881Speter * Note: If you're confused about how this function relates to another 3602251881Speter * of similar name, think of it this way: 3603251881Speter * 3604251881Speter * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits() 3605251881Speter * svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits() 3606251881Speter */ 3607251881Speterstatic svn_error_t * 3608251881Spetertxn_body_txdelta_finalize_edits(void *baton, trail_t *trail) 3609251881Speter{ 3610251881Speter txdelta_baton_t *tb = (txdelta_baton_t *) baton; 3611251881Speter SVN_ERR(svn_fs_base__dag_finalize_edits(tb->node, 3612251881Speter tb->result_checksum, 3613251881Speter tb->root->txn, 3614251881Speter trail, trail->pool)); 3615251881Speter 3616251881Speter /* Make a record of this modification in the changes table. */ 3617251881Speter return add_change(tb->root->fs, tb->root->txn, tb->path, 3618251881Speter svn_fs_base__dag_get_id(tb->node), 3619251881Speter svn_fs_path_change_modify, TRUE, FALSE, trail, 3620251881Speter trail->pool); 3621251881Speter} 3622251881Speter 3623251881Speter 3624251881Speter/* ### see comment in window_consumer() regarding this function. */ 3625251881Speter 3626251881Speter/* Helper function of generic type `svn_write_fn_t'. Implements a 3627251881Speter writable stream which appends to an svn_stringbuf_t. */ 3628251881Speterstatic svn_error_t * 3629251881Speterwrite_to_string(void *baton, const char *data, apr_size_t *len) 3630251881Speter{ 3631251881Speter txdelta_baton_t *tb = (txdelta_baton_t *) baton; 3632251881Speter svn_stringbuf_appendbytes(tb->target_string, data, *len); 3633251881Speter return SVN_NO_ERROR; 3634251881Speter} 3635251881Speter 3636251881Speter 3637251881Speter 3638251881Speter/* The main window handler returned by svn_fs_apply_textdelta. */ 3639251881Speterstatic svn_error_t * 3640251881Speterwindow_consumer(svn_txdelta_window_t *window, void *baton) 3641251881Speter{ 3642251881Speter txdelta_baton_t *tb = (txdelta_baton_t *) baton; 3643251881Speter 3644251881Speter /* Send the window right through to the custom window interpreter. 3645251881Speter In theory, the interpreter will then write more data to 3646251881Speter cb->target_string. */ 3647251881Speter SVN_ERR(tb->interpreter(window, tb->interpreter_baton)); 3648251881Speter 3649251881Speter /* ### the write_to_string() callback for the txdelta's output stream 3650251881Speter ### should be doing all the flush determination logic, not here. 3651251881Speter ### in a drastic case, a window could generate a LOT more than the 3652251881Speter ### maximum buffer size. we want to flush to the underlying target 3653251881Speter ### stream much sooner (e.g. also in a streamy fashion). also, by 3654251881Speter ### moving this logic inside the stream, the stream becomes nice 3655251881Speter ### and encapsulated: it holds all the logic about buffering and 3656251881Speter ### flushing. 3657251881Speter ### 3658251881Speter ### further: I believe the buffering should be removed from tree.c 3659251881Speter ### the buffering should go into the target_stream itself, which 3660251881Speter ### is defined by reps-string.c. Specifically, I think the 3661251881Speter ### rep_write_contents() function will handle the buffering and 3662251881Speter ### the spill to the underlying DB. by locating it there, then 3663251881Speter ### anybody who gets a writable stream for FS content can take 3664251881Speter ### advantage of the buffering capability. this will be important 3665251881Speter ### when we export an FS API function for writing a fulltext into 3666251881Speter ### the FS, rather than forcing that fulltext thru apply_textdelta. 3667251881Speter */ 3668251881Speter 3669251881Speter /* Check to see if we need to purge the portion of the contents that 3670251881Speter have been written thus far. */ 3671251881Speter if ((! window) || (tb->target_string->len > WRITE_BUFFER_SIZE)) 3672251881Speter { 3673251881Speter apr_size_t len = tb->target_string->len; 3674251881Speter SVN_ERR(svn_stream_write(tb->target_stream, 3675251881Speter tb->target_string->data, 3676251881Speter &len)); 3677251881Speter svn_stringbuf_setempty(tb->target_string); 3678251881Speter } 3679251881Speter 3680251881Speter /* Is the window NULL? If so, we're done. */ 3681251881Speter if (! window) 3682251881Speter { 3683251881Speter /* Close the internal-use stream. ### This used to be inside of 3684251881Speter txn_body_fulltext_finalize_edits(), but that invoked a nested 3685251881Speter Berkeley DB transaction -- scandalous! */ 3686251881Speter SVN_ERR(svn_stream_close(tb->target_stream)); 3687251881Speter 3688251881Speter /* Tell the dag subsystem that we're finished with our edits. */ 3689251881Speter SVN_ERR(svn_fs_base__retry_txn(tb->root->fs, 3690251881Speter txn_body_txdelta_finalize_edits, tb, 3691251881Speter FALSE, tb->pool)); 3692251881Speter } 3693251881Speter 3694251881Speter return SVN_NO_ERROR; 3695251881Speter} 3696251881Speter 3697251881Speter 3698251881Speterstatic svn_error_t * 3699251881Spetertxn_body_apply_textdelta(void *baton, trail_t *trail) 3700251881Speter{ 3701251881Speter txdelta_baton_t *tb = (txdelta_baton_t *) baton; 3702251881Speter parent_path_t *parent_path; 3703251881Speter const char *txn_id = tb->root->txn; 3704251881Speter 3705251881Speter /* Call open_path with no flags, as we want this to return an error 3706251881Speter if the node for which we are searching doesn't exist. */ 3707251881Speter SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, 3708251881Speter trail, trail->pool)); 3709251881Speter 3710251881Speter /* Check to see if path is locked; if so, check that we can use it. */ 3711251881Speter if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS) 3712251881Speter SVN_ERR(svn_fs_base__allow_locked_operation(tb->path, FALSE, 3713251881Speter trail, trail->pool)); 3714251881Speter 3715251881Speter /* Now, make sure this path is mutable. */ 3716251881Speter SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, 3717251881Speter trail, trail->pool)); 3718251881Speter tb->node = parent_path->node; 3719251881Speter 3720251881Speter if (tb->base_checksum) 3721251881Speter { 3722251881Speter svn_checksum_t *checksum; 3723251881Speter 3724251881Speter /* Until we finalize the node, its data_key points to the old 3725251881Speter contents, in other words, the base text. */ 3726251881Speter SVN_ERR(svn_fs_base__dag_file_checksum(&checksum, 3727251881Speter tb->base_checksum->kind, 3728251881Speter tb->node, trail, trail->pool)); 3729251881Speter /* TODO: This only compares checksums if they are the same kind, but 3730251881Speter we're calculating both SHA1 and MD5 checksums somewhere in 3731251881Speter reps-strings.c. Could we keep them both around somehow so this 3732251881Speter check could be more comprehensive? */ 3733251881Speter if (!svn_checksum_match(tb->base_checksum, checksum)) 3734251881Speter return svn_checksum_mismatch_err(tb->base_checksum, checksum, 3735251881Speter trail->pool, 3736251881Speter _("Base checksum mismatch on '%s'"), 3737251881Speter tb->path); 3738251881Speter } 3739251881Speter 3740251881Speter /* Make a readable "source" stream out of the current contents of 3741251881Speter ROOT/PATH; obviously, this must done in the context of a db_txn. 3742251881Speter The stream is returned in tb->source_stream. */ 3743251881Speter SVN_ERR(svn_fs_base__dag_get_contents(&(tb->source_stream), 3744251881Speter tb->node, trail, tb->pool)); 3745251881Speter 3746251881Speter /* Make a writable "target" stream */ 3747251881Speter SVN_ERR(svn_fs_base__dag_get_edit_stream(&(tb->target_stream), tb->node, 3748251881Speter txn_id, trail, tb->pool)); 3749251881Speter 3750251881Speter /* Make a writable "string" stream which writes data to 3751251881Speter tb->target_string. */ 3752251881Speter tb->target_string = svn_stringbuf_create_empty(tb->pool); 3753251881Speter tb->string_stream = svn_stream_create(tb, tb->pool); 3754251881Speter svn_stream_set_write(tb->string_stream, write_to_string); 3755251881Speter 3756251881Speter /* Now, create a custom window handler that uses our two streams. */ 3757251881Speter svn_txdelta_apply(tb->source_stream, 3758251881Speter tb->string_stream, 3759251881Speter NULL, 3760251881Speter tb->path, 3761251881Speter tb->pool, 3762251881Speter &(tb->interpreter), 3763251881Speter &(tb->interpreter_baton)); 3764251881Speter 3765251881Speter return SVN_NO_ERROR; 3766251881Speter} 3767251881Speter 3768251881Speter 3769251881Speterstatic svn_error_t * 3770251881Speterbase_apply_textdelta(svn_txdelta_window_handler_t *contents_p, 3771251881Speter void **contents_baton_p, 3772251881Speter svn_fs_root_t *root, 3773251881Speter const char *path, 3774251881Speter svn_checksum_t *base_checksum, 3775251881Speter svn_checksum_t *result_checksum, 3776251881Speter apr_pool_t *pool) 3777251881Speter{ 3778251881Speter txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb)); 3779251881Speter 3780251881Speter tb->root = root; 3781251881Speter tb->path = path; 3782251881Speter tb->pool = pool; 3783251881Speter tb->base_checksum = svn_checksum_dup(base_checksum, pool); 3784251881Speter tb->result_checksum = svn_checksum_dup(result_checksum, pool); 3785251881Speter 3786251881Speter SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_apply_textdelta, tb, 3787251881Speter FALSE, pool)); 3788251881Speter 3789251881Speter *contents_p = window_consumer; 3790251881Speter *contents_baton_p = tb; 3791251881Speter return SVN_NO_ERROR; 3792251881Speter} 3793251881Speter 3794251881Speter/* --- End machinery for svn_fs_apply_textdelta() --- */ 3795251881Speter 3796251881Speter/* --- Machinery for svn_fs_apply_text() --- */ 3797251881Speter 3798251881Speter/* Baton for svn_fs_apply_text(). */ 3799251881Speterstruct text_baton_t 3800251881Speter{ 3801251881Speter /* The original file info */ 3802251881Speter svn_fs_root_t *root; 3803251881Speter const char *path; 3804251881Speter 3805251881Speter /* Derived from the file info */ 3806251881Speter dag_node_t *node; 3807251881Speter 3808251881Speter /* The returned stream that will accept the file's new contents. */ 3809251881Speter svn_stream_t *stream; 3810251881Speter 3811251881Speter /* The actual fs stream that the returned stream will write to. */ 3812251881Speter svn_stream_t *file_stream; 3813251881Speter 3814251881Speter /* Checksum for the final fulltext written to the file. May 3815251881Speter be null, in which case ignored. */ 3816251881Speter svn_checksum_t *result_checksum; 3817251881Speter 3818251881Speter /* Pool used by db txns */ 3819251881Speter apr_pool_t *pool; 3820251881Speter}; 3821251881Speter 3822251881Speter 3823251881Speter/* A trail-ready wrapper around svn_fs_base__dag_finalize_edits, but for 3824251881Speter * fulltext data, not text deltas. Closes BATON->file_stream. 3825251881Speter * 3826251881Speter * Note: If you're confused about how this function relates to another 3827251881Speter * of similar name, think of it this way: 3828251881Speter * 3829251881Speter * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits() 3830251881Speter * svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits() 3831251881Speter */ 3832251881Speterstatic svn_error_t * 3833251881Spetertxn_body_fulltext_finalize_edits(void *baton, trail_t *trail) 3834251881Speter{ 3835251881Speter struct text_baton_t *tb = baton; 3836251881Speter SVN_ERR(svn_fs_base__dag_finalize_edits(tb->node, 3837251881Speter tb->result_checksum, 3838251881Speter tb->root->txn, 3839251881Speter trail, trail->pool)); 3840251881Speter 3841251881Speter /* Make a record of this modification in the changes table. */ 3842251881Speter return add_change(tb->root->fs, tb->root->txn, tb->path, 3843251881Speter svn_fs_base__dag_get_id(tb->node), 3844251881Speter svn_fs_path_change_modify, TRUE, FALSE, trail, 3845251881Speter trail->pool); 3846251881Speter} 3847251881Speter 3848251881Speter/* Write function for the publically returned stream. */ 3849251881Speterstatic svn_error_t * 3850251881Spetertext_stream_writer(void *baton, 3851251881Speter const char *data, 3852251881Speter apr_size_t *len) 3853251881Speter{ 3854251881Speter struct text_baton_t *tb = baton; 3855251881Speter 3856251881Speter /* Psst, here's some data. Pass it on to the -real- file stream. */ 3857251881Speter return svn_stream_write(tb->file_stream, data, len); 3858251881Speter} 3859251881Speter 3860251881Speter/* Close function for the publically returned stream. */ 3861251881Speterstatic svn_error_t * 3862251881Spetertext_stream_closer(void *baton) 3863251881Speter{ 3864251881Speter struct text_baton_t *tb = baton; 3865251881Speter 3866251881Speter /* Close the internal-use stream. ### This used to be inside of 3867251881Speter txn_body_fulltext_finalize_edits(), but that invoked a nested 3868251881Speter Berkeley DB transaction -- scandalous! */ 3869251881Speter SVN_ERR(svn_stream_close(tb->file_stream)); 3870251881Speter 3871251881Speter /* Need to tell fs that we're done sending text */ 3872251881Speter return svn_fs_base__retry_txn(tb->root->fs, 3873251881Speter txn_body_fulltext_finalize_edits, tb, 3874251881Speter FALSE, tb->pool); 3875251881Speter} 3876251881Speter 3877251881Speter 3878251881Speterstatic svn_error_t * 3879251881Spetertxn_body_apply_text(void *baton, trail_t *trail) 3880251881Speter{ 3881251881Speter struct text_baton_t *tb = baton; 3882251881Speter parent_path_t *parent_path; 3883251881Speter const char *txn_id = tb->root->txn; 3884251881Speter 3885251881Speter /* Call open_path with no flags, as we want this to return an error 3886251881Speter if the node for which we are searching doesn't exist. */ 3887251881Speter SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, 3888251881Speter trail, trail->pool)); 3889251881Speter 3890251881Speter /* Check to see if path is locked; if so, check that we can use it. */ 3891251881Speter if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS) 3892251881Speter SVN_ERR(svn_fs_base__allow_locked_operation(tb->path, FALSE, 3893251881Speter trail, trail->pool)); 3894251881Speter 3895251881Speter /* Now, make sure this path is mutable. */ 3896251881Speter SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, 3897251881Speter trail, trail->pool)); 3898251881Speter tb->node = parent_path->node; 3899251881Speter 3900251881Speter /* Make a writable stream for replacing the file's text. */ 3901251881Speter SVN_ERR(svn_fs_base__dag_get_edit_stream(&(tb->file_stream), tb->node, 3902251881Speter txn_id, trail, tb->pool)); 3903251881Speter 3904251881Speter /* Create a 'returnable' stream which writes to the file_stream. */ 3905251881Speter tb->stream = svn_stream_create(tb, tb->pool); 3906251881Speter svn_stream_set_write(tb->stream, text_stream_writer); 3907251881Speter svn_stream_set_close(tb->stream, text_stream_closer); 3908251881Speter 3909251881Speter return SVN_NO_ERROR; 3910251881Speter} 3911251881Speter 3912251881Speter 3913251881Speterstatic svn_error_t * 3914251881Speterbase_apply_text(svn_stream_t **contents_p, 3915251881Speter svn_fs_root_t *root, 3916251881Speter const char *path, 3917251881Speter svn_checksum_t *result_checksum, 3918251881Speter apr_pool_t *pool) 3919251881Speter{ 3920251881Speter struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb)); 3921251881Speter 3922251881Speter tb->root = root; 3923251881Speter tb->path = path; 3924251881Speter tb->pool = pool; 3925251881Speter tb->result_checksum = svn_checksum_dup(result_checksum, pool); 3926251881Speter 3927251881Speter SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_apply_text, tb, 3928251881Speter FALSE, pool)); 3929251881Speter 3930251881Speter *contents_p = tb->stream; 3931251881Speter return SVN_NO_ERROR; 3932251881Speter} 3933251881Speter 3934251881Speter/* --- End machinery for svn_fs_apply_text() --- */ 3935251881Speter 3936251881Speter 3937251881Speter/* Note: we're sharing the `things_changed_args' struct with 3938251881Speter svn_fs_props_changed(). */ 3939251881Speter 3940251881Speterstatic svn_error_t * 3941251881Spetertxn_body_contents_changed(void *baton, trail_t *trail) 3942251881Speter{ 3943251881Speter struct things_changed_args *args = baton; 3944251881Speter dag_node_t *node1, *node2; 3945251881Speter 3946251881Speter SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool)); 3947251881Speter SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool)); 3948251881Speter return svn_fs_base__things_different(NULL, args->changed_p, 3949251881Speter node1, node2, trail, trail->pool); 3950251881Speter} 3951251881Speter 3952251881Speter 3953251881Speter/* Note: it is acceptable for this function to call back into 3954251881Speter top-level interfaces because it does not itself use trails. */ 3955251881Speterstatic svn_error_t * 3956251881Speterbase_contents_changed(svn_boolean_t *changed_p, 3957251881Speter svn_fs_root_t *root1, 3958251881Speter const char *path1, 3959251881Speter svn_fs_root_t *root2, 3960251881Speter const char *path2, 3961251881Speter apr_pool_t *pool) 3962251881Speter{ 3963251881Speter struct things_changed_args args; 3964251881Speter 3965251881Speter /* Check that roots are in the same fs. */ 3966251881Speter if (root1->fs != root2->fs) 3967251881Speter return svn_error_create 3968251881Speter (SVN_ERR_FS_GENERAL, NULL, 3969251881Speter _("Cannot compare file contents between two different filesystems")); 3970251881Speter 3971251881Speter /* Check that both paths are files. */ 3972251881Speter { 3973251881Speter svn_node_kind_t kind; 3974251881Speter 3975251881Speter SVN_ERR(base_check_path(&kind, root1, path1, pool)); 3976251881Speter if (kind != svn_node_file) 3977251881Speter return svn_error_createf 3978251881Speter (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path1); 3979251881Speter 3980251881Speter SVN_ERR(base_check_path(&kind, root2, path2, pool)); 3981251881Speter if (kind != svn_node_file) 3982251881Speter return svn_error_createf 3983251881Speter (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2); 3984251881Speter } 3985251881Speter 3986251881Speter args.root1 = root1; 3987251881Speter args.root2 = root2; 3988251881Speter args.path1 = path1; 3989251881Speter args.path2 = path2; 3990251881Speter args.changed_p = changed_p; 3991251881Speter args.pool = pool; 3992251881Speter 3993251881Speter return svn_fs_base__retry_txn(root1->fs, txn_body_contents_changed, &args, 3994251881Speter TRUE, pool); 3995251881Speter} 3996251881Speter 3997251881Speter 3998251881Speter 3999251881Speter/* Public interface to computing file text deltas. */ 4000251881Speter 4001251881Speter/* Note: it is acceptable for this function to call back into 4002251881Speter public FS API interfaces because it does not itself use trails. */ 4003251881Speterstatic svn_error_t * 4004251881Speterbase_get_file_delta_stream(svn_txdelta_stream_t **stream_p, 4005251881Speter svn_fs_root_t *source_root, 4006251881Speter const char *source_path, 4007251881Speter svn_fs_root_t *target_root, 4008251881Speter const char *target_path, 4009251881Speter apr_pool_t *pool) 4010251881Speter{ 4011251881Speter svn_stream_t *source, *target; 4012251881Speter svn_txdelta_stream_t *delta_stream; 4013251881Speter 4014251881Speter /* Get read functions for the source file contents. */ 4015251881Speter if (source_root && source_path) 4016251881Speter SVN_ERR(base_file_contents(&source, source_root, source_path, pool)); 4017251881Speter else 4018251881Speter source = svn_stream_empty(pool); 4019251881Speter 4020251881Speter /* Get read functions for the target file contents. */ 4021251881Speter SVN_ERR(base_file_contents(&target, target_root, target_path, pool)); 4022251881Speter 4023251881Speter /* Create a delta stream that turns the ancestor into the target. */ 4024251881Speter svn_txdelta2(&delta_stream, source, target, TRUE, pool); 4025251881Speter 4026251881Speter *stream_p = delta_stream; 4027251881Speter return SVN_NO_ERROR; 4028251881Speter} 4029251881Speter 4030251881Speter 4031251881Speter 4032251881Speter/* Finding Changes */ 4033251881Speter 4034251881Speterstruct paths_changed_args 4035251881Speter{ 4036251881Speter apr_hash_t *changes; 4037251881Speter svn_fs_root_t *root; 4038251881Speter}; 4039251881Speter 4040251881Speter 4041251881Speterstatic svn_error_t * 4042251881Spetertxn_body_paths_changed(void *baton, 4043251881Speter trail_t *trail) 4044251881Speter{ 4045251881Speter /* WARNING: This is called *without* the protection of a Berkeley DB 4046251881Speter transaction. If you modify this function, keep that in mind. */ 4047251881Speter 4048251881Speter struct paths_changed_args *args = baton; 4049251881Speter const char *txn_id; 4050251881Speter svn_fs_t *fs = args->root->fs; 4051251881Speter 4052251881Speter /* Get the transaction ID from ROOT. */ 4053251881Speter if (! args->root->is_txn_root) 4054251881Speter SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, args->root->rev, 4055251881Speter trail, trail->pool)); 4056251881Speter else 4057251881Speter txn_id = args->root->txn; 4058251881Speter 4059251881Speter return svn_fs_bdb__changes_fetch(&(args->changes), fs, txn_id, 4060251881Speter trail, trail->pool); 4061251881Speter} 4062251881Speter 4063251881Speter 4064251881Speterstatic svn_error_t * 4065251881Speterbase_paths_changed(apr_hash_t **changed_paths_p, 4066251881Speter svn_fs_root_t *root, 4067251881Speter apr_pool_t *pool) 4068251881Speter{ 4069251881Speter struct paths_changed_args args; 4070251881Speter args.root = root; 4071251881Speter args.changes = NULL; 4072251881Speter SVN_ERR(svn_fs_base__retry(root->fs, txn_body_paths_changed, &args, 4073251881Speter FALSE, pool)); 4074251881Speter *changed_paths_p = args.changes; 4075251881Speter return SVN_NO_ERROR; 4076251881Speter} 4077251881Speter 4078251881Speter 4079251881Speter 4080251881Speter/* Our coolio opaque history object. */ 4081251881Spetertypedef struct base_history_data_t 4082251881Speter{ 4083251881Speter /* filesystem object */ 4084251881Speter svn_fs_t *fs; 4085251881Speter 4086251881Speter /* path and revision of historical location */ 4087251881Speter const char *path; 4088251881Speter svn_revnum_t revision; 4089251881Speter 4090251881Speter /* internal-use hints about where to resume the history search. */ 4091251881Speter const char *path_hint; 4092251881Speter svn_revnum_t rev_hint; 4093251881Speter 4094251881Speter /* FALSE until the first call to svn_fs_history_prev(). */ 4095251881Speter svn_boolean_t is_interesting; 4096251881Speter} base_history_data_t; 4097251881Speter 4098251881Speter 4099251881Speterstatic svn_fs_history_t *assemble_history(svn_fs_t *fs, const char *path, 4100251881Speter svn_revnum_t revision, 4101251881Speter svn_boolean_t is_interesting, 4102251881Speter const char *path_hint, 4103251881Speter svn_revnum_t rev_hint, 4104251881Speter apr_pool_t *pool); 4105251881Speter 4106251881Speter 4107251881Speterstatic svn_error_t * 4108251881Speterbase_node_history(svn_fs_history_t **history_p, 4109251881Speter svn_fs_root_t *root, 4110251881Speter const char *path, 4111251881Speter apr_pool_t *pool) 4112251881Speter{ 4113251881Speter svn_node_kind_t kind; 4114251881Speter 4115251881Speter /* We require a revision root. */ 4116251881Speter if (root->is_txn_root) 4117251881Speter return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL); 4118251881Speter 4119251881Speter /* And we require that the path exist in the root. */ 4120251881Speter SVN_ERR(base_check_path(&kind, root, path, pool)); 4121251881Speter if (kind == svn_node_none) 4122251881Speter return SVN_FS__NOT_FOUND(root, path); 4123251881Speter 4124251881Speter /* Okay, all seems well. Build our history object and return it. */ 4125251881Speter *history_p = assemble_history(root->fs, 4126251881Speter svn_fs__canonicalize_abspath(path, pool), 4127251881Speter root->rev, FALSE, NULL, 4128251881Speter SVN_INVALID_REVNUM, pool); 4129251881Speter return SVN_NO_ERROR; 4130251881Speter} 4131251881Speter 4132251881Speter 4133251881Speter/* Examine the PARENT_PATH structure chain to determine how copy IDs 4134251881Speter would be doled out in the event that PARENT_PATH was made mutable. 4135251881Speter Return the ID of the copy that last affected PARENT_PATH (and the 4136251881Speter COPY itself, if we've already fetched it). 4137251881Speter*/ 4138251881Speterstatic svn_error_t * 4139251881Speterexamine_copy_inheritance(const char **copy_id, 4140251881Speter copy_t **copy, 4141251881Speter svn_fs_t *fs, 4142251881Speter parent_path_t *parent_path, 4143251881Speter trail_t *trail, 4144251881Speter apr_pool_t *pool) 4145251881Speter{ 4146251881Speter /* The default response -- our current copy ID, and no fetched COPY. */ 4147251881Speter *copy_id = svn_fs_base__id_copy_id 4148251881Speter (svn_fs_base__dag_get_id(parent_path->node)); 4149251881Speter *copy = NULL; 4150251881Speter 4151251881Speter /* If we have no parent (we are looking at the root node), or if 4152251881Speter this node is supposed to inherit from itself, return that fact. */ 4153251881Speter if (! parent_path->parent) 4154251881Speter return SVN_NO_ERROR; 4155251881Speter 4156251881Speter /* We could be a branch destination (which would answer our question 4157251881Speter altogether)! But then, again, we might just have been modified 4158251881Speter in this revision, so all bets are off. */ 4159251881Speter if (parent_path->copy_inherit == copy_id_inherit_self) 4160251881Speter { 4161251881Speter /* A copy ID of "0" means we've never been branched. Therefore, 4162251881Speter there are no copies relevant to our history. */ 4163251881Speter if (((*copy_id)[0] == '0') && ((*copy_id)[1] == '\0')) 4164251881Speter return SVN_NO_ERROR; 4165251881Speter 4166251881Speter /* Get the COPY record. If it was a real copy (not an implicit 4167251881Speter one), we have our answer. Otherwise, we fall through to the 4168251881Speter recursive case. */ 4169251881Speter SVN_ERR(svn_fs_bdb__get_copy(copy, fs, *copy_id, trail, pool)); 4170251881Speter if ((*copy)->kind != copy_kind_soft) 4171251881Speter return SVN_NO_ERROR; 4172251881Speter } 4173251881Speter 4174251881Speter /* Otherwise, our answer is dependent upon our parent. */ 4175251881Speter return examine_copy_inheritance(copy_id, copy, fs, 4176251881Speter parent_path->parent, trail, pool); 4177251881Speter} 4178251881Speter 4179251881Speter 4180251881Speterstruct history_prev_args 4181251881Speter{ 4182251881Speter svn_fs_history_t **prev_history_p; 4183251881Speter svn_fs_history_t *history; 4184251881Speter svn_boolean_t cross_copies; 4185251881Speter apr_pool_t *pool; 4186251881Speter}; 4187251881Speter 4188251881Speter 4189251881Speterstatic svn_error_t * 4190251881Spetertxn_body_history_prev(void *baton, trail_t *trail) 4191251881Speter{ 4192251881Speter struct history_prev_args *args = baton; 4193251881Speter svn_fs_history_t **prev_history = args->prev_history_p; 4194251881Speter svn_fs_history_t *history = args->history; 4195251881Speter base_history_data_t *bhd = history->fsap_data; 4196251881Speter const char *commit_path, *src_path, *path = bhd->path; 4197251881Speter svn_revnum_t commit_rev, src_rev, dst_rev, revision = bhd->revision; 4198251881Speter apr_pool_t *retpool = args->pool; 4199251881Speter svn_fs_t *fs = bhd->fs; 4200251881Speter parent_path_t *parent_path; 4201251881Speter dag_node_t *node; 4202251881Speter svn_fs_root_t *root; 4203251881Speter const svn_fs_id_t *node_id; 4204251881Speter const char *end_copy_id = NULL; 4205251881Speter struct revision_root_args rr_args; 4206251881Speter svn_boolean_t reported = bhd->is_interesting; 4207251881Speter const char *txn_id; 4208251881Speter copy_t *copy = NULL; 4209251881Speter svn_boolean_t retry = FALSE; 4210251881Speter 4211251881Speter /* Initialize our return value. */ 4212251881Speter *prev_history = NULL; 4213251881Speter 4214251881Speter /* If our last history report left us hints about where to pickup 4215251881Speter the chase, then our last report was on the destination of a 4216251881Speter copy. If we are crossing copies, start from those locations, 4217251881Speter otherwise, we're all done here. */ 4218251881Speter if (bhd->path_hint && SVN_IS_VALID_REVNUM(bhd->rev_hint)) 4219251881Speter { 4220251881Speter reported = FALSE; 4221251881Speter if (! args->cross_copies) 4222251881Speter return SVN_NO_ERROR; 4223251881Speter path = bhd->path_hint; 4224251881Speter revision = bhd->rev_hint; 4225251881Speter } 4226251881Speter 4227251881Speter /* Construct a ROOT for the current revision. */ 4228251881Speter rr_args.root_p = &root; 4229251881Speter rr_args.rev = revision; 4230251881Speter SVN_ERR(txn_body_revision_root(&rr_args, trail)); 4231251881Speter 4232251881Speter /* Open PATH/REVISION, and get its node and a bunch of other 4233251881Speter goodies. */ 4234251881Speter SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, revision, trail, 4235251881Speter trail->pool)); 4236251881Speter SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, 4237251881Speter trail, trail->pool)); 4238251881Speter node = parent_path->node; 4239251881Speter node_id = svn_fs_base__dag_get_id(node); 4240251881Speter commit_path = svn_fs_base__dag_get_created_path(node); 4241251881Speter SVN_ERR(svn_fs_base__dag_get_revision(&commit_rev, node, 4242251881Speter trail, trail->pool)); 4243251881Speter 4244251881Speter /* The Subversion filesystem is written in such a way that a given 4245251881Speter line of history may have at most one interesting history point 4246251881Speter per filesystem revision. Either that node was edited (and 4247251881Speter possibly copied), or it was copied but not edited. And a copy 4248251881Speter source cannot be from the same revision as its destination. So, 4249251881Speter if our history revision matches its node's commit revision, we 4250251881Speter know that ... */ 4251251881Speter if (revision == commit_rev) 4252251881Speter { 4253251881Speter if (! reported) 4254251881Speter { 4255251881Speter /* ... we either have not yet reported on this revision (and 4256251881Speter need now to do so) ... */ 4257251881Speter *prev_history = assemble_history(fs, 4258251881Speter apr_pstrdup(retpool, commit_path), 4259251881Speter commit_rev, TRUE, NULL, 4260251881Speter SVN_INVALID_REVNUM, retpool); 4261251881Speter return SVN_NO_ERROR; 4262251881Speter } 4263251881Speter else 4264251881Speter { 4265251881Speter /* ... or we *have* reported on this revision, and must now 4266251881Speter progress toward this node's predecessor (unless there is 4267251881Speter no predecessor, in which case we're all done!). */ 4268251881Speter const svn_fs_id_t *pred_id; 4269251881Speter 4270251881Speter SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id, node, 4271251881Speter trail, trail->pool)); 4272251881Speter if (! pred_id) 4273251881Speter return SVN_NO_ERROR; 4274251881Speter 4275251881Speter /* Replace NODE and friends with the information from its 4276251881Speter predecessor. */ 4277251881Speter SVN_ERR(svn_fs_base__dag_get_node(&node, fs, pred_id, 4278251881Speter trail, trail->pool)); 4279251881Speter node_id = svn_fs_base__dag_get_id(node); 4280251881Speter commit_path = svn_fs_base__dag_get_created_path(node); 4281251881Speter SVN_ERR(svn_fs_base__dag_get_revision(&commit_rev, node, 4282251881Speter trail, trail->pool)); 4283251881Speter } 4284251881Speter } 4285251881Speter 4286251881Speter /* Calculate a possibly relevant copy ID. */ 4287251881Speter SVN_ERR(examine_copy_inheritance(&end_copy_id, ©, fs, 4288251881Speter parent_path, trail, trail->pool)); 4289251881Speter 4290251881Speter /* Initialize some state variables. */ 4291251881Speter src_path = NULL; 4292251881Speter src_rev = SVN_INVALID_REVNUM; 4293251881Speter dst_rev = SVN_INVALID_REVNUM; 4294251881Speter 4295251881Speter /* If our current copy ID (which is either the real copy ID of our 4296251881Speter node, or the last copy ID which would affect our node if it were 4297251881Speter to be made mutable) diffs at all from that of its predecessor 4298251881Speter (which is either a real predecessor, or is the node itself 4299251881Speter playing the predecessor role to an imaginary mutable successor), 4300251881Speter then we need to report a copy. */ 4301251881Speter if (svn_fs_base__key_compare(svn_fs_base__id_copy_id(node_id), 4302251881Speter end_copy_id) != 0) 4303251881Speter { 4304251881Speter const char *remainder; 4305251881Speter dag_node_t *dst_node; 4306251881Speter const char *copy_dst; 4307251881Speter 4308251881Speter /* Get the COPY record if we haven't already fetched it. */ 4309251881Speter if (! copy) 4310251881Speter SVN_ERR(svn_fs_bdb__get_copy(©, fs, end_copy_id, trail, 4311251881Speter trail->pool)); 4312251881Speter 4313251881Speter /* Figure out the destination path of the copy operation. */ 4314251881Speter SVN_ERR(svn_fs_base__dag_get_node(&dst_node, fs, 4315251881Speter copy->dst_noderev_id, 4316251881Speter trail, trail->pool)); 4317251881Speter copy_dst = svn_fs_base__dag_get_created_path(dst_node); 4318251881Speter 4319251881Speter /* If our current path was the very destination of the copy, 4320251881Speter then our new current path will be the copy source. If our 4321251881Speter current path was instead the *child* of the destination of 4322251881Speter the copy, then figure out its previous location by taking its 4323251881Speter path relative to the copy destination and appending that to 4324251881Speter the copy source. Finally, if our current path doesn't meet 4325251881Speter one of these other criteria ... ### for now just fallback to 4326251881Speter the old copy hunt algorithm. */ 4327251881Speter remainder = svn_fspath__skip_ancestor(copy_dst, path); 4328251881Speter 4329251881Speter if (remainder) 4330251881Speter { 4331251881Speter /* If we get here, then our current path is the destination 4332251881Speter of, or the child of the destination of, a copy. Fill 4333251881Speter in the return values and get outta here. */ 4334251881Speter SVN_ERR(svn_fs_base__txn_get_revision 4335251881Speter (&src_rev, fs, copy->src_txn_id, trail, trail->pool)); 4336251881Speter SVN_ERR(svn_fs_base__txn_get_revision 4337251881Speter (&dst_rev, fs, 4338251881Speter svn_fs_base__id_txn_id(copy->dst_noderev_id), 4339251881Speter trail, trail->pool)); 4340251881Speter src_path = svn_fspath__join(copy->src_path, remainder, 4341251881Speter trail->pool); 4342251881Speter if (copy->kind == copy_kind_soft) 4343251881Speter retry = TRUE; 4344251881Speter } 4345251881Speter } 4346251881Speter 4347251881Speter /* If we calculated a copy source path and revision, and the 4348251881Speter copy source revision doesn't pre-date a revision in which we 4349251881Speter *know* our node was modified, we'll make a 'copy-style' history 4350251881Speter object. */ 4351251881Speter if (src_path && SVN_IS_VALID_REVNUM(src_rev) && (src_rev >= commit_rev)) 4352251881Speter { 4353251881Speter /* It's possible for us to find a copy location that is the same 4354251881Speter as the history point we've just reported. If that happens, 4355251881Speter we simply need to take another trip through this history 4356251881Speter search. */ 4357251881Speter if ((dst_rev == revision) && reported) 4358251881Speter retry = TRUE; 4359251881Speter 4360251881Speter *prev_history = assemble_history(fs, apr_pstrdup(retpool, path), 4361251881Speter dst_rev, ! retry, 4362251881Speter src_path, src_rev, retpool); 4363251881Speter } 4364251881Speter else 4365251881Speter { 4366251881Speter *prev_history = assemble_history(fs, apr_pstrdup(retpool, commit_path), 4367251881Speter commit_rev, TRUE, NULL, 4368251881Speter SVN_INVALID_REVNUM, retpool); 4369251881Speter } 4370251881Speter 4371251881Speter return SVN_NO_ERROR; 4372251881Speter} 4373251881Speter 4374251881Speter 4375251881Speterstatic svn_error_t * 4376251881Speterbase_history_prev(svn_fs_history_t **prev_history_p, 4377251881Speter svn_fs_history_t *history, 4378251881Speter svn_boolean_t cross_copies, 4379251881Speter apr_pool_t *pool) 4380251881Speter{ 4381251881Speter svn_fs_history_t *prev_history = NULL; 4382251881Speter base_history_data_t *bhd = history->fsap_data; 4383251881Speter svn_fs_t *fs = bhd->fs; 4384251881Speter 4385251881Speter /* Special case: the root directory changes in every single 4386251881Speter revision, no exceptions. And, the root can't be the target (or 4387251881Speter child of a target -- duh) of a copy. So, if that's our path, 4388251881Speter then we need only decrement our revision by 1, and there you go. */ 4389251881Speter if (strcmp(bhd->path, "/") == 0) 4390251881Speter { 4391251881Speter if (! bhd->is_interesting) 4392251881Speter prev_history = assemble_history(fs, "/", bhd->revision, 4393251881Speter 1, NULL, SVN_INVALID_REVNUM, pool); 4394251881Speter else if (bhd->revision > 0) 4395251881Speter prev_history = assemble_history(fs, "/", bhd->revision - 1, 4396251881Speter 1, NULL, SVN_INVALID_REVNUM, pool); 4397251881Speter } 4398251881Speter else 4399251881Speter { 4400251881Speter struct history_prev_args args; 4401251881Speter prev_history = history; 4402251881Speter 4403251881Speter while (1) 4404251881Speter { 4405251881Speter /* Get a trail, and get to work. */ 4406251881Speter 4407251881Speter args.prev_history_p = &prev_history; 4408251881Speter args.history = prev_history; 4409251881Speter args.cross_copies = cross_copies; 4410251881Speter args.pool = pool; 4411251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_history_prev, &args, 4412251881Speter FALSE, pool)); 4413251881Speter if (! prev_history) 4414251881Speter break; 4415251881Speter bhd = prev_history->fsap_data; 4416251881Speter if (bhd->is_interesting) 4417251881Speter break; 4418251881Speter } 4419251881Speter } 4420251881Speter 4421251881Speter *prev_history_p = prev_history; 4422251881Speter return SVN_NO_ERROR; 4423251881Speter} 4424251881Speter 4425251881Speter 4426251881Speterstatic svn_error_t * 4427251881Speterbase_history_location(const char **path, 4428251881Speter svn_revnum_t *revision, 4429251881Speter svn_fs_history_t *history, 4430251881Speter apr_pool_t *pool) 4431251881Speter{ 4432251881Speter base_history_data_t *bhd = history->fsap_data; 4433251881Speter 4434251881Speter *path = apr_pstrdup(pool, bhd->path); 4435251881Speter *revision = bhd->revision; 4436251881Speter return SVN_NO_ERROR; 4437251881Speter} 4438251881Speter 4439251881Speter 4440251881Speterstatic history_vtable_t history_vtable = { 4441251881Speter base_history_prev, 4442251881Speter base_history_location 4443251881Speter}; 4444251881Speter 4445251881Speter 4446251881Speter 4447251881Speterstruct closest_copy_args 4448251881Speter{ 4449251881Speter svn_fs_root_t **root_p; 4450251881Speter const char **path_p; 4451251881Speter svn_fs_root_t *root; 4452251881Speter const char *path; 4453251881Speter apr_pool_t *pool; 4454251881Speter}; 4455251881Speter 4456251881Speter 4457251881Speterstatic svn_error_t * 4458251881Spetertxn_body_closest_copy(void *baton, trail_t *trail) 4459251881Speter{ 4460251881Speter struct closest_copy_args *args = baton; 4461251881Speter svn_fs_root_t *root = args->root; 4462251881Speter const char *path = args->path; 4463251881Speter svn_fs_t *fs = root->fs; 4464251881Speter parent_path_t *parent_path; 4465251881Speter const svn_fs_id_t *node_id; 4466251881Speter const char *txn_id, *copy_id; 4467251881Speter copy_t *copy = NULL; 4468251881Speter svn_fs_root_t *copy_dst_root; 4469251881Speter dag_node_t *path_node_in_copy_dst, *copy_dst_node, *copy_dst_root_node; 4470251881Speter const char *copy_dst_path; 4471251881Speter svn_revnum_t copy_dst_rev, created_rev; 4472251881Speter svn_error_t *err; 4473251881Speter 4474251881Speter *(args->path_p) = NULL; 4475251881Speter *(args->root_p) = NULL; 4476251881Speter 4477251881Speter /* Get the transaction ID associated with our root. */ 4478251881Speter if (root->is_txn_root) 4479251881Speter txn_id = root->txn; 4480251881Speter else 4481251881Speter SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, root->rev, 4482251881Speter trail, trail->pool)); 4483251881Speter 4484251881Speter /* Open PATH in ROOT -- it must exist. */ 4485251881Speter SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, 4486251881Speter trail, trail->pool)); 4487251881Speter node_id = svn_fs_base__dag_get_id(parent_path->node); 4488251881Speter 4489251881Speter /* Now, examine the copy inheritance rules in play should our path 4490251881Speter be made mutable in the future (if it isn't already). This will 4491251881Speter tell us about the youngest affecting copy. */ 4492251881Speter SVN_ERR(examine_copy_inheritance(©_id, ©, fs, parent_path, 4493251881Speter trail, trail->pool)); 4494251881Speter 4495251881Speter /* Easy out: if the copy ID is 0, there's nothing of interest here. */ 4496251881Speter if (((copy_id)[0] == '0') && ((copy_id)[1] == '\0')) 4497251881Speter return SVN_NO_ERROR; 4498251881Speter 4499251881Speter /* Fetch our copy if examine_copy_inheritance() didn't do it for us. */ 4500251881Speter if (! copy) 4501251881Speter SVN_ERR(svn_fs_bdb__get_copy(©, fs, copy_id, trail, trail->pool)); 4502251881Speter 4503251881Speter /* Figure out the destination path and revision of the copy operation. */ 4504251881Speter SVN_ERR(svn_fs_base__dag_get_node(©_dst_node, fs, copy->dst_noderev_id, 4505251881Speter trail, trail->pool)); 4506251881Speter copy_dst_path = svn_fs_base__dag_get_created_path(copy_dst_node); 4507251881Speter SVN_ERR(svn_fs_base__dag_get_revision(©_dst_rev, copy_dst_node, 4508251881Speter trail, trail->pool)); 4509251881Speter 4510251881Speter /* Turn that revision into a revision root. */ 4511251881Speter SVN_ERR(svn_fs_base__dag_revision_root(©_dst_root_node, fs, 4512251881Speter copy_dst_rev, trail, args->pool)); 4513251881Speter copy_dst_root = make_revision_root(fs, copy_dst_rev, 4514251881Speter copy_dst_root_node, args->pool); 4515251881Speter 4516251881Speter /* It is possible that this node was created from scratch at some 4517251881Speter revision between COPY_DST_REV and the transaction associated with 4518251881Speter our ROOT. Make sure that PATH exists as of COPY_DST_REV and is 4519251881Speter related to this node-rev. */ 4520251881Speter err = get_dag(&path_node_in_copy_dst, copy_dst_root, path, 4521251881Speter trail, trail->pool); 4522251881Speter if (err) 4523251881Speter { 4524251881Speter if ((err->apr_err == SVN_ERR_FS_NOT_FOUND) 4525251881Speter || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY)) 4526251881Speter { 4527251881Speter svn_error_clear(err); 4528251881Speter return SVN_NO_ERROR; 4529251881Speter } 4530251881Speter return svn_error_trace(err); 4531251881Speter } 4532251881Speter if ((svn_fs_base__dag_node_kind(path_node_in_copy_dst) == svn_node_none) 4533251881Speter || (! (svn_fs_base__id_check_related 4534251881Speter (node_id, svn_fs_base__dag_get_id(path_node_in_copy_dst))))) 4535251881Speter { 4536251881Speter return SVN_NO_ERROR; 4537251881Speter } 4538251881Speter 4539251881Speter /* One final check must be done here. If you copy a directory and 4540251881Speter create a new entity somewhere beneath that directory in the same 4541251881Speter txn, then we can't claim that the copy affected the new entity. 4542251881Speter For example, if you do: 4543251881Speter 4544251881Speter copy dir1 dir2 4545251881Speter create dir2/new-thing 4546251881Speter commit 4547251881Speter 4548251881Speter then dir2/new-thing was not affected by the copy of dir1 to dir2. 4549251881Speter We detect this situation by asking if PATH@COPY_DST_REV's 4550251881Speter created-rev is COPY_DST_REV, and that node-revision has no 4551251881Speter predecessors, then there is no relevant closest copy. 4552251881Speter */ 4553251881Speter SVN_ERR(svn_fs_base__dag_get_revision(&created_rev, path_node_in_copy_dst, 4554251881Speter trail, trail->pool)); 4555251881Speter if (created_rev == copy_dst_rev) 4556251881Speter { 4557251881Speter const svn_fs_id_t *pred_id; 4558251881Speter SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id, 4559251881Speter path_node_in_copy_dst, 4560251881Speter trail, trail->pool)); 4561251881Speter if (! pred_id) 4562251881Speter return SVN_NO_ERROR; 4563251881Speter } 4564251881Speter 4565251881Speter *(args->path_p) = apr_pstrdup(args->pool, copy_dst_path); 4566251881Speter *(args->root_p) = copy_dst_root; 4567251881Speter 4568251881Speter return SVN_NO_ERROR; 4569251881Speter} 4570251881Speter 4571251881Speter 4572251881Speterstatic svn_error_t * 4573251881Speterbase_closest_copy(svn_fs_root_t **root_p, 4574251881Speter const char **path_p, 4575251881Speter svn_fs_root_t *root, 4576251881Speter const char *path, 4577251881Speter apr_pool_t *pool) 4578251881Speter{ 4579251881Speter struct closest_copy_args args; 4580251881Speter svn_fs_t *fs = root->fs; 4581251881Speter svn_fs_root_t *closest_root = NULL; 4582251881Speter const char *closest_path = NULL; 4583251881Speter 4584251881Speter args.root_p = &closest_root; 4585251881Speter args.path_p = &closest_path; 4586251881Speter args.root = root; 4587251881Speter args.path = path; 4588251881Speter args.pool = pool; 4589251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_closest_copy, &args, 4590251881Speter FALSE, pool)); 4591251881Speter *root_p = closest_root; 4592251881Speter *path_p = closest_path; 4593251881Speter return SVN_NO_ERROR; 4594251881Speter} 4595251881Speter 4596251881Speter 4597251881Speter/* Return a new history object (marked as "interesting") for PATH and 4598251881Speter REVISION, allocated in POOL, and with its members set to the values 4599251881Speter of the parameters provided. Note that PATH and PATH_HINT are not 4600251881Speter duped into POOL -- it is the responsibility of the caller to ensure 4601251881Speter that this happens. */ 4602251881Speterstatic svn_fs_history_t * 4603251881Speterassemble_history(svn_fs_t *fs, 4604251881Speter const char *path, 4605251881Speter svn_revnum_t revision, 4606251881Speter svn_boolean_t is_interesting, 4607251881Speter const char *path_hint, 4608251881Speter svn_revnum_t rev_hint, 4609251881Speter apr_pool_t *pool) 4610251881Speter{ 4611251881Speter svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history)); 4612251881Speter base_history_data_t *bhd = apr_pcalloc(pool, sizeof(*bhd)); 4613251881Speter bhd->path = path; 4614251881Speter bhd->revision = revision; 4615251881Speter bhd->is_interesting = is_interesting; 4616251881Speter bhd->path_hint = path_hint; 4617251881Speter bhd->rev_hint = rev_hint; 4618251881Speter bhd->fs = fs; 4619251881Speter history->vtable = &history_vtable; 4620251881Speter history->fsap_data = bhd; 4621251881Speter return history; 4622251881Speter} 4623251881Speter 4624251881Speter 4625251881Spetersvn_error_t * 4626251881Spetersvn_fs_base__get_path_kind(svn_node_kind_t *kind, 4627251881Speter const char *path, 4628251881Speter trail_t *trail, 4629251881Speter apr_pool_t *pool) 4630251881Speter{ 4631251881Speter svn_revnum_t head_rev; 4632251881Speter svn_fs_root_t *root; 4633251881Speter dag_node_t *root_dir, *path_node; 4634251881Speter svn_error_t *err; 4635251881Speter 4636251881Speter /* Get HEAD revision, */ 4637251881Speter SVN_ERR(svn_fs_bdb__youngest_rev(&head_rev, trail->fs, trail, pool)); 4638251881Speter 4639251881Speter /* Then convert it into a root_t, */ 4640251881Speter SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, head_rev, 4641251881Speter trail, pool)); 4642251881Speter root = make_revision_root(trail->fs, head_rev, root_dir, pool); 4643251881Speter 4644251881Speter /* And get the dag_node for path in the root_t. */ 4645251881Speter err = get_dag(&path_node, root, path, trail, pool); 4646251881Speter if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND)) 4647251881Speter { 4648251881Speter svn_error_clear(err); 4649251881Speter *kind = svn_node_none; 4650251881Speter return SVN_NO_ERROR; 4651251881Speter } 4652251881Speter else if (err) 4653251881Speter return svn_error_trace(err); 4654251881Speter 4655251881Speter *kind = svn_fs_base__dag_node_kind(path_node); 4656251881Speter return SVN_NO_ERROR; 4657251881Speter} 4658251881Speter 4659251881Speter 4660251881Spetersvn_error_t * 4661251881Spetersvn_fs_base__get_path_created_rev(svn_revnum_t *rev, 4662251881Speter const char *path, 4663251881Speter trail_t *trail, 4664251881Speter apr_pool_t *pool) 4665251881Speter{ 4666251881Speter svn_revnum_t head_rev, created_rev; 4667251881Speter svn_fs_root_t *root; 4668251881Speter dag_node_t *root_dir, *path_node; 4669251881Speter svn_error_t *err; 4670251881Speter 4671251881Speter /* Get HEAD revision, */ 4672251881Speter SVN_ERR(svn_fs_bdb__youngest_rev(&head_rev, trail->fs, trail, pool)); 4673251881Speter 4674251881Speter /* Then convert it into a root_t, */ 4675251881Speter SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, head_rev, 4676251881Speter trail, pool)); 4677251881Speter root = make_revision_root(trail->fs, head_rev, root_dir, pool); 4678251881Speter 4679251881Speter /* And get the dag_node for path in the root_t. */ 4680251881Speter err = get_dag(&path_node, root, path, trail, pool); 4681251881Speter if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND)) 4682251881Speter { 4683251881Speter svn_error_clear(err); 4684251881Speter *rev = SVN_INVALID_REVNUM; 4685251881Speter return SVN_NO_ERROR; 4686251881Speter } 4687251881Speter else if (err) 4688251881Speter return svn_error_trace(err); 4689251881Speter 4690251881Speter /* Find the created_rev of the dag_node. */ 4691251881Speter SVN_ERR(svn_fs_base__dag_get_revision(&created_rev, path_node, 4692251881Speter trail, pool)); 4693251881Speter 4694251881Speter *rev = created_rev; 4695251881Speter return SVN_NO_ERROR; 4696251881Speter} 4697251881Speter 4698251881Speter 4699251881Speter 4700251881Speter/*** Finding the Origin of a Line of History ***/ 4701251881Speter 4702251881Speter/* Set *PREV_PATH and *PREV_REV to the path and revision which 4703251881Speter represent the location at which PATH in FS was located immediately 4704251881Speter prior to REVISION iff there was a copy operation (to PATH or one of 4705251881Speter its parent directories) between that previous location and 4706251881Speter PATH@REVISION. 4707251881Speter 4708251881Speter If there was no such copy operation in that portion of PATH's 4709251881Speter history, set *PREV_PATH to NULL and *PREV_REV to SVN_INVALID_REVNUM. 4710251881Speter 4711251881Speter WARNING: Do *not* call this from inside a trail. */ 4712251881Speterstatic svn_error_t * 4713251881Speterprev_location(const char **prev_path, 4714251881Speter svn_revnum_t *prev_rev, 4715251881Speter svn_fs_t *fs, 4716251881Speter svn_fs_root_t *root, 4717251881Speter const char *path, 4718251881Speter apr_pool_t *pool) 4719251881Speter{ 4720251881Speter const char *copy_path, *copy_src_path, *remainder; 4721251881Speter svn_fs_root_t *copy_root; 4722251881Speter svn_revnum_t copy_src_rev; 4723251881Speter 4724251881Speter /* Ask about the most recent copy which affected PATH@REVISION. If 4725251881Speter there was no such copy, we're done. */ 4726251881Speter SVN_ERR(base_closest_copy(©_root, ©_path, root, path, pool)); 4727251881Speter if (! copy_root) 4728251881Speter { 4729251881Speter *prev_rev = SVN_INVALID_REVNUM; 4730251881Speter *prev_path = NULL; 4731251881Speter return SVN_NO_ERROR; 4732251881Speter } 4733251881Speter 4734251881Speter /* Ultimately, it's not the path of the closest copy's source that 4735251881Speter we care about -- it's our own path's location in the copy source 4736251881Speter revision. So we'll tack the relative path that expresses the 4737251881Speter difference between the copy destination and our path in the copy 4738251881Speter revision onto the copy source path to determine this information. 4739251881Speter 4740251881Speter In other words, if our path is "/branches/my-branch/foo/bar", and 4741251881Speter we know that the closest relevant copy was a copy of "/trunk" to 4742251881Speter "/branches/my-branch", then that relative path under the copy 4743251881Speter destination is "/foo/bar". Tacking that onto the copy source 4744251881Speter path tells us that our path was located at "/trunk/foo/bar" 4745251881Speter before the copy. 4746251881Speter */ 4747251881Speter SVN_ERR(base_copied_from(©_src_rev, ©_src_path, 4748251881Speter copy_root, copy_path, pool)); 4749251881Speter remainder = svn_fspath__skip_ancestor(copy_path, path); 4750251881Speter *prev_path = svn_fspath__join(copy_src_path, remainder, pool); 4751251881Speter *prev_rev = copy_src_rev; 4752251881Speter return SVN_NO_ERROR; 4753251881Speter} 4754251881Speter 4755251881Speter 4756251881Speterstruct id_created_rev_args { 4757251881Speter svn_revnum_t revision; 4758251881Speter const svn_fs_id_t *id; 4759251881Speter const char *path; 4760251881Speter}; 4761251881Speter 4762251881Speter 4763251881Speterstatic svn_error_t * 4764251881Spetertxn_body_id_created_rev(void *baton, trail_t *trail) 4765251881Speter{ 4766251881Speter struct id_created_rev_args *args = baton; 4767251881Speter dag_node_t *node; 4768251881Speter 4769251881Speter SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id, 4770251881Speter trail, trail->pool)); 4771251881Speter return svn_fs_base__dag_get_revision(&(args->revision), node, 4772251881Speter trail, trail->pool); 4773251881Speter} 4774251881Speter 4775251881Speter 4776251881Speterstruct get_set_node_origin_args { 4777251881Speter const svn_fs_id_t *origin_id; 4778251881Speter const char *node_id; 4779251881Speter}; 4780251881Speter 4781251881Speter 4782251881Speterstatic svn_error_t * 4783251881Spetertxn_body_get_node_origin(void *baton, trail_t *trail) 4784251881Speter{ 4785251881Speter struct get_set_node_origin_args *args = baton; 4786251881Speter return svn_fs_bdb__get_node_origin(&(args->origin_id), trail->fs, 4787251881Speter args->node_id, trail, trail->pool); 4788251881Speter} 4789251881Speter 4790251881Speterstatic svn_error_t * 4791251881Spetertxn_body_set_node_origin(void *baton, trail_t *trail) 4792251881Speter{ 4793251881Speter struct get_set_node_origin_args *args = baton; 4794251881Speter return svn_fs_bdb__set_node_origin(trail->fs, args->node_id, 4795251881Speter args->origin_id, trail, trail->pool); 4796251881Speter} 4797251881Speter 4798251881Speterstatic svn_error_t * 4799251881Speterbase_node_origin_rev(svn_revnum_t *revision, 4800251881Speter svn_fs_root_t *root, 4801251881Speter const char *path, 4802251881Speter apr_pool_t *pool) 4803251881Speter{ 4804251881Speter svn_fs_t *fs = root->fs; 4805251881Speter base_fs_data_t *bfd = fs->fsap_data; 4806251881Speter struct get_set_node_origin_args args; 4807251881Speter const svn_fs_id_t *origin_id = NULL; 4808251881Speter struct id_created_rev_args icr_args; 4809251881Speter 4810251881Speter /* Canonicalize the input path so that the path-math that 4811251881Speter prev_location() does below will work. */ 4812251881Speter path = svn_fs__canonicalize_abspath(path, pool); 4813251881Speter 4814253734Speter /* Special-case the root node (for performance reasons) */ 4815253734Speter if (strcmp(path, "/") == 0) 4816253734Speter { 4817253734Speter *revision = 0; 4818253734Speter return SVN_NO_ERROR; 4819253734Speter } 4820253734Speter 4821251881Speter /* If we have support for the node-origins table, we'll try to use 4822251881Speter it. */ 4823251881Speter if (bfd->format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT) 4824251881Speter { 4825251881Speter const svn_fs_id_t *id; 4826251881Speter svn_error_t *err; 4827251881Speter 4828251881Speter SVN_ERR(base_node_id(&id, root, path, pool)); 4829251881Speter args.node_id = svn_fs_base__id_node_id(id); 4830251881Speter err = svn_fs_base__retry_txn(root->fs, txn_body_get_node_origin, &args, 4831251881Speter FALSE, pool); 4832251881Speter 4833251881Speter /* If we got a value for the origin node-revision-ID, that's 4834251881Speter great. If we didn't, that's sad but non-fatal -- we'll just 4835251881Speter figure it out the hard way, then record it so we don't have 4836251881Speter suffer again the next time. */ 4837251881Speter if (! err) 4838251881Speter { 4839251881Speter origin_id = args.origin_id; 4840251881Speter } 4841251881Speter else if (err->apr_err == SVN_ERR_FS_NO_SUCH_NODE_ORIGIN) 4842251881Speter { 4843251881Speter svn_error_clear(err); 4844251881Speter err = SVN_NO_ERROR; 4845251881Speter } 4846251881Speter SVN_ERR(err); 4847251881Speter } 4848251881Speter 4849251881Speter /* If we haven't yet found a node origin ID, we'll go spelunking for one. */ 4850251881Speter if (! origin_id) 4851251881Speter { 4852251881Speter svn_fs_root_t *curroot = root; 4853251881Speter apr_pool_t *subpool = svn_pool_create(pool); 4854251881Speter apr_pool_t *predidpool = svn_pool_create(pool); 4855251881Speter svn_stringbuf_t *lastpath = 4856251881Speter svn_stringbuf_create(path, pool); 4857251881Speter svn_revnum_t lastrev = SVN_INVALID_REVNUM; 4858251881Speter const svn_fs_id_t *pred_id; 4859251881Speter 4860251881Speter /* Walk the closest-copy chain back to the first copy in our history. 4861251881Speter 4862251881Speter NOTE: We merely *assume* that this is faster than walking the 4863251881Speter predecessor chain, because we *assume* that copies of parent 4864251881Speter directories happen less often than modifications to a given item. */ 4865251881Speter while (1) 4866251881Speter { 4867251881Speter svn_revnum_t currev; 4868251881Speter const char *curpath = lastpath->data; 4869251881Speter 4870251881Speter /* Get a root pointing to LASTREV. (The first time around, 4871251881Speter LASTREV is invalid, but that's cool because CURROOT is 4872251881Speter already initialized.) */ 4873251881Speter if (SVN_IS_VALID_REVNUM(lastrev)) 4874251881Speter SVN_ERR(svn_fs_base__revision_root(&curroot, fs, 4875251881Speter lastrev, subpool)); 4876251881Speter 4877251881Speter /* Find the previous location using the closest-copy shortcut. */ 4878251881Speter SVN_ERR(prev_location(&curpath, &currev, fs, curroot, 4879251881Speter curpath, subpool)); 4880251881Speter if (! curpath) 4881251881Speter break; 4882251881Speter 4883251881Speter /* Update our LASTPATH and LASTREV variables (which survive 4884251881Speter SUBPOOL). */ 4885251881Speter svn_stringbuf_set(lastpath, curpath); 4886251881Speter lastrev = currev; 4887251881Speter } 4888251881Speter 4889251881Speter /* Walk the predecessor links back to origin. */ 4890251881Speter SVN_ERR(base_node_id(&pred_id, curroot, lastpath->data, pool)); 4891251881Speter while (1) 4892251881Speter { 4893251881Speter struct txn_pred_id_args pid_args; 4894251881Speter svn_pool_clear(subpool); 4895251881Speter pid_args.id = pred_id; 4896251881Speter pid_args.pred_id = NULL; 4897251881Speter pid_args.pool = subpool; 4898251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id, &pid_args, 4899251881Speter FALSE, subpool)); 4900251881Speter if (! pid_args.pred_id) 4901251881Speter break; 4902251881Speter svn_pool_clear(predidpool); 4903251881Speter pred_id = svn_fs_base__id_copy(pid_args.pred_id, predidpool); 4904251881Speter } 4905251881Speter 4906251881Speter /* Okay. PRED_ID should hold our origin ID now. */ 4907251881Speter origin_id = svn_fs_base__id_copy(pred_id, pool); 4908251881Speter 4909251881Speter /* If our filesystem version supports it, let's remember this 4910251881Speter value from now on. */ 4911251881Speter if (bfd->format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT) 4912251881Speter { 4913251881Speter args.origin_id = origin_id; 4914251881Speter SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_set_node_origin, 4915251881Speter &args, TRUE, subpool)); 4916251881Speter } 4917251881Speter 4918251881Speter svn_pool_destroy(predidpool); 4919251881Speter svn_pool_destroy(subpool); 4920251881Speter } 4921251881Speter 4922251881Speter /* Okay. We have an origin node-revision-ID. Let's get a created 4923251881Speter revision from it. */ 4924251881Speter icr_args.id = origin_id; 4925251881Speter SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_id_created_rev, &icr_args, 4926251881Speter TRUE, pool)); 4927251881Speter *revision = icr_args.revision; 4928251881Speter return SVN_NO_ERROR; 4929251881Speter} 4930251881Speter 4931251881Speter 4932251881Speter 4933251881Speter/* Mergeinfo Queries */ 4934251881Speter 4935251881Speter 4936251881Speter/* Examine directory NODE's immediately children for mergeinfo. 4937251881Speter 4938251881Speter For those which have explicit mergeinfo, add their mergeinfo to 4939251881Speter RESULT_CATALOG (allocated in RESULT_CATALOG's pool). 4940251881Speter 4941251881Speter For those which don't, but sit atop trees which contain mergeinfo 4942251881Speter somewhere deeper, add them to *CHILDREN_ATOP_MERGEINFO_TREES, a 4943251881Speter hash mapping dirent names to dag_node_t * objects, allocated 4944251881Speter from that hash's pool. 4945251881Speter 4946251881Speter For those which neither have explicit mergeinfo nor sit atop trees 4947251881Speter which contain mergeinfo, ignore them. 4948251881Speter 4949251881Speter Use TRAIL->pool for temporary allocations. */ 4950251881Speter 4951251881Speterstruct get_mergeinfo_data_and_entries_baton 4952251881Speter{ 4953251881Speter svn_mergeinfo_catalog_t result_catalog; 4954251881Speter apr_hash_t *children_atop_mergeinfo_trees; 4955251881Speter dag_node_t *node; 4956251881Speter const char *node_path; 4957251881Speter}; 4958251881Speter 4959251881Speterstatic svn_error_t * 4960251881Spetertxn_body_get_mergeinfo_data_and_entries(void *baton, trail_t *trail) 4961251881Speter{ 4962251881Speter struct get_mergeinfo_data_and_entries_baton *args = baton; 4963251881Speter dag_node_t *node = args->node; 4964251881Speter apr_hash_t *entries; 4965251881Speter apr_hash_index_t *hi; 4966251881Speter apr_pool_t *iterpool = svn_pool_create(trail->pool); 4967251881Speter apr_pool_t *result_pool = apr_hash_pool_get(args->result_catalog); 4968251881Speter apr_pool_t *children_pool = 4969251881Speter apr_hash_pool_get(args->children_atop_mergeinfo_trees); 4970251881Speter 4971251881Speter SVN_ERR_ASSERT(svn_fs_base__dag_node_kind(node) == svn_node_dir); 4972251881Speter 4973251881Speter SVN_ERR(svn_fs_base__dag_dir_entries(&entries, node, trail, trail->pool)); 4974251881Speter for (hi = apr_hash_first(trail->pool, entries); hi; hi = apr_hash_next(hi)) 4975251881Speter { 4976251881Speter void *val; 4977251881Speter svn_fs_dirent_t *dirent; 4978251881Speter const svn_fs_id_t *child_id; 4979251881Speter dag_node_t *child_node; 4980251881Speter svn_boolean_t has_mergeinfo; 4981251881Speter apr_int64_t kid_count; 4982251881Speter 4983251881Speter svn_pool_clear(iterpool); 4984251881Speter apr_hash_this(hi, NULL, NULL, &val); 4985251881Speter dirent = val; 4986251881Speter child_id = dirent->id; 4987251881Speter 4988251881Speter /* Get the node for this child. */ 4989251881Speter SVN_ERR(svn_fs_base__dag_get_node(&child_node, trail->fs, child_id, 4990251881Speter trail, iterpool)); 4991251881Speter 4992251881Speter /* Query the child node's mergeinfo stats. */ 4993251881Speter SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(&has_mergeinfo, &kid_count, 4994251881Speter child_node, trail, 4995251881Speter iterpool)); 4996251881Speter 4997251881Speter /* If the child has mergeinfo, add it to the result catalog. */ 4998251881Speter if (has_mergeinfo) 4999251881Speter { 5000251881Speter apr_hash_t *plist; 5001251881Speter svn_mergeinfo_t child_mergeinfo; 5002251881Speter svn_string_t *pval; 5003251881Speter svn_error_t *err; 5004251881Speter 5005251881Speter SVN_ERR(svn_fs_base__dag_get_proplist(&plist, child_node, 5006251881Speter trail, iterpool)); 5007251881Speter pval = svn_hash_gets(plist, SVN_PROP_MERGEINFO); 5008251881Speter if (! pval) 5009251881Speter { 5010251881Speter svn_string_t *id_str = svn_fs_base__id_unparse(child_id, 5011251881Speter iterpool); 5012251881Speter return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 5013251881Speter _("Node-revision '%s' claims to have " 5014251881Speter "mergeinfo but doesn't"), 5015251881Speter id_str->data); 5016251881Speter } 5017251881Speter /* Issue #3896: If syntactically invalid mergeinfo is present on 5018251881Speter CHILD_NODE then treat it as if no mergeinfo is present rather 5019251881Speter than raising a parse error. */ 5020251881Speter err = svn_mergeinfo_parse(&child_mergeinfo, pval->data, 5021251881Speter result_pool); 5022251881Speter if (err) 5023251881Speter { 5024251881Speter if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR) 5025251881Speter svn_error_clear(err); 5026251881Speter else 5027251881Speter return svn_error_trace(err); 5028251881Speter } 5029251881Speter else 5030251881Speter { 5031251881Speter svn_hash_sets(args->result_catalog, 5032251881Speter svn_fspath__join(args->node_path, dirent->name, 5033251881Speter result_pool), 5034251881Speter child_mergeinfo); 5035251881Speter } 5036251881Speter } 5037251881Speter 5038251881Speter /* If the child has descendants with mergeinfo -- that is, if 5039251881Speter the count of descendants beneath it carrying mergeinfo, not 5040251881Speter including itself, is non-zero -- then add it to the 5041251881Speter children_atop_mergeinfo_trees hash to be crawled later. */ 5042251881Speter if ((kid_count - (has_mergeinfo ? 1 : 0)) > 0) 5043251881Speter { 5044251881Speter if (svn_fs_base__dag_node_kind(child_node) != svn_node_dir) 5045251881Speter { 5046251881Speter svn_string_t *id_str = svn_fs_base__id_unparse(child_id, 5047251881Speter iterpool); 5048251881Speter return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 5049251881Speter _("Node-revision '%s' claims to sit " 5050251881Speter "atop a tree containing mergeinfo " 5051251881Speter "but is not a directory"), 5052251881Speter id_str->data); 5053251881Speter } 5054251881Speter svn_hash_sets(args->children_atop_mergeinfo_trees, 5055251881Speter apr_pstrdup(children_pool, dirent->name), 5056251881Speter svn_fs_base__dag_dup(child_node, children_pool)); 5057251881Speter } 5058251881Speter } 5059251881Speter 5060251881Speter svn_pool_destroy(iterpool); 5061251881Speter return SVN_NO_ERROR; 5062251881Speter} 5063251881Speter 5064251881Speterstatic svn_error_t * 5065251881Spetercrawl_directory_for_mergeinfo(svn_fs_t *fs, 5066251881Speter dag_node_t *node, 5067251881Speter const char *node_path, 5068251881Speter svn_mergeinfo_catalog_t result_catalog, 5069251881Speter apr_pool_t *pool) 5070251881Speter{ 5071251881Speter struct get_mergeinfo_data_and_entries_baton gmdae_args; 5072251881Speter apr_hash_t *children_atop_mergeinfo_trees = apr_hash_make(pool); 5073251881Speter apr_hash_index_t *hi; 5074251881Speter apr_pool_t *iterpool; 5075251881Speter 5076251881Speter /* Add mergeinfo for immediate children that have it, and fetch 5077251881Speter immediate children that *don't* have it but sit atop trees that do. */ 5078251881Speter gmdae_args.result_catalog = result_catalog; 5079251881Speter gmdae_args.children_atop_mergeinfo_trees = children_atop_mergeinfo_trees; 5080251881Speter gmdae_args.node = node; 5081251881Speter gmdae_args.node_path = node_path; 5082251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_mergeinfo_data_and_entries, 5083251881Speter &gmdae_args, FALSE, pool)); 5084251881Speter 5085251881Speter /* If no children sit atop trees with mergeinfo, we're done. 5086251881Speter Otherwise, recurse on those children. */ 5087251881Speter 5088251881Speter if (apr_hash_count(children_atop_mergeinfo_trees) == 0) 5089251881Speter return SVN_NO_ERROR; 5090251881Speter 5091251881Speter iterpool = svn_pool_create(pool); 5092251881Speter for (hi = apr_hash_first(pool, children_atop_mergeinfo_trees); 5093251881Speter hi; 5094251881Speter hi = apr_hash_next(hi)) 5095251881Speter { 5096251881Speter const void *key; 5097251881Speter void *val; 5098251881Speter svn_pool_clear(iterpool); 5099251881Speter apr_hash_this(hi, &key, NULL, &val); 5100251881Speter SVN_ERR(crawl_directory_for_mergeinfo(fs, val, 5101251881Speter svn_fspath__join(node_path, key, 5102251881Speter iterpool), 5103251881Speter result_catalog, iterpool)); 5104251881Speter } 5105251881Speter svn_pool_destroy(iterpool); 5106251881Speter return SVN_NO_ERROR; 5107251881Speter} 5108251881Speter 5109251881Speter 5110251881Speter/* Calculate the mergeinfo for PATH under revision ROOT using 5111251881Speter inheritance type INHERIT. Set *MERGEINFO to the mergeinfo, or to 5112251881Speter NULL if there is none. Results are allocated in POOL; TRAIL->pool 5113251881Speter is used for temporary allocations. */ 5114251881Speter 5115251881Speterstruct get_mergeinfo_for_path_baton 5116251881Speter{ 5117251881Speter svn_mergeinfo_t *mergeinfo; 5118251881Speter svn_fs_root_t *root; 5119251881Speter const char *path; 5120251881Speter svn_mergeinfo_inheritance_t inherit; 5121251881Speter svn_boolean_t adjust_inherited_mergeinfo; 5122251881Speter apr_pool_t *pool; 5123251881Speter}; 5124251881Speter 5125251881Speterstatic svn_error_t * 5126251881Spetertxn_body_get_mergeinfo_for_path(void *baton, trail_t *trail) 5127251881Speter{ 5128251881Speter struct get_mergeinfo_for_path_baton *args = baton; 5129251881Speter parent_path_t *parent_path, *nearest_ancestor; 5130251881Speter apr_hash_t *proplist; 5131251881Speter svn_string_t *mergeinfo_string; 5132251881Speter apr_pool_t *iterpool; 5133251881Speter dag_node_t *node = NULL; 5134251881Speter 5135251881Speter *(args->mergeinfo) = NULL; 5136251881Speter 5137251881Speter SVN_ERR(open_path(&parent_path, args->root, args->path, 0, 5138251881Speter NULL, trail, trail->pool)); 5139251881Speter 5140251881Speter /* Init the nearest ancestor. */ 5141251881Speter nearest_ancestor = parent_path; 5142251881Speter if (args->inherit == svn_mergeinfo_nearest_ancestor) 5143251881Speter { 5144251881Speter if (! parent_path->parent) 5145251881Speter return SVN_NO_ERROR; 5146251881Speter nearest_ancestor = parent_path->parent; 5147251881Speter } 5148251881Speter 5149251881Speter iterpool = svn_pool_create(trail->pool); 5150251881Speter while (TRUE) 5151251881Speter { 5152251881Speter svn_boolean_t has_mergeinfo; 5153251881Speter apr_int64_t count; 5154251881Speter 5155251881Speter svn_pool_clear(iterpool); 5156251881Speter 5157251881Speter node = nearest_ancestor->node; 5158251881Speter SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(&has_mergeinfo, &count, 5159251881Speter node, trail, iterpool)); 5160251881Speter if (has_mergeinfo) 5161251881Speter break; 5162251881Speter 5163251881Speter /* No need to loop if we're looking for explicit mergeinfo. */ 5164251881Speter if (args->inherit == svn_mergeinfo_explicit) 5165251881Speter { 5166251881Speter svn_pool_destroy(iterpool); 5167251881Speter return SVN_NO_ERROR; 5168251881Speter } 5169251881Speter 5170251881Speter nearest_ancestor = nearest_ancestor->parent; 5171251881Speter 5172251881Speter /* Run out? There's no mergeinfo. */ 5173251881Speter if (! nearest_ancestor) 5174251881Speter { 5175251881Speter svn_pool_destroy(iterpool); 5176251881Speter return SVN_NO_ERROR; 5177251881Speter } 5178251881Speter } 5179251881Speter svn_pool_destroy(iterpool); 5180251881Speter 5181251881Speter SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node, trail, trail->pool)); 5182251881Speter mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO); 5183251881Speter if (! mergeinfo_string) 5184251881Speter { 5185251881Speter svn_string_t *id_str = 5186251881Speter svn_fs_base__id_unparse(svn_fs_base__dag_get_id(node), trail->pool); 5187251881Speter return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 5188251881Speter _("Node-revision '%s' claims to have " 5189251881Speter "mergeinfo but doesn't"), id_str->data); 5190251881Speter } 5191251881Speter 5192251881Speter /* Parse the mergeinfo; store the result in ARGS->MERGEINFO. */ 5193251881Speter { 5194251881Speter /* Issue #3896: If a node has syntactically invalid mergeinfo, then 5195251881Speter treat it as if no mergeinfo is present rather than raising a parse 5196251881Speter error. */ 5197251881Speter svn_error_t *err = svn_mergeinfo_parse(args->mergeinfo, 5198251881Speter mergeinfo_string->data, 5199251881Speter args->pool); 5200251881Speter if (err) 5201251881Speter { 5202251881Speter if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR) 5203251881Speter { 5204251881Speter svn_error_clear(err); 5205251881Speter err = NULL; 5206251881Speter args->mergeinfo = NULL; 5207251881Speter } 5208251881Speter return svn_error_trace(err); 5209251881Speter } 5210251881Speter } 5211251881Speter 5212251881Speter /* If our nearest ancestor is the very path we inquired about, we 5213251881Speter can return the mergeinfo results directly. Otherwise, we're 5214251881Speter inheriting the mergeinfo, so we need to a) remove non-inheritable 5215251881Speter ranges and b) telescope the merged-from paths. */ 5216251881Speter if (args->adjust_inherited_mergeinfo && (nearest_ancestor != parent_path)) 5217251881Speter { 5218251881Speter svn_mergeinfo_t tmp_mergeinfo; 5219251881Speter 5220251881Speter SVN_ERR(svn_mergeinfo_inheritable2(&tmp_mergeinfo, *args->mergeinfo, 5221251881Speter NULL, SVN_INVALID_REVNUM, 5222251881Speter SVN_INVALID_REVNUM, TRUE, 5223251881Speter trail->pool, trail->pool)); 5224251881Speter SVN_ERR(svn_fs__append_to_merged_froms(args->mergeinfo, tmp_mergeinfo, 5225251881Speter parent_path_relpath( 5226251881Speter parent_path, nearest_ancestor, 5227251881Speter trail->pool), 5228251881Speter args->pool)); 5229251881Speter } 5230251881Speter 5231251881Speter return SVN_NO_ERROR; 5232251881Speter} 5233251881Speter 5234251881Speter/* Set **NODE to the dag node for PATH in ROOT (allocated in POOL), 5235251881Speter and query its mergeinfo stats, setting HAS_MERGEINFO and 5236251881Speter CHILD_MERGEINFO_COUNT appropriately. */ 5237251881Speter 5238251881Speterstruct get_node_mergeinfo_stats_baton 5239251881Speter{ 5240251881Speter dag_node_t *node; 5241251881Speter svn_boolean_t has_mergeinfo; 5242251881Speter apr_int64_t child_mergeinfo_count; 5243251881Speter svn_fs_root_t *root; 5244251881Speter const char *path; 5245251881Speter}; 5246251881Speter 5247251881Speterstatic svn_error_t * 5248251881Spetertxn_body_get_node_mergeinfo_stats(void *baton, trail_t *trail) 5249251881Speter{ 5250251881Speter struct get_node_mergeinfo_stats_baton *args = baton; 5251251881Speter 5252251881Speter SVN_ERR(get_dag(&(args->node), args->root, args->path, 5253251881Speter trail, trail->pool)); 5254251881Speter return svn_fs_base__dag_get_mergeinfo_stats(&(args->has_mergeinfo), 5255251881Speter &(args->child_mergeinfo_count), 5256251881Speter args->node, trail, 5257251881Speter trail->pool); 5258251881Speter} 5259251881Speter 5260251881Speter 5261251881Speter/* Get the mergeinfo for a set of paths, returned in 5262251881Speter *MERGEINFO_CATALOG. Returned values are allocated in POOL, while 5263251881Speter temporary values are allocated in a sub-pool. */ 5264251881Speterstatic svn_error_t * 5265251881Speterget_mergeinfos_for_paths(svn_fs_root_t *root, 5266251881Speter svn_mergeinfo_catalog_t *mergeinfo_catalog, 5267251881Speter const apr_array_header_t *paths, 5268251881Speter svn_mergeinfo_inheritance_t inherit, 5269251881Speter svn_boolean_t include_descendants, 5270251881Speter svn_boolean_t adjust_inherited_mergeinfo, 5271251881Speter apr_pool_t *result_pool, 5272251881Speter apr_pool_t *scratch_pool) 5273251881Speter{ 5274251881Speter svn_mergeinfo_catalog_t result_catalog = apr_hash_make(result_pool); 5275251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 5276251881Speter int i; 5277251881Speter 5278251881Speter for (i = 0; i < paths->nelts; i++) 5279251881Speter { 5280251881Speter svn_mergeinfo_t path_mergeinfo; 5281251881Speter struct get_mergeinfo_for_path_baton gmfp_args; 5282251881Speter const char *path = APR_ARRAY_IDX(paths, i, const char *); 5283251881Speter 5284251881Speter svn_pool_clear(iterpool); 5285251881Speter 5286251881Speter path = svn_fs__canonicalize_abspath(path, iterpool); 5287251881Speter 5288251881Speter /* Get the mergeinfo for PATH itself. */ 5289251881Speter gmfp_args.mergeinfo = &path_mergeinfo; 5290251881Speter gmfp_args.root = root; 5291251881Speter gmfp_args.path = path; 5292251881Speter gmfp_args.inherit = inherit; 5293251881Speter gmfp_args.pool = result_pool; 5294251881Speter gmfp_args.adjust_inherited_mergeinfo = adjust_inherited_mergeinfo; 5295251881Speter SVN_ERR(svn_fs_base__retry_txn(root->fs, 5296251881Speter txn_body_get_mergeinfo_for_path, 5297251881Speter &gmfp_args, FALSE, iterpool)); 5298251881Speter if (path_mergeinfo) 5299251881Speter svn_hash_sets(result_catalog, apr_pstrdup(result_pool, path), 5300251881Speter path_mergeinfo); 5301251881Speter 5302251881Speter /* If we're including descendants, do so. */ 5303251881Speter if (include_descendants) 5304251881Speter { 5305251881Speter svn_boolean_t do_crawl; 5306251881Speter struct get_node_mergeinfo_stats_baton gnms_args; 5307251881Speter 5308251881Speter /* Query the node and its mergeinfo stats. */ 5309251881Speter gnms_args.root = root; 5310251881Speter gnms_args.path = path; 5311251881Speter SVN_ERR(svn_fs_base__retry_txn(root->fs, 5312251881Speter txn_body_get_node_mergeinfo_stats, 5313251881Speter &gnms_args, FALSE, iterpool)); 5314251881Speter 5315251881Speter /* Determine if there's anything worth crawling here. */ 5316251881Speter if (svn_fs_base__dag_node_kind(gnms_args.node) != svn_node_dir) 5317251881Speter do_crawl = FALSE; 5318251881Speter else 5319251881Speter do_crawl = ((gnms_args.child_mergeinfo_count > 1) 5320251881Speter || ((gnms_args.child_mergeinfo_count == 1) 5321251881Speter && (! gnms_args.has_mergeinfo))); 5322251881Speter 5323251881Speter /* If it's worth crawling, crawl. */ 5324251881Speter if (do_crawl) 5325251881Speter SVN_ERR(crawl_directory_for_mergeinfo(root->fs, gnms_args.node, 5326251881Speter path, result_catalog, 5327251881Speter iterpool)); 5328251881Speter } 5329251881Speter } 5330251881Speter svn_pool_destroy(iterpool); 5331251881Speter 5332251881Speter *mergeinfo_catalog = result_catalog; 5333251881Speter return SVN_NO_ERROR; 5334251881Speter} 5335251881Speter 5336251881Speter 5337251881Speter/* Implements svn_fs_get_mergeinfo. */ 5338251881Speterstatic svn_error_t * 5339251881Speterbase_get_mergeinfo(svn_mergeinfo_catalog_t *catalog, 5340251881Speter svn_fs_root_t *root, 5341251881Speter const apr_array_header_t *paths, 5342251881Speter svn_mergeinfo_inheritance_t inherit, 5343251881Speter svn_boolean_t include_descendants, 5344251881Speter svn_boolean_t adjust_inherited_mergeinfo, 5345251881Speter apr_pool_t *result_pool, 5346251881Speter apr_pool_t *scratch_pool) 5347251881Speter{ 5348251881Speter /* Verify that our filesystem version supports mergeinfo stuff. */ 5349251881Speter SVN_ERR(svn_fs_base__test_required_feature_format 5350251881Speter (root->fs, "mergeinfo", SVN_FS_BASE__MIN_MERGEINFO_FORMAT)); 5351251881Speter 5352251881Speter /* We require a revision root. */ 5353251881Speter if (root->is_txn_root) 5354251881Speter return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL); 5355251881Speter 5356251881Speter /* Retrieve a path -> mergeinfo mapping. */ 5357251881Speter return get_mergeinfos_for_paths(root, catalog, paths, 5358251881Speter inherit, include_descendants, 5359251881Speter adjust_inherited_mergeinfo, 5360251881Speter result_pool, scratch_pool); 5361251881Speter} 5362251881Speter 5363251881Speter 5364251881Speter 5365251881Speter/* Creating root objects. */ 5366251881Speter 5367251881Speter 5368251881Speterstatic root_vtable_t root_vtable = { 5369251881Speter base_paths_changed, 5370251881Speter base_check_path, 5371251881Speter base_node_history, 5372251881Speter base_node_id, 5373251881Speter base_node_created_rev, 5374251881Speter base_node_origin_rev, 5375251881Speter base_node_created_path, 5376251881Speter base_delete_node, 5377251881Speter base_copied_from, 5378251881Speter base_closest_copy, 5379251881Speter base_node_prop, 5380251881Speter base_node_proplist, 5381251881Speter base_change_node_prop, 5382251881Speter base_props_changed, 5383251881Speter base_dir_entries, 5384251881Speter base_make_dir, 5385251881Speter base_copy, 5386251881Speter base_revision_link, 5387251881Speter base_file_length, 5388251881Speter base_file_checksum, 5389251881Speter base_file_contents, 5390251881Speter NULL, 5391251881Speter base_make_file, 5392251881Speter base_apply_textdelta, 5393251881Speter base_apply_text, 5394251881Speter base_contents_changed, 5395251881Speter base_get_file_delta_stream, 5396251881Speter base_merge, 5397251881Speter base_get_mergeinfo, 5398251881Speter}; 5399251881Speter 5400251881Speter 5401251881Speter/* Construct a new root object in FS, allocated from POOL. */ 5402251881Speterstatic svn_fs_root_t * 5403251881Spetermake_root(svn_fs_t *fs, 5404251881Speter apr_pool_t *pool) 5405251881Speter{ 5406251881Speter svn_fs_root_t *root = apr_pcalloc(pool, sizeof(*root)); 5407251881Speter base_root_data_t *brd = apr_palloc(pool, sizeof(*brd)); 5408251881Speter 5409251881Speter root->fs = fs; 5410251881Speter root->pool = pool; 5411251881Speter 5412251881Speter /* Init the node ID cache. */ 5413251881Speter brd->node_cache = apr_hash_make(pool); 5414251881Speter brd->node_cache_idx = 0; 5415251881Speter root->vtable = &root_vtable; 5416251881Speter root->fsap_data = brd; 5417251881Speter 5418251881Speter return root; 5419251881Speter} 5420251881Speter 5421251881Speter 5422251881Speter/* Construct a root object referring to the root of REVISION in FS, 5423251881Speter whose root directory is ROOT_DIR. Create the new root in POOL. */ 5424251881Speterstatic svn_fs_root_t * 5425251881Spetermake_revision_root(svn_fs_t *fs, 5426251881Speter svn_revnum_t rev, 5427251881Speter dag_node_t *root_dir, 5428251881Speter apr_pool_t *pool) 5429251881Speter{ 5430251881Speter svn_fs_root_t *root = make_root(fs, pool); 5431251881Speter base_root_data_t *brd = root->fsap_data; 5432251881Speter 5433251881Speter root->is_txn_root = FALSE; 5434251881Speter root->rev = rev; 5435251881Speter brd->root_dir = root_dir; 5436251881Speter 5437251881Speter return root; 5438251881Speter} 5439251881Speter 5440251881Speter 5441251881Speter/* Construct a root object referring to the root of the transaction 5442251881Speter named TXN and based on revision BASE_REV in FS. FLAGS represents 5443251881Speter the behavior of the transaction. Create the new root in POOL. */ 5444251881Speterstatic svn_fs_root_t * 5445251881Spetermake_txn_root(svn_fs_t *fs, 5446251881Speter const char *txn, 5447251881Speter svn_revnum_t base_rev, 5448251881Speter apr_uint32_t flags, 5449251881Speter apr_pool_t *pool) 5450251881Speter{ 5451251881Speter svn_fs_root_t *root = make_root(fs, pool); 5452251881Speter root->is_txn_root = TRUE; 5453251881Speter root->txn = apr_pstrdup(root->pool, txn); 5454251881Speter root->txn_flags = flags; 5455251881Speter root->rev = base_rev; 5456251881Speter 5457251881Speter return root; 5458251881Speter} 5459