1251881Speter/*
2251881Speter * fs_loader.c:  Front-end to the various FS back ends
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter#include <string.h>
26251881Speter#include <apr.h>
27299742Sdim#include <apr_atomic.h>
28251881Speter#include <apr_hash.h>
29251881Speter#include <apr_md5.h>
30251881Speter#include <apr_thread_mutex.h>
31251881Speter#include <apr_uuid.h>
32251881Speter#include <apr_strings.h>
33251881Speter
34299742Sdim#include "svn_private_config.h"
35251881Speter#include "svn_hash.h"
36251881Speter#include "svn_ctype.h"
37251881Speter#include "svn_types.h"
38251881Speter#include "svn_dso.h"
39251881Speter#include "svn_version.h"
40251881Speter#include "svn_fs.h"
41251881Speter#include "svn_path.h"
42251881Speter#include "svn_xml.h"
43251881Speter#include "svn_pools.h"
44251881Speter#include "svn_string.h"
45299742Sdim#include "svn_sorts.h"
46251881Speter
47299742Sdim#include "private/svn_atomic.h"
48251881Speter#include "private/svn_fs_private.h"
49251881Speter#include "private/svn_fs_util.h"
50251881Speter#include "private/svn_utf_private.h"
51251881Speter#include "private/svn_mutex.h"
52251881Speter#include "private/svn_subr_private.h"
53251881Speter
54251881Speter#include "fs-loader.h"
55251881Speter
56251881Speter/* This is defined by configure on platforms which use configure, but
57251881Speter   we need to define a fallback for Windows. */
58251881Speter#ifndef DEFAULT_FS_TYPE
59251881Speter#define DEFAULT_FS_TYPE "fsfs"
60251881Speter#endif
61251881Speter
62251881Speter#define FS_TYPE_FILENAME "fs-type"
63251881Speter
64251881Speter/* A pool common to all FS objects.  See the documentation on the
65251881Speter   open/create functions in fs-loader.h and for svn_fs_initialize(). */
66299742Sdimstatic apr_pool_t *common_pool = NULL;
67299742Sdimstatic svn_mutex__t *common_pool_lock = NULL;
68299742Sdimstatic svn_atomic_t common_pool_initialized = FALSE;
69251881Speter
70251881Speter
71251881Speter/* --- Utility functions for the loader --- */
72251881Speter
73251881Speterstruct fs_type_defn {
74251881Speter  const char *fs_type;
75251881Speter  const char *fsap_name;
76251881Speter  fs_init_func_t initfunc;
77299742Sdim  fs_library_vtable_t *vtable;
78251881Speter  struct fs_type_defn *next;
79251881Speter};
80251881Speter
81251881Speterstatic struct fs_type_defn base_defn =
82251881Speter  {
83251881Speter    SVN_FS_TYPE_BDB, "base",
84251881Speter#ifdef SVN_LIBSVN_FS_LINKS_FS_BASE
85251881Speter    svn_fs_base__init,
86251881Speter#else
87251881Speter    NULL,
88251881Speter#endif
89299742Sdim    NULL,
90262253Speter    NULL /* End of static list: this needs to be reset to NULL if the
91262253Speter            common_pool used when setting it has been cleared. */
92251881Speter  };
93251881Speter
94299742Sdimstatic struct fs_type_defn fsx_defn =
95299742Sdim  {
96299742Sdim    SVN_FS_TYPE_FSX, "x",
97299742Sdim#ifdef SVN_LIBSVN_FS_LINKS_FS_X
98299742Sdim    svn_fs_x__init,
99299742Sdim#else
100299742Sdim    NULL,
101299742Sdim#endif
102299742Sdim    NULL,
103299742Sdim    &base_defn
104299742Sdim  };
105299742Sdim
106251881Speterstatic struct fs_type_defn fsfs_defn =
107251881Speter  {
108251881Speter    SVN_FS_TYPE_FSFS, "fs",
109251881Speter#ifdef SVN_LIBSVN_FS_LINKS_FS_FS
110251881Speter    svn_fs_fs__init,
111251881Speter#else
112251881Speter    NULL,
113251881Speter#endif
114299742Sdim    NULL,
115299742Sdim    &fsx_defn
116251881Speter  };
117251881Speter
118251881Speterstatic struct fs_type_defn *fs_modules = &fsfs_defn;
119251881Speter
120251881Speter
121251881Speterstatic svn_error_t *
122251881Speterload_module(fs_init_func_t *initfunc, const char *name, apr_pool_t *pool)
123251881Speter{
124251881Speter  *initfunc = NULL;
125251881Speter
126251881Speter#if defined(SVN_USE_DSO) && APR_HAS_DSO
127251881Speter  {
128251881Speter    apr_dso_handle_t *dso;
129251881Speter    apr_dso_handle_sym_t symbol;
130251881Speter    const char *libname;
131251881Speter    const char *funcname;
132251881Speter    apr_status_t status;
133251881Speter    const char *p;
134251881Speter
135251881Speter    /* Demand a simple alphanumeric name so that the generated DSO
136251881Speter       name is sensible. */
137251881Speter    for (p = name; *p; ++p)
138251881Speter      if (!svn_ctype_isalnum(*p))
139251881Speter        return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
140251881Speter                                 _("Invalid name for FS type '%s'"),
141251881Speter                                 name);
142251881Speter
143251881Speter    libname = apr_psprintf(pool, "libsvn_fs_%s-%d.so.%d",
144251881Speter                           name, SVN_VER_MAJOR, SVN_SOVERSION);
145251881Speter    funcname = apr_psprintf(pool, "svn_fs_%s__init", name);
146251881Speter
147251881Speter    /* Find/load the specified library.  If we get an error, assume
148251881Speter       the library doesn't exist.  The library will be unloaded when
149251881Speter       pool is destroyed. */
150251881Speter    SVN_ERR(svn_dso_load(&dso, libname));
151251881Speter    if (! dso)
152251881Speter      return SVN_NO_ERROR;
153251881Speter
154251881Speter    /* find the initialization routine */
155251881Speter    status = apr_dso_sym(&symbol, dso, funcname);
156251881Speter    if (status)
157251881Speter      return svn_error_wrap_apr(status, _("'%s' does not define '%s()'"),
158251881Speter                                libname, funcname);
159251881Speter
160251881Speter    *initfunc = (fs_init_func_t) symbol;
161251881Speter  }
162251881Speter#endif /* APR_HAS_DSO */
163251881Speter
164251881Speter  return SVN_NO_ERROR;
165251881Speter}
166251881Speter
167251881Speter/* Fetch a library vtable by a pointer into the library definitions array. */
168251881Speterstatic svn_error_t *
169251881Speterget_library_vtable_direct(fs_library_vtable_t **vtable,
170299742Sdim                          struct fs_type_defn *fst,
171251881Speter                          apr_pool_t *pool)
172251881Speter{
173251881Speter  fs_init_func_t initfunc = NULL;
174251881Speter  const svn_version_t *my_version = svn_fs_version();
175251881Speter  const svn_version_t *fs_version;
176251881Speter
177299742Sdim  /* most times, we get lucky */
178299742Sdim  *vtable = apr_atomic_casptr((volatile void **)&fst->vtable, NULL, NULL);
179299742Sdim  if (*vtable)
180299742Sdim    return SVN_NO_ERROR;
181299742Sdim
182299742Sdim  /* o.k. the first access needs to actually load the module, find the
183299742Sdim     vtable and check for version compatibility. */
184251881Speter  initfunc = fst->initfunc;
185251881Speter  if (! initfunc)
186251881Speter    SVN_ERR(load_module(&initfunc, fst->fsap_name, pool));
187251881Speter
188251881Speter  if (! initfunc)
189251881Speter    return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
190251881Speter                             _("Failed to load module for FS type '%s'"),
191251881Speter                             fst->fs_type);
192251881Speter
193251881Speter  {
194251881Speter    /* Per our API compatibility rules, we cannot ensure that
195251881Speter       svn_fs_initialize is called by the application.  If not, we
196251881Speter       cannot create the common pool and lock in a thread-safe fashion,
197251881Speter       nor can we clean up the common pool if libsvn_fs is dynamically
198251881Speter       unloaded.  This function makes a best effort by creating the
199251881Speter       common pool as a child of the global pool; the window of failure
200251881Speter       due to thread collision is small. */
201299742Sdim    SVN_ERR(svn_fs_initialize(NULL));
202251881Speter
203251881Speter    /* Invoke the FS module's initfunc function with the common
204251881Speter       pool protected by a lock. */
205251881Speter    SVN_MUTEX__WITH_LOCK(common_pool_lock,
206251881Speter                         initfunc(my_version, vtable, common_pool));
207251881Speter  }
208251881Speter  fs_version = (*vtable)->get_version();
209251881Speter  if (!svn_ver_equal(my_version, fs_version))
210251881Speter    return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
211251881Speter                             _("Mismatched FS module version for '%s':"
212251881Speter                               " found %d.%d.%d%s,"
213251881Speter                               " expected %d.%d.%d%s"),
214251881Speter                             fst->fs_type,
215251881Speter                             my_version->major, my_version->minor,
216251881Speter                             my_version->patch, my_version->tag,
217251881Speter                             fs_version->major, fs_version->minor,
218251881Speter                             fs_version->patch, fs_version->tag);
219299742Sdim
220299742Sdim  /* the vtable will not change.  Remember it */
221299742Sdim  apr_atomic_casptr((volatile void **)&fst->vtable, *vtable, NULL);
222299742Sdim
223251881Speter  return SVN_NO_ERROR;
224251881Speter}
225251881Speter
226251881Speter#if defined(SVN_USE_DSO) && APR_HAS_DSO
227251881Speter/* Return *FST for the third party FS_TYPE */
228251881Speterstatic svn_error_t *
229251881Speterget_or_allocate_third(struct fs_type_defn **fst,
230251881Speter                      const char *fs_type)
231251881Speter{
232251881Speter  while (*fst)
233251881Speter    {
234251881Speter      if (strcmp(fs_type, (*fst)->fs_type) == 0)
235251881Speter        return SVN_NO_ERROR;
236251881Speter      fst = &(*fst)->next;
237251881Speter    }
238251881Speter
239251881Speter  *fst = apr_palloc(common_pool, sizeof(struct fs_type_defn));
240251881Speter  (*fst)->fs_type = apr_pstrdup(common_pool, fs_type);
241251881Speter  (*fst)->fsap_name = (*fst)->fs_type;
242251881Speter  (*fst)->initfunc = NULL;
243299742Sdim  (*fst)->vtable = NULL;
244251881Speter  (*fst)->next = NULL;
245251881Speter
246251881Speter  return SVN_NO_ERROR;
247251881Speter}
248251881Speter#endif
249251881Speter
250299742Sdim/* Fetch a library *VTABLE by FS_TYPE.
251299742Sdim   Use POOL for temporary allocations. */
252251881Speterstatic svn_error_t *
253251881Speterget_library_vtable(fs_library_vtable_t **vtable, const char *fs_type,
254251881Speter                   apr_pool_t *pool)
255251881Speter{
256299742Sdim  struct fs_type_defn **fst;
257251881Speter  svn_boolean_t known = FALSE;
258251881Speter
259299742Sdim  /* There are three FS module definitions known at compile time.  We
260251881Speter     want to check these without any locking overhead even when
261251881Speter     dynamic third party modules are enabled.  The third party modules
262251881Speter     cannot be checked until the lock is held.  */
263299742Sdim  for (fst = &fs_modules; *fst; fst = &(*fst)->next)
264251881Speter    {
265251881Speter      if (strcmp(fs_type, (*fst)->fs_type) == 0)
266299742Sdim        {
267299742Sdim          known = TRUE;
268299742Sdim          break;
269299742Sdim        }
270299742Sdim      else if (!(*fst)->next)
271299742Sdim        {
272299742Sdim          break;
273299742Sdim        }
274251881Speter    }
275251881Speter
276251881Speter#if defined(SVN_USE_DSO) && APR_HAS_DSO
277251881Speter  /* Third party FS modules that are unknown at compile time.
278251881Speter
279251881Speter     A third party FS is identified by the file fs-type containing a
280251881Speter     third party name, say "foo".  The loader will load the DSO with
281251881Speter     the name "libsvn_fs_foo" and use the entry point with the name
282251881Speter     "svn_fs_foo__init".
283251881Speter
284251881Speter     Note: the BDB and FSFS modules don't follow this naming scheme
285251881Speter     and this allows them to be used to test the third party loader.
286251881Speter     Change the content of fs-type to "base" in a BDB filesystem or to
287251881Speter     "fs" in an FSFS filesystem and they will be loaded as third party
288251881Speter     modules. */
289251881Speter  if (!known)
290251881Speter    {
291251881Speter      fst = &(*fst)->next;
292299742Sdim      /* Best-effort init, see get_library_vtable_direct. */
293299742Sdim      SVN_ERR(svn_fs_initialize(NULL));
294251881Speter      SVN_MUTEX__WITH_LOCK(common_pool_lock,
295251881Speter                           get_or_allocate_third(fst, fs_type));
296251881Speter      known = TRUE;
297251881Speter    }
298251881Speter#endif
299251881Speter  if (!known)
300251881Speter    return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
301251881Speter                             _("Unknown FS type '%s'"), fs_type);
302251881Speter  return get_library_vtable_direct(vtable, *fst, pool);
303251881Speter}
304251881Speter
305251881Spetersvn_error_t *
306251881Spetersvn_fs_type(const char **fs_type, const char *path, apr_pool_t *pool)
307251881Speter{
308251881Speter  const char *filename;
309251881Speter  char buf[128];
310251881Speter  svn_error_t *err;
311251881Speter  apr_file_t *file;
312251881Speter  apr_size_t len;
313251881Speter
314251881Speter  /* Read the fsap-name file to get the FSAP name, or assume the (old)
315251881Speter     default.  For old repositories I suppose we could check some
316251881Speter     other file, DB_CONFIG or strings say, but for now just check the
317251881Speter     directory exists. */
318251881Speter  filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool);
319251881Speter  err = svn_io_file_open(&file, filename, APR_READ|APR_BUFFERED, 0, pool);
320251881Speter  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
321251881Speter    {
322251881Speter      svn_node_kind_t kind;
323251881Speter      svn_error_t *err2 = svn_io_check_path(path, &kind, pool);
324251881Speter      if (err2)
325251881Speter        {
326251881Speter          svn_error_clear(err2);
327251881Speter          return err;
328251881Speter        }
329251881Speter      if (kind == svn_node_dir)
330251881Speter        {
331251881Speter          svn_error_clear(err);
332251881Speter          *fs_type = SVN_FS_TYPE_BDB;
333251881Speter          return SVN_NO_ERROR;
334251881Speter        }
335251881Speter      return err;
336251881Speter    }
337251881Speter  else if (err)
338251881Speter    return err;
339251881Speter
340251881Speter  len = sizeof(buf);
341251881Speter  SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
342251881Speter  SVN_ERR(svn_io_file_close(file, pool));
343251881Speter  *fs_type = apr_pstrdup(pool, buf);
344251881Speter
345251881Speter  return SVN_NO_ERROR;
346251881Speter}
347251881Speter
348251881Speter/* Fetch the library vtable for an existing FS. */
349251881Speterstatic svn_error_t *
350251881Speterfs_library_vtable(fs_library_vtable_t **vtable, const char *path,
351251881Speter                  apr_pool_t *pool)
352251881Speter{
353251881Speter  const char *fs_type;
354251881Speter
355251881Speter  SVN_ERR(svn_fs_type(&fs_type, path, pool));
356251881Speter
357251881Speter  /* Fetch the library vtable by name, now that we've chosen one. */
358299742Sdim  SVN_ERR(get_library_vtable(vtable, fs_type, pool));
359299742Sdim
360299742Sdim  return SVN_NO_ERROR;
361251881Speter}
362251881Speter
363251881Speterstatic svn_error_t *
364251881Speterwrite_fs_type(const char *path, const char *fs_type, apr_pool_t *pool)
365251881Speter{
366251881Speter  const char *filename;
367251881Speter  apr_file_t *file;
368251881Speter
369251881Speter  filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool);
370251881Speter  SVN_ERR(svn_io_file_open(&file, filename,
371251881Speter                           APR_WRITE|APR_CREATE|APR_TRUNCATE|APR_BUFFERED,
372251881Speter                           APR_OS_DEFAULT, pool));
373251881Speter  SVN_ERR(svn_io_file_write_full(file, fs_type, strlen(fs_type), NULL,
374251881Speter                                 pool));
375251881Speter  SVN_ERR(svn_io_file_write_full(file, "\n", 1, NULL, pool));
376251881Speter  return svn_error_trace(svn_io_file_close(file, pool));
377251881Speter}
378251881Speter
379251881Speter
380251881Speter/* --- Functions for operating on filesystems by pathname --- */
381251881Speter
382251881Speterstatic apr_status_t uninit(void *data)
383251881Speter{
384251881Speter  common_pool = NULL;
385299742Sdim  common_pool_lock = NULL;
386299742Sdim  common_pool_initialized = 0;
387299742Sdim
388251881Speter  return APR_SUCCESS;
389251881Speter}
390251881Speter
391299742Sdimstatic svn_error_t *
392299742Sdimsynchronized_initialize(void *baton, apr_pool_t *pool)
393251881Speter{
394251881Speter  common_pool = svn_pool_create(pool);
395262253Speter  base_defn.next = NULL;
396251881Speter  SVN_ERR(svn_mutex__init(&common_pool_lock, TRUE, common_pool));
397251881Speter
398251881Speter  /* ### This won't work if POOL is NULL and libsvn_fs is loaded as a DSO
399251881Speter     ### (via libsvn_ra_local say) since the global common_pool will live
400251881Speter     ### longer than the DSO, which gets unloaded when the pool used to
401251881Speter     ### load it is cleared, and so when the handler runs it will refer to
402251881Speter     ### a function that no longer exists.  libsvn_ra_local attempts to
403251881Speter     ### work around this by explicitly calling svn_fs_initialize. */
404251881Speter  apr_pool_cleanup_register(common_pool, NULL, uninit, apr_pool_cleanup_null);
405251881Speter  return SVN_NO_ERROR;
406251881Speter}
407251881Speter
408299742Sdimsvn_error_t *
409299742Sdimsvn_fs_initialize(apr_pool_t *pool)
410299742Sdim{
411299742Sdim#if defined(SVN_USE_DSO) && APR_HAS_DSO
412299742Sdim  /* Ensure that DSO subsystem is initialized early as possible if
413299742Sdim     we're going to use it. */
414299742Sdim  SVN_ERR(svn_dso_initialize2());
415299742Sdim#endif
416299742Sdim  /* Protect against multiple calls. */
417299742Sdim  return svn_error_trace(svn_atomic__init_once(&common_pool_initialized,
418299742Sdim                                               synchronized_initialize,
419299742Sdim                                               NULL, pool));
420299742Sdim}
421299742Sdim
422251881Speter/* A default warning handling function.  */
423251881Speterstatic void
424251881Speterdefault_warning_func(void *baton, svn_error_t *err)
425251881Speter{
426251881Speter  /* The one unforgiveable sin is to fail silently.  Dumping to stderr
427251881Speter     or /dev/tty is not acceptable default behavior for server
428299742Sdim     processes, since those may both be equivalent to /dev/null.
429299742Sdim
430299742Sdim     That said, be a good citizen and print something anyway, in case it goes
431299742Sdim     somewhere, and our caller hasn't overridden the abort() call.
432299742Sdim   */
433299742Sdim  if (svn_error_get_malfunction_handler()
434299742Sdim      == svn_error_abort_on_malfunction)
435299742Sdim    /* ### TODO: extend the malfunction API such that non-abort()ing consumers
436299742Sdim       ### also get the information on ERR. */
437299742Sdim    svn_handle_error2(err, stderr, FALSE /* fatal */, "svn: fs-loader: ");
438251881Speter  SVN_ERR_MALFUNCTION_NO_RETURN();
439251881Speter}
440251881Speter
441251881Spetersvn_error_t *
442251881Spetersvn_fs__path_valid(const char *path, apr_pool_t *pool)
443251881Speter{
444299742Sdim  char *c;
445299742Sdim
446251881Speter  /* UTF-8 encoded string without NULs. */
447251881Speter  if (! svn_utf__cstring_is_valid(path))
448251881Speter    {
449251881Speter      return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
450251881Speter                               _("Path '%s' is not in UTF-8"), path);
451251881Speter    }
452251881Speter
453251881Speter  /* No "." or ".." elements. */
454251881Speter  if (svn_path_is_backpath_present(path)
455251881Speter      || svn_path_is_dotpath_present(path))
456251881Speter    {
457251881Speter      return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
458251881Speter                               _("Path '%s' contains '.' or '..' element"),
459251881Speter                               path);
460251881Speter    }
461251881Speter
462299742Sdim  /* Raise an error if PATH contains a newline because svn:mergeinfo and
463299742Sdim     friends can't handle them.  Issue #4340 describes a similar problem
464299742Sdim     in the FSFS code itself.
465299742Sdim   */
466299742Sdim  c = strchr(path, '\n');
467299742Sdim  if (c)
468299742Sdim    {
469299742Sdim      return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
470299742Sdim               _("Invalid control character '0x%02x' in path '%s'"),
471299742Sdim               (unsigned char)*c, svn_path_illegal_path_escape(path, pool));
472299742Sdim    }
473299742Sdim
474251881Speter  /* That's good enough. */
475251881Speter  return SVN_NO_ERROR;
476251881Speter}
477251881Speter
478251881Speter/* Allocate svn_fs_t structure. */
479251881Speterstatic svn_fs_t *
480251881Speterfs_new(apr_hash_t *fs_config, apr_pool_t *pool)
481251881Speter{
482251881Speter  svn_fs_t *fs = apr_palloc(pool, sizeof(*fs));
483251881Speter  fs->pool = pool;
484251881Speter  fs->path = NULL;
485251881Speter  fs->warning = default_warning_func;
486251881Speter  fs->warning_baton = NULL;
487251881Speter  fs->config = fs_config;
488251881Speter  fs->access_ctx = NULL;
489251881Speter  fs->vtable = NULL;
490251881Speter  fs->fsap_data = NULL;
491251881Speter  fs->uuid = NULL;
492251881Speter  return fs;
493251881Speter}
494251881Speter
495251881Spetersvn_fs_t *
496251881Spetersvn_fs_new(apr_hash_t *fs_config, apr_pool_t *pool)
497251881Speter{
498251881Speter  return fs_new(fs_config, pool);
499251881Speter}
500251881Speter
501251881Spetervoid
502251881Spetersvn_fs_set_warning_func(svn_fs_t *fs, svn_fs_warning_callback_t warning,
503251881Speter                        void *warning_baton)
504251881Speter{
505251881Speter  fs->warning = warning;
506251881Speter  fs->warning_baton = warning_baton;
507251881Speter}
508251881Speter
509251881Spetersvn_error_t *
510251881Spetersvn_fs_create(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config,
511251881Speter              apr_pool_t *pool)
512251881Speter{
513251881Speter  fs_library_vtable_t *vtable;
514251881Speter
515251881Speter  const char *fs_type = svn_hash__get_cstring(fs_config,
516251881Speter                                              SVN_FS_CONFIG_FS_TYPE,
517251881Speter                                              DEFAULT_FS_TYPE);
518251881Speter  SVN_ERR(get_library_vtable(&vtable, fs_type, pool));
519251881Speter
520251881Speter  /* Create the FS directory and write out the fsap-name file. */
521251881Speter  SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, pool));
522251881Speter  SVN_ERR(write_fs_type(path, fs_type, pool));
523251881Speter
524251881Speter  /* Perform the actual creation. */
525251881Speter  *fs_p = fs_new(fs_config, pool);
526251881Speter
527299742Sdim  SVN_ERR(vtable->create(*fs_p, path, common_pool_lock, pool, common_pool));
528299742Sdim  SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open2));
529251881Speter
530251881Speter  return SVN_NO_ERROR;
531251881Speter}
532251881Speter
533251881Spetersvn_error_t *
534299742Sdimsvn_fs_open2(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config,
535299742Sdim             apr_pool_t *result_pool,
536299742Sdim             apr_pool_t *scratch_pool)
537251881Speter{
538251881Speter  fs_library_vtable_t *vtable;
539251881Speter
540299742Sdim  SVN_ERR(fs_library_vtable(&vtable, path, scratch_pool));
541299742Sdim  *fs_p = fs_new(fs_config, result_pool);
542299742Sdim  SVN_ERR(vtable->open_fs(*fs_p, path, common_pool_lock, scratch_pool,
543299742Sdim                          common_pool));
544299742Sdim  SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open2));
545251881Speter
546251881Speter  return SVN_NO_ERROR;
547251881Speter}
548251881Speter
549251881Spetersvn_error_t *
550299742Sdimsvn_fs_open(svn_fs_t **fs_p,
551299742Sdim            const char *path,
552299742Sdim            apr_hash_t *fs_config,
553299742Sdim            apr_pool_t *pool)
554251881Speter{
555299742Sdim  return svn_fs_open2(fs_p, path, fs_config, pool, pool);
556299742Sdim}
557299742Sdim
558299742Sdimsvn_error_t *
559299742Sdimsvn_fs_upgrade2(const char *path,
560299742Sdim                svn_fs_upgrade_notify_t notify_func,
561299742Sdim                void *notify_baton,
562299742Sdim                svn_cancel_func_t cancel_func,
563299742Sdim                void *cancel_baton,
564299742Sdim                apr_pool_t *scratch_pool)
565299742Sdim{
566251881Speter  fs_library_vtable_t *vtable;
567251881Speter  svn_fs_t *fs;
568251881Speter
569299742Sdim  SVN_ERR(fs_library_vtable(&vtable, path, scratch_pool));
570299742Sdim  fs = fs_new(NULL, scratch_pool);
571251881Speter
572299742Sdim  SVN_ERR(vtable->upgrade_fs(fs, path,
573299742Sdim                             notify_func, notify_baton,
574299742Sdim                             cancel_func, cancel_baton,
575299742Sdim                             common_pool_lock,
576299742Sdim                             scratch_pool, common_pool));
577251881Speter  return SVN_NO_ERROR;
578251881Speter}
579251881Speter
580299742Sdim/* A warning handling function that does not abort on errors,
581299742Sdim   but just lets them be returned normally.  */
582299742Sdimstatic void
583299742Sdimverify_fs_warning_func(void *baton, svn_error_t *err)
584299742Sdim{
585299742Sdim}
586299742Sdim
587251881Spetersvn_error_t *
588251881Spetersvn_fs_verify(const char *path,
589251881Speter              apr_hash_t *fs_config,
590251881Speter              svn_revnum_t start,
591251881Speter              svn_revnum_t end,
592251881Speter              svn_fs_progress_notify_func_t notify_func,
593251881Speter              void *notify_baton,
594251881Speter              svn_cancel_func_t cancel_func,
595251881Speter              void *cancel_baton,
596251881Speter              apr_pool_t *pool)
597251881Speter{
598251881Speter  fs_library_vtable_t *vtable;
599251881Speter  svn_fs_t *fs;
600251881Speter
601251881Speter  SVN_ERR(fs_library_vtable(&vtable, path, pool));
602251881Speter  fs = fs_new(fs_config, pool);
603299742Sdim  svn_fs_set_warning_func(fs, verify_fs_warning_func, NULL);
604251881Speter
605299742Sdim  SVN_ERR(vtable->verify_fs(fs, path, start, end,
606299742Sdim                            notify_func, notify_baton,
607299742Sdim                            cancel_func, cancel_baton,
608299742Sdim                            common_pool_lock,
609299742Sdim                            pool, common_pool));
610251881Speter  return SVN_NO_ERROR;
611251881Speter}
612251881Speter
613251881Speterconst char *
614251881Spetersvn_fs_path(svn_fs_t *fs, apr_pool_t *pool)
615251881Speter{
616251881Speter  return apr_pstrdup(pool, fs->path);
617251881Speter}
618251881Speter
619251881Speterapr_hash_t *
620251881Spetersvn_fs_config(svn_fs_t *fs, apr_pool_t *pool)
621251881Speter{
622251881Speter  if (fs->config)
623251881Speter    return apr_hash_copy(pool, fs->config);
624251881Speter
625251881Speter  return NULL;
626251881Speter}
627251881Speter
628251881Spetersvn_error_t *
629251881Spetersvn_fs_delete_fs(const char *path, apr_pool_t *pool)
630251881Speter{
631251881Speter  fs_library_vtable_t *vtable;
632251881Speter
633251881Speter  SVN_ERR(fs_library_vtable(&vtable, path, pool));
634251881Speter  return svn_error_trace(vtable->delete_fs(path, pool));
635251881Speter}
636251881Speter
637251881Spetersvn_error_t *
638299742Sdimsvn_fs_hotcopy3(const char *src_path, const char *dst_path,
639251881Speter                svn_boolean_t clean, svn_boolean_t incremental,
640299742Sdim                svn_fs_hotcopy_notify_t notify_func,
641299742Sdim                void *notify_baton,
642299742Sdim                svn_cancel_func_t cancel_func,
643299742Sdim                void *cancel_baton,
644251881Speter                apr_pool_t *scratch_pool)
645251881Speter{
646251881Speter  fs_library_vtable_t *vtable;
647251881Speter  const char *src_fs_type;
648251881Speter  svn_fs_t *src_fs;
649251881Speter  svn_fs_t *dst_fs;
650251881Speter  const char *dst_fs_type;
651251881Speter  svn_node_kind_t dst_kind;
652251881Speter
653251881Speter  if (strcmp(src_path, dst_path) == 0)
654251881Speter    return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
655251881Speter                             _("Hotcopy source and destination are equal"));
656251881Speter
657251881Speter  SVN_ERR(svn_fs_type(&src_fs_type, src_path, scratch_pool));
658251881Speter  SVN_ERR(get_library_vtable(&vtable, src_fs_type, scratch_pool));
659251881Speter  src_fs = fs_new(NULL, scratch_pool);
660251881Speter  dst_fs = fs_new(NULL, scratch_pool);
661251881Speter
662251881Speter  SVN_ERR(svn_io_check_path(dst_path, &dst_kind, scratch_pool));
663251881Speter  if (dst_kind == svn_node_file)
664251881Speter    return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
665251881Speter                             _("'%s' already exists and is a file"),
666251881Speter                             svn_dirent_local_style(dst_path,
667251881Speter                                                    scratch_pool));
668251881Speter  if (dst_kind == svn_node_unknown)
669251881Speter    return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
670251881Speter                             _("'%s' already exists and has an unknown "
671251881Speter                               "node kind"),
672251881Speter                             svn_dirent_local_style(dst_path,
673251881Speter                                                    scratch_pool));
674251881Speter  if (dst_kind == svn_node_dir)
675251881Speter    {
676251881Speter      svn_node_kind_t type_file_kind;
677251881Speter
678251881Speter      SVN_ERR(svn_io_check_path(svn_dirent_join(dst_path,
679251881Speter                                                FS_TYPE_FILENAME,
680251881Speter                                                scratch_pool),
681251881Speter                                &type_file_kind, scratch_pool));
682251881Speter      if (type_file_kind != svn_node_none)
683251881Speter        {
684251881Speter          SVN_ERR(svn_fs_type(&dst_fs_type, dst_path, scratch_pool));
685251881Speter          if (strcmp(src_fs_type, dst_fs_type) != 0)
686251881Speter            return svn_error_createf(
687251881Speter                     SVN_ERR_ILLEGAL_TARGET, NULL,
688251881Speter                     _("The filesystem type of the hotcopy source "
689251881Speter                       "('%s') does not match the filesystem "
690251881Speter                       "type of the hotcopy destination ('%s')"),
691251881Speter                     src_fs_type, dst_fs_type);
692251881Speter        }
693251881Speter    }
694251881Speter
695251881Speter  SVN_ERR(vtable->hotcopy(src_fs, dst_fs, src_path, dst_path, clean,
696299742Sdim                          incremental, notify_func, notify_baton,
697299742Sdim                          cancel_func, cancel_baton, common_pool_lock,
698299742Sdim                          scratch_pool, common_pool));
699251881Speter  return svn_error_trace(write_fs_type(dst_path, src_fs_type, scratch_pool));
700251881Speter}
701251881Speter
702251881Spetersvn_error_t *
703251881Spetersvn_fs_pack(const char *path,
704251881Speter            svn_fs_pack_notify_t notify_func,
705251881Speter            void *notify_baton,
706251881Speter            svn_cancel_func_t cancel_func,
707251881Speter            void *cancel_baton,
708251881Speter            apr_pool_t *pool)
709251881Speter{
710251881Speter  fs_library_vtable_t *vtable;
711251881Speter  svn_fs_t *fs;
712251881Speter
713251881Speter  SVN_ERR(fs_library_vtable(&vtable, path, pool));
714251881Speter  fs = fs_new(NULL, pool);
715251881Speter
716299742Sdim  SVN_ERR(vtable->pack_fs(fs, path, notify_func, notify_baton,
717299742Sdim                          cancel_func, cancel_baton, common_pool_lock,
718299742Sdim                          pool, common_pool));
719251881Speter  return SVN_NO_ERROR;
720251881Speter}
721251881Speter
722251881Spetersvn_error_t *
723251881Spetersvn_fs_recover(const char *path,
724251881Speter               svn_cancel_func_t cancel_func, void *cancel_baton,
725251881Speter               apr_pool_t *pool)
726251881Speter{
727251881Speter  fs_library_vtable_t *vtable;
728251881Speter  svn_fs_t *fs;
729251881Speter
730251881Speter  SVN_ERR(fs_library_vtable(&vtable, path, pool));
731251881Speter  fs = fs_new(NULL, pool);
732251881Speter
733299742Sdim  SVN_ERR(vtable->open_fs_for_recovery(fs, path, common_pool_lock,
734299742Sdim                                       pool, common_pool));
735251881Speter  return svn_error_trace(vtable->recover(fs, cancel_func, cancel_baton,
736251881Speter                                         pool));
737251881Speter}
738251881Speter
739251881Spetersvn_error_t *
740251881Spetersvn_fs_verify_root(svn_fs_root_t *root,
741251881Speter                   apr_pool_t *scratch_pool)
742251881Speter{
743251881Speter  svn_fs_t *fs = root->fs;
744251881Speter  SVN_ERR(fs->vtable->verify_root(root, scratch_pool));
745251881Speter
746251881Speter  return SVN_NO_ERROR;
747251881Speter}
748251881Speter
749251881Spetersvn_error_t *
750251881Spetersvn_fs_freeze(svn_fs_t *fs,
751251881Speter              svn_fs_freeze_func_t freeze_func,
752251881Speter              void *freeze_baton,
753251881Speter              apr_pool_t *pool)
754251881Speter{
755251881Speter  SVN_ERR(fs->vtable->freeze(fs, freeze_func, freeze_baton, pool));
756251881Speter
757251881Speter  return SVN_NO_ERROR;
758251881Speter}
759251881Speter
760251881Speter
761251881Speter/* --- Berkeley-specific functions --- */
762251881Speter
763251881Spetersvn_error_t *
764251881Spetersvn_fs_create_berkeley(svn_fs_t *fs, const char *path)
765251881Speter{
766251881Speter  fs_library_vtable_t *vtable;
767251881Speter
768251881Speter  SVN_ERR(get_library_vtable(&vtable, SVN_FS_TYPE_BDB, fs->pool));
769251881Speter
770251881Speter  /* Create the FS directory and write out the fsap-name file. */
771251881Speter  SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, fs->pool));
772251881Speter  SVN_ERR(write_fs_type(path, SVN_FS_TYPE_BDB, fs->pool));
773251881Speter
774251881Speter  /* Perform the actual creation. */
775299742Sdim  SVN_ERR(vtable->create(fs, path, common_pool_lock, fs->pool, common_pool));
776299742Sdim  SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open2));
777251881Speter
778251881Speter  return SVN_NO_ERROR;
779251881Speter}
780251881Speter
781251881Spetersvn_error_t *
782251881Spetersvn_fs_open_berkeley(svn_fs_t *fs, const char *path)
783251881Speter{
784251881Speter  fs_library_vtable_t *vtable;
785251881Speter
786251881Speter  SVN_ERR(fs_library_vtable(&vtable, path, fs->pool));
787299742Sdim  SVN_ERR(vtable->open_fs(fs, path, common_pool_lock, fs->pool, common_pool));
788299742Sdim  SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open2));
789251881Speter
790251881Speter  return SVN_NO_ERROR;
791251881Speter}
792251881Speter
793251881Speterconst char *
794251881Spetersvn_fs_berkeley_path(svn_fs_t *fs, apr_pool_t *pool)
795251881Speter{
796251881Speter  return svn_fs_path(fs, pool);
797251881Speter}
798251881Speter
799251881Spetersvn_error_t *
800251881Spetersvn_fs_delete_berkeley(const char *path, apr_pool_t *pool)
801251881Speter{
802251881Speter  return svn_error_trace(svn_fs_delete_fs(path, pool));
803251881Speter}
804251881Speter
805251881Spetersvn_error_t *
806251881Spetersvn_fs_hotcopy_berkeley(const char *src_path, const char *dest_path,
807251881Speter                        svn_boolean_t clean_logs, apr_pool_t *pool)
808251881Speter{
809299742Sdim  return svn_error_trace(svn_fs_hotcopy3(src_path, dest_path, clean_logs,
810299742Sdim                                         FALSE, NULL, NULL, NULL, NULL,
811299742Sdim                                         pool));
812251881Speter}
813251881Speter
814251881Spetersvn_error_t *
815251881Spetersvn_fs_berkeley_recover(const char *path, apr_pool_t *pool)
816251881Speter{
817251881Speter  return svn_error_trace(svn_fs_recover(path, NULL, NULL, pool));
818251881Speter}
819251881Speter
820251881Spetersvn_error_t *
821251881Spetersvn_fs_set_berkeley_errcall(svn_fs_t *fs,
822251881Speter                            void (*handler)(const char *errpfx, char *msg))
823251881Speter{
824251881Speter  return svn_error_trace(fs->vtable->bdb_set_errcall(fs, handler));
825251881Speter}
826251881Speter
827251881Spetersvn_error_t *
828251881Spetersvn_fs_berkeley_logfiles(apr_array_header_t **logfiles,
829251881Speter                         const char *path,
830251881Speter                         svn_boolean_t only_unused,
831251881Speter                         apr_pool_t *pool)
832251881Speter{
833251881Speter  fs_library_vtable_t *vtable;
834251881Speter
835251881Speter  SVN_ERR(fs_library_vtable(&vtable, path, pool));
836251881Speter  return svn_error_trace(vtable->bdb_logfiles(logfiles, path, only_unused,
837251881Speter                                              pool));
838251881Speter}
839251881Speter
840251881Speter
841251881Speter/* --- Transaction functions --- */
842251881Speter
843251881Spetersvn_error_t *
844251881Spetersvn_fs_begin_txn2(svn_fs_txn_t **txn_p, svn_fs_t *fs, svn_revnum_t rev,
845251881Speter                  apr_uint32_t flags, apr_pool_t *pool)
846251881Speter{
847251881Speter  return svn_error_trace(fs->vtable->begin_txn(txn_p, fs, rev, flags, pool));
848251881Speter}
849251881Speter
850251881Speter
851251881Spetersvn_error_t *
852251881Spetersvn_fs_commit_txn(const char **conflict_p, svn_revnum_t *new_rev,
853299742Sdim                   svn_fs_txn_t *txn, apr_pool_t *pool)
854251881Speter{
855251881Speter  svn_error_t *err;
856251881Speter
857251881Speter  *new_rev = SVN_INVALID_REVNUM;
858251881Speter  if (conflict_p)
859251881Speter    *conflict_p = NULL;
860251881Speter
861251881Speter  err = txn->vtable->commit(conflict_p, new_rev, txn, pool);
862251881Speter
863251881Speter#ifdef SVN_DEBUG
864251881Speter  /* Check postconditions. */
865251881Speter  if (conflict_p)
866251881Speter    {
867251881Speter      SVN_ERR_ASSERT_E(! (SVN_IS_VALID_REVNUM(*new_rev) && *conflict_p != NULL),
868251881Speter                       err);
869251881Speter      SVN_ERR_ASSERT_E((*conflict_p != NULL)
870251881Speter                       == (err && err->apr_err == SVN_ERR_FS_CONFLICT),
871251881Speter                       err);
872251881Speter    }
873251881Speter#endif
874251881Speter
875251881Speter  SVN_ERR(err);
876251881Speter
877251881Speter  return SVN_NO_ERROR;
878251881Speter}
879251881Speter
880251881Spetersvn_error_t *
881251881Spetersvn_fs_abort_txn(svn_fs_txn_t *txn, apr_pool_t *pool)
882251881Speter{
883251881Speter  return svn_error_trace(txn->vtable->abort(txn, pool));
884251881Speter}
885251881Speter
886251881Spetersvn_error_t *
887251881Spetersvn_fs_purge_txn(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
888251881Speter{
889251881Speter  return svn_error_trace(fs->vtable->purge_txn(fs, txn_id, pool));
890251881Speter}
891251881Speter
892251881Spetersvn_error_t *
893251881Spetersvn_fs_txn_name(const char **name_p, svn_fs_txn_t *txn, apr_pool_t *pool)
894251881Speter{
895251881Speter  *name_p = apr_pstrdup(pool, txn->id);
896251881Speter  return SVN_NO_ERROR;
897251881Speter}
898251881Speter
899251881Spetersvn_revnum_t
900251881Spetersvn_fs_txn_base_revision(svn_fs_txn_t *txn)
901251881Speter{
902251881Speter  return txn->base_rev;
903251881Speter}
904251881Speter
905251881Spetersvn_error_t *
906251881Spetersvn_fs_open_txn(svn_fs_txn_t **txn, svn_fs_t *fs, const char *name,
907251881Speter                apr_pool_t *pool)
908251881Speter{
909251881Speter  return svn_error_trace(fs->vtable->open_txn(txn, fs, name, pool));
910251881Speter}
911251881Speter
912251881Spetersvn_error_t *
913251881Spetersvn_fs_list_transactions(apr_array_header_t **names_p, svn_fs_t *fs,
914251881Speter                         apr_pool_t *pool)
915251881Speter{
916251881Speter  return svn_error_trace(fs->vtable->list_transactions(names_p, fs, pool));
917251881Speter}
918251881Speter
919299742Sdimstatic svn_boolean_t
920299742Sdimis_internal_txn_prop(const char *name)
921299742Sdim{
922299742Sdim  return strcmp(name, SVN_FS__PROP_TXN_CHECK_LOCKS) == 0 ||
923299742Sdim         strcmp(name, SVN_FS__PROP_TXN_CHECK_OOD) == 0 ||
924299742Sdim         strcmp(name, SVN_FS__PROP_TXN_CLIENT_DATE) == 0;
925299742Sdim}
926299742Sdim
927251881Spetersvn_error_t *
928251881Spetersvn_fs_txn_prop(svn_string_t **value_p, svn_fs_txn_t *txn,
929251881Speter                const char *propname, apr_pool_t *pool)
930251881Speter{
931299742Sdim  if (is_internal_txn_prop(propname))
932299742Sdim    {
933299742Sdim      *value_p = NULL;
934299742Sdim      return SVN_NO_ERROR;
935299742Sdim    }
936299742Sdim
937251881Speter  return svn_error_trace(txn->vtable->get_prop(value_p, txn, propname, pool));
938251881Speter}
939251881Speter
940251881Spetersvn_error_t *
941251881Spetersvn_fs_txn_proplist(apr_hash_t **table_p, svn_fs_txn_t *txn, apr_pool_t *pool)
942251881Speter{
943299742Sdim  SVN_ERR(txn->vtable->get_proplist(table_p, txn, pool));
944299742Sdim
945299742Sdim  /* Don't give away internal transaction properties. */
946299742Sdim  svn_hash_sets(*table_p, SVN_FS__PROP_TXN_CHECK_LOCKS, NULL);
947299742Sdim  svn_hash_sets(*table_p, SVN_FS__PROP_TXN_CHECK_OOD, NULL);
948299742Sdim  svn_hash_sets(*table_p, SVN_FS__PROP_TXN_CLIENT_DATE, NULL);
949299742Sdim
950299742Sdim  return SVN_NO_ERROR;
951251881Speter}
952251881Speter
953251881Spetersvn_error_t *
954251881Spetersvn_fs_change_txn_prop(svn_fs_txn_t *txn, const char *name,
955251881Speter                       const svn_string_t *value, apr_pool_t *pool)
956251881Speter{
957299742Sdim  if (is_internal_txn_prop(name))
958299742Sdim    return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
959299742Sdim                             _("Attempt to modify internal transaction "
960299742Sdim                               "property '%s'"), name);
961299742Sdim
962251881Speter  return svn_error_trace(txn->vtable->change_prop(txn, name, value, pool));
963251881Speter}
964251881Speter
965251881Spetersvn_error_t *
966251881Spetersvn_fs_change_txn_props(svn_fs_txn_t *txn, const apr_array_header_t *props,
967251881Speter                        apr_pool_t *pool)
968251881Speter{
969299742Sdim  int i;
970299742Sdim
971299742Sdim  for (i = 0; i < props->nelts; ++i)
972299742Sdim    {
973299742Sdim      svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
974299742Sdim
975299742Sdim      if (is_internal_txn_prop(prop->name))
976299742Sdim        return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
977299742Sdim                                 _("Attempt to modify internal transaction "
978299742Sdim                                   "property '%s'"), prop->name);
979299742Sdim    }
980299742Sdim
981251881Speter  return svn_error_trace(txn->vtable->change_props(txn, props, pool));
982251881Speter}
983251881Speter
984251881Speter
985251881Speter/* --- Root functions --- */
986251881Speter
987251881Spetersvn_error_t *
988251881Spetersvn_fs_revision_root(svn_fs_root_t **root_p, svn_fs_t *fs, svn_revnum_t rev,
989251881Speter                     apr_pool_t *pool)
990251881Speter{
991251881Speter  /* We create a subpool for each root object to allow us to implement
992251881Speter     svn_fs_close_root.  */
993251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
994251881Speter  return svn_error_trace(fs->vtable->revision_root(root_p, fs, rev, subpool));
995251881Speter}
996251881Speter
997251881Spetersvn_error_t *
998251881Spetersvn_fs_txn_root(svn_fs_root_t **root_p, svn_fs_txn_t *txn, apr_pool_t *pool)
999251881Speter{
1000251881Speter  /* We create a subpool for each root object to allow us to implement
1001251881Speter     svn_fs_close_root.  */
1002251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
1003251881Speter  return svn_error_trace(txn->vtable->root(root_p, txn, subpool));
1004251881Speter}
1005251881Speter
1006251881Spetervoid
1007251881Spetersvn_fs_close_root(svn_fs_root_t *root)
1008251881Speter{
1009251881Speter  svn_pool_destroy(root->pool);
1010251881Speter}
1011251881Speter
1012251881Spetersvn_fs_t *
1013251881Spetersvn_fs_root_fs(svn_fs_root_t *root)
1014251881Speter{
1015251881Speter  return root->fs;
1016251881Speter}
1017251881Speter
1018251881Spetersvn_boolean_t
1019251881Spetersvn_fs_is_txn_root(svn_fs_root_t *root)
1020251881Speter{
1021251881Speter  return root->is_txn_root;
1022251881Speter}
1023251881Speter
1024251881Spetersvn_boolean_t
1025251881Spetersvn_fs_is_revision_root(svn_fs_root_t *root)
1026251881Speter{
1027251881Speter  return !root->is_txn_root;
1028251881Speter}
1029251881Speter
1030251881Speterconst char *
1031251881Spetersvn_fs_txn_root_name(svn_fs_root_t *root, apr_pool_t *pool)
1032251881Speter{
1033251881Speter  return root->is_txn_root ? apr_pstrdup(pool, root->txn) : NULL;
1034251881Speter}
1035251881Speter
1036251881Spetersvn_revnum_t
1037251881Spetersvn_fs_txn_root_base_revision(svn_fs_root_t *root)
1038251881Speter{
1039251881Speter  return root->is_txn_root ? root->rev : SVN_INVALID_REVNUM;
1040251881Speter}
1041251881Speter
1042251881Spetersvn_revnum_t
1043251881Spetersvn_fs_revision_root_revision(svn_fs_root_t *root)
1044251881Speter{
1045251881Speter  return root->is_txn_root ? SVN_INVALID_REVNUM : root->rev;
1046251881Speter}
1047251881Speter
1048251881Spetersvn_error_t *
1049299742Sdimsvn_fs_paths_changed2(apr_hash_t **changed_paths_p,
1050299742Sdim                      svn_fs_root_t *root,
1051251881Speter                      apr_pool_t *pool)
1052251881Speter{
1053251881Speter  return root->vtable->paths_changed(changed_paths_p, root, pool);
1054251881Speter}
1055251881Speter
1056251881Spetersvn_error_t *
1057251881Spetersvn_fs_paths_changed(apr_hash_t **changed_paths_p, svn_fs_root_t *root,
1058251881Speter                     apr_pool_t *pool)
1059251881Speter{
1060251881Speter  apr_hash_t *changed_paths_new_structs;
1061251881Speter  apr_hash_index_t *hi;
1062251881Speter
1063251881Speter  SVN_ERR(svn_fs_paths_changed2(&changed_paths_new_structs, root, pool));
1064251881Speter  *changed_paths_p = apr_hash_make(pool);
1065251881Speter  for (hi = apr_hash_first(pool, changed_paths_new_structs);
1066251881Speter       hi;
1067251881Speter       hi = apr_hash_next(hi))
1068251881Speter    {
1069251881Speter      const void *vkey;
1070251881Speter      apr_ssize_t klen;
1071251881Speter      void *vval;
1072251881Speter      svn_fs_path_change2_t *val;
1073251881Speter      svn_fs_path_change_t *change;
1074251881Speter      apr_hash_this(hi, &vkey, &klen, &vval);
1075251881Speter      val = vval;
1076251881Speter      change = apr_palloc(pool, sizeof(*change));
1077251881Speter      change->node_rev_id = val->node_rev_id;
1078251881Speter      change->change_kind = val->change_kind;
1079251881Speter      change->text_mod = val->text_mod;
1080251881Speter      change->prop_mod = val->prop_mod;
1081251881Speter      apr_hash_set(*changed_paths_p, vkey, klen, change);
1082251881Speter    }
1083251881Speter  return SVN_NO_ERROR;
1084251881Speter}
1085251881Speter
1086251881Spetersvn_error_t *
1087251881Spetersvn_fs_check_path(svn_node_kind_t *kind_p, svn_fs_root_t *root,
1088251881Speter                  const char *path, apr_pool_t *pool)
1089251881Speter{
1090251881Speter  return svn_error_trace(root->vtable->check_path(kind_p, root, path, pool));
1091251881Speter}
1092251881Speter
1093251881Spetersvn_error_t *
1094299742Sdimsvn_fs_node_history2(svn_fs_history_t **history_p, svn_fs_root_t *root,
1095299742Sdim                     const char *path, apr_pool_t *result_pool,
1096299742Sdim                     apr_pool_t *scratch_pool)
1097299742Sdim{
1098299742Sdim  return svn_error_trace(root->vtable->node_history(history_p, root, path,
1099299742Sdim                                                    result_pool,
1100299742Sdim                                                    scratch_pool));
1101299742Sdim}
1102299742Sdim
1103299742Sdimsvn_error_t *
1104251881Spetersvn_fs_node_history(svn_fs_history_t **history_p, svn_fs_root_t *root,
1105251881Speter                    const char *path, apr_pool_t *pool)
1106251881Speter{
1107251881Speter  return svn_error_trace(root->vtable->node_history(history_p, root, path,
1108299742Sdim                                                    pool, pool));
1109251881Speter}
1110251881Speter
1111251881Spetersvn_error_t *
1112251881Spetersvn_fs_is_dir(svn_boolean_t *is_dir, svn_fs_root_t *root, const char *path,
1113251881Speter              apr_pool_t *pool)
1114251881Speter{
1115251881Speter  svn_node_kind_t kind;
1116251881Speter
1117251881Speter  SVN_ERR(root->vtable->check_path(&kind, root, path, pool));
1118251881Speter  *is_dir = (kind == svn_node_dir);
1119251881Speter  return SVN_NO_ERROR;
1120251881Speter}
1121251881Speter
1122251881Spetersvn_error_t *
1123251881Spetersvn_fs_is_file(svn_boolean_t *is_file, svn_fs_root_t *root, const char *path,
1124251881Speter               apr_pool_t *pool)
1125251881Speter{
1126251881Speter  svn_node_kind_t kind;
1127251881Speter
1128251881Speter  SVN_ERR(root->vtable->check_path(&kind, root, path, pool));
1129251881Speter  *is_file = (kind == svn_node_file);
1130251881Speter  return SVN_NO_ERROR;
1131251881Speter}
1132251881Speter
1133251881Spetersvn_error_t *
1134251881Spetersvn_fs_node_id(const svn_fs_id_t **id_p, svn_fs_root_t *root,
1135251881Speter               const char *path, apr_pool_t *pool)
1136251881Speter{
1137251881Speter  return svn_error_trace(root->vtable->node_id(id_p, root, path, pool));
1138251881Speter}
1139251881Speter
1140251881Spetersvn_error_t *
1141299742Sdimsvn_fs_node_relation(svn_fs_node_relation_t *relation,
1142299742Sdim                     svn_fs_root_t *root_a, const char *path_a,
1143299742Sdim                     svn_fs_root_t *root_b, const char *path_b,
1144299742Sdim                     apr_pool_t *scratch_pool)
1145299742Sdim{
1146299742Sdim  /* Different repository types? */
1147299742Sdim  if (root_a->fs != root_b->fs)
1148299742Sdim    {
1149299742Sdim      *relation = svn_fs_node_unrelated;
1150299742Sdim      return SVN_NO_ERROR;
1151299742Sdim    }
1152299742Sdim
1153299742Sdim  return svn_error_trace(root_a->vtable->node_relation(relation,
1154299742Sdim                                                       root_a, path_a,
1155299742Sdim                                                       root_b, path_b,
1156299742Sdim                                                       scratch_pool));
1157299742Sdim}
1158299742Sdim
1159299742Sdimsvn_error_t *
1160251881Spetersvn_fs_node_created_rev(svn_revnum_t *revision, svn_fs_root_t *root,
1161251881Speter                        const char *path, apr_pool_t *pool)
1162251881Speter{
1163251881Speter  return svn_error_trace(root->vtable->node_created_rev(revision, root, path,
1164251881Speter                                                        pool));
1165251881Speter}
1166251881Speter
1167251881Spetersvn_error_t *
1168251881Spetersvn_fs_node_origin_rev(svn_revnum_t *revision, svn_fs_root_t *root,
1169251881Speter                       const char *path, apr_pool_t *pool)
1170251881Speter{
1171251881Speter  return svn_error_trace(root->vtable->node_origin_rev(revision, root, path,
1172251881Speter                                                       pool));
1173251881Speter}
1174251881Speter
1175251881Spetersvn_error_t *
1176251881Spetersvn_fs_node_created_path(const char **created_path, svn_fs_root_t *root,
1177251881Speter                         const char *path, apr_pool_t *pool)
1178251881Speter{
1179251881Speter  return svn_error_trace(root->vtable->node_created_path(created_path, root,
1180251881Speter                                                         path, pool));
1181251881Speter}
1182251881Speter
1183251881Spetersvn_error_t *
1184251881Spetersvn_fs_node_prop(svn_string_t **value_p, svn_fs_root_t *root,
1185251881Speter                 const char *path, const char *propname, apr_pool_t *pool)
1186251881Speter{
1187251881Speter  return svn_error_trace(root->vtable->node_prop(value_p, root, path,
1188251881Speter                                                 propname, pool));
1189251881Speter}
1190251881Speter
1191251881Spetersvn_error_t *
1192251881Spetersvn_fs_node_proplist(apr_hash_t **table_p, svn_fs_root_t *root,
1193251881Speter                     const char *path, apr_pool_t *pool)
1194251881Speter{
1195251881Speter  return svn_error_trace(root->vtable->node_proplist(table_p, root, path,
1196251881Speter                                                     pool));
1197251881Speter}
1198251881Speter
1199251881Spetersvn_error_t *
1200299742Sdimsvn_fs_node_has_props(svn_boolean_t *has_props,
1201299742Sdim                      svn_fs_root_t *root,
1202299742Sdim                      const char *path,
1203299742Sdim                      apr_pool_t *scratch_pool)
1204299742Sdim{
1205299742Sdim  return svn_error_trace(root->vtable->node_has_props(has_props, root, path,
1206299742Sdim                                                      scratch_pool));
1207299742Sdim}
1208299742Sdim
1209299742Sdimsvn_error_t *
1210251881Spetersvn_fs_change_node_prop(svn_fs_root_t *root, const char *path,
1211251881Speter                        const char *name, const svn_string_t *value,
1212251881Speter                        apr_pool_t *pool)
1213251881Speter{
1214251881Speter  return svn_error_trace(root->vtable->change_node_prop(root, path, name,
1215251881Speter                                                        value, pool));
1216251881Speter}
1217251881Speter
1218251881Spetersvn_error_t *
1219299742Sdimsvn_fs_props_different(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1220299742Sdim                       const char *path1, svn_fs_root_t *root2,
1221299742Sdim                       const char *path2, apr_pool_t *scratch_pool)
1222299742Sdim{
1223299742Sdim  return svn_error_trace(root1->vtable->props_changed(changed_p,
1224299742Sdim                                                      root1, path1,
1225299742Sdim                                                      root2, path2,
1226299742Sdim                                                      TRUE, scratch_pool));
1227299742Sdim}
1228299742Sdim
1229299742Sdimsvn_error_t *
1230251881Spetersvn_fs_props_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1231251881Speter                     const char *path1, svn_fs_root_t *root2,
1232251881Speter                     const char *path2, apr_pool_t *pool)
1233251881Speter{
1234251881Speter  return svn_error_trace(root1->vtable->props_changed(changed_p,
1235251881Speter                                                      root1, path1,
1236251881Speter                                                      root2, path2,
1237299742Sdim                                                      FALSE, pool));
1238251881Speter}
1239251881Speter
1240251881Spetersvn_error_t *
1241251881Spetersvn_fs_copied_from(svn_revnum_t *rev_p, const char **path_p,
1242251881Speter                   svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1243251881Speter{
1244251881Speter  return svn_error_trace(root->vtable->copied_from(rev_p, path_p, root, path,
1245251881Speter                                                   pool));
1246251881Speter}
1247251881Speter
1248251881Spetersvn_error_t *
1249251881Spetersvn_fs_closest_copy(svn_fs_root_t **root_p, const char **path_p,
1250251881Speter                    svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1251251881Speter{
1252251881Speter  return svn_error_trace(root->vtable->closest_copy(root_p, path_p,
1253251881Speter                                                    root, path, pool));
1254251881Speter}
1255251881Speter
1256251881Spetersvn_error_t *
1257251881Spetersvn_fs_get_mergeinfo2(svn_mergeinfo_catalog_t *catalog,
1258251881Speter                      svn_fs_root_t *root,
1259251881Speter                      const apr_array_header_t *paths,
1260251881Speter                      svn_mergeinfo_inheritance_t inherit,
1261251881Speter                      svn_boolean_t include_descendants,
1262251881Speter                      svn_boolean_t adjust_inherited_mergeinfo,
1263251881Speter                      apr_pool_t *result_pool,
1264251881Speter                      apr_pool_t *scratch_pool)
1265251881Speter{
1266251881Speter  return svn_error_trace(root->vtable->get_mergeinfo(
1267251881Speter    catalog, root, paths, inherit, include_descendants,
1268251881Speter    adjust_inherited_mergeinfo, result_pool, scratch_pool));
1269251881Speter}
1270251881Speter
1271251881Spetersvn_error_t *
1272251881Spetersvn_fs_get_mergeinfo(svn_mergeinfo_catalog_t *catalog,
1273251881Speter                     svn_fs_root_t *root,
1274251881Speter                     const apr_array_header_t *paths,
1275251881Speter                     svn_mergeinfo_inheritance_t inherit,
1276251881Speter                     svn_boolean_t include_descendants,
1277251881Speter                     apr_pool_t *pool)
1278251881Speter{
1279251881Speter  return svn_error_trace(root->vtable->get_mergeinfo(catalog, root, paths,
1280251881Speter                                                     inherit,
1281251881Speter                                                     include_descendants,
1282251881Speter                                                     TRUE, pool, pool));
1283251881Speter}
1284251881Speter
1285251881Spetersvn_error_t *
1286299742Sdimsvn_fs__get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo,
1287299742Sdim                               svn_fs_root_t *root,
1288299742Sdim                               const char *path,
1289299742Sdim                               svn_mergeinfo_inheritance_t inherit,
1290299742Sdim                               svn_boolean_t adjust_inherited_mergeinfo,
1291299742Sdim                               apr_pool_t *result_pool,
1292299742Sdim                               apr_pool_t *scratch_pool)
1293299742Sdim{
1294299742Sdim  apr_array_header_t *paths
1295299742Sdim    = apr_array_make(scratch_pool, 1, sizeof(const char *));
1296299742Sdim  svn_mergeinfo_catalog_t catalog;
1297299742Sdim
1298299742Sdim  APR_ARRAY_PUSH(paths, const char *) = path;
1299299742Sdim
1300299742Sdim  SVN_ERR(svn_fs_get_mergeinfo2(&catalog, root, paths,
1301299742Sdim                                inherit, FALSE /*include_descendants*/,
1302299742Sdim                                adjust_inherited_mergeinfo,
1303299742Sdim                                result_pool, scratch_pool));
1304299742Sdim  *mergeinfo = svn_hash_gets(catalog, path);
1305299742Sdim
1306299742Sdim  return SVN_NO_ERROR;
1307299742Sdim}
1308299742Sdim
1309299742Sdimsvn_error_t *
1310251881Spetersvn_fs_merge(const char **conflict_p, svn_fs_root_t *source_root,
1311251881Speter             const char *source_path, svn_fs_root_t *target_root,
1312251881Speter             const char *target_path, svn_fs_root_t *ancestor_root,
1313251881Speter             const char *ancestor_path, apr_pool_t *pool)
1314251881Speter{
1315251881Speter  return svn_error_trace(target_root->vtable->merge(conflict_p,
1316251881Speter                                                    source_root, source_path,
1317251881Speter                                                    target_root, target_path,
1318251881Speter                                                    ancestor_root,
1319251881Speter                                                    ancestor_path, pool));
1320251881Speter}
1321251881Speter
1322251881Spetersvn_error_t *
1323251881Spetersvn_fs_dir_entries(apr_hash_t **entries_p, svn_fs_root_t *root,
1324251881Speter                   const char *path, apr_pool_t *pool)
1325251881Speter{
1326251881Speter  return svn_error_trace(root->vtable->dir_entries(entries_p, root, path,
1327251881Speter                                                   pool));
1328251881Speter}
1329251881Speter
1330251881Spetersvn_error_t *
1331299742Sdimsvn_fs_dir_optimal_order(apr_array_header_t **ordered_p,
1332299742Sdim                         svn_fs_root_t *root,
1333299742Sdim                         apr_hash_t *entries,
1334299742Sdim                         apr_pool_t *result_pool,
1335299742Sdim                         apr_pool_t *scratch_pool)
1336299742Sdim{
1337299742Sdim  return svn_error_trace(root->vtable->dir_optimal_order(ordered_p, root,
1338299742Sdim                                                         entries,
1339299742Sdim                                                         result_pool,
1340299742Sdim                                                         scratch_pool));
1341299742Sdim}
1342299742Sdim
1343299742Sdimsvn_error_t *
1344251881Spetersvn_fs_make_dir(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1345251881Speter{
1346251881Speter  SVN_ERR(svn_fs__path_valid(path, pool));
1347251881Speter  return svn_error_trace(root->vtable->make_dir(root, path, pool));
1348251881Speter}
1349251881Speter
1350251881Spetersvn_error_t *
1351251881Spetersvn_fs_delete(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1352251881Speter{
1353251881Speter  return svn_error_trace(root->vtable->delete_node(root, path, pool));
1354251881Speter}
1355251881Speter
1356251881Spetersvn_error_t *
1357251881Spetersvn_fs_copy(svn_fs_root_t *from_root, const char *from_path,
1358251881Speter            svn_fs_root_t *to_root, const char *to_path, apr_pool_t *pool)
1359251881Speter{
1360251881Speter  SVN_ERR(svn_fs__path_valid(to_path, pool));
1361251881Speter  return svn_error_trace(to_root->vtable->copy(from_root, from_path,
1362251881Speter                                               to_root, to_path, pool));
1363251881Speter}
1364251881Speter
1365251881Spetersvn_error_t *
1366251881Spetersvn_fs_revision_link(svn_fs_root_t *from_root, svn_fs_root_t *to_root,
1367251881Speter                     const char *path, apr_pool_t *pool)
1368251881Speter{
1369251881Speter  return svn_error_trace(to_root->vtable->revision_link(from_root, to_root,
1370251881Speter                                                        path, pool));
1371251881Speter}
1372251881Speter
1373251881Spetersvn_error_t *
1374251881Spetersvn_fs_file_length(svn_filesize_t *length_p, svn_fs_root_t *root,
1375251881Speter                   const char *path, apr_pool_t *pool)
1376251881Speter{
1377251881Speter  return svn_error_trace(root->vtable->file_length(length_p, root, path,
1378251881Speter                                                   pool));
1379251881Speter}
1380251881Speter
1381251881Spetersvn_error_t *
1382251881Spetersvn_fs_file_checksum(svn_checksum_t **checksum,
1383251881Speter                     svn_checksum_kind_t kind,
1384251881Speter                     svn_fs_root_t *root,
1385251881Speter                     const char *path,
1386251881Speter                     svn_boolean_t force,
1387251881Speter                     apr_pool_t *pool)
1388251881Speter{
1389251881Speter  SVN_ERR(root->vtable->file_checksum(checksum, kind, root, path, pool));
1390251881Speter
1391251881Speter  if (force && (*checksum == NULL || (*checksum)->kind != kind))
1392251881Speter    {
1393251881Speter      svn_stream_t *contents, *checksum_contents;
1394251881Speter
1395251881Speter      SVN_ERR(svn_fs_file_contents(&contents, root, path, pool));
1396251881Speter      checksum_contents = svn_stream_checksummed2(contents, checksum, NULL,
1397251881Speter                                                  kind, TRUE, pool);
1398251881Speter
1399251881Speter      /* This will force a read of any remaining data (which is all of it in
1400251881Speter         this case) and dump the checksum into checksum->digest. */
1401251881Speter      SVN_ERR(svn_stream_close(checksum_contents));
1402251881Speter    }
1403251881Speter
1404251881Speter  return SVN_NO_ERROR;
1405251881Speter}
1406251881Speter
1407251881Spetersvn_error_t *
1408251881Spetersvn_fs_file_md5_checksum(unsigned char digest[],
1409251881Speter                         svn_fs_root_t *root,
1410251881Speter                         const char *path,
1411251881Speter                         apr_pool_t *pool)
1412251881Speter{
1413251881Speter  svn_checksum_t *md5sum;
1414251881Speter
1415251881Speter  SVN_ERR(svn_fs_file_checksum(&md5sum, svn_checksum_md5, root, path, TRUE,
1416251881Speter                               pool));
1417251881Speter  memcpy(digest, md5sum->digest, APR_MD5_DIGESTSIZE);
1418251881Speter
1419251881Speter  return SVN_NO_ERROR;
1420251881Speter}
1421251881Speter
1422251881Spetersvn_error_t *
1423251881Spetersvn_fs_file_contents(svn_stream_t **contents, svn_fs_root_t *root,
1424251881Speter                     const char *path, apr_pool_t *pool)
1425251881Speter{
1426251881Speter  return svn_error_trace(root->vtable->file_contents(contents, root, path,
1427251881Speter                                                     pool));
1428251881Speter}
1429251881Speter
1430251881Spetersvn_error_t *
1431251881Spetersvn_fs_try_process_file_contents(svn_boolean_t *success,
1432251881Speter                                 svn_fs_root_t *root,
1433251881Speter                                 const char *path,
1434251881Speter                                 svn_fs_process_contents_func_t processor,
1435251881Speter                                 void* baton,
1436251881Speter                                 apr_pool_t *pool)
1437251881Speter{
1438251881Speter  /* if the FS doesn't implement this function, report a "failed" attempt */
1439251881Speter  if (root->vtable->try_process_file_contents == NULL)
1440251881Speter    {
1441251881Speter      *success = FALSE;
1442251881Speter      return SVN_NO_ERROR;
1443251881Speter    }
1444251881Speter
1445251881Speter  return svn_error_trace(root->vtable->try_process_file_contents(
1446251881Speter                         success,
1447251881Speter                         root, path,
1448251881Speter                         processor, baton, pool));
1449251881Speter}
1450251881Speter
1451251881Spetersvn_error_t *
1452251881Spetersvn_fs_make_file(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1453251881Speter{
1454251881Speter  SVN_ERR(svn_fs__path_valid(path, pool));
1455251881Speter  return svn_error_trace(root->vtable->make_file(root, path, pool));
1456251881Speter}
1457251881Speter
1458251881Spetersvn_error_t *
1459251881Spetersvn_fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
1460251881Speter                       void **contents_baton_p, svn_fs_root_t *root,
1461251881Speter                       const char *path, const char *base_checksum,
1462251881Speter                       const char *result_checksum, apr_pool_t *pool)
1463251881Speter{
1464251881Speter  svn_checksum_t *base, *result;
1465251881Speter
1466251881Speter  /* TODO: If we ever rev this API, we should make the supplied checksums
1467251881Speter     svn_checksum_t structs. */
1468251881Speter  SVN_ERR(svn_checksum_parse_hex(&base, svn_checksum_md5, base_checksum,
1469251881Speter                                 pool));
1470251881Speter  SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum,
1471251881Speter                                 pool));
1472251881Speter
1473251881Speter  return svn_error_trace(root->vtable->apply_textdelta(contents_p,
1474251881Speter                                                       contents_baton_p,
1475251881Speter                                                       root,
1476251881Speter                                                       path,
1477251881Speter                                                       base,
1478251881Speter                                                       result,
1479251881Speter                                                       pool));
1480251881Speter}
1481251881Speter
1482251881Spetersvn_error_t *
1483251881Spetersvn_fs_apply_text(svn_stream_t **contents_p, svn_fs_root_t *root,
1484251881Speter                  const char *path, const char *result_checksum,
1485251881Speter                  apr_pool_t *pool)
1486251881Speter{
1487251881Speter  svn_checksum_t *result;
1488251881Speter
1489251881Speter  /* TODO: If we ever rev this API, we should make the supplied checksum an
1490251881Speter     svn_checksum_t struct. */
1491251881Speter  SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum,
1492251881Speter                                 pool));
1493251881Speter
1494251881Speter  return svn_error_trace(root->vtable->apply_text(contents_p, root, path,
1495251881Speter                                                  result, pool));
1496251881Speter}
1497251881Speter
1498251881Spetersvn_error_t *
1499299742Sdimsvn_fs_contents_different(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1500299742Sdim                          const char *path1, svn_fs_root_t *root2,
1501299742Sdim                          const char *path2, apr_pool_t *scratch_pool)
1502299742Sdim{
1503299742Sdim  return svn_error_trace(root1->vtable->contents_changed(changed_p,
1504299742Sdim                                                         root1, path1,
1505299742Sdim                                                         root2, path2,
1506299742Sdim                                                         TRUE,
1507299742Sdim                                                         scratch_pool));
1508299742Sdim}
1509299742Sdim
1510299742Sdimsvn_error_t *
1511251881Spetersvn_fs_contents_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1512251881Speter                        const char *path1, svn_fs_root_t *root2,
1513251881Speter                        const char *path2, apr_pool_t *pool)
1514251881Speter{
1515251881Speter  return svn_error_trace(root1->vtable->contents_changed(changed_p,
1516251881Speter                                                         root1, path1,
1517251881Speter                                                         root2, path2,
1518299742Sdim                                                         FALSE, pool));
1519251881Speter}
1520251881Speter
1521251881Spetersvn_error_t *
1522251881Spetersvn_fs_youngest_rev(svn_revnum_t *youngest_p, svn_fs_t *fs, apr_pool_t *pool)
1523251881Speter{
1524251881Speter  return svn_error_trace(fs->vtable->youngest_rev(youngest_p, fs, pool));
1525251881Speter}
1526251881Speter
1527251881Spetersvn_error_t *
1528299742Sdimsvn_fs_info_format(int *fs_format,
1529299742Sdim                   svn_version_t **supports_version,
1530299742Sdim                   svn_fs_t *fs,
1531299742Sdim                   apr_pool_t *result_pool,
1532299742Sdim                   apr_pool_t *scratch_pool)
1533299742Sdim{
1534299742Sdim  return svn_error_trace(fs->vtable->info_format(fs_format, supports_version,
1535299742Sdim                                                 fs,
1536299742Sdim                                                 result_pool, scratch_pool));
1537299742Sdim}
1538299742Sdim
1539299742Sdimsvn_error_t *
1540299742Sdimsvn_fs_info_config_files(apr_array_header_t **files,
1541299742Sdim                         svn_fs_t *fs,
1542299742Sdim                         apr_pool_t *result_pool,
1543299742Sdim                         apr_pool_t *scratch_pool)
1544299742Sdim{
1545299742Sdim  return svn_error_trace(fs->vtable->info_config_files(files, fs,
1546299742Sdim                                                       result_pool,
1547299742Sdim                                                       scratch_pool));
1548299742Sdim}
1549299742Sdim
1550299742Sdimsvn_error_t *
1551251881Spetersvn_fs_deltify_revision(svn_fs_t *fs, svn_revnum_t revision, apr_pool_t *pool)
1552251881Speter{
1553251881Speter  return svn_error_trace(fs->vtable->deltify(fs, revision, pool));
1554251881Speter}
1555251881Speter
1556251881Spetersvn_error_t *
1557251881Spetersvn_fs_revision_prop(svn_string_t **value_p, svn_fs_t *fs, svn_revnum_t rev,
1558251881Speter                     const char *propname, apr_pool_t *pool)
1559251881Speter{
1560251881Speter  return svn_error_trace(fs->vtable->revision_prop(value_p, fs, rev,
1561251881Speter                                                   propname, pool));
1562251881Speter}
1563251881Speter
1564251881Spetersvn_error_t *
1565251881Spetersvn_fs_revision_proplist(apr_hash_t **table_p, svn_fs_t *fs, svn_revnum_t rev,
1566251881Speter                         apr_pool_t *pool)
1567251881Speter{
1568251881Speter  return svn_error_trace(fs->vtable->revision_proplist(table_p, fs, rev,
1569251881Speter                                                       pool));
1570251881Speter}
1571251881Speter
1572251881Spetersvn_error_t *
1573251881Spetersvn_fs_change_rev_prop2(svn_fs_t *fs, svn_revnum_t rev, const char *name,
1574251881Speter                        const svn_string_t *const *old_value_p,
1575251881Speter                        const svn_string_t *value, apr_pool_t *pool)
1576251881Speter{
1577251881Speter  return svn_error_trace(fs->vtable->change_rev_prop(fs, rev, name,
1578251881Speter                                                     old_value_p,
1579251881Speter                                                     value, pool));
1580251881Speter}
1581251881Speter
1582251881Spetersvn_error_t *
1583251881Spetersvn_fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
1584251881Speter                             svn_fs_root_t *source_root,
1585251881Speter                             const char *source_path,
1586251881Speter                             svn_fs_root_t *target_root,
1587251881Speter                             const char *target_path, apr_pool_t *pool)
1588251881Speter{
1589251881Speter  return svn_error_trace(target_root->vtable->get_file_delta_stream(
1590251881Speter                           stream_p,
1591251881Speter                           source_root, source_path,
1592251881Speter                           target_root, target_path, pool));
1593251881Speter}
1594251881Speter
1595251881Spetersvn_error_t *
1596251881Spetersvn_fs_get_uuid(svn_fs_t *fs, const char **uuid, apr_pool_t *pool)
1597251881Speter{
1598251881Speter  /* If you change this, consider changing svn_fs__identifier(). */
1599251881Speter  *uuid = apr_pstrdup(pool, fs->uuid);
1600251881Speter  return SVN_NO_ERROR;
1601251881Speter}
1602251881Speter
1603251881Spetersvn_error_t *
1604251881Spetersvn_fs_set_uuid(svn_fs_t *fs, const char *uuid, apr_pool_t *pool)
1605251881Speter{
1606251881Speter  if (! uuid)
1607251881Speter    {
1608251881Speter      uuid = svn_uuid_generate(pool);
1609251881Speter    }
1610251881Speter  else
1611251881Speter    {
1612251881Speter      apr_uuid_t parsed_uuid;
1613251881Speter      apr_status_t apr_err = apr_uuid_parse(&parsed_uuid, uuid);
1614251881Speter      if (apr_err)
1615251881Speter        return svn_error_createf(SVN_ERR_BAD_UUID, NULL,
1616251881Speter                                 _("Malformed UUID '%s'"), uuid);
1617251881Speter    }
1618251881Speter  return svn_error_trace(fs->vtable->set_uuid(fs, uuid, pool));
1619251881Speter}
1620251881Speter
1621251881Spetersvn_error_t *
1622299742Sdimsvn_fs_lock_many(svn_fs_t *fs,
1623299742Sdim                 apr_hash_t *targets,
1624299742Sdim                 const char *comment,
1625299742Sdim                 svn_boolean_t is_dav_comment,
1626299742Sdim                 apr_time_t expiration_date,
1627299742Sdim                 svn_boolean_t steal_lock,
1628299742Sdim                 svn_fs_lock_callback_t lock_callback,
1629299742Sdim                 void *lock_baton,
1630299742Sdim                 apr_pool_t *result_pool,
1631299742Sdim                 apr_pool_t *scratch_pool)
1632251881Speter{
1633299742Sdim  apr_hash_index_t *hi;
1634299742Sdim  apr_hash_t *ok_targets = apr_hash_make(scratch_pool);
1635299742Sdim  svn_error_t *err, *cb_err = SVN_NO_ERROR;
1636299742Sdim
1637251881Speter  /* Enforce that the comment be xml-escapable. */
1638251881Speter  if (comment)
1639299742Sdim    if (! svn_xml_is_xml_safe(comment, strlen(comment)))
1640299742Sdim      return svn_error_create(SVN_ERR_XML_UNESCAPABLE_DATA, NULL,
1641299742Sdim                              _("Lock comment contains illegal characters"));
1642251881Speter
1643299742Sdim  if (expiration_date < 0)
1644299742Sdim    return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
1645299742Sdim              _("Negative expiration date passed to svn_fs_lock"));
1646299742Sdim
1647251881Speter  /* Enforce that the token be an XML-safe URI. */
1648299742Sdim  for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi))
1649251881Speter    {
1650299742Sdim      const svn_fs_lock_target_t *target = apr_hash_this_val(hi);
1651251881Speter
1652299742Sdim      err = SVN_NO_ERROR;
1653299742Sdim      if (target->token)
1654299742Sdim        {
1655299742Sdim          const char *c;
1656251881Speter
1657251881Speter
1658299742Sdim          if (strncmp(target->token, "opaquelocktoken:", 16))
1659299742Sdim            err = svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1660299742Sdim                                    _("Lock token URI '%s' has bad scheme; "
1661299742Sdim                                      "expected '%s'"),
1662299742Sdim                                    target->token, "opaquelocktoken");
1663299742Sdim
1664299742Sdim          if (!err)
1665299742Sdim            for (c = target->token; *c && !err; c++)
1666299742Sdim              if (! svn_ctype_isascii(*c) || svn_ctype_iscntrl(*c))
1667299742Sdim                err = svn_error_createf(
1668299742Sdim                        SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1669299742Sdim                        _("Lock token '%s' is not ASCII or is a "
1670299742Sdim                          "control character at byte %u"),
1671299742Sdim                        target->token,
1672299742Sdim                        (unsigned)(c - target->token));
1673299742Sdim
1674299742Sdim          /* strlen(token) == c - token. */
1675299742Sdim          if (!err && !svn_xml_is_xml_safe(target->token, c - target->token))
1676299742Sdim            err = svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1677299742Sdim                                    _("Lock token URI '%s' is not XML-safe"),
1678299742Sdim                                    target->token);
1679299742Sdim        }
1680299742Sdim
1681299742Sdim      if (err)
1682299742Sdim        {
1683299742Sdim          if (!cb_err && lock_callback)
1684299742Sdim            cb_err = lock_callback(lock_baton, apr_hash_this_key(hi),
1685299742Sdim                                   NULL, err, scratch_pool);
1686299742Sdim          svn_error_clear(err);
1687299742Sdim        }
1688299742Sdim      else
1689299742Sdim        svn_hash_sets(ok_targets, apr_hash_this_key(hi), target);
1690251881Speter    }
1691251881Speter
1692299742Sdim  if (!apr_hash_count(ok_targets))
1693299742Sdim    return svn_error_trace(cb_err);
1694251881Speter
1695299742Sdim  err = fs->vtable->lock(fs, ok_targets, comment, is_dav_comment,
1696299742Sdim                         expiration_date, steal_lock,
1697299742Sdim                         lock_callback, lock_baton,
1698299742Sdim                         result_pool, scratch_pool);
1699299742Sdim
1700299742Sdim  if (err && cb_err)
1701299742Sdim    svn_error_compose(err, cb_err);
1702299742Sdim  else if (!err)
1703299742Sdim    err = cb_err;
1704299742Sdim
1705299742Sdim  return svn_error_trace(err);
1706251881Speter}
1707251881Speter
1708299742Sdimstruct lock_baton_t {
1709299742Sdim  const svn_lock_t *lock;
1710299742Sdim  svn_error_t *fs_err;
1711299742Sdim};
1712299742Sdim
1713299742Sdim/* Implements svn_fs_lock_callback_t. Used by svn_fs_lock and
1714299742Sdim   svn_fs_unlock to record the lock and error from svn_fs_lock_many
1715299742Sdim   and svn_fs_unlock_many. */
1716299742Sdimstatic svn_error_t *
1717299742Sdimlock_cb(void *lock_baton,
1718299742Sdim        const char *path,
1719299742Sdim        const svn_lock_t *lock,
1720299742Sdim        svn_error_t *fs_err,
1721299742Sdim        apr_pool_t *pool)
1722299742Sdim{
1723299742Sdim  struct lock_baton_t *b = lock_baton;
1724299742Sdim
1725299742Sdim  b->lock = lock;
1726299742Sdim  b->fs_err = svn_error_dup(fs_err);
1727299742Sdim
1728299742Sdim  return SVN_NO_ERROR;
1729299742Sdim}
1730299742Sdim
1731251881Spetersvn_error_t *
1732299742Sdimsvn_fs_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path,
1733299742Sdim            const char *token, const char *comment,
1734299742Sdim            svn_boolean_t is_dav_comment, apr_time_t expiration_date,
1735299742Sdim            svn_revnum_t current_rev, svn_boolean_t steal_lock,
1736299742Sdim            apr_pool_t *pool)
1737299742Sdim{
1738299742Sdim  apr_hash_t *targets = apr_hash_make(pool);
1739299742Sdim  svn_fs_lock_target_t target;
1740299742Sdim  svn_error_t *err;
1741299742Sdim  struct lock_baton_t baton = {0};
1742299742Sdim
1743299742Sdim  target.token = token;
1744299742Sdim  target.current_rev = current_rev;
1745299742Sdim  svn_hash_sets(targets, path, &target);
1746299742Sdim
1747299742Sdim  err = svn_fs_lock_many(fs, targets, comment, is_dav_comment,
1748299742Sdim                         expiration_date, steal_lock, lock_cb, &baton,
1749299742Sdim                         pool, pool);
1750299742Sdim
1751299742Sdim  if (baton.lock)
1752299742Sdim    *lock = (svn_lock_t*)baton.lock;
1753299742Sdim
1754299742Sdim  if (err && baton.fs_err)
1755299742Sdim    svn_error_compose(err, baton.fs_err);
1756299742Sdim  else if (!err)
1757299742Sdim    err = baton.fs_err;
1758299742Sdim
1759299742Sdim  return svn_error_trace(err);
1760299742Sdim}
1761299742Sdim
1762299742Sdimsvn_error_t *
1763251881Spetersvn_fs_generate_lock_token(const char **token, svn_fs_t *fs, apr_pool_t *pool)
1764251881Speter{
1765251881Speter  return svn_error_trace(fs->vtable->generate_lock_token(token, fs, pool));
1766251881Speter}
1767251881Speter
1768299742Sdimsvn_fs_lock_target_t *
1769299742Sdimsvn_fs_lock_target_create(const char *token,
1770299742Sdim                          svn_revnum_t current_rev,
1771299742Sdim                          apr_pool_t *result_pool)
1772299742Sdim{
1773299742Sdim  svn_fs_lock_target_t *target = apr_palloc(result_pool,
1774299742Sdim                                            sizeof(svn_fs_lock_target_t));
1775299742Sdim
1776299742Sdim  target->token = token;
1777299742Sdim  target->current_rev = current_rev;
1778299742Sdim
1779299742Sdim  return target;
1780299742Sdim}
1781299742Sdim
1782299742Sdimvoid
1783299742Sdimsvn_fs_lock_target_set_token(svn_fs_lock_target_t *target,
1784299742Sdim                             const char *token)
1785299742Sdim{
1786299742Sdim  target->token = token;
1787299742Sdim}
1788299742Sdim
1789251881Spetersvn_error_t *
1790299742Sdimsvn_fs_unlock_many(svn_fs_t *fs,
1791299742Sdim                   apr_hash_t *targets,
1792299742Sdim                   svn_boolean_t break_lock,
1793299742Sdim                   svn_fs_lock_callback_t lock_callback,
1794299742Sdim                   void *lock_baton,
1795299742Sdim                   apr_pool_t *result_pool,
1796299742Sdim                   apr_pool_t *scratch_pool)
1797299742Sdim{
1798299742Sdim  return svn_error_trace(fs->vtable->unlock(fs, targets, break_lock,
1799299742Sdim                                            lock_callback, lock_baton,
1800299742Sdim                                            result_pool, scratch_pool));
1801299742Sdim}
1802299742Sdim
1803299742Sdimsvn_error_t *
1804251881Spetersvn_fs_unlock(svn_fs_t *fs, const char *path, const char *token,
1805251881Speter              svn_boolean_t break_lock, apr_pool_t *pool)
1806251881Speter{
1807299742Sdim  apr_hash_t *targets = apr_hash_make(pool);
1808299742Sdim  svn_error_t *err;
1809299742Sdim  struct lock_baton_t baton = {0};
1810299742Sdim
1811299742Sdim  if (!token)
1812299742Sdim    token = "";
1813299742Sdim  svn_hash_sets(targets, path, token);
1814299742Sdim
1815299742Sdim  err = svn_fs_unlock_many(fs, targets, break_lock, lock_cb, &baton,
1816299742Sdim                           pool, pool);
1817299742Sdim
1818299742Sdim  if (err && baton.fs_err)
1819299742Sdim    svn_error_compose(err, baton.fs_err);
1820299742Sdim  else if (!err)
1821299742Sdim    err = baton.fs_err;
1822299742Sdim
1823299742Sdim  return svn_error_trace(err);
1824251881Speter}
1825251881Speter
1826251881Spetersvn_error_t *
1827251881Spetersvn_fs_get_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path,
1828251881Speter                apr_pool_t *pool)
1829251881Speter{
1830251881Speter  return svn_error_trace(fs->vtable->get_lock(lock, fs, path, pool));
1831251881Speter}
1832251881Speter
1833251881Spetersvn_error_t *
1834251881Spetersvn_fs_get_locks2(svn_fs_t *fs, const char *path, svn_depth_t depth,
1835251881Speter                  svn_fs_get_locks_callback_t get_locks_func,
1836251881Speter                  void *get_locks_baton, apr_pool_t *pool)
1837251881Speter{
1838251881Speter  SVN_ERR_ASSERT((depth == svn_depth_empty) ||
1839251881Speter                 (depth == svn_depth_files) ||
1840251881Speter                 (depth == svn_depth_immediates) ||
1841251881Speter                 (depth == svn_depth_infinity));
1842251881Speter  return svn_error_trace(fs->vtable->get_locks(fs, path, depth,
1843251881Speter                                               get_locks_func,
1844251881Speter                                               get_locks_baton, pool));
1845251881Speter}
1846251881Speter
1847299742Sdim
1848299742Sdim/* --- History functions --- */
1849299742Sdim
1850251881Spetersvn_error_t *
1851299742Sdimsvn_fs_history_prev2(svn_fs_history_t **prev_history_p,
1852299742Sdim                     svn_fs_history_t *history, svn_boolean_t cross_copies,
1853299742Sdim                     apr_pool_t *result_pool, apr_pool_t *scratch_pool)
1854251881Speter{
1855299742Sdim  return svn_error_trace(history->vtable->prev(prev_history_p, history,
1856299742Sdim                                               cross_copies, result_pool,
1857299742Sdim                                               scratch_pool));
1858251881Speter}
1859251881Speter
1860251881Spetersvn_error_t *
1861251881Spetersvn_fs_history_prev(svn_fs_history_t **prev_history_p,
1862251881Speter                    svn_fs_history_t *history, svn_boolean_t cross_copies,
1863251881Speter                    apr_pool_t *pool)
1864251881Speter{
1865251881Speter  return svn_error_trace(history->vtable->prev(prev_history_p, history,
1866299742Sdim                                               cross_copies, pool, pool));
1867251881Speter}
1868251881Speter
1869251881Spetersvn_error_t *
1870251881Spetersvn_fs_history_location(const char **path, svn_revnum_t *revision,
1871251881Speter                        svn_fs_history_t *history, apr_pool_t *pool)
1872251881Speter{
1873251881Speter  return svn_error_trace(history->vtable->location(path, revision, history,
1874251881Speter                                                   pool));
1875251881Speter}
1876251881Speter
1877251881Speter
1878251881Speter/* --- Node-ID functions --- */
1879251881Speter
1880251881Spetersvn_fs_id_t *
1881251881Spetersvn_fs_parse_id(const char *data, apr_size_t len, apr_pool_t *pool)
1882251881Speter{
1883251881Speter  fs_library_vtable_t *vtable;
1884251881Speter  svn_error_t *err;
1885251881Speter
1886251881Speter  err = get_library_vtable(&vtable, SVN_FS_TYPE_BDB, pool);
1887251881Speter  if (err)
1888251881Speter    {
1889251881Speter      svn_error_clear(err);
1890251881Speter      return NULL;
1891251881Speter    }
1892251881Speter  return vtable->parse_id(data, len, pool);
1893251881Speter}
1894251881Speter
1895251881Spetersvn_string_t *
1896251881Spetersvn_fs_unparse_id(const svn_fs_id_t *id, apr_pool_t *pool)
1897251881Speter{
1898251881Speter  return id->vtable->unparse(id, pool);
1899251881Speter}
1900251881Speter
1901251881Spetersvn_boolean_t
1902251881Spetersvn_fs_check_related(const svn_fs_id_t *a, const svn_fs_id_t *b)
1903251881Speter{
1904299742Sdim  return (a->vtable->compare(a, b) != svn_fs_node_unrelated);
1905251881Speter}
1906251881Speter
1907251881Speterint
1908251881Spetersvn_fs_compare_ids(const svn_fs_id_t *a, const svn_fs_id_t *b)
1909251881Speter{
1910299742Sdim  switch (a->vtable->compare(a, b))
1911299742Sdim    {
1912299742Sdim    case svn_fs_node_unchanged:
1913299742Sdim      return 0;
1914299742Sdim    case svn_fs_node_common_ancestor:
1915299742Sdim      return 1;
1916299742Sdim    default:
1917299742Sdim      return -1;
1918299742Sdim    }
1919251881Speter}
1920251881Speter
1921251881Spetersvn_error_t *
1922251881Spetersvn_fs_print_modules(svn_stringbuf_t *output,
1923251881Speter                     apr_pool_t *pool)
1924251881Speter{
1925299742Sdim  struct fs_type_defn *defn = fs_modules;
1926251881Speter  fs_library_vtable_t *vtable;
1927251881Speter  apr_pool_t *iterpool = svn_pool_create(pool);
1928251881Speter
1929251881Speter  while (defn)
1930251881Speter    {
1931251881Speter      char *line;
1932251881Speter      svn_error_t *err;
1933251881Speter
1934251881Speter      svn_pool_clear(iterpool);
1935251881Speter
1936251881Speter      err = get_library_vtable_direct(&vtable, defn, iterpool);
1937251881Speter      if (err)
1938251881Speter        {
1939251881Speter          if (err->apr_err == SVN_ERR_FS_UNKNOWN_FS_TYPE)
1940251881Speter            {
1941251881Speter              svn_error_clear(err);
1942251881Speter              defn = defn->next;
1943251881Speter              continue;
1944251881Speter            }
1945251881Speter          else
1946251881Speter            return err;
1947251881Speter        }
1948251881Speter
1949251881Speter      line = apr_psprintf(iterpool, "* fs_%s : %s\n",
1950251881Speter                          defn->fsap_name, vtable->get_description());
1951251881Speter      svn_stringbuf_appendcstr(output, line);
1952251881Speter      defn = defn->next;
1953251881Speter    }
1954251881Speter
1955251881Speter  svn_pool_destroy(iterpool);
1956251881Speter
1957251881Speter  return SVN_NO_ERROR;
1958251881Speter}
1959251881Speter
1960251881Spetersvn_fs_path_change2_t *
1961251881Spetersvn_fs_path_change2_create(const svn_fs_id_t *node_rev_id,
1962251881Speter                           svn_fs_path_change_kind_t change_kind,
1963251881Speter                           apr_pool_t *pool)
1964251881Speter{
1965251881Speter  return svn_fs__path_change_create_internal(node_rev_id, change_kind, pool);
1966251881Speter}
1967251881Speter
1968251881Speter/* Return the library version number. */
1969251881Speterconst svn_version_t *
1970251881Spetersvn_fs_version(void)
1971251881Speter{
1972251881Speter  SVN_VERSION_BODY;
1973251881Speter}
1974299742Sdim
1975299742Sdim
1976299742Sdim/** info **/
1977299742Sdimsvn_error_t *
1978299742Sdimsvn_fs_info(const svn_fs_info_placeholder_t **info_p,
1979299742Sdim            svn_fs_t *fs,
1980299742Sdim            apr_pool_t *result_pool,
1981299742Sdim            apr_pool_t *scratch_pool)
1982299742Sdim{
1983299742Sdim  if (fs->vtable->info_fsap)
1984299742Sdim    {
1985299742Sdim      SVN_ERR(fs->vtable->info_fsap((const void **)info_p, fs,
1986299742Sdim                                    result_pool, scratch_pool));
1987299742Sdim    }
1988299742Sdim  else
1989299742Sdim    {
1990299742Sdim      svn_fs_info_placeholder_t *info = apr_palloc(result_pool, sizeof(*info));
1991299742Sdim      /* ### Ask the disk(!), since svn_fs_t doesn't cache the answer. */
1992299742Sdim      SVN_ERR(svn_fs_type(&info->fs_type, fs->path, result_pool));
1993299742Sdim      *info_p = info;
1994299742Sdim    }
1995299742Sdim  return SVN_NO_ERROR;
1996299742Sdim}
1997299742Sdim
1998299742Sdimvoid *
1999299742Sdimsvn_fs_info_dup(const void *info_void,
2000299742Sdim                apr_pool_t *result_pool,
2001299742Sdim                apr_pool_t *scratch_pool)
2002299742Sdim{
2003299742Sdim  const svn_fs_info_placeholder_t *info = info_void;
2004299742Sdim  fs_library_vtable_t *vtable;
2005299742Sdim
2006299742Sdim  SVN_ERR(get_library_vtable(&vtable, info->fs_type, scratch_pool));
2007299742Sdim
2008299742Sdim  if (vtable->info_fsap_dup)
2009299742Sdim    return vtable->info_fsap_dup(info_void, result_pool);
2010299742Sdim  else
2011299742Sdim    return apr_pmemdup(result_pool, info, sizeof(*info));
2012299742Sdim}
2013299742Sdim
2014