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