1/* util.c --- utility functions for FSX 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_x.h" 30#include "id.h" 31#include "util.h" 32 33#include "../libsvn_fs/fs-loader.h" 34 35#include "svn_private_config.h" 36 37/* Following are defines that specify the textual elements of the 38 native filesystem directories and revision files. */ 39 40/* Notes: 41 42To avoid opening and closing the rev-files all the time, it would 43probably be advantageous to keep each rev-file open for the 44lifetime of the transaction object. I'll leave that as a later 45optimization for now. 46 47I didn't keep track of pool lifetimes at all in this code. There 48are likely some errors because of that. 49 50*/ 51 52/* Pathname helper functions */ 53 54/* Return TRUE is REV is packed in FS, FALSE otherwise. */ 55svn_boolean_t 56svn_fs_x__is_packed_rev(svn_fs_t *fs, svn_revnum_t rev) 57{ 58 svn_fs_x__data_t *ffd = fs->fsap_data; 59 60 return (rev < ffd->min_unpacked_rev); 61} 62 63/* Return TRUE is REV is packed in FS, FALSE otherwise. */ 64svn_boolean_t 65svn_fs_x__is_packed_revprop(svn_fs_t *fs, svn_revnum_t rev) 66{ 67 svn_fs_x__data_t *ffd = fs->fsap_data; 68 69 /* rev 0 will not be packed */ 70 return (rev < ffd->min_unpacked_rev) && (rev != 0); 71} 72 73svn_revnum_t 74svn_fs_x__packed_base_rev(svn_fs_t *fs, svn_revnum_t rev) 75{ 76 svn_fs_x__data_t *ffd = fs->fsap_data; 77 78 return rev < ffd->min_unpacked_rev 79 ? rev - (rev % ffd->max_files_per_dir) 80 : rev; 81} 82 83svn_revnum_t 84svn_fs_x__pack_size(svn_fs_t *fs, svn_revnum_t rev) 85{ 86 svn_fs_x__data_t *ffd = fs->fsap_data; 87 88 return rev < ffd->min_unpacked_rev ? ffd->max_files_per_dir : 1; 89} 90 91const char * 92svn_fs_x__path_format(svn_fs_t *fs, 93 apr_pool_t *result_pool) 94{ 95 return svn_dirent_join(fs->path, PATH_FORMAT, result_pool); 96} 97 98const char * 99svn_fs_x__path_uuid(svn_fs_t *fs, 100 apr_pool_t *result_pool) 101{ 102 return svn_dirent_join(fs->path, PATH_UUID, result_pool); 103} 104 105const char * 106svn_fs_x__path_current(svn_fs_t *fs, 107 apr_pool_t *result_pool) 108{ 109 return svn_dirent_join(fs->path, PATH_CURRENT, result_pool); 110} 111 112const char * 113svn_fs_x__path_next(svn_fs_t *fs, 114 apr_pool_t *result_pool) 115{ 116 return svn_dirent_join(fs->path, PATH_NEXT, result_pool); 117} 118 119const char * 120svn_fs_x__path_txn_current(svn_fs_t *fs, 121 apr_pool_t *result_pool) 122{ 123 return svn_dirent_join(fs->path, PATH_TXN_CURRENT, result_pool); 124} 125 126const char * 127svn_fs_x__path_txn_current_lock(svn_fs_t *fs, 128 apr_pool_t *result_pool) 129{ 130 return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, result_pool); 131} 132 133const char * 134svn_fs_x__path_lock(svn_fs_t *fs, 135 apr_pool_t *result_pool) 136{ 137 return svn_dirent_join(fs->path, PATH_LOCK_FILE, result_pool); 138} 139 140const char * 141svn_fs_x__path_pack_lock(svn_fs_t *fs, 142 apr_pool_t *result_pool) 143{ 144 return svn_dirent_join(fs->path, PATH_PACK_LOCK_FILE, result_pool); 145} 146 147const char * 148svn_fs_x__path_revprop_generation(svn_fs_t *fs, 149 apr_pool_t *result_pool) 150{ 151 return svn_dirent_join(fs->path, PATH_REVPROP_GENERATION, result_pool); 152} 153 154/* Return the full path of the file FILENAME within revision REV's shard in 155 * FS. If FILENAME is NULL, return the shard directory directory itself. 156 * PACKED says whether we want the packed shard's name. 157 * 158 * Allocate the result in RESULT_POOL. 159 */static const char* 160construct_shard_sub_path(svn_fs_t *fs, 161 svn_revnum_t rev, 162 svn_boolean_t packed, 163 const char *filename, 164 apr_pool_t *result_pool) 165{ 166 svn_fs_x__data_t *ffd = fs->fsap_data; 167 char buffer[SVN_INT64_BUFFER_SIZE + sizeof(PATH_EXT_PACKED_SHARD)] = { 0 }; 168 169 /* String containing the shard number. */ 170 apr_size_t len = svn__i64toa(buffer, rev / ffd->max_files_per_dir); 171 172 /* Append the suffix. Limit it to the buffer size (should never hit it). */ 173 if (packed) 174 strncpy(buffer + len, PATH_EXT_PACKED_SHARD, sizeof(buffer) - len - 1); 175 176 /* This will also work for NULL FILENAME as well. */ 177 return svn_dirent_join_many(result_pool, fs->path, PATH_REVS_DIR, buffer, 178 filename, SVN_VA_NULL); 179} 180 181const char * 182svn_fs_x__path_rev_packed(svn_fs_t *fs, 183 svn_revnum_t rev, 184 const char *kind, 185 apr_pool_t *result_pool) 186{ 187 assert(svn_fs_x__is_packed_rev(fs, rev)); 188 return construct_shard_sub_path(fs, rev, TRUE, kind, result_pool); 189} 190 191const char * 192svn_fs_x__path_shard(svn_fs_t *fs, 193 svn_revnum_t rev, 194 apr_pool_t *result_pool) 195{ 196 return construct_shard_sub_path(fs, rev, FALSE, NULL, result_pool); 197} 198 199const char * 200svn_fs_x__path_rev(svn_fs_t *fs, 201 svn_revnum_t rev, 202 apr_pool_t *result_pool) 203{ 204 char buffer[SVN_INT64_BUFFER_SIZE + 1]; 205 buffer[0] = 'r'; 206 svn__i64toa(buffer + 1, rev); 207 208 assert(! svn_fs_x__is_packed_rev(fs, rev)); 209 return construct_shard_sub_path(fs, rev, FALSE, buffer, result_pool); 210} 211 212const char * 213svn_fs_x__path_rev_absolute(svn_fs_t *fs, 214 svn_revnum_t rev, 215 apr_pool_t *result_pool) 216{ 217 return svn_fs_x__is_packed_rev(fs, rev) 218 ? svn_fs_x__path_rev_packed(fs, rev, PATH_PACKED, result_pool) 219 : svn_fs_x__path_rev(fs, rev, result_pool); 220} 221 222const char * 223svn_fs_x__path_pack_shard(svn_fs_t *fs, 224 svn_revnum_t rev, 225 apr_pool_t *result_pool) 226{ 227 return construct_shard_sub_path(fs, rev, TRUE, NULL, result_pool); 228} 229 230const char * 231svn_fs_x__path_revprops(svn_fs_t *fs, 232 svn_revnum_t rev, 233 apr_pool_t *result_pool) 234{ 235 char buffer[SVN_INT64_BUFFER_SIZE + 1]; 236 buffer[0] = 'p'; 237 svn__i64toa(buffer + 1, rev); 238 239 assert(! svn_fs_x__is_packed_revprop(fs, rev)); 240 241 /* Revprops for packed r0 are not packed, yet stored in the packed shard. 242 Hence, the second flag must check for packed _rev_ - not revprop. */ 243 return construct_shard_sub_path(fs, rev, 244 svn_fs_x__is_packed_rev(fs, rev) /* sic! */, 245 buffer, result_pool); 246} 247 248const char * 249svn_fs_x__txn_name(svn_fs_x__txn_id_t txn_id, 250 apr_pool_t *result_pool) 251{ 252 char *p = apr_palloc(result_pool, SVN_INT64_BUFFER_SIZE); 253 svn__ui64tobase36(p, txn_id); 254 return p; 255} 256 257svn_error_t * 258svn_fs_x__txn_by_name(svn_fs_x__txn_id_t *txn_id, 259 const char *txn_name) 260{ 261 const char *next; 262 apr_uint64_t id = svn__base36toui64(&next, txn_name); 263 if (next == NULL || *next != 0 || *txn_name == 0) 264 return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, 265 "Malformed TXN name '%s'", txn_name); 266 267 *txn_id = id; 268 return SVN_NO_ERROR; 269} 270 271const char * 272svn_fs_x__path_txns_dir(svn_fs_t *fs, 273 apr_pool_t *result_pool) 274{ 275 return svn_dirent_join(fs->path, PATH_TXNS_DIR, result_pool); 276} 277 278/* Return the full path of the file FILENAME within transaction TXN_ID's 279 * transaction directory in FS. If FILENAME is NULL, return the transaction 280 * directory itself. 281 * 282 * Allocate the result in RESULT_POOL. 283 */ 284static const char * 285construct_txn_path(svn_fs_t *fs, 286 svn_fs_x__txn_id_t txn_id, 287 const char *filename, 288 apr_pool_t *result_pool) 289{ 290 /* Construct the transaction directory name without temp. allocations. */ 291 char buffer[SVN_INT64_BUFFER_SIZE + sizeof(PATH_EXT_TXN)] = { 0 }; 292 apr_size_t len = svn__ui64tobase36(buffer, txn_id); 293 strncpy(buffer + len, PATH_EXT_TXN, sizeof(buffer) - len - 1); 294 295 /* If FILENAME is NULL, it will terminate the list of segments 296 to concatenate. */ 297 return svn_dirent_join_many(result_pool, fs->path, PATH_TXNS_DIR, 298 buffer, filename, SVN_VA_NULL); 299} 300 301const char * 302svn_fs_x__path_txn_dir(svn_fs_t *fs, 303 svn_fs_x__txn_id_t txn_id, 304 apr_pool_t *result_pool) 305{ 306 return construct_txn_path(fs, txn_id, NULL, result_pool); 307} 308 309/* Return the name of the sha1->rep mapping file in transaction TXN_ID 310 * within FS for the given SHA1 checksum. Use POOL for allocations. 311 */ 312const char * 313svn_fs_x__path_txn_sha1(svn_fs_t *fs, 314 svn_fs_x__txn_id_t txn_id, 315 const unsigned char *sha1, 316 apr_pool_t *pool) 317{ 318 svn_checksum_t checksum; 319 checksum.digest = sha1; 320 checksum.kind = svn_checksum_sha1; 321 322 return svn_dirent_join(svn_fs_x__path_txn_dir(fs, txn_id, pool), 323 svn_checksum_to_cstring(&checksum, pool), 324 pool); 325} 326 327const char * 328svn_fs_x__path_txn_changes(svn_fs_t *fs, 329 svn_fs_x__txn_id_t txn_id, 330 apr_pool_t *result_pool) 331{ 332 return construct_txn_path(fs, txn_id, PATH_CHANGES, result_pool); 333} 334 335const char * 336svn_fs_x__path_txn_props(svn_fs_t *fs, 337 svn_fs_x__txn_id_t txn_id, 338 apr_pool_t *result_pool) 339{ 340 return construct_txn_path(fs, txn_id, PATH_TXN_PROPS, result_pool); 341} 342 343const char* 344svn_fs_x__path_l2p_proto_index(svn_fs_t *fs, 345 svn_fs_x__txn_id_t txn_id, 346 apr_pool_t *result_pool) 347{ 348 return construct_txn_path(fs, txn_id, PATH_INDEX PATH_EXT_L2P_INDEX, 349 result_pool); 350} 351 352const char* 353svn_fs_x__path_p2l_proto_index(svn_fs_t *fs, 354 svn_fs_x__txn_id_t txn_id, 355 apr_pool_t *result_pool) 356{ 357 return construct_txn_path(fs, txn_id, PATH_INDEX PATH_EXT_P2L_INDEX, 358 result_pool); 359} 360 361const char * 362svn_fs_x__path_txn_next_ids(svn_fs_t *fs, 363 svn_fs_x__txn_id_t txn_id, 364 apr_pool_t *result_pool) 365{ 366 return construct_txn_path(fs, txn_id, PATH_NEXT_IDS, result_pool); 367} 368 369const char * 370svn_fs_x__path_min_unpacked_rev(svn_fs_t *fs, 371 apr_pool_t *result_pool) 372{ 373 return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REV, result_pool); 374} 375 376const char * 377svn_fs_x__path_txn_proto_revs(svn_fs_t *fs, 378 apr_pool_t *result_pool) 379{ 380 return svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, result_pool); 381} 382 383const char * 384svn_fs_x__path_txn_item_index(svn_fs_t *fs, 385 svn_fs_x__txn_id_t txn_id, 386 apr_pool_t *result_pool) 387{ 388 return construct_txn_path(fs, txn_id, PATH_TXN_ITEM_INDEX, result_pool); 389} 390 391/* Return the full path of the proto-rev file / lock file for transaction 392 * TXN_ID in FS. The SUFFIX determines what file (rev / lock) it will be. 393 * 394 * Allocate the result in RESULT_POOL. 395 */ 396static const char * 397construct_proto_rev_path(svn_fs_t *fs, 398 svn_fs_x__txn_id_t txn_id, 399 const char *suffix, 400 apr_pool_t *result_pool) 401{ 402 /* Construct the file name without temp. allocations. */ 403 char buffer[SVN_INT64_BUFFER_SIZE + sizeof(PATH_EXT_REV_LOCK)] = { 0 }; 404 apr_size_t len = svn__ui64tobase36(buffer, txn_id); 405 strncpy(buffer + len, suffix, sizeof(buffer) - len - 1); 406 407 /* If FILENAME is NULL, it will terminate the list of segments 408 to concatenate. */ 409 return svn_dirent_join_many(result_pool, fs->path, PATH_TXN_PROTOS_DIR, 410 buffer, SVN_VA_NULL); 411} 412 413const char * 414svn_fs_x__path_txn_proto_rev(svn_fs_t *fs, 415 svn_fs_x__txn_id_t txn_id, 416 apr_pool_t *result_pool) 417{ 418 return construct_proto_rev_path(fs, txn_id, PATH_EXT_REV, result_pool); 419} 420 421const char * 422svn_fs_x__path_txn_proto_rev_lock(svn_fs_t *fs, 423 svn_fs_x__txn_id_t txn_id, 424 apr_pool_t *result_pool) 425{ 426 return construct_proto_rev_path(fs, txn_id, PATH_EXT_REV_LOCK, result_pool); 427} 428 429/* Return the full path of the noderev-related file with the extension SUFFIX 430 * for noderev *ID in transaction TXN_ID in FS. 431 * 432 * Allocate the result in RESULT_POOL and temporaries in SCRATCH_POOL. 433 */ 434static const char * 435construct_txn_node_path(svn_fs_t *fs, 436 const svn_fs_x__id_t *id, 437 const char *suffix, 438 apr_pool_t *result_pool, 439 apr_pool_t *scratch_pool) 440{ 441 const char *filename = svn_fs_x__id_unparse(id, result_pool)->data; 442 apr_int64_t txn_id = svn_fs_x__get_txn_id(id->change_set); 443 444 return svn_dirent_join(svn_fs_x__path_txn_dir(fs, txn_id, scratch_pool), 445 apr_psprintf(scratch_pool, PATH_PREFIX_NODE "%s%s", 446 filename, suffix), 447 result_pool); 448} 449 450const char * 451svn_fs_x__path_txn_node_rev(svn_fs_t *fs, 452 const svn_fs_x__id_t *id, 453 apr_pool_t *result_pool, 454 apr_pool_t *scratch_pool) 455{ 456 return construct_txn_node_path(fs, id, "", result_pool, scratch_pool); 457} 458 459const char * 460svn_fs_x__path_txn_node_props(svn_fs_t *fs, 461 const svn_fs_x__id_t *id, 462 apr_pool_t *result_pool, 463 apr_pool_t *scratch_pool) 464{ 465 return construct_txn_node_path(fs, id, PATH_EXT_PROPS, result_pool, 466 scratch_pool); 467} 468 469const char * 470svn_fs_x__path_txn_node_children(svn_fs_t *fs, 471 const svn_fs_x__id_t *id, 472 apr_pool_t *result_pool, 473 apr_pool_t *scratch_pool) 474{ 475 return construct_txn_node_path(fs, id, PATH_EXT_CHILDREN, result_pool, 476 scratch_pool); 477} 478 479svn_error_t * 480svn_fs_x__check_file_buffer_numeric(const char *buf, 481 apr_off_t offset, 482 const char *path, 483 const char *title, 484 apr_pool_t *scratch_pool) 485{ 486 const char *p; 487 488 for (p = buf + offset; *p; p++) 489 if (!svn_ctype_isdigit(*p)) 490 return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL, 491 _("%s file '%s' contains unexpected non-digit '%c' within '%s'"), 492 title, svn_dirent_local_style(path, scratch_pool), *p, buf); 493 494 return SVN_NO_ERROR; 495} 496 497svn_error_t * 498svn_fs_x__read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev, 499 svn_fs_t *fs, 500 apr_pool_t *scratch_pool) 501{ 502 char buf[80]; 503 apr_file_t *file; 504 apr_size_t len; 505 506 SVN_ERR(svn_io_file_open(&file, 507 svn_fs_x__path_min_unpacked_rev(fs, scratch_pool), 508 APR_READ | APR_BUFFERED, 509 APR_OS_DEFAULT, 510 scratch_pool)); 511 len = sizeof(buf); 512 SVN_ERR(svn_io_read_length_line(file, buf, &len, scratch_pool)); 513 SVN_ERR(svn_io_file_close(file, scratch_pool)); 514 515 SVN_ERR(svn_revnum_parse(min_unpacked_rev, buf, NULL)); 516 return SVN_NO_ERROR; 517} 518 519svn_error_t * 520svn_fs_x__update_min_unpacked_rev(svn_fs_t *fs, 521 apr_pool_t *scratch_pool) 522{ 523 svn_fs_x__data_t *ffd = fs->fsap_data; 524 return svn_fs_x__read_min_unpacked_rev(&ffd->min_unpacked_rev, fs, 525 scratch_pool); 526} 527 528/* Write a file FILENAME in directory FS_PATH, containing a single line 529 * with the number REVNUM in ASCII decimal. Move the file into place 530 * atomically, overwriting any existing file. 531 * 532 * Similar to write_current(). */ 533svn_error_t * 534svn_fs_x__write_min_unpacked_rev(svn_fs_t *fs, 535 svn_revnum_t revnum, 536 apr_pool_t *scratch_pool) 537{ 538 svn_fs_x__data_t *ffd = fs->fsap_data; 539 const char *final_path; 540 char buf[SVN_INT64_BUFFER_SIZE]; 541 apr_size_t len = svn__i64toa(buf, revnum); 542 buf[len] = '\n'; 543 544 final_path = svn_fs_x__path_min_unpacked_rev(fs, scratch_pool); 545 546 SVN_ERR(svn_io_write_atomic2(final_path, buf, len + 1, 547 final_path /* copy_perms */, 548 ffd->flush_to_disk, scratch_pool)); 549 550 return SVN_NO_ERROR; 551} 552 553svn_error_t * 554svn_fs_x__read_current(svn_revnum_t *rev, 555 svn_fs_t *fs, 556 apr_pool_t *scratch_pool) 557{ 558 const char *str; 559 svn_stringbuf_t *content; 560 SVN_ERR(svn_fs_x__read_content(&content, 561 svn_fs_x__path_current(fs, scratch_pool), 562 scratch_pool)); 563 SVN_ERR(svn_revnum_parse(rev, content->data, &str)); 564 if (*str != '\n') 565 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 566 _("Corrupt 'current' file")); 567 568 return SVN_NO_ERROR; 569} 570 571/* Atomically update the 'current' file to hold the specified REV. 572 Perform temporary allocations in SCRATCH_POOL. */ 573svn_error_t * 574svn_fs_x__write_current(svn_fs_t *fs, 575 svn_revnum_t rev, 576 apr_pool_t *scratch_pool) 577{ 578 char *buf; 579 const char *tmp_name, *name; 580 apr_file_t *file; 581 582 /* Now we can just write out this line. */ 583 buf = apr_psprintf(scratch_pool, "%ld\n", rev); 584 585 name = svn_fs_x__path_current(fs, scratch_pool); 586 tmp_name = svn_fs_x__path_next(fs, scratch_pool); 587 588 SVN_ERR(svn_io_file_open(&file, tmp_name, 589 APR_WRITE | APR_CREATE | APR_BUFFERED, 590 APR_OS_DEFAULT, scratch_pool)); 591 SVN_ERR(svn_io_file_write_full(file, buf, strlen(buf), NULL, 592 scratch_pool)); 593 SVN_ERR(svn_io_file_close(file, scratch_pool)); 594 595 /* Copying permissions is a no-op on WIN32. */ 596 SVN_ERR(svn_io_copy_perms(name, tmp_name, scratch_pool)); 597 598 /* Move the file into place. */ 599 SVN_ERR(svn_io_file_rename2(tmp_name, name, TRUE, scratch_pool)); 600 601 return SVN_NO_ERROR; 602} 603 604 605svn_error_t * 606svn_fs_x__try_stringbuf_from_file(svn_stringbuf_t **content, 607 svn_boolean_t *missing, 608 const char *path, 609 svn_boolean_t last_attempt, 610 apr_pool_t *result_pool) 611{ 612 svn_error_t *err = svn_stringbuf_from_file2(content, path, result_pool); 613 if (missing) 614 *missing = FALSE; 615 616 if (err) 617 { 618 *content = NULL; 619 620 if (APR_STATUS_IS_ENOENT(err->apr_err)) 621 { 622 if (!last_attempt) 623 { 624 svn_error_clear(err); 625 if (missing) 626 *missing = TRUE; 627 return SVN_NO_ERROR; 628 } 629 } 630#ifdef ESTALE 631 else if (APR_TO_OS_ERROR(err->apr_err) == ESTALE 632 || APR_TO_OS_ERROR(err->apr_err) == EIO) 633 { 634 if (!last_attempt) 635 { 636 svn_error_clear(err); 637 return SVN_NO_ERROR; 638 } 639 } 640#endif 641 } 642 643 return svn_error_trace(err); 644} 645 646/* Fetch the current offset of FILE into *OFFSET_P. */ 647svn_error_t * 648svn_fs_x__read_content(svn_stringbuf_t **content, 649 const char *fname, 650 apr_pool_t *result_pool) 651{ 652 int i; 653 *content = NULL; 654 655 for (i = 0; !*content && (i < SVN_FS_X__RECOVERABLE_RETRY_COUNT); ++i) 656 SVN_ERR(svn_fs_x__try_stringbuf_from_file(content, NULL, 657 fname, i + 1 < SVN_FS_X__RECOVERABLE_RETRY_COUNT, 658 result_pool)); 659 660 if (!*content) 661 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 662 _("Can't read '%s'"), 663 svn_dirent_local_style(fname, result_pool)); 664 665 return SVN_NO_ERROR; 666} 667 668/* Reads a line from STREAM and converts it to a 64 bit integer to be 669 * returned in *RESULT. If we encounter eof, set *HIT_EOF and leave 670 * *RESULT unchanged. If HIT_EOF is NULL, EOF causes an "corrupt FS" 671 * error return. 672 * SCRATCH_POOL is used for temporary allocations. 673 */ 674svn_error_t * 675svn_fs_x__read_number_from_stream(apr_int64_t *result, 676 svn_boolean_t *hit_eof, 677 svn_stream_t *stream, 678 apr_pool_t *scratch_pool) 679{ 680 svn_stringbuf_t *sb; 681 svn_boolean_t eof; 682 svn_error_t *err; 683 684 SVN_ERR(svn_stream_readline(stream, &sb, "\n", &eof, scratch_pool)); 685 if (hit_eof) 686 *hit_eof = eof; 687 else 688 if (eof) 689 return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Unexpected EOF")); 690 691 if (!eof) 692 { 693 err = svn_cstring_atoi64(result, sb->data); 694 if (err) 695 return svn_error_createf(SVN_ERR_FS_CORRUPT, err, 696 _("Number '%s' invalid or too large"), 697 sb->data); 698 } 699 700 return SVN_NO_ERROR; 701} 702 703svn_error_t * 704svn_fs_x__move_into_place(const char *old_filename, 705 const char *new_filename, 706 const char *perms_reference, 707 svn_fs_x__batch_fsync_t *batch, 708 apr_pool_t *scratch_pool) 709{ 710 /* Copying permissions is a no-op on WIN32. */ 711 SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, scratch_pool)); 712 713 /* We use specific 'fsyncing move' Win32 API calls on Windows while the 714 * directory update fsync is POSIX-only. Moreover, there tend to be only 715 * a few moved files (1 or 2) per batch. 716 * 717 * Therefore, we use the platform-optimized "immediate" fsyncs on all 718 * non-POSIX platforms and the "scheduled" fsyncs on POSIX only. 719 */ 720#if defined(SVN_ON_POSIX) 721 /* Move the file into place. */ 722 SVN_ERR(svn_io_file_rename2(old_filename, new_filename, FALSE, 723 scratch_pool)); 724 725 /* Schedule for synchronization. */ 726 SVN_ERR(svn_fs_x__batch_fsync_new_path(batch, new_filename, scratch_pool)); 727#else 728 SVN_ERR(svn_io_file_rename2(old_filename, new_filename, TRUE, 729 scratch_pool)); 730#endif 731 732 return SVN_NO_ERROR; 733} 734