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