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_thread_mutex.h>
32 #include <apr_strings.h>
34 #include "svn_private_config.h"
36 #include "svn_ctype.h"
37 #include "svn_types.h"
39 #include "svn_version.h"
43 #include "svn_pools.h"
44 #include "svn_string.h"
45 #include "svn_sorts.h"
47 #include "private/svn_atomic.h"
48 #include "private/svn_fs_private.h"
49 #include "private/svn_fs_util.h"
50 #include "private/svn_utf_private.h"
51 #include "private/svn_mutex.h"
52 #include "private/svn_subr_private.h"
54 #include "fs-loader.h"
56 /* This is defined by configure on platforms which use configure, but
57 we need to define a fallback for Windows. */
58 #ifndef DEFAULT_FS_TYPE
59 #define DEFAULT_FS_TYPE "fsfs"
62 #define FS_TYPE_FILENAME "fs-type"
64 /* A pool common to all FS objects. See the documentation on the
65 open/create functions in fs-loader.h and for svn_fs_initialize(). */
66 static apr_pool_t *common_pool = NULL;
67 static svn_mutex__t *common_pool_lock = NULL;
68 static svn_atomic_t common_pool_initialized = FALSE;
71 /* --- Utility functions for the loader --- */
75 const char *fsap_name;
76 fs_init_func_t initfunc;
77 fs_library_vtable_t *vtable;
78 struct fs_type_defn *next;
81 static struct fs_type_defn base_defn =
83 SVN_FS_TYPE_BDB, "base",
84 #ifdef SVN_LIBSVN_FS_LINKS_FS_BASE
90 NULL /* End of static list: this needs to be reset to NULL if the
91 common_pool used when setting it has been cleared. */
94 static struct fs_type_defn fsx_defn =
97 #ifdef SVN_LIBSVN_FS_LINKS_FS_X
106 static struct fs_type_defn fsfs_defn =
108 SVN_FS_TYPE_FSFS, "fs",
109 #ifdef SVN_LIBSVN_FS_LINKS_FS_FS
118 static struct fs_type_defn *fs_modules = &fsfs_defn;
122 load_module(fs_init_func_t *initfunc, const char *name, apr_pool_t *pool)
126 #if defined(SVN_USE_DSO) && APR_HAS_DSO
128 apr_dso_handle_t *dso;
129 apr_dso_handle_sym_t symbol;
131 const char *funcname;
135 /* Demand a simple alphanumeric name so that the generated DSO
137 for (p = name; *p; ++p)
138 if (!svn_ctype_isalnum(*p))
139 return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
140 _("Invalid name for FS type '%s'"),
143 libname = apr_psprintf(pool, "libsvn_fs_%s-%d.so.%d",
144 name, SVN_VER_MAJOR, SVN_SOVERSION);
145 funcname = apr_psprintf(pool, "svn_fs_%s__init", name);
147 /* Find/load the specified library. If we get an error, assume
148 the library doesn't exist. The library will be unloaded when
149 pool is destroyed. */
150 SVN_ERR(svn_dso_load(&dso, libname));
154 /* find the initialization routine */
155 status = apr_dso_sym(&symbol, dso, funcname);
157 return svn_error_wrap_apr(status, _("'%s' does not define '%s()'"),
160 *initfunc = (fs_init_func_t) symbol;
162 #endif /* APR_HAS_DSO */
167 /* Fetch a library vtable by a pointer into the library definitions array. */
169 get_library_vtable_direct(fs_library_vtable_t **vtable,
170 struct fs_type_defn *fst,
173 fs_init_func_t initfunc = NULL;
174 const svn_version_t *my_version = svn_fs_version();
175 const svn_version_t *fs_version;
177 /* most times, we get lucky */
178 *vtable = apr_atomic_casptr((volatile void **)&fst->vtable, NULL, NULL);
182 /* o.k. the first access needs to actually load the module, find the
183 vtable and check for version compatibility. */
184 initfunc = fst->initfunc;
186 SVN_ERR(load_module(&initfunc, fst->fsap_name, pool));
189 return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
190 _("Failed to load module for FS type '%s'"),
194 /* Per our API compatibility rules, we cannot ensure that
195 svn_fs_initialize is called by the application. If not, we
196 cannot create the common pool and lock in a thread-safe fashion,
197 nor can we clean up the common pool if libsvn_fs is dynamically
198 unloaded. This function makes a best effort by creating the
199 common pool as a child of the global pool; the window of failure
200 due to thread collision is small. */
201 SVN_ERR(svn_fs_initialize(NULL));
203 /* Invoke the FS module's initfunc function with the common
204 pool protected by a lock. */
205 SVN_MUTEX__WITH_LOCK(common_pool_lock,
206 initfunc(my_version, vtable, common_pool));
208 fs_version = (*vtable)->get_version();
209 if (!svn_ver_equal(my_version, fs_version))
210 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
211 _("Mismatched FS module version for '%s':"
213 " expected %d.%d.%d%s"),
215 my_version->major, my_version->minor,
216 my_version->patch, my_version->tag,
217 fs_version->major, fs_version->minor,
218 fs_version->patch, fs_version->tag);
220 /* the vtable will not change. Remember it */
221 apr_atomic_casptr((volatile void **)&fst->vtable, *vtable, NULL);
226 #if defined(SVN_USE_DSO) && APR_HAS_DSO
227 /* Return *FST for the third party FS_TYPE */
229 get_or_allocate_third(struct fs_type_defn **fst,
234 if (strcmp(fs_type, (*fst)->fs_type) == 0)
239 *fst = apr_palloc(common_pool, sizeof(struct fs_type_defn));
240 (*fst)->fs_type = apr_pstrdup(common_pool, fs_type);
241 (*fst)->fsap_name = (*fst)->fs_type;
242 (*fst)->initfunc = NULL;
243 (*fst)->vtable = NULL;
250 /* Fetch a library *VTABLE by FS_TYPE.
251 Use POOL for temporary allocations. */
253 get_library_vtable(fs_library_vtable_t **vtable, const char *fs_type,
256 struct fs_type_defn **fst;
257 svn_boolean_t known = FALSE;
259 /* There are three FS module definitions known at compile time. We
260 want to check these without any locking overhead even when
261 dynamic third party modules are enabled. The third party modules
262 cannot be checked until the lock is held. */
263 for (fst = &fs_modules; *fst; fst = &(*fst)->next)
265 if (strcmp(fs_type, (*fst)->fs_type) == 0)
270 else if (!(*fst)->next)
276 #if defined(SVN_USE_DSO) && APR_HAS_DSO
277 /* Third party FS modules that are unknown at compile time.
279 A third party FS is identified by the file fs-type containing a
280 third party name, say "foo". The loader will load the DSO with
281 the name "libsvn_fs_foo" and use the entry point with the name
284 Note: the BDB and FSFS modules don't follow this naming scheme
285 and this allows them to be used to test the third party loader.
286 Change the content of fs-type to "base" in a BDB filesystem or to
287 "fs" in an FSFS filesystem and they will be loaded as third party
292 /* Best-effort init, see get_library_vtable_direct. */
293 SVN_ERR(svn_fs_initialize(NULL));
294 SVN_MUTEX__WITH_LOCK(common_pool_lock,
295 get_or_allocate_third(fst, fs_type));
300 return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
301 _("Unknown FS type '%s'"), fs_type);
302 return get_library_vtable_direct(vtable, *fst, pool);
306 svn_fs_type(const char **fs_type, const char *path, apr_pool_t *pool)
308 const char *filename;
314 /* Read the fsap-name file to get the FSAP name, or assume the (old)
315 default. For old repositories I suppose we could check some
316 other file, DB_CONFIG or strings say, but for now just check the
318 filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool);
319 err = svn_io_file_open(&file, filename, APR_READ|APR_BUFFERED, 0, pool);
320 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
322 svn_node_kind_t kind;
323 svn_error_t *err2 = svn_io_check_path(path, &kind, pool);
326 svn_error_clear(err2);
329 if (kind == svn_node_dir)
331 svn_error_clear(err);
332 *fs_type = SVN_FS_TYPE_BDB;
341 SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
342 SVN_ERR(svn_io_file_close(file, pool));
343 *fs_type = apr_pstrdup(pool, buf);
348 /* Fetch the library vtable for an existing FS. */
350 fs_library_vtable(fs_library_vtable_t **vtable, const char *path,
355 SVN_ERR(svn_fs_type(&fs_type, path, pool));
357 /* Fetch the library vtable by name, now that we've chosen one. */
358 SVN_ERR(get_library_vtable(vtable, fs_type, pool));
364 write_fs_type(const char *path, const char *fs_type, apr_pool_t *pool)
366 const char *filename;
369 filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool);
370 SVN_ERR(svn_io_file_open(&file, filename,
371 APR_WRITE|APR_CREATE|APR_TRUNCATE|APR_BUFFERED,
372 APR_OS_DEFAULT, pool));
373 SVN_ERR(svn_io_file_write_full(file, fs_type, strlen(fs_type), NULL,
375 SVN_ERR(svn_io_file_write_full(file, "\n", 1, NULL, pool));
376 return svn_error_trace(svn_io_file_close(file, pool));
380 /* --- Functions for operating on filesystems by pathname --- */
382 static apr_status_t uninit(void *data)
385 common_pool_lock = NULL;
386 common_pool_initialized = 0;
392 synchronized_initialize(void *baton, apr_pool_t *pool)
394 common_pool = svn_pool_create(pool);
395 base_defn.next = NULL;
396 SVN_ERR(svn_mutex__init(&common_pool_lock, TRUE, common_pool));
398 /* ### This won't work if POOL is NULL and libsvn_fs is loaded as a DSO
399 ### (via libsvn_ra_local say) since the global common_pool will live
400 ### longer than the DSO, which gets unloaded when the pool used to
401 ### load it is cleared, and so when the handler runs it will refer to
402 ### a function that no longer exists. libsvn_ra_local attempts to
403 ### work around this by explicitly calling svn_fs_initialize. */
404 apr_pool_cleanup_register(common_pool, NULL, uninit, apr_pool_cleanup_null);
409 svn_fs_initialize(apr_pool_t *pool)
411 #if defined(SVN_USE_DSO) && APR_HAS_DSO
412 /* Ensure that DSO subsystem is initialized early as possible if
413 we're going to use it. */
414 SVN_ERR(svn_dso_initialize2());
416 /* Protect against multiple calls. */
417 return svn_error_trace(svn_atomic__init_once(&common_pool_initialized,
418 synchronized_initialize,
422 /* A default warning handling function. */
424 default_warning_func(void *baton, svn_error_t *err)
426 /* The one unforgiveable sin is to fail silently. Dumping to stderr
427 or /dev/tty is not acceptable default behavior for server
428 processes, since those may both be equivalent to /dev/null.
430 That said, be a good citizen and print something anyway, in case it goes
431 somewhere, and our caller hasn't overridden the abort() call.
433 if (svn_error_get_malfunction_handler()
434 == svn_error_abort_on_malfunction)
435 /* ### TODO: extend the malfunction API such that non-abort()ing consumers
436 ### also get the information on ERR. */
437 svn_handle_error2(err, stderr, FALSE /* fatal */, "svn: fs-loader: ");
438 SVN_ERR_MALFUNCTION_NO_RETURN();
442 svn_fs__path_valid(const char *path, apr_pool_t *pool)
446 /* UTF-8 encoded string without NULs. */
447 if (! svn_utf__cstring_is_valid(path))
449 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
450 _("Path '%s' is not in UTF-8"), path);
453 /* No "." or ".." elements. */
454 if (svn_path_is_backpath_present(path)
455 || svn_path_is_dotpath_present(path))
457 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
458 _("Path '%s' contains '.' or '..' element"),
462 /* Raise an error if PATH contains a newline because svn:mergeinfo and
463 friends can't handle them. Issue #4340 describes a similar problem
464 in the FSFS code itself.
466 c = strchr(path, '\n');
469 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
470 _("Invalid control character '0x%02x' in path '%s'"),
471 (unsigned char)*c, svn_path_illegal_path_escape(path, pool));
474 /* That's good enough. */
478 /* Allocate svn_fs_t structure. */
480 fs_new(apr_hash_t *fs_config, apr_pool_t *pool)
482 svn_fs_t *fs = apr_palloc(pool, sizeof(*fs));
485 fs->warning = default_warning_func;
486 fs->warning_baton = NULL;
487 fs->config = fs_config;
488 fs->access_ctx = NULL;
490 fs->fsap_data = NULL;
496 svn_fs_new(apr_hash_t *fs_config, apr_pool_t *pool)
498 return fs_new(fs_config, pool);
502 svn_fs_set_warning_func(svn_fs_t *fs, svn_fs_warning_callback_t warning,
505 fs->warning = warning;
506 fs->warning_baton = warning_baton;
510 svn_fs_create(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config,
513 fs_library_vtable_t *vtable;
515 const char *fs_type = svn_hash__get_cstring(fs_config,
516 SVN_FS_CONFIG_FS_TYPE,
518 SVN_ERR(get_library_vtable(&vtable, fs_type, pool));
520 /* Create the FS directory and write out the fsap-name file. */
521 SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, pool));
522 SVN_ERR(write_fs_type(path, fs_type, pool));
524 /* Perform the actual creation. */
525 *fs_p = fs_new(fs_config, pool);
527 SVN_ERR(vtable->create(*fs_p, path, common_pool_lock, pool, common_pool));
528 SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open2));
534 svn_fs_open2(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config,
535 apr_pool_t *result_pool,
536 apr_pool_t *scratch_pool)
538 fs_library_vtable_t *vtable;
540 SVN_ERR(fs_library_vtable(&vtable, path, scratch_pool));
541 *fs_p = fs_new(fs_config, result_pool);
542 SVN_ERR(vtable->open_fs(*fs_p, path, common_pool_lock, scratch_pool,
544 SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open2));
550 svn_fs_open(svn_fs_t **fs_p,
552 apr_hash_t *fs_config,
555 return svn_fs_open2(fs_p, path, fs_config, pool, pool);
559 svn_fs_upgrade2(const char *path,
560 svn_fs_upgrade_notify_t notify_func,
562 svn_cancel_func_t cancel_func,
564 apr_pool_t *scratch_pool)
566 fs_library_vtable_t *vtable;
569 SVN_ERR(fs_library_vtable(&vtable, path, scratch_pool));
570 fs = fs_new(NULL, scratch_pool);
572 SVN_ERR(vtable->upgrade_fs(fs, path,
573 notify_func, notify_baton,
574 cancel_func, cancel_baton,
576 scratch_pool, common_pool));
580 /* A warning handling function that does not abort on errors,
581 but just lets them be returned normally. */
583 verify_fs_warning_func(void *baton, svn_error_t *err)
588 svn_fs_verify(const char *path,
589 apr_hash_t *fs_config,
592 svn_fs_progress_notify_func_t notify_func,
594 svn_cancel_func_t cancel_func,
598 fs_library_vtable_t *vtable;
601 SVN_ERR(fs_library_vtable(&vtable, path, pool));
602 fs = fs_new(fs_config, pool);
603 svn_fs_set_warning_func(fs, verify_fs_warning_func, NULL);
605 SVN_ERR(vtable->verify_fs(fs, path, start, end,
606 notify_func, notify_baton,
607 cancel_func, cancel_baton,
614 svn_fs_path(svn_fs_t *fs, apr_pool_t *pool)
616 return apr_pstrdup(pool, fs->path);
620 svn_fs_config(svn_fs_t *fs, apr_pool_t *pool)
623 return apr_hash_copy(pool, fs->config);
629 svn_fs_delete_fs(const char *path, apr_pool_t *pool)
631 fs_library_vtable_t *vtable;
633 SVN_ERR(fs_library_vtable(&vtable, path, pool));
634 return svn_error_trace(vtable->delete_fs(path, pool));
638 svn_fs_hotcopy3(const char *src_path, const char *dst_path,
639 svn_boolean_t clean, svn_boolean_t incremental,
640 svn_fs_hotcopy_notify_t notify_func,
642 svn_cancel_func_t cancel_func,
644 apr_pool_t *scratch_pool)
646 fs_library_vtable_t *vtable;
647 const char *src_fs_type;
650 const char *dst_fs_type;
651 svn_node_kind_t dst_kind;
653 if (strcmp(src_path, dst_path) == 0)
654 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
655 _("Hotcopy source and destination are equal"));
657 SVN_ERR(svn_fs_type(&src_fs_type, src_path, scratch_pool));
658 SVN_ERR(get_library_vtable(&vtable, src_fs_type, scratch_pool));
659 src_fs = fs_new(NULL, scratch_pool);
660 dst_fs = fs_new(NULL, scratch_pool);
662 SVN_ERR(svn_io_check_path(dst_path, &dst_kind, scratch_pool));
663 if (dst_kind == svn_node_file)
664 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
665 _("'%s' already exists and is a file"),
666 svn_dirent_local_style(dst_path,
668 if (dst_kind == svn_node_unknown)
669 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
670 _("'%s' already exists and has an unknown "
672 svn_dirent_local_style(dst_path,
674 if (dst_kind == svn_node_dir)
676 svn_node_kind_t type_file_kind;
678 SVN_ERR(svn_io_check_path(svn_dirent_join(dst_path,
681 &type_file_kind, scratch_pool));
682 if (type_file_kind != svn_node_none)
684 SVN_ERR(svn_fs_type(&dst_fs_type, dst_path, scratch_pool));
685 if (strcmp(src_fs_type, dst_fs_type) != 0)
686 return svn_error_createf(
687 SVN_ERR_ILLEGAL_TARGET, NULL,
688 _("The filesystem type of the hotcopy source "
689 "('%s') does not match the filesystem "
690 "type of the hotcopy destination ('%s')"),
691 src_fs_type, dst_fs_type);
695 SVN_ERR(vtable->hotcopy(src_fs, dst_fs, src_path, dst_path, clean,
696 incremental, notify_func, notify_baton,
697 cancel_func, cancel_baton, common_pool_lock,
698 scratch_pool, common_pool));
699 return svn_error_trace(write_fs_type(dst_path, src_fs_type, scratch_pool));
703 svn_fs_pack(const char *path,
704 svn_fs_pack_notify_t notify_func,
706 svn_cancel_func_t cancel_func,
710 fs_library_vtable_t *vtable;
713 SVN_ERR(fs_library_vtable(&vtable, path, pool));
714 fs = fs_new(NULL, pool);
716 SVN_ERR(vtable->pack_fs(fs, path, notify_func, notify_baton,
717 cancel_func, cancel_baton, common_pool_lock,
723 svn_fs_recover(const char *path,
724 svn_cancel_func_t cancel_func, void *cancel_baton,
727 fs_library_vtable_t *vtable;
730 SVN_ERR(fs_library_vtable(&vtable, path, pool));
731 fs = fs_new(NULL, pool);
733 SVN_ERR(vtable->open_fs_for_recovery(fs, path, common_pool_lock,
735 return svn_error_trace(vtable->recover(fs, cancel_func, cancel_baton,
740 svn_fs_verify_root(svn_fs_root_t *root,
741 apr_pool_t *scratch_pool)
743 svn_fs_t *fs = root->fs;
744 SVN_ERR(fs->vtable->verify_root(root, scratch_pool));
750 svn_fs_freeze(svn_fs_t *fs,
751 svn_fs_freeze_func_t freeze_func,
755 SVN_ERR(fs->vtable->freeze(fs, freeze_func, freeze_baton, pool));
761 /* --- Berkeley-specific functions --- */
764 svn_fs_create_berkeley(svn_fs_t *fs, const char *path)
766 fs_library_vtable_t *vtable;
768 SVN_ERR(get_library_vtable(&vtable, SVN_FS_TYPE_BDB, fs->pool));
770 /* Create the FS directory and write out the fsap-name file. */
771 SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, fs->pool));
772 SVN_ERR(write_fs_type(path, SVN_FS_TYPE_BDB, fs->pool));
774 /* Perform the actual creation. */
775 SVN_ERR(vtable->create(fs, path, common_pool_lock, fs->pool, common_pool));
776 SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open2));
782 svn_fs_open_berkeley(svn_fs_t *fs, const char *path)
784 fs_library_vtable_t *vtable;
786 SVN_ERR(fs_library_vtable(&vtable, path, fs->pool));
787 SVN_ERR(vtable->open_fs(fs, path, common_pool_lock, fs->pool, common_pool));
788 SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open2));
794 svn_fs_berkeley_path(svn_fs_t *fs, apr_pool_t *pool)
796 return svn_fs_path(fs, pool);
800 svn_fs_delete_berkeley(const char *path, apr_pool_t *pool)
802 return svn_error_trace(svn_fs_delete_fs(path, pool));
806 svn_fs_hotcopy_berkeley(const char *src_path, const char *dest_path,
807 svn_boolean_t clean_logs, apr_pool_t *pool)
809 return svn_error_trace(svn_fs_hotcopy3(src_path, dest_path, clean_logs,
810 FALSE, NULL, NULL, NULL, NULL,
815 svn_fs_berkeley_recover(const char *path, apr_pool_t *pool)
817 return svn_error_trace(svn_fs_recover(path, NULL, NULL, pool));
821 svn_fs_set_berkeley_errcall(svn_fs_t *fs,
822 void (*handler)(const char *errpfx, char *msg))
824 return svn_error_trace(fs->vtable->bdb_set_errcall(fs, handler));
828 svn_fs_berkeley_logfiles(apr_array_header_t **logfiles,
830 svn_boolean_t only_unused,
833 fs_library_vtable_t *vtable;
835 SVN_ERR(fs_library_vtable(&vtable, path, pool));
836 return svn_error_trace(vtable->bdb_logfiles(logfiles, path, only_unused,
841 /* --- Transaction functions --- */
844 svn_fs_begin_txn2(svn_fs_txn_t **txn_p, svn_fs_t *fs, svn_revnum_t rev,
845 apr_uint32_t flags, apr_pool_t *pool)
847 return svn_error_trace(fs->vtable->begin_txn(txn_p, fs, rev, flags, pool));
852 svn_fs_commit_txn(const char **conflict_p, svn_revnum_t *new_rev,
853 svn_fs_txn_t *txn, apr_pool_t *pool)
857 *new_rev = SVN_INVALID_REVNUM;
861 err = txn->vtable->commit(conflict_p, new_rev, txn, pool);
864 /* Check postconditions. */
867 SVN_ERR_ASSERT_E(! (SVN_IS_VALID_REVNUM(*new_rev) && *conflict_p != NULL),
869 SVN_ERR_ASSERT_E((*conflict_p != NULL)
870 == (err && err->apr_err == SVN_ERR_FS_CONFLICT),
881 svn_fs_abort_txn(svn_fs_txn_t *txn, apr_pool_t *pool)
883 return svn_error_trace(txn->vtable->abort(txn, pool));
887 svn_fs_purge_txn(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
889 return svn_error_trace(fs->vtable->purge_txn(fs, txn_id, pool));
893 svn_fs_txn_name(const char **name_p, svn_fs_txn_t *txn, apr_pool_t *pool)
895 *name_p = apr_pstrdup(pool, txn->id);
900 svn_fs_txn_base_revision(svn_fs_txn_t *txn)
902 return txn->base_rev;
906 svn_fs_open_txn(svn_fs_txn_t **txn, svn_fs_t *fs, const char *name,
909 return svn_error_trace(fs->vtable->open_txn(txn, fs, name, pool));
913 svn_fs_list_transactions(apr_array_header_t **names_p, svn_fs_t *fs,
916 return svn_error_trace(fs->vtable->list_transactions(names_p, fs, pool));
920 is_internal_txn_prop(const char *name)
922 return strcmp(name, SVN_FS__PROP_TXN_CHECK_LOCKS) == 0 ||
923 strcmp(name, SVN_FS__PROP_TXN_CHECK_OOD) == 0 ||
924 strcmp(name, SVN_FS__PROP_TXN_CLIENT_DATE) == 0;
928 svn_fs_txn_prop(svn_string_t **value_p, svn_fs_txn_t *txn,
929 const char *propname, apr_pool_t *pool)
931 if (is_internal_txn_prop(propname))
937 return svn_error_trace(txn->vtable->get_prop(value_p, txn, propname, pool));
941 svn_fs_txn_proplist(apr_hash_t **table_p, svn_fs_txn_t *txn, apr_pool_t *pool)
943 SVN_ERR(txn->vtable->get_proplist(table_p, txn, pool));
945 /* Don't give away internal transaction properties. */
946 svn_hash_sets(*table_p, SVN_FS__PROP_TXN_CHECK_LOCKS, NULL);
947 svn_hash_sets(*table_p, SVN_FS__PROP_TXN_CHECK_OOD, NULL);
948 svn_hash_sets(*table_p, SVN_FS__PROP_TXN_CLIENT_DATE, NULL);
954 svn_fs_change_txn_prop(svn_fs_txn_t *txn, const char *name,
955 const svn_string_t *value, apr_pool_t *pool)
957 if (is_internal_txn_prop(name))
958 return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
959 _("Attempt to modify internal transaction "
960 "property '%s'"), name);
962 return svn_error_trace(txn->vtable->change_prop(txn, name, value, pool));
966 svn_fs_change_txn_props(svn_fs_txn_t *txn, const apr_array_header_t *props,
971 for (i = 0; i < props->nelts; ++i)
973 svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
975 if (is_internal_txn_prop(prop->name))
976 return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
977 _("Attempt to modify internal transaction "
978 "property '%s'"), prop->name);
981 return svn_error_trace(txn->vtable->change_props(txn, props, pool));
985 /* --- Root functions --- */
988 svn_fs_revision_root(svn_fs_root_t **root_p, svn_fs_t *fs, svn_revnum_t rev,
991 /* We create a subpool for each root object to allow us to implement
992 svn_fs_close_root. */
993 apr_pool_t *subpool = svn_pool_create(pool);
994 return svn_error_trace(fs->vtable->revision_root(root_p, fs, rev, subpool));
998 svn_fs_txn_root(svn_fs_root_t **root_p, svn_fs_txn_t *txn, apr_pool_t *pool)
1000 /* We create a subpool for each root object to allow us to implement
1001 svn_fs_close_root. */
1002 apr_pool_t *subpool = svn_pool_create(pool);
1003 return svn_error_trace(txn->vtable->root(root_p, txn, subpool));
1007 svn_fs_close_root(svn_fs_root_t *root)
1009 svn_pool_destroy(root->pool);
1013 svn_fs_root_fs(svn_fs_root_t *root)
1019 svn_fs_is_txn_root(svn_fs_root_t *root)
1021 return root->is_txn_root;
1025 svn_fs_is_revision_root(svn_fs_root_t *root)
1027 return !root->is_txn_root;
1031 svn_fs_txn_root_name(svn_fs_root_t *root, apr_pool_t *pool)
1033 return root->is_txn_root ? apr_pstrdup(pool, root->txn) : NULL;
1037 svn_fs_txn_root_base_revision(svn_fs_root_t *root)
1039 return root->is_txn_root ? root->rev : SVN_INVALID_REVNUM;
1043 svn_fs_revision_root_revision(svn_fs_root_t *root)
1045 return root->is_txn_root ? SVN_INVALID_REVNUM : root->rev;
1049 svn_fs_paths_changed2(apr_hash_t **changed_paths_p,
1050 svn_fs_root_t *root,
1053 return root->vtable->paths_changed(changed_paths_p, root, pool);
1057 svn_fs_paths_changed(apr_hash_t **changed_paths_p, svn_fs_root_t *root,
1060 apr_hash_t *changed_paths_new_structs;
1061 apr_hash_index_t *hi;
1063 SVN_ERR(svn_fs_paths_changed2(&changed_paths_new_structs, root, pool));
1064 *changed_paths_p = apr_hash_make(pool);
1065 for (hi = apr_hash_first(pool, changed_paths_new_structs);
1067 hi = apr_hash_next(hi))
1072 svn_fs_path_change2_t *val;
1073 svn_fs_path_change_t *change;
1074 apr_hash_this(hi, &vkey, &klen, &vval);
1076 change = apr_palloc(pool, sizeof(*change));
1077 change->node_rev_id = val->node_rev_id;
1078 change->change_kind = val->change_kind;
1079 change->text_mod = val->text_mod;
1080 change->prop_mod = val->prop_mod;
1081 apr_hash_set(*changed_paths_p, vkey, klen, change);
1083 return SVN_NO_ERROR;
1087 svn_fs_check_path(svn_node_kind_t *kind_p, svn_fs_root_t *root,
1088 const char *path, apr_pool_t *pool)
1090 return svn_error_trace(root->vtable->check_path(kind_p, root, path, pool));
1094 svn_fs_node_history2(svn_fs_history_t **history_p, svn_fs_root_t *root,
1095 const char *path, apr_pool_t *result_pool,
1096 apr_pool_t *scratch_pool)
1098 return svn_error_trace(root->vtable->node_history(history_p, root, path,
1104 svn_fs_node_history(svn_fs_history_t **history_p, svn_fs_root_t *root,
1105 const char *path, apr_pool_t *pool)
1107 return svn_error_trace(root->vtable->node_history(history_p, root, path,
1112 svn_fs_is_dir(svn_boolean_t *is_dir, svn_fs_root_t *root, const char *path,
1115 svn_node_kind_t kind;
1117 SVN_ERR(root->vtable->check_path(&kind, root, path, pool));
1118 *is_dir = (kind == svn_node_dir);
1119 return SVN_NO_ERROR;
1123 svn_fs_is_file(svn_boolean_t *is_file, svn_fs_root_t *root, const char *path,
1126 svn_node_kind_t kind;
1128 SVN_ERR(root->vtable->check_path(&kind, root, path, pool));
1129 *is_file = (kind == svn_node_file);
1130 return SVN_NO_ERROR;
1134 svn_fs_node_id(const svn_fs_id_t **id_p, svn_fs_root_t *root,
1135 const char *path, apr_pool_t *pool)
1137 return svn_error_trace(root->vtable->node_id(id_p, root, path, pool));
1141 svn_fs_node_relation(svn_fs_node_relation_t *relation,
1142 svn_fs_root_t *root_a, const char *path_a,
1143 svn_fs_root_t *root_b, const char *path_b,
1144 apr_pool_t *scratch_pool)
1146 /* Different repository types? */
1147 if (root_a->fs != root_b->fs)
1149 *relation = svn_fs_node_unrelated;
1150 return SVN_NO_ERROR;
1153 return svn_error_trace(root_a->vtable->node_relation(relation,
1160 svn_fs_node_created_rev(svn_revnum_t *revision, svn_fs_root_t *root,
1161 const char *path, apr_pool_t *pool)
1163 return svn_error_trace(root->vtable->node_created_rev(revision, root, path,
1168 svn_fs_node_origin_rev(svn_revnum_t *revision, svn_fs_root_t *root,
1169 const char *path, apr_pool_t *pool)
1171 return svn_error_trace(root->vtable->node_origin_rev(revision, root, path,
1176 svn_fs_node_created_path(const char **created_path, svn_fs_root_t *root,
1177 const char *path, apr_pool_t *pool)
1179 return svn_error_trace(root->vtable->node_created_path(created_path, root,
1184 svn_fs_node_prop(svn_string_t **value_p, svn_fs_root_t *root,
1185 const char *path, const char *propname, apr_pool_t *pool)
1187 return svn_error_trace(root->vtable->node_prop(value_p, root, path,
1192 svn_fs_node_proplist(apr_hash_t **table_p, svn_fs_root_t *root,
1193 const char *path, apr_pool_t *pool)
1195 return svn_error_trace(root->vtable->node_proplist(table_p, root, path,
1200 svn_fs_node_has_props(svn_boolean_t *has_props,
1201 svn_fs_root_t *root,
1203 apr_pool_t *scratch_pool)
1205 return svn_error_trace(root->vtable->node_has_props(has_props, root, path,
1210 svn_fs_change_node_prop(svn_fs_root_t *root, const char *path,
1211 const char *name, const svn_string_t *value,
1214 return svn_error_trace(root->vtable->change_node_prop(root, path, name,
1219 svn_fs_props_different(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1220 const char *path1, svn_fs_root_t *root2,
1221 const char *path2, apr_pool_t *scratch_pool)
1223 return svn_error_trace(root1->vtable->props_changed(changed_p,
1226 TRUE, scratch_pool));
1230 svn_fs_props_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1231 const char *path1, svn_fs_root_t *root2,
1232 const char *path2, apr_pool_t *pool)
1234 return svn_error_trace(root1->vtable->props_changed(changed_p,
1241 svn_fs_copied_from(svn_revnum_t *rev_p, const char **path_p,
1242 svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1244 return svn_error_trace(root->vtable->copied_from(rev_p, path_p, root, path,
1249 svn_fs_closest_copy(svn_fs_root_t **root_p, const char **path_p,
1250 svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1252 return svn_error_trace(root->vtable->closest_copy(root_p, path_p,
1257 svn_fs_get_mergeinfo2(svn_mergeinfo_catalog_t *catalog,
1258 svn_fs_root_t *root,
1259 const apr_array_header_t *paths,
1260 svn_mergeinfo_inheritance_t inherit,
1261 svn_boolean_t include_descendants,
1262 svn_boolean_t adjust_inherited_mergeinfo,
1263 apr_pool_t *result_pool,
1264 apr_pool_t *scratch_pool)
1266 return svn_error_trace(root->vtable->get_mergeinfo(
1267 catalog, root, paths, inherit, include_descendants,
1268 adjust_inherited_mergeinfo, result_pool, scratch_pool));
1272 svn_fs_get_mergeinfo(svn_mergeinfo_catalog_t *catalog,
1273 svn_fs_root_t *root,
1274 const apr_array_header_t *paths,
1275 svn_mergeinfo_inheritance_t inherit,
1276 svn_boolean_t include_descendants,
1279 return svn_error_trace(root->vtable->get_mergeinfo(catalog, root, paths,
1281 include_descendants,
1286 svn_fs__get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo,
1287 svn_fs_root_t *root,
1289 svn_mergeinfo_inheritance_t inherit,
1290 svn_boolean_t adjust_inherited_mergeinfo,
1291 apr_pool_t *result_pool,
1292 apr_pool_t *scratch_pool)
1294 apr_array_header_t *paths
1295 = apr_array_make(scratch_pool, 1, sizeof(const char *));
1296 svn_mergeinfo_catalog_t catalog;
1298 APR_ARRAY_PUSH(paths, const char *) = path;
1300 SVN_ERR(svn_fs_get_mergeinfo2(&catalog, root, paths,
1301 inherit, FALSE /*include_descendants*/,
1302 adjust_inherited_mergeinfo,
1303 result_pool, scratch_pool));
1304 *mergeinfo = svn_hash_gets(catalog, path);
1306 return SVN_NO_ERROR;
1310 svn_fs_merge(const char **conflict_p, svn_fs_root_t *source_root,
1311 const char *source_path, svn_fs_root_t *target_root,
1312 const char *target_path, svn_fs_root_t *ancestor_root,
1313 const char *ancestor_path, apr_pool_t *pool)
1315 return svn_error_trace(target_root->vtable->merge(conflict_p,
1316 source_root, source_path,
1317 target_root, target_path,
1319 ancestor_path, pool));
1323 svn_fs_dir_entries(apr_hash_t **entries_p, svn_fs_root_t *root,
1324 const char *path, apr_pool_t *pool)
1326 return svn_error_trace(root->vtable->dir_entries(entries_p, root, path,
1331 svn_fs_dir_optimal_order(apr_array_header_t **ordered_p,
1332 svn_fs_root_t *root,
1333 apr_hash_t *entries,
1334 apr_pool_t *result_pool,
1335 apr_pool_t *scratch_pool)
1337 return svn_error_trace(root->vtable->dir_optimal_order(ordered_p, root,
1344 svn_fs_make_dir(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1346 SVN_ERR(svn_fs__path_valid(path, pool));
1347 return svn_error_trace(root->vtable->make_dir(root, path, pool));
1351 svn_fs_delete(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1353 return svn_error_trace(root->vtable->delete_node(root, path, pool));
1357 svn_fs_copy(svn_fs_root_t *from_root, const char *from_path,
1358 svn_fs_root_t *to_root, const char *to_path, apr_pool_t *pool)
1360 SVN_ERR(svn_fs__path_valid(to_path, pool));
1361 return svn_error_trace(to_root->vtable->copy(from_root, from_path,
1362 to_root, to_path, pool));
1366 svn_fs_revision_link(svn_fs_root_t *from_root, svn_fs_root_t *to_root,
1367 const char *path, apr_pool_t *pool)
1369 return svn_error_trace(to_root->vtable->revision_link(from_root, to_root,
1374 svn_fs_file_length(svn_filesize_t *length_p, svn_fs_root_t *root,
1375 const char *path, apr_pool_t *pool)
1377 return svn_error_trace(root->vtable->file_length(length_p, root, path,
1382 svn_fs_file_checksum(svn_checksum_t **checksum,
1383 svn_checksum_kind_t kind,
1384 svn_fs_root_t *root,
1386 svn_boolean_t force,
1389 SVN_ERR(root->vtable->file_checksum(checksum, kind, root, path, pool));
1391 if (force && (*checksum == NULL || (*checksum)->kind != kind))
1393 svn_stream_t *contents, *checksum_contents;
1395 SVN_ERR(svn_fs_file_contents(&contents, root, path, pool));
1396 checksum_contents = svn_stream_checksummed2(contents, checksum, NULL,
1399 /* This will force a read of any remaining data (which is all of it in
1400 this case) and dump the checksum into checksum->digest. */
1401 SVN_ERR(svn_stream_close(checksum_contents));
1404 return SVN_NO_ERROR;
1408 svn_fs_file_md5_checksum(unsigned char digest[],
1409 svn_fs_root_t *root,
1413 svn_checksum_t *md5sum;
1415 SVN_ERR(svn_fs_file_checksum(&md5sum, svn_checksum_md5, root, path, TRUE,
1417 memcpy(digest, md5sum->digest, APR_MD5_DIGESTSIZE);
1419 return SVN_NO_ERROR;
1423 svn_fs_file_contents(svn_stream_t **contents, svn_fs_root_t *root,
1424 const char *path, apr_pool_t *pool)
1426 return svn_error_trace(root->vtable->file_contents(contents, root, path,
1431 svn_fs_try_process_file_contents(svn_boolean_t *success,
1432 svn_fs_root_t *root,
1434 svn_fs_process_contents_func_t processor,
1438 /* if the FS doesn't implement this function, report a "failed" attempt */
1439 if (root->vtable->try_process_file_contents == NULL)
1442 return SVN_NO_ERROR;
1445 return svn_error_trace(root->vtable->try_process_file_contents(
1448 processor, baton, pool));
1452 svn_fs_make_file(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1454 SVN_ERR(svn_fs__path_valid(path, pool));
1455 return svn_error_trace(root->vtable->make_file(root, path, pool));
1459 svn_fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
1460 void **contents_baton_p, svn_fs_root_t *root,
1461 const char *path, const char *base_checksum,
1462 const char *result_checksum, apr_pool_t *pool)
1464 svn_checksum_t *base, *result;
1466 /* TODO: If we ever rev this API, we should make the supplied checksums
1467 svn_checksum_t structs. */
1468 SVN_ERR(svn_checksum_parse_hex(&base, svn_checksum_md5, base_checksum,
1470 SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum,
1473 return svn_error_trace(root->vtable->apply_textdelta(contents_p,
1483 svn_fs_apply_text(svn_stream_t **contents_p, svn_fs_root_t *root,
1484 const char *path, const char *result_checksum,
1487 svn_checksum_t *result;
1489 /* TODO: If we ever rev this API, we should make the supplied checksum an
1490 svn_checksum_t struct. */
1491 SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum,
1494 return svn_error_trace(root->vtable->apply_text(contents_p, root, path,
1499 svn_fs_contents_different(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1500 const char *path1, svn_fs_root_t *root2,
1501 const char *path2, apr_pool_t *scratch_pool)
1503 return svn_error_trace(root1->vtable->contents_changed(changed_p,
1511 svn_fs_contents_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1512 const char *path1, svn_fs_root_t *root2,
1513 const char *path2, apr_pool_t *pool)
1515 return svn_error_trace(root1->vtable->contents_changed(changed_p,
1522 svn_fs_youngest_rev(svn_revnum_t *youngest_p, svn_fs_t *fs, apr_pool_t *pool)
1524 return svn_error_trace(fs->vtable->youngest_rev(youngest_p, fs, pool));
1528 svn_fs_info_format(int *fs_format,
1529 svn_version_t **supports_version,
1531 apr_pool_t *result_pool,
1532 apr_pool_t *scratch_pool)
1534 return svn_error_trace(fs->vtable->info_format(fs_format, supports_version,
1536 result_pool, scratch_pool));
1540 svn_fs_info_config_files(apr_array_header_t **files,
1542 apr_pool_t *result_pool,
1543 apr_pool_t *scratch_pool)
1545 return svn_error_trace(fs->vtable->info_config_files(files, fs,
1551 svn_fs_deltify_revision(svn_fs_t *fs, svn_revnum_t revision, apr_pool_t *pool)
1553 return svn_error_trace(fs->vtable->deltify(fs, revision, pool));
1557 svn_fs_revision_prop(svn_string_t **value_p, svn_fs_t *fs, svn_revnum_t rev,
1558 const char *propname, apr_pool_t *pool)
1560 return svn_error_trace(fs->vtable->revision_prop(value_p, fs, rev,
1565 svn_fs_revision_proplist(apr_hash_t **table_p, svn_fs_t *fs, svn_revnum_t rev,
1568 return svn_error_trace(fs->vtable->revision_proplist(table_p, fs, rev,
1573 svn_fs_change_rev_prop2(svn_fs_t *fs, svn_revnum_t rev, const char *name,
1574 const svn_string_t *const *old_value_p,
1575 const svn_string_t *value, apr_pool_t *pool)
1577 return svn_error_trace(fs->vtable->change_rev_prop(fs, rev, name,
1583 svn_fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
1584 svn_fs_root_t *source_root,
1585 const char *source_path,
1586 svn_fs_root_t *target_root,
1587 const char *target_path, apr_pool_t *pool)
1589 return svn_error_trace(target_root->vtable->get_file_delta_stream(
1591 source_root, source_path,
1592 target_root, target_path, pool));
1596 svn_fs_get_uuid(svn_fs_t *fs, const char **uuid, apr_pool_t *pool)
1598 /* If you change this, consider changing svn_fs__identifier(). */
1599 *uuid = apr_pstrdup(pool, fs->uuid);
1600 return SVN_NO_ERROR;
1604 svn_fs_set_uuid(svn_fs_t *fs, const char *uuid, apr_pool_t *pool)
1608 uuid = svn_uuid_generate(pool);
1612 apr_uuid_t parsed_uuid;
1613 apr_status_t apr_err = apr_uuid_parse(&parsed_uuid, uuid);
1615 return svn_error_createf(SVN_ERR_BAD_UUID, NULL,
1616 _("Malformed UUID '%s'"), uuid);
1618 return svn_error_trace(fs->vtable->set_uuid(fs, uuid, pool));
1622 svn_fs_lock_many(svn_fs_t *fs,
1623 apr_hash_t *targets,
1624 const char *comment,
1625 svn_boolean_t is_dav_comment,
1626 apr_time_t expiration_date,
1627 svn_boolean_t steal_lock,
1628 svn_fs_lock_callback_t lock_callback,
1630 apr_pool_t *result_pool,
1631 apr_pool_t *scratch_pool)
1633 apr_hash_index_t *hi;
1634 apr_hash_t *ok_targets = apr_hash_make(scratch_pool);
1635 svn_error_t *err, *cb_err = SVN_NO_ERROR;
1637 /* Enforce that the comment be xml-escapable. */
1639 if (! svn_xml_is_xml_safe(comment, strlen(comment)))
1640 return svn_error_create(SVN_ERR_XML_UNESCAPABLE_DATA, NULL,
1641 _("Lock comment contains illegal characters"));
1643 if (expiration_date < 0)
1644 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
1645 _("Negative expiration date passed to svn_fs_lock"));
1647 /* Enforce that the token be an XML-safe URI. */
1648 for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi))
1650 const svn_fs_lock_target_t *target = apr_hash_this_val(hi);
1658 if (strncmp(target->token, "opaquelocktoken:", 16))
1659 err = svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1660 _("Lock token URI '%s' has bad scheme; "
1662 target->token, "opaquelocktoken");
1665 for (c = target->token; *c && !err; c++)
1666 if (! svn_ctype_isascii(*c) || svn_ctype_iscntrl(*c))
1667 err = svn_error_createf(
1668 SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1669 _("Lock token '%s' is not ASCII or is a "
1670 "control character at byte %u"),
1672 (unsigned)(c - target->token));
1674 /* strlen(token) == c - token. */
1675 if (!err && !svn_xml_is_xml_safe(target->token, c - target->token))
1676 err = svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1677 _("Lock token URI '%s' is not XML-safe"),
1683 if (!cb_err && lock_callback)
1684 cb_err = lock_callback(lock_baton, apr_hash_this_key(hi),
1685 NULL, err, scratch_pool);
1686 svn_error_clear(err);
1689 svn_hash_sets(ok_targets, apr_hash_this_key(hi), target);
1692 if (!apr_hash_count(ok_targets))
1693 return svn_error_trace(cb_err);
1695 err = fs->vtable->lock(fs, ok_targets, comment, is_dav_comment,
1696 expiration_date, steal_lock,
1697 lock_callback, lock_baton,
1698 result_pool, scratch_pool);
1701 svn_error_compose(err, cb_err);
1705 return svn_error_trace(err);
1708 struct lock_baton_t {
1709 const svn_lock_t *lock;
1710 svn_error_t *fs_err;
1713 /* Implements svn_fs_lock_callback_t. Used by svn_fs_lock and
1714 svn_fs_unlock to record the lock and error from svn_fs_lock_many
1715 and svn_fs_unlock_many. */
1716 static svn_error_t *
1717 lock_cb(void *lock_baton,
1719 const svn_lock_t *lock,
1720 svn_error_t *fs_err,
1723 struct lock_baton_t *b = lock_baton;
1726 b->fs_err = svn_error_dup(fs_err);
1728 return SVN_NO_ERROR;
1732 svn_fs_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path,
1733 const char *token, const char *comment,
1734 svn_boolean_t is_dav_comment, apr_time_t expiration_date,
1735 svn_revnum_t current_rev, svn_boolean_t steal_lock,
1738 apr_hash_t *targets = apr_hash_make(pool);
1739 svn_fs_lock_target_t target;
1741 struct lock_baton_t baton = {0};
1743 target.token = token;
1744 target.current_rev = current_rev;
1745 svn_hash_sets(targets, path, &target);
1747 err = svn_fs_lock_many(fs, targets, comment, is_dav_comment,
1748 expiration_date, steal_lock, lock_cb, &baton,
1752 *lock = (svn_lock_t*)baton.lock;
1754 if (err && baton.fs_err)
1755 svn_error_compose(err, baton.fs_err);
1759 return svn_error_trace(err);
1763 svn_fs_generate_lock_token(const char **token, svn_fs_t *fs, apr_pool_t *pool)
1765 return svn_error_trace(fs->vtable->generate_lock_token(token, fs, pool));
1768 svn_fs_lock_target_t *
1769 svn_fs_lock_target_create(const char *token,
1770 svn_revnum_t current_rev,
1771 apr_pool_t *result_pool)
1773 svn_fs_lock_target_t *target = apr_palloc(result_pool,
1774 sizeof(svn_fs_lock_target_t));
1776 target->token = token;
1777 target->current_rev = current_rev;
1783 svn_fs_lock_target_set_token(svn_fs_lock_target_t *target,
1786 target->token = token;
1790 svn_fs_unlock_many(svn_fs_t *fs,
1791 apr_hash_t *targets,
1792 svn_boolean_t break_lock,
1793 svn_fs_lock_callback_t lock_callback,
1795 apr_pool_t *result_pool,
1796 apr_pool_t *scratch_pool)
1798 return svn_error_trace(fs->vtable->unlock(fs, targets, break_lock,
1799 lock_callback, lock_baton,
1800 result_pool, scratch_pool));
1804 svn_fs_unlock(svn_fs_t *fs, const char *path, const char *token,
1805 svn_boolean_t break_lock, apr_pool_t *pool)
1807 apr_hash_t *targets = apr_hash_make(pool);
1809 struct lock_baton_t baton = {0};
1813 svn_hash_sets(targets, path, token);
1815 err = svn_fs_unlock_many(fs, targets, break_lock, lock_cb, &baton,
1818 if (err && baton.fs_err)
1819 svn_error_compose(err, baton.fs_err);
1823 return svn_error_trace(err);
1827 svn_fs_get_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path,
1830 return svn_error_trace(fs->vtable->get_lock(lock, fs, path, pool));
1834 svn_fs_get_locks2(svn_fs_t *fs, const char *path, svn_depth_t depth,
1835 svn_fs_get_locks_callback_t get_locks_func,
1836 void *get_locks_baton, apr_pool_t *pool)
1838 SVN_ERR_ASSERT((depth == svn_depth_empty) ||
1839 (depth == svn_depth_files) ||
1840 (depth == svn_depth_immediates) ||
1841 (depth == svn_depth_infinity));
1842 return svn_error_trace(fs->vtable->get_locks(fs, path, depth,
1844 get_locks_baton, pool));
1848 /* --- History functions --- */
1851 svn_fs_history_prev2(svn_fs_history_t **prev_history_p,
1852 svn_fs_history_t *history, svn_boolean_t cross_copies,
1853 apr_pool_t *result_pool, apr_pool_t *scratch_pool)
1855 return svn_error_trace(history->vtable->prev(prev_history_p, history,
1856 cross_copies, result_pool,
1861 svn_fs_history_prev(svn_fs_history_t **prev_history_p,
1862 svn_fs_history_t *history, svn_boolean_t cross_copies,
1865 return svn_error_trace(history->vtable->prev(prev_history_p, history,
1866 cross_copies, pool, pool));
1870 svn_fs_history_location(const char **path, svn_revnum_t *revision,
1871 svn_fs_history_t *history, apr_pool_t *pool)
1873 return svn_error_trace(history->vtable->location(path, revision, history,
1878 /* --- Node-ID functions --- */
1881 svn_fs_parse_id(const char *data, apr_size_t len, apr_pool_t *pool)
1883 fs_library_vtable_t *vtable;
1886 err = get_library_vtable(&vtable, SVN_FS_TYPE_BDB, pool);
1889 svn_error_clear(err);
1892 return vtable->parse_id(data, len, pool);
1896 svn_fs_unparse_id(const svn_fs_id_t *id, apr_pool_t *pool)
1898 return id->vtable->unparse(id, pool);
1902 svn_fs_check_related(const svn_fs_id_t *a, const svn_fs_id_t *b)
1904 return (a->vtable->compare(a, b) != svn_fs_node_unrelated);
1908 svn_fs_compare_ids(const svn_fs_id_t *a, const svn_fs_id_t *b)
1910 switch (a->vtable->compare(a, b))
1912 case svn_fs_node_unchanged:
1914 case svn_fs_node_common_ancestor:
1922 svn_fs_print_modules(svn_stringbuf_t *output,
1925 struct fs_type_defn *defn = fs_modules;
1926 fs_library_vtable_t *vtable;
1927 apr_pool_t *iterpool = svn_pool_create(pool);
1934 svn_pool_clear(iterpool);
1936 err = get_library_vtable_direct(&vtable, defn, iterpool);
1939 if (err->apr_err == SVN_ERR_FS_UNKNOWN_FS_TYPE)
1941 svn_error_clear(err);
1949 line = apr_psprintf(iterpool, "* fs_%s : %s\n",
1950 defn->fsap_name, vtable->get_description());
1951 svn_stringbuf_appendcstr(output, line);
1955 svn_pool_destroy(iterpool);
1957 return SVN_NO_ERROR;
1960 svn_fs_path_change2_t *
1961 svn_fs_path_change2_create(const svn_fs_id_t *node_rev_id,
1962 svn_fs_path_change_kind_t change_kind,
1965 return svn_fs__path_change_create_internal(node_rev_id, change_kind, pool);
1968 /* Return the library version number. */
1969 const svn_version_t *
1970 svn_fs_version(void)
1978 svn_fs_info(const svn_fs_info_placeholder_t **info_p,
1980 apr_pool_t *result_pool,
1981 apr_pool_t *scratch_pool)
1983 if (fs->vtable->info_fsap)
1985 SVN_ERR(fs->vtable->info_fsap((const void **)info_p, fs,
1986 result_pool, scratch_pool));
1990 svn_fs_info_placeholder_t *info = apr_palloc(result_pool, sizeof(*info));
1991 /* ### Ask the disk(!), since svn_fs_t doesn't cache the answer. */
1992 SVN_ERR(svn_fs_type(&info->fs_type, fs->path, result_pool));
1995 return SVN_NO_ERROR;
1999 svn_fs_info_dup(const void *info_void,
2000 apr_pool_t *result_pool,
2001 apr_pool_t *scratch_pool)
2003 const svn_fs_info_placeholder_t *info = info_void;
2004 fs_library_vtable_t *vtable;
2006 SVN_ERR(get_library_vtable(&vtable, info->fs_type, scratch_pool));
2008 if (vtable->info_fsap_dup)
2009 return vtable->info_fsap_dup(info_void, result_pool);
2011 return apr_pmemdup(result_pool, info, sizeof(*info));