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