fs.c revision 262253
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 "id.h"
41#include "rep-cache.h"
42#include "svn_private_config.h"
43#include "private/svn_fs_util.h"
44#include "private/svn_subr_private.h"
45
46#include "../libsvn_fs/fs-loader.h"
47
48/* A prefix for the pool userdata variables used to hold
49   per-filesystem shared data.  See fs_serialized_init. */
50#define SVN_FSFS_SHARED_USERDATA_PREFIX "svn-fsfs-shared-"
51
52
53
54static svn_error_t *
55fs_serialized_init(svn_fs_t *fs, apr_pool_t *common_pool, apr_pool_t *pool)
56{
57  fs_fs_data_t *ffd = fs->fsap_data;
58  const char *key;
59  void *val;
60  fs_fs_shared_data_t *ffsd;
61  apr_status_t status;
62
63  /* Note that we are allocating a small amount of long-lived data for
64     each separate repository opened during the lifetime of the
65     svn_fs_initialize pool.  It's unlikely that anyone will notice
66     the modest expenditure; the alternative is to allocate each structure
67     in a subpool, add a reference-count, and add a serialized deconstructor
68     to the FS vtable.  That's more machinery than it's worth.
69
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
76     repository. */
77
78  SVN_ERR_ASSERT(fs->uuid);
79  key = apr_pstrcat(pool, SVN_FSFS_SHARED_USERDATA_PREFIX, fs->uuid,
80                    (char *) NULL);
81  status = apr_pool_userdata_get(&val, key, common_pool);
82  if (status)
83    return svn_error_wrap_apr(status, _("Can't fetch FSFS shared data"));
84  ffsd = val;
85
86  if (!ffsd)
87    {
88      ffsd = apr_pcalloc(common_pool, sizeof(*ffsd));
89      ffsd->common_pool = common_pool;
90
91      /* POSIX fcntl locks are per-process, so we need a mutex for
92         intra-process synchronization when grabbing the repository write
93         lock. */
94      SVN_ERR(svn_mutex__init(&ffsd->fs_write_lock,
95                              SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
96
97      /* ... not to mention locking the txn-current file. */
98      SVN_ERR(svn_mutex__init(&ffsd->txn_current_lock,
99                              SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
100
101      SVN_ERR(svn_mutex__init(&ffsd->txn_list_lock,
102                              SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
103
104      key = apr_pstrdup(common_pool, key);
105      status = apr_pool_userdata_set(ffsd, key, NULL, common_pool);
106      if (status)
107        return svn_error_wrap_apr(status, _("Can't store FSFS shared data"));
108    }
109
110  ffd->shared = ffsd;
111
112  return SVN_NO_ERROR;
113}
114
115
116
117/* This function is provided for Subversion 1.0.x compatibility.  It
118   has no effect for fsfs backed Subversion filesystems.  It conforms
119   to the fs_library_vtable_t.bdb_set_errcall() API. */
120static svn_error_t *
121fs_set_errcall(svn_fs_t *fs,
122               void (*db_errcall_fcn)(const char *errpfx, char *msg))
123{
124
125  return SVN_NO_ERROR;
126}
127
128struct fs_freeze_baton_t {
129  svn_fs_t *fs;
130  svn_fs_freeze_func_t freeze_func;
131  void *freeze_baton;
132};
133
134static svn_error_t *
135fs_freeze_body(void *baton,
136               apr_pool_t *pool)
137{
138  struct fs_freeze_baton_t *b = baton;
139  svn_boolean_t exists;
140
141  SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, b->fs, pool));
142  if (exists)
143    SVN_ERR(svn_fs_fs__lock_rep_cache(b->fs, pool));
144
145  SVN_ERR(b->freeze_func(b->freeze_baton, pool));
146
147  return SVN_NO_ERROR;
148}
149
150static svn_error_t *
151fs_freeze(svn_fs_t *fs,
152          svn_fs_freeze_func_t freeze_func,
153          void *freeze_baton,
154          apr_pool_t *pool)
155{
156  struct fs_freeze_baton_t b;
157
158  b.fs = fs;
159  b.freeze_func = freeze_func;
160  b.freeze_baton = freeze_baton;
161
162  SVN_ERR(svn_fs__check_fs(fs, TRUE));
163  SVN_ERR(svn_fs_fs__with_write_lock(fs, fs_freeze_body, &b, pool));
164
165  return SVN_NO_ERROR;
166}
167
168
169
170/* The vtable associated with a specific open filesystem. */
171static fs_vtable_t fs_vtable = {
172  svn_fs_fs__youngest_rev,
173  svn_fs_fs__revision_prop,
174  svn_fs_fs__revision_proplist,
175  svn_fs_fs__change_rev_prop,
176  svn_fs_fs__set_uuid,
177  svn_fs_fs__revision_root,
178  svn_fs_fs__begin_txn,
179  svn_fs_fs__open_txn,
180  svn_fs_fs__purge_txn,
181  svn_fs_fs__list_transactions,
182  svn_fs_fs__deltify,
183  svn_fs_fs__lock,
184  svn_fs_fs__generate_lock_token,
185  svn_fs_fs__unlock,
186  svn_fs_fs__get_lock,
187  svn_fs_fs__get_locks,
188  svn_fs_fs__verify_root,
189  fs_freeze,
190  fs_set_errcall
191};
192
193
194/* Creating a new filesystem. */
195
196/* Set up vtable and fsap_data fields in FS. */
197static svn_error_t *
198initialize_fs_struct(svn_fs_t *fs)
199{
200  fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd));
201  fs->vtable = &fs_vtable;
202  fs->fsap_data = ffd;
203  return SVN_NO_ERROR;
204}
205
206/* This implements the fs_library_vtable_t.create() API.  Create a new
207   fsfs-backed Subversion filesystem at path PATH and link it into
208   *FS.  Perform temporary allocations in POOL, and fs-global allocations
209   in COMMON_POOL. */
210static svn_error_t *
211fs_create(svn_fs_t *fs, const char *path, apr_pool_t *pool,
212          apr_pool_t *common_pool)
213{
214  SVN_ERR(svn_fs__check_fs(fs, FALSE));
215
216  SVN_ERR(initialize_fs_struct(fs));
217
218  SVN_ERR(svn_fs_fs__create(fs, path, pool));
219
220  SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
221  return fs_serialized_init(fs, common_pool, pool);
222}
223
224
225
226/* Gaining access to an existing filesystem.  */
227
228/* This implements the fs_library_vtable_t.open() API.  Open an FSFS
229   Subversion filesystem located at PATH, set *FS to point to the
230   correct vtable for the filesystem.  Use POOL for any temporary
231   allocations, and COMMON_POOL for fs-global allocations. */
232static svn_error_t *
233fs_open(svn_fs_t *fs, const char *path, apr_pool_t *pool,
234        apr_pool_t *common_pool)
235{
236  SVN_ERR(initialize_fs_struct(fs));
237
238  SVN_ERR(svn_fs_fs__open(fs, path, pool));
239
240  SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
241  return fs_serialized_init(fs, common_pool, pool);
242}
243
244
245
246/* This implements the fs_library_vtable_t.open_for_recovery() API. */
247static svn_error_t *
248fs_open_for_recovery(svn_fs_t *fs,
249                     const char *path,
250                     apr_pool_t *pool, apr_pool_t *common_pool)
251{
252  /* Recovery for FSFS is currently limited to recreating the 'current'
253     file from the latest revision. */
254
255  /* The only thing we have to watch out for is that the 'current' file
256     might not exist.  So we'll try to create it here unconditionally,
257     and just ignore any errors that might indicate that it's already
258     present. (We'll need it to exist later anyway as a source for the
259     new file's permissions). */
260
261  /* Use a partly-filled fs pointer first to create 'current'.  This will fail
262     if 'current' already exists, but we don't care about that. */
263  fs->path = apr_pstrdup(fs->pool, path);
264  svn_error_clear(svn_io_file_create(svn_fs_fs__path_current(fs, pool),
265                                     "0 1 1\n", pool));
266
267  /* Now open the filesystem properly by calling the vtable method directly. */
268  return fs_open(fs, path, pool, common_pool);
269}
270
271
272
273/* This implements the fs_library_vtable_t.upgrade_fs() API. */
274static svn_error_t *
275fs_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool,
276           apr_pool_t *common_pool)
277{
278  SVN_ERR(svn_fs__check_fs(fs, FALSE));
279  SVN_ERR(initialize_fs_struct(fs));
280  SVN_ERR(svn_fs_fs__open(fs, path, pool));
281  SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
282  SVN_ERR(fs_serialized_init(fs, common_pool, pool));
283  return svn_fs_fs__upgrade(fs, pool);
284}
285
286static svn_error_t *
287fs_verify(svn_fs_t *fs, const char *path,
288          svn_revnum_t start,
289          svn_revnum_t end,
290          svn_fs_progress_notify_func_t notify_func,
291          void *notify_baton,
292          svn_cancel_func_t cancel_func,
293          void *cancel_baton,
294          apr_pool_t *pool,
295          apr_pool_t *common_pool)
296{
297  SVN_ERR(svn_fs__check_fs(fs, FALSE));
298  SVN_ERR(initialize_fs_struct(fs));
299  SVN_ERR(svn_fs_fs__open(fs, path, pool));
300  SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
301  SVN_ERR(fs_serialized_init(fs, common_pool, pool));
302  return svn_fs_fs__verify(fs, start, end, notify_func, notify_baton,
303                           cancel_func, cancel_baton, pool);
304}
305
306static svn_error_t *
307fs_pack(svn_fs_t *fs,
308        const char *path,
309        svn_fs_pack_notify_t notify_func,
310        void *notify_baton,
311        svn_cancel_func_t cancel_func,
312        void *cancel_baton,
313        apr_pool_t *pool,
314        apr_pool_t *common_pool)
315{
316  SVN_ERR(svn_fs__check_fs(fs, FALSE));
317  SVN_ERR(initialize_fs_struct(fs));
318  SVN_ERR(svn_fs_fs__open(fs, path, pool));
319  SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
320  SVN_ERR(fs_serialized_init(fs, common_pool, pool));
321  return svn_fs_fs__pack(fs, notify_func, notify_baton,
322                         cancel_func, cancel_baton, pool);
323}
324
325
326
327
328/* This implements the fs_library_vtable_t.hotcopy() API.  Copy a
329   possibly live Subversion filesystem SRC_FS from SRC_PATH to a
330   DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to
331   re-copy data which already exists in DST_FS.
332   The CLEAN_LOGS argument is ignored and included for Subversion
333   1.0.x compatibility.  Perform all temporary allocations in POOL. */
334static svn_error_t *
335fs_hotcopy(svn_fs_t *src_fs,
336           svn_fs_t *dst_fs,
337           const char *src_path,
338           const char *dst_path,
339           svn_boolean_t clean_logs,
340           svn_boolean_t incremental,
341           svn_cancel_func_t cancel_func,
342           void *cancel_baton,
343           apr_pool_t *pool)
344{
345  SVN_ERR(svn_fs__check_fs(src_fs, FALSE));
346  SVN_ERR(initialize_fs_struct(src_fs));
347  SVN_ERR(svn_fs_fs__open(src_fs, src_path, pool));
348  SVN_ERR(svn_fs_fs__initialize_caches(src_fs, pool));
349  SVN_ERR(fs_serialized_init(src_fs, pool, pool));
350
351  SVN_ERR(svn_fs__check_fs(dst_fs, FALSE));
352  SVN_ERR(initialize_fs_struct(dst_fs));
353  /* In INCREMENTAL mode, svn_fs_fs__hotcopy() will open DST_FS.
354     Otherwise, it's not an FS yet --- possibly just an empty dir --- so
355     can't be opened.
356   */
357  return svn_fs_fs__hotcopy(src_fs, dst_fs, src_path, dst_path,
358                            incremental, cancel_func, cancel_baton, pool);
359}
360
361
362
363/* This function is included for Subversion 1.0.x compatibility.  It
364   has no effect for fsfs backed Subversion filesystems.  It conforms
365   to the fs_library_vtable_t.bdb_logfiles() API. */
366static svn_error_t *
367fs_logfiles(apr_array_header_t **logfiles,
368            const char *path,
369            svn_boolean_t only_unused,
370            apr_pool_t *pool)
371{
372  /* A no-op for FSFS. */
373  *logfiles = apr_array_make(pool, 0, sizeof(const char *));
374
375  return SVN_NO_ERROR;
376}
377
378
379
380
381
382/* Delete the filesystem located at path PATH.  Perform any temporary
383   allocations in POOL. */
384static svn_error_t *
385fs_delete_fs(const char *path,
386             apr_pool_t *pool)
387{
388  /* Remove everything. */
389  return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
390}
391
392static const svn_version_t *
393fs_version(void)
394{
395  SVN_VERSION_BODY;
396}
397
398static const char *
399fs_get_description(void)
400{
401  return _("Module for working with a plain file (FSFS) repository.");
402}
403
404static svn_error_t *
405fs_set_svn_fs_open(svn_fs_t *fs,
406                   svn_error_t *(*svn_fs_open_)(svn_fs_t **,
407                                                const char *,
408                                                apr_hash_t *,
409                                                apr_pool_t *))
410{
411  fs_fs_data_t *ffd = fs->fsap_data;
412  ffd->svn_fs_open_ = svn_fs_open_;
413  return SVN_NO_ERROR;
414}
415
416
417/* Base FS library vtable, used by the FS loader library. */
418
419static fs_library_vtable_t library_vtable = {
420  fs_version,
421  fs_create,
422  fs_open,
423  fs_open_for_recovery,
424  fs_upgrade,
425  fs_verify,
426  fs_delete_fs,
427  fs_hotcopy,
428  fs_get_description,
429  svn_fs_fs__recover,
430  fs_pack,
431  fs_logfiles,
432  NULL /* parse_id */,
433  fs_set_svn_fs_open
434};
435
436svn_error_t *
437svn_fs_fs__init(const svn_version_t *loader_version,
438                fs_library_vtable_t **vtable, apr_pool_t* common_pool)
439{
440  static const svn_version_checklist_t checklist[] =
441    {
442      { "svn_subr",  svn_subr_version },
443      { "svn_delta", svn_delta_version },
444      { NULL, NULL }
445    };
446
447  /* Simplified version check to make sure we can safely use the
448     VTABLE parameter. The FS loader does a more exhaustive check. */
449  if (loader_version->major != SVN_VER_MAJOR)
450    return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
451                             _("Unsupported FS loader version (%d) for fsfs"),
452                             loader_version->major);
453  SVN_ERR(svn_ver_check_list2(fs_version(), checklist, svn_ver_equal));
454
455  *vtable = &library_vtable;
456  return SVN_NO_ERROR;
457}
458