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