]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_fs_fs/fs.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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
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 \f
52
53 static svn_error_t *
54 fs_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 \f
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. */
119 static svn_error_t *
120 fs_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
127 struct fs_freeze_baton_t {
128   svn_fs_t *fs;
129   svn_fs_freeze_func_t freeze_func;
130   void *freeze_baton;
131 };
132
133 static svn_error_t *
134 fs_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
149 static svn_error_t *
150 fs_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 \f
168
169 /* The vtable associated with a specific open filesystem. */
170 static 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 \f
193 /* Creating a new filesystem. */
194
195 /* Set up vtable and fsap_data fields in FS. */
196 static svn_error_t *
197 initialize_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. */
209 static svn_error_t *
210 fs_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 \f
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. */
231 static svn_error_t *
232 fs_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 \f
244
245 /* This implements the fs_library_vtable_t.open_for_recovery() API. */
246 static svn_error_t *
247 fs_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 \f
271
272 /* This implements the fs_library_vtable_t.upgrade_fs() API. */
273 static svn_error_t *
274 fs_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
285 static svn_error_t *
286 fs_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
305 static svn_error_t *
306 fs_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 \f
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. */
333 static svn_error_t *
334 fs_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 \f
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. */
365 static svn_error_t *
366 fs_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 \f
380
381 /* Delete the filesystem located at path PATH.  Perform any temporary
382    allocations in POOL. */
383 static svn_error_t *
384 fs_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
391 static const svn_version_t *
392 fs_version(void)
393 {
394   SVN_VERSION_BODY;
395 }
396
397 static const char *
398 fs_get_description(void)
399 {
400   return _("Module for working with a plain file (FSFS) repository.");
401 }
402
403 static svn_error_t *
404 fs_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 \f
416 /* Base FS library vtable, used by the FS loader library. */
417
418 static 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
435 svn_error_t *
436 svn_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 }