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 * ====================================================================
29 #include <apr_thread_mutex.h>
31 #include <apr_strings.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_private_config.h"
45 #include "private/svn_fs_private.h"
46 #include "private/svn_fs_util.h"
47 #include "private/svn_utf_private.h"
48 #include "private/svn_mutex.h"
49 #include "private/svn_subr_private.h"
51 #include "fs-loader.h"
53 /* This is defined by configure on platforms which use configure, but
54 we need to define a fallback for Windows. */
55 #ifndef DEFAULT_FS_TYPE
56 #define DEFAULT_FS_TYPE "fsfs"
59 #define FS_TYPE_FILENAME "fs-type"
61 /* A pool common to all FS objects. See the documentation on the
62 open/create functions in fs-loader.h and for svn_fs_initialize(). */
63 static apr_pool_t *common_pool;
64 svn_mutex__t *common_pool_lock;
67 /* --- Utility functions for the loader --- */
71 const char *fsap_name;
72 fs_init_func_t initfunc;
73 struct fs_type_defn *next;
76 static struct fs_type_defn base_defn =
78 SVN_FS_TYPE_BDB, "base",
79 #ifdef SVN_LIBSVN_FS_LINKS_FS_BASE
87 static struct fs_type_defn fsfs_defn =
89 SVN_FS_TYPE_FSFS, "fs",
90 #ifdef SVN_LIBSVN_FS_LINKS_FS_FS
98 static struct fs_type_defn *fs_modules = &fsfs_defn;
102 load_module(fs_init_func_t *initfunc, const char *name, apr_pool_t *pool)
106 #if defined(SVN_USE_DSO) && APR_HAS_DSO
108 apr_dso_handle_t *dso;
109 apr_dso_handle_sym_t symbol;
111 const char *funcname;
115 /* Demand a simple alphanumeric name so that the generated DSO
117 for (p = name; *p; ++p)
118 if (!svn_ctype_isalnum(*p))
119 return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
120 _("Invalid name for FS type '%s'"),
123 libname = apr_psprintf(pool, "libsvn_fs_%s-%d.so.%d",
124 name, SVN_VER_MAJOR, SVN_SOVERSION);
125 funcname = apr_psprintf(pool, "svn_fs_%s__init", name);
127 /* Find/load the specified library. If we get an error, assume
128 the library doesn't exist. The library will be unloaded when
129 pool is destroyed. */
130 SVN_ERR(svn_dso_load(&dso, libname));
134 /* find the initialization routine */
135 status = apr_dso_sym(&symbol, dso, funcname);
137 return svn_error_wrap_apr(status, _("'%s' does not define '%s()'"),
140 *initfunc = (fs_init_func_t) symbol;
142 #endif /* APR_HAS_DSO */
147 /* Fetch a library vtable by a pointer into the library definitions array. */
149 get_library_vtable_direct(fs_library_vtable_t **vtable,
150 const struct fs_type_defn *fst,
153 fs_init_func_t initfunc = NULL;
154 const svn_version_t *my_version = svn_fs_version();
155 const svn_version_t *fs_version;
157 initfunc = fst->initfunc;
159 SVN_ERR(load_module(&initfunc, fst->fsap_name, pool));
162 return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
163 _("Failed to load module for FS type '%s'"),
167 /* Per our API compatibility rules, we cannot ensure that
168 svn_fs_initialize is called by the application. If not, we
169 cannot create the common pool and lock in a thread-safe fashion,
170 nor can we clean up the common pool if libsvn_fs is dynamically
171 unloaded. This function makes a best effort by creating the
172 common pool as a child of the global pool; the window of failure
173 due to thread collision is small. */
175 SVN_ERR(svn_fs_initialize(NULL));
177 /* Invoke the FS module's initfunc function with the common
178 pool protected by a lock. */
179 SVN_MUTEX__WITH_LOCK(common_pool_lock,
180 initfunc(my_version, vtable, common_pool));
182 fs_version = (*vtable)->get_version();
183 if (!svn_ver_equal(my_version, fs_version))
184 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
185 _("Mismatched FS module version for '%s':"
187 " expected %d.%d.%d%s"),
189 my_version->major, my_version->minor,
190 my_version->patch, my_version->tag,
191 fs_version->major, fs_version->minor,
192 fs_version->patch, fs_version->tag);
196 #if defined(SVN_USE_DSO) && APR_HAS_DSO
197 /* Return *FST for the third party FS_TYPE */
199 get_or_allocate_third(struct fs_type_defn **fst,
204 if (strcmp(fs_type, (*fst)->fs_type) == 0)
209 *fst = apr_palloc(common_pool, sizeof(struct fs_type_defn));
210 (*fst)->fs_type = apr_pstrdup(common_pool, fs_type);
211 (*fst)->fsap_name = (*fst)->fs_type;
212 (*fst)->initfunc = NULL;
219 /* Fetch a library vtable by FS type. */
221 get_library_vtable(fs_library_vtable_t **vtable, const char *fs_type,
224 struct fs_type_defn **fst = &fs_modules;
225 svn_boolean_t known = FALSE;
227 /* There are two FS module definitions known at compile time. We
228 want to check these without any locking overhead even when
229 dynamic third party modules are enabled. The third party modules
230 cannot be checked until the lock is held. */
231 if (strcmp(fs_type, (*fst)->fs_type) == 0)
236 if (strcmp(fs_type, (*fst)->fs_type) == 0)
240 #if defined(SVN_USE_DSO) && APR_HAS_DSO
241 /* Third party FS modules that are unknown at compile time.
243 A third party FS is identified by the file fs-type containing a
244 third party name, say "foo". The loader will load the DSO with
245 the name "libsvn_fs_foo" and use the entry point with the name
248 Note: the BDB and FSFS modules don't follow this naming scheme
249 and this allows them to be used to test the third party loader.
250 Change the content of fs-type to "base" in a BDB filesystem or to
251 "fs" in an FSFS filesystem and they will be loaded as third party
256 if (!common_pool) /* Best-effort init, see get_library_vtable_direct. */
257 SVN_ERR(svn_fs_initialize(NULL));
258 SVN_MUTEX__WITH_LOCK(common_pool_lock,
259 get_or_allocate_third(fst, fs_type));
264 return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
265 _("Unknown FS type '%s'"), fs_type);
266 return get_library_vtable_direct(vtable, *fst, pool);
270 svn_fs_type(const char **fs_type, const char *path, apr_pool_t *pool)
272 const char *filename;
278 /* Read the fsap-name file to get the FSAP name, or assume the (old)
279 default. For old repositories I suppose we could check some
280 other file, DB_CONFIG or strings say, but for now just check the
282 filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool);
283 err = svn_io_file_open(&file, filename, APR_READ|APR_BUFFERED, 0, pool);
284 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
286 svn_node_kind_t kind;
287 svn_error_t *err2 = svn_io_check_path(path, &kind, pool);
290 svn_error_clear(err2);
293 if (kind == svn_node_dir)
295 svn_error_clear(err);
296 *fs_type = SVN_FS_TYPE_BDB;
305 SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
306 SVN_ERR(svn_io_file_close(file, pool));
307 *fs_type = apr_pstrdup(pool, buf);
312 /* Fetch the library vtable for an existing FS. */
314 fs_library_vtable(fs_library_vtable_t **vtable, const char *path,
319 SVN_ERR(svn_fs_type(&fs_type, path, pool));
321 /* Fetch the library vtable by name, now that we've chosen one. */
322 return svn_error_trace(get_library_vtable(vtable, fs_type, pool));
326 write_fs_type(const char *path, const char *fs_type, apr_pool_t *pool)
328 const char *filename;
331 filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool);
332 SVN_ERR(svn_io_file_open(&file, filename,
333 APR_WRITE|APR_CREATE|APR_TRUNCATE|APR_BUFFERED,
334 APR_OS_DEFAULT, pool));
335 SVN_ERR(svn_io_file_write_full(file, fs_type, strlen(fs_type), NULL,
337 SVN_ERR(svn_io_file_write_full(file, "\n", 1, NULL, pool));
338 return svn_error_trace(svn_io_file_close(file, pool));
342 /* --- Functions for operating on filesystems by pathname --- */
344 static apr_status_t uninit(void *data)
351 svn_fs_initialize(apr_pool_t *pool)
353 /* Protect against multiple calls. */
357 common_pool = svn_pool_create(pool);
358 SVN_ERR(svn_mutex__init(&common_pool_lock, TRUE, common_pool));
360 /* ### This won't work if POOL is NULL and libsvn_fs is loaded as a DSO
361 ### (via libsvn_ra_local say) since the global common_pool will live
362 ### longer than the DSO, which gets unloaded when the pool used to
363 ### load it is cleared, and so when the handler runs it will refer to
364 ### a function that no longer exists. libsvn_ra_local attempts to
365 ### work around this by explicitly calling svn_fs_initialize. */
366 apr_pool_cleanup_register(common_pool, NULL, uninit, apr_pool_cleanup_null);
370 /* A default warning handling function. */
372 default_warning_func(void *baton, svn_error_t *err)
374 /* The one unforgiveable sin is to fail silently. Dumping to stderr
375 or /dev/tty is not acceptable default behavior for server
376 processes, since those may both be equivalent to /dev/null. */
377 SVN_ERR_MALFUNCTION_NO_RETURN();
381 svn_fs__path_valid(const char *path, apr_pool_t *pool)
383 /* UTF-8 encoded string without NULs. */
384 if (! svn_utf__cstring_is_valid(path))
386 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
387 _("Path '%s' is not in UTF-8"), path);
390 /* No "." or ".." elements. */
391 if (svn_path_is_backpath_present(path)
392 || svn_path_is_dotpath_present(path))
394 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
395 _("Path '%s' contains '.' or '..' element"),
399 /* That's good enough. */
403 /* Allocate svn_fs_t structure. */
405 fs_new(apr_hash_t *fs_config, apr_pool_t *pool)
407 svn_fs_t *fs = apr_palloc(pool, sizeof(*fs));
410 fs->warning = default_warning_func;
411 fs->warning_baton = NULL;
412 fs->config = fs_config;
413 fs->access_ctx = NULL;
415 fs->fsap_data = NULL;
421 svn_fs_new(apr_hash_t *fs_config, apr_pool_t *pool)
423 return fs_new(fs_config, pool);
427 svn_fs_set_warning_func(svn_fs_t *fs, svn_fs_warning_callback_t warning,
430 fs->warning = warning;
431 fs->warning_baton = warning_baton;
435 svn_fs_create(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config,
438 fs_library_vtable_t *vtable;
440 const char *fs_type = svn_hash__get_cstring(fs_config,
441 SVN_FS_CONFIG_FS_TYPE,
443 SVN_ERR(get_library_vtable(&vtable, fs_type, pool));
445 /* Create the FS directory and write out the fsap-name file. */
446 SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, pool));
447 SVN_ERR(write_fs_type(path, fs_type, pool));
449 /* Perform the actual creation. */
450 *fs_p = fs_new(fs_config, pool);
452 SVN_MUTEX__WITH_LOCK(common_pool_lock,
453 vtable->create(*fs_p, path, pool, common_pool));
454 SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open));
460 svn_fs_open(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config,
463 fs_library_vtable_t *vtable;
465 SVN_ERR(fs_library_vtable(&vtable, path, pool));
466 *fs_p = fs_new(fs_config, pool);
467 SVN_MUTEX__WITH_LOCK(common_pool_lock,
468 vtable->open_fs(*fs_p, path, pool, common_pool));
469 SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open));
475 svn_fs_upgrade(const char *path, apr_pool_t *pool)
477 fs_library_vtable_t *vtable;
480 SVN_ERR(fs_library_vtable(&vtable, path, pool));
481 fs = fs_new(NULL, pool);
483 SVN_MUTEX__WITH_LOCK(common_pool_lock,
484 vtable->upgrade_fs(fs, path, pool, common_pool));
489 svn_fs_verify(const char *path,
490 apr_hash_t *fs_config,
493 svn_fs_progress_notify_func_t notify_func,
495 svn_cancel_func_t cancel_func,
499 fs_library_vtable_t *vtable;
502 SVN_ERR(fs_library_vtable(&vtable, path, pool));
503 fs = fs_new(fs_config, pool);
505 SVN_MUTEX__WITH_LOCK(common_pool_lock,
506 vtable->verify_fs(fs, path, start, end,
507 notify_func, notify_baton,
508 cancel_func, cancel_baton,
514 svn_fs_path(svn_fs_t *fs, apr_pool_t *pool)
516 return apr_pstrdup(pool, fs->path);
520 svn_fs_config(svn_fs_t *fs, apr_pool_t *pool)
523 return apr_hash_copy(pool, fs->config);
529 svn_fs_delete_fs(const char *path, apr_pool_t *pool)
531 fs_library_vtable_t *vtable;
533 SVN_ERR(fs_library_vtable(&vtable, path, pool));
534 return svn_error_trace(vtable->delete_fs(path, pool));
538 svn_fs_hotcopy2(const char *src_path, const char *dst_path,
539 svn_boolean_t clean, svn_boolean_t incremental,
540 svn_cancel_func_t cancel_func, void *cancel_baton,
541 apr_pool_t *scratch_pool)
543 fs_library_vtable_t *vtable;
544 const char *src_fs_type;
547 const char *dst_fs_type;
548 svn_node_kind_t dst_kind;
550 if (strcmp(src_path, dst_path) == 0)
551 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
552 _("Hotcopy source and destination are equal"));
554 SVN_ERR(svn_fs_type(&src_fs_type, src_path, scratch_pool));
555 SVN_ERR(get_library_vtable(&vtable, src_fs_type, scratch_pool));
556 src_fs = fs_new(NULL, scratch_pool);
557 dst_fs = fs_new(NULL, scratch_pool);
559 SVN_ERR(svn_io_check_path(dst_path, &dst_kind, scratch_pool));
560 if (dst_kind == svn_node_file)
561 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
562 _("'%s' already exists and is a file"),
563 svn_dirent_local_style(dst_path,
565 if (dst_kind == svn_node_unknown)
566 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
567 _("'%s' already exists and has an unknown "
569 svn_dirent_local_style(dst_path,
571 if (dst_kind == svn_node_dir)
573 svn_node_kind_t type_file_kind;
575 SVN_ERR(svn_io_check_path(svn_dirent_join(dst_path,
578 &type_file_kind, scratch_pool));
579 if (type_file_kind != svn_node_none)
581 SVN_ERR(svn_fs_type(&dst_fs_type, dst_path, scratch_pool));
582 if (strcmp(src_fs_type, dst_fs_type) != 0)
583 return svn_error_createf(
584 SVN_ERR_ILLEGAL_TARGET, NULL,
585 _("The filesystem type of the hotcopy source "
586 "('%s') does not match the filesystem "
587 "type of the hotcopy destination ('%s')"),
588 src_fs_type, dst_fs_type);
592 SVN_ERR(vtable->hotcopy(src_fs, dst_fs, src_path, dst_path, clean,
593 incremental, cancel_func, cancel_baton,
595 return svn_error_trace(write_fs_type(dst_path, src_fs_type, scratch_pool));
599 svn_fs_hotcopy(const char *src_path, const char *dest_path,
600 svn_boolean_t clean, apr_pool_t *pool)
602 return svn_error_trace(svn_fs_hotcopy2(src_path, dest_path, clean,
603 FALSE, NULL, NULL, pool));
607 svn_fs_pack(const char *path,
608 svn_fs_pack_notify_t notify_func,
610 svn_cancel_func_t cancel_func,
614 fs_library_vtable_t *vtable;
617 SVN_ERR(fs_library_vtable(&vtable, path, pool));
618 fs = fs_new(NULL, pool);
620 SVN_MUTEX__WITH_LOCK(common_pool_lock,
621 vtable->pack_fs(fs, path, notify_func, notify_baton,
622 cancel_func, cancel_baton, pool,
628 svn_fs_recover(const char *path,
629 svn_cancel_func_t cancel_func, void *cancel_baton,
632 fs_library_vtable_t *vtable;
635 SVN_ERR(fs_library_vtable(&vtable, path, pool));
636 fs = fs_new(NULL, pool);
638 SVN_MUTEX__WITH_LOCK(common_pool_lock,
639 vtable->open_fs_for_recovery(fs, path, pool,
641 return svn_error_trace(vtable->recover(fs, cancel_func, cancel_baton,
646 svn_fs_verify_root(svn_fs_root_t *root,
647 apr_pool_t *scratch_pool)
649 svn_fs_t *fs = root->fs;
650 SVN_ERR(fs->vtable->verify_root(root, scratch_pool));
656 svn_fs_freeze(svn_fs_t *fs,
657 svn_fs_freeze_func_t freeze_func,
661 SVN_ERR(fs->vtable->freeze(fs, freeze_func, freeze_baton, pool));
667 /* --- Berkeley-specific functions --- */
670 svn_fs_create_berkeley(svn_fs_t *fs, const char *path)
672 fs_library_vtable_t *vtable;
674 SVN_ERR(get_library_vtable(&vtable, SVN_FS_TYPE_BDB, fs->pool));
676 /* Create the FS directory and write out the fsap-name file. */
677 SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, fs->pool));
678 SVN_ERR(write_fs_type(path, SVN_FS_TYPE_BDB, fs->pool));
680 /* Perform the actual creation. */
681 SVN_MUTEX__WITH_LOCK(common_pool_lock,
682 vtable->create(fs, path, fs->pool, common_pool));
683 SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open));
689 svn_fs_open_berkeley(svn_fs_t *fs, const char *path)
691 fs_library_vtable_t *vtable;
693 SVN_ERR(fs_library_vtable(&vtable, path, fs->pool));
694 SVN_MUTEX__WITH_LOCK(common_pool_lock,
695 vtable->open_fs(fs, path, fs->pool, common_pool));
696 SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open));
702 svn_fs_berkeley_path(svn_fs_t *fs, apr_pool_t *pool)
704 return svn_fs_path(fs, pool);
708 svn_fs_delete_berkeley(const char *path, apr_pool_t *pool)
710 return svn_error_trace(svn_fs_delete_fs(path, pool));
714 svn_fs_hotcopy_berkeley(const char *src_path, const char *dest_path,
715 svn_boolean_t clean_logs, apr_pool_t *pool)
717 return svn_error_trace(svn_fs_hotcopy2(src_path, dest_path, clean_logs,
718 FALSE, NULL, NULL, pool));
722 svn_fs_berkeley_recover(const char *path, apr_pool_t *pool)
724 return svn_error_trace(svn_fs_recover(path, NULL, NULL, pool));
728 svn_fs_set_berkeley_errcall(svn_fs_t *fs,
729 void (*handler)(const char *errpfx, char *msg))
731 return svn_error_trace(fs->vtable->bdb_set_errcall(fs, handler));
735 svn_fs_berkeley_logfiles(apr_array_header_t **logfiles,
737 svn_boolean_t only_unused,
740 fs_library_vtable_t *vtable;
742 SVN_ERR(fs_library_vtable(&vtable, path, pool));
743 return svn_error_trace(vtable->bdb_logfiles(logfiles, path, only_unused,
748 /* --- Transaction functions --- */
751 svn_fs_begin_txn2(svn_fs_txn_t **txn_p, svn_fs_t *fs, svn_revnum_t rev,
752 apr_uint32_t flags, apr_pool_t *pool)
754 return svn_error_trace(fs->vtable->begin_txn(txn_p, fs, rev, flags, pool));
759 svn_fs_begin_txn(svn_fs_txn_t **txn_p, svn_fs_t *fs, svn_revnum_t rev,
762 return svn_error_trace(svn_fs_begin_txn2(txn_p, fs, rev, 0, pool));
767 svn_fs_commit_txn(const char **conflict_p, svn_revnum_t *new_rev,
768 svn_fs_txn_t *txn, apr_pool_t *pool)
772 *new_rev = SVN_INVALID_REVNUM;
776 err = txn->vtable->commit(conflict_p, new_rev, txn, pool);
779 /* Check postconditions. */
782 SVN_ERR_ASSERT_E(! (SVN_IS_VALID_REVNUM(*new_rev) && *conflict_p != NULL),
784 SVN_ERR_ASSERT_E((*conflict_p != NULL)
785 == (err && err->apr_err == SVN_ERR_FS_CONFLICT),
792 #ifdef PACK_AFTER_EVERY_COMMIT
794 svn_fs_t *fs = txn->fs;
795 const char *fs_path = svn_fs_path(fs, pool);
796 err = svn_fs_pack(fs_path, NULL, NULL, NULL, NULL, pool);
797 if (err && err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)
798 /* Pre-1.6 filesystem. */
799 svn_error_clear(err);
802 return svn_error_trace(err);
811 svn_fs_abort_txn(svn_fs_txn_t *txn, apr_pool_t *pool)
813 return svn_error_trace(txn->vtable->abort(txn, pool));
817 svn_fs_purge_txn(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
819 return svn_error_trace(fs->vtable->purge_txn(fs, txn_id, pool));
823 svn_fs_txn_name(const char **name_p, svn_fs_txn_t *txn, apr_pool_t *pool)
825 *name_p = apr_pstrdup(pool, txn->id);
830 svn_fs_txn_base_revision(svn_fs_txn_t *txn)
832 return txn->base_rev;
836 svn_fs_open_txn(svn_fs_txn_t **txn, svn_fs_t *fs, const char *name,
839 return svn_error_trace(fs->vtable->open_txn(txn, fs, name, pool));
843 svn_fs_list_transactions(apr_array_header_t **names_p, svn_fs_t *fs,
846 return svn_error_trace(fs->vtable->list_transactions(names_p, fs, pool));
850 svn_fs_txn_prop(svn_string_t **value_p, svn_fs_txn_t *txn,
851 const char *propname, apr_pool_t *pool)
853 return svn_error_trace(txn->vtable->get_prop(value_p, txn, propname, pool));
857 svn_fs_txn_proplist(apr_hash_t **table_p, svn_fs_txn_t *txn, apr_pool_t *pool)
859 return svn_error_trace(txn->vtable->get_proplist(table_p, txn, pool));
863 svn_fs_change_txn_prop(svn_fs_txn_t *txn, const char *name,
864 const svn_string_t *value, apr_pool_t *pool)
866 return svn_error_trace(txn->vtable->change_prop(txn, name, value, pool));
870 svn_fs_change_txn_props(svn_fs_txn_t *txn, const apr_array_header_t *props,
873 return svn_error_trace(txn->vtable->change_props(txn, props, pool));
877 /* --- Root functions --- */
880 svn_fs_revision_root(svn_fs_root_t **root_p, svn_fs_t *fs, svn_revnum_t rev,
883 /* We create a subpool for each root object to allow us to implement
884 svn_fs_close_root. */
885 apr_pool_t *subpool = svn_pool_create(pool);
886 return svn_error_trace(fs->vtable->revision_root(root_p, fs, rev, subpool));
890 svn_fs_txn_root(svn_fs_root_t **root_p, svn_fs_txn_t *txn, apr_pool_t *pool)
892 /* We create a subpool for each root object to allow us to implement
893 svn_fs_close_root. */
894 apr_pool_t *subpool = svn_pool_create(pool);
895 return svn_error_trace(txn->vtable->root(root_p, txn, subpool));
899 svn_fs_close_root(svn_fs_root_t *root)
901 svn_pool_destroy(root->pool);
905 svn_fs_root_fs(svn_fs_root_t *root)
911 svn_fs_is_txn_root(svn_fs_root_t *root)
913 return root->is_txn_root;
917 svn_fs_is_revision_root(svn_fs_root_t *root)
919 return !root->is_txn_root;
923 svn_fs_txn_root_name(svn_fs_root_t *root, apr_pool_t *pool)
925 return root->is_txn_root ? apr_pstrdup(pool, root->txn) : NULL;
929 svn_fs_txn_root_base_revision(svn_fs_root_t *root)
931 return root->is_txn_root ? root->rev : SVN_INVALID_REVNUM;
935 svn_fs_revision_root_revision(svn_fs_root_t *root)
937 return root->is_txn_root ? SVN_INVALID_REVNUM : root->rev;
941 svn_fs_paths_changed2(apr_hash_t **changed_paths_p, svn_fs_root_t *root,
944 return root->vtable->paths_changed(changed_paths_p, root, pool);
948 svn_fs_paths_changed(apr_hash_t **changed_paths_p, svn_fs_root_t *root,
951 apr_hash_t *changed_paths_new_structs;
952 apr_hash_index_t *hi;
954 SVN_ERR(svn_fs_paths_changed2(&changed_paths_new_structs, root, pool));
955 *changed_paths_p = apr_hash_make(pool);
956 for (hi = apr_hash_first(pool, changed_paths_new_structs);
958 hi = apr_hash_next(hi))
963 svn_fs_path_change2_t *val;
964 svn_fs_path_change_t *change;
965 apr_hash_this(hi, &vkey, &klen, &vval);
967 change = apr_palloc(pool, sizeof(*change));
968 change->node_rev_id = val->node_rev_id;
969 change->change_kind = val->change_kind;
970 change->text_mod = val->text_mod;
971 change->prop_mod = val->prop_mod;
972 apr_hash_set(*changed_paths_p, vkey, klen, change);
978 svn_fs_check_path(svn_node_kind_t *kind_p, svn_fs_root_t *root,
979 const char *path, apr_pool_t *pool)
981 return svn_error_trace(root->vtable->check_path(kind_p, root, path, pool));
985 svn_fs_node_history(svn_fs_history_t **history_p, svn_fs_root_t *root,
986 const char *path, apr_pool_t *pool)
988 return svn_error_trace(root->vtable->node_history(history_p, root, path,
993 svn_fs_is_dir(svn_boolean_t *is_dir, svn_fs_root_t *root, const char *path,
996 svn_node_kind_t kind;
998 SVN_ERR(root->vtable->check_path(&kind, root, path, pool));
999 *is_dir = (kind == svn_node_dir);
1000 return SVN_NO_ERROR;
1004 svn_fs_is_file(svn_boolean_t *is_file, svn_fs_root_t *root, const char *path,
1007 svn_node_kind_t kind;
1009 SVN_ERR(root->vtable->check_path(&kind, root, path, pool));
1010 *is_file = (kind == svn_node_file);
1011 return SVN_NO_ERROR;
1015 svn_fs_node_id(const svn_fs_id_t **id_p, svn_fs_root_t *root,
1016 const char *path, apr_pool_t *pool)
1018 return svn_error_trace(root->vtable->node_id(id_p, root, path, pool));
1022 svn_fs_node_created_rev(svn_revnum_t *revision, svn_fs_root_t *root,
1023 const char *path, apr_pool_t *pool)
1025 return svn_error_trace(root->vtable->node_created_rev(revision, root, path,
1030 svn_fs_node_origin_rev(svn_revnum_t *revision, svn_fs_root_t *root,
1031 const char *path, apr_pool_t *pool)
1033 return svn_error_trace(root->vtable->node_origin_rev(revision, root, path,
1038 svn_fs_node_created_path(const char **created_path, svn_fs_root_t *root,
1039 const char *path, apr_pool_t *pool)
1041 return svn_error_trace(root->vtable->node_created_path(created_path, root,
1046 svn_fs_node_prop(svn_string_t **value_p, svn_fs_root_t *root,
1047 const char *path, const char *propname, apr_pool_t *pool)
1049 return svn_error_trace(root->vtable->node_prop(value_p, root, path,
1054 svn_fs_node_proplist(apr_hash_t **table_p, svn_fs_root_t *root,
1055 const char *path, apr_pool_t *pool)
1057 return svn_error_trace(root->vtable->node_proplist(table_p, root, path,
1062 svn_fs_change_node_prop(svn_fs_root_t *root, const char *path,
1063 const char *name, const svn_string_t *value,
1066 return svn_error_trace(root->vtable->change_node_prop(root, path, name,
1071 svn_fs_props_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1072 const char *path1, svn_fs_root_t *root2,
1073 const char *path2, apr_pool_t *pool)
1075 return svn_error_trace(root1->vtable->props_changed(changed_p,
1082 svn_fs_copied_from(svn_revnum_t *rev_p, const char **path_p,
1083 svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1085 return svn_error_trace(root->vtable->copied_from(rev_p, path_p, root, path,
1090 svn_fs_closest_copy(svn_fs_root_t **root_p, const char **path_p,
1091 svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1093 return svn_error_trace(root->vtable->closest_copy(root_p, path_p,
1098 svn_fs_get_mergeinfo2(svn_mergeinfo_catalog_t *catalog,
1099 svn_fs_root_t *root,
1100 const apr_array_header_t *paths,
1101 svn_mergeinfo_inheritance_t inherit,
1102 svn_boolean_t include_descendants,
1103 svn_boolean_t adjust_inherited_mergeinfo,
1104 apr_pool_t *result_pool,
1105 apr_pool_t *scratch_pool)
1107 return svn_error_trace(root->vtable->get_mergeinfo(
1108 catalog, root, paths, inherit, include_descendants,
1109 adjust_inherited_mergeinfo, result_pool, scratch_pool));
1113 svn_fs_get_mergeinfo(svn_mergeinfo_catalog_t *catalog,
1114 svn_fs_root_t *root,
1115 const apr_array_header_t *paths,
1116 svn_mergeinfo_inheritance_t inherit,
1117 svn_boolean_t include_descendants,
1120 return svn_error_trace(root->vtable->get_mergeinfo(catalog, root, paths,
1122 include_descendants,
1127 svn_fs_merge(const char **conflict_p, svn_fs_root_t *source_root,
1128 const char *source_path, svn_fs_root_t *target_root,
1129 const char *target_path, svn_fs_root_t *ancestor_root,
1130 const char *ancestor_path, apr_pool_t *pool)
1132 return svn_error_trace(target_root->vtable->merge(conflict_p,
1133 source_root, source_path,
1134 target_root, target_path,
1136 ancestor_path, pool));
1140 svn_fs_dir_entries(apr_hash_t **entries_p, svn_fs_root_t *root,
1141 const char *path, apr_pool_t *pool)
1143 return svn_error_trace(root->vtable->dir_entries(entries_p, root, path,
1148 svn_fs_make_dir(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1150 SVN_ERR(svn_fs__path_valid(path, pool));
1151 return svn_error_trace(root->vtable->make_dir(root, path, pool));
1155 svn_fs_delete(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1157 return svn_error_trace(root->vtable->delete_node(root, path, pool));
1161 svn_fs_copy(svn_fs_root_t *from_root, const char *from_path,
1162 svn_fs_root_t *to_root, const char *to_path, apr_pool_t *pool)
1164 SVN_ERR(svn_fs__path_valid(to_path, pool));
1165 return svn_error_trace(to_root->vtable->copy(from_root, from_path,
1166 to_root, to_path, pool));
1170 svn_fs_revision_link(svn_fs_root_t *from_root, svn_fs_root_t *to_root,
1171 const char *path, apr_pool_t *pool)
1173 return svn_error_trace(to_root->vtable->revision_link(from_root, to_root,
1178 svn_fs_file_length(svn_filesize_t *length_p, svn_fs_root_t *root,
1179 const char *path, apr_pool_t *pool)
1181 return svn_error_trace(root->vtable->file_length(length_p, root, path,
1186 svn_fs_file_checksum(svn_checksum_t **checksum,
1187 svn_checksum_kind_t kind,
1188 svn_fs_root_t *root,
1190 svn_boolean_t force,
1193 SVN_ERR(root->vtable->file_checksum(checksum, kind, root, path, pool));
1195 if (force && (*checksum == NULL || (*checksum)->kind != kind))
1197 svn_stream_t *contents, *checksum_contents;
1199 SVN_ERR(svn_fs_file_contents(&contents, root, path, pool));
1200 checksum_contents = svn_stream_checksummed2(contents, checksum, NULL,
1203 /* This will force a read of any remaining data (which is all of it in
1204 this case) and dump the checksum into checksum->digest. */
1205 SVN_ERR(svn_stream_close(checksum_contents));
1208 return SVN_NO_ERROR;
1212 svn_fs_file_md5_checksum(unsigned char digest[],
1213 svn_fs_root_t *root,
1217 svn_checksum_t *md5sum;
1219 SVN_ERR(svn_fs_file_checksum(&md5sum, svn_checksum_md5, root, path, TRUE,
1221 memcpy(digest, md5sum->digest, APR_MD5_DIGESTSIZE);
1223 return SVN_NO_ERROR;
1227 svn_fs_file_contents(svn_stream_t **contents, svn_fs_root_t *root,
1228 const char *path, apr_pool_t *pool)
1230 return svn_error_trace(root->vtable->file_contents(contents, root, path,
1235 svn_fs_try_process_file_contents(svn_boolean_t *success,
1236 svn_fs_root_t *root,
1238 svn_fs_process_contents_func_t processor,
1242 /* if the FS doesn't implement this function, report a "failed" attempt */
1243 if (root->vtable->try_process_file_contents == NULL)
1246 return SVN_NO_ERROR;
1249 return svn_error_trace(root->vtable->try_process_file_contents(
1252 processor, baton, pool));
1256 svn_fs_make_file(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1258 SVN_ERR(svn_fs__path_valid(path, pool));
1259 return svn_error_trace(root->vtable->make_file(root, path, pool));
1263 svn_fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
1264 void **contents_baton_p, svn_fs_root_t *root,
1265 const char *path, const char *base_checksum,
1266 const char *result_checksum, apr_pool_t *pool)
1268 svn_checksum_t *base, *result;
1270 /* TODO: If we ever rev this API, we should make the supplied checksums
1271 svn_checksum_t structs. */
1272 SVN_ERR(svn_checksum_parse_hex(&base, svn_checksum_md5, base_checksum,
1274 SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum,
1277 return svn_error_trace(root->vtable->apply_textdelta(contents_p,
1287 svn_fs_apply_text(svn_stream_t **contents_p, svn_fs_root_t *root,
1288 const char *path, const char *result_checksum,
1291 svn_checksum_t *result;
1293 /* TODO: If we ever rev this API, we should make the supplied checksum an
1294 svn_checksum_t struct. */
1295 SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum,
1298 return svn_error_trace(root->vtable->apply_text(contents_p, root, path,
1303 svn_fs_contents_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1304 const char *path1, svn_fs_root_t *root2,
1305 const char *path2, apr_pool_t *pool)
1307 return svn_error_trace(root1->vtable->contents_changed(changed_p,
1314 svn_fs_youngest_rev(svn_revnum_t *youngest_p, svn_fs_t *fs, apr_pool_t *pool)
1316 return svn_error_trace(fs->vtable->youngest_rev(youngest_p, fs, pool));
1320 svn_fs_deltify_revision(svn_fs_t *fs, svn_revnum_t revision, apr_pool_t *pool)
1322 return svn_error_trace(fs->vtable->deltify(fs, revision, pool));
1326 svn_fs_revision_prop(svn_string_t **value_p, svn_fs_t *fs, svn_revnum_t rev,
1327 const char *propname, apr_pool_t *pool)
1329 return svn_error_trace(fs->vtable->revision_prop(value_p, fs, rev,
1334 svn_fs_revision_proplist(apr_hash_t **table_p, svn_fs_t *fs, svn_revnum_t rev,
1337 return svn_error_trace(fs->vtable->revision_proplist(table_p, fs, rev,
1342 svn_fs_change_rev_prop2(svn_fs_t *fs, svn_revnum_t rev, const char *name,
1343 const svn_string_t *const *old_value_p,
1344 const svn_string_t *value, apr_pool_t *pool)
1346 return svn_error_trace(fs->vtable->change_rev_prop(fs, rev, name,
1352 svn_fs_change_rev_prop(svn_fs_t *fs, svn_revnum_t rev, const char *name,
1353 const svn_string_t *value, apr_pool_t *pool)
1355 return svn_error_trace(
1356 svn_fs_change_rev_prop2(fs, rev, name, NULL, value, pool));
1360 svn_fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
1361 svn_fs_root_t *source_root,
1362 const char *source_path,
1363 svn_fs_root_t *target_root,
1364 const char *target_path, apr_pool_t *pool)
1366 return svn_error_trace(target_root->vtable->get_file_delta_stream(
1368 source_root, source_path,
1369 target_root, target_path, pool));
1373 svn_fs_get_uuid(svn_fs_t *fs, const char **uuid, apr_pool_t *pool)
1375 /* If you change this, consider changing svn_fs__identifier(). */
1376 *uuid = apr_pstrdup(pool, fs->uuid);
1377 return SVN_NO_ERROR;
1381 svn_fs_set_uuid(svn_fs_t *fs, const char *uuid, apr_pool_t *pool)
1385 uuid = svn_uuid_generate(pool);
1389 apr_uuid_t parsed_uuid;
1390 apr_status_t apr_err = apr_uuid_parse(&parsed_uuid, uuid);
1392 return svn_error_createf(SVN_ERR_BAD_UUID, NULL,
1393 _("Malformed UUID '%s'"), uuid);
1395 return svn_error_trace(fs->vtable->set_uuid(fs, uuid, pool));
1399 svn_fs_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path,
1400 const char *token, const char *comment,
1401 svn_boolean_t is_dav_comment, apr_time_t expiration_date,
1402 svn_revnum_t current_rev, svn_boolean_t steal_lock,
1405 /* Enforce that the comment be xml-escapable. */
1408 if (! svn_xml_is_xml_safe(comment, strlen(comment)))
1409 return svn_error_create
1410 (SVN_ERR_XML_UNESCAPABLE_DATA, NULL,
1411 _("Lock comment contains illegal characters"));
1414 /* Enforce that the token be an XML-safe URI. */
1419 if (strncmp(token, "opaquelocktoken:", 16))
1420 return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1421 _("Lock token URI '%s' has bad scheme; "
1423 token, "opaquelocktoken");
1425 for (c = token; *c; c++)
1426 if (! svn_ctype_isascii(*c))
1427 return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1428 _("Lock token '%s' is not ASCII "
1430 token, (unsigned)(c - token));
1432 /* strlen(token) == c - token. */
1433 if (! svn_xml_is_xml_safe(token, c - token))
1434 return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1435 _("Lock token URI '%s' is not XML-safe"),
1439 if (expiration_date < 0)
1440 return svn_error_create
1441 (SVN_ERR_INCORRECT_PARAMS, NULL,
1442 _("Negative expiration date passed to svn_fs_lock"));
1444 return svn_error_trace(fs->vtable->lock(lock, fs, path, token, comment,
1445 is_dav_comment, expiration_date,
1446 current_rev, steal_lock, pool));
1450 svn_fs_generate_lock_token(const char **token, svn_fs_t *fs, apr_pool_t *pool)
1452 return svn_error_trace(fs->vtable->generate_lock_token(token, fs, pool));
1456 svn_fs_unlock(svn_fs_t *fs, const char *path, const char *token,
1457 svn_boolean_t break_lock, apr_pool_t *pool)
1459 return svn_error_trace(fs->vtable->unlock(fs, path, token, break_lock,
1464 svn_fs_get_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path,
1467 return svn_error_trace(fs->vtable->get_lock(lock, fs, path, pool));
1471 svn_fs_get_locks2(svn_fs_t *fs, const char *path, svn_depth_t depth,
1472 svn_fs_get_locks_callback_t get_locks_func,
1473 void *get_locks_baton, apr_pool_t *pool)
1475 SVN_ERR_ASSERT((depth == svn_depth_empty) ||
1476 (depth == svn_depth_files) ||
1477 (depth == svn_depth_immediates) ||
1478 (depth == svn_depth_infinity));
1479 return svn_error_trace(fs->vtable->get_locks(fs, path, depth,
1481 get_locks_baton, pool));
1485 svn_fs_get_locks(svn_fs_t *fs, const char *path,
1486 svn_fs_get_locks_callback_t get_locks_func,
1487 void *get_locks_baton, apr_pool_t *pool)
1489 return svn_error_trace(svn_fs_get_locks2(fs, path, svn_depth_infinity,
1490 get_locks_func, get_locks_baton,
1495 /* --- History functions --- */
1498 svn_fs_history_prev(svn_fs_history_t **prev_history_p,
1499 svn_fs_history_t *history, svn_boolean_t cross_copies,
1502 return svn_error_trace(history->vtable->prev(prev_history_p, history,
1503 cross_copies, pool));
1507 svn_fs_history_location(const char **path, svn_revnum_t *revision,
1508 svn_fs_history_t *history, apr_pool_t *pool)
1510 return svn_error_trace(history->vtable->location(path, revision, history,
1515 /* --- Node-ID functions --- */
1518 svn_fs_parse_id(const char *data, apr_size_t len, apr_pool_t *pool)
1520 fs_library_vtable_t *vtable;
1523 err = get_library_vtable(&vtable, SVN_FS_TYPE_BDB, pool);
1526 svn_error_clear(err);
1529 return vtable->parse_id(data, len, pool);
1533 svn_fs_unparse_id(const svn_fs_id_t *id, apr_pool_t *pool)
1535 return id->vtable->unparse(id, pool);
1539 svn_fs_check_related(const svn_fs_id_t *a, const svn_fs_id_t *b)
1541 return (a->vtable->compare(a, b) != -1);
1545 svn_fs_compare_ids(const svn_fs_id_t *a, const svn_fs_id_t *b)
1547 return a->vtable->compare(a, b);
1551 svn_fs_print_modules(svn_stringbuf_t *output,
1554 const struct fs_type_defn *defn = fs_modules;
1555 fs_library_vtable_t *vtable;
1556 apr_pool_t *iterpool = svn_pool_create(pool);
1563 svn_pool_clear(iterpool);
1565 err = get_library_vtable_direct(&vtable, defn, iterpool);
1568 if (err->apr_err == SVN_ERR_FS_UNKNOWN_FS_TYPE)
1570 svn_error_clear(err);
1578 line = apr_psprintf(iterpool, "* fs_%s : %s\n",
1579 defn->fsap_name, vtable->get_description());
1580 svn_stringbuf_appendcstr(output, line);
1584 svn_pool_destroy(iterpool);
1586 return SVN_NO_ERROR;
1589 svn_fs_path_change2_t *
1590 svn_fs_path_change2_create(const svn_fs_id_t *node_rev_id,
1591 svn_fs_path_change_kind_t change_kind,
1594 return svn_fs__path_change_create_internal(node_rev_id, change_kind, pool);
1597 /* Return the library version number. */
1598 const svn_version_t *
1599 svn_fs_version(void)