1/* fs_fs.c --- filesystem operations specific to fs_fs 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 "fs_fs.h" 24 25#include <apr_uuid.h> 26 27#include "svn_private_config.h" 28 29#include "svn_checksum.h" 30#include "svn_hash.h" 31#include "svn_props.h" 32#include "svn_time.h" 33#include "svn_dirent_uri.h" 34#include "svn_sorts.h" 35#include "svn_version.h" 36 37#include "cached_data.h" 38#include "id.h" 39#include "index.h" 40#include "rep-cache.h" 41#include "revprops.h" 42#include "transaction.h" 43#include "tree.h" 44#include "util.h" 45 46#include "private/svn_fs_util.h" 47#include "private/svn_io_private.h" 48#include "private/svn_string_private.h" 49#include "private/svn_subr_private.h" 50#include "../libsvn_fs/fs-loader.h" 51 52/* The default maximum number of files per directory to store in the 53 rev and revprops directory. The number below is somewhat arbitrary, 54 and can be overridden by defining the macro while compiling; the 55 figure of 1000 is reasonable for VFAT filesystems, which are by far 56 the worst performers in this area. */ 57#ifndef SVN_FS_FS_DEFAULT_MAX_FILES_PER_DIR 58#define SVN_FS_FS_DEFAULT_MAX_FILES_PER_DIR 1000 59#endif 60 61/* Begin deltification after a node history exceeded this this limit. 62 Useful values are 4 to 64 with 16 being a good compromise between 63 computational overhead and repository size savings. 64 Should be a power of 2. 65 Values < 2 will result in standard skip-delta behavior. */ 66#define SVN_FS_FS_MAX_LINEAR_DELTIFICATION 16 67 68/* Finding a deltification base takes operations proportional to the 69 number of changes being skipped. To prevent exploding runtime 70 during commits, limit the deltification range to this value. 71 Should be a power of 2 minus one. 72 Values < 1 disable deltification. */ 73#define SVN_FS_FS_MAX_DELTIFICATION_WALK 1023 74 75/* Notes: 76 77To avoid opening and closing the rev-files all the time, it would 78probably be advantageous to keep each rev-file open for the 79lifetime of the transaction object. I'll leave that as a later 80optimization for now. 81 82I didn't keep track of pool lifetimes at all in this code. There 83are likely some errors because of that. 84 85*/ 86 87/* Declarations. */ 88 89static svn_error_t * 90get_youngest(svn_revnum_t *youngest_p, svn_fs_t *fs, apr_pool_t *pool); 91 92/* Pathname helper functions */ 93 94static const char * 95path_format(svn_fs_t *fs, apr_pool_t *pool) 96{ 97 return svn_dirent_join(fs->path, PATH_FORMAT, pool); 98} 99 100static APR_INLINE const char * 101path_uuid(svn_fs_t *fs, apr_pool_t *pool) 102{ 103 return svn_dirent_join(fs->path, PATH_UUID, pool); 104} 105 106const char * 107svn_fs_fs__path_current(svn_fs_t *fs, apr_pool_t *pool) 108{ 109 return svn_dirent_join(fs->path, PATH_CURRENT, pool); 110} 111 112 113 114/* Get a lock on empty file LOCK_FILENAME, creating it in POOL. */ 115static svn_error_t * 116get_lock_on_filesystem(const char *lock_filename, 117 apr_pool_t *pool) 118{ 119 return svn_error_trace(svn_io__file_lock_autocreate(lock_filename, pool)); 120} 121 122/* Reset the HAS_WRITE_LOCK member in the FFD given as BATON_VOID. 123 When registered with the pool holding the lock on the lock file, 124 this makes sure the flag gets reset just before we release the lock. */ 125static apr_status_t 126reset_lock_flag(void *baton_void) 127{ 128 fs_fs_data_t *ffd = baton_void; 129 ffd->has_write_lock = FALSE; 130 return APR_SUCCESS; 131} 132 133/* Structure defining a file system lock to be acquired and the function 134 to be executed while the lock is held. 135 136 Instances of this structure may be nested to allow for multiple locks to 137 be taken out before executing the user-provided body. In that case, BODY 138 and BATON of the outer instances will be with_lock and a with_lock_baton_t 139 instance (transparently, no special treatment is required.). It is 140 illegal to attempt to acquire the same lock twice within the same lock 141 chain or via nesting calls using separate lock chains. 142 143 All instances along the chain share the same LOCK_POOL such that only one 144 pool needs to be created and cleared for all locks. We also allocate as 145 much data from that lock pool as possible to minimize memory usage in 146 caller pools. */ 147typedef struct with_lock_baton_t 148{ 149 /* The filesystem we operate on. Same for all instances along the chain. */ 150 svn_fs_t *fs; 151 152 /* Mutex to complement the lock file in an APR threaded process. 153 No-op object for non-threaded processes but never NULL. */ 154 svn_mutex__t *mutex; 155 156 /* Path to the file to lock. */ 157 const char *lock_path; 158 159 /* If true, set FS->HAS_WRITE_LOCK after we acquired the lock. */ 160 svn_boolean_t is_global_lock; 161 162 /* Function body to execute after we acquired the lock. 163 This may be user-provided or a nested call to with_lock(). */ 164 svn_error_t *(*body)(void *baton, 165 apr_pool_t *pool); 166 167 /* Baton to pass to BODY; possibly NULL. 168 This may be user-provided or a nested lock baton instance. */ 169 void *baton; 170 171 /* Pool for all allocations along the lock chain and BODY. Will hold the 172 file locks and gets destroyed after the outermost BODY returned, 173 releasing all file locks. 174 Same for all instances along the chain. */ 175 apr_pool_t *lock_pool; 176 177 /* TRUE, iff BODY is the user-provided body. */ 178 svn_boolean_t is_inner_most_lock; 179 180 /* TRUE, iff this is not a nested lock. 181 Then responsible for destroying LOCK_POOL. */ 182 svn_boolean_t is_outer_most_lock; 183} with_lock_baton_t; 184 185/* Obtain a write lock on the file BATON->LOCK_PATH and call BATON->BODY 186 with BATON->BATON. If this is the outermost lock call, release all file 187 locks after the body returned. If BATON->IS_GLOBAL_LOCK is set, set the 188 HAS_WRITE_LOCK flag while we keep the write lock. */ 189static svn_error_t * 190with_some_lock_file(with_lock_baton_t *baton) 191{ 192 apr_pool_t *pool = baton->lock_pool; 193 svn_error_t *err = get_lock_on_filesystem(baton->lock_path, pool); 194 195 if (!err) 196 { 197 svn_fs_t *fs = baton->fs; 198 fs_fs_data_t *ffd = fs->fsap_data; 199 200 if (baton->is_global_lock) 201 { 202 /* set the "got the lock" flag and register reset function */ 203 apr_pool_cleanup_register(pool, 204 ffd, 205 reset_lock_flag, 206 apr_pool_cleanup_null); 207 ffd->has_write_lock = TRUE; 208 } 209 210 /* nobody else will modify the repo state 211 => read HEAD & pack info once */ 212 if (baton->is_inner_most_lock) 213 { 214 if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT) 215 err = svn_fs_fs__update_min_unpacked_rev(fs, pool); 216 if (!err) 217 err = get_youngest(&ffd->youngest_rev_cache, fs, pool); 218 } 219 220 if (!err) 221 err = baton->body(baton->baton, pool); 222 } 223 224 if (baton->is_outer_most_lock) 225 svn_pool_destroy(pool); 226 227 return svn_error_trace(err); 228} 229 230/* Wraps with_some_lock_file, protecting it with BATON->MUTEX. 231 232 POOL is unused here and only provided for signature compatibility with 233 WITH_LOCK_BATON_T.BODY. */ 234static svn_error_t * 235with_lock(void *baton, 236 apr_pool_t *pool) 237{ 238 with_lock_baton_t *lock_baton = baton; 239 SVN_MUTEX__WITH_LOCK(lock_baton->mutex, with_some_lock_file(lock_baton)); 240 241 return SVN_NO_ERROR; 242} 243 244/* Enum identifying a filesystem lock. */ 245typedef enum lock_id_t 246{ 247 write_lock, 248 txn_lock, 249 pack_lock 250} lock_id_t; 251 252/* Initialize BATON->MUTEX, BATON->LOCK_PATH and BATON->IS_GLOBAL_LOCK 253 according to the LOCK_ID. All other members of BATON must already be 254 valid. */ 255static void 256init_lock_baton(with_lock_baton_t *baton, 257 lock_id_t lock_id) 258{ 259 fs_fs_data_t *ffd = baton->fs->fsap_data; 260 fs_fs_shared_data_t *ffsd = ffd->shared; 261 262 switch (lock_id) 263 { 264 case write_lock: 265 baton->mutex = ffsd->fs_write_lock; 266 baton->lock_path = svn_fs_fs__path_lock(baton->fs, baton->lock_pool); 267 baton->is_global_lock = TRUE; 268 break; 269 270 case txn_lock: 271 baton->mutex = ffsd->txn_current_lock; 272 baton->lock_path = svn_fs_fs__path_txn_current_lock(baton->fs, 273 baton->lock_pool); 274 baton->is_global_lock = FALSE; 275 break; 276 277 case pack_lock: 278 baton->mutex = ffsd->fs_pack_lock; 279 baton->lock_path = svn_fs_fs__path_pack_lock(baton->fs, 280 baton->lock_pool); 281 baton->is_global_lock = FALSE; 282 break; 283 } 284} 285 286/* Return the baton for the innermost lock of a (potential) lock chain. 287 The baton shall take out LOCK_ID from FS and execute BODY with BATON 288 while the lock is being held. Allocate the result in a sub-pool of POOL. 289 */ 290static with_lock_baton_t * 291create_lock_baton(svn_fs_t *fs, 292 lock_id_t lock_id, 293 svn_error_t *(*body)(void *baton, 294 apr_pool_t *pool), 295 void *baton, 296 apr_pool_t *pool) 297{ 298 /* Allocate everything along the lock chain into a single sub-pool. 299 This minimizes memory usage and cleanup overhead. */ 300 apr_pool_t *lock_pool = svn_pool_create(pool); 301 with_lock_baton_t *result = apr_pcalloc(lock_pool, sizeof(*result)); 302 303 /* Store parameters. */ 304 result->fs = fs; 305 result->body = body; 306 result->baton = baton; 307 308 /* File locks etc. will use this pool as well for easy cleanup. */ 309 result->lock_pool = lock_pool; 310 311 /* Right now, we are the first, (only, ) and last struct in the chain. */ 312 result->is_inner_most_lock = TRUE; 313 result->is_outer_most_lock = TRUE; 314 315 /* Select mutex and lock file path depending on LOCK_ID. 316 Also, initialize dependent members (IS_GLOBAL_LOCK only, ATM). */ 317 init_lock_baton(result, lock_id); 318 319 return result; 320} 321 322/* Return a baton that wraps NESTED and requests LOCK_ID as additional lock. 323 * 324 * That means, when you create a lock chain, start with the last / innermost 325 * lock to take out and add the first / outermost lock last. 326 */ 327static with_lock_baton_t * 328chain_lock_baton(lock_id_t lock_id, 329 with_lock_baton_t *nested) 330{ 331 /* Use the same pool for batons along the lock chain. */ 332 apr_pool_t *lock_pool = nested->lock_pool; 333 with_lock_baton_t *result = apr_pcalloc(lock_pool, sizeof(*result)); 334 335 /* All locks along the chain operate on the same FS. */ 336 result->fs = nested->fs; 337 338 /* Execution of this baton means acquiring the nested lock and its 339 execution. */ 340 result->body = with_lock; 341 result->baton = nested; 342 343 /* Shared among all locks along the chain. */ 344 result->lock_pool = lock_pool; 345 346 /* We are the new outermost lock but surely not the innermost lock. */ 347 result->is_inner_most_lock = FALSE; 348 result->is_outer_most_lock = TRUE; 349 nested->is_outer_most_lock = FALSE; 350 351 /* Select mutex and lock file path depending on LOCK_ID. 352 Also, initialize dependent members (IS_GLOBAL_LOCK only, ATM). */ 353 init_lock_baton(result, lock_id); 354 355 return result; 356} 357 358svn_error_t * 359svn_fs_fs__with_write_lock(svn_fs_t *fs, 360 svn_error_t *(*body)(void *baton, 361 apr_pool_t *pool), 362 void *baton, 363 apr_pool_t *pool) 364{ 365 return svn_error_trace( 366 with_lock(create_lock_baton(fs, write_lock, body, baton, pool), 367 pool)); 368} 369 370svn_error_t * 371svn_fs_fs__with_pack_lock(svn_fs_t *fs, 372 svn_error_t *(*body)(void *baton, 373 apr_pool_t *pool), 374 void *baton, 375 apr_pool_t *pool) 376{ 377 return svn_error_trace( 378 with_lock(create_lock_baton(fs, pack_lock, body, baton, pool), 379 pool)); 380} 381 382svn_error_t * 383svn_fs_fs__with_txn_current_lock(svn_fs_t *fs, 384 svn_error_t *(*body)(void *baton, 385 apr_pool_t *pool), 386 void *baton, 387 apr_pool_t *pool) 388{ 389 return svn_error_trace( 390 with_lock(create_lock_baton(fs, txn_lock, body, baton, pool), 391 pool)); 392} 393 394svn_error_t * 395svn_fs_fs__with_all_locks(svn_fs_t *fs, 396 svn_error_t *(*body)(void *baton, 397 apr_pool_t *pool), 398 void *baton, 399 apr_pool_t *pool) 400{ 401 fs_fs_data_t *ffd = fs->fsap_data; 402 403 /* Be sure to use the correct lock ordering as documented in 404 fs_fs_shared_data_t. The lock chain is being created in 405 innermost (last to acquire) -> outermost (first to acquire) order. */ 406 with_lock_baton_t *lock_baton 407 = create_lock_baton(fs, write_lock, body, baton, pool); 408 409 if (ffd->format >= SVN_FS_FS__MIN_PACK_LOCK_FORMAT) 410 lock_baton = chain_lock_baton(pack_lock, lock_baton); 411 412 if (ffd->format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT) 413 lock_baton = chain_lock_baton(txn_lock, lock_baton); 414 415 return svn_error_trace(with_lock(lock_baton, pool)); 416} 417 418 419 420 421 422/* Check that BUF, a nul-terminated buffer of text from format file PATH, 423 contains only digits at OFFSET and beyond, raising an error if not. 424 425 Uses POOL for temporary allocation. */ 426static svn_error_t * 427check_format_file_buffer_numeric(const char *buf, apr_off_t offset, 428 const char *path, apr_pool_t *pool) 429{ 430 return svn_fs_fs__check_file_buffer_numeric(buf, offset, path, "Format", 431 pool); 432} 433 434/* Return the error SVN_ERR_FS_UNSUPPORTED_FORMAT if FS's format 435 number is not the same as a format number supported by this 436 Subversion. */ 437static svn_error_t * 438check_format(int format) 439{ 440 /* Blacklist. These formats may be either younger or older than 441 SVN_FS_FS__FORMAT_NUMBER, but we don't support them. */ 442 if (format == SVN_FS_FS__PACKED_REVPROP_SQLITE_DEV_FORMAT) 443 return svn_error_createf(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL, 444 _("Found format '%d', only created by " 445 "unreleased dev builds; see " 446 "http://subversion.apache.org" 447 "/docs/release-notes/1.7#revprop-packing"), 448 format); 449 450 /* We support all formats from 1-current simultaneously */ 451 if (1 <= format && format <= SVN_FS_FS__FORMAT_NUMBER) 452 return SVN_NO_ERROR; 453 454 return svn_error_createf(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL, 455 _("Expected FS format between '1' and '%d'; found format '%d'"), 456 SVN_FS_FS__FORMAT_NUMBER, format); 457} 458 459/* Read the format number and maximum number of files per directory 460 from PATH and return them in *PFORMAT, *MAX_FILES_PER_DIR and 461 USE_LOG_ADDRESSIONG respectively. 462 463 *MAX_FILES_PER_DIR is obtained from the 'layout' format option, and 464 will be set to zero if a linear scheme should be used. 465 *USE_LOG_ADDRESSIONG is obtained from the 'addressing' format option, 466 and will be set to FALSE for physical addressing. 467 468 Use POOL for temporary allocation. */ 469static svn_error_t * 470read_format(int *pformat, 471 int *max_files_per_dir, 472 svn_boolean_t *use_log_addressing, 473 const char *path, 474 apr_pool_t *pool) 475{ 476 svn_error_t *err; 477 svn_stream_t *stream; 478 svn_stringbuf_t *content; 479 svn_stringbuf_t *buf; 480 svn_boolean_t eos = FALSE; 481 482 err = svn_stringbuf_from_file2(&content, path, pool); 483 if (err && APR_STATUS_IS_ENOENT(err->apr_err)) 484 { 485 /* Treat an absent format file as format 1. Do not try to 486 create the format file on the fly, because the repository 487 might be read-only for us, or this might be a read-only 488 operation, and the spirit of FSFS is to make no changes 489 whatseover in read-only operations. See thread starting at 490 http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=97600 491 for more. */ 492 svn_error_clear(err); 493 *pformat = 1; 494 *max_files_per_dir = 0; 495 *use_log_addressing = FALSE; 496 497 return SVN_NO_ERROR; 498 } 499 SVN_ERR(err); 500 501 stream = svn_stream_from_stringbuf(content, pool); 502 SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eos, pool)); 503 if (buf->len == 0 && eos) 504 { 505 /* Return a more useful error message. */ 506 return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL, 507 _("Can't read first line of format file '%s'"), 508 svn_dirent_local_style(path, pool)); 509 } 510 511 /* Check that the first line contains only digits. */ 512 SVN_ERR(check_format_file_buffer_numeric(buf->data, 0, path, pool)); 513 SVN_ERR(svn_cstring_atoi(pformat, buf->data)); 514 515 /* Check that we support this format at all */ 516 SVN_ERR(check_format(*pformat)); 517 518 /* Set the default values for anything that can be set via an option. */ 519 *max_files_per_dir = 0; 520 *use_log_addressing = FALSE; 521 522 /* Read any options. */ 523 while (!eos) 524 { 525 SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eos, pool)); 526 if (buf->len == 0) 527 break; 528 529 if (*pformat >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT && 530 strncmp(buf->data, "layout ", 7) == 0) 531 { 532 if (strcmp(buf->data + 7, "linear") == 0) 533 { 534 *max_files_per_dir = 0; 535 continue; 536 } 537 538 if (strncmp(buf->data + 7, "sharded ", 8) == 0) 539 { 540 /* Check that the argument is numeric. */ 541 SVN_ERR(check_format_file_buffer_numeric(buf->data, 15, path, pool)); 542 SVN_ERR(svn_cstring_atoi(max_files_per_dir, buf->data + 15)); 543 continue; 544 } 545 } 546 547 if (*pformat >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT && 548 strncmp(buf->data, "addressing ", 11) == 0) 549 { 550 if (strcmp(buf->data + 11, "physical") == 0) 551 { 552 *use_log_addressing = FALSE; 553 continue; 554 } 555 556 if (strcmp(buf->data + 11, "logical") == 0) 557 { 558 *use_log_addressing = TRUE; 559 continue; 560 } 561 } 562 563 return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL, 564 _("'%s' contains invalid filesystem format option '%s'"), 565 svn_dirent_local_style(path, pool), buf->data); 566 } 567 568 /* Non-sharded repositories never use logical addressing. 569 * If the format file is inconsistent in that respect, something 570 * probably went wrong. 571 */ 572 if (*use_log_addressing && !*max_files_per_dir) 573 return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL, 574 _("'%s' specifies logical addressing for a non-sharded repository"), 575 svn_dirent_local_style(path, pool)); 576 577 return SVN_NO_ERROR; 578} 579 580/* Write the format number, maximum number of files per directory and 581 the addressing scheme to a new format file in PATH, possibly expecting 582 to overwrite a previously existing file. 583 584 Use POOL for temporary allocation. */ 585svn_error_t * 586svn_fs_fs__write_format(svn_fs_t *fs, 587 svn_boolean_t overwrite, 588 apr_pool_t *pool) 589{ 590 svn_stringbuf_t *sb; 591 fs_fs_data_t *ffd = fs->fsap_data; 592 const char *path = path_format(fs, pool); 593 594 SVN_ERR_ASSERT(1 <= ffd->format 595 && ffd->format <= SVN_FS_FS__FORMAT_NUMBER); 596 597 sb = svn_stringbuf_createf(pool, "%d\n", ffd->format); 598 599 if (ffd->format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT) 600 { 601 if (ffd->max_files_per_dir) 602 svn_stringbuf_appendcstr(sb, apr_psprintf(pool, "layout sharded %d\n", 603 ffd->max_files_per_dir)); 604 else 605 svn_stringbuf_appendcstr(sb, "layout linear\n"); 606 } 607 608 if (ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT) 609 { 610 if (ffd->use_log_addressing) 611 svn_stringbuf_appendcstr(sb, "addressing logical\n"); 612 else 613 svn_stringbuf_appendcstr(sb, "addressing physical\n"); 614 } 615 616 /* svn_io_write_version_file() does a load of magic to allow it to 617 replace version files that already exist. We only need to do 618 that when we're allowed to overwrite an existing file. */ 619 if (! overwrite) 620 { 621 /* Create the file */ 622 SVN_ERR(svn_io_file_create(path, sb->data, pool)); 623 } 624 else 625 { 626 SVN_ERR(svn_io_write_atomic(path, sb->data, sb->len, 627 NULL /* copy_perms_path */, pool)); 628 } 629 630 /* And set the perms to make it read only */ 631 return svn_io_set_file_read_only(path, FALSE, pool); 632} 633 634svn_boolean_t 635svn_fs_fs__fs_supports_mergeinfo(svn_fs_t *fs) 636{ 637 fs_fs_data_t *ffd = fs->fsap_data; 638 return ffd->format >= SVN_FS_FS__MIN_MERGEINFO_FORMAT; 639} 640 641/* Check that BLOCK_SIZE is a valid block / page size, i.e. it is within 642 * the range of what the current system may address in RAM and it is a 643 * power of 2. Assume that the element size within the block is ITEM_SIZE. 644 * Use SCRATCH_POOL for temporary allocations. 645 */ 646static svn_error_t * 647verify_block_size(apr_int64_t block_size, 648 apr_size_t item_size, 649 const char *name, 650 apr_pool_t *scratch_pool 651 ) 652{ 653 /* Limit range. */ 654 if (block_size <= 0) 655 return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, 656 _("%s is too small for fsfs.conf setting '%s'."), 657 apr_psprintf(scratch_pool, 658 "%" APR_INT64_T_FMT, 659 block_size), 660 name); 661 662 if (block_size > SVN_MAX_OBJECT_SIZE / item_size) 663 return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, 664 _("%s is too large for fsfs.conf setting '%s'."), 665 apr_psprintf(scratch_pool, 666 "%" APR_INT64_T_FMT, 667 block_size), 668 name); 669 670 /* Ensure it is a power of two. 671 * For positive X, X & (X-1) will reset the lowest bit set. 672 * If the result is 0, at most one bit has been set. */ 673 if (0 != (block_size & (block_size - 1))) 674 return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, 675 _("%s is invalid for fsfs.conf setting '%s' " 676 "because it is not a power of 2."), 677 apr_psprintf(scratch_pool, 678 "%" APR_INT64_T_FMT, 679 block_size), 680 name); 681 682 return SVN_NO_ERROR; 683} 684 685/* Read the configuration information of the file system at FS_PATH 686 * and set the respective values in FFD. Use pools as usual. 687 */ 688static svn_error_t * 689read_config(fs_fs_data_t *ffd, 690 const char *fs_path, 691 apr_pool_t *result_pool, 692 apr_pool_t *scratch_pool) 693{ 694 svn_config_t *config; 695 696 SVN_ERR(svn_config_read3(&config, 697 svn_dirent_join(fs_path, PATH_CONFIG, scratch_pool), 698 FALSE, FALSE, FALSE, scratch_pool)); 699 700 /* Initialize ffd->rep_sharing_allowed. */ 701 if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT) 702 SVN_ERR(svn_config_get_bool(config, &ffd->rep_sharing_allowed, 703 CONFIG_SECTION_REP_SHARING, 704 CONFIG_OPTION_ENABLE_REP_SHARING, TRUE)); 705 else 706 ffd->rep_sharing_allowed = FALSE; 707 708 /* Initialize deltification settings in ffd. */ 709 if (ffd->format >= SVN_FS_FS__MIN_DELTIFICATION_FORMAT) 710 { 711 apr_int64_t compression_level; 712 713 SVN_ERR(svn_config_get_bool(config, &ffd->deltify_directories, 714 CONFIG_SECTION_DELTIFICATION, 715 CONFIG_OPTION_ENABLE_DIR_DELTIFICATION, 716 TRUE)); 717 SVN_ERR(svn_config_get_bool(config, &ffd->deltify_properties, 718 CONFIG_SECTION_DELTIFICATION, 719 CONFIG_OPTION_ENABLE_PROPS_DELTIFICATION, 720 TRUE)); 721 SVN_ERR(svn_config_get_int64(config, &ffd->max_deltification_walk, 722 CONFIG_SECTION_DELTIFICATION, 723 CONFIG_OPTION_MAX_DELTIFICATION_WALK, 724 SVN_FS_FS_MAX_DELTIFICATION_WALK)); 725 SVN_ERR(svn_config_get_int64(config, &ffd->max_linear_deltification, 726 CONFIG_SECTION_DELTIFICATION, 727 CONFIG_OPTION_MAX_LINEAR_DELTIFICATION, 728 SVN_FS_FS_MAX_LINEAR_DELTIFICATION)); 729 730 SVN_ERR(svn_config_get_int64(config, &compression_level, 731 CONFIG_SECTION_DELTIFICATION, 732 CONFIG_OPTION_COMPRESSION_LEVEL, 733 SVN_DELTA_COMPRESSION_LEVEL_DEFAULT)); 734 ffd->delta_compression_level 735 = (int)MIN(MAX(SVN_DELTA_COMPRESSION_LEVEL_NONE, compression_level), 736 SVN_DELTA_COMPRESSION_LEVEL_MAX); 737 } 738 else 739 { 740 ffd->deltify_directories = FALSE; 741 ffd->deltify_properties = FALSE; 742 ffd->max_deltification_walk = SVN_FS_FS_MAX_DELTIFICATION_WALK; 743 ffd->max_linear_deltification = SVN_FS_FS_MAX_LINEAR_DELTIFICATION; 744 ffd->delta_compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT; 745 } 746 747 /* Initialize revprop packing settings in ffd. */ 748 if (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT) 749 { 750 SVN_ERR(svn_config_get_bool(config, &ffd->compress_packed_revprops, 751 CONFIG_SECTION_PACKED_REVPROPS, 752 CONFIG_OPTION_COMPRESS_PACKED_REVPROPS, 753 FALSE)); 754 SVN_ERR(svn_config_get_int64(config, &ffd->revprop_pack_size, 755 CONFIG_SECTION_PACKED_REVPROPS, 756 CONFIG_OPTION_REVPROP_PACK_SIZE, 757 ffd->compress_packed_revprops 758 ? 0x10 759 : 0x4)); 760 761 ffd->revprop_pack_size *= 1024; 762 } 763 else 764 { 765 ffd->revprop_pack_size = 0x10000; 766 ffd->compress_packed_revprops = FALSE; 767 } 768 769 if (ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT) 770 { 771 SVN_ERR(svn_config_get_int64(config, &ffd->block_size, 772 CONFIG_SECTION_IO, 773 CONFIG_OPTION_BLOCK_SIZE, 774 64)); 775 SVN_ERR(svn_config_get_int64(config, &ffd->l2p_page_size, 776 CONFIG_SECTION_IO, 777 CONFIG_OPTION_L2P_PAGE_SIZE, 778 0x2000)); 779 SVN_ERR(svn_config_get_int64(config, &ffd->p2l_page_size, 780 CONFIG_SECTION_IO, 781 CONFIG_OPTION_P2L_PAGE_SIZE, 782 0x400)); 783 784 /* Don't accept unreasonable or illegal values. 785 * Block size and P2L page size are in kbytes; 786 * L2P blocks are arrays of apr_off_t. */ 787 SVN_ERR(verify_block_size(ffd->block_size, 0x400, 788 CONFIG_OPTION_BLOCK_SIZE, scratch_pool)); 789 SVN_ERR(verify_block_size(ffd->p2l_page_size, 0x400, 790 CONFIG_OPTION_P2L_PAGE_SIZE, scratch_pool)); 791 SVN_ERR(verify_block_size(ffd->l2p_page_size, sizeof(apr_off_t), 792 CONFIG_OPTION_L2P_PAGE_SIZE, scratch_pool)); 793 794 /* convert kBytes to bytes */ 795 ffd->block_size *= 0x400; 796 ffd->p2l_page_size *= 0x400; 797 /* L2P pages are in entries - not in (k)Bytes */ 798 } 799 else 800 { 801 /* should be irrelevant but we initialize them anyway */ 802 ffd->block_size = 0x1000; /* Matches default APR file buffer size. */ 803 ffd->l2p_page_size = 0x2000; /* Matches above default. */ 804 ffd->p2l_page_size = 0x100000; /* Matches above default in bytes. */ 805 } 806 807 if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT) 808 { 809 SVN_ERR(svn_config_get_bool(config, &ffd->pack_after_commit, 810 CONFIG_SECTION_DEBUG, 811 CONFIG_OPTION_PACK_AFTER_COMMIT, 812 FALSE)); 813 } 814 else 815 { 816 ffd->pack_after_commit = FALSE; 817 } 818 819 /* memcached configuration */ 820 SVN_ERR(svn_cache__make_memcache_from_config(&ffd->memcache, config, 821 result_pool, scratch_pool)); 822 823 SVN_ERR(svn_config_get_bool(config, &ffd->fail_stop, 824 CONFIG_SECTION_CACHES, CONFIG_OPTION_FAIL_STOP, 825 FALSE)); 826 827 return SVN_NO_ERROR; 828} 829 830static svn_error_t * 831write_config(svn_fs_t *fs, 832 apr_pool_t *pool) 833{ 834#define NL APR_EOL_STR 835 static const char * const fsfs_conf_contents = 836"### This file controls the configuration of the FSFS filesystem." NL 837"" NL 838"[" SVN_CACHE_CONFIG_CATEGORY_MEMCACHED_SERVERS "]" NL 839"### These options name memcached servers used to cache internal FSFS" NL 840"### data. See http://www.danga.com/memcached/ for more information on" NL 841"### memcached. To use memcached with FSFS, run one or more memcached" NL 842"### servers, and specify each of them as an option like so:" NL 843"# first-server = 127.0.0.1:11211" NL 844"# remote-memcached = mymemcached.corp.example.com:11212" NL 845"### The option name is ignored; the value is of the form HOST:PORT." NL 846"### memcached servers can be shared between multiple repositories;" NL 847"### however, if you do this, you *must* ensure that repositories have" NL 848"### distinct UUIDs and paths, or else cached data from one repository" NL 849"### might be used by another accidentally. Note also that memcached has" NL 850"### no authentication for reads or writes, so you must ensure that your" NL 851"### memcached servers are only accessible by trusted users." NL 852"" NL 853"[" CONFIG_SECTION_CACHES "]" NL 854"### When a cache-related error occurs, normally Subversion ignores it" NL 855"### and continues, logging an error if the server is appropriately" NL 856"### configured (and ignoring it with file:// access). To make" NL 857"### Subversion never ignore cache errors, uncomment this line." NL 858"# " CONFIG_OPTION_FAIL_STOP " = true" NL 859"" NL 860"[" CONFIG_SECTION_REP_SHARING "]" NL 861"### To conserve space, the filesystem can optionally avoid storing" NL 862"### duplicate representations. This comes at a slight cost in" NL 863"### performance, as maintaining a database of shared representations can" NL 864"### increase commit times. The space savings are dependent upon the size" NL 865"### of the repository, the number of objects it contains and the amount of" NL 866"### duplication between them, usually a function of the branching and" NL 867"### merging process." NL 868"###" NL 869"### The following parameter enables rep-sharing in the repository. It can" NL 870"### be switched on and off at will, but for best space-saving results" NL 871"### should be enabled consistently over the life of the repository." NL 872"### 'svnadmin verify' will check the rep-cache regardless of this setting." NL 873"### rep-sharing is enabled by default." NL 874"# " CONFIG_OPTION_ENABLE_REP_SHARING " = true" NL 875"" NL 876"[" CONFIG_SECTION_DELTIFICATION "]" NL 877"### To conserve space, the filesystem stores data as differences against" NL 878"### existing representations. This comes at a slight cost in performance," NL 879"### as calculating differences can increase commit times. Reading data" NL 880"### will also create higher CPU load and the data will be fragmented." NL 881"### Since deltification tends to save significant amounts of disk space," NL 882"### the overall I/O load can actually be lower." NL 883"###" NL 884"### The options in this section allow for tuning the deltification" NL 885"### strategy. Their effects on data size and server performance may vary" NL 886"### from one repository to another. Versions prior to 1.8 will ignore" NL 887"### this section." NL 888"###" NL 889"### The following parameter enables deltification for directories. It can" NL 890"### be switched on and off at will, but for best space-saving results" NL 891"### should be enabled consistently over the lifetime of the repository." NL 892"### Repositories containing large directories will benefit greatly." NL 893"### In rarely accessed repositories, the I/O overhead may be significant" NL 894"### as caches will most likely be low." NL 895"### directory deltification is enabled by default." NL 896"# " CONFIG_OPTION_ENABLE_DIR_DELTIFICATION " = true" NL 897"###" NL 898"### The following parameter enables deltification for properties on files" NL 899"### and directories. Overall, this is a minor tuning option but can save" NL 900"### some disk space if you merge frequently or frequently change node" NL 901"### properties. You should not activate this if rep-sharing has been" NL 902"### disabled because this may result in a net increase in repository size." NL 903"### property deltification is enabled by default." NL 904"# " CONFIG_OPTION_ENABLE_PROPS_DELTIFICATION " = true" NL 905"###" NL 906"### During commit, the server may need to walk the whole change history of" NL 907"### of a given node to find a suitable deltification base. This linear" NL 908"### process can impact commit times, svnadmin load and similar operations." NL 909"### This setting limits the depth of the deltification history. If the" NL 910"### threshold has been reached, the node will be stored as fulltext and a" NL 911"### new deltification history begins." NL 912"### Note, this is unrelated to svn log." NL 913"### Very large values rarely provide significant additional savings but" NL 914"### can impact performance greatly - in particular if directory" NL 915"### deltification has been activated. Very small values may be useful in" NL 916"### repositories that are dominated by large, changing binaries." NL 917"### Should be a power of two minus 1. A value of 0 will effectively" NL 918"### disable deltification." NL 919"### For 1.8, the default value is 1023; earlier versions have no limit." NL 920"# " CONFIG_OPTION_MAX_DELTIFICATION_WALK " = 1023" NL 921"###" NL 922"### The skip-delta scheme used by FSFS tends to repeatably store redundant" NL 923"### delta information where a simple delta against the latest version is" NL 924"### often smaller. By default, 1.8+ will therefore use skip deltas only" NL 925"### after the linear chain of deltas has grown beyond the threshold" NL 926"### specified by this setting." NL 927"### Values up to 64 can result in some reduction in repository size for" NL 928"### the cost of quickly increasing I/O and CPU costs. Similarly, smaller" NL 929"### numbers can reduce those costs at the cost of more disk space. For" NL 930"### rarely read repositories or those containing larger binaries, this may" NL 931"### present a better trade-off." NL 932"### Should be a power of two. A value of 1 or smaller will cause the" NL 933"### exclusive use of skip-deltas (as in pre-1.8)." NL 934"### For 1.8, the default value is 16; earlier versions use 1." NL 935"# " CONFIG_OPTION_MAX_LINEAR_DELTIFICATION " = 16" NL 936"###" NL 937"### After deltification, we compress the data through zlib to minimize on-" NL 938"### disk size. That can be an expensive and ineffective process. This" NL 939"### setting controls the usage of zlib in future revisions." NL 940"### Revisions with highly compressible data in them may shrink in size" NL 941"### if the setting is increased but may take much longer to commit. The" NL 942"### time taken to uncompress that data again is widely independent of the" NL 943"### compression level." NL 944"### Compression will be ineffective if the incoming content is already" NL 945"### highly compressed. In that case, disabling the compression entirely" NL 946"### will speed up commits as well as reading the data. Repositories with" NL 947"### many small compressible files (source code) but also a high percentage" NL 948"### of large incompressible ones (artwork) may benefit from compression" NL 949"### levels lowered to e.g. 1." NL 950"### Valid values are 0 to 9 with 9 providing the highest compression ratio" NL 951"### and 0 disabling it altogether." NL 952"### The default value is 5." NL 953"# " CONFIG_OPTION_COMPRESSION_LEVEL " = 5" NL 954"" NL 955"[" CONFIG_SECTION_PACKED_REVPROPS "]" NL 956"### This parameter controls the size (in kBytes) of packed revprop files." NL 957"### Revprops of consecutive revisions will be concatenated into a single" NL 958"### file up to but not exceeding the threshold given here. However, each" NL 959"### pack file may be much smaller and revprops of a single revision may be" NL 960"### much larger than the limit set here. The threshold will be applied" NL 961"### before optional compression takes place." NL 962"### Large values will reduce disk space usage at the expense of increased" NL 963"### latency and CPU usage reading and changing individual revprops." NL 964"### Values smaller than 4 kByte will not improve latency any further and " NL 965"### quickly render revprop packing ineffective." NL 966"### revprop-pack-size is 4 kBytes by default for non-compressed revprop" NL 967"### pack files and 16 kBytes when compression has been enabled." NL 968"# " CONFIG_OPTION_REVPROP_PACK_SIZE " = 4" NL 969"###" NL 970"### To save disk space, packed revprop files may be compressed. Standard" NL 971"### revprops tend to allow for very effective compression. Reading and" NL 972"### even more so writing, become significantly more CPU intensive." NL 973"### Compressing packed revprops is disabled by default." NL 974"# " CONFIG_OPTION_COMPRESS_PACKED_REVPROPS " = false" NL 975"" NL 976"[" CONFIG_SECTION_IO "]" NL 977"### Parameters in this section control the data access granularity in" NL 978"### format 7 repositories and later. The defaults should translate into" NL 979"### decent performance over a wide range of setups." NL 980"###" NL 981"### When a specific piece of information needs to be read from disk, a" NL 982"### data block is being read at once and its contents are being cached." NL 983"### If the repository is being stored on a RAID, the block size should be" NL 984"### either 50% or 100% of RAID block size / granularity. Also, your file" NL 985"### system blocks/clusters should be properly aligned and sized. In that" NL 986"### setup, each access will hit only one disk (minimizes I/O load) but" NL 987"### uses all the data provided by the disk in a single access." NL 988"### For SSD-based storage systems, slightly lower values around 16 kB" NL 989"### may improve latency while still maximizing throughput. If block-read" NL 990"### has not been enabled, this will be capped to 4 kBytes." NL 991"### Can be changed at any time but must be a power of 2." NL 992"### block-size is given in kBytes and with a default of 64 kBytes." NL 993"# " CONFIG_OPTION_BLOCK_SIZE " = 64" NL 994"###" NL 995"### The log-to-phys index maps data item numbers to offsets within the" NL 996"### rev or pack file. This index is organized in pages of a fixed maximum" NL 997"### capacity. To access an item, the page table and the respective page" NL 998"### must be read." NL 999"### This parameter only affects revisions with thousands of changed paths." NL 1000"### If you have several extremely large revisions (~1 mio changes), think" NL 1001"### about increasing this setting. Reducing the value will rarely result" NL 1002"### in a net speedup." NL 1003"### This is an expert setting. Must be a power of 2." NL 1004"### l2p-page-size is 8192 entries by default." NL 1005"# " CONFIG_OPTION_L2P_PAGE_SIZE " = 8192" NL 1006"###" NL 1007"### The phys-to-log index maps positions within the rev or pack file to" NL 1008"### to data items, i.e. describes what piece of information is being" NL 1009"### stored at any particular offset. The index describes the rev file" NL 1010"### in chunks (pages) and keeps a global list of all those pages. Large" NL 1011"### pages mean a shorter page table but a larger per-page description of" NL 1012"### data items in it. The latency sweetspot depends on the change size" NL 1013"### distribution but covers a relatively wide range." NL 1014"### If the repository contains very large files, i.e. individual changes" NL 1015"### of tens of MB each, increasing the page size will shorten the index" NL 1016"### file at the expense of a slightly increased latency in sections with" NL 1017"### smaller changes." NL 1018"### For source code repositories, this should be about 16x the block-size." NL 1019"### Must be a power of 2." NL 1020"### p2l-page-size is given in kBytes and with a default of 1024 kBytes." NL 1021"# " CONFIG_OPTION_P2L_PAGE_SIZE " = 1024" NL 1022; 1023#undef NL 1024 return svn_io_file_create(svn_dirent_join(fs->path, PATH_CONFIG, pool), 1025 fsfs_conf_contents, pool); 1026} 1027 1028/* Read / Evaluate the global configuration in FS->CONFIG to set up 1029 * parameters in FS. */ 1030static svn_error_t * 1031read_global_config(svn_fs_t *fs) 1032{ 1033 fs_fs_data_t *ffd = fs->fsap_data; 1034 1035 /* Providing a config hash is optional. */ 1036 if (fs->config) 1037 ffd->use_block_read = svn_hash__get_bool(fs->config, 1038 SVN_FS_CONFIG_FSFS_BLOCK_READ, 1039 FALSE); 1040 else 1041 ffd->use_block_read = FALSE; 1042 1043 /* Ignore the user-specified larger block size if we don't use block-read. 1044 Defaulting to 4k gives us the same access granularity in format 7 as in 1045 older formats. */ 1046 if (!ffd->use_block_read) 1047 ffd->block_size = MIN(0x1000, ffd->block_size); 1048 1049 return SVN_NO_ERROR; 1050} 1051 1052/* Read FS's UUID file and store the data in the FS struct. */ 1053static svn_error_t * 1054read_uuid(svn_fs_t *fs, 1055 apr_pool_t *scratch_pool) 1056{ 1057 fs_fs_data_t *ffd = fs->fsap_data; 1058 apr_file_t *uuid_file; 1059 char buf[APR_UUID_FORMATTED_LENGTH + 2]; 1060 apr_size_t limit; 1061 1062 /* Read the repository uuid. */ 1063 SVN_ERR(svn_io_file_open(&uuid_file, path_uuid(fs, scratch_pool), 1064 APR_READ | APR_BUFFERED, APR_OS_DEFAULT, 1065 scratch_pool)); 1066 1067 limit = sizeof(buf); 1068 SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit, scratch_pool)); 1069 fs->uuid = apr_pstrdup(fs->pool, buf); 1070 1071 /* Read the instance ID. */ 1072 if (ffd->format >= SVN_FS_FS__MIN_INSTANCE_ID_FORMAT) 1073 { 1074 limit = sizeof(buf); 1075 SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit, 1076 scratch_pool)); 1077 ffd->instance_id = apr_pstrdup(fs->pool, buf); 1078 } 1079 else 1080 { 1081 ffd->instance_id = fs->uuid; 1082 } 1083 1084 SVN_ERR(svn_io_file_close(uuid_file, scratch_pool)); 1085 1086 return SVN_NO_ERROR; 1087} 1088 1089svn_error_t * 1090svn_fs_fs__read_format_file(svn_fs_t *fs, apr_pool_t *scratch_pool) 1091{ 1092 fs_fs_data_t *ffd = fs->fsap_data; 1093 int format, max_files_per_dir; 1094 svn_boolean_t use_log_addressing; 1095 1096 /* Read info from format file. */ 1097 SVN_ERR(read_format(&format, &max_files_per_dir, &use_log_addressing, 1098 path_format(fs, scratch_pool), scratch_pool)); 1099 1100 /* Now that we've got *all* info, store / update values in FFD. */ 1101 ffd->format = format; 1102 ffd->max_files_per_dir = max_files_per_dir; 1103 ffd->use_log_addressing = use_log_addressing; 1104 1105 return SVN_NO_ERROR; 1106} 1107 1108svn_error_t * 1109svn_fs_fs__open(svn_fs_t *fs, const char *path, apr_pool_t *pool) 1110{ 1111 fs_fs_data_t *ffd = fs->fsap_data; 1112 fs->path = apr_pstrdup(fs->pool, path); 1113 1114 /* Read the FS format file. */ 1115 SVN_ERR(svn_fs_fs__read_format_file(fs, pool)); 1116 1117 /* Read in and cache the repository uuid. */ 1118 SVN_ERR(read_uuid(fs, pool)); 1119 1120 /* Read the min unpacked revision. */ 1121 if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT) 1122 SVN_ERR(svn_fs_fs__update_min_unpacked_rev(fs, pool)); 1123 1124 /* Read the configuration file. */ 1125 SVN_ERR(read_config(ffd, fs->path, fs->pool, pool)); 1126 1127 /* Global configuration options. */ 1128 SVN_ERR(read_global_config(fs)); 1129 1130 return get_youngest(&(ffd->youngest_rev_cache), fs, pool); 1131} 1132 1133/* Wrapper around svn_io_file_create which ignores EEXIST. */ 1134static svn_error_t * 1135create_file_ignore_eexist(const char *file, 1136 const char *contents, 1137 apr_pool_t *pool) 1138{ 1139 svn_error_t *err = svn_io_file_create(file, contents, pool); 1140 if (err && APR_STATUS_IS_EEXIST(err->apr_err)) 1141 { 1142 svn_error_clear(err); 1143 err = SVN_NO_ERROR; 1144 } 1145 return svn_error_trace(err); 1146} 1147 1148/* Baton type bridging svn_fs_fs__upgrade and upgrade_body carrying 1149 * parameters over between them. */ 1150struct upgrade_baton_t 1151{ 1152 svn_fs_t *fs; 1153 svn_fs_upgrade_notify_t notify_func; 1154 void *notify_baton; 1155 svn_cancel_func_t cancel_func; 1156 void *cancel_baton; 1157}; 1158 1159static svn_error_t * 1160upgrade_body(void *baton, apr_pool_t *pool) 1161{ 1162 struct upgrade_baton_t *upgrade_baton = baton; 1163 svn_fs_t *fs = upgrade_baton->fs; 1164 fs_fs_data_t *ffd = fs->fsap_data; 1165 int format, max_files_per_dir; 1166 svn_boolean_t use_log_addressing; 1167 const char *format_path = path_format(fs, pool); 1168 svn_node_kind_t kind; 1169 svn_boolean_t needs_revprop_shard_cleanup = FALSE; 1170 1171 /* Read the FS format number and max-files-per-dir setting. */ 1172 SVN_ERR(read_format(&format, &max_files_per_dir, &use_log_addressing, 1173 format_path, pool)); 1174 1175 /* If the config file does not exist, create one. */ 1176 SVN_ERR(svn_io_check_path(svn_dirent_join(fs->path, PATH_CONFIG, pool), 1177 &kind, pool)); 1178 switch (kind) 1179 { 1180 case svn_node_none: 1181 SVN_ERR(write_config(fs, pool)); 1182 break; 1183 case svn_node_file: 1184 break; 1185 default: 1186 return svn_error_createf(SVN_ERR_FS_GENERAL, NULL, 1187 _("'%s' is not a regular file." 1188 " Please move it out of " 1189 "the way and try again"), 1190 svn_dirent_join(fs->path, PATH_CONFIG, pool)); 1191 } 1192 1193 /* If we're already up-to-date, there's nothing else to be done here. */ 1194 if (format == SVN_FS_FS__FORMAT_NUMBER) 1195 return SVN_NO_ERROR; 1196 1197 /* If our filesystem predates the existence of the 'txn-current 1198 file', make that file and its corresponding lock file. */ 1199 if (format < SVN_FS_FS__MIN_TXN_CURRENT_FORMAT) 1200 { 1201 SVN_ERR(create_file_ignore_eexist( 1202 svn_fs_fs__path_txn_current(fs, pool), "0\n", 1203 pool)); 1204 SVN_ERR(create_file_ignore_eexist( 1205 svn_fs_fs__path_txn_current_lock(fs, pool), "", 1206 pool)); 1207 } 1208 1209 /* If our filesystem predates the existence of the 'txn-protorevs' 1210 dir, make that directory. */ 1211 if (format < SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT) 1212 { 1213 SVN_ERR(svn_io_make_dir_recursively( 1214 svn_fs_fs__path_txn_proto_revs(fs, pool), pool)); 1215 } 1216 1217 /* If our filesystem is new enough, write the min unpacked rev file. */ 1218 if (format < SVN_FS_FS__MIN_PACKED_FORMAT) 1219 SVN_ERR(svn_io_file_create(svn_fs_fs__path_min_unpacked_rev(fs, pool), 1220 "0\n", pool)); 1221 1222 /* If the file system supports revision packing but not revprop packing 1223 *and* the FS has been sharded, pack the revprops up to the point that 1224 revision data has been packed. However, keep the non-packed revprop 1225 files around until after the format bump */ 1226 if ( format >= SVN_FS_FS__MIN_PACKED_FORMAT 1227 && format < SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT 1228 && max_files_per_dir > 0) 1229 { 1230 needs_revprop_shard_cleanup = TRUE; 1231 SVN_ERR(svn_fs_fs__upgrade_pack_revprops(fs, 1232 upgrade_baton->notify_func, 1233 upgrade_baton->notify_baton, 1234 upgrade_baton->cancel_func, 1235 upgrade_baton->cancel_baton, 1236 pool)); 1237 } 1238 1239 /* We will need the UUID info shortly ... 1240 Read it before the format bump as the UUID file still uses the old 1241 format. */ 1242 SVN_ERR(read_uuid(fs, pool)); 1243 1244 /* Update the format info in the FS struct. Upgrade steps further 1245 down will use the format from FS to create missing info. */ 1246 ffd->format = SVN_FS_FS__FORMAT_NUMBER; 1247 ffd->max_files_per_dir = max_files_per_dir; 1248 ffd->use_log_addressing = use_log_addressing; 1249 1250 /* Always add / bump the instance ID such that no form of caching 1251 accidentally uses outdated information. Keep the UUID. */ 1252 SVN_ERR(svn_fs_fs__set_uuid(fs, fs->uuid, NULL, pool)); 1253 1254 /* Bump the format file. */ 1255 SVN_ERR(svn_fs_fs__write_format(fs, TRUE, pool)); 1256 1257 if (upgrade_baton->notify_func) 1258 SVN_ERR(upgrade_baton->notify_func(upgrade_baton->notify_baton, 1259 SVN_FS_FS__FORMAT_NUMBER, 1260 svn_fs_upgrade_format_bumped, 1261 pool)); 1262 1263 /* Now, it is safe to remove the redundant revprop files. */ 1264 if (needs_revprop_shard_cleanup) 1265 SVN_ERR(svn_fs_fs__upgrade_cleanup_pack_revprops(fs, 1266 upgrade_baton->notify_func, 1267 upgrade_baton->notify_baton, 1268 upgrade_baton->cancel_func, 1269 upgrade_baton->cancel_baton, 1270 pool)); 1271 1272 /* Done */ 1273 return SVN_NO_ERROR; 1274} 1275 1276 1277svn_error_t * 1278svn_fs_fs__upgrade(svn_fs_t *fs, 1279 svn_fs_upgrade_notify_t notify_func, 1280 void *notify_baton, 1281 svn_cancel_func_t cancel_func, 1282 void *cancel_baton, 1283 apr_pool_t *pool) 1284{ 1285 struct upgrade_baton_t baton; 1286 baton.fs = fs; 1287 baton.notify_func = notify_func; 1288 baton.notify_baton = notify_baton; 1289 baton.cancel_func = cancel_func; 1290 baton.cancel_baton = cancel_baton; 1291 1292 return svn_fs_fs__with_all_locks(fs, upgrade_body, (void *)&baton, pool); 1293} 1294 1295/* Find the youngest revision in a repository at path FS_PATH and 1296 return it in *YOUNGEST_P. Perform temporary allocations in 1297 POOL. */ 1298static svn_error_t * 1299get_youngest(svn_revnum_t *youngest_p, 1300 svn_fs_t *fs, 1301 apr_pool_t *pool) 1302{ 1303 apr_uint64_t dummy; 1304 SVN_ERR(svn_fs_fs__read_current(youngest_p, &dummy, &dummy, fs, pool)); 1305 return SVN_NO_ERROR; 1306} 1307 1308 1309svn_error_t * 1310svn_fs_fs__youngest_rev(svn_revnum_t *youngest_p, 1311 svn_fs_t *fs, 1312 apr_pool_t *pool) 1313{ 1314 fs_fs_data_t *ffd = fs->fsap_data; 1315 1316 SVN_ERR(get_youngest(youngest_p, fs, pool)); 1317 ffd->youngest_rev_cache = *youngest_p; 1318 1319 return SVN_NO_ERROR; 1320} 1321 1322int 1323svn_fs_fs__shard_size(svn_fs_t *fs) 1324{ 1325 fs_fs_data_t *ffd = fs->fsap_data; 1326 1327 return ffd->max_files_per_dir; 1328} 1329 1330svn_error_t * 1331svn_fs_fs__min_unpacked_rev(svn_revnum_t *min_unpacked, 1332 svn_fs_t *fs, 1333 apr_pool_t *pool) 1334{ 1335 fs_fs_data_t *ffd = fs->fsap_data; 1336 1337 SVN_ERR(svn_fs_fs__update_min_unpacked_rev(fs, pool)); 1338 *min_unpacked = ffd->min_unpacked_rev; 1339 1340 return SVN_NO_ERROR; 1341} 1342 1343svn_error_t * 1344svn_fs_fs__ensure_revision_exists(svn_revnum_t rev, 1345 svn_fs_t *fs, 1346 apr_pool_t *pool) 1347{ 1348 fs_fs_data_t *ffd = fs->fsap_data; 1349 1350 if (! SVN_IS_VALID_REVNUM(rev)) 1351 return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, 1352 _("Invalid revision number '%ld'"), rev); 1353 1354 1355 /* Did the revision exist the last time we checked the current 1356 file? */ 1357 if (rev <= ffd->youngest_rev_cache) 1358 return SVN_NO_ERROR; 1359 1360 SVN_ERR(get_youngest(&(ffd->youngest_rev_cache), fs, pool)); 1361 1362 /* Check again. */ 1363 if (rev <= ffd->youngest_rev_cache) 1364 return SVN_NO_ERROR; 1365 1366 return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, 1367 _("No such revision %ld"), rev); 1368} 1369 1370svn_error_t * 1371svn_fs_fs__file_length(svn_filesize_t *length, 1372 node_revision_t *noderev, 1373 apr_pool_t *pool) 1374{ 1375 representation_t *data_rep = noderev->data_rep; 1376 if (!data_rep) 1377 { 1378 /* Treat "no representation" as "empty file". */ 1379 *length = 0; 1380 } 1381 else if (data_rep->expanded_size) 1382 { 1383 /* Standard case: a non-empty file. */ 1384 *length = data_rep->expanded_size; 1385 } 1386 else 1387 { 1388 /* Work around a FSFS format quirk (see issue #4554). 1389 1390 A plain representation may specify its EXPANDED LENGTH as "0" 1391 in which case, the SIZE value is what we want. 1392 1393 Because EXPANDED_LENGTH will also be 0 for empty files, while 1394 SIZE is non-null, we need to check wether the content is 1395 actually empty. We simply compare with the MD5 checksum of 1396 empty content (sha-1 is not always available). 1397 */ 1398 svn_checksum_t *empty_md5 1399 = svn_checksum_empty_checksum(svn_checksum_md5, pool); 1400 1401 if (memcmp(empty_md5->digest, data_rep->md5_digest, 1402 sizeof(data_rep->md5_digest))) 1403 { 1404 /* Contents is not empty, i.e. EXPANDED_LENGTH cannot be the 1405 actual file length. */ 1406 *length = data_rep->size; 1407 } 1408 else 1409 { 1410 /* Contents is empty. */ 1411 *length = 0; 1412 } 1413 } 1414 1415 return SVN_NO_ERROR; 1416} 1417 1418svn_boolean_t 1419svn_fs_fs__noderev_same_rep_key(representation_t *a, 1420 representation_t *b) 1421{ 1422 if (a == b) 1423 return TRUE; 1424 1425 if (a == NULL || b == NULL) 1426 return FALSE; 1427 1428 if (a->item_index != b->item_index) 1429 return FALSE; 1430 1431 if (a->revision != b->revision) 1432 return FALSE; 1433 1434 return memcmp(&a->uniquifier, &b->uniquifier, sizeof(a->uniquifier)) == 0; 1435} 1436 1437svn_error_t * 1438svn_fs_fs__file_text_rep_equal(svn_boolean_t *equal, 1439 svn_fs_t *fs, 1440 node_revision_t *a, 1441 node_revision_t *b, 1442 apr_pool_t *scratch_pool) 1443{ 1444 svn_stream_t *contents_a, *contents_b; 1445 representation_t *rep_a = a->data_rep; 1446 representation_t *rep_b = b->data_rep; 1447 svn_boolean_t a_empty = !rep_a; 1448 svn_boolean_t b_empty = !rep_b; 1449 1450 /* This makes sure that neither rep will be NULL later on */ 1451 if (a_empty && b_empty) 1452 { 1453 *equal = TRUE; 1454 return SVN_NO_ERROR; 1455 } 1456 1457 /* Same path in same rev or txn? */ 1458 if (svn_fs_fs__id_eq(a->id, b->id)) 1459 { 1460 *equal = TRUE; 1461 return SVN_NO_ERROR; 1462 } 1463 1464 /* Beware of the combination NULL rep and possibly empty rep. 1465 * Due to EXPANDED_SIZE not being reliable, we can't easily detect empty 1466 * reps. So, we can only take further shortcuts if both reps are given. */ 1467 if (!a_empty && !b_empty) 1468 { 1469 /* File text representations always know their checksums - 1470 * even in a txn. */ 1471 if (memcmp(rep_a->md5_digest, rep_b->md5_digest, 1472 sizeof(rep_a->md5_digest))) 1473 { 1474 *equal = FALSE; 1475 return SVN_NO_ERROR; 1476 } 1477 1478 /* Paranoia. Compare SHA1 checksums because that's the level of 1479 confidence we require for e.g. the working copy. */ 1480 if (rep_a->has_sha1 && rep_b->has_sha1) 1481 { 1482 *equal = memcmp(rep_a->sha1_digest, rep_b->sha1_digest, 1483 sizeof(rep_a->sha1_digest)) == 0; 1484 return SVN_NO_ERROR; 1485 } 1486 } 1487 1488 SVN_ERR(svn_fs_fs__get_contents(&contents_a, fs, rep_a, TRUE, 1489 scratch_pool)); 1490 SVN_ERR(svn_fs_fs__get_contents(&contents_b, fs, rep_b, TRUE, 1491 scratch_pool)); 1492 SVN_ERR(svn_stream_contents_same2(equal, contents_a, contents_b, 1493 scratch_pool)); 1494 1495 return SVN_NO_ERROR; 1496} 1497 1498svn_error_t * 1499svn_fs_fs__prop_rep_equal(svn_boolean_t *equal, 1500 svn_fs_t *fs, 1501 node_revision_t *a, 1502 node_revision_t *b, 1503 apr_pool_t *scratch_pool) 1504{ 1505 representation_t *rep_a = a->prop_rep; 1506 representation_t *rep_b = b->prop_rep; 1507 apr_hash_t *proplist_a; 1508 apr_hash_t *proplist_b; 1509 1510 /* Mainly for a==b==NULL */ 1511 if (rep_a == rep_b) 1512 { 1513 *equal = TRUE; 1514 return SVN_NO_ERROR; 1515 } 1516 1517 /* Committed property lists can be compared quickly */ 1518 if ( rep_a && rep_b 1519 && !svn_fs_fs__id_txn_used(&rep_a->txn_id) 1520 && !svn_fs_fs__id_txn_used(&rep_b->txn_id)) 1521 { 1522 /* MD5 must be given. Having the same checksum is good enough for 1523 accepting the prop lists as equal. */ 1524 *equal = memcmp(rep_a->md5_digest, rep_b->md5_digest, 1525 sizeof(rep_a->md5_digest)) == 0; 1526 return SVN_NO_ERROR; 1527 } 1528 1529 /* Same path in same txn? */ 1530 if (svn_fs_fs__id_eq(a->id, b->id)) 1531 { 1532 *equal = TRUE; 1533 return SVN_NO_ERROR; 1534 } 1535 1536 /* At least one of the reps has been modified in a txn. 1537 Fetch and compare them. */ 1538 SVN_ERR(svn_fs_fs__get_proplist(&proplist_a, fs, a, scratch_pool)); 1539 SVN_ERR(svn_fs_fs__get_proplist(&proplist_b, fs, b, scratch_pool)); 1540 1541 *equal = svn_fs__prop_lists_equal(proplist_a, proplist_b, scratch_pool); 1542 return SVN_NO_ERROR; 1543} 1544 1545 1546svn_error_t * 1547svn_fs_fs__file_checksum(svn_checksum_t **checksum, 1548 node_revision_t *noderev, 1549 svn_checksum_kind_t kind, 1550 apr_pool_t *pool) 1551{ 1552 *checksum = NULL; 1553 1554 if (noderev->data_rep) 1555 { 1556 svn_checksum_t temp; 1557 temp.kind = kind; 1558 1559 switch(kind) 1560 { 1561 case svn_checksum_md5: 1562 temp.digest = noderev->data_rep->md5_digest; 1563 break; 1564 1565 case svn_checksum_sha1: 1566 if (! noderev->data_rep->has_sha1) 1567 return SVN_NO_ERROR; 1568 1569 temp.digest = noderev->data_rep->sha1_digest; 1570 break; 1571 1572 default: 1573 return SVN_NO_ERROR; 1574 } 1575 1576 *checksum = svn_checksum_dup(&temp, pool); 1577 } 1578 1579 return SVN_NO_ERROR; 1580} 1581 1582representation_t * 1583svn_fs_fs__rep_copy(representation_t *rep, 1584 apr_pool_t *pool) 1585{ 1586 if (rep == NULL) 1587 return NULL; 1588 1589 return apr_pmemdup(pool, rep, sizeof(*rep)); 1590} 1591 1592 1593/* Write out the zeroth revision for filesystem FS. 1594 Perform temporary allocations in SCRATCH_POOL. */ 1595static svn_error_t * 1596write_revision_zero(svn_fs_t *fs, 1597 apr_pool_t *scratch_pool) 1598{ 1599 /* Use an explicit sub-pool to have full control over temp file lifetimes. 1600 * Since we have it, use it for everything else as well. */ 1601 apr_pool_t *subpool = svn_pool_create(scratch_pool); 1602 const char *path_revision_zero = svn_fs_fs__path_rev(fs, 0, subpool); 1603 apr_hash_t *proplist; 1604 svn_string_t date; 1605 1606 /* Write out a rev file for revision 0. */ 1607 if (svn_fs_fs__use_log_addressing(fs)) 1608 { 1609 apr_array_header_t *index_entries; 1610 svn_fs_fs__p2l_entry_t *entry; 1611 svn_fs_fs__revision_file_t *rev_file; 1612 const char *l2p_proto_index, *p2l_proto_index; 1613 1614 /* Write a skeleton r0 with no indexes. */ 1615 SVN_ERR(svn_io_file_create(path_revision_zero, 1616 "PLAIN\nEND\nENDREP\n" 1617 "id: 0.0.r0/2\n" 1618 "type: dir\n" 1619 "count: 0\n" 1620 "text: 0 3 4 4 " 1621 "2d2977d1c96f487abe4a1e202dd03b4e\n" 1622 "cpath: /\n" 1623 "\n\n", subpool)); 1624 1625 /* Construct the index P2L contents: describe the 3 items we have. 1626 Be sure to create them in on-disk order. */ 1627 index_entries = apr_array_make(subpool, 3, sizeof(entry)); 1628 1629 entry = apr_pcalloc(subpool, sizeof(*entry)); 1630 entry->offset = 0; 1631 entry->size = 17; 1632 entry->type = SVN_FS_FS__ITEM_TYPE_DIR_REP; 1633 entry->item.revision = 0; 1634 entry->item.number = SVN_FS_FS__ITEM_INDEX_FIRST_USER; 1635 APR_ARRAY_PUSH(index_entries, svn_fs_fs__p2l_entry_t *) = entry; 1636 1637 entry = apr_pcalloc(subpool, sizeof(*entry)); 1638 entry->offset = 17; 1639 entry->size = 89; 1640 entry->type = SVN_FS_FS__ITEM_TYPE_NODEREV; 1641 entry->item.revision = 0; 1642 entry->item.number = SVN_FS_FS__ITEM_INDEX_ROOT_NODE; 1643 APR_ARRAY_PUSH(index_entries, svn_fs_fs__p2l_entry_t *) = entry; 1644 1645 entry = apr_pcalloc(subpool, sizeof(*entry)); 1646 entry->offset = 106; 1647 entry->size = 1; 1648 entry->type = SVN_FS_FS__ITEM_TYPE_CHANGES; 1649 entry->item.revision = 0; 1650 entry->item.number = SVN_FS_FS__ITEM_INDEX_CHANGES; 1651 APR_ARRAY_PUSH(index_entries, svn_fs_fs__p2l_entry_t *) = entry; 1652 1653 /* Now re-open r0, create proto-index files from our entries and 1654 rewrite the index section of r0. */ 1655 SVN_ERR(svn_fs_fs__open_pack_or_rev_file_writable(&rev_file, fs, 0, 1656 subpool, subpool)); 1657 SVN_ERR(svn_fs_fs__p2l_index_from_p2l_entries(&p2l_proto_index, fs, 1658 rev_file, index_entries, 1659 subpool, subpool)); 1660 SVN_ERR(svn_fs_fs__l2p_index_from_p2l_entries(&l2p_proto_index, fs, 1661 index_entries, 1662 subpool, subpool)); 1663 SVN_ERR(svn_fs_fs__add_index_data(fs, rev_file->file, l2p_proto_index, 1664 p2l_proto_index, 0, subpool)); 1665 SVN_ERR(svn_fs_fs__close_revision_file(rev_file)); 1666 } 1667 else 1668 SVN_ERR(svn_io_file_create(path_revision_zero, 1669 "PLAIN\nEND\nENDREP\n" 1670 "id: 0.0.r0/17\n" 1671 "type: dir\n" 1672 "count: 0\n" 1673 "text: 0 0 4 4 " 1674 "2d2977d1c96f487abe4a1e202dd03b4e\n" 1675 "cpath: /\n" 1676 "\n\n17 107\n", subpool)); 1677 1678 SVN_ERR(svn_io_set_file_read_only(path_revision_zero, FALSE, subpool)); 1679 1680 /* Set a date on revision 0. */ 1681 date.data = svn_time_to_cstring(apr_time_now(), subpool); 1682 date.len = strlen(date.data); 1683 proplist = apr_hash_make(subpool); 1684 svn_hash_sets(proplist, SVN_PROP_REVISION_DATE, &date); 1685 SVN_ERR(svn_fs_fs__set_revision_proplist(fs, 0, proplist, subpool)); 1686 1687 svn_pool_destroy(subpool); 1688 return SVN_NO_ERROR; 1689} 1690 1691svn_error_t * 1692svn_fs_fs__create_file_tree(svn_fs_t *fs, 1693 const char *path, 1694 int format, 1695 int shard_size, 1696 svn_boolean_t use_log_addressing, 1697 apr_pool_t *pool) 1698{ 1699 fs_fs_data_t *ffd = fs->fsap_data; 1700 1701 fs->path = apr_pstrdup(fs->pool, path); 1702 ffd->format = format; 1703 1704 /* Use an appropriate sharding mode if supported by the format. */ 1705 if (format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT) 1706 ffd->max_files_per_dir = shard_size; 1707 else 1708 ffd->max_files_per_dir = 0; 1709 1710 /* Select the addressing mode depending on the format. */ 1711 if (format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT) 1712 ffd->use_log_addressing = use_log_addressing; 1713 else 1714 ffd->use_log_addressing = FALSE; 1715 1716 /* Create the revision data directories. */ 1717 if (ffd->max_files_per_dir) 1718 SVN_ERR(svn_io_make_dir_recursively(svn_fs_fs__path_rev_shard(fs, 0, 1719 pool), 1720 pool)); 1721 else 1722 SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path, PATH_REVS_DIR, 1723 pool), 1724 pool)); 1725 1726 /* Create the revprops directory. */ 1727 if (ffd->max_files_per_dir) 1728 SVN_ERR(svn_io_make_dir_recursively(svn_fs_fs__path_revprops_shard(fs, 0, 1729 pool), 1730 pool)); 1731 else 1732 SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path, 1733 PATH_REVPROPS_DIR, 1734 pool), 1735 pool)); 1736 1737 /* Create the transaction directory. */ 1738 SVN_ERR(svn_io_make_dir_recursively(svn_fs_fs__path_txns_dir(fs, pool), 1739 pool)); 1740 1741 /* Create the protorevs directory. */ 1742 if (format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT) 1743 SVN_ERR(svn_io_make_dir_recursively(svn_fs_fs__path_txn_proto_revs(fs, 1744 pool), 1745 pool)); 1746 1747 /* Create the 'current' file. */ 1748 SVN_ERR(svn_io_file_create_empty(svn_fs_fs__path_current(fs, pool), pool)); 1749 SVN_ERR(svn_fs_fs__write_current(fs, 0, 1, 1, pool)); 1750 1751 /* Create the 'uuid' file. */ 1752 SVN_ERR(svn_io_file_create_empty(svn_fs_fs__path_lock(fs, pool), pool)); 1753 SVN_ERR(svn_fs_fs__set_uuid(fs, NULL, NULL, pool)); 1754 1755 /* Create the fsfs.conf file if supported. Older server versions would 1756 simply ignore the file but that might result in a different behavior 1757 than with the later releases. Also, hotcopy would ignore, i.e. not 1758 copy, a fsfs.conf with old formats. */ 1759 if (ffd->format >= SVN_FS_FS__MIN_CONFIG_FILE) 1760 SVN_ERR(write_config(fs, pool)); 1761 1762 SVN_ERR(read_config(ffd, fs->path, fs->pool, pool)); 1763 1764 /* Global configuration options. */ 1765 SVN_ERR(read_global_config(fs)); 1766 1767 /* Add revision 0. */ 1768 SVN_ERR(write_revision_zero(fs, pool)); 1769 1770 /* Create the min unpacked rev file. */ 1771 if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT) 1772 SVN_ERR(svn_io_file_create(svn_fs_fs__path_min_unpacked_rev(fs, pool), 1773 "0\n", pool)); 1774 1775 /* Create the txn-current file if the repository supports 1776 the transaction sequence file. */ 1777 if (format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT) 1778 { 1779 SVN_ERR(svn_io_file_create(svn_fs_fs__path_txn_current(fs, pool), 1780 "0\n", pool)); 1781 SVN_ERR(svn_io_file_create_empty( 1782 svn_fs_fs__path_txn_current_lock(fs, pool), 1783 pool)); 1784 } 1785 1786 ffd->youngest_rev_cache = 0; 1787 return SVN_NO_ERROR; 1788} 1789 1790svn_error_t * 1791svn_fs_fs__create(svn_fs_t *fs, 1792 const char *path, 1793 apr_pool_t *pool) 1794{ 1795 int format = SVN_FS_FS__FORMAT_NUMBER; 1796 int shard_size = SVN_FS_FS_DEFAULT_MAX_FILES_PER_DIR; 1797 svn_boolean_t log_addressing; 1798 1799 /* Process the given filesystem config. */ 1800 if (fs->config) 1801 { 1802 svn_version_t *compatible_version; 1803 const char *shard_size_str; 1804 SVN_ERR(svn_fs__compatible_version(&compatible_version, fs->config, 1805 pool)); 1806 1807 /* select format number */ 1808 switch(compatible_version->minor) 1809 { 1810 case 0: return svn_error_create(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL, 1811 _("FSFS is not compatible with Subversion prior to 1.1")); 1812 1813 case 1: 1814 case 2: 1815 case 3: format = 1; 1816 break; 1817 1818 case 4: format = 2; 1819 break; 1820 1821 case 5: format = 3; 1822 break; 1823 1824 case 6: 1825 case 7: format = 4; 1826 break; 1827 1828 case 8: format = 6; 1829 break; 1830 1831 default:format = SVN_FS_FS__FORMAT_NUMBER; 1832 } 1833 1834 shard_size_str = svn_hash_gets(fs->config, SVN_FS_CONFIG_FSFS_SHARD_SIZE); 1835 if (shard_size_str) 1836 { 1837 apr_int64_t val; 1838 SVN_ERR(svn_cstring_strtoi64(&val, shard_size_str, 0, 1839 APR_INT32_MAX, 10)); 1840 1841 shard_size = (int) val; 1842 } 1843 } 1844 1845 log_addressing = svn_hash__get_bool(fs->config, 1846 SVN_FS_CONFIG_FSFS_LOG_ADDRESSING, 1847 TRUE); 1848 1849 /* Actual FS creation. */ 1850 SVN_ERR(svn_fs_fs__create_file_tree(fs, path, format, shard_size, 1851 log_addressing, pool)); 1852 1853 /* This filesystem is ready. Stamp it with a format number. */ 1854 SVN_ERR(svn_fs_fs__write_format(fs, FALSE, pool)); 1855 1856 return SVN_NO_ERROR; 1857} 1858 1859svn_error_t * 1860svn_fs_fs__set_uuid(svn_fs_t *fs, 1861 const char *uuid, 1862 const char *instance_id, 1863 apr_pool_t *pool) 1864{ 1865 fs_fs_data_t *ffd = fs->fsap_data; 1866 const char *uuid_path = path_uuid(fs, pool); 1867 svn_stringbuf_t *contents = svn_stringbuf_create_empty(pool); 1868 1869 if (! uuid) 1870 uuid = svn_uuid_generate(pool); 1871 1872 if (! instance_id) 1873 instance_id = svn_uuid_generate(pool); 1874 1875 svn_stringbuf_appendcstr(contents, uuid); 1876 svn_stringbuf_appendcstr(contents, "\n"); 1877 1878 if (ffd->format >= SVN_FS_FS__MIN_INSTANCE_ID_FORMAT) 1879 { 1880 svn_stringbuf_appendcstr(contents, instance_id); 1881 svn_stringbuf_appendcstr(contents, "\n"); 1882 } 1883 1884 /* We use the permissions of the 'current' file, because the 'uuid' 1885 file does not exist during repository creation. */ 1886 SVN_ERR(svn_io_write_atomic(uuid_path, contents->data, contents->len, 1887 svn_fs_fs__path_current(fs, pool) /* perms */, 1888 pool)); 1889 1890 fs->uuid = apr_pstrdup(fs->pool, uuid); 1891 1892 if (ffd->format >= SVN_FS_FS__MIN_INSTANCE_ID_FORMAT) 1893 ffd->instance_id = apr_pstrdup(fs->pool, instance_id); 1894 else 1895 ffd->instance_id = fs->uuid; 1896 1897 return SVN_NO_ERROR; 1898} 1899 1900/** Node origin lazy cache. */ 1901 1902/* If directory PATH does not exist, create it and give it the same 1903 permissions as FS_path.*/ 1904svn_error_t * 1905svn_fs_fs__ensure_dir_exists(const char *path, 1906 const char *fs_path, 1907 apr_pool_t *pool) 1908{ 1909 svn_error_t *err = svn_io_dir_make(path, APR_OS_DEFAULT, pool); 1910 if (err && APR_STATUS_IS_EEXIST(err->apr_err)) 1911 { 1912 svn_error_clear(err); 1913 return SVN_NO_ERROR; 1914 } 1915 SVN_ERR(err); 1916 1917 /* We successfully created a new directory. Dup the permissions 1918 from FS->path. */ 1919 return svn_io_copy_perms(fs_path, path, pool); 1920} 1921 1922/* Set *NODE_ORIGINS to a hash mapping 'const char *' node IDs to 1923 'svn_string_t *' node revision IDs. Use POOL for allocations. */ 1924static svn_error_t * 1925get_node_origins_from_file(svn_fs_t *fs, 1926 apr_hash_t **node_origins, 1927 const char *node_origins_file, 1928 apr_pool_t *pool) 1929{ 1930 apr_file_t *fd; 1931 svn_error_t *err; 1932 svn_stream_t *stream; 1933 1934 *node_origins = NULL; 1935 err = svn_io_file_open(&fd, node_origins_file, 1936 APR_READ, APR_OS_DEFAULT, pool); 1937 if (err && APR_STATUS_IS_ENOENT(err->apr_err)) 1938 { 1939 svn_error_clear(err); 1940 return SVN_NO_ERROR; 1941 } 1942 SVN_ERR(err); 1943 1944 stream = svn_stream_from_aprfile2(fd, FALSE, pool); 1945 *node_origins = apr_hash_make(pool); 1946 err = svn_hash_read2(*node_origins, stream, SVN_HASH_TERMINATOR, pool); 1947 if (err) 1948 return svn_error_quick_wrapf(err, _("malformed node origin data in '%s'"), 1949 node_origins_file); 1950 return svn_stream_close(stream); 1951} 1952 1953svn_error_t * 1954svn_fs_fs__get_node_origin(const svn_fs_id_t **origin_id, 1955 svn_fs_t *fs, 1956 const svn_fs_fs__id_part_t *node_id, 1957 apr_pool_t *pool) 1958{ 1959 apr_hash_t *node_origins; 1960 1961 *origin_id = NULL; 1962 SVN_ERR(get_node_origins_from_file(fs, &node_origins, 1963 svn_fs_fs__path_node_origin(fs, node_id, 1964 pool), 1965 pool)); 1966 if (node_origins) 1967 { 1968 char node_id_ptr[SVN_INT64_BUFFER_SIZE]; 1969 apr_size_t len = svn__ui64tobase36(node_id_ptr, node_id->number); 1970 svn_string_t *origin_id_str 1971 = apr_hash_get(node_origins, node_id_ptr, len); 1972 1973 if (origin_id_str) 1974 SVN_ERR(svn_fs_fs__id_parse(origin_id, 1975 apr_pstrdup(pool, origin_id_str->data), 1976 pool)); 1977 } 1978 return SVN_NO_ERROR; 1979} 1980 1981 1982/* Helper for svn_fs_fs__set_node_origin. Takes a NODE_ID/NODE_REV_ID 1983 pair and adds it to the NODE_ORIGINS_PATH file. */ 1984static svn_error_t * 1985set_node_origins_for_file(svn_fs_t *fs, 1986 const char *node_origins_path, 1987 const svn_fs_fs__id_part_t *node_id, 1988 svn_string_t *node_rev_id, 1989 apr_pool_t *pool) 1990{ 1991 const char *path_tmp; 1992 svn_stream_t *stream; 1993 apr_hash_t *origins_hash; 1994 svn_string_t *old_node_rev_id; 1995 1996 /* the hash serialization functions require strings as keys */ 1997 char node_id_ptr[SVN_INT64_BUFFER_SIZE]; 1998 apr_size_t len = svn__ui64tobase36(node_id_ptr, node_id->number); 1999 2000 SVN_ERR(svn_fs_fs__ensure_dir_exists(svn_dirent_join(fs->path, 2001 PATH_NODE_ORIGINS_DIR, 2002 pool), 2003 fs->path, pool)); 2004 2005 /* Read the previously existing origins (if any), and merge our 2006 update with it. */ 2007 SVN_ERR(get_node_origins_from_file(fs, &origins_hash, 2008 node_origins_path, pool)); 2009 if (! origins_hash) 2010 origins_hash = apr_hash_make(pool); 2011 2012 old_node_rev_id = apr_hash_get(origins_hash, node_id_ptr, len); 2013 2014 if (old_node_rev_id && !svn_string_compare(node_rev_id, old_node_rev_id)) 2015 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 2016 _("Node origin for '%s' exists with a different " 2017 "value (%s) than what we were about to store " 2018 "(%s)"), 2019 node_id_ptr, old_node_rev_id->data, 2020 node_rev_id->data); 2021 2022 apr_hash_set(origins_hash, node_id_ptr, len, node_rev_id); 2023 2024 /* Sure, there's a race condition here. Two processes could be 2025 trying to add different cache elements to the same file at the 2026 same time, and the entries added by the first one to write will 2027 be lost. But this is just a cache of reconstructible data, so 2028 we'll accept this problem in return for not having to deal with 2029 locking overhead. */ 2030 2031 /* Create a temporary file, write out our hash, and close the file. */ 2032 SVN_ERR(svn_stream_open_unique(&stream, &path_tmp, 2033 svn_dirent_dirname(node_origins_path, pool), 2034 svn_io_file_del_none, pool, pool)); 2035 SVN_ERR(svn_hash_write2(origins_hash, stream, SVN_HASH_TERMINATOR, pool)); 2036 SVN_ERR(svn_stream_close(stream)); 2037 2038 /* Rename the temp file as the real destination */ 2039 return svn_io_file_rename(path_tmp, node_origins_path, pool); 2040} 2041 2042 2043svn_error_t * 2044svn_fs_fs__set_node_origin(svn_fs_t *fs, 2045 const svn_fs_fs__id_part_t *node_id, 2046 const svn_fs_id_t *node_rev_id, 2047 apr_pool_t *pool) 2048{ 2049 svn_error_t *err; 2050 const char *filename = svn_fs_fs__path_node_origin(fs, node_id, pool); 2051 2052 err = set_node_origins_for_file(fs, filename, 2053 node_id, 2054 svn_fs_fs__id_unparse(node_rev_id, pool), 2055 pool); 2056 if (err && APR_STATUS_IS_EACCES(err->apr_err)) 2057 { 2058 /* It's just a cache; stop trying if I can't write. */ 2059 svn_error_clear(err); 2060 err = NULL; 2061 } 2062 return svn_error_trace(err); 2063} 2064 2065 2066 2067/*** Revisions ***/ 2068 2069svn_error_t * 2070svn_fs_fs__revision_prop(svn_string_t **value_p, 2071 svn_fs_t *fs, 2072 svn_revnum_t rev, 2073 const char *propname, 2074 apr_pool_t *pool) 2075{ 2076 apr_hash_t *table; 2077 2078 SVN_ERR(svn_fs__check_fs(fs, TRUE)); 2079 SVN_ERR(svn_fs_fs__get_revision_proplist(&table, fs, rev, pool)); 2080 2081 *value_p = svn_hash_gets(table, propname); 2082 2083 return SVN_NO_ERROR; 2084} 2085 2086 2087/* Baton used for change_rev_prop_body below. */ 2088struct change_rev_prop_baton { 2089 svn_fs_t *fs; 2090 svn_revnum_t rev; 2091 const char *name; 2092 const svn_string_t *const *old_value_p; 2093 const svn_string_t *value; 2094}; 2095 2096/* The work-horse for svn_fs_fs__change_rev_prop, called with the FS 2097 write lock. This implements the svn_fs_fs__with_write_lock() 2098 'body' callback type. BATON is a 'struct change_rev_prop_baton *'. */ 2099static svn_error_t * 2100change_rev_prop_body(void *baton, apr_pool_t *pool) 2101{ 2102 struct change_rev_prop_baton *cb = baton; 2103 apr_hash_t *table; 2104 2105 SVN_ERR(svn_fs_fs__get_revision_proplist(&table, cb->fs, cb->rev, pool)); 2106 2107 if (cb->old_value_p) 2108 { 2109 const svn_string_t *wanted_value = *cb->old_value_p; 2110 const svn_string_t *present_value = svn_hash_gets(table, cb->name); 2111 if ((!wanted_value != !present_value) 2112 || (wanted_value && present_value 2113 && !svn_string_compare(wanted_value, present_value))) 2114 { 2115 /* What we expected isn't what we found. */ 2116 return svn_error_createf(SVN_ERR_FS_PROP_BASEVALUE_MISMATCH, NULL, 2117 _("revprop '%s' has unexpected value in " 2118 "filesystem"), 2119 cb->name); 2120 } 2121 /* Fall through. */ 2122 } 2123 svn_hash_sets(table, cb->name, cb->value); 2124 2125 return svn_fs_fs__set_revision_proplist(cb->fs, cb->rev, table, pool); 2126} 2127 2128svn_error_t * 2129svn_fs_fs__change_rev_prop(svn_fs_t *fs, 2130 svn_revnum_t rev, 2131 const char *name, 2132 const svn_string_t *const *old_value_p, 2133 const svn_string_t *value, 2134 apr_pool_t *pool) 2135{ 2136 struct change_rev_prop_baton cb; 2137 2138 SVN_ERR(svn_fs__check_fs(fs, TRUE)); 2139 2140 cb.fs = fs; 2141 cb.rev = rev; 2142 cb.name = name; 2143 cb.old_value_p = old_value_p; 2144 cb.value = value; 2145 2146 return svn_fs_fs__with_write_lock(fs, change_rev_prop_body, &cb, pool); 2147} 2148 2149 2150svn_error_t * 2151svn_fs_fs__info_format(int *fs_format, 2152 svn_version_t **supports_version, 2153 svn_fs_t *fs, 2154 apr_pool_t *result_pool, 2155 apr_pool_t *scratch_pool) 2156{ 2157 fs_fs_data_t *ffd = fs->fsap_data; 2158 *fs_format = ffd->format; 2159 *supports_version = apr_palloc(result_pool, sizeof(svn_version_t)); 2160 2161 (*supports_version)->major = SVN_VER_MAJOR; 2162 (*supports_version)->minor = 1; 2163 (*supports_version)->patch = 0; 2164 (*supports_version)->tag = ""; 2165 2166 switch (ffd->format) 2167 { 2168 case 1: 2169 break; 2170 case 2: 2171 (*supports_version)->minor = 4; 2172 break; 2173 case 3: 2174 (*supports_version)->minor = 5; 2175 break; 2176 case 4: 2177 (*supports_version)->minor = 6; 2178 break; 2179 case 6: 2180 (*supports_version)->minor = 8; 2181 break; 2182 case 7: 2183 (*supports_version)->minor = 9; 2184 break; 2185#ifdef SVN_DEBUG 2186# if SVN_FS_FS__FORMAT_NUMBER != 7 2187# error "Need to add a 'case' statement here" 2188# endif 2189#endif 2190 } 2191 2192 return SVN_NO_ERROR; 2193} 2194 2195svn_error_t * 2196svn_fs_fs__info_config_files(apr_array_header_t **files, 2197 svn_fs_t *fs, 2198 apr_pool_t *result_pool, 2199 apr_pool_t *scratch_pool) 2200{ 2201 *files = apr_array_make(result_pool, 1, sizeof(const char *)); 2202 APR_ARRAY_PUSH(*files, const char *) = svn_dirent_join(fs->path, PATH_CONFIG, 2203 result_pool); 2204 return SVN_NO_ERROR; 2205} 2206