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"
43 #include "rep-cache.h"
45 #include "transaction.h"
48 #include "svn_private_config.h"
49 #include "private/svn_fs_util.h"
51 #include "../libsvn_fs/fs-loader.h"
53 /* A prefix for the pool userdata variables used to hold
54 per-filesystem shared data. See fs_serialized_init. */
55 #define SVN_FSFS_SHARED_USERDATA_PREFIX "svn-fsfs-shared-"
59 /* Initialize the part of FS that requires global serialization across all
60 instances. The caller is responsible of ensuring that serialization.
61 Use COMMON_POOL for process-wide and POOL for temporary allocations. */
63 fs_serialized_init(svn_fs_t *fs, apr_pool_t *common_pool, apr_pool_t *pool)
65 fs_fs_data_t *ffd = fs->fsap_data;
68 fs_fs_shared_data_t *ffsd;
71 /* Note that we are allocating a small amount of long-lived data for
72 each separate repository opened during the lifetime of the
73 svn_fs_initialize pool. It's unlikely that anyone will notice
74 the modest expenditure; the alternative is to allocate each structure
75 in a subpool, add a reference-count, and add a serialized destructor
76 to the FS vtable. That's more machinery than it's worth.
78 Picking an appropriate key for the shared data is tricky, because,
79 unfortunately, a filesystem UUID is not really unique. It is implicitly
80 shared between hotcopied (1), dump / loaded (2) or naively copied (3)
81 filesystems. We tackle this problem by using a combination of the UUID
82 and an instance ID as the key. This allows us to avoid key clashing
83 in (1) and (2) for formats >= SVN_FS_FS__MIN_INSTANCE_ID_FORMAT, which
84 do support instance IDs. For old formats the shared data (locks, shared
85 transaction data, ...) will still clash.
87 Speaking of (3), there is not so much we can do about it, except maybe
88 provide a convenient way of fixing things. Naively copied filesystems
89 have identical filesystem UUIDs *and* instance IDs. With the key being
90 a combination of these two, clashes can be fixed by changing either of
91 them (or both), e.g. with svn_fs_set_uuid(). */
93 SVN_ERR_ASSERT(fs->uuid);
94 SVN_ERR_ASSERT(ffd->instance_id);
96 key = apr_pstrcat(pool, SVN_FSFS_SHARED_USERDATA_PREFIX,
97 fs->uuid, ":", ffd->instance_id, SVN_VA_NULL);
98 status = apr_pool_userdata_get(&val, key, common_pool);
100 return svn_error_wrap_apr(status, _("Can't fetch FSFS shared data"));
105 ffsd = apr_pcalloc(common_pool, sizeof(*ffsd));
106 ffsd->common_pool = common_pool;
108 /* POSIX fcntl locks are per-process, so we need a mutex for
109 intra-process synchronization when grabbing the repository write
111 SVN_ERR(svn_mutex__init(&ffsd->fs_write_lock,
112 SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
114 /* ... the pack lock ... */
115 SVN_ERR(svn_mutex__init(&ffsd->fs_pack_lock,
116 SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
118 /* ... not to mention locking the txn-current file. */
119 SVN_ERR(svn_mutex__init(&ffsd->txn_current_lock,
120 SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
122 /* We also need a mutex for synchronizing access to the active
123 transaction list and free transaction pointer. */
124 SVN_ERR(svn_mutex__init(&ffsd->txn_list_lock, TRUE, common_pool));
126 key = apr_pstrdup(common_pool, key);
127 status = apr_pool_userdata_set(ffsd, key, NULL, common_pool);
129 return svn_error_wrap_apr(status, _("Can't store FSFS shared data"));
138 svn_fs_fs__initialize_shared_data(svn_fs_t *fs,
139 svn_mutex__t *common_pool_lock,
141 apr_pool_t *common_pool)
143 SVN_MUTEX__WITH_LOCK(common_pool_lock,
144 fs_serialized_init(fs, common_pool, pool));
152 fs_refresh_revprops(svn_fs_t *fs,
153 apr_pool_t *scratch_pool)
155 svn_fs_fs__reset_revprop_cache(fs);
160 /* This function is provided for Subversion 1.0.x compatibility. It
161 has no effect for fsfs backed Subversion filesystems. It conforms
162 to the fs_library_vtable_t.bdb_set_errcall() API. */
164 fs_set_errcall(svn_fs_t *fs,
165 void (*db_errcall_fcn)(const char *errpfx, char *msg))
171 struct fs_freeze_baton_t {
173 svn_fs_freeze_func_t freeze_func;
178 fs_freeze_body(void *baton,
181 struct fs_freeze_baton_t *b = baton;
182 svn_boolean_t exists;
184 SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, b->fs, pool));
186 SVN_ERR(svn_fs_fs__with_rep_cache_lock(b->fs,
187 b->freeze_func, b->freeze_baton,
190 SVN_ERR(b->freeze_func(b->freeze_baton, pool));
196 fs_freeze_body2(void *baton,
199 struct fs_freeze_baton_t *b = baton;
200 SVN_ERR(svn_fs_fs__with_write_lock(b->fs, fs_freeze_body, baton, pool));
206 fs_freeze(svn_fs_t *fs,
207 svn_fs_freeze_func_t freeze_func,
211 fs_fs_data_t *ffd = fs->fsap_data;
212 struct fs_freeze_baton_t b;
215 b.freeze_func = freeze_func;
216 b.freeze_baton = freeze_baton;
218 SVN_ERR(svn_fs__check_fs(fs, TRUE));
220 if (ffd->format >= SVN_FS_FS__MIN_PACK_LOCK_FORMAT)
221 SVN_ERR(svn_fs_fs__with_pack_lock(fs, fs_freeze_body2, &b, pool));
223 SVN_ERR(fs_freeze_body2(&b, pool));
229 fs_info(const void **fsfs_info,
231 apr_pool_t *result_pool,
232 apr_pool_t *scratch_pool)
234 fs_fs_data_t *ffd = fs->fsap_data;
235 svn_fs_fsfs_info_t *info = apr_palloc(result_pool, sizeof(*info));
236 info->fs_type = SVN_FS_TYPE_FSFS;
237 info->shard_size = ffd->max_files_per_dir;
238 info->min_unpacked_rev = ffd->min_unpacked_rev;
239 info->log_addressing = ffd->use_log_addressing;
244 /* Wrapper around svn_fs_fs__set_uuid() adapting between function
247 fs_set_uuid(svn_fs_t *fs,
251 /* Whenever we set a new UUID, imply that FS will also be a different
252 * instance (on formats that support this). */
253 return svn_error_trace(svn_fs_fs__set_uuid(fs, uuid, NULL, pool));
258 /* The vtable associated with a specific open filesystem. */
259 static fs_vtable_t fs_vtable = {
260 svn_fs_fs__youngest_rev,
262 svn_fs_fs__revision_prop,
263 svn_fs_fs__get_revision_proplist,
264 svn_fs_fs__change_rev_prop,
266 svn_fs_fs__revision_root,
267 svn_fs_fs__begin_txn,
269 svn_fs_fs__purge_txn,
270 svn_fs_fs__list_transactions,
273 svn_fs_fs__generate_lock_token,
276 svn_fs_fs__get_locks,
277 svn_fs_fs__info_format,
278 svn_fs_fs__info_config_files,
280 svn_fs_fs__verify_root,
286 /* Creating a new filesystem. */
288 /* Set up vtable and fsap_data fields in FS. */
290 initialize_fs_struct(svn_fs_t *fs)
292 fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd));
293 ffd->use_log_addressing = FALSE;
294 ffd->revprop_prefix = 0;
295 ffd->flush_to_disk = TRUE;
297 fs->vtable = &fs_vtable;
302 /* Reset vtable and fsap_data fields in FS such that the FS is basically
303 * closed now. Note that FS must not hold locks when you call this. */
305 uninitialize_fs_struct(svn_fs_t *fs)
308 fs->fsap_data = NULL;
311 /* This implements the fs_library_vtable_t.create() API. Create a new
312 fsfs-backed Subversion filesystem at path PATH and link it into
313 *FS. Perform temporary allocations in POOL, and fs-global allocations
314 in COMMON_POOL. The latter must be serialized using COMMON_POOL_LOCK. */
316 fs_create(svn_fs_t *fs,
318 svn_mutex__t *common_pool_lock,
319 apr_pool_t *scratch_pool,
320 apr_pool_t *common_pool)
322 SVN_ERR(svn_fs__check_fs(fs, FALSE));
324 SVN_ERR(initialize_fs_struct(fs));
326 SVN_ERR(svn_fs_fs__create(fs, path, scratch_pool));
328 SVN_ERR(svn_fs_fs__initialize_caches(fs, scratch_pool));
329 SVN_MUTEX__WITH_LOCK(common_pool_lock,
330 fs_serialized_init(fs, common_pool, scratch_pool));
337 /* Gaining access to an existing filesystem. */
339 /* This implements the fs_library_vtable_t.open() API. Open an FSFS
340 Subversion filesystem located at PATH, set *FS to point to the
341 correct vtable for the filesystem. Use POOL for any temporary
342 allocations, and COMMON_POOL for fs-global allocations.
343 The latter must be serialized using COMMON_POOL_LOCK. */
345 fs_open(svn_fs_t *fs,
347 svn_mutex__t *common_pool_lock,
348 apr_pool_t *scratch_pool,
349 apr_pool_t *common_pool)
351 apr_pool_t *subpool = svn_pool_create(scratch_pool);
353 SVN_ERR(svn_fs__check_fs(fs, FALSE));
355 SVN_ERR(initialize_fs_struct(fs));
357 SVN_ERR(svn_fs_fs__open(fs, path, subpool));
359 SVN_ERR(svn_fs_fs__initialize_caches(fs, subpool));
360 SVN_MUTEX__WITH_LOCK(common_pool_lock,
361 fs_serialized_init(fs, common_pool, subpool));
363 svn_pool_destroy(subpool);
370 /* This implements the fs_library_vtable_t.open_for_recovery() API. */
372 fs_open_for_recovery(svn_fs_t *fs,
374 svn_mutex__t *common_pool_lock,
376 apr_pool_t *common_pool)
379 svn_revnum_t youngest_rev;
380 apr_pool_t * subpool = svn_pool_create(pool);
382 /* Recovery for FSFS is currently limited to recreating the 'current'
383 file from the latest revision. */
385 /* The only thing we have to watch out for is that the 'current' file
386 might not exist or contain garbage. So we'll try to read it here
387 and provide or replace the existing file if we couldn't read it.
388 (We'll also need it to exist later anyway as a source for the new
389 file's permissions). */
391 /* Use a partly-filled fs pointer first to create 'current'. */
392 fs->path = apr_pstrdup(fs->pool, path);
394 SVN_ERR(initialize_fs_struct(fs));
396 /* Figure out the repo format and check that we can even handle it. */
397 SVN_ERR(svn_fs_fs__read_format_file(fs, subpool));
399 /* Now, read 'current' and try to patch it if necessary. */
400 err = svn_fs_fs__youngest_rev(&youngest_rev, fs, subpool);
403 const char *file_path;
405 /* 'current' file is missing or contains garbage. Since we are trying
406 * to recover from whatever problem there is, being picky about the
407 * error code here won't do us much good. If there is a persistent
408 * problem that we can't fix, it will show up when we try rewrite the
409 * file a few lines further below and we will report the failure back
412 * Start recovery with HEAD = 0. */
413 svn_error_clear(err);
414 file_path = svn_fs_fs__path_current(fs, subpool);
416 /* Best effort to ensure the file exists and is valid.
417 * This may fail for r/o filesystems etc. */
418 SVN_ERR(svn_io_remove_file2(file_path, TRUE, subpool));
419 SVN_ERR(svn_io_file_create_empty(file_path, subpool));
420 SVN_ERR(svn_fs_fs__write_current(fs, 0, 1, 1, subpool));
423 uninitialize_fs_struct(fs);
424 svn_pool_destroy(subpool);
426 /* Now open the filesystem properly by calling the vtable method directly. */
427 return fs_open(fs, path, common_pool_lock, pool, common_pool);
432 /* This implements the fs_library_vtable_t.upgrade_fs() API. */
434 fs_upgrade(svn_fs_t *fs,
436 svn_fs_upgrade_notify_t notify_func,
438 svn_cancel_func_t cancel_func,
440 svn_mutex__t *common_pool_lock,
442 apr_pool_t *common_pool)
444 SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
445 return svn_fs_fs__upgrade(fs, notify_func, notify_baton,
446 cancel_func, cancel_baton, pool);
450 fs_verify(svn_fs_t *fs, const char *path,
453 svn_fs_progress_notify_func_t notify_func,
455 svn_cancel_func_t cancel_func,
457 svn_mutex__t *common_pool_lock,
459 apr_pool_t *common_pool)
461 SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
462 return svn_fs_fs__verify(fs, start, end, notify_func, notify_baton,
463 cancel_func, cancel_baton, pool);
467 fs_pack(svn_fs_t *fs,
469 svn_fs_pack_notify_t notify_func,
471 svn_cancel_func_t cancel_func,
473 svn_mutex__t *common_pool_lock,
475 apr_pool_t *common_pool)
477 SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
478 return svn_fs_fs__pack(fs, 0, notify_func, notify_baton,
479 cancel_func, cancel_baton, pool);
485 /* This implements the fs_library_vtable_t.hotcopy() API. Copy a
486 possibly live Subversion filesystem SRC_FS from SRC_PATH to a
487 DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to
488 re-copy data which already exists in DST_FS.
489 The CLEAN_LOGS argument is ignored and included for Subversion
490 1.0.x compatibility. Indicate progress via the optional NOTIFY_FUNC
491 callback using NOTIFY_BATON. Perform all temporary allocations in POOL. */
493 fs_hotcopy(svn_fs_t *src_fs,
495 const char *src_path,
496 const char *dst_path,
497 svn_boolean_t clean_logs,
498 svn_boolean_t incremental,
499 svn_fs_hotcopy_notify_t notify_func,
501 svn_cancel_func_t cancel_func,
503 svn_mutex__t *common_pool_lock,
505 apr_pool_t *common_pool)
507 SVN_ERR(fs_open(src_fs, src_path, common_pool_lock, pool, common_pool));
509 SVN_ERR(svn_fs__check_fs(dst_fs, FALSE));
510 SVN_ERR(initialize_fs_struct(dst_fs));
512 /* In INCREMENTAL mode, svn_fs_fs__hotcopy() will open DST_FS.
513 Otherwise, it's not an FS yet --- possibly just an empty dir --- so
516 return svn_fs_fs__hotcopy(src_fs, dst_fs, src_path, dst_path,
517 incremental, notify_func, notify_baton,
518 cancel_func, cancel_baton, common_pool_lock,
524 /* This function is included for Subversion 1.0.x compatibility. It
525 has no effect for fsfs backed Subversion filesystems. It conforms
526 to the fs_library_vtable_t.bdb_logfiles() API. */
528 fs_logfiles(apr_array_header_t **logfiles,
530 svn_boolean_t only_unused,
533 /* A no-op for FSFS. */
534 *logfiles = apr_array_make(pool, 0, sizeof(const char *));
543 /* Delete the filesystem located at path PATH. Perform any temporary
544 allocations in POOL. */
546 fs_delete_fs(const char *path,
549 /* Remove everything. */
550 return svn_error_trace(svn_io_remove_dir2(path, FALSE, NULL, NULL, pool));
553 static const svn_version_t *
560 fs_get_description(void)
562 return _("Module for working with a plain file (FSFS) repository.");
566 fs_set_svn_fs_open(svn_fs_t *fs,
567 svn_error_t *(*svn_fs_open_)(svn_fs_t **,
573 fs_fs_data_t *ffd = fs->fsap_data;
574 ffd->svn_fs_open_ = svn_fs_open_;
579 fs_info_dup(const void *fsfs_info_void,
580 apr_pool_t *result_pool)
582 /* All fields are either ints or static strings. */
583 const svn_fs_fsfs_info_t *fsfs_info = fsfs_info_void;
584 return apr_pmemdup(result_pool, fsfs_info, sizeof(*fsfs_info));
588 /* Base FS library vtable, used by the FS loader library. */
590 static fs_library_vtable_t library_vtable = {
594 fs_open_for_recovery,
609 svn_fs_fs__init(const svn_version_t *loader_version,
610 fs_library_vtable_t **vtable, apr_pool_t* common_pool)
612 static const svn_version_checklist_t checklist[] =
614 { "svn_subr", svn_subr_version },
615 { "svn_delta", svn_delta_version },
616 { "svn_fs_util", svn_fs_util__version },
620 /* Simplified version check to make sure we can safely use the
621 VTABLE parameter. The FS loader does a more exhaustive check. */
622 if (loader_version->major != SVN_VER_MAJOR)
623 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
624 _("Unsupported FS loader version (%d) for fsfs"),
625 loader_version->major);
626 SVN_ERR(svn_ver_check_list2(fs_version(), checklist, svn_ver_equal));
628 *vtable = &library_vtable;