diff options
Diffstat (limited to 'subversion/libsvn_fs_fs/load-index.c')
-rw-r--r-- | subversion/libsvn_fs_fs/load-index.c | 98 |
1 files changed, 87 insertions, 11 deletions
diff --git a/subversion/libsvn_fs_fs/load-index.c b/subversion/libsvn_fs_fs/load-index.c index 3142e8e354023..0ba66725d35cc 100644 --- a/subversion/libsvn_fs_fs/load-index.c +++ b/subversion/libsvn_fs_fs/load-index.c @@ -29,6 +29,53 @@ #include "util.h" #include "transaction.h" +/* From the ENTRIES array of svn_fs_fs__p2l_entry_t*, sorted by offset, + * return the first offset behind the last item. */ +static apr_off_t +get_max_covered(apr_array_header_t *entries) +{ + const svn_fs_fs__p2l_entry_t *entry; + if (entries->nelts == 0) + return -1; + + entry = APR_ARRAY_IDX(entries, entries->nelts - 1, + const svn_fs_fs__p2l_entry_t *); + return entry->offset + entry->size; +} + +/* Make sure that the svn_fs_fs__p2l_entry_t* in ENTRIES are consecutive + * and non-overlapping. Use SCRATCH_POOL for temporaries. */ +static svn_error_t * +check_all_covered(apr_array_header_t *entries, + apr_pool_t *scratch_pool) +{ + int i; + apr_off_t expected = 0; + for (i = 0; i < entries->nelts; ++i) + { + const svn_fs_fs__p2l_entry_t *entry + = APR_ARRAY_IDX(entries, i, const svn_fs_fs__p2l_entry_t *); + + if (entry->offset < expected) + return svn_error_createf(SVN_ERR_INVALID_INPUT, NULL, + "Overlapping index data for offset %s", + apr_psprintf(scratch_pool, + "%" APR_UINT64_T_HEX_FMT, + (apr_uint64_t)expected)); + + if (entry->offset > expected) + return svn_error_createf(SVN_ERR_INVALID_INPUT, NULL, + "Missing index data for offset %s", + apr_psprintf(scratch_pool, + "%" APR_UINT64_T_HEX_FMT, + (apr_uint64_t)expected)); + + expected = entry->offset + entry->size; + } + + return SVN_NO_ERROR; +} + /* A svn_sort__array compatible comparator function, sorting the * svn_fs_fs__p2l_entry_t** given in LHS, RHS by offset. */ static int @@ -52,7 +99,7 @@ svn_fs_fs__load_index(svn_fs_t *fs, apr_array_header_t *entries, apr_pool_t *scratch_pool) { - apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_pool_t *subpool = svn_pool_create(scratch_pool); /* Check the FS format number. */ if (! svn_fs_fs__use_log_addressing(fs)) @@ -68,31 +115,60 @@ svn_fs_fs__load_index(svn_fs_t *fs, const char *l2p_proto_index; const char *p2l_proto_index; svn_fs_fs__revision_file_t *rev_file; + svn_error_t *err; + apr_off_t max_covered = get_max_covered(entries); + + /* Ensure that the index data is complete. */ + SVN_ERR(check_all_covered(entries, scratch_pool)); /* Open rev / pack file & trim indexes + footer off it. */ SVN_ERR(svn_fs_fs__open_pack_or_rev_file_writable(&rev_file, fs, - revision, iterpool, - iterpool)); - SVN_ERR(svn_fs_fs__auto_read_footer(rev_file)); - SVN_ERR(svn_io_file_trunc(rev_file->file, rev_file->l2p_offset, - iterpool)); + revision, subpool, + subpool)); + + /* Remove the existing index info. */ + err = svn_fs_fs__auto_read_footer(rev_file); + if (err) + { + /* Even the index footer cannot be read, even less be trusted. + * Take the range of valid data from the new index data. */ + svn_error_clear(err); + SVN_ERR(svn_io_file_trunc(rev_file->file, max_covered, + subpool)); + } + else + { + /* We assume that the new index data covers all contents. + * Error out if it doesn't. The user can always truncate + * the file themselves. */ + if (max_covered != rev_file->l2p_offset) + return svn_error_createf(SVN_ERR_INVALID_INPUT, NULL, + "New index data ends at %s, old index ended at %s", + apr_psprintf(scratch_pool, "%" APR_UINT64_T_HEX_FMT, + (apr_uint64_t)max_covered), + apr_psprintf(scratch_pool, "%" APR_UINT64_T_HEX_FMT, + (apr_uint64_t) rev_file->l2p_offset)); + + SVN_ERR(svn_io_file_trunc(rev_file->file, rev_file->l2p_offset, + subpool)); + } /* Create proto index files for the new index data * (will be cleaned up automatically with iterpool). */ SVN_ERR(svn_fs_fs__p2l_index_from_p2l_entries(&p2l_proto_index, fs, rev_file, entries, - iterpool, iterpool)); + subpool, subpool)); SVN_ERR(svn_fs_fs__l2p_index_from_p2l_entries(&l2p_proto_index, fs, - entries, iterpool, - iterpool)); + entries, subpool, + subpool)); /* Combine rev data with new index data. */ SVN_ERR(svn_fs_fs__add_index_data(fs, rev_file->file, l2p_proto_index, p2l_proto_index, - rev_file->start_revision, iterpool)); + rev_file->start_revision, subpool)); } - svn_pool_destroy(iterpool); + svn_pool_destroy(subpool); return SVN_NO_ERROR; } |