fs.c revision 299742
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
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. */
63static svn_error_t *
64fs_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
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. */
143static svn_error_t *
144fs_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
151struct fs_freeze_baton_t {
152  svn_fs_t *fs;
153  svn_fs_freeze_func_t freeze_func;
154  void *freeze_baton;
155};
156
157static svn_error_t *
158fs_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
175static svn_error_t *
176fs_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
185static svn_error_t *
186fs_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
208static svn_error_t *
209fs_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. */
226static svn_error_t *
227fs_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
237
238/* The vtable associated with a specific open filesystem. */
239static 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
265/* Creating a new filesystem. */
266
267/* Set up vtable and fsap_data fields in FS. */
268static svn_error_t *
269initialize_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. */
281static void
282uninitialize_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. */
292static svn_error_t *
293fs_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
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. */
321static svn_error_t *
322fs_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
346
347/* This implements the fs_library_vtable_t.open_for_recovery() API. */
348static svn_error_t *
349fs_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
408
409/* This implements the fs_library_vtable_t.upgrade_fs() API. */
410static svn_error_t *
411fs_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
426static svn_error_t *
427fs_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
443static svn_error_t *
444fs_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
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. */
469static svn_error_t *
470fs_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
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. */
513static svn_error_t *
514fs_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
528
529/* Delete the filesystem located at path PATH.  Perform any temporary
530   allocations in POOL. */
531static svn_error_t *
532fs_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
539static const svn_version_t *
540fs_version(void)
541{
542  SVN_VERSION_BODY;
543}
544
545static const char *
546fs_get_description(void)
547{
548  return _("Module for working with a plain file (FSFS) repository.");
549}
550
551static svn_error_t *
552fs_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
564static void *
565fs_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
574/* Base FS library vtable, used by the FS loader library. */
575
576static 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
594svn_error_t *
595svn_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}
617