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