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 
31 #include "svn_fs.h"
32 #include "svn_delta.h"
33 #include "svn_version.h"
34 #include "svn_pools.h"
35 #include "batch_fsync.h"
36 #include "fs.h"
37 #include "fs_x.h"
38 #include "pack.h"
39 #include "recovery.h"
40 #include "hotcopy.h"
41 #include "verify.h"
42 #include "tree.h"
43 #include "lock.h"
44 #include "id.h"
45 #include "revprops.h"
46 #include "rep-cache.h"
47 #include "transaction.h"
48 #include "util.h"
49 #include "svn_private_config.h"
50 #include "private/svn_fs_util.h"
51 
52 #include "../libsvn_fs/fs-loader.h"
53 
54 /* A prefix for the pool userdata variables used to hold
55    per-filesystem shared data.  See fs_serialized_init. */
56 #define SVN_FSX_SHARED_USERDATA_PREFIX "svn-fsx-shared-"
57 
58 
59 
60 /* Initialize the part of FS that requires global serialization across all
61    instances.  The caller is responsible of ensuring that serialization.
62    Use COMMON_POOL for process-wide and SCRATCH_POOL for temporary
63    allocations. */
64 static svn_error_t *
x_serialized_init(svn_fs_t * fs,apr_pool_t * common_pool,apr_pool_t * scratch_pool)65 x_serialized_init(svn_fs_t *fs,
66                   apr_pool_t *common_pool,
67                   apr_pool_t *scratch_pool)
68 {
69   svn_fs_x__data_t *ffd = fs->fsap_data;
70   const char *key;
71   void *val;
72   svn_fs_x__shared_data_t *ffsd;
73   apr_status_t status;
74 
75   /* Note that we are allocating a small amount of long-lived data for
76      each separate repository opened during the lifetime of the
77      svn_fs_initialize pool.  It's unlikely that anyone will notice
78      the modest expenditure; the alternative is to allocate each structure
79      in a subpool, add a reference-count, and add a serialized destructor
80      to the FS vtable.  That's more machinery than it's worth.
81 
82      Picking an appropriate key for the shared data is tricky, because,
83      unfortunately, a filesystem UUID is not really unique.  It is implicitly
84      shared between hotcopied (1), dump / loaded (2) or naively copied (3)
85      filesystems.  We tackle this problem by using a combination of the UUID
86      and an instance ID as the key.  This allows us to avoid key clashing
87      in (1) and (2).
88 
89      Speaking of (3), there is not so much we can do about it, except maybe
90      provide a convenient way of fixing things.  Naively copied filesystems
91      have identical filesystem UUIDs *and* instance IDs.  With the key being
92      a combination of these two, clashes can be fixed by changing either of
93      them (or both), e.g. with svn_fs_set_uuid(). */
94 
95 
96   SVN_ERR_ASSERT(fs->uuid);
97   SVN_ERR_ASSERT(ffd->instance_id);
98 
99   key = apr_pstrcat(scratch_pool, SVN_FSX_SHARED_USERDATA_PREFIX,
100                     fs->uuid, ":", ffd->instance_id, SVN_VA_NULL);
101   status = apr_pool_userdata_get(&val, key, common_pool);
102   if (status)
103     return svn_error_wrap_apr(status, _("Can't fetch FSX shared data"));
104   ffsd = val;
105 
106   if (!ffsd)
107     {
108       ffsd = apr_pcalloc(common_pool, sizeof(*ffsd));
109       ffsd->common_pool = common_pool;
110 
111       /* POSIX fcntl locks are per-process, so we need a mutex for
112          intra-process synchronization when grabbing the repository write
113          lock. */
114       SVN_ERR(svn_mutex__init(&ffsd->fs_write_lock,
115                               SVN_FS_X__USE_LOCK_MUTEX, common_pool));
116 
117       /* ... the pack lock ... */
118       SVN_ERR(svn_mutex__init(&ffsd->fs_pack_lock,
119                               SVN_FS_X__USE_LOCK_MUTEX, common_pool));
120 
121       /* ... not to mention locking the txn-current file. */
122       SVN_ERR(svn_mutex__init(&ffsd->txn_current_lock,
123                               SVN_FS_X__USE_LOCK_MUTEX, common_pool));
124 
125       /* We also need a mutex for synchronizing access to the active
126          transaction list and free transaction pointer. */
127       SVN_ERR(svn_mutex__init(&ffsd->txn_list_lock, TRUE, common_pool));
128 
129       key = apr_pstrdup(common_pool, key);
130       status = apr_pool_userdata_set(ffsd, key, NULL, common_pool);
131       if (status)
132         return svn_error_wrap_apr(status, _("Can't store FSX shared data"));
133     }
134 
135   ffd->shared = ffsd;
136 
137   return SVN_NO_ERROR;
138 }
139 
140 svn_error_t *
svn_fs_x__initialize_shared_data(svn_fs_t * fs,svn_mutex__t * common_pool_lock,apr_pool_t * scratch_pool,apr_pool_t * common_pool)141 svn_fs_x__initialize_shared_data(svn_fs_t *fs,
142                                  svn_mutex__t *common_pool_lock,
143                                  apr_pool_t *scratch_pool,
144                                  apr_pool_t *common_pool)
145 {
146   SVN_MUTEX__WITH_LOCK(common_pool_lock,
147                        x_serialized_init(fs, common_pool, scratch_pool));
148 
149   return SVN_NO_ERROR;
150 }
151 
152 
153 
154 /* This function is provided for Subversion 1.0.x compatibility.  It
155    has no effect for fsx backed Subversion filesystems.  It conforms
156    to the fs_library_vtable_t.bdb_set_errcall() API. */
157 static svn_error_t *
x_set_errcall(svn_fs_t * fs,void (* db_errcall_fcn)(const char * errpfx,char * msg))158 x_set_errcall(svn_fs_t *fs,
159               void (*db_errcall_fcn)(const char *errpfx, char *msg))
160 {
161 
162   return SVN_NO_ERROR;
163 }
164 
165 typedef struct x_freeze_baton_t {
166   svn_fs_t *fs;
167   svn_fs_freeze_func_t freeze_func;
168   void *freeze_baton;
169 } x_freeze_baton_t;
170 
171 static svn_error_t *
x_freeze_body(void * baton,apr_pool_t * scratch_pool)172 x_freeze_body(void *baton,
173               apr_pool_t *scratch_pool)
174 {
175   x_freeze_baton_t *b = baton;
176   svn_boolean_t exists;
177 
178   SVN_ERR(svn_fs_x__exists_rep_cache(&exists, b->fs, scratch_pool));
179   if (exists)
180     SVN_ERR(svn_fs_x__with_rep_cache_lock(b->fs,
181                                           b->freeze_func, b->freeze_baton,
182                                           scratch_pool));
183   else
184     SVN_ERR(b->freeze_func(b->freeze_baton, scratch_pool));
185 
186   return SVN_NO_ERROR;
187 }
188 
189 static svn_error_t *
x_freeze_body2(void * baton,apr_pool_t * scratch_pool)190 x_freeze_body2(void *baton,
191                apr_pool_t *scratch_pool)
192 {
193   x_freeze_baton_t *b = baton;
194   SVN_ERR(svn_fs_x__with_write_lock(b->fs, x_freeze_body, baton,
195                                     scratch_pool));
196 
197   return SVN_NO_ERROR;
198 }
199 
200 static svn_error_t *
x_freeze(svn_fs_t * fs,svn_fs_freeze_func_t freeze_func,void * freeze_baton,apr_pool_t * scratch_pool)201 x_freeze(svn_fs_t *fs,
202          svn_fs_freeze_func_t freeze_func,
203          void *freeze_baton,
204          apr_pool_t *scratch_pool)
205 {
206   x_freeze_baton_t b;
207 
208   b.fs = fs;
209   b.freeze_func = freeze_func;
210   b.freeze_baton = freeze_baton;
211 
212   SVN_ERR(svn_fs__check_fs(fs, TRUE));
213   SVN_ERR(svn_fs_x__with_pack_lock(fs, x_freeze_body2, &b, scratch_pool));
214 
215   return SVN_NO_ERROR;
216 }
217 
218 static svn_error_t *
x_info(const void ** fsx_info,svn_fs_t * fs,apr_pool_t * result_pool,apr_pool_t * scratch_pool)219 x_info(const void **fsx_info,
220        svn_fs_t *fs,
221        apr_pool_t *result_pool,
222        apr_pool_t *scratch_pool)
223 {
224   svn_fs_x__data_t *ffd = fs->fsap_data;
225   svn_fs_fsx_info_t *info = apr_palloc(result_pool, sizeof(*info));
226   info->fs_type = SVN_FS_TYPE_FSX;
227   info->shard_size = ffd->max_files_per_dir;
228   info->min_unpacked_rev = ffd->min_unpacked_rev;
229   *fsx_info = info;
230   return SVN_NO_ERROR;
231 }
232 
233 static svn_error_t *
x_refresh_revprops(svn_fs_t * fs,apr_pool_t * scratch_pool)234 x_refresh_revprops(svn_fs_t *fs,
235                    apr_pool_t *scratch_pool)
236 {
237   svn_fs_x__invalidate_revprop_generation(fs);
238   return SVN_NO_ERROR;
239 }
240 
241 /* Wrapper around svn_fs_x__get_revision_proplist() adapting between function
242    signatures. */
243 static svn_error_t *
x_revision_proplist(apr_hash_t ** proplist_p,svn_fs_t * fs,svn_revnum_t rev,svn_boolean_t refresh,apr_pool_t * result_pool,apr_pool_t * scratch_pool)244 x_revision_proplist(apr_hash_t **proplist_p,
245                     svn_fs_t *fs,
246                     svn_revnum_t rev,
247                     svn_boolean_t refresh,
248                     apr_pool_t *result_pool,
249                     apr_pool_t *scratch_pool)
250 {
251   /* No need to bypass the caches for r/o access to revprops. */
252   SVN_ERR(svn_fs_x__get_revision_proplist(proplist_p, fs, rev, FALSE,
253                                           refresh, result_pool,
254                                           scratch_pool));
255 
256   return SVN_NO_ERROR;
257 }
258 
259 /* Wrapper around svn_fs_x__set_uuid() adapting between function
260    signatures. */
261 static svn_error_t *
x_set_uuid(svn_fs_t * fs,const char * uuid,apr_pool_t * scratch_pool)262 x_set_uuid(svn_fs_t *fs,
263            const char *uuid,
264            apr_pool_t *scratch_pool)
265 {
266   /* Whenever we set a new UUID, imply that FS will also be a different
267    * instance (on formats that support this). */
268   return svn_error_trace(svn_fs_x__set_uuid(fs, uuid, NULL, TRUE,
269                                             scratch_pool));
270 }
271 
272 /* Wrapper around svn_fs_x__begin_txn() providing the scratch pool. */
273 static svn_error_t *
x_begin_txn(svn_fs_txn_t ** txn_p,svn_fs_t * fs,svn_revnum_t rev,apr_uint32_t flags,apr_pool_t * pool)274 x_begin_txn(svn_fs_txn_t **txn_p,
275             svn_fs_t *fs,
276             svn_revnum_t rev,
277             apr_uint32_t flags,
278             apr_pool_t *pool)
279 {
280   apr_pool_t *scratch_pool = svn_pool_create(pool);
281   SVN_ERR(svn_fs_x__begin_txn(txn_p, fs, rev, flags, pool, scratch_pool));
282   svn_pool_destroy(scratch_pool);
283 
284   return SVN_NO_ERROR;
285 }
286 
287 
288 
289 /* The vtable associated with a specific open filesystem. */
290 static fs_vtable_t fs_vtable = {
291   svn_fs_x__youngest_rev,
292   x_refresh_revprops,
293   svn_fs_x__revision_prop,
294   x_revision_proplist,
295   svn_fs_x__change_rev_prop,
296   x_set_uuid,
297   svn_fs_x__revision_root,
298   x_begin_txn,
299   svn_fs_x__open_txn,
300   svn_fs_x__purge_txn,
301   svn_fs_x__list_transactions,
302   svn_fs_x__deltify,
303   svn_fs_x__lock,
304   svn_fs_x__generate_lock_token,
305   svn_fs_x__unlock,
306   svn_fs_x__get_lock,
307   svn_fs_x__get_locks,
308   svn_fs_x__info_format,
309   svn_fs_x__info_config_files,
310   x_info,
311   svn_fs_x__verify_root,
312   x_freeze,
313   x_set_errcall,
314   NULL /* ioctl */
315 };
316 
317 
318 /* Creating a new filesystem. */
319 
320 /* Set up vtable and fsap_data fields in FS. */
321 static svn_error_t *
initialize_fs_struct(svn_fs_t * fs)322 initialize_fs_struct(svn_fs_t *fs)
323 {
324   svn_fs_x__data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd));
325   ffd->revprop_generation = -1;
326   ffd->flush_to_disk = TRUE;
327 
328   fs->vtable = &fs_vtable;
329   fs->fsap_data = ffd;
330   return SVN_NO_ERROR;
331 }
332 
333 /* Reset vtable and fsap_data fields in FS such that the FS is basically
334  * closed now.  Note that FS must not hold locks when you call this. */
335 static void
uninitialize_fs_struct(svn_fs_t * fs)336 uninitialize_fs_struct(svn_fs_t *fs)
337 {
338   fs->vtable = NULL;
339   fs->fsap_data = NULL;
340 }
341 
342 /* This implements the fs_library_vtable_t.create() API.  Create a new
343    fsx-backed Subversion filesystem at path PATH and link it into
344    *FS.
345 
346    Perform temporary allocations in SCRATCH_POOL, and fs-global allocations
347    in COMMON_POOL.  The latter must be serialized using COMMON_POOL_LOCK. */
348 static svn_error_t *
x_create(svn_fs_t * fs,const char * path,svn_mutex__t * common_pool_lock,apr_pool_t * scratch_pool,apr_pool_t * common_pool)349 x_create(svn_fs_t *fs,
350          const char *path,
351          svn_mutex__t *common_pool_lock,
352          apr_pool_t *scratch_pool,
353          apr_pool_t *common_pool)
354 {
355   SVN_ERR(svn_fs__check_fs(fs, FALSE));
356 
357   SVN_ERR(initialize_fs_struct(fs));
358 
359   SVN_ERR(svn_fs_x__create(fs, path, scratch_pool));
360 
361   SVN_ERR(svn_fs_x__initialize_caches(fs, scratch_pool));
362   SVN_MUTEX__WITH_LOCK(common_pool_lock,
363                        x_serialized_init(fs, common_pool, scratch_pool));
364 
365   return SVN_NO_ERROR;
366 }
367 
368 
369 
370 /* Gaining access to an existing filesystem.  */
371 
372 /* This implements the fs_library_vtable_t.open() API.  Open an FSX
373    Subversion filesystem located at PATH, set *FS to point to the
374    correct vtable for the filesystem.  Use SCRATCH_POOL for any temporary
375    allocations, and COMMON_POOL for fs-global allocations.
376    The latter must be serialized using COMMON_POOL_LOCK.  */
377 static svn_error_t *
x_open(svn_fs_t * fs,const char * path,svn_mutex__t * common_pool_lock,apr_pool_t * scratch_pool,apr_pool_t * common_pool)378 x_open(svn_fs_t *fs,
379        const char *path,
380        svn_mutex__t *common_pool_lock,
381        apr_pool_t *scratch_pool,
382        apr_pool_t *common_pool)
383 {
384   apr_pool_t *subpool = svn_pool_create(scratch_pool);
385 
386   SVN_ERR(svn_fs__check_fs(fs, FALSE));
387 
388   SVN_ERR(initialize_fs_struct(fs));
389 
390   SVN_ERR(svn_fs_x__open(fs, path, subpool));
391 
392   SVN_ERR(svn_fs_x__initialize_caches(fs, subpool));
393   SVN_MUTEX__WITH_LOCK(common_pool_lock,
394                        x_serialized_init(fs, common_pool, subpool));
395 
396   svn_pool_destroy(subpool);
397 
398   return SVN_NO_ERROR;
399 }
400 
401 
402 
403 /* This implements the fs_library_vtable_t.open_for_recovery() API. */
404 static svn_error_t *
x_open_for_recovery(svn_fs_t * fs,const char * path,svn_mutex__t * common_pool_lock,apr_pool_t * scratch_pool,apr_pool_t * common_pool)405 x_open_for_recovery(svn_fs_t *fs,
406                     const char *path,
407                     svn_mutex__t *common_pool_lock,
408                     apr_pool_t *scratch_pool,
409                     apr_pool_t *common_pool)
410 {
411   svn_error_t * err;
412   svn_revnum_t youngest_rev;
413   apr_pool_t * subpool = svn_pool_create(scratch_pool);
414 
415   /* Recovery for FSFS is currently limited to recreating the 'current'
416      file from the latest revision. */
417 
418   /* The only thing we have to watch out for is that the 'current' file
419      might not exist or contain garbage.  So we'll try to read it here
420      and provide or replace the existing file if we couldn't read it.
421      (We'll also need it to exist later anyway as a source for the new
422      file's permissions). */
423 
424   /* Use a partly-filled fs pointer first to create 'current'. */
425   fs->path = apr_pstrdup(fs->pool, path);
426 
427   SVN_ERR(initialize_fs_struct(fs));
428 
429   /* Figure out the repo format and check that we can even handle it. */
430   SVN_ERR(svn_fs_x__read_format_file(fs, subpool));
431 
432   /* Now, read 'current' and try to patch it if necessary. */
433   err = svn_fs_x__youngest_rev(&youngest_rev, fs, subpool);
434   if (err)
435     {
436       const char *file_path;
437 
438       /* 'current' file is missing or contains garbage.  Since we are trying
439        * to recover from whatever problem there is, being picky about the
440        * error code here won't do us much good.  If there is a persistent
441        * problem that we can't fix, it will show up when we try rewrite the
442        * file a few lines further below and we will report the failure back
443        * to the caller.
444        *
445        * Start recovery with HEAD = 0. */
446       svn_error_clear(err);
447       file_path = svn_fs_x__path_current(fs, subpool);
448 
449       /* Best effort to ensure the file exists and is valid.
450        * This may fail for r/o filesystems etc. */
451       SVN_ERR(svn_io_remove_file2(file_path, TRUE, subpool));
452       SVN_ERR(svn_io_file_create_empty(file_path, subpool));
453       SVN_ERR(svn_fs_x__write_current(fs, 0, subpool));
454     }
455 
456   uninitialize_fs_struct(fs);
457   svn_pool_destroy(subpool);
458 
459   /* Now open the filesystem properly by calling the vtable method directly. */
460   return x_open(fs, path, common_pool_lock, scratch_pool, common_pool);
461 }
462 
463 
464 
465 /* This implements the fs_library_vtable_t.upgrade_fs() API. */
466 static svn_error_t *
x_upgrade(svn_fs_t * fs,const char * path,svn_fs_upgrade_notify_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_mutex__t * common_pool_lock,apr_pool_t * scratch_pool,apr_pool_t * common_pool)467 x_upgrade(svn_fs_t *fs,
468           const char *path,
469           svn_fs_upgrade_notify_t notify_func,
470           void *notify_baton,
471           svn_cancel_func_t cancel_func,
472           void *cancel_baton,
473           svn_mutex__t *common_pool_lock,
474           apr_pool_t *scratch_pool,
475           apr_pool_t *common_pool)
476 {
477   SVN_ERR(x_open(fs, path, common_pool_lock, scratch_pool, common_pool));
478   return svn_fs_x__upgrade(fs, notify_func, notify_baton,
479                            cancel_func, cancel_baton, scratch_pool);
480 }
481 
482 static svn_error_t *
x_verify(svn_fs_t * fs,const char * path,svn_revnum_t start,svn_revnum_t end,svn_fs_progress_notify_func_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_mutex__t * common_pool_lock,apr_pool_t * scratch_pool,apr_pool_t * common_pool)483 x_verify(svn_fs_t *fs,
484          const char *path,
485          svn_revnum_t start,
486          svn_revnum_t end,
487          svn_fs_progress_notify_func_t notify_func,
488          void *notify_baton,
489          svn_cancel_func_t cancel_func,
490          void *cancel_baton,
491          svn_mutex__t *common_pool_lock,
492          apr_pool_t *scratch_pool,
493          apr_pool_t *common_pool)
494 {
495   SVN_ERR(x_open(fs, path, common_pool_lock, scratch_pool, common_pool));
496   return svn_fs_x__verify(fs, start, end, notify_func, notify_baton,
497                           cancel_func, cancel_baton, scratch_pool);
498 }
499 
500 static svn_error_t *
x_pack(svn_fs_t * fs,const char * path,svn_fs_pack_notify_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_mutex__t * common_pool_lock,apr_pool_t * scratch_pool,apr_pool_t * common_pool)501 x_pack(svn_fs_t *fs,
502        const char *path,
503        svn_fs_pack_notify_t notify_func,
504        void *notify_baton,
505        svn_cancel_func_t cancel_func,
506        void *cancel_baton,
507        svn_mutex__t *common_pool_lock,
508        apr_pool_t *scratch_pool,
509        apr_pool_t *common_pool)
510 {
511   SVN_ERR(x_open(fs, path, common_pool_lock, scratch_pool, common_pool));
512   return svn_fs_x__pack(fs, 0, notify_func, notify_baton,
513                         cancel_func, cancel_baton, scratch_pool);
514 }
515 
516 
517 
518 
519 /* This implements the fs_library_vtable_t.hotcopy() API.  Copy a
520    possibly live Subversion filesystem SRC_FS from SRC_PATH to a
521    DST_FS at DEST_PATH. If INCREMENTAL is TRUE, make an effort not to
522    re-copy data which already exists in DST_FS.
523    The CLEAN_LOGS argument is ignored and included for Subversion
524    1.0.x compatibility.  The NOTIFY_FUNC and NOTIFY_BATON arguments
525    are also currently ignored.
526    Perform all temporary allocations in SCRATCH_POOL. */
527 static svn_error_t *
x_hotcopy(svn_fs_t * src_fs,svn_fs_t * dst_fs,const char * src_path,const char * dst_path,svn_boolean_t clean_logs,svn_boolean_t incremental,svn_fs_hotcopy_notify_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_mutex__t * common_pool_lock,apr_pool_t * scratch_pool,apr_pool_t * common_pool)528 x_hotcopy(svn_fs_t *src_fs,
529           svn_fs_t *dst_fs,
530           const char *src_path,
531           const char *dst_path,
532           svn_boolean_t clean_logs,
533           svn_boolean_t incremental,
534           svn_fs_hotcopy_notify_t notify_func,
535           void *notify_baton,
536           svn_cancel_func_t cancel_func,
537           void *cancel_baton,
538           svn_mutex__t *common_pool_lock,
539           apr_pool_t *scratch_pool,
540           apr_pool_t *common_pool)
541 {
542   /* Open the source repo as usual. */
543   SVN_ERR(x_open(src_fs, src_path, common_pool_lock, scratch_pool,
544                  common_pool));
545   if (cancel_func)
546     SVN_ERR(cancel_func(cancel_baton));
547 
548   SVN_ERR(svn_fs__check_fs(dst_fs, FALSE));
549   SVN_ERR(initialize_fs_struct(dst_fs));
550 
551   /* In INCREMENTAL mode, svn_fs_x__hotcopy() will open DST_FS.
552      Otherwise, it's not an FS yet --- possibly just an empty dir --- so
553      can't be opened.
554    */
555   return svn_fs_x__hotcopy(src_fs, dst_fs, src_path, dst_path,
556                             incremental, notify_func, notify_baton,
557                             cancel_func, cancel_baton, common_pool_lock,
558                             scratch_pool, common_pool);
559 }
560 
561 
562 
563 /* This function is included for Subversion 1.0.x compatibility.  It
564    has no effect for fsx backed Subversion filesystems.  It conforms
565    to the fs_library_vtable_t.bdb_logfiles() API. */
566 static svn_error_t *
x_logfiles(apr_array_header_t ** logfiles,const char * path,svn_boolean_t only_unused,apr_pool_t * pool)567 x_logfiles(apr_array_header_t **logfiles,
568            const char *path,
569            svn_boolean_t only_unused,
570            apr_pool_t *pool)
571 {
572   /* A no-op for FSX. */
573   *logfiles = apr_array_make(pool, 0, sizeof(const char *));
574 
575   return SVN_NO_ERROR;
576 }
577 
578 
579 
580 
581 
582 /* Delete the filesystem located at path PATH.  Perform any temporary
583    allocations in SCRATCH_POOL. */
584 static svn_error_t *
x_delete_fs(const char * path,apr_pool_t * scratch_pool)585 x_delete_fs(const char *path,
586             apr_pool_t *scratch_pool)
587 {
588   /* Remove everything. */
589   return svn_error_trace(svn_io_remove_dir2(path, FALSE, NULL, NULL,
590                                             scratch_pool));
591 }
592 
593 static const svn_version_t *
x_version(void)594 x_version(void)
595 {
596   SVN_VERSION_BODY;
597 }
598 
599 static const char *
x_get_description(void)600 x_get_description(void)
601 {
602   return _("Module for working with an experimental (FSX) repository.");
603 }
604 
605 static svn_error_t *
x_set_svn_fs_open(svn_fs_t * fs,svn_error_t * (* svn_fs_open_)(svn_fs_t **,const char *,apr_hash_t *,apr_pool_t *,apr_pool_t *))606 x_set_svn_fs_open(svn_fs_t *fs,
607                   svn_error_t *(*svn_fs_open_)(svn_fs_t **,
608                                                const char *,
609                                                apr_hash_t *,
610                                                apr_pool_t *,
611                                                apr_pool_t *))
612 {
613   svn_fs_x__data_t *ffd = fs->fsap_data;
614   ffd->svn_fs_open_ = svn_fs_open_;
615   return SVN_NO_ERROR;
616 }
617 
618 static void *
x_info_dup(const void * fsx_info_void,apr_pool_t * result_pool)619 x_info_dup(const void *fsx_info_void,
620            apr_pool_t *result_pool)
621 {
622   /* All fields are either ints or static strings. */
623   const svn_fs_fsx_info_t *fsx_info = fsx_info_void;
624   return apr_pmemdup(result_pool, fsx_info, sizeof(*fsx_info));
625 }
626 
627 
628 /* Base FS library vtable, used by the FS loader library. */
629 
630 static fs_library_vtable_t library_vtable = {
631   x_version,
632   x_create,
633   x_open,
634   x_open_for_recovery,
635   x_upgrade,
636   x_verify,
637   x_delete_fs,
638   x_hotcopy,
639   x_get_description,
640   svn_fs_x__recover,
641   x_pack,
642   x_logfiles,
643   NULL /* parse_id */,
644   x_set_svn_fs_open,
645   x_info_dup,
646   NULL /* ioctl */
647 };
648 
649 svn_error_t *
svn_fs_x__init(const svn_version_t * loader_version,fs_library_vtable_t ** vtable,apr_pool_t * common_pool)650 svn_fs_x__init(const svn_version_t *loader_version,
651                fs_library_vtable_t **vtable,
652                apr_pool_t* common_pool)
653 {
654   static const svn_version_checklist_t checklist[] =
655     {
656       { "svn_subr",  svn_subr_version },
657       { "svn_delta", svn_delta_version },
658       { "svn_fs_util", svn_fs_util__version },
659       { NULL, NULL }
660     };
661 
662   /* Simplified version check to make sure we can safely use the
663      VTABLE parameter. The FS loader does a more exhaustive check. */
664   if (loader_version->major != SVN_VER_MAJOR)
665     return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
666                              _("Unsupported FS loader version (%d) for fsx"),
667                              loader_version->major);
668   SVN_ERR(svn_ver_check_list2(x_version(), checklist, svn_ver_equal));
669 
670   SVN_ERR(svn_fs_x__batch_fsync_init(common_pool));
671 
672   *vtable = &library_vtable;
673   return SVN_NO_ERROR;
674 }
675