1/* 2 * fs_loader.c: Front-end to the various FS back ends 3 * 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 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 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 20 * under the License. 21 * ==================================================================== 22 */ 23 24 25#include <string.h> 26#include <apr.h> 27#include <apr_hash.h> 28#include <apr_md5.h> 29#include <apr_thread_mutex.h> 30#include <apr_uuid.h> 31#include <apr_strings.h> 32 33#include "svn_hash.h" 34#include "svn_ctype.h" 35#include "svn_types.h" 36#include "svn_dso.h" 37#include "svn_version.h" 38#include "svn_fs.h" 39#include "svn_path.h" 40#include "svn_xml.h" 41#include "svn_pools.h" 42#include "svn_string.h" 43#include "svn_private_config.h" 44 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" 50 51#include "fs-loader.h" 52 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" 57#endif 58 59#define FS_TYPE_FILENAME "fs-type" 60 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(). */ 63static apr_pool_t *common_pool; 64svn_mutex__t *common_pool_lock; 65 66 67/* --- Utility functions for the loader --- */ 68 69struct fs_type_defn { 70 const char *fs_type; 71 const char *fsap_name; 72 fs_init_func_t initfunc; 73 struct fs_type_defn *next; 74}; 75 76static struct fs_type_defn base_defn = 77 { 78 SVN_FS_TYPE_BDB, "base", 79#ifdef SVN_LIBSVN_FS_LINKS_FS_BASE 80 svn_fs_base__init, 81#else 82 NULL, 83#endif 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. */ 86 }; 87 88static struct fs_type_defn fsfs_defn = 89 { 90 SVN_FS_TYPE_FSFS, "fs", 91#ifdef SVN_LIBSVN_FS_LINKS_FS_FS 92 svn_fs_fs__init, 93#else 94 NULL, 95#endif 96 &base_defn 97 }; 98 99static struct fs_type_defn *fs_modules = &fsfs_defn; 100 101 102static svn_error_t * 103load_module(fs_init_func_t *initfunc, const char *name, apr_pool_t *pool) 104{ 105 *initfunc = NULL; 106 107#if defined(SVN_USE_DSO) && APR_HAS_DSO 108 { 109 apr_dso_handle_t *dso; 110 apr_dso_handle_sym_t symbol; 111 const char *libname; 112 const char *funcname; 113 apr_status_t status; 114 const char *p; 115 116 /* Demand a simple alphanumeric name so that the generated DSO 117 name is sensible. */ 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'"), 122 name); 123 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); 127 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)); 132 if (! dso) 133 return SVN_NO_ERROR; 134 135 /* find the initialization routine */ 136 status = apr_dso_sym(&symbol, dso, funcname); 137 if (status) 138 return svn_error_wrap_apr(status, _("'%s' does not define '%s()'"), 139 libname, funcname); 140 141 *initfunc = (fs_init_func_t) symbol; 142 } 143#endif /* APR_HAS_DSO */ 144 145 return SVN_NO_ERROR; 146} 147 148/* Fetch a library vtable by a pointer into the library definitions array. */ 149static svn_error_t * 150get_library_vtable_direct(fs_library_vtable_t **vtable, 151 const struct fs_type_defn *fst, 152 apr_pool_t *pool) 153{ 154 fs_init_func_t initfunc = NULL; 155 const svn_version_t *my_version = svn_fs_version(); 156 const svn_version_t *fs_version; 157 158 initfunc = fst->initfunc; 159 if (! initfunc) 160 SVN_ERR(load_module(&initfunc, fst->fsap_name, pool)); 161 162 if (! initfunc) 163 return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL, 164 _("Failed to load module for FS type '%s'"), 165 fst->fs_type); 166 167 { 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. */ 175 if (!common_pool) 176 SVN_ERR(svn_fs_initialize(NULL)); 177 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)); 182 } 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':" 187 " found %d.%d.%d%s," 188 " expected %d.%d.%d%s"), 189 fst->fs_type, 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); 194 return SVN_NO_ERROR; 195} 196 197#if defined(SVN_USE_DSO) && APR_HAS_DSO 198/* Return *FST for the third party FS_TYPE */ 199static svn_error_t * 200get_or_allocate_third(struct fs_type_defn **fst, 201 const char *fs_type) 202{ 203 while (*fst) 204 { 205 if (strcmp(fs_type, (*fst)->fs_type) == 0) 206 return SVN_NO_ERROR; 207 fst = &(*fst)->next; 208 } 209 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; 214 (*fst)->next = NULL; 215 216 return SVN_NO_ERROR; 217} 218#endif 219 220/* Fetch a library vtable by FS type. */ 221static svn_error_t * 222get_library_vtable(fs_library_vtable_t **vtable, const char *fs_type, 223 apr_pool_t *pool) 224{ 225 struct fs_type_defn **fst = &fs_modules; 226 svn_boolean_t known = FALSE; 227 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) 233 known = TRUE; 234 else 235 { 236 fst = &(*fst)->next; 237 if (strcmp(fs_type, (*fst)->fs_type) == 0) 238 known = TRUE; 239 } 240 241#if defined(SVN_USE_DSO) && APR_HAS_DSO 242 /* Third party FS modules that are unknown at compile time. 243 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 247 "svn_fs_foo__init". 248 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 253 modules. */ 254 if (!known) 255 { 256 fst = &(*fst)->next; 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)); 261 known = TRUE; 262 } 263#endif 264 if (!known) 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); 268} 269 270svn_error_t * 271svn_fs_type(const char **fs_type, const char *path, apr_pool_t *pool) 272{ 273 const char *filename; 274 char buf[128]; 275 svn_error_t *err; 276 apr_file_t *file; 277 apr_size_t len; 278 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 282 directory exists. */ 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)) 286 { 287 svn_node_kind_t kind; 288 svn_error_t *err2 = svn_io_check_path(path, &kind, pool); 289 if (err2) 290 { 291 svn_error_clear(err2); 292 return err; 293 } 294 if (kind == svn_node_dir) 295 { 296 svn_error_clear(err); 297 *fs_type = SVN_FS_TYPE_BDB; 298 return SVN_NO_ERROR; 299 } 300 return err; 301 } 302 else if (err) 303 return err; 304 305 len = sizeof(buf); 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); 309 310 return SVN_NO_ERROR; 311} 312 313/* Fetch the library vtable for an existing FS. */ 314static svn_error_t * 315fs_library_vtable(fs_library_vtable_t **vtable, const char *path, 316 apr_pool_t *pool) 317{ 318 const char *fs_type; 319 320 SVN_ERR(svn_fs_type(&fs_type, path, pool)); 321 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)); 324} 325 326static svn_error_t * 327write_fs_type(const char *path, const char *fs_type, apr_pool_t *pool) 328{ 329 const char *filename; 330 apr_file_t *file; 331 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, 337 pool)); 338 SVN_ERR(svn_io_file_write_full(file, "\n", 1, NULL, pool)); 339 return svn_error_trace(svn_io_file_close(file, pool)); 340} 341 342 343/* --- Functions for operating on filesystems by pathname --- */ 344 345static apr_status_t uninit(void *data) 346{ 347 common_pool = NULL; 348 return APR_SUCCESS; 349} 350 351svn_error_t * 352svn_fs_initialize(apr_pool_t *pool) 353{ 354 /* Protect against multiple calls. */ 355 if (common_pool) 356 return SVN_NO_ERROR; 357 358 common_pool = svn_pool_create(pool); 359 base_defn.next = NULL; 360 SVN_ERR(svn_mutex__init(&common_pool_lock, TRUE, common_pool)); 361 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); 369 return SVN_NO_ERROR; 370} 371 372/* A default warning handling function. */ 373static void 374default_warning_func(void *baton, svn_error_t *err) 375{ 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(); 380} 381 382svn_error_t * 383svn_fs__path_valid(const char *path, apr_pool_t *pool) 384{ 385 /* UTF-8 encoded string without NULs. */ 386 if (! svn_utf__cstring_is_valid(path)) 387 { 388 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL, 389 _("Path '%s' is not in UTF-8"), path); 390 } 391 392 /* No "." or ".." elements. */ 393 if (svn_path_is_backpath_present(path) 394 || svn_path_is_dotpath_present(path)) 395 { 396 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL, 397 _("Path '%s' contains '.' or '..' element"), 398 path); 399 } 400 401 /* That's good enough. */ 402 return SVN_NO_ERROR; 403} 404 405/* Allocate svn_fs_t structure. */ 406static svn_fs_t * 407fs_new(apr_hash_t *fs_config, apr_pool_t *pool) 408{ 409 svn_fs_t *fs = apr_palloc(pool, sizeof(*fs)); 410 fs->pool = pool; 411 fs->path = NULL; 412 fs->warning = default_warning_func; 413 fs->warning_baton = NULL; 414 fs->config = fs_config; 415 fs->access_ctx = NULL; 416 fs->vtable = NULL; 417 fs->fsap_data = NULL; 418 fs->uuid = NULL; 419 return fs; 420} 421 422svn_fs_t * 423svn_fs_new(apr_hash_t *fs_config, apr_pool_t *pool) 424{ 425 return fs_new(fs_config, pool); 426} 427 428void 429svn_fs_set_warning_func(svn_fs_t *fs, svn_fs_warning_callback_t warning, 430 void *warning_baton) 431{ 432 fs->warning = warning; 433 fs->warning_baton = warning_baton; 434} 435 436svn_error_t * 437svn_fs_create(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config, 438 apr_pool_t *pool) 439{ 440 fs_library_vtable_t *vtable; 441 442 const char *fs_type = svn_hash__get_cstring(fs_config, 443 SVN_FS_CONFIG_FS_TYPE, 444 DEFAULT_FS_TYPE); 445 SVN_ERR(get_library_vtable(&vtable, fs_type, pool)); 446 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)); 450 451 /* Perform the actual creation. */ 452 *fs_p = fs_new(fs_config, pool); 453 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)); 457 458 return SVN_NO_ERROR; 459} 460 461svn_error_t * 462svn_fs_open(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config, 463 apr_pool_t *pool) 464{ 465 fs_library_vtable_t *vtable; 466 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)); 472 473 return SVN_NO_ERROR; 474} 475 476svn_error_t * 477svn_fs_upgrade(const char *path, apr_pool_t *pool) 478{ 479 fs_library_vtable_t *vtable; 480 svn_fs_t *fs; 481 482 SVN_ERR(fs_library_vtable(&vtable, path, pool)); 483 fs = fs_new(NULL, pool); 484 485 SVN_MUTEX__WITH_LOCK(common_pool_lock, 486 vtable->upgrade_fs(fs, path, pool, common_pool)); 487 return SVN_NO_ERROR; 488} 489 490svn_error_t * 491svn_fs_verify(const char *path, 492 apr_hash_t *fs_config, 493 svn_revnum_t start, 494 svn_revnum_t end, 495 svn_fs_progress_notify_func_t notify_func, 496 void *notify_baton, 497 svn_cancel_func_t cancel_func, 498 void *cancel_baton, 499 apr_pool_t *pool) 500{ 501 fs_library_vtable_t *vtable; 502 svn_fs_t *fs; 503 504 SVN_ERR(fs_library_vtable(&vtable, path, pool)); 505 fs = fs_new(fs_config, pool); 506 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, 511 pool, common_pool)); 512 return SVN_NO_ERROR; 513} 514 515const char * 516svn_fs_path(svn_fs_t *fs, apr_pool_t *pool) 517{ 518 return apr_pstrdup(pool, fs->path); 519} 520 521apr_hash_t * 522svn_fs_config(svn_fs_t *fs, apr_pool_t *pool) 523{ 524 if (fs->config) 525 return apr_hash_copy(pool, fs->config); 526 527 return NULL; 528} 529 530svn_error_t * 531svn_fs_delete_fs(const char *path, apr_pool_t *pool) 532{ 533 fs_library_vtable_t *vtable; 534 535 SVN_ERR(fs_library_vtable(&vtable, path, pool)); 536 return svn_error_trace(vtable->delete_fs(path, pool)); 537} 538 539svn_error_t * 540svn_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) 544{ 545 fs_library_vtable_t *vtable; 546 const char *src_fs_type; 547 svn_fs_t *src_fs; 548 svn_fs_t *dst_fs; 549 const char *dst_fs_type; 550 svn_node_kind_t dst_kind; 551 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")); 555 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); 560 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, 566 scratch_pool)); 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 " 570 "node kind"), 571 svn_dirent_local_style(dst_path, 572 scratch_pool)); 573 if (dst_kind == svn_node_dir) 574 { 575 svn_node_kind_t type_file_kind; 576 577 SVN_ERR(svn_io_check_path(svn_dirent_join(dst_path, 578 FS_TYPE_FILENAME, 579 scratch_pool), 580 &type_file_kind, scratch_pool)); 581 if (type_file_kind != svn_node_none) 582 { 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); 591 } 592 } 593 594 SVN_ERR(vtable->hotcopy(src_fs, dst_fs, src_path, dst_path, clean, 595 incremental, cancel_func, cancel_baton, 596 scratch_pool)); 597 return svn_error_trace(write_fs_type(dst_path, src_fs_type, scratch_pool)); 598} 599 600svn_error_t * 601svn_fs_hotcopy(const char *src_path, const char *dest_path, 602 svn_boolean_t clean, apr_pool_t *pool) 603{ 604 return svn_error_trace(svn_fs_hotcopy2(src_path, dest_path, clean, 605 FALSE, NULL, NULL, pool)); 606} 607 608svn_error_t * 609svn_fs_pack(const char *path, 610 svn_fs_pack_notify_t notify_func, 611 void *notify_baton, 612 svn_cancel_func_t cancel_func, 613 void *cancel_baton, 614 apr_pool_t *pool) 615{ 616 fs_library_vtable_t *vtable; 617 svn_fs_t *fs; 618 619 SVN_ERR(fs_library_vtable(&vtable, path, pool)); 620 fs = fs_new(NULL, pool); 621 622 SVN_MUTEX__WITH_LOCK(common_pool_lock, 623 vtable->pack_fs(fs, path, notify_func, notify_baton, 624 cancel_func, cancel_baton, pool, 625 common_pool)); 626 return SVN_NO_ERROR; 627} 628 629svn_error_t * 630svn_fs_recover(const char *path, 631 svn_cancel_func_t cancel_func, void *cancel_baton, 632 apr_pool_t *pool) 633{ 634 fs_library_vtable_t *vtable; 635 svn_fs_t *fs; 636 637 SVN_ERR(fs_library_vtable(&vtable, path, pool)); 638 fs = fs_new(NULL, pool); 639 640 SVN_MUTEX__WITH_LOCK(common_pool_lock, 641 vtable->open_fs_for_recovery(fs, path, pool, 642 common_pool)); 643 return svn_error_trace(vtable->recover(fs, cancel_func, cancel_baton, 644 pool)); 645} 646 647svn_error_t * 648svn_fs_verify_root(svn_fs_root_t *root, 649 apr_pool_t *scratch_pool) 650{ 651 svn_fs_t *fs = root->fs; 652 SVN_ERR(fs->vtable->verify_root(root, scratch_pool)); 653 654 return SVN_NO_ERROR; 655} 656 657svn_error_t * 658svn_fs_freeze(svn_fs_t *fs, 659 svn_fs_freeze_func_t freeze_func, 660 void *freeze_baton, 661 apr_pool_t *pool) 662{ 663 SVN_ERR(fs->vtable->freeze(fs, freeze_func, freeze_baton, pool)); 664 665 return SVN_NO_ERROR; 666} 667 668 669/* --- Berkeley-specific functions --- */ 670 671svn_error_t * 672svn_fs_create_berkeley(svn_fs_t *fs, const char *path) 673{ 674 fs_library_vtable_t *vtable; 675 676 SVN_ERR(get_library_vtable(&vtable, SVN_FS_TYPE_BDB, fs->pool)); 677 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)); 681 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)); 686 687 return SVN_NO_ERROR; 688} 689 690svn_error_t * 691svn_fs_open_berkeley(svn_fs_t *fs, const char *path) 692{ 693 fs_library_vtable_t *vtable; 694 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)); 699 700 return SVN_NO_ERROR; 701} 702 703const char * 704svn_fs_berkeley_path(svn_fs_t *fs, apr_pool_t *pool) 705{ 706 return svn_fs_path(fs, pool); 707} 708 709svn_error_t * 710svn_fs_delete_berkeley(const char *path, apr_pool_t *pool) 711{ 712 return svn_error_trace(svn_fs_delete_fs(path, pool)); 713} 714 715svn_error_t * 716svn_fs_hotcopy_berkeley(const char *src_path, const char *dest_path, 717 svn_boolean_t clean_logs, apr_pool_t *pool) 718{ 719 return svn_error_trace(svn_fs_hotcopy2(src_path, dest_path, clean_logs, 720 FALSE, NULL, NULL, pool)); 721} 722 723svn_error_t * 724svn_fs_berkeley_recover(const char *path, apr_pool_t *pool) 725{ 726 return svn_error_trace(svn_fs_recover(path, NULL, NULL, pool)); 727} 728 729svn_error_t * 730svn_fs_set_berkeley_errcall(svn_fs_t *fs, 731 void (*handler)(const char *errpfx, char *msg)) 732{ 733 return svn_error_trace(fs->vtable->bdb_set_errcall(fs, handler)); 734} 735 736svn_error_t * 737svn_fs_berkeley_logfiles(apr_array_header_t **logfiles, 738 const char *path, 739 svn_boolean_t only_unused, 740 apr_pool_t *pool) 741{ 742 fs_library_vtable_t *vtable; 743 744 SVN_ERR(fs_library_vtable(&vtable, path, pool)); 745 return svn_error_trace(vtable->bdb_logfiles(logfiles, path, only_unused, 746 pool)); 747} 748 749 750/* --- Transaction functions --- */ 751 752svn_error_t * 753svn_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) 755{ 756 return svn_error_trace(fs->vtable->begin_txn(txn_p, fs, rev, flags, pool)); 757} 758 759 760svn_error_t * 761svn_fs_begin_txn(svn_fs_txn_t **txn_p, svn_fs_t *fs, svn_revnum_t rev, 762 apr_pool_t *pool) 763{ 764 return svn_error_trace(svn_fs_begin_txn2(txn_p, fs, rev, 0, pool)); 765} 766 767 768svn_error_t * 769svn_fs_commit_txn(const char **conflict_p, svn_revnum_t *new_rev, 770 svn_fs_txn_t *txn, apr_pool_t *pool) 771{ 772 svn_error_t *err; 773 774 *new_rev = SVN_INVALID_REVNUM; 775 if (conflict_p) 776 *conflict_p = NULL; 777 778 err = txn->vtable->commit(conflict_p, new_rev, txn, pool); 779 780#ifdef SVN_DEBUG 781 /* Check postconditions. */ 782 if (conflict_p) 783 { 784 SVN_ERR_ASSERT_E(! (SVN_IS_VALID_REVNUM(*new_rev) && *conflict_p != NULL), 785 err); 786 SVN_ERR_ASSERT_E((*conflict_p != NULL) 787 == (err && err->apr_err == SVN_ERR_FS_CONFLICT), 788 err); 789 } 790#endif 791 792 SVN_ERR(err); 793 794#ifdef PACK_AFTER_EVERY_COMMIT 795 { 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); 802 else if (err) 803 /* Real error. */ 804 return svn_error_trace(err); 805 } 806#endif 807 808 return SVN_NO_ERROR; 809} 810 811 812svn_error_t * 813svn_fs_abort_txn(svn_fs_txn_t *txn, apr_pool_t *pool) 814{ 815 return svn_error_trace(txn->vtable->abort(txn, pool)); 816} 817 818svn_error_t * 819svn_fs_purge_txn(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool) 820{ 821 return svn_error_trace(fs->vtable->purge_txn(fs, txn_id, pool)); 822} 823 824svn_error_t * 825svn_fs_txn_name(const char **name_p, svn_fs_txn_t *txn, apr_pool_t *pool) 826{ 827 *name_p = apr_pstrdup(pool, txn->id); 828 return SVN_NO_ERROR; 829} 830 831svn_revnum_t 832svn_fs_txn_base_revision(svn_fs_txn_t *txn) 833{ 834 return txn->base_rev; 835} 836 837svn_error_t * 838svn_fs_open_txn(svn_fs_txn_t **txn, svn_fs_t *fs, const char *name, 839 apr_pool_t *pool) 840{ 841 return svn_error_trace(fs->vtable->open_txn(txn, fs, name, pool)); 842} 843 844svn_error_t * 845svn_fs_list_transactions(apr_array_header_t **names_p, svn_fs_t *fs, 846 apr_pool_t *pool) 847{ 848 return svn_error_trace(fs->vtable->list_transactions(names_p, fs, pool)); 849} 850 851svn_error_t * 852svn_fs_txn_prop(svn_string_t **value_p, svn_fs_txn_t *txn, 853 const char *propname, apr_pool_t *pool) 854{ 855 return svn_error_trace(txn->vtable->get_prop(value_p, txn, propname, pool)); 856} 857 858svn_error_t * 859svn_fs_txn_proplist(apr_hash_t **table_p, svn_fs_txn_t *txn, apr_pool_t *pool) 860{ 861 return svn_error_trace(txn->vtable->get_proplist(table_p, txn, pool)); 862} 863 864svn_error_t * 865svn_fs_change_txn_prop(svn_fs_txn_t *txn, const char *name, 866 const svn_string_t *value, apr_pool_t *pool) 867{ 868 return svn_error_trace(txn->vtable->change_prop(txn, name, value, pool)); 869} 870 871svn_error_t * 872svn_fs_change_txn_props(svn_fs_txn_t *txn, const apr_array_header_t *props, 873 apr_pool_t *pool) 874{ 875 return svn_error_trace(txn->vtable->change_props(txn, props, pool)); 876} 877 878 879/* --- Root functions --- */ 880 881svn_error_t * 882svn_fs_revision_root(svn_fs_root_t **root_p, svn_fs_t *fs, svn_revnum_t rev, 883 apr_pool_t *pool) 884{ 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)); 889} 890 891svn_error_t * 892svn_fs_txn_root(svn_fs_root_t **root_p, svn_fs_txn_t *txn, apr_pool_t *pool) 893{ 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)); 898} 899 900void 901svn_fs_close_root(svn_fs_root_t *root) 902{ 903 svn_pool_destroy(root->pool); 904} 905 906svn_fs_t * 907svn_fs_root_fs(svn_fs_root_t *root) 908{ 909 return root->fs; 910} 911 912svn_boolean_t 913svn_fs_is_txn_root(svn_fs_root_t *root) 914{ 915 return root->is_txn_root; 916} 917 918svn_boolean_t 919svn_fs_is_revision_root(svn_fs_root_t *root) 920{ 921 return !root->is_txn_root; 922} 923 924const char * 925svn_fs_txn_root_name(svn_fs_root_t *root, apr_pool_t *pool) 926{ 927 return root->is_txn_root ? apr_pstrdup(pool, root->txn) : NULL; 928} 929 930svn_revnum_t 931svn_fs_txn_root_base_revision(svn_fs_root_t *root) 932{ 933 return root->is_txn_root ? root->rev : SVN_INVALID_REVNUM; 934} 935 936svn_revnum_t 937svn_fs_revision_root_revision(svn_fs_root_t *root) 938{ 939 return root->is_txn_root ? SVN_INVALID_REVNUM : root->rev; 940} 941 942svn_error_t * 943svn_fs_paths_changed2(apr_hash_t **changed_paths_p, svn_fs_root_t *root, 944 apr_pool_t *pool) 945{ 946 return root->vtable->paths_changed(changed_paths_p, root, pool); 947} 948 949svn_error_t * 950svn_fs_paths_changed(apr_hash_t **changed_paths_p, svn_fs_root_t *root, 951 apr_pool_t *pool) 952{ 953 apr_hash_t *changed_paths_new_structs; 954 apr_hash_index_t *hi; 955 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); 959 hi; 960 hi = apr_hash_next(hi)) 961 { 962 const void *vkey; 963 apr_ssize_t klen; 964 void *vval; 965 svn_fs_path_change2_t *val; 966 svn_fs_path_change_t *change; 967 apr_hash_this(hi, &vkey, &klen, &vval); 968 val = 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); 975 } 976 return SVN_NO_ERROR; 977} 978 979svn_error_t * 980svn_fs_check_path(svn_node_kind_t *kind_p, svn_fs_root_t *root, 981 const char *path, apr_pool_t *pool) 982{ 983 return svn_error_trace(root->vtable->check_path(kind_p, root, path, pool)); 984} 985 986svn_error_t * 987svn_fs_node_history(svn_fs_history_t **history_p, svn_fs_root_t *root, 988 const char *path, apr_pool_t *pool) 989{ 990 return svn_error_trace(root->vtable->node_history(history_p, root, path, 991 pool)); 992} 993 994svn_error_t * 995svn_fs_is_dir(svn_boolean_t *is_dir, svn_fs_root_t *root, const char *path, 996 apr_pool_t *pool) 997{ 998 svn_node_kind_t kind; 999 1000 SVN_ERR(root->vtable->check_path(&kind, root, path, pool)); 1001 *is_dir = (kind == svn_node_dir); 1002 return SVN_NO_ERROR; 1003} 1004 1005svn_error_t * 1006svn_fs_is_file(svn_boolean_t *is_file, svn_fs_root_t *root, const char *path, 1007 apr_pool_t *pool) 1008{ 1009 svn_node_kind_t kind; 1010 1011 SVN_ERR(root->vtable->check_path(&kind, root, path, pool)); 1012 *is_file = (kind == svn_node_file); 1013 return SVN_NO_ERROR; 1014} 1015 1016svn_error_t * 1017svn_fs_node_id(const svn_fs_id_t **id_p, svn_fs_root_t *root, 1018 const char *path, apr_pool_t *pool) 1019{ 1020 return svn_error_trace(root->vtable->node_id(id_p, root, path, pool)); 1021} 1022 1023svn_error_t * 1024svn_fs_node_created_rev(svn_revnum_t *revision, svn_fs_root_t *root, 1025 const char *path, apr_pool_t *pool) 1026{ 1027 return svn_error_trace(root->vtable->node_created_rev(revision, root, path, 1028 pool)); 1029} 1030 1031svn_error_t * 1032svn_fs_node_origin_rev(svn_revnum_t *revision, svn_fs_root_t *root, 1033 const char *path, apr_pool_t *pool) 1034{ 1035 return svn_error_trace(root->vtable->node_origin_rev(revision, root, path, 1036 pool)); 1037} 1038 1039svn_error_t * 1040svn_fs_node_created_path(const char **created_path, svn_fs_root_t *root, 1041 const char *path, apr_pool_t *pool) 1042{ 1043 return svn_error_trace(root->vtable->node_created_path(created_path, root, 1044 path, pool)); 1045} 1046 1047svn_error_t * 1048svn_fs_node_prop(svn_string_t **value_p, svn_fs_root_t *root, 1049 const char *path, const char *propname, apr_pool_t *pool) 1050{ 1051 return svn_error_trace(root->vtable->node_prop(value_p, root, path, 1052 propname, pool)); 1053} 1054 1055svn_error_t * 1056svn_fs_node_proplist(apr_hash_t **table_p, svn_fs_root_t *root, 1057 const char *path, apr_pool_t *pool) 1058{ 1059 return svn_error_trace(root->vtable->node_proplist(table_p, root, path, 1060 pool)); 1061} 1062 1063svn_error_t * 1064svn_fs_change_node_prop(svn_fs_root_t *root, const char *path, 1065 const char *name, const svn_string_t *value, 1066 apr_pool_t *pool) 1067{ 1068 return svn_error_trace(root->vtable->change_node_prop(root, path, name, 1069 value, pool)); 1070} 1071 1072svn_error_t * 1073svn_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) 1076{ 1077 return svn_error_trace(root1->vtable->props_changed(changed_p, 1078 root1, path1, 1079 root2, path2, 1080 pool)); 1081} 1082 1083svn_error_t * 1084svn_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) 1086{ 1087 return svn_error_trace(root->vtable->copied_from(rev_p, path_p, root, path, 1088 pool)); 1089} 1090 1091svn_error_t * 1092svn_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) 1094{ 1095 return svn_error_trace(root->vtable->closest_copy(root_p, path_p, 1096 root, path, pool)); 1097} 1098 1099svn_error_t * 1100svn_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) 1108{ 1109 return svn_error_trace(root->vtable->get_mergeinfo( 1110 catalog, root, paths, inherit, include_descendants, 1111 adjust_inherited_mergeinfo, result_pool, scratch_pool)); 1112} 1113 1114svn_error_t * 1115svn_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, 1120 apr_pool_t *pool) 1121{ 1122 return svn_error_trace(root->vtable->get_mergeinfo(catalog, root, paths, 1123 inherit, 1124 include_descendants, 1125 TRUE, pool, pool)); 1126} 1127 1128svn_error_t * 1129svn_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) 1133{ 1134 return svn_error_trace(target_root->vtable->merge(conflict_p, 1135 source_root, source_path, 1136 target_root, target_path, 1137 ancestor_root, 1138 ancestor_path, pool)); 1139} 1140 1141svn_error_t * 1142svn_fs_dir_entries(apr_hash_t **entries_p, svn_fs_root_t *root, 1143 const char *path, apr_pool_t *pool) 1144{ 1145 return svn_error_trace(root->vtable->dir_entries(entries_p, root, path, 1146 pool)); 1147} 1148 1149svn_error_t * 1150svn_fs_make_dir(svn_fs_root_t *root, const char *path, apr_pool_t *pool) 1151{ 1152 SVN_ERR(svn_fs__path_valid(path, pool)); 1153 return svn_error_trace(root->vtable->make_dir(root, path, pool)); 1154} 1155 1156svn_error_t * 1157svn_fs_delete(svn_fs_root_t *root, const char *path, apr_pool_t *pool) 1158{ 1159 return svn_error_trace(root->vtable->delete_node(root, path, pool)); 1160} 1161 1162svn_error_t * 1163svn_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) 1165{ 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)); 1169} 1170 1171svn_error_t * 1172svn_fs_revision_link(svn_fs_root_t *from_root, svn_fs_root_t *to_root, 1173 const char *path, apr_pool_t *pool) 1174{ 1175 return svn_error_trace(to_root->vtable->revision_link(from_root, to_root, 1176 path, pool)); 1177} 1178 1179svn_error_t * 1180svn_fs_file_length(svn_filesize_t *length_p, svn_fs_root_t *root, 1181 const char *path, apr_pool_t *pool) 1182{ 1183 return svn_error_trace(root->vtable->file_length(length_p, root, path, 1184 pool)); 1185} 1186 1187svn_error_t * 1188svn_fs_file_checksum(svn_checksum_t **checksum, 1189 svn_checksum_kind_t kind, 1190 svn_fs_root_t *root, 1191 const char *path, 1192 svn_boolean_t force, 1193 apr_pool_t *pool) 1194{ 1195 SVN_ERR(root->vtable->file_checksum(checksum, kind, root, path, pool)); 1196 1197 if (force && (*checksum == NULL || (*checksum)->kind != kind)) 1198 { 1199 svn_stream_t *contents, *checksum_contents; 1200 1201 SVN_ERR(svn_fs_file_contents(&contents, root, path, pool)); 1202 checksum_contents = svn_stream_checksummed2(contents, checksum, NULL, 1203 kind, TRUE, pool); 1204 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)); 1208 } 1209 1210 return SVN_NO_ERROR; 1211} 1212 1213svn_error_t * 1214svn_fs_file_md5_checksum(unsigned char digest[], 1215 svn_fs_root_t *root, 1216 const char *path, 1217 apr_pool_t *pool) 1218{ 1219 svn_checksum_t *md5sum; 1220 1221 SVN_ERR(svn_fs_file_checksum(&md5sum, svn_checksum_md5, root, path, TRUE, 1222 pool)); 1223 memcpy(digest, md5sum->digest, APR_MD5_DIGESTSIZE); 1224 1225 return SVN_NO_ERROR; 1226} 1227 1228svn_error_t * 1229svn_fs_file_contents(svn_stream_t **contents, svn_fs_root_t *root, 1230 const char *path, apr_pool_t *pool) 1231{ 1232 return svn_error_trace(root->vtable->file_contents(contents, root, path, 1233 pool)); 1234} 1235 1236svn_error_t * 1237svn_fs_try_process_file_contents(svn_boolean_t *success, 1238 svn_fs_root_t *root, 1239 const char *path, 1240 svn_fs_process_contents_func_t processor, 1241 void* baton, 1242 apr_pool_t *pool) 1243{ 1244 /* if the FS doesn't implement this function, report a "failed" attempt */ 1245 if (root->vtable->try_process_file_contents == NULL) 1246 { 1247 *success = FALSE; 1248 return SVN_NO_ERROR; 1249 } 1250 1251 return svn_error_trace(root->vtable->try_process_file_contents( 1252 success, 1253 root, path, 1254 processor, baton, pool)); 1255} 1256 1257svn_error_t * 1258svn_fs_make_file(svn_fs_root_t *root, const char *path, apr_pool_t *pool) 1259{ 1260 SVN_ERR(svn_fs__path_valid(path, pool)); 1261 return svn_error_trace(root->vtable->make_file(root, path, pool)); 1262} 1263 1264svn_error_t * 1265svn_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) 1269{ 1270 svn_checksum_t *base, *result; 1271 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, 1275 pool)); 1276 SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum, 1277 pool)); 1278 1279 return svn_error_trace(root->vtable->apply_textdelta(contents_p, 1280 contents_baton_p, 1281 root, 1282 path, 1283 base, 1284 result, 1285 pool)); 1286} 1287 1288svn_error_t * 1289svn_fs_apply_text(svn_stream_t **contents_p, svn_fs_root_t *root, 1290 const char *path, const char *result_checksum, 1291 apr_pool_t *pool) 1292{ 1293 svn_checksum_t *result; 1294 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, 1298 pool)); 1299 1300 return svn_error_trace(root->vtable->apply_text(contents_p, root, path, 1301 result, pool)); 1302} 1303 1304svn_error_t * 1305svn_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) 1308{ 1309 return svn_error_trace(root1->vtable->contents_changed(changed_p, 1310 root1, path1, 1311 root2, path2, 1312 pool)); 1313} 1314 1315svn_error_t * 1316svn_fs_youngest_rev(svn_revnum_t *youngest_p, svn_fs_t *fs, apr_pool_t *pool) 1317{ 1318 return svn_error_trace(fs->vtable->youngest_rev(youngest_p, fs, pool)); 1319} 1320 1321svn_error_t * 1322svn_fs_deltify_revision(svn_fs_t *fs, svn_revnum_t revision, apr_pool_t *pool) 1323{ 1324 return svn_error_trace(fs->vtable->deltify(fs, revision, pool)); 1325} 1326 1327svn_error_t * 1328svn_fs_revision_prop(svn_string_t **value_p, svn_fs_t *fs, svn_revnum_t rev, 1329 const char *propname, apr_pool_t *pool) 1330{ 1331 return svn_error_trace(fs->vtable->revision_prop(value_p, fs, rev, 1332 propname, pool)); 1333} 1334 1335svn_error_t * 1336svn_fs_revision_proplist(apr_hash_t **table_p, svn_fs_t *fs, svn_revnum_t rev, 1337 apr_pool_t *pool) 1338{ 1339 return svn_error_trace(fs->vtable->revision_proplist(table_p, fs, rev, 1340 pool)); 1341} 1342 1343svn_error_t * 1344svn_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) 1347{ 1348 return svn_error_trace(fs->vtable->change_rev_prop(fs, rev, name, 1349 old_value_p, 1350 value, pool)); 1351} 1352 1353svn_error_t * 1354svn_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) 1356{ 1357 return svn_error_trace( 1358 svn_fs_change_rev_prop2(fs, rev, name, NULL, value, pool)); 1359} 1360 1361svn_error_t * 1362svn_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) 1367{ 1368 return svn_error_trace(target_root->vtable->get_file_delta_stream( 1369 stream_p, 1370 source_root, source_path, 1371 target_root, target_path, pool)); 1372} 1373 1374svn_error_t * 1375svn_fs_get_uuid(svn_fs_t *fs, const char **uuid, apr_pool_t *pool) 1376{ 1377 /* If you change this, consider changing svn_fs__identifier(). */ 1378 *uuid = apr_pstrdup(pool, fs->uuid); 1379 return SVN_NO_ERROR; 1380} 1381 1382svn_error_t * 1383svn_fs_set_uuid(svn_fs_t *fs, const char *uuid, apr_pool_t *pool) 1384{ 1385 if (! uuid) 1386 { 1387 uuid = svn_uuid_generate(pool); 1388 } 1389 else 1390 { 1391 apr_uuid_t parsed_uuid; 1392 apr_status_t apr_err = apr_uuid_parse(&parsed_uuid, uuid); 1393 if (apr_err) 1394 return svn_error_createf(SVN_ERR_BAD_UUID, NULL, 1395 _("Malformed UUID '%s'"), uuid); 1396 } 1397 return svn_error_trace(fs->vtable->set_uuid(fs, uuid, pool)); 1398} 1399 1400svn_error_t * 1401svn_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, 1405 apr_pool_t *pool) 1406{ 1407 /* Enforce that the comment be xml-escapable. */ 1408 if (comment) 1409 { 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")); 1414 } 1415 1416 /* Enforce that the token be an XML-safe URI. */ 1417 if (token) 1418 { 1419 const char *c; 1420 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; " 1424 "expected '%s'"), 1425 token, "opaquelocktoken"); 1426 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 " 1431 "at byte %u"), 1432 token, (unsigned)(c - token)); 1433 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"), 1438 token); 1439 } 1440 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")); 1445 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)); 1449} 1450 1451svn_error_t * 1452svn_fs_generate_lock_token(const char **token, svn_fs_t *fs, apr_pool_t *pool) 1453{ 1454 return svn_error_trace(fs->vtable->generate_lock_token(token, fs, pool)); 1455} 1456 1457svn_error_t * 1458svn_fs_unlock(svn_fs_t *fs, const char *path, const char *token, 1459 svn_boolean_t break_lock, apr_pool_t *pool) 1460{ 1461 return svn_error_trace(fs->vtable->unlock(fs, path, token, break_lock, 1462 pool)); 1463} 1464 1465svn_error_t * 1466svn_fs_get_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path, 1467 apr_pool_t *pool) 1468{ 1469 return svn_error_trace(fs->vtable->get_lock(lock, fs, path, pool)); 1470} 1471 1472svn_error_t * 1473svn_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) 1476{ 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, 1482 get_locks_func, 1483 get_locks_baton, pool)); 1484} 1485 1486svn_error_t * 1487svn_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) 1490{ 1491 return svn_error_trace(svn_fs_get_locks2(fs, path, svn_depth_infinity, 1492 get_locks_func, get_locks_baton, 1493 pool)); 1494} 1495 1496 1497/* --- History functions --- */ 1498 1499svn_error_t * 1500svn_fs_history_prev(svn_fs_history_t **prev_history_p, 1501 svn_fs_history_t *history, svn_boolean_t cross_copies, 1502 apr_pool_t *pool) 1503{ 1504 return svn_error_trace(history->vtable->prev(prev_history_p, history, 1505 cross_copies, pool)); 1506} 1507 1508svn_error_t * 1509svn_fs_history_location(const char **path, svn_revnum_t *revision, 1510 svn_fs_history_t *history, apr_pool_t *pool) 1511{ 1512 return svn_error_trace(history->vtable->location(path, revision, history, 1513 pool)); 1514} 1515 1516 1517/* --- Node-ID functions --- */ 1518 1519svn_fs_id_t * 1520svn_fs_parse_id(const char *data, apr_size_t len, apr_pool_t *pool) 1521{ 1522 fs_library_vtable_t *vtable; 1523 svn_error_t *err; 1524 1525 err = get_library_vtable(&vtable, SVN_FS_TYPE_BDB, pool); 1526 if (err) 1527 { 1528 svn_error_clear(err); 1529 return NULL; 1530 } 1531 return vtable->parse_id(data, len, pool); 1532} 1533 1534svn_string_t * 1535svn_fs_unparse_id(const svn_fs_id_t *id, apr_pool_t *pool) 1536{ 1537 return id->vtable->unparse(id, pool); 1538} 1539 1540svn_boolean_t 1541svn_fs_check_related(const svn_fs_id_t *a, const svn_fs_id_t *b) 1542{ 1543 return (a->vtable->compare(a, b) != -1); 1544} 1545 1546int 1547svn_fs_compare_ids(const svn_fs_id_t *a, const svn_fs_id_t *b) 1548{ 1549 return a->vtable->compare(a, b); 1550} 1551 1552svn_error_t * 1553svn_fs_print_modules(svn_stringbuf_t *output, 1554 apr_pool_t *pool) 1555{ 1556 const struct fs_type_defn *defn = fs_modules; 1557 fs_library_vtable_t *vtable; 1558 apr_pool_t *iterpool = svn_pool_create(pool); 1559 1560 while (defn) 1561 { 1562 char *line; 1563 svn_error_t *err; 1564 1565 svn_pool_clear(iterpool); 1566 1567 err = get_library_vtable_direct(&vtable, defn, iterpool); 1568 if (err) 1569 { 1570 if (err->apr_err == SVN_ERR_FS_UNKNOWN_FS_TYPE) 1571 { 1572 svn_error_clear(err); 1573 defn = defn->next; 1574 continue; 1575 } 1576 else 1577 return err; 1578 } 1579 1580 line = apr_psprintf(iterpool, "* fs_%s : %s\n", 1581 defn->fsap_name, vtable->get_description()); 1582 svn_stringbuf_appendcstr(output, line); 1583 defn = defn->next; 1584 } 1585 1586 svn_pool_destroy(iterpool); 1587 1588 return SVN_NO_ERROR; 1589} 1590 1591svn_fs_path_change2_t * 1592svn_fs_path_change2_create(const svn_fs_id_t *node_rev_id, 1593 svn_fs_path_change_kind_t change_kind, 1594 apr_pool_t *pool) 1595{ 1596 return svn_fs__path_change_create_internal(node_rev_id, change_kind, pool); 1597} 1598 1599/* Return the library version number. */ 1600const svn_version_t * 1601svn_fs_version(void) 1602{ 1603 SVN_VERSION_BODY; 1604} 1605