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