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