1251881Speter/* revs-txns.c : operations on revision and transactions 2251881Speter * 3251881Speter * ==================================================================== 4251881Speter * Licensed to the Apache Software Foundation (ASF) under one 5251881Speter * or more contributor license agreements. See the NOTICE file 6251881Speter * distributed with this work for additional information 7251881Speter * regarding copyright ownership. The ASF licenses this file 8251881Speter * to you under the Apache License, Version 2.0 (the 9251881Speter * "License"); you may not use this file except in compliance 10251881Speter * with the License. You may obtain a copy of the License at 11251881Speter * 12251881Speter * http://www.apache.org/licenses/LICENSE-2.0 13251881Speter * 14251881Speter * Unless required by applicable law or agreed to in writing, 15251881Speter * software distributed under the License is distributed on an 16251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17251881Speter * KIND, either express or implied. See the License for the 18251881Speter * specific language governing permissions and limitations 19251881Speter * under the License. 20251881Speter * ==================================================================== 21251881Speter */ 22251881Speter 23251881Speter#include <string.h> 24251881Speter 25251881Speter#include <apr_tables.h> 26251881Speter#include <apr_pools.h> 27251881Speter 28251881Speter#include "svn_pools.h" 29251881Speter#include "svn_time.h" 30251881Speter#include "svn_fs.h" 31251881Speter#include "svn_props.h" 32251881Speter#include "svn_hash.h" 33251881Speter#include "svn_io.h" 34251881Speter 35251881Speter#include "fs.h" 36251881Speter#include "dag.h" 37251881Speter#include "err.h" 38251881Speter#include "trail.h" 39251881Speter#include "tree.h" 40251881Speter#include "revs-txns.h" 41251881Speter#include "key-gen.h" 42251881Speter#include "id.h" 43251881Speter#include "bdb/rev-table.h" 44251881Speter#include "bdb/txn-table.h" 45251881Speter#include "bdb/copies-table.h" 46251881Speter#include "bdb/changes-table.h" 47251881Speter#include "../libsvn_fs/fs-loader.h" 48251881Speter 49251881Speter#include "svn_private_config.h" 50251881Speter#include "private/svn_fs_util.h" 51251881Speter 52251881Speter 53251881Speter/*** Helpers ***/ 54251881Speter 55251881Speter/* Set *txn_p to a transaction object allocated in POOL for the 56251881Speter transaction in FS whose id is TXN_ID. If EXPECT_DEAD is set, this 57251881Speter transaction must be a dead one, else an error is returned. If 58251881Speter EXPECT_DEAD is not set, the transaction must *not* be a dead one, 59251881Speter else an error is returned. */ 60251881Speterstatic svn_error_t * 61251881Speterget_txn(transaction_t **txn_p, 62251881Speter svn_fs_t *fs, 63251881Speter const char *txn_id, 64251881Speter svn_boolean_t expect_dead, 65251881Speter trail_t *trail, 66251881Speter apr_pool_t *pool) 67251881Speter{ 68251881Speter transaction_t *txn; 69251881Speter SVN_ERR(svn_fs_bdb__get_txn(&txn, fs, txn_id, trail, pool)); 70251881Speter if (expect_dead && (txn->kind != transaction_kind_dead)) 71251881Speter return svn_error_createf(SVN_ERR_FS_TRANSACTION_NOT_DEAD, 0, 72251881Speter _("Transaction is not dead: '%s'"), txn_id); 73251881Speter if ((! expect_dead) && (txn->kind == transaction_kind_dead)) 74251881Speter return svn_error_createf(SVN_ERR_FS_TRANSACTION_DEAD, 0, 75251881Speter _("Transaction is dead: '%s'"), txn_id); 76251881Speter *txn_p = txn; 77251881Speter return SVN_NO_ERROR; 78251881Speter} 79251881Speter 80251881Speter 81251881Speter/* This is only for symmetry with the get_txn() helper. */ 82251881Speter#define put_txn svn_fs_bdb__put_txn 83251881Speter 84251881Speter 85251881Speter 86251881Speter/*** Revisions ***/ 87251881Speter 88251881Speter/* Return the committed transaction record *TXN_P and its ID *TXN_ID 89251881Speter (as long as those parameters aren't NULL) for the revision REV in 90251881Speter FS as part of TRAIL. */ 91251881Speterstatic svn_error_t * 92251881Speterget_rev_txn(transaction_t **txn_p, 93251881Speter const char **txn_id, 94251881Speter svn_fs_t *fs, 95251881Speter svn_revnum_t rev, 96251881Speter trail_t *trail, 97251881Speter apr_pool_t *pool) 98251881Speter{ 99251881Speter revision_t *revision; 100251881Speter transaction_t *txn; 101251881Speter 102251881Speter SVN_ERR(svn_fs_bdb__get_rev(&revision, fs, rev, trail, pool)); 103251881Speter if (revision->txn_id == NULL) 104251881Speter return svn_fs_base__err_corrupt_fs_revision(fs, rev); 105251881Speter 106251881Speter SVN_ERR(get_txn(&txn, fs, revision->txn_id, FALSE, trail, pool)); 107251881Speter if (txn->revision != rev) 108251881Speter return svn_fs_base__err_corrupt_txn(fs, revision->txn_id); 109251881Speter 110251881Speter if (txn_p) 111251881Speter *txn_p = txn; 112251881Speter if (txn_id) 113251881Speter *txn_id = revision->txn_id; 114251881Speter return SVN_NO_ERROR; 115251881Speter} 116251881Speter 117251881Speter 118251881Spetersvn_error_t * 119251881Spetersvn_fs_base__rev_get_root(const svn_fs_id_t **root_id_p, 120251881Speter svn_fs_t *fs, 121251881Speter svn_revnum_t rev, 122251881Speter trail_t *trail, 123251881Speter apr_pool_t *pool) 124251881Speter{ 125251881Speter transaction_t *txn; 126251881Speter 127251881Speter SVN_ERR(get_rev_txn(&txn, NULL, fs, rev, trail, pool)); 128251881Speter if (txn->root_id == NULL) 129251881Speter return svn_fs_base__err_corrupt_fs_revision(fs, rev); 130251881Speter 131251881Speter *root_id_p = txn->root_id; 132251881Speter return SVN_NO_ERROR; 133251881Speter} 134251881Speter 135251881Speter 136251881Spetersvn_error_t * 137251881Spetersvn_fs_base__rev_get_txn_id(const char **txn_id_p, 138251881Speter svn_fs_t *fs, 139251881Speter svn_revnum_t rev, 140251881Speter trail_t *trail, 141251881Speter apr_pool_t *pool) 142251881Speter{ 143251881Speter revision_t *revision; 144251881Speter 145251881Speter SVN_ERR(svn_fs_bdb__get_rev(&revision, fs, rev, trail, pool)); 146251881Speter if (revision->txn_id == NULL) 147251881Speter return svn_fs_base__err_corrupt_fs_revision(fs, rev); 148251881Speter 149251881Speter *txn_id_p = revision->txn_id; 150251881Speter return SVN_NO_ERROR; 151251881Speter} 152251881Speter 153251881Speter 154251881Speterstatic svn_error_t * 155251881Spetertxn_body_youngest_rev(void *baton, trail_t *trail) 156251881Speter{ 157251881Speter return svn_fs_bdb__youngest_rev(baton, trail->fs, trail, trail->pool); 158251881Speter} 159251881Speter 160251881Speter 161251881Spetersvn_error_t * 162251881Spetersvn_fs_base__youngest_rev(svn_revnum_t *youngest_p, 163251881Speter svn_fs_t *fs, 164251881Speter apr_pool_t *pool) 165251881Speter{ 166251881Speter svn_revnum_t youngest; 167251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 168251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_youngest_rev, &youngest, 169251881Speter TRUE, pool)); 170251881Speter *youngest_p = youngest; 171251881Speter return SVN_NO_ERROR; 172251881Speter} 173251881Speter 174251881Speter 175251881Speterstruct revision_proplist_args { 176251881Speter apr_hash_t **table_p; 177251881Speter svn_revnum_t rev; 178251881Speter}; 179251881Speter 180251881Speter 181251881Speterstatic svn_error_t * 182251881Spetertxn_body_revision_proplist(void *baton, trail_t *trail) 183251881Speter{ 184251881Speter struct revision_proplist_args *args = baton; 185251881Speter transaction_t *txn; 186251881Speter 187251881Speter SVN_ERR(get_rev_txn(&txn, NULL, trail->fs, args->rev, trail, trail->pool)); 188251881Speter *(args->table_p) = txn->proplist; 189251881Speter return SVN_NO_ERROR; 190251881Speter} 191251881Speter 192251881Speter 193251881Spetersvn_error_t * 194251881Spetersvn_fs_base__revision_proplist(apr_hash_t **table_p, 195251881Speter svn_fs_t *fs, 196251881Speter svn_revnum_t rev, 197251881Speter apr_pool_t *pool) 198251881Speter{ 199251881Speter struct revision_proplist_args args; 200251881Speter apr_hash_t *table; 201251881Speter 202251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 203251881Speter 204251881Speter args.table_p = &table; 205251881Speter args.rev = rev; 206251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_revision_proplist, &args, 207251881Speter FALSE, pool)); 208251881Speter 209251881Speter *table_p = table ? table : apr_hash_make(pool); 210251881Speter return SVN_NO_ERROR; 211251881Speter} 212251881Speter 213251881Speter 214251881Spetersvn_error_t * 215251881Spetersvn_fs_base__revision_prop(svn_string_t **value_p, 216251881Speter svn_fs_t *fs, 217251881Speter svn_revnum_t rev, 218251881Speter const char *propname, 219251881Speter apr_pool_t *pool) 220251881Speter{ 221251881Speter struct revision_proplist_args args; 222251881Speter apr_hash_t *table; 223251881Speter 224251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 225251881Speter 226251881Speter /* Get the proplist. */ 227251881Speter args.table_p = &table; 228251881Speter args.rev = rev; 229251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_revision_proplist, &args, 230251881Speter FALSE, pool)); 231251881Speter 232251881Speter /* And then the prop from that list (if there was a list). */ 233251881Speter *value_p = svn_hash_gets(table, propname); 234251881Speter 235251881Speter return SVN_NO_ERROR; 236251881Speter} 237251881Speter 238251881Speter 239251881Spetersvn_error_t * 240251881Spetersvn_fs_base__set_rev_prop(svn_fs_t *fs, 241251881Speter svn_revnum_t rev, 242251881Speter const char *name, 243251881Speter const svn_string_t *const *old_value_p, 244251881Speter const svn_string_t *value, 245251881Speter trail_t *trail, 246251881Speter apr_pool_t *pool) 247251881Speter{ 248251881Speter transaction_t *txn; 249251881Speter const char *txn_id; 250251881Speter 251251881Speter SVN_ERR(get_rev_txn(&txn, &txn_id, fs, rev, trail, pool)); 252251881Speter 253251881Speter /* If there's no proplist, but we're just deleting a property, exit now. */ 254251881Speter if ((! txn->proplist) && (! value)) 255251881Speter return SVN_NO_ERROR; 256251881Speter 257251881Speter /* Now, if there's no proplist, we know we need to make one. */ 258251881Speter if (! txn->proplist) 259251881Speter txn->proplist = apr_hash_make(pool); 260251881Speter 261251881Speter /* Set the property. */ 262251881Speter if (old_value_p) 263251881Speter { 264251881Speter const svn_string_t *wanted_value = *old_value_p; 265251881Speter const svn_string_t *present_value = svn_hash_gets(txn->proplist, name); 266251881Speter if ((!wanted_value != !present_value) 267251881Speter || (wanted_value && present_value 268251881Speter && !svn_string_compare(wanted_value, present_value))) 269251881Speter { 270251881Speter /* What we expected isn't what we found. */ 271251881Speter return svn_error_createf(SVN_ERR_FS_PROP_BASEVALUE_MISMATCH, NULL, 272251881Speter _("revprop '%s' has unexpected value in " 273251881Speter "filesystem"), 274251881Speter name); 275251881Speter } 276251881Speter /* Fall through. */ 277251881Speter } 278251881Speter svn_hash_sets(txn->proplist, name, value); 279251881Speter 280251881Speter /* Overwrite the revision. */ 281251881Speter return put_txn(fs, txn, txn_id, trail, pool); 282251881Speter} 283251881Speter 284251881Speter 285251881Speterstruct change_rev_prop_args { 286251881Speter svn_revnum_t rev; 287251881Speter const char *name; 288251881Speter const svn_string_t *const *old_value_p; 289251881Speter const svn_string_t *value; 290251881Speter}; 291251881Speter 292251881Speter 293251881Speterstatic svn_error_t * 294251881Spetertxn_body_change_rev_prop(void *baton, trail_t *trail) 295251881Speter{ 296251881Speter struct change_rev_prop_args *args = baton; 297251881Speter 298251881Speter return svn_fs_base__set_rev_prop(trail->fs, args->rev, 299251881Speter args->name, args->old_value_p, args->value, 300251881Speter trail, trail->pool); 301251881Speter} 302251881Speter 303251881Speter 304251881Spetersvn_error_t * 305251881Spetersvn_fs_base__change_rev_prop(svn_fs_t *fs, 306251881Speter svn_revnum_t rev, 307251881Speter const char *name, 308251881Speter const svn_string_t *const *old_value_p, 309251881Speter const svn_string_t *value, 310251881Speter apr_pool_t *pool) 311251881Speter{ 312251881Speter struct change_rev_prop_args args; 313251881Speter 314251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 315251881Speter 316251881Speter args.rev = rev; 317251881Speter args.name = name; 318251881Speter args.old_value_p = old_value_p; 319251881Speter args.value = value; 320251881Speter return svn_fs_base__retry_txn(fs, txn_body_change_rev_prop, &args, 321251881Speter TRUE, pool); 322251881Speter} 323251881Speter 324251881Speter 325251881Speter 326251881Speter/*** Transactions ***/ 327251881Speter 328251881Spetersvn_error_t * 329251881Spetersvn_fs_base__txn_make_committed(svn_fs_t *fs, 330251881Speter const char *txn_name, 331251881Speter svn_revnum_t revision, 332251881Speter trail_t *trail, 333251881Speter apr_pool_t *pool) 334251881Speter{ 335251881Speter transaction_t *txn; 336251881Speter 337251881Speter SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision)); 338251881Speter 339251881Speter /* Make sure the TXN is not committed already. */ 340251881Speter SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool)); 341251881Speter if (txn->kind != transaction_kind_normal) 342251881Speter return svn_fs_base__err_txn_not_mutable(fs, txn_name); 343251881Speter 344251881Speter /* Convert TXN to a committed transaction. */ 345251881Speter txn->base_id = NULL; 346251881Speter txn->revision = revision; 347251881Speter txn->kind = transaction_kind_committed; 348251881Speter return put_txn(fs, txn, txn_name, trail, pool); 349251881Speter} 350251881Speter 351251881Speter 352251881Spetersvn_error_t * 353251881Spetersvn_fs_base__txn_get_revision(svn_revnum_t *revision, 354251881Speter svn_fs_t *fs, 355251881Speter const char *txn_name, 356251881Speter trail_t *trail, 357251881Speter apr_pool_t *pool) 358251881Speter{ 359251881Speter transaction_t *txn; 360251881Speter SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool)); 361251881Speter *revision = txn->revision; 362251881Speter return SVN_NO_ERROR; 363251881Speter} 364251881Speter 365251881Speter 366251881Spetersvn_error_t * 367251881Spetersvn_fs_base__get_txn_ids(const svn_fs_id_t **root_id_p, 368251881Speter const svn_fs_id_t **base_root_id_p, 369251881Speter svn_fs_t *fs, 370251881Speter const char *txn_name, 371251881Speter trail_t *trail, 372251881Speter apr_pool_t *pool) 373251881Speter{ 374251881Speter transaction_t *txn; 375251881Speter 376251881Speter SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool)); 377251881Speter if (txn->kind != transaction_kind_normal) 378251881Speter return svn_fs_base__err_txn_not_mutable(fs, txn_name); 379251881Speter 380251881Speter *root_id_p = txn->root_id; 381251881Speter *base_root_id_p = txn->base_id; 382251881Speter return SVN_NO_ERROR; 383251881Speter} 384251881Speter 385251881Speter 386251881Spetersvn_error_t * 387251881Spetersvn_fs_base__set_txn_root(svn_fs_t *fs, 388251881Speter const char *txn_name, 389251881Speter const svn_fs_id_t *new_id, 390251881Speter trail_t *trail, 391251881Speter apr_pool_t *pool) 392251881Speter{ 393251881Speter transaction_t *txn; 394251881Speter 395251881Speter SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool)); 396251881Speter if (txn->kind != transaction_kind_normal) 397251881Speter return svn_fs_base__err_txn_not_mutable(fs, txn_name); 398251881Speter 399251881Speter if (! svn_fs_base__id_eq(txn->root_id, new_id)) 400251881Speter { 401251881Speter txn->root_id = new_id; 402251881Speter SVN_ERR(put_txn(fs, txn, txn_name, trail, pool)); 403251881Speter } 404251881Speter return SVN_NO_ERROR; 405251881Speter} 406251881Speter 407251881Speter 408251881Spetersvn_error_t * 409251881Spetersvn_fs_base__set_txn_base(svn_fs_t *fs, 410251881Speter const char *txn_name, 411251881Speter const svn_fs_id_t *new_id, 412251881Speter trail_t *trail, 413251881Speter apr_pool_t *pool) 414251881Speter{ 415251881Speter transaction_t *txn; 416251881Speter 417251881Speter SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool)); 418251881Speter if (txn->kind != transaction_kind_normal) 419251881Speter return svn_fs_base__err_txn_not_mutable(fs, txn_name); 420251881Speter 421251881Speter if (! svn_fs_base__id_eq(txn->base_id, new_id)) 422251881Speter { 423251881Speter txn->base_id = new_id; 424251881Speter SVN_ERR(put_txn(fs, txn, txn_name, trail, pool)); 425251881Speter } 426251881Speter return SVN_NO_ERROR; 427251881Speter} 428251881Speter 429251881Speter 430251881Spetersvn_error_t * 431251881Spetersvn_fs_base__add_txn_copy(svn_fs_t *fs, 432251881Speter const char *txn_name, 433251881Speter const char *copy_id, 434251881Speter trail_t *trail, 435251881Speter apr_pool_t *pool) 436251881Speter{ 437251881Speter transaction_t *txn; 438251881Speter 439251881Speter /* Get the transaction and ensure its mutability. */ 440251881Speter SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool)); 441251881Speter if (txn->kind != transaction_kind_normal) 442251881Speter return svn_fs_base__err_txn_not_mutable(fs, txn_name); 443251881Speter 444251881Speter /* Allocate a new array if this transaction has no copies. */ 445251881Speter if (! txn->copies) 446251881Speter txn->copies = apr_array_make(pool, 1, sizeof(copy_id)); 447251881Speter 448251881Speter /* Add COPY_ID to the array. */ 449251881Speter APR_ARRAY_PUSH(txn->copies, const char *) = copy_id; 450251881Speter 451251881Speter /* Finally, write out the transaction. */ 452251881Speter return put_txn(fs, txn, txn_name, trail, pool); 453251881Speter} 454251881Speter 455251881Speter 456251881Speter 457251881Speter/* Generic transaction operations. */ 458251881Speter 459251881Speterstruct txn_proplist_args { 460251881Speter apr_hash_t **table_p; 461251881Speter const char *id; 462251881Speter}; 463251881Speter 464251881Speter 465251881Speterstatic svn_error_t * 466251881Spetertxn_body_txn_proplist(void *baton, trail_t *trail) 467251881Speter{ 468251881Speter transaction_t *txn; 469251881Speter struct txn_proplist_args *args = baton; 470251881Speter 471251881Speter SVN_ERR(get_txn(&txn, trail->fs, args->id, FALSE, trail, trail->pool)); 472251881Speter if (txn->kind != transaction_kind_normal) 473251881Speter return svn_fs_base__err_txn_not_mutable(trail->fs, args->id); 474251881Speter 475251881Speter *(args->table_p) = txn->proplist; 476251881Speter return SVN_NO_ERROR; 477251881Speter} 478251881Speter 479251881Speter 480251881Speter 481251881Spetersvn_error_t * 482251881Spetersvn_fs_base__txn_proplist_in_trail(apr_hash_t **table_p, 483251881Speter const char *txn_id, 484251881Speter trail_t *trail) 485251881Speter{ 486251881Speter struct txn_proplist_args args; 487251881Speter apr_hash_t *table; 488251881Speter 489251881Speter args.table_p = &table; 490251881Speter args.id = txn_id; 491251881Speter SVN_ERR(txn_body_txn_proplist(&args, trail)); 492251881Speter 493251881Speter *table_p = table ? table : apr_hash_make(trail->pool); 494251881Speter return SVN_NO_ERROR; 495251881Speter} 496251881Speter 497251881Speter 498251881Speter 499251881Spetersvn_error_t * 500251881Spetersvn_fs_base__txn_proplist(apr_hash_t **table_p, 501251881Speter svn_fs_txn_t *txn, 502251881Speter apr_pool_t *pool) 503251881Speter{ 504251881Speter struct txn_proplist_args args; 505251881Speter apr_hash_t *table; 506251881Speter svn_fs_t *fs = txn->fs; 507251881Speter 508251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 509251881Speter 510251881Speter args.table_p = &table; 511251881Speter args.id = txn->id; 512251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_proplist, &args, 513251881Speter FALSE, pool)); 514251881Speter 515251881Speter *table_p = table ? table : apr_hash_make(pool); 516251881Speter return SVN_NO_ERROR; 517251881Speter} 518251881Speter 519251881Speter 520251881Spetersvn_error_t * 521251881Spetersvn_fs_base__txn_prop(svn_string_t **value_p, 522251881Speter svn_fs_txn_t *txn, 523251881Speter const char *propname, 524251881Speter apr_pool_t *pool) 525251881Speter{ 526251881Speter struct txn_proplist_args args; 527251881Speter apr_hash_t *table; 528251881Speter svn_fs_t *fs = txn->fs; 529251881Speter 530251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 531251881Speter 532251881Speter /* Get the proplist. */ 533251881Speter args.table_p = &table; 534251881Speter args.id = txn->id; 535251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_proplist, &args, 536251881Speter FALSE, pool)); 537251881Speter 538251881Speter /* And then the prop from that list (if there was a list). */ 539251881Speter *value_p = svn_hash_gets(table, propname); 540251881Speter 541251881Speter return SVN_NO_ERROR; 542251881Speter} 543251881Speter 544251881Speter 545251881Speter 546251881Speterstruct change_txn_prop_args { 547251881Speter svn_fs_t *fs; 548251881Speter const char *id; 549251881Speter const char *name; 550251881Speter const svn_string_t *value; 551251881Speter}; 552251881Speter 553251881Speter 554251881Spetersvn_error_t * 555251881Spetersvn_fs_base__set_txn_prop(svn_fs_t *fs, 556251881Speter const char *txn_name, 557251881Speter const char *name, 558251881Speter const svn_string_t *value, 559251881Speter trail_t *trail, 560251881Speter apr_pool_t *pool) 561251881Speter{ 562251881Speter transaction_t *txn; 563251881Speter 564251881Speter SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool)); 565251881Speter if (txn->kind != transaction_kind_normal) 566251881Speter return svn_fs_base__err_txn_not_mutable(fs, txn_name); 567251881Speter 568251881Speter /* If there's no proplist, but we're just deleting a property, exit now. */ 569251881Speter if ((! txn->proplist) && (! value)) 570251881Speter return SVN_NO_ERROR; 571251881Speter 572251881Speter /* Now, if there's no proplist, we know we need to make one. */ 573251881Speter if (! txn->proplist) 574251881Speter txn->proplist = apr_hash_make(pool); 575251881Speter 576251881Speter /* Set the property. */ 577299742Sdim if (svn_hash_gets(txn->proplist, SVN_FS__PROP_TXN_CLIENT_DATE) 578299742Sdim && !strcmp(name, SVN_PROP_REVISION_DATE)) 579299742Sdim svn_hash_sets(txn->proplist, SVN_FS__PROP_TXN_CLIENT_DATE, 580299742Sdim svn_string_create("1", pool)); 581251881Speter svn_hash_sets(txn->proplist, name, value); 582251881Speter 583251881Speter /* Now overwrite the transaction. */ 584251881Speter return put_txn(fs, txn, txn_name, trail, pool); 585251881Speter} 586251881Speter 587251881Speter 588251881Speterstatic svn_error_t * 589251881Spetertxn_body_change_txn_prop(void *baton, trail_t *trail) 590251881Speter{ 591251881Speter struct change_txn_prop_args *args = baton; 592251881Speter return svn_fs_base__set_txn_prop(trail->fs, args->id, args->name, 593251881Speter args->value, trail, trail->pool); 594251881Speter} 595251881Speter 596251881Speter 597251881Spetersvn_error_t * 598251881Spetersvn_fs_base__change_txn_prop(svn_fs_txn_t *txn, 599251881Speter const char *name, 600251881Speter const svn_string_t *value, 601251881Speter apr_pool_t *pool) 602251881Speter{ 603251881Speter struct change_txn_prop_args args; 604251881Speter svn_fs_t *fs = txn->fs; 605251881Speter 606251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 607251881Speter 608251881Speter args.id = txn->id; 609251881Speter args.name = name; 610251881Speter args.value = value; 611251881Speter return svn_fs_base__retry_txn(fs, txn_body_change_txn_prop, &args, 612251881Speter TRUE, pool); 613251881Speter} 614251881Speter 615251881Speter 616251881Spetersvn_error_t * 617251881Spetersvn_fs_base__change_txn_props(svn_fs_txn_t *txn, 618251881Speter const apr_array_header_t *props, 619251881Speter apr_pool_t *pool) 620251881Speter{ 621251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 622251881Speter int i; 623251881Speter 624251881Speter for (i = 0; i < props->nelts; i++) 625251881Speter { 626251881Speter svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t); 627251881Speter 628251881Speter svn_pool_clear(iterpool); 629251881Speter 630251881Speter SVN_ERR(svn_fs_base__change_txn_prop(txn, prop->name, 631251881Speter prop->value, iterpool)); 632251881Speter } 633251881Speter svn_pool_destroy(iterpool); 634251881Speter 635251881Speter return SVN_NO_ERROR; 636251881Speter} 637251881Speter 638251881Speter 639251881Speter/* Creating a transaction */ 640251881Speter 641251881Speterstatic txn_vtable_t txn_vtable = { 642251881Speter svn_fs_base__commit_txn, 643251881Speter svn_fs_base__abort_txn, 644251881Speter svn_fs_base__txn_prop, 645251881Speter svn_fs_base__txn_proplist, 646251881Speter svn_fs_base__change_txn_prop, 647251881Speter svn_fs_base__txn_root, 648251881Speter svn_fs_base__change_txn_props 649251881Speter}; 650251881Speter 651251881Speter 652251881Speter/* Allocate and return a new transaction object in POOL for FS whose 653251881Speter transaction ID is ID. ID is not copied. */ 654251881Speterstatic svn_fs_txn_t * 655251881Spetermake_txn(svn_fs_t *fs, 656251881Speter const char *id, 657251881Speter svn_revnum_t base_rev, 658251881Speter apr_pool_t *pool) 659251881Speter{ 660251881Speter svn_fs_txn_t *txn = apr_pcalloc(pool, sizeof(*txn)); 661251881Speter 662251881Speter txn->fs = fs; 663251881Speter txn->id = id; 664251881Speter txn->base_rev = base_rev; 665251881Speter txn->vtable = &txn_vtable; 666251881Speter txn->fsap_data = NULL; 667251881Speter 668251881Speter return txn; 669251881Speter} 670251881Speter 671251881Speter 672251881Speterstruct begin_txn_args 673251881Speter{ 674251881Speter svn_fs_txn_t **txn_p; 675251881Speter svn_revnum_t base_rev; 676251881Speter apr_uint32_t flags; 677251881Speter}; 678251881Speter 679251881Speter 680251881Speterstatic svn_error_t * 681251881Spetertxn_body_begin_txn(void *baton, trail_t *trail) 682251881Speter{ 683251881Speter struct begin_txn_args *args = baton; 684251881Speter const svn_fs_id_t *root_id; 685251881Speter const char *txn_id; 686251881Speter 687251881Speter SVN_ERR(svn_fs_base__rev_get_root(&root_id, trail->fs, args->base_rev, 688251881Speter trail, trail->pool)); 689251881Speter SVN_ERR(svn_fs_bdb__create_txn(&txn_id, trail->fs, root_id, 690251881Speter trail, trail->pool)); 691251881Speter 692251881Speter if (args->flags & SVN_FS_TXN_CHECK_OOD) 693251881Speter { 694251881Speter struct change_txn_prop_args cpargs; 695251881Speter cpargs.fs = trail->fs; 696251881Speter cpargs.id = txn_id; 697251881Speter cpargs.name = SVN_FS__PROP_TXN_CHECK_OOD; 698251881Speter cpargs.value = svn_string_create("true", trail->pool); 699251881Speter 700251881Speter SVN_ERR(txn_body_change_txn_prop(&cpargs, trail)); 701251881Speter } 702251881Speter 703251881Speter if (args->flags & SVN_FS_TXN_CHECK_LOCKS) 704251881Speter { 705251881Speter struct change_txn_prop_args cpargs; 706251881Speter cpargs.fs = trail->fs; 707251881Speter cpargs.id = txn_id; 708251881Speter cpargs.name = SVN_FS__PROP_TXN_CHECK_LOCKS; 709251881Speter cpargs.value = svn_string_create("true", trail->pool); 710251881Speter 711251881Speter SVN_ERR(txn_body_change_txn_prop(&cpargs, trail)); 712251881Speter } 713251881Speter 714299742Sdim /* Put a datestamp on the newly created txn, so we always know 715299742Sdim exactly how old it is. (This will help sysadmins identify 716299742Sdim long-abandoned txns that may need to be manually removed.) Do 717299742Sdim this before setting CLIENT_DATE so that it is not recorded as an 718299742Sdim explicit setting. */ 719299742Sdim { 720299742Sdim struct change_txn_prop_args cpargs; 721299742Sdim svn_string_t date; 722299742Sdim cpargs.fs = trail->fs; 723299742Sdim cpargs.id = txn_id; 724299742Sdim cpargs.name = SVN_PROP_REVISION_DATE; 725299742Sdim date.data = svn_time_to_cstring(apr_time_now(), trail->pool); 726299742Sdim date.len = strlen(date.data); 727299742Sdim cpargs.value = &date; 728299742Sdim SVN_ERR(txn_body_change_txn_prop(&cpargs, trail)); 729299742Sdim } 730299742Sdim 731299742Sdim if (args->flags & SVN_FS_TXN_CLIENT_DATE) 732299742Sdim { 733299742Sdim struct change_txn_prop_args cpargs; 734299742Sdim cpargs.fs = trail->fs; 735299742Sdim cpargs.id = txn_id; 736299742Sdim cpargs.name = SVN_FS__PROP_TXN_CLIENT_DATE; 737299742Sdim cpargs.value = svn_string_create("0", trail->pool); 738299742Sdim 739299742Sdim SVN_ERR(txn_body_change_txn_prop(&cpargs, trail)); 740299742Sdim } 741299742Sdim 742251881Speter *args->txn_p = make_txn(trail->fs, txn_id, args->base_rev, trail->pool); 743251881Speter return SVN_NO_ERROR; 744251881Speter} 745251881Speter 746251881Speter/* Note: it is acceptable for this function to call back into 747251881Speter public FS API interfaces because it does not itself use trails. */ 748251881Spetersvn_error_t * 749251881Spetersvn_fs_base__begin_txn(svn_fs_txn_t **txn_p, 750251881Speter svn_fs_t *fs, 751251881Speter svn_revnum_t rev, 752251881Speter apr_uint32_t flags, 753251881Speter apr_pool_t *pool) 754251881Speter{ 755251881Speter svn_fs_txn_t *txn; 756251881Speter struct begin_txn_args args; 757251881Speter 758251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 759251881Speter 760251881Speter args.txn_p = &txn; 761251881Speter args.base_rev = rev; 762251881Speter args.flags = flags; 763251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_begin_txn, &args, FALSE, pool)); 764251881Speter 765251881Speter *txn_p = txn; 766251881Speter 767299742Sdim return SVN_NO_ERROR; 768251881Speter} 769251881Speter 770251881Speter 771251881Speterstruct open_txn_args 772251881Speter{ 773251881Speter svn_fs_txn_t **txn_p; 774251881Speter const char *name; 775251881Speter}; 776251881Speter 777251881Speter 778251881Speterstatic svn_error_t * 779251881Spetertxn_body_open_txn(void *baton, trail_t *trail) 780251881Speter{ 781251881Speter struct open_txn_args *args = baton; 782251881Speter transaction_t *fstxn; 783251881Speter svn_revnum_t base_rev = SVN_INVALID_REVNUM; 784251881Speter const char *txn_id; 785251881Speter 786251881Speter SVN_ERR(get_txn(&fstxn, trail->fs, args->name, FALSE, trail, trail->pool)); 787251881Speter if (fstxn->kind != transaction_kind_committed) 788251881Speter { 789251881Speter txn_id = svn_fs_base__id_txn_id(fstxn->base_id); 790251881Speter SVN_ERR(svn_fs_base__txn_get_revision(&base_rev, trail->fs, txn_id, 791251881Speter trail, trail->pool)); 792251881Speter } 793251881Speter 794251881Speter *args->txn_p = make_txn(trail->fs, args->name, base_rev, trail->pool); 795251881Speter return SVN_NO_ERROR; 796251881Speter} 797251881Speter 798251881Speter 799251881Spetersvn_error_t * 800251881Spetersvn_fs_base__open_txn(svn_fs_txn_t **txn_p, 801251881Speter svn_fs_t *fs, 802251881Speter const char *name, 803251881Speter apr_pool_t *pool) 804251881Speter{ 805251881Speter svn_fs_txn_t *txn; 806251881Speter struct open_txn_args args; 807251881Speter 808251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 809251881Speter 810251881Speter args.txn_p = &txn; 811251881Speter args.name = name; 812251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_open_txn, &args, FALSE, pool)); 813251881Speter 814251881Speter *txn_p = txn; 815251881Speter return SVN_NO_ERROR; 816251881Speter} 817251881Speter 818251881Speter 819251881Speterstruct cleanup_txn_args 820251881Speter{ 821251881Speter transaction_t **txn_p; 822251881Speter const char *name; 823251881Speter}; 824251881Speter 825251881Speter 826251881Speterstatic svn_error_t * 827251881Spetertxn_body_cleanup_txn(void *baton, trail_t *trail) 828251881Speter{ 829251881Speter struct cleanup_txn_args *args = baton; 830251881Speter return get_txn(args->txn_p, trail->fs, args->name, TRUE, 831251881Speter trail, trail->pool); 832251881Speter} 833251881Speter 834251881Speter 835251881Speterstatic svn_error_t * 836251881Spetertxn_body_cleanup_txn_copy(void *baton, trail_t *trail) 837251881Speter{ 838251881Speter const char *copy_id = *(const char **)baton; 839251881Speter svn_error_t *err = svn_fs_bdb__delete_copy(trail->fs, copy_id, trail, 840251881Speter trail->pool); 841251881Speter 842251881Speter /* Copy doesn't exist? No sweat. */ 843251881Speter if (err && (err->apr_err == SVN_ERR_FS_NO_SUCH_COPY)) 844251881Speter { 845251881Speter svn_error_clear(err); 846251881Speter err = SVN_NO_ERROR; 847251881Speter } 848251881Speter return svn_error_trace(err); 849251881Speter} 850251881Speter 851251881Speter 852251881Speterstatic svn_error_t * 853251881Spetertxn_body_cleanup_txn_changes(void *baton, trail_t *trail) 854251881Speter{ 855251881Speter const char *key = *(const char **)baton; 856251881Speter 857251881Speter return svn_fs_bdb__changes_delete(trail->fs, key, trail, trail->pool); 858251881Speter} 859251881Speter 860251881Speter 861251881Speterstruct get_dirents_args 862251881Speter{ 863251881Speter apr_hash_t **dirents; 864251881Speter const svn_fs_id_t *id; 865251881Speter const char *txn_id; 866251881Speter}; 867251881Speter 868251881Speter 869251881Speterstatic svn_error_t * 870251881Spetertxn_body_get_dirents(void *baton, trail_t *trail) 871251881Speter{ 872251881Speter struct get_dirents_args *args = baton; 873251881Speter dag_node_t *node; 874251881Speter 875251881Speter /* Get the node. */ 876251881Speter SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id, 877251881Speter trail, trail->pool)); 878251881Speter 879251881Speter /* If immutable, do nothing and return. */ 880251881Speter if (! svn_fs_base__dag_check_mutable(node, args->txn_id)) 881251881Speter return SVN_NO_ERROR; 882251881Speter 883251881Speter /* If a directory, do nothing and return. */ 884251881Speter *(args->dirents) = NULL; 885251881Speter if (svn_fs_base__dag_node_kind(node) != svn_node_dir) 886251881Speter return SVN_NO_ERROR; 887251881Speter 888251881Speter /* Else it's mutable. Get its dirents. */ 889251881Speter return svn_fs_base__dag_dir_entries(args->dirents, node, 890251881Speter trail, trail->pool); 891251881Speter} 892251881Speter 893251881Speter 894251881Speterstruct remove_node_args 895251881Speter{ 896251881Speter const svn_fs_id_t *id; 897251881Speter const char *txn_id; 898251881Speter}; 899251881Speter 900251881Speter 901251881Speterstatic svn_error_t * 902251881Spetertxn_body_remove_node(void *baton, trail_t *trail) 903251881Speter{ 904251881Speter struct remove_node_args *args = baton; 905251881Speter return svn_fs_base__dag_remove_node(trail->fs, args->id, args->txn_id, 906251881Speter trail, trail->pool); 907251881Speter} 908251881Speter 909251881Speter 910251881Speterstatic svn_error_t * 911251881Speterdelete_txn_tree(svn_fs_t *fs, 912251881Speter const svn_fs_id_t *id, 913251881Speter const char *txn_id, 914251881Speter apr_pool_t *pool) 915251881Speter{ 916251881Speter struct get_dirents_args dirent_args; 917251881Speter struct remove_node_args rm_args; 918251881Speter apr_hash_t *dirents = NULL; 919251881Speter apr_hash_index_t *hi; 920251881Speter svn_error_t *err; 921251881Speter 922251881Speter /* If this sucker isn't mutable, there's nothing to do. */ 923299742Sdim if (strcmp(svn_fs_base__id_txn_id(id), txn_id) != 0) 924251881Speter return SVN_NO_ERROR; 925251881Speter 926251881Speter /* See if the thing has dirents that need to be recursed upon. If 927251881Speter you can't find the thing itself, don't sweat it. We probably 928251881Speter already cleaned it up. */ 929251881Speter dirent_args.dirents = &dirents; 930251881Speter dirent_args.id = id; 931251881Speter dirent_args.txn_id = txn_id; 932251881Speter err = svn_fs_base__retry_txn(fs, txn_body_get_dirents, &dirent_args, 933251881Speter FALSE, pool); 934251881Speter if (err && (err->apr_err == SVN_ERR_FS_ID_NOT_FOUND)) 935251881Speter { 936251881Speter svn_error_clear(err); 937251881Speter return SVN_NO_ERROR; 938251881Speter } 939251881Speter SVN_ERR(err); 940251881Speter 941251881Speter /* If there are dirents upon which to recurse ... recurse. */ 942251881Speter if (dirents) 943251881Speter { 944251881Speter apr_pool_t *subpool = svn_pool_create(pool); 945251881Speter 946251881Speter /* Loop over hash entries */ 947251881Speter for (hi = apr_hash_first(pool, dirents); hi; hi = apr_hash_next(hi)) 948251881Speter { 949251881Speter void *val; 950251881Speter svn_fs_dirent_t *dirent; 951251881Speter 952251881Speter svn_pool_clear(subpool); 953251881Speter apr_hash_this(hi, NULL, NULL, &val); 954251881Speter dirent = val; 955251881Speter SVN_ERR(delete_txn_tree(fs, dirent->id, txn_id, subpool)); 956251881Speter } 957251881Speter svn_pool_destroy(subpool); 958251881Speter } 959251881Speter 960251881Speter /* Remove the node. */ 961251881Speter rm_args.id = id; 962251881Speter rm_args.txn_id = txn_id; 963251881Speter return svn_fs_base__retry_txn(fs, txn_body_remove_node, &rm_args, 964251881Speter TRUE, pool); 965251881Speter} 966251881Speter 967251881Speter 968251881Speterstatic svn_error_t * 969251881Spetertxn_body_delete_txn(void *baton, trail_t *trail) 970251881Speter{ 971251881Speter const char *txn_id = *(const char **)baton; 972251881Speter 973251881Speter return svn_fs_bdb__delete_txn(trail->fs, txn_id, trail, trail->pool); 974251881Speter} 975251881Speter 976251881Speter 977251881Spetersvn_error_t * 978251881Spetersvn_fs_base__purge_txn(svn_fs_t *fs, 979251881Speter const char *txn_id, 980251881Speter apr_pool_t *pool) 981251881Speter{ 982251881Speter struct cleanup_txn_args args; 983251881Speter transaction_t *txn; 984251881Speter 985251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 986251881Speter 987251881Speter /* Open the transaction, expecting it to be dead. */ 988251881Speter args.txn_p = &txn; 989251881Speter args.name = txn_id; 990251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_cleanup_txn, &args, 991251881Speter FALSE, pool)); 992251881Speter 993251881Speter /* Delete the mutable portion of the tree hanging from the 994251881Speter transaction (which should gracefully recover if we've already 995251881Speter done this). */ 996251881Speter SVN_ERR(delete_txn_tree(fs, txn->root_id, txn_id, pool)); 997251881Speter 998251881Speter /* Kill the transaction's changes (which should gracefully recover 999251881Speter if...). */ 1000251881Speter SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_cleanup_txn_changes, 1001251881Speter &txn_id, TRUE, pool)); 1002251881Speter 1003251881Speter /* Kill the transaction's copies (which should gracefully...). */ 1004251881Speter if (txn->copies) 1005251881Speter { 1006251881Speter int i; 1007251881Speter 1008251881Speter for (i = 0; i < txn->copies->nelts; i++) 1009251881Speter { 1010251881Speter SVN_ERR(svn_fs_base__retry_txn 1011251881Speter (fs, txn_body_cleanup_txn_copy, 1012251881Speter &APR_ARRAY_IDX(txn->copies, i, const char *), 1013251881Speter TRUE, pool)); 1014251881Speter } 1015251881Speter } 1016251881Speter 1017251881Speter /* Kill the transaction itself (which ... just kidding -- this has 1018251881Speter no graceful failure mode). */ 1019251881Speter return svn_fs_base__retry_txn(fs, txn_body_delete_txn, &txn_id, 1020251881Speter TRUE, pool); 1021251881Speter} 1022251881Speter 1023251881Speter 1024251881Speterstatic svn_error_t * 1025251881Spetertxn_body_abort_txn(void *baton, trail_t *trail) 1026251881Speter{ 1027251881Speter svn_fs_txn_t *txn = baton; 1028251881Speter transaction_t *fstxn; 1029251881Speter 1030251881Speter /* Get the transaction by its id, set it to "dead", and store the 1031251881Speter transaction. */ 1032251881Speter SVN_ERR(get_txn(&fstxn, txn->fs, txn->id, FALSE, trail, trail->pool)); 1033251881Speter if (fstxn->kind != transaction_kind_normal) 1034251881Speter return svn_fs_base__err_txn_not_mutable(txn->fs, txn->id); 1035251881Speter 1036251881Speter fstxn->kind = transaction_kind_dead; 1037251881Speter return put_txn(txn->fs, fstxn, txn->id, trail, trail->pool); 1038251881Speter} 1039251881Speter 1040251881Speter 1041251881Spetersvn_error_t * 1042251881Spetersvn_fs_base__abort_txn(svn_fs_txn_t *txn, 1043251881Speter apr_pool_t *pool) 1044251881Speter{ 1045251881Speter SVN_ERR(svn_fs__check_fs(txn->fs, TRUE)); 1046251881Speter 1047251881Speter /* Set the transaction to "dead". */ 1048251881Speter SVN_ERR(svn_fs_base__retry_txn(txn->fs, txn_body_abort_txn, txn, 1049251881Speter TRUE, pool)); 1050251881Speter 1051251881Speter /* Now, purge it. */ 1052251881Speter SVN_ERR_W(svn_fs_base__purge_txn(txn->fs, txn->id, pool), 1053251881Speter _("Transaction aborted, but cleanup failed")); 1054251881Speter 1055251881Speter return SVN_NO_ERROR; 1056251881Speter} 1057251881Speter 1058251881Speter 1059251881Speterstruct list_transactions_args 1060251881Speter{ 1061251881Speter apr_array_header_t **names_p; 1062251881Speter apr_pool_t *pool; 1063251881Speter}; 1064251881Speter 1065251881Speterstatic svn_error_t * 1066251881Spetertxn_body_list_transactions(void* baton, trail_t *trail) 1067251881Speter{ 1068251881Speter struct list_transactions_args *args = baton; 1069251881Speter return svn_fs_bdb__get_txn_list(args->names_p, trail->fs, 1070251881Speter trail, args->pool); 1071251881Speter} 1072251881Speter 1073251881Spetersvn_error_t * 1074251881Spetersvn_fs_base__list_transactions(apr_array_header_t **names_p, 1075251881Speter svn_fs_t *fs, 1076251881Speter apr_pool_t *pool) 1077251881Speter{ 1078251881Speter apr_array_header_t *names; 1079251881Speter struct list_transactions_args args; 1080251881Speter 1081251881Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 1082251881Speter 1083251881Speter args.names_p = &names; 1084251881Speter args.pool = pool; 1085251881Speter SVN_ERR(svn_fs_base__retry(fs, txn_body_list_transactions, &args, 1086251881Speter FALSE, pool)); 1087251881Speter 1088251881Speter *names_p = names; 1089251881Speter return SVN_NO_ERROR; 1090251881Speter} 1091