Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/cached_data.c URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/cached_data.c?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/cached_data.c (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/cached_data.c Sun Jun 14 20:58:10 2015 @@ -286,6 +286,114 @@ use_block_read(svn_fs_t *fs) return svn_fs_fs__use_log_addressing(fs) && ffd->use_block_read; } +svn_error_t * +svn_fs_fs__fixup_expanded_size(svn_fs_t *fs, + representation_t *rep, + apr_pool_t *scratch_pool) +{ + svn_checksum_t checksum; + svn_checksum_t *empty_md5; + svn_fs_fs__revision_file_t *revision_file; + svn_fs_fs__rep_header_t *rep_header; + + /* Anything to do at all? + * + * Note that a 0 SIZE is only possible for PLAIN reps due to the SVN\1 + * magic prefix in any DELTA rep. */ + if (!rep || rep->expanded_size != 0 || rep->size == 0) + return SVN_NO_ERROR; + + /* This function may only be called for committed data. */ + assert(!svn_fs_fs__id_txn_used(&rep->txn_id)); + + /* EXPANDED_SIZE is 0. If the MD5 does not match the one for empty + * contents, we know that EXPANDED_SIZE == 0 is wrong and needs to + * be set to the actual value given by SIZE. + * + * Using svn_checksum_match() will also accept all-zero values for + * the MD5 digest and only report a mismatch if the MD5 has actually + * been given. */ + empty_md5 = svn_checksum_empty_checksum(svn_checksum_md5, scratch_pool); + + checksum.digest = rep->md5_digest; + checksum.kind = svn_checksum_md5; + if (!svn_checksum_match(empty_md5, &checksum)) + { + rep->expanded_size = rep->size; + return SVN_NO_ERROR; + } + + /* Data in the rep-cache.db does not have MD5 checksums (all zero) on it. + * Compare SHA1 instead. */ + if (rep->has_sha1) + { + svn_checksum_t *empty_sha1 + = svn_checksum_empty_checksum(svn_checksum_sha1, scratch_pool); + + checksum.digest = rep->sha1_digest; + checksum.kind = svn_checksum_sha1; + if (!svn_checksum_match(empty_sha1, &checksum)) + { + rep->expanded_size = rep->size; + return SVN_NO_ERROR; + } + } + + /* Only two cases are left here. + * (1) A non-empty PLAIN rep with a MD5 collision on EMPTY_MD5. + * (2) A DELTA rep with zero-length output. */ + + /* SVN always stores a DELTA rep with zero-length output as an empty + * sequence of txdelta windows, i.e. as "SVN\1". In that case, SIZE is + * 4 bytes. There is no other possible DELTA rep of that size and any + * PLAIN rep of 4 bytes would produce a different MD5. Hence, if SIZE is + * actually 4 here, we know that this is an empty DELTA rep. + * + * Note that it is technically legal to have DELTA reps with a 0 length + * output window. Their on-disk size would be longer. We handle that + * case later together with the equally unlikely MD5 collision. */ + if (rep->size == 4) + { + /* EXPANDED_SIZE is already 0. */ + return SVN_NO_ERROR; + } + + /* We still have the two options, PLAIN or DELTA rep. At this point, we + * are in an extremely unlikely case and can spend some time to figure it + * out. So, let's just look at the representation header. */ + SVN_ERR(open_and_seek_revision(&revision_file, fs, rep->revision, + rep->item_index, scratch_pool)); + SVN_ERR(svn_fs_fs__read_rep_header(&rep_header, revision_file->stream, + scratch_pool, scratch_pool)); + SVN_ERR(svn_fs_fs__close_revision_file(revision_file)); + + /* Only for PLAIN reps do we have to correct EXPANDED_SIZE. */ + if (rep_header->type == svn_fs_fs__rep_plain) + rep->expanded_size = rep->size; + + return SVN_NO_ERROR; +} + +/* Correct known issues with committed NODEREV in FS. + * Uses SCRATCH_POOL for temporaries. + */ +static svn_error_t * +fixup_node_revision(svn_fs_t *fs, + node_revision_t *noderev, + apr_pool_t *scratch_pool) +{ + /* Workaround issue #4031: is-fresh-txn-root in revision files. */ + noderev->is_fresh_txn_root = FALSE; + + /* Make sure EXPANDED_SIZE has the correct value for every rep. */ + SVN_ERR(svn_fs_fs__fixup_expanded_size(fs, noderev->data_rep, + scratch_pool)); + SVN_ERR(svn_fs_fs__fixup_expanded_size(fs, noderev->prop_rep, + scratch_pool)); + + return SVN_NO_ERROR; +} + /* Get the node-revision for the node ID in FS. Set *NODEREV_P to the new node-revision structure, allocated in POOL. See svn_fs_fs__get_node_revision, which wraps this and adds another @@ -376,9 +484,7 @@ get_node_revision_body(node_revision_t * revision_file->stream, result_pool, scratch_pool)); - - /* Workaround issue #4031: is-fresh-txn-root in revision files. */ - (*noderev_p)->is_fresh_txn_root = FALSE; + SVN_ERR(fixup_node_revision(fs, *noderev_p, scratch_pool)); /* The noderev is not in cache, yet. Add it, if caching has been enabled. */ if (ffd->node_revision_cache) @@ -631,7 +737,7 @@ typedef struct rep_state_t -1 if the offset is yet unknown. */ 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? + 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; @@ -765,9 +871,7 @@ create_rep_state_body(rep_state_t **rep_ Since we don't know the depth of the delta chain, let's assume, the whole contents get rewritten 3 times. */ - estimated_window_storage - = 4 * ( (rep->expanded_size ? rep->expanded_size : rep->size) - + SVN_DELTA_WINDOW_SIZE); + estimated_window_storage = 4 * (rep->expanded_size + SVN_DELTA_WINDOW_SIZE); estimated_window_storage = MIN(estimated_window_storage, APR_SIZE_MAX); rs->window_cache = ffd->txdelta_window_cache @@ -952,7 +1056,7 @@ svn_fs_fs__check_rep(representation_t *r if ( entry == NULL || entry->type < SVN_FS_FS__ITEM_TYPE_FILE_REP || entry->type > SVN_FS_FS__ITEM_TYPE_DIR_PROPS) - return svn_error_createf(SVN_ERR_REPOS_CORRUPTED, NULL, + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, _("No representation found at offset %s " "for item %s in revision %ld"), apr_off_t_toa(scratch_pool, offset), @@ -991,7 +1095,7 @@ svn_fs_fs__rep_chain_length(int *chain_l int count = 0; int shards = 1; svn_revnum_t last_shard = rep->revision / shard_size; - + /* Check whether the length of the deltification chain is acceptable. * Otherwise, shared reps may form a non-skipping delta chain in * extreme cases. */ @@ -1104,7 +1208,7 @@ struct rep_read_baton /* The text we've been reading, if we're going to cache it. */ svn_stringbuf_t *current_fulltext; - /* If not NULL, attempt to read the data from this cache. + /* If not NULL, attempt to read the data from this cache. Once that lookup fails, reset it to NULL. */ svn_cache__t *fulltext_cache; @@ -1322,15 +1426,11 @@ set_cached_combined_window(svn_stringbuf ID, and representation REP. Also, set *WINDOW_P to the base window content for *LIST, if it could be found in cache. Otherwise, *LIST will contain the base - representation for the whole delta chain. - Finally, return the expanded size of the representation in - *EXPANDED_SIZE. It will take care of cases where only the on-disk - size is known. */ + representation for the whole delta chain. */ static svn_error_t * build_rep_list(apr_array_header_t **list, svn_stringbuf_t **window_p, rep_state_t **src_state, - svn_filesize_t *expanded_size, svn_fs_t *fs, representation_t *first_rep, apr_pool_t *pool) @@ -1345,24 +1445,9 @@ build_rep_list(apr_array_header_t **list *list = apr_array_make(pool, 1, sizeof(rep_state_t *)); rep = *first_rep; - /* The value as stored in the data struct. - 0 is either for unknown length or actually zero length. */ - *expanded_size = first_rep->expanded_size; - /* for the top-level rep, we need the rep_args */ SVN_ERR(create_rep_state(&rs, &rep_header, &shared_file, &rep, fs, pool, iterpool)); - - /* Unknown size or empty representation? - That implies the this being the first iteration. - Usually size equals on-disk size, except for empty, - compressed representations (delta, size = 4). - Please note that for all non-empty deltas have - a 4-byte header _plus_ some data. */ - if (*expanded_size == 0) - if (rep_header->type == svn_fs_fs__rep_plain || first_rep->size != 4) - *expanded_size = first_rep->size; - while (1) { svn_pool_clear(iterpool); @@ -1550,7 +1635,7 @@ read_plain_window(svn_stringbuf_t **nwin apr_pool_t *scratch_pool) { apr_off_t offset; - + /* RS->FILE may be shared between RS instances -> make sure we point * to the right data. */ SVN_ERR(auto_open_shared_file(rs->sfile)); @@ -1624,9 +1709,11 @@ get_combined_window(svn_stringbuf_t **re /* Maybe, we've got a PLAIN start representation. If we do, read as much data from it as the needed for the txdelta window's source view. - Note that BUF / SOURCE may only be NULL in the first iteration. */ + Note that BUF / SOURCE may only be NULL in the first iteration. + Also note that we may have short-cut reading the delta chain -- + in which case SRC_OPS is 0 and it might not be a PLAIN rep. */ source = buf; - if (source == NULL && rb->src_state != NULL) + if (source == NULL && rb->src_state != NULL && window->src_ops) SVN_ERR(read_plain_window(&source, rb->src_state, window->sview_len, pool, iterpool)); @@ -1664,7 +1751,7 @@ get_combined_window(svn_stringbuf_t **re } /* Returns whether or not the expanded fulltext of the file is cachable - * based on its size SIZE. The decision depends on the cache used by RB. + * based on its size SIZE. The decision depends on the cache used by FFD. */ static svn_boolean_t fulltext_size_is_cachable(fs_fs_data_t *ffd, svn_filesize_t size) @@ -1997,11 +2084,12 @@ rep_read_contents(void *baton, if (!rb->rs_list) { /* Window stream not initialized, yet. Do it now. */ + rb->len = rb->rep.expanded_size; SVN_ERR(build_rep_list(&rb->rs_list, &rb->base_window, - &rb->src_state, &rb->len, rb->fs, &rb->rep, + &rb->src_state, rb->fs, &rb->rep, rb->filehandle_pool)); - /* In case we did read from the fulltext cache before, make the + /* In case we did read from the fulltext cache before, make the * window stream catch up. Also, initialize the fulltext buffer * if we want to cache the fulltext at the end. */ SVN_ERR(skip_contents(rb, rb->fulltext_delivered)); @@ -2065,7 +2153,6 @@ svn_fs_fs__get_contents(svn_stream_t **c else { fs_fs_data_t *ffd = fs->fsap_data; - svn_filesize_t len = rep->expanded_size ? rep->expanded_size : rep->size; struct rep_read_baton *rb; pair_cache_key_t fulltext_cache_key = { 0 }; @@ -2081,7 +2168,7 @@ svn_fs_fs__get_contents(svn_stream_t **c * cache it. */ if (ffd->fulltext_cache && cache_fulltext && SVN_IS_VALID_REVNUM(rep->revision) - && fulltext_size_is_cachable(ffd, len)) + && fulltext_size_is_cachable(ffd, rep->expanded_size)) { rb->fulltext_cache = ffd->fulltext_cache; } @@ -2437,11 +2524,41 @@ read_dir_entries(apr_array_header_t *ent return SVN_NO_ERROR; } -/* Fetch the contents of a directory into ENTRIES. Values are stored +/* For directory NODEREV in FS, return the *FILESIZE of its in-txn + * representation. If the directory representation is comitted data, + * set *FILESIZE to SVN_INVALID_FILESIZE. Use SCRATCH_POOL for temporaries. + */ +static svn_error_t * +get_txn_dir_info(svn_filesize_t *filesize, + svn_fs_t *fs, + node_revision_t *noderev, + apr_pool_t *scratch_pool) +{ + if (noderev->data_rep && svn_fs_fs__id_txn_used(&noderev->data_rep->txn_id)) + { + const svn_io_dirent2_t *dirent; + const char *filename; + + filename = svn_fs_fs__path_txn_node_children(fs, noderev->id, + scratch_pool); + + SVN_ERR(svn_io_stat_dirent2(&dirent, filename, FALSE, FALSE, + scratch_pool, scratch_pool)); + *filesize = dirent->filesize; + } + else + { + *filesize = SVN_INVALID_FILESIZE; + } + + return SVN_NO_ERROR; +} + +/* Fetch the contents of a directory into DIR. Values are stored as filename to string mappings; further conversion is necessary to convert them into svn_fs_dirent_t values. */ static svn_error_t * -get_dir_contents(apr_array_header_t **entries, +get_dir_contents(svn_fs_fs__dir_data_t *dir, svn_fs_t *fs, node_revision_t *noderev, apr_pool_t *result_pool, @@ -2449,18 +2566,31 @@ get_dir_contents(apr_array_header_t **en { svn_stream_t *contents; - *entries = apr_array_make(result_pool, 16, sizeof(svn_fs_dirent_t *)); + /* Initialize the result. */ + dir->entries = apr_array_make(result_pool, 16, sizeof(svn_fs_dirent_t *)); + dir->txn_filesize = SVN_INVALID_FILESIZE; + + /* Read dir contents - unless there is none in which case we are done. */ if (noderev->data_rep && svn_fs_fs__id_txn_used(&noderev->data_rep->txn_id)) { - const char *filename - = svn_fs_fs__path_txn_node_children(fs, noderev->id, scratch_pool); + /* Get location & current size of the directory representation. */ + const char *filename; + apr_file_t *file; + + filename = svn_fs_fs__path_txn_node_children(fs, noderev->id, + scratch_pool); /* The representation is mutable. Read the old directory contents from the mutable children file, followed by the changes we've made in this transaction. */ - SVN_ERR(svn_stream_open_readonly(&contents, filename, scratch_pool, - scratch_pool)); - SVN_ERR(read_dir_entries(*entries, contents, TRUE, noderev->id, + SVN_ERR(svn_io_file_open(&file, filename, APR_READ | APR_BUFFERED, + APR_OS_DEFAULT, scratch_pool)); + + /* Obtain txn children file size. */ + SVN_ERR(svn_io_file_size_get(&dir->txn_filesize, file, scratch_pool)); + + contents = svn_stream_from_aprfile2(file, FALSE, scratch_pool); + SVN_ERR(read_dir_entries(dir->entries, contents, TRUE, noderev->id, result_pool, scratch_pool)); SVN_ERR(svn_stream_close(contents)); } @@ -2469,9 +2599,7 @@ get_dir_contents(apr_array_header_t **en /* Undeltify content before parsing it. Otherwise, we could only * parse it byte-by-byte. */ - apr_size_t len = noderev->data_rep->expanded_size - ? (apr_size_t)noderev->data_rep->expanded_size - : (apr_size_t)noderev->data_rep->size; + apr_size_t len = noderev->data_rep->expanded_size; svn_stringbuf_t *text; /* The representation is immutable. Read it normally. */ @@ -2482,7 +2610,7 @@ get_dir_contents(apr_array_header_t **en /* de-serialize hash */ contents = svn_stream_from_stringbuf(text, scratch_pool); - SVN_ERR(read_dir_entries(*entries, contents, FALSE, noderev->id, + SVN_ERR(read_dir_entries(dir->entries, contents, FALSE, noderev->id, result_pool, scratch_pool)); } @@ -2538,6 +2666,7 @@ svn_fs_fs__rep_contents_dir(apr_array_he { pair_cache_key_t pair_key = { 0 }; const void *key; + svn_fs_fs__dir_data_t *dir; /* find the cache we may use */ svn_cache__t *cache = locate_dir_cache(fs, &key, &pair_key, noderev, @@ -2546,19 +2675,32 @@ svn_fs_fs__rep_contents_dir(apr_array_he { svn_boolean_t found; - SVN_ERR(svn_cache__get((void **)entries_p, &found, cache, key, + SVN_ERR(svn_cache__get((void **)&dir, &found, cache, key, result_pool)); if (found) - return SVN_NO_ERROR; + { + /* Verify that the cached dir info is not stale + * (no-op for committed data). */ + svn_filesize_t filesize; + SVN_ERR(get_txn_dir_info(&filesize, fs, noderev, scratch_pool)); + + if (filesize == dir->txn_filesize) + { + /* Still valid. Done. */ + *entries_p = dir->entries; + return SVN_NO_ERROR; + } + } } /* Read in the directory contents. */ - SVN_ERR(get_dir_contents(entries_p, fs, noderev, result_pool, - scratch_pool)); + dir = apr_pcalloc(scratch_pool, sizeof(*dir)); + SVN_ERR(get_dir_contents(dir, fs, noderev, result_pool, scratch_pool)); + *entries_p = dir->entries; /* Update the cache, if we are to use one. */ if (cache) - SVN_ERR(svn_cache__set(cache, key, *entries_p, scratch_pool)); + SVN_ERR(svn_cache__set(cache, key, dir, scratch_pool)); return SVN_NO_ERROR; } @@ -2590,30 +2732,40 @@ svn_fs_fs__rep_contents_dir_entry(svn_fs scratch_pool); if (cache) { + extract_dir_entry_baton_t baton; + + svn_filesize_t filesize; + SVN_ERR(get_txn_dir_info(&filesize, fs, noderev, scratch_pool)); + /* Cache lookup. */ + baton.txn_filesize = filesize; + baton.name = name; SVN_ERR(svn_cache__get_partial((void **)dirent, &found, cache, key, svn_fs_fs__extract_dir_entry, - (void*)name, + &baton, result_pool)); } /* fetch data from disk if we did not find it in the cache */ if (! found) { - apr_array_header_t *entries; svn_fs_dirent_t *entry; svn_fs_dirent_t *entry_copy = NULL; + svn_fs_fs__dir_data_t dir; - /* read the dir from the file system. It will probably be put it - into the cache for faster lookup in future calls. */ - SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, noderev, - scratch_pool, scratch_pool)); + /* Read in the directory contents. */ + SVN_ERR(get_dir_contents(&dir, fs, noderev, scratch_pool, + scratch_pool)); + + /* Update the cache, if we are to use one. */ + if (cache) + SVN_ERR(svn_cache__set(cache, key, &dir, scratch_pool)); /* find desired entry and return a copy in POOL, if found */ - entry = svn_fs_fs__find_dir_entry(entries, name, NULL); + entry = svn_fs_fs__find_dir_entry(dir.entries, name, NULL); if (entry) { entry_copy = apr_palloc(result_pool, sizeof(*entry_copy)); @@ -2639,16 +2791,27 @@ svn_fs_fs__get_proplist(apr_hash_t **pro if (noderev->prop_rep && svn_fs_fs__id_txn_used(&noderev->prop_rep->txn_id)) { + svn_error_t *err; const char *filename = svn_fs_fs__path_txn_node_props(fs, noderev->id, pool); proplist = apr_hash_make(pool); SVN_ERR(svn_stream_open_readonly(&stream, filename, pool, pool)); - SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, pool)); + err = svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, pool); + if (err) + { + svn_string_t *id_str = svn_fs_fs__id_unparse(noderev->id, pool); + + err = svn_error_compose_create(err, svn_stream_close(stream)); + return svn_error_quick_wrapf(err, + _("malformed property list for node-revision '%s' in '%s'"), + id_str->data, filename); + } SVN_ERR(svn_stream_close(stream)); } else if (noderev->prop_rep) { + svn_error_t *err; fs_fs_data_t *ffd = fs->fsap_data; representation_t *rep = noderev->prop_rep; pair_cache_key_t key = { 0 }; @@ -2667,7 +2830,16 @@ svn_fs_fs__get_proplist(apr_hash_t **pro proplist = apr_hash_make(pool); SVN_ERR(svn_fs_fs__get_contents(&stream, fs, noderev->prop_rep, FALSE, pool)); - SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, pool)); + err = svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, pool); + if (err) + { + svn_string_t *id_str = svn_fs_fs__id_unparse(noderev->id, pool); + + err = svn_error_compose_create(err, svn_stream_close(stream)); + return svn_error_quick_wrapf(err, + _("malformed property list for node-revision '%s'"), + id_str->data); + } SVN_ERR(svn_stream_close(stream)); if (ffd->properties_cache && SVN_IS_VALID_REVNUM(rep->revision)) @@ -2788,7 +2960,7 @@ init_rep_state(rep_state_t *rs, /* this function does not apply to representation containers */ SVN_ERR_ASSERT(entry->type >= SVN_FS_FS__ITEM_TYPE_FILE_REP && entry->type <= SVN_FS_FS__ITEM_TYPE_DIR_PROPS); - + shared_file->rfile = file; shared_file->fs = fs; shared_file->revision = entry->item.revision; @@ -2850,7 +3022,7 @@ get_raw_window_end(void **out, * at or beyond that offset. Use POOL for temporary allocations. * * This function requires RS->RAW_WINDOW_CACHE and RS->WINDOW_CACHE to - * be non-NULL. + * be non-NULL. */ static svn_error_t * cache_windows(svn_fs_t *fs, @@ -2965,7 +3137,7 @@ block_read_windows(svn_fs_fs__rep_header SVN_ERR(init_rep_state(&rs, rep_header, fs, rev_file, entry, result_pool)); - + /* RS->FILE may be shared between RS instances -> make sure we point * to the right data. */ offset = rs.start + rs.current; @@ -3018,7 +3190,7 @@ read_rep_header(svn_fs_fs__rep_header_t { fs_fs_data_t *ffd = fs->fsap_data; svn_boolean_t is_cached = FALSE; - + if (ffd->rep_header_cache) { SVN_ERR(svn_cache__get((void**)rep_header, &is_cached, @@ -3200,9 +3372,7 @@ block_read_noderev(node_revision_t **nod /* read node rev from revision file */ SVN_ERR(svn_fs_fs__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; + SVN_ERR(fixup_node_revision(fs, *noderev_p, scratch_pool)); if (ffd->node_revision_cache) SVN_ERR(svn_cache__set(ffd->node_revision_cache, &key, *noderev_p, @@ -3246,7 +3416,7 @@ block_read(void **result, /* 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_fs__item_offset(&wanted_offset, fs, revision_file,
Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/cached_data.h URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/cached_data.h?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/cached_data.h (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/cached_data.h Sun Jun 14 20:58:10 2015 @@ -30,6 +30,18 @@ +/* Resolve a FSFS quirk: if REP in FS is a "PLAIN" representation, its + * EXPANDED_SIZE element may be 0, in which case its value has to be taken + * from SIZE. + * + * This function ensures that EXPANDED_SIZE in REP always contains the + * actual value. No-op if REP is NULL. Uses SCRATCH_POOL for temporaries. + */ +svn_error_t * +svn_fs_fs__fixup_expanded_size(svn_fs_t *fs, + representation_t *rep, + apr_pool_t *scratch_pool); + /* Set *NODEREV_P to the node-revision for the node ID in FS. Do any allocations in POOL. */ svn_error_t * Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/dag.c URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/dag.c?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/dag.c (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/dag.c Sun Jun 14 20:58:10 2015 @@ -498,6 +498,40 @@ svn_fs_fs__dag_get_proplist(apr_hash_t * return SVN_NO_ERROR; } +svn_error_t * +svn_fs_fs__dag_has_props(svn_boolean_t *has_props, + dag_node_t *node, + apr_pool_t *scratch_pool) +{ + node_revision_t *noderev; + + SVN_ERR(get_node_revision(&noderev, node)); + + if (! noderev->prop_rep) + { + *has_props = FALSE; /* Easy out */ + return SVN_NO_ERROR; + } + + if (svn_fs_fs__id_txn_used(&noderev->prop_rep->txn_id)) + { + /* We are in a commit or something. Check actual properties */ + apr_hash_t *proplist; + + SVN_ERR(svn_fs_fs__get_proplist(&proplist, node->fs, + noderev, scratch_pool)); + + *has_props = proplist ? (0 < apr_hash_count(proplist)) : FALSE; + } + else + { + /* Properties are stored as a standard hash stream, + always ending with "END\n" (4 bytes) */ + *has_props = noderev->prop_rep->expanded_size > 4; + } + + return SVN_NO_ERROR; +} svn_error_t * svn_fs_fs__dag_set_proplist(dag_node_t *node, Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/dag.h URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/dag.h?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/dag.h (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/dag.h Sun Jun 14 20:58:10 2015 @@ -177,6 +177,12 @@ svn_error_t *svn_fs_fs__dag_get_proplist dag_node_t *node, apr_pool_t *pool); +/* Set *HAS_PROPS to TRUE if NODE has properties. Use SCRATCH_POOL + for temporary allocations */ +svn_error_t *svn_fs_fs__dag_has_props(svn_boolean_t *has_props, + dag_node_t *node, + apr_pool_t *scratch_pool); + /* Set the property list of NODE to PROPLIST, allocating from POOL. The node being changed must be mutable. Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs.c URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs.c?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs.c (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs.c Sun Jun 14 20:58:10 2015 @@ -27,7 +27,6 @@ #include <apr_general.h> #include <apr_pools.h> #include <apr_file_io.h> -#include <apr_thread_mutex.h> #include "svn_fs.h" #include "svn_delta.h" @@ -163,9 +162,11 @@ fs_freeze_body(void *baton, SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, b->fs, pool)); if (exists) - SVN_ERR(svn_fs_fs__lock_rep_cache(b->fs, pool)); - - SVN_ERR(b->freeze_func(b->freeze_baton, pool)); + SVN_ERR(svn_fs_fs__with_rep_cache_lock(b->fs, + b->freeze_func, b->freeze_baton, + pool)); + else + SVN_ERR(b->freeze_func(b->freeze_baton, pool)); return SVN_NO_ERROR; } Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs.h URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs.h?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs.h (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs.h Sun Jun 14 20:58:10 2015 @@ -527,7 +527,14 @@ typedef struct representation_t svn_filesize_t size; /* The size of the fulltext of the representation. If this is 0, - * the fulltext size is equal to representation size in the rev file, */ + * for a plain rep, the real fulltext size is equal to the SIZE field. + * For a delta rep, this field is always the real fulltext size. + * + * Note that svn_fs_fs__fixup_expanded_size() checks for these special + * cases and ensures that this field contains the actual value. We call + * it early after reading a representation struct, so most code does not + * have to worry about it. + */ svn_filesize_t expanded_size; /* Is this a representation (still) within a transaction? */ @@ -616,6 +623,18 @@ typedef struct change_t svn_fs_path_change2_t info; } change_t; + +/*** Directory (only used at the cache interface) ***/ +typedef struct svn_fs_fs__dir_data_t +{ + /* Contents, i.e. all directory entries, sorted by name. */ + apr_array_header_t *entries; + + /* SVN_INVALID_FILESIZE for committed data, otherwise the length of the + * in-txn on-disk representation of that directory. */ + svn_filesize_t txn_filesize; +} svn_fs_fs__dir_data_t; + #ifdef __cplusplus } Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs_fs.c URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs_fs.c?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs_fs.c (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/fs_fs.c Sun Jun 14 20:58:10 2015 @@ -401,7 +401,7 @@ svn_fs_fs__with_all_locks(svn_fs_t *fs, fs_fs_data_t *ffd = fs->fsap_data; /* Be sure to use the correct lock ordering as documented in - fs_fs_shared_data_t. The lock chain is being created in + fs_fs_shared_data_t. The lock chain is being created in innermost (last to acquire) -> outermost (first to acquire) order. */ with_lock_baton_t *lock_baton = create_lock_baton(fs, write_lock, body, baton, pool); @@ -1126,7 +1126,9 @@ svn_fs_fs__open(svn_fs_t *fs, const char /* Global configuration options. */ SVN_ERR(read_global_config(fs)); - return get_youngest(&(ffd->youngest_rev_cache), fs, pool); + ffd->youngest_rev_cache = 0; + + return SVN_NO_ERROR; } /* Wrapper around svn_io_file_create which ignores EEXIST. */ @@ -1144,7 +1146,7 @@ create_file_ignore_eexist(const char *fi return svn_error_trace(err); } -/* Baton type bridging svn_fs_fs__upgrade and upgrade_body carrying +/* Baton type bridging svn_fs_fs__upgrade and upgrade_body carrying * parameters over between them. */ struct upgrade_baton_t { @@ -1287,7 +1289,7 @@ svn_fs_fs__upgrade(svn_fs_t *fs, baton.notify_baton = notify_baton; baton.cancel_func = cancel_func; baton.cancel_baton = cancel_baton; - + return svn_fs_fs__with_all_locks(fs, upgrade_body, (void *)&baton, pool); } @@ -1377,38 +1379,9 @@ svn_fs_fs__file_length(svn_filesize_t *l /* Treat "no representation" as "empty file". */ *length = 0; } - else if (data_rep->expanded_size) - { - /* Standard case: a non-empty file. */ - *length = data_rep->expanded_size; - } else { - /* Work around a FSFS format quirk (see issue #4554). - - A plain representation may specify its EXPANDED LENGTH as "0" - in which case, the SIZE value is what we want. - - Because EXPANDED_LENGTH will also be 0 for empty files, while - SIZE is non-null, we need to check wether the content is - actually empty. We simply compare with the MD5 checksum of - empty content (sha-1 is not always available). - */ - svn_checksum_t *empty_md5 - = svn_checksum_empty_checksum(svn_checksum_md5, pool); - - if (memcmp(empty_md5->digest, data_rep->md5_digest, - sizeof(data_rep->md5_digest))) - { - /* Contents is not empty, i.e. EXPANDED_LENGTH cannot be the - actual file length. */ - *length = data_rep->size; - } - else - { - /* Contents is empty. */ - *length = 0; - } + *length = data_rep->expanded_size; } return SVN_NO_ERROR; @@ -1556,7 +1529,7 @@ svn_fs_fs__file_checksum(svn_checksum_t { svn_checksum_t temp; temp.kind = kind; - + switch(kind) { case svn_checksum_md5: @@ -1944,7 +1917,10 @@ get_node_origins_from_file(svn_fs_t *fs, stream = svn_stream_from_aprfile2(fd, FALSE, pool); *node_origins = apr_hash_make(pool); - SVN_ERR(svn_hash_read2(*node_origins, stream, SVN_HASH_TERMINATOR, pool)); + err = svn_hash_read2(*node_origins, stream, SVN_HASH_TERMINATOR, pool); + if (err) + return svn_error_quick_wrapf(err, _("malformed node origin data in '%s'"), + node_origins_file); return svn_stream_close(stream); } Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/hotcopy.c URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/hotcopy.c?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/hotcopy.c (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/hotcopy.c Sun Jun 14 20:58:10 2015 @@ -369,7 +369,7 @@ hotcopy_copy_packed_shard(svn_boolean_t return SVN_NO_ERROR; } -/* Remove file PATH, if it exists - even if it is read-only. +/* Remove file PATH, if it exists - even if it is read-only. * Use POOL for temporary allocations. */ static svn_error_t * hotcopy_remove_file(const char *path, @@ -855,7 +855,6 @@ hotcopy_body(void *baton, apr_pool_t *po * ### so we have no way of just printing a warning via * ### the fs->warning() callback. */ - const char *msg; const char *src_abspath; const char *dst_abspath; const char *config_relpath; @@ -874,13 +873,12 @@ hotcopy_body(void *baton, apr_pool_t *po src_abspath = svn_dirent_dirname(src_abspath, pool); dst_abspath = svn_dirent_dirname(dst_abspath, pool); - msg = apr_psprintf(pool, + return svn_error_quick_wrapf(err, _("Failed to create hotcopy at '%s'. " "The file '%s' is missing from the source " "repository. Please create this file, for " "instance by running 'svnadmin upgrade %s'"), dst_abspath, config_relpath, src_abspath); - return svn_error_quick_wrap(err, msg); } else return svn_error_trace(err); Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/id.c URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/id.c?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/id.c (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/id.c Sun Jun 14 20:58:10 2015 @@ -232,7 +232,7 @@ svn_fs_fs__id_txn_unparse(const svn_fs_f { char string[2 * SVN_INT64_BUFFER_SIZE + 1]; char *p = string; - + p += svn__i64toa(p, txn_id->revision); *(p++) = '-'; p += svn__ui64tobase36(p, txn_id->number); @@ -365,14 +365,21 @@ svn_fs_fs__id_check_related(const svn_fs if (a == b) return TRUE; - /* If both node_ids start with _ and they have differing transaction - IDs, then it is impossible for them to be related. */ - if (id_a->private_id.node_id.revision == SVN_INVALID_REVNUM) + /* If both node_ids have been created within _different_ transactions + (and are still uncommitted), then it is impossible for them to be + related. + + Due to our txn-local temporary IDs, however, they might have been + given the same temporary node ID. We need to detect that case. + */ + if ( id_a->private_id.node_id.revision == SVN_INVALID_REVNUM + && id_b->private_id.node_id.revision == SVN_INVALID_REVNUM) { - if ( !svn_fs_fs__id_part_eq(&id_a->private_id.txn_id, - &id_b->private_id.txn_id) - || !svn_fs_fs__id_txn_used(&id_a->private_id.txn_id)) + if (!svn_fs_fs__id_part_eq(&id_a->private_id.txn_id, + &id_b->private_id.txn_id)) return FALSE; + + /* At this point, matching node_ids implies relatedness. */ } return svn_fs_fs__id_part_eq(&id_a->private_id.node_id, @@ -385,7 +392,7 @@ svn_fs_fs__id_compare(const svn_fs_id_t const svn_fs_id_t *b) { if (svn_fs_fs__id_eq(a, b)) - return svn_fs_node_same; + return svn_fs_node_unchanged; return (svn_fs_fs__id_check_related(a, b) ? svn_fs_node_common_ancestor : svn_fs_node_unrelated); } @@ -418,7 +425,7 @@ svn_fs_fs__id_txn_create_root(const svn_ fs_fs__id_t *id = apr_pcalloc(pool, sizeof(*id)); /* node ID and copy ID are "0" */ - + id->private_id.txn_id = *txn_id; id->private_id.rev_item.revision = SVN_INVALID_REVNUM; @@ -598,7 +605,7 @@ svn_fs_fs__id_serialize(svn_temp_seriali const svn_fs_id_t * const *in) { const fs_fs__id_t *id = (const fs_fs__id_t *)*in; - + /* nothing to do for NULL ids */ if (id == NULL) return; Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/index.c URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/index.c?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/index.c (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/index.c Sun Jun 14 20:58:10 2015 @@ -87,7 +87,7 @@ typedef struct l2p_page_table_entry_t /* Master run-time data structure of an log-to-phys index. It contains * the page tables of every revision covered by that index - but not the - * pages themselves. + * pages themselves. */ typedef struct l2p_header_t { @@ -498,7 +498,7 @@ stream_write_encoded(svn_stream_t *strea apr_uint64_t value) { unsigned char encoded[ENCODED_INT_LENGTH]; - + apr_size_t len = encode_uint(encoded, value); return svn_error_trace(svn_stream_write(stream, (char *)encoded, &len)); } @@ -806,7 +806,7 @@ svn_fs_fs__l2p_index_append(svn_checksum * 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_INDEX_OVERFLOW , NULL, - _("L2P index page size %s" + _("L2P index page size %s" " exceeds current limit of 2G entries"), apr_psprintf(local_pool, "%" APR_UINT64_T_FMT, ffd->l2p_page_size)); @@ -869,7 +869,7 @@ svn_fs_fs__l2p_index_append(svn_checksum 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, "%" APR_UINT64_T_FMT, + apr_psprintf(local_pool, "%" APR_UINT64_T_FMT, proto_entry.item_index), revision + page_counts->nelts); @@ -1277,7 +1277,7 @@ l2p_page_table_access_func(void **out, /* set output as a courtesy to the caller */ *out = table_baton->pages; - + return SVN_NO_ERROR; } @@ -1669,7 +1669,7 @@ l2p_proto_index_lookup(apr_off_t *offset } SVN_ERR(svn_io_file_close(file, scratch_pool)); - + return SVN_NO_ERROR; } @@ -2188,7 +2188,7 @@ get_p2l_header(p2l_header_t **header, /* allocate result data structure */ result = apr_pcalloc(result_pool, sizeof(*result)); - + /* Read table sizes, check them for plausibility and allocate page array. */ SVN_ERR(packed_stream_get(&value, rev_file->p2l_stream)); result->first_revision = (svn_revnum_t)value; @@ -2282,7 +2282,7 @@ p2l_page_info_copy(p2l_page_info_baton_t const p2l_header_t *header, const apr_off_t *offsets) { - /* if the requested offset is out of bounds, return info for + /* if the requested offset is out of bounds, return info for * a zero-sized empty page right behind the last page. */ if (baton->offset / header->page_size < header->page_count) @@ -2595,7 +2595,7 @@ get_p2l_keys(p2l_page_info_baton_t *page apr_pool_t *scratch_pool) { p2l_page_info_baton_t page_info; - + /* request info for the index pages that describes the pack / rev file * contents at pack / rev file position OFFSET. */ page_info.offset = offset; @@ -2611,7 +2611,7 @@ get_p2l_keys(p2l_page_info_baton_t *page /* return results */ if (page_info_p) *page_info_p = page_info; - + /* construct cache key */ if (key_p) { @@ -2621,7 +2621,7 @@ get_p2l_keys(p2l_page_info_baton_t *page key.is_packed = rev_file->is_packed; key.page = page_info.page_no; - *key_p = key; + *key_p = key; } return SVN_NO_ERROR; Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/index.h URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/index.h?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/index.h (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/index.h Sun Jun 14 20:58:10 2015 @@ -71,7 +71,7 @@ svn_fs_fs__l2p_proto_index_add_revision( * Not all possible index values need to be used. OFFSET may be -1 to * mark 'invalid' item indexes but that is already implied for all item * indexes not explicitly given a mapping. - * + * * Use SCRATCH_POOL for temporary allocations. */ svn_error_t * @@ -83,7 +83,7 @@ svn_fs_fs__l2p_proto_index_add_entry(apr /* Use the proto index file stored at PROTO_FILE_NAME, construct the final * log-to-phys index and append it to INDEX_FILE. The first revision will * be REVISION, entries to the next revision will be assigned to REVISION+1 - * and so forth. + * and so forth. * * Return the MD5 checksum of the on-disk index data in *CHECKSUM, allocated * in RESULT_POOL. Use SCRATCH_POOL for temporary allocations. Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/lock.c URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/lock.c?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/lock.c (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/lock.c Sun Jun 14 20:58:10 2015 @@ -222,7 +222,7 @@ write_digest_file(apr_hash_t *children, svn_io_file_del_none, pool, pool)); if ((err = svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool))) { - svn_error_clear(svn_stream_close(stream)); + err = svn_error_compose_create(err, svn_stream_close(stream)); return svn_error_createf(err->apr_err, err, _("Cannot write lock/entries hashfile '%s'"), @@ -273,7 +273,7 @@ read_digest_file(apr_hash_t **children_p hash = apr_hash_make(pool); if ((err = svn_hash_read2(hash, stream, SVN_HASH_TERMINATOR, pool))) { - svn_error_clear(svn_stream_close(stream)); + err = svn_error_compose_create(err, svn_stream_close(stream)); return svn_error_createf(err->apr_err, err, _("Can't parse lock/entries hashfile '%s'"), @@ -355,7 +355,7 @@ set_lock(const char *fs_path, always come back empty. */ SVN_ERR(read_digest_file(&children, NULL, fs_path, digest_path, pool)); - SVN_ERR(write_digest_file(children, lock, fs_path, digest_path, + SVN_ERR(write_digest_file(children, lock, fs_path, digest_path, perms_reference, pool)); return SVN_NO_ERROR; @@ -405,7 +405,7 @@ add_to_digest(const char *fs_path, } if (apr_hash_count(children) != original_count) - SVN_ERR(write_digest_file(children, lock, fs_path, index_digest_path, + SVN_ERR(write_digest_file(children, lock, fs_path, index_digest_path, perms_reference, pool)); return SVN_NO_ERROR; @@ -438,7 +438,7 @@ delete_from_digest(const char *fs_path, } if (apr_hash_count(children) || lock) - SVN_ERR(write_digest_file(children, lock, fs_path, index_digest_path, + SVN_ERR(write_digest_file(children, lock, fs_path, index_digest_path, perms_reference, pool)); else SVN_ERR(svn_io_remove_file2(index_digest_path, TRUE, pool)); @@ -715,6 +715,7 @@ check_lock(svn_error_t **fs_err, const svn_fs_lock_target_t *target, struct lock_baton *lb, svn_fs_root_t *root, + svn_revnum_t youngest_rev, apr_pool_t *pool) { svn_node_kind_t kind; @@ -751,6 +752,15 @@ check_lock(svn_error_t **fs_err, if (SVN_IS_VALID_REVNUM(target->current_rev)) { svn_revnum_t created_rev; + + if (target->current_rev > youngest_rev) + { + *fs_err = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, + _("No such revision %ld"), + target->current_rev); + return SVN_NO_ERROR; + } + SVN_ERR(svn_fs_fs__node_created_rev(&created_rev, root, path, pool)); @@ -856,7 +866,7 @@ lock_body(void *baton, apr_pool_t *pool) info.fs_err = SVN_NO_ERROR; SVN_ERR(check_lock(&info.fs_err, info.path, item->value, lb, root, - iterpool)); + youngest, iterpool)); /* If no error occurred while pre-checking, schedule the index updates for this path. */ @@ -900,14 +910,17 @@ lock_body(void *baton, apr_pool_t *pool) { info->lock = svn_lock_create(lb->result_pool); if (target->token) - info->lock->token = target->token; + info->lock->token = apr_pstrdup(lb->result_pool, target->token); else SVN_ERR(svn_fs_fs__generate_lock_token(&(info->lock->token), lb->fs, lb->result_pool)); + /* The INFO->PATH is already allocated in LB->RESULT_POOL as a result + of svn_fspath__canonicalize() (see svn_fs_fs__lock()). */ info->lock->path = info->path; - info->lock->owner = lb->fs->access_ctx->username; - info->lock->comment = lb->comment; + info->lock->owner = apr_pstrdup(lb->result_pool, + lb->fs->access_ctx->username); + info->lock->comment = apr_pstrdup(lb->result_pool, lb->comment); info->lock->is_dav_comment = lb->is_dav_comment; info->lock->creation_date = apr_time_now(); info->lock->expiration_date = lb->expiration_date; @@ -1096,6 +1109,7 @@ svn_fs_fs__lock(svn_fs_t *fs, apr_array_header_t *sorted_targets; apr_hash_t *canonical_targets = apr_hash_make(scratch_pool); apr_hash_index_t *hi; + apr_pool_t *iterpool; svn_error_t *err, *cb_err = SVN_NO_ERROR; int i; @@ -1136,23 +1150,26 @@ svn_fs_fs__lock(svn_fs_t *fs, lb.steal_lock = steal_lock; lb.result_pool = result_pool; - err = svn_fs_fs__with_write_lock(fs, lock_body, &lb, scratch_pool); + iterpool = svn_pool_create(scratch_pool); + err = svn_fs_fs__with_write_lock(fs, lock_body, &lb, iterpool); for (i = 0; i < lb.infos->nelts; ++i) { struct lock_info_t *info = &APR_ARRAY_IDX(lb.infos, i, struct lock_info_t); + svn_pool_clear(iterpool); if (!cb_err && lock_callback) { if (!info->lock && !info->fs_err) info->fs_err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED, 0, _("Failed to lock '%s'"), info->path); - + cb_err = lock_callback(lock_baton, info->path, info->lock, - info->fs_err, scratch_pool); + info->fs_err, iterpool); } svn_error_clear(info->fs_err); } + svn_pool_destroy(iterpool); if (err && cb_err) svn_error_compose(err, cb_err); @@ -1192,6 +1209,7 @@ svn_fs_fs__unlock(svn_fs_t *fs, apr_array_header_t *sorted_targets; apr_hash_t *canonical_targets = apr_hash_make(scratch_pool); apr_hash_index_t *hi; + apr_pool_t *iterpool; svn_error_t *err, *cb_err = SVN_NO_ERROR; int i; @@ -1226,11 +1244,13 @@ svn_fs_fs__unlock(svn_fs_t *fs, ub.break_lock = break_lock; ub.result_pool = result_pool; - err = svn_fs_fs__with_write_lock(fs, unlock_body, &ub, scratch_pool); + iterpool = svn_pool_create(scratch_pool); + err = svn_fs_fs__with_write_lock(fs, unlock_body, &ub, iterpool); for (i = 0; i < ub.infos->nelts; ++i) { struct unlock_info_t *info = &APR_ARRAY_IDX(ub.infos, i, struct unlock_info_t); + svn_pool_clear(iterpool); if (!cb_err && lock_callback) { if (!info->done && !info->fs_err) @@ -1238,10 +1258,11 @@ svn_fs_fs__unlock(svn_fs_t *fs, 0, _("Failed to unlock '%s'"), info->path); cb_err = lock_callback(lock_baton, info->path, NULL, info->fs_err, - scratch_pool); + iterpool); } svn_error_clear(info->fs_err); } + svn_pool_destroy(iterpool); if (err && cb_err) svn_error_compose(err, cb_err); Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/low_level.c URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/low_level.c?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/low_level.c (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/low_level.c Sun Jun 14 20:58:10 2015 @@ -189,6 +189,19 @@ svn_fs_fs__unparse_revision_trailer(apr_ changes_offset); } +/* If ERR is not NULL, wrap it MESSAGE. The latter must have an %ld + * format parameter that will be filled with REV. */ +static svn_error_t * +wrap_footer_error(svn_error_t *err, + const char *message, + svn_revnum_t rev) +{ + if (err) + return svn_error_quick_wrapf(err, message, rev); + + return SVN_NO_ERROR; +} + svn_error_t * svn_fs_fs__parse_footer(apr_off_t *l2p_offset, svn_checksum_t **l2p_checksum, @@ -196,6 +209,7 @@ svn_fs_fs__parse_footer(apr_off_t *l2p_o svn_checksum_t **p2l_checksum, svn_stringbuf_t *footer, svn_revnum_t rev, + apr_off_t footer_offset, apr_pool_t *result_pool) { apr_int64_t val; @@ -204,17 +218,20 @@ svn_fs_fs__parse_footer(apr_off_t *l2p_o /* Get the L2P offset. */ const char *str = svn_cstring_tokenize(" ", &last_str); if (str == NULL) - return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, - _("Invalid revision footer")); + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + "Invalid r%ld footer", rev); - SVN_ERR(svn_cstring_atoi64(&val, str)); + SVN_ERR(wrap_footer_error(svn_cstring_strtoi64(&val, str, 0, + footer_offset - 1, 10), + "Invalid L2P offset in r%ld footer", + rev)); *l2p_offset = (apr_off_t)val; /* Get the L2P checksum. */ str = svn_cstring_tokenize(" ", &last_str); if (str == NULL) - return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, - _("Invalid revision footer")); + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + "Invalid r%ld footer", rev); SVN_ERR(svn_checksum_parse_hex(l2p_checksum, svn_checksum_md5, str, result_pool)); @@ -222,17 +239,33 @@ svn_fs_fs__parse_footer(apr_off_t *l2p_o /* Get the P2L offset. */ str = svn_cstring_tokenize(" ", &last_str); if (str == NULL) - return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, - _("Invalid revision footer")); + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + "Invalid r%ld footer", rev); - SVN_ERR(svn_cstring_atoi64(&val, str)); + SVN_ERR(wrap_footer_error(svn_cstring_strtoi64(&val, str, 0, + footer_offset - 1, 10), + "Invalid P2L offset in r%ld footer", + rev)); *p2l_offset = (apr_off_t)val; + /* The P2L indes follows the L2P index */ + if (*p2l_offset <= *l2p_offset) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + "P2L offset %s must be larger than L2P offset %s" + " in r%ld footer", + apr_psprintf(result_pool, + "%" APR_UINT64_T_HEX_FMT, + (apr_uint64_t)*p2l_offset), + apr_psprintf(result_pool, + "%" APR_UINT64_T_HEX_FMT, + (apr_uint64_t)*l2p_offset), + rev); + /* Get the P2L checksum. */ str = svn_cstring_tokenize(" ", &last_str); if (str == NULL) - return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, - _("Invalid revision footer")); + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + "Invalid r%ld footer", rev); SVN_ERR(svn_checksum_parse_hex(p2l_checksum, svn_checksum_md5, str, result_pool)); @@ -499,7 +532,7 @@ svn_fs_fs__read_changes_incrementally(sv } while (change); svn_pool_destroy(iterpool); - + return SVN_NO_ERROR; } @@ -725,7 +758,7 @@ svn_fs_fs__parse_representation(represen /* initialize transaction info (never stored) */ svn_fs_fs__id_txn_reset(&rep->txn_id); - + /* while in transactions, it is legal to simply write "-1" */ str = svn_cstring_tokenize(" ", &string); if (str == NULL) @@ -998,7 +1031,7 @@ format_digest(const unsigned char *diges svn_checksum_t checksum; checksum.digest = digest; checksum.kind = kind; - + if (is_null) return "(null)"; @@ -1174,7 +1207,7 @@ svn_fs_fs__write_rep_header(svn_fs_fs__r apr_pool_t *scratch_pool) { const char *text; - + switch (header->type) { case svn_fs_fs__rep_plain: Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/low_level.h URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/low_level.h?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/low_level.h (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/low_level.h Sun Jun 14 20:58:10 2015 @@ -44,7 +44,7 @@ * in *ROOT_OFFSET and the offset of the changed paths list in * *CHANGES_OFFSET. Offsets are relative to the revision's start offset. * ROOT_OFFSET and / or CHANGES_OFFSET may be NULL. - * + * * Note that REV is only used to construct nicer error objects. */ svn_error_t * @@ -66,7 +66,9 @@ svn_fs_fs__unparse_revision_trailer(apr_ * and return the start offsets of the index data in *L2P_OFFSET and * *P2L_OFFSET, respectively. Also, return the expected checksums in * in *L2P_CHECKSUM and *P2L_CHECKSUM. - * + * + * FOOTER_OFFSET is used for validation. + * * Note that REV is only used to construct nicer error objects that * mention this revision. Allocate the checksums in RESULT_POOL. */ @@ -77,6 +79,7 @@ svn_fs_fs__parse_footer(apr_off_t *l2p_o svn_checksum_t **p2l_checksum, svn_stringbuf_t *footer, svn_revnum_t rev, + apr_off_t footer_offset, apr_pool_t *result_pool); /* Given the offset of the L2P index data in L2P_OFFSET, the content Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/pack.c URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/pack.c?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/pack.c (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/pack.c Sun Jun 14 20:58:10 2015 @@ -69,7 +69,7 @@ * each of the 4 buckets separately. The first three will simply order * their items by revision, starting with the newest once. Placing rep * and noderev items is a more elaborate process documented in the code. - * + * * In short, we store items in the following order: * - changed paths lists * - node property @@ -256,7 +256,7 @@ initialize_pack_context(pack_context_t * fs_fs_data_t *ffd = fs->fsap_data; const char *temp_dir; int max_revs = MIN(ffd->max_files_per_dir, max_items); - + SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT); SVN_ERR_ASSERT(shard_rev % ffd->max_files_per_dir == 0); @@ -272,7 +272,7 @@ initialize_pack_context(pack_context_t * context->start_rev = shard_rev; context->end_rev = shard_rev; context->shard_end_rev = shard_rev + ffd->max_files_per_dir; - + /* Create the new directory and pack file. */ context->shard_dir = shard_dir; context->pack_file_dir = pack_file_dir; @@ -349,7 +349,7 @@ reset_pack_context(pack_context_t *conte SVN_ERR(svn_io_file_trunc(context->reps_file, 0, pool)); svn_pool_clear(context->info_pool); - + return SVN_NO_ERROR; } @@ -377,13 +377,15 @@ close_pack_context(pack_context_t *conte SVN_ERR(svn_fs_fs__add_index_data(context->fs, context->pack_file, proto_l2p_index_path, proto_p2l_index_path, - context->shard_rev, + context->shard_rev, pool)); /* remove proto index files */ SVN_ERR(svn_io_remove_file2(proto_l2p_index_path, FALSE, pool)); SVN_ERR(svn_io_remove_file2(proto_p2l_index_path, FALSE, pool)); + /* Ensure that packed file is written to disk.*/ + SVN_ERR(svn_io_file_flush_to_disk(context->pack_file, pool)); SVN_ERR(svn_io_file_close(context->pack_file, pool)); return SVN_NO_ERROR; @@ -402,7 +404,7 @@ copy_file_data(pack_context_t *context, /* most non-representation items will be small. Minimize the buffer * and infrastructure overhead in that case. */ enum { STACK_BUFFER_SIZE = 1024 }; - + if (size < STACK_BUFFER_SIZE) { /* copy small data using a fixed-size buffer on stack */ @@ -481,9 +483,9 @@ copy_item_to_temp(pack_context_t *contex SVN_ERR(svn_fs_fs__get_file_offset(&new_entry->offset, temp_file, pool)); APR_ARRAY_PUSH(entries, svn_fs_fs__p2l_entry_t *) = new_entry; - + SVN_ERR(copy_file_data(context, temp_file, rev_file, entry->size, pool)); - + return SVN_NO_ERROR; } @@ -572,7 +574,7 @@ copy_rep_to_temp(pack_context_t *context /* read & parse the representation header */ stream = svn_stream_from_aprfile2(rev_file, TRUE, pool); SVN_ERR(svn_fs_fs__read_rep_header(&rep_header, stream, pool, pool)); - svn_stream_close(stream); + SVN_ERR(svn_stream_close(stream)); /* if the representation is a delta against some other rep, link the two */ if ( rep_header->type == svn_fs_fs__rep_delta @@ -643,17 +645,18 @@ compare_dir_entries_format6(const svn_so apr_array_header_t * svn_fs_fs__order_dir_entries(svn_fs_t *fs, apr_hash_t *directory, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { apr_array_header_t *ordered = svn_sort__hash(directory, svn_fs_fs__use_log_addressing(fs) ? compare_dir_entries_format7 : compare_dir_entries_format6, - pool); + scratch_pool); apr_array_header_t *result - = apr_array_make(pool, ordered->nelts, sizeof(svn_fs_dirent_t *)); + = apr_array_make(result_pool, ordered->nelts, sizeof(svn_fs_dirent_t *)); int i; for (i = 0; i < ordered->nelts; ++i) @@ -714,7 +717,7 @@ copy_node_to_temp(pack_context_t *contex /* read & parse noderev */ stream = svn_stream_from_aprfile2(rev_file, TRUE, pool); SVN_ERR(svn_fs_fs__read_noderev(&noderev, stream, pool, pool)); - svn_stream_close(stream); + SVN_ERR(svn_stream_close(stream)); /* create a copy of ENTRY, make it point to the copy destination and * store it in CONTEXT */ @@ -736,9 +739,7 @@ copy_node_to_temp(pack_context_t *contex { path_order->rep_id.revision = noderev->data_rep->revision; path_order->rep_id.number = noderev->data_rep->item_index; - path_order->expanded_size = noderev->data_rep->expanded_size - ? noderev->data_rep->expanded_size - : noderev->data_rep->size; + path_order->expanded_size = noderev->data_rep->expanded_size; } /* Sort path is the key used for ordering noderevs and associated reps. @@ -1001,7 +1002,7 @@ sort_reps(pack_context_t *context) /* We now know the final ordering. */ for (i = 0; i < count; ++i) path_order[i] = temp[i]; - + svn_pool_destroy(temp_pool); } @@ -1290,7 +1291,7 @@ pack_range(pack_context_t *context, /* store the indirect array index */ APR_ARRAY_PUSH(context->rev_offsets, int) = context->reps->nelts; - + /* read the phys-to-log index file until we covered the whole rev file. * That index contains enough info to build both target indexes from it. */ while (offset < rev_file->l2p_offset) @@ -1391,7 +1392,7 @@ pack_range(pack_context_t *context, SVN_ERR(write_l2p_index(context, revpool)); svn_pool_destroy(revpool); - + return SVN_NO_ERROR; } @@ -1639,7 +1640,7 @@ svn_fs_fs__get_packed_offset(apr_off_t * /* Packing logic for physical addresssing mode: * Simply concatenate all revision contents. - * + * * Pack the revision shard starting at SHARD_REV containing exactly * MAX_FILES_PER_DIR revisions from SHARD_PATH into the PACK_FILE_DIR, * using POOL for allocations. CANCEL_FUNC and CANCEL_BATON are what you @@ -1655,7 +1656,9 @@ pack_phys_addressed(const char *pack_fil apr_pool_t *pool) { const char *pack_file_path, *manifest_file_path; - svn_stream_t *pack_stream, *manifest_stream; + apr_file_t *pack_file; + apr_file_t *manifest_file; + svn_stream_t *manifest_stream; svn_revnum_t end_rev, rev; apr_off_t next_offset; apr_pool_t *iterpool; @@ -1664,13 +1667,18 @@ pack_phys_addressed(const char *pack_fil pack_file_path = svn_dirent_join(pack_file_dir, PATH_PACKED, pool); manifest_file_path = svn_dirent_join(pack_file_dir, PATH_MANIFEST, pool); - /* Create the new directory and pack file. */ - SVN_ERR(svn_stream_open_writable(&pack_stream, pack_file_path, pool, - pool)); + /* Create the new directory and pack file. + * Use unbuffered apr_file_t since we're going to write using 16kb + * chunks. */ + SVN_ERR(svn_io_file_open(&pack_file, pack_file_path, + APR_WRITE | APR_CREATE | APR_EXCL, + APR_OS_DEFAULT, pool)); /* Create the manifest file. */ - SVN_ERR(svn_stream_open_writable(&manifest_stream, manifest_file_path, - pool, pool)); + SVN_ERR(svn_io_file_open(&manifest_file, manifest_file_path, + APR_WRITE | APR_BUFFERED | APR_CREATE | APR_EXCL, + APR_OS_DEFAULT, pool)); + manifest_stream = svn_stream_from_aprfile2(manifest_file, TRUE, pool); end_rev = start_rev + max_files_per_dir - 1; next_offset = 0; @@ -1697,16 +1705,24 @@ pack_phys_addressed(const char *pack_fil /* Copy all the bits from the rev file to the end of the pack file. */ SVN_ERR(svn_stream_open_readonly(&rev_stream, path, iterpool, iterpool)); - SVN_ERR(svn_stream_copy3(rev_stream, svn_stream_disown(pack_stream, - iterpool), + SVN_ERR(svn_stream_copy3(rev_stream, + svn_stream_from_aprfile2(pack_file, TRUE, pool), cancel_func, cancel_baton, iterpool)); } - /* disallow write access to the manifest file */ + /* Close stream over APR file. */ SVN_ERR(svn_stream_close(manifest_stream)); + + /* Ensure that pack file is written to disk. */ + SVN_ERR(svn_io_file_flush_to_disk(manifest_file, pool)); + SVN_ERR(svn_io_file_close(manifest_file, pool)); + + /* disallow write access to the manifest file */ SVN_ERR(svn_io_set_file_read_only(manifest_file_path, FALSE, iterpool)); - SVN_ERR(svn_stream_close(pack_stream)); + /* Ensure that pack file is written to disk. */ + SVN_ERR(svn_io_file_flush_to_disk(pack_file, pool)); + SVN_ERR(svn_io_file_close(pack_file, pool)); svn_pool_destroy(iterpool); @@ -1756,7 +1772,7 @@ pack_rev_shard(svn_fs_t *fs, SVN_ERR(pack_phys_addressed(pack_file_dir, shard_path, shard_rev, max_files_per_dir, cancel_func, cancel_baton, pool)); - + SVN_ERR(svn_io_copy_perms(shard_path, pack_file_dir, pool)); SVN_ERR(svn_io_set_file_read_only(pack_file_path, FALSE, pool)); Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/pack.h URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/pack.h?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/pack.h (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/pack.h Sun Jun 14 20:58:10 2015 @@ -51,13 +51,14 @@ svn_fs_fs__get_packed_offset(apr_off_t * apr_pool_t *pool); /* Return the svn_dir_entry_t* objects of DIRECTORY in an APR array - * allocated in POOL with entries added in storage (on-disk) order. - * FS format will be used to pick the optimal ordering strategy. + * allocated in RESULT_POOL with entries added in storage (on-disk) order. + * FS' format will be used to pick the optimal ordering strategy. Use + * SCRATCH_POOL for temporary allocations. */ apr_array_header_t * svn_fs_fs__order_dir_entries(svn_fs_t *fs, apr_hash_t *directory, - apr_pool_t *pool); - + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); #endif Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/recovery.c URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/recovery.c?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/recovery.c (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/recovery.c Sun Jun 14 20:58:10 2015 @@ -160,6 +160,7 @@ recover_find_max_ids(svn_fs_t *fs, apr_hash_index_t *hi; apr_pool_t *iterpool; node_revision_t *noderev; + svn_error_t *err; baton.stream = rev_file->stream; SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &offset, pool)); @@ -193,19 +194,26 @@ recover_find_max_ids(svn_fs_t *fs, "representation")); /* Now create a stream that's allowed to read only as much data as is - stored in the representation. Note that this is a directory, i.e. + stored in the representation. Note that this is a directory, i.e. represented using the hash format on disk and can never have 0 length. */ baton.pool = pool; - baton.remaining = noderev->data_rep->expanded_size - ? noderev->data_rep->expanded_size - : noderev->data_rep->size; + baton.remaining = noderev->data_rep->expanded_size; stream = svn_stream_create(&baton, pool); svn_stream_set_read2(stream, NULL /* only full read support */, read_handler_recover); /* Now read the entries from that stream. */ entries = apr_hash_make(pool); - SVN_ERR(svn_hash_read2(entries, stream, SVN_HASH_TERMINATOR, pool)); + err = svn_hash_read2(entries, stream, SVN_HASH_TERMINATOR, pool); + if (err) + { + svn_string_t *id_str = svn_fs_fs__id_unparse(noderev->id, pool); + + err = svn_error_compose_create(err, svn_stream_close(stream)); + return svn_error_quick_wrapf(err, + _("malformed representation for node-revision '%s'"), + id_str->data); + } SVN_ERR(svn_stream_close(stream)); /* Now check each of the entries in our directory to find new node and Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rep-cache-db.sql URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rep-cache-db.sql?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rep-cache-db.sql (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rep-cache-db.sql Sun Jun 14 20:58:10 2015 @@ -63,3 +63,6 @@ WHERE revision > ?1 -- STMT_LOCK_REP BEGIN TRANSACTION; INSERT INTO rep_cache VALUES ('dummy', 0, 0, 0, 0) + +-- STMT_UNLOCK_REP +ROLLBACK TRANSACTION; Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rep-cache.c URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rep-cache.c?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rep-cache.c (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rep-cache.c Sun Jun 14 20:58:10 2015 @@ -24,6 +24,7 @@ #include "svn_private_config.h" +#include "cached_data.h" #include "fs_fs.h" #include "fs.h" #include "rep-cache.h" @@ -273,6 +274,8 @@ svn_fs_fs__get_rep_reference(representat (*rep)->item_index = svn_sqlite__column_int64(stmt, 1); (*rep)->size = svn_sqlite__column_int64(stmt, 2); (*rep)->expanded_size = svn_sqlite__column_int64(stmt, 3); + + SVN_ERR(svn_fs_fs__fixup_expanded_size(fs, *rep, pool)); } else *rep = NULL; @@ -371,9 +374,13 @@ svn_fs_fs__del_rep_reference(svn_fs_t *f return SVN_NO_ERROR; } -svn_error_t * -svn_fs_fs__lock_rep_cache(svn_fs_t *fs, - apr_pool_t *pool) +/* Start a transaction to take an SQLite reserved lock that prevents + other writes. + + See unlock_rep_cache(). */ +static svn_error_t * +lock_rep_cache(svn_fs_t *fs, + apr_pool_t *pool) { fs_fs_data_t *ffd = fs->fsap_data; @@ -384,3 +391,31 @@ svn_fs_fs__lock_rep_cache(svn_fs_t *fs, return SVN_NO_ERROR; } + +/* End the transaction started by lock_rep_cache(). */ +static svn_error_t * +unlock_rep_cache(svn_fs_t *fs, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + + SVN_ERR_ASSERT(ffd->rep_cache_db); /* was opened by lock_rep_cache() */ + + SVN_ERR(svn_sqlite__exec_statements(ffd->rep_cache_db, STMT_UNLOCK_REP)); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_fs__with_rep_cache_lock(svn_fs_t *fs, + svn_error_t *(*body)(void *, + apr_pool_t *), + void *baton, + apr_pool_t *pool) +{ + svn_error_t *err; + + SVN_ERR(lock_rep_cache(fs, pool)); + err = body(baton, pool); + return svn_error_compose_create(err, unlock_rep_cache(fs, pool)); +} Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rep-cache.h URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rep-cache.h?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rep-cache.h (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rep-cache.h Sun Jun 14 20:58:10 2015 @@ -87,10 +87,14 @@ svn_fs_fs__del_rep_reference(svn_fs_t *f apr_pool_t *pool); /* Start a transaction to take an SQLite reserved lock that prevents - other writes. */ + other writes, call BODY, end the transaction, and return what BODY returned. + */ svn_error_t * -svn_fs_fs__lock_rep_cache(svn_fs_t *fs, - apr_pool_t *pool); +svn_fs_fs__with_rep_cache_lock(svn_fs_t *fs, + svn_error_t *(*body)(void *baton, + apr_pool_t *pool), + void *baton, + apr_pool_t *pool); #ifdef __cplusplus } Modified: subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rev_file.c URL: http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rev_file.c?rev=1685464&r1=1685463&r2=1685464&view=diff ============================================================================== --- subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rev_file.c (original) +++ subversion/branches/fsx-1.10/subversion/libsvn_fs_fs/rev_file.c Sun Jun 14 20:58:10 2015 @@ -146,7 +146,7 @@ open_pack_or_rev_file(svn_fs_fs__revisio /* We may have to *temporarily* enable write access. */ err = writable ? auto_make_writable(path, result_pool, scratch_pool) - : SVN_NO_ERROR; + : SVN_NO_ERROR; /* open the revision file in buffered r/o or r/w mode */ if (!err) @@ -259,6 +259,7 @@ svn_fs_fs__auto_read_footer(svn_fs_fs__r SVN_ERR(svn_fs_fs__parse_footer(&file->l2p_offset, &file->l2p_checksum, &file->p2l_offset, &file->p2l_checksum, footer, file->start_revision, + filesize - footer_length - 1, file->pool)); file->footer_offset = filesize - footer_length - 1; }
