1289177Speter/* util.c --- utility functions for FSX repo access 2289177Speter * 3289177Speter * ==================================================================== 4289177Speter * Licensed to the Apache Software Foundation (ASF) under one 5289177Speter * or more contributor license agreements. See the NOTICE file 6289177Speter * distributed with this work for additional information 7289177Speter * regarding copyright ownership. The ASF licenses this file 8289177Speter * to you under the Apache License, Version 2.0 (the 9289177Speter * "License"); you may not use this file except in compliance 10289177Speter * with the License. You may obtain a copy of the License at 11289177Speter * 12289177Speter * http://www.apache.org/licenses/LICENSE-2.0 13289177Speter * 14289177Speter * Unless required by applicable law or agreed to in writing, 15289177Speter * software distributed under the License is distributed on an 16289177Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17289177Speter * KIND, either express or implied. See the License for the 18289177Speter * specific language governing permissions and limitations 19289177Speter * under the License. 20289177Speter * ==================================================================== 21289177Speter */ 22289177Speter 23289177Speter#include <assert.h> 24289177Speter 25289177Speter#include "svn_ctype.h" 26289177Speter#include "svn_dirent_uri.h" 27289177Speter#include "private/svn_string_private.h" 28289177Speter 29289177Speter#include "fs_x.h" 30289177Speter#include "id.h" 31289177Speter#include "util.h" 32289177Speter 33289177Speter#include "../libsvn_fs/fs-loader.h" 34289177Speter 35289177Speter#include "svn_private_config.h" 36289177Speter 37289177Speter/* Following are defines that specify the textual elements of the 38289177Speter native filesystem directories and revision files. */ 39289177Speter 40289177Speter/* Notes: 41289177Speter 42289177SpeterTo avoid opening and closing the rev-files all the time, it would 43289177Speterprobably be advantageous to keep each rev-file open for the 44289177Speterlifetime of the transaction object. I'll leave that as a later 45289177Speteroptimization for now. 46289177Speter 47289177SpeterI didn't keep track of pool lifetimes at all in this code. There 48289177Speterare likely some errors because of that. 49289177Speter 50289177Speter*/ 51289177Speter 52289177Speter/* Pathname helper functions */ 53289177Speter 54289177Speter/* Return TRUE is REV is packed in FS, FALSE otherwise. */ 55289177Spetersvn_boolean_t 56289177Spetersvn_fs_x__is_packed_rev(svn_fs_t *fs, svn_revnum_t rev) 57289177Speter{ 58289177Speter svn_fs_x__data_t *ffd = fs->fsap_data; 59289177Speter 60289177Speter return (rev < ffd->min_unpacked_rev); 61289177Speter} 62289177Speter 63289177Speter/* Return TRUE is REV is packed in FS, FALSE otherwise. */ 64289177Spetersvn_boolean_t 65289177Spetersvn_fs_x__is_packed_revprop(svn_fs_t *fs, svn_revnum_t rev) 66289177Speter{ 67289177Speter svn_fs_x__data_t *ffd = fs->fsap_data; 68289177Speter 69289177Speter /* rev 0 will not be packed */ 70289177Speter return (rev < ffd->min_unpacked_rev) && (rev != 0); 71289177Speter} 72289177Speter 73289177Spetersvn_revnum_t 74289177Spetersvn_fs_x__packed_base_rev(svn_fs_t *fs, svn_revnum_t rev) 75289177Speter{ 76289177Speter svn_fs_x__data_t *ffd = fs->fsap_data; 77289177Speter 78289177Speter return rev < ffd->min_unpacked_rev 79289177Speter ? rev - (rev % ffd->max_files_per_dir) 80289177Speter : rev; 81289177Speter} 82289177Speter 83289177Spetersvn_revnum_t 84289177Spetersvn_fs_x__pack_size(svn_fs_t *fs, svn_revnum_t rev) 85289177Speter{ 86289177Speter svn_fs_x__data_t *ffd = fs->fsap_data; 87289177Speter 88289177Speter return rev < ffd->min_unpacked_rev ? ffd->max_files_per_dir : 1; 89289177Speter} 90289177Speter 91289177Speterconst char * 92289177Spetersvn_fs_x__path_format(svn_fs_t *fs, 93289177Speter apr_pool_t *result_pool) 94289177Speter{ 95289177Speter return svn_dirent_join(fs->path, PATH_FORMAT, result_pool); 96289177Speter} 97289177Speter 98289177Speterconst char * 99289177Spetersvn_fs_x__path_uuid(svn_fs_t *fs, 100289177Speter apr_pool_t *result_pool) 101289177Speter{ 102289177Speter return svn_dirent_join(fs->path, PATH_UUID, result_pool); 103289177Speter} 104289177Speter 105289177Speterconst char * 106289177Spetersvn_fs_x__path_current(svn_fs_t *fs, 107289177Speter apr_pool_t *result_pool) 108289177Speter{ 109289177Speter return svn_dirent_join(fs->path, PATH_CURRENT, result_pool); 110289177Speter} 111289177Speter 112289177Speterconst char * 113289177Spetersvn_fs_x__path_txn_current(svn_fs_t *fs, 114289177Speter apr_pool_t *result_pool) 115289177Speter{ 116289177Speter return svn_dirent_join(fs->path, PATH_TXN_CURRENT, 117289177Speter result_pool); 118289177Speter} 119289177Speter 120289177Speterconst char * 121289177Spetersvn_fs_x__path_txn_current_lock(svn_fs_t *fs, 122289177Speter apr_pool_t *result_pool) 123289177Speter{ 124289177Speter return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, result_pool); 125289177Speter} 126289177Speter 127289177Speterconst char * 128289177Spetersvn_fs_x__path_lock(svn_fs_t *fs, 129289177Speter apr_pool_t *result_pool) 130289177Speter{ 131289177Speter return svn_dirent_join(fs->path, PATH_LOCK_FILE, result_pool); 132289177Speter} 133289177Speter 134289177Speterconst char * 135289177Spetersvn_fs_x__path_pack_lock(svn_fs_t *fs, 136289177Speter apr_pool_t *result_pool) 137289177Speter{ 138289177Speter return svn_dirent_join(fs->path, PATH_PACK_LOCK_FILE, result_pool); 139289177Speter} 140289177Speter 141289177Speterconst char * 142289177Spetersvn_fs_x__path_revprop_generation(svn_fs_t *fs, 143289177Speter apr_pool_t *result_pool) 144289177Speter{ 145289177Speter return svn_dirent_join(fs->path, PATH_REVPROP_GENERATION, result_pool); 146289177Speter} 147289177Speter 148289177Speter/* Return the full path of the file FILENAME within revision REV's shard in 149289177Speter * FS. If FILENAME is NULL, return the shard directory directory itself. 150289177Speter * REVPROPS indicates the parent of the shard parent folder ("revprops" or 151289177Speter * "revs"). PACKED says whether we want the packed shard's name. 152289177Speter * 153289177Speter * Allocate the result in RESULT_POOL. 154289177Speter */static const char* 155289177Speterconstruct_shard_sub_path(svn_fs_t *fs, 156289177Speter svn_revnum_t rev, 157289177Speter svn_boolean_t revprops, 158289177Speter svn_boolean_t packed, 159289177Speter const char *filename, 160289177Speter apr_pool_t *result_pool) 161289177Speter{ 162289177Speter svn_fs_x__data_t *ffd = fs->fsap_data; 163289177Speter char buffer[SVN_INT64_BUFFER_SIZE + sizeof(PATH_EXT_PACKED_SHARD)] = { 0 }; 164289177Speter 165289177Speter /* Select the appropriate parent path constant. */ 166289177Speter const char *parent = revprops ? PATH_REVPROPS_DIR : PATH_REVS_DIR; 167289177Speter 168289177Speter /* String containing the shard number. */ 169289177Speter apr_size_t len = svn__i64toa(buffer, rev / ffd->max_files_per_dir); 170289177Speter 171289177Speter /* Append the suffix. Limit it to the buffer size (should never hit it). */ 172289177Speter if (packed) 173289177Speter strncpy(buffer + len, PATH_EXT_PACKED_SHARD, sizeof(buffer) - len - 1); 174289177Speter 175289177Speter /* This will also work for NULL FILENAME as well. */ 176289177Speter return svn_dirent_join_many(result_pool, fs->path, parent, buffer, 177289177Speter filename, SVN_VA_NULL); 178289177Speter} 179289177Speter 180289177Speterconst char * 181289177Spetersvn_fs_x__path_rev_packed(svn_fs_t *fs, 182289177Speter svn_revnum_t rev, 183289177Speter const char *kind, 184289177Speter apr_pool_t *result_pool) 185289177Speter{ 186289177Speter assert(svn_fs_x__is_packed_rev(fs, rev)); 187289177Speter return construct_shard_sub_path(fs, rev, FALSE, TRUE, kind, result_pool); 188289177Speter} 189289177Speter 190289177Speterconst char * 191289177Spetersvn_fs_x__path_rev_shard(svn_fs_t *fs, 192289177Speter svn_revnum_t rev, 193289177Speter apr_pool_t *result_pool) 194289177Speter{ 195289177Speter return construct_shard_sub_path(fs, rev, FALSE, FALSE, NULL, result_pool); 196289177Speter} 197289177Speter 198289177Speterconst char * 199289177Spetersvn_fs_x__path_rev(svn_fs_t *fs, 200289177Speter svn_revnum_t rev, 201289177Speter apr_pool_t *result_pool) 202289177Speter{ 203289177Speter char buffer[SVN_INT64_BUFFER_SIZE]; 204289177Speter svn__i64toa(buffer, rev); 205289177Speter 206289177Speter assert(! svn_fs_x__is_packed_rev(fs, rev)); 207289177Speter return construct_shard_sub_path(fs, rev, FALSE, FALSE, buffer, result_pool); 208289177Speter} 209289177Speter 210289177Speterconst char * 211289177Spetersvn_fs_x__path_rev_absolute(svn_fs_t *fs, 212289177Speter svn_revnum_t rev, 213289177Speter apr_pool_t *result_pool) 214289177Speter{ 215289177Speter return svn_fs_x__is_packed_rev(fs, rev) 216289177Speter ? svn_fs_x__path_rev_packed(fs, rev, PATH_PACKED, result_pool) 217289177Speter : svn_fs_x__path_rev(fs, rev, result_pool); 218289177Speter} 219289177Speter 220289177Speterconst char * 221289177Spetersvn_fs_x__path_revprops_shard(svn_fs_t *fs, 222289177Speter svn_revnum_t rev, 223289177Speter apr_pool_t *result_pool) 224289177Speter{ 225289177Speter return construct_shard_sub_path(fs, rev, TRUE, FALSE, NULL, result_pool); 226289177Speter} 227289177Speter 228289177Speterconst char * 229289177Spetersvn_fs_x__path_revprops_pack_shard(svn_fs_t *fs, 230289177Speter svn_revnum_t rev, 231289177Speter apr_pool_t *result_pool) 232289177Speter{ 233289177Speter return construct_shard_sub_path(fs, rev, TRUE, TRUE, NULL, result_pool); 234289177Speter} 235289177Speter 236289177Speterconst char * 237289177Spetersvn_fs_x__path_revprops(svn_fs_t *fs, 238289177Speter svn_revnum_t rev, 239289177Speter apr_pool_t *result_pool) 240289177Speter{ 241289177Speter char buffer[SVN_INT64_BUFFER_SIZE]; 242289177Speter svn__i64toa(buffer, rev); 243289177Speter 244289177Speter assert(! svn_fs_x__is_packed_revprop(fs, rev)); 245289177Speter return construct_shard_sub_path(fs, rev, TRUE, FALSE, buffer, result_pool); 246289177Speter} 247289177Speter 248289177Speterconst char * 249289177Spetersvn_fs_x__txn_name(svn_fs_x__txn_id_t txn_id, 250289177Speter apr_pool_t *result_pool) 251289177Speter{ 252289177Speter char *p = apr_palloc(result_pool, SVN_INT64_BUFFER_SIZE); 253289177Speter svn__ui64tobase36(p, txn_id); 254289177Speter return p; 255289177Speter} 256289177Speter 257289177Spetersvn_error_t * 258289177Spetersvn_fs_x__txn_by_name(svn_fs_x__txn_id_t *txn_id, 259289177Speter const char *txn_name) 260289177Speter{ 261289177Speter const char *next; 262289177Speter apr_uint64_t id = svn__base36toui64(&next, txn_name); 263289177Speter if (next == NULL || *next != 0 || *txn_name == 0) 264289177Speter return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, 265289177Speter "Malformed TXN name '%s'", txn_name); 266289177Speter 267289177Speter *txn_id = id; 268289177Speter return SVN_NO_ERROR; 269289177Speter} 270289177Speter 271289177Speterconst char * 272289177Spetersvn_fs_x__path_txns_dir(svn_fs_t *fs, 273289177Speter apr_pool_t *result_pool) 274289177Speter{ 275289177Speter return svn_dirent_join(fs->path, PATH_TXNS_DIR, result_pool); 276289177Speter} 277289177Speter 278289177Speter/* Return the full path of the file FILENAME within transaction TXN_ID's 279289177Speter * transaction directory in FS. If FILENAME is NULL, return the transaction 280289177Speter * directory itself. 281289177Speter * 282289177Speter * Allocate the result in RESULT_POOL. 283289177Speter */ 284289177Speterstatic const char * 285289177Speterconstruct_txn_path(svn_fs_t *fs, 286289177Speter svn_fs_x__txn_id_t txn_id, 287289177Speter const char *filename, 288289177Speter apr_pool_t *result_pool) 289289177Speter{ 290289177Speter /* Construct the transaction directory name without temp. allocations. */ 291289177Speter char buffer[SVN_INT64_BUFFER_SIZE + sizeof(PATH_EXT_TXN)] = { 0 }; 292289177Speter apr_size_t len = svn__ui64tobase36(buffer, txn_id); 293289177Speter strncpy(buffer + len, PATH_EXT_TXN, sizeof(buffer) - len - 1); 294289177Speter 295289177Speter /* If FILENAME is NULL, it will terminate the list of segments 296289177Speter to concatenate. */ 297289177Speter return svn_dirent_join_many(result_pool, fs->path, PATH_TXNS_DIR, 298289177Speter buffer, filename, SVN_VA_NULL); 299289177Speter} 300289177Speter 301289177Speterconst char * 302289177Spetersvn_fs_x__path_txn_dir(svn_fs_t *fs, 303289177Speter svn_fs_x__txn_id_t txn_id, 304289177Speter apr_pool_t *result_pool) 305289177Speter{ 306289177Speter return construct_txn_path(fs, txn_id, NULL, result_pool); 307289177Speter} 308289177Speter 309289177Speter/* Return the name of the sha1->rep mapping file in transaction TXN_ID 310289177Speter * within FS for the given SHA1 checksum. Use POOL for allocations. 311289177Speter */ 312289177Speterconst char * 313289177Spetersvn_fs_x__path_txn_sha1(svn_fs_t *fs, 314289177Speter svn_fs_x__txn_id_t txn_id, 315289177Speter const unsigned char *sha1, 316289177Speter apr_pool_t *pool) 317289177Speter{ 318289177Speter svn_checksum_t checksum; 319289177Speter checksum.digest = sha1; 320289177Speter checksum.kind = svn_checksum_sha1; 321289177Speter 322289177Speter return svn_dirent_join(svn_fs_x__path_txn_dir(fs, txn_id, pool), 323289177Speter svn_checksum_to_cstring(&checksum, pool), 324289177Speter pool); 325289177Speter} 326289177Speter 327289177Speterconst char * 328289177Spetersvn_fs_x__path_txn_changes(svn_fs_t *fs, 329289177Speter svn_fs_x__txn_id_t txn_id, 330289177Speter apr_pool_t *result_pool) 331289177Speter{ 332289177Speter return construct_txn_path(fs, txn_id, PATH_CHANGES, result_pool); 333289177Speter} 334289177Speter 335289177Speterconst char * 336289177Spetersvn_fs_x__path_txn_props(svn_fs_t *fs, 337289177Speter svn_fs_x__txn_id_t txn_id, 338289177Speter apr_pool_t *result_pool) 339289177Speter{ 340289177Speter return construct_txn_path(fs, txn_id, PATH_TXN_PROPS, result_pool); 341289177Speter} 342289177Speter 343289177Speterconst char * 344289177Spetersvn_fs_x__path_txn_props_final(svn_fs_t *fs, 345289177Speter svn_fs_x__txn_id_t txn_id, 346289177Speter apr_pool_t *result_pool) 347289177Speter{ 348289177Speter return construct_txn_path(fs, txn_id, PATH_TXN_PROPS_FINAL, result_pool); 349289177Speter} 350289177Speter 351289177Speterconst char* 352289177Spetersvn_fs_x__path_l2p_proto_index(svn_fs_t *fs, 353289177Speter svn_fs_x__txn_id_t txn_id, 354289177Speter apr_pool_t *result_pool) 355289177Speter{ 356289177Speter return construct_txn_path(fs, txn_id, PATH_INDEX PATH_EXT_L2P_INDEX, 357289177Speter result_pool); 358289177Speter} 359289177Speter 360289177Speterconst char* 361289177Spetersvn_fs_x__path_p2l_proto_index(svn_fs_t *fs, 362289177Speter svn_fs_x__txn_id_t txn_id, 363289177Speter apr_pool_t *result_pool) 364289177Speter{ 365289177Speter return construct_txn_path(fs, txn_id, PATH_INDEX PATH_EXT_P2L_INDEX, 366289177Speter result_pool); 367289177Speter} 368289177Speter 369289177Speterconst char * 370289177Spetersvn_fs_x__path_txn_next_ids(svn_fs_t *fs, 371289177Speter svn_fs_x__txn_id_t txn_id, 372289177Speter apr_pool_t *result_pool) 373289177Speter{ 374289177Speter return construct_txn_path(fs, txn_id, PATH_NEXT_IDS, result_pool); 375289177Speter} 376289177Speter 377289177Speterconst char * 378289177Spetersvn_fs_x__path_min_unpacked_rev(svn_fs_t *fs, 379289177Speter apr_pool_t *result_pool) 380289177Speter{ 381289177Speter return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REV, result_pool); 382289177Speter} 383289177Speter 384289177Speterconst char * 385289177Spetersvn_fs_x__path_txn_proto_revs(svn_fs_t *fs, 386289177Speter apr_pool_t *result_pool) 387289177Speter{ 388289177Speter return svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, result_pool); 389289177Speter} 390289177Speter 391289177Speterconst char * 392289177Spetersvn_fs_x__path_txn_item_index(svn_fs_t *fs, 393289177Speter svn_fs_x__txn_id_t txn_id, 394289177Speter apr_pool_t *result_pool) 395289177Speter{ 396289177Speter return construct_txn_path(fs, txn_id, PATH_TXN_ITEM_INDEX, result_pool); 397289177Speter} 398289177Speter 399289177Speter/* Return the full path of the proto-rev file / lock file for transaction 400289177Speter * TXN_ID in FS. The SUFFIX determines what file (rev / lock) it will be. 401289177Speter * 402289177Speter * Allocate the result in RESULT_POOL. 403289177Speter */ 404289177Speterstatic const char * 405289177Speterconstruct_proto_rev_path(svn_fs_t *fs, 406289177Speter svn_fs_x__txn_id_t txn_id, 407289177Speter const char *suffix, 408289177Speter apr_pool_t *result_pool) 409289177Speter{ 410289177Speter /* Construct the file name without temp. allocations. */ 411289177Speter char buffer[SVN_INT64_BUFFER_SIZE + sizeof(PATH_EXT_REV_LOCK)] = { 0 }; 412289177Speter apr_size_t len = svn__ui64tobase36(buffer, txn_id); 413289177Speter strncpy(buffer + len, suffix, sizeof(buffer) - len - 1); 414289177Speter 415289177Speter /* If FILENAME is NULL, it will terminate the list of segments 416289177Speter to concatenate. */ 417289177Speter return svn_dirent_join_many(result_pool, fs->path, PATH_TXN_PROTOS_DIR, 418289177Speter buffer, SVN_VA_NULL); 419289177Speter} 420289177Speter 421289177Speterconst char * 422289177Spetersvn_fs_x__path_txn_proto_rev(svn_fs_t *fs, 423289177Speter svn_fs_x__txn_id_t txn_id, 424289177Speter apr_pool_t *result_pool) 425289177Speter{ 426289177Speter return construct_proto_rev_path(fs, txn_id, PATH_EXT_REV, result_pool); 427289177Speter} 428289177Speter 429289177Speterconst char * 430289177Spetersvn_fs_x__path_txn_proto_rev_lock(svn_fs_t *fs, 431289177Speter svn_fs_x__txn_id_t txn_id, 432289177Speter apr_pool_t *result_pool) 433289177Speter{ 434289177Speter return construct_proto_rev_path(fs, txn_id, PATH_EXT_REV_LOCK, result_pool); 435289177Speter} 436289177Speter 437289177Speter/* Return the full path of the noderev-related file with the extension SUFFIX 438289177Speter * for noderev *ID in transaction TXN_ID in FS. 439289177Speter * 440289177Speter * Allocate the result in RESULT_POOL and temporaries in SCRATCH_POOL. 441289177Speter */ 442289177Speterstatic const char * 443289177Speterconstruct_txn_node_path(svn_fs_t *fs, 444289177Speter const svn_fs_x__id_t *id, 445289177Speter const char *suffix, 446289177Speter apr_pool_t *result_pool, 447289177Speter apr_pool_t *scratch_pool) 448289177Speter{ 449289177Speter const char *filename = svn_fs_x__id_unparse(id, result_pool)->data; 450289177Speter apr_int64_t txn_id = svn_fs_x__get_txn_id(id->change_set); 451289177Speter 452289177Speter return svn_dirent_join(svn_fs_x__path_txn_dir(fs, txn_id, scratch_pool), 453289177Speter apr_psprintf(scratch_pool, PATH_PREFIX_NODE "%s%s", 454289177Speter filename, suffix), 455289177Speter result_pool); 456289177Speter} 457289177Speter 458289177Speterconst char * 459289177Spetersvn_fs_x__path_txn_node_rev(svn_fs_t *fs, 460289177Speter const svn_fs_x__id_t *id, 461289177Speter apr_pool_t *result_pool, 462289177Speter apr_pool_t *scratch_pool) 463289177Speter{ 464289177Speter return construct_txn_node_path(fs, id, "", result_pool, scratch_pool); 465289177Speter} 466289177Speter 467289177Speterconst char * 468289177Spetersvn_fs_x__path_txn_node_props(svn_fs_t *fs, 469289177Speter const svn_fs_x__id_t *id, 470289177Speter apr_pool_t *result_pool, 471289177Speter apr_pool_t *scratch_pool) 472289177Speter{ 473289177Speter return construct_txn_node_path(fs, id, PATH_EXT_PROPS, result_pool, 474289177Speter scratch_pool); 475289177Speter} 476289177Speter 477289177Speterconst char * 478289177Spetersvn_fs_x__path_txn_node_children(svn_fs_t *fs, 479289177Speter const svn_fs_x__id_t *id, 480289177Speter apr_pool_t *result_pool, 481289177Speter apr_pool_t *scratch_pool) 482289177Speter{ 483289177Speter return construct_txn_node_path(fs, id, PATH_EXT_CHILDREN, result_pool, 484289177Speter scratch_pool); 485289177Speter} 486289177Speter 487289177Spetersvn_error_t * 488289177Spetersvn_fs_x__check_file_buffer_numeric(const char *buf, 489289177Speter apr_off_t offset, 490289177Speter const char *path, 491289177Speter const char *title, 492289177Speter apr_pool_t *scratch_pool) 493289177Speter{ 494289177Speter const char *p; 495289177Speter 496289177Speter for (p = buf + offset; *p; p++) 497289177Speter if (!svn_ctype_isdigit(*p)) 498289177Speter return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL, 499289177Speter _("%s file '%s' contains unexpected non-digit '%c' within '%s'"), 500289177Speter title, svn_dirent_local_style(path, scratch_pool), *p, buf); 501289177Speter 502289177Speter return SVN_NO_ERROR; 503289177Speter} 504289177Speter 505289177Spetersvn_error_t * 506289177Spetersvn_fs_x__read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev, 507289177Speter svn_fs_t *fs, 508289177Speter apr_pool_t *scratch_pool) 509289177Speter{ 510289177Speter char buf[80]; 511289177Speter apr_file_t *file; 512289177Speter apr_size_t len; 513289177Speter 514289177Speter SVN_ERR(svn_io_file_open(&file, 515289177Speter svn_fs_x__path_min_unpacked_rev(fs, scratch_pool), 516289177Speter APR_READ | APR_BUFFERED, 517289177Speter APR_OS_DEFAULT, 518289177Speter scratch_pool)); 519289177Speter len = sizeof(buf); 520289177Speter SVN_ERR(svn_io_read_length_line(file, buf, &len, scratch_pool)); 521289177Speter SVN_ERR(svn_io_file_close(file, scratch_pool)); 522289177Speter 523289177Speter SVN_ERR(svn_revnum_parse(min_unpacked_rev, buf, NULL)); 524289177Speter return SVN_NO_ERROR; 525289177Speter} 526289177Speter 527289177Spetersvn_error_t * 528289177Spetersvn_fs_x__update_min_unpacked_rev(svn_fs_t *fs, 529289177Speter apr_pool_t *scratch_pool) 530289177Speter{ 531289177Speter svn_fs_x__data_t *ffd = fs->fsap_data; 532289177Speter return svn_fs_x__read_min_unpacked_rev(&ffd->min_unpacked_rev, fs, 533289177Speter scratch_pool); 534289177Speter} 535289177Speter 536289177Speter/* Write a file FILENAME in directory FS_PATH, containing a single line 537289177Speter * with the number REVNUM in ASCII decimal. Move the file into place 538289177Speter * atomically, overwriting any existing file. 539289177Speter * 540289177Speter * Similar to write_current(). */ 541289177Spetersvn_error_t * 542289177Spetersvn_fs_x__write_min_unpacked_rev(svn_fs_t *fs, 543289177Speter svn_revnum_t revnum, 544289177Speter apr_pool_t *scratch_pool) 545289177Speter{ 546289177Speter const char *final_path; 547289177Speter char buf[SVN_INT64_BUFFER_SIZE]; 548289177Speter apr_size_t len = svn__i64toa(buf, revnum); 549289177Speter buf[len] = '\n'; 550289177Speter 551289177Speter final_path = svn_fs_x__path_min_unpacked_rev(fs, scratch_pool); 552289177Speter 553289177Speter SVN_ERR(svn_io_write_atomic(final_path, buf, len + 1, 554289177Speter final_path /* copy_perms */, scratch_pool)); 555289177Speter 556289177Speter return SVN_NO_ERROR; 557289177Speter} 558289177Speter 559289177Spetersvn_error_t * 560289177Spetersvn_fs_x__read_current(svn_revnum_t *rev, 561289177Speter svn_fs_t *fs, 562289177Speter apr_pool_t *scratch_pool) 563289177Speter{ 564289177Speter const char *str; 565289177Speter svn_stringbuf_t *content; 566289177Speter SVN_ERR(svn_fs_x__read_content(&content, 567289177Speter svn_fs_x__path_current(fs, scratch_pool), 568289177Speter scratch_pool)); 569289177Speter SVN_ERR(svn_revnum_parse(rev, content->data, &str)); 570289177Speter if (*str != '\n') 571289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, 572289177Speter _("Corrupt 'current' file")); 573289177Speter 574289177Speter return SVN_NO_ERROR; 575289177Speter} 576289177Speter 577289177Speter/* Atomically update the 'current' file to hold the specifed REV. 578289177Speter Perform temporary allocations in SCRATCH_POOL. */ 579289177Spetersvn_error_t * 580289177Spetersvn_fs_x__write_current(svn_fs_t *fs, 581289177Speter svn_revnum_t rev, 582289177Speter apr_pool_t *scratch_pool) 583289177Speter{ 584289177Speter char *buf; 585289177Speter const char *tmp_name, *name; 586289177Speter 587289177Speter /* Now we can just write out this line. */ 588289177Speter buf = apr_psprintf(scratch_pool, "%ld\n", rev); 589289177Speter 590289177Speter name = svn_fs_x__path_current(fs, scratch_pool); 591289177Speter SVN_ERR(svn_io_write_unique(&tmp_name, 592289177Speter svn_dirent_dirname(name, scratch_pool), 593289177Speter buf, strlen(buf), 594289177Speter svn_io_file_del_none, scratch_pool)); 595289177Speter 596289177Speter return svn_fs_x__move_into_place(tmp_name, name, name, scratch_pool); 597289177Speter} 598289177Speter 599289177Speter 600289177Spetersvn_error_t * 601289177Spetersvn_fs_x__try_stringbuf_from_file(svn_stringbuf_t **content, 602289177Speter svn_boolean_t *missing, 603289177Speter const char *path, 604289177Speter svn_boolean_t last_attempt, 605289177Speter apr_pool_t *result_pool) 606289177Speter{ 607289177Speter svn_error_t *err = svn_stringbuf_from_file2(content, path, result_pool); 608289177Speter if (missing) 609289177Speter *missing = FALSE; 610289177Speter 611289177Speter if (err) 612289177Speter { 613289177Speter *content = NULL; 614289177Speter 615289177Speter if (APR_STATUS_IS_ENOENT(err->apr_err)) 616289177Speter { 617289177Speter if (!last_attempt) 618289177Speter { 619289177Speter svn_error_clear(err); 620289177Speter if (missing) 621289177Speter *missing = TRUE; 622289177Speter return SVN_NO_ERROR; 623289177Speter } 624289177Speter } 625289177Speter#ifdef ESTALE 626289177Speter else if (APR_TO_OS_ERROR(err->apr_err) == ESTALE 627289177Speter || APR_TO_OS_ERROR(err->apr_err) == EIO) 628289177Speter { 629289177Speter if (!last_attempt) 630289177Speter { 631289177Speter svn_error_clear(err); 632289177Speter return SVN_NO_ERROR; 633289177Speter } 634289177Speter } 635289177Speter#endif 636289177Speter } 637289177Speter 638289177Speter return svn_error_trace(err); 639289177Speter} 640289177Speter 641289177Speter/* Fetch the current offset of FILE into *OFFSET_P. */ 642289177Spetersvn_error_t * 643289177Spetersvn_fs_x__get_file_offset(apr_off_t *offset_p, 644289177Speter apr_file_t *file, 645289177Speter apr_pool_t *scratch_pool) 646289177Speter{ 647289177Speter apr_off_t offset; 648289177Speter 649289177Speter /* Note that, for buffered files, one (possibly surprising) side-effect 650289177Speter of this call is to flush any unwritten data to disk. */ 651289177Speter offset = 0; 652289177Speter SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, scratch_pool)); 653289177Speter *offset_p = offset; 654289177Speter 655289177Speter return SVN_NO_ERROR; 656289177Speter} 657289177Speter 658289177Spetersvn_error_t * 659289177Spetersvn_fs_x__read_content(svn_stringbuf_t **content, 660289177Speter const char *fname, 661289177Speter apr_pool_t *result_pool) 662289177Speter{ 663289177Speter int i; 664289177Speter *content = NULL; 665289177Speter 666289177Speter for (i = 0; !*content && (i < SVN_FS_X__RECOVERABLE_RETRY_COUNT); ++i) 667289177Speter SVN_ERR(svn_fs_x__try_stringbuf_from_file(content, NULL, 668289177Speter fname, i + 1 < SVN_FS_X__RECOVERABLE_RETRY_COUNT, 669289177Speter result_pool)); 670289177Speter 671289177Speter if (!*content) 672289177Speter return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, 673289177Speter _("Can't read '%s'"), 674289177Speter svn_dirent_local_style(fname, result_pool)); 675289177Speter 676289177Speter return SVN_NO_ERROR; 677289177Speter} 678289177Speter 679289177Speter/* Reads a line from STREAM and converts it to a 64 bit integer to be 680289177Speter * returned in *RESULT. If we encounter eof, set *HIT_EOF and leave 681289177Speter * *RESULT unchanged. If HIT_EOF is NULL, EOF causes an "corrupt FS" 682289177Speter * error return. 683289177Speter * SCRATCH_POOL is used for temporary allocations. 684289177Speter */ 685289177Spetersvn_error_t * 686289177Spetersvn_fs_x__read_number_from_stream(apr_int64_t *result, 687289177Speter svn_boolean_t *hit_eof, 688289177Speter svn_stream_t *stream, 689289177Speter apr_pool_t *scratch_pool) 690289177Speter{ 691289177Speter svn_stringbuf_t *sb; 692289177Speter svn_boolean_t eof; 693289177Speter svn_error_t *err; 694289177Speter 695289177Speter SVN_ERR(svn_stream_readline(stream, &sb, "\n", &eof, scratch_pool)); 696289177Speter if (hit_eof) 697289177Speter *hit_eof = eof; 698289177Speter else 699289177Speter if (eof) 700289177Speter return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Unexpected EOF")); 701289177Speter 702289177Speter if (!eof) 703289177Speter { 704289177Speter err = svn_cstring_atoi64(result, sb->data); 705289177Speter if (err) 706289177Speter return svn_error_createf(SVN_ERR_FS_CORRUPT, err, 707289177Speter _("Number '%s' invalid or too large"), 708289177Speter sb->data); 709289177Speter } 710289177Speter 711289177Speter return SVN_NO_ERROR; 712289177Speter} 713289177Speter 714289177Speter 715289177Speter/* Move a file into place from OLD_FILENAME in the transactions 716289177Speter directory to its final location NEW_FILENAME in the repository. On 717289177Speter Unix, match the permissions of the new file to the permissions of 718289177Speter PERMS_REFERENCE. Temporary allocations are from SCRATCH_POOL. 719289177Speter 720289177Speter This function almost duplicates svn_io_file_move(), but it tries to 721289177Speter guarantee a flush. */ 722289177Spetersvn_error_t * 723289177Spetersvn_fs_x__move_into_place(const char *old_filename, 724289177Speter const char *new_filename, 725289177Speter const char *perms_reference, 726289177Speter apr_pool_t *scratch_pool) 727289177Speter{ 728289177Speter svn_error_t *err; 729289177Speter 730289177Speter SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, scratch_pool)); 731289177Speter 732289177Speter /* Move the file into place. */ 733289177Speter err = svn_io_file_rename(old_filename, new_filename, scratch_pool); 734289177Speter if (err && APR_STATUS_IS_EXDEV(err->apr_err)) 735289177Speter { 736289177Speter apr_file_t *file; 737289177Speter 738289177Speter /* Can't rename across devices; fall back to copying. */ 739289177Speter svn_error_clear(err); 740289177Speter err = SVN_NO_ERROR; 741289177Speter SVN_ERR(svn_io_copy_file(old_filename, new_filename, TRUE, 742289177Speter scratch_pool)); 743289177Speter 744289177Speter /* Flush the target of the copy to disk. */ 745289177Speter SVN_ERR(svn_io_file_open(&file, new_filename, APR_READ, 746289177Speter APR_OS_DEFAULT, scratch_pool)); 747289177Speter /* ### BH: Does this really guarantee a flush of the data written 748289177Speter ### via a completely different handle on all operating systems? 749289177Speter ### 750289177Speter ### Maybe we should perform the copy ourselves instead of making 751289177Speter ### apr do that and flush the real handle? */ 752289177Speter SVN_ERR(svn_io_file_flush_to_disk(file, scratch_pool)); 753289177Speter SVN_ERR(svn_io_file_close(file, scratch_pool)); 754289177Speter } 755289177Speter if (err) 756289177Speter return svn_error_trace(err); 757289177Speter 758289177Speter#ifdef __linux__ 759289177Speter { 760289177Speter /* Linux has the unusual feature that fsync() on a file is not 761289177Speter enough to ensure that a file's directory entries have been 762289177Speter flushed to disk; you have to fsync the directory as well. 763289177Speter On other operating systems, we'd only be asking for trouble 764289177Speter by trying to open and fsync a directory. */ 765289177Speter const char *dirname; 766289177Speter apr_file_t *file; 767289177Speter 768289177Speter dirname = svn_dirent_dirname(new_filename, scratch_pool); 769289177Speter SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT, 770289177Speter scratch_pool)); 771289177Speter SVN_ERR(svn_io_file_flush_to_disk(file, scratch_pool)); 772289177Speter SVN_ERR(svn_io_file_close(file, scratch_pool)); 773289177Speter } 774289177Speter#endif 775289177Speter 776289177Speter return SVN_NO_ERROR; 777289177Speter} 778