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"
41 #include "rep-cache.h"
42 #include "svn_private_config.h"
43 #include "private/svn_fs_util.h"
44 #include "private/svn_subr_private.h"
46 #include "../libsvn_fs/fs-loader.h"
48 /* A prefix for the pool userdata variables used to hold
49 per-filesystem shared data. See fs_serialized_init. */
50 #define SVN_FSFS_SHARED_USERDATA_PREFIX "svn-fsfs-shared-"
55 fs_serialized_init(svn_fs_t *fs, apr_pool_t *common_pool, apr_pool_t *pool)
57 fs_fs_data_t *ffd = fs->fsap_data;
60 fs_fs_shared_data_t *ffsd;
63 /* Note that we are allocating a small amount of long-lived data for
64 each separate repository opened during the lifetime of the
65 svn_fs_initialize pool. It's unlikely that anyone will notice
66 the modest expenditure; the alternative is to allocate each structure
67 in a subpool, add a reference-count, and add a serialized deconstructor
68 to the FS vtable. That's more machinery than it's worth.
70 Using the uuid to obtain the lock creates a corner case if a
71 caller uses svn_fs_set_uuid on the repository in a process where
72 other threads might be using the same repository through another
73 FS object. The only real-world consumer of svn_fs_set_uuid is
74 "svnadmin load", so this is a low-priority problem, and we don't
75 know of a better way of associating such data with the
78 SVN_ERR_ASSERT(fs->uuid);
79 key = apr_pstrcat(pool, SVN_FSFS_SHARED_USERDATA_PREFIX, fs->uuid,
81 status = apr_pool_userdata_get(&val, key, common_pool);
83 return svn_error_wrap_apr(status, _("Can't fetch FSFS shared data"));
88 ffsd = apr_pcalloc(common_pool, sizeof(*ffsd));
89 ffsd->common_pool = common_pool;
91 /* POSIX fcntl locks are per-process, so we need a mutex for
92 intra-process synchronization when grabbing the repository write
94 SVN_ERR(svn_mutex__init(&ffsd->fs_write_lock,
95 SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
97 /* ... not to mention locking the txn-current file. */
98 SVN_ERR(svn_mutex__init(&ffsd->txn_current_lock,
99 SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
101 SVN_ERR(svn_mutex__init(&ffsd->txn_list_lock,
102 SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
104 key = apr_pstrdup(common_pool, key);
105 status = apr_pool_userdata_set(ffsd, key, NULL, common_pool);
107 return svn_error_wrap_apr(status, _("Can't store FSFS shared data"));
117 /* This function is provided for Subversion 1.0.x compatibility. It
118 has no effect for fsfs backed Subversion filesystems. It conforms
119 to the fs_library_vtable_t.bdb_set_errcall() API. */
121 fs_set_errcall(svn_fs_t *fs,
122 void (*db_errcall_fcn)(const char *errpfx, char *msg))
128 struct fs_freeze_baton_t {
130 svn_fs_freeze_func_t freeze_func;
135 fs_freeze_body(void *baton,
138 struct fs_freeze_baton_t *b = baton;
139 svn_boolean_t exists;
141 SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, b->fs, pool));
143 SVN_ERR(svn_fs_fs__lock_rep_cache(b->fs, pool));
145 SVN_ERR(b->freeze_func(b->freeze_baton, pool));
151 fs_freeze(svn_fs_t *fs,
152 svn_fs_freeze_func_t freeze_func,
156 struct fs_freeze_baton_t b;
159 b.freeze_func = freeze_func;
160 b.freeze_baton = freeze_baton;
162 SVN_ERR(svn_fs__check_fs(fs, TRUE));
163 SVN_ERR(svn_fs_fs__with_write_lock(fs, fs_freeze_body, &b, pool));
170 /* The vtable associated with a specific open filesystem. */
171 static fs_vtable_t fs_vtable = {
172 svn_fs_fs__youngest_rev,
173 svn_fs_fs__revision_prop,
174 svn_fs_fs__revision_proplist,
175 svn_fs_fs__change_rev_prop,
177 svn_fs_fs__revision_root,
178 svn_fs_fs__begin_txn,
180 svn_fs_fs__purge_txn,
181 svn_fs_fs__list_transactions,
184 svn_fs_fs__generate_lock_token,
187 svn_fs_fs__get_locks,
188 svn_fs_fs__verify_root,
194 /* Creating a new filesystem. */
196 /* Set up vtable and fsap_data fields in FS. */
198 initialize_fs_struct(svn_fs_t *fs)
200 fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd));
201 fs->vtable = &fs_vtable;
206 /* This implements the fs_library_vtable_t.create() API. Create a new
207 fsfs-backed Subversion filesystem at path PATH and link it into
208 *FS. Perform temporary allocations in POOL, and fs-global allocations
211 fs_create(svn_fs_t *fs, const char *path, apr_pool_t *pool,
212 apr_pool_t *common_pool)
214 SVN_ERR(svn_fs__check_fs(fs, FALSE));
216 SVN_ERR(initialize_fs_struct(fs));
218 SVN_ERR(svn_fs_fs__create(fs, path, pool));
220 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
221 return fs_serialized_init(fs, common_pool, pool);
226 /* Gaining access to an existing filesystem. */
228 /* This implements the fs_library_vtable_t.open() API. Open an FSFS
229 Subversion filesystem located at PATH, set *FS to point to the
230 correct vtable for the filesystem. Use POOL for any temporary
231 allocations, and COMMON_POOL for fs-global allocations. */
233 fs_open(svn_fs_t *fs, const char *path, apr_pool_t *pool,
234 apr_pool_t *common_pool)
236 SVN_ERR(initialize_fs_struct(fs));
238 SVN_ERR(svn_fs_fs__open(fs, path, pool));
240 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
241 return fs_serialized_init(fs, common_pool, pool);
246 /* This implements the fs_library_vtable_t.open_for_recovery() API. */
248 fs_open_for_recovery(svn_fs_t *fs,
250 apr_pool_t *pool, apr_pool_t *common_pool)
252 /* Recovery for FSFS is currently limited to recreating the 'current'
253 file from the latest revision. */
255 /* The only thing we have to watch out for is that the 'current' file
256 might not exist. So we'll try to create it here unconditionally,
257 and just ignore any errors that might indicate that it's already
258 present. (We'll need it to exist later anyway as a source for the
259 new file's permissions). */
261 /* Use a partly-filled fs pointer first to create 'current'. This will fail
262 if 'current' already exists, but we don't care about that. */
263 fs->path = apr_pstrdup(fs->pool, path);
264 svn_error_clear(svn_io_file_create(svn_fs_fs__path_current(fs, pool),
267 /* Now open the filesystem properly by calling the vtable method directly. */
268 return fs_open(fs, path, pool, common_pool);
273 /* This implements the fs_library_vtable_t.upgrade_fs() API. */
275 fs_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool,
276 apr_pool_t *common_pool)
278 SVN_ERR(svn_fs__check_fs(fs, FALSE));
279 SVN_ERR(initialize_fs_struct(fs));
280 SVN_ERR(svn_fs_fs__open(fs, path, pool));
281 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
282 SVN_ERR(fs_serialized_init(fs, common_pool, pool));
283 return svn_fs_fs__upgrade(fs, pool);
287 fs_verify(svn_fs_t *fs, const char *path,
290 svn_fs_progress_notify_func_t notify_func,
292 svn_cancel_func_t cancel_func,
295 apr_pool_t *common_pool)
297 SVN_ERR(svn_fs__check_fs(fs, FALSE));
298 SVN_ERR(initialize_fs_struct(fs));
299 SVN_ERR(svn_fs_fs__open(fs, path, pool));
300 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
301 SVN_ERR(fs_serialized_init(fs, common_pool, pool));
302 return svn_fs_fs__verify(fs, start, end, notify_func, notify_baton,
303 cancel_func, cancel_baton, pool);
307 fs_pack(svn_fs_t *fs,
309 svn_fs_pack_notify_t notify_func,
311 svn_cancel_func_t cancel_func,
314 apr_pool_t *common_pool)
316 SVN_ERR(svn_fs__check_fs(fs, FALSE));
317 SVN_ERR(initialize_fs_struct(fs));
318 SVN_ERR(svn_fs_fs__open(fs, path, pool));
319 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
320 SVN_ERR(fs_serialized_init(fs, common_pool, pool));
321 return svn_fs_fs__pack(fs, notify_func, notify_baton,
322 cancel_func, cancel_baton, pool);
328 /* This implements the fs_library_vtable_t.hotcopy() API. Copy a
329 possibly live Subversion filesystem SRC_FS from SRC_PATH to a
330 DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to
331 re-copy data which already exists in DST_FS.
332 The CLEAN_LOGS argument is ignored and included for Subversion
333 1.0.x compatibility. Perform all temporary allocations in POOL. */
335 fs_hotcopy(svn_fs_t *src_fs,
337 const char *src_path,
338 const char *dst_path,
339 svn_boolean_t clean_logs,
340 svn_boolean_t incremental,
341 svn_cancel_func_t cancel_func,
345 SVN_ERR(svn_fs__check_fs(src_fs, FALSE));
346 SVN_ERR(initialize_fs_struct(src_fs));
347 SVN_ERR(svn_fs_fs__open(src_fs, src_path, pool));
348 SVN_ERR(svn_fs_fs__initialize_caches(src_fs, pool));
349 SVN_ERR(fs_serialized_init(src_fs, pool, pool));
351 SVN_ERR(svn_fs__check_fs(dst_fs, FALSE));
352 SVN_ERR(initialize_fs_struct(dst_fs));
353 /* In INCREMENTAL mode, svn_fs_fs__hotcopy() will open DST_FS.
354 Otherwise, it's not an FS yet --- possibly just an empty dir --- so
357 return svn_fs_fs__hotcopy(src_fs, dst_fs, src_path, dst_path,
358 incremental, cancel_func, cancel_baton, pool);
363 /* This function is included for Subversion 1.0.x compatibility. It
364 has no effect for fsfs backed Subversion filesystems. It conforms
365 to the fs_library_vtable_t.bdb_logfiles() API. */
367 fs_logfiles(apr_array_header_t **logfiles,
369 svn_boolean_t only_unused,
372 /* A no-op for FSFS. */
373 *logfiles = apr_array_make(pool, 0, sizeof(const char *));
382 /* Delete the filesystem located at path PATH. Perform any temporary
383 allocations in POOL. */
385 fs_delete_fs(const char *path,
388 /* Remove everything. */
389 return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
392 static const svn_version_t *
399 fs_get_description(void)
401 return _("Module for working with a plain file (FSFS) repository.");
405 fs_set_svn_fs_open(svn_fs_t *fs,
406 svn_error_t *(*svn_fs_open_)(svn_fs_t **,
411 fs_fs_data_t *ffd = fs->fsap_data;
412 ffd->svn_fs_open_ = svn_fs_open_;
417 /* Base FS library vtable, used by the FS loader library. */
419 static fs_library_vtable_t library_vtable = {
423 fs_open_for_recovery,
437 svn_fs_fs__init(const svn_version_t *loader_version,
438 fs_library_vtable_t **vtable, apr_pool_t* common_pool)
440 static const svn_version_checklist_t checklist[] =
442 { "svn_subr", svn_subr_version },
443 { "svn_delta", svn_delta_version },
447 /* Simplified version check to make sure we can safely use the
448 VTABLE parameter. The FS loader does a more exhaustive check. */
449 if (loader_version->major != SVN_VER_MAJOR)
450 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
451 _("Unsupported FS loader version (%d) for fsfs"),
452 loader_version->major);
453 SVN_ERR(svn_ver_check_list2(fs_version(), checklist, svn_ver_equal));
455 *vtable = &library_vtable;