diff options
Diffstat (limited to 'subversion/libsvn_fs_fs/fs_fs.c')
-rw-r--r-- | subversion/libsvn_fs_fs/fs_fs.c | 370 |
1 files changed, 255 insertions, 115 deletions
diff --git a/subversion/libsvn_fs_fs/fs_fs.c b/subversion/libsvn_fs_fs/fs_fs.c index 103458d8ef0ab..5cb1362d84515 100644 --- a/subversion/libsvn_fs_fs/fs_fs.c +++ b/subversion/libsvn_fs_fs/fs_fs.c @@ -623,8 +623,9 @@ svn_fs_fs__write_format(svn_fs_t *fs, } else { - SVN_ERR(svn_io_write_atomic(path, sb->data, sb->len, - NULL /* copy_perms_path */, pool)); + SVN_ERR(svn_io_write_atomic2(path, sb->data, sb->len, + NULL /* copy_perms_path */, + ffd->flush_to_disk, pool)); } /* And set the perms to make it read only */ @@ -682,6 +683,60 @@ verify_block_size(apr_int64_t block_size, return SVN_NO_ERROR; } +static svn_error_t * +parse_compression_option(compression_type_t *compression_type_p, + int *compression_level_p, + const char *value) +{ + compression_type_t type; + int level; + svn_boolean_t is_valid = TRUE; + + /* compression = none | lz4 | zlib | zlib-1 ... zlib-9 */ + if (strcmp(value, "none") == 0) + { + type = compression_type_none; + level = SVN_DELTA_COMPRESSION_LEVEL_NONE; + } + else if (strcmp(value, "lz4") == 0) + { + type = compression_type_lz4; + level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT; + } + else if (strncmp(value, "zlib", 4) == 0) + { + const char *p = value + 4; + + type = compression_type_zlib; + if (*p == 0) + { + level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT; + } + else if (*p == '-') + { + p++; + SVN_ERR(svn_cstring_atoi(&level, p)); + if (level < 1 || level > 9) + is_valid = FALSE; + } + else + is_valid = FALSE; + } + else + { + is_valid = FALSE; + } + + if (!is_valid) + return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, + _("Invalid 'compression' value '%s' in the config"), + value); + + *compression_type_p = type; + *compression_level_p = level; + return SVN_NO_ERROR; +} + /* Read the configuration information of the file system at FS_PATH * and set the respective values in FFD. Use pools as usual. */ @@ -708,8 +763,6 @@ read_config(fs_fs_data_t *ffd, /* Initialize deltification settings in ffd. */ if (ffd->format >= SVN_FS_FS__MIN_DELTIFICATION_FORMAT) { - apr_int64_t compression_level; - SVN_ERR(svn_config_get_bool(config, &ffd->deltify_directories, CONFIG_SECTION_DELTIFICATION, CONFIG_OPTION_ENABLE_DIR_DELTIFICATION, @@ -726,14 +779,6 @@ read_config(fs_fs_data_t *ffd, CONFIG_SECTION_DELTIFICATION, CONFIG_OPTION_MAX_LINEAR_DELTIFICATION, SVN_FS_FS_MAX_LINEAR_DELTIFICATION)); - - SVN_ERR(svn_config_get_int64(config, &compression_level, - CONFIG_SECTION_DELTIFICATION, - CONFIG_OPTION_COMPRESSION_LEVEL, - SVN_DELTA_COMPRESSION_LEVEL_DEFAULT)); - ffd->delta_compression_level - = (int)MIN(MAX(SVN_DELTA_COMPRESSION_LEVEL_NONE, compression_level), - SVN_DELTA_COMPRESSION_LEVEL_MAX); } else { @@ -741,7 +786,6 @@ read_config(fs_fs_data_t *ffd, ffd->deltify_properties = FALSE; ffd->max_deltification_walk = SVN_FS_FS_MAX_DELTIFICATION_WALK; ffd->max_linear_deltification = SVN_FS_FS_MAX_LINEAR_DELTIFICATION; - ffd->delta_compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT; } /* Initialize revprop packing settings in ffd. */ @@ -755,8 +799,8 @@ read_config(fs_fs_data_t *ffd, CONFIG_SECTION_PACKED_REVPROPS, CONFIG_OPTION_REVPROP_PACK_SIZE, ffd->compress_packed_revprops - ? 0x10 - : 0x4)); + ? 0x40 + : 0x10)); ffd->revprop_pack_size *= 1024; } @@ -816,6 +860,83 @@ read_config(fs_fs_data_t *ffd, ffd->pack_after_commit = FALSE; } + /* Initialize compression settings in ffd. */ + if (ffd->format >= SVN_FS_FS__MIN_DELTIFICATION_FORMAT) + { + const char *compression_val; + const char *compression_level_val; + + svn_config_get(config, &compression_val, + CONFIG_SECTION_DELTIFICATION, + CONFIG_OPTION_COMPRESSION, NULL); + svn_config_get(config, &compression_level_val, + CONFIG_SECTION_DELTIFICATION, + CONFIG_OPTION_COMPRESSION_LEVEL, NULL); + if (compression_val && compression_level_val) + { + return svn_error_create(SVN_ERR_BAD_CONFIG_VALUE, NULL, + _("The 'compression' and 'compression-level' " + "config options are mutually exclusive")); + } + else if (compression_val) + { + SVN_ERR(parse_compression_option(&ffd->delta_compression_type, + &ffd->delta_compression_level, + compression_val)); + if (ffd->delta_compression_type == compression_type_lz4 && + ffd->format < SVN_FS_FS__MIN_SVNDIFF2_FORMAT) + { + return svn_error_create(SVN_ERR_BAD_CONFIG_VALUE, NULL, + _("Compression type 'lz4' requires " + "filesystem format 8 or higher")); + } + } + else if (compression_level_val) + { + /* Handle the deprecated 'compression-level' option. */ + ffd->delta_compression_type = compression_type_zlib; + SVN_ERR(svn_cstring_atoi(&ffd->delta_compression_level, + compression_level_val)); + ffd->delta_compression_level = + MIN(MAX(SVN_DELTA_COMPRESSION_LEVEL_NONE, + ffd->delta_compression_level), + SVN_DELTA_COMPRESSION_LEVEL_MAX); + } + else + { + /* Nothing specified explicitly, use the default settings: + * LZ4 compression for formats supporting it and zlib otherwise. */ + if (ffd->format >= SVN_FS_FS__MIN_SVNDIFF2_FORMAT) + ffd->delta_compression_type = compression_type_lz4; + else + ffd->delta_compression_type = compression_type_zlib; + + ffd->delta_compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT; + } + } + else if (ffd->format >= SVN_FS_FS__MIN_SVNDIFF1_FORMAT) + { + ffd->delta_compression_type = compression_type_zlib; + ffd->delta_compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT; + } + else + { + ffd->delta_compression_type = compression_type_none; + ffd->delta_compression_level = SVN_DELTA_COMPRESSION_LEVEL_NONE; + } + +#ifdef SVN_DEBUG + SVN_ERR(svn_config_get_bool(config, &ffd->verify_before_commit, + CONFIG_SECTION_DEBUG, + CONFIG_OPTION_VERIFY_BEFORE_COMMIT, + TRUE)); +#else + SVN_ERR(svn_config_get_bool(config, &ffd->verify_before_commit, + CONFIG_SECTION_DEBUG, + CONFIG_OPTION_VERIFY_BEFORE_COMMIT, + FALSE)); +#endif + /* memcached configuration */ SVN_ERR(svn_cache__make_memcache_from_config(&ffd->memcache, config, result_pool, scratch_pool)); @@ -934,23 +1055,30 @@ write_config(svn_fs_t *fs, "### For 1.8, the default value is 16; earlier versions use 1." NL "# " CONFIG_OPTION_MAX_LINEAR_DELTIFICATION " = 16" NL "###" NL -"### After deltification, we compress the data through zlib to minimize on-" NL -"### disk size. That can be an expensive and ineffective process. This" NL -"### setting controls the usage of zlib in future revisions." NL -"### Revisions with highly compressible data in them may shrink in size" NL -"### if the setting is increased but may take much longer to commit. The" NL -"### time taken to uncompress that data again is widely independent of the" NL -"### compression level." NL -"### Compression will be ineffective if the incoming content is already" NL -"### highly compressed. In that case, disabling the compression entirely" NL -"### will speed up commits as well as reading the data. Repositories with" NL -"### many small compressible files (source code) but also a high percentage" NL -"### of large incompressible ones (artwork) may benefit from compression" NL -"### levels lowered to e.g. 1." NL -"### Valid values are 0 to 9 with 9 providing the highest compression ratio" NL -"### and 0 disabling it altogether." NL -"### The default value is 5." NL -"# " CONFIG_OPTION_COMPRESSION_LEVEL " = 5" NL +"### After deltification, we compress the data to minimize on-disk size." NL +"### This setting controls the compression algorithm, which will be used in" NL +"### future revisions. It can be used to either disable compression or to" NL +"### select between available algorithms (zlib, lz4). zlib is a general-" NL +"### purpose compression algorithm. lz4 is a fast compression algorithm" NL +"### which should be preferred for repositories with large and, possibly," NL +"### incompressible files. Note that the compression ratio of lz4 is" NL +"### usually lower than the one provided by zlib, but using it can" NL +"### significantly speed up commits as well as reading the data." NL +"### lz4 compression algorithm is supported, starting from format 8" NL +"### repositories, available in Subversion 1.10 and higher." NL +"### The syntax of this option is:" NL +"### " CONFIG_OPTION_COMPRESSION " = none | lz4 | zlib | zlib-1 ... zlib-9" NL +"### Versions prior to Subversion 1.10 will ignore this option." NL +"### The default value is 'lz4' if supported by the repository format and" NL +"### 'zlib' otherwise. 'zlib' is currently equivalent to 'zlib-5'." NL +"# " CONFIG_OPTION_COMPRESSION " = lz4" NL +"###" NL +"### DEPRECATED: The new '" CONFIG_OPTION_COMPRESSION "' option deprecates previously used" NL +"### '" CONFIG_OPTION_COMPRESSION_LEVEL "' option, which was used to configure zlib compression." NL +"### For compatibility with previous versions of Subversion, this option can"NL +"### still be used (and it will result in zlib compression with the" NL +"### corresponding compression level)." NL +"### " CONFIG_OPTION_COMPRESSION_LEVEL " = 0 ... 9 (default is 5)" NL "" NL "[" CONFIG_SECTION_PACKED_REVPROPS "]" NL "### This parameter controls the size (in kBytes) of packed revprop files." NL @@ -963,9 +1091,9 @@ write_config(svn_fs_t *fs, "### latency and CPU usage reading and changing individual revprops." NL "### Values smaller than 4 kByte will not improve latency any further and " NL "### quickly render revprop packing ineffective." NL -"### revprop-pack-size is 4 kBytes by default for non-compressed revprop" NL -"### pack files and 16 kBytes when compression has been enabled." NL -"# " CONFIG_OPTION_REVPROP_PACK_SIZE " = 4" NL +"### revprop-pack-size is 16 kBytes by default for non-compressed revprop" NL +"### pack files and 64 kBytes when compression has been enabled." NL +"# " CONFIG_OPTION_REVPROP_PACK_SIZE " = 16" NL "###" NL "### To save disk space, packed revprop files may be compressed. Standard" NL "### revprops tend to allow for very effective compression. Reading and" NL @@ -1019,6 +1147,13 @@ write_config(svn_fs_t *fs, "### Must be a power of 2." NL "### p2l-page-size is given in kBytes and with a default of 1024 kBytes." NL "# " CONFIG_OPTION_P2L_PAGE_SIZE " = 1024" NL +"" NL +"[" CONFIG_SECTION_DEBUG "]" NL +"###" NL +"### Whether to verify each new revision immediately before finalizing" NL +"### the commit. This is disabled by default except in maintainer-mode" NL +"### builds." NL +"# " CONFIG_OPTION_VERIFY_BEFORE_COMMIT " = false" NL ; #undef NL return svn_io_file_create(svn_dirent_join(fs->path, PATH_CONFIG, pool), @@ -1032,13 +1167,12 @@ read_global_config(svn_fs_t *fs) { fs_fs_data_t *ffd = fs->fsap_data; - /* Providing a config hash is optional. */ - if (fs->config) - ffd->use_block_read = svn_hash__get_bool(fs->config, - SVN_FS_CONFIG_FSFS_BLOCK_READ, - FALSE); - else - ffd->use_block_read = FALSE; + ffd->use_block_read = svn_hash__get_bool(fs->config, + SVN_FS_CONFIG_FSFS_BLOCK_READ, + FALSE); + ffd->flush_to_disk = !svn_hash__get_bool(fs->config, + SVN_FS_CONFIG_NO_FLUSH_TO_DISK, + FALSE); /* Ignore the user-specified larger block size if we don't use block-read. Defaulting to 4k gives us the same access granularity in format 7 as in @@ -1127,7 +1261,9 @@ svn_fs_fs__open(svn_fs_t *fs, const char *path, apr_pool_t *pool) /* Global configuration options. */ SVN_ERR(read_global_config(fs)); - return get_youngest(&(ffd->youngest_rev_cache), fs, pool); + ffd->youngest_rev_cache = 0; + + return SVN_NO_ERROR; } /* Wrapper around svn_io_file_create which ignores EEXIST. */ @@ -1334,7 +1470,10 @@ svn_fs_fs__min_unpacked_rev(svn_revnum_t *min_unpacked, { fs_fs_data_t *ffd = fs->fsap_data; - SVN_ERR(svn_fs_fs__update_min_unpacked_rev(fs, pool)); + /* Calling this for pre-v4 repos is illegal. */ + if (ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT) + SVN_ERR(svn_fs_fs__update_min_unpacked_rev(fs, pool)); + *min_unpacked = ffd->min_unpacked_rev; return SVN_NO_ERROR; @@ -1378,38 +1517,9 @@ svn_fs_fs__file_length(svn_filesize_t *length, /* Treat "no representation" as "empty file". */ *length = 0; } - else if (data_rep->expanded_size) - { - /* Standard case: a non-empty file. */ - *length = data_rep->expanded_size; - } else { - /* Work around a FSFS format quirk (see issue #4554). - - A plain representation may specify its EXPANDED LENGTH as "0" - in which case, the SIZE value is what we want. - - Because EXPANDED_LENGTH will also be 0 for empty files, while - SIZE is non-null, we need to check wether the content is - actually empty. We simply compare with the MD5 checksum of - empty content (sha-1 is not always available). - */ - svn_checksum_t *empty_md5 - = svn_checksum_empty_checksum(svn_checksum_md5, pool); - - if (memcmp(empty_md5->digest, data_rep->md5_digest, - sizeof(data_rep->md5_digest))) - { - /* Contents is not empty, i.e. EXPANDED_LENGTH cannot be the - actual file length. */ - *length = data_rep->size; - } - else - { - /* Contents is empty. */ - *length = 0; - } + *length = data_rep->expanded_size; } return SVN_NO_ERROR; @@ -1444,8 +1554,8 @@ svn_fs_fs__file_text_rep_equal(svn_boolean_t *equal, svn_stream_t *contents_a, *contents_b; representation_t *rep_a = a->data_rep; representation_t *rep_b = b->data_rep; - svn_boolean_t a_empty = !rep_a; - svn_boolean_t b_empty = !rep_b; + svn_boolean_t a_empty = !rep_a || rep_a->expanded_size == 0; + svn_boolean_t b_empty = !rep_b || rep_b->expanded_size == 0; /* This makes sure that neither rep will be NULL later on */ if (a_empty && b_empty) @@ -1454,35 +1564,33 @@ svn_fs_fs__file_text_rep_equal(svn_boolean_t *equal, return SVN_NO_ERROR; } - /* Same path in same rev or txn? */ - if (svn_fs_fs__id_eq(a->id, b->id)) + if (a_empty != b_empty) { - *equal = TRUE; + *equal = FALSE; return SVN_NO_ERROR; } - /* Beware of the combination NULL rep and possibly empty rep. - * Due to EXPANDED_SIZE not being reliable, we can't easily detect empty - * reps. So, we can only take further shortcuts if both reps are given. */ - if (!a_empty && !b_empty) + /* File text representations always know their checksums - even in a txn. */ + if (memcmp(rep_a->md5_digest, rep_b->md5_digest, sizeof(rep_a->md5_digest))) { - /* File text representations always know their checksums - - * even in a txn. */ - if (memcmp(rep_a->md5_digest, rep_b->md5_digest, - sizeof(rep_a->md5_digest))) - { - *equal = FALSE; - return SVN_NO_ERROR; - } + *equal = FALSE; + return SVN_NO_ERROR; + } - /* Paranoia. Compare SHA1 checksums because that's the level of - confidence we require for e.g. the working copy. */ - if (rep_a->has_sha1 && rep_b->has_sha1) - { - *equal = memcmp(rep_a->sha1_digest, rep_b->sha1_digest, - sizeof(rep_a->sha1_digest)) == 0; - return SVN_NO_ERROR; - } + /* Paranoia. Compare SHA1 checksums because that's the level of + confidence we require for e.g. the working copy. */ + if (rep_a->has_sha1 && rep_b->has_sha1) + { + *equal = memcmp(rep_a->sha1_digest, rep_b->sha1_digest, + sizeof(rep_a->sha1_digest)) == 0; + return SVN_NO_ERROR; + } + + /* Same path in same rev or txn? */ + if (svn_fs_fs__id_eq(a->id, b->id)) + { + *equal = TRUE; + return SVN_NO_ERROR; } SVN_ERR(svn_fs_fs__get_contents(&contents_a, fs, rep_a, TRUE, @@ -1519,14 +1627,27 @@ svn_fs_fs__prop_rep_equal(svn_boolean_t *equal, && !svn_fs_fs__id_txn_used(&rep_a->txn_id) && !svn_fs_fs__id_txn_used(&rep_b->txn_id)) { - /* MD5 must be given. Having the same checksum is good enough for - accepting the prop lists as equal. */ - *equal = memcmp(rep_a->md5_digest, rep_b->md5_digest, - sizeof(rep_a->md5_digest)) == 0; - return SVN_NO_ERROR; + /* Same representation? */ + if ( (rep_a->revision == rep_b->revision) + && (rep_a->item_index == rep_b->item_index)) + { + *equal = TRUE; + return SVN_NO_ERROR; + } + + /* Known different content? MD5 must be given. */ + if (memcmp(rep_a->md5_digest, rep_b->md5_digest, + sizeof(rep_a->md5_digest))) + { + *equal = FALSE; + return SVN_NO_ERROR; + } } - /* Same path in same txn? */ + /* Same path in same txn? + * + * For committed reps, IDs cannot be the same here b/c we already know + * that they point to different representations. */ if (svn_fs_fs__id_eq(a->id, b->id)) { *equal = TRUE; @@ -1827,6 +1948,8 @@ svn_fs_fs__create(svn_fs_t *fs, case 8: format = 6; break; + case 9: format = 7; + break; default:format = SVN_FS_FS__FORMAT_NUMBER; } @@ -1883,9 +2006,9 @@ svn_fs_fs__set_uuid(svn_fs_t *fs, /* We use the permissions of the 'current' file, because the 'uuid' file does not exist during repository creation. */ - SVN_ERR(svn_io_write_atomic(uuid_path, contents->data, contents->len, - svn_fs_fs__path_current(fs, pool) /* perms */, - pool)); + SVN_ERR(svn_io_write_atomic2(uuid_path, contents->data, contents->len, + svn_fs_fs__path_current(fs, pool) /* perms */, + ffd->flush_to_disk, pool)); fs->uuid = apr_pstrdup(fs->pool, uuid); @@ -2036,7 +2159,7 @@ set_node_origins_for_file(svn_fs_t *fs, SVN_ERR(svn_stream_close(stream)); /* Rename the temp file as the real destination */ - return svn_io_file_rename(path_tmp, node_origins_path, pool); + return svn_io_file_rename2(path_tmp, node_origins_path, FALSE, pool); } @@ -2071,14 +2194,17 @@ svn_fs_fs__revision_prop(svn_string_t **value_p, svn_fs_t *fs, svn_revnum_t rev, const char *propname, - apr_pool_t *pool) + svn_boolean_t refresh, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { apr_hash_t *table; SVN_ERR(svn_fs__check_fs(fs, TRUE)); - SVN_ERR(svn_fs_fs__get_revision_proplist(&table, fs, rev, pool)); + SVN_ERR(svn_fs_fs__get_revision_proplist(&table, fs, rev, refresh, + scratch_pool, scratch_pool)); - *value_p = svn_hash_gets(table, propname); + *value_p = svn_string_dup(svn_hash_gets(table, propname), result_pool); return SVN_NO_ERROR; } @@ -2101,13 +2227,17 @@ change_rev_prop_body(void *baton, apr_pool_t *pool) { struct change_rev_prop_baton *cb = baton; apr_hash_t *table; + const svn_string_t *present_value; - SVN_ERR(svn_fs_fs__get_revision_proplist(&table, cb->fs, cb->rev, pool)); + /* We always need to read the current revprops from disk. + * Hence, always "refresh" here. */ + SVN_ERR(svn_fs_fs__get_revision_proplist(&table, cb->fs, cb->rev, TRUE, + pool, pool)); + present_value = svn_hash_gets(table, cb->name); if (cb->old_value_p) { const svn_string_t *wanted_value = *cb->old_value_p; - const svn_string_t *present_value = svn_hash_gets(table, cb->name); if ((!wanted_value != !present_value) || (wanted_value && present_value && !svn_string_compare(wanted_value, present_value))) @@ -2120,6 +2250,13 @@ change_rev_prop_body(void *baton, apr_pool_t *pool) } /* Fall through. */ } + + /* If the prop-set is a no-op, skip the actual write. */ + if ((!present_value && !cb->value) + || (present_value && cb->value + && svn_string_compare(present_value, cb->value))) + return SVN_NO_ERROR; + svn_hash_sets(table, cb->name, cb->value); return svn_fs_fs__set_revision_proplist(cb->fs, cb->rev, table, pool); @@ -2182,8 +2319,11 @@ svn_fs_fs__info_format(int *fs_format, case 7: (*supports_version)->minor = 9; break; + case 8: + (*supports_version)->minor = 10; + break; #ifdef SVN_DEBUG -# if SVN_FS_FS__FORMAT_NUMBER != 7 +# if SVN_FS_FS__FORMAT_NUMBER != 8 # error "Need to add a 'case' statement here" # endif #endif |