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