1 /* fs.c --- creating, opening and closing filesystems
2 *
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
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
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
19 * under the License.
20 * ====================================================================
21 */
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include <apr_general.h>
28 #include <apr_pools.h>
29 #include <apr_file_io.h>
30
31 #include "svn_fs.h"
32 #include "svn_delta.h"
33 #include "svn_version.h"
34 #include "svn_pools.h"
35 #include "fs.h"
36 #include "fs_fs.h"
37 #include "tree.h"
38 #include "lock.h"
39 #include "hotcopy.h"
40 #include "id.h"
41 #include "pack.h"
42 #include "recovery.h"
43 #include "rep-cache.h"
44 #include "revprops.h"
45 #include "transaction.h"
46 #include "util.h"
47 #include "verify.h"
48 #include "svn_private_config.h"
49 #include "private/svn_fs_util.h"
50 #include "private/svn_fs_fs_private.h"
51
52 #include "../libsvn_fs/fs-loader.h"
53
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-"
57
58
59
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. */
63 static svn_error_t *
fs_serialized_init(svn_fs_t * fs,apr_pool_t * common_pool,apr_pool_t * pool)64 fs_serialized_init(svn_fs_t *fs, apr_pool_t *common_pool, apr_pool_t *pool)
65 {
66 fs_fs_data_t *ffd = fs->fsap_data;
67 const char *key;
68 void *val;
69 fs_fs_shared_data_t *ffsd;
70 apr_status_t status;
71
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.
78
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.
87
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(). */
93
94 SVN_ERR_ASSERT(fs->uuid);
95 SVN_ERR_ASSERT(ffd->instance_id);
96
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);
100 if (status)
101 return svn_error_wrap_apr(status, _("Can't fetch FSFS shared data"));
102 ffsd = val;
103
104 if (!ffsd)
105 {
106 ffsd = apr_pcalloc(common_pool, sizeof(*ffsd));
107 ffsd->common_pool = common_pool;
108
109 /* POSIX fcntl locks are per-process, so we need a mutex for
110 intra-process synchronization when grabbing the repository write
111 lock. */
112 SVN_ERR(svn_mutex__init(&ffsd->fs_write_lock,
113 SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
114
115 /* ... the pack lock ... */
116 SVN_ERR(svn_mutex__init(&ffsd->fs_pack_lock,
117 SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
118
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));
122
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));
126
127 key = apr_pstrdup(common_pool, key);
128 status = apr_pool_userdata_set(ffsd, key, NULL, common_pool);
129 if (status)
130 return svn_error_wrap_apr(status, _("Can't store FSFS shared data"));
131 }
132
133 ffd->shared = ffsd;
134
135 return SVN_NO_ERROR;
136 }
137
138 svn_error_t *
svn_fs_fs__initialize_shared_data(svn_fs_t * fs,svn_mutex__t * common_pool_lock,apr_pool_t * pool,apr_pool_t * common_pool)139 svn_fs_fs__initialize_shared_data(svn_fs_t *fs,
140 svn_mutex__t *common_pool_lock,
141 apr_pool_t *pool,
142 apr_pool_t *common_pool)
143 {
144 SVN_MUTEX__WITH_LOCK(common_pool_lock,
145 fs_serialized_init(fs, common_pool, pool));
146
147 return SVN_NO_ERROR;
148 }
149
150
151
152 static svn_error_t *
fs_refresh_revprops(svn_fs_t * fs,apr_pool_t * scratch_pool)153 fs_refresh_revprops(svn_fs_t *fs,
154 apr_pool_t *scratch_pool)
155 {
156 svn_fs_fs__reset_revprop_cache(fs);
157
158 return SVN_NO_ERROR;
159 }
160
161 /* This function is provided for Subversion 1.0.x compatibility. It
162 has no effect for fsfs backed Subversion filesystems. It conforms
163 to the fs_library_vtable_t.bdb_set_errcall() API. */
164 static svn_error_t *
fs_set_errcall(svn_fs_t * fs,void (* db_errcall_fcn)(const char * errpfx,char * msg))165 fs_set_errcall(svn_fs_t *fs,
166 void (*db_errcall_fcn)(const char *errpfx, char *msg))
167 {
168
169 return SVN_NO_ERROR;
170 }
171
172 struct fs_freeze_baton_t {
173 svn_fs_t *fs;
174 svn_fs_freeze_func_t freeze_func;
175 void *freeze_baton;
176 };
177
178 static svn_error_t *
fs_freeze_body(void * baton,apr_pool_t * pool)179 fs_freeze_body(void *baton,
180 apr_pool_t *pool)
181 {
182 struct fs_freeze_baton_t *b = baton;
183 svn_boolean_t exists;
184
185 SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, b->fs, pool));
186 if (exists)
187 SVN_ERR(svn_fs_fs__with_rep_cache_lock(b->fs,
188 b->freeze_func, b->freeze_baton,
189 pool));
190 else
191 SVN_ERR(b->freeze_func(b->freeze_baton, pool));
192
193 return SVN_NO_ERROR;
194 }
195
196 static svn_error_t *
fs_freeze_body2(void * baton,apr_pool_t * pool)197 fs_freeze_body2(void *baton,
198 apr_pool_t *pool)
199 {
200 struct fs_freeze_baton_t *b = baton;
201 SVN_ERR(svn_fs_fs__with_write_lock(b->fs, fs_freeze_body, baton, pool));
202
203 return SVN_NO_ERROR;
204 }
205
206 static svn_error_t *
fs_freeze(svn_fs_t * fs,svn_fs_freeze_func_t freeze_func,void * freeze_baton,apr_pool_t * pool)207 fs_freeze(svn_fs_t *fs,
208 svn_fs_freeze_func_t freeze_func,
209 void *freeze_baton,
210 apr_pool_t *pool)
211 {
212 fs_fs_data_t *ffd = fs->fsap_data;
213 struct fs_freeze_baton_t b;
214
215 b.fs = fs;
216 b.freeze_func = freeze_func;
217 b.freeze_baton = freeze_baton;
218
219 SVN_ERR(svn_fs__check_fs(fs, TRUE));
220
221 if (ffd->format >= SVN_FS_FS__MIN_PACK_LOCK_FORMAT)
222 SVN_ERR(svn_fs_fs__with_pack_lock(fs, fs_freeze_body2, &b, pool));
223 else
224 SVN_ERR(fs_freeze_body2(&b, pool));
225
226 return SVN_NO_ERROR;
227 }
228
229 static svn_error_t *
fs_info(const void ** fsfs_info,svn_fs_t * fs,apr_pool_t * result_pool,apr_pool_t * scratch_pool)230 fs_info(const void **fsfs_info,
231 svn_fs_t *fs,
232 apr_pool_t *result_pool,
233 apr_pool_t *scratch_pool)
234 {
235 fs_fs_data_t *ffd = fs->fsap_data;
236 svn_fs_fsfs_info_t *info = apr_palloc(result_pool, sizeof(*info));
237 info->fs_type = SVN_FS_TYPE_FSFS;
238 info->shard_size = ffd->max_files_per_dir;
239 info->min_unpacked_rev = ffd->min_unpacked_rev;
240 info->log_addressing = ffd->use_log_addressing;
241 *fsfs_info = info;
242 return SVN_NO_ERROR;
243 }
244
245 /* Wrapper around svn_fs_fs__set_uuid() adapting between function
246 signatures. */
247 static svn_error_t *
fs_set_uuid(svn_fs_t * fs,const char * uuid,apr_pool_t * pool)248 fs_set_uuid(svn_fs_t *fs,
249 const char *uuid,
250 apr_pool_t *pool)
251 {
252 /* Whenever we set a new UUID, imply that FS will also be a different
253 * instance (on formats that support this). */
254 return svn_error_trace(svn_fs_fs__set_uuid(fs, uuid, NULL, pool));
255 }
256
257
258 static svn_error_t *
fs_ioctl(svn_fs_t * fs,svn_fs_ioctl_code_t ctlcode,void * input_void,void ** output_p,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)259 fs_ioctl(svn_fs_t *fs, svn_fs_ioctl_code_t ctlcode,
260 void *input_void, void **output_p,
261 svn_cancel_func_t cancel_func,
262 void *cancel_baton,
263 apr_pool_t *result_pool,
264 apr_pool_t *scratch_pool)
265 {
266 if (strcmp(ctlcode.fs_type, SVN_FS_TYPE_FSFS) == 0)
267 {
268 if (ctlcode.code == SVN_FS_FS__IOCTL_GET_STATS.code)
269 {
270 svn_fs_fs__ioctl_get_stats_input_t *input = input_void;
271 svn_fs_fs__ioctl_get_stats_output_t *output;
272
273 output = apr_pcalloc(result_pool, sizeof(*output));
274 SVN_ERR(svn_fs_fs__get_stats(&output->stats, fs,
275 input->progress_func,
276 input->progress_baton,
277 cancel_func, cancel_baton,
278 result_pool, scratch_pool));
279 *output_p = output;
280 return SVN_NO_ERROR;
281 }
282 else if (ctlcode.code == SVN_FS_FS__IOCTL_DUMP_INDEX.code)
283 {
284 svn_fs_fs__ioctl_dump_index_input_t *input = input_void;
285
286 SVN_ERR(svn_fs_fs__dump_index(fs, input->revision,
287 input->callback_func,
288 input->callback_baton,
289 cancel_func, cancel_baton,
290 scratch_pool));
291 *output_p = NULL;
292 return SVN_NO_ERROR;
293 }
294 else if (ctlcode.code == SVN_FS_FS__IOCTL_LOAD_INDEX.code)
295 {
296 svn_fs_fs__ioctl_load_index_input_t *input = input_void;
297
298 SVN_ERR(svn_fs_fs__load_index(fs, input->revision, input->entries,
299 scratch_pool));
300 *output_p = NULL;
301 return SVN_NO_ERROR;
302 }
303 else if (ctlcode.code == SVN_FS_FS__IOCTL_REVISION_SIZE.code)
304 {
305 svn_fs_fs__ioctl_revision_size_input_t *input = input_void;
306 svn_fs_fs__ioctl_revision_size_output_t *output
307 = apr_pcalloc(result_pool, sizeof(*output));
308
309 SVN_ERR(svn_fs_fs__revision_size(&output->rev_size,
310 fs, input->revision,
311 scratch_pool));
312 *output_p = output;
313 return SVN_NO_ERROR;
314 }
315 else if (ctlcode.code == SVN_FS_FS__IOCTL_BUILD_REP_CACHE.code)
316 {
317 svn_fs_fs__ioctl_build_rep_cache_input_t *input = input_void;
318
319 SVN_ERR(svn_fs_fs__build_rep_cache(fs,
320 input->start_rev,
321 input->end_rev,
322 input->progress_func,
323 input->progress_baton,
324 cancel_func,
325 cancel_baton,
326 scratch_pool));
327
328 *output_p = NULL;
329 return SVN_NO_ERROR;
330 }
331 }
332
333 return svn_error_create(SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE, NULL, NULL);
334 }
335
336 /* The vtable associated with a specific open filesystem. */
337 static fs_vtable_t fs_vtable = {
338 svn_fs_fs__youngest_rev,
339 fs_refresh_revprops,
340 svn_fs_fs__revision_prop,
341 svn_fs_fs__get_revision_proplist,
342 svn_fs_fs__change_rev_prop,
343 fs_set_uuid,
344 svn_fs_fs__revision_root,
345 svn_fs_fs__begin_txn,
346 svn_fs_fs__open_txn,
347 svn_fs_fs__purge_txn,
348 svn_fs_fs__list_transactions,
349 svn_fs_fs__deltify,
350 svn_fs_fs__lock,
351 svn_fs_fs__generate_lock_token,
352 svn_fs_fs__unlock,
353 svn_fs_fs__get_lock,
354 svn_fs_fs__get_locks,
355 svn_fs_fs__info_format,
356 svn_fs_fs__info_config_files,
357 fs_info,
358 svn_fs_fs__verify_root,
359 fs_freeze,
360 fs_set_errcall,
361 fs_ioctl
362 };
363
364
365 /* Creating a new filesystem. */
366
367 /* Set up vtable and fsap_data fields in FS. */
368 static svn_error_t *
initialize_fs_struct(svn_fs_t * fs)369 initialize_fs_struct(svn_fs_t *fs)
370 {
371 fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd));
372 ffd->use_log_addressing = FALSE;
373 ffd->revprop_prefix = 0;
374 ffd->flush_to_disk = TRUE;
375
376 fs->vtable = &fs_vtable;
377 fs->fsap_data = ffd;
378 return SVN_NO_ERROR;
379 }
380
381 /* Reset vtable and fsap_data fields in FS such that the FS is basically
382 * closed now. Note that FS must not hold locks when you call this. */
383 static void
uninitialize_fs_struct(svn_fs_t * fs)384 uninitialize_fs_struct(svn_fs_t *fs)
385 {
386 fs->vtable = NULL;
387 fs->fsap_data = NULL;
388 }
389
390 /* This implements the fs_library_vtable_t.create() API. Create a new
391 fsfs-backed Subversion filesystem at path PATH and link it into
392 *FS. Perform temporary allocations in POOL, and fs-global allocations
393 in COMMON_POOL. The latter must be serialized using COMMON_POOL_LOCK. */
394 static svn_error_t *
fs_create(svn_fs_t * fs,const char * path,svn_mutex__t * common_pool_lock,apr_pool_t * scratch_pool,apr_pool_t * common_pool)395 fs_create(svn_fs_t *fs,
396 const char *path,
397 svn_mutex__t *common_pool_lock,
398 apr_pool_t *scratch_pool,
399 apr_pool_t *common_pool)
400 {
401 SVN_ERR(svn_fs__check_fs(fs, FALSE));
402
403 SVN_ERR(initialize_fs_struct(fs));
404
405 SVN_ERR(svn_fs_fs__create(fs, path, scratch_pool));
406
407 SVN_ERR(svn_fs_fs__initialize_caches(fs, scratch_pool));
408 SVN_MUTEX__WITH_LOCK(common_pool_lock,
409 fs_serialized_init(fs, common_pool, scratch_pool));
410
411 return SVN_NO_ERROR;
412 }
413
414
415
416 /* Gaining access to an existing filesystem. */
417
418 /* This implements the fs_library_vtable_t.open() API. Open an FSFS
419 Subversion filesystem located at PATH, set *FS to point to the
420 correct vtable for the filesystem. Use POOL for any temporary
421 allocations, and COMMON_POOL for fs-global allocations.
422 The latter must be serialized using COMMON_POOL_LOCK. */
423 static svn_error_t *
fs_open(svn_fs_t * fs,const char * path,svn_mutex__t * common_pool_lock,apr_pool_t * scratch_pool,apr_pool_t * common_pool)424 fs_open(svn_fs_t *fs,
425 const char *path,
426 svn_mutex__t *common_pool_lock,
427 apr_pool_t *scratch_pool,
428 apr_pool_t *common_pool)
429 {
430 apr_pool_t *subpool = svn_pool_create(scratch_pool);
431
432 SVN_ERR(svn_fs__check_fs(fs, FALSE));
433
434 SVN_ERR(initialize_fs_struct(fs));
435
436 SVN_ERR(svn_fs_fs__open(fs, path, subpool));
437
438 SVN_ERR(svn_fs_fs__initialize_caches(fs, subpool));
439 SVN_MUTEX__WITH_LOCK(common_pool_lock,
440 fs_serialized_init(fs, common_pool, subpool));
441
442 svn_pool_destroy(subpool);
443
444 return SVN_NO_ERROR;
445 }
446
447
448
449 /* This implements the fs_library_vtable_t.open_for_recovery() API. */
450 static svn_error_t *
fs_open_for_recovery(svn_fs_t * fs,const char * path,svn_mutex__t * common_pool_lock,apr_pool_t * pool,apr_pool_t * common_pool)451 fs_open_for_recovery(svn_fs_t *fs,
452 const char *path,
453 svn_mutex__t *common_pool_lock,
454 apr_pool_t *pool,
455 apr_pool_t *common_pool)
456 {
457 svn_error_t * err;
458 svn_revnum_t youngest_rev;
459 apr_pool_t * subpool = svn_pool_create(pool);
460
461 /* Recovery for FSFS is currently limited to recreating the 'current'
462 file from the latest revision. */
463
464 /* The only thing we have to watch out for is that the 'current' file
465 might not exist or contain garbage. So we'll try to read it here
466 and provide or replace the existing file if we couldn't read it.
467 (We'll also need it to exist later anyway as a source for the new
468 file's permissions). */
469
470 /* Use a partly-filled fs pointer first to create 'current'. */
471 fs->path = apr_pstrdup(fs->pool, path);
472
473 SVN_ERR(initialize_fs_struct(fs));
474
475 /* Figure out the repo format and check that we can even handle it. */
476 SVN_ERR(svn_fs_fs__read_format_file(fs, subpool));
477
478 /* Now, read 'current' and try to patch it if necessary. */
479 err = svn_fs_fs__youngest_rev(&youngest_rev, fs, subpool);
480 if (err)
481 {
482 const char *file_path;
483
484 /* 'current' file is missing or contains garbage. Since we are trying
485 * to recover from whatever problem there is, being picky about the
486 * error code here won't do us much good. If there is a persistent
487 * problem that we can't fix, it will show up when we try rewrite the
488 * file a few lines further below and we will report the failure back
489 * to the caller.
490 *
491 * Start recovery with HEAD = 0. */
492 svn_error_clear(err);
493 file_path = svn_fs_fs__path_current(fs, subpool);
494
495 /* Best effort to ensure the file exists and is valid.
496 * This may fail for r/o filesystems etc. */
497 SVN_ERR(svn_io_remove_file2(file_path, TRUE, subpool));
498 SVN_ERR(svn_io_file_create_empty(file_path, subpool));
499 SVN_ERR(svn_fs_fs__write_current(fs, 0, 1, 1, subpool));
500 }
501
502 uninitialize_fs_struct(fs);
503 svn_pool_destroy(subpool);
504
505 /* Now open the filesystem properly by calling the vtable method directly. */
506 return fs_open(fs, path, common_pool_lock, pool, common_pool);
507 }
508
509
510
511 /* This implements the fs_library_vtable_t.upgrade_fs() API. */
512 static svn_error_t *
fs_upgrade(svn_fs_t * fs,const char * path,svn_fs_upgrade_notify_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_mutex__t * common_pool_lock,apr_pool_t * pool,apr_pool_t * common_pool)513 fs_upgrade(svn_fs_t *fs,
514 const char *path,
515 svn_fs_upgrade_notify_t notify_func,
516 void *notify_baton,
517 svn_cancel_func_t cancel_func,
518 void *cancel_baton,
519 svn_mutex__t *common_pool_lock,
520 apr_pool_t *pool,
521 apr_pool_t *common_pool)
522 {
523 SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
524 return svn_fs_fs__upgrade(fs, notify_func, notify_baton,
525 cancel_func, cancel_baton, pool);
526 }
527
528 static svn_error_t *
fs_verify(svn_fs_t * fs,const char * path,svn_revnum_t start,svn_revnum_t end,svn_fs_progress_notify_func_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_mutex__t * common_pool_lock,apr_pool_t * pool,apr_pool_t * common_pool)529 fs_verify(svn_fs_t *fs, const char *path,
530 svn_revnum_t start,
531 svn_revnum_t end,
532 svn_fs_progress_notify_func_t notify_func,
533 void *notify_baton,
534 svn_cancel_func_t cancel_func,
535 void *cancel_baton,
536 svn_mutex__t *common_pool_lock,
537 apr_pool_t *pool,
538 apr_pool_t *common_pool)
539 {
540 SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
541 return svn_fs_fs__verify(fs, start, end, notify_func, notify_baton,
542 cancel_func, cancel_baton, pool);
543 }
544
545 static svn_error_t *
fs_pack(svn_fs_t * fs,const char * path,svn_fs_pack_notify_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_mutex__t * common_pool_lock,apr_pool_t * pool,apr_pool_t * common_pool)546 fs_pack(svn_fs_t *fs,
547 const char *path,
548 svn_fs_pack_notify_t notify_func,
549 void *notify_baton,
550 svn_cancel_func_t cancel_func,
551 void *cancel_baton,
552 svn_mutex__t *common_pool_lock,
553 apr_pool_t *pool,
554 apr_pool_t *common_pool)
555 {
556 SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
557 return svn_fs_fs__pack(fs, 0, notify_func, notify_baton,
558 cancel_func, cancel_baton, pool);
559 }
560
561
562
563
564 /* This implements the fs_library_vtable_t.hotcopy() API. Copy a
565 possibly live Subversion filesystem SRC_FS from SRC_PATH to a
566 DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to
567 re-copy data which already exists in DST_FS.
568 The CLEAN_LOGS argument is ignored and included for Subversion
569 1.0.x compatibility. Indicate progress via the optional NOTIFY_FUNC
570 callback using NOTIFY_BATON. Perform all temporary allocations in POOL. */
571 static svn_error_t *
fs_hotcopy(svn_fs_t * src_fs,svn_fs_t * dst_fs,const char * src_path,const char * dst_path,svn_boolean_t clean_logs,svn_boolean_t incremental,svn_fs_hotcopy_notify_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_mutex__t * common_pool_lock,apr_pool_t * pool,apr_pool_t * common_pool)572 fs_hotcopy(svn_fs_t *src_fs,
573 svn_fs_t *dst_fs,
574 const char *src_path,
575 const char *dst_path,
576 svn_boolean_t clean_logs,
577 svn_boolean_t incremental,
578 svn_fs_hotcopy_notify_t notify_func,
579 void *notify_baton,
580 svn_cancel_func_t cancel_func,
581 void *cancel_baton,
582 svn_mutex__t *common_pool_lock,
583 apr_pool_t *pool,
584 apr_pool_t *common_pool)
585 {
586 SVN_ERR(fs_open(src_fs, src_path, common_pool_lock, pool, common_pool));
587
588 SVN_ERR(svn_fs__check_fs(dst_fs, FALSE));
589 SVN_ERR(initialize_fs_struct(dst_fs));
590
591 /* In INCREMENTAL mode, svn_fs_fs__hotcopy() will open DST_FS.
592 Otherwise, it's not an FS yet --- possibly just an empty dir --- so
593 can't be opened.
594 */
595 return svn_fs_fs__hotcopy(src_fs, dst_fs, src_path, dst_path,
596 incremental, notify_func, notify_baton,
597 cancel_func, cancel_baton, common_pool_lock,
598 pool, common_pool);
599 }
600
601
602
603 /* This function is included for Subversion 1.0.x compatibility. It
604 has no effect for fsfs backed Subversion filesystems. It conforms
605 to the fs_library_vtable_t.bdb_logfiles() API. */
606 static svn_error_t *
fs_logfiles(apr_array_header_t ** logfiles,const char * path,svn_boolean_t only_unused,apr_pool_t * pool)607 fs_logfiles(apr_array_header_t **logfiles,
608 const char *path,
609 svn_boolean_t only_unused,
610 apr_pool_t *pool)
611 {
612 /* A no-op for FSFS. */
613 *logfiles = apr_array_make(pool, 0, sizeof(const char *));
614
615 return SVN_NO_ERROR;
616 }
617
618
619
620
621
622 /* Delete the filesystem located at path PATH. Perform any temporary
623 allocations in POOL. */
624 static svn_error_t *
fs_delete_fs(const char * path,apr_pool_t * pool)625 fs_delete_fs(const char *path,
626 apr_pool_t *pool)
627 {
628 /* Remove everything. */
629 return svn_error_trace(svn_io_remove_dir2(path, FALSE, NULL, NULL, pool));
630 }
631
632 static const svn_version_t *
fs_version(void)633 fs_version(void)
634 {
635 SVN_VERSION_BODY;
636 }
637
638 static const char *
fs_get_description(void)639 fs_get_description(void)
640 {
641 return _("Module for working with a plain file (FSFS) repository.");
642 }
643
644 static svn_error_t *
fs_set_svn_fs_open(svn_fs_t * fs,svn_error_t * (* svn_fs_open_)(svn_fs_t **,const char *,apr_hash_t *,apr_pool_t *,apr_pool_t *))645 fs_set_svn_fs_open(svn_fs_t *fs,
646 svn_error_t *(*svn_fs_open_)(svn_fs_t **,
647 const char *,
648 apr_hash_t *,
649 apr_pool_t *,
650 apr_pool_t *))
651 {
652 fs_fs_data_t *ffd = fs->fsap_data;
653 ffd->svn_fs_open_ = svn_fs_open_;
654 return SVN_NO_ERROR;
655 }
656
657 static void *
fs_info_dup(const void * fsfs_info_void,apr_pool_t * result_pool)658 fs_info_dup(const void *fsfs_info_void,
659 apr_pool_t *result_pool)
660 {
661 /* All fields are either ints or static strings. */
662 const svn_fs_fsfs_info_t *fsfs_info = fsfs_info_void;
663 return apr_pmemdup(result_pool, fsfs_info, sizeof(*fsfs_info));
664 }
665
666
667 /* Base FS library vtable, used by the FS loader library. */
668
669 static fs_library_vtable_t library_vtable = {
670 fs_version,
671 fs_create,
672 fs_open,
673 fs_open_for_recovery,
674 fs_upgrade,
675 fs_verify,
676 fs_delete_fs,
677 fs_hotcopy,
678 fs_get_description,
679 svn_fs_fs__recover,
680 fs_pack,
681 fs_logfiles,
682 NULL /* parse_id */,
683 fs_set_svn_fs_open,
684 fs_info_dup,
685 NULL /* ioctl */
686 };
687
688 svn_error_t *
svn_fs_fs__init(const svn_version_t * loader_version,fs_library_vtable_t ** vtable,apr_pool_t * common_pool)689 svn_fs_fs__init(const svn_version_t *loader_version,
690 fs_library_vtable_t **vtable, apr_pool_t* common_pool)
691 {
692 static const svn_version_checklist_t checklist[] =
693 {
694 { "svn_subr", svn_subr_version },
695 { "svn_delta", svn_delta_version },
696 { "svn_fs_util", svn_fs_util__version },
697 { NULL, NULL }
698 };
699
700 /* Simplified version check to make sure we can safely use the
701 VTABLE parameter. The FS loader does a more exhaustive check. */
702 if (loader_version->major != SVN_VER_MAJOR)
703 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
704 _("Unsupported FS loader version (%d) for fsfs"),
705 loader_version->major);
706 SVN_ERR(svn_ver_check_list2(fs_version(), checklist, svn_ver_equal));
707
708 *vtable = &library_vtable;
709 return SVN_NO_ERROR;
710 }
711