1/* util.c --- utility functions for FSFS repo access 2 * 3 * ==================================================================== 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 * ==================================================================== 21 */ 22 23#include <assert.h> 24 25#include "svn_ctype.h" 26#include "svn_dirent_uri.h" 27#include "private/svn_string_private.h" 28 29#include "fs_fs.h" 30#include "pack.h" 31#include "util.h" 32 33#include "../libsvn_fs/fs-loader.h" 34 35#include "svn_private_config.h" 36 37svn_boolean_t 38svn_fs_fs__is_packed_rev(svn_fs_t *fs, 39 svn_revnum_t rev) 40{ 41 fs_fs_data_t *ffd = fs->fsap_data; 42 43 return (rev < ffd->min_unpacked_rev); 44} 45 46svn_boolean_t 47svn_fs_fs__is_packed_revprop(svn_fs_t *fs, 48 svn_revnum_t rev) 49{ 50 fs_fs_data_t *ffd = fs->fsap_data; 51 52 /* rev 0 will not be packed */ 53 return (rev < ffd->min_unpacked_rev) 54 && (rev != 0) 55 && (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT); 56} 57 58svn_revnum_t 59svn_fs_fs__packed_base_rev(svn_fs_t *fs, 60 svn_revnum_t revision) 61{ 62 fs_fs_data_t *ffd = fs->fsap_data; 63 return (revision < ffd->min_unpacked_rev) 64 ? (revision - (revision % ffd->max_files_per_dir)) 65 : revision; 66} 67 68const char * 69svn_fs_fs__path_txn_current(svn_fs_t *fs, 70 apr_pool_t *pool) 71{ 72 return svn_dirent_join(fs->path, PATH_TXN_CURRENT, pool); 73} 74 75const char * 76svn_fs_fs__path_txn_current_lock(svn_fs_t *fs, 77 apr_pool_t *pool) 78{ 79 return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, pool); 80} 81 82const char * 83svn_fs_fs__path_lock(svn_fs_t *fs, 84 apr_pool_t *pool) 85{ 86 return svn_dirent_join(fs->path, PATH_LOCK_FILE, pool); 87} 88 89const char * 90svn_fs_fs__path_pack_lock(svn_fs_t *fs, 91 apr_pool_t *pool) 92{ 93 return svn_dirent_join(fs->path, PATH_PACK_LOCK_FILE, pool); 94} 95 96const char * 97svn_fs_fs__path_revprop_generation(svn_fs_t *fs, 98 apr_pool_t *pool) 99{ 100 return svn_dirent_join(fs->path, PATH_REVPROP_GENERATION, pool); 101} 102 103const char * 104svn_fs_fs__path_rev_packed(svn_fs_t *fs, 105 svn_revnum_t rev, 106 const char *kind, 107 apr_pool_t *pool) 108{ 109 fs_fs_data_t *ffd = fs->fsap_data; 110 111 assert(ffd->max_files_per_dir); 112 assert(svn_fs_fs__is_packed_rev(fs, rev)); 113 114 return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR, 115 apr_psprintf(pool, 116 "%ld" PATH_EXT_PACKED_SHARD, 117 rev / ffd->max_files_per_dir), 118 kind, SVN_VA_NULL); 119} 120 121const char * 122svn_fs_fs__path_rev_shard(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool) 123{ 124 fs_fs_data_t *ffd = fs->fsap_data; 125 126 assert(ffd->max_files_per_dir); 127 return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR, 128 apr_psprintf(pool, "%ld", 129 rev / ffd->max_files_per_dir), 130 SVN_VA_NULL); 131} 132 133const char * 134svn_fs_fs__path_rev(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool) 135{ 136 fs_fs_data_t *ffd = fs->fsap_data; 137 138 assert(! svn_fs_fs__is_packed_rev(fs, rev)); 139 140 if (ffd->max_files_per_dir) 141 { 142 return svn_dirent_join(svn_fs_fs__path_rev_shard(fs, rev, pool), 143 apr_psprintf(pool, "%ld", rev), 144 pool); 145 } 146 147 return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR, 148 apr_psprintf(pool, "%ld", rev), SVN_VA_NULL); 149} 150 151/* Set *PATH to the path of REV in FS with PACKED selecting whether the 152 (potential) pack file or single revision file name is returned. 153 Allocate *PATH in POOL. 154*/ 155static const char * 156path_rev_absolute_internal(svn_fs_t *fs, 157 svn_revnum_t rev, 158 svn_boolean_t packed, 159 apr_pool_t *pool) 160{ 161 return packed 162 ? svn_fs_fs__path_rev_packed(fs, rev, PATH_PACKED, pool) 163 : svn_fs_fs__path_rev(fs, rev, pool); 164} 165 166const char * 167svn_fs_fs__path_rev_absolute(svn_fs_t *fs, 168 svn_revnum_t rev, 169 apr_pool_t *pool) 170{ 171 fs_fs_data_t *ffd = fs->fsap_data; 172 svn_boolean_t is_packed = ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT 173 && svn_fs_fs__is_packed_rev(fs, rev); 174 175 return path_rev_absolute_internal(fs, rev, is_packed, pool); 176} 177 178const char * 179svn_fs_fs__path_revprops_shard(svn_fs_t *fs, 180 svn_revnum_t rev, 181 apr_pool_t *pool) 182{ 183 fs_fs_data_t *ffd = fs->fsap_data; 184 185 assert(ffd->max_files_per_dir); 186 return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR, 187 apr_psprintf(pool, "%ld", 188 rev / ffd->max_files_per_dir), 189 SVN_VA_NULL); 190} 191 192const char * 193svn_fs_fs__path_revprops_pack_shard(svn_fs_t *fs, 194 svn_revnum_t rev, 195 apr_pool_t *pool) 196{ 197 fs_fs_data_t *ffd = fs->fsap_data; 198 199 assert(ffd->max_files_per_dir); 200 return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR, 201 apr_psprintf(pool, "%ld" PATH_EXT_PACKED_SHARD, 202 rev / ffd->max_files_per_dir), 203 SVN_VA_NULL); 204} 205 206const char * 207svn_fs_fs__path_revprops(svn_fs_t *fs, 208 svn_revnum_t rev, 209 apr_pool_t *pool) 210{ 211 fs_fs_data_t *ffd = fs->fsap_data; 212 213 if (ffd->max_files_per_dir) 214 { 215 return svn_dirent_join(svn_fs_fs__path_revprops_shard(fs, rev, pool), 216 apr_psprintf(pool, "%ld", rev), 217 pool); 218 } 219 220 return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR, 221 apr_psprintf(pool, "%ld", rev), SVN_VA_NULL); 222} 223 224/* Return TO_ADD appended to the C string representation of TXN_ID. 225 * Allocate the result in POOL. 226 */ 227static const char * 228combine_txn_id_string(const svn_fs_fs__id_part_t *txn_id, 229 const char *to_add, 230 apr_pool_t *pool) 231{ 232 return apr_pstrcat(pool, svn_fs_fs__id_txn_unparse(txn_id, pool), 233 to_add, SVN_VA_NULL); 234} 235 236const char * 237svn_fs_fs__path_txns_dir(svn_fs_t *fs, 238 apr_pool_t *pool) 239{ 240 return svn_dirent_join(fs->path, PATH_TXNS_DIR, pool); 241} 242 243const char * 244svn_fs_fs__path_txn_dir(svn_fs_t *fs, 245 const svn_fs_fs__id_part_t *txn_id, 246 apr_pool_t *pool) 247{ 248 SVN_ERR_ASSERT_NO_RETURN(txn_id != NULL); 249 return svn_dirent_join(svn_fs_fs__path_txns_dir(fs, pool), 250 combine_txn_id_string(txn_id, PATH_EXT_TXN, pool), 251 pool); 252} 253 254const char* 255svn_fs_fs__path_l2p_proto_index(svn_fs_t *fs, 256 const svn_fs_fs__id_part_t *txn_id, 257 apr_pool_t *pool) 258{ 259 return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), 260 PATH_INDEX PATH_EXT_L2P_INDEX, pool); 261} 262 263const char* 264svn_fs_fs__path_p2l_proto_index(svn_fs_t *fs, 265 const svn_fs_fs__id_part_t *txn_id, 266 apr_pool_t *pool) 267{ 268 return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), 269 PATH_INDEX PATH_EXT_P2L_INDEX, pool); 270} 271 272const char * 273svn_fs_fs__path_txn_item_index(svn_fs_t *fs, 274 const svn_fs_fs__id_part_t *txn_id, 275 apr_pool_t *pool) 276{ 277 return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), 278 PATH_TXN_ITEM_INDEX, pool); 279} 280 281const char * 282svn_fs_fs__path_txn_proto_revs(svn_fs_t *fs, 283 apr_pool_t *pool) 284{ 285 return svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, pool); 286} 287 288const char * 289svn_fs_fs__path_txn_proto_rev(svn_fs_t *fs, 290 const svn_fs_fs__id_part_t *txn_id, 291 apr_pool_t *pool) 292{ 293 fs_fs_data_t *ffd = fs->fsap_data; 294 if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT) 295 return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool), 296 combine_txn_id_string(txn_id, PATH_EXT_REV, pool), 297 pool); 298 else 299 return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), 300 PATH_REV, pool); 301} 302 303 304const char * 305svn_fs_fs__path_txn_proto_rev_lock(svn_fs_t *fs, 306 const svn_fs_fs__id_part_t *txn_id, 307 apr_pool_t *pool) 308{ 309 fs_fs_data_t *ffd = fs->fsap_data; 310 if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT) 311 return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool), 312 combine_txn_id_string(txn_id, PATH_EXT_REV_LOCK, 313 pool), 314 pool); 315 else 316 return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), 317 PATH_REV_LOCK, pool); 318} 319 320const char * 321svn_fs_fs__path_txn_node_rev(svn_fs_t *fs, 322 const svn_fs_id_t *id, 323 apr_pool_t *pool) 324{ 325 char *filename = (char *)svn_fs_fs__id_unparse(id, pool)->data; 326 *strrchr(filename, '.') = '\0'; 327 328 return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, svn_fs_fs__id_txn_id(id), 329 pool), 330 apr_psprintf(pool, PATH_PREFIX_NODE "%s", 331 filename), 332 pool); 333} 334 335const char * 336svn_fs_fs__path_txn_node_props(svn_fs_t *fs, 337 const svn_fs_id_t *id, 338 apr_pool_t *pool) 339{ 340 return apr_pstrcat(pool, svn_fs_fs__path_txn_node_rev(fs, id, pool), 341 PATH_EXT_PROPS, SVN_VA_NULL); 342} 343 344const char * 345svn_fs_fs__path_txn_node_children(svn_fs_t *fs, 346 const svn_fs_id_t *id, 347 apr_pool_t *pool) 348{ 349 return apr_pstrcat(pool, svn_fs_fs__path_txn_node_rev(fs, id, pool), 350 PATH_EXT_CHILDREN, SVN_VA_NULL); 351} 352 353const char * 354svn_fs_fs__path_node_origin(svn_fs_t *fs, 355 const svn_fs_fs__id_part_t *node_id, 356 apr_pool_t *pool) 357{ 358 char buffer[SVN_INT64_BUFFER_SIZE]; 359 apr_size_t len = svn__ui64tobase36(buffer, node_id->number); 360 361 if (len > 1) 362 buffer[len - 1] = '\0'; 363 364 return svn_dirent_join_many(pool, fs->path, PATH_NODE_ORIGINS_DIR, 365 buffer, SVN_VA_NULL); 366} 367 368const char * 369svn_fs_fs__path_min_unpacked_rev(svn_fs_t *fs, 370 apr_pool_t *pool) 371{ 372 return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REV, pool); 373} 374 375svn_error_t * 376svn_fs_fs__check_file_buffer_numeric(const char *buf, 377 apr_off_t offset, 378 const char *path, 379 const char *title, 380 apr_pool_t *pool) 381{ 382 const char *p; 383 384 for (p = buf + offset; *p; p++) 385 if (!svn_ctype_isdigit(*p)) 386 return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL, 387 _("%s file '%s' contains unexpected non-digit '%c' within '%s'"), 388 title, svn_dirent_local_style(path, pool), *p, buf); 389 390 return SVN_NO_ERROR; 391} 392 393svn_error_t * 394svn_fs_fs__read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev, 395 svn_fs_t *fs, 396 apr_pool_t *pool) 397{ 398 char buf[80]; 399 apr_file_t *file; 400 apr_size_t len; 401 402 SVN_ERR(svn_io_file_open(&file, 403 svn_fs_fs__path_min_unpacked_rev(fs, pool), 404 APR_READ | APR_BUFFERED, 405 APR_OS_DEFAULT, 406 pool)); 407 len = sizeof(buf); 408 SVN_ERR(svn_io_read_length_line(file, buf, &len, pool)); 409 SVN_ERR(svn_io_file_close(file, pool)); 410 411 SVN_ERR(svn_revnum_parse(min_unpacked_rev, buf, NULL)); 412 return SVN_NO_ERROR; 413} 414 415svn_error_t * 416svn_fs_fs__update_min_unpacked_rev(svn_fs_t *fs, 417 apr_pool_t *pool) 418{ 419 fs_fs_data_t *ffd = fs->fsap_data; 420 421 SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT); 422 423 return svn_fs_fs__read_min_unpacked_rev(&ffd->min_unpacked_rev, fs, pool); 424} 425 426svn_error_t * 427svn_fs_fs__write_min_unpacked_rev(svn_fs_t *fs, 428 svn_revnum_t revnum, 429 apr_pool_t *scratch_pool) 430{ 431 fs_fs_data_t *ffd = fs->fsap_data; 432 const char *final_path; 433 char buf[SVN_INT64_BUFFER_SIZE]; 434 apr_size_t len = svn__i64toa(buf, revnum); 435 buf[len] = '\n'; 436 437 final_path = svn_fs_fs__path_min_unpacked_rev(fs, scratch_pool); 438 439 SVN_ERR(svn_io_write_atomic2(final_path, buf, len + 1, 440 final_path /* copy_perms */, 441 ffd->flush_to_disk, scratch_pool)); 442 443 return SVN_NO_ERROR; 444} 445 446svn_error_t * 447svn_fs_fs__read_current(svn_revnum_t *rev, 448 apr_uint64_t *next_node_id, 449 apr_uint64_t *next_copy_id, 450 svn_fs_t *fs, 451 apr_pool_t *pool) 452{ 453 fs_fs_data_t *ffd = fs->fsap_data; 454 svn_stringbuf_t *content; 455 456 SVN_ERR(svn_fs_fs__read_content(&content, 457 svn_fs_fs__path_current(fs, pool), 458 pool)); 459 460 if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT) 461 { 462 /* When format 1 and 2 filesystems are upgraded, the 'current' file is 463 left intact. As a consequence, there is a window when a filesystem 464 has a new format, but this file still contains the IDs left from an 465 old format, i.e. looks like "359 j5 v\n". Do not be too strict here 466 and only expect a parseable revision number. */ 467 SVN_ERR(svn_revnum_parse(rev, content->data, NULL)); 468 469 *next_node_id = 0; 470 *next_copy_id = 0; 471 } 472 else 473 { 474 const char *str; 475 476 SVN_ERR(svn_revnum_parse(rev, content->data, &str)); 477 if (*str != ' ') 478 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 479 _("Corrupt 'current' file")); 480 481 *next_node_id = svn__base36toui64(&str, str + 1); 482 if (*str != ' ') 483 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 484 _("Corrupt 'current' file")); 485 486 *next_copy_id = svn__base36toui64(&str, str + 1); 487 if (*str != '\n') 488 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 489 _("Corrupt 'current' file")); 490 } 491 492 return SVN_NO_ERROR; 493} 494 495svn_error_t * 496svn_fs_fs__write_current(svn_fs_t *fs, 497 svn_revnum_t rev, 498 apr_uint64_t next_node_id, 499 apr_uint64_t next_copy_id, 500 apr_pool_t *pool) 501{ 502 char *buf; 503 const char *name; 504 fs_fs_data_t *ffd = fs->fsap_data; 505 506 /* Now we can just write out this line. */ 507 if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT) 508 { 509 buf = apr_psprintf(pool, "%ld\n", rev); 510 } 511 else 512 { 513 char node_id_str[SVN_INT64_BUFFER_SIZE]; 514 char copy_id_str[SVN_INT64_BUFFER_SIZE]; 515 svn__ui64tobase36(node_id_str, next_node_id); 516 svn__ui64tobase36(copy_id_str, next_copy_id); 517 518 buf = apr_psprintf(pool, "%ld %s %s\n", rev, node_id_str, copy_id_str); 519 } 520 521 name = svn_fs_fs__path_current(fs, pool); 522 SVN_ERR(svn_io_write_atomic2(name, buf, strlen(buf), 523 name /* copy_perms_path */, 524 ffd->flush_to_disk, pool)); 525 526 return SVN_NO_ERROR; 527} 528 529svn_error_t * 530svn_fs_fs__try_stringbuf_from_file(svn_stringbuf_t **content, 531 svn_boolean_t *missing, 532 const char *path, 533 svn_boolean_t last_attempt, 534 apr_pool_t *pool) 535{ 536 svn_error_t *err = svn_stringbuf_from_file2(content, path, pool); 537 if (missing) 538 *missing = FALSE; 539 540 if (err) 541 { 542 *content = NULL; 543 544 if (APR_STATUS_IS_ENOENT(err->apr_err)) 545 { 546 if (!last_attempt) 547 { 548 svn_error_clear(err); 549 if (missing) 550 *missing = TRUE; 551 return SVN_NO_ERROR; 552 } 553 } 554#ifdef ESTALE 555 else if (APR_TO_OS_ERROR(err->apr_err) == ESTALE 556 || APR_TO_OS_ERROR(err->apr_err) == EIO) 557 { 558 if (!last_attempt) 559 { 560 svn_error_clear(err); 561 return SVN_NO_ERROR; 562 } 563 } 564#endif 565 } 566 567 return svn_error_trace(err); 568} 569 570svn_error_t * 571svn_fs_fs__read_content(svn_stringbuf_t **content, 572 const char *fname, 573 apr_pool_t *pool) 574{ 575 int i; 576 *content = NULL; 577 578 for (i = 0; !*content && (i < SVN_FS_FS__RECOVERABLE_RETRY_COUNT); ++i) 579 SVN_ERR(svn_fs_fs__try_stringbuf_from_file(content, NULL, 580 fname, i + 1 < SVN_FS_FS__RECOVERABLE_RETRY_COUNT, 581 pool)); 582 583 if (!*content) 584 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 585 _("Can't read '%s'"), 586 svn_dirent_local_style(fname, pool)); 587 588 return SVN_NO_ERROR; 589} 590 591svn_error_t * 592svn_fs_fs__read_number_from_stream(apr_int64_t *result, 593 svn_boolean_t *hit_eof, 594 svn_stream_t *stream, 595 apr_pool_t *scratch_pool) 596{ 597 svn_stringbuf_t *sb; 598 svn_boolean_t eof; 599 svn_error_t *err; 600 601 SVN_ERR(svn_stream_readline(stream, &sb, "\n", &eof, scratch_pool)); 602 if (hit_eof) 603 *hit_eof = eof; 604 else 605 if (eof) 606 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Unexpected EOF")); 607 608 if (!eof) 609 { 610 err = svn_cstring_atoi64(result, sb->data); 611 if (err) 612 return svn_error_createf(SVN_ERR_FS_CORRUPT, err, 613 _("Number '%s' invalid or too large"), 614 sb->data); 615 } 616 617 return SVN_NO_ERROR; 618} 619 620svn_error_t * 621svn_fs_fs__move_into_place(const char *old_filename, 622 const char *new_filename, 623 const char *perms_reference, 624 svn_boolean_t flush_to_disk, 625 apr_pool_t *pool) 626{ 627 svn_error_t *err; 628 apr_file_t *file; 629 630 /* Copying permissions is a no-op on WIN32. */ 631 SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, pool)); 632 633 /* Move the file into place. */ 634 err = svn_io_file_rename2(old_filename, new_filename, flush_to_disk, pool); 635 if (err && APR_STATUS_IS_EXDEV(err->apr_err)) 636 { 637 /* Can't rename across devices; fall back to copying. */ 638 svn_error_clear(err); 639 SVN_ERR(svn_io_copy_file(old_filename, new_filename, TRUE, pool)); 640 641 /* Flush the target of the copy to disk. 642 ### The code below is duplicates svn_io_file_rename2(), because 643 currently we don't have the svn_io_copy_file2() function with 644 a flush_to_disk argument. */ 645 if (flush_to_disk) 646 { 647 SVN_ERR(svn_io_file_open(&file, new_filename, APR_WRITE, 648 APR_OS_DEFAULT, pool)); 649 SVN_ERR(svn_io_file_flush_to_disk(file, pool)); 650 SVN_ERR(svn_io_file_close(file, pool)); 651 } 652 653#ifdef SVN_ON_POSIX 654 if (flush_to_disk) 655 { 656 /* On POSIX, the file name is stored in the file's directory entry. 657 Hence, we need to fsync() that directory as well. 658 On other operating systems, we'd only be asking for trouble 659 by trying to open and fsync a directory. */ 660 const char *dirname; 661 662 dirname = svn_dirent_dirname(new_filename, pool); 663 SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT, 664 pool)); 665 SVN_ERR(svn_io_file_flush_to_disk(file, pool)); 666 SVN_ERR(svn_io_file_close(file, pool)); 667 } 668#endif 669 } 670 else if (err) 671 return svn_error_trace(err); 672 673 return SVN_NO_ERROR; 674} 675 676svn_boolean_t 677svn_fs_fs__use_log_addressing(svn_fs_t *fs) 678{ 679 fs_fs_data_t *ffd = fs->fsap_data; 680 return ffd->use_log_addressing; 681} 682