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