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"
44 #include "rep-cache.h"
46 #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_FSFS_SHARED_USERDATA_PREFIX "svn-fsfs-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 POOL for temporary allocations. */
64 fs_serialized_init(svn_fs_t *fs, apr_pool_t *common_pool, apr_pool_t *pool)
66 fs_fs_data_t *ffd = fs->fsap_data;
69 fs_fs_shared_data_t *ffsd;
72 /* Note that we are allocating a small amount of long-lived data for
73 each separate repository opened during the lifetime of the
74 svn_fs_initialize pool. It's unlikely that anyone will notice
75 the modest expenditure; the alternative is to allocate each structure
76 in a subpool, add a reference-count, and add a serialized destructor
77 to the FS vtable. That's more machinery than it's worth.
79 Picking an appropriate key for the shared data is tricky, because,
80 unfortunately, a filesystem UUID is not really unique. It is implicitly
81 shared between hotcopied (1), dump / loaded (2) or naively copied (3)
82 filesystems. We tackle this problem by using a combination of the UUID
83 and an instance ID as the key. This allows us to avoid key clashing
84 in (1) and (2) for formats >= SVN_FS_FS__MIN_INSTANCE_ID_FORMAT, which
85 do support instance IDs. For old formats the shared data (locks, shared
86 transaction data, ...) will still clash.
88 Speaking of (3), there is not so much we can do about it, except maybe
89 provide a convenient way of fixing things. Naively copied filesystems
90 have identical filesystem UUIDs *and* instance IDs. With the key being
91 a combination of these two, clashes can be fixed by changing either of
92 them (or both), e.g. with svn_fs_set_uuid(). */
94 SVN_ERR_ASSERT(fs->uuid);
95 SVN_ERR_ASSERT(ffd->instance_id);
97 key = apr_pstrcat(pool, SVN_FSFS_SHARED_USERDATA_PREFIX,
98 fs->uuid, ":", ffd->instance_id, SVN_VA_NULL);
99 status = apr_pool_userdata_get(&val, key, common_pool);
101 return svn_error_wrap_apr(status, _("Can't fetch FSFS shared data"));
106 ffsd = apr_pcalloc(common_pool, sizeof(*ffsd));
107 ffsd->common_pool = common_pool;
109 /* POSIX fcntl locks are per-process, so we need a mutex for
110 intra-process synchronization when grabbing the repository write
112 SVN_ERR(svn_mutex__init(&ffsd->fs_write_lock,
113 SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
115 /* ... the pack lock ... */
116 SVN_ERR(svn_mutex__init(&ffsd->fs_pack_lock,
117 SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
119 /* ... not to mention locking the txn-current file. */
120 SVN_ERR(svn_mutex__init(&ffsd->txn_current_lock,
121 SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
123 /* We also need a mutex for synchronizing access to the active
124 transaction list and free transaction pointer. */
125 SVN_ERR(svn_mutex__init(&ffsd->txn_list_lock, TRUE, common_pool));
127 key = apr_pstrdup(common_pool, key);
128 status = apr_pool_userdata_set(ffsd, key, NULL, common_pool);
130 return svn_error_wrap_apr(status, _("Can't store FSFS shared data"));
140 /* This function is provided for Subversion 1.0.x compatibility. It
141 has no effect for fsfs backed Subversion filesystems. It conforms
142 to the fs_library_vtable_t.bdb_set_errcall() API. */
144 fs_set_errcall(svn_fs_t *fs,
145 void (*db_errcall_fcn)(const char *errpfx, char *msg))
151 struct fs_freeze_baton_t {
153 svn_fs_freeze_func_t freeze_func;
158 fs_freeze_body(void *baton,
161 struct fs_freeze_baton_t *b = baton;
162 svn_boolean_t exists;
164 SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, b->fs, pool));
166 SVN_ERR(svn_fs_fs__with_rep_cache_lock(b->fs,
167 b->freeze_func, b->freeze_baton,
170 SVN_ERR(b->freeze_func(b->freeze_baton, pool));
176 fs_freeze_body2(void *baton,
179 struct fs_freeze_baton_t *b = baton;
180 SVN_ERR(svn_fs_fs__with_write_lock(b->fs, fs_freeze_body, baton, pool));
186 fs_freeze(svn_fs_t *fs,
187 svn_fs_freeze_func_t freeze_func,
191 fs_fs_data_t *ffd = fs->fsap_data;
192 struct fs_freeze_baton_t b;
195 b.freeze_func = freeze_func;
196 b.freeze_baton = freeze_baton;
198 SVN_ERR(svn_fs__check_fs(fs, TRUE));
200 if (ffd->format >= SVN_FS_FS__MIN_PACK_LOCK_FORMAT)
201 SVN_ERR(svn_fs_fs__with_pack_lock(fs, fs_freeze_body2, &b, pool));
203 SVN_ERR(fs_freeze_body2(&b, pool));
209 fs_info(const void **fsfs_info,
211 apr_pool_t *result_pool,
212 apr_pool_t *scratch_pool)
214 fs_fs_data_t *ffd = fs->fsap_data;
215 svn_fs_fsfs_info_t *info = apr_palloc(result_pool, sizeof(*info));
216 info->fs_type = SVN_FS_TYPE_FSFS;
217 info->shard_size = ffd->max_files_per_dir;
218 info->min_unpacked_rev = ffd->min_unpacked_rev;
219 info->log_addressing = ffd->use_log_addressing;
224 /* Wrapper around svn_fs_fs__set_uuid() adapting between function
227 fs_set_uuid(svn_fs_t *fs,
231 /* Whenever we set a new UUID, imply that FS will also be a different
232 * instance (on formats that support this). */
233 return svn_error_trace(svn_fs_fs__set_uuid(fs, uuid, NULL, pool));
238 /* The vtable associated with a specific open filesystem. */
239 static fs_vtable_t fs_vtable = {
240 svn_fs_fs__youngest_rev,
241 svn_fs_fs__revision_prop,
242 svn_fs_fs__get_revision_proplist,
243 svn_fs_fs__change_rev_prop,
245 svn_fs_fs__revision_root,
246 svn_fs_fs__begin_txn,
248 svn_fs_fs__purge_txn,
249 svn_fs_fs__list_transactions,
252 svn_fs_fs__generate_lock_token,
255 svn_fs_fs__get_locks,
256 svn_fs_fs__info_format,
257 svn_fs_fs__info_config_files,
259 svn_fs_fs__verify_root,
265 /* Creating a new filesystem. */
267 /* Set up vtable and fsap_data fields in FS. */
269 initialize_fs_struct(svn_fs_t *fs)
271 fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd));
272 ffd->use_log_addressing = FALSE;
274 fs->vtable = &fs_vtable;
279 /* Reset vtable and fsap_data fields in FS such that the FS is basically
280 * closed now. Note that FS must not hold locks when you call this. */
282 uninitialize_fs_struct(svn_fs_t *fs)
285 fs->fsap_data = NULL;
288 /* This implements the fs_library_vtable_t.create() API. Create a new
289 fsfs-backed Subversion filesystem at path PATH and link it into
290 *FS. Perform temporary allocations in POOL, and fs-global allocations
291 in COMMON_POOL. The latter must be serialized using COMMON_POOL_LOCK. */
293 fs_create(svn_fs_t *fs,
295 svn_mutex__t *common_pool_lock,
297 apr_pool_t *common_pool)
299 SVN_ERR(svn_fs__check_fs(fs, FALSE));
301 SVN_ERR(initialize_fs_struct(fs));
303 SVN_ERR(svn_fs_fs__create(fs, path, pool));
305 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
306 SVN_MUTEX__WITH_LOCK(common_pool_lock,
307 fs_serialized_init(fs, common_pool, pool));
314 /* Gaining access to an existing filesystem. */
316 /* This implements the fs_library_vtable_t.open() API. Open an FSFS
317 Subversion filesystem located at PATH, set *FS to point to the
318 correct vtable for the filesystem. Use POOL for any temporary
319 allocations, and COMMON_POOL for fs-global allocations.
320 The latter must be serialized using COMMON_POOL_LOCK. */
322 fs_open(svn_fs_t *fs,
324 svn_mutex__t *common_pool_lock,
326 apr_pool_t *common_pool)
328 apr_pool_t *subpool = svn_pool_create(pool);
330 SVN_ERR(svn_fs__check_fs(fs, FALSE));
332 SVN_ERR(initialize_fs_struct(fs));
334 SVN_ERR(svn_fs_fs__open(fs, path, subpool));
336 SVN_ERR(svn_fs_fs__initialize_caches(fs, subpool));
337 SVN_MUTEX__WITH_LOCK(common_pool_lock,
338 fs_serialized_init(fs, common_pool, subpool));
340 svn_pool_destroy(subpool);
347 /* This implements the fs_library_vtable_t.open_for_recovery() API. */
349 fs_open_for_recovery(svn_fs_t *fs,
351 svn_mutex__t *common_pool_lock,
353 apr_pool_t *common_pool)
356 svn_revnum_t youngest_rev;
357 apr_pool_t * subpool = svn_pool_create(pool);
359 /* Recovery for FSFS is currently limited to recreating the 'current'
360 file from the latest revision. */
362 /* The only thing we have to watch out for is that the 'current' file
363 might not exist or contain garbage. So we'll try to read it here
364 and provide or replace the existing file if we couldn't read it.
365 (We'll also need it to exist later anyway as a source for the new
366 file's permissions). */
368 /* Use a partly-filled fs pointer first to create 'current'. */
369 fs->path = apr_pstrdup(fs->pool, path);
371 SVN_ERR(initialize_fs_struct(fs));
373 /* Figure out the repo format and check that we can even handle it. */
374 SVN_ERR(svn_fs_fs__read_format_file(fs, subpool));
376 /* Now, read 'current' and try to patch it if necessary. */
377 err = svn_fs_fs__youngest_rev(&youngest_rev, fs, subpool);
380 const char *file_path;
382 /* 'current' file is missing or contains garbage. Since we are trying
383 * to recover from whatever problem there is, being picky about the
384 * error code here won't do us much good. If there is a persistent
385 * problem that we can't fix, it will show up when we try rewrite the
386 * file a few lines further below and we will report the failure back
389 * Start recovery with HEAD = 0. */
390 svn_error_clear(err);
391 file_path = svn_fs_fs__path_current(fs, subpool);
393 /* Best effort to ensure the file exists and is valid.
394 * This may fail for r/o filesystems etc. */
395 SVN_ERR(svn_io_remove_file2(file_path, TRUE, subpool));
396 SVN_ERR(svn_io_file_create_empty(file_path, subpool));
397 SVN_ERR(svn_fs_fs__write_current(fs, 0, 1, 1, subpool));
400 uninitialize_fs_struct(fs);
401 svn_pool_destroy(subpool);
403 /* Now open the filesystem properly by calling the vtable method directly. */
404 return fs_open(fs, path, common_pool_lock, pool, common_pool);
409 /* This implements the fs_library_vtable_t.upgrade_fs() API. */
411 fs_upgrade(svn_fs_t *fs,
413 svn_fs_upgrade_notify_t notify_func,
415 svn_cancel_func_t cancel_func,
417 svn_mutex__t *common_pool_lock,
419 apr_pool_t *common_pool)
421 SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
422 return svn_fs_fs__upgrade(fs, notify_func, notify_baton,
423 cancel_func, cancel_baton, pool);
427 fs_verify(svn_fs_t *fs, const char *path,
430 svn_fs_progress_notify_func_t notify_func,
432 svn_cancel_func_t cancel_func,
434 svn_mutex__t *common_pool_lock,
436 apr_pool_t *common_pool)
438 SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
439 return svn_fs_fs__verify(fs, start, end, notify_func, notify_baton,
440 cancel_func, cancel_baton, pool);
444 fs_pack(svn_fs_t *fs,
446 svn_fs_pack_notify_t notify_func,
448 svn_cancel_func_t cancel_func,
450 svn_mutex__t *common_pool_lock,
452 apr_pool_t *common_pool)
454 SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
455 return svn_fs_fs__pack(fs, notify_func, notify_baton,
456 cancel_func, cancel_baton, pool);
462 /* This implements the fs_library_vtable_t.hotcopy() API. Copy a
463 possibly live Subversion filesystem SRC_FS from SRC_PATH to a
464 DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to
465 re-copy data which already exists in DST_FS.
466 The CLEAN_LOGS argument is ignored and included for Subversion
467 1.0.x compatibility. Indicate progress via the optional NOTIFY_FUNC
468 callback using NOTIFY_BATON. Perform all temporary allocations in POOL. */
470 fs_hotcopy(svn_fs_t *src_fs,
472 const char *src_path,
473 const char *dst_path,
474 svn_boolean_t clean_logs,
475 svn_boolean_t incremental,
476 svn_fs_hotcopy_notify_t notify_func,
478 svn_cancel_func_t cancel_func,
480 svn_mutex__t *common_pool_lock,
482 apr_pool_t *common_pool)
484 /* Open the source repo as usual. */
485 SVN_ERR(fs_open(src_fs, src_path, common_pool_lock, pool, common_pool));
487 SVN_ERR(cancel_func(cancel_baton));
489 /* Test target repo when in INCREMENTAL mode, initialize it when not.
490 * For this, we need our FS internal data structures to be temporarily
492 SVN_ERR(initialize_fs_struct(dst_fs));
493 SVN_ERR(svn_fs_fs__hotcopy_prepare_target(src_fs, dst_fs, dst_path,
495 uninitialize_fs_struct(dst_fs);
497 /* Now, the destination repo should open just fine. */
498 SVN_ERR(fs_open(dst_fs, dst_path, common_pool_lock, pool, common_pool));
500 SVN_ERR(cancel_func(cancel_baton));
502 /* Now, we may copy data as needed ... */
503 return svn_fs_fs__hotcopy(src_fs, dst_fs, incremental,
504 notify_func, notify_baton,
505 cancel_func, cancel_baton, pool);
510 /* This function is included for Subversion 1.0.x compatibility. It
511 has no effect for fsfs backed Subversion filesystems. It conforms
512 to the fs_library_vtable_t.bdb_logfiles() API. */
514 fs_logfiles(apr_array_header_t **logfiles,
516 svn_boolean_t only_unused,
519 /* A no-op for FSFS. */
520 *logfiles = apr_array_make(pool, 0, sizeof(const char *));
529 /* Delete the filesystem located at path PATH. Perform any temporary
530 allocations in POOL. */
532 fs_delete_fs(const char *path,
535 /* Remove everything. */
536 return svn_error_trace(svn_io_remove_dir2(path, FALSE, NULL, NULL, pool));
539 static const svn_version_t *
546 fs_get_description(void)
548 return _("Module for working with a plain file (FSFS) repository.");
552 fs_set_svn_fs_open(svn_fs_t *fs,
553 svn_error_t *(*svn_fs_open_)(svn_fs_t **,
559 fs_fs_data_t *ffd = fs->fsap_data;
560 ffd->svn_fs_open_ = svn_fs_open_;
565 fs_info_dup(const void *fsfs_info_void,
566 apr_pool_t *result_pool)
568 /* All fields are either ints or static strings. */
569 const svn_fs_fsfs_info_t *fsfs_info = fsfs_info_void;
570 return apr_pmemdup(result_pool, fsfs_info, sizeof(*fsfs_info));
574 /* Base FS library vtable, used by the FS loader library. */
576 static fs_library_vtable_t library_vtable = {
580 fs_open_for_recovery,
595 svn_fs_fs__init(const svn_version_t *loader_version,
596 fs_library_vtable_t **vtable, apr_pool_t* common_pool)
598 static const svn_version_checklist_t checklist[] =
600 { "svn_subr", svn_subr_version },
601 { "svn_delta", svn_delta_version },
602 { "svn_fs_util", svn_fs_util__version },
606 /* Simplified version check to make sure we can safely use the
607 VTABLE parameter. The FS loader does a more exhaustive check. */
608 if (loader_version->major != SVN_VER_MAJOR)
609 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
610 _("Unsupported FS loader version (%d) for fsfs"),
611 loader_version->major);
612 SVN_ERR(svn_ver_check_list2(fs_version(), checklist, svn_ver_equal));
614 *vtable = &library_vtable;