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