]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_fs_fs/fs.c
MFC r257129,257936,258084,258569,258602,262250,262251
[FreeBSD/stable/10.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       SVN_ERR(svn_mutex__init(&ffsd->txn_list_lock,
102                               SVN_FS_FS__USE_LOCK_MUTEX, common_pool));
103
104       key = apr_pstrdup(common_pool, key);
105       status = apr_pool_userdata_set(ffsd, key, NULL, common_pool);
106       if (status)
107         return svn_error_wrap_apr(status, _("Can't store FSFS shared data"));
108     }
109
110   ffd->shared = ffsd;
111
112   return SVN_NO_ERROR;
113 }
114
115 \f
116
117 /* This function is provided for Subversion 1.0.x compatibility.  It
118    has no effect for fsfs backed Subversion filesystems.  It conforms
119    to the fs_library_vtable_t.bdb_set_errcall() API. */
120 static svn_error_t *
121 fs_set_errcall(svn_fs_t *fs,
122                void (*db_errcall_fcn)(const char *errpfx, char *msg))
123 {
124
125   return SVN_NO_ERROR;
126 }
127
128 struct fs_freeze_baton_t {
129   svn_fs_t *fs;
130   svn_fs_freeze_func_t freeze_func;
131   void *freeze_baton;
132 };
133
134 static svn_error_t *
135 fs_freeze_body(void *baton,
136                apr_pool_t *pool)
137 {
138   struct fs_freeze_baton_t *b = baton;
139   svn_boolean_t exists;
140
141   SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, b->fs, pool));
142   if (exists)
143     SVN_ERR(svn_fs_fs__lock_rep_cache(b->fs, pool));
144
145   SVN_ERR(b->freeze_func(b->freeze_baton, pool));
146
147   return SVN_NO_ERROR;
148 }
149
150 static svn_error_t *
151 fs_freeze(svn_fs_t *fs,
152           svn_fs_freeze_func_t freeze_func,
153           void *freeze_baton,
154           apr_pool_t *pool)
155 {
156   struct fs_freeze_baton_t b;
157
158   b.fs = fs;
159   b.freeze_func = freeze_func;
160   b.freeze_baton = freeze_baton;
161
162   SVN_ERR(svn_fs__check_fs(fs, TRUE));
163   SVN_ERR(svn_fs_fs__with_write_lock(fs, fs_freeze_body, &b, pool));
164
165   return SVN_NO_ERROR;
166 }
167
168 \f
169
170 /* The vtable associated with a specific open filesystem. */
171 static fs_vtable_t fs_vtable = {
172   svn_fs_fs__youngest_rev,
173   svn_fs_fs__revision_prop,
174   svn_fs_fs__revision_proplist,
175   svn_fs_fs__change_rev_prop,
176   svn_fs_fs__set_uuid,
177   svn_fs_fs__revision_root,
178   svn_fs_fs__begin_txn,
179   svn_fs_fs__open_txn,
180   svn_fs_fs__purge_txn,
181   svn_fs_fs__list_transactions,
182   svn_fs_fs__deltify,
183   svn_fs_fs__lock,
184   svn_fs_fs__generate_lock_token,
185   svn_fs_fs__unlock,
186   svn_fs_fs__get_lock,
187   svn_fs_fs__get_locks,
188   svn_fs_fs__verify_root,
189   fs_freeze,
190   fs_set_errcall
191 };
192
193 \f
194 /* Creating a new filesystem. */
195
196 /* Set up vtable and fsap_data fields in FS. */
197 static svn_error_t *
198 initialize_fs_struct(svn_fs_t *fs)
199 {
200   fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd));
201   fs->vtable = &fs_vtable;
202   fs->fsap_data = ffd;
203   return SVN_NO_ERROR;
204 }
205
206 /* This implements the fs_library_vtable_t.create() API.  Create a new
207    fsfs-backed Subversion filesystem at path PATH and link it into
208    *FS.  Perform temporary allocations in POOL, and fs-global allocations
209    in COMMON_POOL. */
210 static svn_error_t *
211 fs_create(svn_fs_t *fs, const char *path, apr_pool_t *pool,
212           apr_pool_t *common_pool)
213 {
214   SVN_ERR(svn_fs__check_fs(fs, FALSE));
215
216   SVN_ERR(initialize_fs_struct(fs));
217
218   SVN_ERR(svn_fs_fs__create(fs, path, pool));
219
220   SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
221   return fs_serialized_init(fs, common_pool, pool);
222 }
223
224
225 \f
226 /* Gaining access to an existing filesystem.  */
227
228 /* This implements the fs_library_vtable_t.open() API.  Open an FSFS
229    Subversion filesystem located at PATH, set *FS to point to the
230    correct vtable for the filesystem.  Use POOL for any temporary
231    allocations, and COMMON_POOL for fs-global allocations. */
232 static svn_error_t *
233 fs_open(svn_fs_t *fs, const char *path, apr_pool_t *pool,
234         apr_pool_t *common_pool)
235 {
236   SVN_ERR(initialize_fs_struct(fs));
237
238   SVN_ERR(svn_fs_fs__open(fs, path, pool));
239
240   SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
241   return fs_serialized_init(fs, common_pool, pool);
242 }
243
244 \f
245
246 /* This implements the fs_library_vtable_t.open_for_recovery() API. */
247 static svn_error_t *
248 fs_open_for_recovery(svn_fs_t *fs,
249                      const char *path,
250                      apr_pool_t *pool, apr_pool_t *common_pool)
251 {
252   /* Recovery for FSFS is currently limited to recreating the 'current'
253      file from the latest revision. */
254
255   /* The only thing we have to watch out for is that the 'current' file
256      might not exist.  So we'll try to create it here unconditionally,
257      and just ignore any errors that might indicate that it's already
258      present. (We'll need it to exist later anyway as a source for the
259      new file's permissions). */
260
261   /* Use a partly-filled fs pointer first to create 'current'.  This will fail
262      if 'current' already exists, but we don't care about that. */
263   fs->path = apr_pstrdup(fs->pool, path);
264   svn_error_clear(svn_io_file_create(svn_fs_fs__path_current(fs, pool),
265                                      "0 1 1\n", pool));
266
267   /* Now open the filesystem properly by calling the vtable method directly. */
268   return fs_open(fs, path, pool, common_pool);
269 }
270
271 \f
272
273 /* This implements the fs_library_vtable_t.upgrade_fs() API. */
274 static svn_error_t *
275 fs_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool,
276            apr_pool_t *common_pool)
277 {
278   SVN_ERR(svn_fs__check_fs(fs, FALSE));
279   SVN_ERR(initialize_fs_struct(fs));
280   SVN_ERR(svn_fs_fs__open(fs, path, pool));
281   SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
282   SVN_ERR(fs_serialized_init(fs, common_pool, pool));
283   return svn_fs_fs__upgrade(fs, pool);
284 }
285
286 static svn_error_t *
287 fs_verify(svn_fs_t *fs, const char *path,
288           svn_revnum_t start,
289           svn_revnum_t end,
290           svn_fs_progress_notify_func_t notify_func,
291           void *notify_baton,
292           svn_cancel_func_t cancel_func,
293           void *cancel_baton,
294           apr_pool_t *pool,
295           apr_pool_t *common_pool)
296 {
297   SVN_ERR(svn_fs__check_fs(fs, FALSE));
298   SVN_ERR(initialize_fs_struct(fs));
299   SVN_ERR(svn_fs_fs__open(fs, path, pool));
300   SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
301   SVN_ERR(fs_serialized_init(fs, common_pool, pool));
302   return svn_fs_fs__verify(fs, start, end, notify_func, notify_baton,
303                            cancel_func, cancel_baton, pool);
304 }
305
306 static svn_error_t *
307 fs_pack(svn_fs_t *fs,
308         const char *path,
309         svn_fs_pack_notify_t notify_func,
310         void *notify_baton,
311         svn_cancel_func_t cancel_func,
312         void *cancel_baton,
313         apr_pool_t *pool,
314         apr_pool_t *common_pool)
315 {
316   SVN_ERR(svn_fs__check_fs(fs, FALSE));
317   SVN_ERR(initialize_fs_struct(fs));
318   SVN_ERR(svn_fs_fs__open(fs, path, pool));
319   SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
320   SVN_ERR(fs_serialized_init(fs, common_pool, pool));
321   return svn_fs_fs__pack(fs, notify_func, notify_baton,
322                          cancel_func, cancel_baton, pool);
323 }
324
325
326 \f
327
328 /* This implements the fs_library_vtable_t.hotcopy() API.  Copy a
329    possibly live Subversion filesystem SRC_FS from SRC_PATH to a
330    DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to
331    re-copy data which already exists in DST_FS.
332    The CLEAN_LOGS argument is ignored and included for Subversion
333    1.0.x compatibility.  Perform all temporary allocations in POOL. */
334 static svn_error_t *
335 fs_hotcopy(svn_fs_t *src_fs,
336            svn_fs_t *dst_fs,
337            const char *src_path,
338            const char *dst_path,
339            svn_boolean_t clean_logs,
340            svn_boolean_t incremental,
341            svn_cancel_func_t cancel_func,
342            void *cancel_baton,
343            apr_pool_t *pool)
344 {
345   SVN_ERR(svn_fs__check_fs(src_fs, FALSE));
346   SVN_ERR(initialize_fs_struct(src_fs));
347   SVN_ERR(svn_fs_fs__open(src_fs, src_path, pool));
348   SVN_ERR(svn_fs_fs__initialize_caches(src_fs, pool));
349   SVN_ERR(fs_serialized_init(src_fs, pool, pool));
350
351   SVN_ERR(svn_fs__check_fs(dst_fs, FALSE));
352   SVN_ERR(initialize_fs_struct(dst_fs));
353   /* In INCREMENTAL mode, svn_fs_fs__hotcopy() will open DST_FS.
354      Otherwise, it's not an FS yet --- possibly just an empty dir --- so
355      can't be opened.
356    */
357   return svn_fs_fs__hotcopy(src_fs, dst_fs, src_path, dst_path,
358                             incremental, cancel_func, cancel_baton, pool);
359 }
360
361 \f
362
363 /* This function is included for Subversion 1.0.x compatibility.  It
364    has no effect for fsfs backed Subversion filesystems.  It conforms
365    to the fs_library_vtable_t.bdb_logfiles() API. */
366 static svn_error_t *
367 fs_logfiles(apr_array_header_t **logfiles,
368             const char *path,
369             svn_boolean_t only_unused,
370             apr_pool_t *pool)
371 {
372   /* A no-op for FSFS. */
373   *logfiles = apr_array_make(pool, 0, sizeof(const char *));
374
375   return SVN_NO_ERROR;
376 }
377
378
379
380 \f
381
382 /* Delete the filesystem located at path PATH.  Perform any temporary
383    allocations in POOL. */
384 static svn_error_t *
385 fs_delete_fs(const char *path,
386              apr_pool_t *pool)
387 {
388   /* Remove everything. */
389   return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
390 }
391
392 static const svn_version_t *
393 fs_version(void)
394 {
395   SVN_VERSION_BODY;
396 }
397
398 static const char *
399 fs_get_description(void)
400 {
401   return _("Module for working with a plain file (FSFS) repository.");
402 }
403
404 static svn_error_t *
405 fs_set_svn_fs_open(svn_fs_t *fs,
406                    svn_error_t *(*svn_fs_open_)(svn_fs_t **,
407                                                 const char *,
408                                                 apr_hash_t *,
409                                                 apr_pool_t *))
410 {
411   fs_fs_data_t *ffd = fs->fsap_data;
412   ffd->svn_fs_open_ = svn_fs_open_;
413   return SVN_NO_ERROR;
414 }
415
416 \f
417 /* Base FS library vtable, used by the FS loader library. */
418
419 static fs_library_vtable_t library_vtable = {
420   fs_version,
421   fs_create,
422   fs_open,
423   fs_open_for_recovery,
424   fs_upgrade,
425   fs_verify,
426   fs_delete_fs,
427   fs_hotcopy,
428   fs_get_description,
429   svn_fs_fs__recover,
430   fs_pack,
431   fs_logfiles,
432   NULL /* parse_id */,
433   fs_set_svn_fs_open
434 };
435
436 svn_error_t *
437 svn_fs_fs__init(const svn_version_t *loader_version,
438                 fs_library_vtable_t **vtable, apr_pool_t* common_pool)
439 {
440   static const svn_version_checklist_t checklist[] =
441     {
442       { "svn_subr",  svn_subr_version },
443       { "svn_delta", svn_delta_version },
444       { NULL, NULL }
445     };
446
447   /* Simplified version check to make sure we can safely use the
448      VTABLE parameter. The FS loader does a more exhaustive check. */
449   if (loader_version->major != SVN_VER_MAJOR)
450     return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
451                              _("Unsupported FS loader version (%d) for fsfs"),
452                              loader_version->major);
453   SVN_ERR(svn_ver_check_list2(fs_version(), checklist, svn_ver_equal));
454
455   *vtable = &library_vtable;
456   return SVN_NO_ERROR;
457 }