1 /* fs.c --- creating, opening and closing filesystems
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 <apr_general.h>
28 #include <apr_pools.h>
29 #include <apr_file_io.h>
32 #include "svn_delta.h"
33 #include "svn_version.h"
34 #include "svn_pools.h"
35 #include "batch_fsync.h"
46 #include "rep-cache.h"
47 #include "transaction.h"
49 #include "svn_private_config.h"
50 #include "private/svn_fs_util.h"
52 #include "../libsvn_fs/fs-loader.h"
54 /* A prefix for the pool userdata variables used to hold
55 per-filesystem shared data. See fs_serialized_init. */
56 #define SVN_FSX_SHARED_USERDATA_PREFIX "svn-fsx-shared-"
60 /* Initialize the part of FS that requires global serialization across all
61 instances. The caller is responsible of ensuring that serialization.
62 Use COMMON_POOL for process-wide and SCRATCH_POOL for temporary
65 x_serialized_init(svn_fs_t *fs,
66 apr_pool_t *common_pool,
67 apr_pool_t *scratch_pool)
69 svn_fs_x__data_t *ffd = fs->fsap_data;
72 svn_fs_x__shared_data_t *ffsd;
75 /* Note that we are allocating a small amount of long-lived data for
76 each separate repository opened during the lifetime of the
77 svn_fs_initialize pool. It's unlikely that anyone will notice
78 the modest expenditure; the alternative is to allocate each structure
79 in a subpool, add a reference-count, and add a serialized destructor
80 to the FS vtable. That's more machinery than it's worth.
82 Picking an appropriate key for the shared data is tricky, because,
83 unfortunately, a filesystem UUID is not really unique. It is implicitly
84 shared between hotcopied (1), dump / loaded (2) or naively copied (3)
85 filesystems. We tackle this problem by using a combination of the UUID
86 and an instance ID as the key. This allows us to avoid key clashing
89 Speaking of (3), there is not so much we can do about it, except maybe
90 provide a convenient way of fixing things. Naively copied filesystems
91 have identical filesystem UUIDs *and* instance IDs. With the key being
92 a combination of these two, clashes can be fixed by changing either of
93 them (or both), e.g. with svn_fs_set_uuid(). */
96 SVN_ERR_ASSERT(fs->uuid);
97 SVN_ERR_ASSERT(ffd->instance_id);
99 key = apr_pstrcat(scratch_pool, SVN_FSX_SHARED_USERDATA_PREFIX,
100 fs->uuid, ":", ffd->instance_id, SVN_VA_NULL);
101 status = apr_pool_userdata_get(&val, key, common_pool);
103 return svn_error_wrap_apr(status, _("Can't fetch FSX shared data"));
108 ffsd = apr_pcalloc(common_pool, sizeof(*ffsd));
109 ffsd->common_pool = common_pool;
111 /* POSIX fcntl locks are per-process, so we need a mutex for
112 intra-process synchronization when grabbing the repository write
114 SVN_ERR(svn_mutex__init(&ffsd->fs_write_lock,
115 SVN_FS_X__USE_LOCK_MUTEX, common_pool));
117 /* ... the pack lock ... */
118 SVN_ERR(svn_mutex__init(&ffsd->fs_pack_lock,
119 SVN_FS_X__USE_LOCK_MUTEX, common_pool));
121 /* ... not to mention locking the txn-current file. */
122 SVN_ERR(svn_mutex__init(&ffsd->txn_current_lock,
123 SVN_FS_X__USE_LOCK_MUTEX, common_pool));
125 /* We also need a mutex for synchronizing access to the active
126 transaction list and free transaction pointer. */
127 SVN_ERR(svn_mutex__init(&ffsd->txn_list_lock, TRUE, common_pool));
129 key = apr_pstrdup(common_pool, key);
130 status = apr_pool_userdata_set(ffsd, key, NULL, common_pool);
132 return svn_error_wrap_apr(status, _("Can't store FSX shared data"));
141 svn_fs_x__initialize_shared_data(svn_fs_t *fs,
142 svn_mutex__t *common_pool_lock,
143 apr_pool_t *scratch_pool,
144 apr_pool_t *common_pool)
146 SVN_MUTEX__WITH_LOCK(common_pool_lock,
147 x_serialized_init(fs, common_pool, scratch_pool));
154 /* This function is provided for Subversion 1.0.x compatibility. It
155 has no effect for fsx backed Subversion filesystems. It conforms
156 to the fs_library_vtable_t.bdb_set_errcall() API. */
158 x_set_errcall(svn_fs_t *fs,
159 void (*db_errcall_fcn)(const char *errpfx, char *msg))
165 typedef struct x_freeze_baton_t {
167 svn_fs_freeze_func_t freeze_func;
172 x_freeze_body(void *baton,
173 apr_pool_t *scratch_pool)
175 x_freeze_baton_t *b = baton;
176 svn_boolean_t exists;
178 SVN_ERR(svn_fs_x__exists_rep_cache(&exists, b->fs, scratch_pool));
180 SVN_ERR(svn_fs_x__with_rep_cache_lock(b->fs,
181 b->freeze_func, b->freeze_baton,
184 SVN_ERR(b->freeze_func(b->freeze_baton, scratch_pool));
190 x_freeze_body2(void *baton,
191 apr_pool_t *scratch_pool)
193 x_freeze_baton_t *b = baton;
194 SVN_ERR(svn_fs_x__with_write_lock(b->fs, x_freeze_body, baton,
201 x_freeze(svn_fs_t *fs,
202 svn_fs_freeze_func_t freeze_func,
204 apr_pool_t *scratch_pool)
209 b.freeze_func = freeze_func;
210 b.freeze_baton = freeze_baton;
212 SVN_ERR(svn_fs__check_fs(fs, TRUE));
213 SVN_ERR(svn_fs_x__with_pack_lock(fs, x_freeze_body2, &b, scratch_pool));
219 x_info(const void **fsx_info,
221 apr_pool_t *result_pool,
222 apr_pool_t *scratch_pool)
224 svn_fs_x__data_t *ffd = fs->fsap_data;
225 svn_fs_fsx_info_t *info = apr_palloc(result_pool, sizeof(*info));
226 info->fs_type = SVN_FS_TYPE_FSX;
227 info->shard_size = ffd->max_files_per_dir;
228 info->min_unpacked_rev = ffd->min_unpacked_rev;
234 x_refresh_revprops(svn_fs_t *fs,
235 apr_pool_t *scratch_pool)
237 svn_fs_x__invalidate_revprop_generation(fs);
241 /* Wrapper around svn_fs_x__get_revision_proplist() adapting between function
244 x_revision_proplist(apr_hash_t **proplist_p,
247 svn_boolean_t refresh,
248 apr_pool_t *result_pool,
249 apr_pool_t *scratch_pool)
251 /* No need to bypass the caches for r/o access to revprops. */
252 SVN_ERR(svn_fs_x__get_revision_proplist(proplist_p, fs, rev, FALSE,
253 refresh, result_pool,
259 /* Wrapper around svn_fs_x__set_uuid() adapting between function
262 x_set_uuid(svn_fs_t *fs,
264 apr_pool_t *scratch_pool)
266 /* Whenever we set a new UUID, imply that FS will also be a different
267 * instance (on formats that support this). */
268 return svn_error_trace(svn_fs_x__set_uuid(fs, uuid, NULL, TRUE,
272 /* Wrapper around svn_fs_x__begin_txn() providing the scratch pool. */
274 x_begin_txn(svn_fs_txn_t **txn_p,
280 apr_pool_t *scratch_pool = svn_pool_create(pool);
281 SVN_ERR(svn_fs_x__begin_txn(txn_p, fs, rev, flags, pool, scratch_pool));
282 svn_pool_destroy(scratch_pool);
289 /* The vtable associated with a specific open filesystem. */
290 static fs_vtable_t fs_vtable = {
291 svn_fs_x__youngest_rev,
293 svn_fs_x__revision_prop,
295 svn_fs_x__change_rev_prop,
297 svn_fs_x__revision_root,
301 svn_fs_x__list_transactions,
304 svn_fs_x__generate_lock_token,
308 svn_fs_x__info_format,
309 svn_fs_x__info_config_files,
311 svn_fs_x__verify_root,
317 /* Creating a new filesystem. */
319 /* Set up vtable and fsap_data fields in FS. */
321 initialize_fs_struct(svn_fs_t *fs)
323 svn_fs_x__data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd));
324 ffd->revprop_generation = -1;
325 ffd->flush_to_disk = TRUE;
327 fs->vtable = &fs_vtable;
332 /* Reset vtable and fsap_data fields in FS such that the FS is basically
333 * closed now. Note that FS must not hold locks when you call this. */
335 uninitialize_fs_struct(svn_fs_t *fs)
338 fs->fsap_data = NULL;
341 /* This implements the fs_library_vtable_t.create() API. Create a new
342 fsx-backed Subversion filesystem at path PATH and link it into
345 Perform temporary allocations in SCRATCH_POOL, and fs-global allocations
346 in COMMON_POOL. The latter must be serialized using COMMON_POOL_LOCK. */
348 x_create(svn_fs_t *fs,
350 svn_mutex__t *common_pool_lock,
351 apr_pool_t *scratch_pool,
352 apr_pool_t *common_pool)
354 SVN_ERR(svn_fs__check_fs(fs, FALSE));
356 SVN_ERR(initialize_fs_struct(fs));
358 SVN_ERR(svn_fs_x__create(fs, path, scratch_pool));
360 SVN_ERR(svn_fs_x__initialize_caches(fs, scratch_pool));
361 SVN_MUTEX__WITH_LOCK(common_pool_lock,
362 x_serialized_init(fs, common_pool, scratch_pool));
369 /* Gaining access to an existing filesystem. */
371 /* This implements the fs_library_vtable_t.open() API. Open an FSX
372 Subversion filesystem located at PATH, set *FS to point to the
373 correct vtable for the filesystem. Use SCRATCH_POOL for any temporary
374 allocations, and COMMON_POOL for fs-global allocations.
375 The latter must be serialized using COMMON_POOL_LOCK. */
379 svn_mutex__t *common_pool_lock,
380 apr_pool_t *scratch_pool,
381 apr_pool_t *common_pool)
383 apr_pool_t *subpool = svn_pool_create(scratch_pool);
385 SVN_ERR(svn_fs__check_fs(fs, FALSE));
387 SVN_ERR(initialize_fs_struct(fs));
389 SVN_ERR(svn_fs_x__open(fs, path, subpool));
391 SVN_ERR(svn_fs_x__initialize_caches(fs, subpool));
392 SVN_MUTEX__WITH_LOCK(common_pool_lock,
393 x_serialized_init(fs, common_pool, subpool));
395 svn_pool_destroy(subpool);
402 /* This implements the fs_library_vtable_t.open_for_recovery() API. */
404 x_open_for_recovery(svn_fs_t *fs,
406 svn_mutex__t *common_pool_lock,
407 apr_pool_t *scratch_pool,
408 apr_pool_t *common_pool)
411 svn_revnum_t youngest_rev;
412 apr_pool_t * subpool = svn_pool_create(scratch_pool);
414 /* Recovery for FSFS is currently limited to recreating the 'current'
415 file from the latest revision. */
417 /* The only thing we have to watch out for is that the 'current' file
418 might not exist or contain garbage. So we'll try to read it here
419 and provide or replace the existing file if we couldn't read it.
420 (We'll also need it to exist later anyway as a source for the new
421 file's permissions). */
423 /* Use a partly-filled fs pointer first to create 'current'. */
424 fs->path = apr_pstrdup(fs->pool, path);
426 SVN_ERR(initialize_fs_struct(fs));
428 /* Figure out the repo format and check that we can even handle it. */
429 SVN_ERR(svn_fs_x__read_format_file(fs, subpool));
431 /* Now, read 'current' and try to patch it if necessary. */
432 err = svn_fs_x__youngest_rev(&youngest_rev, fs, subpool);
435 const char *file_path;
437 /* 'current' file is missing or contains garbage. Since we are trying
438 * to recover from whatever problem there is, being picky about the
439 * error code here won't do us much good. If there is a persistent
440 * problem that we can't fix, it will show up when we try rewrite the
441 * file a few lines further below and we will report the failure back
444 * Start recovery with HEAD = 0. */
445 svn_error_clear(err);
446 file_path = svn_fs_x__path_current(fs, subpool);
448 /* Best effort to ensure the file exists and is valid.
449 * This may fail for r/o filesystems etc. */
450 SVN_ERR(svn_io_remove_file2(file_path, TRUE, subpool));
451 SVN_ERR(svn_io_file_create_empty(file_path, subpool));
452 SVN_ERR(svn_fs_x__write_current(fs, 0, subpool));
455 uninitialize_fs_struct(fs);
456 svn_pool_destroy(subpool);
458 /* Now open the filesystem properly by calling the vtable method directly. */
459 return x_open(fs, path, common_pool_lock, scratch_pool, common_pool);
464 /* This implements the fs_library_vtable_t.upgrade_fs() API. */
466 x_upgrade(svn_fs_t *fs,
468 svn_fs_upgrade_notify_t notify_func,
470 svn_cancel_func_t cancel_func,
472 svn_mutex__t *common_pool_lock,
473 apr_pool_t *scratch_pool,
474 apr_pool_t *common_pool)
476 SVN_ERR(x_open(fs, path, common_pool_lock, scratch_pool, common_pool));
477 return svn_fs_x__upgrade(fs, notify_func, notify_baton,
478 cancel_func, cancel_baton, scratch_pool);
482 x_verify(svn_fs_t *fs,
486 svn_fs_progress_notify_func_t notify_func,
488 svn_cancel_func_t cancel_func,
490 svn_mutex__t *common_pool_lock,
491 apr_pool_t *scratch_pool,
492 apr_pool_t *common_pool)
494 SVN_ERR(x_open(fs, path, common_pool_lock, scratch_pool, common_pool));
495 return svn_fs_x__verify(fs, start, end, notify_func, notify_baton,
496 cancel_func, cancel_baton, scratch_pool);
502 svn_fs_pack_notify_t notify_func,
504 svn_cancel_func_t cancel_func,
506 svn_mutex__t *common_pool_lock,
507 apr_pool_t *scratch_pool,
508 apr_pool_t *common_pool)
510 SVN_ERR(x_open(fs, path, common_pool_lock, scratch_pool, common_pool));
511 return svn_fs_x__pack(fs, 0, notify_func, notify_baton,
512 cancel_func, cancel_baton, scratch_pool);
518 /* This implements the fs_library_vtable_t.hotcopy() API. Copy a
519 possibly live Subversion filesystem SRC_FS from SRC_PATH to a
520 DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to
521 re-copy data which already exists in DST_FS.
522 The CLEAN_LOGS argument is ignored and included for Subversion
523 1.0.x compatibility. The NOTIFY_FUNC and NOTIFY_BATON arguments
524 are also currently ignored.
525 Perform all temporary allocations in SCRATCH_POOL. */
527 x_hotcopy(svn_fs_t *src_fs,
529 const char *src_path,
530 const char *dst_path,
531 svn_boolean_t clean_logs,
532 svn_boolean_t incremental,
533 svn_fs_hotcopy_notify_t notify_func,
535 svn_cancel_func_t cancel_func,
537 svn_mutex__t *common_pool_lock,
538 apr_pool_t *scratch_pool,
539 apr_pool_t *common_pool)
541 /* Open the source repo as usual. */
542 SVN_ERR(x_open(src_fs, src_path, common_pool_lock, scratch_pool,
545 SVN_ERR(cancel_func(cancel_baton));
547 SVN_ERR(svn_fs__check_fs(dst_fs, FALSE));
548 SVN_ERR(initialize_fs_struct(dst_fs));
550 /* In INCREMENTAL mode, svn_fs_x__hotcopy() will open DST_FS.
551 Otherwise, it's not an FS yet --- possibly just an empty dir --- so
554 return svn_fs_x__hotcopy(src_fs, dst_fs, src_path, dst_path,
555 incremental, notify_func, notify_baton,
556 cancel_func, cancel_baton, common_pool_lock,
557 scratch_pool, common_pool);
562 /* This function is included for Subversion 1.0.x compatibility. It
563 has no effect for fsx backed Subversion filesystems. It conforms
564 to the fs_library_vtable_t.bdb_logfiles() API. */
566 x_logfiles(apr_array_header_t **logfiles,
568 svn_boolean_t only_unused,
571 /* A no-op for FSX. */
572 *logfiles = apr_array_make(pool, 0, sizeof(const char *));
581 /* Delete the filesystem located at path PATH. Perform any temporary
582 allocations in SCRATCH_POOL. */
584 x_delete_fs(const char *path,
585 apr_pool_t *scratch_pool)
587 /* Remove everything. */
588 return svn_error_trace(svn_io_remove_dir2(path, FALSE, NULL, NULL,
592 static const svn_version_t *
599 x_get_description(void)
601 return _("Module for working with an experimental (FSX) repository.");
605 x_set_svn_fs_open(svn_fs_t *fs,
606 svn_error_t *(*svn_fs_open_)(svn_fs_t **,
612 svn_fs_x__data_t *ffd = fs->fsap_data;
613 ffd->svn_fs_open_ = svn_fs_open_;
618 x_info_dup(const void *fsx_info_void,
619 apr_pool_t *result_pool)
621 /* All fields are either ints or static strings. */
622 const svn_fs_fsx_info_t *fsx_info = fsx_info_void;
623 return apr_pmemdup(result_pool, fsx_info, sizeof(*fsx_info));
627 /* Base FS library vtable, used by the FS loader library. */
629 static fs_library_vtable_t library_vtable = {
648 svn_fs_x__init(const svn_version_t *loader_version,
649 fs_library_vtable_t **vtable,
650 apr_pool_t* common_pool)
652 static const svn_version_checklist_t checklist[] =
654 { "svn_subr", svn_subr_version },
655 { "svn_delta", svn_delta_version },
656 { "svn_fs_util", svn_fs_util__version },
660 /* Simplified version check to make sure we can safely use the
661 VTABLE parameter. The FS loader does a more exhaustive check. */
662 if (loader_version->major != SVN_VER_MAJOR)
663 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
664 _("Unsupported FS loader version (%d) for fsx"),
665 loader_version->major);
666 SVN_ERR(svn_ver_check_list2(x_version(), checklist, svn_ver_equal));
668 SVN_ERR(svn_fs_x__batch_fsync_init(common_pool));
670 *vtable = &library_vtable;