1251881Speter/* fs.c --- creating, opening and closing filesystems
2251881Speter *
3251881Speter * ====================================================================
4251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
5251881Speter *    or more contributor license agreements.  See the NOTICE file
6251881Speter *    distributed with this work for additional information
7251881Speter *    regarding copyright ownership.  The ASF licenses this file
8251881Speter *    to you under the Apache License, Version 2.0 (the
9251881Speter *    "License"); you may not use this file except in compliance
10251881Speter *    with the License.  You may obtain a copy of the License at
11251881Speter *
12251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
13251881Speter *
14251881Speter *    Unless required by applicable law or agreed to in writing,
15251881Speter *    software distributed under the License is distributed on an
16251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17251881Speter *    KIND, either express or implied.  See the License for the
18251881Speter *    specific language governing permissions and limitations
19251881Speter *    under the License.
20251881Speter * ====================================================================
21251881Speter */
22251881Speter
23251881Speter#include <stdlib.h>
24251881Speter#include <stdio.h>
25251881Speter#include <string.h>
26251881Speter
27251881Speter#include <apr_general.h>
28251881Speter#include <apr_pools.h>
29251881Speter#include <apr_file_io.h>
30251881Speter#include <apr_thread_mutex.h>
31251881Speter
32251881Speter#include "svn_fs.h"
33251881Speter#include "svn_delta.h"
34251881Speter#include "svn_version.h"
35251881Speter#include "svn_pools.h"
36251881Speter#include "fs.h"
37251881Speter#include "fs_fs.h"
38251881Speter#include "tree.h"
39251881Speter#include "lock.h"
40251881Speter#include "id.h"
41251881Speter#include "rep-cache.h"
42251881Speter#include "svn_private_config.h"
43251881Speter#include "private/svn_fs_util.h"
44262253Speter#include "private/svn_subr_private.h"
45251881Speter
46251881Speter#include "../libsvn_fs/fs-loader.h"
47251881Speter
48251881Speter/* A prefix for the pool userdata variables used to hold
49251881Speter   per-filesystem shared data.  See fs_serialized_init. */
50251881Speter#define SVN_FSFS_SHARED_USERDATA_PREFIX "svn-fsfs-shared-"
51251881Speter
52251881Speter
53251881Speter
54251881Speterstatic svn_error_t *
55251881Speterfs_serialized_init(svn_fs_t *fs, apr_pool_t *common_pool, apr_pool_t *pool)
56251881Speter{
57251881Speter  fs_fs_data_t *ffd = fs->fsap_data;
58251881Speter  const char *key;
59251881Speter  void *val;
60251881Speter  fs_fs_shared_data_t *ffsd;
61251881Speter  apr_status_t status;
62251881Speter
63251881Speter  /* Note that we are allocating a small amount of long-lived data for
64251881Speter     each separate repository opened during the lifetime of the
65251881Speter     svn_fs_initialize pool.  It's unlikely that anyone will notice
66251881Speter     the modest expenditure; the alternative is to allocate each structure
67251881Speter     in a subpool, add a reference-count, and add a serialized deconstructor
68251881Speter     to the FS vtable.  That's more machinery than it's worth.
69251881Speter
70251881Speter     Using the uuid to obtain the lock creates a corner case if a
71251881Speter     caller uses svn_fs_set_uuid on the repository in a process where
72251881Speter     other threads might be using the same repository through another
73251881Speter     FS object.  The only real-world consumer of svn_fs_set_uuid is
74251881Speter     "svnadmin load", so this is a low-priority problem, and we don't
75251881Speter     know of a better way of associating such data with the
76251881Speter     repository. */
77251881Speter
78251881Speter  SVN_ERR_ASSERT(fs->uuid);
79251881Speter  key = apr_pstrcat(pool, SVN_FSFS_SHARED_USERDATA_PREFIX, fs->uuid,
80251881Speter                    (char *) NULL);
81251881Speter  status = apr_pool_userdata_get(&val, key, common_pool);
82251881Speter  if (status)
83251881Speter    return svn_error_wrap_apr(status, _("Can't fetch FSFS shared data"));
84251881Speter  ffsd = val;
85251881Speter
86251881Speter  if (!ffsd)
87251881Speter    {
88251881Speter      ffsd = apr_pcalloc(common_pool, sizeof(*ffsd));
89251881Speter      ffsd->common_pool = common_pool;
90251881Speter
91251881Speter      /* POSIX fcntl locks are per-process, so we need a mutex for
92251881Speter         intra-process synchronization when grabbing the repository write
93251881Speter         lock. */
94251881Speter      SVN_ERR(svn_mutex__init(&ffsd->fs_write_lock,
95251881Speter                              SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
96251881Speter
97251881Speter      /* ... not to mention locking the txn-current file. */
98251881Speter      SVN_ERR(svn_mutex__init(&ffsd->txn_current_lock,
99251881Speter                              SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
100251881Speter
101269847Speter      /* We also need a mutex for synchronizing access to the active
102269847Speter         transaction list and free transaction pointer.  This one is
103269847Speter         enabled unconditionally. */
104251881Speter      SVN_ERR(svn_mutex__init(&ffsd->txn_list_lock,
105269847Speter                              TRUE, common_pool));
106251881Speter
107251881Speter      key = apr_pstrdup(common_pool, key);
108251881Speter      status = apr_pool_userdata_set(ffsd, key, NULL, common_pool);
109251881Speter      if (status)
110251881Speter        return svn_error_wrap_apr(status, _("Can't store FSFS shared data"));
111251881Speter    }
112251881Speter
113251881Speter  ffd->shared = ffsd;
114251881Speter
115251881Speter  return SVN_NO_ERROR;
116251881Speter}
117251881Speter
118251881Speter
119251881Speter
120251881Speter/* This function is provided for Subversion 1.0.x compatibility.  It
121251881Speter   has no effect for fsfs backed Subversion filesystems.  It conforms
122251881Speter   to the fs_library_vtable_t.bdb_set_errcall() API. */
123251881Speterstatic svn_error_t *
124251881Speterfs_set_errcall(svn_fs_t *fs,
125251881Speter               void (*db_errcall_fcn)(const char *errpfx, char *msg))
126251881Speter{
127251881Speter
128251881Speter  return SVN_NO_ERROR;
129251881Speter}
130251881Speter
131251881Speterstruct fs_freeze_baton_t {
132251881Speter  svn_fs_t *fs;
133251881Speter  svn_fs_freeze_func_t freeze_func;
134251881Speter  void *freeze_baton;
135251881Speter};
136251881Speter
137251881Speterstatic svn_error_t *
138251881Speterfs_freeze_body(void *baton,
139251881Speter               apr_pool_t *pool)
140251881Speter{
141251881Speter  struct fs_freeze_baton_t *b = baton;
142251881Speter  svn_boolean_t exists;
143251881Speter
144251881Speter  SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, b->fs, pool));
145251881Speter  if (exists)
146251881Speter    SVN_ERR(svn_fs_fs__lock_rep_cache(b->fs, pool));
147251881Speter
148251881Speter  SVN_ERR(b->freeze_func(b->freeze_baton, pool));
149251881Speter
150251881Speter  return SVN_NO_ERROR;
151251881Speter}
152251881Speter
153251881Speterstatic svn_error_t *
154251881Speterfs_freeze(svn_fs_t *fs,
155251881Speter          svn_fs_freeze_func_t freeze_func,
156251881Speter          void *freeze_baton,
157251881Speter          apr_pool_t *pool)
158251881Speter{
159251881Speter  struct fs_freeze_baton_t b;
160251881Speter
161251881Speter  b.fs = fs;
162251881Speter  b.freeze_func = freeze_func;
163251881Speter  b.freeze_baton = freeze_baton;
164251881Speter
165251881Speter  SVN_ERR(svn_fs__check_fs(fs, TRUE));
166251881Speter  SVN_ERR(svn_fs_fs__with_write_lock(fs, fs_freeze_body, &b, pool));
167251881Speter
168251881Speter  return SVN_NO_ERROR;
169251881Speter}
170251881Speter
171251881Speter
172251881Speter
173251881Speter/* The vtable associated with a specific open filesystem. */
174251881Speterstatic fs_vtable_t fs_vtable = {
175251881Speter  svn_fs_fs__youngest_rev,
176251881Speter  svn_fs_fs__revision_prop,
177251881Speter  svn_fs_fs__revision_proplist,
178251881Speter  svn_fs_fs__change_rev_prop,
179251881Speter  svn_fs_fs__set_uuid,
180251881Speter  svn_fs_fs__revision_root,
181251881Speter  svn_fs_fs__begin_txn,
182251881Speter  svn_fs_fs__open_txn,
183251881Speter  svn_fs_fs__purge_txn,
184251881Speter  svn_fs_fs__list_transactions,
185251881Speter  svn_fs_fs__deltify,
186251881Speter  svn_fs_fs__lock,
187251881Speter  svn_fs_fs__generate_lock_token,
188251881Speter  svn_fs_fs__unlock,
189251881Speter  svn_fs_fs__get_lock,
190251881Speter  svn_fs_fs__get_locks,
191251881Speter  svn_fs_fs__verify_root,
192251881Speter  fs_freeze,
193251881Speter  fs_set_errcall
194251881Speter};
195251881Speter
196251881Speter
197251881Speter/* Creating a new filesystem. */
198251881Speter
199251881Speter/* Set up vtable and fsap_data fields in FS. */
200251881Speterstatic svn_error_t *
201251881Speterinitialize_fs_struct(svn_fs_t *fs)
202251881Speter{
203251881Speter  fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd));
204251881Speter  fs->vtable = &fs_vtable;
205251881Speter  fs->fsap_data = ffd;
206251881Speter  return SVN_NO_ERROR;
207251881Speter}
208251881Speter
209251881Speter/* This implements the fs_library_vtable_t.create() API.  Create a new
210251881Speter   fsfs-backed Subversion filesystem at path PATH and link it into
211251881Speter   *FS.  Perform temporary allocations in POOL, and fs-global allocations
212251881Speter   in COMMON_POOL. */
213251881Speterstatic svn_error_t *
214251881Speterfs_create(svn_fs_t *fs, const char *path, apr_pool_t *pool,
215251881Speter          apr_pool_t *common_pool)
216251881Speter{
217251881Speter  SVN_ERR(svn_fs__check_fs(fs, FALSE));
218251881Speter
219251881Speter  SVN_ERR(initialize_fs_struct(fs));
220251881Speter
221251881Speter  SVN_ERR(svn_fs_fs__create(fs, path, pool));
222251881Speter
223251881Speter  SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
224251881Speter  return fs_serialized_init(fs, common_pool, pool);
225251881Speter}
226251881Speter
227251881Speter
228251881Speter
229251881Speter/* Gaining access to an existing filesystem.  */
230251881Speter
231251881Speter/* This implements the fs_library_vtable_t.open() API.  Open an FSFS
232251881Speter   Subversion filesystem located at PATH, set *FS to point to the
233251881Speter   correct vtable for the filesystem.  Use POOL for any temporary
234251881Speter   allocations, and COMMON_POOL for fs-global allocations. */
235251881Speterstatic svn_error_t *
236251881Speterfs_open(svn_fs_t *fs, const char *path, apr_pool_t *pool,
237251881Speter        apr_pool_t *common_pool)
238251881Speter{
239251881Speter  SVN_ERR(initialize_fs_struct(fs));
240251881Speter
241251881Speter  SVN_ERR(svn_fs_fs__open(fs, path, pool));
242251881Speter
243251881Speter  SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
244251881Speter  return fs_serialized_init(fs, common_pool, pool);
245251881Speter}
246251881Speter
247251881Speter
248251881Speter
249251881Speter/* This implements the fs_library_vtable_t.open_for_recovery() API. */
250251881Speterstatic svn_error_t *
251251881Speterfs_open_for_recovery(svn_fs_t *fs,
252251881Speter                     const char *path,
253251881Speter                     apr_pool_t *pool, apr_pool_t *common_pool)
254251881Speter{
255251881Speter  /* Recovery for FSFS is currently limited to recreating the 'current'
256251881Speter     file from the latest revision. */
257251881Speter
258251881Speter  /* The only thing we have to watch out for is that the 'current' file
259251881Speter     might not exist.  So we'll try to create it here unconditionally,
260251881Speter     and just ignore any errors that might indicate that it's already
261251881Speter     present. (We'll need it to exist later anyway as a source for the
262251881Speter     new file's permissions). */
263251881Speter
264251881Speter  /* Use a partly-filled fs pointer first to create 'current'.  This will fail
265251881Speter     if 'current' already exists, but we don't care about that. */
266251881Speter  fs->path = apr_pstrdup(fs->pool, path);
267251881Speter  svn_error_clear(svn_io_file_create(svn_fs_fs__path_current(fs, pool),
268251881Speter                                     "0 1 1\n", pool));
269251881Speter
270251881Speter  /* Now open the filesystem properly by calling the vtable method directly. */
271251881Speter  return fs_open(fs, path, pool, common_pool);
272251881Speter}
273251881Speter
274251881Speter
275251881Speter
276251881Speter/* This implements the fs_library_vtable_t.upgrade_fs() API. */
277251881Speterstatic svn_error_t *
278251881Speterfs_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool,
279251881Speter           apr_pool_t *common_pool)
280251881Speter{
281251881Speter  SVN_ERR(svn_fs__check_fs(fs, FALSE));
282251881Speter  SVN_ERR(initialize_fs_struct(fs));
283251881Speter  SVN_ERR(svn_fs_fs__open(fs, path, pool));
284251881Speter  SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
285251881Speter  SVN_ERR(fs_serialized_init(fs, common_pool, pool));
286251881Speter  return svn_fs_fs__upgrade(fs, pool);
287251881Speter}
288251881Speter
289251881Speterstatic svn_error_t *
290251881Speterfs_verify(svn_fs_t *fs, const char *path,
291251881Speter          svn_revnum_t start,
292251881Speter          svn_revnum_t end,
293251881Speter          svn_fs_progress_notify_func_t notify_func,
294251881Speter          void *notify_baton,
295251881Speter          svn_cancel_func_t cancel_func,
296251881Speter          void *cancel_baton,
297251881Speter          apr_pool_t *pool,
298251881Speter          apr_pool_t *common_pool)
299251881Speter{
300251881Speter  SVN_ERR(svn_fs__check_fs(fs, FALSE));
301251881Speter  SVN_ERR(initialize_fs_struct(fs));
302251881Speter  SVN_ERR(svn_fs_fs__open(fs, path, pool));
303251881Speter  SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
304251881Speter  SVN_ERR(fs_serialized_init(fs, common_pool, pool));
305251881Speter  return svn_fs_fs__verify(fs, start, end, notify_func, notify_baton,
306251881Speter                           cancel_func, cancel_baton, pool);
307251881Speter}
308251881Speter
309251881Speterstatic svn_error_t *
310251881Speterfs_pack(svn_fs_t *fs,
311251881Speter        const char *path,
312251881Speter        svn_fs_pack_notify_t notify_func,
313251881Speter        void *notify_baton,
314251881Speter        svn_cancel_func_t cancel_func,
315251881Speter        void *cancel_baton,
316251881Speter        apr_pool_t *pool,
317251881Speter        apr_pool_t *common_pool)
318251881Speter{
319251881Speter  SVN_ERR(svn_fs__check_fs(fs, FALSE));
320251881Speter  SVN_ERR(initialize_fs_struct(fs));
321251881Speter  SVN_ERR(svn_fs_fs__open(fs, path, pool));
322251881Speter  SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
323251881Speter  SVN_ERR(fs_serialized_init(fs, common_pool, pool));
324251881Speter  return svn_fs_fs__pack(fs, notify_func, notify_baton,
325251881Speter                         cancel_func, cancel_baton, pool);
326251881Speter}
327251881Speter
328251881Speter
329251881Speter
330251881Speter
331251881Speter/* This implements the fs_library_vtable_t.hotcopy() API.  Copy a
332251881Speter   possibly live Subversion filesystem SRC_FS from SRC_PATH to a
333251881Speter   DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to
334251881Speter   re-copy data which already exists in DST_FS.
335251881Speter   The CLEAN_LOGS argument is ignored and included for Subversion
336251881Speter   1.0.x compatibility.  Perform all temporary allocations in POOL. */
337251881Speterstatic svn_error_t *
338251881Speterfs_hotcopy(svn_fs_t *src_fs,
339251881Speter           svn_fs_t *dst_fs,
340251881Speter           const char *src_path,
341251881Speter           const char *dst_path,
342251881Speter           svn_boolean_t clean_logs,
343251881Speter           svn_boolean_t incremental,
344251881Speter           svn_cancel_func_t cancel_func,
345251881Speter           void *cancel_baton,
346251881Speter           apr_pool_t *pool)
347251881Speter{
348251881Speter  SVN_ERR(svn_fs__check_fs(src_fs, FALSE));
349251881Speter  SVN_ERR(initialize_fs_struct(src_fs));
350251881Speter  SVN_ERR(svn_fs_fs__open(src_fs, src_path, pool));
351251881Speter  SVN_ERR(svn_fs_fs__initialize_caches(src_fs, pool));
352251881Speter  SVN_ERR(fs_serialized_init(src_fs, pool, pool));
353251881Speter
354251881Speter  SVN_ERR(svn_fs__check_fs(dst_fs, FALSE));
355251881Speter  SVN_ERR(initialize_fs_struct(dst_fs));
356251881Speter  /* In INCREMENTAL mode, svn_fs_fs__hotcopy() will open DST_FS.
357251881Speter     Otherwise, it's not an FS yet --- possibly just an empty dir --- so
358251881Speter     can't be opened.
359251881Speter   */
360251881Speter  return svn_fs_fs__hotcopy(src_fs, dst_fs, src_path, dst_path,
361251881Speter                            incremental, cancel_func, cancel_baton, pool);
362251881Speter}
363251881Speter
364251881Speter
365251881Speter
366251881Speter/* This function is included for Subversion 1.0.x compatibility.  It
367251881Speter   has no effect for fsfs backed Subversion filesystems.  It conforms
368251881Speter   to the fs_library_vtable_t.bdb_logfiles() API. */
369251881Speterstatic svn_error_t *
370251881Speterfs_logfiles(apr_array_header_t **logfiles,
371251881Speter            const char *path,
372251881Speter            svn_boolean_t only_unused,
373251881Speter            apr_pool_t *pool)
374251881Speter{
375251881Speter  /* A no-op for FSFS. */
376251881Speter  *logfiles = apr_array_make(pool, 0, sizeof(const char *));
377251881Speter
378251881Speter  return SVN_NO_ERROR;
379251881Speter}
380251881Speter
381251881Speter
382251881Speter
383251881Speter
384251881Speter
385251881Speter/* Delete the filesystem located at path PATH.  Perform any temporary
386251881Speter   allocations in POOL. */
387251881Speterstatic svn_error_t *
388251881Speterfs_delete_fs(const char *path,
389251881Speter             apr_pool_t *pool)
390251881Speter{
391251881Speter  /* Remove everything. */
392251881Speter  return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
393251881Speter}
394251881Speter
395251881Speterstatic const svn_version_t *
396251881Speterfs_version(void)
397251881Speter{
398251881Speter  SVN_VERSION_BODY;
399251881Speter}
400251881Speter
401251881Speterstatic const char *
402251881Speterfs_get_description(void)
403251881Speter{
404251881Speter  return _("Module for working with a plain file (FSFS) repository.");
405251881Speter}
406251881Speter
407251881Speterstatic svn_error_t *
408251881Speterfs_set_svn_fs_open(svn_fs_t *fs,
409251881Speter                   svn_error_t *(*svn_fs_open_)(svn_fs_t **,
410251881Speter                                                const char *,
411251881Speter                                                apr_hash_t *,
412251881Speter                                                apr_pool_t *))
413251881Speter{
414251881Speter  fs_fs_data_t *ffd = fs->fsap_data;
415251881Speter  ffd->svn_fs_open_ = svn_fs_open_;
416251881Speter  return SVN_NO_ERROR;
417251881Speter}
418251881Speter
419251881Speter
420251881Speter/* Base FS library vtable, used by the FS loader library. */
421251881Speter
422251881Speterstatic fs_library_vtable_t library_vtable = {
423251881Speter  fs_version,
424251881Speter  fs_create,
425251881Speter  fs_open,
426251881Speter  fs_open_for_recovery,
427251881Speter  fs_upgrade,
428251881Speter  fs_verify,
429251881Speter  fs_delete_fs,
430251881Speter  fs_hotcopy,
431251881Speter  fs_get_description,
432251881Speter  svn_fs_fs__recover,
433251881Speter  fs_pack,
434251881Speter  fs_logfiles,
435251881Speter  NULL /* parse_id */,
436251881Speter  fs_set_svn_fs_open
437251881Speter};
438251881Speter
439251881Spetersvn_error_t *
440251881Spetersvn_fs_fs__init(const svn_version_t *loader_version,
441251881Speter                fs_library_vtable_t **vtable, apr_pool_t* common_pool)
442251881Speter{
443251881Speter  static const svn_version_checklist_t checklist[] =
444251881Speter    {
445251881Speter      { "svn_subr",  svn_subr_version },
446251881Speter      { "svn_delta", svn_delta_version },
447251881Speter      { NULL, NULL }
448251881Speter    };
449251881Speter
450251881Speter  /* Simplified version check to make sure we can safely use the
451251881Speter     VTABLE parameter. The FS loader does a more exhaustive check. */
452251881Speter  if (loader_version->major != SVN_VER_MAJOR)
453251881Speter    return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
454251881Speter                             _("Unsupported FS loader version (%d) for fsfs"),
455251881Speter                             loader_version->major);
456262253Speter  SVN_ERR(svn_ver_check_list2(fs_version(), checklist, svn_ver_equal));
457251881Speter
458251881Speter  *vtable = &library_vtable;
459251881Speter  return SVN_NO_ERROR;
460251881Speter}
461