fs-loader.c revision 299742
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_atomic.h> 28#include <apr_hash.h> 29#include <apr_md5.h> 30#include <apr_thread_mutex.h> 31#include <apr_uuid.h> 32#include <apr_strings.h> 33 34#include "svn_private_config.h" 35#include "svn_hash.h" 36#include "svn_ctype.h" 37#include "svn_types.h" 38#include "svn_dso.h" 39#include "svn_version.h" 40#include "svn_fs.h" 41#include "svn_path.h" 42#include "svn_xml.h" 43#include "svn_pools.h" 44#include "svn_string.h" 45#include "svn_sorts.h" 46 47#include "private/svn_atomic.h" 48#include "private/svn_fs_private.h" 49#include "private/svn_fs_util.h" 50#include "private/svn_utf_private.h" 51#include "private/svn_mutex.h" 52#include "private/svn_subr_private.h" 53 54#include "fs-loader.h" 55 56/* This is defined by configure on platforms which use configure, but 57 we need to define a fallback for Windows. */ 58#ifndef DEFAULT_FS_TYPE 59#define DEFAULT_FS_TYPE "fsfs" 60#endif 61 62#define FS_TYPE_FILENAME "fs-type" 63 64/* A pool common to all FS objects. See the documentation on the 65 open/create functions in fs-loader.h and for svn_fs_initialize(). */ 66static apr_pool_t *common_pool = NULL; 67static svn_mutex__t *common_pool_lock = NULL; 68static svn_atomic_t common_pool_initialized = FALSE; 69 70 71/* --- Utility functions for the loader --- */ 72 73struct fs_type_defn { 74 const char *fs_type; 75 const char *fsap_name; 76 fs_init_func_t initfunc; 77 fs_library_vtable_t *vtable; 78 struct fs_type_defn *next; 79}; 80 81static struct fs_type_defn base_defn = 82 { 83 SVN_FS_TYPE_BDB, "base", 84#ifdef SVN_LIBSVN_FS_LINKS_FS_BASE 85 svn_fs_base__init, 86#else 87 NULL, 88#endif 89 NULL, 90 NULL /* End of static list: this needs to be reset to NULL if the 91 common_pool used when setting it has been cleared. */ 92 }; 93 94static struct fs_type_defn fsx_defn = 95 { 96 SVN_FS_TYPE_FSX, "x", 97#ifdef SVN_LIBSVN_FS_LINKS_FS_X 98 svn_fs_x__init, 99#else 100 NULL, 101#endif 102 NULL, 103 &base_defn 104 }; 105 106static struct fs_type_defn fsfs_defn = 107 { 108 SVN_FS_TYPE_FSFS, "fs", 109#ifdef SVN_LIBSVN_FS_LINKS_FS_FS 110 svn_fs_fs__init, 111#else 112 NULL, 113#endif 114 NULL, 115 &fsx_defn 116 }; 117 118static struct fs_type_defn *fs_modules = &fsfs_defn; 119 120 121static svn_error_t * 122load_module(fs_init_func_t *initfunc, const char *name, apr_pool_t *pool) 123{ 124 *initfunc = NULL; 125 126#if defined(SVN_USE_DSO) && APR_HAS_DSO 127 { 128 apr_dso_handle_t *dso; 129 apr_dso_handle_sym_t symbol; 130 const char *libname; 131 const char *funcname; 132 apr_status_t status; 133 const char *p; 134 135 /* Demand a simple alphanumeric name so that the generated DSO 136 name is sensible. */ 137 for (p = name; *p; ++p) 138 if (!svn_ctype_isalnum(*p)) 139 return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL, 140 _("Invalid name for FS type '%s'"), 141 name); 142 143 libname = apr_psprintf(pool, "libsvn_fs_%s-%d.so.%d", 144 name, SVN_VER_MAJOR, SVN_SOVERSION); 145 funcname = apr_psprintf(pool, "svn_fs_%s__init", name); 146 147 /* Find/load the specified library. If we get an error, assume 148 the library doesn't exist. The library will be unloaded when 149 pool is destroyed. */ 150 SVN_ERR(svn_dso_load(&dso, libname)); 151 if (! dso) 152 return SVN_NO_ERROR; 153 154 /* find the initialization routine */ 155 status = apr_dso_sym(&symbol, dso, funcname); 156 if (status) 157 return svn_error_wrap_apr(status, _("'%s' does not define '%s()'"), 158 libname, funcname); 159 160 *initfunc = (fs_init_func_t) symbol; 161 } 162#endif /* APR_HAS_DSO */ 163 164 return SVN_NO_ERROR; 165} 166 167/* Fetch a library vtable by a pointer into the library definitions array. */ 168static svn_error_t * 169get_library_vtable_direct(fs_library_vtable_t **vtable, 170 struct fs_type_defn *fst, 171 apr_pool_t *pool) 172{ 173 fs_init_func_t initfunc = NULL; 174 const svn_version_t *my_version = svn_fs_version(); 175 const svn_version_t *fs_version; 176 177 /* most times, we get lucky */ 178 *vtable = apr_atomic_casptr((volatile void **)&fst->vtable, NULL, NULL); 179 if (*vtable) 180 return SVN_NO_ERROR; 181 182 /* o.k. the first access needs to actually load the module, find the 183 vtable and check for version compatibility. */ 184 initfunc = fst->initfunc; 185 if (! initfunc) 186 SVN_ERR(load_module(&initfunc, fst->fsap_name, pool)); 187 188 if (! initfunc) 189 return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL, 190 _("Failed to load module for FS type '%s'"), 191 fst->fs_type); 192 193 { 194 /* Per our API compatibility rules, we cannot ensure that 195 svn_fs_initialize is called by the application. If not, we 196 cannot create the common pool and lock in a thread-safe fashion, 197 nor can we clean up the common pool if libsvn_fs is dynamically 198 unloaded. This function makes a best effort by creating the 199 common pool as a child of the global pool; the window of failure 200 due to thread collision is small. */ 201 SVN_ERR(svn_fs_initialize(NULL)); 202 203 /* Invoke the FS module's initfunc function with the common 204 pool protected by a lock. */ 205 SVN_MUTEX__WITH_LOCK(common_pool_lock, 206 initfunc(my_version, vtable, common_pool)); 207 } 208 fs_version = (*vtable)->get_version(); 209 if (!svn_ver_equal(my_version, fs_version)) 210 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL, 211 _("Mismatched FS module version for '%s':" 212 " found %d.%d.%d%s," 213 " expected %d.%d.%d%s"), 214 fst->fs_type, 215 my_version->major, my_version->minor, 216 my_version->patch, my_version->tag, 217 fs_version->major, fs_version->minor, 218 fs_version->patch, fs_version->tag); 219 220 /* the vtable will not change. Remember it */ 221 apr_atomic_casptr((volatile void **)&fst->vtable, *vtable, NULL); 222 223 return SVN_NO_ERROR; 224} 225 226#if defined(SVN_USE_DSO) && APR_HAS_DSO 227/* Return *FST for the third party FS_TYPE */ 228static svn_error_t * 229get_or_allocate_third(struct fs_type_defn **fst, 230 const char *fs_type) 231{ 232 while (*fst) 233 { 234 if (strcmp(fs_type, (*fst)->fs_type) == 0) 235 return SVN_NO_ERROR; 236 fst = &(*fst)->next; 237 } 238 239 *fst = apr_palloc(common_pool, sizeof(struct fs_type_defn)); 240 (*fst)->fs_type = apr_pstrdup(common_pool, fs_type); 241 (*fst)->fsap_name = (*fst)->fs_type; 242 (*fst)->initfunc = NULL; 243 (*fst)->vtable = NULL; 244 (*fst)->next = NULL; 245 246 return SVN_NO_ERROR; 247} 248#endif 249 250/* Fetch a library *VTABLE by FS_TYPE. 251 Use POOL for temporary allocations. */ 252static svn_error_t * 253get_library_vtable(fs_library_vtable_t **vtable, const char *fs_type, 254 apr_pool_t *pool) 255{ 256 struct fs_type_defn **fst; 257 svn_boolean_t known = FALSE; 258 259 /* There are three FS module definitions known at compile time. We 260 want to check these without any locking overhead even when 261 dynamic third party modules are enabled. The third party modules 262 cannot be checked until the lock is held. */ 263 for (fst = &fs_modules; *fst; fst = &(*fst)->next) 264 { 265 if (strcmp(fs_type, (*fst)->fs_type) == 0) 266 { 267 known = TRUE; 268 break; 269 } 270 else if (!(*fst)->next) 271 { 272 break; 273 } 274 } 275 276#if defined(SVN_USE_DSO) && APR_HAS_DSO 277 /* Third party FS modules that are unknown at compile time. 278 279 A third party FS is identified by the file fs-type containing a 280 third party name, say "foo". The loader will load the DSO with 281 the name "libsvn_fs_foo" and use the entry point with the name 282 "svn_fs_foo__init". 283 284 Note: the BDB and FSFS modules don't follow this naming scheme 285 and this allows them to be used to test the third party loader. 286 Change the content of fs-type to "base" in a BDB filesystem or to 287 "fs" in an FSFS filesystem and they will be loaded as third party 288 modules. */ 289 if (!known) 290 { 291 fst = &(*fst)->next; 292 /* Best-effort init, see get_library_vtable_direct. */ 293 SVN_ERR(svn_fs_initialize(NULL)); 294 SVN_MUTEX__WITH_LOCK(common_pool_lock, 295 get_or_allocate_third(fst, fs_type)); 296 known = TRUE; 297 } 298#endif 299 if (!known) 300 return svn_error_createf(SVN_ERR_FS_UNKNOWN_FS_TYPE, NULL, 301 _("Unknown FS type '%s'"), fs_type); 302 return get_library_vtable_direct(vtable, *fst, pool); 303} 304 305svn_error_t * 306svn_fs_type(const char **fs_type, const char *path, apr_pool_t *pool) 307{ 308 const char *filename; 309 char buf[128]; 310 svn_error_t *err; 311 apr_file_t *file; 312 apr_size_t len; 313 314 /* Read the fsap-name file to get the FSAP name, or assume the (old) 315 default. For old repositories I suppose we could check some 316 other file, DB_CONFIG or strings say, but for now just check the 317 directory exists. */ 318 filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool); 319 err = svn_io_file_open(&file, filename, APR_READ|APR_BUFFERED, 0, pool); 320 if (err && APR_STATUS_IS_ENOENT(err->apr_err)) 321 { 322 svn_node_kind_t kind; 323 svn_error_t *err2 = svn_io_check_path(path, &kind, pool); 324 if (err2) 325 { 326 svn_error_clear(err2); 327 return err; 328 } 329 if (kind == svn_node_dir) 330 { 331 svn_error_clear(err); 332 *fs_type = SVN_FS_TYPE_BDB; 333 return SVN_NO_ERROR; 334 } 335 return err; 336 } 337 else if (err) 338 return err; 339 340 len = sizeof(buf); 341 SVN_ERR(svn_io_read_length_line(file, buf, &len, pool)); 342 SVN_ERR(svn_io_file_close(file, pool)); 343 *fs_type = apr_pstrdup(pool, buf); 344 345 return SVN_NO_ERROR; 346} 347 348/* Fetch the library vtable for an existing FS. */ 349static svn_error_t * 350fs_library_vtable(fs_library_vtable_t **vtable, const char *path, 351 apr_pool_t *pool) 352{ 353 const char *fs_type; 354 355 SVN_ERR(svn_fs_type(&fs_type, path, pool)); 356 357 /* Fetch the library vtable by name, now that we've chosen one. */ 358 SVN_ERR(get_library_vtable(vtable, fs_type, pool)); 359 360 return SVN_NO_ERROR; 361} 362 363static svn_error_t * 364write_fs_type(const char *path, const char *fs_type, apr_pool_t *pool) 365{ 366 const char *filename; 367 apr_file_t *file; 368 369 filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool); 370 SVN_ERR(svn_io_file_open(&file, filename, 371 APR_WRITE|APR_CREATE|APR_TRUNCATE|APR_BUFFERED, 372 APR_OS_DEFAULT, pool)); 373 SVN_ERR(svn_io_file_write_full(file, fs_type, strlen(fs_type), NULL, 374 pool)); 375 SVN_ERR(svn_io_file_write_full(file, "\n", 1, NULL, pool)); 376 return svn_error_trace(svn_io_file_close(file, pool)); 377} 378 379 380/* --- Functions for operating on filesystems by pathname --- */ 381 382static apr_status_t uninit(void *data) 383{ 384 common_pool = NULL; 385 common_pool_lock = NULL; 386 common_pool_initialized = 0; 387 388 return APR_SUCCESS; 389} 390 391static svn_error_t * 392synchronized_initialize(void *baton, apr_pool_t *pool) 393{ 394 common_pool = svn_pool_create(pool); 395 base_defn.next = NULL; 396 SVN_ERR(svn_mutex__init(&common_pool_lock, TRUE, common_pool)); 397 398 /* ### This won't work if POOL is NULL and libsvn_fs is loaded as a DSO 399 ### (via libsvn_ra_local say) since the global common_pool will live 400 ### longer than the DSO, which gets unloaded when the pool used to 401 ### load it is cleared, and so when the handler runs it will refer to 402 ### a function that no longer exists. libsvn_ra_local attempts to 403 ### work around this by explicitly calling svn_fs_initialize. */ 404 apr_pool_cleanup_register(common_pool, NULL, uninit, apr_pool_cleanup_null); 405 return SVN_NO_ERROR; 406} 407 408svn_error_t * 409svn_fs_initialize(apr_pool_t *pool) 410{ 411#if defined(SVN_USE_DSO) && APR_HAS_DSO 412 /* Ensure that DSO subsystem is initialized early as possible if 413 we're going to use it. */ 414 SVN_ERR(svn_dso_initialize2()); 415#endif 416 /* Protect against multiple calls. */ 417 return svn_error_trace(svn_atomic__init_once(&common_pool_initialized, 418 synchronized_initialize, 419 NULL, pool)); 420} 421 422/* A default warning handling function. */ 423static void 424default_warning_func(void *baton, svn_error_t *err) 425{ 426 /* The one unforgiveable sin is to fail silently. Dumping to stderr 427 or /dev/tty is not acceptable default behavior for server 428 processes, since those may both be equivalent to /dev/null. 429 430 That said, be a good citizen and print something anyway, in case it goes 431 somewhere, and our caller hasn't overridden the abort() call. 432 */ 433 if (svn_error_get_malfunction_handler() 434 == svn_error_abort_on_malfunction) 435 /* ### TODO: extend the malfunction API such that non-abort()ing consumers 436 ### also get the information on ERR. */ 437 svn_handle_error2(err, stderr, FALSE /* fatal */, "svn: fs-loader: "); 438 SVN_ERR_MALFUNCTION_NO_RETURN(); 439} 440 441svn_error_t * 442svn_fs__path_valid(const char *path, apr_pool_t *pool) 443{ 444 char *c; 445 446 /* UTF-8 encoded string without NULs. */ 447 if (! svn_utf__cstring_is_valid(path)) 448 { 449 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL, 450 _("Path '%s' is not in UTF-8"), path); 451 } 452 453 /* No "." or ".." elements. */ 454 if (svn_path_is_backpath_present(path) 455 || svn_path_is_dotpath_present(path)) 456 { 457 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL, 458 _("Path '%s' contains '.' or '..' element"), 459 path); 460 } 461 462 /* Raise an error if PATH contains a newline because svn:mergeinfo and 463 friends can't handle them. Issue #4340 describes a similar problem 464 in the FSFS code itself. 465 */ 466 c = strchr(path, '\n'); 467 if (c) 468 { 469 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL, 470 _("Invalid control character '0x%02x' in path '%s'"), 471 (unsigned char)*c, svn_path_illegal_path_escape(path, pool)); 472 } 473 474 /* That's good enough. */ 475 return SVN_NO_ERROR; 476} 477 478/* Allocate svn_fs_t structure. */ 479static svn_fs_t * 480fs_new(apr_hash_t *fs_config, apr_pool_t *pool) 481{ 482 svn_fs_t *fs = apr_palloc(pool, sizeof(*fs)); 483 fs->pool = pool; 484 fs->path = NULL; 485 fs->warning = default_warning_func; 486 fs->warning_baton = NULL; 487 fs->config = fs_config; 488 fs->access_ctx = NULL; 489 fs->vtable = NULL; 490 fs->fsap_data = NULL; 491 fs->uuid = NULL; 492 return fs; 493} 494 495svn_fs_t * 496svn_fs_new(apr_hash_t *fs_config, apr_pool_t *pool) 497{ 498 return fs_new(fs_config, pool); 499} 500 501void 502svn_fs_set_warning_func(svn_fs_t *fs, svn_fs_warning_callback_t warning, 503 void *warning_baton) 504{ 505 fs->warning = warning; 506 fs->warning_baton = warning_baton; 507} 508 509svn_error_t * 510svn_fs_create(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config, 511 apr_pool_t *pool) 512{ 513 fs_library_vtable_t *vtable; 514 515 const char *fs_type = svn_hash__get_cstring(fs_config, 516 SVN_FS_CONFIG_FS_TYPE, 517 DEFAULT_FS_TYPE); 518 SVN_ERR(get_library_vtable(&vtable, fs_type, pool)); 519 520 /* Create the FS directory and write out the fsap-name file. */ 521 SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, pool)); 522 SVN_ERR(write_fs_type(path, fs_type, pool)); 523 524 /* Perform the actual creation. */ 525 *fs_p = fs_new(fs_config, pool); 526 527 SVN_ERR(vtable->create(*fs_p, path, common_pool_lock, pool, common_pool)); 528 SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open2)); 529 530 return SVN_NO_ERROR; 531} 532 533svn_error_t * 534svn_fs_open2(svn_fs_t **fs_p, const char *path, apr_hash_t *fs_config, 535 apr_pool_t *result_pool, 536 apr_pool_t *scratch_pool) 537{ 538 fs_library_vtable_t *vtable; 539 540 SVN_ERR(fs_library_vtable(&vtable, path, scratch_pool)); 541 *fs_p = fs_new(fs_config, result_pool); 542 SVN_ERR(vtable->open_fs(*fs_p, path, common_pool_lock, scratch_pool, 543 common_pool)); 544 SVN_ERR(vtable->set_svn_fs_open(*fs_p, svn_fs_open2)); 545 546 return SVN_NO_ERROR; 547} 548 549svn_error_t * 550svn_fs_open(svn_fs_t **fs_p, 551 const char *path, 552 apr_hash_t *fs_config, 553 apr_pool_t *pool) 554{ 555 return svn_fs_open2(fs_p, path, fs_config, pool, pool); 556} 557 558svn_error_t * 559svn_fs_upgrade2(const char *path, 560 svn_fs_upgrade_notify_t notify_func, 561 void *notify_baton, 562 svn_cancel_func_t cancel_func, 563 void *cancel_baton, 564 apr_pool_t *scratch_pool) 565{ 566 fs_library_vtable_t *vtable; 567 svn_fs_t *fs; 568 569 SVN_ERR(fs_library_vtable(&vtable, path, scratch_pool)); 570 fs = fs_new(NULL, scratch_pool); 571 572 SVN_ERR(vtable->upgrade_fs(fs, path, 573 notify_func, notify_baton, 574 cancel_func, cancel_baton, 575 common_pool_lock, 576 scratch_pool, common_pool)); 577 return SVN_NO_ERROR; 578} 579 580/* A warning handling function that does not abort on errors, 581 but just lets them be returned normally. */ 582static void 583verify_fs_warning_func(void *baton, svn_error_t *err) 584{ 585} 586 587svn_error_t * 588svn_fs_verify(const char *path, 589 apr_hash_t *fs_config, 590 svn_revnum_t start, 591 svn_revnum_t end, 592 svn_fs_progress_notify_func_t notify_func, 593 void *notify_baton, 594 svn_cancel_func_t cancel_func, 595 void *cancel_baton, 596 apr_pool_t *pool) 597{ 598 fs_library_vtable_t *vtable; 599 svn_fs_t *fs; 600 601 SVN_ERR(fs_library_vtable(&vtable, path, pool)); 602 fs = fs_new(fs_config, pool); 603 svn_fs_set_warning_func(fs, verify_fs_warning_func, NULL); 604 605 SVN_ERR(vtable->verify_fs(fs, path, start, end, 606 notify_func, notify_baton, 607 cancel_func, cancel_baton, 608 common_pool_lock, 609 pool, common_pool)); 610 return SVN_NO_ERROR; 611} 612 613const char * 614svn_fs_path(svn_fs_t *fs, apr_pool_t *pool) 615{ 616 return apr_pstrdup(pool, fs->path); 617} 618 619apr_hash_t * 620svn_fs_config(svn_fs_t *fs, apr_pool_t *pool) 621{ 622 if (fs->config) 623 return apr_hash_copy(pool, fs->config); 624 625 return NULL; 626} 627 628svn_error_t * 629svn_fs_delete_fs(const char *path, apr_pool_t *pool) 630{ 631 fs_library_vtable_t *vtable; 632 633 SVN_ERR(fs_library_vtable(&vtable, path, pool)); 634 return svn_error_trace(vtable->delete_fs(path, pool)); 635} 636 637svn_error_t * 638svn_fs_hotcopy3(const char *src_path, const char *dst_path, 639 svn_boolean_t clean, svn_boolean_t incremental, 640 svn_fs_hotcopy_notify_t notify_func, 641 void *notify_baton, 642 svn_cancel_func_t cancel_func, 643 void *cancel_baton, 644 apr_pool_t *scratch_pool) 645{ 646 fs_library_vtable_t *vtable; 647 const char *src_fs_type; 648 svn_fs_t *src_fs; 649 svn_fs_t *dst_fs; 650 const char *dst_fs_type; 651 svn_node_kind_t dst_kind; 652 653 if (strcmp(src_path, dst_path) == 0) 654 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 655 _("Hotcopy source and destination are equal")); 656 657 SVN_ERR(svn_fs_type(&src_fs_type, src_path, scratch_pool)); 658 SVN_ERR(get_library_vtable(&vtable, src_fs_type, scratch_pool)); 659 src_fs = fs_new(NULL, scratch_pool); 660 dst_fs = fs_new(NULL, scratch_pool); 661 662 SVN_ERR(svn_io_check_path(dst_path, &dst_kind, scratch_pool)); 663 if (dst_kind == svn_node_file) 664 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 665 _("'%s' already exists and is a file"), 666 svn_dirent_local_style(dst_path, 667 scratch_pool)); 668 if (dst_kind == svn_node_unknown) 669 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 670 _("'%s' already exists and has an unknown " 671 "node kind"), 672 svn_dirent_local_style(dst_path, 673 scratch_pool)); 674 if (dst_kind == svn_node_dir) 675 { 676 svn_node_kind_t type_file_kind; 677 678 SVN_ERR(svn_io_check_path(svn_dirent_join(dst_path, 679 FS_TYPE_FILENAME, 680 scratch_pool), 681 &type_file_kind, scratch_pool)); 682 if (type_file_kind != svn_node_none) 683 { 684 SVN_ERR(svn_fs_type(&dst_fs_type, dst_path, scratch_pool)); 685 if (strcmp(src_fs_type, dst_fs_type) != 0) 686 return svn_error_createf( 687 SVN_ERR_ILLEGAL_TARGET, NULL, 688 _("The filesystem type of the hotcopy source " 689 "('%s') does not match the filesystem " 690 "type of the hotcopy destination ('%s')"), 691 src_fs_type, dst_fs_type); 692 } 693 } 694 695 SVN_ERR(vtable->hotcopy(src_fs, dst_fs, src_path, dst_path, clean, 696 incremental, notify_func, notify_baton, 697 cancel_func, cancel_baton, common_pool_lock, 698 scratch_pool, common_pool)); 699 return svn_error_trace(write_fs_type(dst_path, src_fs_type, scratch_pool)); 700} 701 702svn_error_t * 703svn_fs_pack(const char *path, 704 svn_fs_pack_notify_t notify_func, 705 void *notify_baton, 706 svn_cancel_func_t cancel_func, 707 void *cancel_baton, 708 apr_pool_t *pool) 709{ 710 fs_library_vtable_t *vtable; 711 svn_fs_t *fs; 712 713 SVN_ERR(fs_library_vtable(&vtable, path, pool)); 714 fs = fs_new(NULL, pool); 715 716 SVN_ERR(vtable->pack_fs(fs, path, notify_func, notify_baton, 717 cancel_func, cancel_baton, common_pool_lock, 718 pool, common_pool)); 719 return SVN_NO_ERROR; 720} 721 722svn_error_t * 723svn_fs_recover(const char *path, 724 svn_cancel_func_t cancel_func, void *cancel_baton, 725 apr_pool_t *pool) 726{ 727 fs_library_vtable_t *vtable; 728 svn_fs_t *fs; 729 730 SVN_ERR(fs_library_vtable(&vtable, path, pool)); 731 fs = fs_new(NULL, pool); 732 733 SVN_ERR(vtable->open_fs_for_recovery(fs, path, common_pool_lock, 734 pool, common_pool)); 735 return svn_error_trace(vtable->recover(fs, cancel_func, cancel_baton, 736 pool)); 737} 738 739svn_error_t * 740svn_fs_verify_root(svn_fs_root_t *root, 741 apr_pool_t *scratch_pool) 742{ 743 svn_fs_t *fs = root->fs; 744 SVN_ERR(fs->vtable->verify_root(root, scratch_pool)); 745 746 return SVN_NO_ERROR; 747} 748 749svn_error_t * 750svn_fs_freeze(svn_fs_t *fs, 751 svn_fs_freeze_func_t freeze_func, 752 void *freeze_baton, 753 apr_pool_t *pool) 754{ 755 SVN_ERR(fs->vtable->freeze(fs, freeze_func, freeze_baton, pool)); 756 757 return SVN_NO_ERROR; 758} 759 760 761/* --- Berkeley-specific functions --- */ 762 763svn_error_t * 764svn_fs_create_berkeley(svn_fs_t *fs, const char *path) 765{ 766 fs_library_vtable_t *vtable; 767 768 SVN_ERR(get_library_vtable(&vtable, SVN_FS_TYPE_BDB, fs->pool)); 769 770 /* Create the FS directory and write out the fsap-name file. */ 771 SVN_ERR(svn_io_dir_make_sgid(path, APR_OS_DEFAULT, fs->pool)); 772 SVN_ERR(write_fs_type(path, SVN_FS_TYPE_BDB, fs->pool)); 773 774 /* Perform the actual creation. */ 775 SVN_ERR(vtable->create(fs, path, common_pool_lock, fs->pool, common_pool)); 776 SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open2)); 777 778 return SVN_NO_ERROR; 779} 780 781svn_error_t * 782svn_fs_open_berkeley(svn_fs_t *fs, const char *path) 783{ 784 fs_library_vtable_t *vtable; 785 786 SVN_ERR(fs_library_vtable(&vtable, path, fs->pool)); 787 SVN_ERR(vtable->open_fs(fs, path, common_pool_lock, fs->pool, common_pool)); 788 SVN_ERR(vtable->set_svn_fs_open(fs, svn_fs_open2)); 789 790 return SVN_NO_ERROR; 791} 792 793const char * 794svn_fs_berkeley_path(svn_fs_t *fs, apr_pool_t *pool) 795{ 796 return svn_fs_path(fs, pool); 797} 798 799svn_error_t * 800svn_fs_delete_berkeley(const char *path, apr_pool_t *pool) 801{ 802 return svn_error_trace(svn_fs_delete_fs(path, pool)); 803} 804 805svn_error_t * 806svn_fs_hotcopy_berkeley(const char *src_path, const char *dest_path, 807 svn_boolean_t clean_logs, apr_pool_t *pool) 808{ 809 return svn_error_trace(svn_fs_hotcopy3(src_path, dest_path, clean_logs, 810 FALSE, NULL, NULL, NULL, NULL, 811 pool)); 812} 813 814svn_error_t * 815svn_fs_berkeley_recover(const char *path, apr_pool_t *pool) 816{ 817 return svn_error_trace(svn_fs_recover(path, NULL, NULL, pool)); 818} 819 820svn_error_t * 821svn_fs_set_berkeley_errcall(svn_fs_t *fs, 822 void (*handler)(const char *errpfx, char *msg)) 823{ 824 return svn_error_trace(fs->vtable->bdb_set_errcall(fs, handler)); 825} 826 827svn_error_t * 828svn_fs_berkeley_logfiles(apr_array_header_t **logfiles, 829 const char *path, 830 svn_boolean_t only_unused, 831 apr_pool_t *pool) 832{ 833 fs_library_vtable_t *vtable; 834 835 SVN_ERR(fs_library_vtable(&vtable, path, pool)); 836 return svn_error_trace(vtable->bdb_logfiles(logfiles, path, only_unused, 837 pool)); 838} 839 840 841/* --- Transaction functions --- */ 842 843svn_error_t * 844svn_fs_begin_txn2(svn_fs_txn_t **txn_p, svn_fs_t *fs, svn_revnum_t rev, 845 apr_uint32_t flags, apr_pool_t *pool) 846{ 847 return svn_error_trace(fs->vtable->begin_txn(txn_p, fs, rev, flags, pool)); 848} 849 850 851svn_error_t * 852svn_fs_commit_txn(const char **conflict_p, svn_revnum_t *new_rev, 853 svn_fs_txn_t *txn, apr_pool_t *pool) 854{ 855 svn_error_t *err; 856 857 *new_rev = SVN_INVALID_REVNUM; 858 if (conflict_p) 859 *conflict_p = NULL; 860 861 err = txn->vtable->commit(conflict_p, new_rev, txn, pool); 862 863#ifdef SVN_DEBUG 864 /* Check postconditions. */ 865 if (conflict_p) 866 { 867 SVN_ERR_ASSERT_E(! (SVN_IS_VALID_REVNUM(*new_rev) && *conflict_p != NULL), 868 err); 869 SVN_ERR_ASSERT_E((*conflict_p != NULL) 870 == (err && err->apr_err == SVN_ERR_FS_CONFLICT), 871 err); 872 } 873#endif 874 875 SVN_ERR(err); 876 877 return SVN_NO_ERROR; 878} 879 880svn_error_t * 881svn_fs_abort_txn(svn_fs_txn_t *txn, apr_pool_t *pool) 882{ 883 return svn_error_trace(txn->vtable->abort(txn, pool)); 884} 885 886svn_error_t * 887svn_fs_purge_txn(svn_fs_t *fs, const char *txn_id, apr_pool_t *pool) 888{ 889 return svn_error_trace(fs->vtable->purge_txn(fs, txn_id, pool)); 890} 891 892svn_error_t * 893svn_fs_txn_name(const char **name_p, svn_fs_txn_t *txn, apr_pool_t *pool) 894{ 895 *name_p = apr_pstrdup(pool, txn->id); 896 return SVN_NO_ERROR; 897} 898 899svn_revnum_t 900svn_fs_txn_base_revision(svn_fs_txn_t *txn) 901{ 902 return txn->base_rev; 903} 904 905svn_error_t * 906svn_fs_open_txn(svn_fs_txn_t **txn, svn_fs_t *fs, const char *name, 907 apr_pool_t *pool) 908{ 909 return svn_error_trace(fs->vtable->open_txn(txn, fs, name, pool)); 910} 911 912svn_error_t * 913svn_fs_list_transactions(apr_array_header_t **names_p, svn_fs_t *fs, 914 apr_pool_t *pool) 915{ 916 return svn_error_trace(fs->vtable->list_transactions(names_p, fs, pool)); 917} 918 919static svn_boolean_t 920is_internal_txn_prop(const char *name) 921{ 922 return strcmp(name, SVN_FS__PROP_TXN_CHECK_LOCKS) == 0 || 923 strcmp(name, SVN_FS__PROP_TXN_CHECK_OOD) == 0 || 924 strcmp(name, SVN_FS__PROP_TXN_CLIENT_DATE) == 0; 925} 926 927svn_error_t * 928svn_fs_txn_prop(svn_string_t **value_p, svn_fs_txn_t *txn, 929 const char *propname, apr_pool_t *pool) 930{ 931 if (is_internal_txn_prop(propname)) 932 { 933 *value_p = NULL; 934 return SVN_NO_ERROR; 935 } 936 937 return svn_error_trace(txn->vtable->get_prop(value_p, txn, propname, pool)); 938} 939 940svn_error_t * 941svn_fs_txn_proplist(apr_hash_t **table_p, svn_fs_txn_t *txn, apr_pool_t *pool) 942{ 943 SVN_ERR(txn->vtable->get_proplist(table_p, txn, pool)); 944 945 /* Don't give away internal transaction properties. */ 946 svn_hash_sets(*table_p, SVN_FS__PROP_TXN_CHECK_LOCKS, NULL); 947 svn_hash_sets(*table_p, SVN_FS__PROP_TXN_CHECK_OOD, NULL); 948 svn_hash_sets(*table_p, SVN_FS__PROP_TXN_CLIENT_DATE, NULL); 949 950 return SVN_NO_ERROR; 951} 952 953svn_error_t * 954svn_fs_change_txn_prop(svn_fs_txn_t *txn, const char *name, 955 const svn_string_t *value, apr_pool_t *pool) 956{ 957 if (is_internal_txn_prop(name)) 958 return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, 959 _("Attempt to modify internal transaction " 960 "property '%s'"), name); 961 962 return svn_error_trace(txn->vtable->change_prop(txn, name, value, pool)); 963} 964 965svn_error_t * 966svn_fs_change_txn_props(svn_fs_txn_t *txn, const apr_array_header_t *props, 967 apr_pool_t *pool) 968{ 969 int i; 970 971 for (i = 0; i < props->nelts; ++i) 972 { 973 svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t); 974 975 if (is_internal_txn_prop(prop->name)) 976 return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, 977 _("Attempt to modify internal transaction " 978 "property '%s'"), prop->name); 979 } 980 981 return svn_error_trace(txn->vtable->change_props(txn, props, pool)); 982} 983 984 985/* --- Root functions --- */ 986 987svn_error_t * 988svn_fs_revision_root(svn_fs_root_t **root_p, svn_fs_t *fs, svn_revnum_t rev, 989 apr_pool_t *pool) 990{ 991 /* We create a subpool for each root object to allow us to implement 992 svn_fs_close_root. */ 993 apr_pool_t *subpool = svn_pool_create(pool); 994 return svn_error_trace(fs->vtable->revision_root(root_p, fs, rev, subpool)); 995} 996 997svn_error_t * 998svn_fs_txn_root(svn_fs_root_t **root_p, svn_fs_txn_t *txn, apr_pool_t *pool) 999{ 1000 /* We create a subpool for each root object to allow us to implement 1001 svn_fs_close_root. */ 1002 apr_pool_t *subpool = svn_pool_create(pool); 1003 return svn_error_trace(txn->vtable->root(root_p, txn, subpool)); 1004} 1005 1006void 1007svn_fs_close_root(svn_fs_root_t *root) 1008{ 1009 svn_pool_destroy(root->pool); 1010} 1011 1012svn_fs_t * 1013svn_fs_root_fs(svn_fs_root_t *root) 1014{ 1015 return root->fs; 1016} 1017 1018svn_boolean_t 1019svn_fs_is_txn_root(svn_fs_root_t *root) 1020{ 1021 return root->is_txn_root; 1022} 1023 1024svn_boolean_t 1025svn_fs_is_revision_root(svn_fs_root_t *root) 1026{ 1027 return !root->is_txn_root; 1028} 1029 1030const char * 1031svn_fs_txn_root_name(svn_fs_root_t *root, apr_pool_t *pool) 1032{ 1033 return root->is_txn_root ? apr_pstrdup(pool, root->txn) : NULL; 1034} 1035 1036svn_revnum_t 1037svn_fs_txn_root_base_revision(svn_fs_root_t *root) 1038{ 1039 return root->is_txn_root ? root->rev : SVN_INVALID_REVNUM; 1040} 1041 1042svn_revnum_t 1043svn_fs_revision_root_revision(svn_fs_root_t *root) 1044{ 1045 return root->is_txn_root ? SVN_INVALID_REVNUM : root->rev; 1046} 1047 1048svn_error_t * 1049svn_fs_paths_changed2(apr_hash_t **changed_paths_p, 1050 svn_fs_root_t *root, 1051 apr_pool_t *pool) 1052{ 1053 return root->vtable->paths_changed(changed_paths_p, root, pool); 1054} 1055 1056svn_error_t * 1057svn_fs_paths_changed(apr_hash_t **changed_paths_p, svn_fs_root_t *root, 1058 apr_pool_t *pool) 1059{ 1060 apr_hash_t *changed_paths_new_structs; 1061 apr_hash_index_t *hi; 1062 1063 SVN_ERR(svn_fs_paths_changed2(&changed_paths_new_structs, root, pool)); 1064 *changed_paths_p = apr_hash_make(pool); 1065 for (hi = apr_hash_first(pool, changed_paths_new_structs); 1066 hi; 1067 hi = apr_hash_next(hi)) 1068 { 1069 const void *vkey; 1070 apr_ssize_t klen; 1071 void *vval; 1072 svn_fs_path_change2_t *val; 1073 svn_fs_path_change_t *change; 1074 apr_hash_this(hi, &vkey, &klen, &vval); 1075 val = vval; 1076 change = apr_palloc(pool, sizeof(*change)); 1077 change->node_rev_id = val->node_rev_id; 1078 change->change_kind = val->change_kind; 1079 change->text_mod = val->text_mod; 1080 change->prop_mod = val->prop_mod; 1081 apr_hash_set(*changed_paths_p, vkey, klen, change); 1082 } 1083 return SVN_NO_ERROR; 1084} 1085 1086svn_error_t * 1087svn_fs_check_path(svn_node_kind_t *kind_p, svn_fs_root_t *root, 1088 const char *path, apr_pool_t *pool) 1089{ 1090 return svn_error_trace(root->vtable->check_path(kind_p, root, path, pool)); 1091} 1092 1093svn_error_t * 1094svn_fs_node_history2(svn_fs_history_t **history_p, svn_fs_root_t *root, 1095 const char *path, apr_pool_t *result_pool, 1096 apr_pool_t *scratch_pool) 1097{ 1098 return svn_error_trace(root->vtable->node_history(history_p, root, path, 1099 result_pool, 1100 scratch_pool)); 1101} 1102 1103svn_error_t * 1104svn_fs_node_history(svn_fs_history_t **history_p, svn_fs_root_t *root, 1105 const char *path, apr_pool_t *pool) 1106{ 1107 return svn_error_trace(root->vtable->node_history(history_p, root, path, 1108 pool, pool)); 1109} 1110 1111svn_error_t * 1112svn_fs_is_dir(svn_boolean_t *is_dir, svn_fs_root_t *root, const char *path, 1113 apr_pool_t *pool) 1114{ 1115 svn_node_kind_t kind; 1116 1117 SVN_ERR(root->vtable->check_path(&kind, root, path, pool)); 1118 *is_dir = (kind == svn_node_dir); 1119 return SVN_NO_ERROR; 1120} 1121 1122svn_error_t * 1123svn_fs_is_file(svn_boolean_t *is_file, svn_fs_root_t *root, const char *path, 1124 apr_pool_t *pool) 1125{ 1126 svn_node_kind_t kind; 1127 1128 SVN_ERR(root->vtable->check_path(&kind, root, path, pool)); 1129 *is_file = (kind == svn_node_file); 1130 return SVN_NO_ERROR; 1131} 1132 1133svn_error_t * 1134svn_fs_node_id(const svn_fs_id_t **id_p, svn_fs_root_t *root, 1135 const char *path, apr_pool_t *pool) 1136{ 1137 return svn_error_trace(root->vtable->node_id(id_p, root, path, pool)); 1138} 1139 1140svn_error_t * 1141svn_fs_node_relation(svn_fs_node_relation_t *relation, 1142 svn_fs_root_t *root_a, const char *path_a, 1143 svn_fs_root_t *root_b, const char *path_b, 1144 apr_pool_t *scratch_pool) 1145{ 1146 /* Different repository types? */ 1147 if (root_a->fs != root_b->fs) 1148 { 1149 *relation = svn_fs_node_unrelated; 1150 return SVN_NO_ERROR; 1151 } 1152 1153 return svn_error_trace(root_a->vtable->node_relation(relation, 1154 root_a, path_a, 1155 root_b, path_b, 1156 scratch_pool)); 1157} 1158 1159svn_error_t * 1160svn_fs_node_created_rev(svn_revnum_t *revision, svn_fs_root_t *root, 1161 const char *path, apr_pool_t *pool) 1162{ 1163 return svn_error_trace(root->vtable->node_created_rev(revision, root, path, 1164 pool)); 1165} 1166 1167svn_error_t * 1168svn_fs_node_origin_rev(svn_revnum_t *revision, svn_fs_root_t *root, 1169 const char *path, apr_pool_t *pool) 1170{ 1171 return svn_error_trace(root->vtable->node_origin_rev(revision, root, path, 1172 pool)); 1173} 1174 1175svn_error_t * 1176svn_fs_node_created_path(const char **created_path, svn_fs_root_t *root, 1177 const char *path, apr_pool_t *pool) 1178{ 1179 return svn_error_trace(root->vtable->node_created_path(created_path, root, 1180 path, pool)); 1181} 1182 1183svn_error_t * 1184svn_fs_node_prop(svn_string_t **value_p, svn_fs_root_t *root, 1185 const char *path, const char *propname, apr_pool_t *pool) 1186{ 1187 return svn_error_trace(root->vtable->node_prop(value_p, root, path, 1188 propname, pool)); 1189} 1190 1191svn_error_t * 1192svn_fs_node_proplist(apr_hash_t **table_p, svn_fs_root_t *root, 1193 const char *path, apr_pool_t *pool) 1194{ 1195 return svn_error_trace(root->vtable->node_proplist(table_p, root, path, 1196 pool)); 1197} 1198 1199svn_error_t * 1200svn_fs_node_has_props(svn_boolean_t *has_props, 1201 svn_fs_root_t *root, 1202 const char *path, 1203 apr_pool_t *scratch_pool) 1204{ 1205 return svn_error_trace(root->vtable->node_has_props(has_props, root, path, 1206 scratch_pool)); 1207} 1208 1209svn_error_t * 1210svn_fs_change_node_prop(svn_fs_root_t *root, const char *path, 1211 const char *name, const svn_string_t *value, 1212 apr_pool_t *pool) 1213{ 1214 return svn_error_trace(root->vtable->change_node_prop(root, path, name, 1215 value, pool)); 1216} 1217 1218svn_error_t * 1219svn_fs_props_different(svn_boolean_t *changed_p, svn_fs_root_t *root1, 1220 const char *path1, svn_fs_root_t *root2, 1221 const char *path2, apr_pool_t *scratch_pool) 1222{ 1223 return svn_error_trace(root1->vtable->props_changed(changed_p, 1224 root1, path1, 1225 root2, path2, 1226 TRUE, scratch_pool)); 1227} 1228 1229svn_error_t * 1230svn_fs_props_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1, 1231 const char *path1, svn_fs_root_t *root2, 1232 const char *path2, apr_pool_t *pool) 1233{ 1234 return svn_error_trace(root1->vtable->props_changed(changed_p, 1235 root1, path1, 1236 root2, path2, 1237 FALSE, pool)); 1238} 1239 1240svn_error_t * 1241svn_fs_copied_from(svn_revnum_t *rev_p, const char **path_p, 1242 svn_fs_root_t *root, const char *path, apr_pool_t *pool) 1243{ 1244 return svn_error_trace(root->vtable->copied_from(rev_p, path_p, root, path, 1245 pool)); 1246} 1247 1248svn_error_t * 1249svn_fs_closest_copy(svn_fs_root_t **root_p, const char **path_p, 1250 svn_fs_root_t *root, const char *path, apr_pool_t *pool) 1251{ 1252 return svn_error_trace(root->vtable->closest_copy(root_p, path_p, 1253 root, path, pool)); 1254} 1255 1256svn_error_t * 1257svn_fs_get_mergeinfo2(svn_mergeinfo_catalog_t *catalog, 1258 svn_fs_root_t *root, 1259 const apr_array_header_t *paths, 1260 svn_mergeinfo_inheritance_t inherit, 1261 svn_boolean_t include_descendants, 1262 svn_boolean_t adjust_inherited_mergeinfo, 1263 apr_pool_t *result_pool, 1264 apr_pool_t *scratch_pool) 1265{ 1266 return svn_error_trace(root->vtable->get_mergeinfo( 1267 catalog, root, paths, inherit, include_descendants, 1268 adjust_inherited_mergeinfo, result_pool, scratch_pool)); 1269} 1270 1271svn_error_t * 1272svn_fs_get_mergeinfo(svn_mergeinfo_catalog_t *catalog, 1273 svn_fs_root_t *root, 1274 const apr_array_header_t *paths, 1275 svn_mergeinfo_inheritance_t inherit, 1276 svn_boolean_t include_descendants, 1277 apr_pool_t *pool) 1278{ 1279 return svn_error_trace(root->vtable->get_mergeinfo(catalog, root, paths, 1280 inherit, 1281 include_descendants, 1282 TRUE, pool, pool)); 1283} 1284 1285svn_error_t * 1286svn_fs__get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo, 1287 svn_fs_root_t *root, 1288 const char *path, 1289 svn_mergeinfo_inheritance_t inherit, 1290 svn_boolean_t adjust_inherited_mergeinfo, 1291 apr_pool_t *result_pool, 1292 apr_pool_t *scratch_pool) 1293{ 1294 apr_array_header_t *paths 1295 = apr_array_make(scratch_pool, 1, sizeof(const char *)); 1296 svn_mergeinfo_catalog_t catalog; 1297 1298 APR_ARRAY_PUSH(paths, const char *) = path; 1299 1300 SVN_ERR(svn_fs_get_mergeinfo2(&catalog, root, paths, 1301 inherit, FALSE /*include_descendants*/, 1302 adjust_inherited_mergeinfo, 1303 result_pool, scratch_pool)); 1304 *mergeinfo = svn_hash_gets(catalog, path); 1305 1306 return SVN_NO_ERROR; 1307} 1308 1309svn_error_t * 1310svn_fs_merge(const char **conflict_p, svn_fs_root_t *source_root, 1311 const char *source_path, svn_fs_root_t *target_root, 1312 const char *target_path, svn_fs_root_t *ancestor_root, 1313 const char *ancestor_path, apr_pool_t *pool) 1314{ 1315 return svn_error_trace(target_root->vtable->merge(conflict_p, 1316 source_root, source_path, 1317 target_root, target_path, 1318 ancestor_root, 1319 ancestor_path, pool)); 1320} 1321 1322svn_error_t * 1323svn_fs_dir_entries(apr_hash_t **entries_p, svn_fs_root_t *root, 1324 const char *path, apr_pool_t *pool) 1325{ 1326 return svn_error_trace(root->vtable->dir_entries(entries_p, root, path, 1327 pool)); 1328} 1329 1330svn_error_t * 1331svn_fs_dir_optimal_order(apr_array_header_t **ordered_p, 1332 svn_fs_root_t *root, 1333 apr_hash_t *entries, 1334 apr_pool_t *result_pool, 1335 apr_pool_t *scratch_pool) 1336{ 1337 return svn_error_trace(root->vtable->dir_optimal_order(ordered_p, root, 1338 entries, 1339 result_pool, 1340 scratch_pool)); 1341} 1342 1343svn_error_t * 1344svn_fs_make_dir(svn_fs_root_t *root, const char *path, apr_pool_t *pool) 1345{ 1346 SVN_ERR(svn_fs__path_valid(path, pool)); 1347 return svn_error_trace(root->vtable->make_dir(root, path, pool)); 1348} 1349 1350svn_error_t * 1351svn_fs_delete(svn_fs_root_t *root, const char *path, apr_pool_t *pool) 1352{ 1353 return svn_error_trace(root->vtable->delete_node(root, path, pool)); 1354} 1355 1356svn_error_t * 1357svn_fs_copy(svn_fs_root_t *from_root, const char *from_path, 1358 svn_fs_root_t *to_root, const char *to_path, apr_pool_t *pool) 1359{ 1360 SVN_ERR(svn_fs__path_valid(to_path, pool)); 1361 return svn_error_trace(to_root->vtable->copy(from_root, from_path, 1362 to_root, to_path, pool)); 1363} 1364 1365svn_error_t * 1366svn_fs_revision_link(svn_fs_root_t *from_root, svn_fs_root_t *to_root, 1367 const char *path, apr_pool_t *pool) 1368{ 1369 return svn_error_trace(to_root->vtable->revision_link(from_root, to_root, 1370 path, pool)); 1371} 1372 1373svn_error_t * 1374svn_fs_file_length(svn_filesize_t *length_p, svn_fs_root_t *root, 1375 const char *path, apr_pool_t *pool) 1376{ 1377 return svn_error_trace(root->vtable->file_length(length_p, root, path, 1378 pool)); 1379} 1380 1381svn_error_t * 1382svn_fs_file_checksum(svn_checksum_t **checksum, 1383 svn_checksum_kind_t kind, 1384 svn_fs_root_t *root, 1385 const char *path, 1386 svn_boolean_t force, 1387 apr_pool_t *pool) 1388{ 1389 SVN_ERR(root->vtable->file_checksum(checksum, kind, root, path, pool)); 1390 1391 if (force && (*checksum == NULL || (*checksum)->kind != kind)) 1392 { 1393 svn_stream_t *contents, *checksum_contents; 1394 1395 SVN_ERR(svn_fs_file_contents(&contents, root, path, pool)); 1396 checksum_contents = svn_stream_checksummed2(contents, checksum, NULL, 1397 kind, TRUE, pool); 1398 1399 /* This will force a read of any remaining data (which is all of it in 1400 this case) and dump the checksum into checksum->digest. */ 1401 SVN_ERR(svn_stream_close(checksum_contents)); 1402 } 1403 1404 return SVN_NO_ERROR; 1405} 1406 1407svn_error_t * 1408svn_fs_file_md5_checksum(unsigned char digest[], 1409 svn_fs_root_t *root, 1410 const char *path, 1411 apr_pool_t *pool) 1412{ 1413 svn_checksum_t *md5sum; 1414 1415 SVN_ERR(svn_fs_file_checksum(&md5sum, svn_checksum_md5, root, path, TRUE, 1416 pool)); 1417 memcpy(digest, md5sum->digest, APR_MD5_DIGESTSIZE); 1418 1419 return SVN_NO_ERROR; 1420} 1421 1422svn_error_t * 1423svn_fs_file_contents(svn_stream_t **contents, svn_fs_root_t *root, 1424 const char *path, apr_pool_t *pool) 1425{ 1426 return svn_error_trace(root->vtable->file_contents(contents, root, path, 1427 pool)); 1428} 1429 1430svn_error_t * 1431svn_fs_try_process_file_contents(svn_boolean_t *success, 1432 svn_fs_root_t *root, 1433 const char *path, 1434 svn_fs_process_contents_func_t processor, 1435 void* baton, 1436 apr_pool_t *pool) 1437{ 1438 /* if the FS doesn't implement this function, report a "failed" attempt */ 1439 if (root->vtable->try_process_file_contents == NULL) 1440 { 1441 *success = FALSE; 1442 return SVN_NO_ERROR; 1443 } 1444 1445 return svn_error_trace(root->vtable->try_process_file_contents( 1446 success, 1447 root, path, 1448 processor, baton, pool)); 1449} 1450 1451svn_error_t * 1452svn_fs_make_file(svn_fs_root_t *root, const char *path, apr_pool_t *pool) 1453{ 1454 SVN_ERR(svn_fs__path_valid(path, pool)); 1455 return svn_error_trace(root->vtable->make_file(root, path, pool)); 1456} 1457 1458svn_error_t * 1459svn_fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p, 1460 void **contents_baton_p, svn_fs_root_t *root, 1461 const char *path, const char *base_checksum, 1462 const char *result_checksum, apr_pool_t *pool) 1463{ 1464 svn_checksum_t *base, *result; 1465 1466 /* TODO: If we ever rev this API, we should make the supplied checksums 1467 svn_checksum_t structs. */ 1468 SVN_ERR(svn_checksum_parse_hex(&base, svn_checksum_md5, base_checksum, 1469 pool)); 1470 SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum, 1471 pool)); 1472 1473 return svn_error_trace(root->vtable->apply_textdelta(contents_p, 1474 contents_baton_p, 1475 root, 1476 path, 1477 base, 1478 result, 1479 pool)); 1480} 1481 1482svn_error_t * 1483svn_fs_apply_text(svn_stream_t **contents_p, svn_fs_root_t *root, 1484 const char *path, const char *result_checksum, 1485 apr_pool_t *pool) 1486{ 1487 svn_checksum_t *result; 1488 1489 /* TODO: If we ever rev this API, we should make the supplied checksum an 1490 svn_checksum_t struct. */ 1491 SVN_ERR(svn_checksum_parse_hex(&result, svn_checksum_md5, result_checksum, 1492 pool)); 1493 1494 return svn_error_trace(root->vtable->apply_text(contents_p, root, path, 1495 result, pool)); 1496} 1497 1498svn_error_t * 1499svn_fs_contents_different(svn_boolean_t *changed_p, svn_fs_root_t *root1, 1500 const char *path1, svn_fs_root_t *root2, 1501 const char *path2, apr_pool_t *scratch_pool) 1502{ 1503 return svn_error_trace(root1->vtable->contents_changed(changed_p, 1504 root1, path1, 1505 root2, path2, 1506 TRUE, 1507 scratch_pool)); 1508} 1509 1510svn_error_t * 1511svn_fs_contents_changed(svn_boolean_t *changed_p, svn_fs_root_t *root1, 1512 const char *path1, svn_fs_root_t *root2, 1513 const char *path2, apr_pool_t *pool) 1514{ 1515 return svn_error_trace(root1->vtable->contents_changed(changed_p, 1516 root1, path1, 1517 root2, path2, 1518 FALSE, pool)); 1519} 1520 1521svn_error_t * 1522svn_fs_youngest_rev(svn_revnum_t *youngest_p, svn_fs_t *fs, apr_pool_t *pool) 1523{ 1524 return svn_error_trace(fs->vtable->youngest_rev(youngest_p, fs, pool)); 1525} 1526 1527svn_error_t * 1528svn_fs_info_format(int *fs_format, 1529 svn_version_t **supports_version, 1530 svn_fs_t *fs, 1531 apr_pool_t *result_pool, 1532 apr_pool_t *scratch_pool) 1533{ 1534 return svn_error_trace(fs->vtable->info_format(fs_format, supports_version, 1535 fs, 1536 result_pool, scratch_pool)); 1537} 1538 1539svn_error_t * 1540svn_fs_info_config_files(apr_array_header_t **files, 1541 svn_fs_t *fs, 1542 apr_pool_t *result_pool, 1543 apr_pool_t *scratch_pool) 1544{ 1545 return svn_error_trace(fs->vtable->info_config_files(files, fs, 1546 result_pool, 1547 scratch_pool)); 1548} 1549 1550svn_error_t * 1551svn_fs_deltify_revision(svn_fs_t *fs, svn_revnum_t revision, apr_pool_t *pool) 1552{ 1553 return svn_error_trace(fs->vtable->deltify(fs, revision, pool)); 1554} 1555 1556svn_error_t * 1557svn_fs_revision_prop(svn_string_t **value_p, svn_fs_t *fs, svn_revnum_t rev, 1558 const char *propname, apr_pool_t *pool) 1559{ 1560 return svn_error_trace(fs->vtable->revision_prop(value_p, fs, rev, 1561 propname, pool)); 1562} 1563 1564svn_error_t * 1565svn_fs_revision_proplist(apr_hash_t **table_p, svn_fs_t *fs, svn_revnum_t rev, 1566 apr_pool_t *pool) 1567{ 1568 return svn_error_trace(fs->vtable->revision_proplist(table_p, fs, rev, 1569 pool)); 1570} 1571 1572svn_error_t * 1573svn_fs_change_rev_prop2(svn_fs_t *fs, svn_revnum_t rev, const char *name, 1574 const svn_string_t *const *old_value_p, 1575 const svn_string_t *value, apr_pool_t *pool) 1576{ 1577 return svn_error_trace(fs->vtable->change_rev_prop(fs, rev, name, 1578 old_value_p, 1579 value, pool)); 1580} 1581 1582svn_error_t * 1583svn_fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p, 1584 svn_fs_root_t *source_root, 1585 const char *source_path, 1586 svn_fs_root_t *target_root, 1587 const char *target_path, apr_pool_t *pool) 1588{ 1589 return svn_error_trace(target_root->vtable->get_file_delta_stream( 1590 stream_p, 1591 source_root, source_path, 1592 target_root, target_path, pool)); 1593} 1594 1595svn_error_t * 1596svn_fs_get_uuid(svn_fs_t *fs, const char **uuid, apr_pool_t *pool) 1597{ 1598 /* If you change this, consider changing svn_fs__identifier(). */ 1599 *uuid = apr_pstrdup(pool, fs->uuid); 1600 return SVN_NO_ERROR; 1601} 1602 1603svn_error_t * 1604svn_fs_set_uuid(svn_fs_t *fs, const char *uuid, apr_pool_t *pool) 1605{ 1606 if (! uuid) 1607 { 1608 uuid = svn_uuid_generate(pool); 1609 } 1610 else 1611 { 1612 apr_uuid_t parsed_uuid; 1613 apr_status_t apr_err = apr_uuid_parse(&parsed_uuid, uuid); 1614 if (apr_err) 1615 return svn_error_createf(SVN_ERR_BAD_UUID, NULL, 1616 _("Malformed UUID '%s'"), uuid); 1617 } 1618 return svn_error_trace(fs->vtable->set_uuid(fs, uuid, pool)); 1619} 1620 1621svn_error_t * 1622svn_fs_lock_many(svn_fs_t *fs, 1623 apr_hash_t *targets, 1624 const char *comment, 1625 svn_boolean_t is_dav_comment, 1626 apr_time_t expiration_date, 1627 svn_boolean_t steal_lock, 1628 svn_fs_lock_callback_t lock_callback, 1629 void *lock_baton, 1630 apr_pool_t *result_pool, 1631 apr_pool_t *scratch_pool) 1632{ 1633 apr_hash_index_t *hi; 1634 apr_hash_t *ok_targets = apr_hash_make(scratch_pool); 1635 svn_error_t *err, *cb_err = SVN_NO_ERROR; 1636 1637 /* Enforce that the comment be xml-escapable. */ 1638 if (comment) 1639 if (! svn_xml_is_xml_safe(comment, strlen(comment))) 1640 return svn_error_create(SVN_ERR_XML_UNESCAPABLE_DATA, NULL, 1641 _("Lock comment contains illegal characters")); 1642 1643 if (expiration_date < 0) 1644 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 1645 _("Negative expiration date passed to svn_fs_lock")); 1646 1647 /* Enforce that the token be an XML-safe URI. */ 1648 for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi)) 1649 { 1650 const svn_fs_lock_target_t *target = apr_hash_this_val(hi); 1651 1652 err = SVN_NO_ERROR; 1653 if (target->token) 1654 { 1655 const char *c; 1656 1657 1658 if (strncmp(target->token, "opaquelocktoken:", 16)) 1659 err = svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL, 1660 _("Lock token URI '%s' has bad scheme; " 1661 "expected '%s'"), 1662 target->token, "opaquelocktoken"); 1663 1664 if (!err) 1665 for (c = target->token; *c && !err; c++) 1666 if (! svn_ctype_isascii(*c) || svn_ctype_iscntrl(*c)) 1667 err = svn_error_createf( 1668 SVN_ERR_FS_BAD_LOCK_TOKEN, NULL, 1669 _("Lock token '%s' is not ASCII or is a " 1670 "control character at byte %u"), 1671 target->token, 1672 (unsigned)(c - target->token)); 1673 1674 /* strlen(token) == c - token. */ 1675 if (!err && !svn_xml_is_xml_safe(target->token, c - target->token)) 1676 err = svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL, 1677 _("Lock token URI '%s' is not XML-safe"), 1678 target->token); 1679 } 1680 1681 if (err) 1682 { 1683 if (!cb_err && lock_callback) 1684 cb_err = lock_callback(lock_baton, apr_hash_this_key(hi), 1685 NULL, err, scratch_pool); 1686 svn_error_clear(err); 1687 } 1688 else 1689 svn_hash_sets(ok_targets, apr_hash_this_key(hi), target); 1690 } 1691 1692 if (!apr_hash_count(ok_targets)) 1693 return svn_error_trace(cb_err); 1694 1695 err = fs->vtable->lock(fs, ok_targets, comment, is_dav_comment, 1696 expiration_date, steal_lock, 1697 lock_callback, lock_baton, 1698 result_pool, scratch_pool); 1699 1700 if (err && cb_err) 1701 svn_error_compose(err, cb_err); 1702 else if (!err) 1703 err = cb_err; 1704 1705 return svn_error_trace(err); 1706} 1707 1708struct lock_baton_t { 1709 const svn_lock_t *lock; 1710 svn_error_t *fs_err; 1711}; 1712 1713/* Implements svn_fs_lock_callback_t. Used by svn_fs_lock and 1714 svn_fs_unlock to record the lock and error from svn_fs_lock_many 1715 and svn_fs_unlock_many. */ 1716static svn_error_t * 1717lock_cb(void *lock_baton, 1718 const char *path, 1719 const svn_lock_t *lock, 1720 svn_error_t *fs_err, 1721 apr_pool_t *pool) 1722{ 1723 struct lock_baton_t *b = lock_baton; 1724 1725 b->lock = lock; 1726 b->fs_err = svn_error_dup(fs_err); 1727 1728 return SVN_NO_ERROR; 1729} 1730 1731svn_error_t * 1732svn_fs_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path, 1733 const char *token, const char *comment, 1734 svn_boolean_t is_dav_comment, apr_time_t expiration_date, 1735 svn_revnum_t current_rev, svn_boolean_t steal_lock, 1736 apr_pool_t *pool) 1737{ 1738 apr_hash_t *targets = apr_hash_make(pool); 1739 svn_fs_lock_target_t target; 1740 svn_error_t *err; 1741 struct lock_baton_t baton = {0}; 1742 1743 target.token = token; 1744 target.current_rev = current_rev; 1745 svn_hash_sets(targets, path, &target); 1746 1747 err = svn_fs_lock_many(fs, targets, comment, is_dav_comment, 1748 expiration_date, steal_lock, lock_cb, &baton, 1749 pool, pool); 1750 1751 if (baton.lock) 1752 *lock = (svn_lock_t*)baton.lock; 1753 1754 if (err && baton.fs_err) 1755 svn_error_compose(err, baton.fs_err); 1756 else if (!err) 1757 err = baton.fs_err; 1758 1759 return svn_error_trace(err); 1760} 1761 1762svn_error_t * 1763svn_fs_generate_lock_token(const char **token, svn_fs_t *fs, apr_pool_t *pool) 1764{ 1765 return svn_error_trace(fs->vtable->generate_lock_token(token, fs, pool)); 1766} 1767 1768svn_fs_lock_target_t * 1769svn_fs_lock_target_create(const char *token, 1770 svn_revnum_t current_rev, 1771 apr_pool_t *result_pool) 1772{ 1773 svn_fs_lock_target_t *target = apr_palloc(result_pool, 1774 sizeof(svn_fs_lock_target_t)); 1775 1776 target->token = token; 1777 target->current_rev = current_rev; 1778 1779 return target; 1780} 1781 1782void 1783svn_fs_lock_target_set_token(svn_fs_lock_target_t *target, 1784 const char *token) 1785{ 1786 target->token = token; 1787} 1788 1789svn_error_t * 1790svn_fs_unlock_many(svn_fs_t *fs, 1791 apr_hash_t *targets, 1792 svn_boolean_t break_lock, 1793 svn_fs_lock_callback_t lock_callback, 1794 void *lock_baton, 1795 apr_pool_t *result_pool, 1796 apr_pool_t *scratch_pool) 1797{ 1798 return svn_error_trace(fs->vtable->unlock(fs, targets, break_lock, 1799 lock_callback, lock_baton, 1800 result_pool, scratch_pool)); 1801} 1802 1803svn_error_t * 1804svn_fs_unlock(svn_fs_t *fs, const char *path, const char *token, 1805 svn_boolean_t break_lock, apr_pool_t *pool) 1806{ 1807 apr_hash_t *targets = apr_hash_make(pool); 1808 svn_error_t *err; 1809 struct lock_baton_t baton = {0}; 1810 1811 if (!token) 1812 token = ""; 1813 svn_hash_sets(targets, path, token); 1814 1815 err = svn_fs_unlock_many(fs, targets, break_lock, lock_cb, &baton, 1816 pool, pool); 1817 1818 if (err && baton.fs_err) 1819 svn_error_compose(err, baton.fs_err); 1820 else if (!err) 1821 err = baton.fs_err; 1822 1823 return svn_error_trace(err); 1824} 1825 1826svn_error_t * 1827svn_fs_get_lock(svn_lock_t **lock, svn_fs_t *fs, const char *path, 1828 apr_pool_t *pool) 1829{ 1830 return svn_error_trace(fs->vtable->get_lock(lock, fs, path, pool)); 1831} 1832 1833svn_error_t * 1834svn_fs_get_locks2(svn_fs_t *fs, const char *path, svn_depth_t depth, 1835 svn_fs_get_locks_callback_t get_locks_func, 1836 void *get_locks_baton, apr_pool_t *pool) 1837{ 1838 SVN_ERR_ASSERT((depth == svn_depth_empty) || 1839 (depth == svn_depth_files) || 1840 (depth == svn_depth_immediates) || 1841 (depth == svn_depth_infinity)); 1842 return svn_error_trace(fs->vtable->get_locks(fs, path, depth, 1843 get_locks_func, 1844 get_locks_baton, pool)); 1845} 1846 1847 1848/* --- History functions --- */ 1849 1850svn_error_t * 1851svn_fs_history_prev2(svn_fs_history_t **prev_history_p, 1852 svn_fs_history_t *history, svn_boolean_t cross_copies, 1853 apr_pool_t *result_pool, apr_pool_t *scratch_pool) 1854{ 1855 return svn_error_trace(history->vtable->prev(prev_history_p, history, 1856 cross_copies, result_pool, 1857 scratch_pool)); 1858} 1859 1860svn_error_t * 1861svn_fs_history_prev(svn_fs_history_t **prev_history_p, 1862 svn_fs_history_t *history, svn_boolean_t cross_copies, 1863 apr_pool_t *pool) 1864{ 1865 return svn_error_trace(history->vtable->prev(prev_history_p, history, 1866 cross_copies, pool, pool)); 1867} 1868 1869svn_error_t * 1870svn_fs_history_location(const char **path, svn_revnum_t *revision, 1871 svn_fs_history_t *history, apr_pool_t *pool) 1872{ 1873 return svn_error_trace(history->vtable->location(path, revision, history, 1874 pool)); 1875} 1876 1877 1878/* --- Node-ID functions --- */ 1879 1880svn_fs_id_t * 1881svn_fs_parse_id(const char *data, apr_size_t len, apr_pool_t *pool) 1882{ 1883 fs_library_vtable_t *vtable; 1884 svn_error_t *err; 1885 1886 err = get_library_vtable(&vtable, SVN_FS_TYPE_BDB, pool); 1887 if (err) 1888 { 1889 svn_error_clear(err); 1890 return NULL; 1891 } 1892 return vtable->parse_id(data, len, pool); 1893} 1894 1895svn_string_t * 1896svn_fs_unparse_id(const svn_fs_id_t *id, apr_pool_t *pool) 1897{ 1898 return id->vtable->unparse(id, pool); 1899} 1900 1901svn_boolean_t 1902svn_fs_check_related(const svn_fs_id_t *a, const svn_fs_id_t *b) 1903{ 1904 return (a->vtable->compare(a, b) != svn_fs_node_unrelated); 1905} 1906 1907int 1908svn_fs_compare_ids(const svn_fs_id_t *a, const svn_fs_id_t *b) 1909{ 1910 switch (a->vtable->compare(a, b)) 1911 { 1912 case svn_fs_node_unchanged: 1913 return 0; 1914 case svn_fs_node_common_ancestor: 1915 return 1; 1916 default: 1917 return -1; 1918 } 1919} 1920 1921svn_error_t * 1922svn_fs_print_modules(svn_stringbuf_t *output, 1923 apr_pool_t *pool) 1924{ 1925 struct fs_type_defn *defn = fs_modules; 1926 fs_library_vtable_t *vtable; 1927 apr_pool_t *iterpool = svn_pool_create(pool); 1928 1929 while (defn) 1930 { 1931 char *line; 1932 svn_error_t *err; 1933 1934 svn_pool_clear(iterpool); 1935 1936 err = get_library_vtable_direct(&vtable, defn, iterpool); 1937 if (err) 1938 { 1939 if (err->apr_err == SVN_ERR_FS_UNKNOWN_FS_TYPE) 1940 { 1941 svn_error_clear(err); 1942 defn = defn->next; 1943 continue; 1944 } 1945 else 1946 return err; 1947 } 1948 1949 line = apr_psprintf(iterpool, "* fs_%s : %s\n", 1950 defn->fsap_name, vtable->get_description()); 1951 svn_stringbuf_appendcstr(output, line); 1952 defn = defn->next; 1953 } 1954 1955 svn_pool_destroy(iterpool); 1956 1957 return SVN_NO_ERROR; 1958} 1959 1960svn_fs_path_change2_t * 1961svn_fs_path_change2_create(const svn_fs_id_t *node_rev_id, 1962 svn_fs_path_change_kind_t change_kind, 1963 apr_pool_t *pool) 1964{ 1965 return svn_fs__path_change_create_internal(node_rev_id, change_kind, pool); 1966} 1967 1968/* Return the library version number. */ 1969const svn_version_t * 1970svn_fs_version(void) 1971{ 1972 SVN_VERSION_BODY; 1973} 1974 1975 1976/** info **/ 1977svn_error_t * 1978svn_fs_info(const svn_fs_info_placeholder_t **info_p, 1979 svn_fs_t *fs, 1980 apr_pool_t *result_pool, 1981 apr_pool_t *scratch_pool) 1982{ 1983 if (fs->vtable->info_fsap) 1984 { 1985 SVN_ERR(fs->vtable->info_fsap((const void **)info_p, fs, 1986 result_pool, scratch_pool)); 1987 } 1988 else 1989 { 1990 svn_fs_info_placeholder_t *info = apr_palloc(result_pool, sizeof(*info)); 1991 /* ### Ask the disk(!), since svn_fs_t doesn't cache the answer. */ 1992 SVN_ERR(svn_fs_type(&info->fs_type, fs->path, result_pool)); 1993 *info_p = info; 1994 } 1995 return SVN_NO_ERROR; 1996} 1997 1998void * 1999svn_fs_info_dup(const void *info_void, 2000 apr_pool_t *result_pool, 2001 apr_pool_t *scratch_pool) 2002{ 2003 const svn_fs_info_placeholder_t *info = info_void; 2004 fs_library_vtable_t *vtable; 2005 2006 SVN_ERR(get_library_vtable(&vtable, info->fs_type, scratch_pool)); 2007 2008 if (vtable->info_fsap_dup) 2009 return vtable->info_fsap_dup(info_void, result_pool); 2010 else 2011 return apr_pmemdup(result_pool, info, sizeof(*info)); 2012} 2013 2014