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