1/* fs.c --- creating, opening and closing filesystems
2 *
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
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
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
19 *    under the License.
20 * ====================================================================
21 */
22
23#include <stdlib.h>
24#include <stdio.h>
25#include <string.h>
26
27#include <apr_general.h>
28#include <apr_pools.h>
29#include <apr_file_io.h>
30
31#include "svn_hash.h"
32#include "svn_pools.h"
33#include "svn_fs.h"
34#include "svn_path.h"
35#include "svn_utf.h"
36#include "svn_delta.h"
37#include "svn_version.h"
38#include "fs.h"
39#include "err.h"
40#include "dag.h"
41#include "revs-txns.h"
42#include "uuid.h"
43#include "tree.h"
44#include "id.h"
45#include "lock.h"
46#define SVN_WANT_BDB
47#include "svn_private_config.h"
48
49#include "bdb/bdb-err.h"
50#include "bdb/bdb_compat.h"
51#include "bdb/env.h"
52#include "bdb/nodes-table.h"
53#include "bdb/rev-table.h"
54#include "bdb/txn-table.h"
55#include "bdb/copies-table.h"
56#include "bdb/changes-table.h"
57#include "bdb/reps-table.h"
58#include "bdb/strings-table.h"
59#include "bdb/uuids-table.h"
60#include "bdb/locks-table.h"
61#include "bdb/lock-tokens-table.h"
62#include "bdb/node-origins-table.h"
63#include "bdb/miscellaneous-table.h"
64#include "bdb/checksum-reps-table.h"
65
66#include "../libsvn_fs/fs-loader.h"
67#include "private/svn_fs_util.h"
68#include "private/svn_subr_private.h"
69
70
71
72/* Checking for return values, and reporting errors.  */
73
74/* Check that we're using the right Berkeley DB version. */
75/* FIXME: This check should be abstracted into the DB back-end layer. */
76static svn_error_t *
77check_bdb_version(void)
78{
79  int major, minor, patch;
80
81  db_version(&major, &minor, &patch);
82
83  /* First, check that we're using a reasonably correct of Berkeley DB. */
84  if ((major < SVN_FS_WANT_DB_MAJOR)
85      || (major == SVN_FS_WANT_DB_MAJOR && minor < SVN_FS_WANT_DB_MINOR)
86      || (major == SVN_FS_WANT_DB_MAJOR && minor == SVN_FS_WANT_DB_MINOR
87          && patch < SVN_FS_WANT_DB_PATCH))
88    return svn_error_createf(SVN_ERR_FS_GENERAL, 0,
89                             _("Bad database version: got %d.%d.%d,"
90                               " should be at least %d.%d.%d"),
91                             major, minor, patch,
92                             SVN_FS_WANT_DB_MAJOR,
93                             SVN_FS_WANT_DB_MINOR,
94                             SVN_FS_WANT_DB_PATCH);
95
96  /* Now, check that the version we're running against is the same as
97     the one we compiled with. */
98  if (major != DB_VERSION_MAJOR || minor != DB_VERSION_MINOR)
99    return svn_error_createf(SVN_ERR_FS_GENERAL, 0,
100                             _("Bad database version:"
101                               " compiled with %d.%d.%d,"
102                               " running against %d.%d.%d"),
103                             DB_VERSION_MAJOR,
104                             DB_VERSION_MINOR,
105                             DB_VERSION_PATCH,
106                             major, minor, patch);
107  return SVN_NO_ERROR;
108}
109
110
111
112/* Cleanup functions.  */
113
114/* Close a database in the filesystem FS.
115   DB_PTR is a pointer to the DB pointer in *FS to close.
116   NAME is the name of the database, for use in error messages.  */
117static svn_error_t *
118cleanup_fs_db(svn_fs_t *fs, DB **db_ptr, const char *name)
119{
120  /* If the BDB environment is panicked, don't do anything, since
121     attempting to close the database will fail anyway. */
122  base_fs_data_t *bfd = fs->fsap_data;
123  if (*db_ptr && !svn_fs_bdb__get_panic(bfd->bdb))
124    {
125      DB *db = *db_ptr;
126      char *msg = apr_psprintf(fs->pool, "closing '%s' database", name);
127      int db_err;
128
129      *db_ptr = 0;
130      db_err = db->close(db, 0);
131      if (db_err == DB_RUNRECOVERY)
132        {
133          /* We can ignore DB_RUNRECOVERY errors from DB->close, but
134             must set the panic flag in the environment baton.  The
135             error will be propagated appropriately from
136             svn_fs_bdb__close. */
137          svn_fs_bdb__set_panic(bfd->bdb);
138          db_err = 0;
139        }
140
141#if SVN_BDB_HAS_DB_INCOMPLETE
142      /* We can ignore DB_INCOMPLETE on db->close and db->sync; it
143       * just means someone else was using the db at the same time
144       * we were.  See the Berkeley documentation at:
145       * http://www.sleepycat.com/docs/ref/program/errorret.html#DB_INCOMPLETE
146       * http://www.sleepycat.com/docs/api_c/db_close.html
147       */
148      if (db_err == DB_INCOMPLETE)
149        db_err = 0;
150#endif /* SVN_BDB_HAS_DB_INCOMPLETE */
151
152      SVN_ERR(BDB_WRAP(fs, msg, db_err));
153    }
154
155  return SVN_NO_ERROR;
156}
157
158/* Close whatever Berkeley DB resources are allocated to FS.  */
159static svn_error_t *
160cleanup_fs(svn_fs_t *fs)
161{
162  base_fs_data_t *bfd = fs->fsap_data;
163  bdb_env_baton_t *bdb = (bfd ? bfd->bdb : NULL);
164
165  if (!bdb)
166    return SVN_NO_ERROR;
167
168  /* Close the databases.  */
169  SVN_ERR(cleanup_fs_db(fs, &bfd->nodes, "nodes"));
170  SVN_ERR(cleanup_fs_db(fs, &bfd->revisions, "revisions"));
171  SVN_ERR(cleanup_fs_db(fs, &bfd->transactions, "transactions"));
172  SVN_ERR(cleanup_fs_db(fs, &bfd->copies, "copies"));
173  SVN_ERR(cleanup_fs_db(fs, &bfd->changes, "changes"));
174  SVN_ERR(cleanup_fs_db(fs, &bfd->representations, "representations"));
175  SVN_ERR(cleanup_fs_db(fs, &bfd->strings, "strings"));
176  SVN_ERR(cleanup_fs_db(fs, &bfd->uuids, "uuids"));
177  SVN_ERR(cleanup_fs_db(fs, &bfd->locks, "locks"));
178  SVN_ERR(cleanup_fs_db(fs, &bfd->lock_tokens, "lock-tokens"));
179  SVN_ERR(cleanup_fs_db(fs, &bfd->node_origins, "node-origins"));
180  SVN_ERR(cleanup_fs_db(fs, &bfd->checksum_reps, "checksum-reps"));
181  SVN_ERR(cleanup_fs_db(fs, &bfd->miscellaneous, "miscellaneous"));
182
183  /* Finally, close the environment.  */
184  bfd->bdb = 0;
185  {
186    svn_error_t *err = svn_fs_bdb__close(bdb);
187    if (err)
188      return svn_error_createf
189        (err->apr_err, err,
190         _("Berkeley DB error for filesystem '%s'"
191           " while closing environment:\n"),
192         fs->path);
193  }
194  return SVN_NO_ERROR;
195}
196
197#if 0   /* Set to 1 for instrumenting. */
198static void print_fs_stats(svn_fs_t *fs)
199{
200  base_fs_data_t *bfd = fs->fsap_data;
201  DB_TXN_STAT *t;
202  DB_LOCK_STAT *l;
203  int db_err;
204
205  /* Print transaction statistics for this DB env. */
206  if ((db_err = bfd->bdb->env->txn_stat(bfd->bdb->env, &t, 0)) != 0)
207    fprintf(stderr, "Error running bfd->bdb->env->txn_stat(): %s",
208            db_strerror(db_err));
209  else
210    {
211      printf("*** DB transaction stats, right before closing env:\n");
212      printf("   Number of transactions currently active: %d\n",
213             t->st_nactive);
214      printf("   Max number of active transactions at any one time: %d\n",
215             t->st_maxnactive);
216      printf("   Number of transactions that have begun: %d\n",
217             t->st_nbegins);
218      printf("   Number of transactions that have aborted: %d\n",
219             t->st_naborts);
220      printf("   Number of transactions that have committed: %d\n",
221             t->st_ncommits);
222      printf("   Number of times a thread was forced to wait: %d\n",
223             t->st_region_wait);
224      printf("   Number of times a thread didn't need to wait: %d\n",
225             t->st_region_nowait);
226      printf("*** End DB transaction stats.\n\n");
227    }
228
229  /* Print transaction statistics for this DB env. */
230  if ((db_err = bfd->bdb->env->lock_stat(bfd->bdb->env, &l, 0)) != 0)
231    fprintf(stderr, "Error running bfd->bdb->env->lock_stat(): %s",
232            db_strerror(db_err));
233  else
234    {
235      printf("*** DB lock stats, right before closing env:\n");
236      printf("   The number of current locks: %d\n",
237             l->st_nlocks);
238      printf("   Max number of locks at any one time: %d\n",
239             l->st_maxnlocks);
240      printf("   Number of current lockers: %d\n",
241             l->st_nlockers);
242      printf("   Max number of lockers at any one time: %d\n",
243             l->st_maxnlockers);
244      printf("   Number of current objects: %d\n",
245             l->st_nobjects);
246      printf("   Max number of objects at any one time: %d\n",
247             l->st_maxnobjects);
248      printf("   Total number of locks requested: %d\n",
249             l->st_nrequests);
250      printf("   Total number of locks released: %d\n",
251             l->st_nreleases);
252      printf("   Total number of lock reqs failed because "
253             "DB_LOCK_NOWAIT was set: %d\n", l->st_nnowaits);
254      printf("   Total number of locks not immediately available "
255             "due to conflicts: %d\n", l->st_nconflicts);
256      printf("   Number of deadlocks detected: %d\n", l->st_ndeadlocks);
257      printf("   Number of times a thread waited before "
258             "obtaining the region lock: %d\n", l->st_region_wait);
259      printf("   Number of times a thread didn't have to wait: %d\n",
260             l->st_region_nowait);
261      printf("*** End DB lock stats.\n\n");
262    }
263
264}
265#else
266#  define print_fs_stats(fs)
267#endif /* 0/1 */
268
269/* An APR pool cleanup function for a filesystem.  DATA must be a
270   pointer to the filesystem to clean up.
271
272   When the filesystem object's pool is freed, we want the resources
273   held by Berkeley DB to go away, just like everything else.  So we
274   register this cleanup function with the filesystem's pool, and let
275   it take care of closing the databases, the environment, and any
276   other DB objects we might be using.  APR calls this function before
277   actually freeing the pool's memory.
278
279   It's a pity that we can't return an svn_error_t object from an APR
280   cleanup function.  For now, we return the rather generic
281   SVN_ERR_FS_CLEANUP, and pass the real svn_error_t to the registered
282   warning callback.  */
283
284static apr_status_t
285cleanup_fs_apr(void *data)
286{
287  svn_fs_t *fs = data;
288  svn_error_t *err;
289
290  print_fs_stats(fs);
291
292  err = cleanup_fs(fs);
293  if (! err)
294    return APR_SUCCESS;
295
296  /* Darn. An error during cleanup. Call the warning handler to
297     try and do something "right" with this error. Note that
298     the default will simply abort().  */
299  (*fs->warning)(fs->warning_baton, err);
300
301  svn_error_clear(err);
302
303  return SVN_ERR_FS_CLEANUP;
304}
305
306
307static svn_error_t *
308base_bdb_set_errcall(svn_fs_t *fs,
309                     void (*db_errcall_fcn)(const char *errpfx, char *msg))
310{
311  base_fs_data_t *bfd = fs->fsap_data;
312
313  SVN_ERR(svn_fs__check_fs(fs, TRUE));
314  bfd->bdb->error_info->user_callback = db_errcall_fcn;
315
316  return SVN_NO_ERROR;
317}
318
319
320/* Write the DB_CONFIG file. */
321static svn_error_t *
322bdb_write_config(svn_fs_t *fs)
323{
324  const char *dbconfig_file_name =
325    svn_dirent_join(fs->path, BDB_CONFIG_FILE, fs->pool);
326  apr_file_t *dbconfig_file = NULL;
327  int i;
328
329  static const char dbconfig_contents[] =
330    "# This is the configuration file for the Berkeley DB environment\n"
331    "# used by your Subversion repository.\n"
332    "# You must run 'svnadmin recover' whenever you modify this file,\n"
333    "# for your changes to take effect.\n"
334    "\n"
335    "### Lock subsystem\n"
336    "#\n"
337    "# Make sure you read the documentation at:\n"
338    "#\n"
339    "#   http://docs.oracle.com/cd/E17076_02/html/programmer_reference/lock_max.html\n"
340    "#\n"
341    "# before tweaking these values.\n"
342    "#\n"
343    "set_lk_max_locks   2000\n"
344    "set_lk_max_lockers 2000\n"
345    "set_lk_max_objects 2000\n"
346    "\n"
347    "### Log file subsystem\n"
348    "#\n"
349    "# Make sure you read the documentation at:\n"
350    "#\n"
351    "#   http://docs.oracle.com/cd/E17076_02/html/api_reference/C/envset_lg_bsize.html\n"
352    "#   http://docs.oracle.com/cd/E17076_02/html/api_reference/C/envset_lg_max.html\n"
353    "#   http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_limits.html\n"
354    "#\n"
355    "# Increase the size of the in-memory log buffer from the default\n"
356    "# of 32 Kbytes to 256 Kbytes.  Decrease the log file size from\n"
357    "# 10 Mbytes to 1 Mbyte.  This will help reduce the amount of disk\n"
358    "# space required for hot backups.  The size of the log file must be\n"
359    "# at least four times the size of the in-memory log buffer.\n"
360    "#\n"
361    "# Note: Decreasing the in-memory buffer size below 256 Kbytes will hurt\n"
362    "# hurt commit performance. For details, see:\n"
363    "#\n"
364    "#   http://svn.haxx.se/dev/archive-2002-02/0141.shtml\n"
365    "#\n"
366    "set_lg_bsize     262144\n"
367    "set_lg_max      1048576\n"
368    "#\n"
369    "# If you see \"log region out of memory\" errors, bump lg_regionmax.\n"
370    "# For more information, see:\n"
371    "#\n"
372    "#   http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_config.html\n"
373    "#   http://svn.haxx.se/users/archive-2004-10/1000.shtml\n"
374    "#\n"
375    "set_lg_regionmax 131072\n"
376    "#\n"
377    /* ### Configure this with "svnadmin create --bdb-cache-size" */
378    "# The default cache size in BDB is only 256k. As explained in\n"
379    "# http://svn.haxx.se/dev/archive-2004-12/0368.shtml, this is too\n"
380    "# small for most applications. Bump this number if \"db_stat -m\"\n"
381    "# shows too many cache misses.\n"
382    "#\n"
383    "set_cachesize    0 1048576 1\n";
384
385  /* Run-time configurable options.
386     Each option set consists of a minimum required BDB version, a
387     config hash key, a header, an inactive form and an active
388     form. We always write the header; then, depending on the
389     run-time configuration and the BDB version we're compiling
390     against, we write either the active or inactive form of the
391     value. */
392  static const struct
393  {
394    int bdb_major;
395    int bdb_minor;
396    const char *config_key;
397    const char *header;
398    const char *inactive;
399    const char *active;
400  } dbconfig_options[] = {
401    /* Controlled by "svnadmin create --bdb-txn-nosync" */
402    { 4, 0, SVN_FS_CONFIG_BDB_TXN_NOSYNC,
403      /* header */
404      "#\n"
405      "# Disable fsync of log files on transaction commit. Read the\n"
406      "# documentation about DB_TXN_NOSYNC at:\n"
407      "#\n"
408      "#   http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_config.html\n"
409      "#\n"
410      "# [requires Berkeley DB 4.0]\n"
411      "#\n",
412      /* inactive */
413      "#set_flags DB_TXN_NOSYNC\n",
414      /* active */
415      "set_flags DB_TXN_NOSYNC\n" },
416    /* Controlled by "svnadmin create --bdb-log-keep" */
417    { 4, 2, SVN_FS_CONFIG_BDB_LOG_AUTOREMOVE,
418      /* header */
419      "#\n"
420      "# Enable automatic removal of unused transaction log files.\n"
421      "# Read the documentation about DB_LOG_AUTOREMOVE at:\n"
422      "#\n"
423      "#   http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_config.html\n"
424      "#\n"
425      "# [requires Berkeley DB 4.2]\n"
426      "#\n",
427      /* inactive */
428      "#set_flags DB_LOG_AUTOREMOVE\n",
429      /* active */
430      "set_flags DB_LOG_AUTOREMOVE\n" },
431  };
432  static const int dbconfig_options_length =
433    sizeof(dbconfig_options)/sizeof(*dbconfig_options);
434
435
436  SVN_ERR(svn_io_file_open(&dbconfig_file, dbconfig_file_name,
437                           APR_WRITE | APR_CREATE, APR_OS_DEFAULT,
438                           fs->pool));
439
440  SVN_ERR(svn_io_file_write_full(dbconfig_file, dbconfig_contents,
441                                 sizeof(dbconfig_contents) - 1, NULL,
442                                 fs->pool));
443
444  /* Write the variable DB_CONFIG flags. */
445  for (i = 0; i < dbconfig_options_length; ++i)
446    {
447      void *value = NULL;
448      const char *choice;
449
450      if (fs->config)
451        {
452          value = svn_hash_gets(fs->config, dbconfig_options[i].config_key);
453        }
454
455      SVN_ERR(svn_io_file_write_full(dbconfig_file,
456                                     dbconfig_options[i].header,
457                                     strlen(dbconfig_options[i].header),
458                                     NULL, fs->pool));
459
460      if (((DB_VERSION_MAJOR == dbconfig_options[i].bdb_major
461            && DB_VERSION_MINOR >= dbconfig_options[i].bdb_minor)
462           || DB_VERSION_MAJOR > dbconfig_options[i].bdb_major)
463          && value != NULL && strcmp(value, "0") != 0)
464        choice = dbconfig_options[i].active;
465      else
466        choice = dbconfig_options[i].inactive;
467
468      SVN_ERR(svn_io_file_write_full(dbconfig_file, choice, strlen(choice),
469                                     NULL, fs->pool));
470    }
471
472  return svn_io_file_close(dbconfig_file, fs->pool);
473}
474
475static svn_error_t *
476base_bdb_verify_root(svn_fs_root_t *root,
477                     apr_pool_t *scratch_pool)
478{
479  /* Verifying is currently a no op for BDB. */
480  return SVN_NO_ERROR;
481}
482
483static svn_error_t *
484base_bdb_freeze(svn_fs_t *fs,
485                svn_fs_freeze_func_t freeze_func,
486                void *freeze_baton,
487                apr_pool_t *pool)
488{
489  SVN__NOT_IMPLEMENTED();
490}
491
492
493/* Creating a new filesystem */
494
495static fs_vtable_t fs_vtable = {
496  svn_fs_base__youngest_rev,
497  svn_fs_base__revision_prop,
498  svn_fs_base__revision_proplist,
499  svn_fs_base__change_rev_prop,
500  svn_fs_base__set_uuid,
501  svn_fs_base__revision_root,
502  svn_fs_base__begin_txn,
503  svn_fs_base__open_txn,
504  svn_fs_base__purge_txn,
505  svn_fs_base__list_transactions,
506  svn_fs_base__deltify,
507  svn_fs_base__lock,
508  svn_fs_base__generate_lock_token,
509  svn_fs_base__unlock,
510  svn_fs_base__get_lock,
511  svn_fs_base__get_locks,
512  base_bdb_verify_root,
513  base_bdb_freeze,
514  base_bdb_set_errcall,
515};
516
517/* Where the format number is stored. */
518#define FORMAT_FILE   "format"
519
520/* Depending on CREATE, create or open the environment and databases
521   for filesystem FS in PATH. Use POOL for temporary allocations. */
522static svn_error_t *
523open_databases(svn_fs_t *fs,
524               svn_boolean_t create,
525               int format,
526               const char *path,
527               apr_pool_t *pool)
528{
529  base_fs_data_t *bfd;
530
531  SVN_ERR(svn_fs__check_fs(fs, FALSE));
532
533  bfd = apr_pcalloc(fs->pool, sizeof(*bfd));
534  fs->vtable = &fs_vtable;
535  fs->fsap_data = bfd;
536
537  /* Initialize the fs's path. */
538  fs->path = apr_pstrdup(fs->pool, path);
539
540  if (create)
541    SVN_ERR(bdb_write_config(fs));
542
543  /* Create the Berkeley DB environment.  */
544  {
545    svn_error_t *err = svn_fs_bdb__open(&(bfd->bdb), path,
546                                        SVN_BDB_STANDARD_ENV_FLAGS,
547                                        0666, fs->pool);
548    if (err)
549      {
550        if (create)
551          return svn_error_createf
552            (err->apr_err, err,
553             _("Berkeley DB error for filesystem '%s'"
554               " while creating environment:\n"),
555             fs->path);
556        else
557          return svn_error_createf
558            (err->apr_err, err,
559             _("Berkeley DB error for filesystem '%s'"
560               " while opening environment:\n"),
561             fs->path);
562      }
563  }
564
565  /* We must register the FS cleanup function *after* opening the
566     environment, so that it's run before the environment baton
567     cleanup. */
568  apr_pool_cleanup_register(fs->pool, fs, cleanup_fs_apr,
569                            apr_pool_cleanup_null);
570
571
572  /* Create the databases in the environment.  */
573  SVN_ERR(BDB_WRAP(fs, (create
574                        ? N_("creating 'nodes' table")
575                        : N_("opening 'nodes' table")),
576                   svn_fs_bdb__open_nodes_table(&bfd->nodes,
577                                                bfd->bdb->env,
578                                                create)));
579  SVN_ERR(BDB_WRAP(fs, (create
580                        ? N_("creating 'revisions' table")
581                        : N_("opening 'revisions' table")),
582                   svn_fs_bdb__open_revisions_table(&bfd->revisions,
583                                                    bfd->bdb->env,
584                                                    create)));
585  SVN_ERR(BDB_WRAP(fs, (create
586                        ? N_("creating 'transactions' table")
587                        : N_("opening 'transactions' table")),
588                   svn_fs_bdb__open_transactions_table(&bfd->transactions,
589                                                       bfd->bdb->env,
590                                                       create)));
591  SVN_ERR(BDB_WRAP(fs, (create
592                        ? N_("creating 'copies' table")
593                        : N_("opening 'copies' table")),
594                   svn_fs_bdb__open_copies_table(&bfd->copies,
595                                                 bfd->bdb->env,
596                                                 create)));
597  SVN_ERR(BDB_WRAP(fs, (create
598                        ? N_("creating 'changes' table")
599                        : N_("opening 'changes' table")),
600                   svn_fs_bdb__open_changes_table(&bfd->changes,
601                                                  bfd->bdb->env,
602                                                  create)));
603  SVN_ERR(BDB_WRAP(fs, (create
604                        ? N_("creating 'representations' table")
605                        : N_("opening 'representations' table")),
606                   svn_fs_bdb__open_reps_table(&bfd->representations,
607                                               bfd->bdb->env,
608                                               create)));
609  SVN_ERR(BDB_WRAP(fs, (create
610                        ? N_("creating 'strings' table")
611                        : N_("opening 'strings' table")),
612                   svn_fs_bdb__open_strings_table(&bfd->strings,
613                                                  bfd->bdb->env,
614                                                  create)));
615  SVN_ERR(BDB_WRAP(fs, (create
616                        ? N_("creating 'uuids' table")
617                        : N_("opening 'uuids' table")),
618                   svn_fs_bdb__open_uuids_table(&bfd->uuids,
619                                                bfd->bdb->env,
620                                                create)));
621  SVN_ERR(BDB_WRAP(fs, (create
622                        ? N_("creating 'locks' table")
623                        : N_("opening 'locks' table")),
624                   svn_fs_bdb__open_locks_table(&bfd->locks,
625                                                bfd->bdb->env,
626                                                create)));
627  SVN_ERR(BDB_WRAP(fs, (create
628                        ? N_("creating 'lock-tokens' table")
629                        : N_("opening 'lock-tokens' table")),
630                   svn_fs_bdb__open_lock_tokens_table(&bfd->lock_tokens,
631                                                      bfd->bdb->env,
632                                                      create)));
633
634  if (format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT)
635    {
636      SVN_ERR(BDB_WRAP(fs, (create
637                            ? N_("creating 'node-origins' table")
638                            : N_("opening 'node-origins' table")),
639                       svn_fs_bdb__open_node_origins_table(&bfd->node_origins,
640                                                           bfd->bdb->env,
641                                                           create)));
642    }
643
644  if (format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT)
645    {
646      SVN_ERR(BDB_WRAP(fs, (create
647                            ? N_("creating 'miscellaneous' table")
648                            : N_("opening 'miscellaneous' table")),
649                       svn_fs_bdb__open_miscellaneous_table(&bfd->miscellaneous,
650                                                            bfd->bdb->env,
651                                                            create)));
652    }
653
654  if (format >= SVN_FS_BASE__MIN_REP_SHARING_FORMAT)
655    {
656      SVN_ERR(BDB_WRAP(fs, (create
657                            ? N_("creating 'checksum-reps' table")
658                            : N_("opening 'checksum-reps' table")),
659                       svn_fs_bdb__open_checksum_reps_table(&bfd->checksum_reps,
660                                                            bfd->bdb->env,
661                                                            create)));
662    }
663
664  return SVN_NO_ERROR;
665}
666
667
668/* Called by functions that initialize an svn_fs_t struct, after that
669   initialization is done, to populate svn_fs_t->uuid. */
670static svn_error_t *
671populate_opened_fs(svn_fs_t *fs, apr_pool_t *scratch_pool)
672{
673  SVN_ERR(svn_fs_base__populate_uuid(fs, scratch_pool));
674  return SVN_NO_ERROR;
675}
676
677static svn_error_t *
678base_create(svn_fs_t *fs, const char *path, apr_pool_t *pool,
679            apr_pool_t *common_pool)
680{
681  int format = SVN_FS_BASE__FORMAT_NUMBER;
682  svn_error_t *svn_err;
683
684  /* See if compatibility with older versions was explicitly requested. */
685  if (fs->config)
686    {
687      if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE))
688        format = 1;
689      else if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE))
690        format = 2;
691      else if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE))
692        format = 3;
693    }
694
695  /* Create the environment and databases. */
696  svn_err = open_databases(fs, TRUE, format, path, pool);
697  if (svn_err) goto error;
698
699  /* Initialize the DAG subsystem. */
700  svn_err = svn_fs_base__dag_init_fs(fs);
701  if (svn_err) goto error;
702
703  /* This filesystem is ready.  Stamp it with a format number. */
704  svn_err = svn_io_write_version_file(
705   svn_dirent_join(fs->path, FORMAT_FILE, pool), format, pool);
706  if (svn_err) goto error;
707
708  ((base_fs_data_t *) fs->fsap_data)->format = format;
709
710  SVN_ERR(populate_opened_fs(fs, pool));
711  return SVN_NO_ERROR;;
712
713error:
714  svn_error_clear(cleanup_fs(fs));
715  return svn_err;
716}
717
718
719/* Gaining access to an existing Berkeley DB-based filesystem.  */
720
721svn_error_t *
722svn_fs_base__test_required_feature_format(svn_fs_t *fs,
723                                          const char *feature,
724                                          int requires)
725{
726  base_fs_data_t *bfd = fs->fsap_data;
727  if (bfd->format < requires)
728    return svn_error_createf
729      (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
730       _("The '%s' feature requires version %d of the filesystem schema; "
731         "filesystem '%s' uses only version %d"),
732       feature, requires, fs->path, bfd->format);
733  return SVN_NO_ERROR;
734}
735
736/* Return the error SVN_ERR_FS_UNSUPPORTED_FORMAT if FS's format
737   number is not the same as the format number supported by this
738   Subversion. */
739static svn_error_t *
740check_format(int format)
741{
742  /* We currently support any format less than the compiled format number
743     simultaneously.  */
744  if (format <= SVN_FS_BASE__FORMAT_NUMBER)
745    return SVN_NO_ERROR;
746
747  return svn_error_createf(
748        SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
749        _("Expected FS format '%d'; found format '%d'"),
750        SVN_FS_BASE__FORMAT_NUMBER, format);
751}
752
753static svn_error_t *
754base_open(svn_fs_t *fs, const char *path, apr_pool_t *pool,
755          apr_pool_t *common_pool)
756{
757  int format;
758  svn_error_t *svn_err;
759  svn_boolean_t write_format_file = FALSE;
760
761  /* Read the FS format number. */
762  svn_err = svn_io_read_version_file(&format,
763                                     svn_dirent_join(path, FORMAT_FILE, pool),
764                                     pool);
765  if (svn_err && APR_STATUS_IS_ENOENT(svn_err->apr_err))
766    {
767      /* Pre-1.2 filesystems did not have a format file (you could say
768         they were format "0"), so they get upgraded on the fly.
769         However, we stopped "upgrading on the fly" in 1.5, so older
770         filesystems should only be bumped to 1.3, which is format "1". */
771      svn_error_clear(svn_err);
772      svn_err = SVN_NO_ERROR;
773      format = 1;
774      write_format_file = TRUE;
775    }
776  else if (svn_err)
777    goto error;
778
779  /* Create the environment and databases. */
780  svn_err = open_databases(fs, FALSE, format, path, pool);
781  if (svn_err) goto error;
782
783  ((base_fs_data_t *) fs->fsap_data)->format = format;
784  SVN_ERR(check_format(format));
785
786  /* If we lack a format file, write one. */
787  if (write_format_file)
788    {
789      svn_err = svn_io_write_version_file(svn_dirent_join(path, FORMAT_FILE,
790                                                        pool),
791                                          format, pool);
792      if (svn_err) goto error;
793    }
794
795  SVN_ERR(populate_opened_fs(fs, pool));
796  return SVN_NO_ERROR;
797
798 error:
799  svn_error_clear(cleanup_fs(fs));
800  return svn_err;
801}
802
803
804/* Running recovery on a Berkeley DB-based filesystem.  */
805
806
807/* Recover a database at PATH. Perform catastrophic recovery if FATAL
808   is TRUE. Use POOL for temporary allocation. */
809static svn_error_t *
810bdb_recover(const char *path, svn_boolean_t fatal, apr_pool_t *pool)
811{
812  bdb_env_baton_t *bdb;
813
814  /* Here's the comment copied from db_recover.c:
815
816     Initialize the environment -- we don't actually do anything
817     else, that all that's needed to run recovery.
818
819     Note that we specify a private environment, as we're about to
820     create a region, and we don't want to leave it around.  If we
821     leave the region around, the application that should create it
822     will simply join it instead, and will then be running with
823     incorrectly sized (and probably terribly small) caches.  */
824
825  /* Note that since we're using a private environment, we shoudl
826     /not/ initialize locking. We want the environment files to go
827     away. */
828
829  SVN_ERR(svn_fs_bdb__open(&bdb, path,
830                           ((fatal ? DB_RECOVER_FATAL : DB_RECOVER)
831                            | SVN_BDB_PRIVATE_ENV_FLAGS),
832                           0666, pool));
833  return svn_fs_bdb__close(bdb);
834}
835
836static svn_error_t *
837base_open_for_recovery(svn_fs_t *fs, const char *path, apr_pool_t *pool,
838                       apr_pool_t *common_pool)
839{
840  /* Just stash the path in the fs pointer - it's all we really need. */
841  fs->path = apr_pstrdup(fs->pool, path);
842
843  return SVN_NO_ERROR;
844}
845
846static svn_error_t *
847base_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool,
848             apr_pool_t *common_pool)
849{
850  const char *version_file_path;
851  int old_format_number;
852  svn_error_t *err;
853
854  version_file_path = svn_dirent_join(path, FORMAT_FILE, pool);
855
856  /* Read the old number so we've got it on hand later on. */
857  err = svn_io_read_version_file(&old_format_number, version_file_path, pool);
858  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
859    {
860      /* Pre-1.2 filesystems do not have a 'format' file. */
861      old_format_number = 0;
862      svn_error_clear(err);
863      err = SVN_NO_ERROR;
864    }
865  SVN_ERR(err);
866
867  /* Bump the format file's stored version number. */
868  SVN_ERR(svn_io_write_version_file(version_file_path,
869                                    SVN_FS_BASE__FORMAT_NUMBER, pool));
870
871  /* Check and see if we need to record the "bump" revision. */
872  if (old_format_number < SVN_FS_BASE__MIN_FORWARD_DELTAS_FORMAT)
873    {
874      apr_pool_t *subpool = svn_pool_create(pool);
875      svn_revnum_t youngest_rev;
876      const char *value;
877
878      /* Open the filesystem in a subpool (so we can control its
879         closure) and do our fiddling.
880
881         NOTE: By using base_open() here instead of open_databases(),
882         we will end up re-reading the format file that we just wrote.
883         But it's better to use the existing encapsulation of "opening
884         the filesystem" rather than duplicating (or worse, partially
885         duplicating) that logic here.  */
886      SVN_ERR(base_open(fs, path, subpool, common_pool));
887
888      /* Fetch the youngest rev, and record it */
889      SVN_ERR(svn_fs_base__youngest_rev(&youngest_rev, fs, subpool));
890      value = apr_psprintf(subpool, "%ld", youngest_rev);
891      SVN_ERR(svn_fs_base__miscellaneous_set
892              (fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE,
893               value, subpool));
894      svn_pool_destroy(subpool);
895    }
896
897  return SVN_NO_ERROR;
898}
899
900static svn_error_t *
901base_verify(svn_fs_t *fs, const char *path,
902            svn_revnum_t start,
903            svn_revnum_t end,
904            svn_fs_progress_notify_func_t notify_func,
905            void *notify_baton,
906            svn_cancel_func_t cancel_func,
907            void *cancel_baton,
908            apr_pool_t *pool,
909            apr_pool_t *common_pool)
910{
911  /* Verifying is currently a no op for BDB. */
912  return SVN_NO_ERROR;
913}
914
915static svn_error_t *
916base_bdb_recover(svn_fs_t *fs,
917                 svn_cancel_func_t cancel_func, void *cancel_baton,
918                 apr_pool_t *pool)
919{
920  /* The fs pointer is a fake created in base_open_for_recovery above.
921     We only care about the path. */
922  return bdb_recover(fs->path, FALSE, pool);
923}
924
925static svn_error_t *
926base_bdb_pack(svn_fs_t *fs,
927              const char *path,
928              svn_fs_pack_notify_t notify_func,
929              void *notify_baton,
930              svn_cancel_func_t cancel,
931              void *cancel_baton,
932              apr_pool_t *pool,
933              apr_pool_t *common_pool)
934{
935  /* Packing is currently a no op for BDB. */
936  return SVN_NO_ERROR;
937}
938
939
940
941/* Running the 'archive' command on a Berkeley DB-based filesystem.  */
942
943
944static svn_error_t *
945base_bdb_logfiles(apr_array_header_t **logfiles,
946                  const char *path,
947                  svn_boolean_t only_unused,
948                  apr_pool_t *pool)
949{
950  bdb_env_baton_t *bdb;
951  char **filelist;
952  char **filename;
953  u_int32_t flags = only_unused ? 0 : DB_ARCH_LOG;
954
955  *logfiles = apr_array_make(pool, 4, sizeof(const char *));
956
957  SVN_ERR(svn_fs_bdb__open(&bdb, path,
958                           SVN_BDB_STANDARD_ENV_FLAGS,
959                           0666, pool));
960  SVN_BDB_ERR(bdb, bdb->env->log_archive(bdb->env, &filelist, flags));
961
962  if (filelist == NULL)
963    return svn_fs_bdb__close(bdb);
964
965  for (filename = filelist; *filename != NULL; ++filename)
966    {
967      APR_ARRAY_PUSH(*logfiles, const char *) = apr_pstrdup(pool, *filename);
968    }
969
970  free(filelist);
971
972  return svn_fs_bdb__close(bdb);
973}
974
975
976
977/* Copying a live Berkeley DB-base filesystem.  */
978
979/**
980 * Delete all unused log files from DBD enviroment at @a live_path that exist
981 * in @a backup_path.
982 */
983static svn_error_t *
984svn_fs_base__clean_logs(const char *live_path,
985                        const char *backup_path,
986                        apr_pool_t *pool)
987{
988  apr_array_header_t *logfiles;
989
990  SVN_ERR(base_bdb_logfiles(&logfiles,
991                            live_path,
992                            TRUE,        /* Only unused logs */
993                            pool));
994
995  {  /* Process unused logs from live area */
996    int idx;
997    apr_pool_t *sub_pool = svn_pool_create(pool);
998
999    /* Process log files. */
1000    for (idx = 0; idx < logfiles->nelts; idx++)
1001      {
1002        const char *log_file = APR_ARRAY_IDX(logfiles, idx, const char *);
1003        const char *live_log_path;
1004        const char *backup_log_path;
1005
1006        svn_pool_clear(sub_pool);
1007        live_log_path = svn_dirent_join(live_path, log_file, sub_pool);
1008        backup_log_path = svn_dirent_join(backup_path, log_file, sub_pool);
1009
1010        { /* Compare files. No point in using MD5 and wasting CPU cycles as we
1011             got full copies of both logs */
1012
1013          svn_boolean_t files_match = FALSE;
1014          svn_node_kind_t kind;
1015
1016          /* Check to see if there is a corresponding log file in the backup
1017             directory */
1018          SVN_ERR(svn_io_check_path(backup_log_path, &kind, pool));
1019
1020          /* If the copy of the log exists, compare them */
1021          if (kind == svn_node_file)
1022            SVN_ERR(svn_io_files_contents_same_p(&files_match,
1023                                                 live_log_path,
1024                                                 backup_log_path,
1025                                                 sub_pool));
1026
1027          /* If log files do not match, go to the next log file. */
1028          if (!files_match)
1029            continue;
1030        }
1031
1032        SVN_ERR(svn_io_remove_file2(live_log_path, FALSE, sub_pool));
1033      }
1034
1035    svn_pool_destroy(sub_pool);
1036  }
1037
1038  return SVN_NO_ERROR;
1039}
1040
1041
1042/* DB_ENV->get_flags() and DB->get_pagesize() don't exist prior to
1043   Berkeley DB 4.2. */
1044#if SVN_BDB_VERSION_AT_LEAST(4, 2)
1045
1046/* Open the BDB environment at PATH and compare its configuration
1047   flags with FLAGS.  If every flag in FLAGS is set in the
1048   environment, then set *MATCH to true.  Else set *MATCH to false. */
1049static svn_error_t *
1050check_env_flags(svn_boolean_t *match,
1051                u_int32_t flags,
1052                const char *path,
1053                apr_pool_t *pool)
1054{
1055  bdb_env_baton_t *bdb;
1056#if SVN_BDB_VERSION_AT_LEAST(4, 7)
1057  int flag_state;
1058#else
1059  u_int32_t envflags;
1060#endif
1061
1062  SVN_ERR(svn_fs_bdb__open(&bdb, path,
1063                           SVN_BDB_STANDARD_ENV_FLAGS,
1064                           0666, pool));
1065#if SVN_BDB_VERSION_AT_LEAST(4, 7)
1066  SVN_BDB_ERR(bdb, bdb->env->log_get_config(bdb->env, flags, &flag_state));
1067#else
1068  SVN_BDB_ERR(bdb, bdb->env->get_flags(bdb->env, &envflags));
1069#endif
1070
1071  SVN_ERR(svn_fs_bdb__close(bdb));
1072
1073#if SVN_BDB_VERSION_AT_LEAST(4, 7)
1074  if (flag_state == 0)
1075#else
1076  if (flags & envflags)
1077#endif
1078    *match = TRUE;
1079  else
1080    *match = FALSE;
1081
1082  return SVN_NO_ERROR;
1083}
1084
1085
1086/* Set *PAGESIZE to the size of pages used to hold items in the
1087   database environment located at PATH.
1088*/
1089static svn_error_t *
1090get_db_pagesize(u_int32_t *pagesize,
1091                const char *path,
1092                apr_pool_t *pool)
1093{
1094  bdb_env_baton_t *bdb;
1095  DB *nodes_table;
1096
1097  SVN_ERR(svn_fs_bdb__open(&bdb, path,
1098                           SVN_BDB_STANDARD_ENV_FLAGS,
1099                           0666, pool));
1100
1101  /* ### We're only asking for the pagesize on the 'nodes' table.
1102         Is this enough?  We never call DB->set_pagesize() on any of
1103         our tables, so presumably BDB is using the same default
1104         pagesize for all our databases, right? */
1105  SVN_BDB_ERR(bdb, svn_fs_bdb__open_nodes_table(&nodes_table, bdb->env,
1106                                                FALSE));
1107  SVN_BDB_ERR(bdb, nodes_table->get_pagesize(nodes_table, pagesize));
1108  SVN_BDB_ERR(bdb, nodes_table->close(nodes_table, 0));
1109
1110  return svn_fs_bdb__close(bdb);
1111}
1112#endif /* SVN_BDB_VERSION_AT_LEAST(4, 2) */
1113
1114
1115/* Copy FILENAME from SRC_DIR to DST_DIR in byte increments of size
1116   CHUNKSIZE.  The read/write buffer of size CHUNKSIZE will be
1117   allocated in POOL.  If ALLOW_MISSING is set, we won't make a fuss
1118   if FILENAME isn't found in SRC_DIR; otherwise, we will.  */
1119static svn_error_t *
1120copy_db_file_safely(const char *src_dir,
1121                    const char *dst_dir,
1122                    const char *filename,
1123                    u_int32_t chunksize,
1124                    svn_boolean_t allow_missing,
1125                    apr_pool_t *pool)
1126{
1127  apr_file_t *s = NULL, *d = NULL;  /* init to null important for APR */
1128  const char *file_src_path = svn_dirent_join(src_dir, filename, pool);
1129  const char *file_dst_path = svn_dirent_join(dst_dir, filename, pool);
1130  svn_error_t *err;
1131  char *buf;
1132
1133  /* Open source file.  If it's missing and that's allowed, there's
1134     nothing more to do here. */
1135  err = svn_io_file_open(&s, file_src_path,
1136                         (APR_READ | APR_LARGEFILE),
1137                         APR_OS_DEFAULT, pool);
1138  if (err && APR_STATUS_IS_ENOENT(err->apr_err) && allow_missing)
1139    {
1140      svn_error_clear(err);
1141      return SVN_NO_ERROR;
1142    }
1143  SVN_ERR(err);
1144
1145  /* Open destination file. */
1146  SVN_ERR(svn_io_file_open(&d, file_dst_path, (APR_WRITE | APR_CREATE |
1147                                               APR_LARGEFILE),
1148                           APR_OS_DEFAULT, pool));
1149
1150  /* Allocate our read/write buffer. */
1151  buf = apr_palloc(pool, chunksize);
1152
1153  /* Copy bytes till the cows come home. */
1154  while (1)
1155    {
1156      apr_size_t bytes_this_time = chunksize;
1157      svn_error_t *read_err, *write_err;
1158
1159      /* Read 'em. */
1160      if ((read_err = svn_io_file_read(s, buf, &bytes_this_time, pool)))
1161        {
1162          if (APR_STATUS_IS_EOF(read_err->apr_err))
1163            svn_error_clear(read_err);
1164          else
1165            {
1166              svn_error_clear(svn_io_file_close(s, pool));
1167              svn_error_clear(svn_io_file_close(d, pool));
1168              return read_err;
1169            }
1170        }
1171
1172      /* Write 'em. */
1173      if ((write_err = svn_io_file_write_full(d, buf, bytes_this_time, NULL,
1174                                              pool)))
1175        {
1176          svn_error_clear(svn_io_file_close(s, pool));
1177          svn_error_clear(svn_io_file_close(d, pool));
1178          return write_err;
1179        }
1180
1181      /* read_err is either NULL, or a dangling pointer - but it is only a
1182         dangling pointer if it used to be an EOF error. */
1183      if (read_err)
1184        {
1185          SVN_ERR(svn_io_file_close(s, pool));
1186          SVN_ERR(svn_io_file_close(d, pool));
1187          break;  /* got EOF on read, all files closed, all done. */
1188        }
1189    }
1190
1191  return SVN_NO_ERROR;
1192}
1193
1194
1195
1196
1197static svn_error_t *
1198base_hotcopy(svn_fs_t *src_fs,
1199             svn_fs_t *dst_fs,
1200             const char *src_path,
1201             const char *dest_path,
1202             svn_boolean_t clean_logs,
1203             svn_boolean_t incremental,
1204             svn_cancel_func_t cancel_func,
1205             void *cancel_baton,
1206             apr_pool_t *pool)
1207{
1208  svn_error_t *err;
1209  u_int32_t pagesize;
1210  svn_boolean_t log_autoremove = FALSE;
1211  int format;
1212
1213  if (incremental)
1214    return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
1215                             _("BDB repositories do not support incremental "
1216                               "hotcopy"));
1217
1218  /* Check the FS format number to be certain that we know how to
1219     hotcopy this FS.  Pre-1.2 filesystems did not have a format file (you
1220     could say they were format "0"), so we will error here.  This is not
1221     optimal, but since this has been the case since 1.2.0, and no one has
1222     complained, it apparently isn't much of a concern.  (We did not check
1223     the 'format' file in 1.2.x, but we did blindly try to copy 'locks',
1224     which would have errored just the same.)  */
1225  SVN_ERR(svn_io_read_version_file(
1226          &format, svn_dirent_join(src_path, FORMAT_FILE, pool), pool));
1227  SVN_ERR(check_format(format));
1228
1229  /* If using Berkeley DB 4.2 or later, note whether the DB_LOG_AUTO_REMOVE
1230     feature is on.  If it is, we have a potential race condition:
1231     another process might delete a logfile while we're in the middle
1232     of copying all the logfiles.  (This is not a huge deal; at worst,
1233     the hotcopy fails with a file-not-found error.) */
1234#if SVN_BDB_VERSION_AT_LEAST(4, 2)
1235  err = check_env_flags(&log_autoremove,
1236#if SVN_BDB_VERSION_AT_LEAST(4, 7)
1237                          DB_LOG_AUTO_REMOVE,
1238 /* DB_LOG_AUTO_REMOVE was named DB_LOG_AUTOREMOVE before Berkeley DB 4.7. */
1239#else
1240                          DB_LOG_AUTOREMOVE,
1241#endif
1242                          src_path, pool);
1243#endif
1244  SVN_ERR(err);
1245
1246  /* Copy the DB_CONFIG file. */
1247  SVN_ERR(svn_io_dir_file_copy(src_path, dest_path, "DB_CONFIG", pool));
1248
1249  /* In order to copy the database files safely and atomically, we
1250     must copy them in chunks which are multiples of the page-size
1251     used by BDB.  See sleepycat docs for details, or svn issue #1818. */
1252#if SVN_BDB_VERSION_AT_LEAST(4, 2)
1253  SVN_ERR(get_db_pagesize(&pagesize, src_path, pool));
1254  if (pagesize < SVN__STREAM_CHUNK_SIZE)
1255    {
1256      /* use the largest multiple of BDB pagesize we can. */
1257      int multiple = SVN__STREAM_CHUNK_SIZE / pagesize;
1258      pagesize *= multiple;
1259    }
1260#else
1261  /* default to 128K chunks, which should be safe.
1262     BDB almost certainly uses a power-of-2 pagesize. */
1263  pagesize = (4096 * 32);
1264#endif
1265
1266  /* Copy the databases.  */
1267  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1268                              "nodes", pagesize, FALSE, pool));
1269  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1270                              "transactions", pagesize, FALSE, pool));
1271  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1272                              "revisions", pagesize, FALSE, pool));
1273  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1274                              "copies", pagesize, FALSE, pool));
1275  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1276                              "changes", pagesize, FALSE, pool));
1277  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1278                              "representations", pagesize, FALSE, pool));
1279  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1280                              "strings", pagesize, FALSE, pool));
1281  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1282                              "uuids", pagesize, TRUE, pool));
1283  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1284                              "locks", pagesize, TRUE, pool));
1285  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1286                              "lock-tokens", pagesize, TRUE, pool));
1287  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1288                              "node-origins", pagesize, TRUE, pool));
1289  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1290                              "checksum-reps", pagesize, TRUE, pool));
1291  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1292                              "miscellaneous", pagesize, TRUE, pool));
1293
1294  {
1295    apr_array_header_t *logfiles;
1296    int idx;
1297    apr_pool_t *subpool;
1298
1299    SVN_ERR(base_bdb_logfiles(&logfiles,
1300                              src_path,
1301                              FALSE,   /* All logs */
1302                              pool));
1303
1304    /* Process log files. */
1305    subpool = svn_pool_create(pool);
1306    for (idx = 0; idx < logfiles->nelts; idx++)
1307      {
1308        svn_pool_clear(subpool);
1309        err = svn_io_dir_file_copy(src_path, dest_path,
1310                                   APR_ARRAY_IDX(logfiles, idx,
1311                                                 const char *),
1312                                   subpool);
1313        if (err)
1314          {
1315            if (log_autoremove)
1316              return
1317                svn_error_quick_wrap
1318                (err,
1319                 _("Error copying logfile;  the DB_LOG_AUTOREMOVE feature\n"
1320                   "may be interfering with the hotcopy algorithm.  If\n"
1321                   "the problem persists, try deactivating this feature\n"
1322                   "in DB_CONFIG"));
1323            else
1324              return svn_error_trace(err);
1325          }
1326      }
1327    svn_pool_destroy(subpool);
1328  }
1329
1330  /* Since this is a copy we will have exclusive access to the repository. */
1331  err = bdb_recover(dest_path, TRUE, pool);
1332  if (err)
1333    {
1334      if (log_autoremove)
1335        return
1336          svn_error_quick_wrap
1337          (err,
1338           _("Error running catastrophic recovery on hotcopy;  the\n"
1339             "DB_LOG_AUTOREMOVE feature may be interfering with the\n"
1340             "hotcopy algorithm.  If the problem persists, try deactivating\n"
1341             "this feature in DB_CONFIG"));
1342      else
1343        return svn_error_trace(err);
1344    }
1345
1346  /* Only now that the hotcopied filesystem is complete,
1347     stamp it with a format file. */
1348  SVN_ERR(svn_io_write_version_file(
1349             svn_dirent_join(dest_path, FORMAT_FILE, pool), format, pool));
1350
1351  if (clean_logs)
1352    SVN_ERR(svn_fs_base__clean_logs(src_path, dest_path, pool));
1353
1354  return SVN_NO_ERROR;
1355}
1356
1357
1358
1359/* Deleting a Berkeley DB-based filesystem.  */
1360
1361
1362static svn_error_t *
1363base_delete_fs(const char *path,
1364               apr_pool_t *pool)
1365{
1366  /* First, use the Berkeley DB library function to remove any shared
1367     memory segments.  */
1368  SVN_ERR(svn_fs_bdb__remove(path, pool));
1369
1370  /* Remove the environment directory. */
1371  return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
1372}
1373
1374static const svn_version_t *
1375base_version(void)
1376{
1377  SVN_VERSION_BODY;
1378}
1379
1380static const char *
1381base_get_description(void)
1382{
1383  return _("Module for working with a Berkeley DB repository.");
1384}
1385
1386static svn_error_t *
1387base_set_svn_fs_open(svn_fs_t *fs,
1388                     svn_error_t *(*svn_fs_open_)(svn_fs_t **,
1389                                                  const char *,
1390                                                  apr_hash_t *,
1391                                                  apr_pool_t *))
1392{
1393  return SVN_NO_ERROR;
1394}
1395
1396
1397/* Base FS library vtable, used by the FS loader library. */
1398static fs_library_vtable_t library_vtable = {
1399  base_version,
1400  base_create,
1401  base_open,
1402  base_open_for_recovery,
1403  base_upgrade,
1404  base_verify,
1405  base_delete_fs,
1406  base_hotcopy,
1407  base_get_description,
1408  base_bdb_recover,
1409  base_bdb_pack,
1410  base_bdb_logfiles,
1411  svn_fs_base__id_parse,
1412  base_set_svn_fs_open
1413};
1414
1415svn_error_t *
1416svn_fs_base__init(const svn_version_t *loader_version,
1417                  fs_library_vtable_t **vtable, apr_pool_t* common_pool)
1418{
1419  static const svn_version_checklist_t checklist[] =
1420    {
1421      { "svn_subr",  svn_subr_version },
1422      { "svn_delta", svn_delta_version },
1423      { NULL, NULL }
1424    };
1425
1426  /* Simplified version check to make sure we can safely use the
1427     VTABLE parameter. The FS loader does a more exhaustive check. */
1428  if (loader_version->major != SVN_VER_MAJOR)
1429    return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
1430                             _("Unsupported FS loader version (%d) for bdb"),
1431                             loader_version->major);
1432  SVN_ERR(svn_ver_check_list2(base_version(), checklist, svn_ver_equal));
1433  SVN_ERR(check_bdb_version());
1434  SVN_ERR(svn_fs_bdb__init(common_pool));
1435
1436  *vtable = &library_vtable;
1437  return SVN_NO_ERROR;
1438}
1439