Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.c?rev=1634609&r1=1634608&r2=1634609&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.c Mon Oct 27 17:28:13 2014 @@ -55,6 +55,16 @@ svn_fs_fs__is_packed_revprop(svn_fs_t *f && (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) @@ -224,15 +234,25 @@ combine_txn_id_string(const svn_fs_fs__i } const char * +svn_fs_fs__path_txns_dir(svn_fs_t *fs, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + + return ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT + ? svn_dirent_join(fs->path, PATH_TXNS_LA_DIR, pool) + : 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_many(pool, fs->path, PATH_TXNS_DIR, - combine_txn_id_string(txn_id, PATH_EXT_TXN, - pool), - SVN_VA_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* @@ -263,16 +283,22 @@ svn_fs_fs__path_txn_item_index(svn_fs_t } 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_many(pool, fs->path, PATH_TXN_PROTOS_DIR, - combine_txn_id_string(txn_id, PATH_EXT_REV, - pool), - SVN_VA_NULL); + 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); @@ -286,11 +312,10 @@ svn_fs_fs__path_txn_proto_rev_lock(svn_f { fs_fs_data_t *ffd = fs->fsap_data; if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT) - return svn_dirent_join_many(pool, fs->path, PATH_TXN_PROTOS_DIR, - combine_txn_id_string(txn_id, - PATH_EXT_REV_LOCK, - pool), - SVN_VA_NULL); + 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); @@ -387,7 +412,7 @@ svn_fs_fs__read_min_unpacked_rev(svn_rev SVN_ERR(svn_io_read_length_line(file, buf, &len, pool)); SVN_ERR(svn_io_file_close(file, pool)); - *min_unpacked_rev = SVN_STR_TO_REV(buf); + SVN_ERR(svn_revnum_parse(min_unpacked_rev, buf, NULL)); return SVN_NO_ERROR; } @@ -429,7 +454,6 @@ svn_fs_fs__read_current(svn_revnum_t *re { fs_fs_data_t *ffd = fs->fsap_data; svn_stringbuf_t *content; - const char *str; SVN_ERR(svn_fs_fs__read_content(&content, svn_fs_fs__path_current(fs, pool), @@ -437,16 +461,20 @@ svn_fs_fs__read_current(svn_revnum_t *re if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT) { - SVN_ERR(svn_revnum_parse(rev, content->data, &str)); - if (*str != '\n') - return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, - _("Corrupt 'current' file")); + /* 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,
Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.h URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.h?rev=1634609&r1=1634608&r2=1634609&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.h (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.h Mon Oct 27 17:28:13 2014 @@ -76,6 +76,12 @@ svn_boolean_t svn_fs_fs__is_packed_revprop(svn_fs_t *fs, svn_revnum_t rev); +/* Return the first revision in the pack / rev file containing REVISION in + * filesystem FS. For non-packed revs, this will simply be REVISION. */ +svn_revnum_t +svn_fs_fs__packed_base_rev(svn_fs_t *fs, + svn_revnum_t revision); + /* Return the full path of the rev shard directory that will contain * revision REV in FS. Allocate the result in POOL. */ @@ -183,6 +189,13 @@ const char * svn_fs_fs__path_min_unpacked_rev(svn_fs_t *fs, apr_pool_t *pool); +/* Return the path of the 'transactions' directory in FS. + * The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_txns_dir(svn_fs_t *fs, + apr_pool_t *pool); + /* Return the path of the directory containing the transaction TXN_ID in FS. * The result will be allocated in POOL. */ @@ -191,6 +204,13 @@ svn_fs_fs__path_txn_dir(svn_fs_t *fs, const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool); +/* Return the path of the 'txn-protorevs' directory in FS, even if that + * folder may not exist in FS. The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_txn_proto_revs(svn_fs_t *fs, + apr_pool_t *pool); + /* Return the path of the proto-revision file for transaction TXN_ID in FS. * The result will be allocated in POOL. */ Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/verify.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/verify.c?rev=1634609&r1=1634608&r2=1634609&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/verify.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/verify.c Mon Oct 27 17:28:13 2014 @@ -159,6 +159,93 @@ verify_rep_cache(svn_fs_t *fs, return SVN_NO_ERROR; } +/* Verify that the MD5 checksum of the data between offsets START and END + * in FILE matches the EXPECTED checksum. If there is a mismatch use the + * indedx NAME in the error message. Supports cancellation with CANCEL_FUNC + * and CANCEL_BATON. SCRATCH_POOL is for temporary allocations. */ +static svn_error_t * +verify_index_checksum(apr_file_t *file, + const char *name, + apr_off_t start, + apr_off_t end, + svn_checksum_t *expected, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + unsigned char buffer[SVN__STREAM_CHUNK_SIZE]; + apr_off_t size = end - start; + svn_checksum_t *actual; + svn_checksum_ctx_t *context + = svn_checksum_ctx_create(svn_checksum_md5, scratch_pool); + + /* Calculate the index checksum. */ + SVN_ERR(svn_io_file_seek(file, APR_SET, &start, scratch_pool)); + while (size > 0) + { + apr_size_t to_read = size > sizeof(buffer) + ? sizeof(buffer) + : (apr_size_t)size; + SVN_ERR(svn_io_file_read_full2(file, buffer, to_read, NULL, NULL, + scratch_pool)); + SVN_ERR(svn_checksum_update(context, buffer, to_read)); + size -= to_read; + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + } + + SVN_ERR(svn_checksum_final(&actual, context, scratch_pool)); + + /* Verify that it matches the expected checksum. */ + if (!svn_checksum_match(expected, actual)) + { + const char *file_name; + + SVN_ERR(svn_io_file_name_get(&file_name, file, scratch_pool)); + SVN_ERR(svn_checksum_mismatch_err(expected, actual, scratch_pool, + _("%s checksum mismatch in file %s"), + name, file_name)); + } + + return SVN_NO_ERROR; +} + +/* Verify the MD5 checksums of the index data in the rev / pack file + * containing revision START in FS. If given, invoke CANCEL_FUNC with + * CANCEL_BATON at regular intervals. Use SCRATCH_POOL for temporary + * allocations. + */ +static svn_error_t * +verify_index_checksums(svn_fs_t *fs, + svn_revnum_t start, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + svn_fs_fs__revision_file_t *rev_file; + + /* Open the rev / pack file and read the footer */ + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, start, + scratch_pool, scratch_pool)); + SVN_ERR(svn_fs_fs__auto_read_footer(rev_file)); + + /* Verify the index contents against the checksum from the footer. */ + SVN_ERR(verify_index_checksum(rev_file->file, "L2P index", + rev_file->l2p_offset, rev_file->p2l_offset, + rev_file->l2p_checksum, + cancel_func, cancel_baton, scratch_pool)); + SVN_ERR(verify_index_checksum(rev_file->file, "P2L index", + rev_file->p2l_offset, rev_file->footer_offset, + rev_file->p2l_checksum, + cancel_func, cancel_baton, scratch_pool)); + + /* Done. */ + SVN_ERR(svn_fs_fs__close_revision_file(rev_file)); + + return SVN_NO_ERROR; +} + /* Verify that for all log-to-phys index entries for revisions START to * START + COUNT-1 in FS there is a consistent entry in the phys-to-log * index. If given, invoke CANCEL_FUNC with CANCEL_BATON at regular @@ -182,7 +269,8 @@ compare_l2p_to_p2l_index(svn_fs_t *fs, iterpool)); /* determine the range of items to check for each revision */ - SVN_ERR(svn_fs_fs__l2p_get_max_ids(&max_ids, fs, start, count, pool)); + SVN_ERR(svn_fs_fs__l2p_get_max_ids(&max_ids, fs, start, count, pool, + iterpool)); /* check all items in all revisions if the given range */ for (i = 0; i < max_ids->nelts; ++i) @@ -205,10 +293,11 @@ compare_l2p_to_p2l_index(svn_fs_t *fs, /* find the corresponding P2L entry */ SVN_ERR(svn_fs_fs__p2l_entry_lookup(&p2l_entry, fs, rev_file, - revision, offset, iterpool)); + revision, offset, iterpool, + iterpool)); if (p2l_entry == NULL) - return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_INCONSISTENT, + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, NULL, _("p2l index entry not found for " "PHYS %s returned by " @@ -218,7 +307,7 @@ compare_l2p_to_p2l_index(svn_fs_t *fs, if ( p2l_entry->item.number != k || p2l_entry->item.revision != revision) - return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_INCONSISTENT, + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, NULL, _("p2l index info LOG r%ld:i%ld" " does not match " @@ -282,9 +371,9 @@ compare_p2l_to_l2p_index(svn_fs_t *fs, /* get all entries for the current block */ SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, fs, rev_file, start, offset, ffd->p2l_page_size, - iterpool)); + iterpool, iterpool)); if (entries->nelts == 0) - return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_CORRUPTION, + return svn_error_createf(SVN_ERR_FS_INDEX_CORRUPTION, NULL, _("p2l does not cover offset %s" " for revision %ld"), @@ -301,7 +390,24 @@ compare_p2l_to_l2p_index(svn_fs_t *fs, = &APR_ARRAY_IDX(entries, i, svn_fs_fs__p2l_entry_t); /* check all sub-items for consist entries in the L2P index */ - if (entry->type != SVN_FS_FS__ITEM_TYPE_UNUSED) + if (entry->type == SVN_FS_FS__ITEM_TYPE_UNUSED) + { + /* There is no L2P entry for unused rev file sections. + * And its P2L index data is hardly ever used. But we + * should still check whether someone tempered with it. */ + if ( entry->item.revision != SVN_INVALID_REVNUM + && ( entry->item.revision < start + || entry->item.revision >= start + count)) + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, + NULL, + _("Empty P2L entry for PHYS %s " + "refers to revision %ld outside " + "the rev / pack file (%ld-%ld)"), + apr_off_t_toa(pool, entry->offset), + entry->item.number, + start, start + count - 1); + } + else { apr_off_t l2p_offset; SVN_ERR(svn_fs_fs__item_offset(&l2p_offset, fs, rev_file, @@ -309,7 +415,7 @@ compare_p2l_to_l2p_index(svn_fs_t *fs, entry->item.number, iterpool)); if (l2p_offset != entry->offset) - return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_INCONSISTENT, + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, NULL, _("l2p index entry PHYS %s" "does not match p2l index value " @@ -416,7 +522,7 @@ expected_checksum(apr_file_t *file, SVN_ERR(svn_io_file_name_get(&file_name, file, pool)); return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, - _("Checksum mismatch item at offset %s of " + _("Checksum mismatch in item at offset %s of " "length %s bytes in file %s"), apr_off_t_toa(pool, entry->offset), apr_off_t_toa(pool, entry->size), file_name); @@ -511,7 +617,7 @@ compare_p2l_to_rev(svn_fs_t *fs, pool)); if (rev_file->l2p_offset != max_offset) - return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_INCONSISTENT, NULL, + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, NULL, _("File size of %s for revision r%ld does " "not match p2l index size of %s"), apr_off_t_toa(pool, rev_file->l2p_offset), start, @@ -532,7 +638,12 @@ compare_p2l_to_rev(svn_fs_t *fs, /* get all entries for the current block */ SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, fs, rev_file, start, offset, ffd->p2l_page_size, - iterpool)); + iterpool, iterpool)); + + /* The above might have moved the file pointer. + * Ensure we actually start reading at OFFSET. */ + SVN_ERR(svn_io_file_aligned_seek(rev_file->file, ffd->block_size, + NULL, offset, iterpool)); /* process all entries (and later continue with the next block) */ for (i = 0; i < entries->nelts; ++i) @@ -550,7 +661,7 @@ compare_p2l_to_rev(svn_fs_t *fs, /* p2l index must cover all rev / pack file offsets exactly once */ if (entry->offset != offset) - return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_INCONSISTENT, + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, NULL, _("p2l index entry for revision r%ld" " is non-contiguous between offsets " @@ -566,7 +677,7 @@ compare_p2l_to_rev(svn_fs_t *fs, if (entry->offset != max_offset) SVN_ERR(read_all_nul(rev_file->file, entry->size, pool)); } - else if (entry->fnv1_checksum) + else { if (entry->size < STREAM_THRESHOLD) SVN_ERR(expected_buffered_checksum(rev_file->file, entry, @@ -592,16 +703,6 @@ compare_p2l_to_rev(svn_fs_t *fs, } static svn_revnum_t -packed_base_rev(svn_fs_t *fs, svn_revnum_t rev) -{ - fs_fs_data_t *ffd = fs->fsap_data; - - return rev < ffd->min_unpacked_rev - ? rev - (rev % ffd->max_files_per_dir) - : rev; -} - -static svn_revnum_t pack_size(svn_fs_t *fs, svn_revnum_t rev) { fs_fs_data_t *ffd = fs->fsap_data; @@ -635,7 +736,7 @@ verify_index_consistency(svn_fs_t *fs, svn_error_t *err = SVN_NO_ERROR; svn_revnum_t count = pack_size(fs, revision); - svn_revnum_t pack_start = packed_base_rev(fs, revision); + svn_revnum_t pack_start = svn_fs_fs__packed_base_rev(fs, revision); svn_revnum_t pack_end = pack_start + count; svn_pool_clear(iterpool); @@ -643,9 +744,14 @@ verify_index_consistency(svn_fs_t *fs, if (notify_func && (pack_start % ffd->max_files_per_dir == 0)) notify_func(pack_start, notify_baton, iterpool); + /* Check for external corruption to the indexes. */ + err = verify_index_checksums(fs, pack_start, cancel_func, + cancel_baton, iterpool); + /* two-way index check */ - err = compare_l2p_to_p2l_index(fs, pack_start, pack_end - pack_start, - cancel_func, cancel_baton, iterpool); + if (!err) + err = compare_l2p_to_p2l_index(fs, pack_start, pack_end - pack_start, + cancel_func, cancel_baton, iterpool); if (!err) err = compare_p2l_to_l2p_index(fs, pack_start, pack_end - pack_start, cancel_func, cancel_baton, iterpool); @@ -668,7 +774,7 @@ verify_index_consistency(svn_fs_t *fs, /* We could simply assign revision here but the code below is more intuitive to maintainers. */ - next_revision = packed_base_rev(fs, revision); + next_revision = svn_fs_fs__packed_base_rev(fs, revision); } else { Propchange: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/ ------------------------------------------------------------------------------ Merged /subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs:r1619782-1620595 Merged /subversion/trunk/subversion/libsvn_fs_fs:r1631115,1631171,1631180 Merged /subversion/branches/revprop-caching-ng/subversion/libsvn_fs_x:r1620597 Merged /subversion/trunk/subversion/libsvn_fs_x:r1622194-1634606 Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/cached_data.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/cached_data.c?rev=1634609&r1=1634608&r2=1634609&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/cached_data.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/cached_data.c Mon Oct 27 17:28:13 2014 @@ -772,13 +772,18 @@ svn_fs_x__rep_chain_length(int *chain_le svn_revnum_t shard_size = ffd->max_files_per_dir ? ffd->max_files_per_dir : 1; - apr_pool_t *sub_pool = svn_pool_create(pool); svn_boolean_t is_delta = FALSE; int count = 0; int shards = 1; svn_revnum_t revision = svn_fs_x__get_revnum(rep->id.change_set); svn_revnum_t last_shard = revision / shard_size; - + + /* Note that this iteration pool will be used in a non-standard way. + * To reuse open file handles between iterations (e.g. while within the + * same pack file), we only clear this pool once in a while instead of + * at the start of each iteration. */ + apr_pool_t *iterpool = svn_pool_create(pool); + /* Check whether the length of the deltification chain is acceptable. * Otherwise, shared reps may form a non-skipping delta chain in * extreme cases. */ @@ -806,7 +811,7 @@ svn_fs_x__rep_chain_length(int *chain_le &file_hint, &base_rep, fs, - sub_pool)); + iterpool)); base_rep.id.change_set = svn_fs_x__change_set_by_rev(header->base_revision); @@ -814,18 +819,28 @@ svn_fs_x__rep_chain_length(int *chain_le base_rep.size = header->base_length; is_delta = header->type == svn_fs_x__rep_delta; + /* Clear it the ITERPOOL once in a while. Doing it too frequently + * renders the FILE_HINT ineffective. Doing too infrequently, may + * leave us with too many open file handles. + * + * Note that this is mostly about efficiency, with larger values + * being more efficient, and any non-zero value is legal here. When + * reading deltified contents, we may keep 10s of rev files open at + * the same time and the system has to cope with that. Thus, the + * limit of 16 chosen below is in the same ballpark. + */ ++count; if (count % 16 == 0) { file_hint = NULL; - svn_pool_clear(sub_pool); + svn_pool_clear(iterpool); } } while (is_delta && base_rep.id.change_set); *chain_length = count; *shard_count = shards; - svn_pool_destroy(sub_pool); + svn_pool_destroy(iterpool); return SVN_NO_ERROR; } @@ -3116,7 +3131,7 @@ block_read(void **result, key.revision = svn_fs_x__get_revnum(entry->items[0].change_set); key.second = entry->items[0].number; - SVN_ERR(svn_io_file_seek(revision_file, SEEK_SET, + SVN_ERR(svn_io_file_seek(revision_file, APR_SET, &entry->offset, iterpool)); switch (entry->type) { Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/caching.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/caching.c?rev=1634609&r1=1634608&r2=1634609&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/caching.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/caching.c Mon Oct 27 17:28:13 2014 @@ -126,19 +126,17 @@ read_config(const char **cache_namespace * Revprop caching significantly speeds up operations like * svn ls -v. However, it requires synchronization that may * not be available or efficient in the current server setup. - * - * If the caller chose option "2", enable revprop caching if - * the required API support is there to make it efficient. + * Option "2" is equivalent to "1". */ if (strcmp(svn_hash__get_cstring(fs->config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, ""), "2")) *cache_revprops = svn_hash__get_bool(fs->config, - SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, - FALSE); + SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, + FALSE); else - *cache_revprops = svn_named_atomic__is_efficient(); + *cache_revprops = TRUE; return SVN_NO_ERROR; } Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs.c?rev=1634609&r1=1634608&r2=1634609&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs.c Mon Oct 27 17:28:13 2014 @@ -190,13 +190,26 @@ x_info(const void **fsx_info, return SVN_NO_ERROR; } +/* Wrapper around svn_fs_x__get_revision_proplist() adapting between function + signatures. */ +static svn_error_t * +x_revision_proplist(apr_hash_t **proplist_p, + svn_fs_t *fs, + svn_revnum_t rev, + apr_pool_t *pool) +{ + /* No need to bypass the caches for r/o access to revprops. */ + return svn_error_trace(svn_fs_x__get_revision_proplist(proplist_p, fs, + rev, FALSE, pool)); +} + /* The vtable associated with a specific open filesystem. */ static fs_vtable_t fs_vtable = { svn_fs_x__youngest_rev, svn_fs_x__revision_prop, - svn_fs_x__revision_proplist, + x_revision_proplist, svn_fs_x__change_rev_prop, svn_fs_x__set_uuid, svn_fs_x__revision_root, Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs.h URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs.h?rev=1634609&r1=1634608&r2=1634609&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs.h (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs.h Mon Oct 27 17:28:13 2014 @@ -36,7 +36,6 @@ #include "private/svn_fs_private.h" #include "private/svn_sqlite.h" #include "private/svn_mutex.h" -#include "private/svn_named_atomic.h" #include "id.h" @@ -288,17 +287,9 @@ typedef struct fs_x_data_t rep key (revision/offset) to svn_stringbuf_t. */ svn_cache__t *fulltext_cache; - /* Access object to the atomics namespace used by revprop caching. - Will be NULL until the first access. */ - svn_atomic_namespace__t *revprop_namespace; - /* Access object to the revprop "generation". Will be NULL until - the first access. */ - svn_named_atomic__t *revprop_generation; - - /* Access object to the revprop update timeout. Will be NULL until - the first access. */ - svn_named_atomic__t *revprop_timeout; + the first access. May be also get closed and set to NULL again. */ + apr_file_t *revprop_generation_file; /* Revision property cache. Maps from (rev,generation) to apr_hash_t. */ svn_cache__t *revprop_cache; Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs_x.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs_x.c?rev=1634609&r1=1634608&r2=1634609&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs_x.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/fs_x.c Mon Oct 27 17:28:13 2014 @@ -664,7 +664,7 @@ svn_fs_x__revision_proplist(apr_hash_t * svn_revnum_t rev, apr_pool_t *pool) { - SVN_ERR(svn_fs_x__get_revision_proplist(proplist_p, fs, rev, pool)); + SVN_ERR(svn_fs_x__get_revision_proplist(proplist_p, fs, rev, FALSE, pool)); return SVN_NO_ERROR; } @@ -960,6 +960,9 @@ svn_fs_x__create(svn_fs_t *fs, SVN_ERR(svn_io_file_create_empty(svn_fs_x__path_txn_current_lock(fs, pool), pool)); + /* Initialize the revprop caching info. */ + SVN_ERR(svn_fs_x__reset_revprop_generation_file(fs, pool)); + /* This filesystem is ready. Stamp it with a format number. */ SVN_ERR(svn_fs_x__write_format(fs, FALSE, pool)); Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/hotcopy.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/hotcopy.c?rev=1634609&r1=1634608&r2=1634609&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/hotcopy.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/hotcopy.c Mon Oct 27 17:28:13 2014 @@ -891,12 +891,7 @@ hotcopy_body(void *baton, apr_pool_t *po * reset it to zero (since this is on a different path, it will not * overlap with data already in cache). Also, clean up stale files * used for the named atomics implementation. */ - SVN_ERR(svn_io_check_path(svn_fs_x__path_revprop_generation(src_fs, pool), - &kind, pool)); - if (kind == svn_node_file) - SVN_ERR(svn_fs_x__write_revprop_generation_file(dst_fs, 0, pool)); - - SVN_ERR(svn_fs_x__cleanup_revprop_namespace(dst_fs)); + SVN_ERR(svn_fs_x__reset_revprop_generation_file(dst_fs, pool)); return SVN_NO_ERROR; } Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/index.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/index.c?rev=1634609&r1=1634608&r2=1634609&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/index.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/index.c Mon Oct 27 17:28:13 2014 @@ -200,7 +200,7 @@ stream_error_create(packed_number_stream apr_off_t offset = 0; SVN_ERR(svn_io_file_name_get(&file_name, stream->file, stream->pool)); - SVN_ERR(svn_io_file_seek(stream->file, SEEK_CUR, &offset, stream->pool)); + SVN_ERR(svn_io_file_seek(stream->file, APR_CUR, &offset, stream->pool)); return svn_error_createf(err, NULL, message, file_name, (apr_uint64_t)offset); @@ -292,7 +292,7 @@ packed_stream_read(packed_number_stream_ /* let's catch corrupted data early. It would surely cause * havoc further down the line. */ if SVN__PREDICT_FALSE(shift > 8 * sizeof(value)) - return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_CORRUPTION, NULL, + return svn_error_createf(SVN_ERR_FS_INDEX_CORRUPTION, NULL, _("Corrupt index: number too large")); } } @@ -718,7 +718,7 @@ svn_fs_x__l2p_index_create(svn_fs_t *fs, /* Paranoia check that makes later casting to int32 safe. * The current implementation is limited to 2G entries per page. */ if (ffd->l2p_page_size > APR_INT32_MAX) - return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_OVERFLOW , NULL, + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, _("L2P index page size %s" " exceeds current limit of 2G entries"), apr_psprintf(local_pool, "%" APR_UINT64_T_FMT, @@ -781,7 +781,7 @@ svn_fs_x__l2p_index_create(svn_fs_t *fs, l2p_page_entry_t page_entry = { 0 }; if (proto_entry.item_index > APR_INT32_MAX) - return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_OVERFLOW , NULL, + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, _("Item index %s too large " "in l2p proto index for revision %ld"), apr_psprintf(local_pool, @@ -808,7 +808,7 @@ svn_fs_x__l2p_index_create(svn_fs_t *fs, /* Paranoia check that makes later casting to int32 safe. * The current implementation is limited to 2G pages per index. */ if (page_counts->nelts > APR_INT32_MAX) - return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_OVERFLOW , NULL, + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, _("L2P index page count %d" " exceeds current limit of 2G pages"), page_counts->nelts); @@ -915,7 +915,7 @@ l2p_header_copy(l2p_page_info_baton_t *b /* revision offset within the index file */ apr_size_t rel_revision = baton->revision - header->first_revision; if (rel_revision >= header->revision_count) - return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_REVISION , NULL, + return svn_error_createf(SVN_ERR_FS_INDEX_REVISION , NULL, _("Revision %ld not covered by item index"), baton->revision); @@ -941,7 +941,7 @@ l2p_header_copy(l2p_page_info_baton_t *b max_item_index = (apr_uint64_t)header->page_size * (last_entry - first_entry); if (baton->item_index >= max_item_index) - return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_OVERFLOW , NULL, + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, _("Item index %s exceeds l2p limit " "of %s for revision %ld"), apr_psprintf(scratch_pool, @@ -1070,7 +1070,7 @@ get_l2p_header_body(l2p_header_t **heade if (result->first_revision > revision || result->first_revision + result->revision_count <= revision) - return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_CORRUPTION, NULL, + return svn_error_createf(SVN_ERR_FS_INDEX_CORRUPTION, NULL, _("Corrupt L2P index for r%ld only covers r%ld:%ld"), revision, result->first_revision, result->first_revision + result->revision_count); @@ -1292,7 +1292,7 @@ l2p_page_get_offset(l2p_page_baton_t *ba { /* overflow check */ if (page->entry_count <= baton->page_offset) - return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_OVERFLOW , NULL, + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, _("Item index %s too large in" " revision %ld"), apr_psprintf(pool, "%" APR_UINT64_T_FMT, @@ -1843,7 +1843,7 @@ svn_fs_x__p2l_index_create(svn_fs_t *fs, = svn_spillbuf__create(0x10000, 0x1000000, local_pool); /* for loop temps ... */ - apr_pool_t *iter_pool = svn_pool_create(pool); + apr_pool_t *iterpool = svn_pool_create(pool); /* start at the beginning of the source file */ SVN_ERR(svn_io_file_open(&proto_index, proto_file_name, @@ -1861,20 +1861,20 @@ svn_fs_x__p2l_index_create(svn_fs_t *fs, svn_revnum_t last_revision = revision; apr_uint64_t last_number = 0; - svn_pool_clear(iter_pool); + svn_pool_clear(iterpool); /* (attempt to) read the next entry from the source */ SVN_ERR(svn_io_file_read_full2(proto_index, &entry, sizeof(entry), - &read, &eof, iter_pool)); + &read, &eof, iterpool)); SVN_ERR_ASSERT(eof || read == sizeof(entry)); if (entry.item_count && !eof) { to_read = entry.item_count * sizeof(*entry.items); - entry.items = apr_palloc(iter_pool, to_read); + entry.items = apr_palloc(iterpool, to_read); SVN_ERR(svn_io_file_read_full2(proto_index, entry.items, to_read, - &read, &eof, iter_pool)); + &read, &eof, iterpool)); SVN_ERR_ASSERT(eof || read == to_read); } @@ -1884,7 +1884,7 @@ svn_fs_x__p2l_index_create(svn_fs_t *fs, apr_size_t entry_size; to_read = sizeof(entry_size); SVN_ERR(svn_io_file_read_full2(proto_index, &entry_size, to_read, - &read, &eof, iter_pool)); + &read, &eof, iterpool)); SVN_ERR_ASSERT(eof || read == to_read); } @@ -1925,20 +1925,20 @@ svn_fs_x__p2l_index_create(svn_fs_t *fs, { SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, encode_uint(encoded, entry.offset), - iter_pool)); + iterpool)); last_revision = revision; } /* write simple item / container entry */ SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, encode_uint(encoded, entry.size), - iter_pool)); + iterpool)); SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, encode_uint(encoded, entry.type + entry.item_count * 16), - iter_pool)); + iterpool)); SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, encode_uint(encoded, entry.fnv1_checksum), - iter_pool)); + iterpool)); /* container contents (only one for non-container items) */ for (sub_item = 0; sub_item < entry.item_count; ++sub_item) @@ -1948,7 +1948,7 @@ svn_fs_x__p2l_index_create(svn_fs_t *fs, apr_int64_t diff = item_rev - last_revision; SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, encode_int(encoded, diff), - iter_pool)); + iterpool)); last_revision = item_rev; } @@ -1957,7 +1957,7 @@ svn_fs_x__p2l_index_create(svn_fs_t *fs, apr_int64_t diff = entry.items[sub_item].number - last_number; SVN_ERR(svn_spillbuf__write(buffer, (const char *)encoded, encode_int(encoded, diff), - iter_pool)); + iterpool)); last_number = entry.items[sub_item].number; } @@ -2007,7 +2007,7 @@ svn_fs_x__p2l_index_create(svn_fs_t *fs, SVN_ERR(svn_io_file_close(index_file, local_pool)); SVN_ERR(svn_io_set_file_read_only(file_name, FALSE, local_pool)); - svn_pool_destroy(iter_pool); + svn_pool_destroy(iterpool); svn_pool_destroy(local_pool); return SVN_NO_ERROR; @@ -2438,7 +2438,7 @@ get_p2l_keys(p2l_page_info_baton_t *page if (page_info.page_count <= page_info.page_no) { SVN_ERR(packed_stream_close(*stream)); - return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_OVERFLOW , NULL, + return svn_error_createf(SVN_ERR_FS_INDEX_OVERFLOW , NULL, _("Offset %s too large in revision %ld"), apr_off_t_toa(pool, offset), revision); } Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/noderevs.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/noderevs.c?rev=1634609&r1=1634608&r2=1634609&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/noderevs.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/noderevs.c Mon Oct 27 17:28:13 2014 @@ -60,8 +60,6 @@ typedef struct binary_id_t } binary_id_t; /* Our internal representation of an representation. - * We simply omit the uniquifier, which allows us to share instances of - * binary_representation_t and uniquify them in a shared_representation_t. */ typedef struct binary_representation_t { Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/pack.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/pack.c?rev=1634609&r1=1634608&r2=1634609&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/pack.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/pack.c Mon Oct 27 17:28:13 2014 @@ -483,7 +483,7 @@ copy_item_to_temp(pack_context_t *contex svn_fs_x__p2l_entry_t *new_entry = svn_fs_x__p2l_entry_dup(entry, context->info_pool); new_entry->offset = 0; - SVN_ERR(svn_io_file_seek(temp_file, SEEK_CUR, &new_entry->offset, pool)); + SVN_ERR(svn_io_file_seek(temp_file, APR_CUR, &new_entry->offset, pool)); APR_ARRAY_PUSH(entries, svn_fs_x__p2l_entry_t *) = new_entry; SVN_ERR(copy_file_data(context, temp_file, rev_file, entry->size, pool)); @@ -573,7 +573,7 @@ copy_rep_to_temp(pack_context_t *context * store it in CONTEXT */ entry = svn_fs_x__p2l_entry_dup(entry, context->info_pool); entry->offset = 0; - SVN_ERR(svn_io_file_seek(context->reps_file, SEEK_CUR, &entry->offset, + SVN_ERR(svn_io_file_seek(context->reps_file, APR_CUR, &entry->offset, pool)); add_item_rep_mapping(context, entry); @@ -596,7 +596,7 @@ copy_rep_to_temp(pack_context_t *context } /* copy the whole rep (including header!) to our temp file */ - SVN_ERR(svn_io_file_seek(rev_file, SEEK_SET, &source_offset, pool)); + SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &source_offset, pool)); SVN_ERR(copy_file_data(context, context->reps_file, rev_file, entry->size, pool)); @@ -699,12 +699,12 @@ copy_node_to_temp(pack_context_t *contex * store it in CONTEXT */ entry = svn_fs_x__p2l_entry_dup(entry, context->info_pool); entry->offset = 0; - SVN_ERR(svn_io_file_seek(context->reps_file, SEEK_CUR, + SVN_ERR(svn_io_file_seek(context->reps_file, APR_CUR, &entry->offset, pool)); add_item_rep_mapping(context, entry); /* copy the noderev to our temp file */ - SVN_ERR(svn_io_file_seek(rev_file, SEEK_SET, &source_offset, pool)); + SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &source_offset, pool)); SVN_ERR(copy_file_data(context, context->reps_file, rev_file, entry->size, pool)); @@ -1245,7 +1245,7 @@ write_reps_container(pack_context_t *con SVN_ERR(svn_fs_x__write_reps_container(pack_stream, container, pool)); SVN_ERR(svn_stream_close(pack_stream)); - SVN_ERR(svn_io_file_seek(context->pack_file, SEEK_CUR, &offset, pool)); + SVN_ERR(svn_io_file_seek(context->pack_file, APR_CUR, &offset, pool)); container_entry.offset = context->pack_offset; container_entry.size = offset - container_entry.offset; @@ -1327,7 +1327,7 @@ write_reps_containers(pack_context_t *co /* select the change list in the source file, parse it and add it to * the container */ - SVN_ERR(svn_io_file_seek(temp_file, SEEK_SET, &entry->offset, + SVN_ERR(svn_io_file_seek(temp_file, APR_SET, &entry->offset, iterpool)); SVN_ERR(svn_fs_x__get_representation_length(&representation.size, &representation.expanded_size, @@ -1414,7 +1414,7 @@ store_items(pack_context_t *context, /* select the item in the source file and copy it into the target * pack file */ - SVN_ERR(svn_io_file_seek(temp_file, SEEK_SET, &entry->offset, + SVN_ERR(svn_io_file_seek(temp_file, APR_SET, &entry->offset, iterpool)); SVN_ERR(copy_file_data(context, context->pack_file, temp_file, entry->size, iterpool)); @@ -1558,7 +1558,7 @@ write_changes_container(pack_context_t * container, pool)); SVN_ERR(svn_stream_close(pack_stream)); - SVN_ERR(svn_io_file_seek(context->pack_file, SEEK_CUR, &offset, pool)); + SVN_ERR(svn_io_file_seek(context->pack_file, APR_CUR, &offset, pool)); container_entry.offset = context->pack_offset; container_entry.size = offset - container_entry.offset; @@ -1654,7 +1654,7 @@ write_changes_containers(pack_context_t /* select the change list in the source file, parse it and add it to * the container */ - SVN_ERR(svn_io_file_seek(temp_file, SEEK_SET, &entry->offset, + SVN_ERR(svn_io_file_seek(temp_file, APR_SET, &entry->offset, iterpool)); SVN_ERR(svn_fs_x__read_changes(&changes, temp_stream, iterpool)); SVN_ERR(svn_fs_x__changes_append_list(&list_index, container, changes)); @@ -1880,7 +1880,7 @@ pack_range(pack_context_t *context, offset = entry->offset; if (offset < finfo.size) { - SVN_ERR(svn_io_file_seek(rev_file, SEEK_SET, &offset, + SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &offset, iterpool)); if (entry->type == SVN_FS_X__ITEM_TYPE_CHANGES) Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/recovery.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/recovery.c?rev=1634609&r1=1634608&r2=1634609&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/recovery.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/recovery.c Mon Oct 27 17:28:13 2014 @@ -122,7 +122,7 @@ recover_body(void *baton, apr_pool_t *po svn_node_kind_t youngest_revprops_kind; /* Lose potentially corrupted data in temp files */ - SVN_ERR(svn_fs_x__cleanup_revprop_namespace(fs)); + SVN_ERR(svn_fs_x__reset_revprop_generation_file(fs, pool)); /* We need to know the largest revision in the filesystem. */ SVN_ERR(recover_get_largest_revision(fs, &max_rev, pool)); Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/revprops.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/revprops.c?rev=1634609&r1=1634608&r2=1634609&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/revprops.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/revprops.c Mon Oct 27 17:28:13 2014 @@ -21,6 +21,7 @@ */ #include <assert.h> +#include <apr_md5.h> #include "svn_pools.h" #include "svn_hash.h" @@ -42,11 +43,15 @@ process got aborted and that we have re-read revprops. */ #define REVPROP_CHANGE_TIMEOUT (10 * 1000000) -/* The following are names of atomics that will be used to communicate - * revprop updates across all processes on this machine. */ -#define ATOMIC_REVPROP_GENERATION "rev-prop-generation" -#define ATOMIC_REVPROP_TIMEOUT "rev-prop-timeout" -#define ATOMIC_REVPROP_NAMESPACE "rev-prop-atomics" +/* In case of an inconsistent read, close the generation file, yield, + re-open and re-read. This is the number of times we try this before + giving up. */ +#define GENERATION_READ_RETRY_COUNT 100 + +/* Maximum size of the generation number file contents (including NUL). */ +#define CHECKSUMMED_NUMBER_BUFFER_LEN \ + (SVN_INT64_BUFFER_SIZE + 3 + APR_MD5_DIGESTSIZE * 2) + svn_error_t * svn_fs_x__upgrade_pack_revprops(svn_fs_t *fs, @@ -147,179 +152,251 @@ svn_fs_x__upgrade_cleanup_pack_revprops( * * Revprop caching needs to be activated and will be deactivated for the * respective FS instance if the necessary infrastructure could not be - * initialized. In deactivated mode, there is almost no runtime overhead - * associated with revprop caching. As long as no revprops are being read - * or changed, revprop caching imposes no overhead. + * initialized. As long as no revprops are being read or changed, revprop + * caching imposes no overhead. * * When activated, we cache revprops using (revision, generation) pairs * as keys with the generation being incremented upon every revprop change. * Since the cache is process-local, the generation needs to be tracked * for at least as long as the process lives but may be reset afterwards. * - * To track the revprop generation, we use two-layer approach. On the lower - * level, we use named atomics to have a system-wide consistent value for - * the current revprop generation. However, those named atomics will only - * remain valid for as long as at least one process / thread in the system - * accesses revprops in the respective repository. The underlying shared - * memory gets cleaned up afterwards. - * - * On the second level, we will use a persistent file to track the latest - * revprop generation. It will be written upon each revprop change but - * only be read if we are the first process to initialize the named atomics - * with that value. - * - * The overhead for the second and following accesses to revprops is - * almost zero on most systems. - * - * - * Tech aspects: - * ------------- - * - * A problem is that we need to provide a globally available file name to - * back the SHM implementation on OSes that need it. We can only assume - * write access to some file within the respective repositories. Because - * a given server process may access thousands of repositories during its - * lifetime, keeping the SHM data alive for all of them is also not an - * option. - * - * So, we store the new revprop generation on disk as part of each - * setrevprop call, i.e. this write will be serialized and the write order - * be guaranteed by the repository write lock. - * - * The only racy situation occurs when the data is being read again by two - * processes concurrently but in that situation, the first process to - * finish that procedure is guaranteed to be the only one that initializes - * the SHM data. Since even writers will first go through that - * initialization phase, they will never operate on stale data. + * We track the revprop generation in a persistent, unbuffered file that + * we may keep open for the lifetime of the svn_fs_t. It is the OS' + * responsibility to provide us with the latest contents upon read. To + * detect incomplete updates due to non-atomic reads, we put a MD5 checksum + * next to the actual generation number and verify that it matches. + * + * Since we cannot guarantee that the OS will provide us with up-to-date + * data buffers for open files, we re-open and re-read the file before + * modifying it. This will prevent lost updates. + * + * A race condition exists between switching to the modified revprop data + * and bumping the generation number. In particular, the process may crash + * just after switching to the new revprop data and before bumping the + * generation. To be able to detect this scenario, we bump the generation + * twice per revprop change: once immediately before (creating an odd number) + * and once after the atomic switch (even generation). + * + * A writer holding the write lock can immediately assume a crashed writer + * in case of an odd generation or they would not have been able to acquire + * the lock. A reader detecting an odd generation will use that number and + * be forced to re-read any revprop data - usually getting the new revprops + * already. If the generation file modification timestamp is too old, the + * reader will assume a crashed writer, acquire the write lock and bump + * the generation if it is still odd. So, for about REVPROP_CHANGE_TIMEOUT + * after the crash, reader caches may be stale. */ -/* Read revprop generation as stored on disk for repository FS. The result - * is returned in *CURRENT. Default to 2 if no such file is available. +/* If the revprop generation file in FS is open, close it. This is a no-op + * if the file is not open. */ static svn_error_t * -read_revprop_generation_file(apr_int64_t *current, - svn_fs_t *fs, - apr_pool_t *pool) +close_revprop_generation_file(svn_fs_t *fs, + apr_pool_t *scratch_pool) { - svn_error_t *err; - apr_file_t *file; - char buf[80]; - apr_size_t len; - const char *path = svn_fs_x__path_revprop_generation(fs, pool); - - err = svn_io_file_open(&file, path, - APR_READ | APR_BUFFERED, - APR_OS_DEFAULT, pool); - if (err && APR_STATUS_IS_ENOENT(err->apr_err)) + fs_x_data_t *ffd = fs->fsap_data; + if (ffd->revprop_generation_file) { - svn_error_clear(err); - *current = 2; - - return SVN_NO_ERROR; + SVN_ERR(svn_io_file_close(ffd->revprop_generation_file, scratch_pool)); + ffd->revprop_generation_file = NULL; } - SVN_ERR(err); - - len = sizeof(buf); - SVN_ERR(svn_io_read_length_line(file, buf, &len, pool)); - /* Check that the first line contains only digits. */ - SVN_ERR(svn_fs_x__check_file_buffer_numeric(buf, 0, path, - "Revprop Generation", pool)); - SVN_ERR(svn_cstring_atoi64(current, buf)); - - return svn_io_file_close(file, pool); + return SVN_NO_ERROR; } -/* Write the CURRENT revprop generation to disk for repository FS. +/* Make sure the revprop_generation member in FS is set. If READ_ONLY is + * set, open the file w/o write permission if the file is not open yet. + * The file is kept open if it has sufficient rights (or more) but will be + * closed and re-opened if it provided insufficient access rights. + * + * Call only for repos that support revprop caching. */ -svn_error_t * -svn_fs_x__write_revprop_generation_file(svn_fs_t *fs, - apr_int64_t current, - apr_pool_t *pool) +static svn_error_t * +open_revprop_generation_file(svn_fs_t *fs, + svn_boolean_t read_only, + apr_pool_t *scratch_pool) { - char buf[SVN_INT64_BUFFER_SIZE]; - apr_size_t len = svn__i64toa(buf, current); - buf[len] = '\n'; + fs_x_data_t *ffd = fs->fsap_data; + apr_int32_t flags = read_only ? APR_READ : (APR_READ | APR_WRITE); - SVN_ERR(svn_io_write_atomic(svn_fs_x__path_revprop_generation(fs, pool), - buf, len + 1, - NULL /* copy_perms */, pool)); + /* Close the current file handle if it has insufficient rights. */ + if ( ffd->revprop_generation_file + && (apr_file_flags_get(ffd->revprop_generation_file) & flags) != flags) + SVN_ERR(close_revprop_generation_file(fs, scratch_pool)); + + /* If not open already, open with sufficient rights. */ + if (ffd->revprop_generation_file == NULL) + { + const char *path = svn_fs_x__path_revprop_generation(fs, scratch_pool); + SVN_ERR(svn_io_file_open(&ffd->revprop_generation_file, path, + flags, APR_OS_DEFAULT, fs->pool)); + } return SVN_NO_ERROR; } -/* Make sure the revprop_namespace member in FS is set. */ +/* Return the textual representation of NUMBER and its checksum in *BUFFER. + */ static svn_error_t * -ensure_revprop_namespace(svn_fs_t *fs) +checkedsummed_number(svn_stringbuf_t **buffer, + apr_int64_t number, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - fs_x_data_t *ffd = fs->fsap_data; + svn_checksum_t *checksum; + const char *digest; + + char str[SVN_INT64_BUFFER_SIZE]; + apr_size_t len = svn__i64toa(str, number); + str[len] = 0; - return ffd->revprop_namespace == NULL - ? svn_atomic_namespace__create(&ffd->revprop_namespace, - svn_dirent_join(fs->path, - ATOMIC_REVPROP_NAMESPACE, - fs->pool), - fs->pool) - : SVN_NO_ERROR; + SVN_ERR(svn_checksum(&checksum, svn_checksum_md5, str, len, scratch_pool)); + digest = svn_checksum_to_cstring_display(checksum, scratch_pool); + + *buffer = svn_stringbuf_createf(result_pool, "%s %s\n", digest, str); + + return SVN_NO_ERROR; } -svn_error_t * -svn_fs_x__cleanup_revprop_namespace(svn_fs_t *fs) +/* Extract the generation number from the text BUFFER of LEN bytes and + * verify it against the checksum in the same BUFFER. If they match, return + * the generation in *NUMBER. Otherwise, return an error. + * BUFFER does not need to be NUL-terminated. + */ +static svn_error_t * +verify_extract_number(apr_int64_t *number, + const char *buffer, + apr_size_t len, + apr_pool_t *scratch_pool) { - const char *name = svn_dirent_join(fs->path, - ATOMIC_REVPROP_NAMESPACE, - fs->pool); - return svn_error_trace(svn_atomic_namespace__cleanup(name, fs->pool)); + const char *digest_end = strchr(buffer, ' '); + + /* Does the buffer even contain checksum _and_ number? */ + if (digest_end != NULL) + { + svn_checksum_t *expected; + svn_checksum_t *actual; + + SVN_ERR(svn_checksum_parse_hex(&expected, svn_checksum_md5, buffer, + scratch_pool)); + SVN_ERR(svn_checksum(&actual, svn_checksum_md5, digest_end + 1, + (buffer + len) - (digest_end + 1), scratch_pool)); + + if (svn_checksum_match(expected, actual)) + return svn_error_trace(svn_cstring_atoi64(number, digest_end + 1)); + } + + /* Incomplete buffer or not a match. */ + return svn_error_create(SVN_ERR_FS_INVALID_GENERATION, NULL, + _("Invalid generation number data.")); } -/* Make sure the revprop_generation member in FS is set and, if necessary, - * initialized with the latest value stored on disk. +/* Read revprop generation as stored on disk for repository FS. The result is + * returned in *CURRENT. Call only for repos that support revprop caching. */ static svn_error_t * -ensure_revprop_generation(svn_fs_t *fs, apr_pool_t *pool) +read_revprop_generation_file(apr_int64_t *current, + svn_fs_t *fs, + apr_pool_t *scratch_pool) { fs_x_data_t *ffd = fs->fsap_data; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + char buf[CHECKSUMMED_NUMBER_BUFFER_LEN]; + apr_size_t len; + apr_off_t offset = 0; + int i; + svn_error_t *err = SVN_NO_ERROR; - SVN_ERR(ensure_revprop_namespace(fs)); - if (ffd->revprop_generation == NULL) + /* Retry in case of incomplete file buffer updates. */ + for (i = 0; i < GENERATION_READ_RETRY_COUNT; ++i) { - apr_int64_t current; + svn_error_clear(err); + svn_pool_clear(iterpool); - SVN_ERR(svn_named_atomic__get(&ffd->revprop_generation, - ffd->revprop_namespace, - ATOMIC_REVPROP_GENERATION, - TRUE)); - - /* If the generation is at 0, we just created a new namespace - * (it would be at least 2 otherwise). Read the latest generation - * from disk and if we are the first one to initialize the atomic - * (i.e. is still 0), set it to the value just gotten. + /* If we can't even access the data, things are very wrong. + * Don't retry in that case. */ - SVN_ERR(svn_named_atomic__read(¤t, ffd->revprop_generation)); - if (current == 0) - { - SVN_ERR(read_revprop_generation_file(¤t, fs, pool)); - SVN_ERR(svn_named_atomic__cmpxchg(NULL, current, 0, - ffd->revprop_generation)); - } + SVN_ERR(open_revprop_generation_file(fs, TRUE, iterpool)); + SVN_ERR(svn_io_file_seek(ffd->revprop_generation_file, APR_SET, &offset, + iterpool)); + + len = sizeof(buf); + SVN_ERR(svn_io_read_length_line(ffd->revprop_generation_file, buf, &len, + iterpool)); + + /* Some data has been read. It will most likely be complete and + * consistent. Extract and verify anyway. */ + err = verify_extract_number(current, buf, len, iterpool); + if (!err) + break; + + /* Got unlucky and data was invalid. Retry. */ + SVN_ERR(close_revprop_generation_file(fs, iterpool)); + +#if APR_HAS_THREADS + apr_thread_yield(); +#else + apr_sleep(0); +#endif } - return SVN_NO_ERROR; + svn_pool_destroy(iterpool); + + /* If we had to give up, propagate the error. */ + return svn_error_trace(err); } -/* Make sure the revprop_timeout member in FS is set. */ +/* Write the CURRENT revprop generation to disk for repository FS. + * Call only for repos that support revprop caching. + */ static svn_error_t * -ensure_revprop_timeout(svn_fs_t *fs) +write_revprop_generation_file(svn_fs_t *fs, + apr_int64_t current, + apr_pool_t *scratch_pool) { fs_x_data_t *ffd = fs->fsap_data; + svn_stringbuf_t *buffer; + apr_off_t offset = 0; + + SVN_ERR(checkedsummed_number(&buffer, current, scratch_pool, scratch_pool)); + + SVN_ERR(open_revprop_generation_file(fs, FALSE, scratch_pool)); + SVN_ERR(svn_io_file_seek(ffd->revprop_generation_file, APR_SET, &offset, + scratch_pool)); + SVN_ERR(svn_io_file_write_full(ffd->revprop_generation_file, buffer->data, + buffer->len, NULL, scratch_pool)); + SVN_ERR(svn_io_file_flush_to_disk(ffd->revprop_generation_file, + scratch_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__reset_revprop_generation_file(svn_fs_t *fs, + apr_pool_t *scratch_pool) +{ + const char *path = svn_fs_x__path_revprop_generation(fs, scratch_pool); + svn_stringbuf_t *buffer; - SVN_ERR(ensure_revprop_namespace(fs)); - return ffd->revprop_timeout == NULL - ? svn_named_atomic__get(&ffd->revprop_timeout, - ffd->revprop_namespace, - ATOMIC_REVPROP_TIMEOUT, - TRUE) - : SVN_NO_ERROR; + /* Unconditionally close the revprop generation file. + * Don't care about FS formats. This ensures consistent internal state. */ + SVN_ERR(close_revprop_generation_file(fs, scratch_pool)); + + /* Unconditionally remove any old revprop generation file. + * Don't care about FS formats. This ensures consistent on-disk state + * for old format repositories. */ + SVN_ERR(svn_io_remove_file2(path, TRUE, scratch_pool)); + + /* Write the initial revprop generation file contents, if supported by + * the current format. This ensures consistent on-disk state for new + * format repositories. */ + SVN_ERR(checkedsummed_number(&buffer, 0, scratch_pool, scratch_pool)); + SVN_ERR(svn_io_write_atomic(path, buffer->data, buffer->len, NULL, + scratch_pool)); + + /* ffd->revprop_generation_file will be re-opened on demand. */ + + return SVN_NO_ERROR; } /* Create an error object with the given MESSAGE and pass it to the @@ -344,7 +421,8 @@ log_revprop_cache_init_warning(svn_fs_t /* Test whether revprop cache and necessary infrastructure are available in FS. */ static svn_boolean_t -has_revprop_cache(svn_fs_t *fs, apr_pool_t *pool) +has_revprop_cache(svn_fs_t *fs, + apr_pool_t *scratch_pool) { fs_x_data_t *ffd = fs->fsap_data; svn_error_t *error; @@ -353,23 +431,8 @@ has_revprop_cache(svn_fs_t *fs, apr_pool if (ffd->revprop_cache == NULL) return FALSE; - /* is it efficient? */ - if (!svn_named_atomic__is_efficient()) - { - /* access to it would be quite slow - * -> disable the revprop cache for good - */ - ffd->revprop_cache = NULL; - log_revprop_cache_init_warning(fs, NULL, - "Revprop caching for '%s' disabled" - " because it would be inefficient.", - pool); - - return FALSE; - } - - /* try to access our SHM-backed infrastructure */ - error = ensure_revprop_generation(fs, pool); + /* try initialize our file-backed infrastructure */ + error = open_revprop_generation_file(fs, TRUE, scratch_pool); if (error) { /* failure -> disable revprop cache for good */ @@ -377,9 +440,9 @@ has_revprop_cache(svn_fs_t *fs, apr_pool ffd->revprop_cache = NULL; log_revprop_cache_init_warning(fs, error, "Revprop caching for '%s' disabled " - "because SHM infrastructure for revprop " + "because infrastructure for revprop " "caching failed to initialize.", - pool); + scratch_pool); return FALSE; } @@ -393,8 +456,8 @@ typedef struct revprop_generation_fixup_ /* revprop generation to read */ apr_int64_t *generation; - /* containing the revprop_generation member to query */ - fs_x_data_t *ffd; + /* file system context */ + svn_fs_t *fs; } revprop_generation_upgrade_t; /* If the revprop generation has an odd value, it means the original writer @@ -406,23 +469,31 @@ typedef struct revprop_generation_fixup_ */ static svn_error_t * revprop_generation_fixup(void *void_baton, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { revprop_generation_upgrade_t *baton = void_baton; - assert(baton->ffd->has_write_lock); + fs_x_data_t *ffd = baton->fs->fsap_data; + assert(ffd->has_write_lock); + + /* Make sure we don't operate on stale OS buffers. */ + SVN_ERR(close_revprop_generation_file(baton->fs, scratch_pool)); /* Maybe, either the original revprop writer or some other reader has already corrected / bumped the revprop generation. Thus, we need - to read it again. */ - SVN_ERR(svn_named_atomic__read(baton->generation, - baton->ffd->revprop_generation)); + to read it again. However, we will now be the only ones changing + the file contents due to us holding the write lock. */ + SVN_ERR(read_revprop_generation_file(baton->generation, baton->fs, + scratch_pool)); /* Cause everyone to re-read revprops upon their next access, if the last revprop write did not complete properly. */ - while (*baton->generation % 2) - SVN_ERR(svn_named_atomic__add(baton->generation, - 1, - baton->ffd->revprop_generation)); + if (*baton->generation % 2) + { + ++*baton->generation; + SVN_ERR(write_revprop_generation_file(baton->fs, + *baton->generation, + scratch_pool)); + } return SVN_NO_ERROR; } @@ -433,42 +504,46 @@ revprop_generation_fixup(void *void_bato static svn_error_t * read_revprop_generation(apr_int64_t *generation, svn_fs_t *fs, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { apr_int64_t current = 0; fs_x_data_t *ffd = fs->fsap_data; /* read the current revprop generation number */ - SVN_ERR(ensure_revprop_generation(fs, pool)); - SVN_ERR(svn_named_atomic__read(¤t, ffd->revprop_generation)); + SVN_ERR(read_revprop_generation_file(¤t, fs, scratch_pool)); /* is an unfinished revprop write under the way? */ if (current % 2) { - apr_int64_t timeout = 0; + svn_boolean_t timeout = FALSE; - /* read timeout for the write operation */ - SVN_ERR(ensure_revprop_timeout(fs)); - SVN_ERR(svn_named_atomic__read(&timeout, ffd->revprop_timeout)); - - /* has the writer process been aborted, - * i.e. has the timeout been reached? + /* Has the writer process been aborted? + * Either by timeout or by us being the writer now. */ - if (apr_time_now() > timeout) + if (!ffd->has_write_lock) + { + apr_time_t mtime; + SVN_ERR(svn_io_file_affected_time(&mtime, + svn_fs_x__path_revprop_generation(fs, scratch_pool), + scratch_pool)); + timeout = apr_time_now() > mtime + REVPROP_CHANGE_TIMEOUT; + } + + if (ffd->has_write_lock || timeout) { revprop_generation_upgrade_t baton; baton.generation = ¤t; - baton.ffd = ffd; + baton.fs = fs; /* Ensure that the original writer process no longer exists by * acquiring the write lock to this repository. Then, fix up * the revprop generation. */ if (ffd->has_write_lock) - SVN_ERR(revprop_generation_fixup(&baton, pool)); + SVN_ERR(revprop_generation_fixup(&baton, scratch_pool)); else SVN_ERR(svn_fs_x__with_write_lock(fs, revprop_generation_fixup, - &baton, pool)); + &baton, scratch_pool)); } } @@ -477,64 +552,54 @@ read_revprop_generation(apr_int64_t *gen return SVN_NO_ERROR; } -/* Set the revprop generation to the next odd number to indicate that - there is a revprop write process under way. If that times out, - readers shall recover from that state & re-read revprops. - Use the access object in FS to set the shared mem value. */ +/* Set the revprop generation in FS to the next odd number to indicate + that there is a revprop write process under way. Return that value + in *GENERATION. If the change times out, readers shall recover from + that state & re-read revprops. + This is a no-op for repo formats that don't support revprop caching. */ static svn_error_t * -begin_revprop_change(svn_fs_t *fs, apr_pool_t *pool) +begin_revprop_change(apr_int64_t *generation, + svn_fs_t *fs, + apr_pool_t *scratch_pool) { - apr_int64_t current; fs_x_data_t *ffd = fs->fsap_data; + SVN_ERR_ASSERT(ffd->has_write_lock); - /* set the timeout for the write operation */ - SVN_ERR(ensure_revprop_timeout(fs)); - SVN_ERR(svn_named_atomic__write(NULL, - apr_time_now() + REVPROP_CHANGE_TIMEOUT, - ffd->revprop_timeout)); + /* Close and re-open to make sure we read the latest data. */ + SVN_ERR(close_revprop_generation_file(fs, scratch_pool)); + SVN_ERR(open_revprop_generation_file(fs, FALSE, scratch_pool)); - /* set the revprop generation to an odd value to indicate - * that a write is in progress + /* Set the revprop generation to an odd value to indicate + * that a write is in progress. */ - SVN_ERR(ensure_revprop_generation(fs, pool)); - do - { - SVN_ERR(svn_named_atomic__add(¤t, - 1, - ffd->revprop_generation)); - } - while (current % 2 == 0); + SVN_ERR(read_revprop_generation(generation, fs, scratch_pool)); + ++*generation; + SVN_ERR(write_revprop_generation_file(fs, *generation, scratch_pool)); return SVN_NO_ERROR; } -/* Set the revprop generation to the next even number to indicate that +/* Set the revprop generation in FS to the next even generation after + the odd value in GENERATION to indicate that a) readers shall re-read revprops, and - b) the write process has been completed (no recovery required) - Use the access object in FS to set the shared mem value. */ + b) the write process has been completed (no recovery required). + This is a no-op for repo formats that don't support revprop caching. */ static svn_error_t * -end_revprop_change(svn_fs_t *fs, apr_pool_t *pool) +end_revprop_change(svn_fs_t *fs, + apr_int64_t generation, + apr_pool_t *scratch_pool) { - apr_int64_t current = 1; fs_x_data_t *ffd = fs->fsap_data; + SVN_ERR_ASSERT(ffd->has_write_lock); + SVN_ERR_ASSERT(generation % 2); - /* set the revprop generation to an even value to indicate - * that a write has been completed + /* Set the revprop generation to an even value to indicate + * that a write has been completed. Since we held the write + * lock, nobody else could have updated the file contents. */ - SVN_ERR(ensure_revprop_generation(fs, pool)); - do - { - SVN_ERR(svn_named_atomic__add(¤t, - 1, - ffd->revprop_generation)); - } - while (current % 2); - - /* Save the latest generation to disk. FS is currently in a "locked" - * state such that we can be sure the be the only ones to write that - * file. - */ - return svn_fs_x__write_revprop_generation_file(fs, current, pool); + SVN_ERR(write_revprop_generation_file(fs, generation + 1, scratch_pool)); + + return SVN_NO_ERROR; } /* Container for all data required to access the packed revprop file @@ -943,6 +1008,7 @@ svn_error_t * svn_fs_x__get_revision_proplist(apr_hash_t **proplist_p, svn_fs_t *fs, svn_revnum_t rev, + svn_boolean_t bypass_cache, apr_pool_t *pool) { fs_x_data_t *ffd = fs->fsap_data; @@ -955,7 +1021,7 @@ svn_fs_x__get_revision_proplist(apr_hash SVN_ERR(svn_fs_x__ensure_revision_exists(rev, fs, pool)); /* Try cache lookup first. */ - if (has_revprop_cache(fs, pool)) + if (!bypass_cache && has_revprop_cache(fs, pool)) { svn_boolean_t is_cached; pair_cache_key_t key = { 0 }; @@ -1054,17 +1120,19 @@ switch_to_new_revprop(svn_fs_t *fs, svn_boolean_t bump_generation, apr_pool_t *pool) { + apr_int64_t generation; + /* Now, we may actually be replacing revprops. Make sure that all other threads and processes will know about this. */ if (bump_generation) - SVN_ERR(begin_revprop_change(fs, pool)); + SVN_ERR(begin_revprop_change(&generation, fs, pool)); SVN_ERR(svn_fs_x__move_into_place(tmp_path, final_path, perms_reference, pool)); /* Indicate that the update (if relevant) has been completed. */ if (bump_generation) - SVN_ERR(end_revprop_change(fs, pool)); + SVN_ERR(end_revprop_change(fs, generation, pool)); /* Clean up temporary files, if necessary. */ if (files_to_delete) @@ -1433,20 +1501,18 @@ svn_fs_x__set_revision_proplist(svn_fs_t is_packed = svn_fs_x__is_packed_revprop(fs, rev); /* Test whether revprops already exist for this revision. - * Only then will we need to bump the revprop generation. */ - if (has_revprop_cache(fs, pool)) + * Only then will we need to bump the revprop generation. + * The fact that they did not yet exist is never cached. */ + if (is_packed) { - if (is_packed) - { - bump_generation = TRUE; - } - else - { - svn_node_kind_t kind; - SVN_ERR(svn_io_check_path(svn_fs_x__path_revprops(fs, rev, pool), - &kind, pool)); - bump_generation = kind != svn_node_none; - } + bump_generation = TRUE; + } + else + { + svn_node_kind_t kind; + SVN_ERR(svn_io_check_path(svn_fs_x__path_revprops(fs, rev, pool), + &kind, pool)); + bump_generation = kind != svn_node_none; } /* Serialize the new revprop data */ Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/revprops.h URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/revprops.h?rev=1634609&r1=1634608&r2=1634609&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/revprops.h (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/revprops.h Mon Oct 27 17:28:13 2014 @@ -29,17 +29,14 @@ extern "C" { #endif /* __cplusplus */ -/* Write the CURRENT revprop generation to disk for repository FS. +/* Auto-create / replace the revprop generation file in FS with its + * initial contents. In any case, FS will not hold an open handle to + * it after this function succeeds. */ svn_error_t * -svn_fs_x__write_revprop_generation_file(svn_fs_t *fs, - apr_int64_t current, +svn_fs_x__reset_revprop_generation_file(svn_fs_t *fs, apr_pool_t *pool); -/* Make sure the revprop_namespace member in FS is set. */ -svn_error_t * -svn_fs_x__cleanup_revprop_namespace(svn_fs_t *fs); - /* In the filesystem FS, pack all revprop shards up to min_unpacked_rev. * * NOTE: Keep the old non-packed shards around until after the format bump. @@ -77,6 +74,7 @@ svn_fs_x__upgrade_cleanup_pack_revprops( apr_pool_t *scratch_pool); /* Read the revprops for revision REV in FS and return them in *PROPERTIES_P. + * If BYPASS_CACHE is set, don't consult the disks but always read from disk. * * Allocations will be done in POOL. */ @@ -84,6 +82,7 @@ svn_error_t * svn_fs_x__get_revision_proplist(apr_hash_t **proplist_p, svn_fs_t *fs, svn_revnum_t rev, + svn_boolean_t bypass_cache, apr_pool_t *pool); /* Set the revision property list of revision REV in filesystem FS to