]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - subversion/libsvn_fs_fs/fs.c
Import Subversion-1.10.0
[FreeBSD/FreeBSD.git] / 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
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
51 #include "../libsvn_fs/fs-loader.h"
52
53 /* A prefix for the pool userdata variables used to hold
54    per-filesystem shared data.  See fs_serialized_init. */
55 #define SVN_FSFS_SHARED_USERDATA_PREFIX "svn-fsfs-shared-"
56
57 \f
58
59 /* Initialize the part of FS that requires global serialization across all
60    instances.  The caller is responsible of ensuring that serialization.
61    Use COMMON_POOL for process-wide and POOL for temporary allocations. */
62 static svn_error_t *
63 fs_serialized_init(svn_fs_t *fs, apr_pool_t *common_pool, apr_pool_t *pool)
64 {
65   fs_fs_data_t *ffd = fs->fsap_data;
66   const char *key;
67   void *val;
68   fs_fs_shared_data_t *ffsd;
69   apr_status_t status;
70
71   /* Note that we are allocating a small amount of long-lived data for
72      each separate repository opened during the lifetime of the
73      svn_fs_initialize pool.  It's unlikely that anyone will notice
74      the modest expenditure; the alternative is to allocate each structure
75      in a subpool, add a reference-count, and add a serialized destructor
76      to the FS vtable.  That's more machinery than it's worth.
77
78      Picking an appropriate key for the shared data is tricky, because,
79      unfortunately, a filesystem UUID is not really unique.  It is implicitly
80      shared between hotcopied (1), dump / loaded (2) or naively copied (3)
81      filesystems.  We tackle this problem by using a combination of the UUID
82      and an instance ID as the key.  This allows us to avoid key clashing
83      in (1) and (2) for formats >= SVN_FS_FS__MIN_INSTANCE_ID_FORMAT, which
84      do support instance IDs.  For old formats the shared data (locks, shared
85      transaction data, ...) will still clash.
86
87      Speaking of (3), there is not so much we can do about it, except maybe
88      provide a convenient way of fixing things.  Naively copied filesystems
89      have identical filesystem UUIDs *and* instance IDs.  With the key being
90      a combination of these two, clashes can be fixed by changing either of
91      them (or both), e.g. with svn_fs_set_uuid(). */
92
93   SVN_ERR_ASSERT(fs->uuid);
94   SVN_ERR_ASSERT(ffd->instance_id);
95
96   key = apr_pstrcat(pool, SVN_FSFS_SHARED_USERDATA_PREFIX,
97                     fs->uuid, ":", ffd->instance_id, SVN_VA_NULL);
98   status = apr_pool_userdata_get(&val, key, common_pool);
99   if (status)
100     return svn_error_wrap_apr(status, _("Can't fetch FSFS shared data"));
101   ffsd = val;
102
103   if (!ffsd)
104     {
105       ffsd = apr_pcalloc(common_pool, sizeof(*ffsd));
106       ffsd->common_pool = common_pool;
107
108       /* POSIX fcntl locks are per-process, so we need a mutex for
109          intra-process synchronization when grabbing the repository write
110          lock. */
111       SVN_ERR(svn_mutex__init(&ffsd->fs_write_lock,
112                               SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
113
114       /* ... the pack lock ... */
115       SVN_ERR(svn_mutex__init(&ffsd->fs_pack_lock,
116                               SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
117
118       /* ... not to mention locking the txn-current file. */
119       SVN_ERR(svn_mutex__init(&ffsd->txn_current_lock,
120                               SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
121
122       /* We also need a mutex for synchronizing access to the active
123          transaction list and free transaction pointer. */
124       SVN_ERR(svn_mutex__init(&ffsd->txn_list_lock, TRUE, common_pool));
125
126       key = apr_pstrdup(common_pool, key);
127       status = apr_pool_userdata_set(ffsd, key, NULL, common_pool);
128       if (status)
129         return svn_error_wrap_apr(status, _("Can't store FSFS shared data"));
130     }
131
132   ffd->shared = ffsd;
133
134   return SVN_NO_ERROR;
135 }
136
137 svn_error_t *
138 svn_fs_fs__initialize_shared_data(svn_fs_t *fs,
139                                   svn_mutex__t *common_pool_lock,
140                                   apr_pool_t *pool,
141                                   apr_pool_t *common_pool)
142 {
143   SVN_MUTEX__WITH_LOCK(common_pool_lock,
144                        fs_serialized_init(fs, common_pool, pool));
145
146   return SVN_NO_ERROR;
147 }
148
149 \f
150
151 static svn_error_t *
152 fs_refresh_revprops(svn_fs_t *fs,
153                     apr_pool_t *scratch_pool)
154 {
155   svn_fs_fs__reset_revprop_cache(fs);
156
157   return SVN_NO_ERROR;
158 }
159
160 /* This function is provided for Subversion 1.0.x compatibility.  It
161    has no effect for fsfs backed Subversion filesystems.  It conforms
162    to the fs_library_vtable_t.bdb_set_errcall() API. */
163 static svn_error_t *
164 fs_set_errcall(svn_fs_t *fs,
165                void (*db_errcall_fcn)(const char *errpfx, char *msg))
166 {
167
168   return SVN_NO_ERROR;
169 }
170
171 struct fs_freeze_baton_t {
172   svn_fs_t *fs;
173   svn_fs_freeze_func_t freeze_func;
174   void *freeze_baton;
175 };
176
177 static svn_error_t *
178 fs_freeze_body(void *baton,
179                apr_pool_t *pool)
180 {
181   struct fs_freeze_baton_t *b = baton;
182   svn_boolean_t exists;
183
184   SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, b->fs, pool));
185   if (exists)
186     SVN_ERR(svn_fs_fs__with_rep_cache_lock(b->fs,
187                                            b->freeze_func, b->freeze_baton,
188                                            pool));
189   else
190     SVN_ERR(b->freeze_func(b->freeze_baton, pool));
191
192   return SVN_NO_ERROR;
193 }
194
195 static svn_error_t *
196 fs_freeze_body2(void *baton,
197                 apr_pool_t *pool)
198 {
199   struct fs_freeze_baton_t *b = baton;
200   SVN_ERR(svn_fs_fs__with_write_lock(b->fs, fs_freeze_body, baton, pool));
201
202   return SVN_NO_ERROR;
203 }
204
205 static svn_error_t *
206 fs_freeze(svn_fs_t *fs,
207           svn_fs_freeze_func_t freeze_func,
208           void *freeze_baton,
209           apr_pool_t *pool)
210 {
211   fs_fs_data_t *ffd = fs->fsap_data;
212   struct fs_freeze_baton_t b;
213
214   b.fs = fs;
215   b.freeze_func = freeze_func;
216   b.freeze_baton = freeze_baton;
217
218   SVN_ERR(svn_fs__check_fs(fs, TRUE));
219
220   if (ffd->format >= SVN_FS_FS__MIN_PACK_LOCK_FORMAT)
221     SVN_ERR(svn_fs_fs__with_pack_lock(fs, fs_freeze_body2, &b, pool));
222   else
223     SVN_ERR(fs_freeze_body2(&b, pool));
224
225   return SVN_NO_ERROR;
226 }
227
228 static svn_error_t *
229 fs_info(const void **fsfs_info,
230         svn_fs_t *fs,
231         apr_pool_t *result_pool,
232         apr_pool_t *scratch_pool)
233 {
234   fs_fs_data_t *ffd = fs->fsap_data;
235   svn_fs_fsfs_info_t *info = apr_palloc(result_pool, sizeof(*info));
236   info->fs_type = SVN_FS_TYPE_FSFS;
237   info->shard_size = ffd->max_files_per_dir;
238   info->min_unpacked_rev = ffd->min_unpacked_rev;
239   info->log_addressing = ffd->use_log_addressing;
240   *fsfs_info = info;
241   return SVN_NO_ERROR;
242 }
243
244 /* Wrapper around svn_fs_fs__set_uuid() adapting between function
245    signatures. */
246 static svn_error_t *
247 fs_set_uuid(svn_fs_t *fs,
248             const char *uuid,
249             apr_pool_t *pool)
250 {
251   /* Whenever we set a new UUID, imply that FS will also be a different
252    * instance (on formats that support this). */
253   return svn_error_trace(svn_fs_fs__set_uuid(fs, uuid, NULL, pool));
254 }
255
256 \f
257
258 /* The vtable associated with a specific open filesystem. */
259 static fs_vtable_t fs_vtable = {
260   svn_fs_fs__youngest_rev,
261   fs_refresh_revprops,
262   svn_fs_fs__revision_prop,
263   svn_fs_fs__get_revision_proplist,
264   svn_fs_fs__change_rev_prop,
265   fs_set_uuid,
266   svn_fs_fs__revision_root,
267   svn_fs_fs__begin_txn,
268   svn_fs_fs__open_txn,
269   svn_fs_fs__purge_txn,
270   svn_fs_fs__list_transactions,
271   svn_fs_fs__deltify,
272   svn_fs_fs__lock,
273   svn_fs_fs__generate_lock_token,
274   svn_fs_fs__unlock,
275   svn_fs_fs__get_lock,
276   svn_fs_fs__get_locks,
277   svn_fs_fs__info_format,
278   svn_fs_fs__info_config_files,
279   fs_info,
280   svn_fs_fs__verify_root,
281   fs_freeze,
282   fs_set_errcall
283 };
284
285 \f
286 /* Creating a new filesystem. */
287
288 /* Set up vtable and fsap_data fields in FS. */
289 static svn_error_t *
290 initialize_fs_struct(svn_fs_t *fs)
291 {
292   fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd));
293   ffd->use_log_addressing = FALSE;
294   ffd->revprop_prefix = 0;
295   ffd->flush_to_disk = TRUE;
296
297   fs->vtable = &fs_vtable;
298   fs->fsap_data = ffd;
299   return SVN_NO_ERROR;
300 }
301
302 /* Reset vtable and fsap_data fields in FS such that the FS is basically
303  * closed now.  Note that FS must not hold locks when you call this. */
304 static void
305 uninitialize_fs_struct(svn_fs_t *fs)
306 {
307   fs->vtable = NULL;
308   fs->fsap_data = NULL;
309 }
310
311 /* This implements the fs_library_vtable_t.create() API.  Create a new
312    fsfs-backed Subversion filesystem at path PATH and link it into
313    *FS.  Perform temporary allocations in POOL, and fs-global allocations
314    in COMMON_POOL.  The latter must be serialized using COMMON_POOL_LOCK. */
315 static svn_error_t *
316 fs_create(svn_fs_t *fs,
317           const char *path,
318           svn_mutex__t *common_pool_lock,
319           apr_pool_t *scratch_pool,
320           apr_pool_t *common_pool)
321 {
322   SVN_ERR(svn_fs__check_fs(fs, FALSE));
323
324   SVN_ERR(initialize_fs_struct(fs));
325
326   SVN_ERR(svn_fs_fs__create(fs, path, scratch_pool));
327
328   SVN_ERR(svn_fs_fs__initialize_caches(fs, scratch_pool));
329   SVN_MUTEX__WITH_LOCK(common_pool_lock,
330                        fs_serialized_init(fs, common_pool, scratch_pool));
331
332   return SVN_NO_ERROR;
333 }
334
335
336 \f
337 /* Gaining access to an existing filesystem.  */
338
339 /* This implements the fs_library_vtable_t.open() API.  Open an FSFS
340    Subversion filesystem located at PATH, set *FS to point to the
341    correct vtable for the filesystem.  Use POOL for any temporary
342    allocations, and COMMON_POOL for fs-global allocations.
343    The latter must be serialized using COMMON_POOL_LOCK. */
344 static svn_error_t *
345 fs_open(svn_fs_t *fs,
346         const char *path,
347         svn_mutex__t *common_pool_lock,
348         apr_pool_t *scratch_pool,
349         apr_pool_t *common_pool)
350 {
351   apr_pool_t *subpool = svn_pool_create(scratch_pool);
352
353   SVN_ERR(svn_fs__check_fs(fs, FALSE));
354
355   SVN_ERR(initialize_fs_struct(fs));
356
357   SVN_ERR(svn_fs_fs__open(fs, path, subpool));
358
359   SVN_ERR(svn_fs_fs__initialize_caches(fs, subpool));
360   SVN_MUTEX__WITH_LOCK(common_pool_lock,
361                        fs_serialized_init(fs, common_pool, subpool));
362
363   svn_pool_destroy(subpool);
364
365   return SVN_NO_ERROR;
366 }
367
368 \f
369
370 /* This implements the fs_library_vtable_t.open_for_recovery() API. */
371 static svn_error_t *
372 fs_open_for_recovery(svn_fs_t *fs,
373                      const char *path,
374                      svn_mutex__t *common_pool_lock,
375                      apr_pool_t *pool,
376                      apr_pool_t *common_pool)
377 {
378   svn_error_t * err;
379   svn_revnum_t youngest_rev;
380   apr_pool_t * subpool = svn_pool_create(pool);
381
382   /* Recovery for FSFS is currently limited to recreating the 'current'
383      file from the latest revision. */
384
385   /* The only thing we have to watch out for is that the 'current' file
386      might not exist or contain garbage.  So we'll try to read it here
387      and provide or replace the existing file if we couldn't read it.
388      (We'll also need it to exist later anyway as a source for the new
389      file's permissions). */
390
391   /* Use a partly-filled fs pointer first to create 'current'. */
392   fs->path = apr_pstrdup(fs->pool, path);
393
394   SVN_ERR(initialize_fs_struct(fs));
395
396   /* Figure out the repo format and check that we can even handle it. */
397   SVN_ERR(svn_fs_fs__read_format_file(fs, subpool));
398
399   /* Now, read 'current' and try to patch it if necessary. */
400   err = svn_fs_fs__youngest_rev(&youngest_rev, fs, subpool);
401   if (err)
402     {
403       const char *file_path;
404
405       /* 'current' file is missing or contains garbage.  Since we are trying
406        * to recover from whatever problem there is, being picky about the
407        * error code here won't do us much good.  If there is a persistent
408        * problem that we can't fix, it will show up when we try rewrite the
409        * file a few lines further below and we will report the failure back
410        * to the caller.
411        *
412        * Start recovery with HEAD = 0. */
413       svn_error_clear(err);
414       file_path = svn_fs_fs__path_current(fs, subpool);
415
416       /* Best effort to ensure the file exists and is valid.
417        * This may fail for r/o filesystems etc. */
418       SVN_ERR(svn_io_remove_file2(file_path, TRUE, subpool));
419       SVN_ERR(svn_io_file_create_empty(file_path, subpool));
420       SVN_ERR(svn_fs_fs__write_current(fs, 0, 1, 1, subpool));
421     }
422
423   uninitialize_fs_struct(fs);
424   svn_pool_destroy(subpool);
425
426   /* Now open the filesystem properly by calling the vtable method directly. */
427   return fs_open(fs, path, common_pool_lock, pool, common_pool);
428 }
429
430 \f
431
432 /* This implements the fs_library_vtable_t.upgrade_fs() API. */
433 static svn_error_t *
434 fs_upgrade(svn_fs_t *fs,
435            const char *path,
436            svn_fs_upgrade_notify_t notify_func,
437            void *notify_baton,
438            svn_cancel_func_t cancel_func,
439            void *cancel_baton,
440            svn_mutex__t *common_pool_lock,
441            apr_pool_t *pool,
442            apr_pool_t *common_pool)
443 {
444   SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
445   return svn_fs_fs__upgrade(fs, notify_func, notify_baton,
446                             cancel_func, cancel_baton, pool);
447 }
448
449 static svn_error_t *
450 fs_verify(svn_fs_t *fs, const char *path,
451           svn_revnum_t start,
452           svn_revnum_t end,
453           svn_fs_progress_notify_func_t notify_func,
454           void *notify_baton,
455           svn_cancel_func_t cancel_func,
456           void *cancel_baton,
457           svn_mutex__t *common_pool_lock,
458           apr_pool_t *pool,
459           apr_pool_t *common_pool)
460 {
461   SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
462   return svn_fs_fs__verify(fs, start, end, notify_func, notify_baton,
463                            cancel_func, cancel_baton, pool);
464 }
465
466 static svn_error_t *
467 fs_pack(svn_fs_t *fs,
468         const char *path,
469         svn_fs_pack_notify_t notify_func,
470         void *notify_baton,
471         svn_cancel_func_t cancel_func,
472         void *cancel_baton,
473         svn_mutex__t *common_pool_lock,
474         apr_pool_t *pool,
475         apr_pool_t *common_pool)
476 {
477   SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
478   return svn_fs_fs__pack(fs, 0, notify_func, notify_baton,
479                          cancel_func, cancel_baton, pool);
480 }
481
482
483 \f
484
485 /* This implements the fs_library_vtable_t.hotcopy() API.  Copy a
486    possibly live Subversion filesystem SRC_FS from SRC_PATH to a
487    DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to
488    re-copy data which already exists in DST_FS.
489    The CLEAN_LOGS argument is ignored and included for Subversion
490    1.0.x compatibility.  Indicate progress via the optional NOTIFY_FUNC
491    callback using NOTIFY_BATON.  Perform all temporary allocations in POOL. */
492 static svn_error_t *
493 fs_hotcopy(svn_fs_t *src_fs,
494            svn_fs_t *dst_fs,
495            const char *src_path,
496            const char *dst_path,
497            svn_boolean_t clean_logs,
498            svn_boolean_t incremental,
499            svn_fs_hotcopy_notify_t notify_func,
500            void *notify_baton,
501            svn_cancel_func_t cancel_func,
502            void *cancel_baton,
503            svn_mutex__t *common_pool_lock,
504            apr_pool_t *pool,
505            apr_pool_t *common_pool)
506 {
507   SVN_ERR(fs_open(src_fs, src_path, common_pool_lock, pool, common_pool));
508
509   SVN_ERR(svn_fs__check_fs(dst_fs, FALSE));
510   SVN_ERR(initialize_fs_struct(dst_fs));
511
512   /* In INCREMENTAL mode, svn_fs_fs__hotcopy() will open DST_FS.
513      Otherwise, it's not an FS yet --- possibly just an empty dir --- so
514      can't be opened.
515    */
516   return svn_fs_fs__hotcopy(src_fs, dst_fs, src_path, dst_path,
517                             incremental, notify_func, notify_baton,
518                             cancel_func, cancel_baton, common_pool_lock,
519                             pool, common_pool);
520 }
521
522 \f
523
524 /* This function is included for Subversion 1.0.x compatibility.  It
525    has no effect for fsfs backed Subversion filesystems.  It conforms
526    to the fs_library_vtable_t.bdb_logfiles() API. */
527 static svn_error_t *
528 fs_logfiles(apr_array_header_t **logfiles,
529             const char *path,
530             svn_boolean_t only_unused,
531             apr_pool_t *pool)
532 {
533   /* A no-op for FSFS. */
534   *logfiles = apr_array_make(pool, 0, sizeof(const char *));
535
536   return SVN_NO_ERROR;
537 }
538
539
540
541 \f
542
543 /* Delete the filesystem located at path PATH.  Perform any temporary
544    allocations in POOL. */
545 static svn_error_t *
546 fs_delete_fs(const char *path,
547              apr_pool_t *pool)
548 {
549   /* Remove everything. */
550   return svn_error_trace(svn_io_remove_dir2(path, FALSE, NULL, NULL, pool));
551 }
552
553 static const svn_version_t *
554 fs_version(void)
555 {
556   SVN_VERSION_BODY;
557 }
558
559 static const char *
560 fs_get_description(void)
561 {
562   return _("Module for working with a plain file (FSFS) repository.");
563 }
564
565 static svn_error_t *
566 fs_set_svn_fs_open(svn_fs_t *fs,
567                    svn_error_t *(*svn_fs_open_)(svn_fs_t **,
568                                                 const char *,
569                                                 apr_hash_t *,
570                                                 apr_pool_t *,
571                                                 apr_pool_t *))
572 {
573   fs_fs_data_t *ffd = fs->fsap_data;
574   ffd->svn_fs_open_ = svn_fs_open_;
575   return SVN_NO_ERROR;
576 }
577
578 static void *
579 fs_info_dup(const void *fsfs_info_void,
580             apr_pool_t *result_pool)
581 {
582   /* All fields are either ints or static strings. */
583   const svn_fs_fsfs_info_t *fsfs_info = fsfs_info_void;
584   return apr_pmemdup(result_pool, fsfs_info, sizeof(*fsfs_info));
585 }
586
587 \f
588 /* Base FS library vtable, used by the FS loader library. */
589
590 static fs_library_vtable_t library_vtable = {
591   fs_version,
592   fs_create,
593   fs_open,
594   fs_open_for_recovery,
595   fs_upgrade,
596   fs_verify,
597   fs_delete_fs,
598   fs_hotcopy,
599   fs_get_description,
600   svn_fs_fs__recover,
601   fs_pack,
602   fs_logfiles,
603   NULL /* parse_id */,
604   fs_set_svn_fs_open,
605   fs_info_dup
606 };
607
608 svn_error_t *
609 svn_fs_fs__init(const svn_version_t *loader_version,
610                 fs_library_vtable_t **vtable, apr_pool_t* common_pool)
611 {
612   static const svn_version_checklist_t checklist[] =
613     {
614       { "svn_subr",  svn_subr_version },
615       { "svn_delta", svn_delta_version },
616       { "svn_fs_util", svn_fs_util__version },
617       { NULL, NULL }
618     };
619
620   /* Simplified version check to make sure we can safely use the
621      VTABLE parameter. The FS loader does a more exhaustive check. */
622   if (loader_version->major != SVN_VER_MAJOR)
623     return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
624                              _("Unsupported FS loader version (%d) for fsfs"),
625                              loader_version->major);
626   SVN_ERR(svn_ver_check_list2(fs_version(), checklist, svn_ver_equal));
627
628   *vtable = &library_vtable;
629   return SVN_NO_ERROR;
630 }