1289177Speter/* fs_x.c --- filesystem operations specific to fs_x 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 "fs_x.h" 24289177Speter 25289177Speter#include <apr_uuid.h> 26289177Speter 27289177Speter#include "svn_hash.h" 28289177Speter#include "svn_props.h" 29289177Speter#include "svn_time.h" 30289177Speter#include "svn_dirent_uri.h" 31289177Speter#include "svn_sorts.h" 32289177Speter#include "svn_version.h" 33289177Speter 34289177Speter#include "cached_data.h" 35289177Speter#include "id.h" 36289177Speter#include "rep-cache.h" 37289177Speter#include "revprops.h" 38289177Speter#include "transaction.h" 39289177Speter#include "tree.h" 40289177Speter#include "util.h" 41289177Speter#include "index.h" 42289177Speter 43289177Speter#include "private/svn_fs_util.h" 44289177Speter#include "private/svn_string_private.h" 45289177Speter#include "private/svn_subr_private.h" 46289177Speter#include "../libsvn_fs/fs-loader.h" 47289177Speter 48289177Speter#include "svn_private_config.h" 49289177Speter 50289177Speter/* The default maximum number of files per directory to store in the 51289177Speter rev and revprops directory. The number below is somewhat arbitrary, 52289177Speter and can be overridden by defining the macro while compiling; the 53289177Speter figure of 1000 is reasonable for VFAT filesystems, which are by far 54289177Speter the worst performers in this area. */ 55289177Speter#ifndef SVN_FS_X_DEFAULT_MAX_FILES_PER_DIR 56289177Speter#define SVN_FS_X_DEFAULT_MAX_FILES_PER_DIR 1000 57289177Speter#endif 58289177Speter 59289177Speter/* Begin deltification after a node history exceeded this this limit. 60289177Speter Useful values are 4 to 64 with 16 being a good compromise between 61289177Speter computational overhead and repository size savings. 62289177Speter Should be a power of 2. 63289177Speter Values < 2 will result in standard skip-delta behavior. */ 64289177Speter#define SVN_FS_X_MAX_LINEAR_DELTIFICATION 16 65289177Speter 66289177Speter/* Finding a deltification base takes operations proportional to the 67289177Speter number of changes being skipped. To prevent exploding runtime 68289177Speter during commits, limit the deltification range to this value. 69289177Speter Should be a power of 2 minus one. 70289177Speter Values < 1 disable deltification. */ 71289177Speter#define SVN_FS_X_MAX_DELTIFICATION_WALK 1023 72289177Speter 73289177Speter 74289177Speter 75289177Speter 76289177Speter/* Check that BUF, a nul-terminated buffer of text from format file PATH, 77289177Speter contains only digits at OFFSET and beyond, raising an error if not. 78289177Speter 79289177Speter Uses SCRATCH_POOL for temporary allocation. */ 80289177Speterstatic svn_error_t * 81289177Spetercheck_format_file_buffer_numeric(const char *buf, 82289177Speter apr_off_t offset, 83289177Speter const char *path, 84289177Speter apr_pool_t *scratch_pool) 85289177Speter{ 86289177Speter return svn_fs_x__check_file_buffer_numeric(buf, offset, path, "Format", 87289177Speter scratch_pool); 88289177Speter} 89289177Speter 90289177Speter/* Return the error SVN_ERR_FS_UNSUPPORTED_FORMAT if FS's format 91289177Speter number is not the same as a format number supported by this 92289177Speter Subversion. */ 93289177Speterstatic svn_error_t * 94289177Spetercheck_format(int format) 95289177Speter{ 96289177Speter /* Put blacklisted versions here. */ 97289177Speter 98289177Speter /* We support all formats from 1-current simultaneously */ 99289177Speter if (1 <= format && format <= SVN_FS_X__FORMAT_NUMBER) 100289177Speter return SVN_NO_ERROR; 101289177Speter 102289177Speter return svn_error_createf(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL, 103289177Speter _("Expected FS format between '1' and '%d'; found format '%d'"), 104289177Speter SVN_FS_X__FORMAT_NUMBER, format); 105289177Speter} 106289177Speter 107289177Speter/* Read the format file at PATH and set *PFORMAT to the format version found 108289177Speter * and *MAX_FILES_PER_DIR to the shard size. Use SCRATCH_POOL for temporary 109289177Speter * allocations. */ 110289177Speterstatic svn_error_t * 111289177Speterread_format(int *pformat, 112289177Speter int *max_files_per_dir, 113289177Speter const char *path, 114289177Speter apr_pool_t *scratch_pool) 115289177Speter{ 116289177Speter svn_stream_t *stream; 117289177Speter svn_stringbuf_t *content; 118289177Speter svn_stringbuf_t *buf; 119289177Speter svn_boolean_t eos = FALSE; 120289177Speter 121289177Speter SVN_ERR(svn_stringbuf_from_file2(&content, path, scratch_pool)); 122289177Speter stream = svn_stream_from_stringbuf(content, scratch_pool); 123289177Speter SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eos, scratch_pool)); 124289177Speter if (buf->len == 0 && eos) 125289177Speter { 126289177Speter /* Return a more useful error message. */ 127289177Speter return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL, 128289177Speter _("Can't read first line of format file '%s'"), 129289177Speter svn_dirent_local_style(path, scratch_pool)); 130289177Speter } 131289177Speter 132289177Speter /* Check that the first line contains only digits. */ 133289177Speter SVN_ERR(check_format_file_buffer_numeric(buf->data, 0, path, scratch_pool)); 134289177Speter SVN_ERR(svn_cstring_atoi(pformat, buf->data)); 135289177Speter 136289177Speter /* Check that we support this format at all */ 137289177Speter SVN_ERR(check_format(*pformat)); 138289177Speter 139289177Speter /* Read any options. */ 140289177Speter SVN_ERR(svn_stream_readline(stream, &buf, "\n", &eos, scratch_pool)); 141289177Speter if (!eos && strncmp(buf->data, "layout sharded ", 15) == 0) 142289177Speter { 143289177Speter /* Check that the argument is numeric. */ 144289177Speter SVN_ERR(check_format_file_buffer_numeric(buf->data, 15, path, 145289177Speter scratch_pool)); 146289177Speter SVN_ERR(svn_cstring_atoi(max_files_per_dir, buf->data + 15)); 147289177Speter } 148289177Speter else 149289177Speter return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL, 150289177Speter _("'%s' contains invalid filesystem format option '%s'"), 151289177Speter svn_dirent_local_style(path, scratch_pool), buf->data); 152289177Speter 153289177Speter return SVN_NO_ERROR; 154289177Speter} 155289177Speter 156289177Speter/* Write the format number and maximum number of files per directory 157289177Speter to a new format file in PATH, possibly expecting to overwrite a 158289177Speter previously existing file. 159289177Speter 160289177Speter Use SCRATCH_POOL for temporary allocation. */ 161289177Spetersvn_error_t * 162289177Spetersvn_fs_x__write_format(svn_fs_t *fs, 163289177Speter svn_boolean_t overwrite, 164289177Speter apr_pool_t *scratch_pool) 165289177Speter{ 166289177Speter svn_stringbuf_t *sb; 167289177Speter const char *path = svn_fs_x__path_format(fs, scratch_pool); 168289177Speter svn_fs_x__data_t *ffd = fs->fsap_data; 169289177Speter 170289177Speter SVN_ERR_ASSERT(1 <= ffd->format && ffd->format <= SVN_FS_X__FORMAT_NUMBER); 171289177Speter 172289177Speter sb = svn_stringbuf_createf(scratch_pool, "%d\n", ffd->format); 173289177Speter svn_stringbuf_appendcstr(sb, apr_psprintf(scratch_pool, 174289177Speter "layout sharded %d\n", 175289177Speter ffd->max_files_per_dir)); 176289177Speter 177289177Speter /* svn_io_write_version_file() does a load of magic to allow it to 178289177Speter replace version files that already exist. We only need to do 179289177Speter that when we're allowed to overwrite an existing file. */ 180289177Speter if (! overwrite) 181289177Speter { 182289177Speter /* Create the file */ 183289177Speter SVN_ERR(svn_io_file_create(path, sb->data, scratch_pool)); 184289177Speter } 185289177Speter else 186289177Speter { 187289177Speter SVN_ERR(svn_io_write_atomic(path, sb->data, sb->len, 188289177Speter NULL /* copy_perms_path */, scratch_pool)); 189289177Speter } 190289177Speter 191289177Speter /* And set the perms to make it read only */ 192289177Speter return svn_io_set_file_read_only(path, FALSE, scratch_pool); 193289177Speter} 194289177Speter 195289177Speter/* Check that BLOCK_SIZE is a valid block / page size, i.e. it is within 196289177Speter * the range of what the current system may address in RAM and it is a 197289177Speter * power of 2. Assume that the element size within the block is ITEM_SIZE. 198289177Speter * Use SCRATCH_POOL for temporary allocations. 199289177Speter */ 200289177Speterstatic svn_error_t * 201289177Speterverify_block_size(apr_int64_t block_size, 202289177Speter apr_size_t item_size, 203289177Speter const char *name, 204289177Speter apr_pool_t *scratch_pool) 205289177Speter{ 206289177Speter /* Limit range. */ 207289177Speter if (block_size <= 0) 208289177Speter return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, 209289177Speter _("%s is too small for fsfs.conf setting '%s'."), 210289177Speter apr_psprintf(scratch_pool, 211289177Speter "%" APR_INT64_T_FMT, 212289177Speter block_size), 213289177Speter name); 214289177Speter 215289177Speter if (block_size > SVN_MAX_OBJECT_SIZE / item_size) 216289177Speter return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, 217289177Speter _("%s is too large for fsfs.conf setting '%s'."), 218289177Speter apr_psprintf(scratch_pool, 219289177Speter "%" APR_INT64_T_FMT, 220289177Speter block_size), 221289177Speter name); 222289177Speter 223289177Speter /* Ensure it is a power of two. 224289177Speter * For positive X, X & (X-1) will reset the lowest bit set. 225289177Speter * If the result is 0, at most one bit has been set. */ 226289177Speter if (0 != (block_size & (block_size - 1))) 227289177Speter return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, 228289177Speter _("%s is invalid for fsfs.conf setting '%s' " 229289177Speter "because it is not a power of 2."), 230289177Speter apr_psprintf(scratch_pool, 231289177Speter "%" APR_INT64_T_FMT, 232289177Speter block_size), 233289177Speter name); 234289177Speter 235289177Speter return SVN_NO_ERROR; 236289177Speter} 237289177Speter 238289177Speter/* Read the configuration information of the file system at FS_PATH 239289177Speter * and set the respective values in FFD. Use pools as usual. 240289177Speter */ 241289177Speterstatic svn_error_t * 242289177Speterread_config(svn_fs_x__data_t *ffd, 243289177Speter const char *fs_path, 244289177Speter apr_pool_t *result_pool, 245289177Speter apr_pool_t *scratch_pool) 246289177Speter{ 247289177Speter svn_config_t *config; 248289177Speter apr_int64_t compression_level; 249289177Speter 250289177Speter SVN_ERR(svn_config_read3(&config, 251289177Speter svn_dirent_join(fs_path, PATH_CONFIG, scratch_pool), 252289177Speter FALSE, FALSE, FALSE, scratch_pool)); 253289177Speter 254289177Speter /* Initialize ffd->rep_sharing_allowed. */ 255289177Speter SVN_ERR(svn_config_get_bool(config, &ffd->rep_sharing_allowed, 256289177Speter CONFIG_SECTION_REP_SHARING, 257289177Speter CONFIG_OPTION_ENABLE_REP_SHARING, TRUE)); 258289177Speter 259289177Speter /* Initialize deltification settings in ffd. */ 260289177Speter SVN_ERR(svn_config_get_int64(config, &ffd->max_deltification_walk, 261289177Speter CONFIG_SECTION_DELTIFICATION, 262289177Speter CONFIG_OPTION_MAX_DELTIFICATION_WALK, 263289177Speter SVN_FS_X_MAX_DELTIFICATION_WALK)); 264289177Speter SVN_ERR(svn_config_get_int64(config, &ffd->max_linear_deltification, 265289177Speter CONFIG_SECTION_DELTIFICATION, 266289177Speter CONFIG_OPTION_MAX_LINEAR_DELTIFICATION, 267289177Speter SVN_FS_X_MAX_LINEAR_DELTIFICATION)); 268289177Speter SVN_ERR(svn_config_get_int64(config, &compression_level, 269289177Speter CONFIG_SECTION_DELTIFICATION, 270289177Speter CONFIG_OPTION_COMPRESSION_LEVEL, 271289177Speter SVN_DELTA_COMPRESSION_LEVEL_DEFAULT)); 272289177Speter ffd->delta_compression_level 273289177Speter = (int)MIN(MAX(SVN_DELTA_COMPRESSION_LEVEL_NONE, compression_level), 274289177Speter SVN_DELTA_COMPRESSION_LEVEL_MAX); 275289177Speter 276289177Speter /* Initialize revprop packing settings in ffd. */ 277289177Speter SVN_ERR(svn_config_get_bool(config, &ffd->compress_packed_revprops, 278289177Speter CONFIG_SECTION_PACKED_REVPROPS, 279289177Speter CONFIG_OPTION_COMPRESS_PACKED_REVPROPS, 280289177Speter TRUE)); 281289177Speter SVN_ERR(svn_config_get_int64(config, &ffd->revprop_pack_size, 282289177Speter CONFIG_SECTION_PACKED_REVPROPS, 283289177Speter CONFIG_OPTION_REVPROP_PACK_SIZE, 284289177Speter ffd->compress_packed_revprops 285289177Speter ? 0x100 286289177Speter : 0x40)); 287289177Speter 288289177Speter ffd->revprop_pack_size *= 1024; 289289177Speter 290289177Speter /* I/O settings in ffd. */ 291289177Speter SVN_ERR(svn_config_get_int64(config, &ffd->block_size, 292289177Speter CONFIG_SECTION_IO, 293289177Speter CONFIG_OPTION_BLOCK_SIZE, 294289177Speter 64)); 295289177Speter SVN_ERR(svn_config_get_int64(config, &ffd->l2p_page_size, 296289177Speter CONFIG_SECTION_IO, 297289177Speter CONFIG_OPTION_L2P_PAGE_SIZE, 298289177Speter 0x2000)); 299289177Speter SVN_ERR(svn_config_get_int64(config, &ffd->p2l_page_size, 300289177Speter CONFIG_SECTION_IO, 301289177Speter CONFIG_OPTION_P2L_PAGE_SIZE, 302289177Speter 0x400)); 303289177Speter 304289177Speter /* Don't accept unreasonable or illegal values. 305289177Speter * Block size and P2L page size are in kbytes; 306289177Speter * L2P blocks are arrays of apr_off_t. */ 307289177Speter SVN_ERR(verify_block_size(ffd->block_size, 0x400, 308289177Speter CONFIG_OPTION_BLOCK_SIZE, scratch_pool)); 309289177Speter SVN_ERR(verify_block_size(ffd->p2l_page_size, 0x400, 310289177Speter CONFIG_OPTION_P2L_PAGE_SIZE, scratch_pool)); 311289177Speter SVN_ERR(verify_block_size(ffd->l2p_page_size, sizeof(apr_off_t), 312289177Speter CONFIG_OPTION_L2P_PAGE_SIZE, scratch_pool)); 313289177Speter 314289177Speter /* convert kBytes to bytes */ 315289177Speter ffd->block_size *= 0x400; 316289177Speter ffd->p2l_page_size *= 0x400; 317289177Speter /* L2P pages are in entries - not in (k)Bytes */ 318289177Speter 319289177Speter /* Debug options. */ 320289177Speter SVN_ERR(svn_config_get_bool(config, &ffd->pack_after_commit, 321289177Speter CONFIG_SECTION_DEBUG, 322289177Speter CONFIG_OPTION_PACK_AFTER_COMMIT, 323289177Speter FALSE)); 324289177Speter 325289177Speter /* memcached configuration */ 326289177Speter SVN_ERR(svn_cache__make_memcache_from_config(&ffd->memcache, config, 327289177Speter result_pool, scratch_pool)); 328289177Speter 329289177Speter SVN_ERR(svn_config_get_bool(config, &ffd->fail_stop, 330289177Speter CONFIG_SECTION_CACHES, CONFIG_OPTION_FAIL_STOP, 331289177Speter FALSE)); 332289177Speter 333289177Speter return SVN_NO_ERROR; 334289177Speter} 335289177Speter 336289177Speter/* Write FS' initial configuration file. 337289177Speter * Use SCRATCH_POOL for temporary allocations. */ 338289177Speterstatic svn_error_t * 339289177Speterwrite_config(svn_fs_t *fs, 340289177Speter apr_pool_t *scratch_pool) 341289177Speter{ 342289177Speter#define NL APR_EOL_STR 343289177Speter static const char * const fsx_conf_contents = 344289177Speter"### This file controls the configuration of the FSX filesystem." NL 345289177Speter"" NL 346289177Speter"[" SVN_CACHE_CONFIG_CATEGORY_MEMCACHED_SERVERS "]" NL 347289177Speter"### These options name memcached servers used to cache internal FSX" NL 348289177Speter"### data. See http://www.danga.com/memcached/ for more information on" NL 349289177Speter"### memcached. To use memcached with FSX, run one or more memcached" NL 350289177Speter"### servers, and specify each of them as an option like so:" NL 351289177Speter"# first-server = 127.0.0.1:11211" NL 352289177Speter"# remote-memcached = mymemcached.corp.example.com:11212" NL 353289177Speter"### The option name is ignored; the value is of the form HOST:PORT." NL 354289177Speter"### memcached servers can be shared between multiple repositories;" NL 355289177Speter"### however, if you do this, you *must* ensure that repositories have" NL 356289177Speter"### distinct UUIDs and paths, or else cached data from one repository" NL 357289177Speter"### might be used by another accidentally. Note also that memcached has" NL 358289177Speter"### no authentication for reads or writes, so you must ensure that your" NL 359289177Speter"### memcached servers are only accessible by trusted users." NL 360289177Speter"" NL 361289177Speter"[" CONFIG_SECTION_CACHES "]" NL 362289177Speter"### When a cache-related error occurs, normally Subversion ignores it" NL 363289177Speter"### and continues, logging an error if the server is appropriately" NL 364289177Speter"### configured (and ignoring it with file:// access). To make" NL 365289177Speter"### Subversion never ignore cache errors, uncomment this line." NL 366289177Speter"# " CONFIG_OPTION_FAIL_STOP " = true" NL 367289177Speter"" NL 368289177Speter"[" CONFIG_SECTION_REP_SHARING "]" NL 369289177Speter"### To conserve space, the filesystem can optionally avoid storing" NL 370289177Speter"### duplicate representations. This comes at a slight cost in" NL 371289177Speter"### performance, as maintaining a database of shared representations can" NL 372289177Speter"### increase commit times. The space savings are dependent upon the size" NL 373289177Speter"### of the repository, the number of objects it contains and the amount of" NL 374289177Speter"### duplication between them, usually a function of the branching and" NL 375289177Speter"### merging process." NL 376289177Speter"###" NL 377289177Speter"### The following parameter enables rep-sharing in the repository. It can" NL 378289177Speter"### be switched on and off at will, but for best space-saving results" NL 379289177Speter"### should be enabled consistently over the life of the repository." NL 380289177Speter"### 'svnadmin verify' will check the rep-cache regardless of this setting." NL 381289177Speter"### rep-sharing is enabled by default." NL 382289177Speter"# " CONFIG_OPTION_ENABLE_REP_SHARING " = true" NL 383289177Speter"" NL 384289177Speter"[" CONFIG_SECTION_DELTIFICATION "]" NL 385289177Speter"### To conserve space, the filesystem stores data as differences against" NL 386289177Speter"### existing representations. This comes at a slight cost in performance," NL 387289177Speter"### as calculating differences can increase commit times. Reading data" NL 388289177Speter"### will also create higher CPU load and the data will be fragmented." NL 389289177Speter"### Since deltification tends to save significant amounts of disk space," NL 390289177Speter"### the overall I/O load can actually be lower." NL 391289177Speter"###" NL 392289177Speter"### The options in this section allow for tuning the deltification" NL 393289177Speter"### strategy. Their effects on data size and server performance may vary" NL 394289177Speter"### from one repository to another." NL 395289177Speter"###" NL 396289177Speter"### During commit, the server may need to walk the whole change history of" NL 397289177Speter"### of a given node to find a suitable deltification base. This linear" NL 398289177Speter"### process can impact commit times, svnadmin load and similar operations." NL 399289177Speter"### This setting limits the depth of the deltification history. If the" NL 400289177Speter"### threshold has been reached, the node will be stored as fulltext and a" NL 401289177Speter"### new deltification history begins." NL 402289177Speter"### Note, this is unrelated to svn log." NL 403289177Speter"### Very large values rarely provide significant additional savings but" NL 404289177Speter"### can impact performance greatly - in particular if directory" NL 405289177Speter"### deltification has been activated. Very small values may be useful in" NL 406289177Speter"### repositories that are dominated by large, changing binaries." NL 407289177Speter"### Should be a power of two minus 1. A value of 0 will effectively" NL 408289177Speter"### disable deltification." NL 409289177Speter"### For 1.9, the default value is 1023." NL 410289177Speter"# " CONFIG_OPTION_MAX_DELTIFICATION_WALK " = 1023" NL 411289177Speter"###" NL 412289177Speter"### The skip-delta scheme used by FSX tends to repeatably store redundant" NL 413289177Speter"### delta information where a simple delta against the latest version is" NL 414289177Speter"### often smaller. By default, 1.9+ will therefore use skip deltas only" NL 415289177Speter"### after the linear chain of deltas has grown beyond the threshold" NL 416289177Speter"### specified by this setting." NL 417289177Speter"### Values up to 64 can result in some reduction in repository size for" NL 418289177Speter"### the cost of quickly increasing I/O and CPU costs. Similarly, smaller" NL 419289177Speter"### numbers can reduce those costs at the cost of more disk space. For" NL 420289177Speter"### rarely read repositories or those containing larger binaries, this may" NL 421289177Speter"### present a better trade-off." NL 422289177Speter"### Should be a power of two. A value of 1 or smaller will cause the" NL 423289177Speter"### exclusive use of skip-deltas." NL 424289177Speter"### For 1.8, the default value is 16." NL 425289177Speter"# " CONFIG_OPTION_MAX_LINEAR_DELTIFICATION " = 16" NL 426289177Speter"###" NL 427289177Speter"### After deltification, we compress the data through zlib to minimize on-" NL 428289177Speter"### disk size. That can be an expensive and ineffective process. This" NL 429289177Speter"### setting controls the usage of zlib in future revisions." NL 430289177Speter"### Revisions with highly compressible data in them may shrink in size" NL 431289177Speter"### if the setting is increased but may take much longer to commit. The" NL 432289177Speter"### time taken to uncompress that data again is widely independent of the" NL 433289177Speter"### compression level." NL 434289177Speter"### Compression will be ineffective if the incoming content is already" NL 435289177Speter"### highly compressed. In that case, disabling the compression entirely" NL 436289177Speter"### will speed up commits as well as reading the data. Repositories with" NL 437289177Speter"### many small compressible files (source code) but also a high percentage" NL 438289177Speter"### of large incompressible ones (artwork) may benefit from compression" NL 439289177Speter"### levels lowered to e.g. 1." NL 440289177Speter"### Valid values are 0 to 9 with 9 providing the highest compression ratio" NL 441289177Speter"### and 0 disabling it altogether." NL 442289177Speter"### The default value is 5." NL 443289177Speter"# " CONFIG_OPTION_COMPRESSION_LEVEL " = 5" NL 444289177Speter"" NL 445289177Speter"[" CONFIG_SECTION_PACKED_REVPROPS "]" NL 446289177Speter"### This parameter controls the size (in kBytes) of packed revprop files." NL 447289177Speter"### Revprops of consecutive revisions will be concatenated into a single" NL 448289177Speter"### file up to but not exceeding the threshold given here. However, each" NL 449289177Speter"### pack file may be much smaller and revprops of a single revision may be" NL 450289177Speter"### much larger than the limit set here. The threshold will be applied" NL 451289177Speter"### before optional compression takes place." NL 452289177Speter"### Large values will reduce disk space usage at the expense of increased" NL 453289177Speter"### latency and CPU usage reading and changing individual revprops. They" NL 454289177Speter"### become an advantage when revprop caching has been enabled because a" NL 455289177Speter"### lot of data can be read in one go. Values smaller than 4 kByte will" NL 456289177Speter"### not improve latency any further and quickly render revprop packing" NL 457289177Speter"### ineffective." NL 458289177Speter"### revprop-pack-size is 64 kBytes by default for non-compressed revprop" NL 459289177Speter"### pack files and 256 kBytes when compression has been enabled." NL 460289177Speter"# " CONFIG_OPTION_REVPROP_PACK_SIZE " = 64" NL 461289177Speter"###" NL 462289177Speter"### To save disk space, packed revprop files may be compressed. Standard" NL 463289177Speter"### revprops tend to allow for very effective compression. Reading and" NL 464289177Speter"### even more so writing, become significantly more CPU intensive. With" NL 465289177Speter"### revprop caching enabled, the overhead can be offset by reduced I/O" NL 466289177Speter"### unless you often modify revprops after packing." NL 467289177Speter"### Compressing packed revprops is enabled by default." NL 468289177Speter"# " CONFIG_OPTION_COMPRESS_PACKED_REVPROPS " = true" NL 469289177Speter"" NL 470289177Speter"[" CONFIG_SECTION_IO "]" NL 471289177Speter"### Parameters in this section control the data access granularity in" NL 472289177Speter"### format 7 repositories and later. The defaults should translate into" NL 473289177Speter"### decent performance over a wide range of setups." NL 474289177Speter"###" NL 475289177Speter"### When a specific piece of information needs to be read from disk, a" NL 476289177Speter"### data block is being read at once and its contents are being cached." NL 477289177Speter"### If the repository is being stored on a RAID, the block size should be" NL 478289177Speter"### either 50% or 100% of RAID block size / granularity. Also, your file" NL 479289177Speter"### system blocks/clusters should be properly aligned and sized. In that" NL 480289177Speter"### setup, each access will hit only one disk (minimizes I/O load) but" NL 481289177Speter"### uses all the data provided by the disk in a single access." NL 482289177Speter"### For SSD-based storage systems, slightly lower values around 16 kB" NL 483289177Speter"### may improve latency while still maximizing throughput." NL 484289177Speter"### Can be changed at any time but must be a power of 2." NL 485289177Speter"### block-size is given in kBytes and with a default of 64 kBytes." NL 486289177Speter"# " CONFIG_OPTION_BLOCK_SIZE " = 64" NL 487289177Speter"###" NL 488289177Speter"### The log-to-phys index maps data item numbers to offsets within the" NL 489289177Speter"### rev or pack file. This index is organized in pages of a fixed maximum" NL 490289177Speter"### capacity. To access an item, the page table and the respective page" NL 491289177Speter"### must be read." NL 492289177Speter"### This parameter only affects revisions with thousands of changed paths." NL 493289177Speter"### If you have several extremely large revisions (~1 mio changes), think" NL 494289177Speter"### about increasing this setting. Reducing the value will rarely result" NL 495289177Speter"### in a net speedup." NL 496289177Speter"### This is an expert setting. Must be a power of 2." NL 497289177Speter"### l2p-page-size is 8192 entries by default." NL 498289177Speter"# " CONFIG_OPTION_L2P_PAGE_SIZE " = 8192" NL 499289177Speter"###" NL 500289177Speter"### The phys-to-log index maps positions within the rev or pack file to" NL 501289177Speter"### to data items, i.e. describes what piece of information is being" NL 502289177Speter"### stored at any particular offset. The index describes the rev file" NL 503289177Speter"### in chunks (pages) and keeps a global list of all those pages. Large" NL 504289177Speter"### pages mean a shorter page table but a larger per-page description of" NL 505289177Speter"### data items in it. The latency sweet spot depends on the change size" NL 506289177Speter"### distribution but covers a relatively wide range." NL 507289177Speter"### If the repository contains very large files, i.e. individual changes" NL 508289177Speter"### of tens of MB each, increasing the page size will shorten the index" NL 509289177Speter"### file at the expense of a slightly increased latency in sections with" NL 510289177Speter"### smaller changes." NL 511289177Speter"### For source code repositories, this should be about 16x the block-size." NL 512289177Speter"### Must be a power of 2." NL 513289177Speter"### p2l-page-size is given in kBytes and with a default of 1024 kBytes." NL 514289177Speter"# " CONFIG_OPTION_P2L_PAGE_SIZE " = 1024" NL 515289177Speter; 516289177Speter#undef NL 517289177Speter return svn_io_file_create(svn_dirent_join(fs->path, PATH_CONFIG, 518289177Speter scratch_pool), 519289177Speter fsx_conf_contents, scratch_pool); 520289177Speter} 521289177Speter 522289177Speter/* Read FS's UUID file and store the data in the FS struct. */ 523289177Speterstatic svn_error_t * 524289177Speterread_uuid(svn_fs_t *fs, 525289177Speter apr_pool_t *scratch_pool) 526289177Speter{ 527289177Speter svn_fs_x__data_t *ffd = fs->fsap_data; 528289177Speter apr_file_t *uuid_file; 529289177Speter char buf[APR_UUID_FORMATTED_LENGTH + 2]; 530289177Speter apr_size_t limit; 531289177Speter 532289177Speter /* Read the repository uuid. */ 533289177Speter SVN_ERR(svn_io_file_open(&uuid_file, svn_fs_x__path_uuid(fs, scratch_pool), 534289177Speter APR_READ | APR_BUFFERED, APR_OS_DEFAULT, 535289177Speter scratch_pool)); 536289177Speter 537289177Speter limit = sizeof(buf); 538289177Speter SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit, scratch_pool)); 539289177Speter fs->uuid = apr_pstrdup(fs->pool, buf); 540289177Speter 541289177Speter /* Read the instance ID. */ 542289177Speter limit = sizeof(buf); 543289177Speter SVN_ERR(svn_io_read_length_line(uuid_file, buf, &limit, 544289177Speter scratch_pool)); 545289177Speter ffd->instance_id = apr_pstrdup(fs->pool, buf); 546289177Speter 547289177Speter SVN_ERR(svn_io_file_close(uuid_file, scratch_pool)); 548289177Speter 549289177Speter return SVN_NO_ERROR; 550289177Speter} 551289177Speter 552289177Spetersvn_error_t * 553289177Spetersvn_fs_x__read_format_file(svn_fs_t *fs, 554289177Speter apr_pool_t *scratch_pool) 555289177Speter{ 556289177Speter svn_fs_x__data_t *ffd = fs->fsap_data; 557289177Speter int format, max_files_per_dir; 558289177Speter 559289177Speter /* Read info from format file. */ 560289177Speter SVN_ERR(read_format(&format, &max_files_per_dir, 561289177Speter svn_fs_x__path_format(fs, scratch_pool), scratch_pool)); 562289177Speter 563289177Speter /* Now that we've got *all* info, store / update values in FFD. */ 564289177Speter ffd->format = format; 565289177Speter ffd->max_files_per_dir = max_files_per_dir; 566289177Speter 567289177Speter return SVN_NO_ERROR; 568289177Speter} 569289177Speter 570289177Spetersvn_error_t * 571289177Spetersvn_fs_x__open(svn_fs_t *fs, 572289177Speter const char *path, 573289177Speter apr_pool_t *scratch_pool) 574289177Speter{ 575289177Speter svn_fs_x__data_t *ffd = fs->fsap_data; 576289177Speter fs->path = apr_pstrdup(fs->pool, path); 577289177Speter 578289177Speter /* Read the FS format file. */ 579289177Speter SVN_ERR(svn_fs_x__read_format_file(fs, scratch_pool)); 580289177Speter 581289177Speter /* Read in and cache the repository uuid. */ 582289177Speter SVN_ERR(read_uuid(fs, scratch_pool)); 583289177Speter 584289177Speter /* Read the min unpacked revision. */ 585289177Speter SVN_ERR(svn_fs_x__update_min_unpacked_rev(fs, scratch_pool)); 586289177Speter 587289177Speter /* Read the configuration file. */ 588289177Speter SVN_ERR(read_config(ffd, fs->path, fs->pool, scratch_pool)); 589289177Speter 590289177Speter return svn_error_trace(svn_fs_x__read_current(&ffd->youngest_rev_cache, 591289177Speter fs, scratch_pool)); 592289177Speter} 593289177Speter 594289177Speter/* Baton type bridging svn_fs_x__upgrade and upgrade_body carrying 595289177Speter * parameters over between them. */ 596289177Spetertypedef struct upgrade_baton_t 597289177Speter{ 598289177Speter svn_fs_t *fs; 599289177Speter svn_fs_upgrade_notify_t notify_func; 600289177Speter void *notify_baton; 601289177Speter svn_cancel_func_t cancel_func; 602289177Speter void *cancel_baton; 603289177Speter} upgrade_baton_t; 604289177Speter 605289177Speter/* Upgrade the FS given in upgrade_baton_t *)BATON to the latest format 606289177Speter * version. Apply options an invoke callback from that BATON. 607289177Speter * Temporary allocations are to be made from SCRATCH_POOL. 608289177Speter * 609289177Speter * At the moment, this is a simple placeholder as we don't support upgrades 610289177Speter * from experimental FSX versions. 611289177Speter */ 612289177Speterstatic svn_error_t * 613289177Speterupgrade_body(void *baton, 614289177Speter apr_pool_t *scratch_pool) 615289177Speter{ 616289177Speter upgrade_baton_t *upgrade_baton = baton; 617289177Speter svn_fs_t *fs = upgrade_baton->fs; 618289177Speter int format, max_files_per_dir; 619289177Speter const char *format_path = svn_fs_x__path_format(fs, scratch_pool); 620289177Speter 621289177Speter /* Read the FS format number and max-files-per-dir setting. */ 622289177Speter SVN_ERR(read_format(&format, &max_files_per_dir, format_path, 623289177Speter scratch_pool)); 624289177Speter 625289177Speter /* If we're already up-to-date, there's nothing else to be done here. */ 626289177Speter if (format == SVN_FS_X__FORMAT_NUMBER) 627289177Speter return SVN_NO_ERROR; 628289177Speter 629289177Speter /* Done */ 630289177Speter return SVN_NO_ERROR; 631289177Speter} 632289177Speter 633289177Speter 634289177Spetersvn_error_t * 635289177Spetersvn_fs_x__upgrade(svn_fs_t *fs, 636289177Speter svn_fs_upgrade_notify_t notify_func, 637289177Speter void *notify_baton, 638289177Speter svn_cancel_func_t cancel_func, 639289177Speter void *cancel_baton, 640289177Speter apr_pool_t *scratch_pool) 641289177Speter{ 642289177Speter upgrade_baton_t baton; 643289177Speter baton.fs = fs; 644289177Speter baton.notify_func = notify_func; 645289177Speter baton.notify_baton = notify_baton; 646289177Speter baton.cancel_func = cancel_func; 647289177Speter baton.cancel_baton = cancel_baton; 648289177Speter 649289177Speter return svn_fs_x__with_all_locks(fs, upgrade_body, (void *)&baton, 650289177Speter scratch_pool); 651289177Speter} 652289177Speter 653289177Speter 654289177Spetersvn_error_t * 655289177Spetersvn_fs_x__youngest_rev(svn_revnum_t *youngest_p, 656289177Speter svn_fs_t *fs, 657289177Speter apr_pool_t *scratch_pool) 658289177Speter{ 659289177Speter svn_fs_x__data_t *ffd = fs->fsap_data; 660289177Speter SVN_ERR(svn_fs_x__read_current(youngest_p, fs, scratch_pool)); 661289177Speter ffd->youngest_rev_cache = *youngest_p; 662289177Speter 663289177Speter return SVN_NO_ERROR; 664289177Speter} 665289177Speter 666289177Spetersvn_error_t * 667289177Spetersvn_fs_x__ensure_revision_exists(svn_revnum_t rev, 668289177Speter svn_fs_t *fs, 669289177Speter apr_pool_t *scratch_pool) 670289177Speter{ 671289177Speter svn_fs_x__data_t *ffd = fs->fsap_data; 672289177Speter 673289177Speter if (! SVN_IS_VALID_REVNUM(rev)) 674289177Speter return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, 675289177Speter _("Invalid revision number '%ld'"), rev); 676289177Speter 677289177Speter 678289177Speter /* Did the revision exist the last time we checked the current 679289177Speter file? */ 680289177Speter if (rev <= ffd->youngest_rev_cache) 681289177Speter return SVN_NO_ERROR; 682289177Speter 683289177Speter SVN_ERR(svn_fs_x__read_current(&ffd->youngest_rev_cache, fs, scratch_pool)); 684289177Speter 685289177Speter /* Check again. */ 686289177Speter if (rev <= ffd->youngest_rev_cache) 687289177Speter return SVN_NO_ERROR; 688289177Speter 689289177Speter return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, 690289177Speter _("No such revision %ld"), rev); 691289177Speter} 692289177Speter 693289177Speter 694289177Spetersvn_error_t * 695289177Spetersvn_fs_x__file_length(svn_filesize_t *length, 696289177Speter svn_fs_x__noderev_t *noderev) 697289177Speter{ 698289177Speter if (noderev->data_rep) 699289177Speter *length = noderev->data_rep->expanded_size; 700289177Speter else 701289177Speter *length = 0; 702289177Speter 703289177Speter return SVN_NO_ERROR; 704289177Speter} 705289177Speter 706289177Spetersvn_boolean_t 707289177Spetersvn_fs_x__file_text_rep_equal(svn_fs_x__representation_t *a, 708289177Speter svn_fs_x__representation_t *b) 709289177Speter{ 710289177Speter svn_boolean_t a_empty = a == NULL || a->expanded_size == 0; 711289177Speter svn_boolean_t b_empty = b == NULL || b->expanded_size == 0; 712289177Speter 713289177Speter /* This makes sure that neither rep will be NULL later on */ 714289177Speter if (a_empty && b_empty) 715289177Speter return TRUE; 716289177Speter 717289177Speter if (a_empty != b_empty) 718289177Speter return FALSE; 719289177Speter 720289177Speter /* Same physical representation? Note that these IDs are always up-to-date 721289177Speter instead of e.g. being set lazily. */ 722289177Speter if (svn_fs_x__id_eq(&a->id, &b->id)) 723289177Speter return TRUE; 724289177Speter 725289177Speter /* Contents are equal if the checksums match. These are also always known. 726289177Speter */ 727289177Speter return memcmp(a->md5_digest, b->md5_digest, sizeof(a->md5_digest)) == 0 728289177Speter && memcmp(a->sha1_digest, b->sha1_digest, sizeof(a->sha1_digest)) == 0; 729289177Speter} 730289177Speter 731289177Spetersvn_error_t * 732289177Spetersvn_fs_x__prop_rep_equal(svn_boolean_t *equal, 733289177Speter svn_fs_t *fs, 734289177Speter svn_fs_x__noderev_t *a, 735289177Speter svn_fs_x__noderev_t *b, 736289177Speter svn_boolean_t strict, 737289177Speter apr_pool_t *scratch_pool) 738289177Speter{ 739289177Speter svn_fs_x__representation_t *rep_a = a->prop_rep; 740289177Speter svn_fs_x__representation_t *rep_b = b->prop_rep; 741289177Speter apr_hash_t *proplist_a; 742289177Speter apr_hash_t *proplist_b; 743289177Speter 744289177Speter /* Mainly for a==b==NULL */ 745289177Speter if (rep_a == rep_b) 746289177Speter { 747289177Speter *equal = TRUE; 748289177Speter return SVN_NO_ERROR; 749289177Speter } 750289177Speter 751289177Speter /* Committed property lists can be compared quickly */ 752289177Speter if ( rep_a && rep_b 753289177Speter && svn_fs_x__is_revision(rep_a->id.change_set) 754289177Speter && svn_fs_x__is_revision(rep_b->id.change_set)) 755289177Speter { 756289177Speter /* MD5 must be given. Having the same checksum is good enough for 757289177Speter accepting the prop lists as equal. */ 758289177Speter *equal = memcmp(rep_a->md5_digest, rep_b->md5_digest, 759289177Speter sizeof(rep_a->md5_digest)) == 0; 760289177Speter return SVN_NO_ERROR; 761289177Speter } 762289177Speter 763289177Speter /* Same path in same txn? */ 764289177Speter if (svn_fs_x__id_eq(&a->noderev_id, &b->noderev_id)) 765289177Speter { 766289177Speter *equal = TRUE; 767289177Speter return SVN_NO_ERROR; 768289177Speter } 769289177Speter 770289177Speter /* Skip the expensive bits unless we are in strict mode. 771289177Speter Simply assume that there is a different. */ 772289177Speter if (!strict) 773289177Speter { 774289177Speter *equal = FALSE; 775289177Speter return SVN_NO_ERROR; 776289177Speter } 777289177Speter 778289177Speter /* At least one of the reps has been modified in a txn. 779289177Speter Fetch and compare them. */ 780289177Speter SVN_ERR(svn_fs_x__get_proplist(&proplist_a, fs, a, scratch_pool, 781289177Speter scratch_pool)); 782289177Speter SVN_ERR(svn_fs_x__get_proplist(&proplist_b, fs, b, scratch_pool, 783289177Speter scratch_pool)); 784289177Speter 785289177Speter *equal = svn_fs__prop_lists_equal(proplist_a, proplist_b, scratch_pool); 786289177Speter return SVN_NO_ERROR; 787289177Speter} 788289177Speter 789289177Speter 790289177Spetersvn_error_t * 791289177Spetersvn_fs_x__file_checksum(svn_checksum_t **checksum, 792289177Speter svn_fs_x__noderev_t *noderev, 793289177Speter svn_checksum_kind_t kind, 794289177Speter apr_pool_t *result_pool) 795289177Speter{ 796289177Speter *checksum = NULL; 797289177Speter 798289177Speter if (noderev->data_rep) 799289177Speter { 800289177Speter svn_checksum_t temp; 801289177Speter temp.kind = kind; 802289177Speter 803289177Speter switch(kind) 804289177Speter { 805289177Speter case svn_checksum_md5: 806289177Speter temp.digest = noderev->data_rep->md5_digest; 807289177Speter break; 808289177Speter 809289177Speter case svn_checksum_sha1: 810289177Speter if (! noderev->data_rep->has_sha1) 811289177Speter return SVN_NO_ERROR; 812289177Speter 813289177Speter temp.digest = noderev->data_rep->sha1_digest; 814289177Speter break; 815289177Speter 816289177Speter default: 817289177Speter return SVN_NO_ERROR; 818289177Speter } 819289177Speter 820289177Speter *checksum = svn_checksum_dup(&temp, result_pool); 821289177Speter } 822289177Speter 823289177Speter return SVN_NO_ERROR; 824289177Speter} 825289177Speter 826289177Spetersvn_fs_x__representation_t * 827289177Spetersvn_fs_x__rep_copy(svn_fs_x__representation_t *rep, 828289177Speter apr_pool_t *result_pool) 829289177Speter{ 830289177Speter if (rep == NULL) 831289177Speter return NULL; 832289177Speter 833289177Speter return apr_pmemdup(result_pool, rep, sizeof(*rep)); 834289177Speter} 835289177Speter 836289177Speter 837289177Speter/* Write out the zeroth revision for filesystem FS. 838289177Speter Perform temporary allocations in SCRATCH_POOL. */ 839289177Speterstatic svn_error_t * 840289177Speterwrite_revision_zero(svn_fs_t *fs, 841289177Speter apr_pool_t *scratch_pool) 842289177Speter{ 843289177Speter /* Use an explicit sub-pool to have full control over temp file lifetimes. 844289177Speter * Since we have it, use it for everything else as well. */ 845289177Speter apr_pool_t *subpool = svn_pool_create(scratch_pool); 846289177Speter const char *path_revision_zero = svn_fs_x__path_rev(fs, 0, subpool); 847289177Speter apr_hash_t *proplist; 848289177Speter svn_string_t date; 849289177Speter 850289177Speter apr_array_header_t *index_entries; 851289177Speter svn_fs_x__p2l_entry_t *entry; 852289177Speter svn_fs_x__revision_file_t *rev_file; 853289177Speter const char *l2p_proto_index, *p2l_proto_index; 854289177Speter 855289177Speter /* Construct a skeleton r0 with no indexes. */ 856289177Speter svn_string_t *noderev_str = svn_string_create("id: 2+0\n" 857289177Speter "node: 0+0\n" 858289177Speter "copy: 0+0\n" 859289177Speter "type: dir\n" 860289177Speter "count: 0\n" 861289177Speter "cpath: /\n" 862289177Speter "\n", 863289177Speter subpool); 864289177Speter svn_string_t *changes_str = svn_string_create("\n", 865289177Speter subpool); 866289177Speter svn_string_t *r0 = svn_string_createf(subpool, "%s%s", 867289177Speter noderev_str->data, 868289177Speter changes_str->data); 869289177Speter 870289177Speter /* Write skeleton r0 to disk. */ 871289177Speter SVN_ERR(svn_io_file_create(path_revision_zero, r0->data, subpool)); 872289177Speter 873289177Speter /* Construct the index P2L contents: describe the 2 items we have. 874289177Speter Be sure to create them in on-disk order. */ 875289177Speter index_entries = apr_array_make(subpool, 2, sizeof(entry)); 876289177Speter 877289177Speter entry = apr_pcalloc(subpool, sizeof(*entry)); 878289177Speter entry->offset = 0; 879289177Speter entry->size = (apr_off_t)noderev_str->len; 880289177Speter entry->type = SVN_FS_X__ITEM_TYPE_NODEREV; 881289177Speter entry->item_count = 1; 882289177Speter entry->items = apr_pcalloc(subpool, sizeof(*entry->items)); 883289177Speter entry->items[0].change_set = 0; 884289177Speter entry->items[0].number = SVN_FS_X__ITEM_INDEX_ROOT_NODE; 885289177Speter APR_ARRAY_PUSH(index_entries, svn_fs_x__p2l_entry_t *) = entry; 886289177Speter 887289177Speter entry = apr_pcalloc(subpool, sizeof(*entry)); 888289177Speter entry->offset = (apr_off_t)noderev_str->len; 889289177Speter entry->size = (apr_off_t)changes_str->len; 890289177Speter entry->type = SVN_FS_X__ITEM_TYPE_CHANGES; 891289177Speter entry->item_count = 1; 892289177Speter entry->items = apr_pcalloc(subpool, sizeof(*entry->items)); 893289177Speter entry->items[0].change_set = 0; 894289177Speter entry->items[0].number = SVN_FS_X__ITEM_INDEX_CHANGES; 895289177Speter APR_ARRAY_PUSH(index_entries, svn_fs_x__p2l_entry_t *) = entry; 896289177Speter 897289177Speter /* Now re-open r0, create proto-index files from our entries and 898289177Speter rewrite the index section of r0. */ 899289177Speter SVN_ERR(svn_fs_x__open_pack_or_rev_file_writable(&rev_file, fs, 0, 900289177Speter subpool, subpool)); 901289177Speter SVN_ERR(svn_fs_x__p2l_index_from_p2l_entries(&p2l_proto_index, fs, 902289177Speter rev_file, index_entries, 903289177Speter subpool, subpool)); 904289177Speter SVN_ERR(svn_fs_x__l2p_index_from_p2l_entries(&l2p_proto_index, fs, 905289177Speter index_entries, 906289177Speter subpool, subpool)); 907289177Speter SVN_ERR(svn_fs_x__add_index_data(fs, rev_file->file, l2p_proto_index, 908289177Speter p2l_proto_index, 0, subpool)); 909289177Speter SVN_ERR(svn_fs_x__close_revision_file(rev_file)); 910289177Speter 911289177Speter SVN_ERR(svn_io_set_file_read_only(path_revision_zero, FALSE, fs->pool)); 912289177Speter 913289177Speter /* Set a date on revision 0. */ 914289177Speter date.data = svn_time_to_cstring(apr_time_now(), fs->pool); 915289177Speter date.len = strlen(date.data); 916289177Speter proplist = apr_hash_make(fs->pool); 917289177Speter svn_hash_sets(proplist, SVN_PROP_REVISION_DATE, &date); 918289177Speter return svn_fs_x__set_revision_proplist(fs, 0, proplist, fs->pool); 919289177Speter} 920289177Speter 921289177Spetersvn_error_t * 922289177Spetersvn_fs_x__create_file_tree(svn_fs_t *fs, 923289177Speter const char *path, 924289177Speter int format, 925289177Speter int shard_size, 926289177Speter apr_pool_t *scratch_pool) 927289177Speter{ 928289177Speter svn_fs_x__data_t *ffd = fs->fsap_data; 929289177Speter 930289177Speter fs->path = apr_pstrdup(fs->pool, path); 931289177Speter ffd->format = format; 932289177Speter 933289177Speter /* Use an appropriate sharding mode if supported by the format. */ 934289177Speter ffd->max_files_per_dir = shard_size; 935289177Speter 936289177Speter /* Create the revision data directories. */ 937289177Speter SVN_ERR(svn_io_make_dir_recursively( 938289177Speter svn_fs_x__path_rev_shard(fs, 0, scratch_pool), 939289177Speter scratch_pool)); 940289177Speter 941289177Speter /* Create the revprops directory. */ 942289177Speter SVN_ERR(svn_io_make_dir_recursively( 943289177Speter svn_fs_x__path_revprops_shard(fs, 0, scratch_pool), 944289177Speter scratch_pool)); 945289177Speter 946289177Speter /* Create the transaction directory. */ 947289177Speter SVN_ERR(svn_io_make_dir_recursively( 948289177Speter svn_fs_x__path_txns_dir(fs, scratch_pool), 949289177Speter scratch_pool)); 950289177Speter 951289177Speter /* Create the protorevs directory. */ 952289177Speter SVN_ERR(svn_io_make_dir_recursively( 953289177Speter svn_fs_x__path_txn_proto_revs(fs, scratch_pool), 954289177Speter scratch_pool)); 955289177Speter 956289177Speter /* Create the 'current' file. */ 957289177Speter SVN_ERR(svn_io_file_create_empty(svn_fs_x__path_current(fs, scratch_pool), 958289177Speter scratch_pool)); 959289177Speter SVN_ERR(svn_fs_x__write_current(fs, 0, scratch_pool)); 960289177Speter 961289177Speter /* Create the 'uuid' file. */ 962289177Speter SVN_ERR(svn_io_file_create_empty(svn_fs_x__path_lock(fs, scratch_pool), 963289177Speter scratch_pool)); 964289177Speter SVN_ERR(svn_fs_x__set_uuid(fs, NULL, NULL, scratch_pool)); 965289177Speter 966289177Speter /* Create the fsfs.conf file. */ 967289177Speter SVN_ERR(write_config(fs, scratch_pool)); 968289177Speter SVN_ERR(read_config(ffd, fs->path, fs->pool, scratch_pool)); 969289177Speter 970289177Speter /* Add revision 0. */ 971289177Speter SVN_ERR(write_revision_zero(fs, scratch_pool)); 972289177Speter 973289177Speter /* Create the min unpacked rev file. */ 974289177Speter SVN_ERR(svn_io_file_create( 975289177Speter svn_fs_x__path_min_unpacked_rev(fs, scratch_pool), 976289177Speter "0\n", scratch_pool)); 977289177Speter 978289177Speter /* Create the txn-current file if the repository supports 979289177Speter the transaction sequence file. */ 980289177Speter SVN_ERR(svn_io_file_create(svn_fs_x__path_txn_current(fs, scratch_pool), 981289177Speter "0\n", scratch_pool)); 982289177Speter SVN_ERR(svn_io_file_create_empty( 983289177Speter svn_fs_x__path_txn_current_lock(fs, scratch_pool), 984289177Speter scratch_pool)); 985289177Speter 986289177Speter /* Initialize the revprop caching info. */ 987289177Speter SVN_ERR(svn_fs_x__reset_revprop_generation_file(fs, scratch_pool)); 988289177Speter 989289177Speter ffd->youngest_rev_cache = 0; 990289177Speter return SVN_NO_ERROR; 991289177Speter} 992289177Speter 993289177Spetersvn_error_t * 994289177Spetersvn_fs_x__create(svn_fs_t *fs, 995289177Speter const char *path, 996289177Speter apr_pool_t *scratch_pool) 997289177Speter{ 998289177Speter int format = SVN_FS_X__FORMAT_NUMBER; 999289177Speter svn_fs_x__data_t *ffd = fs->fsap_data; 1000289177Speter 1001289177Speter fs->path = apr_pstrdup(fs->pool, path); 1002289177Speter /* See if compatibility with older versions was explicitly requested. */ 1003289177Speter if (fs->config) 1004289177Speter { 1005289177Speter svn_version_t *compatible_version; 1006289177Speter SVN_ERR(svn_fs__compatible_version(&compatible_version, fs->config, 1007289177Speter scratch_pool)); 1008289177Speter 1009289177Speter /* select format number */ 1010289177Speter switch(compatible_version->minor) 1011289177Speter { 1012289177Speter case 0: 1013289177Speter case 1: 1014289177Speter case 2: 1015289177Speter case 3: 1016289177Speter case 4: 1017289177Speter case 5: 1018289177Speter case 6: 1019289177Speter case 7: 1020289177Speter case 8: return svn_error_create(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL, 1021289177Speter _("FSX is not compatible with Subversion prior to 1.9")); 1022289177Speter 1023289177Speter default:format = SVN_FS_X__FORMAT_NUMBER; 1024289177Speter } 1025289177Speter } 1026289177Speter 1027289177Speter /* Actual FS creation. */ 1028289177Speter SVN_ERR(svn_fs_x__create_file_tree(fs, path, format, 1029289177Speter SVN_FS_X_DEFAULT_MAX_FILES_PER_DIR, 1030289177Speter scratch_pool)); 1031289177Speter 1032289177Speter /* This filesystem is ready. Stamp it with a format number. */ 1033289177Speter SVN_ERR(svn_fs_x__write_format(fs, FALSE, scratch_pool)); 1034289177Speter 1035289177Speter ffd->youngest_rev_cache = 0; 1036289177Speter return SVN_NO_ERROR; 1037289177Speter} 1038289177Speter 1039289177Spetersvn_error_t * 1040289177Spetersvn_fs_x__set_uuid(svn_fs_t *fs, 1041289177Speter const char *uuid, 1042289177Speter const char *instance_id, 1043289177Speter apr_pool_t *scratch_pool) 1044289177Speter{ 1045289177Speter svn_fs_x__data_t *ffd = fs->fsap_data; 1046289177Speter const char *uuid_path = svn_fs_x__path_uuid(fs, scratch_pool); 1047289177Speter svn_stringbuf_t *contents = svn_stringbuf_create_empty(scratch_pool); 1048289177Speter 1049289177Speter if (! uuid) 1050289177Speter uuid = svn_uuid_generate(scratch_pool); 1051289177Speter 1052289177Speter if (! instance_id) 1053289177Speter instance_id = svn_uuid_generate(scratch_pool); 1054289177Speter 1055289177Speter svn_stringbuf_appendcstr(contents, uuid); 1056289177Speter svn_stringbuf_appendcstr(contents, "\n"); 1057289177Speter svn_stringbuf_appendcstr(contents, instance_id); 1058289177Speter svn_stringbuf_appendcstr(contents, "\n"); 1059289177Speter 1060289177Speter /* We use the permissions of the 'current' file, because the 'uuid' 1061289177Speter file does not exist during repository creation. */ 1062289177Speter SVN_ERR(svn_io_write_atomic(uuid_path, contents->data, contents->len, 1063289177Speter /* perms */ 1064289177Speter svn_fs_x__path_current(fs, scratch_pool), 1065289177Speter scratch_pool)); 1066289177Speter 1067289177Speter fs->uuid = apr_pstrdup(fs->pool, uuid); 1068289177Speter ffd->instance_id = apr_pstrdup(fs->pool, instance_id); 1069289177Speter 1070289177Speter return SVN_NO_ERROR; 1071289177Speter} 1072289177Speter 1073289177Speter/** Node origin lazy cache. */ 1074289177Speter 1075289177Speter/* If directory PATH does not exist, create it and give it the same 1076289177Speter permissions as FS_path.*/ 1077289177Spetersvn_error_t * 1078289177Spetersvn_fs_x__ensure_dir_exists(const char *path, 1079289177Speter const char *fs_path, 1080289177Speter apr_pool_t *scratch_pool) 1081289177Speter{ 1082289177Speter svn_error_t *err = svn_io_dir_make(path, APR_OS_DEFAULT, scratch_pool); 1083289177Speter if (err && APR_STATUS_IS_EEXIST(err->apr_err)) 1084289177Speter { 1085289177Speter svn_error_clear(err); 1086289177Speter return SVN_NO_ERROR; 1087289177Speter } 1088289177Speter SVN_ERR(err); 1089289177Speter 1090289177Speter /* We successfully created a new directory. Dup the permissions 1091289177Speter from FS->path. */ 1092289177Speter return svn_io_copy_perms(fs_path, path, scratch_pool); 1093289177Speter} 1094289177Speter 1095289177Speter 1096289177Speter/*** Revisions ***/ 1097289177Speter 1098289177Spetersvn_error_t * 1099289177Spetersvn_fs_x__revision_prop(svn_string_t **value_p, 1100289177Speter svn_fs_t *fs, 1101289177Speter svn_revnum_t rev, 1102289177Speter const char *propname, 1103289177Speter apr_pool_t *result_pool, 1104289177Speter apr_pool_t *scratch_pool) 1105289177Speter{ 1106289177Speter apr_hash_t *table; 1107289177Speter 1108289177Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 1109289177Speter SVN_ERR(svn_fs_x__get_revision_proplist(&table, fs, rev, FALSE, 1110289177Speter scratch_pool, scratch_pool)); 1111289177Speter 1112289177Speter *value_p = svn_string_dup(svn_hash_gets(table, propname), result_pool); 1113289177Speter 1114289177Speter return SVN_NO_ERROR; 1115289177Speter} 1116289177Speter 1117289177Speter 1118289177Speter/* Baton used for change_rev_prop_body below. */ 1119289177Spetertypedef struct change_rev_prop_baton_t { 1120289177Speter svn_fs_t *fs; 1121289177Speter svn_revnum_t rev; 1122289177Speter const char *name; 1123289177Speter const svn_string_t *const *old_value_p; 1124289177Speter const svn_string_t *value; 1125289177Speter} change_rev_prop_baton_t; 1126289177Speter 1127289177Speter/* The work-horse for svn_fs_x__change_rev_prop, called with the FS 1128289177Speter write lock. This implements the svn_fs_x__with_write_lock() 1129289177Speter 'body' callback type. BATON is a 'change_rev_prop_baton_t *'. */ 1130289177Speterstatic svn_error_t * 1131289177Speterchange_rev_prop_body(void *baton, 1132289177Speter apr_pool_t *scratch_pool) 1133289177Speter{ 1134289177Speter change_rev_prop_baton_t *cb = baton; 1135289177Speter apr_hash_t *table; 1136289177Speter 1137289177Speter /* Read current revprop values from disk (never from cache). 1138289177Speter Even if somehow the cache got out of sync, we want to make sure that 1139289177Speter we read, update and write up-to-date data. */ 1140289177Speter SVN_ERR(svn_fs_x__get_revision_proplist(&table, cb->fs, cb->rev, TRUE, 1141289177Speter scratch_pool, scratch_pool)); 1142289177Speter 1143289177Speter if (cb->old_value_p) 1144289177Speter { 1145289177Speter const svn_string_t *wanted_value = *cb->old_value_p; 1146289177Speter const svn_string_t *present_value = svn_hash_gets(table, cb->name); 1147289177Speter if ((!wanted_value != !present_value) 1148289177Speter || (wanted_value && present_value 1149289177Speter && !svn_string_compare(wanted_value, present_value))) 1150289177Speter { 1151289177Speter /* What we expected isn't what we found. */ 1152289177Speter return svn_error_createf(SVN_ERR_FS_PROP_BASEVALUE_MISMATCH, NULL, 1153289177Speter _("revprop '%s' has unexpected value in " 1154289177Speter "filesystem"), 1155289177Speter cb->name); 1156289177Speter } 1157289177Speter /* Fall through. */ 1158289177Speter } 1159289177Speter svn_hash_sets(table, cb->name, cb->value); 1160289177Speter 1161289177Speter return svn_fs_x__set_revision_proplist(cb->fs, cb->rev, table, 1162289177Speter scratch_pool); 1163289177Speter} 1164289177Speter 1165289177Spetersvn_error_t * 1166289177Spetersvn_fs_x__change_rev_prop(svn_fs_t *fs, 1167289177Speter svn_revnum_t rev, 1168289177Speter const char *name, 1169289177Speter const svn_string_t *const *old_value_p, 1170289177Speter const svn_string_t *value, 1171289177Speter apr_pool_t *scratch_pool) 1172289177Speter{ 1173289177Speter change_rev_prop_baton_t cb; 1174289177Speter 1175289177Speter SVN_ERR(svn_fs__check_fs(fs, TRUE)); 1176289177Speter 1177289177Speter cb.fs = fs; 1178289177Speter cb.rev = rev; 1179289177Speter cb.name = name; 1180289177Speter cb.old_value_p = old_value_p; 1181289177Speter cb.value = value; 1182289177Speter 1183289177Speter return svn_fs_x__with_write_lock(fs, change_rev_prop_body, &cb, 1184289177Speter scratch_pool); 1185289177Speter} 1186289177Speter 1187289177Speter 1188289177Spetersvn_error_t * 1189289177Spetersvn_fs_x__info_format(int *fs_format, 1190289177Speter svn_version_t **supports_version, 1191289177Speter svn_fs_t *fs, 1192289177Speter apr_pool_t *result_pool, 1193289177Speter apr_pool_t *scratch_pool) 1194289177Speter{ 1195289177Speter svn_fs_x__data_t *ffd = fs->fsap_data; 1196289177Speter *fs_format = ffd->format; 1197289177Speter *supports_version = apr_palloc(result_pool, sizeof(svn_version_t)); 1198289177Speter 1199289177Speter (*supports_version)->major = SVN_VER_MAJOR; 1200289177Speter (*supports_version)->minor = 9; 1201289177Speter (*supports_version)->patch = 0; 1202289177Speter (*supports_version)->tag = ""; 1203289177Speter 1204289177Speter switch (ffd->format) 1205289177Speter { 1206289177Speter case 1: 1207289177Speter break; 1208289177Speter#ifdef SVN_DEBUG 1209289177Speter# if SVN_FS_X__FORMAT_NUMBER != 1 1210289177Speter# error "Need to add a 'case' statement here" 1211289177Speter# endif 1212289177Speter#endif 1213289177Speter } 1214289177Speter 1215289177Speter return SVN_NO_ERROR; 1216289177Speter} 1217289177Speter 1218289177Spetersvn_error_t * 1219289177Spetersvn_fs_x__info_config_files(apr_array_header_t **files, 1220289177Speter svn_fs_t *fs, 1221289177Speter apr_pool_t *result_pool, 1222289177Speter apr_pool_t *scratch_pool) 1223289177Speter{ 1224289177Speter *files = apr_array_make(result_pool, 1, sizeof(const char *)); 1225289177Speter APR_ARRAY_PUSH(*files, const char *) = svn_dirent_join(fs->path, PATH_CONFIG, 1226289177Speter result_pool); 1227289177Speter return SVN_NO_ERROR; 1228289177Speter} 1229