fs.c revision 362181
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_refresh_revision(svn_fs_t *fs,
475                          apr_pool_t *scratch_pool)
476{
477  return SVN_NO_ERROR;
478}
479
480static svn_error_t *
481base_bdb_info_format(int *fs_format,
482                     svn_version_t **supports_version,
483                     svn_fs_t *fs,
484                     apr_pool_t *result_pool,
485                     apr_pool_t *scratch_pool)
486{
487  base_fs_data_t *bfd = fs->fsap_data;
488
489  *fs_format = bfd->format;
490  *supports_version = apr_palloc(result_pool, sizeof(svn_version_t));
491
492  (*supports_version)->major = SVN_VER_MAJOR;
493  (*supports_version)->minor = 0;
494  (*supports_version)->patch = 0;
495  (*supports_version)->tag = "";
496
497  switch (bfd->format)
498    {
499    case 1:
500      break;
501    case 2:
502      (*supports_version)->minor = 4;
503      break;
504    case 3:
505      (*supports_version)->minor = 5;
506      break;
507    case 4:
508      (*supports_version)->minor = 6;
509      break;
510#ifdef SVN_DEBUG
511# if SVN_FS_BASE__FORMAT_NUMBER != 4
512#  error "Need to add a 'case' statement here"
513# endif
514#endif
515    }
516
517  return SVN_NO_ERROR;
518}
519
520static svn_error_t *
521base_bdb_info_config_files(apr_array_header_t **files,
522                           svn_fs_t *fs,
523                           apr_pool_t *result_pool,
524                           apr_pool_t *scratch_pool)
525{
526  *files = apr_array_make(result_pool, 1, sizeof(const char *));
527  APR_ARRAY_PUSH(*files, const char *) = svn_dirent_join(fs->path,
528                                                         BDB_CONFIG_FILE,
529                                                         result_pool);
530  return SVN_NO_ERROR;
531}
532
533static svn_error_t *
534base_bdb_verify_root(svn_fs_root_t *root,
535                     apr_pool_t *scratch_pool)
536{
537  /* Verifying is currently a no op for BDB. */
538  return SVN_NO_ERROR;
539}
540
541static svn_error_t *
542base_bdb_freeze(svn_fs_t *fs,
543                svn_fs_freeze_func_t freeze_func,
544                void *freeze_baton,
545                apr_pool_t *pool)
546{
547  SVN__NOT_IMPLEMENTED();
548}
549
550
551/* Creating a new filesystem */
552
553static fs_vtable_t fs_vtable = {
554  svn_fs_base__youngest_rev,
555  base_bdb_refresh_revision,
556  svn_fs_base__revision_prop,
557  svn_fs_base__revision_proplist,
558  svn_fs_base__change_rev_prop,
559  svn_fs_base__set_uuid,
560  svn_fs_base__revision_root,
561  svn_fs_base__begin_txn,
562  svn_fs_base__open_txn,
563  svn_fs_base__purge_txn,
564  svn_fs_base__list_transactions,
565  svn_fs_base__deltify,
566  svn_fs_base__lock,
567  svn_fs_base__generate_lock_token,
568  svn_fs_base__unlock,
569  svn_fs_base__get_lock,
570  svn_fs_base__get_locks,
571  base_bdb_info_format,
572  base_bdb_info_config_files,
573  NULL /* info_fsap */,
574  base_bdb_verify_root,
575  base_bdb_freeze,
576  base_bdb_set_errcall,
577  NULL /* ioctl */
578};
579
580/* Where the format number is stored. */
581#define FORMAT_FILE   "format"
582
583/* Depending on CREATE, create or open the environment and databases
584   for filesystem FS in PATH. */
585static svn_error_t *
586open_databases(svn_fs_t *fs,
587               svn_boolean_t create,
588               int format,
589               const char *path)
590{
591  base_fs_data_t *bfd;
592
593  SVN_ERR(svn_fs__check_fs(fs, FALSE));
594
595  bfd = apr_pcalloc(fs->pool, sizeof(*bfd));
596  fs->vtable = &fs_vtable;
597  fs->fsap_data = bfd;
598
599  /* Initialize the fs's path. */
600  fs->path = apr_pstrdup(fs->pool, path);
601
602  if (create)
603    SVN_ERR(bdb_write_config(fs));
604
605  /* Create the Berkeley DB environment.  */
606  {
607    svn_error_t *err = svn_fs_bdb__open(&(bfd->bdb), path,
608                                        SVN_BDB_STANDARD_ENV_FLAGS,
609                                        0666, fs->pool);
610    if (err)
611      {
612        if (create)
613          return svn_error_createf
614            (err->apr_err, err,
615             _("Berkeley DB error for filesystem '%s'"
616               " while creating environment:\n"),
617             fs->path);
618        else
619          return svn_error_createf
620            (err->apr_err, err,
621             _("Berkeley DB error for filesystem '%s'"
622               " while opening environment:\n"),
623             fs->path);
624      }
625  }
626
627  /* We must register the FS cleanup function *after* opening the
628     environment, so that it's run before the environment baton
629     cleanup. */
630  apr_pool_cleanup_register(fs->pool, fs, cleanup_fs_apr,
631                            apr_pool_cleanup_null);
632
633
634  /* Create the databases in the environment.  */
635  SVN_ERR(BDB_WRAP(fs, (create
636                        ? N_("creating 'nodes' table")
637                        : N_("opening 'nodes' table")),
638                   svn_fs_bdb__open_nodes_table(&bfd->nodes,
639                                                bfd->bdb->env,
640                                                create)));
641  SVN_ERR(BDB_WRAP(fs, (create
642                        ? N_("creating 'revisions' table")
643                        : N_("opening 'revisions' table")),
644                   svn_fs_bdb__open_revisions_table(&bfd->revisions,
645                                                    bfd->bdb->env,
646                                                    create)));
647  SVN_ERR(BDB_WRAP(fs, (create
648                        ? N_("creating 'transactions' table")
649                        : N_("opening 'transactions' table")),
650                   svn_fs_bdb__open_transactions_table(&bfd->transactions,
651                                                       bfd->bdb->env,
652                                                       create)));
653  SVN_ERR(BDB_WRAP(fs, (create
654                        ? N_("creating 'copies' table")
655                        : N_("opening 'copies' table")),
656                   svn_fs_bdb__open_copies_table(&bfd->copies,
657                                                 bfd->bdb->env,
658                                                 create)));
659  SVN_ERR(BDB_WRAP(fs, (create
660                        ? N_("creating 'changes' table")
661                        : N_("opening 'changes' table")),
662                   svn_fs_bdb__open_changes_table(&bfd->changes,
663                                                  bfd->bdb->env,
664                                                  create)));
665  SVN_ERR(BDB_WRAP(fs, (create
666                        ? N_("creating 'representations' table")
667                        : N_("opening 'representations' table")),
668                   svn_fs_bdb__open_reps_table(&bfd->representations,
669                                               bfd->bdb->env,
670                                               create)));
671  SVN_ERR(BDB_WRAP(fs, (create
672                        ? N_("creating 'strings' table")
673                        : N_("opening 'strings' table")),
674                   svn_fs_bdb__open_strings_table(&bfd->strings,
675                                                  bfd->bdb->env,
676                                                  create)));
677  SVN_ERR(BDB_WRAP(fs, (create
678                        ? N_("creating 'uuids' table")
679                        : N_("opening 'uuids' table")),
680                   svn_fs_bdb__open_uuids_table(&bfd->uuids,
681                                                bfd->bdb->env,
682                                                create)));
683  SVN_ERR(BDB_WRAP(fs, (create
684                        ? N_("creating 'locks' table")
685                        : N_("opening 'locks' table")),
686                   svn_fs_bdb__open_locks_table(&bfd->locks,
687                                                bfd->bdb->env,
688                                                create)));
689  SVN_ERR(BDB_WRAP(fs, (create
690                        ? N_("creating 'lock-tokens' table")
691                        : N_("opening 'lock-tokens' table")),
692                   svn_fs_bdb__open_lock_tokens_table(&bfd->lock_tokens,
693                                                      bfd->bdb->env,
694                                                      create)));
695
696  if (format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT)
697    {
698      SVN_ERR(BDB_WRAP(fs, (create
699                            ? N_("creating 'node-origins' table")
700                            : N_("opening 'node-origins' table")),
701                       svn_fs_bdb__open_node_origins_table(&bfd->node_origins,
702                                                           bfd->bdb->env,
703                                                           create)));
704    }
705
706  if (format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT)
707    {
708      SVN_ERR(BDB_WRAP(fs, (create
709                            ? N_("creating 'miscellaneous' table")
710                            : N_("opening 'miscellaneous' table")),
711                       svn_fs_bdb__open_miscellaneous_table(&bfd->miscellaneous,
712                                                            bfd->bdb->env,
713                                                            create)));
714    }
715
716  if (format >= SVN_FS_BASE__MIN_REP_SHARING_FORMAT)
717    {
718      SVN_ERR(BDB_WRAP(fs, (create
719                            ? N_("creating 'checksum-reps' table")
720                            : N_("opening 'checksum-reps' table")),
721                       svn_fs_bdb__open_checksum_reps_table(&bfd->checksum_reps,
722                                                            bfd->bdb->env,
723                                                            create)));
724    }
725
726  return SVN_NO_ERROR;
727}
728
729
730/* Called by functions that initialize an svn_fs_t struct, after that
731   initialization is done, to populate svn_fs_t->uuid. */
732static svn_error_t *
733populate_opened_fs(svn_fs_t *fs, apr_pool_t *scratch_pool)
734{
735  SVN_ERR(svn_fs_base__populate_uuid(fs, scratch_pool));
736  return SVN_NO_ERROR;
737}
738
739static svn_error_t *
740base_create(svn_fs_t *fs,
741            const char *path,
742            svn_mutex__t *common_pool_lock,
743            apr_pool_t *scratch_pool,
744            apr_pool_t *common_pool)
745{
746  int format = SVN_FS_BASE__FORMAT_NUMBER;
747  svn_error_t *svn_err;
748
749  /* See if compatibility with older versions was explicitly requested. */
750  if (fs->config)
751    {
752      svn_version_t *compatible_version;
753      SVN_ERR(svn_fs__compatible_version(&compatible_version, fs->config,
754                                         scratch_pool));
755
756      /* select format number */
757      switch(compatible_version->minor)
758        {
759          case 0:
760          case 1:
761          case 2:
762          case 3: format = 1;
763                  break;
764
765          case 4: format = 2;
766                  break;
767
768          case 5: format = 3;
769                  break;
770
771          default:format = SVN_FS_BASE__FORMAT_NUMBER;
772        }
773    }
774
775  /* Create the environment and databases. */
776  svn_err = open_databases(fs, TRUE, format, path);
777  if (svn_err) goto error;
778
779  /* Initialize the DAG subsystem. */
780  svn_err = svn_fs_base__dag_init_fs(fs);
781  if (svn_err) goto error;
782
783  /* This filesystem is ready.  Stamp it with a format number. */
784  svn_err = svn_io_write_version_file(svn_dirent_join(fs->path, FORMAT_FILE,
785                                                      scratch_pool),
786                                      format, scratch_pool);
787  if (svn_err) goto error;
788
789  ((base_fs_data_t *) fs->fsap_data)->format = format;
790
791  SVN_ERR(populate_opened_fs(fs, scratch_pool));
792  return SVN_NO_ERROR;
793
794error:
795  return svn_error_compose_create(svn_err,
796                                  svn_error_trace(cleanup_fs(fs)));
797}
798
799
800/* Gaining access to an existing Berkeley DB-based filesystem.  */
801
802svn_error_t *
803svn_fs_base__test_required_feature_format(svn_fs_t *fs,
804                                          const char *feature,
805                                          int requires)
806{
807  base_fs_data_t *bfd = fs->fsap_data;
808  if (bfd->format < requires)
809    return svn_error_createf
810      (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
811       _("The '%s' feature requires version %d of the filesystem schema; "
812         "filesystem '%s' uses only version %d"),
813       feature, requires, fs->path, bfd->format);
814  return SVN_NO_ERROR;
815}
816
817/* Return the error SVN_ERR_FS_UNSUPPORTED_FORMAT if FS's format
818   number is not the same as the format number supported by this
819   Subversion. */
820static svn_error_t *
821check_format(int format)
822{
823  /* We currently support any format less than the compiled format number
824     simultaneously.  */
825  if (format <= SVN_FS_BASE__FORMAT_NUMBER)
826    return SVN_NO_ERROR;
827
828  return svn_error_createf(
829        SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
830        _("Expected FS format '%d'; found format '%d'"),
831        SVN_FS_BASE__FORMAT_NUMBER, format);
832}
833
834static svn_error_t *
835base_open(svn_fs_t *fs,
836          const char *path,
837          svn_mutex__t *common_pool_lock,
838          apr_pool_t *scratch_pool,
839          apr_pool_t *common_pool)
840{
841  int format;
842  svn_error_t *svn_err;
843  svn_boolean_t write_format_file = FALSE;
844
845  /* Read the FS format number. */
846  svn_err = svn_io_read_version_file(&format,
847                                     svn_dirent_join(path, FORMAT_FILE,
848                                                     scratch_pool),
849                                     scratch_pool);
850  if (svn_err && APR_STATUS_IS_ENOENT(svn_err->apr_err))
851    {
852      /* Pre-1.2 filesystems did not have a format file (you could say
853         they were format "0"), so they get upgraded on the fly.
854         However, we stopped "upgrading on the fly" in 1.5, so older
855         filesystems should only be bumped to 1.3, which is format "1". */
856      svn_error_clear(svn_err);
857      svn_err = SVN_NO_ERROR;
858      format = 1;
859      write_format_file = TRUE;
860    }
861  else if (svn_err)
862    goto error;
863
864  /* Create the environment and databases. */
865  svn_err = open_databases(fs, FALSE, format, path);
866  if (svn_err) goto error;
867
868  ((base_fs_data_t *) fs->fsap_data)->format = format;
869  SVN_ERR(check_format(format));
870
871  /* If we lack a format file, write one. */
872  if (write_format_file)
873    {
874      svn_err = svn_io_write_version_file(svn_dirent_join(path, FORMAT_FILE,
875                                                        scratch_pool),
876                                          format, scratch_pool);
877      if (svn_err) goto error;
878    }
879
880  SVN_ERR(populate_opened_fs(fs, scratch_pool));
881  return SVN_NO_ERROR;
882
883 error:
884  return svn_error_compose_create(svn_err,
885                                  svn_error_trace(cleanup_fs(fs)));
886}
887
888
889/* Running recovery on a Berkeley DB-based filesystem.  */
890
891
892/* Recover a database at PATH. Perform catastrophic recovery if FATAL
893   is TRUE. Use POOL for temporary allocation. */
894static svn_error_t *
895bdb_recover(const char *path, svn_boolean_t fatal, apr_pool_t *pool)
896{
897  bdb_env_baton_t *bdb;
898
899  /* Here's the comment copied from db_recover.c:
900
901     Initialize the environment -- we don't actually do anything
902     else, that all that's needed to run recovery.
903
904     Note that we specify a private environment, as we're about to
905     create a region, and we don't want to leave it around.  If we
906     leave the region around, the application that should create it
907     will simply join it instead, and will then be running with
908     incorrectly sized (and probably terribly small) caches.  */
909
910  /* Note that since we're using a private environment, we shoudl
911     /not/ initialize locking. We want the environment files to go
912     away. */
913
914  SVN_ERR(svn_fs_bdb__open(&bdb, path,
915                           ((fatal ? DB_RECOVER_FATAL : DB_RECOVER)
916                            | SVN_BDB_PRIVATE_ENV_FLAGS),
917                           0666, pool));
918  return svn_fs_bdb__close(bdb);
919}
920
921static svn_error_t *
922base_open_for_recovery(svn_fs_t *fs,
923                       const char *path,
924                       svn_mutex__t *common_pool_lock,
925                       apr_pool_t *pool,
926                       apr_pool_t *common_pool)
927{
928  /* Just stash the path in the fs pointer - it's all we really need. */
929  fs->path = apr_pstrdup(fs->pool, path);
930
931  return SVN_NO_ERROR;
932}
933
934static svn_error_t *
935base_upgrade(svn_fs_t *fs,
936             const char *path,
937             svn_fs_upgrade_notify_t notify_func,
938             void *notify_baton,
939             svn_cancel_func_t cancel_func,
940             void *cancel_baton,
941             svn_mutex__t *common_pool_lock,
942             apr_pool_t *pool,
943             apr_pool_t *common_pool)
944{
945  const char *version_file_path;
946  int old_format_number;
947  svn_error_t *err;
948
949  version_file_path = svn_dirent_join(path, FORMAT_FILE, pool);
950
951  /* Read the old number so we've got it on hand later on. */
952  err = svn_io_read_version_file(&old_format_number, version_file_path, pool);
953  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
954    {
955      /* Pre-1.2 filesystems do not have a 'format' file. */
956      old_format_number = 0;
957      svn_error_clear(err);
958      err = SVN_NO_ERROR;
959    }
960  SVN_ERR(err);
961  SVN_ERR(check_format(old_format_number));
962
963  /* Bump the format file's stored version number. */
964  SVN_ERR(svn_io_write_version_file(version_file_path,
965                                    SVN_FS_BASE__FORMAT_NUMBER, pool));
966  if (notify_func)
967    SVN_ERR(notify_func(notify_baton, SVN_FS_BASE__FORMAT_NUMBER,
968                        svn_fs_upgrade_format_bumped, pool));
969
970  /* Check and see if we need to record the "bump" revision. */
971  if (old_format_number < SVN_FS_BASE__MIN_FORWARD_DELTAS_FORMAT)
972    {
973      apr_pool_t *subpool = svn_pool_create(pool);
974      svn_revnum_t youngest_rev;
975      const char *value;
976
977      /* Open the filesystem in a subpool (so we can control its
978         closure) and do our fiddling.
979
980         NOTE: By using base_open() here instead of open_databases(),
981         we will end up re-reading the format file that we just wrote.
982         But it's better to use the existing encapsulation of "opening
983         the filesystem" rather than duplicating (or worse, partially
984         duplicating) that logic here.  */
985      SVN_ERR(base_open(fs, path, common_pool_lock, subpool, common_pool));
986
987      /* Fetch the youngest rev, and record it */
988      SVN_ERR(svn_fs_base__youngest_rev(&youngest_rev, fs, subpool));
989      value = apr_psprintf(subpool, "%ld", youngest_rev);
990      SVN_ERR(svn_fs_base__miscellaneous_set
991              (fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE,
992               value, subpool));
993      svn_pool_destroy(subpool);
994    }
995
996  return SVN_NO_ERROR;
997}
998
999static svn_error_t *
1000base_verify(svn_fs_t *fs, const char *path,
1001            svn_revnum_t start,
1002            svn_revnum_t end,
1003            svn_fs_progress_notify_func_t notify_func,
1004            void *notify_baton,
1005            svn_cancel_func_t cancel_func,
1006            void *cancel_baton,
1007            svn_mutex__t *common_pool_lock,
1008            apr_pool_t *pool,
1009            apr_pool_t *common_pool)
1010{
1011  /* Verifying is currently a no op for BDB. */
1012  return SVN_NO_ERROR;
1013}
1014
1015static svn_error_t *
1016base_bdb_recover(svn_fs_t *fs,
1017                 svn_cancel_func_t cancel_func, void *cancel_baton,
1018                 apr_pool_t *pool)
1019{
1020  /* The fs pointer is a fake created in base_open_for_recovery above.
1021     We only care about the path. */
1022  return bdb_recover(fs->path, FALSE, pool);
1023}
1024
1025static svn_error_t *
1026base_bdb_pack(svn_fs_t *fs,
1027              const char *path,
1028              svn_fs_pack_notify_t notify_func,
1029              void *notify_baton,
1030              svn_cancel_func_t cancel,
1031              void *cancel_baton,
1032              svn_mutex__t *common_pool_lock,
1033              apr_pool_t *pool,
1034              apr_pool_t *common_pool)
1035{
1036  /* Packing is currently a no op for BDB. */
1037  return SVN_NO_ERROR;
1038}
1039
1040
1041
1042/* Running the 'archive' command on a Berkeley DB-based filesystem.  */
1043
1044
1045static svn_error_t *
1046base_bdb_logfiles(apr_array_header_t **logfiles,
1047                  const char *path,
1048                  svn_boolean_t only_unused,
1049                  apr_pool_t *pool)
1050{
1051  bdb_env_baton_t *bdb;
1052  char **filelist;
1053  char **filename;
1054  u_int32_t flags = only_unused ? 0 : DB_ARCH_LOG;
1055
1056  *logfiles = apr_array_make(pool, 4, sizeof(const char *));
1057
1058  SVN_ERR(svn_fs_bdb__open(&bdb, path,
1059                           SVN_BDB_STANDARD_ENV_FLAGS,
1060                           0666, pool));
1061  SVN_BDB_ERR(bdb, bdb->env->log_archive(bdb->env, &filelist, flags));
1062
1063  if (filelist == NULL)
1064    return svn_fs_bdb__close(bdb);
1065
1066  for (filename = filelist; *filename != NULL; ++filename)
1067    {
1068      APR_ARRAY_PUSH(*logfiles, const char *) = apr_pstrdup(pool, *filename);
1069    }
1070
1071  free(filelist);
1072
1073  return svn_fs_bdb__close(bdb);
1074}
1075
1076
1077
1078/* Copying a live Berkeley DB-base filesystem.  */
1079
1080/**
1081 * Delete all unused log files from DBD enviroment at @a live_path that exist
1082 * in @a backup_path.
1083 */
1084static svn_error_t *
1085svn_fs_base__clean_logs(const char *live_path,
1086                        const char *backup_path,
1087                        apr_pool_t *pool)
1088{
1089  apr_array_header_t *logfiles;
1090
1091  SVN_ERR(base_bdb_logfiles(&logfiles,
1092                            live_path,
1093                            TRUE,        /* Only unused logs */
1094                            pool));
1095
1096  {  /* Process unused logs from live area */
1097    int idx;
1098    apr_pool_t *subpool = svn_pool_create(pool);
1099
1100    /* Process log files. */
1101    for (idx = 0; idx < logfiles->nelts; idx++)
1102      {
1103        const char *log_file = APR_ARRAY_IDX(logfiles, idx, const char *);
1104        const char *live_log_path;
1105        const char *backup_log_path;
1106
1107        svn_pool_clear(subpool);
1108        live_log_path = svn_dirent_join(live_path, log_file, subpool);
1109        backup_log_path = svn_dirent_join(backup_path, log_file, subpool);
1110
1111        { /* Compare files. No point in using MD5 and wasting CPU cycles as we
1112             got full copies of both logs */
1113
1114          svn_boolean_t files_match = FALSE;
1115          svn_node_kind_t kind;
1116
1117          /* Check to see if there is a corresponding log file in the backup
1118             directory */
1119          SVN_ERR(svn_io_check_path(backup_log_path, &kind, pool));
1120
1121          /* If the copy of the log exists, compare them */
1122          if (kind == svn_node_file)
1123            SVN_ERR(svn_io_files_contents_same_p(&files_match,
1124                                                 live_log_path,
1125                                                 backup_log_path,
1126                                                 subpool));
1127
1128          /* If log files do not match, go to the next log file. */
1129          if (!files_match)
1130            continue;
1131        }
1132
1133        SVN_ERR(svn_io_remove_file2(live_log_path, FALSE, subpool));
1134      }
1135
1136    svn_pool_destroy(subpool);
1137  }
1138
1139  return SVN_NO_ERROR;
1140}
1141
1142
1143/* DB_ENV->get_flags() and DB->get_pagesize() don't exist prior to
1144   Berkeley DB 4.2. */
1145#if SVN_BDB_VERSION_AT_LEAST(4, 2)
1146
1147/* Open the BDB environment at PATH and compare its configuration
1148   flags with FLAGS.  If every flag in FLAGS is set in the
1149   environment, then set *MATCH to true.  Else set *MATCH to false. */
1150static svn_error_t *
1151check_env_flags(svn_boolean_t *match,
1152                u_int32_t flags,
1153                const char *path,
1154                apr_pool_t *pool)
1155{
1156  bdb_env_baton_t *bdb;
1157#if SVN_BDB_VERSION_AT_LEAST(4, 7)
1158  int flag_state;
1159#else
1160  u_int32_t envflags;
1161#endif
1162
1163  SVN_ERR(svn_fs_bdb__open(&bdb, path,
1164                           SVN_BDB_STANDARD_ENV_FLAGS,
1165                           0666, pool));
1166#if SVN_BDB_VERSION_AT_LEAST(4, 7)
1167  SVN_BDB_ERR(bdb, bdb->env->log_get_config(bdb->env, flags, &flag_state));
1168#else
1169  SVN_BDB_ERR(bdb, bdb->env->get_flags(bdb->env, &envflags));
1170#endif
1171
1172  SVN_ERR(svn_fs_bdb__close(bdb));
1173
1174#if SVN_BDB_VERSION_AT_LEAST(4, 7)
1175  if (flag_state == 0)
1176#else
1177  if (flags & envflags)
1178#endif
1179    *match = TRUE;
1180  else
1181    *match = FALSE;
1182
1183  return SVN_NO_ERROR;
1184}
1185
1186
1187/* Set *PAGESIZE to the size of pages used to hold items in the
1188   database environment located at PATH.
1189*/
1190static svn_error_t *
1191get_db_pagesize(u_int32_t *pagesize,
1192                const char *path,
1193                apr_pool_t *pool)
1194{
1195  bdb_env_baton_t *bdb;
1196  DB *nodes_table;
1197
1198  SVN_ERR(svn_fs_bdb__open(&bdb, path,
1199                           SVN_BDB_STANDARD_ENV_FLAGS,
1200                           0666, pool));
1201
1202  /* ### We're only asking for the pagesize on the 'nodes' table.
1203         Is this enough?  We never call DB->set_pagesize() on any of
1204         our tables, so presumably BDB is using the same default
1205         pagesize for all our databases, right? */
1206  SVN_BDB_ERR(bdb, svn_fs_bdb__open_nodes_table(&nodes_table, bdb->env,
1207                                                FALSE));
1208  SVN_BDB_ERR(bdb, nodes_table->get_pagesize(nodes_table, pagesize));
1209  SVN_BDB_ERR(bdb, nodes_table->close(nodes_table, 0));
1210
1211  return svn_fs_bdb__close(bdb);
1212}
1213#endif /* SVN_BDB_VERSION_AT_LEAST(4, 2) */
1214
1215
1216/* Copy FILENAME from SRC_DIR to DST_DIR in byte increments of size
1217   CHUNKSIZE.  The read/write buffer of size CHUNKSIZE will be
1218   allocated in POOL.  If ALLOW_MISSING is set, we won't make a fuss
1219   if FILENAME isn't found in SRC_DIR; otherwise, we will.  */
1220static svn_error_t *
1221copy_db_file_safely(const char *src_dir,
1222                    const char *dst_dir,
1223                    const char *filename,
1224                    u_int32_t chunksize,
1225                    svn_boolean_t allow_missing,
1226                    apr_pool_t *pool)
1227{
1228  apr_file_t *s = NULL, *d = NULL;  /* init to null important for APR */
1229  const char *file_src_path = svn_dirent_join(src_dir, filename, pool);
1230  const char *file_dst_path = svn_dirent_join(dst_dir, filename, pool);
1231  svn_error_t *err;
1232  char *buf;
1233
1234  /* Open source file.  If it's missing and that's allowed, there's
1235     nothing more to do here. */
1236  err = svn_io_file_open(&s, file_src_path,
1237                         (APR_READ | APR_LARGEFILE),
1238                         APR_OS_DEFAULT, pool);
1239  if (err && APR_STATUS_IS_ENOENT(err->apr_err) && allow_missing)
1240    {
1241      svn_error_clear(err);
1242      return SVN_NO_ERROR;
1243    }
1244  SVN_ERR(err);
1245
1246  /* Open destination file. */
1247  SVN_ERR(svn_io_file_open(&d, file_dst_path, (APR_WRITE | APR_CREATE |
1248                                               APR_LARGEFILE),
1249                           APR_OS_DEFAULT, pool));
1250
1251  /* Allocate our read/write buffer. */
1252  buf = apr_palloc(pool, chunksize);
1253
1254  /* Copy bytes till the cows come home. */
1255  while (1)
1256    {
1257      apr_size_t bytes_this_time = chunksize;
1258      svn_error_t *read_err, *write_err;
1259
1260      /* Read 'em. */
1261      if ((read_err = svn_io_file_read(s, buf, &bytes_this_time, pool)))
1262        {
1263          if (APR_STATUS_IS_EOF(read_err->apr_err))
1264            svn_error_clear(read_err);
1265          else
1266            {
1267              svn_error_clear(svn_io_file_close(s, pool));
1268              svn_error_clear(svn_io_file_close(d, pool));
1269              return read_err;
1270            }
1271        }
1272
1273      /* Write 'em. */
1274      if ((write_err = svn_io_file_write_full(d, buf, bytes_this_time, NULL,
1275                                              pool)))
1276        {
1277          svn_error_clear(svn_io_file_close(s, pool));
1278          svn_error_clear(svn_io_file_close(d, pool));
1279          return write_err;
1280        }
1281
1282      /* read_err is either NULL, or a dangling pointer - but it is only a
1283         dangling pointer if it used to be an EOF error. */
1284      if (read_err)
1285        {
1286          SVN_ERR(svn_io_file_close(s, pool));
1287          SVN_ERR(svn_io_file_close(d, pool));
1288          break;  /* got EOF on read, all files closed, all done. */
1289        }
1290    }
1291
1292  return SVN_NO_ERROR;
1293}
1294
1295
1296
1297
1298static svn_error_t *
1299base_hotcopy(svn_fs_t *src_fs,
1300             svn_fs_t *dst_fs,
1301             const char *src_path,
1302             const char *dest_path,
1303             svn_boolean_t clean_logs,
1304             svn_boolean_t incremental,
1305             svn_fs_hotcopy_notify_t notify_func,
1306             void *notify_baton,
1307             svn_cancel_func_t cancel_func,
1308             void *cancel_baton,
1309             svn_mutex__t *common_pool_lock,
1310             apr_pool_t *pool,
1311             apr_pool_t *common_pool)
1312{
1313  svn_error_t *err;
1314  u_int32_t pagesize;
1315  svn_boolean_t log_autoremove = FALSE;
1316  int format;
1317
1318  if (incremental)
1319    return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
1320                             _("BDB repositories do not support incremental "
1321                               "hotcopy"));
1322
1323  /* Check the FS format number to be certain that we know how to
1324     hotcopy this FS.  Pre-1.2 filesystems did not have a format file (you
1325     could say they were format "0"), so we will error here.  This is not
1326     optimal, but since this has been the case since 1.2.0, and no one has
1327     complained, it apparently isn't much of a concern.  (We did not check
1328     the 'format' file in 1.2.x, but we did blindly try to copy 'locks',
1329     which would have errored just the same.)  */
1330  SVN_ERR(svn_io_read_version_file(
1331          &format, svn_dirent_join(src_path, FORMAT_FILE, pool), pool));
1332  SVN_ERR(check_format(format));
1333
1334  /* If using Berkeley DB 4.2 or later, note whether the DB_LOG_AUTO_REMOVE
1335     feature is on.  If it is, we have a potential race condition:
1336     another process might delete a logfile while we're in the middle
1337     of copying all the logfiles.  (This is not a huge deal; at worst,
1338     the hotcopy fails with a file-not-found error.) */
1339#if SVN_BDB_VERSION_AT_LEAST(4, 2)
1340  err = check_env_flags(&log_autoremove,
1341#if SVN_BDB_VERSION_AT_LEAST(4, 7)
1342                          DB_LOG_AUTO_REMOVE,
1343 /* DB_LOG_AUTO_REMOVE was named DB_LOG_AUTOREMOVE before Berkeley DB 4.7. */
1344#else
1345                          DB_LOG_AUTOREMOVE,
1346#endif
1347                          src_path, pool);
1348#endif
1349  SVN_ERR(err);
1350
1351  /* Copy the DB_CONFIG file. */
1352  SVN_ERR(svn_io_dir_file_copy(src_path, dest_path, "DB_CONFIG", pool));
1353
1354  /* In order to copy the database files safely and atomically, we
1355     must copy them in chunks which are multiples of the page-size
1356     used by BDB.  See sleepycat docs for details, or svn issue #1818. */
1357#if SVN_BDB_VERSION_AT_LEAST(4, 2)
1358  SVN_ERR(get_db_pagesize(&pagesize, src_path, pool));
1359  if (pagesize < SVN__STREAM_CHUNK_SIZE)
1360    {
1361      /* use the largest multiple of BDB pagesize we can. */
1362      int multiple = SVN__STREAM_CHUNK_SIZE / pagesize;
1363      pagesize *= multiple;
1364    }
1365#else
1366  /* default to 128K chunks, which should be safe.
1367     BDB almost certainly uses a power-of-2 pagesize. */
1368  pagesize = (4096 * 32);
1369#endif
1370
1371  /* Copy the databases.  */
1372  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1373                              "nodes", pagesize, FALSE, pool));
1374  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1375                              "transactions", pagesize, FALSE, pool));
1376  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1377                              "revisions", pagesize, FALSE, pool));
1378  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1379                              "copies", pagesize, FALSE, pool));
1380  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1381                              "changes", pagesize, FALSE, pool));
1382  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1383                              "representations", pagesize, FALSE, pool));
1384  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1385                              "strings", pagesize, FALSE, pool));
1386  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1387                              "uuids", pagesize, TRUE, pool));
1388  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1389                              "locks", pagesize, TRUE, pool));
1390  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1391                              "lock-tokens", pagesize, TRUE, pool));
1392  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1393                              "node-origins", pagesize, TRUE, pool));
1394  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1395                              "checksum-reps", pagesize, TRUE, pool));
1396  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1397                              "miscellaneous", pagesize, TRUE, pool));
1398
1399  {
1400    apr_array_header_t *logfiles;
1401    int idx;
1402    apr_pool_t *subpool;
1403
1404    SVN_ERR(base_bdb_logfiles(&logfiles,
1405                              src_path,
1406                              FALSE,   /* All logs */
1407                              pool));
1408
1409    /* Process log files. */
1410    subpool = svn_pool_create(pool);
1411    for (idx = 0; idx < logfiles->nelts; idx++)
1412      {
1413        svn_pool_clear(subpool);
1414        err = svn_io_dir_file_copy(src_path, dest_path,
1415                                   APR_ARRAY_IDX(logfiles, idx,
1416                                                 const char *),
1417                                   subpool);
1418        if (err)
1419          {
1420            if (log_autoremove)
1421              return
1422                svn_error_quick_wrap
1423                (err,
1424                 _("Error copying logfile;  the DB_LOG_AUTOREMOVE feature\n"
1425                   "may be interfering with the hotcopy algorithm.  If\n"
1426                   "the problem persists, try deactivating this feature\n"
1427                   "in DB_CONFIG"));
1428            else
1429              return svn_error_trace(err);
1430          }
1431      }
1432    svn_pool_destroy(subpool);
1433  }
1434
1435  /* Since this is a copy we will have exclusive access to the repository. */
1436  err = bdb_recover(dest_path, TRUE, pool);
1437  if (err)
1438    {
1439      if (log_autoremove)
1440        return
1441          svn_error_quick_wrap
1442          (err,
1443           _("Error running catastrophic recovery on hotcopy;  the\n"
1444             "DB_LOG_AUTOREMOVE feature may be interfering with the\n"
1445             "hotcopy algorithm.  If the problem persists, try deactivating\n"
1446             "this feature in DB_CONFIG"));
1447      else
1448        return svn_error_trace(err);
1449    }
1450
1451  /* Only now that the hotcopied filesystem is complete,
1452     stamp it with a format file. */
1453  SVN_ERR(svn_io_write_version_file(
1454             svn_dirent_join(dest_path, FORMAT_FILE, pool), format, pool));
1455
1456  if (clean_logs)
1457    SVN_ERR(svn_fs_base__clean_logs(src_path, dest_path, pool));
1458
1459  return SVN_NO_ERROR;
1460}
1461
1462
1463
1464/* Deleting a Berkeley DB-based filesystem.  */
1465
1466
1467static svn_error_t *
1468base_delete_fs(const char *path,
1469               apr_pool_t *pool)
1470{
1471  /* First, use the Berkeley DB library function to remove any shared
1472     memory segments.  */
1473  SVN_ERR(svn_fs_bdb__remove(path, pool));
1474
1475  /* Remove the environment directory. */
1476  return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
1477}
1478
1479static const svn_version_t *
1480base_version(void)
1481{
1482  SVN_VERSION_BODY;
1483}
1484
1485static const char *
1486base_get_description(void)
1487{
1488  return _("Module for working with a Berkeley DB repository.");
1489}
1490
1491static svn_error_t *
1492base_set_svn_fs_open(svn_fs_t *fs,
1493                     svn_error_t *(*svn_fs_open_)(svn_fs_t **,
1494                                                  const char *,
1495                                                  apr_hash_t *,
1496                                                  apr_pool_t *,
1497                                                  apr_pool_t *))
1498{
1499  return SVN_NO_ERROR;
1500}
1501
1502
1503/* Base FS library vtable, used by the FS loader library. */
1504static fs_library_vtable_t library_vtable = {
1505  base_version,
1506  base_create,
1507  base_open,
1508  base_open_for_recovery,
1509  base_upgrade,
1510  base_verify,
1511  base_delete_fs,
1512  base_hotcopy,
1513  base_get_description,
1514  base_bdb_recover,
1515  base_bdb_pack,
1516  base_bdb_logfiles,
1517  svn_fs_base__id_parse,
1518  base_set_svn_fs_open,
1519  NULL /* info_fsap_dup */,
1520  NULL /* ioctl */
1521};
1522
1523svn_error_t *
1524svn_fs_base__init(const svn_version_t *loader_version,
1525                  fs_library_vtable_t **vtable, apr_pool_t* common_pool)
1526{
1527  static const svn_version_checklist_t checklist[] =
1528    {
1529      { "svn_subr",  svn_subr_version },
1530      { "svn_delta", svn_delta_version },
1531      { "svn_fs_util", svn_fs_util__version },
1532      { NULL, NULL }
1533    };
1534
1535  /* Simplified version check to make sure we can safely use the
1536     VTABLE parameter. The FS loader does a more exhaustive check. */
1537  if (loader_version->major != SVN_VER_MAJOR)
1538    return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
1539                             _("Unsupported FS loader version (%d) for bdb"),
1540                             loader_version->major);
1541  SVN_ERR(svn_ver_check_list2(base_version(), checklist, svn_ver_equal));
1542  SVN_ERR(check_bdb_version());
1543  SVN_ERR(svn_fs_bdb__init(common_pool));
1544
1545  *vtable = &library_vtable;
1546  return SVN_NO_ERROR;
1547}
1548