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>
30 #include <apr_thread_mutex.h>
33 #include "svn_delta.h"
34 #include "svn_version.h"
35 #include "svn_pools.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"));
142 /* This function is provided for Subversion 1.0.x compatibility. It
143 has no effect for fsx backed Subversion filesystems. It conforms
144 to the fs_library_vtable_t.bdb_set_errcall() API. */
146 x_set_errcall(svn_fs_t *fs,
147 void (*db_errcall_fcn)(const char *errpfx, char *msg))
153 typedef struct x_freeze_baton_t {
155 svn_fs_freeze_func_t freeze_func;
160 x_freeze_body(void *baton,
161 apr_pool_t *scratch_pool)
163 x_freeze_baton_t *b = baton;
164 svn_boolean_t exists;
166 SVN_ERR(svn_fs_x__exists_rep_cache(&exists, b->fs, scratch_pool));
168 SVN_ERR(svn_fs_x__with_rep_cache_lock(b->fs,
169 b->freeze_func, b->freeze_baton,
172 SVN_ERR(b->freeze_func(b->freeze_baton, scratch_pool));
178 x_freeze_body2(void *baton,
179 apr_pool_t *scratch_pool)
181 x_freeze_baton_t *b = baton;
182 SVN_ERR(svn_fs_x__with_write_lock(b->fs, x_freeze_body, baton,
189 x_freeze(svn_fs_t *fs,
190 svn_fs_freeze_func_t freeze_func,
192 apr_pool_t *scratch_pool)
197 b.freeze_func = freeze_func;
198 b.freeze_baton = freeze_baton;
200 SVN_ERR(svn_fs__check_fs(fs, TRUE));
201 SVN_ERR(svn_fs_x__with_pack_lock(fs, x_freeze_body2, &b, scratch_pool));
207 x_info(const void **fsx_info,
209 apr_pool_t *result_pool,
210 apr_pool_t *scratch_pool)
212 svn_fs_x__data_t *ffd = fs->fsap_data;
213 svn_fs_fsx_info_t *info = apr_palloc(result_pool, sizeof(*info));
214 info->fs_type = SVN_FS_TYPE_FSX;
215 info->shard_size = ffd->max_files_per_dir;
216 info->min_unpacked_rev = ffd->min_unpacked_rev;
221 /* Wrapper around svn_fs_x__revision_prop() adapting between function
224 x_revision_prop(svn_string_t **value_p,
227 const char *propname,
230 apr_pool_t *scratch_pool = svn_pool_create(pool);
231 SVN_ERR(svn_fs_x__revision_prop(value_p, fs, rev, propname, pool,
233 svn_pool_destroy(scratch_pool);
238 /* Wrapper around svn_fs_x__get_revision_proplist() adapting between function
241 x_revision_proplist(apr_hash_t **proplist_p,
246 apr_pool_t *scratch_pool = svn_pool_create(pool);
248 /* No need to bypass the caches for r/o access to revprops. */
249 SVN_ERR(svn_fs_x__get_revision_proplist(proplist_p, fs, rev, FALSE,
250 pool, scratch_pool));
251 svn_pool_destroy(scratch_pool);
256 /* Wrapper around svn_fs_x__set_uuid() adapting between function
259 x_set_uuid(svn_fs_t *fs,
261 apr_pool_t *scratch_pool)
263 /* Whenever we set a new UUID, imply that FS will also be a different
264 * instance (on formats that support this). */
265 return svn_error_trace(svn_fs_x__set_uuid(fs, uuid, NULL, scratch_pool));
268 /* Wrapper around svn_fs_x__begin_txn() providing the scratch pool. */
270 x_begin_txn(svn_fs_txn_t **txn_p,
276 apr_pool_t *scratch_pool = svn_pool_create(pool);
277 SVN_ERR(svn_fs_x__begin_txn(txn_p, fs, rev, flags, pool, scratch_pool));
278 svn_pool_destroy(scratch_pool);
285 /* The vtable associated with a specific open filesystem. */
286 static fs_vtable_t fs_vtable = {
287 svn_fs_x__youngest_rev,
290 svn_fs_x__change_rev_prop,
292 svn_fs_x__revision_root,
296 svn_fs_x__list_transactions,
299 svn_fs_x__generate_lock_token,
303 svn_fs_x__info_format,
304 svn_fs_x__info_config_files,
306 svn_fs_x__verify_root,
312 /* Creating a new filesystem. */
314 /* Set up vtable and fsap_data fields in FS. */
316 initialize_fs_struct(svn_fs_t *fs)
318 svn_fs_x__data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd));
319 fs->vtable = &fs_vtable;
324 /* Reset vtable and fsap_data fields in FS such that the FS is basically
325 * closed now. Note that FS must not hold locks when you call this. */
327 uninitialize_fs_struct(svn_fs_t *fs)
330 fs->fsap_data = NULL;
333 /* This implements the fs_library_vtable_t.create() API. Create a new
334 fsx-backed Subversion filesystem at path PATH and link it into
337 Perform temporary allocations in SCRATCH_POOL, and fs-global allocations
338 in COMMON_POOL. The latter must be serialized using COMMON_POOL_LOCK. */
340 x_create(svn_fs_t *fs,
342 svn_mutex__t *common_pool_lock,
343 apr_pool_t *scratch_pool,
344 apr_pool_t *common_pool)
346 SVN_ERR(svn_fs__check_fs(fs, FALSE));
348 SVN_ERR(initialize_fs_struct(fs));
350 SVN_ERR(svn_fs_x__create(fs, path, scratch_pool));
352 SVN_ERR(svn_fs_x__initialize_caches(fs, scratch_pool));
353 SVN_MUTEX__WITH_LOCK(common_pool_lock,
354 x_serialized_init(fs, common_pool, scratch_pool));
361 /* Gaining access to an existing filesystem. */
363 /* This implements the fs_library_vtable_t.open() API. Open an FSX
364 Subversion filesystem located at PATH, set *FS to point to the
365 correct vtable for the filesystem. Use SCRATCH_POOL for any temporary
366 allocations, and COMMON_POOL for fs-global allocations.
367 The latter must be serialized using COMMON_POOL_LOCK. */
371 svn_mutex__t *common_pool_lock,
372 apr_pool_t *scratch_pool,
373 apr_pool_t *common_pool)
375 apr_pool_t *subpool = svn_pool_create(scratch_pool);
377 SVN_ERR(svn_fs__check_fs(fs, FALSE));
379 SVN_ERR(initialize_fs_struct(fs));
381 SVN_ERR(svn_fs_x__open(fs, path, subpool));
383 SVN_ERR(svn_fs_x__initialize_caches(fs, subpool));
384 SVN_MUTEX__WITH_LOCK(common_pool_lock,
385 x_serialized_init(fs, common_pool, subpool));
387 svn_pool_destroy(subpool);
394 /* This implements the fs_library_vtable_t.open_for_recovery() API. */
396 x_open_for_recovery(svn_fs_t *fs,
398 svn_mutex__t *common_pool_lock,
399 apr_pool_t *scratch_pool,
400 apr_pool_t *common_pool)
403 svn_revnum_t youngest_rev;
404 apr_pool_t * subpool = svn_pool_create(scratch_pool);
406 /* Recovery for FSFS is currently limited to recreating the 'current'
407 file from the latest revision. */
409 /* The only thing we have to watch out for is that the 'current' file
410 might not exist or contain garbage. So we'll try to read it here
411 and provide or replace the existing file if we couldn't read it.
412 (We'll also need it to exist later anyway as a source for the new
413 file's permissions). */
415 /* Use a partly-filled fs pointer first to create 'current'. */
416 fs->path = apr_pstrdup(fs->pool, path);
418 SVN_ERR(initialize_fs_struct(fs));
420 /* Figure out the repo format and check that we can even handle it. */
421 SVN_ERR(svn_fs_x__read_format_file(fs, subpool));
423 /* Now, read 'current' and try to patch it if necessary. */
424 err = svn_fs_x__youngest_rev(&youngest_rev, fs, subpool);
427 const char *file_path;
429 /* 'current' file is missing or contains garbage. Since we are trying
430 * to recover from whatever problem there is, being picky about the
431 * error code here won't do us much good. If there is a persistent
432 * problem that we can't fix, it will show up when we try rewrite the
433 * file a few lines further below and we will report the failure back
436 * Start recovery with HEAD = 0. */
437 svn_error_clear(err);
438 file_path = svn_fs_x__path_current(fs, subpool);
440 /* Best effort to ensure the file exists and is valid.
441 * This may fail for r/o filesystems etc. */
442 SVN_ERR(svn_io_remove_file2(file_path, TRUE, subpool));
443 SVN_ERR(svn_io_file_create_empty(file_path, subpool));
444 SVN_ERR(svn_fs_x__write_current(fs, 0, subpool));
447 uninitialize_fs_struct(fs);
448 svn_pool_destroy(subpool);
450 /* Now open the filesystem properly by calling the vtable method directly. */
451 return x_open(fs, path, common_pool_lock, scratch_pool, common_pool);
456 /* This implements the fs_library_vtable_t.upgrade_fs() API. */
458 x_upgrade(svn_fs_t *fs,
460 svn_fs_upgrade_notify_t notify_func,
462 svn_cancel_func_t cancel_func,
464 svn_mutex__t *common_pool_lock,
465 apr_pool_t *scratch_pool,
466 apr_pool_t *common_pool)
468 SVN_ERR(x_open(fs, path, common_pool_lock, scratch_pool, common_pool));
469 return svn_fs_x__upgrade(fs, notify_func, notify_baton,
470 cancel_func, cancel_baton, scratch_pool);
474 x_verify(svn_fs_t *fs,
478 svn_fs_progress_notify_func_t notify_func,
480 svn_cancel_func_t cancel_func,
482 svn_mutex__t *common_pool_lock,
483 apr_pool_t *scratch_pool,
484 apr_pool_t *common_pool)
486 SVN_ERR(x_open(fs, path, common_pool_lock, scratch_pool, common_pool));
487 return svn_fs_x__verify(fs, start, end, notify_func, notify_baton,
488 cancel_func, cancel_baton, scratch_pool);
494 svn_fs_pack_notify_t notify_func,
496 svn_cancel_func_t cancel_func,
498 svn_mutex__t *common_pool_lock,
499 apr_pool_t *scratch_pool,
500 apr_pool_t *common_pool)
502 SVN_ERR(x_open(fs, path, common_pool_lock, scratch_pool, common_pool));
503 return svn_fs_x__pack(fs, notify_func, notify_baton,
504 cancel_func, cancel_baton, scratch_pool);
510 /* This implements the fs_library_vtable_t.hotcopy() API. Copy a
511 possibly live Subversion filesystem SRC_FS from SRC_PATH to a
512 DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to
513 re-copy data which already exists in DST_FS.
514 The CLEAN_LOGS argument is ignored and included for Subversion
515 1.0.x compatibility. The NOTIFY_FUNC and NOTIFY_BATON arguments
516 are also currently ignored.
517 Perform all temporary allocations in SCRATCH_POOL. */
519 x_hotcopy(svn_fs_t *src_fs,
521 const char *src_path,
522 const char *dst_path,
523 svn_boolean_t clean_logs,
524 svn_boolean_t incremental,
525 svn_fs_hotcopy_notify_t notify_func,
527 svn_cancel_func_t cancel_func,
529 svn_mutex__t *common_pool_lock,
530 apr_pool_t *scratch_pool,
531 apr_pool_t *common_pool)
533 /* Open the source repo as usual. */
534 SVN_ERR(x_open(src_fs, src_path, common_pool_lock, scratch_pool,
537 SVN_ERR(cancel_func(cancel_baton));
539 /* Test target repo when in INCREMENTAL mode, initialize it when not.
540 * For this, we need our FS internal data structures to be temporarily
542 SVN_ERR(initialize_fs_struct(dst_fs));
543 SVN_ERR(svn_fs_x__hotcopy_prepare_target(src_fs, dst_fs, dst_path,
544 incremental, scratch_pool));
545 uninitialize_fs_struct(dst_fs);
547 /* Now, the destination repo should open just fine. */
548 SVN_ERR(x_open(dst_fs, dst_path, common_pool_lock, scratch_pool,
551 SVN_ERR(cancel_func(cancel_baton));
553 /* Now, we may copy data as needed ... */
554 return svn_fs_x__hotcopy(src_fs, dst_fs, incremental,
555 notify_func, notify_baton,
556 cancel_func, cancel_baton, scratch_pool);
561 /* This function is included for Subversion 1.0.x compatibility. It
562 has no effect for fsx backed Subversion filesystems. It conforms
563 to the fs_library_vtable_t.bdb_logfiles() API. */
565 x_logfiles(apr_array_header_t **logfiles,
567 svn_boolean_t only_unused,
570 /* A no-op for FSX. */
571 *logfiles = apr_array_make(pool, 0, sizeof(const char *));
580 /* Delete the filesystem located at path PATH. Perform any temporary
581 allocations in SCRATCH_POOL. */
583 x_delete_fs(const char *path,
584 apr_pool_t *scratch_pool)
586 /* Remove everything. */
587 return svn_error_trace(svn_io_remove_dir2(path, FALSE, NULL, NULL,
591 static const svn_version_t *
598 x_get_description(void)
600 return _("Module for working with an experimental (FSX) repository.");
604 x_set_svn_fs_open(svn_fs_t *fs,
605 svn_error_t *(*svn_fs_open_)(svn_fs_t **,
611 svn_fs_x__data_t *ffd = fs->fsap_data;
612 ffd->svn_fs_open_ = svn_fs_open_;
617 x_info_dup(const void *fsx_info_void,
618 apr_pool_t *result_pool)
620 /* All fields are either ints or static strings. */
621 const svn_fs_fsx_info_t *fsx_info = fsx_info_void;
622 return apr_pmemdup(result_pool, fsx_info, sizeof(*fsx_info));
626 /* Base FS library vtable, used by the FS loader library. */
628 static fs_library_vtable_t library_vtable = {
647 svn_fs_x__init(const svn_version_t *loader_version,
648 fs_library_vtable_t **vtable,
649 apr_pool_t* common_pool)
651 static const svn_version_checklist_t checklist[] =
653 { "svn_subr", svn_subr_version },
654 { "svn_delta", svn_delta_version },
655 { "svn_fs_util", svn_fs_util__version },
659 /* Simplified version check to make sure we can safely use the
660 VTABLE parameter. The FS loader does a more exhaustive check. */
661 if (loader_version->major != SVN_VER_MAJOR)
662 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
663 _("Unsupported FS loader version (%d) for fsx"),
664 loader_version->major);
665 SVN_ERR(svn_ver_check_list2(x_version(), checklist, svn_ver_equal));
667 *vtable = &library_vtable;