source: valtobtest/subversion-1.6.2/subversion/libsvn_fs_base/revs-txns.c @ 3

Last change on this file since 3 was 3, checked in by valtob, 15 years ago

subversion source 1.6.2 as test

File size: 27.2 KB
Line 
1/* revs-txns.c : operations on revision and transactions
2 *
3 * ====================================================================
4 * Copyright (c) 2000-2007 CollabNet.  All rights reserved.
5 *
6 * This software is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution.  The terms
8 * are also available at http://subversion.tigris.org/license-1.html.
9 * If newer versions of this license are posted there, you may use a
10 * newer version instead, at your option.
11 *
12 * This software consists of voluntary contributions made by many
13 * individuals.  For exact contribution history, see the revision
14 * history and logs, available at http://subversion.tigris.org/.
15 * ====================================================================
16 */
17
18#include <string.h>
19
20#include <apr_tables.h>
21#include <apr_pools.h>
22
23#include "svn_pools.h"
24#include "svn_time.h"
25#include "svn_fs.h"
26#include "svn_props.h"
27#include "svn_hash.h"
28#include "svn_io.h"
29
30#include "fs.h"
31#include "dag.h"
32#include "err.h"
33#include "trail.h"
34#include "tree.h"
35#include "revs-txns.h"
36#include "key-gen.h"
37#include "id.h"
38#include "bdb/rev-table.h"
39#include "bdb/txn-table.h"
40#include "bdb/copies-table.h"
41#include "bdb/changes-table.h"
42#include "../libsvn_fs/fs-loader.h"
43
44#include "svn_private_config.h"
45#include "private/svn_fs_util.h"
46
47
48/*** Helpers ***/
49
50/* Set *txn_p to a transaction object allocated in POOL for the
51   transaction in FS whose id is TXN_ID.  If EXPECT_DEAD is set, this
52   transaction must be a dead one, else an error is returned.  If
53   EXPECT_DEAD is not set, an error is thrown if the transaction is
54   *not* dead. */
55static svn_error_t *
56get_txn(transaction_t **txn_p,
57        svn_fs_t *fs,
58        const char *txn_id,
59        svn_boolean_t expect_dead,
60        trail_t *trail,
61        apr_pool_t *pool)
62{
63  transaction_t *txn;
64  SVN_ERR(svn_fs_bdb__get_txn(&txn, fs, txn_id, trail, pool));
65  if (expect_dead && (txn->kind != transaction_kind_dead))
66    return svn_error_createf(SVN_ERR_FS_TRANSACTION_NOT_DEAD, 0,
67                             _("Transaction is not dead: '%s'"), txn_id);
68  if ((! expect_dead) && (txn->kind == transaction_kind_dead))
69    return svn_error_createf(SVN_ERR_FS_TRANSACTION_DEAD, 0,
70                             _("Transaction is dead: '%s'"), txn_id);
71  *txn_p = txn;
72  return SVN_NO_ERROR;
73}
74
75
76/* This is only for symmetry with the get_txn() helper. */
77#define put_txn svn_fs_bdb__put_txn
78
79
80
81/*** Revisions ***/
82
83/* Return the committed transaction record *TXN_P and its ID *TXN_ID
84   (as long as those parameters aren't NULL) for the revision REV in
85   FS as part of TRAIL.  */
86static svn_error_t *
87get_rev_txn(transaction_t **txn_p,
88            const char **txn_id,
89            svn_fs_t *fs,
90            svn_revnum_t rev,
91            trail_t *trail,
92            apr_pool_t *pool)
93{
94  revision_t *revision;
95  transaction_t *txn;
96
97  SVN_ERR(svn_fs_bdb__get_rev(&revision, fs, rev, trail, pool));
98  if (revision->txn_id == NULL)
99    return svn_fs_base__err_corrupt_fs_revision(fs, rev);
100
101  SVN_ERR(get_txn(&txn, fs, revision->txn_id, FALSE, trail, pool));
102  if (txn->revision != rev)
103    return svn_fs_base__err_corrupt_txn(fs, revision->txn_id);
104
105  if (txn_p)
106    *txn_p = txn;
107  if (txn_id)
108    *txn_id = revision->txn_id;
109  return SVN_NO_ERROR;
110}
111
112
113svn_error_t *
114svn_fs_base__rev_get_root(const svn_fs_id_t **root_id_p,
115                          svn_fs_t *fs,
116                          svn_revnum_t rev,
117                          trail_t *trail,
118                          apr_pool_t *pool)
119{
120  transaction_t *txn;
121
122  SVN_ERR(get_rev_txn(&txn, NULL, fs, rev, trail, pool));
123  if (txn->root_id == NULL)
124    return svn_fs_base__err_corrupt_fs_revision(fs, rev);
125
126  *root_id_p = txn->root_id;
127  return SVN_NO_ERROR;
128}
129
130
131svn_error_t *
132svn_fs_base__rev_get_txn_id(const char **txn_id_p,
133                            svn_fs_t *fs,
134                            svn_revnum_t rev,
135                            trail_t *trail,
136                            apr_pool_t *pool)
137{
138  revision_t *revision;
139
140  SVN_ERR(svn_fs_bdb__get_rev(&revision, fs, rev, trail, pool));
141  if (revision->txn_id == NULL)
142    return svn_fs_base__err_corrupt_fs_revision(fs, rev);
143
144  *txn_id_p = revision->txn_id;
145  return SVN_NO_ERROR;
146}
147
148
149static svn_error_t *
150txn_body_youngest_rev(void *baton, trail_t *trail)
151{
152  return svn_fs_bdb__youngest_rev(baton, trail->fs, trail, trail->pool);
153}
154
155
156svn_error_t *
157svn_fs_base__youngest_rev(svn_revnum_t *youngest_p,
158                          svn_fs_t *fs,
159                          apr_pool_t *pool)
160{
161  svn_revnum_t youngest;
162  SVN_ERR(svn_fs__check_fs(fs, TRUE));
163  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_youngest_rev, &youngest,
164                                 TRUE, pool));
165  *youngest_p = youngest;
166  return SVN_NO_ERROR;
167}
168
169
170struct revision_proplist_args {
171  apr_hash_t **table_p;
172  svn_revnum_t rev;
173};
174
175
176static svn_error_t *
177txn_body_revision_proplist(void *baton, trail_t *trail)
178{
179  struct revision_proplist_args *args = baton;
180  transaction_t *txn;
181
182  SVN_ERR(get_rev_txn(&txn, NULL, trail->fs, args->rev, trail, trail->pool));
183  *(args->table_p) = txn->proplist;
184  return SVN_NO_ERROR;
185}
186
187
188svn_error_t *
189svn_fs_base__revision_proplist(apr_hash_t **table_p,
190                               svn_fs_t *fs,
191                               svn_revnum_t rev,
192                               apr_pool_t *pool)
193{
194  struct revision_proplist_args args;
195  apr_hash_t *table;
196
197  SVN_ERR(svn_fs__check_fs(fs, TRUE));
198
199  args.table_p = &table;
200  args.rev = rev;
201  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_revision_proplist, &args,
202                                 FALSE, pool));
203
204  *table_p = table ? table : apr_hash_make(pool);
205  return SVN_NO_ERROR;
206}
207
208
209svn_error_t *
210svn_fs_base__revision_prop(svn_string_t **value_p,
211                           svn_fs_t *fs,
212                           svn_revnum_t rev,
213                           const char *propname,
214                           apr_pool_t *pool)
215{
216  struct revision_proplist_args args;
217  apr_hash_t *table;
218
219  SVN_ERR(svn_fs__check_fs(fs, TRUE));
220
221  /* Get the proplist. */
222  args.table_p = &table;
223  args.rev = rev;
224  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_revision_proplist, &args,
225                                 FALSE, pool));
226
227  /* And then the prop from that list (if there was a list). */
228  *value_p = NULL;
229  if (table)
230    *value_p = apr_hash_get(table, propname, APR_HASH_KEY_STRING);
231
232  return SVN_NO_ERROR;
233}
234
235
236svn_error_t *
237svn_fs_base__set_rev_prop(svn_fs_t *fs,
238                          svn_revnum_t rev,
239                          const char *name,
240                          const svn_string_t *value,
241                          trail_t *trail,
242                          apr_pool_t *pool)
243{
244  transaction_t *txn;
245  const char *txn_id;
246
247  SVN_ERR(get_rev_txn(&txn, &txn_id, fs, rev, trail, pool));
248
249  /* If there's no proplist, but we're just deleting a property, exit now. */
250  if ((! txn->proplist) && (! value))
251    return SVN_NO_ERROR;
252
253  /* Now, if there's no proplist, we know we need to make one. */
254  if (! txn->proplist)
255    txn->proplist = apr_hash_make(pool);
256
257  /* Set the property. */
258  apr_hash_set(txn->proplist, name, APR_HASH_KEY_STRING, value);
259
260  /* Overwrite the revision. */
261  return put_txn(fs, txn, txn_id, trail, pool);
262}
263
264
265struct change_rev_prop_args {
266  svn_revnum_t rev;
267  const char *name;
268  const svn_string_t *value;
269};
270
271
272static svn_error_t *
273txn_body_change_rev_prop(void *baton, trail_t *trail)
274{
275  struct change_rev_prop_args *args = baton;
276
277  return svn_fs_base__set_rev_prop(trail->fs, args->rev,
278                                   args->name, args->value,
279                                   trail, trail->pool);
280}
281
282
283svn_error_t *
284svn_fs_base__change_rev_prop(svn_fs_t *fs,
285                             svn_revnum_t rev,
286                             const char *name,
287                             const svn_string_t *value,
288                             apr_pool_t *pool)
289{
290  struct change_rev_prop_args args;
291
292  SVN_ERR(svn_fs__check_fs(fs, TRUE));
293
294  args.rev = rev;
295  args.name = name;
296  args.value = value;
297  return svn_fs_base__retry_txn(fs, txn_body_change_rev_prop, &args, 
298                                TRUE, pool);
299}
300
301
302
303/*** Transactions ***/
304
305svn_error_t *
306svn_fs_base__txn_make_committed(svn_fs_t *fs,
307                                const char *txn_name,
308                                svn_revnum_t revision,
309                                trail_t *trail,
310                                apr_pool_t *pool)
311{
312  transaction_t *txn;
313
314  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
315
316  /* Make sure the TXN is not committed already. */
317  SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
318  if (txn->kind != transaction_kind_normal)
319    return svn_fs_base__err_txn_not_mutable(fs, txn_name);
320
321  /* Convert TXN to a committed transaction. */
322  txn->base_id = NULL;
323  txn->revision = revision;
324  txn->kind = transaction_kind_committed;
325  return put_txn(fs, txn, txn_name, trail, pool);
326}
327
328
329svn_error_t *
330svn_fs_base__txn_get_revision(svn_revnum_t *revision,
331                              svn_fs_t *fs,
332                              const char *txn_name,
333                              trail_t *trail,
334                              apr_pool_t *pool)
335{
336  transaction_t *txn;
337  SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
338  *revision = txn->revision;
339  return SVN_NO_ERROR;
340}
341
342
343svn_error_t *
344svn_fs_base__get_txn_ids(const svn_fs_id_t **root_id_p,
345                         const svn_fs_id_t **base_root_id_p,
346                         svn_fs_t *fs,
347                         const char *txn_name,
348                         trail_t *trail,
349                         apr_pool_t *pool)
350{
351  transaction_t *txn;
352
353  SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
354  if (txn->kind != transaction_kind_normal)
355    return svn_fs_base__err_txn_not_mutable(fs, txn_name);
356
357  *root_id_p = txn->root_id;
358  *base_root_id_p = txn->base_id;
359  return SVN_NO_ERROR;
360}
361
362
363svn_error_t *
364svn_fs_base__set_txn_root(svn_fs_t *fs,
365                          const char *txn_name,
366                          const svn_fs_id_t *new_id,
367                          trail_t *trail,
368                          apr_pool_t *pool)
369{
370  transaction_t *txn;
371
372  SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
373  if (txn->kind != transaction_kind_normal)
374    return svn_fs_base__err_txn_not_mutable(fs, txn_name);
375
376  if (! svn_fs_base__id_eq(txn->root_id, new_id))
377    {
378      txn->root_id = new_id;
379      SVN_ERR(put_txn(fs, txn, txn_name, trail, pool));
380    }
381  return SVN_NO_ERROR;
382}
383
384
385svn_error_t *
386svn_fs_base__set_txn_base(svn_fs_t *fs,
387                          const char *txn_name,
388                          const svn_fs_id_t *new_id,
389                          trail_t *trail,
390                          apr_pool_t *pool)
391{
392  transaction_t *txn;
393
394  SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
395  if (txn->kind != transaction_kind_normal)
396    return svn_fs_base__err_txn_not_mutable(fs, txn_name);
397
398  if (! svn_fs_base__id_eq(txn->base_id, new_id))
399    {
400      txn->base_id = new_id;
401      SVN_ERR(put_txn(fs, txn, txn_name, trail, pool));
402    }
403  return SVN_NO_ERROR;
404}
405
406
407svn_error_t *
408svn_fs_base__add_txn_copy(svn_fs_t *fs,
409                          const char *txn_name,
410                          const char *copy_id,
411                          trail_t *trail,
412                          apr_pool_t *pool)
413{
414  transaction_t *txn;
415
416  /* Get the transaction and ensure its mutability. */
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);
420
421  /* Allocate a new array if this transaction has no copies. */
422  if (! txn->copies)
423    txn->copies = apr_array_make(pool, 1, sizeof(copy_id));
424
425  /* Add COPY_ID to the array. */
426  APR_ARRAY_PUSH(txn->copies, const char *) = copy_id;
427
428  /* Finally, write out the transaction. */
429  return put_txn(fs, txn, txn_name, trail, pool);
430}
431
432
433
434/* Generic transaction operations.  */
435
436struct txn_proplist_args {
437  apr_hash_t **table_p;
438  const char *id;
439};
440
441
442static svn_error_t *
443txn_body_txn_proplist(void *baton, trail_t *trail)
444{
445  transaction_t *txn;
446  struct txn_proplist_args *args = baton;
447
448  SVN_ERR(get_txn(&txn, trail->fs, args->id, FALSE, trail, trail->pool));
449  if (txn->kind != transaction_kind_normal)
450    return svn_fs_base__err_txn_not_mutable(trail->fs, args->id);
451
452  *(args->table_p) = txn->proplist;
453  return SVN_NO_ERROR;
454}
455
456
457
458svn_error_t *
459svn_fs_base__txn_proplist_in_trail(apr_hash_t **table_p,
460                                   const char *txn_id,
461                                   trail_t *trail)
462{
463  struct txn_proplist_args args;
464  apr_hash_t *table;
465
466  args.table_p = &table;
467  args.id = txn_id;
468  SVN_ERR(txn_body_txn_proplist(&args, trail));
469
470  *table_p = table ? table : apr_hash_make(trail->pool);
471  return SVN_NO_ERROR;
472}
473
474
475
476svn_error_t *
477svn_fs_base__txn_proplist(apr_hash_t **table_p,
478                          svn_fs_txn_t *txn,
479                          apr_pool_t *pool)
480{
481  struct txn_proplist_args args;
482  apr_hash_t *table;
483  svn_fs_t *fs = txn->fs;
484
485  SVN_ERR(svn_fs__check_fs(fs, TRUE));
486
487  args.table_p = &table;
488  args.id = txn->id;
489  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_proplist, &args,
490                                 FALSE, pool));
491
492  *table_p = table ? table : apr_hash_make(pool);
493  return SVN_NO_ERROR;
494}
495
496
497svn_error_t *
498svn_fs_base__txn_prop(svn_string_t **value_p,
499                      svn_fs_txn_t *txn,
500                      const char *propname,
501                      apr_pool_t *pool)
502{
503  struct txn_proplist_args args;
504  apr_hash_t *table;
505  svn_fs_t *fs = txn->fs;
506
507  SVN_ERR(svn_fs__check_fs(fs, TRUE));
508
509  /* Get the proplist. */
510  args.table_p = &table;
511  args.id = txn->id;
512  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_proplist, &args,
513                                 FALSE, pool));
514
515  /* And then the prop from that list (if there was a list). */
516  *value_p = NULL;
517  if (table)
518    *value_p = apr_hash_get(table, propname, APR_HASH_KEY_STRING);
519  return SVN_NO_ERROR;
520}
521
522
523
524struct change_txn_prop_args {
525  svn_fs_t *fs;
526  const char *id;
527  const char *name;
528  const svn_string_t *value;
529};
530
531
532svn_error_t *
533svn_fs_base__set_txn_prop(svn_fs_t *fs,
534                          const char *txn_name,
535                          const char *name,
536                          const svn_string_t *value,
537                          trail_t *trail,
538                          apr_pool_t *pool)
539{
540  transaction_t *txn;
541
542  SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
543  if (txn->kind != transaction_kind_normal)
544    return svn_fs_base__err_txn_not_mutable(fs, txn_name);
545
546  /* If there's no proplist, but we're just deleting a property, exit now. */
547  if ((! txn->proplist) && (! value))
548    return SVN_NO_ERROR;
549
550  /* Now, if there's no proplist, we know we need to make one. */
551  if (! txn->proplist)
552    txn->proplist = apr_hash_make(pool);
553
554  /* Set the property. */
555  apr_hash_set(txn->proplist, name, APR_HASH_KEY_STRING, value);
556
557  /* Now overwrite the transaction. */
558  return put_txn(fs, txn, txn_name, trail, pool);
559}
560
561
562static svn_error_t *
563txn_body_change_txn_prop(void *baton, trail_t *trail)
564{
565  struct change_txn_prop_args *args = baton;
566  return svn_fs_base__set_txn_prop(trail->fs, args->id, args->name,
567                                   args->value, trail, trail->pool);
568}
569
570
571svn_error_t *
572svn_fs_base__change_txn_prop(svn_fs_txn_t *txn,
573                             const char *name,
574                             const svn_string_t *value,
575                             apr_pool_t *pool)
576{
577  struct change_txn_prop_args args;
578  svn_fs_t *fs = txn->fs;
579
580  SVN_ERR(svn_fs__check_fs(fs, TRUE));
581
582  args.id = txn->id;
583  args.name = name;
584  args.value = value;
585  return svn_fs_base__retry_txn(fs, txn_body_change_txn_prop, &args,
586                                TRUE, pool);
587}
588
589
590svn_error_t *
591svn_fs_base__change_txn_props(svn_fs_txn_t *txn,
592                              apr_array_header_t *props,
593                              apr_pool_t *pool)
594{
595  apr_pool_t *iterpool = svn_pool_create(pool);
596  int i;
597
598  for (i = 0; i < props->nelts; i++)
599    {
600      svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
601
602      svn_pool_clear(iterpool);
603
604      SVN_ERR(svn_fs_base__change_txn_prop(txn, prop->name,
605                                           prop->value, iterpool));
606    }
607  svn_pool_destroy(iterpool);
608
609  return SVN_NO_ERROR;
610}
611
612
613/* Creating a transaction */
614
615static txn_vtable_t txn_vtable = {
616  svn_fs_base__commit_txn,
617  svn_fs_base__abort_txn,
618  svn_fs_base__txn_prop,
619  svn_fs_base__txn_proplist,
620  svn_fs_base__change_txn_prop,
621  svn_fs_base__txn_root,
622  svn_fs_base__change_txn_props
623};
624
625
626/* Allocate and return a new transaction object in POOL for FS whose
627   transaction ID is ID.  ID is not copied.  */
628static svn_fs_txn_t *
629make_txn(svn_fs_t *fs,
630         const char *id,
631         svn_revnum_t base_rev,
632         apr_pool_t *pool)
633{
634  svn_fs_txn_t *txn = apr_pcalloc(pool, sizeof(*txn));
635
636  txn->fs = fs;
637  txn->id = id;
638  txn->base_rev = base_rev;
639  txn->vtable = &txn_vtable;
640  txn->fsap_data = NULL;
641
642  return txn;
643}
644
645
646struct begin_txn_args
647{
648  svn_fs_txn_t **txn_p;
649  svn_revnum_t rev;
650  apr_uint32_t flags;
651};
652
653
654static svn_error_t *
655txn_body_begin_txn(void *baton, trail_t *trail)
656{
657  struct begin_txn_args *args = baton;
658  const svn_fs_id_t *root_id;
659  const char *txn_id;
660
661  SVN_ERR(svn_fs_base__rev_get_root(&root_id, trail->fs, args->rev,
662                                    trail, trail->pool));
663  SVN_ERR(svn_fs_bdb__create_txn(&txn_id, trail->fs, root_id,
664                                 trail, trail->pool));
665
666  if (args->flags & SVN_FS_TXN_CHECK_OOD)
667    {
668      struct change_txn_prop_args cpargs;
669      cpargs.fs = trail->fs;
670      cpargs.id = txn_id;
671      cpargs.name = SVN_FS__PROP_TXN_CHECK_OOD;
672      cpargs.value = svn_string_create("true", trail->pool);
673
674      SVN_ERR(txn_body_change_txn_prop(&cpargs, trail));
675    }
676
677  if (args->flags & SVN_FS_TXN_CHECK_LOCKS)
678    {
679      struct change_txn_prop_args cpargs;
680      cpargs.fs = trail->fs;
681      cpargs.id = txn_id;
682      cpargs.name = SVN_FS__PROP_TXN_CHECK_LOCKS;
683      cpargs.value = svn_string_create("true", trail->pool);
684
685      SVN_ERR(txn_body_change_txn_prop(&cpargs, trail));
686    }
687
688  *args->txn_p = make_txn(trail->fs, txn_id, args->rev, trail->pool);
689  return SVN_NO_ERROR;
690}
691
692
693
694
695/* Note:  it is acceptable for this function to call back into
696   public FS API interfaces because it does not itself use trails.  */
697svn_error_t *
698svn_fs_base__begin_txn(svn_fs_txn_t **txn_p,
699                       svn_fs_t *fs,
700                       svn_revnum_t rev,
701                       apr_uint32_t flags,
702                       apr_pool_t *pool)
703{
704  svn_fs_txn_t *txn;
705  struct begin_txn_args args;
706  svn_string_t date;
707
708  SVN_ERR(svn_fs__check_fs(fs, TRUE));
709
710  args.txn_p = &txn;
711  args.rev   = rev;
712  args.flags = flags;
713  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_begin_txn, &args, FALSE, pool));
714
715  *txn_p = txn;
716
717  /* Put a datestamp on the newly created txn, so we always know
718     exactly how old it is.  (This will help sysadmins identify
719     long-abandoned txns that may need to be manually removed.)  When
720     a txn is promoted to a revision, this property will be
721     automatically overwritten with a revision datestamp. */
722  date.data = svn_time_to_cstring(apr_time_now(), pool);
723  date.len = strlen(date.data);
724  return svn_fs_base__change_txn_prop(txn, SVN_PROP_REVISION_DATE,
725                                       &date, pool);
726}
727
728
729struct open_txn_args
730{
731  svn_fs_txn_t **txn_p;
732  const char *name;
733};
734
735
736static svn_error_t *
737txn_body_open_txn(void *baton, trail_t *trail)
738{
739  struct open_txn_args *args = baton;
740  transaction_t *fstxn;
741  svn_revnum_t base_rev = SVN_INVALID_REVNUM;
742  const char *txn_id;
743
744  SVN_ERR(get_txn(&fstxn, trail->fs, args->name, FALSE, trail, trail->pool));
745  if (fstxn->kind != transaction_kind_committed)
746    {
747      txn_id = svn_fs_base__id_txn_id(fstxn->base_id);
748      SVN_ERR(svn_fs_base__txn_get_revision(&base_rev, trail->fs, txn_id,
749                                            trail, trail->pool));
750    }
751
752  *args->txn_p = make_txn(trail->fs, args->name, base_rev, trail->pool);
753  return SVN_NO_ERROR;
754}
755
756
757svn_error_t *
758svn_fs_base__open_txn(svn_fs_txn_t **txn_p,
759                      svn_fs_t *fs,
760                      const char *name,
761                      apr_pool_t *pool)
762{
763  svn_fs_txn_t *txn;
764  struct open_txn_args args;
765
766  SVN_ERR(svn_fs__check_fs(fs, TRUE));
767
768  args.txn_p = &txn;
769  args.name = name;
770  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_open_txn, &args, FALSE, pool));
771
772  *txn_p = txn;
773  return SVN_NO_ERROR;
774}
775
776
777struct cleanup_txn_args
778{
779  transaction_t **txn_p;
780  const char *name;
781};
782
783
784static svn_error_t *
785txn_body_cleanup_txn(void *baton, trail_t *trail)
786{
787  struct cleanup_txn_args *args = baton;
788  return get_txn(args->txn_p, trail->fs, args->name, TRUE,
789                 trail, trail->pool);
790}
791
792
793static svn_error_t *
794txn_body_cleanup_txn_copy(void *baton, trail_t *trail)
795{
796  svn_error_t *err = svn_fs_bdb__delete_copy(trail->fs, baton, trail,
797                                             trail->pool);
798
799  /* Copy doesn't exist?  No sweat. */
800  if (err && (err->apr_err == SVN_ERR_FS_NO_SUCH_COPY))
801    {
802      svn_error_clear(err);
803      err = SVN_NO_ERROR;
804    }
805  return err;
806}
807
808
809static svn_error_t *
810txn_body_cleanup_txn_changes(void *baton, trail_t *trail)
811{
812  return svn_fs_bdb__changes_delete(trail->fs, baton, trail, trail->pool);
813}
814
815
816struct get_dirents_args
817{
818  apr_hash_t **dirents;
819  const svn_fs_id_t *id;
820  const char *txn_id;
821};
822
823
824static svn_error_t *
825txn_body_get_dirents(void *baton, trail_t *trail)
826{
827  struct get_dirents_args *args = baton;
828  dag_node_t *node;
829
830  /* Get the node. */
831  SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id,
832                                    trail, trail->pool));
833
834  /* If immutable, do nothing and return. */
835  if (! svn_fs_base__dag_check_mutable(node, args->txn_id))
836    return SVN_NO_ERROR;
837
838  /* If a directory, do nothing and return. */
839  *(args->dirents) = NULL;
840  if (svn_fs_base__dag_node_kind(node) != svn_node_dir)
841    return SVN_NO_ERROR;
842
843  /* Else it's mutable.  Get its dirents. */
844  return svn_fs_base__dag_dir_entries(args->dirents, node,
845                                      trail, trail->pool);
846}
847
848
849struct remove_node_args
850{
851  const svn_fs_id_t *id;
852  const char *txn_id;
853};
854
855
856static svn_error_t *
857txn_body_remove_node(void *baton, trail_t *trail)
858{
859  struct remove_node_args *args = baton;
860  return svn_fs_base__dag_remove_node(trail->fs, args->id, args->txn_id,
861                                      trail, trail->pool);
862}
863
864
865static svn_error_t *
866delete_txn_tree(svn_fs_t *fs,
867                const svn_fs_id_t *id,
868                const char *txn_id,
869                apr_pool_t *pool)
870{
871  struct get_dirents_args dirent_args;
872  struct remove_node_args rm_args;
873  apr_hash_t *dirents = NULL;
874  apr_hash_index_t *hi;
875  svn_error_t *err;
876
877  /* If this sucker isn't mutable, there's nothing to do. */
878  if (svn_fs_base__key_compare(svn_fs_base__id_txn_id(id), txn_id) != 0)
879    return SVN_NO_ERROR;
880
881  /* See if the thing has dirents that need to be recursed upon.  If
882     you can't find the thing itself, don't sweat it.  We probably
883     already cleaned it up. */
884  dirent_args.dirents = &dirents;
885  dirent_args.id = id;
886  dirent_args.txn_id = txn_id;
887  err = svn_fs_base__retry_txn(fs, txn_body_get_dirents, &dirent_args,
888                               FALSE, pool);
889  if (err && (err->apr_err == SVN_ERR_FS_ID_NOT_FOUND))
890    {
891      svn_error_clear(err);
892      return SVN_NO_ERROR;
893    }
894  SVN_ERR(err);
895
896  /* If there are dirents upon which to recurse ... recurse. */
897  if (dirents)
898    {
899      apr_pool_t *subpool = svn_pool_create(pool);
900
901      /* Loop over hash entries */
902      for (hi = apr_hash_first(pool, dirents); hi; hi = apr_hash_next(hi))
903        {
904          void *val;
905          svn_fs_dirent_t *dirent;
906
907          svn_pool_clear(subpool);
908          apr_hash_this(hi, NULL, NULL, &val);
909          dirent = val;
910          SVN_ERR(delete_txn_tree(fs, dirent->id, txn_id, subpool));
911        }
912      svn_pool_destroy(subpool);
913    }
914
915  /* Remove the node. */
916  rm_args.id = id;
917  rm_args.txn_id = txn_id;
918  return svn_fs_base__retry_txn(fs, txn_body_remove_node, &rm_args,
919                                TRUE, pool);
920}
921
922
923static svn_error_t *
924txn_body_delete_txn(void *baton, trail_t *trail)
925{
926  return svn_fs_bdb__delete_txn(trail->fs, baton, trail, trail->pool);
927}
928
929
930svn_error_t *
931svn_fs_base__purge_txn(svn_fs_t *fs,
932                       const char *txn_id,
933                       apr_pool_t *pool)
934{
935  struct cleanup_txn_args args;
936  transaction_t *txn;
937  int i;
938
939  SVN_ERR(svn_fs__check_fs(fs, TRUE));
940
941  /* Open the transaction, expecting it to be dead. */
942  args.txn_p = &txn;
943  args.name = txn_id;
944  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_cleanup_txn, &args,
945                                 FALSE, pool));
946
947  /* Delete the mutable portion of the tree hanging from the
948     transaction (which should gracefully recover if we've already
949     done this). */
950  SVN_ERR(delete_txn_tree(fs, txn->root_id, txn_id, pool));
951
952  /* Kill the transaction's changes (which should gracefully recover
953     if...). */
954  SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_cleanup_txn_changes,
955                                 (void *)txn_id, TRUE, pool));
956
957  /* Kill the transaction's copies (which should gracefully...). */
958  if (txn->copies)
959    {
960      for (i = 0; i < txn->copies->nelts; i++)
961        {
962          SVN_ERR(svn_fs_base__retry_txn
963                  (fs, txn_body_cleanup_txn_copy,
964                   (void *)APR_ARRAY_IDX(txn->copies, i, const char *),
965                   TRUE, pool));
966        }
967    }
968
969  /* Kill the transaction itself (which ... just kidding -- this has
970     no graceful failure mode). */
971  return svn_fs_base__retry_txn(fs, txn_body_delete_txn, (void *)txn_id,
972                                TRUE, pool);
973}
974
975
976static svn_error_t *
977txn_body_abort_txn(void *baton, trail_t *trail)
978{
979  svn_fs_txn_t *txn = baton;
980  transaction_t *fstxn;
981
982  /* Get the transaction by its id, set it to "dead", and store the
983     transaction. */
984  SVN_ERR(get_txn(&fstxn, txn->fs, txn->id, FALSE, trail, trail->pool));
985  if (fstxn->kind != transaction_kind_normal)
986    return svn_fs_base__err_txn_not_mutable(txn->fs, txn->id);
987
988  fstxn->kind = transaction_kind_dead;
989  return put_txn(txn->fs, fstxn, txn->id, trail, trail->pool);
990}
991
992
993svn_error_t *
994svn_fs_base__abort_txn(svn_fs_txn_t *txn,
995                       apr_pool_t *pool)
996{
997  SVN_ERR(svn_fs__check_fs(txn->fs, TRUE));
998
999  /* Set the transaction to "dead". */
1000  SVN_ERR(svn_fs_base__retry_txn(txn->fs, txn_body_abort_txn, txn,
1001                                 TRUE, pool));
1002
1003  /* Now, purge it. */
1004  SVN_ERR_W(svn_fs_base__purge_txn(txn->fs, txn->id, pool),
1005            _("Transaction aborted, but cleanup failed"));
1006
1007  return SVN_NO_ERROR;
1008}
1009
1010
1011struct list_transactions_args
1012{
1013  apr_array_header_t **names_p;
1014  apr_pool_t *pool;
1015};
1016
1017static svn_error_t *
1018txn_body_list_transactions(void* baton, trail_t *trail)
1019{
1020  struct list_transactions_args *args = baton;
1021  return svn_fs_bdb__get_txn_list(args->names_p, trail->fs,
1022                                  trail, args->pool);
1023}
1024
1025svn_error_t *
1026svn_fs_base__list_transactions(apr_array_header_t **names_p,
1027                               svn_fs_t *fs,
1028                               apr_pool_t *pool)
1029{
1030  apr_array_header_t *names;
1031  struct list_transactions_args args;
1032
1033  SVN_ERR(svn_fs__check_fs(fs, TRUE));
1034
1035  args.names_p = &names;
1036  args.pool = pool;
1037  SVN_ERR(svn_fs_base__retry(fs, txn_body_list_transactions, &args,
1038                             FALSE, pool));
1039
1040  *names_p = names;
1041  return SVN_NO_ERROR;
1042}
Note: See TracBrowser for help on using the repository browser.