]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_fs_fs/fs.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_fs_fs / fs.c
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 #include <apr_thread_mutex.h>
31
32 #include "svn_fs.h"
33 #include "svn_delta.h"
34 #include "svn_version.h"
35 #include "svn_pools.h"
36 #include "fs.h"
37 #include "fs_fs.h"
38 #include "tree.h"
39 #include "lock.h"
40 #include "hotcopy.h"
41 #include "id.h"
42 #include "pack.h"
43 #include "recovery.h"
44 #include "rep-cache.h"
45 #include "revprops.h"
46 #include "transaction.h"
47 #include "util.h"
48 #include "verify.h"
49 #include "svn_private_config.h"
50 #include "private/svn_fs_util.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 \f
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 *
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 \f
139
140 /* This function is provided for Subversion 1.0.x compatibility.  It
141    has no effect for fsfs backed Subversion filesystems.  It conforms
142    to the fs_library_vtable_t.bdb_set_errcall() API. */
143 static svn_error_t *
144 fs_set_errcall(svn_fs_t *fs,
145                void (*db_errcall_fcn)(const char *errpfx, char *msg))
146 {
147
148   return SVN_NO_ERROR;
149 }
150
151 struct fs_freeze_baton_t {
152   svn_fs_t *fs;
153   svn_fs_freeze_func_t freeze_func;
154   void *freeze_baton;
155 };
156
157 static svn_error_t *
158 fs_freeze_body(void *baton,
159                apr_pool_t *pool)
160 {
161   struct fs_freeze_baton_t *b = baton;
162   svn_boolean_t exists;
163
164   SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, b->fs, pool));
165   if (exists)
166     SVN_ERR(svn_fs_fs__with_rep_cache_lock(b->fs,
167                                            b->freeze_func, b->freeze_baton,
168                                            pool));
169   else
170     SVN_ERR(b->freeze_func(b->freeze_baton, pool));
171
172   return SVN_NO_ERROR;
173 }
174
175 static svn_error_t *
176 fs_freeze_body2(void *baton,
177                 apr_pool_t *pool)
178 {
179   struct fs_freeze_baton_t *b = baton;
180   SVN_ERR(svn_fs_fs__with_write_lock(b->fs, fs_freeze_body, baton, pool));
181
182   return SVN_NO_ERROR;
183 }
184
185 static svn_error_t *
186 fs_freeze(svn_fs_t *fs,
187           svn_fs_freeze_func_t freeze_func,
188           void *freeze_baton,
189           apr_pool_t *pool)
190 {
191   fs_fs_data_t *ffd = fs->fsap_data;
192   struct fs_freeze_baton_t b;
193
194   b.fs = fs;
195   b.freeze_func = freeze_func;
196   b.freeze_baton = freeze_baton;
197
198   SVN_ERR(svn_fs__check_fs(fs, TRUE));
199
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));
202   else
203     SVN_ERR(fs_freeze_body2(&b, pool));
204
205   return SVN_NO_ERROR;
206 }
207
208 static svn_error_t *
209 fs_info(const void **fsfs_info,
210         svn_fs_t *fs,
211         apr_pool_t *result_pool,
212         apr_pool_t *scratch_pool)
213 {
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;
220   *fsfs_info = info;
221   return SVN_NO_ERROR;
222 }
223
224 /* Wrapper around svn_fs_fs__set_uuid() adapting between function
225    signatures. */
226 static svn_error_t *
227 fs_set_uuid(svn_fs_t *fs,
228             const char *uuid,
229             apr_pool_t *pool)
230 {
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));
234 }
235
236 \f
237
238 /* The vtable associated with a specific open filesystem. */
239 static fs_vtable_t fs_vtable = {
240   svn_fs_fs__youngest_rev,
241   svn_fs_fs__revision_prop,
242   svn_fs_fs__get_revision_proplist,
243   svn_fs_fs__change_rev_prop,
244   fs_set_uuid,
245   svn_fs_fs__revision_root,
246   svn_fs_fs__begin_txn,
247   svn_fs_fs__open_txn,
248   svn_fs_fs__purge_txn,
249   svn_fs_fs__list_transactions,
250   svn_fs_fs__deltify,
251   svn_fs_fs__lock,
252   svn_fs_fs__generate_lock_token,
253   svn_fs_fs__unlock,
254   svn_fs_fs__get_lock,
255   svn_fs_fs__get_locks,
256   svn_fs_fs__info_format,
257   svn_fs_fs__info_config_files,
258   fs_info,
259   svn_fs_fs__verify_root,
260   fs_freeze,
261   fs_set_errcall
262 };
263
264 \f
265 /* Creating a new filesystem. */
266
267 /* Set up vtable and fsap_data fields in FS. */
268 static svn_error_t *
269 initialize_fs_struct(svn_fs_t *fs)
270 {
271   fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd));
272   ffd->use_log_addressing = FALSE;
273
274   fs->vtable = &fs_vtable;
275   fs->fsap_data = ffd;
276   return SVN_NO_ERROR;
277 }
278
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. */
281 static void
282 uninitialize_fs_struct(svn_fs_t *fs)
283 {
284   fs->vtable = NULL;
285   fs->fsap_data = NULL;
286 }
287
288 /* This implements the fs_library_vtable_t.create() API.  Create a new
289    fsfs-backed Subversion filesystem at path PATH and link it into
290    *FS.  Perform temporary allocations in POOL, and fs-global allocations
291    in COMMON_POOL.  The latter must be serialized using COMMON_POOL_LOCK. */
292 static svn_error_t *
293 fs_create(svn_fs_t *fs,
294           const char *path,
295           svn_mutex__t *common_pool_lock,
296           apr_pool_t *pool,
297           apr_pool_t *common_pool)
298 {
299   SVN_ERR(svn_fs__check_fs(fs, FALSE));
300
301   SVN_ERR(initialize_fs_struct(fs));
302
303   SVN_ERR(svn_fs_fs__create(fs, path, pool));
304
305   SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
306   SVN_MUTEX__WITH_LOCK(common_pool_lock,
307                        fs_serialized_init(fs, common_pool, pool));
308
309   return SVN_NO_ERROR;
310 }
311
312
313 \f
314 /* Gaining access to an existing filesystem.  */
315
316 /* This implements the fs_library_vtable_t.open() API.  Open an FSFS
317    Subversion filesystem located at PATH, set *FS to point to the
318    correct vtable for the filesystem.  Use POOL for any temporary
319    allocations, and COMMON_POOL for fs-global allocations.
320    The latter must be serialized using COMMON_POOL_LOCK. */
321 static svn_error_t *
322 fs_open(svn_fs_t *fs,
323         const char *path,
324         svn_mutex__t *common_pool_lock,
325         apr_pool_t *pool,
326         apr_pool_t *common_pool)
327 {
328   apr_pool_t *subpool = svn_pool_create(pool);
329
330   SVN_ERR(svn_fs__check_fs(fs, FALSE));
331
332   SVN_ERR(initialize_fs_struct(fs));
333
334   SVN_ERR(svn_fs_fs__open(fs, path, subpool));
335
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));
339
340   svn_pool_destroy(subpool);
341
342   return SVN_NO_ERROR;
343 }
344
345 \f
346
347 /* This implements the fs_library_vtable_t.open_for_recovery() API. */
348 static svn_error_t *
349 fs_open_for_recovery(svn_fs_t *fs,
350                      const char *path,
351                      svn_mutex__t *common_pool_lock,
352                      apr_pool_t *pool,
353                      apr_pool_t *common_pool)
354 {
355   svn_error_t * err;
356   svn_revnum_t youngest_rev;
357   apr_pool_t * subpool = svn_pool_create(pool);
358
359   /* Recovery for FSFS is currently limited to recreating the 'current'
360      file from the latest revision. */
361
362   /* The only thing we have to watch out for is that the 'current' file
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). */
367
368   /* Use a partly-filled fs pointer first to create 'current'. */
369   fs->path = apr_pstrdup(fs->pool, path);
370
371   SVN_ERR(initialize_fs_struct(fs));
372
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));
375
376   /* Now, read 'current' and try to patch it if necessary. */
377   err = svn_fs_fs__youngest_rev(&youngest_rev, fs, subpool);
378   if (err)
379     {
380       const char *file_path;
381
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
387        * to the caller.
388        *
389        * Start recovery with HEAD = 0. */
390       svn_error_clear(err);
391       file_path = svn_fs_fs__path_current(fs, subpool);
392
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));
398     }
399
400   uninitialize_fs_struct(fs);
401   svn_pool_destroy(subpool);
402
403   /* Now open the filesystem properly by calling the vtable method directly. */
404   return fs_open(fs, path, common_pool_lock, pool, common_pool);
405 }
406
407 \f
408
409 /* This implements the fs_library_vtable_t.upgrade_fs() API. */
410 static svn_error_t *
411 fs_upgrade(svn_fs_t *fs,
412            const char *path,
413            svn_fs_upgrade_notify_t notify_func,
414            void *notify_baton,
415            svn_cancel_func_t cancel_func,
416            void *cancel_baton,
417            svn_mutex__t *common_pool_lock,
418            apr_pool_t *pool,
419            apr_pool_t *common_pool)
420 {
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);
424 }
425
426 static svn_error_t *
427 fs_verify(svn_fs_t *fs, const char *path,
428           svn_revnum_t start,
429           svn_revnum_t end,
430           svn_fs_progress_notify_func_t notify_func,
431           void *notify_baton,
432           svn_cancel_func_t cancel_func,
433           void *cancel_baton,
434           svn_mutex__t *common_pool_lock,
435           apr_pool_t *pool,
436           apr_pool_t *common_pool)
437 {
438   SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
439   return svn_fs_fs__verify(fs, start, end, notify_func, notify_baton,
440                            cancel_func, cancel_baton, pool);
441 }
442
443 static svn_error_t *
444 fs_pack(svn_fs_t *fs,
445         const char *path,
446         svn_fs_pack_notify_t notify_func,
447         void *notify_baton,
448         svn_cancel_func_t cancel_func,
449         void *cancel_baton,
450         svn_mutex__t *common_pool_lock,
451         apr_pool_t *pool,
452         apr_pool_t *common_pool)
453 {
454   SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
455   return svn_fs_fs__pack(fs, notify_func, notify_baton,
456                          cancel_func, cancel_baton, pool);
457 }
458
459
460 \f
461
462 /* This implements the fs_library_vtable_t.hotcopy() API.  Copy a
463    possibly live Subversion filesystem SRC_FS from SRC_PATH to a
464    DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to
465    re-copy data which already exists in DST_FS.
466    The CLEAN_LOGS argument is ignored and included for Subversion
467    1.0.x compatibility.  Indicate progress via the optional NOTIFY_FUNC
468    callback using NOTIFY_BATON.  Perform all temporary allocations in POOL. */
469 static svn_error_t *
470 fs_hotcopy(svn_fs_t *src_fs,
471            svn_fs_t *dst_fs,
472            const char *src_path,
473            const char *dst_path,
474            svn_boolean_t clean_logs,
475            svn_boolean_t incremental,
476            svn_fs_hotcopy_notify_t notify_func,
477            void *notify_baton,
478            svn_cancel_func_t cancel_func,
479            void *cancel_baton,
480            svn_mutex__t *common_pool_lock,
481            apr_pool_t *pool,
482            apr_pool_t *common_pool)
483 {
484   /* Open the source repo as usual. */
485   SVN_ERR(fs_open(src_fs, src_path, common_pool_lock, pool, common_pool));
486   if (cancel_func)
487     SVN_ERR(cancel_func(cancel_baton));
488
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
491    * available. */
492   SVN_ERR(initialize_fs_struct(dst_fs));
493   SVN_ERR(svn_fs_fs__hotcopy_prepare_target(src_fs, dst_fs, dst_path,
494                                             incremental, pool));
495   uninitialize_fs_struct(dst_fs);
496
497   /* Now, the destination repo should open just fine. */
498   SVN_ERR(fs_open(dst_fs, dst_path, common_pool_lock, pool, common_pool));
499   if (cancel_func)
500     SVN_ERR(cancel_func(cancel_baton));
501
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);
506 }
507
508 \f
509
510 /* This function is included for Subversion 1.0.x compatibility.  It
511    has no effect for fsfs backed Subversion filesystems.  It conforms
512    to the fs_library_vtable_t.bdb_logfiles() API. */
513 static svn_error_t *
514 fs_logfiles(apr_array_header_t **logfiles,
515             const char *path,
516             svn_boolean_t only_unused,
517             apr_pool_t *pool)
518 {
519   /* A no-op for FSFS. */
520   *logfiles = apr_array_make(pool, 0, sizeof(const char *));
521
522   return SVN_NO_ERROR;
523 }
524
525
526
527 \f
528
529 /* Delete the filesystem located at path PATH.  Perform any temporary
530    allocations in POOL. */
531 static svn_error_t *
532 fs_delete_fs(const char *path,
533              apr_pool_t *pool)
534 {
535   /* Remove everything. */
536   return svn_error_trace(svn_io_remove_dir2(path, FALSE, NULL, NULL, pool));
537 }
538
539 static const svn_version_t *
540 fs_version(void)
541 {
542   SVN_VERSION_BODY;
543 }
544
545 static const char *
546 fs_get_description(void)
547 {
548   return _("Module for working with a plain file (FSFS) repository.");
549 }
550
551 static svn_error_t *
552 fs_set_svn_fs_open(svn_fs_t *fs,
553                    svn_error_t *(*svn_fs_open_)(svn_fs_t **,
554                                                 const char *,
555                                                 apr_hash_t *,
556                                                 apr_pool_t *,
557                                                 apr_pool_t *))
558 {
559   fs_fs_data_t *ffd = fs->fsap_data;
560   ffd->svn_fs_open_ = svn_fs_open_;
561   return SVN_NO_ERROR;
562 }
563
564 static void *
565 fs_info_dup(const void *fsfs_info_void,
566             apr_pool_t *result_pool)
567 {
568   /* All fields are either ints or static strings. */
569   const svn_fs_fsfs_info_t *fsfs_info = fsfs_info_void;
570   return apr_pmemdup(result_pool, fsfs_info, sizeof(*fsfs_info));
571 }
572
573 \f
574 /* Base FS library vtable, used by the FS loader library. */
575
576 static fs_library_vtable_t library_vtable = {
577   fs_version,
578   fs_create,
579   fs_open,
580   fs_open_for_recovery,
581   fs_upgrade,
582   fs_verify,
583   fs_delete_fs,
584   fs_hotcopy,
585   fs_get_description,
586   svn_fs_fs__recover,
587   fs_pack,
588   fs_logfiles,
589   NULL /* parse_id */,
590   fs_set_svn_fs_open,
591   fs_info_dup
592 };
593
594 svn_error_t *
595 svn_fs_fs__init(const svn_version_t *loader_version,
596                 fs_library_vtable_t **vtable, apr_pool_t* common_pool)
597 {
598   static const svn_version_checklist_t checklist[] =
599     {
600       { "svn_subr",  svn_subr_version },
601       { "svn_delta", svn_delta_version },
602       { "svn_fs_util", svn_fs_util__version },
603       { NULL, NULL }
604     };
605
606   /* Simplified version check to make sure we can safely use the
607      VTABLE parameter. The FS loader does a more exhaustive check. */
608   if (loader_version->major != SVN_VER_MAJOR)
609     return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
610                              _("Unsupported FS loader version (%d) for fsfs"),
611                              loader_version->major);
612   SVN_ERR(svn_ver_check_list2(fs_version(), checklist, svn_ver_equal));
613
614   *vtable = &library_vtable;
615   return SVN_NO_ERROR;
616 }