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
84 NULL /* End of static list: this needs to be reset to NULL if the
85 common_pool used when setting it has been cleared. */
88 static struct fs_type_defn fsfs_defn =
90 SVN_FS_TYPE_FSFS, "fs",
91 #ifdef SVN_LIBSVN_FS_LINKS_FS_FS
99 static struct fs_type_defn *fs_modules = &fsfs_defn;
103 load_module(fs_init_func_t *initfunc, const char *name, apr_pool_t *pool)
107 #if defined(SVN_USE_DSO) && APR_HAS_DSO
109 apr_dso_handle_t *dso;
110 apr_dso_handle_sym_t symbol;
112 const char *funcname;
116 /* Demand a simple alphanumeric name so that the generated DSO
118 for (p = name; *p; ++p)
119 if (!svn_ctype_isalnum(*p))
120 return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
121 _("Invalid name for FS type '%s'"),
124 libname = apr_psprintf(pool, "libsvn_fs_%s-%d.so.%d",
125 name, SVN_VER_MAJOR, SVN_SOVERSION);
126 funcname = apr_psprintf(pool, "svn_fs_%s__init", name);
128 /* Find/load the specified library. If we get an error, assume
129 the library doesn't exist. The library will be unloaded when
130 pool is destroyed. */
131 SVN_ERR(svn_dso_load(&dso, libname));
135 /* find the initialization routine */
136 status = apr_dso_sym(&symbol, dso, funcname);
138 return svn_error_wrap_apr(status, _("'%s' does not define '%s()'"),
141 *initfunc = (fs_init_func_t) symbol;
143 #endif /* APR_HAS_DSO */
148 /* Fetch a library vtable by a pointer into the library definitions array. */
150 get_library_vtable_direct(fs_library_vtable_t **vtable,
151 const struct fs_type_defn *fst,
154 fs_init_func_t initfunc = NULL;
155 const svn_version_t *my_version = svn_fs_version();
156 const svn_version_t *fs_version;
158 initfunc = fst->initfunc;
160 SVN_ERR(load_module(&initfunc, fst->fsap_name, pool));
163 return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
164 _("Failed to load module for FS type '%s'"),
168 /* Per our API compatibility rules, we cannot ensure that
169 svn_fs_initialize is called by the application. If not, we
170 cannot create the common pool and lock in a thread-safe fashion,
171 nor can we clean up the common pool if libsvn_fs is dynamically
172 unloaded. This function makes a best effort by creating the
173 common pool as a child of the global pool; the window of failure
174 due to thread collision is small. */
176 SVN_ERR(svn_fs_initialize(NULL));
178 /* Invoke the FS module's initfunc function with the common
179 pool protected by a lock. */
180 SVN_MUTEX__WITH_LOCK(common_pool_lock,
181 initfunc(my_version, vtable, common_pool));
183 fs_version = (*vtable)->get_version();
184 if (!svn_ver_equal(my_version, fs_version))
185 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
186 _("Mismatched FS module version for '%s':"
188 " expected %d.%d.%d%s"),
190 my_version->major, my_version->minor,
191 my_version->patch, my_version->tag,
192 fs_version->major, fs_version->minor,
193 fs_version->patch, fs_version->tag);
197 #if defined(SVN_USE_DSO) && APR_HAS_DSO
198 /* Return *FST for the third party FS_TYPE */
200 get_or_allocate_third(struct fs_type_defn **fst,
205 if (strcmp(fs_type, (*fst)->fs_type) == 0)
210 *fst = apr_palloc(common_pool, sizeof(struct fs_type_defn));
211 (*fst)->fs_type = apr_pstrdup(common_pool, fs_type);
212 (*fst)->fsap_name = (*fst)->fs_type;
213 (*fst)->initfunc = NULL;
220 /* Fetch a library vtable by FS type. */
222 get_library_vtable(fs_library_vtable_t **vtable, const char *fs_type,
225 struct fs_type_defn **fst = &fs_modules;
226 svn_boolean_t known = FALSE;
228 /* There are two FS module definitions known at compile time. We
229 want to check these without any locking overhead even when
230 dynamic third party modules are enabled. The third party modules
231 cannot be checked until the lock is held. */
232 if (strcmp(fs_type, (*fst)->fs_type) == 0)
237 if (strcmp(fs_type, (*fst)->fs_type) == 0)
241 #if defined(SVN_USE_DSO) && APR_HAS_DSO
242 /* Third party FS modules that are unknown at compile time.
244 A third party FS is identified by the file fs-type containing a
245 third party name, say "foo". The loader will load the DSO with
246 the name "libsvn_fs_foo" and use the entry point with the name
249 Note: the BDB and FSFS modules don't follow this naming scheme
250 and this allows them to be used to test the third party loader.
251 Change the content of fs-type to "base" in a BDB filesystem or to
252 "fs" in an FSFS filesystem and they will be loaded as third party
257 if (!common_pool) /* Best-effort init, see get_library_vtable_direct. */
258 SVN_ERR(svn_fs_initialize(NULL));
259 SVN_MUTEX__WITH_LOCK(common_pool_lock,
260 get_or_allocate_third(fst, fs_type));
265 return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL,
266 _("Unknown FS type '%s'"), fs_type);
267 return get_library_vtable_direct(vtable, *fst, pool);
271 svn_fs_type(const char **fs_type, const char *path, apr_pool_t *pool)
273 const char *filename;
279 /* Read the fsap-name file to get the FSAP name, or assume the (old)
280 default. For old repositories I suppose we could check some
281 other file, DB_CONFIG or strings say, but for now just check the
283 filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool);
284 err = svn_io_file_open(&file, filename, APR_READ|APR_BUFFERED, 0, pool);
285 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
287 svn_node_kind_t kind;
288 svn_error_t *err2 = svn_io_check_path(path, &kind, pool);
291 svn_error_clear(err2);
294 if (kind == svn_node_dir)
296 svn_error_clear(err);
297 *fs_type = SVN_FS_TYPE_BDB;
306 SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
307 SVN_ERR(svn_io_file_close(file, pool));
308 *fs_type = apr_pstrdup(pool, buf);
313 /* Fetch the library vtable for an existing FS. */
315 fs_library_vtable(fs_library_vtable_t **vtable, const char *path,
320 SVN_ERR(svn_fs_type(&fs_type, path, pool));
322 /* Fetch the library vtable by name, now that we've chosen one. */
323 return svn_error_trace(get_library_vtable(vtable, fs_type, pool));
327 write_fs_type(const char *path, const char *fs_type, apr_pool_t *pool)
329 const char *filename;
332 filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool);
333 SVN_ERR(svn_io_file_open(&file, filename,
334 APR_WRITE|APR_CREATE|APR_TRUNCATE|APR_BUFFERED,
335 APR_OS_DEFAULT, pool));
336 SVN_ERR(svn_io_file_write_full(file, fs_type, strlen(fs_type), NULL,
338 SVN_ERR(svn_io_file_write_full(file, "\n", 1, NULL, pool));
339 return svn_error_trace(svn_io_file_close(file, pool));
343 /* --- Functions for operating on filesystems by pathname --- */
345 static apr_status_t uninit(void *data)
352 svn_fs_initialize(apr_pool_t *pool)
354 /* Protect against multiple calls. */
358 common_pool = svn_pool_create(pool);
359 base_defn.next = NULL;
360 SVN_ERR(svn_mutex__init(&common_pool_lock, TRUE, common_pool));
362 /* ### This won't work if POOL is NULL and libsvn_fs is loaded as a DSO
363 ### (via libsvn_ra_local say) since the global common_pool will live
364 ### longer than the DSO, which gets unloaded when the pool used to
365 ### load it is cleared, and so when the handler runs it will refer to
366 ### a function that no longer exists. libsvn_ra_local attempts to
367 ### work around this by explicitly calling svn_fs_initialize. */
368 apr_pool_cleanup_register(common_pool, NULL, uninit, apr_pool_cleanup_null);
372 /* A default warning handling function. */
374 default_warning_func(void *baton, svn_error_t *err)
376 /* The one unforgiveable sin is to fail silently. Dumping to stderr
377 or /dev/tty is not acceptable default behavior for server
378 processes, since those may both be equivalent to /dev/null. */
379 SVN_ERR_MALFUNCTION_NO_RETURN();
383 svn_fs__path_valid(const char *path, apr_pool_t *pool)
385 /* UTF-8 encoded string without NULs. */
386 if (! svn_utf__cstring_is_valid(path))
388 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
389 _("Path '%s' is not in UTF-8"), path);
392 /* No "." or ".." elements. */
393 if (svn_path_is_backpath_present(path)
394 || svn_path_is_dotpath_present(path))
396 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
397 _("Path '%s' contains '.' or '..' element"),
401 /* That's good enough. */
405 /* Allocate svn_fs_t structure. */
407 fs_new(apr_hash_t *fs_config, apr_pool_t *pool)
409 svn_fs_t *fs = apr_palloc(pool, sizeof(*fs));
412 fs->warning = default_warning_func;
413 fs->warning_baton = NULL;
414 fs->config = fs_config;
415 fs->access_ctx = NULL;
417 fs->fsap_data = NULL;
423 svn_fs_new(apr_hash_t *fs_config, apr_pool_t *pool)
425 return fs_new(fs_config, pool);
429 svn_fs_set_warning_func(svn_fs_t *fs, svn_fs_warning_callback_t warning,
432 fs->warning = warning;
433 fs->warning_baton = warning_baton;
437 svn_fs_create(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config,
440 fs_library_vtable_t *vtable;
442 const char *fs_type = svn_hash__get_cstring(fs_config,
443 SVN_FS_CONFIG_FS_TYPE,
445 SVN_ERR(get_library_vtable(&vtable, fs_type, pool));
447 /* Create the FS directory and write out the fsap-name file. */
448 SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, pool));
449 SVN_ERR(write_fs_type(path, fs_type, pool));
451 /* Perform the actual creation. */
452 *fs_p = fs_new(fs_config, pool);
454 SVN_MUTEX__WITH_LOCK(common_pool_lock,
455 vtable->create(*fs_p, path, pool, common_pool));
456 SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open));
462 svn_fs_open(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config,
465 fs_library_vtable_t *vtable;
467 SVN_ERR(fs_library_vtable(&vtable, path, pool));
468 *fs_p = fs_new(fs_config, pool);
469 SVN_MUTEX__WITH_LOCK(common_pool_lock,
470 vtable->open_fs(*fs_p, path, pool, common_pool));
471 SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open));
477 svn_fs_upgrade(const char *path, apr_pool_t *pool)
479 fs_library_vtable_t *vtable;
482 SVN_ERR(fs_library_vtable(&vtable, path, pool));
483 fs = fs_new(NULL, pool);
485 SVN_MUTEX__WITH_LOCK(common_pool_lock,
486 vtable->upgrade_fs(fs, path, pool, common_pool));
491 svn_fs_verify(const char *path,
492 apr_hash_t *fs_config,
495 svn_fs_progress_notify_func_t notify_func,
497 svn_cancel_func_t cancel_func,
501 fs_library_vtable_t *vtable;
504 SVN_ERR(fs_library_vtable(&vtable, path, pool));
505 fs = fs_new(fs_config, pool);
507 SVN_MUTEX__WITH_LOCK(common_pool_lock,
508 vtable->verify_fs(fs, path, start, end,
509 notify_func, notify_baton,
510 cancel_func, cancel_baton,
516 svn_fs_path(svn_fs_t *fs, apr_pool_t *pool)
518 return apr_pstrdup(pool, fs->path);
522 svn_fs_config(svn_fs_t *fs, apr_pool_t *pool)
525 return apr_hash_copy(pool, fs->config);
531 svn_fs_delete_fs(const char *path, apr_pool_t *pool)
533 fs_library_vtable_t *vtable;
535 SVN_ERR(fs_library_vtable(&vtable, path, pool));
536 return svn_error_trace(vtable->delete_fs(path, pool));
540 svn_fs_hotcopy2(const char *src_path, const char *dst_path,
541 svn_boolean_t clean, svn_boolean_t incremental,
542 svn_cancel_func_t cancel_func, void *cancel_baton,
543 apr_pool_t *scratch_pool)
545 fs_library_vtable_t *vtable;
546 const char *src_fs_type;
549 const char *dst_fs_type;
550 svn_node_kind_t dst_kind;
552 if (strcmp(src_path, dst_path) == 0)
553 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
554 _("Hotcopy source and destination are equal"));
556 SVN_ERR(svn_fs_type(&src_fs_type, src_path, scratch_pool));
557 SVN_ERR(get_library_vtable(&vtable, src_fs_type, scratch_pool));
558 src_fs = fs_new(NULL, scratch_pool);
559 dst_fs = fs_new(NULL, scratch_pool);
561 SVN_ERR(svn_io_check_path(dst_path, &dst_kind, scratch_pool));
562 if (dst_kind == svn_node_file)
563 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
564 _("'%s' already exists and is a file"),
565 svn_dirent_local_style(dst_path,
567 if (dst_kind == svn_node_unknown)
568 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
569 _("'%s' already exists and has an unknown "
571 svn_dirent_local_style(dst_path,
573 if (dst_kind == svn_node_dir)
575 svn_node_kind_t type_file_kind;
577 SVN_ERR(svn_io_check_path(svn_dirent_join(dst_path,
580 &type_file_kind, scratch_pool));
581 if (type_file_kind != svn_node_none)
583 SVN_ERR(svn_fs_type(&dst_fs_type, dst_path, scratch_pool));
584 if (strcmp(src_fs_type, dst_fs_type) != 0)
585 return svn_error_createf(
586 SVN_ERR_ILLEGAL_TARGET, NULL,
587 _("The filesystem type of the hotcopy source "
588 "('%s') does not match the filesystem "
589 "type of the hotcopy destination ('%s')"),
590 src_fs_type, dst_fs_type);
594 SVN_ERR(vtable->hotcopy(src_fs, dst_fs, src_path, dst_path, clean,
595 incremental, cancel_func, cancel_baton,
597 return svn_error_trace(write_fs_type(dst_path, src_fs_type, scratch_pool));
601 svn_fs_hotcopy(const char *src_path, const char *dest_path,
602 svn_boolean_t clean, apr_pool_t *pool)
604 return svn_error_trace(svn_fs_hotcopy2(src_path, dest_path, clean,
605 FALSE, NULL, NULL, pool));
609 svn_fs_pack(const char *path,
610 svn_fs_pack_notify_t notify_func,
612 svn_cancel_func_t cancel_func,
616 fs_library_vtable_t *vtable;
619 SVN_ERR(fs_library_vtable(&vtable, path, pool));
620 fs = fs_new(NULL, pool);
622 SVN_MUTEX__WITH_LOCK(common_pool_lock,
623 vtable->pack_fs(fs, path, notify_func, notify_baton,
624 cancel_func, cancel_baton, pool,
630 svn_fs_recover(const char *path,
631 svn_cancel_func_t cancel_func, void *cancel_baton,
634 fs_library_vtable_t *vtable;
637 SVN_ERR(fs_library_vtable(&vtable, path, pool));
638 fs = fs_new(NULL, pool);
640 SVN_MUTEX__WITH_LOCK(common_pool_lock,
641 vtable->open_fs_for_recovery(fs, path, pool,
643 return svn_error_trace(vtable->recover(fs, cancel_func, cancel_baton,
648 svn_fs_verify_root(svn_fs_root_t *root,
649 apr_pool_t *scratch_pool)
651 svn_fs_t *fs = root->fs;
652 SVN_ERR(fs->vtable->verify_root(root, scratch_pool));
658 svn_fs_freeze(svn_fs_t *fs,
659 svn_fs_freeze_func_t freeze_func,
663 SVN_ERR(fs->vtable->freeze(fs, freeze_func, freeze_baton, pool));
669 /* --- Berkeley-specific functions --- */
672 svn_fs_create_berkeley(svn_fs_t *fs, const char *path)
674 fs_library_vtable_t *vtable;
676 SVN_ERR(get_library_vtable(&vtable, SVN_FS_TYPE_BDB, fs->pool));
678 /* Create the FS directory and write out the fsap-name file. */
679 SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, fs->pool));
680 SVN_ERR(write_fs_type(path, SVN_FS_TYPE_BDB, fs->pool));
682 /* Perform the actual creation. */
683 SVN_MUTEX__WITH_LOCK(common_pool_lock,
684 vtable->create(fs, path, fs->pool, common_pool));
685 SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open));
691 svn_fs_open_berkeley(svn_fs_t *fs, const char *path)
693 fs_library_vtable_t *vtable;
695 SVN_ERR(fs_library_vtable(&vtable, path, fs->pool));
696 SVN_MUTEX__WITH_LOCK(common_pool_lock,
697 vtable->open_fs(fs, path, fs->pool, common_pool));
698 SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open));
704 svn_fs_berkeley_path(svn_fs_t *fs, apr_pool_t *pool)
706 return svn_fs_path(fs, pool);
710 svn_fs_delete_berkeley(const char *path, apr_pool_t *pool)
712 return svn_error_trace(svn_fs_delete_fs(path, pool));
716 svn_fs_hotcopy_berkeley(const char *src_path, const char *dest_path,
717 svn_boolean_t clean_logs, apr_pool_t *pool)
719 return svn_error_trace(svn_fs_hotcopy2(src_path, dest_path, clean_logs,
720 FALSE, NULL, NULL, pool));
724 svn_fs_berkeley_recover(const char *path, apr_pool_t *pool)
726 return svn_error_trace(svn_fs_recover(path, NULL, NULL, pool));
730 svn_fs_set_berkeley_errcall(svn_fs_t *fs,
731 void (*handler)(const char *errpfx, char *msg))
733 return svn_error_trace(fs->vtable->bdb_set_errcall(fs, handler));
737 svn_fs_berkeley_logfiles(apr_array_header_t **logfiles,
739 svn_boolean_t only_unused,
742 fs_library_vtable_t *vtable;
744 SVN_ERR(fs_library_vtable(&vtable, path, pool));
745 return svn_error_trace(vtable->bdb_logfiles(logfiles, path, only_unused,
750 /* --- Transaction functions --- */
753 svn_fs_begin_txn2(svn_fs_txn_t **txn_p, svn_fs_t *fs, svn_revnum_t rev,
754 apr_uint32_t flags, apr_pool_t *pool)
756 return svn_error_trace(fs->vtable->begin_txn(txn_p, fs, rev, flags, pool));
761 svn_fs_begin_txn(svn_fs_txn_t **txn_p, svn_fs_t *fs, svn_revnum_t rev,
764 return svn_error_trace(svn_fs_begin_txn2(txn_p, fs, rev, 0, pool));
769 svn_fs_commit_txn(const char **conflict_p, svn_revnum_t *new_rev,
770 svn_fs_txn_t *txn, apr_pool_t *pool)
774 *new_rev = SVN_INVALID_REVNUM;
778 err = txn->vtable->commit(conflict_p, new_rev, txn, pool);
781 /* Check postconditions. */
784 SVN_ERR_ASSERT_E(! (SVN_IS_VALID_REVNUM(*new_rev) && *conflict_p != NULL),
786 SVN_ERR_ASSERT_E((*conflict_p != NULL)
787 == (err && err->apr_err == SVN_ERR_FS_CONFLICT),
794 #ifdef PACK_AFTER_EVERY_COMMIT
796 svn_fs_t *fs = txn->fs;
797 const char *fs_path = svn_fs_path(fs, pool);
798 err = svn_fs_pack(fs_path, NULL, NULL, NULL, NULL, pool);
799 if (err && err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)
800 /* Pre-1.6 filesystem. */
801 svn_error_clear(err);
804 return svn_error_trace(err);
813 svn_fs_abort_txn(svn_fs_txn_t *txn, apr_pool_t *pool)
815 return svn_error_trace(txn->vtable->abort(txn, pool));
819 svn_fs_purge_txn(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool)
821 return svn_error_trace(fs->vtable->purge_txn(fs, txn_id, pool));
825 svn_fs_txn_name(const char **name_p, svn_fs_txn_t *txn, apr_pool_t *pool)
827 *name_p = apr_pstrdup(pool, txn->id);
832 svn_fs_txn_base_revision(svn_fs_txn_t *txn)
834 return txn->base_rev;
838 svn_fs_open_txn(svn_fs_txn_t **txn, svn_fs_t *fs, const char *name,
841 return svn_error_trace(fs->vtable->open_txn(txn, fs, name, pool));
845 svn_fs_list_transactions(apr_array_header_t **names_p, svn_fs_t *fs,
848 return svn_error_trace(fs->vtable->list_transactions(names_p, fs, pool));
852 svn_fs_txn_prop(svn_string_t **value_p, svn_fs_txn_t *txn,
853 const char *propname, apr_pool_t *pool)
855 return svn_error_trace(txn->vtable->get_prop(value_p, txn, propname, pool));
859 svn_fs_txn_proplist(apr_hash_t **table_p, svn_fs_txn_t *txn, apr_pool_t *pool)
861 return svn_error_trace(txn->vtable->get_proplist(table_p, txn, pool));
865 svn_fs_change_txn_prop(svn_fs_txn_t *txn, const char *name,
866 const svn_string_t *value, apr_pool_t *pool)
868 return svn_error_trace(txn->vtable->change_prop(txn, name, value, pool));
872 svn_fs_change_txn_props(svn_fs_txn_t *txn, const apr_array_header_t *props,
875 return svn_error_trace(txn->vtable->change_props(txn, props, pool));
879 /* --- Root functions --- */
882 svn_fs_revision_root(svn_fs_root_t **root_p, svn_fs_t *fs, svn_revnum_t rev,
885 /* We create a subpool for each root object to allow us to implement
886 svn_fs_close_root. */
887 apr_pool_t *subpool = svn_pool_create(pool);
888 return svn_error_trace(fs->vtable->revision_root(root_p, fs, rev, subpool));
892 svn_fs_txn_root(svn_fs_root_t **root_p, svn_fs_txn_t *txn, apr_pool_t *pool)
894 /* We create a subpool for each root object to allow us to implement
895 svn_fs_close_root. */
896 apr_pool_t *subpool = svn_pool_create(pool);
897 return svn_error_trace(txn->vtable->root(root_p, txn, subpool));
901 svn_fs_close_root(svn_fs_root_t *root)
903 svn_pool_destroy(root->pool);
907 svn_fs_root_fs(svn_fs_root_t *root)
913 svn_fs_is_txn_root(svn_fs_root_t *root)
915 return root->is_txn_root;
919 svn_fs_is_revision_root(svn_fs_root_t *root)
921 return !root->is_txn_root;
925 svn_fs_txn_root_name(svn_fs_root_t *root, apr_pool_t *pool)
927 return root->is_txn_root ? apr_pstrdup(pool, root->txn) : NULL;
931 svn_fs_txn_root_base_revision(svn_fs_root_t *root)
933 return root->is_txn_root ? root->rev : SVN_INVALID_REVNUM;
937 svn_fs_revision_root_revision(svn_fs_root_t *root)
939 return root->is_txn_root ? SVN_INVALID_REVNUM : root->rev;
943 svn_fs_paths_changed2(apr_hash_t **changed_paths_p, svn_fs_root_t *root,
946 return root->vtable->paths_changed(changed_paths_p, root, pool);
950 svn_fs_paths_changed(apr_hash_t **changed_paths_p, svn_fs_root_t *root,
953 apr_hash_t *changed_paths_new_structs;
954 apr_hash_index_t *hi;
956 SVN_ERR(svn_fs_paths_changed2(&changed_paths_new_structs, root, pool));
957 *changed_paths_p = apr_hash_make(pool);
958 for (hi = apr_hash_first(pool, changed_paths_new_structs);
960 hi = apr_hash_next(hi))
965 svn_fs_path_change2_t *val;
966 svn_fs_path_change_t *change;
967 apr_hash_this(hi, &vkey, &klen, &vval);
969 change = apr_palloc(pool, sizeof(*change));
970 change->node_rev_id = val->node_rev_id;
971 change->change_kind = val->change_kind;
972 change->text_mod = val->text_mod;
973 change->prop_mod = val->prop_mod;
974 apr_hash_set(*changed_paths_p, vkey, klen, change);
980 svn_fs_check_path(svn_node_kind_t *kind_p, svn_fs_root_t *root,
981 const char *path, apr_pool_t *pool)
983 return svn_error_trace(root->vtable->check_path(kind_p, root, path, pool));
987 svn_fs_node_history(svn_fs_history_t **history_p, svn_fs_root_t *root,
988 const char *path, apr_pool_t *pool)
990 return svn_error_trace(root->vtable->node_history(history_p, root, path,
995 svn_fs_is_dir(svn_boolean_t *is_dir, svn_fs_root_t *root, const char *path,
998 svn_node_kind_t kind;
1000 SVN_ERR(root->vtable->check_path(&kind, root, path, pool));
1001 *is_dir = (kind == svn_node_dir);
1002 return SVN_NO_ERROR;
1006 svn_fs_is_file(svn_boolean_t *is_file, svn_fs_root_t *root, const char *path,
1009 svn_node_kind_t kind;
1011 SVN_ERR(root->vtable->check_path(&kind, root, path, pool));
1012 *is_file = (kind == svn_node_file);
1013 return SVN_NO_ERROR;
1017 svn_fs_node_id(const svn_fs_id_t **id_p, svn_fs_root_t *root,
1018 const char *path, apr_pool_t *pool)
1020 return svn_error_trace(root->vtable->node_id(id_p, root, path, pool));
1024 svn_fs_node_created_rev(svn_revnum_t *revision, svn_fs_root_t *root,
1025 const char *path, apr_pool_t *pool)
1027 return svn_error_trace(root->vtable->node_created_rev(revision, root, path,
1032 svn_fs_node_origin_rev(svn_revnum_t *revision, svn_fs_root_t *root,
1033 const char *path, apr_pool_t *pool)
1035 return svn_error_trace(root->vtable->node_origin_rev(revision, root, path,
1040 svn_fs_node_created_path(const char **created_path, svn_fs_root_t *root,
1041 const char *path, apr_pool_t *pool)
1043 return svn_error_trace(root->vtable->node_created_path(created_path, root,
1048 svn_fs_node_prop(svn_string_t **value_p, svn_fs_root_t *root,
1049 const char *path, const char *propname, apr_pool_t *pool)
1051 return svn_error_trace(root->vtable->node_prop(value_p, root, path,
1056 svn_fs_node_proplist(apr_hash_t **table_p, svn_fs_root_t *root,
1057 const char *path, apr_pool_t *pool)
1059 return svn_error_trace(root->vtable->node_proplist(table_p, root, path,
1064 svn_fs_change_node_prop(svn_fs_root_t *root, const char *path,
1065 const char *name, const svn_string_t *value,
1068 return svn_error_trace(root->vtable->change_node_prop(root, path, name,
1073 svn_fs_props_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1074 const char *path1, svn_fs_root_t *root2,
1075 const char *path2, apr_pool_t *pool)
1077 return svn_error_trace(root1->vtable->props_changed(changed_p,
1084 svn_fs_copied_from(svn_revnum_t *rev_p, const char **path_p,
1085 svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1087 return svn_error_trace(root->vtable->copied_from(rev_p, path_p, root, path,
1092 svn_fs_closest_copy(svn_fs_root_t **root_p, const char **path_p,
1093 svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1095 return svn_error_trace(root->vtable->closest_copy(root_p, path_p,
1100 svn_fs_get_mergeinfo2(svn_mergeinfo_catalog_t *catalog,
1101 svn_fs_root_t *root,
1102 const apr_array_header_t *paths,
1103 svn_mergeinfo_inheritance_t inherit,
1104 svn_boolean_t include_descendants,
1105 svn_boolean_t adjust_inherited_mergeinfo,
1106 apr_pool_t *result_pool,
1107 apr_pool_t *scratch_pool)
1109 return svn_error_trace(root->vtable->get_mergeinfo(
1110 catalog, root, paths, inherit, include_descendants,
1111 adjust_inherited_mergeinfo, result_pool, scratch_pool));
1115 svn_fs_get_mergeinfo(svn_mergeinfo_catalog_t *catalog,
1116 svn_fs_root_t *root,
1117 const apr_array_header_t *paths,
1118 svn_mergeinfo_inheritance_t inherit,
1119 svn_boolean_t include_descendants,
1122 return svn_error_trace(root->vtable->get_mergeinfo(catalog, root, paths,
1124 include_descendants,
1129 svn_fs_merge(const char **conflict_p, svn_fs_root_t *source_root,
1130 const char *source_path, svn_fs_root_t *target_root,
1131 const char *target_path, svn_fs_root_t *ancestor_root,
1132 const char *ancestor_path, apr_pool_t *pool)
1134 return svn_error_trace(target_root->vtable->merge(conflict_p,
1135 source_root, source_path,
1136 target_root, target_path,
1138 ancestor_path, pool));
1142 svn_fs_dir_entries(apr_hash_t **entries_p, svn_fs_root_t *root,
1143 const char *path, apr_pool_t *pool)
1145 return svn_error_trace(root->vtable->dir_entries(entries_p, root, path,
1150 svn_fs_make_dir(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1152 SVN_ERR(svn_fs__path_valid(path, pool));
1153 return svn_error_trace(root->vtable->make_dir(root, path, pool));
1157 svn_fs_delete(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1159 return svn_error_trace(root->vtable->delete_node(root, path, pool));
1163 svn_fs_copy(svn_fs_root_t *from_root, const char *from_path,
1164 svn_fs_root_t *to_root, const char *to_path, apr_pool_t *pool)
1166 SVN_ERR(svn_fs__path_valid(to_path, pool));
1167 return svn_error_trace(to_root->vtable->copy(from_root, from_path,
1168 to_root, to_path, pool));
1172 svn_fs_revision_link(svn_fs_root_t *from_root, svn_fs_root_t *to_root,
1173 const char *path, apr_pool_t *pool)
1175 return svn_error_trace(to_root->vtable->revision_link(from_root, to_root,
1180 svn_fs_file_length(svn_filesize_t *length_p, svn_fs_root_t *root,
1181 const char *path, apr_pool_t *pool)
1183 return svn_error_trace(root->vtable->file_length(length_p, root, path,
1188 svn_fs_file_checksum(svn_checksum_t **checksum,
1189 svn_checksum_kind_t kind,
1190 svn_fs_root_t *root,
1192 svn_boolean_t force,
1195 SVN_ERR(root->vtable->file_checksum(checksum, kind, root, path, pool));
1197 if (force && (*checksum == NULL || (*checksum)->kind != kind))
1199 svn_stream_t *contents, *checksum_contents;
1201 SVN_ERR(svn_fs_file_contents(&contents, root, path, pool));
1202 checksum_contents = svn_stream_checksummed2(contents, checksum, NULL,
1205 /* This will force a read of any remaining data (which is all of it in
1206 this case) and dump the checksum into checksum->digest. */
1207 SVN_ERR(svn_stream_close(checksum_contents));
1210 return SVN_NO_ERROR;
1214 svn_fs_file_md5_checksum(unsigned char digest[],
1215 svn_fs_root_t *root,
1219 svn_checksum_t *md5sum;
1221 SVN_ERR(svn_fs_file_checksum(&md5sum, svn_checksum_md5, root, path, TRUE,
1223 memcpy(digest, md5sum->digest, APR_MD5_DIGESTSIZE);
1225 return SVN_NO_ERROR;
1229 svn_fs_file_contents(svn_stream_t **contents, svn_fs_root_t *root,
1230 const char *path, apr_pool_t *pool)
1232 return svn_error_trace(root->vtable->file_contents(contents, root, path,
1237 svn_fs_try_process_file_contents(svn_boolean_t *success,
1238 svn_fs_root_t *root,
1240 svn_fs_process_contents_func_t processor,
1244 /* if the FS doesn't implement this function, report a "failed" attempt */
1245 if (root->vtable->try_process_file_contents == NULL)
1248 return SVN_NO_ERROR;
1251 return svn_error_trace(root->vtable->try_process_file_contents(
1254 processor, baton, pool));
1258 svn_fs_make_file(svn_fs_root_t *root, const char *path, apr_pool_t *pool)
1260 SVN_ERR(svn_fs__path_valid(path, pool));
1261 return svn_error_trace(root->vtable->make_file(root, path, pool));
1265 svn_fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
1266 void **contents_baton_p, svn_fs_root_t *root,
1267 const char *path, const char *base_checksum,
1268 const char *result_checksum, apr_pool_t *pool)
1270 svn_checksum_t *base, *result;
1272 /* TODO: If we ever rev this API, we should make the supplied checksums
1273 svn_checksum_t structs. */
1274 SVN_ERR(svn_checksum_parse_hex(&base, svn_checksum_md5, base_checksum,
1276 SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum,
1279 return svn_error_trace(root->vtable->apply_textdelta(contents_p,
1289 svn_fs_apply_text(svn_stream_t **contents_p, svn_fs_root_t *root,
1290 const char *path, const char *result_checksum,
1293 svn_checksum_t *result;
1295 /* TODO: If we ever rev this API, we should make the supplied checksum an
1296 svn_checksum_t struct. */
1297 SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum,
1300 return svn_error_trace(root->vtable->apply_text(contents_p, root, path,
1305 svn_fs_contents_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1,
1306 const char *path1, svn_fs_root_t *root2,
1307 const char *path2, apr_pool_t *pool)
1309 return svn_error_trace(root1->vtable->contents_changed(changed_p,
1316 svn_fs_youngest_rev(svn_revnum_t *youngest_p, svn_fs_t *fs, apr_pool_t *pool)
1318 return svn_error_trace(fs->vtable->youngest_rev(youngest_p, fs, pool));
1322 svn_fs_deltify_revision(svn_fs_t *fs, svn_revnum_t revision, apr_pool_t *pool)
1324 return svn_error_trace(fs->vtable->deltify(fs, revision, pool));
1328 svn_fs_revision_prop(svn_string_t **value_p, svn_fs_t *fs, svn_revnum_t rev,
1329 const char *propname, apr_pool_t *pool)
1331 return svn_error_trace(fs->vtable->revision_prop(value_p, fs, rev,
1336 svn_fs_revision_proplist(apr_hash_t **table_p, svn_fs_t *fs, svn_revnum_t rev,
1339 return svn_error_trace(fs->vtable->revision_proplist(table_p, fs, rev,
1344 svn_fs_change_rev_prop2(svn_fs_t *fs, svn_revnum_t rev, const char *name,
1345 const svn_string_t *const *old_value_p,
1346 const svn_string_t *value, apr_pool_t *pool)
1348 return svn_error_trace(fs->vtable->change_rev_prop(fs, rev, name,
1354 svn_fs_change_rev_prop(svn_fs_t *fs, svn_revnum_t rev, const char *name,
1355 const svn_string_t *value, apr_pool_t *pool)
1357 return svn_error_trace(
1358 svn_fs_change_rev_prop2(fs, rev, name, NULL, value, pool));
1362 svn_fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
1363 svn_fs_root_t *source_root,
1364 const char *source_path,
1365 svn_fs_root_t *target_root,
1366 const char *target_path, apr_pool_t *pool)
1368 return svn_error_trace(target_root->vtable->get_file_delta_stream(
1370 source_root, source_path,
1371 target_root, target_path, pool));
1375 svn_fs_get_uuid(svn_fs_t *fs, const char **uuid, apr_pool_t *pool)
1377 /* If you change this, consider changing svn_fs__identifier(). */
1378 *uuid = apr_pstrdup(pool, fs->uuid);
1379 return SVN_NO_ERROR;
1383 svn_fs_set_uuid(svn_fs_t *fs, const char *uuid, apr_pool_t *pool)
1387 uuid = svn_uuid_generate(pool);
1391 apr_uuid_t parsed_uuid;
1392 apr_status_t apr_err = apr_uuid_parse(&parsed_uuid, uuid);
1394 return svn_error_createf(SVN_ERR_BAD_UUID, NULL,
1395 _("Malformed UUID '%s'"), uuid);
1397 return svn_error_trace(fs->vtable->set_uuid(fs, uuid, pool));
1401 svn_fs_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path,
1402 const char *token, const char *comment,
1403 svn_boolean_t is_dav_comment, apr_time_t expiration_date,
1404 svn_revnum_t current_rev, svn_boolean_t steal_lock,
1407 /* Enforce that the comment be xml-escapable. */
1410 if (! svn_xml_is_xml_safe(comment, strlen(comment)))
1411 return svn_error_create
1412 (SVN_ERR_XML_UNESCAPABLE_DATA, NULL,
1413 _("Lock comment contains illegal characters"));
1416 /* Enforce that the token be an XML-safe URI. */
1421 if (strncmp(token, "opaquelocktoken:", 16))
1422 return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1423 _("Lock token URI '%s' has bad scheme; "
1425 token, "opaquelocktoken");
1427 for (c = token; *c; c++)
1428 if (! svn_ctype_isascii(*c))
1429 return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1430 _("Lock token '%s' is not ASCII "
1432 token, (unsigned)(c - token));
1434 /* strlen(token) == c - token. */
1435 if (! svn_xml_is_xml_safe(token, c - token))
1436 return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
1437 _("Lock token URI '%s' is not XML-safe"),
1441 if (expiration_date < 0)
1442 return svn_error_create
1443 (SVN_ERR_INCORRECT_PARAMS, NULL,
1444 _("Negative expiration date passed to svn_fs_lock"));
1446 return svn_error_trace(fs->vtable->lock(lock, fs, path, token, comment,
1447 is_dav_comment, expiration_date,
1448 current_rev, steal_lock, pool));
1452 svn_fs_generate_lock_token(const char **token, svn_fs_t *fs, apr_pool_t *pool)
1454 return svn_error_trace(fs->vtable->generate_lock_token(token, fs, pool));
1458 svn_fs_unlock(svn_fs_t *fs, const char *path, const char *token,
1459 svn_boolean_t break_lock, apr_pool_t *pool)
1461 return svn_error_trace(fs->vtable->unlock(fs, path, token, break_lock,
1466 svn_fs_get_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path,
1469 return svn_error_trace(fs->vtable->get_lock(lock, fs, path, pool));
1473 svn_fs_get_locks2(svn_fs_t *fs, const char *path, svn_depth_t depth,
1474 svn_fs_get_locks_callback_t get_locks_func,
1475 void *get_locks_baton, apr_pool_t *pool)
1477 SVN_ERR_ASSERT((depth == svn_depth_empty) ||
1478 (depth == svn_depth_files) ||
1479 (depth == svn_depth_immediates) ||
1480 (depth == svn_depth_infinity));
1481 return svn_error_trace(fs->vtable->get_locks(fs, path, depth,
1483 get_locks_baton, pool));
1487 svn_fs_get_locks(svn_fs_t *fs, const char *path,
1488 svn_fs_get_locks_callback_t get_locks_func,
1489 void *get_locks_baton, apr_pool_t *pool)
1491 return svn_error_trace(svn_fs_get_locks2(fs, path, svn_depth_infinity,
1492 get_locks_func, get_locks_baton,
1497 /* --- History functions --- */
1500 svn_fs_history_prev(svn_fs_history_t **prev_history_p,
1501 svn_fs_history_t *history, svn_boolean_t cross_copies,
1504 return svn_error_trace(history->vtable->prev(prev_history_p, history,
1505 cross_copies, pool));
1509 svn_fs_history_location(const char **path, svn_revnum_t *revision,
1510 svn_fs_history_t *history, apr_pool_t *pool)
1512 return svn_error_trace(history->vtable->location(path, revision, history,
1517 /* --- Node-ID functions --- */
1520 svn_fs_parse_id(const char *data, apr_size_t len, apr_pool_t *pool)
1522 fs_library_vtable_t *vtable;
1525 err = get_library_vtable(&vtable, SVN_FS_TYPE_BDB, pool);
1528 svn_error_clear(err);
1531 return vtable->parse_id(data, len, pool);
1535 svn_fs_unparse_id(const svn_fs_id_t *id, apr_pool_t *pool)
1537 return id->vtable->unparse(id, pool);
1541 svn_fs_check_related(const svn_fs_id_t *a, const svn_fs_id_t *b)
1543 return (a->vtable->compare(a, b) != -1);
1547 svn_fs_compare_ids(const svn_fs_id_t *a, const svn_fs_id_t *b)
1549 return a->vtable->compare(a, b);
1553 svn_fs_print_modules(svn_stringbuf_t *output,
1556 const struct fs_type_defn *defn = fs_modules;
1557 fs_library_vtable_t *vtable;
1558 apr_pool_t *iterpool = svn_pool_create(pool);
1565 svn_pool_clear(iterpool);
1567 err = get_library_vtable_direct(&vtable, defn, iterpool);
1570 if (err->apr_err == SVN_ERR_FS_UNKNOWN_FS_TYPE)
1572 svn_error_clear(err);
1580 line = apr_psprintf(iterpool, "* fs_%s : %s\n",
1581 defn->fsap_name, vtable->get_description());
1582 svn_stringbuf_appendcstr(output, line);
1586 svn_pool_destroy(iterpool);
1588 return SVN_NO_ERROR;
1591 svn_fs_path_change2_t *
1592 svn_fs_path_change2_create(const svn_fs_id_t *node_rev_id,
1593 svn_fs_path_change_kind_t change_kind,
1596 return svn_fs__path_change_create_internal(node_rev_id, change_kind, pool);
1599 /* Return the library version number. */
1600 const svn_version_t *
1601 svn_fs_version(void)