64
73
each separate repository opened during the lifetime of the
65
74
svn_fs_initialize pool. It's unlikely that anyone will notice
66
75
the modest expenditure; the alternative is to allocate each structure
67
in a subpool, add a reference-count, and add a serialized deconstructor
76
in a subpool, add a reference-count, and add a serialized destructor
68
77
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
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(). */
78
94
SVN_ERR_ASSERT(fs->uuid);
79
key = apr_pstrcat(pool, SVN_FSFS_SHARED_USERDATA_PREFIX, 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);
81
99
status = apr_pool_userdata_get(&val, key, common_pool);
83
101
return svn_error_wrap_apr(status, _("Can't fetch FSFS shared data"));
94
112
SVN_ERR(svn_mutex__init(&ffsd->fs_write_lock,
95
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));
97
119
/* ... not to mention locking the txn-current file. */
98
120
SVN_ERR(svn_mutex__init(&ffsd->txn_current_lock,
99
121
SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
101
123
/* 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,
124
transaction list and free transaction pointer. */
125
SVN_ERR(svn_mutex__init(&ffsd->txn_list_lock, TRUE, common_pool));
107
127
key = apr_pstrdup(common_pool, key);
108
128
status = apr_pool_userdata_set(ffsd, key, NULL, common_pool);
144
164
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));
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));
150
182
return SVN_NO_ERROR;
163
196
b.freeze_baton = freeze_baton;
165
198
SVN_ERR(svn_fs__check_fs(fs, TRUE));
166
SVN_ERR(svn_fs_fs__with_write_lock(fs, fs_freeze_body, &b, pool));
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));
201
269
initialize_fs_struct(svn_fs_t *fs)
203
271
fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd));
272
ffd->use_log_addressing = FALSE;
204
274
fs->vtable = &fs_vtable;
205
275
fs->fsap_data = ffd;
206
276
return SVN_NO_ERROR;
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;
209
288
/* This implements the fs_library_vtable_t.create() API. Create a new
210
289
fsfs-backed Subversion filesystem at path PATH and link it into
211
290
*FS. Perform temporary allocations in POOL, and fs-global allocations
291
in COMMON_POOL. The latter must be serialized using COMMON_POOL_LOCK. */
213
292
static svn_error_t *
214
fs_create(svn_fs_t *fs, const char *path, apr_pool_t *pool,
293
fs_create(svn_fs_t *fs,
295
svn_mutex__t *common_pool_lock,
215
297
apr_pool_t *common_pool)
217
299
SVN_ERR(svn_fs__check_fs(fs, FALSE));
231
316
/* This implements the fs_library_vtable_t.open() API. Open an FSFS
232
317
Subversion filesystem located at PATH, set *FS to point to the
233
318
correct vtable for the filesystem. Use POOL for any temporary
234
allocations, and COMMON_POOL for fs-global allocations. */
319
allocations, and COMMON_POOL for fs-global allocations.
320
The latter must be serialized using COMMON_POOL_LOCK. */
235
321
static svn_error_t *
236
fs_open(svn_fs_t *fs, const char *path, apr_pool_t *pool,
322
fs_open(svn_fs_t *fs,
324
svn_mutex__t *common_pool_lock,
237
326
apr_pool_t *common_pool)
328
apr_pool_t *subpool = svn_pool_create(pool);
330
SVN_ERR(svn_fs__check_fs(fs, FALSE));
239
332
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);
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);
250
348
static svn_error_t *
251
349
fs_open_for_recovery(svn_fs_t *fs,
252
350
const char *path,
253
apr_pool_t *pool, apr_pool_t *common_pool)
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);
255
359
/* Recovery for FSFS is currently limited to recreating the 'current'
256
360
file from the latest revision. */
258
362
/* 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). */
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). */
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. */
368
/* Use a partly-filled fs pointer first to create 'current'. */
266
369
fs->path = apr_pstrdup(fs->pool, path);
267
svn_error_clear(svn_io_file_create(svn_fs_fs__path_current(fs, pool),
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);
270
403
/* Now open the filesystem properly by calling the vtable method directly. */
271
return fs_open(fs, path, pool, common_pool);
404
return fs_open(fs, path, common_pool_lock, pool, common_pool);
276
409
/* This implements the fs_library_vtable_t.upgrade_fs() API. */
277
410
static svn_error_t *
278
fs_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool,
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,
279
419
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);
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);
289
426
static svn_error_t *
294
431
void *notify_baton,
295
432
svn_cancel_func_t cancel_func,
296
433
void *cancel_baton,
434
svn_mutex__t *common_pool_lock,
297
435
apr_pool_t *pool,
298
436
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));
438
SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
305
439
return svn_fs_fs__verify(fs, start, end, notify_func, notify_baton,
306
440
cancel_func, cancel_baton, pool);
313
447
void *notify_baton,
314
448
svn_cancel_func_t cancel_func,
315
449
void *cancel_baton,
450
svn_mutex__t *common_pool_lock,
316
451
apr_pool_t *pool,
317
452
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));
454
SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
324
455
return svn_fs_fs__pack(fs, notify_func, notify_baton,
325
456
cancel_func, cancel_baton, pool);
341
473
const char *dst_path,
342
474
svn_boolean_t clean_logs,
343
475
svn_boolean_t incremental,
476
svn_fs_hotcopy_notify_t notify_func,
344
478
svn_cancel_func_t cancel_func,
345
479
void *cancel_baton,
480
svn_mutex__t *common_pool_lock,
482
apr_pool_t *common_pool)
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));
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));
354
SVN_ERR(svn_fs__check_fs(dst_fs, FALSE));
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
355
492
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);
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);