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      /* 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,
105                              TRUE, common_pool));
106
107      key = apr_pstrdup(common_pool, key);
108      status = apr_pool_userdata_set(ffsd, key, NULL, common_pool);
109      if (status)
110        return svn_error_wrap_apr(status, _("Can't store FSFS shared data"));
111    }
112
113  ffd->shared = ffsd;
114
115  return SVN_NO_ERROR;
116}
117
118
119
120/* This function is provided for Subversion 1.0.x compatibility.  It
121   has no effect for fsfs backed Subversion filesystems.  It conforms
122   to the fs_library_vtable_t.bdb_set_errcall() API. */
123static svn_error_t *
124fs_set_errcall(svn_fs_t *fs,
125               void (*db_errcall_fcn)(const char *errpfx, char *msg))
126{
127
128  return SVN_NO_ERROR;
129}
130
131struct fs_freeze_baton_t {
132  svn_fs_t *fs;
133  svn_fs_freeze_func_t freeze_func;
134  void *freeze_baton;
135};
136
137static svn_error_t *
138fs_freeze_body(void *baton,
139               apr_pool_t *pool)
140{
141  struct fs_freeze_baton_t *b = baton;
142  svn_boolean_t exists;
143
144  SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, b->fs, pool));
145  if (exists)
146    SVN_ERR(svn_fs_fs__lock_rep_cache(b->fs, pool));
147
148  SVN_ERR(b->freeze_func(b->freeze_baton, pool));
149
150  return SVN_NO_ERROR;
151}
152
153static svn_error_t *
154fs_freeze(svn_fs_t *fs,
155          svn_fs_freeze_func_t freeze_func,
156          void *freeze_baton,
157          apr_pool_t *pool)
158{
159  struct fs_freeze_baton_t b;
160
161  b.fs = fs;
162  b.freeze_func = freeze_func;
163  b.freeze_baton = freeze_baton;
164
165  SVN_ERR(svn_fs__check_fs(fs, TRUE));
166  SVN_ERR(svn_fs_fs__with_write_lock(fs, fs_freeze_body, &b, pool));
167
168  return SVN_NO_ERROR;
169}
170
171
172
173/* The vtable associated with a specific open filesystem. */
174static fs_vtable_t fs_vtable = {
175  svn_fs_fs__youngest_rev,
176  svn_fs_fs__revision_prop,
177  svn_fs_fs__revision_proplist,
178  svn_fs_fs__change_rev_prop,
179  svn_fs_fs__set_uuid,
180  svn_fs_fs__revision_root,
181  svn_fs_fs__begin_txn,
182  svn_fs_fs__open_txn,
183  svn_fs_fs__purge_txn,
184  svn_fs_fs__list_transactions,
185  svn_fs_fs__deltify,
186  svn_fs_fs__lock,
187  svn_fs_fs__generate_lock_token,
188  svn_fs_fs__unlock,
189  svn_fs_fs__get_lock,
190  svn_fs_fs__get_locks,
191  svn_fs_fs__verify_root,
192  fs_freeze,
193  fs_set_errcall
194};
195
196
197/* Creating a new filesystem. */
198
199/* Set up vtable and fsap_data fields in FS. */
200static svn_error_t *
201initialize_fs_struct(svn_fs_t *fs)
202{
203  fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd));
204  fs->vtable = &fs_vtable;
205  fs->fsap_data = ffd;
206  return SVN_NO_ERROR;
207}
208
209/* This implements the fs_library_vtable_t.create() API.  Create a new
210   fsfs-backed Subversion filesystem at path PATH and link it into
211   *FS.  Perform temporary allocations in POOL, and fs-global allocations
212   in COMMON_POOL. */
213static svn_error_t *
214fs_create(svn_fs_t *fs, const char *path, apr_pool_t *pool,
215          apr_pool_t *common_pool)
216{
217  SVN_ERR(svn_fs__check_fs(fs, FALSE));
218
219  SVN_ERR(initialize_fs_struct(fs));
220
221  SVN_ERR(svn_fs_fs__create(fs, path, pool));
222
223  SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
224  return fs_serialized_init(fs, common_pool, pool);
225}
226
227
228
229/* Gaining access to an existing filesystem.  */
230
231/* This implements the fs_library_vtable_t.open() API.  Open an FSFS
232   Subversion filesystem located at PATH, set *FS to point to the
233   correct vtable for the filesystem.  Use POOL for any temporary
234   allocations, and COMMON_POOL for fs-global allocations. */
235static svn_error_t *
236fs_open(svn_fs_t *fs, const char *path, apr_pool_t *pool,
237        apr_pool_t *common_pool)
238{
239  SVN_ERR(initialize_fs_struct(fs));
240
241  SVN_ERR(svn_fs_fs__open(fs, path, pool));
242
243  SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
244  return fs_serialized_init(fs, common_pool, pool);
245}
246
247
248
249/* This implements the fs_library_vtable_t.open_for_recovery() API. */
250static svn_error_t *
251fs_open_for_recovery(svn_fs_t *fs,
252                     const char *path,
253                     apr_pool_t *pool, apr_pool_t *common_pool)
254{
255  /* Recovery for FSFS is currently limited to recreating the 'current'
256     file from the latest revision. */
257
258  /* 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). */
263
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. */
266  fs->path = apr_pstrdup(fs->pool, path);
267  svn_error_clear(svn_io_file_create(svn_fs_fs__path_current(fs, pool),
268                                     "0 1 1\n", pool));
269
270  /* Now open the filesystem properly by calling the vtable method directly. */
271  return fs_open(fs, path, pool, common_pool);
272}
273
274
275
276/* This implements the fs_library_vtable_t.upgrade_fs() API. */
277static svn_error_t *
278fs_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool,
279           apr_pool_t *common_pool)
280{
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);
287}
288
289static svn_error_t *
290fs_verify(svn_fs_t *fs, const char *path,
291          svn_revnum_t start,
292          svn_revnum_t end,
293          svn_fs_progress_notify_func_t notify_func,
294          void *notify_baton,
295          svn_cancel_func_t cancel_func,
296          void *cancel_baton,
297          apr_pool_t *pool,
298          apr_pool_t *common_pool)
299{
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));
305  return svn_fs_fs__verify(fs, start, end, notify_func, notify_baton,
306                           cancel_func, cancel_baton, pool);
307}
308
309static svn_error_t *
310fs_pack(svn_fs_t *fs,
311        const char *path,
312        svn_fs_pack_notify_t notify_func,
313        void *notify_baton,
314        svn_cancel_func_t cancel_func,
315        void *cancel_baton,
316        apr_pool_t *pool,
317        apr_pool_t *common_pool)
318{
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));
324  return svn_fs_fs__pack(fs, notify_func, notify_baton,
325                         cancel_func, cancel_baton, pool);
326}
327
328
329
330
331/* This implements the fs_library_vtable_t.hotcopy() API.  Copy a
332   possibly live Subversion filesystem SRC_FS from SRC_PATH to a
333   DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to
334   re-copy data which already exists in DST_FS.
335   The CLEAN_LOGS argument is ignored and included for Subversion
336   1.0.x compatibility.  Perform all temporary allocations in POOL. */
337static svn_error_t *
338fs_hotcopy(svn_fs_t *src_fs,
339           svn_fs_t *dst_fs,
340           const char *src_path,
341           const char *dst_path,
342           svn_boolean_t clean_logs,
343           svn_boolean_t incremental,
344           svn_cancel_func_t cancel_func,
345           void *cancel_baton,
346           apr_pool_t *pool)
347{
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));
353
354  SVN_ERR(svn_fs__check_fs(dst_fs, FALSE));
355  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
358     can't be opened.
359   */
360  return svn_fs_fs__hotcopy(src_fs, dst_fs, src_path, dst_path,
361                            incremental, cancel_func, cancel_baton, pool);
362}
363
364
365
366/* This function is included for Subversion 1.0.x compatibility.  It
367   has no effect for fsfs backed Subversion filesystems.  It conforms
368   to the fs_library_vtable_t.bdb_logfiles() API. */
369static svn_error_t *
370fs_logfiles(apr_array_header_t **logfiles,
371            const char *path,
372            svn_boolean_t only_unused,
373            apr_pool_t *pool)
374{
375  /* A no-op for FSFS. */
376  *logfiles = apr_array_make(pool, 0, sizeof(const char *));
377
378  return SVN_NO_ERROR;
379}
380
381
382
383
384
385/* Delete the filesystem located at path PATH.  Perform any temporary
386   allocations in POOL. */
387static svn_error_t *
388fs_delete_fs(const char *path,
389             apr_pool_t *pool)
390{
391  /* Remove everything. */
392  return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
393}
394
395static const svn_version_t *
396fs_version(void)
397{
398  SVN_VERSION_BODY;
399}
400
401static const char *
402fs_get_description(void)
403{
404  return _("Module for working with a plain file (FSFS) repository.");
405}
406
407static svn_error_t *
408fs_set_svn_fs_open(svn_fs_t *fs,
409                   svn_error_t *(*svn_fs_open_)(svn_fs_t **,
410                                                const char *,
411                                                apr_hash_t *,
412                                                apr_pool_t *))
413{
414  fs_fs_data_t *ffd = fs->fsap_data;
415  ffd->svn_fs_open_ = svn_fs_open_;
416  return SVN_NO_ERROR;
417}
418
419
420/* Base FS library vtable, used by the FS loader library. */
421
422static fs_library_vtable_t library_vtable = {
423  fs_version,
424  fs_create,
425  fs_open,
426  fs_open_for_recovery,
427  fs_upgrade,
428  fs_verify,
429  fs_delete_fs,
430  fs_hotcopy,
431  fs_get_description,
432  svn_fs_fs__recover,
433  fs_pack,
434  fs_logfiles,
435  NULL /* parse_id */,
436  fs_set_svn_fs_open
437};
438
439svn_error_t *
440svn_fs_fs__init(const svn_version_t *loader_version,
441                fs_library_vtable_t **vtable, apr_pool_t* common_pool)
442{
443  static const svn_version_checklist_t checklist[] =
444    {
445      { "svn_subr",  svn_subr_version },
446      { "svn_delta", svn_delta_version },
447      { NULL, NULL }
448    };
449
450  /* Simplified version check to make sure we can safely use the
451     VTABLE parameter. The FS loader does a more exhaustive check. */
452  if (loader_version->major != SVN_VER_MAJOR)
453    return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
454                             _("Unsupported FS loader version (%d) for fsfs"),
455                             loader_version->major);
456  SVN_ERR(svn_ver_check_list2(fs_version(), checklist, svn_ver_equal));
457
458  *vtable = &library_vtable;
459  return SVN_NO_ERROR;
460}
461