1 /* revs-txns.c : operations on revision and transactions
3 * ====================================================================
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
20 * ====================================================================
25 #include <apr_tables.h>
26 #include <apr_pools.h>
28 #include "svn_pools.h"
31 #include "svn_props.h"
40 #include "revs-txns.h"
43 #include "bdb/rev-table.h"
44 #include "bdb/txn-table.h"
45 #include "bdb/copies-table.h"
46 #include "bdb/changes-table.h"
47 #include "../libsvn_fs/fs-loader.h"
49 #include "svn_private_config.h"
50 #include "private/svn_fs_util.h"
55 /* Set *txn_p to a transaction object allocated in POOL for the
56 transaction in FS whose id is TXN_ID. If EXPECT_DEAD is set, this
57 transaction must be a dead one, else an error is returned. If
58 EXPECT_DEAD is not set, the transaction must *not* be a dead one,
59 else an error is returned. */
61 get_txn(transaction_t **txn_p,
64 svn_boolean_t expect_dead,
69 SVN_ERR(svn_fs_bdb__get_txn(&txn, fs, txn_id, trail, pool));
70 if (expect_dead && (txn->kind != transaction_kind_dead))
71 return svn_error_createf(SVN_ERR_FS_TRANSACTION_NOT_DEAD, 0,
72 _("Transaction is not dead: '%s'"), txn_id);
73 if ((! expect_dead) && (txn->kind == transaction_kind_dead))
74 return svn_error_createf(SVN_ERR_FS_TRANSACTION_DEAD, 0,
75 _("Transaction is dead: '%s'"), txn_id);
81 /* This is only for symmetry with the get_txn() helper. */
82 #define put_txn svn_fs_bdb__put_txn
88 /* Return the committed transaction record *TXN_P and its ID *TXN_ID
89 (as long as those parameters aren't NULL) for the revision REV in
90 FS as part of TRAIL. */
92 get_rev_txn(transaction_t **txn_p,
102 SVN_ERR(svn_fs_bdb__get_rev(&revision, fs, rev, trail, pool));
103 if (revision->txn_id == NULL)
104 return svn_fs_base__err_corrupt_fs_revision(fs, rev);
106 SVN_ERR(get_txn(&txn, fs, revision->txn_id, FALSE, trail, pool));
107 if (txn->revision != rev)
108 return svn_fs_base__err_corrupt_txn(fs, revision->txn_id);
113 *txn_id = revision->txn_id;
119 svn_fs_base__rev_get_root(const svn_fs_id_t **root_id_p,
127 SVN_ERR(get_rev_txn(&txn, NULL, fs, rev, trail, pool));
128 if (txn->root_id == NULL)
129 return svn_fs_base__err_corrupt_fs_revision(fs, rev);
131 *root_id_p = txn->root_id;
137 svn_fs_base__rev_get_txn_id(const char **txn_id_p,
143 revision_t *revision;
145 SVN_ERR(svn_fs_bdb__get_rev(&revision, fs, rev, trail, pool));
146 if (revision->txn_id == NULL)
147 return svn_fs_base__err_corrupt_fs_revision(fs, rev);
149 *txn_id_p = revision->txn_id;
155 txn_body_youngest_rev(void *baton, trail_t *trail)
157 return svn_fs_bdb__youngest_rev(baton, trail->fs, trail, trail->pool);
162 svn_fs_base__youngest_rev(svn_revnum_t *youngest_p,
166 svn_revnum_t youngest;
167 SVN_ERR(svn_fs__check_fs(fs, TRUE));
168 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_youngest_rev, &youngest,
170 *youngest_p = youngest;
175 struct revision_proplist_args {
176 apr_hash_t **table_p;
182 txn_body_revision_proplist(void *baton, trail_t *trail)
184 struct revision_proplist_args *args = baton;
187 SVN_ERR(get_rev_txn(&txn, NULL, trail->fs, args->rev, trail, trail->pool));
188 *(args->table_p) = txn->proplist;
194 svn_fs_base__revision_proplist(apr_hash_t **table_p,
199 struct revision_proplist_args args;
202 SVN_ERR(svn_fs__check_fs(fs, TRUE));
204 args.table_p = &table;
206 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_revision_proplist, &args,
209 *table_p = table ? table : apr_hash_make(pool);
215 svn_fs_base__revision_prop(svn_string_t **value_p,
218 const char *propname,
221 struct revision_proplist_args args;
224 SVN_ERR(svn_fs__check_fs(fs, TRUE));
226 /* Get the proplist. */
227 args.table_p = &table;
229 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_revision_proplist, &args,
232 /* And then the prop from that list (if there was a list). */
233 *value_p = svn_hash_gets(table, propname);
240 svn_fs_base__set_rev_prop(svn_fs_t *fs,
243 const svn_string_t *const *old_value_p,
244 const svn_string_t *value,
251 SVN_ERR(get_rev_txn(&txn, &txn_id, fs, rev, trail, pool));
253 /* If there's no proplist, but we're just deleting a property, exit now. */
254 if ((! txn->proplist) && (! value))
257 /* Now, if there's no proplist, we know we need to make one. */
259 txn->proplist = apr_hash_make(pool);
261 /* Set the property. */
264 const svn_string_t *wanted_value = *old_value_p;
265 const svn_string_t *present_value = svn_hash_gets(txn->proplist, name);
266 if ((!wanted_value != !present_value)
267 || (wanted_value && present_value
268 && !svn_string_compare(wanted_value, present_value)))
270 /* What we expected isn't what we found. */
271 return svn_error_createf(SVN_ERR_FS_PROP_BASEVALUE_MISMATCH, NULL,
272 _("revprop '%s' has unexpected value in "
278 svn_hash_sets(txn->proplist, name, value);
280 /* Overwrite the revision. */
281 return put_txn(fs, txn, txn_id, trail, pool);
285 struct change_rev_prop_args {
288 const svn_string_t *const *old_value_p;
289 const svn_string_t *value;
294 txn_body_change_rev_prop(void *baton, trail_t *trail)
296 struct change_rev_prop_args *args = baton;
298 return svn_fs_base__set_rev_prop(trail->fs, args->rev,
299 args->name, args->old_value_p, args->value,
305 svn_fs_base__change_rev_prop(svn_fs_t *fs,
308 const svn_string_t *const *old_value_p,
309 const svn_string_t *value,
312 struct change_rev_prop_args args;
314 SVN_ERR(svn_fs__check_fs(fs, TRUE));
318 args.old_value_p = old_value_p;
320 return svn_fs_base__retry_txn(fs, txn_body_change_rev_prop, &args,
326 /*** Transactions ***/
329 svn_fs_base__txn_make_committed(svn_fs_t *fs,
330 const char *txn_name,
331 svn_revnum_t revision,
337 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
339 /* Make sure the TXN is not committed already. */
340 SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
341 if (txn->kind != transaction_kind_normal)
342 return svn_fs_base__err_txn_not_mutable(fs, txn_name);
344 /* Convert TXN to a committed transaction. */
346 txn->revision = revision;
347 txn->kind = transaction_kind_committed;
348 return put_txn(fs, txn, txn_name, trail, pool);
353 svn_fs_base__txn_get_revision(svn_revnum_t *revision,
355 const char *txn_name,
360 SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
361 *revision = txn->revision;
367 svn_fs_base__get_txn_ids(const svn_fs_id_t **root_id_p,
368 const svn_fs_id_t **base_root_id_p,
370 const char *txn_name,
376 SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
377 if (txn->kind != transaction_kind_normal)
378 return svn_fs_base__err_txn_not_mutable(fs, txn_name);
380 *root_id_p = txn->root_id;
381 *base_root_id_p = txn->base_id;
387 svn_fs_base__set_txn_root(svn_fs_t *fs,
388 const char *txn_name,
389 const svn_fs_id_t *new_id,
395 SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
396 if (txn->kind != transaction_kind_normal)
397 return svn_fs_base__err_txn_not_mutable(fs, txn_name);
399 if (! svn_fs_base__id_eq(txn->root_id, new_id))
401 txn->root_id = new_id;
402 SVN_ERR(put_txn(fs, txn, txn_name, trail, pool));
409 svn_fs_base__set_txn_base(svn_fs_t *fs,
410 const char *txn_name,
411 const svn_fs_id_t *new_id,
417 SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
418 if (txn->kind != transaction_kind_normal)
419 return svn_fs_base__err_txn_not_mutable(fs, txn_name);
421 if (! svn_fs_base__id_eq(txn->base_id, new_id))
423 txn->base_id = new_id;
424 SVN_ERR(put_txn(fs, txn, txn_name, trail, pool));
431 svn_fs_base__add_txn_copy(svn_fs_t *fs,
432 const char *txn_name,
439 /* Get the transaction and ensure its mutability. */
440 SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
441 if (txn->kind != transaction_kind_normal)
442 return svn_fs_base__err_txn_not_mutable(fs, txn_name);
444 /* Allocate a new array if this transaction has no copies. */
446 txn->copies = apr_array_make(pool, 1, sizeof(copy_id));
448 /* Add COPY_ID to the array. */
449 APR_ARRAY_PUSH(txn->copies, const char *) = copy_id;
451 /* Finally, write out the transaction. */
452 return put_txn(fs, txn, txn_name, trail, pool);
457 /* Generic transaction operations. */
459 struct txn_proplist_args {
460 apr_hash_t **table_p;
466 txn_body_txn_proplist(void *baton, trail_t *trail)
469 struct txn_proplist_args *args = baton;
471 SVN_ERR(get_txn(&txn, trail->fs, args->id, FALSE, trail, trail->pool));
472 if (txn->kind != transaction_kind_normal)
473 return svn_fs_base__err_txn_not_mutable(trail->fs, args->id);
475 *(args->table_p) = txn->proplist;
482 svn_fs_base__txn_proplist_in_trail(apr_hash_t **table_p,
486 struct txn_proplist_args args;
489 args.table_p = &table;
491 SVN_ERR(txn_body_txn_proplist(&args, trail));
493 *table_p = table ? table : apr_hash_make(trail->pool);
500 svn_fs_base__txn_proplist(apr_hash_t **table_p,
504 struct txn_proplist_args args;
506 svn_fs_t *fs = txn->fs;
508 SVN_ERR(svn_fs__check_fs(fs, TRUE));
510 args.table_p = &table;
512 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_proplist, &args,
515 *table_p = table ? table : apr_hash_make(pool);
521 svn_fs_base__txn_prop(svn_string_t **value_p,
523 const char *propname,
526 struct txn_proplist_args args;
528 svn_fs_t *fs = txn->fs;
530 SVN_ERR(svn_fs__check_fs(fs, TRUE));
532 /* Get the proplist. */
533 args.table_p = &table;
535 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_proplist, &args,
538 /* And then the prop from that list (if there was a list). */
539 *value_p = svn_hash_gets(table, propname);
546 struct change_txn_prop_args {
550 const svn_string_t *value;
555 svn_fs_base__set_txn_prop(svn_fs_t *fs,
556 const char *txn_name,
558 const svn_string_t *value,
564 SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
565 if (txn->kind != transaction_kind_normal)
566 return svn_fs_base__err_txn_not_mutable(fs, txn_name);
568 /* If there's no proplist, but we're just deleting a property, exit now. */
569 if ((! txn->proplist) && (! value))
572 /* Now, if there's no proplist, we know we need to make one. */
574 txn->proplist = apr_hash_make(pool);
576 /* Set the property. */
577 if (svn_hash_gets(txn->proplist, SVN_FS__PROP_TXN_CLIENT_DATE)
578 && !strcmp(name, SVN_PROP_REVISION_DATE))
579 svn_hash_sets(txn->proplist, SVN_FS__PROP_TXN_CLIENT_DATE,
580 svn_string_create("1", pool));
581 svn_hash_sets(txn->proplist, name, value);
583 /* Now overwrite the transaction. */
584 return put_txn(fs, txn, txn_name, trail, pool);
589 txn_body_change_txn_prop(void *baton, trail_t *trail)
591 struct change_txn_prop_args *args = baton;
592 return svn_fs_base__set_txn_prop(trail->fs, args->id, args->name,
593 args->value, trail, trail->pool);
598 svn_fs_base__change_txn_prop(svn_fs_txn_t *txn,
600 const svn_string_t *value,
603 struct change_txn_prop_args args;
604 svn_fs_t *fs = txn->fs;
606 SVN_ERR(svn_fs__check_fs(fs, TRUE));
611 return svn_fs_base__retry_txn(fs, txn_body_change_txn_prop, &args,
617 svn_fs_base__change_txn_props(svn_fs_txn_t *txn,
618 const apr_array_header_t *props,
621 apr_pool_t *iterpool = svn_pool_create(pool);
624 for (i = 0; i < props->nelts; i++)
626 svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
628 svn_pool_clear(iterpool);
630 SVN_ERR(svn_fs_base__change_txn_prop(txn, prop->name,
631 prop->value, iterpool));
633 svn_pool_destroy(iterpool);
639 /* Creating a transaction */
641 static txn_vtable_t txn_vtable = {
642 svn_fs_base__commit_txn,
643 svn_fs_base__abort_txn,
644 svn_fs_base__txn_prop,
645 svn_fs_base__txn_proplist,
646 svn_fs_base__change_txn_prop,
647 svn_fs_base__txn_root,
648 svn_fs_base__change_txn_props
652 /* Allocate and return a new transaction object in POOL for FS whose
653 transaction ID is ID. ID is not copied. */
654 static svn_fs_txn_t *
655 make_txn(svn_fs_t *fs,
657 svn_revnum_t base_rev,
660 svn_fs_txn_t *txn = apr_pcalloc(pool, sizeof(*txn));
664 txn->base_rev = base_rev;
665 txn->vtable = &txn_vtable;
666 txn->fsap_data = NULL;
672 struct begin_txn_args
674 svn_fs_txn_t **txn_p;
675 svn_revnum_t base_rev;
681 txn_body_begin_txn(void *baton, trail_t *trail)
683 struct begin_txn_args *args = baton;
684 const svn_fs_id_t *root_id;
687 SVN_ERR(svn_fs_base__rev_get_root(&root_id, trail->fs, args->base_rev,
688 trail, trail->pool));
689 SVN_ERR(svn_fs_bdb__create_txn(&txn_id, trail->fs, root_id,
690 trail, trail->pool));
692 if (args->flags & SVN_FS_TXN_CHECK_OOD)
694 struct change_txn_prop_args cpargs;
695 cpargs.fs = trail->fs;
697 cpargs.name = SVN_FS__PROP_TXN_CHECK_OOD;
698 cpargs.value = svn_string_create("true", trail->pool);
700 SVN_ERR(txn_body_change_txn_prop(&cpargs, trail));
703 if (args->flags & SVN_FS_TXN_CHECK_LOCKS)
705 struct change_txn_prop_args cpargs;
706 cpargs.fs = trail->fs;
708 cpargs.name = SVN_FS__PROP_TXN_CHECK_LOCKS;
709 cpargs.value = svn_string_create("true", trail->pool);
711 SVN_ERR(txn_body_change_txn_prop(&cpargs, trail));
714 /* Put a datestamp on the newly created txn, so we always know
715 exactly how old it is. (This will help sysadmins identify
716 long-abandoned txns that may need to be manually removed.) Do
717 this before setting CLIENT_DATE so that it is not recorded as an
720 struct change_txn_prop_args cpargs;
722 cpargs.fs = trail->fs;
724 cpargs.name = SVN_PROP_REVISION_DATE;
725 date.data = svn_time_to_cstring(apr_time_now(), trail->pool);
726 date.len = strlen(date.data);
727 cpargs.value = &date;
728 SVN_ERR(txn_body_change_txn_prop(&cpargs, trail));
731 if (args->flags & SVN_FS_TXN_CLIENT_DATE)
733 struct change_txn_prop_args cpargs;
734 cpargs.fs = trail->fs;
736 cpargs.name = SVN_FS__PROP_TXN_CLIENT_DATE;
737 cpargs.value = svn_string_create("0", trail->pool);
739 SVN_ERR(txn_body_change_txn_prop(&cpargs, trail));
742 *args->txn_p = make_txn(trail->fs, txn_id, args->base_rev, trail->pool);
746 /* Note: it is acceptable for this function to call back into
747 public FS API interfaces because it does not itself use trails. */
749 svn_fs_base__begin_txn(svn_fs_txn_t **txn_p,
756 struct begin_txn_args args;
758 SVN_ERR(svn_fs__check_fs(fs, TRUE));
763 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_begin_txn, &args, FALSE, pool));
773 svn_fs_txn_t **txn_p;
779 txn_body_open_txn(void *baton, trail_t *trail)
781 struct open_txn_args *args = baton;
782 transaction_t *fstxn;
783 svn_revnum_t base_rev = SVN_INVALID_REVNUM;
786 SVN_ERR(get_txn(&fstxn, trail->fs, args->name, FALSE, trail, trail->pool));
787 if (fstxn->kind != transaction_kind_committed)
789 txn_id = svn_fs_base__id_txn_id(fstxn->base_id);
790 SVN_ERR(svn_fs_base__txn_get_revision(&base_rev, trail->fs, txn_id,
791 trail, trail->pool));
794 *args->txn_p = make_txn(trail->fs, args->name, base_rev, trail->pool);
800 svn_fs_base__open_txn(svn_fs_txn_t **txn_p,
806 struct open_txn_args args;
808 SVN_ERR(svn_fs__check_fs(fs, TRUE));
812 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_open_txn, &args, FALSE, pool));
819 struct cleanup_txn_args
821 transaction_t **txn_p;
827 txn_body_cleanup_txn(void *baton, trail_t *trail)
829 struct cleanup_txn_args *args = baton;
830 return get_txn(args->txn_p, trail->fs, args->name, TRUE,
836 txn_body_cleanup_txn_copy(void *baton, trail_t *trail)
838 const char *copy_id = *(const char **)baton;
839 svn_error_t *err = svn_fs_bdb__delete_copy(trail->fs, copy_id, trail,
842 /* Copy doesn't exist? No sweat. */
843 if (err && (err->apr_err == SVN_ERR_FS_NO_SUCH_COPY))
845 svn_error_clear(err);
848 return svn_error_trace(err);
853 txn_body_cleanup_txn_changes(void *baton, trail_t *trail)
855 const char *key = *(const char **)baton;
857 return svn_fs_bdb__changes_delete(trail->fs, key, trail, trail->pool);
861 struct get_dirents_args
863 apr_hash_t **dirents;
864 const svn_fs_id_t *id;
870 txn_body_get_dirents(void *baton, trail_t *trail)
872 struct get_dirents_args *args = baton;
876 SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id,
877 trail, trail->pool));
879 /* If immutable, do nothing and return. */
880 if (! svn_fs_base__dag_check_mutable(node, args->txn_id))
883 /* If a directory, do nothing and return. */
884 *(args->dirents) = NULL;
885 if (svn_fs_base__dag_node_kind(node) != svn_node_dir)
888 /* Else it's mutable. Get its dirents. */
889 return svn_fs_base__dag_dir_entries(args->dirents, node,
894 struct remove_node_args
896 const svn_fs_id_t *id;
902 txn_body_remove_node(void *baton, trail_t *trail)
904 struct remove_node_args *args = baton;
905 return svn_fs_base__dag_remove_node(trail->fs, args->id, args->txn_id,
911 delete_txn_tree(svn_fs_t *fs,
912 const svn_fs_id_t *id,
916 struct get_dirents_args dirent_args;
917 struct remove_node_args rm_args;
918 apr_hash_t *dirents = NULL;
919 apr_hash_index_t *hi;
922 /* If this sucker isn't mutable, there's nothing to do. */
923 if (strcmp(svn_fs_base__id_txn_id(id), txn_id) != 0)
926 /* See if the thing has dirents that need to be recursed upon. If
927 you can't find the thing itself, don't sweat it. We probably
928 already cleaned it up. */
929 dirent_args.dirents = &dirents;
931 dirent_args.txn_id = txn_id;
932 err = svn_fs_base__retry_txn(fs, txn_body_get_dirents, &dirent_args,
934 if (err && (err->apr_err == SVN_ERR_FS_ID_NOT_FOUND))
936 svn_error_clear(err);
941 /* If there are dirents upon which to recurse ... recurse. */
944 apr_pool_t *subpool = svn_pool_create(pool);
946 /* Loop over hash entries */
947 for (hi = apr_hash_first(pool, dirents); hi; hi = apr_hash_next(hi))
950 svn_fs_dirent_t *dirent;
952 svn_pool_clear(subpool);
953 apr_hash_this(hi, NULL, NULL, &val);
955 SVN_ERR(delete_txn_tree(fs, dirent->id, txn_id, subpool));
957 svn_pool_destroy(subpool);
960 /* Remove the node. */
962 rm_args.txn_id = txn_id;
963 return svn_fs_base__retry_txn(fs, txn_body_remove_node, &rm_args,
969 txn_body_delete_txn(void *baton, trail_t *trail)
971 const char *txn_id = *(const char **)baton;
973 return svn_fs_bdb__delete_txn(trail->fs, txn_id, trail, trail->pool);
978 svn_fs_base__purge_txn(svn_fs_t *fs,
982 struct cleanup_txn_args args;
985 SVN_ERR(svn_fs__check_fs(fs, TRUE));
987 /* Open the transaction, expecting it to be dead. */
990 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_cleanup_txn, &args,
993 /* Delete the mutable portion of the tree hanging from the
994 transaction (which should gracefully recover if we've already
996 SVN_ERR(delete_txn_tree(fs, txn->root_id, txn_id, pool));
998 /* Kill the transaction's changes (which should gracefully recover
1000 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_cleanup_txn_changes,
1001 &txn_id, TRUE, pool));
1003 /* Kill the transaction's copies (which should gracefully...). */
1008 for (i = 0; i < txn->copies->nelts; i++)
1010 SVN_ERR(svn_fs_base__retry_txn
1011 (fs, txn_body_cleanup_txn_copy,
1012 &APR_ARRAY_IDX(txn->copies, i, const char *),
1017 /* Kill the transaction itself (which ... just kidding -- this has
1018 no graceful failure mode). */
1019 return svn_fs_base__retry_txn(fs, txn_body_delete_txn, &txn_id,
1024 static svn_error_t *
1025 txn_body_abort_txn(void *baton, trail_t *trail)
1027 svn_fs_txn_t *txn = baton;
1028 transaction_t *fstxn;
1030 /* Get the transaction by its id, set it to "dead", and store the
1032 SVN_ERR(get_txn(&fstxn, txn->fs, txn->id, FALSE, trail, trail->pool));
1033 if (fstxn->kind != transaction_kind_normal)
1034 return svn_fs_base__err_txn_not_mutable(txn->fs, txn->id);
1036 fstxn->kind = transaction_kind_dead;
1037 return put_txn(txn->fs, fstxn, txn->id, trail, trail->pool);
1042 svn_fs_base__abort_txn(svn_fs_txn_t *txn,
1045 SVN_ERR(svn_fs__check_fs(txn->fs, TRUE));
1047 /* Set the transaction to "dead". */
1048 SVN_ERR(svn_fs_base__retry_txn(txn->fs, txn_body_abort_txn, txn,
1051 /* Now, purge it. */
1052 SVN_ERR_W(svn_fs_base__purge_txn(txn->fs, txn->id, pool),
1053 _("Transaction aborted, but cleanup failed"));
1055 return SVN_NO_ERROR;
1059 struct list_transactions_args
1061 apr_array_header_t **names_p;
1065 static svn_error_t *
1066 txn_body_list_transactions(void* baton, trail_t *trail)
1068 struct list_transactions_args *args = baton;
1069 return svn_fs_bdb__get_txn_list(args->names_p, trail->fs,
1074 svn_fs_base__list_transactions(apr_array_header_t **names_p,
1078 apr_array_header_t *names;
1079 struct list_transactions_args args;
1081 SVN_ERR(svn_fs__check_fs(fs, TRUE));
1083 args.names_p = &names;
1085 SVN_ERR(svn_fs_base__retry(fs, txn_body_list_transactions, &args,
1089 return SVN_NO_ERROR;