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"
45 #include "../libsvn_fs/fs-loader.h"
47 /* A prefix for the pool userdata variables used to hold
48 per-filesystem shared data. See fs_serialized_init. */
49 #define SVN_FSFS_SHARED_USERDATA_PREFIX "svn-fsfs-shared-"
54 fs_serialized_init(svn_fs_t *fs, apr_pool_t *common_pool, apr_pool_t *pool)
56 fs_fs_data_t *ffd = fs->fsap_data;
59 fs_fs_shared_data_t *ffsd;
62 /* Note that we are allocating a small amount of long-lived data for
63 each separate repository opened during the lifetime of the
64 svn_fs_initialize pool. It's unlikely that anyone will notice
65 the modest expenditure; the alternative is to allocate each structure
66 in a subpool, add a reference-count, and add a serialized deconstructor
67 to the FS vtable. That's more machinery than it's worth.
69 Using the uuid to obtain the lock creates a corner case if a
70 caller uses svn_fs_set_uuid on the repository in a process where
71 other threads might be using the same repository through another
72 FS object. The only real-world consumer of svn_fs_set_uuid is
73 "svnadmin load", so this is a low-priority problem, and we don't
74 know of a better way of associating such data with the
77 SVN_ERR_ASSERT(fs->uuid);
78 key = apr_pstrcat(pool, SVN_FSFS_SHARED_USERDATA_PREFIX, fs->uuid,
80 status = apr_pool_userdata_get(&val, key, common_pool);
82 return svn_error_wrap_apr(status, _("Can't fetch FSFS shared data"));
87 ffsd = apr_pcalloc(common_pool, sizeof(*ffsd));
88 ffsd->common_pool = common_pool;
90 /* POSIX fcntl locks are per-process, so we need a mutex for
91 intra-process synchronization when grabbing the repository write
93 SVN_ERR(svn_mutex__init(&ffsd->fs_write_lock,
94 SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
96 /* ... not to mention locking the txn-current file. */
97 SVN_ERR(svn_mutex__init(&ffsd->txn_current_lock,
98 SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
100 SVN_ERR(svn_mutex__init(&ffsd->txn_list_lock,
101 SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
103 key = apr_pstrdup(common_pool, key);
104 status = apr_pool_userdata_set(ffsd, key, NULL, common_pool);
106 return svn_error_wrap_apr(status, _("Can't store FSFS shared data"));
116 /* This function is provided for Subversion 1.0.x compatibility. It
117 has no effect for fsfs backed Subversion filesystems. It conforms
118 to the fs_library_vtable_t.bdb_set_errcall() API. */
120 fs_set_errcall(svn_fs_t *fs,
121 void (*db_errcall_fcn)(const char *errpfx, char *msg))
127 struct fs_freeze_baton_t {
129 svn_fs_freeze_func_t freeze_func;
134 fs_freeze_body(void *baton,
137 struct fs_freeze_baton_t *b = baton;
138 svn_boolean_t exists;
140 SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, b->fs, pool));
142 SVN_ERR(svn_fs_fs__lock_rep_cache(b->fs, pool));
144 SVN_ERR(b->freeze_func(b->freeze_baton, pool));
150 fs_freeze(svn_fs_t *fs,
151 svn_fs_freeze_func_t freeze_func,
155 struct fs_freeze_baton_t b;
158 b.freeze_func = freeze_func;
159 b.freeze_baton = freeze_baton;
161 SVN_ERR(svn_fs__check_fs(fs, TRUE));
162 SVN_ERR(svn_fs_fs__with_write_lock(fs, fs_freeze_body, &b, pool));
169 /* The vtable associated with a specific open filesystem. */
170 static fs_vtable_t fs_vtable = {
171 svn_fs_fs__youngest_rev,
172 svn_fs_fs__revision_prop,
173 svn_fs_fs__revision_proplist,
174 svn_fs_fs__change_rev_prop,
176 svn_fs_fs__revision_root,
177 svn_fs_fs__begin_txn,
179 svn_fs_fs__purge_txn,
180 svn_fs_fs__list_transactions,
183 svn_fs_fs__generate_lock_token,
186 svn_fs_fs__get_locks,
187 svn_fs_fs__verify_root,
193 /* Creating a new filesystem. */
195 /* Set up vtable and fsap_data fields in FS. */
197 initialize_fs_struct(svn_fs_t *fs)
199 fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd));
200 fs->vtable = &fs_vtable;
205 /* This implements the fs_library_vtable_t.create() API. Create a new
206 fsfs-backed Subversion filesystem at path PATH and link it into
207 *FS. Perform temporary allocations in POOL, and fs-global allocations
210 fs_create(svn_fs_t *fs, const char *path, apr_pool_t *pool,
211 apr_pool_t *common_pool)
213 SVN_ERR(svn_fs__check_fs(fs, FALSE));
215 SVN_ERR(initialize_fs_struct(fs));
217 SVN_ERR(svn_fs_fs__create(fs, path, pool));
219 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
220 return fs_serialized_init(fs, common_pool, pool);
225 /* Gaining access to an existing filesystem. */
227 /* This implements the fs_library_vtable_t.open() API. Open an FSFS
228 Subversion filesystem located at PATH, set *FS to point to the
229 correct vtable for the filesystem. Use POOL for any temporary
230 allocations, and COMMON_POOL for fs-global allocations. */
232 fs_open(svn_fs_t *fs, const char *path, apr_pool_t *pool,
233 apr_pool_t *common_pool)
235 SVN_ERR(initialize_fs_struct(fs));
237 SVN_ERR(svn_fs_fs__open(fs, path, pool));
239 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
240 return fs_serialized_init(fs, common_pool, pool);
245 /* This implements the fs_library_vtable_t.open_for_recovery() API. */
247 fs_open_for_recovery(svn_fs_t *fs,
249 apr_pool_t *pool, apr_pool_t *common_pool)
251 /* Recovery for FSFS is currently limited to recreating the 'current'
252 file from the latest revision. */
254 /* The only thing we have to watch out for is that the 'current' file
255 might not exist. So we'll try to create it here unconditionally,
256 and just ignore any errors that might indicate that it's already
257 present. (We'll need it to exist later anyway as a source for the
258 new file's permissions). */
260 /* Use a partly-filled fs pointer first to create 'current'. This will fail
261 if 'current' already exists, but we don't care about that. */
262 fs->path = apr_pstrdup(fs->pool, path);
263 svn_error_clear(svn_io_file_create(svn_fs_fs__path_current(fs, pool),
266 /* Now open the filesystem properly by calling the vtable method directly. */
267 return fs_open(fs, path, pool, common_pool);
272 /* This implements the fs_library_vtable_t.upgrade_fs() API. */
274 fs_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool,
275 apr_pool_t *common_pool)
277 SVN_ERR(svn_fs__check_fs(fs, FALSE));
278 SVN_ERR(initialize_fs_struct(fs));
279 SVN_ERR(svn_fs_fs__open(fs, path, pool));
280 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
281 SVN_ERR(fs_serialized_init(fs, common_pool, pool));
282 return svn_fs_fs__upgrade(fs, pool);
286 fs_verify(svn_fs_t *fs, const char *path,
289 svn_fs_progress_notify_func_t notify_func,
291 svn_cancel_func_t cancel_func,
294 apr_pool_t *common_pool)
296 SVN_ERR(svn_fs__check_fs(fs, FALSE));
297 SVN_ERR(initialize_fs_struct(fs));
298 SVN_ERR(svn_fs_fs__open(fs, path, pool));
299 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
300 SVN_ERR(fs_serialized_init(fs, common_pool, pool));
301 return svn_fs_fs__verify(fs, start, end, notify_func, notify_baton,
302 cancel_func, cancel_baton, pool);
306 fs_pack(svn_fs_t *fs,
308 svn_fs_pack_notify_t notify_func,
310 svn_cancel_func_t cancel_func,
313 apr_pool_t *common_pool)
315 SVN_ERR(svn_fs__check_fs(fs, FALSE));
316 SVN_ERR(initialize_fs_struct(fs));
317 SVN_ERR(svn_fs_fs__open(fs, path, pool));
318 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
319 SVN_ERR(fs_serialized_init(fs, common_pool, pool));
320 return svn_fs_fs__pack(fs, notify_func, notify_baton,
321 cancel_func, cancel_baton, pool);
327 /* This implements the fs_library_vtable_t.hotcopy() API. Copy a
328 possibly live Subversion filesystem SRC_FS from SRC_PATH to a
329 DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to
330 re-copy data which already exists in DST_FS.
331 The CLEAN_LOGS argument is ignored and included for Subversion
332 1.0.x compatibility. Perform all temporary allocations in POOL. */
334 fs_hotcopy(svn_fs_t *src_fs,
336 const char *src_path,
337 const char *dst_path,
338 svn_boolean_t clean_logs,
339 svn_boolean_t incremental,
340 svn_cancel_func_t cancel_func,
344 SVN_ERR(svn_fs__check_fs(src_fs, FALSE));
345 SVN_ERR(initialize_fs_struct(src_fs));
346 SVN_ERR(svn_fs_fs__open(src_fs, src_path, pool));
347 SVN_ERR(svn_fs_fs__initialize_caches(src_fs, pool));
348 SVN_ERR(fs_serialized_init(src_fs, pool, pool));
350 SVN_ERR(svn_fs__check_fs(dst_fs, FALSE));
351 SVN_ERR(initialize_fs_struct(dst_fs));
352 /* In INCREMENTAL mode, svn_fs_fs__hotcopy() will open DST_FS.
353 Otherwise, it's not an FS yet --- possibly just an empty dir --- so
356 return svn_fs_fs__hotcopy(src_fs, dst_fs, src_path, dst_path,
357 incremental, cancel_func, cancel_baton, pool);
362 /* This function is included for Subversion 1.0.x compatibility. It
363 has no effect for fsfs backed Subversion filesystems. It conforms
364 to the fs_library_vtable_t.bdb_logfiles() API. */
366 fs_logfiles(apr_array_header_t **logfiles,
368 svn_boolean_t only_unused,
371 /* A no-op for FSFS. */
372 *logfiles = apr_array_make(pool, 0, sizeof(const char *));
381 /* Delete the filesystem located at path PATH. Perform any temporary
382 allocations in POOL. */
384 fs_delete_fs(const char *path,
387 /* Remove everything. */
388 return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
391 static const svn_version_t *
398 fs_get_description(void)
400 return _("Module for working with a plain file (FSFS) repository.");
404 fs_set_svn_fs_open(svn_fs_t *fs,
405 svn_error_t *(*svn_fs_open_)(svn_fs_t **,
410 fs_fs_data_t *ffd = fs->fsap_data;
411 ffd->svn_fs_open_ = svn_fs_open_;
416 /* Base FS library vtable, used by the FS loader library. */
418 static fs_library_vtable_t library_vtable = {
422 fs_open_for_recovery,
436 svn_fs_fs__init(const svn_version_t *loader_version,
437 fs_library_vtable_t **vtable, apr_pool_t* common_pool)
439 static const svn_version_checklist_t checklist[] =
441 { "svn_subr", svn_subr_version },
442 { "svn_delta", svn_delta_version },
446 /* Simplified version check to make sure we can safely use the
447 VTABLE parameter. The FS loader does a more exhaustive check. */
448 if (loader_version->major != SVN_VER_MAJOR)
449 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
450 _("Unsupported FS loader version (%d) for fsfs"),
451 loader_version->major);
452 SVN_ERR(svn_ver_check_list(fs_version(), checklist));
454 *vtable = &library_vtable;