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