1 /* fs_fs.c --- filesystem operations specific to fs_fs
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
20 * ====================================================================
27 #include "svn_private_config.h"
29 #include "svn_checksum.h"
31 #include "svn_props.h"
33 #include "svn_dirent_uri.h"
34 #include "svn_sorts.h"
35 #include "svn_version.h"
37 #include "cached_data.h"
40 #include "rep-cache.h"
42 #include "transaction.h"
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"
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
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
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
77 To avoid opening and closing the rev-files all the time, it would
78 probably be advantageous to keep each rev-file open for the
79 lifetime of the transaction object. I'll leave that as a later
82 I didn't keep track of pool lifetimes at all in this code. There
83 are likely some errors because of that.
90 get_youngest(svn_revnum_t *youngest_p, svn_fs_t *fs, apr_pool_t *pool);
92 /* Pathname helper functions */
95 path_format(svn_fs_t *fs, apr_pool_t *pool)
97 return svn_dirent_join(fs->path, PATH_FORMAT, pool);
100 static APR_INLINE const char *
101 path_uuid(svn_fs_t *fs, apr_pool_t *pool)
103 return svn_dirent_join(fs->path, PATH_UUID, pool);
107 svn_fs_fs__path_current(svn_fs_t *fs, apr_pool_t *pool)
109 return svn_dirent_join(fs->path, PATH_CURRENT, pool);
114 /* Get a lock on empty file LOCK_FILENAME, creating it in POOL. */
116 get_lock_on_filesystem(const char *lock_filename,
119 return svn_error_trace(svn_io__file_lock_autocreate(lock_filename, pool));
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. */
126 reset_lock_flag(void *baton_void)
128 fs_fs_data_t *ffd = baton_void;
129 ffd->has_write_lock = FALSE;
133 /* Structure defining a file system lock to be acquired and the function
134 to be executed while the lock is held.
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.
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
147 typedef struct with_lock_baton_t
149 /* The filesystem we operate on. Same for all instances along the chain. */
152 /* Mutex to complement the lock file in an APR threaded process.
153 No-op object for non-threaded processes but never NULL. */
156 /* Path to the file to lock. */
157 const char *lock_path;
159 /* If true, set FS->HAS_WRITE_LOCK after we acquired the lock. */
160 svn_boolean_t is_global_lock;
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,
167 /* Baton to pass to BODY; possibly NULL.
168 This may be user-provided or a nested lock baton instance. */
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;
177 /* TRUE, iff BODY is the user-provided body. */
178 svn_boolean_t is_inner_most_lock;
180 /* TRUE, iff this is not a nested lock.
181 Then responsible for destroying LOCK_POOL. */
182 svn_boolean_t is_outer_most_lock;
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. */
190 with_some_lock_file(with_lock_baton_t *baton)
192 apr_pool_t *pool = baton->lock_pool;
193 svn_error_t *err = get_lock_on_filesystem(baton->lock_path, pool);
197 svn_fs_t *fs = baton->fs;
198 fs_fs_data_t *ffd = fs->fsap_data;
200 if (baton->is_global_lock)
202 /* set the "got the lock" flag and register reset function */
203 apr_pool_cleanup_register(pool,
206 apr_pool_cleanup_null);
207 ffd->has_write_lock = TRUE;
210 /* nobody else will modify the repo state
211 => read HEAD & pack info once */
212 if (baton->is_inner_most_lock)
214 if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
215 err = svn_fs_fs__update_min_unpacked_rev(fs, pool);
217 err = get_youngest(&ffd->youngest_rev_cache, fs, pool);
221 err = baton->body(baton->baton, pool);
224 if (baton->is_outer_most_lock)
225 svn_pool_destroy(pool);
227 return svn_error_trace(err);
230 /* Wraps with_some_lock_file, protecting it with BATON->MUTEX.
232 POOL is unused here and only provided for signature compatibility with
233 WITH_LOCK_BATON_T.BODY. */
235 with_lock(void *baton,
238 with_lock_baton_t *lock_baton = baton;
239 SVN_MUTEX__WITH_LOCK(lock_baton->mutex, with_some_lock_file(lock_baton));
244 /* Enum identifying a filesystem lock. */
245 typedef enum lock_id_t
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
256 init_lock_baton(with_lock_baton_t *baton,
259 fs_fs_data_t *ffd = baton->fs->fsap_data;
260 fs_fs_shared_data_t *ffsd = ffd->shared;
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;
271 baton->mutex = ffsd->txn_current_lock;
272 baton->lock_path = svn_fs_fs__path_txn_current_lock(baton->fs,
274 baton->is_global_lock = FALSE;
278 baton->mutex = ffsd->fs_pack_lock;
279 baton->lock_path = svn_fs_fs__path_pack_lock(baton->fs,
281 baton->is_global_lock = FALSE;
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.
290 static with_lock_baton_t *
291 create_lock_baton(svn_fs_t *fs,
293 svn_error_t *(*body)(void *baton,
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));
303 /* Store parameters. */
306 result->baton = baton;
308 /* File locks etc. will use this pool as well for easy cleanup. */
309 result->lock_pool = lock_pool;
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;
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);
322 /* Return a baton that wraps NESTED and requests LOCK_ID as additional lock.
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.
327 static with_lock_baton_t *
328 chain_lock_baton(lock_id_t lock_id,
329 with_lock_baton_t *nested)
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));
335 /* All locks along the chain operate on the same FS. */
336 result->fs = nested->fs;
338 /* Execution of this baton means acquiring the nested lock and its
340 result->body = with_lock;
341 result->baton = nested;
343 /* Shared among all locks along the chain. */
344 result->lock_pool = lock_pool;
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;
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);
359 svn_fs_fs__with_write_lock(svn_fs_t *fs,
360 svn_error_t *(*body)(void *baton,
365 return svn_error_trace(
366 with_lock(create_lock_baton(fs, write_lock, body, baton, pool),
371 svn_fs_fs__with_pack_lock(svn_fs_t *fs,
372 svn_error_t *(*body)(void *baton,
377 return svn_error_trace(
378 with_lock(create_lock_baton(fs, pack_lock, body, baton, pool),
383 svn_fs_fs__with_txn_current_lock(svn_fs_t *fs,
384 svn_error_t *(*body)(void *baton,
389 return svn_error_trace(
390 with_lock(create_lock_baton(fs, txn_lock, body, baton, pool),
395 svn_fs_fs__with_all_locks(svn_fs_t *fs,
396 svn_error_t *(*body)(void *baton,
401 fs_fs_data_t *ffd = fs->fsap_data;
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);
409 if (ffd->format >= SVN_FS_FS__MIN_PACK_LOCK_FORMAT)
410 lock_baton = chain_lock_baton(pack_lock, lock_baton);
412 if (ffd->format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
413 lock_baton = chain_lock_baton(txn_lock, lock_baton);
415 return svn_error_trace(with_lock(lock_baton, pool));
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.
425 Uses POOL for temporary allocation. */
427 check_format_file_buffer_numeric(const char *buf, apr_off_t offset,
428 const char *path, apr_pool_t *pool)
430 return svn_fs_fs__check_file_buffer_numeric(buf, offset, path, "Format",
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
438 check_format(int format)
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"),
450 /* We support all formats from 1-current simultaneously */
451 if (1 <= format && format <= SVN_FS_FS__FORMAT_NUMBER)
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);
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.
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.
468 Use POOL for temporary allocation. */
470 read_format(int *pformat,
471 int *max_files_per_dir,
472 svn_boolean_t *use_log_addressing,
477 svn_stream_t *stream;
478 svn_stringbuf_t *content;
479 svn_stringbuf_t *buf;
480 svn_boolean_t eos = FALSE;
482 err = svn_stringbuf_from_file2(&content, path, pool);
483 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
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
492 svn_error_clear(err);
494 *max_files_per_dir = 0;
495 *use_log_addressing = FALSE;
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)
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));
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));
515 /* Check that we support this format at all */
516 SVN_ERR(check_format(*pformat));
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;
522 /* Read any options. */
525 SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eos, pool));
529 if (*pformat >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT &&
530 strncmp(buf->data, "layout ", 7) == 0)
532 if (strcmp(buf->data + 7, "linear") == 0)
534 *max_files_per_dir = 0;
538 if (strncmp(buf->data + 7, "sharded ", 8) == 0)
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));
547 if (*pformat >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT &&
548 strncmp(buf->data, "addressing ", 11) == 0)
550 if (strcmp(buf->data + 11, "physical") == 0)
552 *use_log_addressing = FALSE;
556 if (strcmp(buf->data + 11, "logical") == 0)
558 *use_log_addressing = TRUE;
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);
568 /* Non-sharded repositories never use logical addressing.
569 * If the format file is inconsistent in that respect, something
570 * probably went wrong.
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));
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.
584 Use POOL for temporary allocation. */
586 svn_fs_fs__write_format(svn_fs_t *fs,
587 svn_boolean_t overwrite,
591 fs_fs_data_t *ffd = fs->fsap_data;
592 const char *path = path_format(fs, pool);
594 SVN_ERR_ASSERT(1 <= ffd->format
595 && ffd->format <= SVN_FS_FS__FORMAT_NUMBER);
597 sb = svn_stringbuf_createf(pool, "%d\n", ffd->format);
599 if (ffd->format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT)
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));
605 svn_stringbuf_appendcstr(sb, "layout linear\n");
608 if (ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT)
610 if (ffd->use_log_addressing)
611 svn_stringbuf_appendcstr(sb, "addressing logical\n");
613 svn_stringbuf_appendcstr(sb, "addressing physical\n");
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. */
621 /* Create the file */
622 SVN_ERR(svn_io_file_create(path, sb->data, pool));
626 SVN_ERR(svn_io_write_atomic2(path, sb->data, sb->len,
627 NULL /* copy_perms_path */,
628 ffd->flush_to_disk, pool));
631 /* And set the perms to make it read only */
632 return svn_io_set_file_read_only(path, FALSE, pool);
636 svn_fs_fs__fs_supports_mergeinfo(svn_fs_t *fs)
638 fs_fs_data_t *ffd = fs->fsap_data;
639 return ffd->format >= SVN_FS_FS__MIN_MERGEINFO_FORMAT;
642 /* Check that BLOCK_SIZE is a valid block / page size, i.e. it is within
643 * the range of what the current system may address in RAM and it is a
644 * power of 2. Assume that the element size within the block is ITEM_SIZE.
645 * Use SCRATCH_POOL for temporary allocations.
648 verify_block_size(apr_int64_t block_size,
649 apr_size_t item_size,
651 apr_pool_t *scratch_pool
656 return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
657 _("%s is too small for fsfs.conf setting '%s'."),
658 apr_psprintf(scratch_pool,
663 if (block_size > SVN_MAX_OBJECT_SIZE / item_size)
664 return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
665 _("%s is too large for fsfs.conf setting '%s'."),
666 apr_psprintf(scratch_pool,
671 /* Ensure it is a power of two.
672 * For positive X, X & (X-1) will reset the lowest bit set.
673 * If the result is 0, at most one bit has been set. */
674 if (0 != (block_size & (block_size - 1)))
675 return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
676 _("%s is invalid for fsfs.conf setting '%s' "
677 "because it is not a power of 2."),
678 apr_psprintf(scratch_pool,
687 parse_compression_option(compression_type_t *compression_type_p,
688 int *compression_level_p,
691 compression_type_t type;
693 svn_boolean_t is_valid = TRUE;
695 /* compression = none | lz4 | zlib | zlib-1 ... zlib-9 */
696 if (strcmp(value, "none") == 0)
698 type = compression_type_none;
699 level = SVN_DELTA_COMPRESSION_LEVEL_NONE;
701 else if (strcmp(value, "lz4") == 0)
703 type = compression_type_lz4;
704 level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
706 else if (strncmp(value, "zlib", 4) == 0)
708 const char *p = value + 4;
710 type = compression_type_zlib;
713 level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
718 SVN_ERR(svn_cstring_atoi(&level, p));
719 if (level < 1 || level > 9)
731 return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
732 _("Invalid 'compression' value '%s' in the config"),
735 *compression_type_p = type;
736 *compression_level_p = level;
740 /* Read the configuration information of the file system at FS_PATH
741 * and set the respective values in FFD. Use pools as usual.
744 read_config(fs_fs_data_t *ffd,
746 apr_pool_t *result_pool,
747 apr_pool_t *scratch_pool)
749 svn_config_t *config;
751 SVN_ERR(svn_config_read3(&config,
752 svn_dirent_join(fs_path, PATH_CONFIG, scratch_pool),
753 FALSE, FALSE, FALSE, scratch_pool));
755 /* Initialize ffd->rep_sharing_allowed. */
756 if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT)
757 SVN_ERR(svn_config_get_bool(config, &ffd->rep_sharing_allowed,
758 CONFIG_SECTION_REP_SHARING,
759 CONFIG_OPTION_ENABLE_REP_SHARING, TRUE));
761 ffd->rep_sharing_allowed = FALSE;
763 /* Initialize deltification settings in ffd. */
764 if (ffd->format >= SVN_FS_FS__MIN_DELTIFICATION_FORMAT)
766 SVN_ERR(svn_config_get_bool(config, &ffd->deltify_directories,
767 CONFIG_SECTION_DELTIFICATION,
768 CONFIG_OPTION_ENABLE_DIR_DELTIFICATION,
770 SVN_ERR(svn_config_get_bool(config, &ffd->deltify_properties,
771 CONFIG_SECTION_DELTIFICATION,
772 CONFIG_OPTION_ENABLE_PROPS_DELTIFICATION,
774 SVN_ERR(svn_config_get_int64(config, &ffd->max_deltification_walk,
775 CONFIG_SECTION_DELTIFICATION,
776 CONFIG_OPTION_MAX_DELTIFICATION_WALK,
777 SVN_FS_FS_MAX_DELTIFICATION_WALK));
778 SVN_ERR(svn_config_get_int64(config, &ffd->max_linear_deltification,
779 CONFIG_SECTION_DELTIFICATION,
780 CONFIG_OPTION_MAX_LINEAR_DELTIFICATION,
781 SVN_FS_FS_MAX_LINEAR_DELTIFICATION));
785 ffd->deltify_directories = FALSE;
786 ffd->deltify_properties = FALSE;
787 ffd->max_deltification_walk = SVN_FS_FS_MAX_DELTIFICATION_WALK;
788 ffd->max_linear_deltification = SVN_FS_FS_MAX_LINEAR_DELTIFICATION;
791 /* Initialize revprop packing settings in ffd. */
792 if (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
794 SVN_ERR(svn_config_get_bool(config, &ffd->compress_packed_revprops,
795 CONFIG_SECTION_PACKED_REVPROPS,
796 CONFIG_OPTION_COMPRESS_PACKED_REVPROPS,
798 SVN_ERR(svn_config_get_int64(config, &ffd->revprop_pack_size,
799 CONFIG_SECTION_PACKED_REVPROPS,
800 CONFIG_OPTION_REVPROP_PACK_SIZE,
801 ffd->compress_packed_revprops
805 ffd->revprop_pack_size *= 1024;
809 ffd->revprop_pack_size = 0x10000;
810 ffd->compress_packed_revprops = FALSE;
813 if (ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT)
815 SVN_ERR(svn_config_get_int64(config, &ffd->block_size,
817 CONFIG_OPTION_BLOCK_SIZE,
819 SVN_ERR(svn_config_get_int64(config, &ffd->l2p_page_size,
821 CONFIG_OPTION_L2P_PAGE_SIZE,
823 SVN_ERR(svn_config_get_int64(config, &ffd->p2l_page_size,
825 CONFIG_OPTION_P2L_PAGE_SIZE,
828 /* Don't accept unreasonable or illegal values.
829 * Block size and P2L page size are in kbytes;
830 * L2P blocks are arrays of apr_off_t. */
831 SVN_ERR(verify_block_size(ffd->block_size, 0x400,
832 CONFIG_OPTION_BLOCK_SIZE, scratch_pool));
833 SVN_ERR(verify_block_size(ffd->p2l_page_size, 0x400,
834 CONFIG_OPTION_P2L_PAGE_SIZE, scratch_pool));
835 SVN_ERR(verify_block_size(ffd->l2p_page_size, sizeof(apr_off_t),
836 CONFIG_OPTION_L2P_PAGE_SIZE, scratch_pool));
838 /* convert kBytes to bytes */
839 ffd->block_size *= 0x400;
840 ffd->p2l_page_size *= 0x400;
841 /* L2P pages are in entries - not in (k)Bytes */
845 /* should be irrelevant but we initialize them anyway */
846 ffd->block_size = 0x1000; /* Matches default APR file buffer size. */
847 ffd->l2p_page_size = 0x2000; /* Matches above default. */
848 ffd->p2l_page_size = 0x100000; /* Matches above default in bytes. */
851 if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
853 SVN_ERR(svn_config_get_bool(config, &ffd->pack_after_commit,
854 CONFIG_SECTION_DEBUG,
855 CONFIG_OPTION_PACK_AFTER_COMMIT,
860 ffd->pack_after_commit = FALSE;
863 /* Initialize compression settings in ffd. */
864 if (ffd->format >= SVN_FS_FS__MIN_DELTIFICATION_FORMAT)
866 const char *compression_val;
867 const char *compression_level_val;
869 svn_config_get(config, &compression_val,
870 CONFIG_SECTION_DELTIFICATION,
871 CONFIG_OPTION_COMPRESSION, NULL);
872 svn_config_get(config, &compression_level_val,
873 CONFIG_SECTION_DELTIFICATION,
874 CONFIG_OPTION_COMPRESSION_LEVEL, NULL);
875 if (compression_val && compression_level_val)
877 return svn_error_create(SVN_ERR_BAD_CONFIG_VALUE, NULL,
878 _("The 'compression' and 'compression-level' "
879 "config options are mutually exclusive"));
881 else if (compression_val)
883 SVN_ERR(parse_compression_option(&ffd->delta_compression_type,
884 &ffd->delta_compression_level,
886 if (ffd->delta_compression_type == compression_type_lz4 &&
887 ffd->format < SVN_FS_FS__MIN_SVNDIFF2_FORMAT)
889 return svn_error_create(SVN_ERR_BAD_CONFIG_VALUE, NULL,
890 _("Compression type 'lz4' requires "
891 "filesystem format 8 or higher"));
894 else if (compression_level_val)
896 /* Handle the deprecated 'compression-level' option. */
897 ffd->delta_compression_type = compression_type_zlib;
898 SVN_ERR(svn_cstring_atoi(&ffd->delta_compression_level,
899 compression_level_val));
900 ffd->delta_compression_level =
901 MIN(MAX(SVN_DELTA_COMPRESSION_LEVEL_NONE,
902 ffd->delta_compression_level),
903 SVN_DELTA_COMPRESSION_LEVEL_MAX);
907 /* Nothing specified explicitly, use the default settings:
908 * LZ4 compression for formats supporting it and zlib otherwise. */
909 if (ffd->format >= SVN_FS_FS__MIN_SVNDIFF2_FORMAT)
910 ffd->delta_compression_type = compression_type_lz4;
912 ffd->delta_compression_type = compression_type_zlib;
914 ffd->delta_compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
917 else if (ffd->format >= SVN_FS_FS__MIN_SVNDIFF1_FORMAT)
919 ffd->delta_compression_type = compression_type_zlib;
920 ffd->delta_compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
924 ffd->delta_compression_type = compression_type_none;
925 ffd->delta_compression_level = SVN_DELTA_COMPRESSION_LEVEL_NONE;
929 SVN_ERR(svn_config_get_bool(config, &ffd->verify_before_commit,
930 CONFIG_SECTION_DEBUG,
931 CONFIG_OPTION_VERIFY_BEFORE_COMMIT,
934 SVN_ERR(svn_config_get_bool(config, &ffd->verify_before_commit,
935 CONFIG_SECTION_DEBUG,
936 CONFIG_OPTION_VERIFY_BEFORE_COMMIT,
940 /* memcached configuration */
941 SVN_ERR(svn_cache__make_memcache_from_config(&ffd->memcache, config,
942 result_pool, scratch_pool));
944 SVN_ERR(svn_config_get_bool(config, &ffd->fail_stop,
945 CONFIG_SECTION_CACHES, CONFIG_OPTION_FAIL_STOP,
952 write_config(svn_fs_t *fs,
955 #define NL APR_EOL_STR
956 static const char * const fsfs_conf_contents =
957 "### This file controls the configuration of the FSFS filesystem." NL
959 "[" SVN_CACHE_CONFIG_CATEGORY_MEMCACHED_SERVERS "]" NL
960 "### These options name memcached servers used to cache internal FSFS" NL
961 "### data. See http://www.danga.com/memcached/ for more information on" NL
962 "### memcached. To use memcached with FSFS, run one or more memcached" NL
963 "### servers, and specify each of them as an option like so:" NL
964 "# first-server = 127.0.0.1:11211" NL
965 "# remote-memcached = mymemcached.corp.example.com:11212" NL
966 "### The option name is ignored; the value is of the form HOST:PORT." NL
967 "### memcached servers can be shared between multiple repositories;" NL
968 "### however, if you do this, you *must* ensure that repositories have" NL
969 "### distinct UUIDs and paths, or else cached data from one repository" NL
970 "### might be used by another accidentally. Note also that memcached has" NL
971 "### no authentication for reads or writes, so you must ensure that your" NL
972 "### memcached servers are only accessible by trusted users." NL
974 "[" CONFIG_SECTION_CACHES "]" NL
975 "### When a cache-related error occurs, normally Subversion ignores it" NL
976 "### and continues, logging an error if the server is appropriately" NL
977 "### configured (and ignoring it with file:// access). To make" NL
978 "### Subversion never ignore cache errors, uncomment this line." NL
979 "# " CONFIG_OPTION_FAIL_STOP " = true" NL
981 "[" CONFIG_SECTION_REP_SHARING "]" NL
982 "### To conserve space, the filesystem can optionally avoid storing" NL
983 "### duplicate representations. This comes at a slight cost in" NL
984 "### performance, as maintaining a database of shared representations can" NL
985 "### increase commit times. The space savings are dependent upon the size" NL
986 "### of the repository, the number of objects it contains and the amount of" NL
987 "### duplication between them, usually a function of the branching and" NL
988 "### merging process." NL
990 "### The following parameter enables rep-sharing in the repository. It can" NL
991 "### be switched on and off at will, but for best space-saving results" NL
992 "### should be enabled consistently over the life of the repository." NL
993 "### 'svnadmin verify' will check the rep-cache regardless of this setting." NL
994 "### rep-sharing is enabled by default." NL
995 "# " CONFIG_OPTION_ENABLE_REP_SHARING " = true" NL
997 "[" CONFIG_SECTION_DELTIFICATION "]" NL
998 "### To conserve space, the filesystem stores data as differences against" NL
999 "### existing representations. This comes at a slight cost in performance," NL
1000 "### as calculating differences can increase commit times. Reading data" NL
1001 "### will also create higher CPU load and the data will be fragmented." NL
1002 "### Since deltification tends to save significant amounts of disk space," NL
1003 "### the overall I/O load can actually be lower." NL
1005 "### The options in this section allow for tuning the deltification" NL
1006 "### strategy. Their effects on data size and server performance may vary" NL
1007 "### from one repository to another. Versions prior to 1.8 will ignore" NL
1008 "### this section." NL
1010 "### The following parameter enables deltification for directories. It can" NL
1011 "### be switched on and off at will, but for best space-saving results" NL
1012 "### should be enabled consistently over the lifetime of the repository." NL
1013 "### Repositories containing large directories will benefit greatly." NL
1014 "### In rarely accessed repositories, the I/O overhead may be significant" NL
1015 "### as caches will most likely be low." NL
1016 "### directory deltification is enabled by default." NL
1017 "# " CONFIG_OPTION_ENABLE_DIR_DELTIFICATION " = true" NL
1019 "### The following parameter enables deltification for properties on files" NL
1020 "### and directories. Overall, this is a minor tuning option but can save" NL
1021 "### some disk space if you merge frequently or frequently change node" NL
1022 "### properties. You should not activate this if rep-sharing has been" NL
1023 "### disabled because this may result in a net increase in repository size." NL
1024 "### property deltification is enabled by default." NL
1025 "# " CONFIG_OPTION_ENABLE_PROPS_DELTIFICATION " = true" NL
1027 "### During commit, the server may need to walk the whole change history of" NL
1028 "### of a given node to find a suitable deltification base. This linear" NL
1029 "### process can impact commit times, svnadmin load and similar operations." NL
1030 "### This setting limits the depth of the deltification history. If the" NL
1031 "### threshold has been reached, the node will be stored as fulltext and a" NL
1032 "### new deltification history begins." NL
1033 "### Note, this is unrelated to svn log." NL
1034 "### Very large values rarely provide significant additional savings but" NL
1035 "### can impact performance greatly - in particular if directory" NL
1036 "### deltification has been activated. Very small values may be useful in" NL
1037 "### repositories that are dominated by large, changing binaries." NL
1038 "### Should be a power of two minus 1. A value of 0 will effectively" NL
1039 "### disable deltification." NL
1040 "### For 1.8, the default value is 1023; earlier versions have no limit." NL
1041 "# " CONFIG_OPTION_MAX_DELTIFICATION_WALK " = 1023" NL
1043 "### The skip-delta scheme used by FSFS tends to repeatably store redundant" NL
1044 "### delta information where a simple delta against the latest version is" NL
1045 "### often smaller. By default, 1.8+ will therefore use skip deltas only" NL
1046 "### after the linear chain of deltas has grown beyond the threshold" NL
1047 "### specified by this setting." NL
1048 "### Values up to 64 can result in some reduction in repository size for" NL
1049 "### the cost of quickly increasing I/O and CPU costs. Similarly, smaller" NL
1050 "### numbers can reduce those costs at the cost of more disk space. For" NL
1051 "### rarely read repositories or those containing larger binaries, this may" NL
1052 "### present a better trade-off." NL
1053 "### Should be a power of two. A value of 1 or smaller will cause the" NL
1054 "### exclusive use of skip-deltas (as in pre-1.8)." NL
1055 "### For 1.8, the default value is 16; earlier versions use 1." NL
1056 "# " CONFIG_OPTION_MAX_LINEAR_DELTIFICATION " = 16" NL
1058 "### After deltification, we compress the data to minimize on-disk size." NL
1059 "### This setting controls the compression algorithm, which will be used in" NL
1060 "### future revisions. It can be used to either disable compression or to" NL
1061 "### select between available algorithms (zlib, lz4). zlib is a general-" NL
1062 "### purpose compression algorithm. lz4 is a fast compression algorithm" NL
1063 "### which should be preferred for repositories with large and, possibly," NL
1064 "### incompressible files. Note that the compression ratio of lz4 is" NL
1065 "### usually lower than the one provided by zlib, but using it can" NL
1066 "### significantly speed up commits as well as reading the data." NL
1067 "### lz4 compression algorithm is supported, starting from format 8" NL
1068 "### repositories, available in Subversion 1.10 and higher." NL
1069 "### The syntax of this option is:" NL
1070 "### " CONFIG_OPTION_COMPRESSION " = none | lz4 | zlib | zlib-1 ... zlib-9" NL
1071 "### Versions prior to Subversion 1.10 will ignore this option." NL
1072 "### The default value is 'lz4' if supported by the repository format and" NL
1073 "### 'zlib' otherwise. 'zlib' is currently equivalent to 'zlib-5'." NL
1074 "# " CONFIG_OPTION_COMPRESSION " = lz4" NL
1076 "### DEPRECATED: The new '" CONFIG_OPTION_COMPRESSION "' option deprecates previously used" NL
1077 "### '" CONFIG_OPTION_COMPRESSION_LEVEL "' option, which was used to configure zlib compression." NL
1078 "### For compatibility with previous versions of Subversion, this option can"NL
1079 "### still be used (and it will result in zlib compression with the" NL
1080 "### corresponding compression level)." NL
1081 "### " CONFIG_OPTION_COMPRESSION_LEVEL " = 0 ... 9 (default is 5)" NL
1083 "[" CONFIG_SECTION_PACKED_REVPROPS "]" NL
1084 "### This parameter controls the size (in kBytes) of packed revprop files." NL
1085 "### Revprops of consecutive revisions will be concatenated into a single" NL
1086 "### file up to but not exceeding the threshold given here. However, each" NL
1087 "### pack file may be much smaller and revprops of a single revision may be" NL
1088 "### much larger than the limit set here. The threshold will be applied" NL
1089 "### before optional compression takes place." NL
1090 "### Large values will reduce disk space usage at the expense of increased" NL
1091 "### latency and CPU usage reading and changing individual revprops." NL
1092 "### Values smaller than 4 kByte will not improve latency any further and " NL
1093 "### quickly render revprop packing ineffective." NL
1094 "### revprop-pack-size is 16 kBytes by default for non-compressed revprop" NL
1095 "### pack files and 64 kBytes when compression has been enabled." NL
1096 "# " CONFIG_OPTION_REVPROP_PACK_SIZE " = 16" NL
1098 "### To save disk space, packed revprop files may be compressed. Standard" NL
1099 "### revprops tend to allow for very effective compression. Reading and" NL
1100 "### even more so writing, become significantly more CPU intensive." NL
1101 "### Compressing packed revprops is disabled by default." NL
1102 "# " CONFIG_OPTION_COMPRESS_PACKED_REVPROPS " = false" NL
1104 "[" CONFIG_SECTION_IO "]" NL
1105 "### Parameters in this section control the data access granularity in" NL
1106 "### format 7 repositories and later. The defaults should translate into" NL
1107 "### decent performance over a wide range of setups." NL
1109 "### When a specific piece of information needs to be read from disk, a" NL
1110 "### data block is being read at once and its contents are being cached." NL
1111 "### If the repository is being stored on a RAID, the block size should be" NL
1112 "### either 50% or 100% of RAID block size / granularity. Also, your file" NL
1113 "### system blocks/clusters should be properly aligned and sized. In that" NL
1114 "### setup, each access will hit only one disk (minimizes I/O load) but" NL
1115 "### uses all the data provided by the disk in a single access." NL
1116 "### For SSD-based storage systems, slightly lower values around 16 kB" NL
1117 "### may improve latency while still maximizing throughput. If block-read" NL
1118 "### has not been enabled, this will be capped to 4 kBytes." NL
1119 "### Can be changed at any time but must be a power of 2." NL
1120 "### block-size is given in kBytes and with a default of 64 kBytes." NL
1121 "# " CONFIG_OPTION_BLOCK_SIZE " = 64" NL
1123 "### The log-to-phys index maps data item numbers to offsets within the" NL
1124 "### rev or pack file. This index is organized in pages of a fixed maximum" NL
1125 "### capacity. To access an item, the page table and the respective page" NL
1126 "### must be read." NL
1127 "### This parameter only affects revisions with thousands of changed paths." NL
1128 "### If you have several extremely large revisions (~1 mio changes), think" NL
1129 "### about increasing this setting. Reducing the value will rarely result" NL
1130 "### in a net speedup." NL
1131 "### This is an expert setting. Must be a power of 2." NL
1132 "### l2p-page-size is 8192 entries by default." NL
1133 "# " CONFIG_OPTION_L2P_PAGE_SIZE " = 8192" NL
1135 "### The phys-to-log index maps positions within the rev or pack file to" NL
1136 "### to data items, i.e. describes what piece of information is being" NL
1137 "### stored at any particular offset. The index describes the rev file" NL
1138 "### in chunks (pages) and keeps a global list of all those pages. Large" NL
1139 "### pages mean a shorter page table but a larger per-page description of" NL
1140 "### data items in it. The latency sweetspot depends on the change size" NL
1141 "### distribution but covers a relatively wide range." NL
1142 "### If the repository contains very large files, i.e. individual changes" NL
1143 "### of tens of MB each, increasing the page size will shorten the index" NL
1144 "### file at the expense of a slightly increased latency in sections with" NL
1145 "### smaller changes." NL
1146 "### For source code repositories, this should be about 16x the block-size." NL
1147 "### Must be a power of 2." NL
1148 "### p2l-page-size is given in kBytes and with a default of 1024 kBytes." NL
1149 "# " CONFIG_OPTION_P2L_PAGE_SIZE " = 1024" NL
1151 "[" CONFIG_SECTION_DEBUG "]" NL
1153 "### Whether to verify each new revision immediately before finalizing" NL
1154 "### the commit. This is disabled by default except in maintainer-mode" NL
1156 "# " CONFIG_OPTION_VERIFY_BEFORE_COMMIT " = false" NL
1159 return svn_io_file_create(svn_dirent_join(fs->path, PATH_CONFIG, pool),
1160 fsfs_conf_contents, pool);
1163 /* Read / Evaluate the global configuration in FS->CONFIG to set up
1164 * parameters in FS. */
1165 static svn_error_t *
1166 read_global_config(svn_fs_t *fs)
1168 fs_fs_data_t *ffd = fs->fsap_data;
1170 ffd->use_block_read = svn_hash__get_bool(fs->config,
1171 SVN_FS_CONFIG_FSFS_BLOCK_READ,
1173 ffd->flush_to_disk = !svn_hash__get_bool(fs->config,
1174 SVN_FS_CONFIG_NO_FLUSH_TO_DISK,
1177 /* Ignore the user-specified larger block size if we don't use block-read.
1178 Defaulting to 4k gives us the same access granularity in format 7 as in
1180 if (!ffd->use_block_read)
1181 ffd->block_size = MIN(0x1000, ffd->block_size);
1183 return SVN_NO_ERROR;
1186 /* Read FS's UUID file and store the data in the FS struct. */
1187 static svn_error_t *
1188 read_uuid(svn_fs_t *fs,
1189 apr_pool_t *scratch_pool)
1191 fs_fs_data_t *ffd = fs->fsap_data;
1192 apr_file_t *uuid_file;
1193 char buf[APR_UUID_FORMATTED_LENGTH + 2];
1196 /* Read the repository uuid. */
1197 SVN_ERR(svn_io_file_open(&uuid_file, path_uuid(fs, scratch_pool),
1198 APR_READ | APR_BUFFERED, APR_OS_DEFAULT,
1201 limit = sizeof(buf);
1202 SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit, scratch_pool));
1203 fs->uuid = apr_pstrdup(fs->pool, buf);
1205 /* Read the instance ID. */
1206 if (ffd->format >= SVN_FS_FS__MIN_INSTANCE_ID_FORMAT)
1208 limit = sizeof(buf);
1209 SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit,
1211 ffd->instance_id = apr_pstrdup(fs->pool, buf);
1215 ffd->instance_id = fs->uuid;
1218 SVN_ERR(svn_io_file_close(uuid_file, scratch_pool));
1220 return SVN_NO_ERROR;
1224 svn_fs_fs__read_format_file(svn_fs_t *fs, apr_pool_t *scratch_pool)
1226 fs_fs_data_t *ffd = fs->fsap_data;
1227 int format, max_files_per_dir;
1228 svn_boolean_t use_log_addressing;
1230 /* Read info from format file. */
1231 SVN_ERR(read_format(&format, &max_files_per_dir, &use_log_addressing,
1232 path_format(fs, scratch_pool), scratch_pool));
1234 /* Now that we've got *all* info, store / update values in FFD. */
1235 ffd->format = format;
1236 ffd->max_files_per_dir = max_files_per_dir;
1237 ffd->use_log_addressing = use_log_addressing;
1239 return SVN_NO_ERROR;
1243 svn_fs_fs__open(svn_fs_t *fs, const char *path, apr_pool_t *pool)
1245 fs_fs_data_t *ffd = fs->fsap_data;
1246 fs->path = apr_pstrdup(fs->pool, path);
1248 /* Read the FS format file. */
1249 SVN_ERR(svn_fs_fs__read_format_file(fs, pool));
1251 /* Read in and cache the repository uuid. */
1252 SVN_ERR(read_uuid(fs, pool));
1254 /* Read the min unpacked revision. */
1255 if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
1256 SVN_ERR(svn_fs_fs__update_min_unpacked_rev(fs, pool));
1258 /* Read the configuration file. */
1259 SVN_ERR(read_config(ffd, fs->path, fs->pool, pool));
1261 /* Global configuration options. */
1262 SVN_ERR(read_global_config(fs));
1264 ffd->youngest_rev_cache = 0;
1266 return SVN_NO_ERROR;
1269 /* Wrapper around svn_io_file_create which ignores EEXIST. */
1270 static svn_error_t *
1271 create_file_ignore_eexist(const char *file,
1272 const char *contents,
1275 svn_error_t *err = svn_io_file_create(file, contents, pool);
1276 if (err && APR_STATUS_IS_EEXIST(err->apr_err))
1278 svn_error_clear(err);
1281 return svn_error_trace(err);
1284 /* Baton type bridging svn_fs_fs__upgrade and upgrade_body carrying
1285 * parameters over between them. */
1286 struct upgrade_baton_t
1289 svn_fs_upgrade_notify_t notify_func;
1291 svn_cancel_func_t cancel_func;
1295 static svn_error_t *
1296 upgrade_body(void *baton, apr_pool_t *pool)
1298 struct upgrade_baton_t *upgrade_baton = baton;
1299 svn_fs_t *fs = upgrade_baton->fs;
1300 fs_fs_data_t *ffd = fs->fsap_data;
1301 int format, max_files_per_dir;
1302 svn_boolean_t use_log_addressing;
1303 const char *format_path = path_format(fs, pool);
1304 svn_node_kind_t kind;
1305 svn_boolean_t needs_revprop_shard_cleanup = FALSE;
1307 /* Read the FS format number and max-files-per-dir setting. */
1308 SVN_ERR(read_format(&format, &max_files_per_dir, &use_log_addressing,
1309 format_path, pool));
1311 /* If the config file does not exist, create one. */
1312 SVN_ERR(svn_io_check_path(svn_dirent_join(fs->path, PATH_CONFIG, pool),
1317 SVN_ERR(write_config(fs, pool));
1322 return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
1323 _("'%s' is not a regular file."
1324 " Please move it out of "
1325 "the way and try again"),
1326 svn_dirent_join(fs->path, PATH_CONFIG, pool));
1329 /* If we're already up-to-date, there's nothing else to be done here. */
1330 if (format == SVN_FS_FS__FORMAT_NUMBER)
1331 return SVN_NO_ERROR;
1333 /* If our filesystem predates the existence of the 'txn-current
1334 file', make that file and its corresponding lock file. */
1335 if (format < SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
1337 SVN_ERR(create_file_ignore_eexist(
1338 svn_fs_fs__path_txn_current(fs, pool), "0\n",
1340 SVN_ERR(create_file_ignore_eexist(
1341 svn_fs_fs__path_txn_current_lock(fs, pool), "",
1345 /* If our filesystem predates the existence of the 'txn-protorevs'
1346 dir, make that directory. */
1347 if (format < SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
1349 SVN_ERR(svn_io_make_dir_recursively(
1350 svn_fs_fs__path_txn_proto_revs(fs, pool), pool));
1353 /* If our filesystem is new enough, write the min unpacked rev file. */
1354 if (format < SVN_FS_FS__MIN_PACKED_FORMAT)
1355 SVN_ERR(svn_io_file_create(svn_fs_fs__path_min_unpacked_rev(fs, pool),
1358 /* If the file system supports revision packing but not revprop packing
1359 *and* the FS has been sharded, pack the revprops up to the point that
1360 revision data has been packed. However, keep the non-packed revprop
1361 files around until after the format bump */
1362 if ( format >= SVN_FS_FS__MIN_PACKED_FORMAT
1363 && format < SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT
1364 && max_files_per_dir > 0)
1366 needs_revprop_shard_cleanup = TRUE;
1367 SVN_ERR(svn_fs_fs__upgrade_pack_revprops(fs,
1368 upgrade_baton->notify_func,
1369 upgrade_baton->notify_baton,
1370 upgrade_baton->cancel_func,
1371 upgrade_baton->cancel_baton,
1375 /* We will need the UUID info shortly ...
1376 Read it before the format bump as the UUID file still uses the old
1378 SVN_ERR(read_uuid(fs, pool));
1380 /* Update the format info in the FS struct. Upgrade steps further
1381 down will use the format from FS to create missing info. */
1382 ffd->format = SVN_FS_FS__FORMAT_NUMBER;
1383 ffd->max_files_per_dir = max_files_per_dir;
1384 ffd->use_log_addressing = use_log_addressing;
1386 /* Always add / bump the instance ID such that no form of caching
1387 accidentally uses outdated information. Keep the UUID. */
1388 SVN_ERR(svn_fs_fs__set_uuid(fs, fs->uuid, NULL, pool));
1390 /* Bump the format file. */
1391 SVN_ERR(svn_fs_fs__write_format(fs, TRUE, pool));
1393 if (upgrade_baton->notify_func)
1394 SVN_ERR(upgrade_baton->notify_func(upgrade_baton->notify_baton,
1395 SVN_FS_FS__FORMAT_NUMBER,
1396 svn_fs_upgrade_format_bumped,
1399 /* Now, it is safe to remove the redundant revprop files. */
1400 if (needs_revprop_shard_cleanup)
1401 SVN_ERR(svn_fs_fs__upgrade_cleanup_pack_revprops(fs,
1402 upgrade_baton->notify_func,
1403 upgrade_baton->notify_baton,
1404 upgrade_baton->cancel_func,
1405 upgrade_baton->cancel_baton,
1409 return SVN_NO_ERROR;
1414 svn_fs_fs__upgrade(svn_fs_t *fs,
1415 svn_fs_upgrade_notify_t notify_func,
1417 svn_cancel_func_t cancel_func,
1421 struct upgrade_baton_t baton;
1423 baton.notify_func = notify_func;
1424 baton.notify_baton = notify_baton;
1425 baton.cancel_func = cancel_func;
1426 baton.cancel_baton = cancel_baton;
1428 return svn_fs_fs__with_all_locks(fs, upgrade_body, (void *)&baton, pool);
1431 /* Find the youngest revision in a repository at path FS_PATH and
1432 return it in *YOUNGEST_P. Perform temporary allocations in
1434 static svn_error_t *
1435 get_youngest(svn_revnum_t *youngest_p,
1440 SVN_ERR(svn_fs_fs__read_current(youngest_p, &dummy, &dummy, fs, pool));
1441 return SVN_NO_ERROR;
1446 svn_fs_fs__youngest_rev(svn_revnum_t *youngest_p,
1450 fs_fs_data_t *ffd = fs->fsap_data;
1452 SVN_ERR(get_youngest(youngest_p, fs, pool));
1453 ffd->youngest_rev_cache = *youngest_p;
1455 return SVN_NO_ERROR;
1459 svn_fs_fs__shard_size(svn_fs_t *fs)
1461 fs_fs_data_t *ffd = fs->fsap_data;
1463 return ffd->max_files_per_dir;
1467 svn_fs_fs__min_unpacked_rev(svn_revnum_t *min_unpacked,
1471 fs_fs_data_t *ffd = fs->fsap_data;
1473 /* Calling this for pre-v4 repos is illegal. */
1474 if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
1475 SVN_ERR(svn_fs_fs__update_min_unpacked_rev(fs, pool));
1477 *min_unpacked = ffd->min_unpacked_rev;
1479 return SVN_NO_ERROR;
1483 svn_fs_fs__ensure_revision_exists(svn_revnum_t rev,
1487 fs_fs_data_t *ffd = fs->fsap_data;
1489 if (! SVN_IS_VALID_REVNUM(rev))
1490 return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
1491 _("Invalid revision number '%ld'"), rev);
1494 /* Did the revision exist the last time we checked the current
1496 if (rev <= ffd->youngest_rev_cache)
1497 return SVN_NO_ERROR;
1499 SVN_ERR(get_youngest(&(ffd->youngest_rev_cache), fs, pool));
1502 if (rev <= ffd->youngest_rev_cache)
1503 return SVN_NO_ERROR;
1505 return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
1506 _("No such revision %ld"), rev);
1510 svn_fs_fs__file_length(svn_filesize_t *length,
1511 node_revision_t *noderev,
1514 representation_t *data_rep = noderev->data_rep;
1517 /* Treat "no representation" as "empty file". */
1522 *length = data_rep->expanded_size;
1525 return SVN_NO_ERROR;
1529 svn_fs_fs__noderev_same_rep_key(representation_t *a,
1530 representation_t *b)
1535 if (a == NULL || b == NULL)
1538 if (a->item_index != b->item_index)
1541 if (a->revision != b->revision)
1544 return memcmp(&a->uniquifier, &b->uniquifier, sizeof(a->uniquifier)) == 0;
1548 svn_fs_fs__file_text_rep_equal(svn_boolean_t *equal,
1552 apr_pool_t *scratch_pool)
1554 svn_stream_t *contents_a, *contents_b;
1555 representation_t *rep_a = a->data_rep;
1556 representation_t *rep_b = b->data_rep;
1557 svn_boolean_t a_empty = !rep_a || rep_a->expanded_size == 0;
1558 svn_boolean_t b_empty = !rep_b || rep_b->expanded_size == 0;
1560 /* This makes sure that neither rep will be NULL later on */
1561 if (a_empty && b_empty)
1564 return SVN_NO_ERROR;
1567 if (a_empty != b_empty)
1570 return SVN_NO_ERROR;
1573 /* File text representations always know their checksums - even in a txn. */
1574 if (memcmp(rep_a->md5_digest, rep_b->md5_digest, sizeof(rep_a->md5_digest)))
1577 return SVN_NO_ERROR;
1580 /* Paranoia. Compare SHA1 checksums because that's the level of
1581 confidence we require for e.g. the working copy. */
1582 if (rep_a->has_sha1 && rep_b->has_sha1)
1584 *equal = memcmp(rep_a->sha1_digest, rep_b->sha1_digest,
1585 sizeof(rep_a->sha1_digest)) == 0;
1586 return SVN_NO_ERROR;
1589 /* Same path in same rev or txn? */
1590 if (svn_fs_fs__id_eq(a->id, b->id))
1593 return SVN_NO_ERROR;
1596 SVN_ERR(svn_fs_fs__get_contents(&contents_a, fs, rep_a, TRUE,
1598 SVN_ERR(svn_fs_fs__get_contents(&contents_b, fs, rep_b, TRUE,
1600 SVN_ERR(svn_stream_contents_same2(equal, contents_a, contents_b,
1603 return SVN_NO_ERROR;
1607 svn_fs_fs__prop_rep_equal(svn_boolean_t *equal,
1611 apr_pool_t *scratch_pool)
1613 representation_t *rep_a = a->prop_rep;
1614 representation_t *rep_b = b->prop_rep;
1615 apr_hash_t *proplist_a;
1616 apr_hash_t *proplist_b;
1618 /* Mainly for a==b==NULL */
1622 return SVN_NO_ERROR;
1625 /* Committed property lists can be compared quickly */
1627 && !svn_fs_fs__id_txn_used(&rep_a->txn_id)
1628 && !svn_fs_fs__id_txn_used(&rep_b->txn_id))
1630 /* Same representation? */
1631 if ( (rep_a->revision == rep_b->revision)
1632 && (rep_a->item_index == rep_b->item_index))
1635 return SVN_NO_ERROR;
1638 /* Known different content? MD5 must be given. */
1639 if (memcmp(rep_a->md5_digest, rep_b->md5_digest,
1640 sizeof(rep_a->md5_digest)))
1643 return SVN_NO_ERROR;
1647 /* Same path in same txn?
1649 * For committed reps, IDs cannot be the same here b/c we already know
1650 * that they point to different representations. */
1651 if (svn_fs_fs__id_eq(a->id, b->id))
1654 return SVN_NO_ERROR;
1657 /* At least one of the reps has been modified in a txn.
1658 Fetch and compare them. */
1659 SVN_ERR(svn_fs_fs__get_proplist(&proplist_a, fs, a, scratch_pool));
1660 SVN_ERR(svn_fs_fs__get_proplist(&proplist_b, fs, b, scratch_pool));
1662 *equal = svn_fs__prop_lists_equal(proplist_a, proplist_b, scratch_pool);
1663 return SVN_NO_ERROR;
1668 svn_fs_fs__file_checksum(svn_checksum_t **checksum,
1669 node_revision_t *noderev,
1670 svn_checksum_kind_t kind,
1675 if (noderev->data_rep)
1677 svn_checksum_t temp;
1682 case svn_checksum_md5:
1683 temp.digest = noderev->data_rep->md5_digest;
1686 case svn_checksum_sha1:
1687 if (! noderev->data_rep->has_sha1)
1688 return SVN_NO_ERROR;
1690 temp.digest = noderev->data_rep->sha1_digest;
1694 return SVN_NO_ERROR;
1697 *checksum = svn_checksum_dup(&temp, pool);
1700 return SVN_NO_ERROR;
1704 svn_fs_fs__rep_copy(representation_t *rep,
1710 return apr_pmemdup(pool, rep, sizeof(*rep));
1714 /* Write out the zeroth revision for filesystem FS.
1715 Perform temporary allocations in SCRATCH_POOL. */
1716 static svn_error_t *
1717 write_revision_zero(svn_fs_t *fs,
1718 apr_pool_t *scratch_pool)
1720 /* Use an explicit sub-pool to have full control over temp file lifetimes.
1721 * Since we have it, use it for everything else as well. */
1722 apr_pool_t *subpool = svn_pool_create(scratch_pool);
1723 const char *path_revision_zero = svn_fs_fs__path_rev(fs, 0, subpool);
1724 apr_hash_t *proplist;
1727 /* Write out a rev file for revision 0. */
1728 if (svn_fs_fs__use_log_addressing(fs))
1730 apr_array_header_t *index_entries;
1731 svn_fs_fs__p2l_entry_t *entry;
1732 svn_fs_fs__revision_file_t *rev_file;
1733 const char *l2p_proto_index, *p2l_proto_index;
1735 /* Write a skeleton r0 with no indexes. */
1736 SVN_ERR(svn_io_file_create(path_revision_zero,
1737 "PLAIN\nEND\nENDREP\n"
1742 "2d2977d1c96f487abe4a1e202dd03b4e\n"
1746 /* Construct the index P2L contents: describe the 3 items we have.
1747 Be sure to create them in on-disk order. */
1748 index_entries = apr_array_make(subpool, 3, sizeof(entry));
1750 entry = apr_pcalloc(subpool, sizeof(*entry));
1753 entry->type = SVN_FS_FS__ITEM_TYPE_DIR_REP;
1754 entry->item.revision = 0;
1755 entry->item.number = SVN_FS_FS__ITEM_INDEX_FIRST_USER;
1756 APR_ARRAY_PUSH(index_entries, svn_fs_fs__p2l_entry_t *) = entry;
1758 entry = apr_pcalloc(subpool, sizeof(*entry));
1761 entry->type = SVN_FS_FS__ITEM_TYPE_NODEREV;
1762 entry->item.revision = 0;
1763 entry->item.number = SVN_FS_FS__ITEM_INDEX_ROOT_NODE;
1764 APR_ARRAY_PUSH(index_entries, svn_fs_fs__p2l_entry_t *) = entry;
1766 entry = apr_pcalloc(subpool, sizeof(*entry));
1767 entry->offset = 106;
1769 entry->type = SVN_FS_FS__ITEM_TYPE_CHANGES;
1770 entry->item.revision = 0;
1771 entry->item.number = SVN_FS_FS__ITEM_INDEX_CHANGES;
1772 APR_ARRAY_PUSH(index_entries, svn_fs_fs__p2l_entry_t *) = entry;
1774 /* Now re-open r0, create proto-index files from our entries and
1775 rewrite the index section of r0. */
1776 SVN_ERR(svn_fs_fs__open_pack_or_rev_file_writable(&rev_file, fs, 0,
1778 SVN_ERR(svn_fs_fs__p2l_index_from_p2l_entries(&p2l_proto_index, fs,
1779 rev_file, index_entries,
1781 SVN_ERR(svn_fs_fs__l2p_index_from_p2l_entries(&l2p_proto_index, fs,
1784 SVN_ERR(svn_fs_fs__add_index_data(fs, rev_file->file, l2p_proto_index,
1785 p2l_proto_index, 0, subpool));
1786 SVN_ERR(svn_fs_fs__close_revision_file(rev_file));
1789 SVN_ERR(svn_io_file_create(path_revision_zero,
1790 "PLAIN\nEND\nENDREP\n"
1795 "2d2977d1c96f487abe4a1e202dd03b4e\n"
1797 "\n\n17 107\n", subpool));
1799 SVN_ERR(svn_io_set_file_read_only(path_revision_zero, FALSE, subpool));
1801 /* Set a date on revision 0. */
1802 date.data = svn_time_to_cstring(apr_time_now(), subpool);
1803 date.len = strlen(date.data);
1804 proplist = apr_hash_make(subpool);
1805 svn_hash_sets(proplist, SVN_PROP_REVISION_DATE, &date);
1806 SVN_ERR(svn_fs_fs__set_revision_proplist(fs, 0, proplist, subpool));
1808 svn_pool_destroy(subpool);
1809 return SVN_NO_ERROR;
1813 svn_fs_fs__create_file_tree(svn_fs_t *fs,
1817 svn_boolean_t use_log_addressing,
1820 fs_fs_data_t *ffd = fs->fsap_data;
1822 fs->path = apr_pstrdup(fs->pool, path);
1823 ffd->format = format;
1825 /* Use an appropriate sharding mode if supported by the format. */
1826 if (format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT)
1827 ffd->max_files_per_dir = shard_size;
1829 ffd->max_files_per_dir = 0;
1831 /* Select the addressing mode depending on the format. */
1832 if (format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT)
1833 ffd->use_log_addressing = use_log_addressing;
1835 ffd->use_log_addressing = FALSE;
1837 /* Create the revision data directories. */
1838 if (ffd->max_files_per_dir)
1839 SVN_ERR(svn_io_make_dir_recursively(svn_fs_fs__path_rev_shard(fs, 0,
1843 SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path, PATH_REVS_DIR,
1847 /* Create the revprops directory. */
1848 if (ffd->max_files_per_dir)
1849 SVN_ERR(svn_io_make_dir_recursively(svn_fs_fs__path_revprops_shard(fs, 0,
1853 SVN_ERR(svn_io_make_dir_recursively(svn_dirent_join(path,
1858 /* Create the transaction directory. */
1859 SVN_ERR(svn_io_make_dir_recursively(svn_fs_fs__path_txns_dir(fs, pool),
1862 /* Create the protorevs directory. */
1863 if (format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT)
1864 SVN_ERR(svn_io_make_dir_recursively(svn_fs_fs__path_txn_proto_revs(fs,
1868 /* Create the 'current' file. */
1869 SVN_ERR(svn_io_file_create_empty(svn_fs_fs__path_current(fs, pool), pool));
1870 SVN_ERR(svn_fs_fs__write_current(fs, 0, 1, 1, pool));
1872 /* Create the 'uuid' file. */
1873 SVN_ERR(svn_io_file_create_empty(svn_fs_fs__path_lock(fs, pool), pool));
1874 SVN_ERR(svn_fs_fs__set_uuid(fs, NULL, NULL, pool));
1876 /* Create the fsfs.conf file if supported. Older server versions would
1877 simply ignore the file but that might result in a different behavior
1878 than with the later releases. Also, hotcopy would ignore, i.e. not
1879 copy, a fsfs.conf with old formats. */
1880 if (ffd->format >= SVN_FS_FS__MIN_CONFIG_FILE)
1881 SVN_ERR(write_config(fs, pool));
1883 SVN_ERR(read_config(ffd, fs->path, fs->pool, pool));
1885 /* Global configuration options. */
1886 SVN_ERR(read_global_config(fs));
1888 /* Add revision 0. */
1889 SVN_ERR(write_revision_zero(fs, pool));
1891 /* Create the min unpacked rev file. */
1892 if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT)
1893 SVN_ERR(svn_io_file_create(svn_fs_fs__path_min_unpacked_rev(fs, pool),
1896 /* Create the txn-current file if the repository supports
1897 the transaction sequence file. */
1898 if (format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
1900 SVN_ERR(svn_io_file_create(svn_fs_fs__path_txn_current(fs, pool),
1902 SVN_ERR(svn_io_file_create_empty(
1903 svn_fs_fs__path_txn_current_lock(fs, pool),
1907 ffd->youngest_rev_cache = 0;
1908 return SVN_NO_ERROR;
1912 svn_fs_fs__create(svn_fs_t *fs,
1916 int format = SVN_FS_FS__FORMAT_NUMBER;
1917 int shard_size = SVN_FS_FS_DEFAULT_MAX_FILES_PER_DIR;
1918 svn_boolean_t log_addressing;
1920 /* Process the given filesystem config. */
1923 svn_version_t *compatible_version;
1924 const char *shard_size_str;
1925 SVN_ERR(svn_fs__compatible_version(&compatible_version, fs->config,
1928 /* select format number */
1929 switch(compatible_version->minor)
1931 case 0: return svn_error_create(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
1932 _("FSFS is not compatible with Subversion prior to 1.1"));
1954 default:format = SVN_FS_FS__FORMAT_NUMBER;
1957 shard_size_str = svn_hash_gets(fs->config, SVN_FS_CONFIG_FSFS_SHARD_SIZE);
1961 SVN_ERR(svn_cstring_strtoi64(&val, shard_size_str, 0,
1962 APR_INT32_MAX, 10));
1964 shard_size = (int) val;
1968 log_addressing = svn_hash__get_bool(fs->config,
1969 SVN_FS_CONFIG_FSFS_LOG_ADDRESSING,
1972 /* Actual FS creation. */
1973 SVN_ERR(svn_fs_fs__create_file_tree(fs, path, format, shard_size,
1974 log_addressing, pool));
1976 /* This filesystem is ready. Stamp it with a format number. */
1977 SVN_ERR(svn_fs_fs__write_format(fs, FALSE, pool));
1979 return SVN_NO_ERROR;
1983 svn_fs_fs__set_uuid(svn_fs_t *fs,
1985 const char *instance_id,
1988 fs_fs_data_t *ffd = fs->fsap_data;
1989 const char *uuid_path = path_uuid(fs, pool);
1990 svn_stringbuf_t *contents = svn_stringbuf_create_empty(pool);
1993 uuid = svn_uuid_generate(pool);
1996 instance_id = svn_uuid_generate(pool);
1998 svn_stringbuf_appendcstr(contents, uuid);
1999 svn_stringbuf_appendcstr(contents, "\n");
2001 if (ffd->format >= SVN_FS_FS__MIN_INSTANCE_ID_FORMAT)
2003 svn_stringbuf_appendcstr(contents, instance_id);
2004 svn_stringbuf_appendcstr(contents, "\n");
2007 /* We use the permissions of the 'current' file, because the 'uuid'
2008 file does not exist during repository creation. */
2009 SVN_ERR(svn_io_write_atomic2(uuid_path, contents->data, contents->len,
2010 svn_fs_fs__path_current(fs, pool) /* perms */,
2011 ffd->flush_to_disk, pool));
2013 fs->uuid = apr_pstrdup(fs->pool, uuid);
2015 if (ffd->format >= SVN_FS_FS__MIN_INSTANCE_ID_FORMAT)
2016 ffd->instance_id = apr_pstrdup(fs->pool, instance_id);
2018 ffd->instance_id = fs->uuid;
2020 return SVN_NO_ERROR;
2023 /** Node origin lazy cache. */
2025 /* If directory PATH does not exist, create it and give it the same
2026 permissions as FS_path.*/
2028 svn_fs_fs__ensure_dir_exists(const char *path,
2029 const char *fs_path,
2032 svn_error_t *err = svn_io_dir_make(path, APR_OS_DEFAULT, pool);
2033 if (err && APR_STATUS_IS_EEXIST(err->apr_err))
2035 svn_error_clear(err);
2036 return SVN_NO_ERROR;
2040 /* We successfully created a new directory. Dup the permissions
2042 return svn_io_copy_perms(fs_path, path, pool);
2045 /* Set *NODE_ORIGINS to a hash mapping 'const char *' node IDs to
2046 'svn_string_t *' node revision IDs. Use POOL for allocations. */
2047 static svn_error_t *
2048 get_node_origins_from_file(svn_fs_t *fs,
2049 apr_hash_t **node_origins,
2050 const char *node_origins_file,
2055 svn_stream_t *stream;
2057 *node_origins = NULL;
2058 err = svn_io_file_open(&fd, node_origins_file,
2059 APR_READ, APR_OS_DEFAULT, pool);
2060 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
2062 svn_error_clear(err);
2063 return SVN_NO_ERROR;
2067 stream = svn_stream_from_aprfile2(fd, FALSE, pool);
2068 *node_origins = apr_hash_make(pool);
2069 err = svn_hash_read2(*node_origins, stream, SVN_HASH_TERMINATOR, pool);
2071 return svn_error_quick_wrapf(err, _("malformed node origin data in '%s'"),
2073 return svn_stream_close(stream);
2077 svn_fs_fs__get_node_origin(const svn_fs_id_t **origin_id,
2079 const svn_fs_fs__id_part_t *node_id,
2082 apr_hash_t *node_origins;
2085 SVN_ERR(get_node_origins_from_file(fs, &node_origins,
2086 svn_fs_fs__path_node_origin(fs, node_id,
2091 char node_id_ptr[SVN_INT64_BUFFER_SIZE];
2092 apr_size_t len = svn__ui64tobase36(node_id_ptr, node_id->number);
2093 svn_string_t *origin_id_str
2094 = apr_hash_get(node_origins, node_id_ptr, len);
2097 SVN_ERR(svn_fs_fs__id_parse(origin_id,
2098 apr_pstrdup(pool, origin_id_str->data),
2101 return SVN_NO_ERROR;
2105 /* Helper for svn_fs_fs__set_node_origin. Takes a NODE_ID/NODE_REV_ID
2106 pair and adds it to the NODE_ORIGINS_PATH file. */
2107 static svn_error_t *
2108 set_node_origins_for_file(svn_fs_t *fs,
2109 const char *node_origins_path,
2110 const svn_fs_fs__id_part_t *node_id,
2111 svn_string_t *node_rev_id,
2114 const char *path_tmp;
2115 svn_stream_t *stream;
2116 apr_hash_t *origins_hash;
2117 svn_string_t *old_node_rev_id;
2119 /* the hash serialization functions require strings as keys */
2120 char node_id_ptr[SVN_INT64_BUFFER_SIZE];
2121 apr_size_t len = svn__ui64tobase36(node_id_ptr, node_id->number);
2123 SVN_ERR(svn_fs_fs__ensure_dir_exists(svn_dirent_join(fs->path,
2124 PATH_NODE_ORIGINS_DIR,
2128 /* Read the previously existing origins (if any), and merge our
2130 SVN_ERR(get_node_origins_from_file(fs, &origins_hash,
2131 node_origins_path, pool));
2133 origins_hash = apr_hash_make(pool);
2135 old_node_rev_id = apr_hash_get(origins_hash, node_id_ptr, len);
2137 if (old_node_rev_id && !svn_string_compare(node_rev_id, old_node_rev_id))
2138 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
2139 _("Node origin for '%s' exists with a different "
2140 "value (%s) than what we were about to store "
2142 node_id_ptr, old_node_rev_id->data,
2145 apr_hash_set(origins_hash, node_id_ptr, len, node_rev_id);
2147 /* Sure, there's a race condition here. Two processes could be
2148 trying to add different cache elements to the same file at the
2149 same time, and the entries added by the first one to write will
2150 be lost. But this is just a cache of reconstructible data, so
2151 we'll accept this problem in return for not having to deal with
2152 locking overhead. */
2154 /* Create a temporary file, write out our hash, and close the file. */
2155 SVN_ERR(svn_stream_open_unique(&stream, &path_tmp,
2156 svn_dirent_dirname(node_origins_path, pool),
2157 svn_io_file_del_none, pool, pool));
2158 SVN_ERR(svn_hash_write2(origins_hash, stream, SVN_HASH_TERMINATOR, pool));
2159 SVN_ERR(svn_stream_close(stream));
2161 /* Rename the temp file as the real destination */
2162 return svn_io_file_rename2(path_tmp, node_origins_path, FALSE, pool);
2167 svn_fs_fs__set_node_origin(svn_fs_t *fs,
2168 const svn_fs_fs__id_part_t *node_id,
2169 const svn_fs_id_t *node_rev_id,
2173 const char *filename = svn_fs_fs__path_node_origin(fs, node_id, pool);
2175 err = set_node_origins_for_file(fs, filename,
2177 svn_fs_fs__id_unparse(node_rev_id, pool),
2179 if (err && APR_STATUS_IS_EACCES(err->apr_err))
2181 /* It's just a cache; stop trying if I can't write. */
2182 svn_error_clear(err);
2185 return svn_error_trace(err);
2193 svn_fs_fs__revision_prop(svn_string_t **value_p,
2196 const char *propname,
2197 svn_boolean_t refresh,
2198 apr_pool_t *result_pool,
2199 apr_pool_t *scratch_pool)
2203 SVN_ERR(svn_fs__check_fs(fs, TRUE));
2204 SVN_ERR(svn_fs_fs__get_revision_proplist(&table, fs, rev, refresh,
2205 scratch_pool, scratch_pool));
2207 *value_p = svn_string_dup(svn_hash_gets(table, propname), result_pool);
2209 return SVN_NO_ERROR;
2213 /* Baton used for change_rev_prop_body below. */
2214 struct change_rev_prop_baton {
2218 const svn_string_t *const *old_value_p;
2219 const svn_string_t *value;
2222 /* The work-horse for svn_fs_fs__change_rev_prop, called with the FS
2223 write lock. This implements the svn_fs_fs__with_write_lock()
2224 'body' callback type. BATON is a 'struct change_rev_prop_baton *'. */
2225 static svn_error_t *
2226 change_rev_prop_body(void *baton, apr_pool_t *pool)
2228 struct change_rev_prop_baton *cb = baton;
2230 const svn_string_t *present_value;
2232 /* We always need to read the current revprops from disk.
2233 * Hence, always "refresh" here. */
2234 SVN_ERR(svn_fs_fs__get_revision_proplist(&table, cb->fs, cb->rev, TRUE,
2236 present_value = svn_hash_gets(table, cb->name);
2238 if (cb->old_value_p)
2240 const svn_string_t *wanted_value = *cb->old_value_p;
2241 if ((!wanted_value != !present_value)
2242 || (wanted_value && present_value
2243 && !svn_string_compare(wanted_value, present_value)))
2245 /* What we expected isn't what we found. */
2246 return svn_error_createf(SVN_ERR_FS_PROP_BASEVALUE_MISMATCH, NULL,
2247 _("revprop '%s' has unexpected value in "
2254 /* If the prop-set is a no-op, skip the actual write. */
2255 if ((!present_value && !cb->value)
2256 || (present_value && cb->value
2257 && svn_string_compare(present_value, cb->value)))
2258 return SVN_NO_ERROR;
2260 svn_hash_sets(table, cb->name, cb->value);
2262 return svn_fs_fs__set_revision_proplist(cb->fs, cb->rev, table, pool);
2266 svn_fs_fs__change_rev_prop(svn_fs_t *fs,
2269 const svn_string_t *const *old_value_p,
2270 const svn_string_t *value,
2273 struct change_rev_prop_baton cb;
2275 SVN_ERR(svn_fs__check_fs(fs, TRUE));
2280 cb.old_value_p = old_value_p;
2283 return svn_fs_fs__with_write_lock(fs, change_rev_prop_body, &cb, pool);
2288 svn_fs_fs__info_format(int *fs_format,
2289 svn_version_t **supports_version,
2291 apr_pool_t *result_pool,
2292 apr_pool_t *scratch_pool)
2294 fs_fs_data_t *ffd = fs->fsap_data;
2295 *fs_format = ffd->format;
2296 *supports_version = apr_palloc(result_pool, sizeof(svn_version_t));
2298 (*supports_version)->major = SVN_VER_MAJOR;
2299 (*supports_version)->minor = 1;
2300 (*supports_version)->patch = 0;
2301 (*supports_version)->tag = "";
2303 switch (ffd->format)
2308 (*supports_version)->minor = 4;
2311 (*supports_version)->minor = 5;
2314 (*supports_version)->minor = 6;
2317 (*supports_version)->minor = 8;
2320 (*supports_version)->minor = 9;
2323 (*supports_version)->minor = 10;
2326 # if SVN_FS_FS__FORMAT_NUMBER != 8
2327 # error "Need to add a 'case' statement here"
2332 return SVN_NO_ERROR;
2336 svn_fs_fs__info_config_files(apr_array_header_t **files,
2338 apr_pool_t *result_pool,
2339 apr_pool_t *scratch_pool)
2341 *files = apr_array_make(result_pool, 1, sizeof(const char *));
2342 APR_ARRAY_PUSH(*files, const char *) = svn_dirent_join(fs->path, PATH_CONFIG,
2344 return SVN_NO_ERROR;