2 * fs_loader.c: Front-end to the various FS back ends
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
27 #include <apr_atomic.h>
30 #include <apr_strings.h>
32 #include "svn_private_config.h"
34 #include "svn_ctype.h"
35 #include "svn_types.h"
37 #include "svn_version.h"
41 #include "svn_pools.h"
42 #include "svn_string.h"
43 #include "svn_sorts.h"
45 #include "private/svn_atomic.h"
46 #include "private/svn_fs_private.h"
47 #include "private/svn_fs_util.h"
48 #include "private/svn_fspath.h"
49 #include "private/svn_utf_private.h"
50 #include "private/svn_mutex.h"
51 #include "private/svn_subr_private.h"
53 #include "fs-loader.h"
55 /* This is defined by configure on platforms which use configure, but
56 we need to define a fallback for Windows. */
57 #ifndef DEFAULT_FS_TYPE
58 #define DEFAULT_FS_TYPE "fsfs"
61 #define FS_TYPE_FILENAME "fs-type"
63 /* If a FS backend does not implement the PATHS_CHANGED vtable function,
64 it will get emulated. However, if this macro is defined to non-null
65 then the API will always be emulated when feasible, i.e. the calls
66 get "re-directed" to the new API implementation. */
67 #ifndef SVN_FS_EMULATE_PATHS_CHANGED
68 #define SVN_FS_EMULATE_PATHS_CHANGED FALSE
71 /* If a FS backend does not implement the REPORT_CHANGES vtable function,
72 it will get emulated. However, if this macro is defined to non-null
73 then the API will always be emulated when feasible, i.e. the calls
74 get "re-directed" to the old API implementation. */
75 #ifndef SVN_FS_EMULATE_REPORT_CHANGES
76 #define SVN_FS_EMULATE_REPORT_CHANGES FALSE
79 /* A pool common to all FS objects. See the documentation on the
80 open/create functions in fs-loader.h and for svn_fs_initialize(). */
81 static apr_pool_t *common_pool = NULL;
82 static svn_mutex__t *common_pool_lock = NULL;
83 static svn_atomic_t common_pool_initialized = FALSE;
86 /* --- Utility functions for the loader --- */
90 const char *fsap_name;
91 fs_init_func_t initfunc;
92 void * volatile vtable; /* fs_library_vtable_t */
93 struct fs_type_defn *next;
96 static struct fs_type_defn base_defn =
98 SVN_FS_TYPE_BDB, "base",
99 #ifdef SVN_LIBSVN_FS_LINKS_FS_BASE
105 NULL /* End of static list: this needs to be reset to NULL if the
106 common_pool used when setting it has been cleared. */
109 static struct fs_type_defn fsx_defn =
111 SVN_FS_TYPE_FSX, "x",
112 #ifdef SVN_LIBSVN_FS_LINKS_FS_X
121 static struct fs_type_defn fsfs_defn =
123 SVN_FS_TYPE_FSFS, "fs",
124 #ifdef SVN_LIBSVN_FS_LINKS_FS_FS
133 static struct fs_type_defn *fs_modules = &fsfs_defn;
137 load_module(fs_init_func_t *initfunc, const char *name, apr_pool_t *pool)
141 #if defined(SVN_USE_DSO) && APR_HAS_DSO
143 apr_dso_handle_t *dso;
144 apr_dso_handle_sym_t symbol;
146 const char *funcname;
150 /* Demand a simple alphanumeric name so that the generated DSO
152 for (p = name; *p; ++p)
153 if (!svn_ctype_isalnum(*p))
154 return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
155 _("Invalid name for FS type '%s'"),
158 libname = apr_psprintf(pool, "libsvn_fs_%s-" SVN_DSO_SUFFIX_FMT,
159 name, SVN_VER_MAJOR, SVN_SOVERSION);
160 funcname = apr_psprintf(pool, "svn_fs_%s__init", name);
162 /* Find/load the specified library. If we get an error, assume
163 the library doesn't exist. The library will be unloaded when
164 pool is destroyed. */
165 SVN_ERR(svn_dso_load(&dso, libname));
169 /* find the initialization routine */
170 status = apr_dso_sym(&symbol, dso, funcname);
172 return svn_error_wrap_apr(status, _("'%s' does not define '%s()'"),
175 *initfunc = (fs_init_func_t) symbol;
177 #endif /* APR_HAS_DSO */
182 /* Fetch a library vtable by a pointer into the library definitions array. */
184 get_library_vtable_direct(fs_library_vtable_t **vtable,
185 struct fs_type_defn *fst,
188 fs_init_func_t initfunc = NULL;
189 const svn_version_t *my_version = svn_fs_version();
190 const svn_version_t *fs_version;
192 /* most times, we get lucky */
193 *vtable = svn_atomic_casptr(&fst->vtable, NULL, NULL);
197 /* o.k. the first access needs to actually load the module, find the
198 vtable and check for version compatibility. */
199 initfunc = fst->initfunc;
201 SVN_ERR(load_module(&initfunc, fst->fsap_name, pool));
204 return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
205 _("Failed to load module for FS type '%s'"),
209 /* Per our API compatibility rules, we cannot ensure that
210 svn_fs_initialize is called by the application. If not, we
211 cannot create the common pool and lock in a thread-safe fashion,
212 nor can we clean up the common pool if libsvn_fs is dynamically
213 unloaded. This function makes a best effort by creating the
214 common pool as a child of the global pool; the window of failure
215 due to thread collision is small. */
216 SVN_ERR(svn_fs_initialize(NULL));
218 /* Invoke the FS module's initfunc function with the common
219 pool protected by a lock. */
220 SVN_MUTEX__WITH_LOCK(common_pool_lock,
221 initfunc(my_version, vtable, common_pool));
223 fs_version = (*vtable)->get_version();
224 if (!svn_ver_equal(my_version, fs_version))
225 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
226 _("Mismatched FS module version for '%s':"
228 " expected %d.%d.%d%s"),
230 my_version->major, my_version->minor,
231 my_version->patch, my_version->tag,
232 fs_version->major, fs_version->minor,
233 fs_version->patch, fs_version->tag);
235 /* the vtable will not change. Remember it */
236 svn_atomic_casptr(&fst->vtable, *vtable, NULL);
241 #if defined(SVN_USE_DSO) && APR_HAS_DSO
242 /* Return *FST for the third party FS_TYPE */
244 get_or_allocate_third(struct fs_type_defn **fst,
249 if (strcmp(fs_type, (*fst)->fs_type) == 0)
254 *fst = apr_palloc(common_pool, sizeof(struct fs_type_defn));
255 (*fst)->fs_type = apr_pstrdup(common_pool, fs_type);
256 (*fst)->fsap_name = (*fst)->fs_type;
257 (*fst)->initfunc = NULL;
258 (*fst)->vtable = NULL;
265 /* Fetch a library *VTABLE by FS_TYPE.
266 Use POOL for temporary allocations. */
268 get_library_vtable(fs_library_vtable_t **vtable, const char *fs_type,
271 struct fs_type_defn **fst;
272 svn_boolean_t known = FALSE;
274 /* There are three FS module definitions known at compile time. We
275 want to check these without any locking overhead even when
276 dynamic third party modules are enabled. The third party modules
277 cannot be checked until the lock is held. */
278 for (fst = &fs_modules; *fst; fst = &(*fst)->next)
280 if (strcmp(fs_type, (*fst)->fs_type) == 0)
285 else if (!(*fst)->next)
291 #if defined(SVN_USE_DSO) && APR_HAS_DSO
292 /* Third party FS modules that are unknown at compile time.
294 A third party FS is identified by the file fs-type containing a
295 third party name, say "foo". The loader will load the DSO with
296 the name "libsvn_fs_foo" and use the entry point with the name
299 Note: the BDB and FSFS modules don't follow this naming scheme
300 and this allows them to be used to test the third party loader.
301 Change the content of fs-type to "base" in a BDB filesystem or to
302 "fs" in an FSFS filesystem and they will be loaded as third party
307 /* Best-effort init, see get_library_vtable_direct. */
308 SVN_ERR(svn_fs_initialize(NULL));
309 SVN_MUTEX__WITH_LOCK(common_pool_lock,
310 get_or_allocate_third(fst, fs_type));
315 return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
316 _("Unknown FS type '%s'"), fs_type);
317 return get_library_vtable_direct(vtable, *fst, pool);
321 svn_fs_type(const char **fs_type, const char *path, apr_pool_t *pool)
323 const char *filename;
329 /* Read the fsap-name file to get the FSAP name, or assume the (old)
330 default. For old repositories I suppose we could check some
331 other file, DB_CONFIG or strings say, but for now just check the
333 filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool);
334 err = svn_io_file_open(&file, filename, APR_READ|APR_BUFFERED, 0, pool);
335 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
337 svn_node_kind_t kind;
338 svn_error_t *err2 = svn_io_check_path(path, &kind, pool);
341 svn_error_clear(err2);
344 if (kind == svn_node_dir)
346 svn_error_clear(err);
347 *fs_type = SVN_FS_TYPE_BDB;
356 SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
357 SVN_ERR(svn_io_file_close(file, pool));
358 *fs_type = apr_pstrdup(pool, buf);
363 /* Fetch the library vtable for an existing FS. */
365 fs_library_vtable(fs_library_vtable_t **vtable, const char *path,
370 SVN_ERR(svn_fs_type(&fs_type, path, pool));
372 /* Fetch the library vtable by name, now that we've chosen one. */
373 SVN_ERR(get_library_vtable(vtable, fs_type, pool));
379 write_fs_type(const char *path, const char *fs_type, apr_pool_t *pool)
381 const char *filename;
384 filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool);
385 SVN_ERR(svn_io_file_open(&file, filename,
386 APR_WRITE|APR_CREATE|APR_TRUNCATE|APR_BUFFERED,
387 APR_OS_DEFAULT, pool));
388 SVN_ERR(svn_io_file_write_full(file, fs_type, strlen(fs_type), NULL,
390 SVN_ERR(svn_io_file_write_full(file, "\n", 1, NULL, pool));
391 return svn_error_trace(svn_io_file_close(file, pool));
395 /* --- Functions for operating on filesystems by pathname --- */
397 static apr_status_t uninit(void *data)
400 common_pool_lock = NULL;
401 common_pool_initialized = 0;
407 synchronized_initialize(void *baton, apr_pool_t *pool)
409 common_pool = svn_pool_create(pool);
410 base_defn.next = NULL;
411 SVN_ERR(svn_mutex__init(&common_pool_lock, TRUE, common_pool));
413 /* ### This won't work if POOL is NULL and libsvn_fs is loaded as a DSO
414 ### (via libsvn_ra_local say) since the global common_pool will live
415 ### longer than the DSO, which gets unloaded when the pool used to
416 ### load it is cleared, and so when the handler runs it will refer to
417 ### a function that no longer exists. libsvn_ra_local attempts to
418 ### work around this by explicitly calling svn_fs_initialize. */
419 apr_pool_cleanup_register(common_pool, NULL, uninit, apr_pool_cleanup_null);
424 svn_fs_initialize(apr_pool_t *pool)
426 #if defined(SVN_USE_DSO) && APR_HAS_DSO
427 /* Ensure that DSO subsystem is initialized early as possible if
428 we're going to use it. */
429 SVN_ERR(svn_dso_initialize2());
431 /* Protect against multiple calls. */
432 return svn_error_trace(svn_atomic__init_once(&common_pool_initialized,
433 synchronized_initialize,
437 /* A default warning handling function. */
439 default_warning_func(void *baton, svn_error_t *err)
441 /* The one unforgiveable sin is to fail silently. Dumping to stderr
442 or /dev/tty is not acceptable default behavior for server
443 processes, since those may both be equivalent to /dev/null.
445 That said, be a good citizen and print something anyway, in case it goes
446 somewhere, and our caller hasn't overridden the abort() call.
448 if (svn_error_get_malfunction_handler()
449 == svn_error_abort_on_malfunction)
450 /* ### TODO: extend the malfunction API such that non-abort()ing consumers
451 ### also get the information on ERR. */
452 svn_handle_error2(err, stderr, FALSE /* fatal */, "svn: fs-loader: ");
453 SVN_ERR_MALFUNCTION_NO_RETURN();
457 svn_fs__path_valid(const char *path, apr_pool_t *pool)
461 /* UTF-8 encoded string without NULs. */
462 if (! svn_utf__cstring_is_valid(path))
464 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
465 _("Path '%s' is not in UTF-8"), path);
468 /* No "." or ".." elements. */
469 if (svn_path_is_backpath_present(path)
470 || svn_path_is_dotpath_present(path))
472 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
473 _("Path '%s' contains '.' or '..' element"),
477 /* Raise an error if PATH contains a newline because svn:mergeinfo and
478 friends can't handle them. Issue #4340 describes a similar problem
479 in the FSFS code itself.
481 c = strchr(path, '\n');
484 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
485 _("Invalid control character '0x%02x' in path '%s'"),
486 (unsigned char)*c, svn_path_illegal_path_escape(path, pool));
489 /* That's good enough. */
493 /* Allocate svn_fs_t structure. */
495 fs_new(apr_hash_t *fs_config, apr_pool_t *pool)
497 svn_fs_t *fs = apr_palloc(pool, sizeof(*fs));
500 fs->warning = default_warning_func;
501 fs->warning_baton = NULL;
502 fs->config = fs_config;
503 fs->access_ctx = NULL;
505 fs->fsap_data = NULL;
511 svn_fs_new(apr_hash_t *fs_config, apr_pool_t *pool)
513 return fs_new(fs_config, pool);
517 svn_fs_set_warning_func(svn_fs_t *fs, svn_fs_warning_callback_t warning,
520 fs->warning = warning;
521 fs->warning_baton = warning_baton;
525 svn_fs_create2(svn_fs_t **fs_p,
527 apr_hash_t *fs_config,
528 apr_pool_t *result_pool,
529 apr_pool_t *scratch_pool)
531 fs_library_vtable_t *vtable;
533 const char *fs_type = svn_hash__get_cstring(fs_config,
534 SVN_FS_CONFIG_FS_TYPE,
536 SVN_ERR(get_library_vtable(&vtable, fs_type, scratch_pool));
538 /* Create the FS directory and write out the fsap-name file. */
539 SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, scratch_pool));
540 SVN_ERR(write_fs_type(path, fs_type, scratch_pool));
542 /* Perform the actual creation. */
543 *fs_p = fs_new(fs_config, result_pool);
545 SVN_ERR(vtable->create(*fs_p, path, common_pool_lock, scratch_pool,
547 SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open2));
553 svn_fs_open2(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config,
554 apr_pool_t *result_pool,
555 apr_pool_t *scratch_pool)
557 fs_library_vtable_t *vtable;
559 SVN_ERR(fs_library_vtable(&vtable, path, scratch_pool));
560 *fs_p = fs_new(fs_config, result_pool);
561 SVN_ERR(vtable->open_fs(*fs_p, path, common_pool_lock, scratch_pool,
563 SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open2));
569 svn_fs_upgrade2(const char *path,
570 svn_fs_upgrade_notify_t notify_func,
572 svn_cancel_func_t cancel_func,
574 apr_pool_t *scratch_pool)
576 fs_library_vtable_t *vtable;
579 SVN_ERR(fs_library_vtable(&vtable, path, scratch_pool));
580 fs = fs_new(NULL, scratch_pool);
582 SVN_ERR(vtable->upgrade_fs(fs, path,
583 notify_func, notify_baton,
584 cancel_func, cancel_baton,
586 scratch_pool, common_pool));
590 /* A warning handling function that does not abort on errors,
591 but just lets them be returned normally. */
593 verify_fs_warning_func(void *baton, svn_error_t *err)
598 svn_fs_verify(const char *path,
599 apr_hash_t *fs_config,
602 svn_fs_progress_notify_func_t notify_func,
604 svn_cancel_func_t cancel_func,
608 fs_library_vtable_t *vtable;
611 SVN_ERR(fs_library_vtable(&vtable, path, pool));
612 fs = fs_new(fs_config, pool);
613 svn_fs_set_warning_func(fs, verify_fs_warning_func, NULL);
615 SVN_ERR(vtable->verify_fs(fs, path, start, end,
616 notify_func, notify_baton,
617 cancel_func, cancel_baton,
624 svn_fs_path(svn_fs_t *fs, apr_pool_t *pool)
626 return apr_pstrdup(pool, fs->path);
630 svn_fs_config(svn_fs_t *fs, apr_pool_t *pool)
633 return apr_hash_copy(pool, fs->config);
639 svn_fs_delete_fs(const char *path, apr_pool_t *pool)
641 fs_library_vtable_t *vtable;
643 SVN_ERR(fs_library_vtable(&vtable, path, pool));
644 return svn_error_trace(vtable->delete_fs(path, pool));
648 svn_fs_hotcopy3(const char *src_path, const char *dst_path,
649 svn_boolean_t clean, svn_boolean_t incremental,
650 svn_fs_hotcopy_notify_t notify_func,
652 svn_cancel_func_t cancel_func,
654 apr_pool_t *scratch_pool)
656 fs_library_vtable_t *vtable;
657 const char *src_fs_type;
660 const char *dst_fs_type;
661 svn_node_kind_t dst_kind;
663 if (strcmp(src_path, dst_path) == 0)
664 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
665 _("Hotcopy source and destination are equal"));
667 SVN_ERR(svn_fs_type(&src_fs_type, src_path, scratch_pool));
668 SVN_ERR(get_library_vtable(&vtable, src_fs_type, scratch_pool));
669 src_fs = fs_new(NULL, scratch_pool);
670 dst_fs = fs_new(NULL, scratch_pool);
672 SVN_ERR(svn_io_check_path(dst_path, &dst_kind, scratch_pool));
673 if (dst_kind == svn_node_file)
674 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
675 _("'%s' already exists and is a file"),
676 svn_dirent_local_style(dst_path,
678 if (dst_kind == svn_node_unknown)
679 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
680 _("'%s' already exists and has an unknown "
682 svn_dirent_local_style(dst_path,
684 if (dst_kind == svn_node_dir)
686 svn_node_kind_t type_file_kind;
688 SVN_ERR(svn_io_check_path(svn_dirent_join(dst_path,
691 &type_file_kind, scratch_pool));
692 if (type_file_kind != svn_node_none)
694 SVN_ERR(svn_fs_type(&dst_fs_type, dst_path, scratch_pool));
695 if (strcmp(src_fs_type, dst_fs_type) != 0)
696 return svn_error_createf(
697 SVN_ERR_ILLEGAL_TARGET, NULL,
698 _("The filesystem type of the hotcopy source "
699 "('%s') does not match the filesystem "
700 "type of the hotcopy destination ('%s')"),
701 src_fs_type, dst_fs_type);
705 SVN_ERR(vtable->hotcopy(src_fs, dst_fs, src_path, dst_path, clean,
706 incremental, notify_func, notify_baton,
707 cancel_func, cancel_baton, common_pool_lock,
708 scratch_pool, common_pool));
709 return svn_error_trace(write_fs_type(dst_path, src_fs_type, scratch_pool));
713 svn_fs_pack(const char *path,
714 svn_fs_pack_notify_t notify_func,
716 svn_cancel_func_t cancel_func,
720 fs_library_vtable_t *vtable;
723 SVN_ERR(fs_library_vtable(&vtable, path, pool));
724 fs = fs_new(NULL, pool);
726 SVN_ERR(vtable->pack_fs(fs, path, notify_func, notify_baton,
727 cancel_func, cancel_baton, common_pool_lock,
733 svn_fs_recover(const char *path,
734 svn_cancel_func_t cancel_func, void *cancel_baton,
737 fs_library_vtable_t *vtable;
740 SVN_ERR(fs_library_vtable(&vtable, path, pool));
741 fs = fs_new(NULL, pool);
743 SVN_ERR(vtable->open_fs_for_recovery(fs, path, common_pool_lock,
745 return svn_error_trace(vtable->recover(fs, cancel_func, cancel_baton,
750 svn_fs_verify_root(svn_fs_root_t *root,
751 apr_pool_t *scratch_pool)
753 svn_fs_t *fs = root->fs;
754 SVN_ERR(fs->vtable->verify_root(root, scratch_pool));
760 svn_fs_freeze(svn_fs_t *fs,
761 svn_fs_freeze_func_t freeze_func,
765 SVN_ERR(fs->vtable->freeze(fs, freeze_func, freeze_baton, pool));
771 /* --- Berkeley-specific functions --- */
774 svn_fs_create_berkeley(svn_fs_t *fs, const char *path)
776 fs_library_vtable_t *vtable;
778 SVN_ERR(get_library_vtable(&vtable, SVN_FS_TYPE_BDB, fs->pool));
780 /* Create the FS directory and write out the fsap-name file. */
781 SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, fs->pool));
782 SVN_ERR(write_fs_type(path, SVN_FS_TYPE_BDB, fs->pool));
784 /* Perform the actual creation. */
785 SVN_ERR(vtable->create(fs, path, common_pool_lock, fs->pool, common_pool));
786 SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open2));
792 svn_fs_open_berkeley(svn_fs_t *fs, const char *path)
794 fs_library_vtable_t *vtable;
796 SVN_ERR(fs_library_vtable(&vtable, path, fs->pool));
797 SVN_ERR(vtable->open_fs(fs, path, common_pool_lock, fs->pool, common_pool));
798 SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open2));
804 svn_fs_berkeley_path(svn_fs_t *fs, apr_pool_t *pool)
806 return svn_fs_path(fs, pool);
810 svn_fs_delete_berkeley(const char *path, apr_pool_t *pool)
812 return svn_error_trace(svn_fs_delete_fs(path, pool));
816 svn_fs_hotcopy_berkeley(const char *src_path, const char *dest_path,
817 svn_boolean_t clean_logs, apr_pool_t *pool)
819 return svn_error_trace(svn_fs_hotcopy3(src_path, dest_path, clean_logs,
820 FALSE, NULL, NULL, NULL, NULL,
825 svn_fs_berkeley_recover(const char *path, apr_pool_t *pool)
827 return svn_error_trace(svn_fs_recover(path, NULL, NULL, pool));
831 svn_fs_set_berkeley_errcall(svn_fs_t *fs,
832 void (*handler)(const char *errpfx, char *msg))
834 return svn_error_trace(fs->vtable->bdb_set_errcall(fs, handler));
838 svn_fs_berkeley_logfiles(apr_array_header_t **logfiles,
840 svn_boolean_t only_unused,
843 fs_library_vtable_t *vtable;
845 SVN_ERR(fs_library_vtable(&vtable, path, pool));
846 return svn_error_trace(vtable->bdb_logfiles(logfiles, path, only_unused,
851 /* --- Transaction functions --- */
854 svn_fs_begin_txn2(svn_fs_txn_t **txn_p, svn_fs_t *fs, svn_revnum_t rev,
855 apr_uint32_t flags, apr_pool_t *pool)
857 return svn_error_trace(fs->vtable->begin_txn(txn_p, fs, rev, flags, pool));
862 svn_fs_commit_txn(const char **conflict_p, svn_revnum_t *new_rev,
863 svn_fs_txn_t *txn, apr_pool_t *pool)
867 *new_rev = SVN_INVALID_REVNUM;
871 err = txn->vtable->commit(conflict_p, new_rev, txn, pool);
874 /* Check postconditions. */
877 SVN_ERR_ASSERT_E(! (SVN_IS_VALID_REVNUM(*new_rev) && *conflict_p != NULL),
879 SVN_ERR_ASSERT_E((*conflict_p != NULL)
880 == (err && err->apr_err == SVN_ERR_FS_CONFLICT),
891 svn_fs_abort_txn(svn_fs_txn_t *txn, apr_pool_t *pool)
893 return svn_error_trace(txn->vtable->abort(txn, pool));
897 svn_fs_purge_txn(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
899 return svn_error_trace(fs->vtable->purge_txn(fs, txn_id, pool));
903 svn_fs_txn_name(const char **name_p, svn_fs_txn_t *txn, apr_pool_t *pool)
905 *name_p = apr_pstrdup(pool, txn->id);
910 svn_fs_txn_base_revision(svn_fs_txn_t *txn)
912 return txn->base_rev;
916 svn_fs_open_txn(svn_fs_txn_t **txn, svn_fs_t *fs, const char *name,
919 return svn_error_trace(fs->vtable->open_txn(txn, fs, name, pool));
923 svn_fs_list_transactions(apr_array_header_t **names_p, svn_fs_t *fs,
926 return svn_error_trace(fs->vtable->list_transactions(names_p, fs, pool));
930 is_internal_txn_prop(const char *name)
932 return strcmp(name, SVN_FS__PROP_TXN_CHECK_LOCKS) == 0 ||
933 strcmp(name, SVN_FS__PROP_TXN_CHECK_OOD) == 0 ||
934 strcmp(name, SVN_FS__PROP_TXN_CLIENT_DATE) == 0;
938 svn_fs_txn_prop(svn_string_t **value_p, svn_fs_txn_t *txn,
939 const char *propname, apr_pool_t *pool)
941 if (is_internal_txn_prop(propname))
947 return svn_error_trace(txn->vtable->get_prop(value_p, txn, propname, pool));
951 svn_fs_txn_proplist(apr_hash_t **table_p, svn_fs_txn_t *txn, apr_pool_t *pool)
953 SVN_ERR(txn->vtable->get_proplist(table_p, txn, pool));
955 /* Don't give away internal transaction properties. */
956 svn_hash_sets(*table_p, SVN_FS__PROP_TXN_CHECK_LOCKS, NULL);
957 svn_hash_sets(*table_p, SVN_FS__PROP_TXN_CHECK_OOD, NULL);
958 svn_hash_sets(*table_p, SVN_FS__PROP_TXN_CLIENT_DATE, NULL);
964 svn_fs_change_txn_prop(svn_fs_txn_t *txn, const char *name,
965 const svn_string_t *value, apr_pool_t *pool)
967 if (is_internal_txn_prop(name))
968 return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
969 _("Attempt to modify internal transaction "
970 "property '%s'"), name);
972 return svn_error_trace(txn->vtable->change_prop(txn, name, value, pool));
976 svn_fs_change_txn_props(svn_fs_txn_t *txn, const apr_array_header_t *props,
981 for (i = 0; i < props->nelts; ++i)
983 svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
985 if (is_internal_txn_prop(prop->name))
986 return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
987 _("Attempt to modify internal transaction "
988 "property '%s'"), prop->name);
991 return svn_error_trace(txn->vtable->change_props(txn, props, pool));
995 /* --- Root functions --- */
998 svn_fs_revision_root(svn_fs_root_t **root_p, svn_fs_t *fs, svn_revnum_t rev,
1001 /* We create a subpool for each root object to allow us to implement
1002 svn_fs_close_root. */
1003 apr_pool_t *subpool = svn_pool_create(pool);
1004 return svn_error_trace(fs->vtable->revision_root(root_p, fs, rev, subpool));
1008 svn_fs_txn_root(svn_fs_root_t **root_p, svn_fs_txn_t *txn, apr_pool_t *pool)
1010 /* We create a subpool for each root object to allow us to implement
1011 svn_fs_close_root. */
1012 apr_pool_t *subpool = svn_pool_create(pool);
1013 return svn_error_trace(txn->vtable->root(root_p, txn, subpool));
1017 svn_fs_close_root(svn_fs_root_t *root)
1019 svn_pool_destroy(root->pool);
1023 svn_fs_root_fs(svn_fs_root_t *root)
1029 svn_fs_is_txn_root(svn_fs_root_t *root)
1031 return root->is_txn_root;
1035 svn_fs_is_revision_root(svn_fs_root_t *root)
1037 return !root->is_txn_root;
1041 svn_fs_txn_root_name(svn_fs_root_t *root, apr_pool_t *pool)
1043 return root->is_txn_root ? apr_pstrdup(pool, root->txn) : NULL;
1047 svn_fs_txn_root_base_revision(svn_fs_root_t *root)
1049 return root->is_txn_root ? root->rev : SVN_INVALID_REVNUM;
1053 svn_fs_revision_root_revision(svn_fs_root_t *root)
1055 return root->is_txn_root ? SVN_INVALID_REVNUM : root->rev;
1059 svn_fs_path_change_get(svn_fs_path_change3_t **change,
1060 svn_fs_path_change_iterator_t *iterator)
1062 return iterator->vtable->get(change, iterator);
1066 svn_fs_paths_changed2(apr_hash_t **changed_paths_p,
1067 svn_fs_root_t *root,
1070 svn_boolean_t emulate = !root->vtable->paths_changed
1071 || SVN_FS_EMULATE_PATHS_CHANGED;
1075 apr_pool_t *scratch_pool = svn_pool_create(pool);
1076 apr_hash_t *changes = svn_hash__make(pool);
1078 svn_fs_path_change_iterator_t *iterator;
1079 svn_fs_path_change3_t *change;
1081 SVN_ERR(svn_fs_paths_changed3(&iterator, root, scratch_pool,
1084 SVN_ERR(svn_fs_path_change_get(&change, iterator));
1087 svn_fs_path_change2_t *copy;
1088 const svn_fs_id_t *id_copy;
1089 const char *change_path = change->path.data;
1090 svn_fs_root_t *change_root = root;
1092 /* Copy CHANGE to old API struct. */
1093 if (change->change_kind == svn_fs_path_change_delete)
1094 SVN_ERR(svn_fs__get_deleted_node(&change_root, &change_path,
1095 change_root, change_path,
1096 scratch_pool, scratch_pool));
1098 SVN_ERR(svn_fs_node_id(&id_copy, change_root, change_path, pool));
1100 copy = svn_fs_path_change2_create(id_copy, change->change_kind,
1102 copy->copyfrom_known = change->copyfrom_known;
1103 if ( copy->copyfrom_known
1104 && SVN_IS_VALID_REVNUM(change->copyfrom_rev))
1106 copy->copyfrom_rev = change->copyfrom_rev;
1107 copy->copyfrom_path = apr_pstrdup(pool, change->copyfrom_path);
1109 copy->mergeinfo_mod = change->mergeinfo_mod;
1110 copy->node_kind = change->node_kind;
1111 copy->prop_mod = change->prop_mod;
1112 copy->text_mod = change->text_mod;
1114 svn_hash_sets(changes, apr_pstrmemdup(pool, change->path.data,
1115 change->path.len), copy);
1118 SVN_ERR(svn_fs_path_change_get(&change, iterator));
1120 svn_pool_destroy(scratch_pool);
1122 *changed_paths_p = changes;
1126 SVN_ERR(root->vtable->paths_changed(changed_paths_p, root, pool));
1129 return SVN_NO_ERROR;
1132 /* Implement svn_fs_path_change_iterator_t on top of svn_fs_paths_changed2. */
1134 /* The iterator builds upon a hash iterator, which in turn operates on the
1135 full prefetched changes list. */
1136 typedef struct fsap_iterator_data_t
1138 apr_hash_index_t *hi;
1140 /* For efficicency such that we don't need to dynamically allocate
1141 yet another copy of that data. */
1142 svn_fs_path_change3_t change;
1143 } fsap_iterator_data_t;
1145 static svn_error_t *
1146 changes_iterator_get(svn_fs_path_change3_t **change,
1147 svn_fs_path_change_iterator_t *iterator)
1149 fsap_iterator_data_t *data = iterator->fsap_data;
1153 const char *path = apr_hash_this_key(data->hi);
1154 svn_fs_path_change2_t *entry = apr_hash_this_val(data->hi);
1156 data->change.path.data = path;
1157 data->change.path.len = apr_hash_this_key_len(data->hi);
1158 data->change.change_kind = entry->change_kind;
1159 data->change.node_kind = entry->node_kind;
1160 data->change.text_mod = entry->text_mod;
1161 data->change.prop_mod = entry->prop_mod;
1162 data->change.mergeinfo_mod = entry->mergeinfo_mod;
1163 data->change.copyfrom_known = entry->copyfrom_known;
1164 data->change.copyfrom_rev = entry->copyfrom_rev;
1165 data->change.copyfrom_path = entry->copyfrom_path;
1167 *change = &data->change;
1168 data->hi = apr_hash_next(data->hi);
1175 return SVN_NO_ERROR;
1178 static changes_iterator_vtable_t iterator_vtable =
1180 changes_iterator_get
1184 svn_fs_paths_changed3(svn_fs_path_change_iterator_t **iterator,
1185 svn_fs_root_t *root,
1186 apr_pool_t *result_pool,
1187 apr_pool_t *scratch_pool)
1189 svn_boolean_t emulate = !root->vtable->report_changes
1190 || ( SVN_FS_EMULATE_REPORT_CHANGES
1191 && root->vtable->paths_changed);
1195 svn_fs_path_change_iterator_t *result;
1196 fsap_iterator_data_t *data;
1198 apr_hash_t *changes;
1199 SVN_ERR(root->vtable->paths_changed(&changes, root, result_pool));
1201 data = apr_pcalloc(result_pool, sizeof(*data));
1202 data->hi = apr_hash_first(result_pool, changes);
1204 result = apr_pcalloc(result_pool, sizeof(*result));
1205 result->fsap_data = data;
1206 result->vtable = &iterator_vtable;
1212 SVN_ERR(root->vtable->report_changes(iterator, root, result_pool,
1216 return SVN_NO_ERROR;
1220 svn_fs_check_path(svn_node_kind_t *kind_p, svn_fs_root_t *root,
1221 const char *path, apr_pool_t *pool)
1223 return svn_error_trace(root->vtable->check_path(kind_p, root, path, pool));
1227 svn_fs_node_history2(svn_fs_history_t **history_p, svn_fs_root_t *root,
1228 const char *path, apr_pool_t *result_pool,
1229 apr_pool_t *scratch_pool)
1231 return svn_error_trace(root->vtable->node_history(history_p, root, path,
1237 svn_fs_is_dir(svn_boolean_t *is_dir, svn_fs_root_t *root, const char *path,
1240 svn_node_kind_t kind;
1242 SVN_ERR(root->vtable->check_path(&kind, root, path, pool));
1243 *is_dir = (kind == svn_node_dir);
1244 return SVN_NO_ERROR;
1248 svn_fs_is_file(svn_boolean_t *is_file, svn_fs_root_t *root, const char *path,
1251 svn_node_kind_t kind;
1253 SVN_ERR(root->vtable->check_path(&kind, root, path, pool));
1254 *is_file = (kind == svn_node_file);
1255 return SVN_NO_ERROR;
1259 svn_fs_node_id(const svn_fs_id_t **id_p, svn_fs_root_t *root,
1260 const char *path, apr_pool_t *pool)
1262 return svn_error_trace(root->vtable->node_id(id_p, root, path, pool));
1266 svn_fs_node_relation(svn_fs_node_relation_t *relation,
1267 svn_fs_root_t *root_a, const char *path_a,
1268 svn_fs_root_t *root_b, const char *path_b,
1269 apr_pool_t *scratch_pool)
1271 /* Different repository types? */
1272 if (root_a->fs != root_b->fs)
1274 *relation = svn_fs_node_unrelated;
1275 return SVN_NO_ERROR;
1278 return svn_error_trace(root_a->vtable->node_relation(relation,
1285 svn_fs_node_created_rev(svn_revnum_t *revision, svn_fs_root_t *root,
1286 const char *path, apr_pool_t *pool)
1288 return svn_error_trace(root->vtable->node_created_rev(revision, root, path,
1293 svn_fs_node_origin_rev(svn_revnum_t *revision, svn_fs_root_t *root,
1294 const char *path, apr_pool_t *pool)
1296 return svn_error_trace(root->vtable->node_origin_rev(revision, root, path,
1301 svn_fs_node_created_path(const char **created_path, svn_fs_root_t *root,
1302 const char *path, apr_pool_t *pool)
1304 return svn_error_trace(root->vtable->node_created_path(created_path, root,
1309 svn_fs_node_prop(svn_string_t **value_p, svn_fs_root_t *root,
1310 const char *path, const char *propname, apr_pool_t *pool)
1312 return svn_error_trace(root->vtable->node_prop(value_p, root, path,
1317 svn_fs_node_proplist(apr_hash_t **table_p, svn_fs_root_t *root,
1318 const char *path, apr_pool_t *pool)
1320 return svn_error_trace(root->vtable->node_proplist(table_p, root, path,
1325 svn_fs_node_has_props(svn_boolean_t *has_props,
1326 svn_fs_root_t *root,
1328 apr_pool_t *scratch_pool)
1330 return svn_error_trace(root->vtable->node_has_props(has_props, root, path,
1335 svn_fs_change_node_prop(svn_fs_root_t *root, const char *path,
1336 const char *name, const svn_string_t *value,
1339 return svn_error_trace(root->vtable->change_node_prop(root, path, name,
1344 svn_fs_props_different(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1345 const char *path1, svn_fs_root_t *root2,
1346 const char *path2, apr_pool_t *scratch_pool)
1348 return svn_error_trace(root1->vtable->props_changed(changed_p,
1351 TRUE, scratch_pool));
1355 svn_fs_props_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1356 const char *path1, svn_fs_root_t *root2,
1357 const char *path2, apr_pool_t *pool)
1359 return svn_error_trace(root1->vtable->props_changed(changed_p,
1366 svn_fs_copied_from(svn_revnum_t *rev_p, const char **path_p,
1367 svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1369 return svn_error_trace(root->vtable->copied_from(rev_p, path_p, root, path,
1374 svn_fs_closest_copy(svn_fs_root_t **root_p, const char **path_p,
1375 svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1377 return svn_error_trace(root->vtable->closest_copy(root_p, path_p,
1382 svn_fs_get_mergeinfo3(svn_fs_root_t *root,
1383 const apr_array_header_t *paths,
1384 svn_mergeinfo_inheritance_t inherit,
1385 svn_boolean_t include_descendants,
1386 svn_boolean_t adjust_inherited_mergeinfo,
1387 svn_fs_mergeinfo_receiver_t receiver,
1389 apr_pool_t *scratch_pool)
1391 return svn_error_trace(root->vtable->get_mergeinfo(
1392 root, paths, inherit, include_descendants, adjust_inherited_mergeinfo,
1393 receiver, baton, scratch_pool));
1396 /* Baton type to be used with mergeinfo_receiver(). It provides some of
1397 * the parameters passed to svn_fs__get_mergeinfo_for_path. */
1398 typedef struct mergeinfo_receiver_baton_t
1400 svn_mergeinfo_t *mergeinfo;
1401 apr_pool_t *result_pool;
1402 } mergeinfo_receiver_baton_t;
1404 static svn_error_t *
1405 mergeinfo_receiver(const char *path,
1406 svn_mergeinfo_t mergeinfo,
1408 apr_pool_t *scratch_pool)
1410 mergeinfo_receiver_baton_t *b = baton;
1411 *b->mergeinfo = svn_mergeinfo_dup(mergeinfo, b->result_pool);
1413 return SVN_NO_ERROR;
1417 svn_fs__get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo,
1418 svn_fs_root_t *root,
1420 svn_mergeinfo_inheritance_t inherit,
1421 svn_boolean_t adjust_inherited_mergeinfo,
1422 apr_pool_t *result_pool,
1423 apr_pool_t *scratch_pool)
1425 apr_array_header_t *paths
1426 = apr_array_make(scratch_pool, 1, sizeof(const char *));
1428 mergeinfo_receiver_baton_t baton;
1429 baton.mergeinfo = mergeinfo;
1430 baton.result_pool = result_pool;
1432 APR_ARRAY_PUSH(paths, const char *) = path;
1435 SVN_ERR(svn_fs_get_mergeinfo3(root, paths,
1436 inherit, FALSE /*include_descendants*/,
1437 adjust_inherited_mergeinfo,
1438 mergeinfo_receiver, &baton,
1441 return SVN_NO_ERROR;
1445 svn_fs_merge(const char **conflict_p, svn_fs_root_t *source_root,
1446 const char *source_path, svn_fs_root_t *target_root,
1447 const char *target_path, svn_fs_root_t *ancestor_root,
1448 const char *ancestor_path, apr_pool_t *pool)
1450 return svn_error_trace(target_root->vtable->merge(conflict_p,
1451 source_root, source_path,
1452 target_root, target_path,
1454 ancestor_path, pool));
1458 svn_fs_dir_entries(apr_hash_t **entries_p, svn_fs_root_t *root,
1459 const char *path, apr_pool_t *pool)
1461 return svn_error_trace(root->vtable->dir_entries(entries_p, root, path,
1466 svn_fs_dir_optimal_order(apr_array_header_t **ordered_p,
1467 svn_fs_root_t *root,
1468 apr_hash_t *entries,
1469 apr_pool_t *result_pool,
1470 apr_pool_t *scratch_pool)
1472 return svn_error_trace(root->vtable->dir_optimal_order(ordered_p, root,
1479 svn_fs_make_dir(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1481 SVN_ERR(svn_fs__path_valid(path, pool));
1482 return svn_error_trace(root->vtable->make_dir(root, path, pool));
1486 svn_fs_delete(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1488 return svn_error_trace(root->vtable->delete_node(root, path, pool));
1492 svn_fs_copy(svn_fs_root_t *from_root, const char *from_path,
1493 svn_fs_root_t *to_root, const char *to_path, apr_pool_t *pool)
1495 SVN_ERR(svn_fs__path_valid(to_path, pool));
1496 return svn_error_trace(to_root->vtable->copy(from_root, from_path,
1497 to_root, to_path, pool));
1501 svn_fs_revision_link(svn_fs_root_t *from_root, svn_fs_root_t *to_root,
1502 const char *path, apr_pool_t *pool)
1504 return svn_error_trace(to_root->vtable->revision_link(from_root, to_root,
1509 svn_fs_file_length(svn_filesize_t *length_p, svn_fs_root_t *root,
1510 const char *path, apr_pool_t *pool)
1512 return svn_error_trace(root->vtable->file_length(length_p, root, path,
1517 svn_fs_file_checksum(svn_checksum_t **checksum,
1518 svn_checksum_kind_t kind,
1519 svn_fs_root_t *root,
1521 svn_boolean_t force,
1524 SVN_ERR(root->vtable->file_checksum(checksum, kind, root, path, pool));
1526 if (force && (*checksum == NULL || (*checksum)->kind != kind))
1528 svn_stream_t *contents;
1530 SVN_ERR(svn_fs_file_contents(&contents, root, path, pool));
1531 SVN_ERR(svn_stream_contents_checksum(checksum, contents, kind,
1535 return SVN_NO_ERROR;
1539 svn_fs_file_contents(svn_stream_t **contents, svn_fs_root_t *root,
1540 const char *path, apr_pool_t *pool)
1542 return svn_error_trace(root->vtable->file_contents(contents, root, path,
1547 svn_fs_try_process_file_contents(svn_boolean_t *success,
1548 svn_fs_root_t *root,
1550 svn_fs_process_contents_func_t processor,
1554 /* if the FS doesn't implement this function, report a "failed" attempt */
1555 if (root->vtable->try_process_file_contents == NULL)
1558 return SVN_NO_ERROR;
1561 return svn_error_trace(root->vtable->try_process_file_contents(
1564 processor, baton, pool));
1568 svn_fs_make_file(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1570 SVN_ERR(svn_fs__path_valid(path, pool));
1571 return svn_error_trace(root->vtable->make_file(root, path, pool));
1575 svn_fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
1576 void **contents_baton_p, svn_fs_root_t *root,
1577 const char *path, const char *base_checksum,
1578 const char *result_checksum, apr_pool_t *pool)
1580 svn_checksum_t *base, *result;
1582 /* TODO: If we ever rev this API, we should make the supplied checksums
1583 svn_checksum_t structs. */
1584 SVN_ERR(svn_checksum_parse_hex(&base, svn_checksum_md5, base_checksum,
1586 SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum,
1589 return svn_error_trace(root->vtable->apply_textdelta(contents_p,
1599 svn_fs_apply_text(svn_stream_t **contents_p, svn_fs_root_t *root,
1600 const char *path, const char *result_checksum,
1603 svn_checksum_t *result;
1605 /* TODO: If we ever rev this API, we should make the supplied checksum an
1606 svn_checksum_t struct. */
1607 SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum,
1610 return svn_error_trace(root->vtable->apply_text(contents_p, root, path,
1615 svn_fs_contents_different(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1616 const char *path1, svn_fs_root_t *root2,
1617 const char *path2, apr_pool_t *scratch_pool)
1619 return svn_error_trace(root1->vtable->contents_changed(changed_p,
1627 svn_fs_contents_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1628 const char *path1, svn_fs_root_t *root2,
1629 const char *path2, apr_pool_t *pool)
1631 return svn_error_trace(root1->vtable->contents_changed(changed_p,
1638 svn_fs_youngest_rev(svn_revnum_t *youngest_p, svn_fs_t *fs, apr_pool_t *pool)
1640 return svn_error_trace(fs->vtable->youngest_rev(youngest_p, fs, pool));
1644 svn_fs_info_format(int *fs_format,
1645 svn_version_t **supports_version,
1647 apr_pool_t *result_pool,
1648 apr_pool_t *scratch_pool)
1650 return svn_error_trace(fs->vtable->info_format(fs_format, supports_version,
1652 result_pool, scratch_pool));
1656 svn_fs_info_config_files(apr_array_header_t **files,
1658 apr_pool_t *result_pool,
1659 apr_pool_t *scratch_pool)
1661 return svn_error_trace(fs->vtable->info_config_files(files, fs,
1667 svn_fs_deltify_revision(svn_fs_t *fs, svn_revnum_t revision, apr_pool_t *pool)
1669 return svn_error_trace(fs->vtable->deltify(fs, revision, pool));
1673 svn_fs_refresh_revision_props(svn_fs_t *fs,
1674 apr_pool_t *scratch_pool)
1676 return svn_error_trace(fs->vtable->refresh_revprops(fs, scratch_pool));
1680 svn_fs_revision_prop2(svn_string_t **value_p,
1683 const char *propname,
1684 svn_boolean_t refresh,
1685 apr_pool_t *result_pool,
1686 apr_pool_t *scratch_pool)
1688 return svn_error_trace(fs->vtable->revision_prop(value_p, fs, rev,
1695 svn_fs_revision_proplist2(apr_hash_t **table_p,
1698 svn_boolean_t refresh,
1699 apr_pool_t *result_pool,
1700 apr_pool_t *scratch_pool)
1702 return svn_error_trace(fs->vtable->revision_proplist(table_p, fs, rev,
1709 svn_fs_change_rev_prop2(svn_fs_t *fs, svn_revnum_t rev, const char *name,
1710 const svn_string_t *const *old_value_p,
1711 const svn_string_t *value, apr_pool_t *pool)
1713 return svn_error_trace(fs->vtable->change_rev_prop(fs, rev, name,
1719 svn_fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
1720 svn_fs_root_t *source_root,
1721 const char *source_path,
1722 svn_fs_root_t *target_root,
1723 const char *target_path, apr_pool_t *pool)
1725 return svn_error_trace(target_root->vtable->get_file_delta_stream(
1727 source_root, source_path,
1728 target_root, target_path, pool));
1732 svn_fs__get_deleted_node(svn_fs_root_t **node_root,
1733 const char **node_path,
1734 svn_fs_root_t *root,
1736 apr_pool_t *result_pool,
1737 apr_pool_t *scratch_pool)
1739 const char *parent_path, *name;
1740 svn_fs_root_t *copy_root;
1741 const char *copy_path;
1743 /* History traversal does not work with transaction roots.
1744 * Therefore, do it "by hand". */
1746 /* If the parent got copied in ROOT, PATH got copied with it.
1747 * Otherwise, we will find the node at PATH in the revision prior to ROOT.
1749 svn_fspath__split(&parent_path, &name, path, scratch_pool);
1750 SVN_ERR(svn_fs_closest_copy(©_root, ©_path, root, parent_path,
1753 /* Copied in ROOT? */
1755 && ( svn_fs_revision_root_revision(copy_root)
1756 == svn_fs_revision_root_revision(root)))
1758 svn_revnum_t copyfrom_rev;
1759 const char *copyfrom_path;
1760 const char *rel_path;
1761 SVN_ERR(svn_fs_copied_from(©from_rev, ©from_path,
1762 copy_root, copy_path, scratch_pool));
1764 SVN_ERR(svn_fs_revision_root(node_root, svn_fs_root_fs(root),
1765 copyfrom_rev, result_pool));
1766 rel_path = svn_fspath__skip_ancestor(copy_path, path);
1767 *node_path = svn_fspath__join(copyfrom_path, rel_path, result_pool);
1771 svn_revnum_t revision;
1772 svn_revnum_t previous_rev;
1774 /* Determine the latest revision before ROOT. */
1775 revision = svn_fs_revision_root_revision(root);
1776 if (SVN_IS_VALID_REVNUM(revision))
1777 previous_rev = revision - 1;
1779 previous_rev = svn_fs_txn_root_base_revision(root);
1781 SVN_ERR(svn_fs_revision_root(node_root, svn_fs_root_fs(root),
1782 previous_rev, result_pool));
1783 *node_path = apr_pstrdup(result_pool, path);
1786 return SVN_NO_ERROR;
1790 svn_fs_get_uuid(svn_fs_t *fs, const char **uuid, apr_pool_t *pool)
1792 /* If you change this, consider changing svn_fs__identifier(). */
1793 *uuid = apr_pstrdup(pool, fs->uuid);
1794 return SVN_NO_ERROR;
1798 svn_fs_set_uuid(svn_fs_t *fs, const char *uuid, apr_pool_t *pool)
1802 uuid = svn_uuid_generate(pool);
1806 apr_uuid_t parsed_uuid;
1807 apr_status_t apr_err = apr_uuid_parse(&parsed_uuid, uuid);
1809 return svn_error_createf(SVN_ERR_BAD_UUID, NULL,
1810 _("Malformed UUID '%s'"), uuid);
1812 return svn_error_trace(fs->vtable->set_uuid(fs, uuid, pool));
1816 svn_fs_lock_many(svn_fs_t *fs,
1817 apr_hash_t *targets,
1818 const char *comment,
1819 svn_boolean_t is_dav_comment,
1820 apr_time_t expiration_date,
1821 svn_boolean_t steal_lock,
1822 svn_fs_lock_callback_t lock_callback,
1824 apr_pool_t *result_pool,
1825 apr_pool_t *scratch_pool)
1827 apr_hash_index_t *hi;
1828 apr_hash_t *ok_targets = apr_hash_make(scratch_pool);
1829 svn_error_t *err, *cb_err = SVN_NO_ERROR;
1831 /* Enforce that the comment be xml-escapable. */
1833 if (! svn_xml_is_xml_safe(comment, strlen(comment)))
1834 return svn_error_create(SVN_ERR_XML_UNESCAPABLE_DATA, NULL,
1835 _("Lock comment contains illegal characters"));
1837 if (expiration_date < 0)
1838 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
1839 _("Negative expiration date passed to svn_fs_lock"));
1841 /* Enforce that the token be an XML-safe URI. */
1842 for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi))
1844 const svn_fs_lock_target_t *target = apr_hash_this_val(hi);
1852 if (strncmp(target->token, "opaquelocktoken:", 16))
1853 err = svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1854 _("Lock token URI '%s' has bad scheme; "
1856 target->token, "opaquelocktoken");
1860 for (c = target->token; *c && !err; c++)
1861 if (! svn_ctype_isascii(*c) || svn_ctype_iscntrl(*c))
1862 err = svn_error_createf(
1863 SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1864 _("Lock token '%s' is not ASCII or is a "
1865 "control character at byte %u"),
1867 (unsigned)(c - target->token));
1869 /* strlen(token) == c - token. */
1870 if (!err && !svn_xml_is_xml_safe(target->token,
1872 err = svn_error_createf(
1873 SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1874 _("Lock token URI '%s' is not XML-safe"),
1881 if (!cb_err && lock_callback)
1882 cb_err = lock_callback(lock_baton, apr_hash_this_key(hi),
1883 NULL, err, scratch_pool);
1884 svn_error_clear(err);
1887 svn_hash_sets(ok_targets, apr_hash_this_key(hi), target);
1890 if (!apr_hash_count(ok_targets))
1891 return svn_error_trace(cb_err);
1893 err = fs->vtable->lock(fs, ok_targets, comment, is_dav_comment,
1894 expiration_date, steal_lock,
1895 lock_callback, lock_baton,
1896 result_pool, scratch_pool);
1899 svn_error_compose(err, cb_err);
1903 return svn_error_trace(err);
1906 struct lock_baton_t {
1907 const svn_lock_t *lock;
1908 svn_error_t *fs_err;
1911 /* Implements svn_fs_lock_callback_t. Used by svn_fs_lock and
1912 svn_fs_unlock to record the lock and error from svn_fs_lock_many
1913 and svn_fs_unlock_many. */
1914 static svn_error_t *
1915 lock_cb(void *lock_baton,
1917 const svn_lock_t *lock,
1918 svn_error_t *fs_err,
1921 struct lock_baton_t *b = lock_baton;
1924 b->fs_err = svn_error_dup(fs_err);
1926 return SVN_NO_ERROR;
1930 svn_fs_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path,
1931 const char *token, const char *comment,
1932 svn_boolean_t is_dav_comment, apr_time_t expiration_date,
1933 svn_revnum_t current_rev, svn_boolean_t steal_lock,
1936 apr_hash_t *targets = apr_hash_make(pool);
1937 svn_fs_lock_target_t target;
1939 struct lock_baton_t baton = {0};
1941 target.token = token;
1942 target.current_rev = current_rev;
1943 svn_hash_sets(targets, path, &target);
1945 err = svn_fs_lock_many(fs, targets, comment, is_dav_comment,
1946 expiration_date, steal_lock, lock_cb, &baton,
1950 *lock = (svn_lock_t*)baton.lock;
1952 if (err && baton.fs_err)
1953 svn_error_compose(err, baton.fs_err);
1957 return svn_error_trace(err);
1961 svn_fs_generate_lock_token(const char **token, svn_fs_t *fs, apr_pool_t *pool)
1963 return svn_error_trace(fs->vtable->generate_lock_token(token, fs, pool));
1966 svn_fs_lock_target_t *
1967 svn_fs_lock_target_create(const char *token,
1968 svn_revnum_t current_rev,
1969 apr_pool_t *result_pool)
1971 svn_fs_lock_target_t *target = apr_palloc(result_pool, sizeof(*target));
1973 target->token = token;
1974 target->current_rev = current_rev;
1980 svn_fs_lock_target_set_token(svn_fs_lock_target_t *target,
1983 target->token = token;
1987 svn_fs_unlock_many(svn_fs_t *fs,
1988 apr_hash_t *targets,
1989 svn_boolean_t break_lock,
1990 svn_fs_lock_callback_t lock_callback,
1992 apr_pool_t *result_pool,
1993 apr_pool_t *scratch_pool)
1995 return svn_error_trace(fs->vtable->unlock(fs, targets, break_lock,
1996 lock_callback, lock_baton,
1997 result_pool, scratch_pool));
2001 svn_fs_unlock(svn_fs_t *fs, const char *path, const char *token,
2002 svn_boolean_t break_lock, apr_pool_t *pool)
2004 apr_hash_t *targets = apr_hash_make(pool);
2006 struct lock_baton_t baton = {0};
2010 svn_hash_sets(targets, path, token);
2012 err = svn_fs_unlock_many(fs, targets, break_lock, lock_cb, &baton,
2015 if (err && baton.fs_err)
2016 svn_error_compose(err, baton.fs_err);
2020 return svn_error_trace(err);
2024 svn_fs_get_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path,
2027 return svn_error_trace(fs->vtable->get_lock(lock, fs, path, pool));
2031 svn_fs_get_locks2(svn_fs_t *fs, const char *path, svn_depth_t depth,
2032 svn_fs_get_locks_callback_t get_locks_func,
2033 void *get_locks_baton, apr_pool_t *pool)
2035 SVN_ERR_ASSERT((depth == svn_depth_empty) ||
2036 (depth == svn_depth_files) ||
2037 (depth == svn_depth_immediates) ||
2038 (depth == svn_depth_infinity));
2039 return svn_error_trace(fs->vtable->get_locks(fs, path, depth,
2041 get_locks_baton, pool));
2045 /* --- History functions --- */
2048 svn_fs_history_prev2(svn_fs_history_t **prev_history_p,
2049 svn_fs_history_t *history, svn_boolean_t cross_copies,
2050 apr_pool_t *result_pool, apr_pool_t *scratch_pool)
2052 return svn_error_trace(history->vtable->prev(prev_history_p, history,
2053 cross_copies, result_pool,
2058 svn_fs_history_location(const char **path, svn_revnum_t *revision,
2059 svn_fs_history_t *history, apr_pool_t *pool)
2061 return svn_error_trace(history->vtable->location(path, revision, history,
2066 /* --- Node-ID functions --- */
2069 svn_fs_parse_id(const char *data, apr_size_t len, apr_pool_t *pool)
2071 fs_library_vtable_t *vtable;
2074 err = get_library_vtable(&vtable, SVN_FS_TYPE_BDB, pool);
2077 svn_error_clear(err);
2080 return vtable->parse_id(data, len, pool);
2084 svn_fs_unparse_id(const svn_fs_id_t *id, apr_pool_t *pool)
2086 return id->vtable->unparse(id, pool);
2090 svn_fs_check_related(const svn_fs_id_t *a, const svn_fs_id_t *b)
2092 return (a->vtable->compare(a, b) != svn_fs_node_unrelated);
2096 svn_fs_compare_ids(const svn_fs_id_t *a, const svn_fs_id_t *b)
2098 switch (a->vtable->compare(a, b))
2100 case svn_fs_node_unchanged:
2102 case svn_fs_node_common_ancestor:
2110 svn_fs_print_modules(svn_stringbuf_t *output,
2113 struct fs_type_defn *defn = fs_modules;
2114 fs_library_vtable_t *vtable;
2115 apr_pool_t *iterpool = svn_pool_create(pool);
2122 svn_pool_clear(iterpool);
2124 err = get_library_vtable_direct(&vtable, defn, iterpool);
2127 if (err->apr_err == SVN_ERR_FS_UNKNOWN_FS_TYPE)
2129 svn_error_clear(err);
2137 line = apr_psprintf(iterpool, "* fs_%s : %s\n",
2138 defn->fsap_name, vtable->get_description());
2139 svn_stringbuf_appendcstr(output, line);
2143 svn_pool_destroy(iterpool);
2145 return SVN_NO_ERROR;
2148 svn_fs_path_change2_t *
2149 svn_fs_path_change2_create(const svn_fs_id_t *node_rev_id,
2150 svn_fs_path_change_kind_t change_kind,
2153 return svn_fs__path_change_create_internal(node_rev_id, change_kind, pool);
2156 svn_fs_path_change3_t *
2157 svn_fs_path_change3_create(svn_fs_path_change_kind_t change_kind,
2158 apr_pool_t *result_pool)
2160 return svn_fs__path_change_create_internal2(change_kind, result_pool);
2163 svn_fs_path_change3_t *
2164 svn_fs_path_change3_dup(svn_fs_path_change3_t *change,
2165 apr_pool_t *result_pool)
2167 svn_fs_path_change3_t *copy = apr_pmemdup(result_pool, change,
2170 copy->path.data = apr_pstrmemdup(result_pool, copy->path.data,
2172 if (copy->copyfrom_path)
2173 copy->copyfrom_path = apr_pstrdup(result_pool, change->copyfrom_path);
2178 /* Return the library version number. */
2179 const svn_version_t *
2180 svn_fs_version(void)
2188 svn_fs_info(const svn_fs_info_placeholder_t **info_p,
2190 apr_pool_t *result_pool,
2191 apr_pool_t *scratch_pool)
2193 if (fs->vtable->info_fsap)
2195 SVN_ERR(fs->vtable->info_fsap((const void **)info_p, fs,
2196 result_pool, scratch_pool));
2200 svn_fs_info_placeholder_t *info = apr_palloc(result_pool, sizeof(*info));
2201 /* ### Ask the disk(!), since svn_fs_t doesn't cache the answer. */
2202 SVN_ERR(svn_fs_type(&info->fs_type, fs->path, result_pool));
2205 return SVN_NO_ERROR;
2209 svn_fs_info_dup(const void *info_void,
2210 apr_pool_t *result_pool,
2211 apr_pool_t *scratch_pool)
2213 const svn_fs_info_placeholder_t *info = info_void;
2214 fs_library_vtable_t *vtable;
2216 SVN_ERR(get_library_vtable(&vtable, info->fs_type, scratch_pool));
2218 if (vtable->info_fsap_dup)
2219 return vtable->info_fsap_dup(info_void, result_pool);
2221 return apr_pmemdup(result_pool, info, sizeof(*info));