1 /* fs.c --- creating, opening and closing filesystems
2 *
3 * ====================================================================
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 * ====================================================================
21 */
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include <apr_general.h>
28 #include <apr_pools.h>
29 #include <apr_file_io.h>
30
31 #include "svn_hash.h"
32 #include "svn_pools.h"
33 #include "svn_fs.h"
34 #include "svn_path.h"
35 #include "svn_utf.h"
36 #include "svn_delta.h"
37 #include "svn_version.h"
38 #include "fs.h"
39 #include "err.h"
40 #include "dag.h"
41 #include "revs-txns.h"
42 #include "uuid.h"
43 #include "tree.h"
44 #include "id.h"
45 #include "lock.h"
46 #define SVN_WANT_BDB
47 #include "svn_private_config.h"
48
49 #include "bdb/bdb-err.h"
50 #include "bdb/bdb_compat.h"
51 #include "bdb/env.h"
52 #include "bdb/nodes-table.h"
53 #include "bdb/rev-table.h"
54 #include "bdb/txn-table.h"
55 #include "bdb/copies-table.h"
56 #include "bdb/changes-table.h"
57 #include "bdb/reps-table.h"
58 #include "bdb/strings-table.h"
59 #include "bdb/uuids-table.h"
60 #include "bdb/locks-table.h"
61 #include "bdb/lock-tokens-table.h"
62 #include "bdb/node-origins-table.h"
63 #include "bdb/miscellaneous-table.h"
64 #include "bdb/checksum-reps-table.h"
65
66 #include "../libsvn_fs/fs-loader.h"
67 #include "private/svn_fs_util.h"
68
69
70 /* Checking for return values, and reporting errors. */
71
72 /* Check that we're using the right Berkeley DB version. */
73 /* FIXME: This check should be abstracted into the DB back-end layer. */
74 static svn_error_t *
check_bdb_version(void)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
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 *
cleanup_fs_db(svn_fs_t * fs,DB ** db_ptr,const char * name)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 *
cleanup_fs(svn_fs_t * fs)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
cleanup_fs_apr(void * data)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
305 static svn_error_t *
base_bdb_set_errcall(svn_fs_t * fs,void (* db_errcall_fcn)(const char * errpfx,char * msg))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
318 /* Write the DB_CONFIG file. */
319 static svn_error_t *
bdb_write_config(svn_fs_t * fs)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 *
base_bdb_refresh_revision(svn_fs_t * fs,apr_pool_t * scratch_pool)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 *
base_bdb_info_format(int * fs_format,svn_version_t ** supports_version,svn_fs_t * fs,apr_pool_t * result_pool,apr_pool_t * scratch_pool)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 *
base_bdb_info_config_files(apr_array_header_t ** files,svn_fs_t * fs,apr_pool_t * result_pool,apr_pool_t * scratch_pool)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 *
base_bdb_verify_root(svn_fs_root_t * root,apr_pool_t * scratch_pool)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 *
base_bdb_freeze(svn_fs_t * fs,svn_fs_freeze_func_t freeze_func,void * freeze_baton,apr_pool_t * pool)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
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 *
open_databases(svn_fs_t * fs,svn_boolean_t create,int format,const char * path)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 *
populate_opened_fs(svn_fs_t * fs,apr_pool_t * scratch_pool)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 *
base_create(svn_fs_t * fs,const char * path,svn_mutex__t * common_pool_lock,apr_pool_t * scratch_pool,apr_pool_t * common_pool)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
800 /* Gaining access to an existing Berkeley DB-based filesystem. */
801
802 svn_error_t *
svn_fs_base__test_required_feature_format(svn_fs_t * fs,const char * feature,int requires)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 *
check_format(int format)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 *
base_open(svn_fs_t * fs,const char * path,svn_mutex__t * common_pool_lock,apr_pool_t * scratch_pool,apr_pool_t * common_pool)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
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 *
bdb_recover(const char * path,svn_boolean_t fatal,apr_pool_t * pool)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 *
base_open_for_recovery(svn_fs_t * fs,const char * path,svn_mutex__t * common_pool_lock,apr_pool_t * pool,apr_pool_t * common_pool)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 *
base_upgrade(svn_fs_t * fs,const char * path,svn_fs_upgrade_notify_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_mutex__t * common_pool_lock,apr_pool_t * pool,apr_pool_t * common_pool)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 *
base_verify(svn_fs_t * fs,const char * path,svn_revnum_t start,svn_revnum_t end,svn_fs_progress_notify_func_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_mutex__t * common_pool_lock,apr_pool_t * pool,apr_pool_t * common_pool)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 *
base_bdb_recover(svn_fs_t * fs,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * pool)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 *
base_bdb_pack(svn_fs_t * fs,const char * path,svn_fs_pack_notify_t notify_func,void * notify_baton,svn_cancel_func_t cancel,void * cancel_baton,svn_mutex__t * common_pool_lock,apr_pool_t * pool,apr_pool_t * common_pool)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
1042 /* Running the 'archive' command on a Berkeley DB-based filesystem. */
1043
1044
1045 static svn_error_t *
base_bdb_logfiles(apr_array_header_t ** logfiles,const char * path,svn_boolean_t only_unused,apr_pool_t * pool)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
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 *
svn_fs_base__clean_logs(const char * live_path,const char * backup_path,apr_pool_t * pool)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 *
check_env_flags(svn_boolean_t * match,u_int32_t flags,const char * path,apr_pool_t * pool)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 *
get_db_pagesize(u_int32_t * pagesize,const char * path,apr_pool_t * pool)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 *
copy_db_file_safely(const char * src_dir,const char * dst_dir,const char * filename,u_int32_t chunksize,svn_boolean_t allow_missing,apr_pool_t * pool)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 *
base_hotcopy(svn_fs_t * src_fs,svn_fs_t * dst_fs,const char * src_path,const char * dest_path,svn_boolean_t clean_logs,svn_boolean_t incremental,svn_fs_hotcopy_notify_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_mutex__t * common_pool_lock,apr_pool_t * pool,apr_pool_t * common_pool)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
1464 /* Deleting a Berkeley DB-based filesystem. */
1465
1466
1467 static svn_error_t *
base_delete_fs(const char * path,apr_pool_t * pool)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 *
base_version(void)1480 base_version(void)
1481 {
1482 SVN_VERSION_BODY;
1483 }
1484
1485 static const char *
base_get_description(void)1486 base_get_description(void)
1487 {
1488 return _("Module for working with a Berkeley DB repository.");
1489 }
1490
1491 static svn_error_t *
base_set_svn_fs_open(svn_fs_t * fs,svn_error_t * (* svn_fs_open_)(svn_fs_t **,const char *,apr_hash_t *,apr_pool_t *,apr_pool_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
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 *
svn_fs_base__init(const svn_version_t * loader_version,fs_library_vtable_t ** vtable,apr_pool_t * common_pool)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 }
1548