fs.c revision 262253
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#include "private/svn_subr_private.h" 69 70 71 72/* Checking for return values, and reporting errors. */ 73 74/* Check that we're using the right Berkeley DB version. */ 75/* FIXME: This check should be abstracted into the DB back-end layer. */ 76static svn_error_t * 77check_bdb_version(void) 78{ 79 int major, minor, patch; 80 81 db_version(&major, &minor, &patch); 82 83 /* First, check that we're using a reasonably correct of Berkeley DB. */ 84 if ((major < SVN_FS_WANT_DB_MAJOR) 85 || (major == SVN_FS_WANT_DB_MAJOR && minor < SVN_FS_WANT_DB_MINOR) 86 || (major == SVN_FS_WANT_DB_MAJOR && minor == SVN_FS_WANT_DB_MINOR 87 && patch < SVN_FS_WANT_DB_PATCH)) 88 return svn_error_createf(SVN_ERR_FS_GENERAL, 0, 89 _("Bad database version: got %d.%d.%d," 90 " should be at least %d.%d.%d"), 91 major, minor, patch, 92 SVN_FS_WANT_DB_MAJOR, 93 SVN_FS_WANT_DB_MINOR, 94 SVN_FS_WANT_DB_PATCH); 95 96 /* Now, check that the version we're running against is the same as 97 the one we compiled with. */ 98 if (major != DB_VERSION_MAJOR || minor != DB_VERSION_MINOR) 99 return svn_error_createf(SVN_ERR_FS_GENERAL, 0, 100 _("Bad database version:" 101 " compiled with %d.%d.%d," 102 " running against %d.%d.%d"), 103 DB_VERSION_MAJOR, 104 DB_VERSION_MINOR, 105 DB_VERSION_PATCH, 106 major, minor, patch); 107 return SVN_NO_ERROR; 108} 109 110 111 112/* Cleanup functions. */ 113 114/* Close a database in the filesystem FS. 115 DB_PTR is a pointer to the DB pointer in *FS to close. 116 NAME is the name of the database, for use in error messages. */ 117static svn_error_t * 118cleanup_fs_db(svn_fs_t *fs, DB **db_ptr, const char *name) 119{ 120 /* If the BDB environment is panicked, don't do anything, since 121 attempting to close the database will fail anyway. */ 122 base_fs_data_t *bfd = fs->fsap_data; 123 if (*db_ptr && !svn_fs_bdb__get_panic(bfd->bdb)) 124 { 125 DB *db = *db_ptr; 126 char *msg = apr_psprintf(fs->pool, "closing '%s' database", name); 127 int db_err; 128 129 *db_ptr = 0; 130 db_err = db->close(db, 0); 131 if (db_err == DB_RUNRECOVERY) 132 { 133 /* We can ignore DB_RUNRECOVERY errors from DB->close, but 134 must set the panic flag in the environment baton. The 135 error will be propagated appropriately from 136 svn_fs_bdb__close. */ 137 svn_fs_bdb__set_panic(bfd->bdb); 138 db_err = 0; 139 } 140 141#if SVN_BDB_HAS_DB_INCOMPLETE 142 /* We can ignore DB_INCOMPLETE on db->close and db->sync; it 143 * just means someone else was using the db at the same time 144 * we were. See the Berkeley documentation at: 145 * http://www.sleepycat.com/docs/ref/program/errorret.html#DB_INCOMPLETE 146 * http://www.sleepycat.com/docs/api_c/db_close.html 147 */ 148 if (db_err == DB_INCOMPLETE) 149 db_err = 0; 150#endif /* SVN_BDB_HAS_DB_INCOMPLETE */ 151 152 SVN_ERR(BDB_WRAP(fs, msg, db_err)); 153 } 154 155 return SVN_NO_ERROR; 156} 157 158/* Close whatever Berkeley DB resources are allocated to FS. */ 159static svn_error_t * 160cleanup_fs(svn_fs_t *fs) 161{ 162 base_fs_data_t *bfd = fs->fsap_data; 163 bdb_env_baton_t *bdb = (bfd ? bfd->bdb : NULL); 164 165 if (!bdb) 166 return SVN_NO_ERROR; 167 168 /* Close the databases. */ 169 SVN_ERR(cleanup_fs_db(fs, &bfd->nodes, "nodes")); 170 SVN_ERR(cleanup_fs_db(fs, &bfd->revisions, "revisions")); 171 SVN_ERR(cleanup_fs_db(fs, &bfd->transactions, "transactions")); 172 SVN_ERR(cleanup_fs_db(fs, &bfd->copies, "copies")); 173 SVN_ERR(cleanup_fs_db(fs, &bfd->changes, "changes")); 174 SVN_ERR(cleanup_fs_db(fs, &bfd->representations, "representations")); 175 SVN_ERR(cleanup_fs_db(fs, &bfd->strings, "strings")); 176 SVN_ERR(cleanup_fs_db(fs, &bfd->uuids, "uuids")); 177 SVN_ERR(cleanup_fs_db(fs, &bfd->locks, "locks")); 178 SVN_ERR(cleanup_fs_db(fs, &bfd->lock_tokens, "lock-tokens")); 179 SVN_ERR(cleanup_fs_db(fs, &bfd->node_origins, "node-origins")); 180 SVN_ERR(cleanup_fs_db(fs, &bfd->checksum_reps, "checksum-reps")); 181 SVN_ERR(cleanup_fs_db(fs, &bfd->miscellaneous, "miscellaneous")); 182 183 /* Finally, close the environment. */ 184 bfd->bdb = 0; 185 { 186 svn_error_t *err = svn_fs_bdb__close(bdb); 187 if (err) 188 return svn_error_createf 189 (err->apr_err, err, 190 _("Berkeley DB error for filesystem '%s'" 191 " while closing environment:\n"), 192 fs->path); 193 } 194 return SVN_NO_ERROR; 195} 196 197#if 0 /* Set to 1 for instrumenting. */ 198static void print_fs_stats(svn_fs_t *fs) 199{ 200 base_fs_data_t *bfd = fs->fsap_data; 201 DB_TXN_STAT *t; 202 DB_LOCK_STAT *l; 203 int db_err; 204 205 /* Print transaction statistics for this DB env. */ 206 if ((db_err = bfd->bdb->env->txn_stat(bfd->bdb->env, &t, 0)) != 0) 207 fprintf(stderr, "Error running bfd->bdb->env->txn_stat(): %s", 208 db_strerror(db_err)); 209 else 210 { 211 printf("*** DB transaction stats, right before closing env:\n"); 212 printf(" Number of transactions currently active: %d\n", 213 t->st_nactive); 214 printf(" Max number of active transactions at any one time: %d\n", 215 t->st_maxnactive); 216 printf(" Number of transactions that have begun: %d\n", 217 t->st_nbegins); 218 printf(" Number of transactions that have aborted: %d\n", 219 t->st_naborts); 220 printf(" Number of transactions that have committed: %d\n", 221 t->st_ncommits); 222 printf(" Number of times a thread was forced to wait: %d\n", 223 t->st_region_wait); 224 printf(" Number of times a thread didn't need to wait: %d\n", 225 t->st_region_nowait); 226 printf("*** End DB transaction stats.\n\n"); 227 } 228 229 /* Print transaction statistics for this DB env. */ 230 if ((db_err = bfd->bdb->env->lock_stat(bfd->bdb->env, &l, 0)) != 0) 231 fprintf(stderr, "Error running bfd->bdb->env->lock_stat(): %s", 232 db_strerror(db_err)); 233 else 234 { 235 printf("*** DB lock stats, right before closing env:\n"); 236 printf(" The number of current locks: %d\n", 237 l->st_nlocks); 238 printf(" Max number of locks at any one time: %d\n", 239 l->st_maxnlocks); 240 printf(" Number of current lockers: %d\n", 241 l->st_nlockers); 242 printf(" Max number of lockers at any one time: %d\n", 243 l->st_maxnlockers); 244 printf(" Number of current objects: %d\n", 245 l->st_nobjects); 246 printf(" Max number of objects at any one time: %d\n", 247 l->st_maxnobjects); 248 printf(" Total number of locks requested: %d\n", 249 l->st_nrequests); 250 printf(" Total number of locks released: %d\n", 251 l->st_nreleases); 252 printf(" Total number of lock reqs failed because " 253 "DB_LOCK_NOWAIT was set: %d\n", l->st_nnowaits); 254 printf(" Total number of locks not immediately available " 255 "due to conflicts: %d\n", l->st_nconflicts); 256 printf(" Number of deadlocks detected: %d\n", l->st_ndeadlocks); 257 printf(" Number of times a thread waited before " 258 "obtaining the region lock: %d\n", l->st_region_wait); 259 printf(" Number of times a thread didn't have to wait: %d\n", 260 l->st_region_nowait); 261 printf("*** End DB lock stats.\n\n"); 262 } 263 264} 265#else 266# define print_fs_stats(fs) 267#endif /* 0/1 */ 268 269/* An APR pool cleanup function for a filesystem. DATA must be a 270 pointer to the filesystem to clean up. 271 272 When the filesystem object's pool is freed, we want the resources 273 held by Berkeley DB to go away, just like everything else. So we 274 register this cleanup function with the filesystem's pool, and let 275 it take care of closing the databases, the environment, and any 276 other DB objects we might be using. APR calls this function before 277 actually freeing the pool's memory. 278 279 It's a pity that we can't return an svn_error_t object from an APR 280 cleanup function. For now, we return the rather generic 281 SVN_ERR_FS_CLEANUP, and pass the real svn_error_t to the registered 282 warning callback. */ 283 284static apr_status_t 285cleanup_fs_apr(void *data) 286{ 287 svn_fs_t *fs = data; 288 svn_error_t *err; 289 290 print_fs_stats(fs); 291 292 err = cleanup_fs(fs); 293 if (! err) 294 return APR_SUCCESS; 295 296 /* Darn. An error during cleanup. Call the warning handler to 297 try and do something "right" with this error. Note that 298 the default will simply abort(). */ 299 (*fs->warning)(fs->warning_baton, err); 300 301 svn_error_clear(err); 302 303 return SVN_ERR_FS_CLEANUP; 304} 305 306 307static svn_error_t * 308base_bdb_set_errcall(svn_fs_t *fs, 309 void (*db_errcall_fcn)(const char *errpfx, char *msg)) 310{ 311 base_fs_data_t *bfd = fs->fsap_data; 312 313 SVN_ERR(svn_fs__check_fs(fs, TRUE)); 314 bfd->bdb->error_info->user_callback = db_errcall_fcn; 315 316 return SVN_NO_ERROR; 317} 318 319 320/* Write the DB_CONFIG file. */ 321static svn_error_t * 322bdb_write_config(svn_fs_t *fs) 323{ 324 const char *dbconfig_file_name = 325 svn_dirent_join(fs->path, BDB_CONFIG_FILE, fs->pool); 326 apr_file_t *dbconfig_file = NULL; 327 int i; 328 329 static const char dbconfig_contents[] = 330 "# This is the configuration file for the Berkeley DB environment\n" 331 "# used by your Subversion repository.\n" 332 "# You must run 'svnadmin recover' whenever you modify this file,\n" 333 "# for your changes to take effect.\n" 334 "\n" 335 "### Lock subsystem\n" 336 "#\n" 337 "# Make sure you read the documentation at:\n" 338 "#\n" 339 "# http://docs.oracle.com/cd/E17076_02/html/programmer_reference/lock_max.html\n" 340 "#\n" 341 "# before tweaking these values.\n" 342 "#\n" 343 "set_lk_max_locks 2000\n" 344 "set_lk_max_lockers 2000\n" 345 "set_lk_max_objects 2000\n" 346 "\n" 347 "### Log file subsystem\n" 348 "#\n" 349 "# Make sure you read the documentation at:\n" 350 "#\n" 351 "# http://docs.oracle.com/cd/E17076_02/html/api_reference/C/envset_lg_bsize.html\n" 352 "# http://docs.oracle.com/cd/E17076_02/html/api_reference/C/envset_lg_max.html\n" 353 "# http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_limits.html\n" 354 "#\n" 355 "# Increase the size of the in-memory log buffer from the default\n" 356 "# of 32 Kbytes to 256 Kbytes. Decrease the log file size from\n" 357 "# 10 Mbytes to 1 Mbyte. This will help reduce the amount of disk\n" 358 "# space required for hot backups. The size of the log file must be\n" 359 "# at least four times the size of the in-memory log buffer.\n" 360 "#\n" 361 "# Note: Decreasing the in-memory buffer size below 256 Kbytes will hurt\n" 362 "# hurt commit performance. For details, see:\n" 363 "#\n" 364 "# http://svn.haxx.se/dev/archive-2002-02/0141.shtml\n" 365 "#\n" 366 "set_lg_bsize 262144\n" 367 "set_lg_max 1048576\n" 368 "#\n" 369 "# If you see \"log region out of memory\" errors, bump lg_regionmax.\n" 370 "# For more information, see:\n" 371 "#\n" 372 "# http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_config.html\n" 373 "# http://svn.haxx.se/users/archive-2004-10/1000.shtml\n" 374 "#\n" 375 "set_lg_regionmax 131072\n" 376 "#\n" 377 /* ### Configure this with "svnadmin create --bdb-cache-size" */ 378 "# The default cache size in BDB is only 256k. As explained in\n" 379 "# http://svn.haxx.se/dev/archive-2004-12/0368.shtml, this is too\n" 380 "# small for most applications. Bump this number if \"db_stat -m\"\n" 381 "# shows too many cache misses.\n" 382 "#\n" 383 "set_cachesize 0 1048576 1\n"; 384 385 /* Run-time configurable options. 386 Each option set consists of a minimum required BDB version, a 387 config hash key, a header, an inactive form and an active 388 form. We always write the header; then, depending on the 389 run-time configuration and the BDB version we're compiling 390 against, we write either the active or inactive form of the 391 value. */ 392 static const struct 393 { 394 int bdb_major; 395 int bdb_minor; 396 const char *config_key; 397 const char *header; 398 const char *inactive; 399 const char *active; 400 } dbconfig_options[] = { 401 /* Controlled by "svnadmin create --bdb-txn-nosync" */ 402 { 4, 0, SVN_FS_CONFIG_BDB_TXN_NOSYNC, 403 /* header */ 404 "#\n" 405 "# Disable fsync of log files on transaction commit. Read the\n" 406 "# documentation about DB_TXN_NOSYNC at:\n" 407 "#\n" 408 "# http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_config.html\n" 409 "#\n" 410 "# [requires Berkeley DB 4.0]\n" 411 "#\n", 412 /* inactive */ 413 "#set_flags DB_TXN_NOSYNC\n", 414 /* active */ 415 "set_flags DB_TXN_NOSYNC\n" }, 416 /* Controlled by "svnadmin create --bdb-log-keep" */ 417 { 4, 2, SVN_FS_CONFIG_BDB_LOG_AUTOREMOVE, 418 /* header */ 419 "#\n" 420 "# Enable automatic removal of unused transaction log files.\n" 421 "# Read the documentation about DB_LOG_AUTOREMOVE at:\n" 422 "#\n" 423 "# http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_config.html\n" 424 "#\n" 425 "# [requires Berkeley DB 4.2]\n" 426 "#\n", 427 /* inactive */ 428 "#set_flags DB_LOG_AUTOREMOVE\n", 429 /* active */ 430 "set_flags DB_LOG_AUTOREMOVE\n" }, 431 }; 432 static const int dbconfig_options_length = 433 sizeof(dbconfig_options)/sizeof(*dbconfig_options); 434 435 436 SVN_ERR(svn_io_file_open(&dbconfig_file, dbconfig_file_name, 437 APR_WRITE | APR_CREATE, APR_OS_DEFAULT, 438 fs->pool)); 439 440 SVN_ERR(svn_io_file_write_full(dbconfig_file, dbconfig_contents, 441 sizeof(dbconfig_contents) - 1, NULL, 442 fs->pool)); 443 444 /* Write the variable DB_CONFIG flags. */ 445 for (i = 0; i < dbconfig_options_length; ++i) 446 { 447 void *value = NULL; 448 const char *choice; 449 450 if (fs->config) 451 { 452 value = svn_hash_gets(fs->config, dbconfig_options[i].config_key); 453 } 454 455 SVN_ERR(svn_io_file_write_full(dbconfig_file, 456 dbconfig_options[i].header, 457 strlen(dbconfig_options[i].header), 458 NULL, fs->pool)); 459 460 if (((DB_VERSION_MAJOR == dbconfig_options[i].bdb_major 461 && DB_VERSION_MINOR >= dbconfig_options[i].bdb_minor) 462 || DB_VERSION_MAJOR > dbconfig_options[i].bdb_major) 463 && value != NULL && strcmp(value, "0") != 0) 464 choice = dbconfig_options[i].active; 465 else 466 choice = dbconfig_options[i].inactive; 467 468 SVN_ERR(svn_io_file_write_full(dbconfig_file, choice, strlen(choice), 469 NULL, fs->pool)); 470 } 471 472 return svn_io_file_close(dbconfig_file, fs->pool); 473} 474 475static svn_error_t * 476base_bdb_verify_root(svn_fs_root_t *root, 477 apr_pool_t *scratch_pool) 478{ 479 /* Verifying is currently a no op for BDB. */ 480 return SVN_NO_ERROR; 481} 482 483static svn_error_t * 484base_bdb_freeze(svn_fs_t *fs, 485 svn_fs_freeze_func_t freeze_func, 486 void *freeze_baton, 487 apr_pool_t *pool) 488{ 489 SVN__NOT_IMPLEMENTED(); 490} 491 492 493/* Creating a new filesystem */ 494 495static fs_vtable_t fs_vtable = { 496 svn_fs_base__youngest_rev, 497 svn_fs_base__revision_prop, 498 svn_fs_base__revision_proplist, 499 svn_fs_base__change_rev_prop, 500 svn_fs_base__set_uuid, 501 svn_fs_base__revision_root, 502 svn_fs_base__begin_txn, 503 svn_fs_base__open_txn, 504 svn_fs_base__purge_txn, 505 svn_fs_base__list_transactions, 506 svn_fs_base__deltify, 507 svn_fs_base__lock, 508 svn_fs_base__generate_lock_token, 509 svn_fs_base__unlock, 510 svn_fs_base__get_lock, 511 svn_fs_base__get_locks, 512 base_bdb_verify_root, 513 base_bdb_freeze, 514 base_bdb_set_errcall, 515}; 516 517/* Where the format number is stored. */ 518#define FORMAT_FILE "format" 519 520/* Depending on CREATE, create or open the environment and databases 521 for filesystem FS in PATH. Use POOL for temporary allocations. */ 522static svn_error_t * 523open_databases(svn_fs_t *fs, 524 svn_boolean_t create, 525 int format, 526 const char *path, 527 apr_pool_t *pool) 528{ 529 base_fs_data_t *bfd; 530 531 SVN_ERR(svn_fs__check_fs(fs, FALSE)); 532 533 bfd = apr_pcalloc(fs->pool, sizeof(*bfd)); 534 fs->vtable = &fs_vtable; 535 fs->fsap_data = bfd; 536 537 /* Initialize the fs's path. */ 538 fs->path = apr_pstrdup(fs->pool, path); 539 540 if (create) 541 SVN_ERR(bdb_write_config(fs)); 542 543 /* Create the Berkeley DB environment. */ 544 { 545 svn_error_t *err = svn_fs_bdb__open(&(bfd->bdb), path, 546 SVN_BDB_STANDARD_ENV_FLAGS, 547 0666, fs->pool); 548 if (err) 549 { 550 if (create) 551 return svn_error_createf 552 (err->apr_err, err, 553 _("Berkeley DB error for filesystem '%s'" 554 " while creating environment:\n"), 555 fs->path); 556 else 557 return svn_error_createf 558 (err->apr_err, err, 559 _("Berkeley DB error for filesystem '%s'" 560 " while opening environment:\n"), 561 fs->path); 562 } 563 } 564 565 /* We must register the FS cleanup function *after* opening the 566 environment, so that it's run before the environment baton 567 cleanup. */ 568 apr_pool_cleanup_register(fs->pool, fs, cleanup_fs_apr, 569 apr_pool_cleanup_null); 570 571 572 /* Create the databases in the environment. */ 573 SVN_ERR(BDB_WRAP(fs, (create 574 ? N_("creating 'nodes' table") 575 : N_("opening 'nodes' table")), 576 svn_fs_bdb__open_nodes_table(&bfd->nodes, 577 bfd->bdb->env, 578 create))); 579 SVN_ERR(BDB_WRAP(fs, (create 580 ? N_("creating 'revisions' table") 581 : N_("opening 'revisions' table")), 582 svn_fs_bdb__open_revisions_table(&bfd->revisions, 583 bfd->bdb->env, 584 create))); 585 SVN_ERR(BDB_WRAP(fs, (create 586 ? N_("creating 'transactions' table") 587 : N_("opening 'transactions' table")), 588 svn_fs_bdb__open_transactions_table(&bfd->transactions, 589 bfd->bdb->env, 590 create))); 591 SVN_ERR(BDB_WRAP(fs, (create 592 ? N_("creating 'copies' table") 593 : N_("opening 'copies' table")), 594 svn_fs_bdb__open_copies_table(&bfd->copies, 595 bfd->bdb->env, 596 create))); 597 SVN_ERR(BDB_WRAP(fs, (create 598 ? N_("creating 'changes' table") 599 : N_("opening 'changes' table")), 600 svn_fs_bdb__open_changes_table(&bfd->changes, 601 bfd->bdb->env, 602 create))); 603 SVN_ERR(BDB_WRAP(fs, (create 604 ? N_("creating 'representations' table") 605 : N_("opening 'representations' table")), 606 svn_fs_bdb__open_reps_table(&bfd->representations, 607 bfd->bdb->env, 608 create))); 609 SVN_ERR(BDB_WRAP(fs, (create 610 ? N_("creating 'strings' table") 611 : N_("opening 'strings' table")), 612 svn_fs_bdb__open_strings_table(&bfd->strings, 613 bfd->bdb->env, 614 create))); 615 SVN_ERR(BDB_WRAP(fs, (create 616 ? N_("creating 'uuids' table") 617 : N_("opening 'uuids' table")), 618 svn_fs_bdb__open_uuids_table(&bfd->uuids, 619 bfd->bdb->env, 620 create))); 621 SVN_ERR(BDB_WRAP(fs, (create 622 ? N_("creating 'locks' table") 623 : N_("opening 'locks' table")), 624 svn_fs_bdb__open_locks_table(&bfd->locks, 625 bfd->bdb->env, 626 create))); 627 SVN_ERR(BDB_WRAP(fs, (create 628 ? N_("creating 'lock-tokens' table") 629 : N_("opening 'lock-tokens' table")), 630 svn_fs_bdb__open_lock_tokens_table(&bfd->lock_tokens, 631 bfd->bdb->env, 632 create))); 633 634 if (format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT) 635 { 636 SVN_ERR(BDB_WRAP(fs, (create 637 ? N_("creating 'node-origins' table") 638 : N_("opening 'node-origins' table")), 639 svn_fs_bdb__open_node_origins_table(&bfd->node_origins, 640 bfd->bdb->env, 641 create))); 642 } 643 644 if (format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT) 645 { 646 SVN_ERR(BDB_WRAP(fs, (create 647 ? N_("creating 'miscellaneous' table") 648 : N_("opening 'miscellaneous' table")), 649 svn_fs_bdb__open_miscellaneous_table(&bfd->miscellaneous, 650 bfd->bdb->env, 651 create))); 652 } 653 654 if (format >= SVN_FS_BASE__MIN_REP_SHARING_FORMAT) 655 { 656 SVN_ERR(BDB_WRAP(fs, (create 657 ? N_("creating 'checksum-reps' table") 658 : N_("opening 'checksum-reps' table")), 659 svn_fs_bdb__open_checksum_reps_table(&bfd->checksum_reps, 660 bfd->bdb->env, 661 create))); 662 } 663 664 return SVN_NO_ERROR; 665} 666 667 668/* Called by functions that initialize an svn_fs_t struct, after that 669 initialization is done, to populate svn_fs_t->uuid. */ 670static svn_error_t * 671populate_opened_fs(svn_fs_t *fs, apr_pool_t *scratch_pool) 672{ 673 SVN_ERR(svn_fs_base__populate_uuid(fs, scratch_pool)); 674 return SVN_NO_ERROR; 675} 676 677static svn_error_t * 678base_create(svn_fs_t *fs, const char *path, apr_pool_t *pool, 679 apr_pool_t *common_pool) 680{ 681 int format = SVN_FS_BASE__FORMAT_NUMBER; 682 svn_error_t *svn_err; 683 684 /* See if compatibility with older versions was explicitly requested. */ 685 if (fs->config) 686 { 687 if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE)) 688 format = 1; 689 else if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE)) 690 format = 2; 691 else if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE)) 692 format = 3; 693 } 694 695 /* Create the environment and databases. */ 696 svn_err = open_databases(fs, TRUE, format, path, pool); 697 if (svn_err) goto error; 698 699 /* Initialize the DAG subsystem. */ 700 svn_err = svn_fs_base__dag_init_fs(fs); 701 if (svn_err) goto error; 702 703 /* This filesystem is ready. Stamp it with a format number. */ 704 svn_err = svn_io_write_version_file( 705 svn_dirent_join(fs->path, FORMAT_FILE, pool), format, pool); 706 if (svn_err) goto error; 707 708 ((base_fs_data_t *) fs->fsap_data)->format = format; 709 710 SVN_ERR(populate_opened_fs(fs, pool)); 711 return SVN_NO_ERROR;; 712 713error: 714 svn_error_clear(cleanup_fs(fs)); 715 return svn_err; 716} 717 718 719/* Gaining access to an existing Berkeley DB-based filesystem. */ 720 721svn_error_t * 722svn_fs_base__test_required_feature_format(svn_fs_t *fs, 723 const char *feature, 724 int requires) 725{ 726 base_fs_data_t *bfd = fs->fsap_data; 727 if (bfd->format < requires) 728 return svn_error_createf 729 (SVN_ERR_UNSUPPORTED_FEATURE, NULL, 730 _("The '%s' feature requires version %d of the filesystem schema; " 731 "filesystem '%s' uses only version %d"), 732 feature, requires, fs->path, bfd->format); 733 return SVN_NO_ERROR; 734} 735 736/* Return the error SVN_ERR_FS_UNSUPPORTED_FORMAT if FS's format 737 number is not the same as the format number supported by this 738 Subversion. */ 739static svn_error_t * 740check_format(int format) 741{ 742 /* We currently support any format less than the compiled format number 743 simultaneously. */ 744 if (format <= SVN_FS_BASE__FORMAT_NUMBER) 745 return SVN_NO_ERROR; 746 747 return svn_error_createf( 748 SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL, 749 _("Expected FS format '%d'; found format '%d'"), 750 SVN_FS_BASE__FORMAT_NUMBER, format); 751} 752 753static svn_error_t * 754base_open(svn_fs_t *fs, const char *path, apr_pool_t *pool, 755 apr_pool_t *common_pool) 756{ 757 int format; 758 svn_error_t *svn_err; 759 svn_boolean_t write_format_file = FALSE; 760 761 /* Read the FS format number. */ 762 svn_err = svn_io_read_version_file(&format, 763 svn_dirent_join(path, FORMAT_FILE, pool), 764 pool); 765 if (svn_err && APR_STATUS_IS_ENOENT(svn_err->apr_err)) 766 { 767 /* Pre-1.2 filesystems did not have a format file (you could say 768 they were format "0"), so they get upgraded on the fly. 769 However, we stopped "upgrading on the fly" in 1.5, so older 770 filesystems should only be bumped to 1.3, which is format "1". */ 771 svn_error_clear(svn_err); 772 svn_err = SVN_NO_ERROR; 773 format = 1; 774 write_format_file = TRUE; 775 } 776 else if (svn_err) 777 goto error; 778 779 /* Create the environment and databases. */ 780 svn_err = open_databases(fs, FALSE, format, path, pool); 781 if (svn_err) goto error; 782 783 ((base_fs_data_t *) fs->fsap_data)->format = format; 784 SVN_ERR(check_format(format)); 785 786 /* If we lack a format file, write one. */ 787 if (write_format_file) 788 { 789 svn_err = svn_io_write_version_file(svn_dirent_join(path, FORMAT_FILE, 790 pool), 791 format, pool); 792 if (svn_err) goto error; 793 } 794 795 SVN_ERR(populate_opened_fs(fs, pool)); 796 return SVN_NO_ERROR; 797 798 error: 799 svn_error_clear(cleanup_fs(fs)); 800 return svn_err; 801} 802 803 804/* Running recovery on a Berkeley DB-based filesystem. */ 805 806 807/* Recover a database at PATH. Perform catastrophic recovery if FATAL 808 is TRUE. Use POOL for temporary allocation. */ 809static svn_error_t * 810bdb_recover(const char *path, svn_boolean_t fatal, apr_pool_t *pool) 811{ 812 bdb_env_baton_t *bdb; 813 814 /* Here's the comment copied from db_recover.c: 815 816 Initialize the environment -- we don't actually do anything 817 else, that all that's needed to run recovery. 818 819 Note that we specify a private environment, as we're about to 820 create a region, and we don't want to leave it around. If we 821 leave the region around, the application that should create it 822 will simply join it instead, and will then be running with 823 incorrectly sized (and probably terribly small) caches. */ 824 825 /* Note that since we're using a private environment, we shoudl 826 /not/ initialize locking. We want the environment files to go 827 away. */ 828 829 SVN_ERR(svn_fs_bdb__open(&bdb, path, 830 ((fatal ? DB_RECOVER_FATAL : DB_RECOVER) 831 | SVN_BDB_PRIVATE_ENV_FLAGS), 832 0666, pool)); 833 return svn_fs_bdb__close(bdb); 834} 835 836static svn_error_t * 837base_open_for_recovery(svn_fs_t *fs, const char *path, apr_pool_t *pool, 838 apr_pool_t *common_pool) 839{ 840 /* Just stash the path in the fs pointer - it's all we really need. */ 841 fs->path = apr_pstrdup(fs->pool, path); 842 843 return SVN_NO_ERROR; 844} 845 846static svn_error_t * 847base_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool, 848 apr_pool_t *common_pool) 849{ 850 const char *version_file_path; 851 int old_format_number; 852 svn_error_t *err; 853 854 version_file_path = svn_dirent_join(path, FORMAT_FILE, pool); 855 856 /* Read the old number so we've got it on hand later on. */ 857 err = svn_io_read_version_file(&old_format_number, version_file_path, pool); 858 if (err && APR_STATUS_IS_ENOENT(err->apr_err)) 859 { 860 /* Pre-1.2 filesystems do not have a 'format' file. */ 861 old_format_number = 0; 862 svn_error_clear(err); 863 err = SVN_NO_ERROR; 864 } 865 SVN_ERR(err); 866 867 /* Bump the format file's stored version number. */ 868 SVN_ERR(svn_io_write_version_file(version_file_path, 869 SVN_FS_BASE__FORMAT_NUMBER, pool)); 870 871 /* Check and see if we need to record the "bump" revision. */ 872 if (old_format_number < SVN_FS_BASE__MIN_FORWARD_DELTAS_FORMAT) 873 { 874 apr_pool_t *subpool = svn_pool_create(pool); 875 svn_revnum_t youngest_rev; 876 const char *value; 877 878 /* Open the filesystem in a subpool (so we can control its 879 closure) and do our fiddling. 880 881 NOTE: By using base_open() here instead of open_databases(), 882 we will end up re-reading the format file that we just wrote. 883 But it's better to use the existing encapsulation of "opening 884 the filesystem" rather than duplicating (or worse, partially 885 duplicating) that logic here. */ 886 SVN_ERR(base_open(fs, path, subpool, common_pool)); 887 888 /* Fetch the youngest rev, and record it */ 889 SVN_ERR(svn_fs_base__youngest_rev(&youngest_rev, fs, subpool)); 890 value = apr_psprintf(subpool, "%ld", youngest_rev); 891 SVN_ERR(svn_fs_base__miscellaneous_set 892 (fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE, 893 value, subpool)); 894 svn_pool_destroy(subpool); 895 } 896 897 return SVN_NO_ERROR; 898} 899 900static svn_error_t * 901base_verify(svn_fs_t *fs, const char *path, 902 svn_revnum_t start, 903 svn_revnum_t end, 904 svn_fs_progress_notify_func_t notify_func, 905 void *notify_baton, 906 svn_cancel_func_t cancel_func, 907 void *cancel_baton, 908 apr_pool_t *pool, 909 apr_pool_t *common_pool) 910{ 911 /* Verifying is currently a no op for BDB. */ 912 return SVN_NO_ERROR; 913} 914 915static svn_error_t * 916base_bdb_recover(svn_fs_t *fs, 917 svn_cancel_func_t cancel_func, void *cancel_baton, 918 apr_pool_t *pool) 919{ 920 /* The fs pointer is a fake created in base_open_for_recovery above. 921 We only care about the path. */ 922 return bdb_recover(fs->path, FALSE, pool); 923} 924 925static svn_error_t * 926base_bdb_pack(svn_fs_t *fs, 927 const char *path, 928 svn_fs_pack_notify_t notify_func, 929 void *notify_baton, 930 svn_cancel_func_t cancel, 931 void *cancel_baton, 932 apr_pool_t *pool, 933 apr_pool_t *common_pool) 934{ 935 /* Packing is currently a no op for BDB. */ 936 return SVN_NO_ERROR; 937} 938 939 940 941/* Running the 'archive' command on a Berkeley DB-based filesystem. */ 942 943 944static svn_error_t * 945base_bdb_logfiles(apr_array_header_t **logfiles, 946 const char *path, 947 svn_boolean_t only_unused, 948 apr_pool_t *pool) 949{ 950 bdb_env_baton_t *bdb; 951 char **filelist; 952 char **filename; 953 u_int32_t flags = only_unused ? 0 : DB_ARCH_LOG; 954 955 *logfiles = apr_array_make(pool, 4, sizeof(const char *)); 956 957 SVN_ERR(svn_fs_bdb__open(&bdb, path, 958 SVN_BDB_STANDARD_ENV_FLAGS, 959 0666, pool)); 960 SVN_BDB_ERR(bdb, bdb->env->log_archive(bdb->env, &filelist, flags)); 961 962 if (filelist == NULL) 963 return svn_fs_bdb__close(bdb); 964 965 for (filename = filelist; *filename != NULL; ++filename) 966 { 967 APR_ARRAY_PUSH(*logfiles, const char *) = apr_pstrdup(pool, *filename); 968 } 969 970 free(filelist); 971 972 return svn_fs_bdb__close(bdb); 973} 974 975 976 977/* Copying a live Berkeley DB-base filesystem. */ 978 979/** 980 * Delete all unused log files from DBD enviroment at @a live_path that exist 981 * in @a backup_path. 982 */ 983static svn_error_t * 984svn_fs_base__clean_logs(const char *live_path, 985 const char *backup_path, 986 apr_pool_t *pool) 987{ 988 apr_array_header_t *logfiles; 989 990 SVN_ERR(base_bdb_logfiles(&logfiles, 991 live_path, 992 TRUE, /* Only unused logs */ 993 pool)); 994 995 { /* Process unused logs from live area */ 996 int idx; 997 apr_pool_t *sub_pool = svn_pool_create(pool); 998 999 /* Process log files. */ 1000 for (idx = 0; idx < logfiles->nelts; idx++) 1001 { 1002 const char *log_file = APR_ARRAY_IDX(logfiles, idx, const char *); 1003 const char *live_log_path; 1004 const char *backup_log_path; 1005 1006 svn_pool_clear(sub_pool); 1007 live_log_path = svn_dirent_join(live_path, log_file, sub_pool); 1008 backup_log_path = svn_dirent_join(backup_path, log_file, sub_pool); 1009 1010 { /* Compare files. No point in using MD5 and wasting CPU cycles as we 1011 got full copies of both logs */ 1012 1013 svn_boolean_t files_match = FALSE; 1014 svn_node_kind_t kind; 1015 1016 /* Check to see if there is a corresponding log file in the backup 1017 directory */ 1018 SVN_ERR(svn_io_check_path(backup_log_path, &kind, pool)); 1019 1020 /* If the copy of the log exists, compare them */ 1021 if (kind == svn_node_file) 1022 SVN_ERR(svn_io_files_contents_same_p(&files_match, 1023 live_log_path, 1024 backup_log_path, 1025 sub_pool)); 1026 1027 /* If log files do not match, go to the next log file. */ 1028 if (!files_match) 1029 continue; 1030 } 1031 1032 SVN_ERR(svn_io_remove_file2(live_log_path, FALSE, sub_pool)); 1033 } 1034 1035 svn_pool_destroy(sub_pool); 1036 } 1037 1038 return SVN_NO_ERROR; 1039} 1040 1041 1042/* DB_ENV->get_flags() and DB->get_pagesize() don't exist prior to 1043 Berkeley DB 4.2. */ 1044#if SVN_BDB_VERSION_AT_LEAST(4, 2) 1045 1046/* Open the BDB environment at PATH and compare its configuration 1047 flags with FLAGS. If every flag in FLAGS is set in the 1048 environment, then set *MATCH to true. Else set *MATCH to false. */ 1049static svn_error_t * 1050check_env_flags(svn_boolean_t *match, 1051 u_int32_t flags, 1052 const char *path, 1053 apr_pool_t *pool) 1054{ 1055 bdb_env_baton_t *bdb; 1056#if SVN_BDB_VERSION_AT_LEAST(4, 7) 1057 int flag_state; 1058#else 1059 u_int32_t envflags; 1060#endif 1061 1062 SVN_ERR(svn_fs_bdb__open(&bdb, path, 1063 SVN_BDB_STANDARD_ENV_FLAGS, 1064 0666, pool)); 1065#if SVN_BDB_VERSION_AT_LEAST(4, 7) 1066 SVN_BDB_ERR(bdb, bdb->env->log_get_config(bdb->env, flags, &flag_state)); 1067#else 1068 SVN_BDB_ERR(bdb, bdb->env->get_flags(bdb->env, &envflags)); 1069#endif 1070 1071 SVN_ERR(svn_fs_bdb__close(bdb)); 1072 1073#if SVN_BDB_VERSION_AT_LEAST(4, 7) 1074 if (flag_state == 0) 1075#else 1076 if (flags & envflags) 1077#endif 1078 *match = TRUE; 1079 else 1080 *match = FALSE; 1081 1082 return SVN_NO_ERROR; 1083} 1084 1085 1086/* Set *PAGESIZE to the size of pages used to hold items in the 1087 database environment located at PATH. 1088*/ 1089static svn_error_t * 1090get_db_pagesize(u_int32_t *pagesize, 1091 const char *path, 1092 apr_pool_t *pool) 1093{ 1094 bdb_env_baton_t *bdb; 1095 DB *nodes_table; 1096 1097 SVN_ERR(svn_fs_bdb__open(&bdb, path, 1098 SVN_BDB_STANDARD_ENV_FLAGS, 1099 0666, pool)); 1100 1101 /* ### We're only asking for the pagesize on the 'nodes' table. 1102 Is this enough? We never call DB->set_pagesize() on any of 1103 our tables, so presumably BDB is using the same default 1104 pagesize for all our databases, right? */ 1105 SVN_BDB_ERR(bdb, svn_fs_bdb__open_nodes_table(&nodes_table, bdb->env, 1106 FALSE)); 1107 SVN_BDB_ERR(bdb, nodes_table->get_pagesize(nodes_table, pagesize)); 1108 SVN_BDB_ERR(bdb, nodes_table->close(nodes_table, 0)); 1109 1110 return svn_fs_bdb__close(bdb); 1111} 1112#endif /* SVN_BDB_VERSION_AT_LEAST(4, 2) */ 1113 1114 1115/* Copy FILENAME from SRC_DIR to DST_DIR in byte increments of size 1116 CHUNKSIZE. The read/write buffer of size CHUNKSIZE will be 1117 allocated in POOL. If ALLOW_MISSING is set, we won't make a fuss 1118 if FILENAME isn't found in SRC_DIR; otherwise, we will. */ 1119static svn_error_t * 1120copy_db_file_safely(const char *src_dir, 1121 const char *dst_dir, 1122 const char *filename, 1123 u_int32_t chunksize, 1124 svn_boolean_t allow_missing, 1125 apr_pool_t *pool) 1126{ 1127 apr_file_t *s = NULL, *d = NULL; /* init to null important for APR */ 1128 const char *file_src_path = svn_dirent_join(src_dir, filename, pool); 1129 const char *file_dst_path = svn_dirent_join(dst_dir, filename, pool); 1130 svn_error_t *err; 1131 char *buf; 1132 1133 /* Open source file. If it's missing and that's allowed, there's 1134 nothing more to do here. */ 1135 err = svn_io_file_open(&s, file_src_path, 1136 (APR_READ | APR_LARGEFILE), 1137 APR_OS_DEFAULT, pool); 1138 if (err && APR_STATUS_IS_ENOENT(err->apr_err) && allow_missing) 1139 { 1140 svn_error_clear(err); 1141 return SVN_NO_ERROR; 1142 } 1143 SVN_ERR(err); 1144 1145 /* Open destination file. */ 1146 SVN_ERR(svn_io_file_open(&d, file_dst_path, (APR_WRITE | APR_CREATE | 1147 APR_LARGEFILE), 1148 APR_OS_DEFAULT, pool)); 1149 1150 /* Allocate our read/write buffer. */ 1151 buf = apr_palloc(pool, chunksize); 1152 1153 /* Copy bytes till the cows come home. */ 1154 while (1) 1155 { 1156 apr_size_t bytes_this_time = chunksize; 1157 svn_error_t *read_err, *write_err; 1158 1159 /* Read 'em. */ 1160 if ((read_err = svn_io_file_read(s, buf, &bytes_this_time, pool))) 1161 { 1162 if (APR_STATUS_IS_EOF(read_err->apr_err)) 1163 svn_error_clear(read_err); 1164 else 1165 { 1166 svn_error_clear(svn_io_file_close(s, pool)); 1167 svn_error_clear(svn_io_file_close(d, pool)); 1168 return read_err; 1169 } 1170 } 1171 1172 /* Write 'em. */ 1173 if ((write_err = svn_io_file_write_full(d, buf, bytes_this_time, NULL, 1174 pool))) 1175 { 1176 svn_error_clear(svn_io_file_close(s, pool)); 1177 svn_error_clear(svn_io_file_close(d, pool)); 1178 return write_err; 1179 } 1180 1181 /* read_err is either NULL, or a dangling pointer - but it is only a 1182 dangling pointer if it used to be an EOF error. */ 1183 if (read_err) 1184 { 1185 SVN_ERR(svn_io_file_close(s, pool)); 1186 SVN_ERR(svn_io_file_close(d, pool)); 1187 break; /* got EOF on read, all files closed, all done. */ 1188 } 1189 } 1190 1191 return SVN_NO_ERROR; 1192} 1193 1194 1195 1196 1197static svn_error_t * 1198base_hotcopy(svn_fs_t *src_fs, 1199 svn_fs_t *dst_fs, 1200 const char *src_path, 1201 const char *dest_path, 1202 svn_boolean_t clean_logs, 1203 svn_boolean_t incremental, 1204 svn_cancel_func_t cancel_func, 1205 void *cancel_baton, 1206 apr_pool_t *pool) 1207{ 1208 svn_error_t *err; 1209 u_int32_t pagesize; 1210 svn_boolean_t log_autoremove = FALSE; 1211 int format; 1212 1213 if (incremental) 1214 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 1215 _("BDB repositories do not support incremental " 1216 "hotcopy")); 1217 1218 /* Check the FS format number to be certain that we know how to 1219 hotcopy this FS. Pre-1.2 filesystems did not have a format file (you 1220 could say they were format "0"), so we will error here. This is not 1221 optimal, but since this has been the case since 1.2.0, and no one has 1222 complained, it apparently isn't much of a concern. (We did not check 1223 the 'format' file in 1.2.x, but we did blindly try to copy 'locks', 1224 which would have errored just the same.) */ 1225 SVN_ERR(svn_io_read_version_file( 1226 &format, svn_dirent_join(src_path, FORMAT_FILE, pool), pool)); 1227 SVN_ERR(check_format(format)); 1228 1229 /* If using Berkeley DB 4.2 or later, note whether the DB_LOG_AUTO_REMOVE 1230 feature is on. If it is, we have a potential race condition: 1231 another process might delete a logfile while we're in the middle 1232 of copying all the logfiles. (This is not a huge deal; at worst, 1233 the hotcopy fails with a file-not-found error.) */ 1234#if SVN_BDB_VERSION_AT_LEAST(4, 2) 1235 err = check_env_flags(&log_autoremove, 1236#if SVN_BDB_VERSION_AT_LEAST(4, 7) 1237 DB_LOG_AUTO_REMOVE, 1238 /* DB_LOG_AUTO_REMOVE was named DB_LOG_AUTOREMOVE before Berkeley DB 4.7. */ 1239#else 1240 DB_LOG_AUTOREMOVE, 1241#endif 1242 src_path, pool); 1243#endif 1244 SVN_ERR(err); 1245 1246 /* Copy the DB_CONFIG file. */ 1247 SVN_ERR(svn_io_dir_file_copy(src_path, dest_path, "DB_CONFIG", pool)); 1248 1249 /* In order to copy the database files safely and atomically, we 1250 must copy them in chunks which are multiples of the page-size 1251 used by BDB. See sleepycat docs for details, or svn issue #1818. */ 1252#if SVN_BDB_VERSION_AT_LEAST(4, 2) 1253 SVN_ERR(get_db_pagesize(&pagesize, src_path, pool)); 1254 if (pagesize < SVN__STREAM_CHUNK_SIZE) 1255 { 1256 /* use the largest multiple of BDB pagesize we can. */ 1257 int multiple = SVN__STREAM_CHUNK_SIZE / pagesize; 1258 pagesize *= multiple; 1259 } 1260#else 1261 /* default to 128K chunks, which should be safe. 1262 BDB almost certainly uses a power-of-2 pagesize. */ 1263 pagesize = (4096 * 32); 1264#endif 1265 1266 /* Copy the databases. */ 1267 SVN_ERR(copy_db_file_safely(src_path, dest_path, 1268 "nodes", pagesize, FALSE, pool)); 1269 SVN_ERR(copy_db_file_safely(src_path, dest_path, 1270 "transactions", pagesize, FALSE, pool)); 1271 SVN_ERR(copy_db_file_safely(src_path, dest_path, 1272 "revisions", pagesize, FALSE, pool)); 1273 SVN_ERR(copy_db_file_safely(src_path, dest_path, 1274 "copies", pagesize, FALSE, pool)); 1275 SVN_ERR(copy_db_file_safely(src_path, dest_path, 1276 "changes", pagesize, FALSE, pool)); 1277 SVN_ERR(copy_db_file_safely(src_path, dest_path, 1278 "representations", pagesize, FALSE, pool)); 1279 SVN_ERR(copy_db_file_safely(src_path, dest_path, 1280 "strings", pagesize, FALSE, pool)); 1281 SVN_ERR(copy_db_file_safely(src_path, dest_path, 1282 "uuids", pagesize, TRUE, pool)); 1283 SVN_ERR(copy_db_file_safely(src_path, dest_path, 1284 "locks", pagesize, TRUE, pool)); 1285 SVN_ERR(copy_db_file_safely(src_path, dest_path, 1286 "lock-tokens", pagesize, TRUE, pool)); 1287 SVN_ERR(copy_db_file_safely(src_path, dest_path, 1288 "node-origins", pagesize, TRUE, pool)); 1289 SVN_ERR(copy_db_file_safely(src_path, dest_path, 1290 "checksum-reps", pagesize, TRUE, pool)); 1291 SVN_ERR(copy_db_file_safely(src_path, dest_path, 1292 "miscellaneous", pagesize, TRUE, pool)); 1293 1294 { 1295 apr_array_header_t *logfiles; 1296 int idx; 1297 apr_pool_t *subpool; 1298 1299 SVN_ERR(base_bdb_logfiles(&logfiles, 1300 src_path, 1301 FALSE, /* All logs */ 1302 pool)); 1303 1304 /* Process log files. */ 1305 subpool = svn_pool_create(pool); 1306 for (idx = 0; idx < logfiles->nelts; idx++) 1307 { 1308 svn_pool_clear(subpool); 1309 err = svn_io_dir_file_copy(src_path, dest_path, 1310 APR_ARRAY_IDX(logfiles, idx, 1311 const char *), 1312 subpool); 1313 if (err) 1314 { 1315 if (log_autoremove) 1316 return 1317 svn_error_quick_wrap 1318 (err, 1319 _("Error copying logfile; the DB_LOG_AUTOREMOVE feature\n" 1320 "may be interfering with the hotcopy algorithm. If\n" 1321 "the problem persists, try deactivating this feature\n" 1322 "in DB_CONFIG")); 1323 else 1324 return svn_error_trace(err); 1325 } 1326 } 1327 svn_pool_destroy(subpool); 1328 } 1329 1330 /* Since this is a copy we will have exclusive access to the repository. */ 1331 err = bdb_recover(dest_path, TRUE, pool); 1332 if (err) 1333 { 1334 if (log_autoremove) 1335 return 1336 svn_error_quick_wrap 1337 (err, 1338 _("Error running catastrophic recovery on hotcopy; the\n" 1339 "DB_LOG_AUTOREMOVE feature may be interfering with the\n" 1340 "hotcopy algorithm. If the problem persists, try deactivating\n" 1341 "this feature in DB_CONFIG")); 1342 else 1343 return svn_error_trace(err); 1344 } 1345 1346 /* Only now that the hotcopied filesystem is complete, 1347 stamp it with a format file. */ 1348 SVN_ERR(svn_io_write_version_file( 1349 svn_dirent_join(dest_path, FORMAT_FILE, pool), format, pool)); 1350 1351 if (clean_logs) 1352 SVN_ERR(svn_fs_base__clean_logs(src_path, dest_path, pool)); 1353 1354 return SVN_NO_ERROR; 1355} 1356 1357 1358 1359/* Deleting a Berkeley DB-based filesystem. */ 1360 1361 1362static svn_error_t * 1363base_delete_fs(const char *path, 1364 apr_pool_t *pool) 1365{ 1366 /* First, use the Berkeley DB library function to remove any shared 1367 memory segments. */ 1368 SVN_ERR(svn_fs_bdb__remove(path, pool)); 1369 1370 /* Remove the environment directory. */ 1371 return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool); 1372} 1373 1374static const svn_version_t * 1375base_version(void) 1376{ 1377 SVN_VERSION_BODY; 1378} 1379 1380static const char * 1381base_get_description(void) 1382{ 1383 return _("Module for working with a Berkeley DB repository."); 1384} 1385 1386static svn_error_t * 1387base_set_svn_fs_open(svn_fs_t *fs, 1388 svn_error_t *(*svn_fs_open_)(svn_fs_t **, 1389 const char *, 1390 apr_hash_t *, 1391 apr_pool_t *)) 1392{ 1393 return SVN_NO_ERROR; 1394} 1395 1396 1397/* Base FS library vtable, used by the FS loader library. */ 1398static fs_library_vtable_t library_vtable = { 1399 base_version, 1400 base_create, 1401 base_open, 1402 base_open_for_recovery, 1403 base_upgrade, 1404 base_verify, 1405 base_delete_fs, 1406 base_hotcopy, 1407 base_get_description, 1408 base_bdb_recover, 1409 base_bdb_pack, 1410 base_bdb_logfiles, 1411 svn_fs_base__id_parse, 1412 base_set_svn_fs_open 1413}; 1414 1415svn_error_t * 1416svn_fs_base__init(const svn_version_t *loader_version, 1417 fs_library_vtable_t **vtable, apr_pool_t* common_pool) 1418{ 1419 static const svn_version_checklist_t checklist[] = 1420 { 1421 { "svn_subr", svn_subr_version }, 1422 { "svn_delta", svn_delta_version }, 1423 { NULL, NULL } 1424 }; 1425 1426 /* Simplified version check to make sure we can safely use the 1427 VTABLE parameter. The FS loader does a more exhaustive check. */ 1428 if (loader_version->major != SVN_VER_MAJOR) 1429 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL, 1430 _("Unsupported FS loader version (%d) for bdb"), 1431 loader_version->major); 1432 SVN_ERR(svn_ver_check_list2(base_version(), checklist, svn_ver_equal)); 1433 SVN_ERR(check_bdb_version()); 1434 SVN_ERR(svn_fs_bdb__init(common_pool)); 1435 1436 *vtable = &library_vtable; 1437 return SVN_NO_ERROR; 1438} 1439