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