xref: /trueos/contrib/subversion/subversion/libsvn_fs/fs-loader.c (revision 7cfbe47f2542407346168136f8e33b5015eda94f)
1 /*
2  * fs_loader.c:  Front-end to the various FS back ends
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23 
24 
25 #include <string.h>
26 #include <apr.h>
27 #include <apr_hash.h>
28 #include <apr_md5.h>
29 #include <apr_thread_mutex.h>
30 #include <apr_uuid.h>
31 #include <apr_strings.h>
32 
33 #include "svn_hash.h"
34 #include "svn_ctype.h"
35 #include "svn_types.h"
36 #include "svn_dso.h"
37 #include "svn_version.h"
38 #include "svn_fs.h"
39 #include "svn_path.h"
40 #include "svn_xml.h"
41 #include "svn_pools.h"
42 #include "svn_string.h"
43 #include "svn_private_config.h"
44 
45 #include "private/svn_fs_private.h"
46 #include "private/svn_fs_util.h"
47 #include "private/svn_utf_private.h"
48 #include "private/svn_mutex.h"
49 #include "private/svn_subr_private.h"
50 
51 #include "fs-loader.h"
52 
53 /* This is defined by configure on platforms which use configure, but
54    we need to define a fallback for Windows. */
55 #ifndef DEFAULT_FS_TYPE
56 #define DEFAULT_FS_TYPE "fsfs"
57 #endif
58 
59 #define FS_TYPE_FILENAME "fs-type"
60 
61 /* A pool common to all FS objects.  See the documentation on the
62    open/create functions in fs-loader.h and for svn_fs_initialize(). */
63 static apr_pool_t *common_pool;
64 svn_mutex__t *common_pool_lock;
65 
66 
67 /* --- Utility functions for the loader --- */
68 
69 struct fs_type_defn {
70   const char *fs_type;
71   const char *fsap_name;
72   fs_init_func_t initfunc;
73   struct fs_type_defn *next;
74 };
75 
76 static struct fs_type_defn base_defn =
77   {
78     SVN_FS_TYPE_BDB, "base",
79 #ifdef SVN_LIBSVN_FS_LINKS_FS_BASE
80     svn_fs_base__init,
81 #else
82     NULL,
83 #endif
84     NULL /* End of static list: this needs to be reset to NULL if the
85             common_pool used when setting it has been cleared. */
86   };
87 
88 static struct fs_type_defn fsfs_defn =
89   {
90     SVN_FS_TYPE_FSFS, "fs",
91 #ifdef SVN_LIBSVN_FS_LINKS_FS_FS
92     svn_fs_fs__init,
93 #else
94     NULL,
95 #endif
96     &base_defn
97   };
98 
99 static struct fs_type_defn *fs_modules = &fsfs_defn;
100 
101 
102 static svn_error_t *
load_module(fs_init_func_t * initfunc,const char * name,apr_pool_t * pool)103 load_module(fs_init_func_t *initfunc, const char *name, apr_pool_t *pool)
104 {
105   *initfunc = NULL;
106 
107 #if defined(SVN_USE_DSO) && APR_HAS_DSO
108   {
109     apr_dso_handle_t *dso;
110     apr_dso_handle_sym_t symbol;
111     const char *libname;
112     const char *funcname;
113     apr_status_t status;
114     const char *p;
115 
116     /* Demand a simple alphanumeric name so that the generated DSO
117        name is sensible. */
118     for (p = name; *p; ++p)
119       if (!svn_ctype_isalnum(*p))
120         return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
121                                  _("Invalid name for FS type '%s'"),
122                                  name);
123 
124     libname = apr_psprintf(pool, "libsvn_fs_%s-%d.so.%d",
125                            name, SVN_VER_MAJOR, SVN_SOVERSION);
126     funcname = apr_psprintf(pool, "svn_fs_%s__init", name);
127 
128     /* Find/load the specified library.  If we get an error, assume
129        the library doesn't exist.  The library will be unloaded when
130        pool is destroyed. */
131     SVN_ERR(svn_dso_load(&dso, libname));
132     if (! dso)
133       return SVN_NO_ERROR;
134 
135     /* find the initialization routine */
136     status = apr_dso_sym(&symbol, dso, funcname);
137     if (status)
138       return svn_error_wrap_apr(status, _("'%s' does not define '%s()'"),
139                                 libname, funcname);
140 
141     *initfunc = (fs_init_func_t) symbol;
142   }
143 #endif /* APR_HAS_DSO */
144 
145   return SVN_NO_ERROR;
146 }
147 
148 /* Fetch a library vtable by a pointer into the library definitions array. */
149 static svn_error_t *
get_library_vtable_direct(fs_library_vtable_t ** vtable,const struct fs_type_defn * fst,apr_pool_t * pool)150 get_library_vtable_direct(fs_library_vtable_t **vtable,
151                           const struct fs_type_defn *fst,
152                           apr_pool_t *pool)
153 {
154   fs_init_func_t initfunc = NULL;
155   const svn_version_t *my_version = svn_fs_version();
156   const svn_version_t *fs_version;
157 
158   initfunc = fst->initfunc;
159   if (! initfunc)
160     SVN_ERR(load_module(&initfunc, fst->fsap_name, pool));
161 
162   if (! initfunc)
163     return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
164                              _("Failed to load module for FS type '%s'"),
165                              fst->fs_type);
166 
167   {
168     /* Per our API compatibility rules, we cannot ensure that
169        svn_fs_initialize is called by the application.  If not, we
170        cannot create the common pool and lock in a thread-safe fashion,
171        nor can we clean up the common pool if libsvn_fs is dynamically
172        unloaded.  This function makes a best effort by creating the
173        common pool as a child of the global pool; the window of failure
174        due to thread collision is small. */
175     if (!common_pool)
176       SVN_ERR(svn_fs_initialize(NULL));
177 
178     /* Invoke the FS module's initfunc function with the common
179        pool protected by a lock. */
180     SVN_MUTEX__WITH_LOCK(common_pool_lock,
181                          initfunc(my_version, vtable, common_pool));
182   }
183   fs_version = (*vtable)->get_version();
184   if (!svn_ver_equal(my_version, fs_version))
185     return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
186                              _("Mismatched FS module version for '%s':"
187                                " found %d.%d.%d%s,"
188                                " expected %d.%d.%d%s"),
189                              fst->fs_type,
190                              my_version->major, my_version->minor,
191                              my_version->patch, my_version->tag,
192                              fs_version->major, fs_version->minor,
193                              fs_version->patch, fs_version->tag);
194   return SVN_NO_ERROR;
195 }
196 
197 #if defined(SVN_USE_DSO) && APR_HAS_DSO
198 /* Return *FST for the third party FS_TYPE */
199 static svn_error_t *
get_or_allocate_third(struct fs_type_defn ** fst,const char * fs_type)200 get_or_allocate_third(struct fs_type_defn **fst,
201                       const char *fs_type)
202 {
203   while (*fst)
204     {
205       if (strcmp(fs_type, (*fst)->fs_type) == 0)
206         return SVN_NO_ERROR;
207       fst = &(*fst)->next;
208     }
209 
210   *fst = apr_palloc(common_pool, sizeof(struct fs_type_defn));
211   (*fst)->fs_type = apr_pstrdup(common_pool, fs_type);
212   (*fst)->fsap_name = (*fst)->fs_type;
213   (*fst)->initfunc = NULL;
214   (*fst)->next = NULL;
215 
216   return SVN_NO_ERROR;
217 }
218 #endif
219 
220 /* Fetch a library vtable by FS type. */
221 static svn_error_t *
get_library_vtable(fs_library_vtable_t ** vtable,const char * fs_type,apr_pool_t * pool)222 get_library_vtable(fs_library_vtable_t **vtable, const char *fs_type,
223                    apr_pool_t *pool)
224 {
225   struct fs_type_defn **fst = &fs_modules;
226   svn_boolean_t known = FALSE;
227 
228   /* There are two FS module definitions known at compile time.  We
229      want to check these without any locking overhead even when
230      dynamic third party modules are enabled.  The third party modules
231      cannot be checked until the lock is held.  */
232   if (strcmp(fs_type, (*fst)->fs_type) == 0)
233     known = TRUE;
234   else
235     {
236       fst = &(*fst)->next;
237       if (strcmp(fs_type, (*fst)->fs_type) == 0)
238         known = TRUE;
239     }
240 
241 #if defined(SVN_USE_DSO) && APR_HAS_DSO
242   /* Third party FS modules that are unknown at compile time.
243 
244      A third party FS is identified by the file fs-type containing a
245      third party name, say "foo".  The loader will load the DSO with
246      the name "libsvn_fs_foo" and use the entry point with the name
247      "svn_fs_foo__init".
248 
249      Note: the BDB and FSFS modules don't follow this naming scheme
250      and this allows them to be used to test the third party loader.
251      Change the content of fs-type to "base" in a BDB filesystem or to
252      "fs" in an FSFS filesystem and they will be loaded as third party
253      modules. */
254   if (!known)
255     {
256       fst = &(*fst)->next;
257       if (!common_pool)  /* Best-effort init, see get_library_vtable_direct. */
258         SVN_ERR(svn_fs_initialize(NULL));
259       SVN_MUTEX__WITH_LOCK(common_pool_lock,
260                            get_or_allocate_third(fst, fs_type));
261       known = TRUE;
262     }
263 #endif
264   if (!known)
265     return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
266                              _("Unknown FS type '%s'"), fs_type);
267   return get_library_vtable_direct(vtable, *fst, pool);
268 }
269 
270 svn_error_t *
svn_fs_type(const char ** fs_type,const char * path,apr_pool_t * pool)271 svn_fs_type(const char **fs_type, const char *path, apr_pool_t *pool)
272 {
273   const char *filename;
274   char buf[128];
275   svn_error_t *err;
276   apr_file_t *file;
277   apr_size_t len;
278 
279   /* Read the fsap-name file to get the FSAP name, or assume the (old)
280      default.  For old repositories I suppose we could check some
281      other file, DB_CONFIG or strings say, but for now just check the
282      directory exists. */
283   filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool);
284   err = svn_io_file_open(&file, filename, APR_READ|APR_BUFFERED, 0, pool);
285   if (err && APR_STATUS_IS_ENOENT(err->apr_err))
286     {
287       svn_node_kind_t kind;
288       svn_error_t *err2 = svn_io_check_path(path, &kind, pool);
289       if (err2)
290         {
291           svn_error_clear(err2);
292           return err;
293         }
294       if (kind == svn_node_dir)
295         {
296           svn_error_clear(err);
297           *fs_type = SVN_FS_TYPE_BDB;
298           return SVN_NO_ERROR;
299         }
300       return err;
301     }
302   else if (err)
303     return err;
304 
305   len = sizeof(buf);
306   SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
307   SVN_ERR(svn_io_file_close(file, pool));
308   *fs_type = apr_pstrdup(pool, buf);
309 
310   return SVN_NO_ERROR;
311 }
312 
313 /* Fetch the library vtable for an existing FS. */
314 static svn_error_t *
fs_library_vtable(fs_library_vtable_t ** vtable,const char * path,apr_pool_t * pool)315 fs_library_vtable(fs_library_vtable_t **vtable, const char *path,
316                   apr_pool_t *pool)
317 {
318   const char *fs_type;
319 
320   SVN_ERR(svn_fs_type(&fs_type, path, pool));
321 
322   /* Fetch the library vtable by name, now that we've chosen one. */
323   return svn_error_trace(get_library_vtable(vtable, fs_type, pool));
324 }
325 
326 static svn_error_t *
write_fs_type(const char * path,const char * fs_type,apr_pool_t * pool)327 write_fs_type(const char *path, const char *fs_type, apr_pool_t *pool)
328 {
329   const char *filename;
330   apr_file_t *file;
331 
332   filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool);
333   SVN_ERR(svn_io_file_open(&file, filename,
334                            APR_WRITE|APR_CREATE|APR_TRUNCATE|APR_BUFFERED,
335                            APR_OS_DEFAULT, pool));
336   SVN_ERR(svn_io_file_write_full(file, fs_type, strlen(fs_type), NULL,
337                                  pool));
338   SVN_ERR(svn_io_file_write_full(file, "\n", 1, NULL, pool));
339   return svn_error_trace(svn_io_file_close(file, pool));
340 }
341 
342 
343 /* --- Functions for operating on filesystems by pathname --- */
344 
uninit(void * data)345 static apr_status_t uninit(void *data)
346 {
347   common_pool = NULL;
348   return APR_SUCCESS;
349 }
350 
351 svn_error_t *
svn_fs_initialize(apr_pool_t * pool)352 svn_fs_initialize(apr_pool_t *pool)
353 {
354   /* Protect against multiple calls. */
355   if (common_pool)
356     return SVN_NO_ERROR;
357 
358   common_pool = svn_pool_create(pool);
359   base_defn.next = NULL;
360   SVN_ERR(svn_mutex__init(&common_pool_lock, TRUE, common_pool));
361 
362   /* ### This won't work if POOL is NULL and libsvn_fs is loaded as a DSO
363      ### (via libsvn_ra_local say) since the global common_pool will live
364      ### longer than the DSO, which gets unloaded when the pool used to
365      ### load it is cleared, and so when the handler runs it will refer to
366      ### a function that no longer exists.  libsvn_ra_local attempts to
367      ### work around this by explicitly calling svn_fs_initialize. */
368   apr_pool_cleanup_register(common_pool, NULL, uninit, apr_pool_cleanup_null);
369   return SVN_NO_ERROR;
370 }
371 
372 /* A default warning handling function.  */
373 static void
default_warning_func(void * baton,svn_error_t * err)374 default_warning_func(void *baton, svn_error_t *err)
375 {
376   /* The one unforgiveable sin is to fail silently.  Dumping to stderr
377      or /dev/tty is not acceptable default behavior for server
378      processes, since those may both be equivalent to /dev/null.  */
379   SVN_ERR_MALFUNCTION_NO_RETURN();
380 }
381 
382 svn_error_t *
svn_fs__path_valid(const char * path,apr_pool_t * pool)383 svn_fs__path_valid(const char *path, apr_pool_t *pool)
384 {
385   /* UTF-8 encoded string without NULs. */
386   if (! svn_utf__cstring_is_valid(path))
387     {
388       return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
389                                _("Path '%s' is not in UTF-8"), path);
390     }
391 
392   /* No "." or ".." elements. */
393   if (svn_path_is_backpath_present(path)
394       || svn_path_is_dotpath_present(path))
395     {
396       return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
397                                _("Path '%s' contains '.' or '..' element"),
398                                path);
399     }
400 
401   /* That's good enough. */
402   return SVN_NO_ERROR;
403 }
404 
405 /* Allocate svn_fs_t structure. */
406 static svn_fs_t *
fs_new(apr_hash_t * fs_config,apr_pool_t * pool)407 fs_new(apr_hash_t *fs_config, apr_pool_t *pool)
408 {
409   svn_fs_t *fs = apr_palloc(pool, sizeof(*fs));
410   fs->pool = pool;
411   fs->path = NULL;
412   fs->warning = default_warning_func;
413   fs->warning_baton = NULL;
414   fs->config = fs_config;
415   fs->access_ctx = NULL;
416   fs->vtable = NULL;
417   fs->fsap_data = NULL;
418   fs->uuid = NULL;
419   return fs;
420 }
421 
422 svn_fs_t *
svn_fs_new(apr_hash_t * fs_config,apr_pool_t * pool)423 svn_fs_new(apr_hash_t *fs_config, apr_pool_t *pool)
424 {
425   return fs_new(fs_config, pool);
426 }
427 
428 void
svn_fs_set_warning_func(svn_fs_t * fs,svn_fs_warning_callback_t warning,void * warning_baton)429 svn_fs_set_warning_func(svn_fs_t *fs, svn_fs_warning_callback_t warning,
430                         void *warning_baton)
431 {
432   fs->warning = warning;
433   fs->warning_baton = warning_baton;
434 }
435 
436 svn_error_t *
svn_fs_create(svn_fs_t ** fs_p,const char * path,apr_hash_t * fs_config,apr_pool_t * pool)437 svn_fs_create(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config,
438               apr_pool_t *pool)
439 {
440   fs_library_vtable_t *vtable;
441 
442   const char *fs_type = svn_hash__get_cstring(fs_config,
443                                               SVN_FS_CONFIG_FS_TYPE,
444                                               DEFAULT_FS_TYPE);
445   SVN_ERR(get_library_vtable(&vtable, fs_type, pool));
446 
447   /* Create the FS directory and write out the fsap-name file. */
448   SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, pool));
449   SVN_ERR(write_fs_type(path, fs_type, pool));
450 
451   /* Perform the actual creation. */
452   *fs_p = fs_new(fs_config, pool);
453 
454   SVN_MUTEX__WITH_LOCK(common_pool_lock,
455                        vtable->create(*fs_p, path, pool, common_pool));
456   SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open));
457 
458   return SVN_NO_ERROR;
459 }
460 
461 svn_error_t *
svn_fs_open(svn_fs_t ** fs_p,const char * path,apr_hash_t * fs_config,apr_pool_t * pool)462 svn_fs_open(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config,
463             apr_pool_t *pool)
464 {
465   fs_library_vtable_t *vtable;
466 
467   SVN_ERR(fs_library_vtable(&vtable, path, pool));
468   *fs_p = fs_new(fs_config, pool);
469   SVN_MUTEX__WITH_LOCK(common_pool_lock,
470                        vtable->open_fs(*fs_p, path, pool, common_pool));
471   SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open));
472 
473   return SVN_NO_ERROR;
474 }
475 
476 svn_error_t *
svn_fs_upgrade(const char * path,apr_pool_t * pool)477 svn_fs_upgrade(const char *path, apr_pool_t *pool)
478 {
479   fs_library_vtable_t *vtable;
480   svn_fs_t *fs;
481 
482   SVN_ERR(fs_library_vtable(&vtable, path, pool));
483   fs = fs_new(NULL, pool);
484 
485   SVN_MUTEX__WITH_LOCK(common_pool_lock,
486                        vtable->upgrade_fs(fs, path, pool, common_pool));
487   return SVN_NO_ERROR;
488 }
489 
490 svn_error_t *
svn_fs_verify(const char * path,apr_hash_t * fs_config,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,apr_pool_t * pool)491 svn_fs_verify(const char *path,
492               apr_hash_t *fs_config,
493               svn_revnum_t start,
494               svn_revnum_t end,
495               svn_fs_progress_notify_func_t notify_func,
496               void *notify_baton,
497               svn_cancel_func_t cancel_func,
498               void *cancel_baton,
499               apr_pool_t *pool)
500 {
501   fs_library_vtable_t *vtable;
502   svn_fs_t *fs;
503 
504   SVN_ERR(fs_library_vtable(&vtable, path, pool));
505   fs = fs_new(fs_config, pool);
506 
507   SVN_MUTEX__WITH_LOCK(common_pool_lock,
508                        vtable->verify_fs(fs, path, start, end,
509                                          notify_func, notify_baton,
510                                          cancel_func, cancel_baton,
511                                          pool, common_pool));
512   return SVN_NO_ERROR;
513 }
514 
515 const char *
svn_fs_path(svn_fs_t * fs,apr_pool_t * pool)516 svn_fs_path(svn_fs_t *fs, apr_pool_t *pool)
517 {
518   return apr_pstrdup(pool, fs->path);
519 }
520 
521 apr_hash_t *
svn_fs_config(svn_fs_t * fs,apr_pool_t * pool)522 svn_fs_config(svn_fs_t *fs, apr_pool_t *pool)
523 {
524   if (fs->config)
525     return apr_hash_copy(pool, fs->config);
526 
527   return NULL;
528 }
529 
530 svn_error_t *
svn_fs_delete_fs(const char * path,apr_pool_t * pool)531 svn_fs_delete_fs(const char *path, apr_pool_t *pool)
532 {
533   fs_library_vtable_t *vtable;
534 
535   SVN_ERR(fs_library_vtable(&vtable, path, pool));
536   return svn_error_trace(vtable->delete_fs(path, pool));
537 }
538 
539 svn_error_t *
svn_fs_hotcopy2(const char * src_path,const char * dst_path,svn_boolean_t clean,svn_boolean_t incremental,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)540 svn_fs_hotcopy2(const char *src_path, const char *dst_path,
541                 svn_boolean_t clean, svn_boolean_t incremental,
542                 svn_cancel_func_t cancel_func, void *cancel_baton,
543                 apr_pool_t *scratch_pool)
544 {
545   fs_library_vtable_t *vtable;
546   const char *src_fs_type;
547   svn_fs_t *src_fs;
548   svn_fs_t *dst_fs;
549   const char *dst_fs_type;
550   svn_node_kind_t dst_kind;
551 
552   if (strcmp(src_path, dst_path) == 0)
553     return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
554                              _("Hotcopy source and destination are equal"));
555 
556   SVN_ERR(svn_fs_type(&src_fs_type, src_path, scratch_pool));
557   SVN_ERR(get_library_vtable(&vtable, src_fs_type, scratch_pool));
558   src_fs = fs_new(NULL, scratch_pool);
559   dst_fs = fs_new(NULL, scratch_pool);
560 
561   SVN_ERR(svn_io_check_path(dst_path, &dst_kind, scratch_pool));
562   if (dst_kind == svn_node_file)
563     return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
564                              _("'%s' already exists and is a file"),
565                              svn_dirent_local_style(dst_path,
566                                                     scratch_pool));
567   if (dst_kind == svn_node_unknown)
568     return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
569                              _("'%s' already exists and has an unknown "
570                                "node kind"),
571                              svn_dirent_local_style(dst_path,
572                                                     scratch_pool));
573   if (dst_kind == svn_node_dir)
574     {
575       svn_node_kind_t type_file_kind;
576 
577       SVN_ERR(svn_io_check_path(svn_dirent_join(dst_path,
578                                                 FS_TYPE_FILENAME,
579                                                 scratch_pool),
580                                 &type_file_kind, scratch_pool));
581       if (type_file_kind != svn_node_none)
582         {
583           SVN_ERR(svn_fs_type(&dst_fs_type, dst_path, scratch_pool));
584           if (strcmp(src_fs_type, dst_fs_type) != 0)
585             return svn_error_createf(
586                      SVN_ERR_ILLEGAL_TARGET, NULL,
587                      _("The filesystem type of the hotcopy source "
588                        "('%s') does not match the filesystem "
589                        "type of the hotcopy destination ('%s')"),
590                      src_fs_type, dst_fs_type);
591         }
592     }
593 
594   SVN_ERR(vtable->hotcopy(src_fs, dst_fs, src_path, dst_path, clean,
595                           incremental, cancel_func, cancel_baton,
596                           scratch_pool));
597   return svn_error_trace(write_fs_type(dst_path, src_fs_type, scratch_pool));
598 }
599 
600 svn_error_t *
svn_fs_hotcopy(const char * src_path,const char * dest_path,svn_boolean_t clean,apr_pool_t * pool)601 svn_fs_hotcopy(const char *src_path, const char *dest_path,
602                svn_boolean_t clean, apr_pool_t *pool)
603 {
604   return svn_error_trace(svn_fs_hotcopy2(src_path, dest_path, clean,
605                                          FALSE, NULL, NULL, pool));
606 }
607 
608 svn_error_t *
svn_fs_pack(const char * path,svn_fs_pack_notify_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * pool)609 svn_fs_pack(const char *path,
610             svn_fs_pack_notify_t notify_func,
611             void *notify_baton,
612             svn_cancel_func_t cancel_func,
613             void *cancel_baton,
614             apr_pool_t *pool)
615 {
616   fs_library_vtable_t *vtable;
617   svn_fs_t *fs;
618 
619   SVN_ERR(fs_library_vtable(&vtable, path, pool));
620   fs = fs_new(NULL, pool);
621 
622   SVN_MUTEX__WITH_LOCK(common_pool_lock,
623                        vtable->pack_fs(fs, path, notify_func, notify_baton,
624                                        cancel_func, cancel_baton, pool,
625                                        common_pool));
626   return SVN_NO_ERROR;
627 }
628 
629 svn_error_t *
svn_fs_recover(const char * path,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * pool)630 svn_fs_recover(const char *path,
631                svn_cancel_func_t cancel_func, void *cancel_baton,
632                apr_pool_t *pool)
633 {
634   fs_library_vtable_t *vtable;
635   svn_fs_t *fs;
636 
637   SVN_ERR(fs_library_vtable(&vtable, path, pool));
638   fs = fs_new(NULL, pool);
639 
640   SVN_MUTEX__WITH_LOCK(common_pool_lock,
641                        vtable->open_fs_for_recovery(fs, path, pool,
642                                                     common_pool));
643   return svn_error_trace(vtable->recover(fs, cancel_func, cancel_baton,
644                                          pool));
645 }
646 
647 svn_error_t *
svn_fs_verify_root(svn_fs_root_t * root,apr_pool_t * scratch_pool)648 svn_fs_verify_root(svn_fs_root_t *root,
649                    apr_pool_t *scratch_pool)
650 {
651   svn_fs_t *fs = root->fs;
652   SVN_ERR(fs->vtable->verify_root(root, scratch_pool));
653 
654   return SVN_NO_ERROR;
655 }
656 
657 svn_error_t *
svn_fs_freeze(svn_fs_t * fs,svn_fs_freeze_func_t freeze_func,void * freeze_baton,apr_pool_t * pool)658 svn_fs_freeze(svn_fs_t *fs,
659               svn_fs_freeze_func_t freeze_func,
660               void *freeze_baton,
661               apr_pool_t *pool)
662 {
663   SVN_ERR(fs->vtable->freeze(fs, freeze_func, freeze_baton, pool));
664 
665   return SVN_NO_ERROR;
666 }
667 
668 
669 /* --- Berkeley-specific functions --- */
670 
671 svn_error_t *
svn_fs_create_berkeley(svn_fs_t * fs,const char * path)672 svn_fs_create_berkeley(svn_fs_t *fs, const char *path)
673 {
674   fs_library_vtable_t *vtable;
675 
676   SVN_ERR(get_library_vtable(&vtable, SVN_FS_TYPE_BDB, fs->pool));
677 
678   /* Create the FS directory and write out the fsap-name file. */
679   SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, fs->pool));
680   SVN_ERR(write_fs_type(path, SVN_FS_TYPE_BDB, fs->pool));
681 
682   /* Perform the actual creation. */
683   SVN_MUTEX__WITH_LOCK(common_pool_lock,
684                        vtable->create(fs, path, fs->pool, common_pool));
685   SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open));
686 
687   return SVN_NO_ERROR;
688 }
689 
690 svn_error_t *
svn_fs_open_berkeley(svn_fs_t * fs,const char * path)691 svn_fs_open_berkeley(svn_fs_t *fs, const char *path)
692 {
693   fs_library_vtable_t *vtable;
694 
695   SVN_ERR(fs_library_vtable(&vtable, path, fs->pool));
696   SVN_MUTEX__WITH_LOCK(common_pool_lock,
697                        vtable->open_fs(fs, path, fs->pool, common_pool));
698   SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open));
699 
700   return SVN_NO_ERROR;
701 }
702 
703 const char *
svn_fs_berkeley_path(svn_fs_t * fs,apr_pool_t * pool)704 svn_fs_berkeley_path(svn_fs_t *fs, apr_pool_t *pool)
705 {
706   return svn_fs_path(fs, pool);
707 }
708 
709 svn_error_t *
svn_fs_delete_berkeley(const char * path,apr_pool_t * pool)710 svn_fs_delete_berkeley(const char *path, apr_pool_t *pool)
711 {
712   return svn_error_trace(svn_fs_delete_fs(path, pool));
713 }
714 
715 svn_error_t *
svn_fs_hotcopy_berkeley(const char * src_path,const char * dest_path,svn_boolean_t clean_logs,apr_pool_t * pool)716 svn_fs_hotcopy_berkeley(const char *src_path, const char *dest_path,
717                         svn_boolean_t clean_logs, apr_pool_t *pool)
718 {
719   return svn_error_trace(svn_fs_hotcopy2(src_path, dest_path, clean_logs,
720                                          FALSE, NULL, NULL, pool));
721 }
722 
723 svn_error_t *
svn_fs_berkeley_recover(const char * path,apr_pool_t * pool)724 svn_fs_berkeley_recover(const char *path, apr_pool_t *pool)
725 {
726   return svn_error_trace(svn_fs_recover(path, NULL, NULL, pool));
727 }
728 
729 svn_error_t *
svn_fs_set_berkeley_errcall(svn_fs_t * fs,void (* handler)(const char * errpfx,char * msg))730 svn_fs_set_berkeley_errcall(svn_fs_t *fs,
731                             void (*handler)(const char *errpfx, char *msg))
732 {
733   return svn_error_trace(fs->vtable->bdb_set_errcall(fs, handler));
734 }
735 
736 svn_error_t *
svn_fs_berkeley_logfiles(apr_array_header_t ** logfiles,const char * path,svn_boolean_t only_unused,apr_pool_t * pool)737 svn_fs_berkeley_logfiles(apr_array_header_t **logfiles,
738                          const char *path,
739                          svn_boolean_t only_unused,
740                          apr_pool_t *pool)
741 {
742   fs_library_vtable_t *vtable;
743 
744   SVN_ERR(fs_library_vtable(&vtable, path, pool));
745   return svn_error_trace(vtable->bdb_logfiles(logfiles, path, only_unused,
746                                               pool));
747 }
748 
749 
750 /* --- Transaction functions --- */
751 
752 svn_error_t *
svn_fs_begin_txn2(svn_fs_txn_t ** txn_p,svn_fs_t * fs,svn_revnum_t rev,apr_uint32_t flags,apr_pool_t * pool)753 svn_fs_begin_txn2(svn_fs_txn_t **txn_p, svn_fs_t *fs, svn_revnum_t rev,
754                   apr_uint32_t flags, apr_pool_t *pool)
755 {
756   return svn_error_trace(fs->vtable->begin_txn(txn_p, fs, rev, flags, pool));
757 }
758 
759 
760 svn_error_t *
svn_fs_begin_txn(svn_fs_txn_t ** txn_p,svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)761 svn_fs_begin_txn(svn_fs_txn_t **txn_p, svn_fs_t *fs, svn_revnum_t rev,
762                  apr_pool_t *pool)
763 {
764   return svn_error_trace(svn_fs_begin_txn2(txn_p, fs, rev, 0, pool));
765 }
766 
767 
768 svn_error_t *
svn_fs_commit_txn(const char ** conflict_p,svn_revnum_t * new_rev,svn_fs_txn_t * txn,apr_pool_t * pool)769 svn_fs_commit_txn(const char **conflict_p, svn_revnum_t *new_rev,
770                   svn_fs_txn_t *txn, apr_pool_t *pool)
771 {
772   svn_error_t *err;
773 
774   *new_rev = SVN_INVALID_REVNUM;
775   if (conflict_p)
776     *conflict_p = NULL;
777 
778   err = txn->vtable->commit(conflict_p, new_rev, txn, pool);
779 
780 #ifdef SVN_DEBUG
781   /* Check postconditions. */
782   if (conflict_p)
783     {
784       SVN_ERR_ASSERT_E(! (SVN_IS_VALID_REVNUM(*new_rev) && *conflict_p != NULL),
785                        err);
786       SVN_ERR_ASSERT_E((*conflict_p != NULL)
787                        == (err && err->apr_err == SVN_ERR_FS_CONFLICT),
788                        err);
789     }
790 #endif
791 
792   SVN_ERR(err);
793 
794 #ifdef PACK_AFTER_EVERY_COMMIT
795   {
796     svn_fs_t *fs = txn->fs;
797     const char *fs_path = svn_fs_path(fs, pool);
798     err = svn_fs_pack(fs_path, NULL, NULL, NULL, NULL, pool);
799     if (err && err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)
800       /* Pre-1.6 filesystem. */
801       svn_error_clear(err);
802     else if (err)
803       /* Real error. */
804       return svn_error_trace(err);
805   }
806 #endif
807 
808   return SVN_NO_ERROR;
809 }
810 
811 
812 svn_error_t *
svn_fs_abort_txn(svn_fs_txn_t * txn,apr_pool_t * pool)813 svn_fs_abort_txn(svn_fs_txn_t *txn, apr_pool_t *pool)
814 {
815   return svn_error_trace(txn->vtable->abort(txn, pool));
816 }
817 
818 svn_error_t *
svn_fs_purge_txn(svn_fs_t * fs,const char * txn_id,apr_pool_t * pool)819 svn_fs_purge_txn(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
820 {
821   return svn_error_trace(fs->vtable->purge_txn(fs, txn_id, pool));
822 }
823 
824 svn_error_t *
svn_fs_txn_name(const char ** name_p,svn_fs_txn_t * txn,apr_pool_t * pool)825 svn_fs_txn_name(const char **name_p, svn_fs_txn_t *txn, apr_pool_t *pool)
826 {
827   *name_p = apr_pstrdup(pool, txn->id);
828   return SVN_NO_ERROR;
829 }
830 
831 svn_revnum_t
svn_fs_txn_base_revision(svn_fs_txn_t * txn)832 svn_fs_txn_base_revision(svn_fs_txn_t *txn)
833 {
834   return txn->base_rev;
835 }
836 
837 svn_error_t *
svn_fs_open_txn(svn_fs_txn_t ** txn,svn_fs_t * fs,const char * name,apr_pool_t * pool)838 svn_fs_open_txn(svn_fs_txn_t **txn, svn_fs_t *fs, const char *name,
839                 apr_pool_t *pool)
840 {
841   return svn_error_trace(fs->vtable->open_txn(txn, fs, name, pool));
842 }
843 
844 svn_error_t *
svn_fs_list_transactions(apr_array_header_t ** names_p,svn_fs_t * fs,apr_pool_t * pool)845 svn_fs_list_transactions(apr_array_header_t **names_p, svn_fs_t *fs,
846                          apr_pool_t *pool)
847 {
848   return svn_error_trace(fs->vtable->list_transactions(names_p, fs, pool));
849 }
850 
851 svn_error_t *
svn_fs_txn_prop(svn_string_t ** value_p,svn_fs_txn_t * txn,const char * propname,apr_pool_t * pool)852 svn_fs_txn_prop(svn_string_t **value_p, svn_fs_txn_t *txn,
853                 const char *propname, apr_pool_t *pool)
854 {
855   return svn_error_trace(txn->vtable->get_prop(value_p, txn, propname, pool));
856 }
857 
858 svn_error_t *
svn_fs_txn_proplist(apr_hash_t ** table_p,svn_fs_txn_t * txn,apr_pool_t * pool)859 svn_fs_txn_proplist(apr_hash_t **table_p, svn_fs_txn_t *txn, apr_pool_t *pool)
860 {
861   return svn_error_trace(txn->vtable->get_proplist(table_p, txn, pool));
862 }
863 
864 svn_error_t *
svn_fs_change_txn_prop(svn_fs_txn_t * txn,const char * name,const svn_string_t * value,apr_pool_t * pool)865 svn_fs_change_txn_prop(svn_fs_txn_t *txn, const char *name,
866                        const svn_string_t *value, apr_pool_t *pool)
867 {
868   return svn_error_trace(txn->vtable->change_prop(txn, name, value, pool));
869 }
870 
871 svn_error_t *
svn_fs_change_txn_props(svn_fs_txn_t * txn,const apr_array_header_t * props,apr_pool_t * pool)872 svn_fs_change_txn_props(svn_fs_txn_t *txn, const apr_array_header_t *props,
873                         apr_pool_t *pool)
874 {
875   return svn_error_trace(txn->vtable->change_props(txn, props, pool));
876 }
877 
878 
879 /* --- Root functions --- */
880 
881 svn_error_t *
svn_fs_revision_root(svn_fs_root_t ** root_p,svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)882 svn_fs_revision_root(svn_fs_root_t **root_p, svn_fs_t *fs, svn_revnum_t rev,
883                      apr_pool_t *pool)
884 {
885   /* We create a subpool for each root object to allow us to implement
886      svn_fs_close_root.  */
887   apr_pool_t *subpool = svn_pool_create(pool);
888   return svn_error_trace(fs->vtable->revision_root(root_p, fs, rev, subpool));
889 }
890 
891 svn_error_t *
svn_fs_txn_root(svn_fs_root_t ** root_p,svn_fs_txn_t * txn,apr_pool_t * pool)892 svn_fs_txn_root(svn_fs_root_t **root_p, svn_fs_txn_t *txn, apr_pool_t *pool)
893 {
894   /* We create a subpool for each root object to allow us to implement
895      svn_fs_close_root.  */
896   apr_pool_t *subpool = svn_pool_create(pool);
897   return svn_error_trace(txn->vtable->root(root_p, txn, subpool));
898 }
899 
900 void
svn_fs_close_root(svn_fs_root_t * root)901 svn_fs_close_root(svn_fs_root_t *root)
902 {
903   svn_pool_destroy(root->pool);
904 }
905 
906 svn_fs_t *
svn_fs_root_fs(svn_fs_root_t * root)907 svn_fs_root_fs(svn_fs_root_t *root)
908 {
909   return root->fs;
910 }
911 
912 svn_boolean_t
svn_fs_is_txn_root(svn_fs_root_t * root)913 svn_fs_is_txn_root(svn_fs_root_t *root)
914 {
915   return root->is_txn_root;
916 }
917 
918 svn_boolean_t
svn_fs_is_revision_root(svn_fs_root_t * root)919 svn_fs_is_revision_root(svn_fs_root_t *root)
920 {
921   return !root->is_txn_root;
922 }
923 
924 const char *
svn_fs_txn_root_name(svn_fs_root_t * root,apr_pool_t * pool)925 svn_fs_txn_root_name(svn_fs_root_t *root, apr_pool_t *pool)
926 {
927   return root->is_txn_root ? apr_pstrdup(pool, root->txn) : NULL;
928 }
929 
930 svn_revnum_t
svn_fs_txn_root_base_revision(svn_fs_root_t * root)931 svn_fs_txn_root_base_revision(svn_fs_root_t *root)
932 {
933   return root->is_txn_root ? root->rev : SVN_INVALID_REVNUM;
934 }
935 
936 svn_revnum_t
svn_fs_revision_root_revision(svn_fs_root_t * root)937 svn_fs_revision_root_revision(svn_fs_root_t *root)
938 {
939   return root->is_txn_root ? SVN_INVALID_REVNUM : root->rev;
940 }
941 
942 svn_error_t *
svn_fs_paths_changed2(apr_hash_t ** changed_paths_p,svn_fs_root_t * root,apr_pool_t * pool)943 svn_fs_paths_changed2(apr_hash_t **changed_paths_p, svn_fs_root_t *root,
944                       apr_pool_t *pool)
945 {
946   return root->vtable->paths_changed(changed_paths_p, root, pool);
947 }
948 
949 svn_error_t *
svn_fs_paths_changed(apr_hash_t ** changed_paths_p,svn_fs_root_t * root,apr_pool_t * pool)950 svn_fs_paths_changed(apr_hash_t **changed_paths_p, svn_fs_root_t *root,
951                      apr_pool_t *pool)
952 {
953   apr_hash_t *changed_paths_new_structs;
954   apr_hash_index_t *hi;
955 
956   SVN_ERR(svn_fs_paths_changed2(&changed_paths_new_structs, root, pool));
957   *changed_paths_p = apr_hash_make(pool);
958   for (hi = apr_hash_first(pool, changed_paths_new_structs);
959        hi;
960        hi = apr_hash_next(hi))
961     {
962       const void *vkey;
963       apr_ssize_t klen;
964       void *vval;
965       svn_fs_path_change2_t *val;
966       svn_fs_path_change_t *change;
967       apr_hash_this(hi, &vkey, &klen, &vval);
968       val = vval;
969       change = apr_palloc(pool, sizeof(*change));
970       change->node_rev_id = val->node_rev_id;
971       change->change_kind = val->change_kind;
972       change->text_mod = val->text_mod;
973       change->prop_mod = val->prop_mod;
974       apr_hash_set(*changed_paths_p, vkey, klen, change);
975     }
976   return SVN_NO_ERROR;
977 }
978 
979 svn_error_t *
svn_fs_check_path(svn_node_kind_t * kind_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)980 svn_fs_check_path(svn_node_kind_t *kind_p, svn_fs_root_t *root,
981                   const char *path, apr_pool_t *pool)
982 {
983   return svn_error_trace(root->vtable->check_path(kind_p, root, path, pool));
984 }
985 
986 svn_error_t *
svn_fs_node_history(svn_fs_history_t ** history_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)987 svn_fs_node_history(svn_fs_history_t **history_p, svn_fs_root_t *root,
988                     const char *path, apr_pool_t *pool)
989 {
990   return svn_error_trace(root->vtable->node_history(history_p, root, path,
991                                                     pool));
992 }
993 
994 svn_error_t *
svn_fs_is_dir(svn_boolean_t * is_dir,svn_fs_root_t * root,const char * path,apr_pool_t * pool)995 svn_fs_is_dir(svn_boolean_t *is_dir, svn_fs_root_t *root, const char *path,
996               apr_pool_t *pool)
997 {
998   svn_node_kind_t kind;
999 
1000   SVN_ERR(root->vtable->check_path(&kind, root, path, pool));
1001   *is_dir = (kind == svn_node_dir);
1002   return SVN_NO_ERROR;
1003 }
1004 
1005 svn_error_t *
svn_fs_is_file(svn_boolean_t * is_file,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1006 svn_fs_is_file(svn_boolean_t *is_file, svn_fs_root_t *root, const char *path,
1007                apr_pool_t *pool)
1008 {
1009   svn_node_kind_t kind;
1010 
1011   SVN_ERR(root->vtable->check_path(&kind, root, path, pool));
1012   *is_file = (kind == svn_node_file);
1013   return SVN_NO_ERROR;
1014 }
1015 
1016 svn_error_t *
svn_fs_node_id(const svn_fs_id_t ** id_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1017 svn_fs_node_id(const svn_fs_id_t **id_p, svn_fs_root_t *root,
1018                const char *path, apr_pool_t *pool)
1019 {
1020   return svn_error_trace(root->vtable->node_id(id_p, root, path, pool));
1021 }
1022 
1023 svn_error_t *
svn_fs_node_created_rev(svn_revnum_t * revision,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1024 svn_fs_node_created_rev(svn_revnum_t *revision, svn_fs_root_t *root,
1025                         const char *path, apr_pool_t *pool)
1026 {
1027   return svn_error_trace(root->vtable->node_created_rev(revision, root, path,
1028                                                         pool));
1029 }
1030 
1031 svn_error_t *
svn_fs_node_origin_rev(svn_revnum_t * revision,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1032 svn_fs_node_origin_rev(svn_revnum_t *revision, svn_fs_root_t *root,
1033                        const char *path, apr_pool_t *pool)
1034 {
1035   return svn_error_trace(root->vtable->node_origin_rev(revision, root, path,
1036                                                        pool));
1037 }
1038 
1039 svn_error_t *
svn_fs_node_created_path(const char ** created_path,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1040 svn_fs_node_created_path(const char **created_path, svn_fs_root_t *root,
1041                          const char *path, apr_pool_t *pool)
1042 {
1043   return svn_error_trace(root->vtable->node_created_path(created_path, root,
1044                                                          path, pool));
1045 }
1046 
1047 svn_error_t *
svn_fs_node_prop(svn_string_t ** value_p,svn_fs_root_t * root,const char * path,const char * propname,apr_pool_t * pool)1048 svn_fs_node_prop(svn_string_t **value_p, svn_fs_root_t *root,
1049                  const char *path, const char *propname, apr_pool_t *pool)
1050 {
1051   return svn_error_trace(root->vtable->node_prop(value_p, root, path,
1052                                                  propname, pool));
1053 }
1054 
1055 svn_error_t *
svn_fs_node_proplist(apr_hash_t ** table_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1056 svn_fs_node_proplist(apr_hash_t **table_p, svn_fs_root_t *root,
1057                      const char *path, apr_pool_t *pool)
1058 {
1059   return svn_error_trace(root->vtable->node_proplist(table_p, root, path,
1060                                                      pool));
1061 }
1062 
1063 svn_error_t *
svn_fs_change_node_prop(svn_fs_root_t * root,const char * path,const char * name,const svn_string_t * value,apr_pool_t * pool)1064 svn_fs_change_node_prop(svn_fs_root_t *root, const char *path,
1065                         const char *name, const svn_string_t *value,
1066                         apr_pool_t *pool)
1067 {
1068   return svn_error_trace(root->vtable->change_node_prop(root, path, name,
1069                                                         value, pool));
1070 }
1071 
1072 svn_error_t *
svn_fs_props_changed(svn_boolean_t * changed_p,svn_fs_root_t * root1,const char * path1,svn_fs_root_t * root2,const char * path2,apr_pool_t * pool)1073 svn_fs_props_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1074                      const char *path1, svn_fs_root_t *root2,
1075                      const char *path2, apr_pool_t *pool)
1076 {
1077   return svn_error_trace(root1->vtable->props_changed(changed_p,
1078                                                       root1, path1,
1079                                                       root2, path2,
1080                                                       pool));
1081 }
1082 
1083 svn_error_t *
svn_fs_copied_from(svn_revnum_t * rev_p,const char ** path_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1084 svn_fs_copied_from(svn_revnum_t *rev_p, const char **path_p,
1085                    svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1086 {
1087   return svn_error_trace(root->vtable->copied_from(rev_p, path_p, root, path,
1088                                                    pool));
1089 }
1090 
1091 svn_error_t *
svn_fs_closest_copy(svn_fs_root_t ** root_p,const char ** path_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1092 svn_fs_closest_copy(svn_fs_root_t **root_p, const char **path_p,
1093                     svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1094 {
1095   return svn_error_trace(root->vtable->closest_copy(root_p, path_p,
1096                                                     root, path, pool));
1097 }
1098 
1099 svn_error_t *
svn_fs_get_mergeinfo2(svn_mergeinfo_catalog_t * catalog,svn_fs_root_t * root,const apr_array_header_t * paths,svn_mergeinfo_inheritance_t inherit,svn_boolean_t include_descendants,svn_boolean_t adjust_inherited_mergeinfo,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1100 svn_fs_get_mergeinfo2(svn_mergeinfo_catalog_t *catalog,
1101                       svn_fs_root_t *root,
1102                       const apr_array_header_t *paths,
1103                       svn_mergeinfo_inheritance_t inherit,
1104                       svn_boolean_t include_descendants,
1105                       svn_boolean_t adjust_inherited_mergeinfo,
1106                       apr_pool_t *result_pool,
1107                       apr_pool_t *scratch_pool)
1108 {
1109   return svn_error_trace(root->vtable->get_mergeinfo(
1110     catalog, root, paths, inherit, include_descendants,
1111     adjust_inherited_mergeinfo, result_pool, scratch_pool));
1112 }
1113 
1114 svn_error_t *
svn_fs_get_mergeinfo(svn_mergeinfo_catalog_t * catalog,svn_fs_root_t * root,const apr_array_header_t * paths,svn_mergeinfo_inheritance_t inherit,svn_boolean_t include_descendants,apr_pool_t * pool)1115 svn_fs_get_mergeinfo(svn_mergeinfo_catalog_t *catalog,
1116                      svn_fs_root_t *root,
1117                      const apr_array_header_t *paths,
1118                      svn_mergeinfo_inheritance_t inherit,
1119                      svn_boolean_t include_descendants,
1120                      apr_pool_t *pool)
1121 {
1122   return svn_error_trace(root->vtable->get_mergeinfo(catalog, root, paths,
1123                                                      inherit,
1124                                                      include_descendants,
1125                                                      TRUE, pool, pool));
1126 }
1127 
1128 svn_error_t *
svn_fs_merge(const char ** conflict_p,svn_fs_root_t * source_root,const char * source_path,svn_fs_root_t * target_root,const char * target_path,svn_fs_root_t * ancestor_root,const char * ancestor_path,apr_pool_t * pool)1129 svn_fs_merge(const char **conflict_p, svn_fs_root_t *source_root,
1130              const char *source_path, svn_fs_root_t *target_root,
1131              const char *target_path, svn_fs_root_t *ancestor_root,
1132              const char *ancestor_path, apr_pool_t *pool)
1133 {
1134   return svn_error_trace(target_root->vtable->merge(conflict_p,
1135                                                     source_root, source_path,
1136                                                     target_root, target_path,
1137                                                     ancestor_root,
1138                                                     ancestor_path, pool));
1139 }
1140 
1141 svn_error_t *
svn_fs_dir_entries(apr_hash_t ** entries_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1142 svn_fs_dir_entries(apr_hash_t **entries_p, svn_fs_root_t *root,
1143                    const char *path, apr_pool_t *pool)
1144 {
1145   return svn_error_trace(root->vtable->dir_entries(entries_p, root, path,
1146                                                    pool));
1147 }
1148 
1149 svn_error_t *
svn_fs_make_dir(svn_fs_root_t * root,const char * path,apr_pool_t * pool)1150 svn_fs_make_dir(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1151 {
1152   SVN_ERR(svn_fs__path_valid(path, pool));
1153   return svn_error_trace(root->vtable->make_dir(root, path, pool));
1154 }
1155 
1156 svn_error_t *
svn_fs_delete(svn_fs_root_t * root,const char * path,apr_pool_t * pool)1157 svn_fs_delete(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1158 {
1159   return svn_error_trace(root->vtable->delete_node(root, path, pool));
1160 }
1161 
1162 svn_error_t *
svn_fs_copy(svn_fs_root_t * from_root,const char * from_path,svn_fs_root_t * to_root,const char * to_path,apr_pool_t * pool)1163 svn_fs_copy(svn_fs_root_t *from_root, const char *from_path,
1164             svn_fs_root_t *to_root, const char *to_path, apr_pool_t *pool)
1165 {
1166   SVN_ERR(svn_fs__path_valid(to_path, pool));
1167   return svn_error_trace(to_root->vtable->copy(from_root, from_path,
1168                                                to_root, to_path, pool));
1169 }
1170 
1171 svn_error_t *
svn_fs_revision_link(svn_fs_root_t * from_root,svn_fs_root_t * to_root,const char * path,apr_pool_t * pool)1172 svn_fs_revision_link(svn_fs_root_t *from_root, svn_fs_root_t *to_root,
1173                      const char *path, apr_pool_t *pool)
1174 {
1175   return svn_error_trace(to_root->vtable->revision_link(from_root, to_root,
1176                                                         path, pool));
1177 }
1178 
1179 svn_error_t *
svn_fs_file_length(svn_filesize_t * length_p,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1180 svn_fs_file_length(svn_filesize_t *length_p, svn_fs_root_t *root,
1181                    const char *path, apr_pool_t *pool)
1182 {
1183   return svn_error_trace(root->vtable->file_length(length_p, root, path,
1184                                                    pool));
1185 }
1186 
1187 svn_error_t *
svn_fs_file_checksum(svn_checksum_t ** checksum,svn_checksum_kind_t kind,svn_fs_root_t * root,const char * path,svn_boolean_t force,apr_pool_t * pool)1188 svn_fs_file_checksum(svn_checksum_t **checksum,
1189                      svn_checksum_kind_t kind,
1190                      svn_fs_root_t *root,
1191                      const char *path,
1192                      svn_boolean_t force,
1193                      apr_pool_t *pool)
1194 {
1195   SVN_ERR(root->vtable->file_checksum(checksum, kind, root, path, pool));
1196 
1197   if (force && (*checksum == NULL || (*checksum)->kind != kind))
1198     {
1199       svn_stream_t *contents, *checksum_contents;
1200 
1201       SVN_ERR(svn_fs_file_contents(&contents, root, path, pool));
1202       checksum_contents = svn_stream_checksummed2(contents, checksum, NULL,
1203                                                   kind, TRUE, pool);
1204 
1205       /* This will force a read of any remaining data (which is all of it in
1206          this case) and dump the checksum into checksum->digest. */
1207       SVN_ERR(svn_stream_close(checksum_contents));
1208     }
1209 
1210   return SVN_NO_ERROR;
1211 }
1212 
1213 svn_error_t *
svn_fs_file_md5_checksum(unsigned char digest[],svn_fs_root_t * root,const char * path,apr_pool_t * pool)1214 svn_fs_file_md5_checksum(unsigned char digest[],
1215                          svn_fs_root_t *root,
1216                          const char *path,
1217                          apr_pool_t *pool)
1218 {
1219   svn_checksum_t *md5sum;
1220 
1221   SVN_ERR(svn_fs_file_checksum(&md5sum, svn_checksum_md5, root, path, TRUE,
1222                                pool));
1223   memcpy(digest, md5sum->digest, APR_MD5_DIGESTSIZE);
1224 
1225   return SVN_NO_ERROR;
1226 }
1227 
1228 svn_error_t *
svn_fs_file_contents(svn_stream_t ** contents,svn_fs_root_t * root,const char * path,apr_pool_t * pool)1229 svn_fs_file_contents(svn_stream_t **contents, svn_fs_root_t *root,
1230                      const char *path, apr_pool_t *pool)
1231 {
1232   return svn_error_trace(root->vtable->file_contents(contents, root, path,
1233                                                      pool));
1234 }
1235 
1236 svn_error_t *
svn_fs_try_process_file_contents(svn_boolean_t * success,svn_fs_root_t * root,const char * path,svn_fs_process_contents_func_t processor,void * baton,apr_pool_t * pool)1237 svn_fs_try_process_file_contents(svn_boolean_t *success,
1238                                  svn_fs_root_t *root,
1239                                  const char *path,
1240                                  svn_fs_process_contents_func_t processor,
1241                                  void* baton,
1242                                  apr_pool_t *pool)
1243 {
1244   /* if the FS doesn't implement this function, report a "failed" attempt */
1245   if (root->vtable->try_process_file_contents == NULL)
1246     {
1247       *success = FALSE;
1248       return SVN_NO_ERROR;
1249     }
1250 
1251   return svn_error_trace(root->vtable->try_process_file_contents(
1252                          success,
1253                          root, path,
1254                          processor, baton, pool));
1255 }
1256 
1257 svn_error_t *
svn_fs_make_file(svn_fs_root_t * root,const char * path,apr_pool_t * pool)1258 svn_fs_make_file(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1259 {
1260   SVN_ERR(svn_fs__path_valid(path, pool));
1261   return svn_error_trace(root->vtable->make_file(root, path, pool));
1262 }
1263 
1264 svn_error_t *
svn_fs_apply_textdelta(svn_txdelta_window_handler_t * contents_p,void ** contents_baton_p,svn_fs_root_t * root,const char * path,const char * base_checksum,const char * result_checksum,apr_pool_t * pool)1265 svn_fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
1266                        void **contents_baton_p, svn_fs_root_t *root,
1267                        const char *path, const char *base_checksum,
1268                        const char *result_checksum, apr_pool_t *pool)
1269 {
1270   svn_checksum_t *base, *result;
1271 
1272   /* TODO: If we ever rev this API, we should make the supplied checksums
1273      svn_checksum_t structs. */
1274   SVN_ERR(svn_checksum_parse_hex(&base, svn_checksum_md5, base_checksum,
1275                                  pool));
1276   SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum,
1277                                  pool));
1278 
1279   return svn_error_trace(root->vtable->apply_textdelta(contents_p,
1280                                                        contents_baton_p,
1281                                                        root,
1282                                                        path,
1283                                                        base,
1284                                                        result,
1285                                                        pool));
1286 }
1287 
1288 svn_error_t *
svn_fs_apply_text(svn_stream_t ** contents_p,svn_fs_root_t * root,const char * path,const char * result_checksum,apr_pool_t * pool)1289 svn_fs_apply_text(svn_stream_t **contents_p, svn_fs_root_t *root,
1290                   const char *path, const char *result_checksum,
1291                   apr_pool_t *pool)
1292 {
1293   svn_checksum_t *result;
1294 
1295   /* TODO: If we ever rev this API, we should make the supplied checksum an
1296      svn_checksum_t struct. */
1297   SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum,
1298                                  pool));
1299 
1300   return svn_error_trace(root->vtable->apply_text(contents_p, root, path,
1301                                                   result, pool));
1302 }
1303 
1304 svn_error_t *
svn_fs_contents_changed(svn_boolean_t * changed_p,svn_fs_root_t * root1,const char * path1,svn_fs_root_t * root2,const char * path2,apr_pool_t * pool)1305 svn_fs_contents_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1306                         const char *path1, svn_fs_root_t *root2,
1307                         const char *path2, apr_pool_t *pool)
1308 {
1309   return svn_error_trace(root1->vtable->contents_changed(changed_p,
1310                                                          root1, path1,
1311                                                          root2, path2,
1312                                                          pool));
1313 }
1314 
1315 svn_error_t *
svn_fs_youngest_rev(svn_revnum_t * youngest_p,svn_fs_t * fs,apr_pool_t * pool)1316 svn_fs_youngest_rev(svn_revnum_t *youngest_p, svn_fs_t *fs, apr_pool_t *pool)
1317 {
1318   return svn_error_trace(fs->vtable->youngest_rev(youngest_p, fs, pool));
1319 }
1320 
1321 svn_error_t *
svn_fs_deltify_revision(svn_fs_t * fs,svn_revnum_t revision,apr_pool_t * pool)1322 svn_fs_deltify_revision(svn_fs_t *fs, svn_revnum_t revision, apr_pool_t *pool)
1323 {
1324   return svn_error_trace(fs->vtable->deltify(fs, revision, pool));
1325 }
1326 
1327 svn_error_t *
svn_fs_revision_prop(svn_string_t ** value_p,svn_fs_t * fs,svn_revnum_t rev,const char * propname,apr_pool_t * pool)1328 svn_fs_revision_prop(svn_string_t **value_p, svn_fs_t *fs, svn_revnum_t rev,
1329                      const char *propname, apr_pool_t *pool)
1330 {
1331   return svn_error_trace(fs->vtable->revision_prop(value_p, fs, rev,
1332                                                    propname, pool));
1333 }
1334 
1335 svn_error_t *
svn_fs_revision_proplist(apr_hash_t ** table_p,svn_fs_t * fs,svn_revnum_t rev,apr_pool_t * pool)1336 svn_fs_revision_proplist(apr_hash_t **table_p, svn_fs_t *fs, svn_revnum_t rev,
1337                          apr_pool_t *pool)
1338 {
1339   return svn_error_trace(fs->vtable->revision_proplist(table_p, fs, rev,
1340                                                        pool));
1341 }
1342 
1343 svn_error_t *
svn_fs_change_rev_prop2(svn_fs_t * fs,svn_revnum_t rev,const char * name,const svn_string_t * const * old_value_p,const svn_string_t * value,apr_pool_t * pool)1344 svn_fs_change_rev_prop2(svn_fs_t *fs, svn_revnum_t rev, const char *name,
1345                         const svn_string_t *const *old_value_p,
1346                         const svn_string_t *value, apr_pool_t *pool)
1347 {
1348   return svn_error_trace(fs->vtable->change_rev_prop(fs, rev, name,
1349                                                      old_value_p,
1350                                                      value, pool));
1351 }
1352 
1353 svn_error_t *
svn_fs_change_rev_prop(svn_fs_t * fs,svn_revnum_t rev,const char * name,const svn_string_t * value,apr_pool_t * pool)1354 svn_fs_change_rev_prop(svn_fs_t *fs, svn_revnum_t rev, const char *name,
1355                        const svn_string_t *value, apr_pool_t *pool)
1356 {
1357   return svn_error_trace(
1358            svn_fs_change_rev_prop2(fs, rev, name, NULL, value, pool));
1359 }
1360 
1361 svn_error_t *
svn_fs_get_file_delta_stream(svn_txdelta_stream_t ** stream_p,svn_fs_root_t * source_root,const char * source_path,svn_fs_root_t * target_root,const char * target_path,apr_pool_t * pool)1362 svn_fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
1363                              svn_fs_root_t *source_root,
1364                              const char *source_path,
1365                              svn_fs_root_t *target_root,
1366                              const char *target_path, apr_pool_t *pool)
1367 {
1368   return svn_error_trace(target_root->vtable->get_file_delta_stream(
1369                            stream_p,
1370                            source_root, source_path,
1371                            target_root, target_path, pool));
1372 }
1373 
1374 svn_error_t *
svn_fs_get_uuid(svn_fs_t * fs,const char ** uuid,apr_pool_t * pool)1375 svn_fs_get_uuid(svn_fs_t *fs, const char **uuid, apr_pool_t *pool)
1376 {
1377   /* If you change this, consider changing svn_fs__identifier(). */
1378   *uuid = apr_pstrdup(pool, fs->uuid);
1379   return SVN_NO_ERROR;
1380 }
1381 
1382 svn_error_t *
svn_fs_set_uuid(svn_fs_t * fs,const char * uuid,apr_pool_t * pool)1383 svn_fs_set_uuid(svn_fs_t *fs, const char *uuid, apr_pool_t *pool)
1384 {
1385   if (! uuid)
1386     {
1387       uuid = svn_uuid_generate(pool);
1388     }
1389   else
1390     {
1391       apr_uuid_t parsed_uuid;
1392       apr_status_t apr_err = apr_uuid_parse(&parsed_uuid, uuid);
1393       if (apr_err)
1394         return svn_error_createf(SVN_ERR_BAD_UUID, NULL,
1395                                  _("Malformed UUID '%s'"), uuid);
1396     }
1397   return svn_error_trace(fs->vtable->set_uuid(fs, uuid, pool));
1398 }
1399 
1400 svn_error_t *
svn_fs_lock(svn_lock_t ** lock,svn_fs_t * fs,const char * path,const char * token,const char * comment,svn_boolean_t is_dav_comment,apr_time_t expiration_date,svn_revnum_t current_rev,svn_boolean_t steal_lock,apr_pool_t * pool)1401 svn_fs_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path,
1402             const char *token, const char *comment,
1403             svn_boolean_t is_dav_comment, apr_time_t expiration_date,
1404             svn_revnum_t current_rev, svn_boolean_t steal_lock,
1405             apr_pool_t *pool)
1406 {
1407   /* Enforce that the comment be xml-escapable. */
1408   if (comment)
1409     {
1410       if (! svn_xml_is_xml_safe(comment, strlen(comment)))
1411         return svn_error_create
1412           (SVN_ERR_XML_UNESCAPABLE_DATA, NULL,
1413            _("Lock comment contains illegal characters"));
1414     }
1415 
1416   /* Enforce that the token be an XML-safe URI. */
1417   if (token)
1418     {
1419       const char *c;
1420 
1421       if (strncmp(token, "opaquelocktoken:", 16))
1422         return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1423                                  _("Lock token URI '%s' has bad scheme; "
1424                                    "expected '%s'"),
1425                                  token, "opaquelocktoken");
1426 
1427       for (c = token; *c; c++)
1428         if (! svn_ctype_isascii(*c))
1429           return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1430                                    _("Lock token '%s' is not ASCII "
1431                                      "at byte %u"),
1432                                    token, (unsigned)(c - token));
1433 
1434       /* strlen(token) == c - token. */
1435       if (! svn_xml_is_xml_safe(token, c - token))
1436         return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1437                                  _("Lock token URI '%s' is not XML-safe"),
1438                                  token);
1439     }
1440 
1441   if (expiration_date < 0)
1442         return svn_error_create
1443           (SVN_ERR_INCORRECT_PARAMS, NULL,
1444            _("Negative expiration date passed to svn_fs_lock"));
1445 
1446   return svn_error_trace(fs->vtable->lock(lock, fs, path, token, comment,
1447                                           is_dav_comment, expiration_date,
1448                                           current_rev, steal_lock, pool));
1449 }
1450 
1451 svn_error_t *
svn_fs_generate_lock_token(const char ** token,svn_fs_t * fs,apr_pool_t * pool)1452 svn_fs_generate_lock_token(const char **token, svn_fs_t *fs, apr_pool_t *pool)
1453 {
1454   return svn_error_trace(fs->vtable->generate_lock_token(token, fs, pool));
1455 }
1456 
1457 svn_error_t *
svn_fs_unlock(svn_fs_t * fs,const char * path,const char * token,svn_boolean_t break_lock,apr_pool_t * pool)1458 svn_fs_unlock(svn_fs_t *fs, const char *path, const char *token,
1459               svn_boolean_t break_lock, apr_pool_t *pool)
1460 {
1461   return svn_error_trace(fs->vtable->unlock(fs, path, token, break_lock,
1462                                             pool));
1463 }
1464 
1465 svn_error_t *
svn_fs_get_lock(svn_lock_t ** lock,svn_fs_t * fs,const char * path,apr_pool_t * pool)1466 svn_fs_get_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path,
1467                 apr_pool_t *pool)
1468 {
1469   return svn_error_trace(fs->vtable->get_lock(lock, fs, path, pool));
1470 }
1471 
1472 svn_error_t *
svn_fs_get_locks2(svn_fs_t * fs,const char * path,svn_depth_t depth,svn_fs_get_locks_callback_t get_locks_func,void * get_locks_baton,apr_pool_t * pool)1473 svn_fs_get_locks2(svn_fs_t *fs, const char *path, svn_depth_t depth,
1474                   svn_fs_get_locks_callback_t get_locks_func,
1475                   void *get_locks_baton, apr_pool_t *pool)
1476 {
1477   SVN_ERR_ASSERT((depth == svn_depth_empty) ||
1478                  (depth == svn_depth_files) ||
1479                  (depth == svn_depth_immediates) ||
1480                  (depth == svn_depth_infinity));
1481   return svn_error_trace(fs->vtable->get_locks(fs, path, depth,
1482                                                get_locks_func,
1483                                                get_locks_baton, pool));
1484 }
1485 
1486 svn_error_t *
svn_fs_get_locks(svn_fs_t * fs,const char * path,svn_fs_get_locks_callback_t get_locks_func,void * get_locks_baton,apr_pool_t * pool)1487 svn_fs_get_locks(svn_fs_t *fs, const char *path,
1488                  svn_fs_get_locks_callback_t get_locks_func,
1489                  void *get_locks_baton, apr_pool_t *pool)
1490 {
1491   return svn_error_trace(svn_fs_get_locks2(fs, path, svn_depth_infinity,
1492                                            get_locks_func, get_locks_baton,
1493                                            pool));
1494 }
1495 
1496 
1497 /* --- History functions --- */
1498 
1499 svn_error_t *
svn_fs_history_prev(svn_fs_history_t ** prev_history_p,svn_fs_history_t * history,svn_boolean_t cross_copies,apr_pool_t * pool)1500 svn_fs_history_prev(svn_fs_history_t **prev_history_p,
1501                     svn_fs_history_t *history, svn_boolean_t cross_copies,
1502                     apr_pool_t *pool)
1503 {
1504   return svn_error_trace(history->vtable->prev(prev_history_p, history,
1505                                                cross_copies, pool));
1506 }
1507 
1508 svn_error_t *
svn_fs_history_location(const char ** path,svn_revnum_t * revision,svn_fs_history_t * history,apr_pool_t * pool)1509 svn_fs_history_location(const char **path, svn_revnum_t *revision,
1510                         svn_fs_history_t *history, apr_pool_t *pool)
1511 {
1512   return svn_error_trace(history->vtable->location(path, revision, history,
1513                                                    pool));
1514 }
1515 
1516 
1517 /* --- Node-ID functions --- */
1518 
1519 svn_fs_id_t *
svn_fs_parse_id(const char * data,apr_size_t len,apr_pool_t * pool)1520 svn_fs_parse_id(const char *data, apr_size_t len, apr_pool_t *pool)
1521 {
1522   fs_library_vtable_t *vtable;
1523   svn_error_t *err;
1524 
1525   err = get_library_vtable(&vtable, SVN_FS_TYPE_BDB, pool);
1526   if (err)
1527     {
1528       svn_error_clear(err);
1529       return NULL;
1530     }
1531   return vtable->parse_id(data, len, pool);
1532 }
1533 
1534 svn_string_t *
svn_fs_unparse_id(const svn_fs_id_t * id,apr_pool_t * pool)1535 svn_fs_unparse_id(const svn_fs_id_t *id, apr_pool_t *pool)
1536 {
1537   return id->vtable->unparse(id, pool);
1538 }
1539 
1540 svn_boolean_t
svn_fs_check_related(const svn_fs_id_t * a,const svn_fs_id_t * b)1541 svn_fs_check_related(const svn_fs_id_t *a, const svn_fs_id_t *b)
1542 {
1543   return (a->vtable->compare(a, b) != -1);
1544 }
1545 
1546 int
svn_fs_compare_ids(const svn_fs_id_t * a,const svn_fs_id_t * b)1547 svn_fs_compare_ids(const svn_fs_id_t *a, const svn_fs_id_t *b)
1548 {
1549   return a->vtable->compare(a, b);
1550 }
1551 
1552 svn_error_t *
svn_fs_print_modules(svn_stringbuf_t * output,apr_pool_t * pool)1553 svn_fs_print_modules(svn_stringbuf_t *output,
1554                      apr_pool_t *pool)
1555 {
1556   const struct fs_type_defn *defn = fs_modules;
1557   fs_library_vtable_t *vtable;
1558   apr_pool_t *iterpool = svn_pool_create(pool);
1559 
1560   while (defn)
1561     {
1562       char *line;
1563       svn_error_t *err;
1564 
1565       svn_pool_clear(iterpool);
1566 
1567       err = get_library_vtable_direct(&vtable, defn, iterpool);
1568       if (err)
1569         {
1570           if (err->apr_err == SVN_ERR_FS_UNKNOWN_FS_TYPE)
1571             {
1572               svn_error_clear(err);
1573               defn = defn->next;
1574               continue;
1575             }
1576           else
1577             return err;
1578         }
1579 
1580       line = apr_psprintf(iterpool, "* fs_%s : %s\n",
1581                           defn->fsap_name, vtable->get_description());
1582       svn_stringbuf_appendcstr(output, line);
1583       defn = defn->next;
1584     }
1585 
1586   svn_pool_destroy(iterpool);
1587 
1588   return SVN_NO_ERROR;
1589 }
1590 
1591 svn_fs_path_change2_t *
svn_fs_path_change2_create(const svn_fs_id_t * node_rev_id,svn_fs_path_change_kind_t change_kind,apr_pool_t * pool)1592 svn_fs_path_change2_create(const svn_fs_id_t *node_rev_id,
1593                            svn_fs_path_change_kind_t change_kind,
1594                            apr_pool_t *pool)
1595 {
1596   return svn_fs__path_change_create_internal(node_rev_id, change_kind, pool);
1597 }
1598 
1599 /* Return the library version number. */
1600 const svn_version_t *
svn_fs_version(void)1601 svn_fs_version(void)
1602 {
1603   SVN_VERSION_BODY;
1604 }
1605