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=1641647&r1=1641646&r2=1641647&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 Tue Nov 25 16:26:48 2014 @@ -48,12 +48,12 @@ #include "svn_private_config.h" -/* forward-declare */ +/* forward-declare. See implementation for the docstring */ static svn_error_t * block_read(void **result, svn_fs_t *fs, const svn_fs_x__id_part_t *id, - apr_file_t *revision_file, + svn_fs_x__revision_file_t *revision_file, apr_pool_t *result_pool, apr_pool_t *scratch_pool); @@ -70,7 +70,7 @@ static svn_error_t * dgb__log_access(svn_fs_t *fs, const svn_fs_x__id_part_t *id, void *item, - int item_type, + apr_uint32_t item_type, apr_pool_t *scratch_pool) { /* no-op if this macro is not defined */ @@ -192,21 +192,21 @@ aligned_seek(svn_fs_t *fs, store the newly opened file in FILE. Seek to the item's location before returning. Perform temporary allocations in POOL. */ static svn_error_t * -open_and_seek_revision(apr_file_t **file, +open_and_seek_revision(svn_fs_x__revision_file_t **file, svn_fs_t *fs, const svn_fs_x__id_part_t *id, apr_pool_t *pool) { - apr_file_t *rev_file; + svn_fs_x__revision_file_t *rev_file; apr_off_t offset = -1; apr_uint32_t sub_item = 0; svn_revnum_t rev = svn_fs_x__get_revnum(id->change_set); SVN_ERR(svn_fs_x__ensure_revision_exists(rev, fs, pool)); - SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, rev, pool)); - SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, id, pool)); - SVN_ERR(aligned_seek(fs, rev_file, NULL, offset, pool)); + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, rev, pool, pool)); + SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, rev_file, id, pool)); + SVN_ERR(aligned_seek(fs, rev_file->file, NULL, offset, pool)); *file = rev_file; @@ -217,24 +217,20 @@ open_and_seek_revision(apr_file_t **file to its position and store the newly opened file in FILE. Perform temporary allocations in POOL. */ static svn_error_t * -open_and_seek_transaction(apr_file_t **file, +open_and_seek_transaction(svn_fs_x__revision_file_t **file, svn_fs_t *fs, representation_t *rep, apr_pool_t *pool) { - apr_file_t *rev_file; apr_off_t offset; apr_uint32_t sub_item = 0; apr_int64_t txn_id = svn_fs_x__get_txn_id(rep->id.change_set); - SVN_ERR(svn_io_file_open(&rev_file, - svn_fs_x__path_txn_proto_rev(fs, txn_id, pool), - APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool)); + SVN_ERR(svn_fs_x__open_proto_rev_file(file, fs, txn_id, pool, pool)); - SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, &rep->id, pool)); - SVN_ERR(aligned_seek(fs, rev_file, NULL, offset, pool)); - - *file = rev_file; + SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, *file, &rep->id, + pool)); + SVN_ERR(aligned_seek(fs, (*file)->file, NULL, offset, pool)); return SVN_NO_ERROR; } @@ -243,7 +239,7 @@ open_and_seek_transaction(apr_file_t **f the correct file and seek to the correction location. Store this file in *FILE_P. Perform any allocations in POOL. */ static svn_error_t * -open_and_seek_representation(apr_file_t **file_p, +open_and_seek_representation(svn_fs_x__revision_file_t **file_p, svn_fs_t *fs, representation_t *rep, apr_pool_t *pool) @@ -274,20 +270,24 @@ static svn_error_t * get_node_revision_body(node_revision_t **noderev_p, svn_fs_t *fs, const svn_fs_id_t *id, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - apr_file_t *revision_file; svn_error_t *err; svn_boolean_t is_cached = FALSE; fs_x_data_t *ffd = fs->fsap_data; if (svn_fs_x__id_is_txn(id)) { + apr_file_t *file; + /* This is a transaction node-rev. Its storage logic is very different from that of rev / pack files. */ - err = svn_io_file_open(&revision_file, - svn_fs_x__path_txn_node_rev(fs, id, pool), - APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool); + err = svn_io_file_open(&file, + svn_fs_x__path_txn_node_rev(fs, id, + scratch_pool), + APR_READ | APR_BUFFERED, APR_OS_DEFAULT, + scratch_pool); if (err) { if (APR_STATUS_IS_ENOENT(err->apr_err)) @@ -300,33 +300,38 @@ get_node_revision_body(node_revision_t * } SVN_ERR(svn_fs_x__read_noderev(noderev_p, - svn_stream_from_aprfile2(revision_file, + svn_stream_from_aprfile2(file, FALSE, - pool), - pool)); + scratch_pool), + result_pool, scratch_pool)); } else { + svn_fs_x__revision_file_t *revision_file; + /* noderevs in rev / pack files can be cached */ const svn_fs_x__id_part_t *noderev_id = svn_fs_x__id_noderev_id(id); svn_revnum_t revision = svn_fs_x__get_revnum(noderev_id->change_set); pair_cache_key_t key; + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&revision_file, fs, revision, + scratch_pool, scratch_pool)); + /* First, try a noderevs container cache lookup. */ if ( svn_fs_x__is_packed_rev(fs, revision) && ffd->noderevs_container_cache) { apr_off_t offset; apr_uint32_t sub_item; - SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, noderev_id, - pool)); + SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, revision_file, + noderev_id, scratch_pool)); key.revision = svn_fs_x__packed_base_rev(fs, revision); key.second = offset; SVN_ERR(svn_cache__get_partial((void **)noderev_p, &is_cached, ffd->noderevs_container_cache, &key, svn_fs_x__noderevs_get_func, - &sub_item, pool)); + &sub_item, result_pool)); if (is_cached) return SVN_NO_ERROR; } @@ -342,22 +347,23 @@ get_node_revision_body(node_revision_t * &is_cached, ffd->node_revision_cache, &key, - pool)); + result_pool)); if (is_cached) return SVN_NO_ERROR; } - /* someone needs to read the data from this file: */ - err = open_and_seek_revision(&revision_file, fs, noderev_id, pool); + /* read the data from disk */ + SVN_ERR(open_and_seek_revision(&revision_file, fs, noderev_id, + scratch_pool)); /* block-read will parse the whole block and will also return - the one noderev that we need right now. */ + the one noderev that we need right now. */ SVN_ERR(block_read((void **)noderev_p, fs, noderev_id, revision_file, - pool, - pool)); - SVN_ERR(svn_io_file_close(revision_file, pool)); + result_pool, + scratch_pool)); + SVN_ERR(svn_fs_x__close_revision_file(revision_file)); } return SVN_NO_ERROR; @@ -367,21 +373,23 @@ svn_error_t * svn_fs_x__get_node_revision(node_revision_t **noderev_p, svn_fs_t *fs, const svn_fs_id_t *id, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { const svn_fs_x__id_part_t *noderev_id = svn_fs_x__id_noderev_id(id); - svn_error_t *err = get_node_revision_body(noderev_p, fs, id, pool); + svn_error_t *err = get_node_revision_body(noderev_p, fs, id, + result_pool, scratch_pool); if (err && err->apr_err == SVN_ERR_FS_CORRUPT) { - svn_string_t *id_string = svn_fs_x__id_unparse(id, pool); + svn_string_t *id_string = svn_fs_x__id_unparse(id, scratch_pool); return svn_error_createf(SVN_ERR_FS_CORRUPT, err, "Corrupt node-revision '%s'", id_string->data); } SVN_ERR(dgb__log_access(fs, noderev_id, *noderev_p, - SVN_FS_X__ITEM_TYPE_NODEREV, pool)); + SVN_FS_X__ITEM_TYPE_NODEREV, scratch_pool)); return svn_error_trace(err); } @@ -407,6 +415,10 @@ svn_fs_x__get_mergeinfo_count(apr_int64_ fs_x_data_t *ffd = fs->fsap_data; svn_revnum_t revision = svn_fs_x__get_revnum(noderev_id->change_set); + svn_fs_x__revision_file_t *rev_file; + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, revision, pool, + pool)); + if ( svn_fs_x__is_packed_rev(fs, revision) && ffd->noderevs_container_cache) { @@ -415,7 +427,7 @@ svn_fs_x__get_mergeinfo_count(apr_int64_ apr_uint32_t sub_item; svn_boolean_t is_cached; - SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, + SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, rev_file, noderev_id, pool)); key.revision = svn_fs_x__packed_base_rev(fs, revision); key.second = offset; @@ -431,7 +443,7 @@ svn_fs_x__get_mergeinfo_count(apr_int64_ #endif /* fallback to the naive implementation handling all edge cases */ - SVN_ERR(svn_fs_x__get_node_revision(&noderev, fs, id, pool)); + SVN_ERR(svn_fs_x__get_node_revision(&noderev, fs, id, pool, pool)); *count = noderev->mergeinfo_count; return SVN_NO_ERROR; @@ -442,10 +454,11 @@ svn_error_t * svn_fs_x__rev_get_root(svn_fs_id_t **root_id_p, svn_fs_t *fs, svn_revnum_t rev, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - SVN_ERR(svn_fs_x__ensure_revision_exists(rev, fs, pool)); - *root_id_p = svn_fs_x__id_create_root(rev, pool); + SVN_ERR(svn_fs_x__ensure_revision_exists(rev, fs, scratch_pool)); + *root_id_p = svn_fs_x__id_create_root(rev, result_pool); return SVN_NO_ERROR; } @@ -455,15 +468,13 @@ svn_fs_x__rev_get_root(svn_fs_id_t **roo typedef struct shared_file_t { /* The opened file. NULL while file is not open, yet. */ - apr_file_t *file; - - /* Stream wrapper around FILE. NULL while file is not open, yet. */ - svn_stream_t *stream; + svn_fs_x__revision_file_t *rfile; /* file system to open the file in */ svn_fs_t *fs; - /* revision contained in the file */ + /* a revision contained in the FILE. Since this file may be shared, + that value may be different from REP_STATE_T->REVISION. */ svn_revnum_t revision; /* pool to use when creating the FILE. This guarantees that the file @@ -477,7 +488,7 @@ typedef struct shared_file_t typedef struct rep_state_t { /* shared lazy-open rev/pack file structure */ - shared_file_t *file; + shared_file_t *sfile; /* The txdelta window cache to use or NULL. */ svn_cache__t *window_cache; /* Caches un-deltified windows. May be NULL. */ @@ -490,16 +501,96 @@ typedef struct rep_state_t apr_size_t header_size; apr_off_t start; /* The starting offset for the raw svndiff data minus header. - -1 if the offset is yet unknwon. */ + -1 if the offset is yet unknown. */ /* sub-item index in case the rep is containered */ apr_uint32_t sub_item; - apr_off_t current;/* The current offset relative to start. */ - apr_off_t size; /* Final value of CURRENT. */ + apr_off_t current;/* The current offset relative to START. */ + apr_off_t size; /* The on-disk size of the representation. */ int ver; /* If a delta, what svndiff version? -1 for unknown delta version. */ int chunk_index; /* number of the window to read */ } rep_state_t; +/* Simple wrapper around svn_fs_x__get_file_offset to simplify callers. */ +static svn_error_t * +get_file_offset(apr_off_t *offset, + rep_state_t *rs, + apr_pool_t *pool) +{ + return svn_error_trace(svn_fs_x__get_file_offset(offset, + rs->sfile->rfile->file, + pool)); +} + +/* Simple wrapper around svn_io_file_aligned_seek to simplify callers. */ +static svn_error_t * +rs_aligned_seek(rep_state_t *rs, + apr_off_t *buffer_start, + apr_off_t offset, + apr_pool_t *pool) +{ + fs_x_data_t *ffd = rs->sfile->fs->fsap_data; + return svn_error_trace(svn_io_file_aligned_seek(rs->sfile->rfile->file, + ffd->block_size, + buffer_start, offset, + pool)); +} + +/* Open FILE->FILE and FILE->STREAM if they haven't been opened, yet. */ +static svn_error_t* +auto_open_shared_file(shared_file_t *file) +{ + if (file->rfile == NULL) + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&file->rfile, file->fs, + file->revision, file->pool, + file->pool)); + + return SVN_NO_ERROR; +} + +/* Set RS->START to the begin of the representation raw in RS->SFILE->RFILE, + if that hasn't been done yet. Use POOL for temporary allocations. */ +static svn_error_t* +auto_set_start_offset(rep_state_t *rs, apr_pool_t *pool) +{ + if (rs->start == -1) + { + SVN_ERR(svn_fs_x__item_offset(&rs->start, &rs->sub_item, + rs->sfile->fs, rs->sfile->rfile, + &rs->rep_id, pool)); + rs->start += rs->header_size; + } + + return SVN_NO_ERROR; +} + +/* Set RS->VER depending on what is found in the already open RS->FILE->FILE + if the diff version is still unknown. Use POOL for temporary allocations. + */ +static svn_error_t* +auto_read_diff_version(rep_state_t *rs, apr_pool_t *pool) +{ + if (rs->ver == -1) + { + char buf[4]; + SVN_ERR(rs_aligned_seek(rs, NULL, rs->start, pool)); + SVN_ERR(svn_io_file_read_full2(rs->sfile->rfile->file, buf, + sizeof(buf), NULL, NULL, pool)); + + /* ### Layering violation */ + if (! ((buf[0] == 'S') && (buf[1] == 'V') && (buf[2] == 'N'))) + return svn_error_create + (SVN_ERR_FS_CORRUPT, NULL, + _("Malformed svndiff data in representation")); + rs->ver = buf[3]; + + rs->chunk_index = 0; + rs->current = 4; + } + + return SVN_NO_ERROR; +} + /* See create_rep_state, which wraps this and adds another error. */ static svn_error_t * create_rep_state_body(rep_state_t **rep_state, @@ -507,10 +598,11 @@ create_rep_state_body(rep_state_t **rep_ shared_file_t **shared_file, representation_t *rep, svn_fs_t *fs, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { fs_x_data_t *ffd = fs->fsap_data; - rep_state_t *rs = apr_pcalloc(pool, sizeof(*rs)); + rep_state_t *rs = apr_pcalloc(result_pool, sizeof(*rs)); svn_fs_x__rep_header_t *rh; svn_boolean_t is_cached = FALSE; svn_revnum_t revision = svn_fs_x__get_revnum(rep->id.change_set); @@ -525,7 +617,7 @@ create_rep_state_body(rep_state_t **rep_ * we can re-use the same, already open file object */ svn_boolean_t reuse_shared_file - = shared_file && *shared_file && (*shared_file)->file + = shared_file && *shared_file && (*shared_file)->rfile && SVN_IS_VALID_REVNUM((*shared_file)->revision) && (*shared_file)->revision < ffd->min_unpacked_rev && revision < ffd->min_unpacked_rev @@ -567,116 +659,109 @@ create_rep_state_body(rep_state_t **rep_ /* cache lookup, i.e. skip reading the rep header if possible */ if (ffd->rep_header_cache && SVN_IS_VALID_REVNUM(revision)) SVN_ERR(svn_cache__get((void **) &rh, &is_cached, - ffd->rep_header_cache, &key, pool)); + ffd->rep_header_cache, &key, result_pool)); - if (is_cached) + /* initialize the (shared) FILE member in RS */ + if (reuse_shared_file) { - if (reuse_shared_file) - { - rs->file = *shared_file; - } - else - { - shared_file_t *file = apr_pcalloc(pool, sizeof(*file)); - SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision)); - - file->revision = revision; - file->pool = pool; - file->fs = fs; - rs->file = file; - - /* remember the current file, if suggested by the caller */ - if (shared_file) - *shared_file = file; - } + rs->sfile = *shared_file; } else { + shared_file_t *file = apr_pcalloc(result_pool, sizeof(*file)); + file->revision = revision; + file->pool = result_pool; + file->fs = fs; + rs->sfile = file; + + /* remember the current file, if suggested by the caller */ + if (shared_file) + *shared_file = file; + } + + /* read rep header, if necessary */ + if (!is_cached) + { /* we will need the on-disk location for non-txn reps */ apr_off_t offset; - apr_uint32_t sub_item; + svn_boolean_t in_container = TRUE; - if (SVN_IS_VALID_REVNUM(revision)) - SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, &rep->id, pool)); + /* ensure file is open and navigate to the start of rep header */ + if (reuse_shared_file) + { + /* ... we can re-use the same, already open file object. + * This implies that we don't read from a txn. + */ + rs->sfile = *shared_file; + SVN_ERR(auto_open_shared_file(rs->sfile)); + } + else + { + /* otherwise, create a new file object. May or may not be + * an in-txn file. + */ + SVN_ERR(open_and_seek_representation(&rs->sfile->rfile, fs, rep, + result_pool)); + } - /* is rep stored in some star-deltified container? */ if (SVN_IS_VALID_REVNUM(revision)) { - svn_boolean_t in_container = TRUE; + apr_uint32_t sub_item; + + SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, + rs->sfile->rfile, &rep->id, + scratch_pool)); + + /* is rep stored in some star-deltified container? */ if (sub_item == 0) { svn_fs_x__p2l_entry_t *entry; - SVN_ERR(svn_fs_x__p2l_entry_lookup(&entry, fs, revision, - offset, pool)); + SVN_ERR(svn_fs_x__p2l_entry_lookup(&entry, fs, rs->sfile->rfile, + revision, offset, + scratch_pool, scratch_pool)); in_container = entry->type == SVN_FS_X__ITEM_TYPE_REPS_CONT; } if (in_container) { /* construct a container rep header */ - *rep_header = apr_pcalloc(pool, sizeof(**rep_header)); + *rep_header = apr_pcalloc(result_pool, sizeof(**rep_header)); (*rep_header)->type = svn_fs_x__rep_container; - /* provide an empty shared file struct */ - rs->file = apr_pcalloc(pool, sizeof(*rs->file)); - rs->file->revision = revision; - rs->file->pool = pool; - rs->file->fs = fs; - /* exit to caller */ *rep_state = rs; return SVN_NO_ERROR; } - } - - if (reuse_shared_file) - { - /* ... we can re-use the same, already open file object - */ - SVN_ERR_ASSERT(sub_item == 0); - SVN_ERR(aligned_seek(fs, (*shared_file)->file, NULL, offset, pool)); - rs->file = *shared_file; + SVN_ERR(rs_aligned_seek(rs, NULL, offset, scratch_pool)); } - else - { - shared_file_t *file = apr_pcalloc(pool, sizeof(*file)); - file->revision = revision; - file->pool = pool; - file->fs = fs; - /* otherwise, create a new file object - */ - SVN_ERR(open_and_seek_representation(&file->file, fs, rep, pool)); - file->stream = svn_stream_from_aprfile2(file->file, TRUE, - file->pool); - rs->file = file; - - /* remember the current file, if suggested by the caller */ - if (shared_file) - *shared_file = file; - } - - SVN_ERR(svn_fs_x__read_rep_header(&rh, rs->file->stream, pool)); - SVN_ERR(svn_fs_x__get_file_offset(&rs->start, rs->file->file, pool)); + SVN_ERR(svn_fs_x__read_rep_header(&rh, rs->sfile->rfile->stream, + result_pool, scratch_pool)); + SVN_ERR(get_file_offset(&rs->start, rs, result_pool)); + /* populate the cache if appropriate */ if (SVN_IS_VALID_REVNUM(revision)) { - SVN_ERR(block_read(NULL, fs, &rs->rep_id, rs->file->file, pool, pool)); + SVN_ERR(block_read(NULL, fs, &rs->rep_id, rs->sfile->rfile, + result_pool, scratch_pool)); if (ffd->rep_header_cache) - SVN_ERR(svn_cache__set(ffd->rep_header_cache, &key, rh, pool)); + SVN_ERR(svn_cache__set(ffd->rep_header_cache, &key, rh, + scratch_pool)); } } + /* finalize */ SVN_ERR(dgb__log_access(fs, &rs->rep_id, rh, SVN_FS_X__ITEM_TYPE_ANY_REP, - pool)); + scratch_pool)); rs->header_size = rh->header_size; *rep_state = rs; *rep_header = rh; - /* We are dealing with a delta, find out what version. */ rs->chunk_index = 0; + + /* skip "SVNx" diff marker */ rs->current = 4; return SVN_NO_ERROR; @@ -699,14 +784,14 @@ create_rep_state(rep_state_t **rep_state shared_file_t **shared_file, representation_t *rep, svn_fs_t *fs, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_error_t *err = create_rep_state_body(rep_state, rep_header, - shared_file, rep, fs, pool); + shared_file, rep, fs, + result_pool, scratch_pool); if (err && err->apr_err == SVN_ERR_FS_CORRUPT) { - fs_x_data_t *ffd = fs->fsap_data; - /* ### This always returns "-1" for transaction reps, because ### this particular bit of code doesn't know if the rep is ### stored in the protorev or in the mutable area (for props @@ -718,7 +803,8 @@ create_rep_state(rep_state_t **rep_state "Corrupt representation '%s'", rep ? svn_fs_x__unparse_representation - (rep, ffd->format, TRUE, pool)->data + (rep, TRUE, scratch_pool, + scratch_pool)->data : "(null)"); } /* ### Call representation_string() ? */ @@ -728,18 +814,24 @@ create_rep_state(rep_state_t **rep_state svn_error_t * svn_fs_x__check_rep(representation_t *rep, svn_fs_t *fs, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { apr_off_t offset; apr_uint32_t sub_item; svn_fs_x__p2l_entry_t *entry; svn_revnum_t revision = svn_fs_x__get_revnum(rep->id.change_set); + svn_fs_x__revision_file_t *rev_file; + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&rev_file, fs, revision, + scratch_pool, scratch_pool)); + /* Does REP->ID refer to an actual item? Which one is it? */ - SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, &rep->id, pool)); + SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, rev_file, &rep->id, + scratch_pool)); /* What is the type of that item? */ - SVN_ERR(svn_fs_x__p2l_entry_lookup(&entry, fs, revision, offset, pool)); + SVN_ERR(svn_fs_x__p2l_entry_lookup(&entry, fs, rev_file, revision, offset, + scratch_pool, scratch_pool)); /* Verify that we've got an item that is actually a representation. */ if ( entry == NULL @@ -751,8 +843,8 @@ svn_fs_x__check_rep(representation_t *re return svn_error_createf(SVN_ERR_REPOS_CORRUPTED, NULL, _("No representation found at offset %s " "for item %s in revision %ld"), - apr_off_t_toa(pool, offset), - apr_psprintf(pool, "%" APR_UINT64_T_FMT, + apr_off_t_toa(scratch_pool, offset), + apr_psprintf(scratch_pool, "%" APR_UINT64_T_FMT, rep->id.number), revision); @@ -766,12 +858,10 @@ svn_fs_x__rep_chain_length(int *chain_le int *shard_count, representation_t *rep, svn_fs_t *fs, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { fs_x_data_t *ffd = fs->fsap_data; - svn_revnum_t shard_size = ffd->max_files_per_dir - ? ffd->max_files_per_dir - : 1; + svn_revnum_t shard_size = ffd->max_files_per_dir; svn_boolean_t is_delta = FALSE; int count = 0; int shards = 1; @@ -782,7 +872,7 @@ svn_fs_x__rep_chain_length(int *chain_le * 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); + apr_pool_t *iterpool = svn_pool_create(scratch_pool); /* Check whether the length of the deltification chain is acceptable. * Otherwise, shared reps may form a non-skipping delta chain in @@ -811,6 +901,7 @@ svn_fs_x__rep_chain_length(int *chain_le &file_hint, &base_rep, fs, + iterpool, iterpool)); base_rep.id.change_set @@ -970,9 +1061,14 @@ get_cached_window_sizes_func(void **out, return SVN_NO_ERROR; } -/* Return the packed & expanded sizes of the window addressed by RS. If the - * window cannot be found in the window cache, set *IS_CACHED to FALSE. - * Otherwise, set it to TRUE and return the data in *SIZES, allocated in POOL. +/* Read the WINDOW_P number CHUNK_INDEX for the representation given in + * rep state RS from the current FSFS session's cache. This will be a + * no-op and IS_CACHED will be set to FALSE if no cache has been given. + * If a cache is available IS_CACHED will inform the caller about the + * success of the lookup. Allocations of the window in will be made + * from RESULT_POOL. Use SCRATCH_POOL for temporary allocations. + * + * If the information could be found, put RS to CHUNK_INDEX. */ static svn_error_t * get_cached_window_sizes(window_sizes_t **sizes, @@ -1005,7 +1101,8 @@ get_cached_window(svn_txdelta_window_t * rep_state_t *rs, int chunk_index, svn_boolean_t *is_cached, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { if (! rs->window_cache) { @@ -1023,7 +1120,7 @@ get_cached_window(svn_txdelta_window_t * is_cached, rs->window_cache, &key, - pool)); + result_pool)); if (*is_cached) { @@ -1146,19 +1243,23 @@ build_rep_list(apr_array_header_t **list svn_fs_x__rep_header_t *rep_header; svn_boolean_t is_cached = FALSE; shared_file_t *shared_file = NULL; + apr_pool_t *iterpool = svn_pool_create(pool); *list = apr_array_make(pool, 1, sizeof(rep_state_t *)); rep = *first_rep; /* for the top-level rep, we need the rep_args */ - SVN_ERR(create_rep_state(&rs, &rep_header, &shared_file, &rep, fs, pool)); + SVN_ERR(create_rep_state(&rs, &rep_header, &shared_file, &rep, fs, pool, + iterpool)); while (1) { + svn_pool_clear(iterpool); + /* fetch state, if that has not been done already */ if (!rs) SVN_ERR(create_rep_state(&rs, &rep_header, &shared_file, - &rep, fs, pool)); + &rep, fs, pool, iterpool)); /* for txn reps and containered reps, there won't be a cached * combined window */ @@ -1174,14 +1275,14 @@ build_rep_list(apr_array_header_t **list rs->current = 0; rs->size = (*window_p)->len; *src_state = rs; - return SVN_NO_ERROR; + break; } if (rep_header->type == svn_fs_x__rep_container) { /* This is a container item, so just return the current rep_state. */ *src_state = rs; - return SVN_NO_ERROR; + break; } /* Push this rep onto the list. If it's self-compressed, we're done. */ @@ -1189,7 +1290,7 @@ build_rep_list(apr_array_header_t **list if (rep_header->type == svn_fs_x__rep_self_delta) { *src_state = NULL; - return SVN_NO_ERROR; + break; } rep.id.change_set @@ -1199,6 +1300,9 @@ build_rep_list(apr_array_header_t **list rs = NULL; } + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; } @@ -1240,68 +1344,12 @@ rep_read_get_baton(struct rep_read_baton return SVN_NO_ERROR; } -/* Open FILE->FILE and FILE->STREAM if they haven't been opened, yet. */ -static svn_error_t* -auto_open_shared_file(shared_file_t *file) -{ - if (file->file == NULL) - { - SVN_ERR(svn_fs_x__open_pack_or_rev_file(&file->file, file->fs, - file->revision, file->pool)); - file->stream = svn_stream_from_aprfile2(file->file, TRUE, file->pool); - } - - return SVN_NO_ERROR; -} - -/* Set RS->START to the begin of the representation raw in RS->FILE->FILE, - if that hasn't been done yet. Use POOL for temporary allocations. */ -static svn_error_t* -auto_set_start_offset(rep_state_t *rs, apr_pool_t *pool) -{ - if (rs->start == -1) - { - SVN_ERR(svn_fs_x__item_offset(&rs->start, &rs->sub_item, - rs->file->fs, &rs->rep_id, pool)); - rs->start += rs->header_size; - } - - return SVN_NO_ERROR; -} - -/* Set RS->VER depending on what is found in the already open RS->FILE->FILE - if the diff version is still unknown. Use POOL for temporary allocations. - */ -static svn_error_t* -auto_read_diff_version(rep_state_t *rs, apr_pool_t *pool) -{ - if (rs->ver == -1) - { - char buf[4]; - SVN_ERR(aligned_seek(rs->file->fs, rs->file->file, NULL, rs->start, - pool)); - SVN_ERR(svn_io_file_read_full2(rs->file->file, buf, sizeof(buf), - NULL, NULL, pool)); - - /* ### Layering violation */ - if (! ((buf[0] == 'S') && (buf[1] == 'V') && (buf[2] == 'N'))) - return svn_error_create - (SVN_ERR_FS_CORRUPT, NULL, - _("Malformed svndiff data in representation")); - rs->ver = buf[3]; - - rs->chunk_index = 0; - rs->current = 4; - } - - return SVN_NO_ERROR; -} - /* Skip forwards to THIS_CHUNK in REP_STATE and then read the next delta window into *NWIN. */ static svn_error_t * read_delta_window(svn_txdelta_window_t **nwin, int this_chunk, - rep_state_t *rs, apr_pool_t *pool) + rep_state_t *rs, apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_boolean_t is_cached; apr_off_t start_offset; @@ -1310,16 +1358,17 @@ read_delta_window(svn_txdelta_window_t * SVN_ERR_ASSERT(rs->chunk_index <= this_chunk); - SVN_ERR(dgb__log_access(rs->file->fs, &rs->rep_id, NULL, - SVN_FS_X__ITEM_TYPE_ANY_REP, pool)); + SVN_ERR(dgb__log_access(rs->sfile->fs, &rs->rep_id, NULL, + SVN_FS_X__ITEM_TYPE_ANY_REP, scratch_pool)); /* Read the next window. But first, try to find it in the cache. */ - SVN_ERR(get_cached_window(nwin, rs, this_chunk, &is_cached, pool)); + SVN_ERR(get_cached_window(nwin, rs, this_chunk, &is_cached, + result_pool, scratch_pool)); if (is_cached) return SVN_NO_ERROR; /* someone has to actually read the data from file. Open it */ - SVN_ERR(auto_open_shared_file(rs->file)); + SVN_ERR(auto_open_shared_file(rs->sfile)); /* invoke the 'block-read' feature for non-txn data. However, don't do that if we are in the middle of some representation, @@ -1328,37 +1377,38 @@ read_delta_window(svn_txdelta_window_t * && svn_fs_x__is_revision(rs->rep_id.change_set) && rs->window_cache) { - SVN_ERR(block_read(NULL, rs->file->fs, &rs->rep_id, rs->file->file, - pool, pool)); + SVN_ERR(block_read(NULL, rs->sfile->fs, &rs->rep_id, + rs->sfile->rfile, result_pool, scratch_pool)); /* reading the whole block probably also provided us with the desired txdelta window */ - SVN_ERR(get_cached_window(nwin, rs, this_chunk, &is_cached, pool)); + SVN_ERR(get_cached_window(nwin, rs, this_chunk, &is_cached, + result_pool, scratch_pool)); if (is_cached) return SVN_NO_ERROR; } /* data is still not cached -> we need to read it. Make sure we have all the necessary info. */ - SVN_ERR(auto_set_start_offset(rs, pool)); - SVN_ERR(auto_read_diff_version(rs, pool)); + SVN_ERR(auto_set_start_offset(rs, scratch_pool)); + SVN_ERR(auto_read_diff_version(rs, scratch_pool)); /* RS->FILE may be shared between RS instances -> make sure we point * to the right data. */ start_offset = rs->start + rs->current; - SVN_ERR(aligned_seek(rs->file->fs, rs->file->file, NULL, start_offset, - pool)); + SVN_ERR(rs_aligned_seek(rs, NULL, start_offset, scratch_pool)); /* Skip windows to reach the current chunk if we aren't there yet. */ - iterpool = svn_pool_create(pool); + iterpool = svn_pool_create(scratch_pool); while (rs->chunk_index < this_chunk) { + apr_file_t *file = rs->sfile->rfile->file; svn_pool_clear(iterpool); - SVN_ERR(svn_txdelta_skip_svndiff_window(rs->file->file, rs->ver, - iterpool)); + + SVN_ERR(svn_txdelta_skip_svndiff_window(file, rs->ver, iterpool)); rs->chunk_index++; - SVN_ERR(svn_fs_x__get_file_offset(&start_offset, rs->file->file, - iterpool)); + SVN_ERR(svn_fs_x__get_file_offset(&start_offset, file, iterpool)); + rs->current = start_offset - rs->start; if (rs->current >= rs->size) return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, @@ -1369,9 +1419,9 @@ read_delta_window(svn_txdelta_window_t * svn_pool_destroy(iterpool); /* Actually read the next window. */ - SVN_ERR(svn_txdelta_read_svndiff_window(nwin, rs->file->stream, rs->ver, - pool)); - SVN_ERR(svn_fs_x__get_file_offset(&end_offset, rs->file->file, pool)); + SVN_ERR(svn_txdelta_read_svndiff_window(nwin, rs->sfile->rfile->stream, + rs->ver, result_pool)); + SVN_ERR(get_file_offset(&end_offset, rs, scratch_pool)); rs->current = end_offset - rs->start; if (rs->current > rs->size) return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, @@ -1381,7 +1431,7 @@ read_delta_window(svn_txdelta_window_t * /* the window has not been cached before, thus cache it now * (if caching is used for them at all) */ if (svn_fs_x__is_revision(rs->rep_id.change_set)) - SVN_ERR(set_cached_window(*nwin, rs, start_offset, pool)); + SVN_ERR(set_cached_window(*nwin, rs, start_offset, scratch_pool)); return SVN_NO_ERROR; } @@ -1391,15 +1441,16 @@ static svn_error_t * read_container_window(svn_stringbuf_t **nwin, rep_state_t *rs, apr_size_t size, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_fs_x__rep_extractor_t *extractor = NULL; - svn_fs_t *fs = rs->file->fs; + svn_fs_t *fs = rs->sfile->fs; fs_x_data_t *ffd = fs->fsap_data; pair_cache_key_t key; svn_revnum_t revision = svn_fs_x__get_revnum(rs->rep_id.change_set); - SVN_ERR(auto_set_start_offset(rs, pool)); + SVN_ERR(auto_set_start_offset(rs, scratch_pool)); key.revision = svn_fs_x__packed_base_rev(fs, revision); key.second = rs->start; @@ -1414,19 +1465,19 @@ read_container_window(svn_stringbuf_t ** SVN_ERR(svn_cache__get_partial((void**)&extractor, &is_cached, ffd->reps_container_cache, &key, svn_fs_x__reps_get_func, &baton, - pool)); + result_pool)); } /* read from disk, if necessary */ if (extractor == NULL) { - SVN_ERR(auto_open_shared_file(rs->file)); + SVN_ERR(auto_open_shared_file(rs->sfile)); SVN_ERR(block_read((void **)&extractor, fs, &rs->rep_id, - rs->file->file, pool, pool)); + rs->sfile->rfile, result_pool, scratch_pool)); } SVN_ERR(svn_fs_x__extractor_drive(nwin, extractor, rs->current, size, - pool, pool)); + result_pool, scratch_pool)); /* Update RS. */ rs->current += (apr_off_t)size; @@ -1446,6 +1497,7 @@ get_combined_window(svn_stringbuf_t **re apr_array_header_t *windows; svn_stringbuf_t *source, *buf = rb->base_window; rep_state_t *rs; + apr_pool_t *iterpool; /* Read all windows that we need to combine. This is fine because the size of each window is relatively small (100kB) and skip- @@ -1453,12 +1505,16 @@ get_combined_window(svn_stringbuf_t **re Stop early if one of them does not depend on its predecessors. */ window_pool = svn_pool_create(rb->pool); windows = apr_array_make(window_pool, 0, sizeof(svn_txdelta_window_t *)); + iterpool = svn_pool_create(rb->pool); for (i = 0; i < rb->rs_list->nelts; ++i) { svn_txdelta_window_t *window; + svn_pool_clear(iterpool); + rs = APR_ARRAY_IDX(rb->rs_list, i, rep_state_t *); - SVN_ERR(read_delta_window(&window, rb->chunk_index, rs, window_pool)); + SVN_ERR(read_delta_window(&window, rb->chunk_index, rs, window_pool, + iterpool)); APR_ARRAY_PUSH(windows, svn_txdelta_window_t *) = window; if (window->src_ops == 0) @@ -1474,6 +1530,8 @@ get_combined_window(svn_stringbuf_t **re { svn_txdelta_window_t *window; + svn_pool_clear(iterpool); + rs = APR_ARRAY_IDX(rb->rs_list, i, rep_state_t *); window = APR_ARRAY_IDX(windows, i, svn_txdelta_window_t *); @@ -1484,7 +1542,7 @@ get_combined_window(svn_stringbuf_t **re source = buf; if (source == NULL && rb->src_state != NULL) SVN_ERR(read_container_window(&source, rb->src_state, - window->sview_len, pool)); + window->sview_len, pool, iterpool)); /* Combine this window with the current one. */ new_pool = svn_pool_create(rb->pool); @@ -1511,6 +1569,7 @@ get_combined_window(svn_stringbuf_t **re svn_pool_destroy(pool); pool = new_pool; } + svn_pool_destroy(iterpool); svn_pool_destroy(window_pool); @@ -1549,8 +1608,7 @@ static svn_error_t * init_rep_state(rep_state_t *rs, svn_fs_x__rep_header_t *rep_header, svn_fs_t *fs, - apr_file_t *file, - svn_stream_t *stream, + svn_fs_x__revision_file_t *rev_file, svn_fs_x__p2l_entry_t* entry, apr_pool_t *pool) { @@ -1562,13 +1620,12 @@ init_rep_state(rep_state_t *rs, && entry->type <= SVN_FS_X__ITEM_TYPE_DIR_PROPS); SVN_ERR_ASSERT(entry->item_count == 1); - shared_file->file = file; - shared_file->stream = stream; + shared_file->rfile = rev_file; shared_file->fs = fs; shared_file->revision = svn_fs_x__get_revnum(entry->items[0].change_set); shared_file->pool = pool; - rs->file = shared_file; + rs->sfile = shared_file; rs->rep_id = entry->items[0]; rs->header_size = rep_header->header_size; rs->start = entry->offset + rs->header_size; @@ -1627,16 +1684,17 @@ cache_windows(svn_filesize_t *fulltext_l apr_off_t block_start; /* navigate to & read the current window */ - SVN_ERR(aligned_seek(fs, rs->file->file, &block_start, - start_offset, pool)); - SVN_ERR(svn_txdelta_read_svndiff_window(&window, rs->file->stream, + SVN_ERR(rs_aligned_seek(rs, &block_start, start_offset, pool)); + SVN_ERR(svn_txdelta_read_svndiff_window(&window, + rs->sfile->rfile->stream, rs->ver, pool)); /* aggregate expanded window size */ *fulltext_len += window->tview_len; /* determine on-disk window size */ - SVN_ERR(svn_fs_x__get_file_offset(&end_offset, rs->file->file, + SVN_ERR(svn_fs_x__get_file_offset(&end_offset, + rs->sfile->rfile->file, pool)); rs->current = end_offset - rs->start; if (rs->current > rs->size) @@ -1681,7 +1739,7 @@ read_rep_header(svn_fs_x__rep_header_t * return SVN_NO_ERROR; } - SVN_ERR(svn_fs_x__read_rep_header(rep_header, stream, pool)); + SVN_ERR(svn_fs_x__read_rep_header(rep_header, stream, pool, pool)); if (ffd->rep_header_cache) SVN_ERR(svn_cache__set(ffd->rep_header_cache, key, *rep_header, pool)); @@ -1693,8 +1751,7 @@ svn_error_t * svn_fs_x__get_representation_length(svn_filesize_t *packed_len, svn_filesize_t *expanded_len, svn_fs_t *fs, - apr_file_t *file, - svn_stream_t *stream, + svn_fs_x__revision_file_t *rev_file, svn_fs_x__p2l_entry_t* entry, apr_pool_t *pool) { @@ -1711,12 +1768,12 @@ svn_fs_x__get_representation_length(svn_ key.revision = svn_fs_x__get_revnum(entry->items[0].change_set); key.is_packed = svn_fs_x__is_packed_rev(fs, key.revision); key.item_index = entry->items[0].number; - SVN_ERR(read_rep_header(&rep_header, fs, stream, &key, pool)); + SVN_ERR(read_rep_header(&rep_header, fs, rev_file->stream, &key, pool)); /* prepare representation reader state (rs) structure */ - SVN_ERR(init_rep_state(&rs, rep_header, fs, file, stream, entry, pool)); + SVN_ERR(init_rep_state(&rs, rep_header, fs, rev_file, entry, pool)); - /* RS->FILE may be shared between RS instances -> make sure we point + /* RS->SFILE may be shared between RS instances -> make sure we point * to the right data. */ *packed_len = rs.size; SVN_ERR(cache_windows(expanded_len, fs, &rs, -1, pool)); @@ -1749,7 +1806,7 @@ get_contents_from_windows(struct rep_rea * the delta rep size _before_ putting the data into a * a container. */ SVN_ERR(read_container_window(&rb->base_window, rs, - rb->len, rb->pool)); + rb->len, rb->pool, rb->pool)); rs->current -= rb->base_window->len; } @@ -2221,14 +2278,18 @@ delta_read_next_window(svn_txdelta_windo apr_pool_t *pool) { struct delta_read_baton *drb = baton; + apr_pool_t *scratch_pool = svn_pool_create(pool); *window = NULL; if (drb->rs->current < drb->rs->size) { - SVN_ERR(read_delta_window(window, drb->rs->chunk_index, drb->rs, pool)); + SVN_ERR(read_delta_window(window, drb->rs->chunk_index, drb->rs, pool, + scratch_pool)); drb->rs->chunk_index++; } + svn_pool_destroy(scratch_pool); + return SVN_NO_ERROR; } @@ -2276,7 +2337,7 @@ svn_fs_x__get_file_delta_stream(svn_txde { /* Read target's base rep if any. */ SVN_ERR(create_rep_state(&rep_state, &rep_header, NULL, - target->data_rep, fs, pool)); + target->data_rep, fs, pool, pool)); /* Try a shortcut: if the target is stored as a delta against the source, then just use that delta. */ @@ -2307,10 +2368,10 @@ svn_fs_x__get_file_delta_stream(svn_txde } /* Don't keep file handles open for longer than necessary. */ - if (rep_state->file->file) + if (rep_state->sfile->rfile) { - SVN_ERR(svn_io_file_close(rep_state->file->file, pool)); - rep_state->file->file = NULL; + SVN_ERR(svn_fs_x__close_revision_file(rep_state->sfile->rfile)); + rep_state->sfile->rfile = NULL; } } @@ -2446,7 +2507,7 @@ read_dir_entries(apr_array_header_t *ent _("Directory entry corrupt in '%s'"), svn_fs_x__id_unparse(id, scratch_pool)->data); - dirent->id = svn_fs_x__id_parse(str, strlen(str), result_pool); + SVN_ERR(svn_fs_x__id_parse(&dirent->id, str, result_pool)); /* In incremental mode, update the hash; otherwise, write to the * final array. */ @@ -2502,26 +2563,22 @@ get_dir_contents(apr_array_header_t **en } else if (noderev->data_rep) { - /* use a temporary pool for temp objects. - * Also undeltify content before parsing it. Otherwise, we could only + /* Undeltify content before parsing it. Otherwise, we could only * parse it byte-by-byte. */ - apr_pool_t *text_pool = svn_pool_create(scratch_pool); apr_size_t len = noderev->data_rep->expanded_size; svn_stringbuf_t *text; /* The representation is immutable. Read it normally. */ SVN_ERR(svn_fs_x__get_contents(&contents, fs, noderev->data_rep, - FALSE, text_pool)); - SVN_ERR(svn_stringbuf_from_stream(&text, contents, len, text_pool)); + FALSE, scratch_pool)); + SVN_ERR(svn_stringbuf_from_stream(&text, contents, len, scratch_pool)); SVN_ERR(svn_stream_close(contents)); /* de-serialize hash */ - contents = svn_stream_from_stringbuf(text, text_pool); + contents = svn_stream_from_stringbuf(text, scratch_pool); SVN_ERR(read_dir_entries(*entries, contents, FALSE, noderev->id, result_pool, scratch_pool)); - - svn_pool_destroy(text_pool); } return SVN_NO_ERROR; @@ -2723,7 +2780,7 @@ svn_fs_x__get_changes(apr_array_header_t svn_revnum_t rev, apr_pool_t *result_pool) { - apr_file_t *revision_file; + svn_fs_x__revision_file_t *revision_file; svn_boolean_t found; fs_x_data_t *ffd = fs->fsap_data; apr_pool_t *scratch_pool = svn_pool_create(result_pool); @@ -2732,6 +2789,12 @@ svn_fs_x__get_changes(apr_array_header_t id.change_set = svn_fs_x__change_set_by_rev(rev); id.number = SVN_FS_X__ITEM_INDEX_CHANGES; + /* Provide revision file. */ + + SVN_ERR(svn_fs_x__ensure_revision_exists(rev, fs, scratch_pool)); + SVN_ERR(svn_fs_x__open_pack_or_rev_file(&revision_file, fs, rev, + scratch_pool, scratch_pool)); + /* try cache lookup first */ if (ffd->changes_container_cache && svn_fs_x__is_packed_rev(fs, rev)) @@ -2740,8 +2803,8 @@ svn_fs_x__get_changes(apr_array_header_t apr_uint32_t sub_item; pair_cache_key_t key; - SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, &id, - scratch_pool)); + SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, revision_file, + &id, scratch_pool)); key.revision = svn_fs_x__packed_base_rev(fs, rev); key.second = offset; @@ -2762,17 +2825,11 @@ svn_fs_x__get_changes(apr_array_header_t if (!found) { - /* read changes from revision file */ - - SVN_ERR(svn_fs_x__ensure_revision_exists(rev, fs, scratch_pool)); - SVN_ERR(svn_fs_x__open_pack_or_rev_file(&revision_file, fs, rev, - scratch_pool)); - /* 'block-read' will also provide us with the desired data */ SVN_ERR(block_read((void **)changes, fs, &id, revision_file, result_pool, scratch_pool)); - SVN_ERR(svn_io_file_close(revision_file, scratch_pool)); + SVN_ERR(svn_fs_x__close_revision_file(revision_file)); } SVN_ERR(dgb__log_access(fs, &id, *changes, SVN_FS_X__ITEM_TYPE_CHANGES, @@ -2790,8 +2847,7 @@ svn_fs_x__get_changes(apr_array_header_t */ static svn_error_t * block_read_contents(svn_fs_t *fs, - apr_file_t *file, - svn_stream_t *stream, + svn_fs_x__revision_file_t *rev_file, svn_fs_x__p2l_entry_t* entry, pair_cache_key_t *key, apr_off_t max_offset, @@ -2810,40 +2866,59 @@ block_read_contents(svn_fs_t *fs, header_key.is_packed = svn_fs_x__is_packed_rev(fs, header_key.revision); header_key.item_index = key->second; - SVN_ERR(read_rep_header(&rep_header, fs, stream, &header_key, + SVN_ERR(read_rep_header(&rep_header, fs, rev_file->stream, &header_key, scratch_pool)); - SVN_ERR(init_rep_state(&rs, rep_header, fs, file, stream, entry, - scratch_pool)); + SVN_ERR(init_rep_state(&rs, rep_header, fs, rev_file, entry, scratch_pool)); SVN_ERR(cache_windows(&fulltext_len, fs, &rs, max_offset, scratch_pool)); return SVN_NO_ERROR; } -static svn_error_t * -auto_select_stream(svn_stream_t **stream, - svn_fs_t *fs, - apr_file_t *file, - svn_stream_t *file_stream, - svn_fs_x__p2l_entry_t* entry, - apr_pool_t *pool) -{ - fs_x_data_t *ffd = fs->fsap_data; +/* For the given REV_FILE in FS, in *STREAM return a stream covering the + * item specified by ENTRY. Also, verify the item's content by low-level + * checksum. Allocate the result in POOL. + */ +static svn_error_t * +read_item(svn_stream_t **stream, + svn_fs_t *fs, + svn_fs_x__revision_file_t *rev_file, + svn_fs_x__p2l_entry_t* entry, + apr_pool_t *pool) +{ + apr_uint32_t digest; + svn_checksum_t *expected, *actual; + apr_uint32_t plain_digest; + + /* Read item into string buffer. */ + svn_stringbuf_t *text = svn_stringbuf_create_ensure(entry->size, pool); + text->len = entry->size; + text->data[text->len] = 0; + SVN_ERR(svn_io_file_read_full2(rev_file->file, text->data, text->len, + NULL, NULL, pool)); + + /* Return (construct, calculate) stream and checksum. */ + *stream = svn_stream_from_stringbuf(text, pool); + digest = svn__fnv1a_32x4(text->data, text->len); - if (((entry->offset + entry->size) ^ entry->offset) >= ffd->block_size) - { - svn_stringbuf_t *text = svn_stringbuf_create_ensure(entry->size, pool); - text->len = entry->size; - text->data[text->len] = 0; - SVN_ERR(svn_io_file_read_full2(file, text->data, text->len, NULL, - NULL, pool)); - *stream = svn_stream_from_stringbuf(text, pool); - } - else - { - *stream = file_stream; - } + /* Checksums will match most of the time. */ + if (entry->fnv1_checksum == digest) + return SVN_NO_ERROR; - return SVN_NO_ERROR; + /* Construct proper checksum objects from their digests to allow for + * nice error messages. */ + plain_digest = htonl(entry->fnv1_checksum); + expected = svn_checksum__from_digest_fnv1a_32x4( + (const unsigned char *)&plain_digest, pool); + plain_digest = htonl(digest); + actual = svn_checksum__from_digest_fnv1a_32x4( + (const unsigned char *)&plain_digest, pool); + + /* Construct the full error message with all the info we have. */ + return svn_checksum_mismatch_err(expected, actual, pool, + _("Low-level checksum mismatch while reading\n" + "%s bytes of meta data at offset %s "), + apr_psprintf(pool, "%" APR_OFF_T_FMT, entry->size), + apr_psprintf(pool, "%" APR_OFF_T_FMT, entry->offset)); } /* Read all txdelta / plain windows following REP_HEADER in FS as described @@ -2855,8 +2930,7 @@ auto_select_stream(svn_stream_t **stream static svn_error_t * block_read_changes(apr_array_header_t **changes, svn_fs_t *fs, - apr_file_t *file, - svn_stream_t *file_stream, + svn_fs_x__revision_file_t *rev_file, svn_fs_x__p2l_entry_t* entry, svn_boolean_t must_read, apr_pool_t *result_pool, @@ -2881,18 +2955,27 @@ block_read_changes(apr_array_header_t ** return SVN_NO_ERROR; } - SVN_ERR(auto_select_stream(&stream, fs, file, file_stream, entry, - scratch_pool)); + SVN_ERR(read_item(&stream, fs, rev_file, entry, scratch_pool)); /* read changes from revision file */ - SVN_ERR(svn_fs_x__read_changes(changes, stream, result_pool)); + SVN_ERR(svn_fs_x__read_changes(changes, stream, result_pool, scratch_pool)); /* cache for future reference */ if (ffd->changes_cache) - SVN_ERR(svn_cache__set(ffd->changes_cache, &revision, *changes, - scratch_pool)); + { + /* Guesstimate for the size of the in-cache representation. */ + apr_size_t estimated_size = (apr_size_t)250 * (*changes)->nelts; + + /* Don't even serialize data that probably won't fit into the + * cache. This often implies that either CHANGES is very + * large, memory is scarce or both. Having a huge temporary + * copy would not be a good thing in either case. */ + if (svn_cache__is_cachable(ffd->changes_cache, estimated_size)) + SVN_ERR(svn_cache__set(ffd->changes_cache, &revision, *changes, + scratch_pool)); + } return SVN_NO_ERROR; } @@ -2900,8 +2983,7 @@ block_read_changes(apr_array_header_t ** static svn_error_t * block_read_changes_container(apr_array_header_t **changes, svn_fs_t *fs, - apr_file_t *file, - svn_stream_t *file_stream, + svn_fs_x__revision_file_t *rev_file, svn_fs_x__p2l_entry_t* entry, apr_uint32_t sub_item, svn_boolean_t must_read, @@ -2927,8 +3009,7 @@ block_read_changes_container(apr_array_h return SVN_NO_ERROR; } - SVN_ERR(auto_select_stream(&stream, fs, file, file_stream, entry, - scratch_pool)); + SVN_ERR(read_item(&stream, fs, rev_file, entry, scratch_pool)); /* read changes from revision file */ @@ -2951,8 +3032,7 @@ block_read_changes_container(apr_array_h static svn_error_t * block_read_noderev(node_revision_t **noderev_p, svn_fs_t *fs, - apr_file_t *file, - svn_stream_t *file_stream, + svn_fs_x__revision_file_t *rev_file, svn_fs_x__p2l_entry_t* entry, pair_cache_key_t *key, svn_boolean_t must_read, @@ -2977,12 +3057,12 @@ block_read_noderev(node_revision_t **nod return SVN_NO_ERROR; } - SVN_ERR(auto_select_stream(&stream, fs, file, file_stream, entry, - scratch_pool)); + SVN_ERR(read_item(&stream, fs, rev_file, entry, scratch_pool)); /* read node rev from revision file */ - SVN_ERR(svn_fs_x__read_noderev(noderev_p, stream, result_pool)); + SVN_ERR(svn_fs_x__read_noderev(noderev_p, stream, result_pool, + scratch_pool)); /* Workaround issue #4031: is-fresh-txn-root in revision files. */ (*noderev_p)->is_fresh_txn_root = FALSE; @@ -2997,8 +3077,7 @@ block_read_noderev(node_revision_t **nod static svn_error_t * block_read_noderevs_container(node_revision_t **noderev_p, svn_fs_t *fs, - apr_file_t *file, - svn_stream_t *file_stream, + svn_fs_x__revision_file_t *rev_file, svn_fs_x__p2l_entry_t* entry, apr_uint32_t sub_item, svn_boolean_t must_read, @@ -3024,16 +3103,13 @@ block_read_noderevs_container(node_revis return SVN_NO_ERROR; } - SVN_ERR(auto_select_stream(&stream, fs, file, file_stream, entry, - scratch_pool)); + SVN_ERR(read_item(&stream, fs, rev_file, entry, scratch_pool)); /* read noderevs from revision file */ - SVN_ERR(svn_fs_x__read_noderevs_container(&container, stream, scratch_pool, scratch_pool)); /* extract requested data */ - if (must_read) SVN_ERR(svn_fs_x__noderevs_get(noderev_p, container, sub_item, result_pool)); @@ -3048,8 +3124,7 @@ block_read_noderevs_container(node_revis static svn_error_t * block_read_reps_container(svn_fs_x__rep_extractor_t **extractor, svn_fs_t *fs, - apr_file_t *file, - svn_stream_t *file_stream, + svn_fs_x__revision_file_t *rev_file, svn_fs_x__p2l_entry_t* entry, apr_uint32_t sub_item, svn_boolean_t must_read, @@ -3075,11 +3150,9 @@ block_read_reps_container(svn_fs_x__rep_ return SVN_NO_ERROR; } - SVN_ERR(auto_select_stream(&stream, fs, file, file_stream, entry, - scratch_pool)); + SVN_ERR(read_item(&stream, fs, rev_file, entry, scratch_pool)); /* read noderevs from revision file */ - SVN_ERR(svn_fs_x__read_reps_container(&container, stream, result_pool, scratch_pool)); @@ -3100,7 +3173,7 @@ static svn_error_t * block_read(void **result, svn_fs_t *fs, const svn_fs_x__id_part_t *id, - apr_file_t *revision_file, + svn_fs_x__revision_file_t *revision_file, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -3113,24 +3186,25 @@ block_read(void **result, int run_count = 0; int i; apr_pool_t *iterpool = svn_pool_create(scratch_pool); - svn_stream_t *stream = svn_stream_from_aprfile2(revision_file, TRUE, - scratch_pool); /* don't try this on transaction protorev files */ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision)); /* index lookup: find the OFFSET of the item we *must* read plus (in the * "do-while" block) the list of items in the same block. */ - SVN_ERR(svn_fs_x__item_offset(&wanted_offset, &wanted_sub_item, fs, id, - iterpool)); + SVN_ERR(svn_fs_x__item_offset(&wanted_offset, &wanted_sub_item, fs, + revision_file, id, iterpool)); offset = wanted_offset; do { /* fetch list of items in the block surrounding OFFSET */ - SVN_ERR(aligned_seek(fs, revision_file, &block_start, offset, iterpool)); - SVN_ERR(svn_fs_x__p2l_index_lookup(&entries, fs, revision, block_start, - ffd->block_size, scratch_pool)); + SVN_ERR(aligned_seek(fs, revision_file->file, &block_start, offset, + iterpool)); + SVN_ERR(svn_fs_x__p2l_index_lookup(&entries, fs, revision_file, + revision, block_start, + ffd->block_size, scratch_pool, + scratch_pool)); /* read all items from the block */ for (i = 0; i < entries->nelts; ++i) @@ -3166,7 +3240,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, APR_SET, + SVN_ERR(svn_io_file_seek(revision_file->file, SEEK_SET, &entry->offset, iterpool)); switch (entry->type) { @@ -3174,7 +3248,7 @@ block_read(void **result, case SVN_FS_X__ITEM_TYPE_DIR_REP: case SVN_FS_X__ITEM_TYPE_FILE_PROPS: case SVN_FS_X__ITEM_TYPE_DIR_PROPS: - SVN_ERR(block_read_contents(fs, revision_file, stream, + SVN_ERR(block_read_contents(fs, revision_file, entry, &key, is_wanted ? -1 @@ -3185,14 +3259,14 @@ block_read(void **result, case SVN_FS_X__ITEM_TYPE_NODEREV: if (ffd->node_revision_cache || is_result) SVN_ERR(block_read_noderev((node_revision_t **)&item, - fs, revision_file, stream, + fs, revision_file, entry, &key, is_result, pool, iterpool)); break; case SVN_FS_X__ITEM_TYPE_CHANGES: SVN_ERR(block_read_changes((apr_array_header_t **)&item, - fs, revision_file, stream, + fs, revision_file, entry, is_result, pool, iterpool)); break; @@ -3200,7 +3274,7 @@ block_read(void **result, case SVN_FS_X__ITEM_TYPE_CHANGES_CONT: SVN_ERR(block_read_changes_container ((apr_array_header_t **)&item, - fs, revision_file, stream, + fs, revision_file, entry, wanted_sub_item, is_result, pool, iterpool)); break; @@ -3208,7 +3282,7 @@ block_read(void **result, case SVN_FS_X__ITEM_TYPE_NODEREVS_CONT: SVN_ERR(block_read_noderevs_container ((node_revision_t **)&item, - fs, revision_file, stream, + fs, revision_file, entry, wanted_sub_item, is_result, pool, iterpool)); break; @@ -3216,7 +3290,7 @@ block_read(void **result, case SVN_FS_X__ITEM_TYPE_REPS_CONT: SVN_ERR(block_read_reps_container ((svn_fs_x__rep_extractor_t **)&item, - fs, revision_file, stream, + fs, revision_file, entry, wanted_sub_item, is_result, pool, iterpool)); break; @@ -3243,7 +3317,6 @@ block_read(void **result, /* if the caller requested a result, we must have provided one by now */ assert(!result || *result); - SVN_ERR(svn_stream_close(stream)); svn_pool_destroy(iterpool); return SVN_NO_ERROR;
Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/cached_data.h URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/cached_data.h?rev=1641647&r1=1641646&r2=1641647&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/cached_data.h (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/cached_data.h Tue Nov 25 16:26:48 2014 @@ -37,7 +37,8 @@ svn_error_t * svn_fs_x__get_node_revision(node_revision_t **noderev_p, svn_fs_t *fs, const svn_fs_id_t *id, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* Set *COUNT to the value of the mergeinfo_count member of the node- revision for the node ID in FS. Do any allocations in POOL. */ @@ -53,25 +54,26 @@ svn_error_t * svn_fs_x__rev_get_root(svn_fs_id_t **root_id, svn_fs_t *fs, svn_revnum_t rev, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* Verify that representation REP in FS can be accessed. - Do any allocations in POOL. */ + Do any allocations in SCRATCH_POOL. */ svn_error_t * svn_fs_x__check_rep(representation_t *rep, svn_fs_t *fs, - apr_pool_t *pool); + apr_pool_t *scratch_pool); /* Follow the representation delta chain in FS starting with REP. The number of reps (including REP) in the chain will be returned in *CHAIN_LENGTH. *SHARD_COUNT will be set to the number of shards - accessed. Do any allocations in POOL. */ + accessed. Do any allocations in SCRATCH_POOL. */ svn_error_t * svn_fs_x__rep_chain_length(int *chain_length, int *shard_count, representation_t *rep, svn_fs_t *fs, - apr_pool_t *pool); + apr_pool_t *scratch_pool); /* Set *CONTENTS to be a readable svn_stream_t that receives the text representation REP as seen in filesystem FS. If CACHE_FULLTEXT is @@ -95,8 +97,7 @@ svn_error_t * svn_fs_x__get_representation_length(svn_filesize_t *packed_len, svn_filesize_t *expanded_len, svn_fs_t *fs, - apr_file_t *file, - svn_stream_t *stream, + svn_fs_x__revision_file_t *rev_file, svn_fs_x__p2l_entry_t* entry, apr_pool_t *pool); Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/dag.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_x/dag.c?rev=1641647&r1=1641646&r2=1641647&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_x/dag.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_x/dag.c Tue Nov 25 16:26:48 2014 @@ -160,10 +160,13 @@ get_node_revision(node_revision_t **node if (! node->node_revision) { node_revision_t *noderev; + apr_pool_t *scratch_pool = svn_pool_create(node->node_pool); SVN_ERR(svn_fs_x__get_node_revision(&noderev, node->fs, - node->id, node->node_pool)); + node->id, node->node_pool, + scratch_pool)); node->node_revision = noderev; + svn_pool_destroy(scratch_pool); } /* Now NODE->node_revision is set. */ @@ -613,7 +616,7 @@ svn_fs_x__dag_revision_root(dag_node_t * /* Construct the node. */ new_node = apr_pcalloc(pool, sizeof(*new_node)); new_node->fs = fs; - SVN_ERR(svn_fs_x__rev_get_root(&new_node->id, fs, rev, pool)); + SVN_ERR(svn_fs_x__rev_get_root(&new_node->id, fs, rev, pool, pool)); /* Grab the contents so we can inspect the node's kind and created path. */ new_node->node_pool = pool; 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=1641647&r1=1641646&r2=1641647&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 Tue Nov 25 16:26:48 2014 @@ -45,6 +45,7 @@ #include "revprops.h" #include "rep-cache.h" #include "transaction.h" +#include "util.h" #include "svn_private_config.h" #include "private/svn_fs_util.h" @@ -72,20 +73,28 @@ x_serialized_init(svn_fs_t *fs, apr_pool each separate repository opened during the lifetime of the svn_fs_initialize pool. It's unlikely that anyone will notice the modest expenditure; the alternative is to allocate each structure - in a subpool, add a reference-count, and add a serialized deconstructor + in a subpool, add a reference-count, and add a serialized destructor to the FS vtable. That's more machinery than it's worth. - Using the uuid to obtain the lock creates a corner case if a - caller uses svn_fs_set_uuid on the repository in a process where - other threads might be using the same repository through another - FS object. The only real-world consumer of svn_fs_set_uuid is - "svnadmin load", so this is a low-priority problem, and we don't - know of a better way of associating such data with the - repository. */ + Picking an appropriate key for the shared data is tricky, because, + unfortunately, a filesystem UUID is not really unique. It is implicitly + shared between hotcopied (1), dump / loaded (2) or naively copied (3) + filesystems. We tackle this problem by using a combination of the UUID + and an instance ID as the key. This allows us to avoid key clashing + in (1) and (2). + + Speaking of (3), there is not so much we can do about it, except maybe + provide a convenient way of fixing things. Naively copied filesystems + have identical filesystem UUIDs *and* instance IDs. With the key being + a combination of these two, clashes can be fixed by changing either of + them (or both), e.g. with svn_fs_set_uuid(). */ + SVN_ERR_ASSERT(fs->uuid); - key = apr_pstrcat(pool, SVN_FSX_SHARED_USERDATA_PREFIX, fs->uuid, - SVN_VA_NULL); + SVN_ERR_ASSERT(ffd->instance_id); + + key = apr_pstrcat(pool, SVN_FSX_SHARED_USERDATA_PREFIX, + fs->uuid, ":", ffd->instance_id, SVN_VA_NULL); status = apr_pool_userdata_get(&val, key, common_pool); if (status) return svn_error_wrap_apr(status, _("Can't fetch FSX shared data")); @@ -99,13 +108,16 @@ x_serialized_init(svn_fs_t *fs, apr_pool /* POSIX fcntl locks are per-process, so we need a mutex for intra-process synchronization when grabbing the repository write lock. */ - SVN_ERR(svn_mutex__init(&ffsd->fs_write_lock, TRUE, common_pool)); + SVN_ERR(svn_mutex__init(&ffsd->fs_write_lock, + SVN_FS_X__USE_LOCK_MUTEX, common_pool)); /* ... the pack lock ... */ - SVN_ERR(svn_mutex__init(&ffsd->fs_pack_lock, TRUE, common_pool)); + SVN_ERR(svn_mutex__init(&ffsd->fs_pack_lock, + SVN_FS_X__USE_LOCK_MUTEX, common_pool)); /* ... not to mention locking the txn-current file. */ - SVN_ERR(svn_mutex__init(&ffsd->txn_current_lock, TRUE, common_pool)); + SVN_ERR(svn_mutex__init(&ffsd->txn_current_lock, + SVN_FS_X__USE_LOCK_MUTEX, common_pool)); /* We also need a mutex for synchronizing access to the active transaction list and free transaction pointer. */ @@ -158,6 +170,16 @@ x_freeze_body(void *baton, } static svn_error_t * +x_freeze_body2(void *baton, + apr_pool_t *pool) +{ + struct x_freeze_baton_t *b = baton; + SVN_ERR(svn_fs_x__with_write_lock(b->fs, x_freeze_body, baton, pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * x_freeze(svn_fs_t *fs, svn_fs_freeze_func_t freeze_func, void *freeze_baton, @@ -170,7 +192,7 @@ x_freeze(svn_fs_t *fs, b.freeze_baton = freeze_baton; SVN_ERR(svn_fs__check_fs(fs, TRUE)); - SVN_ERR(svn_fs_x__with_write_lock(fs, x_freeze_body, &b, pool)); + SVN_ERR(svn_fs_x__with_pack_lock(fs, x_freeze_body2, &b, pool)); return SVN_NO_ERROR; } @@ -203,6 +225,18 @@ x_revision_proplist(apr_hash_t **proplis rev, FALSE, pool)); } +/* Wrapper around svn_fs_x__set_uuid() adapting between function + signatures. */ +static svn_error_t * +x_set_uuid(svn_fs_t *fs, + const char *uuid, + apr_pool_t *pool) +{ + /* Whenever we set a new UUID, imply that FS will also be a different + * instance (on formats that support this). */ + return svn_error_trace(svn_fs_x__set_uuid(fs, uuid, NULL, pool)); +} + /* The vtable associated with a specific open filesystem. */ @@ -211,7 +245,7 @@ static fs_vtable_t fs_vtable = { svn_fs_x__revision_prop, x_revision_proplist, svn_fs_x__change_rev_prop, - svn_fs_x__set_uuid, + x_set_uuid, svn_fs_x__revision_root, svn_fs_x__begin_txn, svn_fs_x__open_txn, @@ -244,6 +278,15 @@ initialize_fs_struct(svn_fs_t *fs) return SVN_NO_ERROR; } +/* Reset vtable and fsap_data fields in FS such that the FS is basically + * closed now. Note that FS must not hold locks when you call this. */ +static void +uninitialize_fs_struct(svn_fs_t *fs) +{ + fs->vtable = NULL; + fs->fsap_data = NULL; +} + /* This implements the fs_library_vtable_t.create() API. Create a new fsx-backed Subversion filesystem at path PATH and link it into *FS. Perform temporary allocations in POOL, and fs-global allocations @@ -311,20 +354,53 @@ x_open_for_recovery(svn_fs_t *fs, apr_pool_t *pool, apr_pool_t *common_pool) { - /* Recovery for FSX is currently limited to recreating the 'current' + svn_error_t * err; + svn_revnum_t youngest_rev; + apr_pool_t * subpool = svn_pool_create(pool); + + /* Recovery for FSFS is currently limited to recreating the 'current' file from the latest revision. */ /* The only thing we have to watch out for is that the 'current' file - might not exist. So we'll try to create it here unconditionally, - and just ignore any errors that might indicate that it's already - present. (We'll need it to exist later anyway as a source for the - new file's permissions). */ + might not exist or contain garbage. So we'll try to read it here + and provide or replace the existing file if we couldn't read it. + (We'll also need it to exist later anyway as a source for the new + file's permissions). */ - /* Use a partly-filled fs pointer first to create 'current'. This will fail - if 'current' already exists, but we don't care about that. */ + /* Use a partly-filled fs pointer first to create 'current'. */ fs->path = apr_pstrdup(fs->pool, path); - svn_error_clear(svn_io_file_create(svn_fs_x__path_current(fs, pool), - "0 1 1\n", pool)); + + SVN_ERR(initialize_fs_struct(fs)); + + /* Figure out the repo format and check that we can even handle it. */ + SVN_ERR(svn_fs_x__read_format_file(fs, subpool)); + + /* Now, read 'current' and try to patch it if necessary. */ + err = svn_fs_x__youngest_rev(&youngest_rev, fs, subpool); + if (err) + { + const char *file_path; + + /* 'current' file is missing or contains garbage. Since we are trying + * to recover from whatever problem there is, being picky about the + * error code here won't do us much good. If there is a persistent + * problem that we can't fix, it will show up when we try rewrite the + * file a few lines further below and we will report the failure back + * to the caller. + * + * Start recovery with HEAD = 0. */ + svn_error_clear(err); + file_path = svn_fs_x__path_current(fs, subpool); + + /* Best effort to ensure the file exists and is valid. + * This may fail for r/o filesystems etc. */ + SVN_ERR(svn_io_remove_file2(file_path, TRUE, subpool)); + SVN_ERR(svn_io_file_create_empty(file_path, subpool)); + SVN_ERR(svn_fs_x__write_current(fs, 0, subpool)); + } + + uninitialize_fs_struct(fs); + svn_pool_destroy(subpool); /* Now open the filesystem properly by calling the vtable method directly. */ return x_open(fs, path, common_pool_lock, pool, common_pool); @@ -412,11 +488,13 @@ x_hotcopy(svn_fs_t *src_fs, if (cancel_func) SVN_ERR(cancel_func(cancel_baton)); - /* Provide FFD for DST_FS, test / initialize target repo, remove FFD. */ + /* Test target repo when in INCREMENTAL mode, initialize it when not. + * For this, we need our FS internal data structures to be temporarily + * available. */ SVN_ERR(initialize_fs_struct(dst_fs)); SVN_ERR(svn_fs_x__hotcopy_prepare_target(src_fs, dst_fs, dst_path, incremental, pool)); - dst_fs->fsap_data = NULL; + uninitialize_fs_struct(dst_fs); /* Now, the destination repo should open just fine. */ SVN_ERR(x_open(dst_fs, dst_path, common_pool_lock, pool, common_pool)); @@ -424,8 +502,9 @@ x_hotcopy(svn_fs_t *src_fs, SVN_ERR(cancel_func(cancel_baton)); /* Now, we may copy data as needed ... */ - return svn_fs_x__hotcopy(src_fs, dst_fs, - incremental, cancel_func, cancel_baton, pool); + return svn_fs_x__hotcopy(src_fs, dst_fs, incremental, + notify_func, notify_baton, + cancel_func, cancel_baton, pool); } @@ -456,7 +535,7 @@ x_delete_fs(const char *path, apr_pool_t *pool) { /* Remove everything. */ - return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool); + return svn_error_trace(svn_io_remove_dir2(path, FALSE, NULL, NULL, pool)); } static const svn_version_t * 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=1641647&r1=1641646&r2=1641647&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 Tue Nov 25 16:26:48 2014 @@ -126,6 +126,16 @@ extern "C" { */ #define SVN_FS_X__FORMAT_NUMBER 1 +/* On most operating systems apr implements file locks per process, not + per file. On Windows apr implements the locking as per file handle + locks, so we don't have to add our own mutex for just in-process + synchronization. */ +#if APR_HAS_THREADS && !defined(WIN32) +#define SVN_FS_X__USE_LOCK_MUTEX 1 +#else +#define SVN_FS_X__USE_LOCK_MUTEX 0 +#endif + /* Private FSX-specific data shared between all svn_txn_t objects that relate to a particular transaction in a filesystem (as identified by transaction id and filesystem UUID). Objects of this type are @@ -245,9 +255,11 @@ typedef struct fs_x_data_t /* The maximum number of files to store per directory. */ int max_files_per_dir; - /* Rev / pack file read granularity. */ + /* Rev / pack file read granularity in bytes. */ apr_int64_t block_size; + /* Rev / pack file granularity (in bytes) covered by a single phys-to-log + * index page. */ /* Capacity in entries of log-to-phys index pages */ apr_int64_t l2p_page_size; @@ -400,6 +412,12 @@ typedef struct fs_x_data_t /* Pack after every commit. */ svn_boolean_t pack_after_commit; + /* Per-instance filesystem ID, which provides an additional level of + uniqueness for filesystems that share the same UUID, but should + still be distinguishable (e.g. backups produced by svn_fs_hotcopy() + or dump / load cycles). */ + const char *instance_id; + /* Pointer to svn_fs_open. */ svn_error_t *(*svn_fs_open_)(svn_fs_t **, const char *, apr_hash_t *, apr_pool_t *, apr_pool_t *);
