]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/libsvn_fs/fs-loader.c
Update Subversion and dependencies to 1.14.0 LTS.
[FreeBSD/FreeBSD.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_atomic.h>
28 #include <apr_hash.h>
29 #include <apr_uuid.h>
30 #include <apr_strings.h>
31
32 #include "svn_private_config.h"
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_sorts.h"
44
45 #include "private/svn_atomic.h"
46 #include "private/svn_fs_private.h"
47 #include "private/svn_fs_util.h"
48 #include "private/svn_fspath.h"
49 #include "private/svn_utf_private.h"
50 #include "private/svn_mutex.h"
51 #include "private/svn_subr_private.h"
52
53 #include "fs-loader.h"
54
55 /* This is defined by configure on platforms which use configure, but
56    we need to define a fallback for Windows. */
57 #ifndef DEFAULT_FS_TYPE
58 #define DEFAULT_FS_TYPE "fsfs"
59 #endif
60
61 #define FS_TYPE_FILENAME "fs-type"
62
63 /* If a FS backend does not implement the PATHS_CHANGED vtable function,
64    it will get emulated.  However, if this macro is defined to non-null
65    then the API will always be emulated when feasible, i.e. the calls
66    get "re-directed" to the new API implementation. */
67 #ifndef SVN_FS_EMULATE_PATHS_CHANGED
68 #define SVN_FS_EMULATE_PATHS_CHANGED FALSE
69 #endif
70
71 /* If a FS backend does not implement the REPORT_CHANGES vtable function,
72    it will get emulated.  However, if this macro is defined to non-null
73    then the API will always be emulated when feasible, i.e. the calls
74    get "re-directed" to the old API implementation. */
75 #ifndef SVN_FS_EMULATE_REPORT_CHANGES
76 #define SVN_FS_EMULATE_REPORT_CHANGES FALSE
77 #endif
78
79 /* A pool common to all FS objects.  See the documentation on the
80    open/create functions in fs-loader.h and for svn_fs_initialize(). */
81 static apr_pool_t *common_pool = NULL;
82 static svn_mutex__t *common_pool_lock = NULL;
83 static svn_atomic_t common_pool_initialized = FALSE;
84
85 \f
86 /* --- Utility functions for the loader --- */
87
88 struct fs_type_defn {
89   const char *fs_type;
90   const char *fsap_name;
91   fs_init_func_t initfunc;
92   void * volatile vtable; /* fs_library_vtable_t */
93   struct fs_type_defn *next;
94 };
95
96 static struct fs_type_defn base_defn =
97   {
98     SVN_FS_TYPE_BDB, "base",
99 #ifdef SVN_LIBSVN_FS_LINKS_FS_BASE
100     svn_fs_base__init,
101 #else
102     NULL,
103 #endif
104     NULL,
105     NULL /* End of static list: this needs to be reset to NULL if the
106             common_pool used when setting it has been cleared. */
107   };
108
109 static struct fs_type_defn fsx_defn =
110   {
111     SVN_FS_TYPE_FSX, "x",
112 #ifdef SVN_LIBSVN_FS_LINKS_FS_X
113     svn_fs_x__init,
114 #else
115     NULL,
116 #endif
117     NULL,
118     &base_defn
119   };
120
121 static struct fs_type_defn fsfs_defn =
122   {
123     SVN_FS_TYPE_FSFS, "fs",
124 #ifdef SVN_LIBSVN_FS_LINKS_FS_FS
125     svn_fs_fs__init,
126 #else
127     NULL,
128 #endif
129     NULL,
130     &fsx_defn
131   };
132
133 static struct fs_type_defn *fs_modules = &fsfs_defn;
134
135
136 static svn_error_t *
137 load_module(fs_init_func_t *initfunc, const char *name, apr_pool_t *pool)
138 {
139   *initfunc = NULL;
140
141 #if defined(SVN_USE_DSO) && APR_HAS_DSO
142   {
143     apr_dso_handle_t *dso;
144     apr_dso_handle_sym_t symbol;
145     const char *libname;
146     const char *funcname;
147     apr_status_t status;
148     const char *p;
149
150     /* Demand a simple alphanumeric name so that the generated DSO
151        name is sensible. */
152     for (p = name; *p; ++p)
153       if (!svn_ctype_isalnum(*p))
154         return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
155                                  _("Invalid name for FS type '%s'"),
156                                  name);
157
158     libname = apr_psprintf(pool, "libsvn_fs_%s-" SVN_DSO_SUFFIX_FMT,
159                            name, SVN_VER_MAJOR, SVN_SOVERSION);
160     funcname = apr_psprintf(pool, "svn_fs_%s__init", name);
161
162     /* Find/load the specified library.  If we get an error, assume
163        the library doesn't exist.  The library will be unloaded when
164        pool is destroyed. */
165     SVN_ERR(svn_dso_load(&dso, libname));
166     if (! dso)
167       return SVN_NO_ERROR;
168
169     /* find the initialization routine */
170     status = apr_dso_sym(&symbol, dso, funcname);
171     if (status)
172       return svn_error_wrap_apr(status, _("'%s' does not define '%s()'"),
173                                 libname, funcname);
174
175     *initfunc = (fs_init_func_t) symbol;
176   }
177 #endif /* APR_HAS_DSO */
178
179   return SVN_NO_ERROR;
180 }
181
182 /* Fetch a library vtable by a pointer into the library definitions array. */
183 static svn_error_t *
184 get_library_vtable_direct(fs_library_vtable_t **vtable,
185                           struct fs_type_defn *fst,
186                           apr_pool_t *pool)
187 {
188   fs_init_func_t initfunc = NULL;
189   const svn_version_t *my_version = svn_fs_version();
190   const svn_version_t *fs_version;
191
192   /* most times, we get lucky */
193   *vtable = svn_atomic_casptr(&fst->vtable, NULL, NULL);
194   if (*vtable)
195     return SVN_NO_ERROR;
196
197   /* o.k. the first access needs to actually load the module, find the
198      vtable and check for version compatibility. */
199   initfunc = fst->initfunc;
200   if (! initfunc)
201     SVN_ERR(load_module(&initfunc, fst->fsap_name, pool));
202
203   if (! initfunc)
204     return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
205                              _("Failed to load module for FS type '%s'"),
206                              fst->fs_type);
207
208   {
209     /* Per our API compatibility rules, we cannot ensure that
210        svn_fs_initialize is called by the application.  If not, we
211        cannot create the common pool and lock in a thread-safe fashion,
212        nor can we clean up the common pool if libsvn_fs is dynamically
213        unloaded.  This function makes a best effort by creating the
214        common pool as a child of the global pool; the window of failure
215        due to thread collision is small. */
216     SVN_ERR(svn_fs_initialize(NULL));
217
218     /* Invoke the FS module's initfunc function with the common
219        pool protected by a lock. */
220     SVN_MUTEX__WITH_LOCK(common_pool_lock,
221                          initfunc(my_version, vtable, common_pool));
222   }
223   fs_version = (*vtable)->get_version();
224   if (!svn_ver_equal(my_version, fs_version))
225     return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
226                              _("Mismatched FS module version for '%s':"
227                                " found %d.%d.%d%s,"
228                                " expected %d.%d.%d%s"),
229                              fst->fs_type,
230                              my_version->major, my_version->minor,
231                              my_version->patch, my_version->tag,
232                              fs_version->major, fs_version->minor,
233                              fs_version->patch, fs_version->tag);
234
235   /* the vtable will not change.  Remember it */
236   svn_atomic_casptr(&fst->vtable, *vtable, NULL);
237
238   return SVN_NO_ERROR;
239 }
240
241 #if defined(SVN_USE_DSO) && APR_HAS_DSO
242 /* Return *FST for the third party FS_TYPE */
243 static svn_error_t *
244 get_or_allocate_third(struct fs_type_defn **fst,
245                       const char *fs_type)
246 {
247   while (*fst)
248     {
249       if (strcmp(fs_type, (*fst)->fs_type) == 0)
250         return SVN_NO_ERROR;
251       fst = &(*fst)->next;
252     }
253
254   *fst = apr_palloc(common_pool, sizeof(struct fs_type_defn));
255   (*fst)->fs_type = apr_pstrdup(common_pool, fs_type);
256   (*fst)->fsap_name = (*fst)->fs_type;
257   (*fst)->initfunc = NULL;
258   (*fst)->vtable = NULL;
259   (*fst)->next = NULL;
260
261   return SVN_NO_ERROR;
262 }
263 #endif
264
265 /* Fetch a library *VTABLE by FS_TYPE.
266    Use POOL for temporary allocations. */
267 static svn_error_t *
268 get_library_vtable(fs_library_vtable_t **vtable, const char *fs_type,
269                    apr_pool_t *pool)
270 {
271   struct fs_type_defn **fst;
272   svn_boolean_t known = FALSE;
273
274   /* There are three FS module definitions known at compile time.  We
275      want to check these without any locking overhead even when
276      dynamic third party modules are enabled.  The third party modules
277      cannot be checked until the lock is held.  */
278   for (fst = &fs_modules; *fst; fst = &(*fst)->next)
279     {
280       if (strcmp(fs_type, (*fst)->fs_type) == 0)
281         {
282           known = TRUE;
283           break;
284         }
285       else if (!(*fst)->next)
286         {
287           break;
288         }
289     }
290
291 #if defined(SVN_USE_DSO) && APR_HAS_DSO
292   /* Third party FS modules that are unknown at compile time.
293
294      A third party FS is identified by the file fs-type containing a
295      third party name, say "foo".  The loader will load the DSO with
296      the name "libsvn_fs_foo" and use the entry point with the name
297      "svn_fs_foo__init".
298
299      Note: the BDB and FSFS modules don't follow this naming scheme
300      and this allows them to be used to test the third party loader.
301      Change the content of fs-type to "base" in a BDB filesystem or to
302      "fs" in an FSFS filesystem and they will be loaded as third party
303      modules. */
304   if (!known)
305     {
306       fst = &(*fst)->next;
307       /* Best-effort init, see get_library_vtable_direct. */
308       SVN_ERR(svn_fs_initialize(NULL));
309       SVN_MUTEX__WITH_LOCK(common_pool_lock,
310                            get_or_allocate_third(fst, fs_type));
311       known = TRUE;
312     }
313 #endif
314   if (!known)
315     return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
316                              _("Unknown FS type '%s'"), fs_type);
317   return get_library_vtable_direct(vtable, *fst, pool);
318 }
319
320 svn_error_t *
321 svn_fs_type(const char **fs_type, const char *path, apr_pool_t *pool)
322 {
323   const char *filename;
324   char buf[128];
325   svn_error_t *err;
326   apr_file_t *file;
327   apr_size_t len;
328
329   /* Read the fsap-name file to get the FSAP name, or assume the (old)
330      default.  For old repositories I suppose we could check some
331      other file, DB_CONFIG or strings say, but for now just check the
332      directory exists. */
333   filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool);
334   err = svn_io_file_open(&file, filename, APR_READ|APR_BUFFERED, 0, pool);
335   if (err && APR_STATUS_IS_ENOENT(err->apr_err))
336     {
337       svn_node_kind_t kind;
338       svn_error_t *err2 = svn_io_check_path(path, &kind, pool);
339       if (err2)
340         {
341           svn_error_clear(err2);
342           return err;
343         }
344       if (kind == svn_node_dir)
345         {
346           svn_error_clear(err);
347           *fs_type = SVN_FS_TYPE_BDB;
348           return SVN_NO_ERROR;
349         }
350       return err;
351     }
352   else if (err)
353     return err;
354
355   len = sizeof(buf);
356   SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
357   SVN_ERR(svn_io_file_close(file, pool));
358   *fs_type = apr_pstrdup(pool, buf);
359
360   return SVN_NO_ERROR;
361 }
362
363 /* Fetch the library vtable for an existing FS. */
364 static svn_error_t *
365 fs_library_vtable(fs_library_vtable_t **vtable, const char *path,
366                   apr_pool_t *pool)
367 {
368   const char *fs_type;
369
370   SVN_ERR(svn_fs_type(&fs_type, path, pool));
371
372   /* Fetch the library vtable by name, now that we've chosen one. */
373   SVN_ERR(get_library_vtable(vtable, fs_type, pool));
374
375   return SVN_NO_ERROR;
376 }
377
378 static svn_error_t *
379 write_fs_type(const char *path, const char *fs_type, apr_pool_t *pool)
380 {
381   const char *filename;
382   apr_file_t *file;
383
384   filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool);
385   SVN_ERR(svn_io_file_open(&file, filename,
386                            APR_WRITE|APR_CREATE|APR_TRUNCATE|APR_BUFFERED,
387                            APR_OS_DEFAULT, pool));
388   SVN_ERR(svn_io_file_write_full(file, fs_type, strlen(fs_type), NULL,
389                                  pool));
390   SVN_ERR(svn_io_file_write_full(file, "\n", 1, NULL, pool));
391   return svn_error_trace(svn_io_file_close(file, pool));
392 }
393
394 \f
395 /* --- Functions for operating on filesystems by pathname --- */
396
397 static apr_status_t uninit(void *data)
398 {
399   common_pool = NULL;
400   common_pool_lock = NULL;
401   common_pool_initialized = 0;
402
403   return APR_SUCCESS;
404 }
405
406 static svn_error_t *
407 synchronized_initialize(void *baton, apr_pool_t *pool)
408 {
409   common_pool = svn_pool_create(pool);
410   base_defn.next = NULL;
411   SVN_ERR(svn_mutex__init(&common_pool_lock, TRUE, common_pool));
412
413   /* ### This won't work if POOL is NULL and libsvn_fs is loaded as a DSO
414      ### (via libsvn_ra_local say) since the global common_pool will live
415      ### longer than the DSO, which gets unloaded when the pool used to
416      ### load it is cleared, and so when the handler runs it will refer to
417      ### a function that no longer exists.  libsvn_ra_local attempts to
418      ### work around this by explicitly calling svn_fs_initialize. */
419   apr_pool_cleanup_register(common_pool, NULL, uninit, apr_pool_cleanup_null);
420   return SVN_NO_ERROR;
421 }
422
423 svn_error_t *
424 svn_fs_initialize(apr_pool_t *pool)
425 {
426 #if defined(SVN_USE_DSO) && APR_HAS_DSO
427   /* Ensure that DSO subsystem is initialized early as possible if
428      we're going to use it. */
429   SVN_ERR(svn_dso_initialize2());
430 #endif
431   /* Protect against multiple calls. */
432   return svn_error_trace(svn_atomic__init_once(&common_pool_initialized,
433                                                synchronized_initialize,
434                                                NULL, pool));
435 }
436
437 /* A default warning handling function.  */
438 static void
439 default_warning_func(void *baton, svn_error_t *err)
440 {
441   /* The one unforgiveable sin is to fail silently.  Dumping to stderr
442      or /dev/tty is not acceptable default behavior for server
443      processes, since those may both be equivalent to /dev/null.
444
445      That said, be a good citizen and print something anyway, in case it goes
446      somewhere, and our caller hasn't overridden the abort() call.
447    */
448   if (svn_error_get_malfunction_handler()
449       == svn_error_abort_on_malfunction)
450     /* ### TODO: extend the malfunction API such that non-abort()ing consumers
451        ### also get the information on ERR. */
452     svn_handle_error2(err, stderr, FALSE /* fatal */, "svn: fs-loader: ");
453   SVN_ERR_MALFUNCTION_NO_RETURN();
454 }
455
456 svn_error_t *
457 svn_fs__path_valid(const char *path, apr_pool_t *pool)
458 {
459   char *c;
460
461   /* UTF-8 encoded string without NULs. */
462   if (! svn_utf__cstring_is_valid(path))
463     {
464       return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
465                                _("Path '%s' is not in UTF-8"), path);
466     }
467
468   /* No "." or ".." elements. */
469   if (svn_path_is_backpath_present(path)
470       || svn_path_is_dotpath_present(path))
471     {
472       return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
473                                _("Path '%s' contains '.' or '..' element"),
474                                path);
475     }
476
477   /* Raise an error if PATH contains a newline because svn:mergeinfo and
478      friends can't handle them.  Issue #4340 describes a similar problem
479      in the FSFS code itself.
480    */
481   c = strchr(path, '\n');
482   if (c)
483     {
484       return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
485                _("Invalid control character '0x%02x' in path '%s'"),
486                (unsigned char)*c, svn_path_illegal_path_escape(path, pool));
487     }
488
489   /* That's good enough. */
490   return SVN_NO_ERROR;
491 }
492
493 /* Allocate svn_fs_t structure. */
494 static svn_fs_t *
495 fs_new(apr_hash_t *fs_config, apr_pool_t *pool)
496 {
497   svn_fs_t *fs = apr_palloc(pool, sizeof(*fs));
498   fs->pool = pool;
499   fs->path = NULL;
500   fs->warning = default_warning_func;
501   fs->warning_baton = NULL;
502   fs->config = fs_config;
503   fs->access_ctx = NULL;
504   fs->vtable = NULL;
505   fs->fsap_data = NULL;
506   fs->uuid = NULL;
507   return fs;
508 }
509
510 svn_fs_t *
511 svn_fs_new(apr_hash_t *fs_config, apr_pool_t *pool)
512 {
513   return fs_new(fs_config, pool);
514 }
515
516 void
517 svn_fs_set_warning_func(svn_fs_t *fs, svn_fs_warning_callback_t warning,
518                         void *warning_baton)
519 {
520   fs->warning = warning;
521   fs->warning_baton = warning_baton;
522 }
523
524 svn_error_t *
525 svn_fs_create2(svn_fs_t **fs_p,
526                const char *path,
527                apr_hash_t *fs_config,
528                apr_pool_t *result_pool,
529                apr_pool_t *scratch_pool)
530 {
531   fs_library_vtable_t *vtable;
532
533   const char *fs_type = svn_hash__get_cstring(fs_config,
534                                               SVN_FS_CONFIG_FS_TYPE,
535                                               DEFAULT_FS_TYPE);
536   SVN_ERR(get_library_vtable(&vtable, fs_type, scratch_pool));
537
538   /* Create the FS directory and write out the fsap-name file. */
539   SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, scratch_pool));
540   SVN_ERR(write_fs_type(path, fs_type, scratch_pool));
541
542   /* Perform the actual creation. */
543   *fs_p = fs_new(fs_config, result_pool);
544
545   SVN_ERR(vtable->create(*fs_p, path, common_pool_lock, scratch_pool,
546                          common_pool));
547   SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open2));
548
549   return SVN_NO_ERROR;
550 }
551
552 svn_error_t *
553 svn_fs_open2(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config,
554              apr_pool_t *result_pool,
555              apr_pool_t *scratch_pool)
556 {
557   fs_library_vtable_t *vtable;
558
559   SVN_ERR(fs_library_vtable(&vtable, path, scratch_pool));
560   *fs_p = fs_new(fs_config, result_pool);
561   SVN_ERR(vtable->open_fs(*fs_p, path, common_pool_lock, scratch_pool,
562                           common_pool));
563   SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open2));
564
565   return SVN_NO_ERROR;
566 }
567
568 svn_error_t *
569 svn_fs_upgrade2(const char *path,
570                 svn_fs_upgrade_notify_t notify_func,
571                 void *notify_baton,
572                 svn_cancel_func_t cancel_func,
573                 void *cancel_baton,
574                 apr_pool_t *scratch_pool)
575 {
576   fs_library_vtable_t *vtable;
577   svn_fs_t *fs;
578
579   SVN_ERR(fs_library_vtable(&vtable, path, scratch_pool));
580   fs = fs_new(NULL, scratch_pool);
581
582   SVN_ERR(vtable->upgrade_fs(fs, path,
583                              notify_func, notify_baton,
584                              cancel_func, cancel_baton,
585                              common_pool_lock,
586                              scratch_pool, common_pool));
587   return SVN_NO_ERROR;
588 }
589
590 /* A warning handling function that does not abort on errors,
591    but just lets them be returned normally.  */
592 static void
593 verify_fs_warning_func(void *baton, svn_error_t *err)
594 {
595 }
596
597 svn_error_t *
598 svn_fs_verify(const char *path,
599               apr_hash_t *fs_config,
600               svn_revnum_t start,
601               svn_revnum_t end,
602               svn_fs_progress_notify_func_t notify_func,
603               void *notify_baton,
604               svn_cancel_func_t cancel_func,
605               void *cancel_baton,
606               apr_pool_t *pool)
607 {
608   fs_library_vtable_t *vtable;
609   svn_fs_t *fs;
610
611   SVN_ERR(fs_library_vtable(&vtable, path, pool));
612   fs = fs_new(fs_config, pool);
613   svn_fs_set_warning_func(fs, verify_fs_warning_func, NULL);
614
615   SVN_ERR(vtable->verify_fs(fs, path, start, end,
616                             notify_func, notify_baton,
617                             cancel_func, cancel_baton,
618                             common_pool_lock,
619                             pool, common_pool));
620   return SVN_NO_ERROR;
621 }
622
623 const char *
624 svn_fs_path(svn_fs_t *fs, apr_pool_t *pool)
625 {
626   return apr_pstrdup(pool, fs->path);
627 }
628
629 apr_hash_t *
630 svn_fs_config(svn_fs_t *fs, apr_pool_t *pool)
631 {
632   if (fs->config)
633     return apr_hash_copy(pool, fs->config);
634
635   return NULL;
636 }
637
638 svn_error_t *
639 svn_fs_delete_fs(const char *path, apr_pool_t *pool)
640 {
641   fs_library_vtable_t *vtable;
642
643   SVN_ERR(fs_library_vtable(&vtable, path, pool));
644   return svn_error_trace(vtable->delete_fs(path, pool));
645 }
646
647 svn_error_t *
648 svn_fs_hotcopy3(const char *src_path, const char *dst_path,
649                 svn_boolean_t clean, svn_boolean_t incremental,
650                 svn_fs_hotcopy_notify_t notify_func,
651                 void *notify_baton,
652                 svn_cancel_func_t cancel_func,
653                 void *cancel_baton,
654                 apr_pool_t *scratch_pool)
655 {
656   fs_library_vtable_t *vtable;
657   const char *src_fs_type;
658   svn_fs_t *src_fs;
659   svn_fs_t *dst_fs;
660   const char *dst_fs_type;
661   svn_node_kind_t dst_kind;
662
663   if (strcmp(src_path, dst_path) == 0)
664     return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
665                              _("Hotcopy source and destination are equal"));
666
667   SVN_ERR(svn_fs_type(&src_fs_type, src_path, scratch_pool));
668   SVN_ERR(get_library_vtable(&vtable, src_fs_type, scratch_pool));
669   src_fs = fs_new(NULL, scratch_pool);
670   dst_fs = fs_new(NULL, scratch_pool);
671
672   SVN_ERR(svn_io_check_path(dst_path, &dst_kind, scratch_pool));
673   if (dst_kind == svn_node_file)
674     return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
675                              _("'%s' already exists and is a file"),
676                              svn_dirent_local_style(dst_path,
677                                                     scratch_pool));
678   if (dst_kind == svn_node_unknown)
679     return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
680                              _("'%s' already exists and has an unknown "
681                                "node kind"),
682                              svn_dirent_local_style(dst_path,
683                                                     scratch_pool));
684   if (dst_kind == svn_node_dir)
685     {
686       svn_node_kind_t type_file_kind;
687
688       SVN_ERR(svn_io_check_path(svn_dirent_join(dst_path,
689                                                 FS_TYPE_FILENAME,
690                                                 scratch_pool),
691                                 &type_file_kind, scratch_pool));
692       if (type_file_kind != svn_node_none)
693         {
694           SVN_ERR(svn_fs_type(&dst_fs_type, dst_path, scratch_pool));
695           if (strcmp(src_fs_type, dst_fs_type) != 0)
696             return svn_error_createf(
697                      SVN_ERR_ILLEGAL_TARGET, NULL,
698                      _("The filesystem type of the hotcopy source "
699                        "('%s') does not match the filesystem "
700                        "type of the hotcopy destination ('%s')"),
701                      src_fs_type, dst_fs_type);
702         }
703     }
704
705   SVN_ERR(vtable->hotcopy(src_fs, dst_fs, src_path, dst_path, clean,
706                           incremental, notify_func, notify_baton,
707                           cancel_func, cancel_baton, common_pool_lock,
708                           scratch_pool, common_pool));
709   return svn_error_trace(write_fs_type(dst_path, src_fs_type, scratch_pool));
710 }
711
712 svn_error_t *
713 svn_fs_pack(const char *path,
714             svn_fs_pack_notify_t notify_func,
715             void *notify_baton,
716             svn_cancel_func_t cancel_func,
717             void *cancel_baton,
718             apr_pool_t *pool)
719 {
720   fs_library_vtable_t *vtable;
721   svn_fs_t *fs;
722
723   SVN_ERR(fs_library_vtable(&vtable, path, pool));
724   fs = fs_new(NULL, pool);
725
726   SVN_ERR(vtable->pack_fs(fs, path, notify_func, notify_baton,
727                           cancel_func, cancel_baton, common_pool_lock,
728                           pool, common_pool));
729   return SVN_NO_ERROR;
730 }
731
732 svn_error_t *
733 svn_fs_recover(const char *path,
734                svn_cancel_func_t cancel_func, void *cancel_baton,
735                apr_pool_t *pool)
736 {
737   fs_library_vtable_t *vtable;
738   svn_fs_t *fs;
739
740   SVN_ERR(fs_library_vtable(&vtable, path, pool));
741   fs = fs_new(NULL, pool);
742
743   SVN_ERR(vtable->open_fs_for_recovery(fs, path, common_pool_lock,
744                                        pool, common_pool));
745   return svn_error_trace(vtable->recover(fs, cancel_func, cancel_baton,
746                                          pool));
747 }
748
749 svn_error_t *
750 svn_fs_verify_root(svn_fs_root_t *root,
751                    apr_pool_t *scratch_pool)
752 {
753   svn_fs_t *fs = root->fs;
754   SVN_ERR(fs->vtable->verify_root(root, scratch_pool));
755
756   return SVN_NO_ERROR;
757 }
758
759 svn_error_t *
760 svn_fs_freeze(svn_fs_t *fs,
761               svn_fs_freeze_func_t freeze_func,
762               void *freeze_baton,
763               apr_pool_t *pool)
764 {
765   SVN_ERR(fs->vtable->freeze(fs, freeze_func, freeze_baton, pool));
766
767   return SVN_NO_ERROR;
768 }
769
770 \f
771 /* --- Berkeley-specific functions --- */
772
773 svn_error_t *
774 svn_fs_create_berkeley(svn_fs_t *fs, const char *path)
775 {
776   fs_library_vtable_t *vtable;
777
778   SVN_ERR(get_library_vtable(&vtable, SVN_FS_TYPE_BDB, fs->pool));
779
780   /* Create the FS directory and write out the fsap-name file. */
781   SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, fs->pool));
782   SVN_ERR(write_fs_type(path, SVN_FS_TYPE_BDB, fs->pool));
783
784   /* Perform the actual creation. */
785   SVN_ERR(vtable->create(fs, path, common_pool_lock, fs->pool, common_pool));
786   SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open2));
787
788   return SVN_NO_ERROR;
789 }
790
791 svn_error_t *
792 svn_fs_open_berkeley(svn_fs_t *fs, const char *path)
793 {
794   fs_library_vtable_t *vtable;
795
796   SVN_ERR(fs_library_vtable(&vtable, path, fs->pool));
797   SVN_ERR(vtable->open_fs(fs, path, common_pool_lock, fs->pool, common_pool));
798   SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open2));
799
800   return SVN_NO_ERROR;
801 }
802
803 const char *
804 svn_fs_berkeley_path(svn_fs_t *fs, apr_pool_t *pool)
805 {
806   return svn_fs_path(fs, pool);
807 }
808
809 svn_error_t *
810 svn_fs_delete_berkeley(const char *path, apr_pool_t *pool)
811 {
812   return svn_error_trace(svn_fs_delete_fs(path, pool));
813 }
814
815 svn_error_t *
816 svn_fs_hotcopy_berkeley(const char *src_path, const char *dest_path,
817                         svn_boolean_t clean_logs, apr_pool_t *pool)
818 {
819   return svn_error_trace(svn_fs_hotcopy3(src_path, dest_path, clean_logs,
820                                          FALSE, NULL, NULL, NULL, NULL,
821                                          pool));
822 }
823
824 svn_error_t *
825 svn_fs_berkeley_recover(const char *path, apr_pool_t *pool)
826 {
827   return svn_error_trace(svn_fs_recover(path, NULL, NULL, pool));
828 }
829
830 svn_error_t *
831 svn_fs_set_berkeley_errcall(svn_fs_t *fs,
832                             void (*handler)(const char *errpfx, char *msg))
833 {
834   return svn_error_trace(fs->vtable->bdb_set_errcall(fs, handler));
835 }
836
837 svn_error_t *
838 svn_fs_berkeley_logfiles(apr_array_header_t **logfiles,
839                          const char *path,
840                          svn_boolean_t only_unused,
841                          apr_pool_t *pool)
842 {
843   fs_library_vtable_t *vtable;
844
845   SVN_ERR(fs_library_vtable(&vtable, path, pool));
846   return svn_error_trace(vtable->bdb_logfiles(logfiles, path, only_unused,
847                                               pool));
848 }
849
850 \f
851 /* --- Transaction functions --- */
852
853 svn_error_t *
854 svn_fs_begin_txn2(svn_fs_txn_t **txn_p, svn_fs_t *fs, svn_revnum_t rev,
855                   apr_uint32_t flags, apr_pool_t *pool)
856 {
857   return svn_error_trace(fs->vtable->begin_txn(txn_p, fs, rev, flags, pool));
858 }
859
860
861 svn_error_t *
862 svn_fs_commit_txn(const char **conflict_p, svn_revnum_t *new_rev,
863                    svn_fs_txn_t *txn, apr_pool_t *pool)
864 {
865   svn_error_t *err;
866
867   *new_rev = SVN_INVALID_REVNUM;
868   if (conflict_p)
869     *conflict_p = NULL;
870
871   err = txn->vtable->commit(conflict_p, new_rev, txn, pool);
872
873 #ifdef SVN_DEBUG
874   /* Check postconditions. */
875   if (conflict_p)
876     {
877       SVN_ERR_ASSERT_E(! (SVN_IS_VALID_REVNUM(*new_rev) && *conflict_p != NULL),
878                        err);
879       SVN_ERR_ASSERT_E((*conflict_p != NULL)
880                        == (err && err->apr_err == SVN_ERR_FS_CONFLICT),
881                        err);
882     }
883 #endif
884
885   SVN_ERR(err);
886
887   return SVN_NO_ERROR;
888 }
889
890 svn_error_t *
891 svn_fs_abort_txn(svn_fs_txn_t *txn, apr_pool_t *pool)
892 {
893   return svn_error_trace(txn->vtable->abort(txn, pool));
894 }
895
896 svn_error_t *
897 svn_fs_purge_txn(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
898 {
899   return svn_error_trace(fs->vtable->purge_txn(fs, txn_id, pool));
900 }
901
902 svn_error_t *
903 svn_fs_txn_name(const char **name_p, svn_fs_txn_t *txn, apr_pool_t *pool)
904 {
905   *name_p = apr_pstrdup(pool, txn->id);
906   return SVN_NO_ERROR;
907 }
908
909 svn_revnum_t
910 svn_fs_txn_base_revision(svn_fs_txn_t *txn)
911 {
912   return txn->base_rev;
913 }
914
915 svn_error_t *
916 svn_fs_open_txn(svn_fs_txn_t **txn, svn_fs_t *fs, const char *name,
917                 apr_pool_t *pool)
918 {
919   return svn_error_trace(fs->vtable->open_txn(txn, fs, name, pool));
920 }
921
922 svn_error_t *
923 svn_fs_list_transactions(apr_array_header_t **names_p, svn_fs_t *fs,
924                          apr_pool_t *pool)
925 {
926   return svn_error_trace(fs->vtable->list_transactions(names_p, fs, pool));
927 }
928
929 static svn_boolean_t
930 is_internal_txn_prop(const char *name)
931 {
932   return strcmp(name, SVN_FS__PROP_TXN_CHECK_LOCKS) == 0 ||
933          strcmp(name, SVN_FS__PROP_TXN_CHECK_OOD) == 0 ||
934          strcmp(name, SVN_FS__PROP_TXN_CLIENT_DATE) == 0;
935 }
936
937 svn_error_t *
938 svn_fs_txn_prop(svn_string_t **value_p, svn_fs_txn_t *txn,
939                 const char *propname, apr_pool_t *pool)
940 {
941   if (is_internal_txn_prop(propname))
942     {
943       *value_p = NULL;
944       return SVN_NO_ERROR;
945     }
946
947   return svn_error_trace(txn->vtable->get_prop(value_p, txn, propname, pool));
948 }
949
950 svn_error_t *
951 svn_fs_txn_proplist(apr_hash_t **table_p, svn_fs_txn_t *txn, apr_pool_t *pool)
952 {
953   SVN_ERR(txn->vtable->get_proplist(table_p, txn, pool));
954
955   /* Don't give away internal transaction properties. */
956   svn_hash_sets(*table_p, SVN_FS__PROP_TXN_CHECK_LOCKS, NULL);
957   svn_hash_sets(*table_p, SVN_FS__PROP_TXN_CHECK_OOD, NULL);
958   svn_hash_sets(*table_p, SVN_FS__PROP_TXN_CLIENT_DATE, NULL);
959
960   return SVN_NO_ERROR;
961 }
962
963 svn_error_t *
964 svn_fs_change_txn_prop(svn_fs_txn_t *txn, const char *name,
965                        const svn_string_t *value, apr_pool_t *pool)
966 {
967   if (is_internal_txn_prop(name))
968     return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
969                              _("Attempt to modify internal transaction "
970                                "property '%s'"), name);
971
972   return svn_error_trace(txn->vtable->change_prop(txn, name, value, pool));
973 }
974
975 svn_error_t *
976 svn_fs_change_txn_props(svn_fs_txn_t *txn, const apr_array_header_t *props,
977                         apr_pool_t *pool)
978 {
979   int i;
980
981   for (i = 0; i < props->nelts; ++i)
982     {
983       svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
984
985       if (is_internal_txn_prop(prop->name))
986         return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
987                                  _("Attempt to modify internal transaction "
988                                    "property '%s'"), prop->name);
989     }
990
991   return svn_error_trace(txn->vtable->change_props(txn, props, pool));
992 }
993
994 \f
995 /* --- Root functions --- */
996
997 svn_error_t *
998 svn_fs_revision_root(svn_fs_root_t **root_p, svn_fs_t *fs, svn_revnum_t rev,
999                      apr_pool_t *pool)
1000 {
1001   /* We create a subpool for each root object to allow us to implement
1002      svn_fs_close_root.  */
1003   apr_pool_t *subpool = svn_pool_create(pool);
1004   return svn_error_trace(fs->vtable->revision_root(root_p, fs, rev, subpool));
1005 }
1006
1007 svn_error_t *
1008 svn_fs_txn_root(svn_fs_root_t **root_p, svn_fs_txn_t *txn, apr_pool_t *pool)
1009 {
1010   /* We create a subpool for each root object to allow us to implement
1011      svn_fs_close_root.  */
1012   apr_pool_t *subpool = svn_pool_create(pool);
1013   return svn_error_trace(txn->vtable->root(root_p, txn, subpool));
1014 }
1015
1016 void
1017 svn_fs_close_root(svn_fs_root_t *root)
1018 {
1019   svn_pool_destroy(root->pool);
1020 }
1021
1022 svn_fs_t *
1023 svn_fs_root_fs(svn_fs_root_t *root)
1024 {
1025   return root->fs;
1026 }
1027
1028 svn_boolean_t
1029 svn_fs_is_txn_root(svn_fs_root_t *root)
1030 {
1031   return root->is_txn_root;
1032 }
1033
1034 svn_boolean_t
1035 svn_fs_is_revision_root(svn_fs_root_t *root)
1036 {
1037   return !root->is_txn_root;
1038 }
1039
1040 const char *
1041 svn_fs_txn_root_name(svn_fs_root_t *root, apr_pool_t *pool)
1042 {
1043   return root->is_txn_root ? apr_pstrdup(pool, root->txn) : NULL;
1044 }
1045
1046 svn_revnum_t
1047 svn_fs_txn_root_base_revision(svn_fs_root_t *root)
1048 {
1049   return root->is_txn_root ? root->rev : SVN_INVALID_REVNUM;
1050 }
1051
1052 svn_revnum_t
1053 svn_fs_revision_root_revision(svn_fs_root_t *root)
1054 {
1055   return root->is_txn_root ? SVN_INVALID_REVNUM : root->rev;
1056 }
1057
1058 svn_error_t *
1059 svn_fs_path_change_get(svn_fs_path_change3_t **change,
1060                        svn_fs_path_change_iterator_t *iterator)
1061 {
1062   return iterator->vtable->get(change, iterator);
1063 }
1064
1065 svn_error_t *
1066 svn_fs_paths_changed2(apr_hash_t **changed_paths_p,
1067                       svn_fs_root_t *root,
1068                       apr_pool_t *pool)
1069 {
1070   svn_boolean_t emulate =    !root->vtable->paths_changed
1071                           || SVN_FS_EMULATE_PATHS_CHANGED;
1072
1073   if (emulate)
1074     {
1075       apr_pool_t *scratch_pool = svn_pool_create(pool);
1076       apr_hash_t *changes = svn_hash__make(pool);
1077
1078       svn_fs_path_change_iterator_t *iterator;
1079       svn_fs_path_change3_t *change;
1080
1081       SVN_ERR(svn_fs_paths_changed3(&iterator, root, scratch_pool,
1082                                     scratch_pool));
1083
1084       SVN_ERR(svn_fs_path_change_get(&change, iterator));
1085       while (change)
1086         {
1087           svn_fs_path_change2_t *copy;
1088           const svn_fs_id_t *id_copy;
1089           const char *change_path = change->path.data;
1090           svn_fs_root_t *change_root = root;
1091
1092           /* Copy CHANGE to old API struct. */
1093           if (change->change_kind == svn_fs_path_change_delete)
1094             SVN_ERR(svn_fs__get_deleted_node(&change_root, &change_path,
1095                                              change_root, change_path,
1096                                              scratch_pool, scratch_pool));
1097
1098           SVN_ERR(svn_fs_node_id(&id_copy, change_root, change_path, pool));
1099
1100           copy = svn_fs_path_change2_create(id_copy, change->change_kind,
1101                                             pool);
1102           copy->copyfrom_known = change->copyfrom_known;
1103           if (   copy->copyfrom_known
1104               && SVN_IS_VALID_REVNUM(change->copyfrom_rev))
1105             {
1106               copy->copyfrom_rev = change->copyfrom_rev;
1107               copy->copyfrom_path = apr_pstrdup(pool, change->copyfrom_path);
1108             }
1109           copy->mergeinfo_mod = change->mergeinfo_mod;
1110           copy->node_kind = change->node_kind;
1111           copy->prop_mod = change->prop_mod;
1112           copy->text_mod = change->text_mod;
1113
1114           svn_hash_sets(changes, apr_pstrmemdup(pool, change->path.data,
1115                                                 change->path.len), copy);
1116
1117           /* Next change. */
1118           SVN_ERR(svn_fs_path_change_get(&change, iterator));
1119         }
1120       svn_pool_destroy(scratch_pool);
1121
1122       *changed_paths_p = changes;
1123     }
1124   else
1125     {
1126       SVN_ERR(root->vtable->paths_changed(changed_paths_p, root, pool));
1127     }
1128
1129   return SVN_NO_ERROR;
1130 }
1131
1132 /* Implement svn_fs_path_change_iterator_t on top of svn_fs_paths_changed2. */
1133
1134 /* The iterator builds upon a hash iterator, which in turn operates on the
1135    full prefetched changes list. */
1136 typedef struct fsap_iterator_data_t
1137 {
1138   apr_hash_index_t *hi;
1139
1140   /* For efficicency such that we don't need to dynamically allocate
1141      yet another copy of that data. */
1142   svn_fs_path_change3_t change;
1143 } fsap_iterator_data_t;
1144
1145 static svn_error_t *
1146 changes_iterator_get(svn_fs_path_change3_t **change,
1147                      svn_fs_path_change_iterator_t *iterator)
1148 {
1149   fsap_iterator_data_t *data = iterator->fsap_data;
1150
1151   if (data->hi)
1152     {
1153       const char *path = apr_hash_this_key(data->hi);
1154       svn_fs_path_change2_t *entry = apr_hash_this_val(data->hi);
1155
1156       data->change.path.data = path;
1157       data->change.path.len = apr_hash_this_key_len(data->hi);
1158       data->change.change_kind = entry->change_kind;
1159       data->change.node_kind = entry->node_kind;
1160       data->change.text_mod = entry->text_mod;
1161       data->change.prop_mod = entry->prop_mod;
1162       data->change.mergeinfo_mod = entry->mergeinfo_mod;
1163       data->change.copyfrom_known = entry->copyfrom_known;
1164       data->change.copyfrom_rev = entry->copyfrom_rev;
1165       data->change.copyfrom_path = entry->copyfrom_path;
1166
1167       *change = &data->change;
1168       data->hi = apr_hash_next(data->hi);
1169     }
1170   else
1171     {
1172       *change = NULL;
1173     }
1174
1175   return SVN_NO_ERROR;
1176 }
1177
1178 static changes_iterator_vtable_t iterator_vtable =
1179 {
1180   changes_iterator_get
1181 };
1182
1183 svn_error_t *
1184 svn_fs_paths_changed3(svn_fs_path_change_iterator_t **iterator,
1185                       svn_fs_root_t *root,
1186                       apr_pool_t *result_pool,
1187                       apr_pool_t *scratch_pool)
1188 {
1189   svn_boolean_t emulate =    !root->vtable->report_changes
1190                           || (   SVN_FS_EMULATE_REPORT_CHANGES
1191                               && root->vtable->paths_changed);
1192
1193   if (emulate)
1194     {
1195       svn_fs_path_change_iterator_t *result;
1196       fsap_iterator_data_t *data;
1197
1198       apr_hash_t *changes;
1199       SVN_ERR(root->vtable->paths_changed(&changes, root, result_pool));
1200
1201       data = apr_pcalloc(result_pool, sizeof(*data));
1202       data->hi = apr_hash_first(result_pool, changes);
1203
1204       result = apr_pcalloc(result_pool, sizeof(*result));
1205       result->fsap_data = data;
1206       result->vtable = &iterator_vtable;
1207
1208       *iterator = result;
1209     }
1210   else
1211     {
1212       SVN_ERR(root->vtable->report_changes(iterator, root, result_pool,
1213                                            scratch_pool));
1214     }
1215
1216   return SVN_NO_ERROR;
1217 }
1218
1219 svn_error_t *
1220 svn_fs_check_path(svn_node_kind_t *kind_p, svn_fs_root_t *root,
1221                   const char *path, apr_pool_t *pool)
1222 {
1223   return svn_error_trace(root->vtable->check_path(kind_p, root, path, pool));
1224 }
1225
1226 svn_error_t *
1227 svn_fs_node_history2(svn_fs_history_t **history_p, svn_fs_root_t *root,
1228                      const char *path, apr_pool_t *result_pool,
1229                      apr_pool_t *scratch_pool)
1230 {
1231   return svn_error_trace(root->vtable->node_history(history_p, root, path,
1232                                                     result_pool,
1233                                                     scratch_pool));
1234 }
1235
1236 svn_error_t *
1237 svn_fs_is_dir(svn_boolean_t *is_dir, svn_fs_root_t *root, const char *path,
1238               apr_pool_t *pool)
1239 {
1240   svn_node_kind_t kind;
1241
1242   SVN_ERR(root->vtable->check_path(&kind, root, path, pool));
1243   *is_dir = (kind == svn_node_dir);
1244   return SVN_NO_ERROR;
1245 }
1246
1247 svn_error_t *
1248 svn_fs_is_file(svn_boolean_t *is_file, svn_fs_root_t *root, const char *path,
1249                apr_pool_t *pool)
1250 {
1251   svn_node_kind_t kind;
1252
1253   SVN_ERR(root->vtable->check_path(&kind, root, path, pool));
1254   *is_file = (kind == svn_node_file);
1255   return SVN_NO_ERROR;
1256 }
1257
1258 svn_error_t *
1259 svn_fs_node_id(const svn_fs_id_t **id_p, svn_fs_root_t *root,
1260                const char *path, apr_pool_t *pool)
1261 {
1262   return svn_error_trace(root->vtable->node_id(id_p, root, path, pool));
1263 }
1264
1265 svn_error_t *
1266 svn_fs_node_relation(svn_fs_node_relation_t *relation,
1267                      svn_fs_root_t *root_a, const char *path_a,
1268                      svn_fs_root_t *root_b, const char *path_b,
1269                      apr_pool_t *scratch_pool)
1270 {
1271   /* Different repository types? */
1272   if (root_a->fs != root_b->fs)
1273     {
1274       *relation = svn_fs_node_unrelated;
1275       return SVN_NO_ERROR;
1276     }
1277
1278   return svn_error_trace(root_a->vtable->node_relation(relation,
1279                                                        root_a, path_a,
1280                                                        root_b, path_b,
1281                                                        scratch_pool));
1282 }
1283
1284 svn_error_t *
1285 svn_fs_node_created_rev(svn_revnum_t *revision, svn_fs_root_t *root,
1286                         const char *path, apr_pool_t *pool)
1287 {
1288   return svn_error_trace(root->vtable->node_created_rev(revision, root, path,
1289                                                         pool));
1290 }
1291
1292 svn_error_t *
1293 svn_fs_node_origin_rev(svn_revnum_t *revision, svn_fs_root_t *root,
1294                        const char *path, apr_pool_t *pool)
1295 {
1296   return svn_error_trace(root->vtable->node_origin_rev(revision, root, path,
1297                                                        pool));
1298 }
1299
1300 svn_error_t *
1301 svn_fs_node_created_path(const char **created_path, svn_fs_root_t *root,
1302                          const char *path, apr_pool_t *pool)
1303 {
1304   return svn_error_trace(root->vtable->node_created_path(created_path, root,
1305                                                          path, pool));
1306 }
1307
1308 svn_error_t *
1309 svn_fs_node_prop(svn_string_t **value_p, svn_fs_root_t *root,
1310                  const char *path, const char *propname, apr_pool_t *pool)
1311 {
1312   return svn_error_trace(root->vtable->node_prop(value_p, root, path,
1313                                                  propname, pool));
1314 }
1315
1316 svn_error_t *
1317 svn_fs_node_proplist(apr_hash_t **table_p, svn_fs_root_t *root,
1318                      const char *path, apr_pool_t *pool)
1319 {
1320   return svn_error_trace(root->vtable->node_proplist(table_p, root, path,
1321                                                      pool));
1322 }
1323
1324 svn_error_t *
1325 svn_fs_node_has_props(svn_boolean_t *has_props,
1326                       svn_fs_root_t *root,
1327                       const char *path,
1328                       apr_pool_t *scratch_pool)
1329 {
1330   return svn_error_trace(root->vtable->node_has_props(has_props, root, path,
1331                                                       scratch_pool));
1332 }
1333
1334 svn_error_t *
1335 svn_fs_change_node_prop(svn_fs_root_t *root, const char *path,
1336                         const char *name, const svn_string_t *value,
1337                         apr_pool_t *pool)
1338 {
1339   return svn_error_trace(root->vtable->change_node_prop(root, path, name,
1340                                                         value, pool));
1341 }
1342
1343 svn_error_t *
1344 svn_fs_props_different(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1345                        const char *path1, svn_fs_root_t *root2,
1346                        const char *path2, apr_pool_t *scratch_pool)
1347 {
1348   return svn_error_trace(root1->vtable->props_changed(changed_p,
1349                                                       root1, path1,
1350                                                       root2, path2,
1351                                                       TRUE, scratch_pool));
1352 }
1353
1354 svn_error_t *
1355 svn_fs_props_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1356                      const char *path1, svn_fs_root_t *root2,
1357                      const char *path2, apr_pool_t *pool)
1358 {
1359   return svn_error_trace(root1->vtable->props_changed(changed_p,
1360                                                       root1, path1,
1361                                                       root2, path2,
1362                                                       FALSE, pool));
1363 }
1364
1365 svn_error_t *
1366 svn_fs_copied_from(svn_revnum_t *rev_p, const char **path_p,
1367                    svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1368 {
1369   return svn_error_trace(root->vtable->copied_from(rev_p, path_p, root, path,
1370                                                    pool));
1371 }
1372
1373 svn_error_t *
1374 svn_fs_closest_copy(svn_fs_root_t **root_p, const char **path_p,
1375                     svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1376 {
1377   return svn_error_trace(root->vtable->closest_copy(root_p, path_p,
1378                                                     root, path, pool));
1379 }
1380
1381 svn_error_t *
1382 svn_fs_get_mergeinfo3(svn_fs_root_t *root,
1383                       const apr_array_header_t *paths,
1384                       svn_mergeinfo_inheritance_t inherit,
1385                       svn_boolean_t include_descendants,
1386                       svn_boolean_t adjust_inherited_mergeinfo,
1387                       svn_fs_mergeinfo_receiver_t receiver,
1388                       void *baton,
1389                       apr_pool_t *scratch_pool)
1390 {
1391   return svn_error_trace(root->vtable->get_mergeinfo(
1392     root, paths, inherit, include_descendants, adjust_inherited_mergeinfo,
1393     receiver, baton, scratch_pool));
1394 }
1395
1396 /* Baton type to be used with mergeinfo_receiver().  It provides some of
1397  * the parameters passed to svn_fs__get_mergeinfo_for_path. */
1398 typedef struct mergeinfo_receiver_baton_t
1399 {
1400   svn_mergeinfo_t *mergeinfo;
1401   apr_pool_t *result_pool;
1402 } mergeinfo_receiver_baton_t;
1403
1404 static svn_error_t *
1405 mergeinfo_receiver(const char *path,
1406                    svn_mergeinfo_t mergeinfo,
1407                    void *baton,
1408                    apr_pool_t *scratch_pool)
1409 {
1410   mergeinfo_receiver_baton_t *b = baton;
1411   *b->mergeinfo = svn_mergeinfo_dup(mergeinfo, b->result_pool);
1412
1413   return SVN_NO_ERROR;
1414 }
1415
1416 svn_error_t *
1417 svn_fs__get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo,
1418                                svn_fs_root_t *root,
1419                                const char *path,
1420                                svn_mergeinfo_inheritance_t inherit,
1421                                svn_boolean_t adjust_inherited_mergeinfo,
1422                                apr_pool_t *result_pool,
1423                                apr_pool_t *scratch_pool)
1424 {
1425   apr_array_header_t *paths
1426     = apr_array_make(scratch_pool, 1, sizeof(const char *));
1427
1428   mergeinfo_receiver_baton_t baton;
1429   baton.mergeinfo = mergeinfo;
1430   baton.result_pool = result_pool;
1431
1432   APR_ARRAY_PUSH(paths, const char *) = path;
1433
1434   *mergeinfo = NULL;
1435   SVN_ERR(svn_fs_get_mergeinfo3(root, paths,
1436                                 inherit, FALSE /*include_descendants*/,
1437                                 adjust_inherited_mergeinfo,
1438                                 mergeinfo_receiver, &baton,
1439                                 scratch_pool));
1440
1441   return SVN_NO_ERROR;
1442 }
1443
1444 svn_error_t *
1445 svn_fs_merge(const char **conflict_p, svn_fs_root_t *source_root,
1446              const char *source_path, svn_fs_root_t *target_root,
1447              const char *target_path, svn_fs_root_t *ancestor_root,
1448              const char *ancestor_path, apr_pool_t *pool)
1449 {
1450   return svn_error_trace(target_root->vtable->merge(conflict_p,
1451                                                     source_root, source_path,
1452                                                     target_root, target_path,
1453                                                     ancestor_root,
1454                                                     ancestor_path, pool));
1455 }
1456
1457 svn_error_t *
1458 svn_fs_dir_entries(apr_hash_t **entries_p, svn_fs_root_t *root,
1459                    const char *path, apr_pool_t *pool)
1460 {
1461   return svn_error_trace(root->vtable->dir_entries(entries_p, root, path,
1462                                                    pool));
1463 }
1464
1465 svn_error_t *
1466 svn_fs_dir_optimal_order(apr_array_header_t **ordered_p,
1467                          svn_fs_root_t *root,
1468                          apr_hash_t *entries,
1469                          apr_pool_t *result_pool,
1470                          apr_pool_t *scratch_pool)
1471 {
1472   return svn_error_trace(root->vtable->dir_optimal_order(ordered_p, root,
1473                                                          entries,
1474                                                          result_pool,
1475                                                          scratch_pool));
1476 }
1477
1478 svn_error_t *
1479 svn_fs_make_dir(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1480 {
1481   SVN_ERR(svn_fs__path_valid(path, pool));
1482   return svn_error_trace(root->vtable->make_dir(root, path, pool));
1483 }
1484
1485 svn_error_t *
1486 svn_fs_delete(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1487 {
1488   return svn_error_trace(root->vtable->delete_node(root, path, pool));
1489 }
1490
1491 svn_error_t *
1492 svn_fs_copy(svn_fs_root_t *from_root, const char *from_path,
1493             svn_fs_root_t *to_root, const char *to_path, apr_pool_t *pool)
1494 {
1495   SVN_ERR(svn_fs__path_valid(to_path, pool));
1496   return svn_error_trace(to_root->vtable->copy(from_root, from_path,
1497                                                to_root, to_path, pool));
1498 }
1499
1500 svn_error_t *
1501 svn_fs_revision_link(svn_fs_root_t *from_root, svn_fs_root_t *to_root,
1502                      const char *path, apr_pool_t *pool)
1503 {
1504   return svn_error_trace(to_root->vtable->revision_link(from_root, to_root,
1505                                                         path, pool));
1506 }
1507
1508 svn_error_t *
1509 svn_fs_file_length(svn_filesize_t *length_p, svn_fs_root_t *root,
1510                    const char *path, apr_pool_t *pool)
1511 {
1512   return svn_error_trace(root->vtable->file_length(length_p, root, path,
1513                                                    pool));
1514 }
1515
1516 svn_error_t *
1517 svn_fs_file_checksum(svn_checksum_t **checksum,
1518                      svn_checksum_kind_t kind,
1519                      svn_fs_root_t *root,
1520                      const char *path,
1521                      svn_boolean_t force,
1522                      apr_pool_t *pool)
1523 {
1524   SVN_ERR(root->vtable->file_checksum(checksum, kind, root, path, pool));
1525
1526   if (force && (*checksum == NULL || (*checksum)->kind != kind))
1527     {
1528       svn_stream_t *contents;
1529
1530       SVN_ERR(svn_fs_file_contents(&contents, root, path, pool));
1531       SVN_ERR(svn_stream_contents_checksum(checksum, contents, kind,
1532                                            pool, pool));
1533     }
1534
1535   return SVN_NO_ERROR;
1536 }
1537
1538 svn_error_t *
1539 svn_fs_file_contents(svn_stream_t **contents, svn_fs_root_t *root,
1540                      const char *path, apr_pool_t *pool)
1541 {
1542   return svn_error_trace(root->vtable->file_contents(contents, root, path,
1543                                                      pool));
1544 }
1545
1546 svn_error_t *
1547 svn_fs_try_process_file_contents(svn_boolean_t *success,
1548                                  svn_fs_root_t *root,
1549                                  const char *path,
1550                                  svn_fs_process_contents_func_t processor,
1551                                  void* baton,
1552                                  apr_pool_t *pool)
1553 {
1554   /* if the FS doesn't implement this function, report a "failed" attempt */
1555   if (root->vtable->try_process_file_contents == NULL)
1556     {
1557       *success = FALSE;
1558       return SVN_NO_ERROR;
1559     }
1560
1561   return svn_error_trace(root->vtable->try_process_file_contents(
1562                          success,
1563                          root, path,
1564                          processor, baton, pool));
1565 }
1566
1567 svn_error_t *
1568 svn_fs_make_file(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1569 {
1570   SVN_ERR(svn_fs__path_valid(path, pool));
1571   return svn_error_trace(root->vtable->make_file(root, path, pool));
1572 }
1573
1574 svn_error_t *
1575 svn_fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
1576                        void **contents_baton_p, svn_fs_root_t *root,
1577                        const char *path, const char *base_checksum,
1578                        const char *result_checksum, apr_pool_t *pool)
1579 {
1580   svn_checksum_t *base, *result;
1581
1582   /* TODO: If we ever rev this API, we should make the supplied checksums
1583      svn_checksum_t structs. */
1584   SVN_ERR(svn_checksum_parse_hex(&base, svn_checksum_md5, base_checksum,
1585                                  pool));
1586   SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum,
1587                                  pool));
1588
1589   return svn_error_trace(root->vtable->apply_textdelta(contents_p,
1590                                                        contents_baton_p,
1591                                                        root,
1592                                                        path,
1593                                                        base,
1594                                                        result,
1595                                                        pool));
1596 }
1597
1598 svn_error_t *
1599 svn_fs_apply_text(svn_stream_t **contents_p, svn_fs_root_t *root,
1600                   const char *path, const char *result_checksum,
1601                   apr_pool_t *pool)
1602 {
1603   svn_checksum_t *result;
1604
1605   /* TODO: If we ever rev this API, we should make the supplied checksum an
1606      svn_checksum_t struct. */
1607   SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum,
1608                                  pool));
1609
1610   return svn_error_trace(root->vtable->apply_text(contents_p, root, path,
1611                                                   result, pool));
1612 }
1613
1614 svn_error_t *
1615 svn_fs_contents_different(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1616                           const char *path1, svn_fs_root_t *root2,
1617                           const char *path2, apr_pool_t *scratch_pool)
1618 {
1619   return svn_error_trace(root1->vtable->contents_changed(changed_p,
1620                                                          root1, path1,
1621                                                          root2, path2,
1622                                                          TRUE,
1623                                                          scratch_pool));
1624 }
1625
1626 svn_error_t *
1627 svn_fs_contents_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1628                         const char *path1, svn_fs_root_t *root2,
1629                         const char *path2, apr_pool_t *pool)
1630 {
1631   return svn_error_trace(root1->vtable->contents_changed(changed_p,
1632                                                          root1, path1,
1633                                                          root2, path2,
1634                                                          FALSE, pool));
1635 }
1636
1637 svn_error_t *
1638 svn_fs_youngest_rev(svn_revnum_t *youngest_p, svn_fs_t *fs, apr_pool_t *pool)
1639 {
1640   return svn_error_trace(fs->vtable->youngest_rev(youngest_p, fs, pool));
1641 }
1642
1643 svn_error_t *
1644 svn_fs_info_format(int *fs_format,
1645                    svn_version_t **supports_version,
1646                    svn_fs_t *fs,
1647                    apr_pool_t *result_pool,
1648                    apr_pool_t *scratch_pool)
1649 {
1650   return svn_error_trace(fs->vtable->info_format(fs_format, supports_version,
1651                                                  fs,
1652                                                  result_pool, scratch_pool));
1653 }
1654
1655 svn_error_t *
1656 svn_fs_info_config_files(apr_array_header_t **files,
1657                          svn_fs_t *fs,
1658                          apr_pool_t *result_pool,
1659                          apr_pool_t *scratch_pool)
1660 {
1661   return svn_error_trace(fs->vtable->info_config_files(files, fs,
1662                                                        result_pool,
1663                                                        scratch_pool));
1664 }
1665
1666 svn_error_t *
1667 svn_fs_deltify_revision(svn_fs_t *fs, svn_revnum_t revision, apr_pool_t *pool)
1668 {
1669   return svn_error_trace(fs->vtable->deltify(fs, revision, pool));
1670 }
1671
1672 svn_error_t *
1673 svn_fs_refresh_revision_props(svn_fs_t *fs,
1674                               apr_pool_t *scratch_pool)
1675 {
1676   return svn_error_trace(fs->vtable->refresh_revprops(fs, scratch_pool));
1677 }
1678
1679 svn_error_t *
1680 svn_fs_revision_prop2(svn_string_t **value_p,
1681                       svn_fs_t *fs,
1682                       svn_revnum_t rev,
1683                       const char *propname,
1684                       svn_boolean_t refresh,
1685                       apr_pool_t *result_pool,
1686                       apr_pool_t *scratch_pool)
1687 {
1688   return svn_error_trace(fs->vtable->revision_prop(value_p, fs, rev,
1689                                                    propname, refresh,
1690                                                    result_pool,
1691                                                    scratch_pool));
1692 }
1693
1694 svn_error_t *
1695 svn_fs_revision_proplist2(apr_hash_t **table_p,
1696                           svn_fs_t *fs,
1697                           svn_revnum_t rev,
1698                           svn_boolean_t refresh,
1699                           apr_pool_t *result_pool,
1700                           apr_pool_t *scratch_pool)
1701 {
1702   return svn_error_trace(fs->vtable->revision_proplist(table_p, fs, rev,
1703                                                        refresh,
1704                                                        result_pool,
1705                                                        scratch_pool));
1706 }
1707
1708 svn_error_t *
1709 svn_fs_change_rev_prop2(svn_fs_t *fs, svn_revnum_t rev, const char *name,
1710                         const svn_string_t *const *old_value_p,
1711                         const svn_string_t *value, apr_pool_t *pool)
1712 {
1713   return svn_error_trace(fs->vtable->change_rev_prop(fs, rev, name,
1714                                                      old_value_p,
1715                                                      value, pool));
1716 }
1717
1718 svn_error_t *
1719 svn_fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
1720                              svn_fs_root_t *source_root,
1721                              const char *source_path,
1722                              svn_fs_root_t *target_root,
1723                              const char *target_path, apr_pool_t *pool)
1724 {
1725   return svn_error_trace(target_root->vtable->get_file_delta_stream(
1726                            stream_p,
1727                            source_root, source_path,
1728                            target_root, target_path, pool));
1729 }
1730
1731 svn_error_t *
1732 svn_fs__get_deleted_node(svn_fs_root_t **node_root,
1733                          const char **node_path,
1734                          svn_fs_root_t *root,
1735                          const char *path,
1736                          apr_pool_t *result_pool,
1737                          apr_pool_t *scratch_pool)
1738 {
1739   const char *parent_path, *name;
1740   svn_fs_root_t *copy_root;
1741   const char *copy_path;
1742
1743   /* History traversal does not work with transaction roots.
1744    * Therefore, do it "by hand". */
1745
1746   /* If the parent got copied in ROOT, PATH got copied with it.
1747    * Otherwise, we will find the node at PATH in the revision prior to ROOT.
1748    */
1749   svn_fspath__split(&parent_path, &name, path, scratch_pool);
1750   SVN_ERR(svn_fs_closest_copy(&copy_root, &copy_path, root, parent_path,
1751                               scratch_pool));
1752
1753   /* Copied in ROOT? */
1754   if (   copy_root
1755       && (   svn_fs_revision_root_revision(copy_root)
1756           == svn_fs_revision_root_revision(root)))
1757     {
1758       svn_revnum_t copyfrom_rev;
1759       const char *copyfrom_path;
1760       const char *rel_path;
1761       SVN_ERR(svn_fs_copied_from(&copyfrom_rev, &copyfrom_path,
1762                                  copy_root, copy_path, scratch_pool));
1763
1764       SVN_ERR(svn_fs_revision_root(node_root, svn_fs_root_fs(root),
1765                                    copyfrom_rev, result_pool));
1766       rel_path = svn_fspath__skip_ancestor(copy_path, path);
1767       *node_path = svn_fspath__join(copyfrom_path, rel_path, result_pool);
1768     }
1769   else
1770     {
1771       svn_revnum_t revision;
1772       svn_revnum_t previous_rev;
1773
1774       /* Determine the latest revision before ROOT. */
1775       revision = svn_fs_revision_root_revision(root);
1776       if (SVN_IS_VALID_REVNUM(revision))
1777         previous_rev = revision - 1;
1778       else
1779         previous_rev = svn_fs_txn_root_base_revision(root);
1780
1781       SVN_ERR(svn_fs_revision_root(node_root, svn_fs_root_fs(root),
1782                                    previous_rev, result_pool));
1783       *node_path = apr_pstrdup(result_pool, path);
1784     }
1785
1786   return SVN_NO_ERROR;
1787 }
1788
1789 svn_error_t *
1790 svn_fs_get_uuid(svn_fs_t *fs, const char **uuid, apr_pool_t *pool)
1791 {
1792   /* If you change this, consider changing svn_fs__identifier(). */
1793   *uuid = apr_pstrdup(pool, fs->uuid);
1794   return SVN_NO_ERROR;
1795 }
1796
1797 svn_error_t *
1798 svn_fs_set_uuid(svn_fs_t *fs, const char *uuid, apr_pool_t *pool)
1799 {
1800   if (! uuid)
1801     {
1802       uuid = svn_uuid_generate(pool);
1803     }
1804   else
1805     {
1806       apr_uuid_t parsed_uuid;
1807       apr_status_t apr_err = apr_uuid_parse(&parsed_uuid, uuid);
1808       if (apr_err)
1809         return svn_error_createf(SVN_ERR_BAD_UUID, NULL,
1810                                  _("Malformed UUID '%s'"), uuid);
1811     }
1812   return svn_error_trace(fs->vtable->set_uuid(fs, uuid, pool));
1813 }
1814
1815 svn_error_t *
1816 svn_fs_lock_many(svn_fs_t *fs,
1817                  apr_hash_t *targets,
1818                  const char *comment,
1819                  svn_boolean_t is_dav_comment,
1820                  apr_time_t expiration_date,
1821                  svn_boolean_t steal_lock,
1822                  svn_fs_lock_callback_t lock_callback,
1823                  void *lock_baton,
1824                  apr_pool_t *result_pool,
1825                  apr_pool_t *scratch_pool)
1826 {
1827   apr_hash_index_t *hi;
1828   apr_hash_t *ok_targets = apr_hash_make(scratch_pool);
1829   svn_error_t *err, *cb_err = SVN_NO_ERROR;
1830
1831   /* Enforce that the comment be xml-escapable. */
1832   if (comment)
1833     if (! svn_xml_is_xml_safe(comment, strlen(comment)))
1834       return svn_error_create(SVN_ERR_XML_UNESCAPABLE_DATA, NULL,
1835                               _("Lock comment contains illegal characters"));
1836
1837   if (expiration_date < 0)
1838     return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
1839               _("Negative expiration date passed to svn_fs_lock"));
1840
1841   /* Enforce that the token be an XML-safe URI. */
1842   for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi))
1843     {
1844       const svn_fs_lock_target_t *target = apr_hash_this_val(hi);
1845
1846       err = SVN_NO_ERROR;
1847       if (target->token)
1848         {
1849           const char *c;
1850
1851
1852           if (strncmp(target->token, "opaquelocktoken:", 16))
1853             err = svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1854                                     _("Lock token URI '%s' has bad scheme; "
1855                                       "expected '%s'"),
1856                                     target->token, "opaquelocktoken");
1857
1858           if (!err)
1859             {
1860               for (c = target->token; *c && !err; c++)
1861                 if (! svn_ctype_isascii(*c) || svn_ctype_iscntrl(*c))
1862                   err = svn_error_createf(
1863                           SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1864                           _("Lock token '%s' is not ASCII or is a "
1865                             "control character at byte %u"),
1866                           target->token,
1867                           (unsigned)(c - target->token));
1868
1869               /* strlen(token) == c - token. */
1870               if (!err && !svn_xml_is_xml_safe(target->token,
1871                                                c - target->token))
1872                 err = svn_error_createf(
1873                             SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1874                             _("Lock token URI '%s' is not XML-safe"),
1875                             target->token);
1876             }
1877         }
1878
1879       if (err)
1880         {
1881           if (!cb_err && lock_callback)
1882             cb_err = lock_callback(lock_baton, apr_hash_this_key(hi),
1883                                    NULL, err, scratch_pool);
1884           svn_error_clear(err);
1885         }
1886       else
1887         svn_hash_sets(ok_targets, apr_hash_this_key(hi), target);
1888     }
1889
1890   if (!apr_hash_count(ok_targets))
1891     return svn_error_trace(cb_err);
1892
1893   err = fs->vtable->lock(fs, ok_targets, comment, is_dav_comment,
1894                          expiration_date, steal_lock,
1895                          lock_callback, lock_baton,
1896                          result_pool, scratch_pool);
1897
1898   if (err && cb_err)
1899     svn_error_compose(err, cb_err);
1900   else if (!err)
1901     err = cb_err;
1902
1903   return svn_error_trace(err);
1904 }
1905
1906 struct lock_baton_t {
1907   const svn_lock_t *lock;
1908   svn_error_t *fs_err;
1909 };
1910
1911 /* Implements svn_fs_lock_callback_t. Used by svn_fs_lock and
1912    svn_fs_unlock to record the lock and error from svn_fs_lock_many
1913    and svn_fs_unlock_many. */
1914 static svn_error_t *
1915 lock_cb(void *lock_baton,
1916         const char *path,
1917         const svn_lock_t *lock,
1918         svn_error_t *fs_err,
1919         apr_pool_t *pool)
1920 {
1921   struct lock_baton_t *b = lock_baton;
1922
1923   b->lock = lock;
1924   b->fs_err = svn_error_dup(fs_err);
1925
1926   return SVN_NO_ERROR;
1927 }
1928
1929 svn_error_t *
1930 svn_fs_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path,
1931             const char *token, const char *comment,
1932             svn_boolean_t is_dav_comment, apr_time_t expiration_date,
1933             svn_revnum_t current_rev, svn_boolean_t steal_lock,
1934             apr_pool_t *pool)
1935 {
1936   apr_hash_t *targets = apr_hash_make(pool);
1937   svn_fs_lock_target_t target;
1938   svn_error_t *err;
1939   struct lock_baton_t baton = {0};
1940
1941   target.token = token;
1942   target.current_rev = current_rev;
1943   svn_hash_sets(targets, path, &target);
1944
1945   err = svn_fs_lock_many(fs, targets, comment, is_dav_comment,
1946                          expiration_date, steal_lock, lock_cb, &baton,
1947                          pool, pool);
1948
1949   if (baton.lock)
1950     *lock = (svn_lock_t*)baton.lock;
1951
1952   if (err && baton.fs_err)
1953     svn_error_compose(err, baton.fs_err);
1954   else if (!err)
1955     err = baton.fs_err;
1956
1957   return svn_error_trace(err);
1958 }
1959
1960 svn_error_t *
1961 svn_fs_generate_lock_token(const char **token, svn_fs_t *fs, apr_pool_t *pool)
1962 {
1963   return svn_error_trace(fs->vtable->generate_lock_token(token, fs, pool));
1964 }
1965
1966 svn_fs_lock_target_t *
1967 svn_fs_lock_target_create(const char *token,
1968                           svn_revnum_t current_rev,
1969                           apr_pool_t *result_pool)
1970 {
1971   svn_fs_lock_target_t *target = apr_palloc(result_pool, sizeof(*target));
1972
1973   target->token = token;
1974   target->current_rev = current_rev;
1975
1976   return target;
1977 }
1978
1979 void
1980 svn_fs_lock_target_set_token(svn_fs_lock_target_t *target,
1981                              const char *token)
1982 {
1983   target->token = token;
1984 }
1985
1986 svn_error_t *
1987 svn_fs_unlock_many(svn_fs_t *fs,
1988                    apr_hash_t *targets,
1989                    svn_boolean_t break_lock,
1990                    svn_fs_lock_callback_t lock_callback,
1991                    void *lock_baton,
1992                    apr_pool_t *result_pool,
1993                    apr_pool_t *scratch_pool)
1994 {
1995   return svn_error_trace(fs->vtable->unlock(fs, targets, break_lock,
1996                                             lock_callback, lock_baton,
1997                                             result_pool, scratch_pool));
1998 }
1999
2000 svn_error_t *
2001 svn_fs_unlock(svn_fs_t *fs, const char *path, const char *token,
2002               svn_boolean_t break_lock, apr_pool_t *pool)
2003 {
2004   apr_hash_t *targets = apr_hash_make(pool);
2005   svn_error_t *err;
2006   struct lock_baton_t baton = {0};
2007
2008   if (!token)
2009     token = "";
2010   svn_hash_sets(targets, path, token);
2011
2012   err = svn_fs_unlock_many(fs, targets, break_lock, lock_cb, &baton,
2013                            pool, pool);
2014
2015   if (err && baton.fs_err)
2016     svn_error_compose(err, baton.fs_err);
2017   else if (!err)
2018     err = baton.fs_err;
2019
2020   return svn_error_trace(err);
2021 }
2022
2023 svn_error_t *
2024 svn_fs_get_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path,
2025                 apr_pool_t *pool)
2026 {
2027   return svn_error_trace(fs->vtable->get_lock(lock, fs, path, pool));
2028 }
2029
2030 svn_error_t *
2031 svn_fs_get_locks2(svn_fs_t *fs, const char *path, svn_depth_t depth,
2032                   svn_fs_get_locks_callback_t get_locks_func,
2033                   void *get_locks_baton, apr_pool_t *pool)
2034 {
2035   SVN_ERR_ASSERT((depth == svn_depth_empty) ||
2036                  (depth == svn_depth_files) ||
2037                  (depth == svn_depth_immediates) ||
2038                  (depth == svn_depth_infinity));
2039   return svn_error_trace(fs->vtable->get_locks(fs, path, depth,
2040                                                get_locks_func,
2041                                                get_locks_baton, pool));
2042 }
2043
2044 \f
2045 /* --- History functions --- */
2046
2047 svn_error_t *
2048 svn_fs_history_prev2(svn_fs_history_t **prev_history_p,
2049                      svn_fs_history_t *history, svn_boolean_t cross_copies,
2050                      apr_pool_t *result_pool, apr_pool_t *scratch_pool)
2051 {
2052   return svn_error_trace(history->vtable->prev(prev_history_p, history,
2053                                                cross_copies, result_pool,
2054                                                scratch_pool));
2055 }
2056
2057 svn_error_t *
2058 svn_fs_history_location(const char **path, svn_revnum_t *revision,
2059                         svn_fs_history_t *history, apr_pool_t *pool)
2060 {
2061   return svn_error_trace(history->vtable->location(path, revision, history,
2062                                                    pool));
2063 }
2064
2065 \f
2066 /* --- Node-ID functions --- */
2067
2068 svn_fs_id_t *
2069 svn_fs_parse_id(const char *data, apr_size_t len, apr_pool_t *pool)
2070 {
2071   fs_library_vtable_t *vtable;
2072   svn_error_t *err;
2073
2074   err = get_library_vtable(&vtable, SVN_FS_TYPE_BDB, pool);
2075   if (err)
2076     {
2077       svn_error_clear(err);
2078       return NULL;
2079     }
2080   return vtable->parse_id(data, len, pool);
2081 }
2082
2083 svn_string_t *
2084 svn_fs_unparse_id(const svn_fs_id_t *id, apr_pool_t *pool)
2085 {
2086   return id->vtable->unparse(id, pool);
2087 }
2088
2089 svn_boolean_t
2090 svn_fs_check_related(const svn_fs_id_t *a, const svn_fs_id_t *b)
2091 {
2092   return (a->vtable->compare(a, b) != svn_fs_node_unrelated);
2093 }
2094
2095 int
2096 svn_fs_compare_ids(const svn_fs_id_t *a, const svn_fs_id_t *b)
2097 {
2098   switch (a->vtable->compare(a, b))
2099     {
2100     case svn_fs_node_unchanged:
2101       return 0;
2102     case svn_fs_node_common_ancestor:
2103       return 1;
2104     default:
2105       return -1;
2106     }
2107 }
2108
2109 svn_error_t *
2110 svn_fs_print_modules(svn_stringbuf_t *output,
2111                      apr_pool_t *pool)
2112 {
2113   struct fs_type_defn *defn = fs_modules;
2114   fs_library_vtable_t *vtable;
2115   apr_pool_t *iterpool = svn_pool_create(pool);
2116
2117   while (defn)
2118     {
2119       char *line;
2120       svn_error_t *err;
2121
2122       svn_pool_clear(iterpool);
2123
2124       err = get_library_vtable_direct(&vtable, defn, iterpool);
2125       if (err)
2126         {
2127           if (err->apr_err == SVN_ERR_FS_UNKNOWN_FS_TYPE)
2128             {
2129               svn_error_clear(err);
2130               defn = defn->next;
2131               continue;
2132             }
2133           else
2134             return err;
2135         }
2136
2137       line = apr_psprintf(iterpool, "* fs_%s : %s\n",
2138                           defn->fsap_name, vtable->get_description());
2139       svn_stringbuf_appendcstr(output, line);
2140       defn = defn->next;
2141     }
2142
2143   svn_pool_destroy(iterpool);
2144
2145   return SVN_NO_ERROR;
2146 }
2147
2148 svn_fs_path_change2_t *
2149 svn_fs_path_change2_create(const svn_fs_id_t *node_rev_id,
2150                            svn_fs_path_change_kind_t change_kind,
2151                            apr_pool_t *pool)
2152 {
2153   return svn_fs__path_change_create_internal(node_rev_id, change_kind, pool);
2154 }
2155
2156 svn_fs_path_change3_t *
2157 svn_fs_path_change3_create(svn_fs_path_change_kind_t change_kind,
2158                            apr_pool_t *result_pool)
2159 {
2160   return svn_fs__path_change_create_internal2(change_kind, result_pool);
2161 }
2162
2163 svn_fs_path_change3_t *
2164 svn_fs_path_change3_dup(svn_fs_path_change3_t *change,
2165                         apr_pool_t *result_pool)
2166 {
2167   svn_fs_path_change3_t *copy = apr_pmemdup(result_pool, change,
2168                                             sizeof(*copy));
2169
2170   copy->path.data = apr_pstrmemdup(result_pool, copy->path.data,
2171                                    copy->path.len);
2172   if (copy->copyfrom_path)
2173     copy->copyfrom_path = apr_pstrdup(result_pool, change->copyfrom_path);
2174
2175   return copy;
2176 }
2177
2178 /* Return the library version number. */
2179 const svn_version_t *
2180 svn_fs_version(void)
2181 {
2182   SVN_VERSION_BODY;
2183 }
2184
2185 \f
2186 /** info **/
2187 svn_error_t *
2188 svn_fs_info(const svn_fs_info_placeholder_t **info_p,
2189             svn_fs_t *fs,
2190             apr_pool_t *result_pool,
2191             apr_pool_t *scratch_pool)
2192 {
2193   if (fs->vtable->info_fsap)
2194     {
2195       SVN_ERR(fs->vtable->info_fsap((const void **)info_p, fs,
2196                                     result_pool, scratch_pool));
2197     }
2198   else
2199     {
2200       svn_fs_info_placeholder_t *info = apr_palloc(result_pool, sizeof(*info));
2201       /* ### Ask the disk(!), since svn_fs_t doesn't cache the answer. */
2202       SVN_ERR(svn_fs_type(&info->fs_type, fs->path, result_pool));
2203       *info_p = info;
2204     }
2205   return SVN_NO_ERROR;
2206 }
2207
2208 void *
2209 svn_fs_info_dup(const void *info_void,
2210                 apr_pool_t *result_pool,
2211                 apr_pool_t *scratch_pool)
2212 {
2213   const svn_fs_info_placeholder_t *info = info_void;
2214   fs_library_vtable_t *vtable;
2215
2216   SVN_ERR(get_library_vtable(&vtable, info->fs_type, scratch_pool));
2217
2218   if (vtable->info_fsap_dup)
2219     return vtable->info_fsap_dup(info_void, result_pool);
2220   else
2221     return apr_pmemdup(result_pool, info, sizeof(*info));
2222 }
2223
2224 svn_error_t *
2225 svn_fs_ioctl(svn_fs_t *fs,
2226              svn_fs_ioctl_code_t ctlcode,
2227              void *input,
2228              void **output_p,
2229              svn_cancel_func_t cancel_func,
2230              void *cancel_baton,
2231              apr_pool_t *result_pool,
2232              apr_pool_t *scratch_pool)
2233 {
2234   void *output;
2235
2236   if (fs)
2237     {
2238       if (!fs->vtable->ioctl)
2239         return svn_error_create(SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE, NULL, NULL);
2240
2241       SVN_ERR(fs->vtable->ioctl(fs, ctlcode, input, &output,
2242                                 cancel_func, cancel_baton,
2243                                 result_pool, scratch_pool));
2244     }
2245   else
2246     {
2247       fs_library_vtable_t *vtable;
2248
2249       SVN_ERR(get_library_vtable(&vtable, ctlcode.fs_type, scratch_pool));
2250
2251       if (!vtable->ioctl)
2252         return svn_error_create(SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE, NULL, NULL);
2253
2254       SVN_ERR(vtable->ioctl(ctlcode, input, &output,
2255                             cancel_func, cancel_baton,
2256                             result_pool, scratch_pool));
2257     }
2258
2259   if (output_p)
2260     *output_p = output;
2261   return SVN_NO_ERROR;
2262 }