]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/subversion/subversion/libsvn_fs_fs/fs.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / subversion / subversion / libsvn_fs_fs / fs.c
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 \f
53
54 static svn_error_t *
55 fs_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 \f
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. */
123 static svn_error_t *
124 fs_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
131 struct fs_freeze_baton_t {
132   svn_fs_t *fs;
133   svn_fs_freeze_func_t freeze_func;
134   void *freeze_baton;
135 };
136
137 static svn_error_t *
138 fs_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
153 static svn_error_t *
154 fs_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 \f
172
173 /* The vtable associated with a specific open filesystem. */
174 static 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 \f
197 /* Creating a new filesystem. */
198
199 /* Set up vtable and fsap_data fields in FS. */
200 static svn_error_t *
201 initialize_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. */
213 static svn_error_t *
214 fs_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 \f
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. */
235 static svn_error_t *
236 fs_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 \f
248
249 /* This implements the fs_library_vtable_t.open_for_recovery() API. */
250 static svn_error_t *
251 fs_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 \f
275
276 /* This implements the fs_library_vtable_t.upgrade_fs() API. */
277 static svn_error_t *
278 fs_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
289 static svn_error_t *
290 fs_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
309 static svn_error_t *
310 fs_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 \f
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. */
337 static svn_error_t *
338 fs_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 \f
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. */
369 static svn_error_t *
370 fs_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 \f
384
385 /* Delete the filesystem located at path PATH.  Perform any temporary
386    allocations in POOL. */
387 static svn_error_t *
388 fs_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
395 static const svn_version_t *
396 fs_version(void)
397 {
398   SVN_VERSION_BODY;
399 }
400
401 static const char *
402 fs_get_description(void)
403 {
404   return _("Module for working with a plain file (FSFS) repository.");
405 }
406
407 static svn_error_t *
408 fs_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 \f
420 /* Base FS library vtable, used by the FS loader library. */
421
422 static 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
439 svn_error_t *
440 svn_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 }