]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/subversion/subversion/libsvn_fs/fs-loader.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / subversion / subversion / libsvn_fs / fs-loader.c
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 \f
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 \f
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 \f
343 /* --- Functions for operating on filesystems by pathname --- */
344
345 static apr_status_t uninit(void *data)
346 {
347   common_pool = NULL;
348   return APR_SUCCESS;
349 }
350
351 svn_error_t *
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
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 *
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 *
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 *
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
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 \f
669 /* --- Berkeley-specific functions --- */
670
671 svn_error_t *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 \f
750 /* --- Transaction functions --- */
751
752 svn_error_t *
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 *
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 *
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 *
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 *
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 *
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
832 svn_fs_txn_base_revision(svn_fs_txn_t *txn)
833 {
834   return txn->base_rev;
835 }
836
837 svn_error_t *
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 *
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 *
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 *
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 *
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 *
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 \f
879 /* --- Root functions --- */
880
881 svn_error_t *
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 *
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
901 svn_fs_close_root(svn_fs_root_t *root)
902 {
903   svn_pool_destroy(root->pool);
904 }
905
906 svn_fs_t *
907 svn_fs_root_fs(svn_fs_root_t *root)
908 {
909   return root->fs;
910 }
911
912 svn_boolean_t
913 svn_fs_is_txn_root(svn_fs_root_t *root)
914 {
915   return root->is_txn_root;
916 }
917
918 svn_boolean_t
919 svn_fs_is_revision_root(svn_fs_root_t *root)
920 {
921   return !root->is_txn_root;
922 }
923
924 const char *
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
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
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 \f
1497 /* --- History functions --- */
1498
1499 svn_error_t *
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 *
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 \f
1517 /* --- Node-ID functions --- */
1518
1519 svn_fs_id_t *
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 *
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
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
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 *
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 *
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 *
1601 svn_fs_version(void)
1602 {
1603   SVN_VERSION_BODY;
1604 }