diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
commit | cf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch) | |
tree | da27775a2161723ef342e91af41a8b51fedef405 /subversion/libsvn_fs_fs/util.c | |
parent | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff) | |
download | subversion-tarball-master.tar.gz |
subversion-1.9.7HEADsubversion-1.9.7master
Diffstat (limited to 'subversion/libsvn_fs_fs/util.c')
-rw-r--r-- | subversion/libsvn_fs_fs/util.c | 694 |
1 files changed, 694 insertions, 0 deletions
diff --git a/subversion/libsvn_fs_fs/util.c b/subversion/libsvn_fs_fs/util.c new file mode 100644 index 0000000..faa1e3d --- /dev/null +++ b/subversion/libsvn_fs_fs/util.c @@ -0,0 +1,694 @@ +/* util.c --- utility functions for FSFS repo access + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + +#include <assert.h> + +#include "svn_ctype.h" +#include "svn_dirent_uri.h" +#include "private/svn_string_private.h" + +#include "fs_fs.h" +#include "pack.h" +#include "util.h" + +#include "../libsvn_fs/fs-loader.h" + +#include "svn_private_config.h" + +svn_boolean_t +svn_fs_fs__is_packed_rev(svn_fs_t *fs, + svn_revnum_t rev) +{ + fs_fs_data_t *ffd = fs->fsap_data; + + return (rev < ffd->min_unpacked_rev); +} + +svn_boolean_t +svn_fs_fs__is_packed_revprop(svn_fs_t *fs, + svn_revnum_t rev) +{ + fs_fs_data_t *ffd = fs->fsap_data; + + /* rev 0 will not be packed */ + return (rev < ffd->min_unpacked_rev) + && (rev != 0) + && (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT); +} + +svn_revnum_t +svn_fs_fs__packed_base_rev(svn_fs_t *fs, + svn_revnum_t revision) +{ + fs_fs_data_t *ffd = fs->fsap_data; + return (revision < ffd->min_unpacked_rev) + ? (revision - (revision % ffd->max_files_per_dir)) + : revision; +} + +const char * +svn_fs_fs__path_txn_current(svn_fs_t *fs, + apr_pool_t *pool) +{ + return svn_dirent_join(fs->path, PATH_TXN_CURRENT, pool); +} + +const char * +svn_fs_fs__path_txn_current_lock(svn_fs_t *fs, + apr_pool_t *pool) +{ + return svn_dirent_join(fs->path, PATH_TXN_CURRENT_LOCK, pool); +} + +const char * +svn_fs_fs__path_lock(svn_fs_t *fs, + apr_pool_t *pool) +{ + return svn_dirent_join(fs->path, PATH_LOCK_FILE, pool); +} + +const char * +svn_fs_fs__path_pack_lock(svn_fs_t *fs, + apr_pool_t *pool) +{ + return svn_dirent_join(fs->path, PATH_PACK_LOCK_FILE, pool); +} + +const char * +svn_fs_fs__path_revprop_generation(svn_fs_t *fs, + apr_pool_t *pool) +{ + return svn_dirent_join(fs->path, PATH_REVPROP_GENERATION, pool); +} + +const char * +svn_fs_fs__path_rev_packed(svn_fs_t *fs, + svn_revnum_t rev, + const char *kind, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + + assert(ffd->max_files_per_dir); + assert(svn_fs_fs__is_packed_rev(fs, rev)); + + return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR, + apr_psprintf(pool, + "%ld" PATH_EXT_PACKED_SHARD, + rev / ffd->max_files_per_dir), + kind, SVN_VA_NULL); +} + +const char * +svn_fs_fs__path_rev_shard(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + + assert(ffd->max_files_per_dir); + return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR, + apr_psprintf(pool, "%ld", + rev / ffd->max_files_per_dir), + SVN_VA_NULL); +} + +const char * +svn_fs_fs__path_rev(svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + + assert(! svn_fs_fs__is_packed_rev(fs, rev)); + + if (ffd->max_files_per_dir) + { + return svn_dirent_join(svn_fs_fs__path_rev_shard(fs, rev, pool), + apr_psprintf(pool, "%ld", rev), + pool); + } + + return svn_dirent_join_many(pool, fs->path, PATH_REVS_DIR, + apr_psprintf(pool, "%ld", rev), SVN_VA_NULL); +} + +/* Set *PATH to the path of REV in FS with PACKED selecting whether the + (potential) pack file or single revision file name is returned. + Allocate *PATH in POOL. +*/ +static const char * +path_rev_absolute_internal(svn_fs_t *fs, + svn_revnum_t rev, + svn_boolean_t packed, + apr_pool_t *pool) +{ + return packed + ? svn_fs_fs__path_rev_packed(fs, rev, PATH_PACKED, pool) + : svn_fs_fs__path_rev(fs, rev, pool); +} + +const char * +svn_fs_fs__path_rev_absolute(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + svn_boolean_t is_packed = ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT + && svn_fs_fs__is_packed_rev(fs, rev); + + return path_rev_absolute_internal(fs, rev, is_packed, pool); +} + +const char * +svn_fs_fs__path_revprops_shard(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + + assert(ffd->max_files_per_dir); + return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR, + apr_psprintf(pool, "%ld", + rev / ffd->max_files_per_dir), + SVN_VA_NULL); +} + +const char * +svn_fs_fs__path_revprops_pack_shard(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + + assert(ffd->max_files_per_dir); + return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR, + apr_psprintf(pool, "%ld" PATH_EXT_PACKED_SHARD, + rev / ffd->max_files_per_dir), + SVN_VA_NULL); +} + +const char * +svn_fs_fs__path_revprops(svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + + if (ffd->max_files_per_dir) + { + return svn_dirent_join(svn_fs_fs__path_revprops_shard(fs, rev, pool), + apr_psprintf(pool, "%ld", rev), + pool); + } + + return svn_dirent_join_many(pool, fs->path, PATH_REVPROPS_DIR, + apr_psprintf(pool, "%ld", rev), SVN_VA_NULL); +} + +/* Return TO_ADD appended to the C string representation of TXN_ID. + * Allocate the result in POOL. + */ +static const char * +combine_txn_id_string(const svn_fs_fs__id_part_t *txn_id, + const char *to_add, + apr_pool_t *pool) +{ + return apr_pstrcat(pool, svn_fs_fs__id_txn_unparse(txn_id, pool), + to_add, SVN_VA_NULL); +} + +const char * +svn_fs_fs__path_txns_dir(svn_fs_t *fs, + apr_pool_t *pool) +{ + return svn_dirent_join(fs->path, PATH_TXNS_DIR, pool); +} + +const char * +svn_fs_fs__path_txn_dir(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + SVN_ERR_ASSERT_NO_RETURN(txn_id != NULL); + return svn_dirent_join(svn_fs_fs__path_txns_dir(fs, pool), + combine_txn_id_string(txn_id, PATH_EXT_TXN, pool), + pool); +} + +const char* +svn_fs_fs__path_l2p_proto_index(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), + PATH_INDEX PATH_EXT_L2P_INDEX, pool); +} + +const char* +svn_fs_fs__path_p2l_proto_index(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), + PATH_INDEX PATH_EXT_P2L_INDEX, pool); +} + +const char * +svn_fs_fs__path_txn_item_index(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), + PATH_TXN_ITEM_INDEX, pool); +} + +const char * +svn_fs_fs__path_txn_proto_revs(svn_fs_t *fs, + apr_pool_t *pool) +{ + return svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, pool); +} + +const char * +svn_fs_fs__path_txn_proto_rev(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT) + return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool), + combine_txn_id_string(txn_id, PATH_EXT_REV, pool), + pool); + else + return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), + PATH_REV, pool); +} + + +const char * +svn_fs_fs__path_txn_proto_rev_lock(svn_fs_t *fs, + const svn_fs_fs__id_part_t *txn_id, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT) + return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool), + combine_txn_id_string(txn_id, PATH_EXT_REV_LOCK, + pool), + pool); + else + return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), + PATH_REV_LOCK, pool); +} + +const char * +svn_fs_fs__path_txn_node_rev(svn_fs_t *fs, + const svn_fs_id_t *id, + apr_pool_t *pool) +{ + char *filename = (char *)svn_fs_fs__id_unparse(id, pool)->data; + *strrchr(filename, '.') = '\0'; + + return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, svn_fs_fs__id_txn_id(id), + pool), + apr_psprintf(pool, PATH_PREFIX_NODE "%s", + filename), + pool); +} + +const char * +svn_fs_fs__path_txn_node_props(svn_fs_t *fs, + const svn_fs_id_t *id, + apr_pool_t *pool) +{ + return apr_pstrcat(pool, svn_fs_fs__path_txn_node_rev(fs, id, pool), + PATH_EXT_PROPS, SVN_VA_NULL); +} + +const char * +svn_fs_fs__path_txn_node_children(svn_fs_t *fs, + const svn_fs_id_t *id, + apr_pool_t *pool) +{ + return apr_pstrcat(pool, svn_fs_fs__path_txn_node_rev(fs, id, pool), + PATH_EXT_CHILDREN, SVN_VA_NULL); +} + +const char * +svn_fs_fs__path_node_origin(svn_fs_t *fs, + const svn_fs_fs__id_part_t *node_id, + apr_pool_t *pool) +{ + char buffer[SVN_INT64_BUFFER_SIZE]; + apr_size_t len = svn__ui64tobase36(buffer, node_id->number); + + if (len > 1) + buffer[len - 1] = '\0'; + + return svn_dirent_join_many(pool, fs->path, PATH_NODE_ORIGINS_DIR, + buffer, SVN_VA_NULL); +} + +const char * +svn_fs_fs__path_min_unpacked_rev(svn_fs_t *fs, + apr_pool_t *pool) +{ + return svn_dirent_join(fs->path, PATH_MIN_UNPACKED_REV, pool); +} + +svn_error_t * +svn_fs_fs__check_file_buffer_numeric(const char *buf, + apr_off_t offset, + const char *path, + const char *title, + apr_pool_t *pool) +{ + const char *p; + + for (p = buf + offset; *p; p++) + if (!svn_ctype_isdigit(*p)) + return svn_error_createf(SVN_ERR_BAD_VERSION_FILE_FORMAT, NULL, + _("%s file '%s' contains unexpected non-digit '%c' within '%s'"), + title, svn_dirent_local_style(path, pool), *p, buf); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__read_min_unpacked_rev(svn_revnum_t *min_unpacked_rev, + svn_fs_t *fs, + apr_pool_t *pool) +{ + char buf[80]; + apr_file_t *file; + apr_size_t len; + + SVN_ERR(svn_io_file_open(&file, + svn_fs_fs__path_min_unpacked_rev(fs, pool), + APR_READ | APR_BUFFERED, + APR_OS_DEFAULT, + pool)); + len = sizeof(buf); + SVN_ERR(svn_io_read_length_line(file, buf, &len, pool)); + SVN_ERR(svn_io_file_close(file, pool)); + + SVN_ERR(svn_revnum_parse(min_unpacked_rev, buf, NULL)); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__update_min_unpacked_rev(svn_fs_t *fs, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + + SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_PACKED_FORMAT); + + return svn_fs_fs__read_min_unpacked_rev(&ffd->min_unpacked_rev, fs, pool); +} + +svn_error_t * +svn_fs_fs__write_min_unpacked_rev(svn_fs_t *fs, + svn_revnum_t revnum, + apr_pool_t *scratch_pool) +{ + const char *final_path; + char buf[SVN_INT64_BUFFER_SIZE]; + apr_size_t len = svn__i64toa(buf, revnum); + buf[len] = '\n'; + + final_path = svn_fs_fs__path_min_unpacked_rev(fs, scratch_pool); + + SVN_ERR(svn_io_write_atomic(final_path, buf, len + 1, + final_path /* copy_perms */, scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__read_current(svn_revnum_t *rev, + apr_uint64_t *next_node_id, + apr_uint64_t *next_copy_id, + svn_fs_t *fs, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + svn_stringbuf_t *content; + + SVN_ERR(svn_fs_fs__read_content(&content, + svn_fs_fs__path_current(fs, pool), + pool)); + + if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT) + { + /* When format 1 and 2 filesystems are upgraded, the 'current' file is + left intact. As a consequence, there is a window when a filesystem + has a new format, but this file still contains the IDs left from an + old format, i.e. looks like "359 j5 v\n". Do not be too strict here + and only expect a parseable revision number. */ + SVN_ERR(svn_revnum_parse(rev, content->data, NULL)); + + *next_node_id = 0; + *next_copy_id = 0; + } + else + { + const char *str; + + SVN_ERR(svn_revnum_parse(rev, content->data, &str)); + if (*str != ' ') + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Corrupt 'current' file")); + + *next_node_id = svn__base36toui64(&str, str + 1); + if (*str != ' ') + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Corrupt 'current' file")); + + *next_copy_id = svn__base36toui64(&str, str + 1); + if (*str != '\n') + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Corrupt 'current' file")); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__write_current(svn_fs_t *fs, + svn_revnum_t rev, + apr_uint64_t next_node_id, + apr_uint64_t next_copy_id, + apr_pool_t *pool) +{ + char *buf; + const char *name; + fs_fs_data_t *ffd = fs->fsap_data; + + /* Now we can just write out this line. */ + if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT) + { + buf = apr_psprintf(pool, "%ld\n", rev); + } + else + { + char node_id_str[SVN_INT64_BUFFER_SIZE]; + char copy_id_str[SVN_INT64_BUFFER_SIZE]; + svn__ui64tobase36(node_id_str, next_node_id); + svn__ui64tobase36(copy_id_str, next_copy_id); + + buf = apr_psprintf(pool, "%ld %s %s\n", rev, node_id_str, copy_id_str); + } + + name = svn_fs_fs__path_current(fs, pool); + SVN_ERR(svn_io_write_atomic(name, buf, strlen(buf), + name /* copy_perms_path */, pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__try_stringbuf_from_file(svn_stringbuf_t **content, + svn_boolean_t *missing, + const char *path, + svn_boolean_t last_attempt, + apr_pool_t *pool) +{ + svn_error_t *err = svn_stringbuf_from_file2(content, path, pool); + if (missing) + *missing = FALSE; + + if (err) + { + *content = NULL; + + if (APR_STATUS_IS_ENOENT(err->apr_err)) + { + if (!last_attempt) + { + svn_error_clear(err); + if (missing) + *missing = TRUE; + return SVN_NO_ERROR; + } + } +#ifdef ESTALE + else if (APR_TO_OS_ERROR(err->apr_err) == ESTALE + || APR_TO_OS_ERROR(err->apr_err) == EIO) + { + if (!last_attempt) + { + svn_error_clear(err); + return SVN_NO_ERROR; + } + } +#endif + } + + return svn_error_trace(err); +} + +svn_error_t * +svn_fs_fs__get_file_offset(apr_off_t *offset_p, + apr_file_t *file, + apr_pool_t *pool) +{ + apr_off_t offset; + + /* Note that, for buffered files, one (possibly surprising) side-effect + of this call is to flush any unwritten data to disk. */ + offset = 0; + SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool)); + *offset_p = offset; + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__read_content(svn_stringbuf_t **content, + const char *fname, + apr_pool_t *pool) +{ + int i; + *content = NULL; + + for (i = 0; !*content && (i < SVN_FS_FS__RECOVERABLE_RETRY_COUNT); ++i) + SVN_ERR(svn_fs_fs__try_stringbuf_from_file(content, NULL, + fname, i + 1 < SVN_FS_FS__RECOVERABLE_RETRY_COUNT, + pool)); + + if (!*content) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Can't read '%s'"), + svn_dirent_local_style(fname, pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__read_number_from_stream(apr_int64_t *result, + svn_boolean_t *hit_eof, + svn_stream_t *stream, + apr_pool_t *scratch_pool) +{ + svn_stringbuf_t *sb; + svn_boolean_t eof; + svn_error_t *err; + + SVN_ERR(svn_stream_readline(stream, &sb, "\n", &eof, scratch_pool)); + if (hit_eof) + *hit_eof = eof; + else + if (eof) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Unexpected EOF")); + + if (!eof) + { + err = svn_cstring_atoi64(result, sb->data); + if (err) + return svn_error_createf(SVN_ERR_FS_CORRUPT, err, + _("Number '%s' invalid or too large"), + sb->data); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__move_into_place(const char *old_filename, + const char *new_filename, + const char *perms_reference, + apr_pool_t *pool) +{ + svn_error_t *err; + + SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, pool)); + + /* Move the file into place. */ + err = svn_io_file_rename(old_filename, new_filename, pool); + if (err && APR_STATUS_IS_EXDEV(err->apr_err)) + { + apr_file_t *file; + + /* Can't rename across devices; fall back to copying. */ + svn_error_clear(err); + err = SVN_NO_ERROR; + SVN_ERR(svn_io_copy_file(old_filename, new_filename, TRUE, pool)); + + /* Flush the target of the copy to disk. */ + SVN_ERR(svn_io_file_open(&file, new_filename, APR_READ, + APR_OS_DEFAULT, pool)); + /* ### BH: Does this really guarantee a flush of the data written + ### via a completely different handle on all operating systems? + ### + ### Maybe we should perform the copy ourselves instead of making + ### apr do that and flush the real handle? */ + SVN_ERR(svn_io_file_flush_to_disk(file, pool)); + SVN_ERR(svn_io_file_close(file, pool)); + } + if (err) + return svn_error_trace(err); + +#ifdef __linux__ + { + /* Linux has the unusual feature that fsync() on a file is not + enough to ensure that a file's directory entries have been + flushed to disk; you have to fsync the directory as well. + On other operating systems, we'd only be asking for trouble + by trying to open and fsync a directory. */ + const char *dirname; + apr_file_t *file; + + dirname = svn_dirent_dirname(new_filename, pool); + SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT, + pool)); + SVN_ERR(svn_io_file_flush_to_disk(file, pool)); + SVN_ERR(svn_io_file_close(file, pool)); + } +#endif + + return SVN_NO_ERROR; +} + +svn_boolean_t +svn_fs_fs__use_log_addressing(svn_fs_t *fs) +{ + fs_fs_data_t *ffd = fs->fsap_data; + return ffd->use_log_addressing; +} |