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