fs.c revision 309512
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#include <apr_thread_mutex.h> 31 32#include "svn_fs.h" 33#include "svn_delta.h" 34#include "svn_version.h" 35#include "svn_pools.h" 36#include "fs.h" 37#include "fs_fs.h" 38#include "tree.h" 39#include "lock.h" 40#include "hotcopy.h" 41#include "id.h" 42#include "pack.h" 43#include "recovery.h" 44#include "rep-cache.h" 45#include "revprops.h" 46#include "transaction.h" 47#include "util.h" 48#include "verify.h" 49#include "svn_private_config.h" 50#include "private/svn_fs_util.h" 51 52#include "../libsvn_fs/fs-loader.h" 53 54/* A prefix for the pool userdata variables used to hold 55 per-filesystem shared data. See fs_serialized_init. */ 56#define SVN_FSFS_SHARED_USERDATA_PREFIX "svn-fsfs-shared-" 57 58 59 60/* Initialize the part of FS that requires global serialization across all 61 instances. The caller is responsible of ensuring that serialization. 62 Use COMMON_POOL for process-wide and POOL for temporary allocations. */ 63static svn_error_t * 64fs_serialized_init(svn_fs_t *fs, apr_pool_t *common_pool, apr_pool_t *pool) 65{ 66 fs_fs_data_t *ffd = fs->fsap_data; 67 const char *key; 68 void *val; 69 fs_fs_shared_data_t *ffsd; 70 apr_status_t status; 71 72 /* Note that we are allocating a small amount of long-lived data for 73 each separate repository opened during the lifetime of the 74 svn_fs_initialize pool. It's unlikely that anyone will notice 75 the modest expenditure; the alternative is to allocate each structure 76 in a subpool, add a reference-count, and add a serialized destructor 77 to the FS vtable. That's more machinery than it's worth. 78 79 Picking an appropriate key for the shared data is tricky, because, 80 unfortunately, a filesystem UUID is not really unique. It is implicitly 81 shared between hotcopied (1), dump / loaded (2) or naively copied (3) 82 filesystems. We tackle this problem by using a combination of the UUID 83 and an instance ID as the key. This allows us to avoid key clashing 84 in (1) and (2) for formats >= SVN_FS_FS__MIN_INSTANCE_ID_FORMAT, which 85 do support instance IDs. For old formats the shared data (locks, shared 86 transaction data, ...) will still clash. 87 88 Speaking of (3), there is not so much we can do about it, except maybe 89 provide a convenient way of fixing things. Naively copied filesystems 90 have identical filesystem UUIDs *and* instance IDs. With the key being 91 a combination of these two, clashes can be fixed by changing either of 92 them (or both), e.g. with svn_fs_set_uuid(). */ 93 94 SVN_ERR_ASSERT(fs->uuid); 95 SVN_ERR_ASSERT(ffd->instance_id); 96 97 key = apr_pstrcat(pool, SVN_FSFS_SHARED_USERDATA_PREFIX, 98 fs->uuid, ":", ffd->instance_id, SVN_VA_NULL); 99 status = apr_pool_userdata_get(&val, key, common_pool); 100 if (status) 101 return svn_error_wrap_apr(status, _("Can't fetch FSFS shared data")); 102 ffsd = val; 103 104 if (!ffsd) 105 { 106 ffsd = apr_pcalloc(common_pool, sizeof(*ffsd)); 107 ffsd->common_pool = common_pool; 108 109 /* POSIX fcntl locks are per-process, so we need a mutex for 110 intra-process synchronization when grabbing the repository write 111 lock. */ 112 SVN_ERR(svn_mutex__init(&ffsd->fs_write_lock, 113 SVN_FS_FS__USE_LOCK_MUTEX, common_pool)); 114 115 /* ... the pack lock ... */ 116 SVN_ERR(svn_mutex__init(&ffsd->fs_pack_lock, 117 SVN_FS_FS__USE_LOCK_MUTEX, common_pool)); 118 119 /* ... not to mention locking the txn-current file. */ 120 SVN_ERR(svn_mutex__init(&ffsd->txn_current_lock, 121 SVN_FS_FS__USE_LOCK_MUTEX, common_pool)); 122 123 /* We also need a mutex for synchronizing access to the active 124 transaction list and free transaction pointer. */ 125 SVN_ERR(svn_mutex__init(&ffsd->txn_list_lock, TRUE, common_pool)); 126 127 key = apr_pstrdup(common_pool, key); 128 status = apr_pool_userdata_set(ffsd, key, NULL, common_pool); 129 if (status) 130 return svn_error_wrap_apr(status, _("Can't store FSFS shared data")); 131 } 132 133 ffd->shared = ffsd; 134 135 return SVN_NO_ERROR; 136} 137 138 139 140/* This function is provided for Subversion 1.0.x compatibility. It 141 has no effect for fsfs backed Subversion filesystems. It conforms 142 to the fs_library_vtable_t.bdb_set_errcall() API. */ 143static svn_error_t * 144fs_set_errcall(svn_fs_t *fs, 145 void (*db_errcall_fcn)(const char *errpfx, char *msg)) 146{ 147 148 return SVN_NO_ERROR; 149} 150 151struct fs_freeze_baton_t { 152 svn_fs_t *fs; 153 svn_fs_freeze_func_t freeze_func; 154 void *freeze_baton; 155}; 156 157static svn_error_t * 158fs_freeze_body(void *baton, 159 apr_pool_t *pool) 160{ 161 struct fs_freeze_baton_t *b = baton; 162 svn_boolean_t exists; 163 164 SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, b->fs, pool)); 165 if (exists) 166 SVN_ERR(svn_fs_fs__with_rep_cache_lock(b->fs, 167 b->freeze_func, b->freeze_baton, 168 pool)); 169 else 170 SVN_ERR(b->freeze_func(b->freeze_baton, pool)); 171 172 return SVN_NO_ERROR; 173} 174 175static svn_error_t * 176fs_freeze_body2(void *baton, 177 apr_pool_t *pool) 178{ 179 struct fs_freeze_baton_t *b = baton; 180 SVN_ERR(svn_fs_fs__with_write_lock(b->fs, fs_freeze_body, baton, pool)); 181 182 return SVN_NO_ERROR; 183} 184 185static svn_error_t * 186fs_freeze(svn_fs_t *fs, 187 svn_fs_freeze_func_t freeze_func, 188 void *freeze_baton, 189 apr_pool_t *pool) 190{ 191 fs_fs_data_t *ffd = fs->fsap_data; 192 struct fs_freeze_baton_t b; 193 194 b.fs = fs; 195 b.freeze_func = freeze_func; 196 b.freeze_baton = freeze_baton; 197 198 SVN_ERR(svn_fs__check_fs(fs, TRUE)); 199 200 if (ffd->format >= SVN_FS_FS__MIN_PACK_LOCK_FORMAT) 201 SVN_ERR(svn_fs_fs__with_pack_lock(fs, fs_freeze_body2, &b, pool)); 202 else 203 SVN_ERR(fs_freeze_body2(&b, pool)); 204 205 return SVN_NO_ERROR; 206} 207 208static svn_error_t * 209fs_info(const void **fsfs_info, 210 svn_fs_t *fs, 211 apr_pool_t *result_pool, 212 apr_pool_t *scratch_pool) 213{ 214 fs_fs_data_t *ffd = fs->fsap_data; 215 svn_fs_fsfs_info_t *info = apr_palloc(result_pool, sizeof(*info)); 216 info->fs_type = SVN_FS_TYPE_FSFS; 217 info->shard_size = ffd->max_files_per_dir; 218 info->min_unpacked_rev = ffd->min_unpacked_rev; 219 info->log_addressing = ffd->use_log_addressing; 220 *fsfs_info = info; 221 return SVN_NO_ERROR; 222} 223 224/* Wrapper around svn_fs_fs__set_uuid() adapting between function 225 signatures. */ 226static svn_error_t * 227fs_set_uuid(svn_fs_t *fs, 228 const char *uuid, 229 apr_pool_t *pool) 230{ 231 /* Whenever we set a new UUID, imply that FS will also be a different 232 * instance (on formats that support this). */ 233 return svn_error_trace(svn_fs_fs__set_uuid(fs, uuid, NULL, pool)); 234} 235 236 237 238/* The vtable associated with a specific open filesystem. */ 239static fs_vtable_t fs_vtable = { 240 svn_fs_fs__youngest_rev, 241 svn_fs_fs__revision_prop, 242 svn_fs_fs__get_revision_proplist, 243 svn_fs_fs__change_rev_prop, 244 fs_set_uuid, 245 svn_fs_fs__revision_root, 246 svn_fs_fs__begin_txn, 247 svn_fs_fs__open_txn, 248 svn_fs_fs__purge_txn, 249 svn_fs_fs__list_transactions, 250 svn_fs_fs__deltify, 251 svn_fs_fs__lock, 252 svn_fs_fs__generate_lock_token, 253 svn_fs_fs__unlock, 254 svn_fs_fs__get_lock, 255 svn_fs_fs__get_locks, 256 svn_fs_fs__info_format, 257 svn_fs_fs__info_config_files, 258 fs_info, 259 svn_fs_fs__verify_root, 260 fs_freeze, 261 fs_set_errcall 262}; 263 264 265/* Creating a new filesystem. */ 266 267/* Set up vtable and fsap_data fields in FS. */ 268static svn_error_t * 269initialize_fs_struct(svn_fs_t *fs) 270{ 271 fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd)); 272 ffd->use_log_addressing = FALSE; 273 274 fs->vtable = &fs_vtable; 275 fs->fsap_data = ffd; 276 return SVN_NO_ERROR; 277} 278 279/* Reset vtable and fsap_data fields in FS such that the FS is basically 280 * closed now. Note that FS must not hold locks when you call this. */ 281static void 282uninitialize_fs_struct(svn_fs_t *fs) 283{ 284 fs->vtable = NULL; 285 fs->fsap_data = NULL; 286} 287 288/* This implements the fs_library_vtable_t.create() API. Create a new 289 fsfs-backed Subversion filesystem at path PATH and link it into 290 *FS. Perform temporary allocations in POOL, and fs-global allocations 291 in COMMON_POOL. The latter must be serialized using COMMON_POOL_LOCK. */ 292static svn_error_t * 293fs_create(svn_fs_t *fs, 294 const char *path, 295 svn_mutex__t *common_pool_lock, 296 apr_pool_t *pool, 297 apr_pool_t *common_pool) 298{ 299 SVN_ERR(svn_fs__check_fs(fs, FALSE)); 300 301 SVN_ERR(initialize_fs_struct(fs)); 302 303 SVN_ERR(svn_fs_fs__create(fs, path, pool)); 304 305 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool)); 306 SVN_MUTEX__WITH_LOCK(common_pool_lock, 307 fs_serialized_init(fs, common_pool, pool)); 308 309 return SVN_NO_ERROR; 310} 311 312 313 314/* Gaining access to an existing filesystem. */ 315 316/* This implements the fs_library_vtable_t.open() API. Open an FSFS 317 Subversion filesystem located at PATH, set *FS to point to the 318 correct vtable for the filesystem. Use POOL for any temporary 319 allocations, and COMMON_POOL for fs-global allocations. 320 The latter must be serialized using COMMON_POOL_LOCK. */ 321static svn_error_t * 322fs_open(svn_fs_t *fs, 323 const char *path, 324 svn_mutex__t *common_pool_lock, 325 apr_pool_t *pool, 326 apr_pool_t *common_pool) 327{ 328 apr_pool_t *subpool = svn_pool_create(pool); 329 330 SVN_ERR(svn_fs__check_fs(fs, FALSE)); 331 332 SVN_ERR(initialize_fs_struct(fs)); 333 334 SVN_ERR(svn_fs_fs__open(fs, path, subpool)); 335 336 SVN_ERR(svn_fs_fs__initialize_caches(fs, subpool)); 337 SVN_MUTEX__WITH_LOCK(common_pool_lock, 338 fs_serialized_init(fs, common_pool, subpool)); 339 340 svn_pool_destroy(subpool); 341 342 return SVN_NO_ERROR; 343} 344 345 346 347/* This implements the fs_library_vtable_t.open_for_recovery() API. */ 348static svn_error_t * 349fs_open_for_recovery(svn_fs_t *fs, 350 const char *path, 351 svn_mutex__t *common_pool_lock, 352 apr_pool_t *pool, 353 apr_pool_t *common_pool) 354{ 355 svn_error_t * err; 356 svn_revnum_t youngest_rev; 357 apr_pool_t * subpool = svn_pool_create(pool); 358 359 /* Recovery for FSFS is currently limited to recreating the 'current' 360 file from the latest revision. */ 361 362 /* The only thing we have to watch out for is that the 'current' file 363 might not exist or contain garbage. So we'll try to read it here 364 and provide or replace the existing file if we couldn't read it. 365 (We'll also need it to exist later anyway as a source for the new 366 file's permissions). */ 367 368 /* Use a partly-filled fs pointer first to create 'current'. */ 369 fs->path = apr_pstrdup(fs->pool, path); 370 371 SVN_ERR(initialize_fs_struct(fs)); 372 373 /* Figure out the repo format and check that we can even handle it. */ 374 SVN_ERR(svn_fs_fs__read_format_file(fs, subpool)); 375 376 /* Now, read 'current' and try to patch it if necessary. */ 377 err = svn_fs_fs__youngest_rev(&youngest_rev, fs, subpool); 378 if (err) 379 { 380 const char *file_path; 381 382 /* 'current' file is missing or contains garbage. Since we are trying 383 * to recover from whatever problem there is, being picky about the 384 * error code here won't do us much good. If there is a persistent 385 * problem that we can't fix, it will show up when we try rewrite the 386 * file a few lines further below and we will report the failure back 387 * to the caller. 388 * 389 * Start recovery with HEAD = 0. */ 390 svn_error_clear(err); 391 file_path = svn_fs_fs__path_current(fs, subpool); 392 393 /* Best effort to ensure the file exists and is valid. 394 * This may fail for r/o filesystems etc. */ 395 SVN_ERR(svn_io_remove_file2(file_path, TRUE, subpool)); 396 SVN_ERR(svn_io_file_create_empty(file_path, subpool)); 397 SVN_ERR(svn_fs_fs__write_current(fs, 0, 1, 1, subpool)); 398 } 399 400 uninitialize_fs_struct(fs); 401 svn_pool_destroy(subpool); 402 403 /* Now open the filesystem properly by calling the vtable method directly. */ 404 return fs_open(fs, path, common_pool_lock, pool, common_pool); 405} 406 407 408 409/* This implements the fs_library_vtable_t.upgrade_fs() API. */ 410static svn_error_t * 411fs_upgrade(svn_fs_t *fs, 412 const char *path, 413 svn_fs_upgrade_notify_t notify_func, 414 void *notify_baton, 415 svn_cancel_func_t cancel_func, 416 void *cancel_baton, 417 svn_mutex__t *common_pool_lock, 418 apr_pool_t *pool, 419 apr_pool_t *common_pool) 420{ 421 SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool)); 422 return svn_fs_fs__upgrade(fs, notify_func, notify_baton, 423 cancel_func, cancel_baton, pool); 424} 425 426static svn_error_t * 427fs_verify(svn_fs_t *fs, const char *path, 428 svn_revnum_t start, 429 svn_revnum_t end, 430 svn_fs_progress_notify_func_t notify_func, 431 void *notify_baton, 432 svn_cancel_func_t cancel_func, 433 void *cancel_baton, 434 svn_mutex__t *common_pool_lock, 435 apr_pool_t *pool, 436 apr_pool_t *common_pool) 437{ 438 SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool)); 439 return svn_fs_fs__verify(fs, start, end, notify_func, notify_baton, 440 cancel_func, cancel_baton, pool); 441} 442 443static svn_error_t * 444fs_pack(svn_fs_t *fs, 445 const char *path, 446 svn_fs_pack_notify_t notify_func, 447 void *notify_baton, 448 svn_cancel_func_t cancel_func, 449 void *cancel_baton, 450 svn_mutex__t *common_pool_lock, 451 apr_pool_t *pool, 452 apr_pool_t *common_pool) 453{ 454 SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool)); 455 return svn_fs_fs__pack(fs, 0, notify_func, notify_baton, 456 cancel_func, cancel_baton, pool); 457} 458 459 460 461 462/* This implements the fs_library_vtable_t.hotcopy() API. Copy a 463 possibly live Subversion filesystem SRC_FS from SRC_PATH to a 464 DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to 465 re-copy data which already exists in DST_FS. 466 The CLEAN_LOGS argument is ignored and included for Subversion 467 1.0.x compatibility. Indicate progress via the optional NOTIFY_FUNC 468 callback using NOTIFY_BATON. Perform all temporary allocations in POOL. */ 469static svn_error_t * 470fs_hotcopy(svn_fs_t *src_fs, 471 svn_fs_t *dst_fs, 472 const char *src_path, 473 const char *dst_path, 474 svn_boolean_t clean_logs, 475 svn_boolean_t incremental, 476 svn_fs_hotcopy_notify_t notify_func, 477 void *notify_baton, 478 svn_cancel_func_t cancel_func, 479 void *cancel_baton, 480 svn_mutex__t *common_pool_lock, 481 apr_pool_t *pool, 482 apr_pool_t *common_pool) 483{ 484 /* Open the source repo as usual. */ 485 SVN_ERR(fs_open(src_fs, src_path, common_pool_lock, pool, common_pool)); 486 if (cancel_func) 487 SVN_ERR(cancel_func(cancel_baton)); 488 489 /* Test target repo when in INCREMENTAL mode, initialize it when not. 490 * For this, we need our FS internal data structures to be temporarily 491 * available. */ 492 SVN_ERR(initialize_fs_struct(dst_fs)); 493 SVN_ERR(svn_fs_fs__hotcopy_prepare_target(src_fs, dst_fs, dst_path, 494 incremental, pool)); 495 uninitialize_fs_struct(dst_fs); 496 497 /* Now, the destination repo should open just fine. */ 498 SVN_ERR(fs_open(dst_fs, dst_path, common_pool_lock, pool, common_pool)); 499 if (cancel_func) 500 SVN_ERR(cancel_func(cancel_baton)); 501 502 /* Now, we may copy data as needed ... */ 503 return svn_fs_fs__hotcopy(src_fs, dst_fs, incremental, 504 notify_func, notify_baton, 505 cancel_func, cancel_baton, pool); 506} 507 508 509 510/* This function is included for Subversion 1.0.x compatibility. It 511 has no effect for fsfs backed Subversion filesystems. It conforms 512 to the fs_library_vtable_t.bdb_logfiles() API. */ 513static svn_error_t * 514fs_logfiles(apr_array_header_t **logfiles, 515 const char *path, 516 svn_boolean_t only_unused, 517 apr_pool_t *pool) 518{ 519 /* A no-op for FSFS. */ 520 *logfiles = apr_array_make(pool, 0, sizeof(const char *)); 521 522 return SVN_NO_ERROR; 523} 524 525 526 527 528 529/* Delete the filesystem located at path PATH. Perform any temporary 530 allocations in POOL. */ 531static svn_error_t * 532fs_delete_fs(const char *path, 533 apr_pool_t *pool) 534{ 535 /* Remove everything. */ 536 return svn_error_trace(svn_io_remove_dir2(path, FALSE, NULL, NULL, pool)); 537} 538 539static const svn_version_t * 540fs_version(void) 541{ 542 SVN_VERSION_BODY; 543} 544 545static const char * 546fs_get_description(void) 547{ 548 return _("Module for working with a plain file (FSFS) repository."); 549} 550 551static svn_error_t * 552fs_set_svn_fs_open(svn_fs_t *fs, 553 svn_error_t *(*svn_fs_open_)(svn_fs_t **, 554 const char *, 555 apr_hash_t *, 556 apr_pool_t *, 557 apr_pool_t *)) 558{ 559 fs_fs_data_t *ffd = fs->fsap_data; 560 ffd->svn_fs_open_ = svn_fs_open_; 561 return SVN_NO_ERROR; 562} 563 564static void * 565fs_info_dup(const void *fsfs_info_void, 566 apr_pool_t *result_pool) 567{ 568 /* All fields are either ints or static strings. */ 569 const svn_fs_fsfs_info_t *fsfs_info = fsfs_info_void; 570 return apr_pmemdup(result_pool, fsfs_info, sizeof(*fsfs_info)); 571} 572 573 574/* Base FS library vtable, used by the FS loader library. */ 575 576static fs_library_vtable_t library_vtable = { 577 fs_version, 578 fs_create, 579 fs_open, 580 fs_open_for_recovery, 581 fs_upgrade, 582 fs_verify, 583 fs_delete_fs, 584 fs_hotcopy, 585 fs_get_description, 586 svn_fs_fs__recover, 587 fs_pack, 588 fs_logfiles, 589 NULL /* parse_id */, 590 fs_set_svn_fs_open, 591 fs_info_dup 592}; 593 594svn_error_t * 595svn_fs_fs__init(const svn_version_t *loader_version, 596 fs_library_vtable_t **vtable, apr_pool_t* common_pool) 597{ 598 static const svn_version_checklist_t checklist[] = 599 { 600 { "svn_subr", svn_subr_version }, 601 { "svn_delta", svn_delta_version }, 602 { "svn_fs_util", svn_fs_util__version }, 603 { NULL, NULL } 604 }; 605 606 /* Simplified version check to make sure we can safely use the 607 VTABLE parameter. The FS loader does a more exhaustive check. */ 608 if (loader_version->major != SVN_VER_MAJOR) 609 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL, 610 _("Unsupported FS loader version (%d) for fsfs"), 611 loader_version->major); 612 SVN_ERR(svn_ver_check_list2(fs_version(), checklist, svn_ver_equal)); 613 614 *vtable = &library_vtable; 615 return SVN_NO_ERROR; 616} 617