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 /* We also need a mutex for synchronizing access to the active
102 transaction list and free transaction pointer. This one is
103 enabled unconditionally. */
104 SVN_ERR(svn_mutex__init(&ffsd->txn_list_lock,
107 key = apr_pstrdup(common_pool, key);
108 status = apr_pool_userdata_set(ffsd, key, NULL, common_pool);
110 return svn_error_wrap_apr(status, _("Can't store FSFS shared data"));
120 /* This function is provided for Subversion 1.0.x compatibility. It
121 has no effect for fsfs backed Subversion filesystems. It conforms
122 to the fs_library_vtable_t.bdb_set_errcall() API. */
124 fs_set_errcall(svn_fs_t *fs,
125 void (*db_errcall_fcn)(const char *errpfx, char *msg))
131 struct fs_freeze_baton_t {
133 svn_fs_freeze_func_t freeze_func;
138 fs_freeze_body(void *baton,
141 struct fs_freeze_baton_t *b = baton;
142 svn_boolean_t exists;
144 SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, b->fs, pool));
146 SVN_ERR(svn_fs_fs__lock_rep_cache(b->fs, pool));
148 SVN_ERR(b->freeze_func(b->freeze_baton, pool));
154 fs_freeze(svn_fs_t *fs,
155 svn_fs_freeze_func_t freeze_func,
159 struct fs_freeze_baton_t b;
162 b.freeze_func = freeze_func;
163 b.freeze_baton = freeze_baton;
165 SVN_ERR(svn_fs__check_fs(fs, TRUE));
166 SVN_ERR(svn_fs_fs__with_write_lock(fs, fs_freeze_body, &b, pool));
173 /* The vtable associated with a specific open filesystem. */
174 static fs_vtable_t fs_vtable = {
175 svn_fs_fs__youngest_rev,
176 svn_fs_fs__revision_prop,
177 svn_fs_fs__revision_proplist,
178 svn_fs_fs__change_rev_prop,
180 svn_fs_fs__revision_root,
181 svn_fs_fs__begin_txn,
183 svn_fs_fs__purge_txn,
184 svn_fs_fs__list_transactions,
187 svn_fs_fs__generate_lock_token,
190 svn_fs_fs__get_locks,
191 svn_fs_fs__verify_root,
197 /* Creating a new filesystem. */
199 /* Set up vtable and fsap_data fields in FS. */
201 initialize_fs_struct(svn_fs_t *fs)
203 fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd));
204 fs->vtable = &fs_vtable;
209 /* This implements the fs_library_vtable_t.create() API. Create a new
210 fsfs-backed Subversion filesystem at path PATH and link it into
211 *FS. Perform temporary allocations in POOL, and fs-global allocations
214 fs_create(svn_fs_t *fs, const char *path, apr_pool_t *pool,
215 apr_pool_t *common_pool)
217 SVN_ERR(svn_fs__check_fs(fs, FALSE));
219 SVN_ERR(initialize_fs_struct(fs));
221 SVN_ERR(svn_fs_fs__create(fs, path, pool));
223 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
224 return fs_serialized_init(fs, common_pool, pool);
229 /* Gaining access to an existing filesystem. */
231 /* This implements the fs_library_vtable_t.open() API. Open an FSFS
232 Subversion filesystem located at PATH, set *FS to point to the
233 correct vtable for the filesystem. Use POOL for any temporary
234 allocations, and COMMON_POOL for fs-global allocations. */
236 fs_open(svn_fs_t *fs, const char *path, apr_pool_t *pool,
237 apr_pool_t *common_pool)
239 SVN_ERR(initialize_fs_struct(fs));
241 SVN_ERR(svn_fs_fs__open(fs, path, pool));
243 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
244 return fs_serialized_init(fs, common_pool, pool);
249 /* This implements the fs_library_vtable_t.open_for_recovery() API. */
251 fs_open_for_recovery(svn_fs_t *fs,
253 apr_pool_t *pool, apr_pool_t *common_pool)
255 /* Recovery for FSFS is currently limited to recreating the 'current'
256 file from the latest revision. */
258 /* The only thing we have to watch out for is that the 'current' file
259 might not exist. So we'll try to create it here unconditionally,
260 and just ignore any errors that might indicate that it's already
261 present. (We'll need it to exist later anyway as a source for the
262 new file's permissions). */
264 /* Use a partly-filled fs pointer first to create 'current'. This will fail
265 if 'current' already exists, but we don't care about that. */
266 fs->path = apr_pstrdup(fs->pool, path);
267 svn_error_clear(svn_io_file_create(svn_fs_fs__path_current(fs, pool),
270 /* Now open the filesystem properly by calling the vtable method directly. */
271 return fs_open(fs, path, pool, common_pool);
276 /* This implements the fs_library_vtable_t.upgrade_fs() API. */
278 fs_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool,
279 apr_pool_t *common_pool)
281 SVN_ERR(svn_fs__check_fs(fs, FALSE));
282 SVN_ERR(initialize_fs_struct(fs));
283 SVN_ERR(svn_fs_fs__open(fs, path, pool));
284 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
285 SVN_ERR(fs_serialized_init(fs, common_pool, pool));
286 return svn_fs_fs__upgrade(fs, pool);
290 fs_verify(svn_fs_t *fs, const char *path,
293 svn_fs_progress_notify_func_t notify_func,
295 svn_cancel_func_t cancel_func,
298 apr_pool_t *common_pool)
300 SVN_ERR(svn_fs__check_fs(fs, FALSE));
301 SVN_ERR(initialize_fs_struct(fs));
302 SVN_ERR(svn_fs_fs__open(fs, path, pool));
303 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
304 SVN_ERR(fs_serialized_init(fs, common_pool, pool));
305 return svn_fs_fs__verify(fs, start, end, notify_func, notify_baton,
306 cancel_func, cancel_baton, pool);
310 fs_pack(svn_fs_t *fs,
312 svn_fs_pack_notify_t notify_func,
314 svn_cancel_func_t cancel_func,
317 apr_pool_t *common_pool)
319 SVN_ERR(svn_fs__check_fs(fs, FALSE));
320 SVN_ERR(initialize_fs_struct(fs));
321 SVN_ERR(svn_fs_fs__open(fs, path, pool));
322 SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
323 SVN_ERR(fs_serialized_init(fs, common_pool, pool));
324 return svn_fs_fs__pack(fs, notify_func, notify_baton,
325 cancel_func, cancel_baton, pool);
331 /* This implements the fs_library_vtable_t.hotcopy() API. Copy a
332 possibly live Subversion filesystem SRC_FS from SRC_PATH to a
333 DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to
334 re-copy data which already exists in DST_FS.
335 The CLEAN_LOGS argument is ignored and included for Subversion
336 1.0.x compatibility. Perform all temporary allocations in POOL. */
338 fs_hotcopy(svn_fs_t *src_fs,
340 const char *src_path,
341 const char *dst_path,
342 svn_boolean_t clean_logs,
343 svn_boolean_t incremental,
344 svn_cancel_func_t cancel_func,
348 SVN_ERR(svn_fs__check_fs(src_fs, FALSE));
349 SVN_ERR(initialize_fs_struct(src_fs));
350 SVN_ERR(svn_fs_fs__open(src_fs, src_path, pool));
351 SVN_ERR(svn_fs_fs__initialize_caches(src_fs, pool));
352 SVN_ERR(fs_serialized_init(src_fs, pool, pool));
354 SVN_ERR(svn_fs__check_fs(dst_fs, FALSE));
355 SVN_ERR(initialize_fs_struct(dst_fs));
356 /* In INCREMENTAL mode, svn_fs_fs__hotcopy() will open DST_FS.
357 Otherwise, it's not an FS yet --- possibly just an empty dir --- so
360 return svn_fs_fs__hotcopy(src_fs, dst_fs, src_path, dst_path,
361 incremental, cancel_func, cancel_baton, pool);
366 /* This function is included for Subversion 1.0.x compatibility. It
367 has no effect for fsfs backed Subversion filesystems. It conforms
368 to the fs_library_vtable_t.bdb_logfiles() API. */
370 fs_logfiles(apr_array_header_t **logfiles,
372 svn_boolean_t only_unused,
375 /* A no-op for FSFS. */
376 *logfiles = apr_array_make(pool, 0, sizeof(const char *));
385 /* Delete the filesystem located at path PATH. Perform any temporary
386 allocations in POOL. */
388 fs_delete_fs(const char *path,
391 /* Remove everything. */
392 return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
395 static const svn_version_t *
402 fs_get_description(void)
404 return _("Module for working with a plain file (FSFS) repository.");
408 fs_set_svn_fs_open(svn_fs_t *fs,
409 svn_error_t *(*svn_fs_open_)(svn_fs_t **,
414 fs_fs_data_t *ffd = fs->fsap_data;
415 ffd->svn_fs_open_ = svn_fs_open_;
420 /* Base FS library vtable, used by the FS loader library. */
422 static fs_library_vtable_t library_vtable = {
426 fs_open_for_recovery,
440 svn_fs_fs__init(const svn_version_t *loader_version,
441 fs_library_vtable_t **vtable, apr_pool_t* common_pool)
443 static const svn_version_checklist_t checklist[] =
445 { "svn_subr", svn_subr_version },
446 { "svn_delta", svn_delta_version },
450 /* Simplified version check to make sure we can safely use the
451 VTABLE parameter. The FS loader does a more exhaustive check. */
452 if (loader_version->major != SVN_VER_MAJOR)
453 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
454 _("Unsupported FS loader version (%d) for fsfs"),
455 loader_version->major);
456 SVN_ERR(svn_ver_check_list2(fs_version(), checklist, svn_ver_equal));
458 *vtable = &library_vtable;