Modified: subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/index.h URL: http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/index.h?rev=1632906&r1=1632905&r2=1632906&view=diff ============================================================================== --- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/index.h (original) +++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/index.h Sun Oct 19 13:55:35 2014 @@ -48,43 +48,22 @@ #define SVN_FS_FS__ITEM_TYPE_ANY_REP 7 /* item is any representation. Only used in pre-format7. */ -/* (user visible) entry in the phys-to-log index. It describes a section - * of some packed / non-packed rev file as containing a specific item. - * There must be no overlapping / conflicting entries. - */ -typedef struct svn_fs_fs__p2l_entry_t -{ - /* offset of the first byte that belongs to the item */ - apr_off_t offset; - - /* length of the item in bytes */ - apr_off_t size; - - /* type of the item (see SVN_FS_FS__ITEM_TYPE_*) defines */ - unsigned type; - - /* modified FNV-1a checksum. 0 if unknown checksum */ - apr_uint32_t fnv1_checksum; - - /* item in that block */ - svn_fs_fs__id_part_t item; -} svn_fs_fs__p2l_entry_t; - /* Open / create a log-to-phys index file with the full file path name - * FILE_NAME. Return the open file in *PROTO_INDEX and use POOL for - * allocations. + * FILE_NAME. Return the open file in *PROTO_INDEX allocated in + * RESULT_POOL. */ svn_error_t * svn_fs_fs__l2p_proto_index_open(apr_file_t **proto_index, const char *file_name, - apr_pool_t *pool); + apr_pool_t *result_pool); /* Call this function before adding entries for the next revision to the - * log-to-phys index file in PROTO_INDEX. Use POOL for allocations. + * log-to-phys index file in PROTO_INDEX. Use SCRATCH_POOL for temporary + * allocations. */ svn_error_t * svn_fs_fs__l2p_proto_index_add_revision(apr_file_t *proto_index, - apr_pool_t *pool); + apr_pool_t *scratch_pool); /* Add a new mapping, ITEM_INDEX to the OFFSET, to log-to-phys index file * in PROTO_INDEX. Please note that mappings may be added in any order @@ -93,73 +72,83 @@ svn_fs_fs__l2p_proto_index_add_revision( * mark 'invalid' item indexes but that is already implied for all item * indexes not explicitly given a mapping. * - * Use POOL for allocations. + * Use SCRATCH_POOL for temporary allocations. */ svn_error_t * svn_fs_fs__l2p_proto_index_add_entry(apr_file_t *proto_index, apr_off_t offset, apr_uint64_t item_index, - apr_pool_t *pool); + apr_pool_t *scratch_pool); /* 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. Use POOL for allocations. + * 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. */ svn_error_t * -svn_fs_fs__l2p_index_append(svn_fs_t *fs, +svn_fs_fs__l2p_index_append(svn_checksum_t **checksum, + svn_fs_t *fs, apr_file_t *index_file, const char *proto_file_name, svn_revnum_t revision, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* Open / create a phys-to-log index file with the full file path name - * FILE_NAME. Return the open file in *PROTO_INDEX and use POOL for - * allocations. + * FILE_NAME. Return the open file in *PROTO_INDEX allocated in + * RESULT_POOL. */ svn_error_t * svn_fs_fs__p2l_proto_index_open(apr_file_t **proto_index, const char *file_name, - apr_pool_t *pool); + apr_pool_t *result_pool); /* Add a new mapping ENTRY to the phys-to-log index file in PROTO_INDEX. * The entries must be added in ascending offset order and must not leave * intermittent ranges uncovered. The revision value in ENTRY may be - * SVN_INVALID_REVISION. Use POOL for allocations. + * SVN_INVALID_REVISION. Use SCRATCH_POOL for temporary allocations. */ svn_error_t * svn_fs_fs__p2l_proto_index_add_entry(apr_file_t *proto_index, svn_fs_fs__p2l_entry_t *entry, - apr_pool_t *pool); + apr_pool_t *scratch_pool); /* Set *NEXT_OFFSET to the first offset behind the last entry in the * phys-to-log proto index file PROTO_INDEX. This will be 0 for empty - * index files. Use POOL for temporary allocations. + * index files. Use SCRATCH_POOL for temporary allocations. */ svn_error_t * svn_fs_fs__p2l_proto_index_next_offset(apr_off_t *next_offset, apr_file_t *proto_index, - apr_pool_t *pool); + apr_pool_t *scratch_pool); /* Use the proto index file stored at PROTO_FILE_NAME, construct the final * phys-to-log index and append it to INDEX_FILE. Entries without a valid * revision will be assigned to the REVISION given here. - * Use POOL for allocations. + * + * Return the MD5 checksum of the on-disk index data in *CHECKSUM, allocated + * in RESULT_POOL. Use SCRATCH_POOL for temporary allocations. */ svn_error_t * -svn_fs_fs__p2l_index_append(svn_fs_t *fs, +svn_fs_fs__p2l_index_append(svn_checksum_t **checksum, + svn_fs_t *fs, apr_file_t *index_file, const char *proto_file_name, svn_revnum_t revision, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* Use the phys-to-log mapping files in FS to build a list of entries * that (at least partly) overlap with the range given by BLOCK_START * offset and BLOCK_SIZE in the rep / pack file containing REVISION. - * Return the array in *ENTRIES with svn_fs_fs__p2l_entry_t as elements. - * REV_FILE determines whether to access single rev or pack file data. - * If that is not available anymore (neither in cache nor on disk), - * return an error. Use POOL for allocations. + * Return the array in *ENTRIES with svn_fs_fs__p2l_entry_t as elements, + * allocated in RESULT_POOL. REV_FILE determines whether to access single + * rev or pack file data. If that is not available anymore (neither in + * cache nor on disk), return an error. Use SCRATCH_POOL for temporary + * allocations. * * Note that (only) the first and the last mapping may cross a cluster * boundary. @@ -171,14 +160,16 @@ svn_fs_fs__p2l_index_lookup(apr_array_he svn_revnum_t revision, apr_off_t block_start, apr_off_t block_size, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* Use the phys-to-log mapping files in FS to return the entry for the * item starting at global OFFSET in the rep file containing REVISION in - * *ENTRY. Sets *ENTRY to NULL if no item starts at exactly that offset. - * REV_FILE determines whether to access single rev or pack file data. - * If that is not available anymore (neither in cache nor on disk), - * return an error. Use POOL for allocations. + * *ENTRY, allocated in RESULT_POOL. Sets *ENTRY to NULL if no item starts + * at exactly that offset. REV_FILE determines whether to access single + * rev or pack file data. If that is not available anymore (neither in + * cache nor on disk), return an error. Use SCRATCH_POOL for temporary + * allocations. */ svn_error_t * svn_fs_fs__p2l_entry_lookup(svn_fs_fs__p2l_entry_t **entry, @@ -186,7 +177,8 @@ svn_fs_fs__p2l_entry_lookup(svn_fs_fs__p svn_fs_fs__revision_file_t *rev_file, svn_revnum_t revision, apr_off_t offset, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* For ITEM_INDEX within REV in FS, return the position in the respective * rev or pack file in *ABSOLUTE_POSITION. If TXN_ID is not NULL, return @@ -197,7 +189,7 @@ svn_fs_fs__p2l_entry_lookup(svn_fs_fs__p * If that is not available anymore (neither in cache nor on disk), re-open * the rev / pack file and retry to open the index file. For anything but * committed log addressed revisions, REV_FILE may be NULL. - * Use POOL for allocations. + * Use SCRATCH_POOL for temporary allocations. */ svn_error_t * svn_fs_fs__item_offset(apr_off_t *absolute_position, @@ -206,32 +198,34 @@ svn_fs_fs__item_offset(apr_off_t *absolu svn_revnum_t revision, const svn_fs_fs__id_part_t *txn_id, apr_uint64_t item_index, - apr_pool_t *pool); + apr_pool_t *scratch_pool); /* Use the log-to-phys indexes in FS to determine the maximum item indexes * assigned to revision START_REV to START_REV + COUNT - 1. That is a * close upper limit to the actual number of items in the respective revs. - * Return the results in *MAX_IDS, allocated in POOL. + * Return the results in *MAX_IDS, allocated in RESULT_POOL. + * Use SCRATCH_POOL for temporary allocations. */ svn_error_t * svn_fs_fs__l2p_get_max_ids(apr_array_header_t **max_ids, svn_fs_t *fs, svn_revnum_t start_rev, apr_size_t count, - apr_pool_t *pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* In *OFFSET, return the last OFFSET in the pack / rev file containing. * REV_FILE determines whether to access single rev or pack file data. * If that is not available anymore (neither in cache nor on disk), re-open * the rev / pack file and retry to open the index file. - * Use POOL for allocations. + * Use SCRATCH_POOL for temporary allocations. */ svn_error_t * svn_fs_fs__p2l_get_max_offset(apr_off_t *offset, svn_fs_t *fs, svn_fs_fs__revision_file_t *rev_file, svn_revnum_t revision, - apr_pool_t *pool); + apr_pool_t *scratch_pool); /* Index (re-)creation utilities. */
Modified: subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/lock.c URL: http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/lock.c?rev=1632906&r1=1632905&r2=1632906&view=diff ============================================================================== --- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/lock.c (original) +++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/lock.c Sun Oct 19 13:55:35 2014 @@ -386,7 +386,8 @@ add_to_digest(const char *fs_path, const char *index_digest_path; apr_hash_t *children; svn_lock_t *lock; - int i, original_count; + int i; + unsigned int original_count; SVN_ERR(digest_path_from_path(&index_digest_path, fs_path, index_path, pool)); Modified: subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/low_level.c URL: http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/low_level.c?rev=1632906&r1=1632905&r2=1632906&view=diff ============================================================================== --- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/low_level.c (original) +++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/low_level.c Sun Oct 19 13:55:35 2014 @@ -27,6 +27,7 @@ #include "private/svn_sorts_private.h" #include "private/svn_string_private.h" #include "private/svn_subr_private.h" +#include "private/svn_fspath.h" #include "../libsvn_fs/fs-loader.h" @@ -69,6 +70,36 @@ * various flags. */ #define MAX_CHANGE_LINE_LEN FSFS_MAX_PATH_LEN + 256 +/* Convert the C string in *TEXT to a revision number and return it in *REV. + * Overflows, negative values other than -1 and terminating characters other + * than 0x20 or 0x0 will cause an error. Set *TEXT to the first char after + * the initial separator or to EOS. + */ +static svn_error_t * +parse_revnum(svn_revnum_t *rev, + const char **text) +{ + const char *string = *text; + if ((string[0] == '-') && (string[1] == '1')) + { + *rev = SVN_INVALID_REVNUM; + string += 2; + } + else + { + SVN_ERR(svn_revnum_parse(rev, string, &string)); + } + + if (*string == ' ') + ++string; + else if (*string != '\0') + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid character in revision number")); + + *text = string; + return SVN_NO_ERROR; +} + svn_error_t * svn_fs_fs__parse_revision_trailer(apr_off_t *root_offset, apr_off_t *changes_offset, @@ -160,38 +191,71 @@ svn_fs_fs__unparse_revision_trailer(apr_ svn_error_t * svn_fs_fs__parse_footer(apr_off_t *l2p_offset, + svn_checksum_t **l2p_checksum, apr_off_t *p2l_offset, + svn_checksum_t **p2l_checksum, svn_stringbuf_t *footer, - svn_revnum_t rev) + svn_revnum_t rev, + apr_pool_t *result_pool) { apr_int64_t val; + char *last_str = footer->data; - /* Split the footer into the 2 number strings. */ - char *seperator = strchr(footer->data, ' '); - if (!seperator) - return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, - _("Revision file (r%ld) has corrupt footer"), - rev); - *seperator = '\0'; + /* 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")); - /* Convert offset values. */ - SVN_ERR(svn_cstring_atoi64(&val, footer->data)); + SVN_ERR(svn_cstring_atoi64(&val, str)); *l2p_offset = (apr_off_t)val; - SVN_ERR(svn_cstring_atoi64(&val, seperator + 1)); + + /* 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")); + + SVN_ERR(svn_checksum_parse_hex(l2p_checksum, svn_checksum_md5, str, + result_pool)); + + /* 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")); + + SVN_ERR(svn_cstring_atoi64(&val, str)); *p2l_offset = (apr_off_t)val; + /* 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")); + + SVN_ERR(svn_checksum_parse_hex(p2l_checksum, svn_checksum_md5, str, + result_pool)); + return SVN_NO_ERROR; } svn_stringbuf_t * svn_fs_fs__unparse_footer(apr_off_t l2p_offset, + svn_checksum_t *l2p_checksum, apr_off_t p2l_offset, - apr_pool_t *result_pool) + svn_checksum_t *p2l_checksum, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { return svn_stringbuf_createf(result_pool, - "%" APR_OFF_T_FMT " %" APR_OFF_T_FMT, + "%" APR_OFF_T_FMT " %s %" APR_OFF_T_FMT " %s", l2p_offset, - p2l_offset); + svn_checksum_to_cstring(l2p_checksum, + scratch_pool), + p2l_offset, + svn_checksum_to_cstring(p2l_checksum, + scratch_pool)); } /* Read the next entry in the changes record from file FILE and store @@ -228,7 +292,7 @@ read_change(change_t **change_p, return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Invalid changes line in rev-file")); - info->node_rev_id = svn_fs_fs__id_parse(str, result_pool); + SVN_ERR(svn_fs_fs__id_parse(&info->node_rev_id, str, result_pool)); if (info->node_rev_id == NULL) return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Invalid changes line in rev-file")); @@ -324,7 +388,9 @@ read_change(change_t **change_p, } /* Get the mergeinfo-mod flag if given. Otherwise, the next thing - is the path starting with a slash. */ + is the path starting with a slash. Also, we must initialize the + flag explicitly because 0 is not valid for a svn_tristate_t. */ + info->mergeinfo_mod = svn_tristate_unknown; if (*last_str != '/') { str = svn_cstring_tokenize(" ", &last_str); @@ -346,8 +412,12 @@ read_change(change_t **change_p, _("Invalid mergeinfo-mod flag in rev-file")); } } - + /* Get the changed path. */ + if (!svn_fspath__is_canonical(last_str)) + return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, + _("Invalid path in changes line")); + change->path.len = strlen(last_str); change->path.data = apr_pstrdup(result_pool, last_str); @@ -362,15 +432,11 @@ read_change(change_t **change_p, else { last_str = line->data; - str = svn_cstring_tokenize(" ", &last_str); - if (! str) - return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, - _("Invalid changes line in rev-file")); - info->copyfrom_rev = SVN_STR_TO_REV(str); + SVN_ERR(parse_revnum(&info->copyfrom_rev, (const char **)&last_str)); - if (! last_str) + if (!svn_fspath__is_canonical(last_str)) return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, - _("Invalid changes line in rev-file")); + _("Invalid copy-from path in changes line")); info->copyfrom_path = apr_pstrdup(result_pool, last_str); } @@ -437,11 +503,11 @@ svn_fs_fs__read_changes_incrementally(sv return SVN_NO_ERROR; } -/* Write a single change entry, path PATH, change CHANGE, and copyfrom - string COPYFROM, into the file specified by FILE. Only include the - node kind field if INCLUDE_NODE_KIND is true. Only include the - mergeinfo-mod field if INCLUDE_MERGEINFO_MODS is true. All temporary - allocations are in SCRATCH_POOL. */ +/* Write a single change entry, path PATH, change CHANGE, to STREAM. + + Only include the node kind field if INCLUDE_NODE_KIND is true. Only + include the mergeinfo-mod field if INCLUDE_MERGEINFO_MODS is true. + All temporary allocations are in SCRATCH_POOL. */ static svn_error_t * write_change_entry(svn_stream_t *stream, const char *path, @@ -534,13 +600,19 @@ svn_fs_fs__write_changes(svn_stream_t *s svn_boolean_t include_node_kinds = ffd->format >= SVN_FS_FS__MIN_KIND_IN_CHANGED_FORMAT; svn_boolean_t include_mergeinfo_mods = - ffd->format >= SVN_FS_FS__MIN_MERGEINFO_IN_CHANGES_FORMAT; + ffd->format >= SVN_FS_FS__MIN_MERGEINFO_IN_CHANGED_FORMAT; apr_array_header_t *sorted_changed_paths; int i; /* For the sake of the repository administrator sort the changes so that the final file is deterministic and repeatable, however the - rest of the FSFS code doesn't require any particular order here. */ + rest of the FSFS code doesn't require any particular order here. + + Also, this sorting is only effective in writing all entries with + a single call as write_final_changed_path_info() does. For the + list being written incrementally during transaction, we actually + *must not* change the order of entries from different calls. + */ sorted_changed_paths = svn_sort__hash(changes, svn_sort_compare_items_lexically, scratch_pool); @@ -584,8 +656,8 @@ read_header_block(apr_hash_t **headers, { svn_stringbuf_t *header_str; const char *name, *value; - apr_ssize_t i = 0; - apr_ssize_t name_len; + apr_size_t i = 0; + apr_size_t name_len; svn_boolean_t eof; SVN_ERR(svn_stream_readline(stream, &header_str, "\n", &eof, @@ -609,13 +681,10 @@ read_header_block(apr_hash_t **headers, name = header_str->data; name_len = i; - /* Skip over the NULL byte and the space following it. */ - i += 2; - - if (i > header_str->len) + /* Check if we have enough data to parse. */ + if (i + 2 > header_str->len) { /* Restore the original line for the error. */ - i -= 2; header_str->data[i] = ':'; return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, _("Found malformed header '%s' in " @@ -623,6 +692,9 @@ read_header_block(apr_hash_t **headers, header_str->data); } + /* Skip over the NULL byte and the space following it. */ + i += 2; + value = header_str->data + i; /* header_str is safely in our pool, so we can use bits of it as @@ -649,12 +721,7 @@ svn_fs_fs__parse_representation(represen rep = apr_pcalloc(result_pool, sizeof(*rep)); *rep_p = rep; - str = svn_cstring_tokenize(" ", &string); - if (str == NULL) - return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, - _("Malformed text representation offset line in node-rev")); - - rep->revision = SVN_STR_TO_REV(str); + SVN_ERR(parse_revnum(&rep->revision, (const char **)&string)); /* initialize transaction info (never stored) */ svn_fs_fs__id_txn_reset(&rep->txn_id); @@ -797,7 +864,7 @@ svn_fs_fs__read_noderev(node_revision_t SVN_ERR(svn_stream_close(stream)); - noderev->id = svn_fs_fs__id_parse(value, result_pool); + SVN_ERR(svn_fs_fs__id_parse(&noderev->id, value, result_pool)); noderev_id = value; /* for error messages later */ /* Read the type. */ @@ -848,13 +915,19 @@ svn_fs_fs__read_noderev(node_revision_t } else { + if (!svn_fspath__is_canonical(value)) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + _("Non-canonical cpath field in node-rev '%s'"), + noderev_id); + noderev->created_path = apr_pstrdup(result_pool, value); } /* Get the predecessor ID. */ value = svn_hash_gets(headers, HEADER_PRED); if (value) - noderev->predecessor_id = svn_fs_fs__id_parse(value, result_pool); + SVN_ERR(svn_fs_fs__id_parse(&noderev->predecessor_id, value, + result_pool)); /* Get the copyroot. */ value = svn_hash_gets(headers, HEADER_COPYROOT); @@ -865,17 +938,9 @@ svn_fs_fs__read_noderev(node_revision_t } else { - char *str; - - str = svn_cstring_tokenize(" ", &value); - if (str == NULL) - return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, - _("Malformed copyroot line in node-rev '%s'"), - noderev_id); + SVN_ERR(parse_revnum(&noderev->copyroot_rev, (const char **)&value)); - noderev->copyroot_rev = SVN_STR_TO_REV(str); - - if (*value == '\0') + if (!svn_fspath__is_canonical(value)) return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, _("Malformed copyroot line in node-rev '%s'"), noderev_id); @@ -891,13 +956,7 @@ svn_fs_fs__read_noderev(node_revision_t } else { - char *str = svn_cstring_tokenize(" ", &value); - if (str == NULL) - return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, - _("Malformed copyfrom line in node-rev '%s'"), - noderev_id); - - noderev->copyfrom_rev = SVN_STR_TO_REV(str); + SVN_ERR(parse_revnum(&noderev->copyfrom_rev, (const char **)&value)); if (*value == 0) return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, @@ -1088,10 +1147,7 @@ svn_fs_fs__read_rep_header(svn_fs_fs__re if (! str || (strcmp(str, REP_DELTA) != 0)) goto error; - str = svn_cstring_tokenize(" ", &last_str); - if (! str) - goto error; - (*header)->base_revision = SVN_STR_TO_REV(str); + SVN_ERR(parse_revnum(&(*header)->base_revision, (const char **)&last_str)); str = svn_cstring_tokenize(" ", &last_str); if (! str) Modified: subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/low_level.h URL: http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/low_level.h?rev=1632906&r1=1632905&r2=1632906&view=diff ============================================================================== --- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/low_level.h (original) +++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/low_level.h Sun Oct 19 13:55:35 2014 @@ -64,25 +64,35 @@ svn_fs_fs__unparse_revision_trailer(apr_ /* Given the format 7+ revision / pack FOOTER, parse it destructively * and return the start offsets of the index data in *L2P_OFFSET and - * *P2L_OFFSET, respectively. + * *P2L_OFFSET, respectively. Also, return the expected checksums in + * in *L2P_CHECKSUM and *P2L_CHECKSUM. * * Note that REV is only used to construct nicer error objects that - * mention this revision. + * mention this revision. Allocate the checksums in RESULT_POOL. */ svn_error_t * svn_fs_fs__parse_footer(apr_off_t *l2p_offset, + svn_checksum_t **l2p_checksum, apr_off_t *p2l_offset, + svn_checksum_t **p2l_checksum, svn_stringbuf_t *footer, - svn_revnum_t rev); + svn_revnum_t rev, + apr_pool_t *result_pool); -/* Given the offset of the L2P index data in L2P_OFFSET and the offset of - * the P2L index data in P2L_OFFSET, return the corresponding format 7+ - * revision / pack file footer. Allocate it in RESULT_POOL. +/* Given the offset of the L2P index data in L2P_OFFSET, the content + * checksum in L2P_CHECKSUM and the offset plus checksum of the P2L + * index data in P2L_OFFSET and P2L_CHECKSUM. + * + * Return the corresponding format 7+ revision / pack file footer. + * Allocate it in RESULT_POOL and use SCRATCH_POOL for temporary. */ svn_stringbuf_t * svn_fs_fs__unparse_footer(apr_off_t l2p_offset, + svn_checksum_t *l2p_checksum, apr_off_t p2l_offset, - apr_pool_t *result_pool); + svn_checksum_t *p2l_checksum, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* Read all the changes from STREAM and store them in *CHANGES, allocated in RESULT_POOL. Do temporary allocations in SCRATCH_POOL. */ @@ -112,8 +122,9 @@ svn_fs_fs__read_changes_incrementally(sv /* Write the changed path info from CHANGES in filesystem FS to the output stream STREAM. You may call this function multiple time on - the same stream but the last call should set TERMINATE_LIST to write - an extra empty line that marks the end of the changed paths list. + the same stream. If you are writing to a (proto-)revision file, + the last call must set TERMINATE_LIST to write an extra empty line + that marks the end of the changed paths list. Perform temporary allocations in SCRATCH_POOL. */ svn_error_t * Modified: subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/pack.c URL: http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/pack.c?rev=1632906&r1=1632905&r2=1632906&view=diff ============================================================================== --- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/pack.c (original) +++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/pack.c Sun Oct 19 13:55:35 2014 @@ -479,7 +479,7 @@ copy_item_to_temp(pack_context_t *contex svn_fs_fs__p2l_entry_t *new_entry = apr_pmemdup(context->info_pool, entry, sizeof(*entry)); new_entry->offset = 0; - SVN_ERR(svn_io_file_seek(temp_file, SEEK_CUR, &new_entry->offset, pool)); + SVN_ERR(svn_io_file_seek(temp_file, APR_CUR, &new_entry->offset, pool)); APR_ARRAY_PUSH(entries, svn_fs_fs__p2l_entry_t *) = new_entry; SVN_ERR(copy_file_data(context, temp_file, rev_file, entry->size, pool)); @@ -567,7 +567,7 @@ copy_rep_to_temp(pack_context_t *context * store it in CONTEXT */ entry = apr_pmemdup(context->info_pool, entry, sizeof(*entry)); entry->offset = 0; - SVN_ERR(svn_io_file_seek(context->reps_file, SEEK_CUR, &entry->offset, + SVN_ERR(svn_io_file_seek(context->reps_file, APR_CUR, &entry->offset, pool)); add_item_rep_mapping(context, entry); @@ -589,7 +589,7 @@ copy_rep_to_temp(pack_context_t *context } /* copy the whole rep (including header!) to our temp file */ - SVN_ERR(svn_io_file_seek(rev_file, SEEK_SET, &source_offset, pool)); + SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &source_offset, pool)); SVN_ERR(copy_file_data(context, context->reps_file, rev_file, entry->size, pool)); @@ -723,12 +723,12 @@ copy_node_to_temp(pack_context_t *contex * store it in CONTEXT */ entry = apr_pmemdup(context->info_pool, entry, sizeof(*entry)); entry->offset = 0; - SVN_ERR(svn_io_file_seek(context->reps_file, SEEK_CUR, + SVN_ERR(svn_io_file_seek(context->reps_file, APR_CUR, &entry->offset, pool)); add_item_rep_mapping(context, entry); /* copy the noderev to our temp file */ - SVN_ERR(svn_io_file_seek(rev_file, SEEK_SET, &source_offset, pool)); + SVN_ERR(svn_io_file_seek(rev_file, APR_SET, &source_offset, pool)); SVN_ERR(copy_file_data(context, context->reps_file, rev_file, entry->size, pool)); @@ -1112,7 +1112,7 @@ store_item(pack_context_t *context, /* select the item in the source file and copy it into the target * pack file */ - SVN_ERR(svn_io_file_seek(temp_file, SEEK_SET, &item->offset, pool)); + SVN_ERR(svn_io_file_seek(temp_file, APR_SET, &item->offset, pool)); SVN_ERR(copy_file_data(context, context->pack_file, temp_file, item->size, pool)); @@ -1165,7 +1165,6 @@ copy_reps_from_temp(pack_context_t *cont { apr_pool_t *iterpool = svn_pool_create(pool); apr_array_header_t *path_order = context->path_order; - apr_array_header_t *parts = apr_array_make(pool, 16, sizeof(void*)); int i; /* copy items in path order. */ @@ -1185,9 +1184,6 @@ copy_reps_from_temp(pack_context_t *cont SVN_ERR(store_item(context, temp_file, node_part, iterpool)); if (rep_part) SVN_ERR(store_item(context, temp_file, rep_part, iterpool)); - - /* processed all items */ - apr_array_clear(parts); } svn_pool_destroy(iterpool); @@ -1311,7 +1307,8 @@ pack_range(pack_context_t *context, SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, context->fs, rev_file, revision, offset, - ffd->p2l_page_size, iterpool)); + ffd->p2l_page_size, iterpool, + iterpool)); for (i = 0; i < entries->nelts; ++i) { @@ -1329,7 +1326,7 @@ pack_range(pack_context_t *context, offset = entry->offset; if (offset < rev_file->l2p_offset) { - SVN_ERR(svn_io_file_seek(rev_file->file, SEEK_SET, &offset, + SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &offset, iterpool2)); if (entry->type == SVN_FS_FS__ITEM_TYPE_CHANGES) @@ -1445,7 +1442,8 @@ append_revision(pack_context_t *context, svn_pool_clear(iterpool); SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, context->fs, rev_file, context->start_rev, offset, - ffd->p2l_page_size, iterpool)); + ffd->p2l_page_size, iterpool, + iterpool)); for (i = 0; i < entries->nelts; ++i) { @@ -1531,7 +1529,7 @@ pack_log_addressed(svn_fs_t *fs, /* phase 1: determine the size of the revisions to pack */ SVN_ERR(svn_fs_fs__l2p_get_max_ids(&max_ids, fs, shard_rev, context.shard_end_rev - shard_rev, - pool)); + pool, pool)); /* pack revisions in ranges that don't exceed MAX_MEM */ for (i = 0; i < max_ids->nelts; ++i) Modified: subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/recovery.c URL: http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/recovery.c?rev=1632906&r1=1632905&r2=1632906&view=diff ============================================================================== --- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/recovery.c (original) +++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/recovery.c Sun Oct 19 13:55:35 2014 @@ -216,7 +216,7 @@ recover_find_max_ids(svn_fs_t *fs, char *str_val; char *str; svn_node_kind_t kind; - svn_fs_id_t *id; + const svn_fs_id_t *id; const svn_fs_fs__id_part_t *rev_item; apr_uint64_t node_id, copy_id; apr_off_t child_dir_offset; @@ -246,7 +246,7 @@ recover_find_max_ids(svn_fs_t *fs, return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, _("Directory entry corrupt")); - id = svn_fs_fs__id_parse(str, iterpool); + SVN_ERR(svn_fs_fs__id_parse(&id, str, iterpool)); rev_item = svn_fs_fs__id_rev_item(id); if (rev_item->revision != rev) @@ -348,6 +348,11 @@ recover_body(void *baton, apr_pool_t *po /* Revert revprop generation file into a consistent state. */ SVN_ERR(svn_fs_fs__reset_revprop_generation_file(fs, pool)); + /* The admin may have created a plain copy of this repo before attempting + to recover it (hotcopy may or may not work with corrupted repos). + Bump the instance ID. */ + SVN_ERR(svn_fs_fs__set_uuid(fs, fs->uuid, NULL, pool)); + /* We need to know the largest revision in the filesystem. */ SVN_ERR(recover_get_largest_revision(fs, &max_rev, pool)); Modified: subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/rev_file.c URL: http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/rev_file.c?rev=1632906&r1=1632905&r2=1632906&view=diff ============================================================================== --- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/rev_file.c (original) +++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/rev_file.c Sun Oct 19 13:55:35 2014 @@ -31,6 +31,8 @@ #include "private/svn_io_private.h" #include "svn_private_config.h" +/* Initialize the *FILE structure for REVISION in filesystem FS. Set its + * pool member to the provided POOL. */ static void init_revision_file(svn_fs_fs__revision_file_t *file, svn_fs_t *fs, @@ -40,9 +42,7 @@ init_revision_file(svn_fs_fs__revision_f fs_fs_data_t *ffd = fs->fsap_data; file->is_packed = svn_fs_fs__is_packed_rev(fs, revision); - file->start_revision = revision < ffd->min_unpacked_rev - ? revision - (revision % ffd->max_files_per_dir) - : revision; + file->start_revision = svn_fs_fs__packed_base_rev(fs, revision); file->file = NULL; file->stream = NULL; @@ -50,7 +50,9 @@ init_revision_file(svn_fs_fs__revision_f file->l2p_stream = NULL; file->block_size = ffd->block_size; file->l2p_offset = -1; + file->l2p_checksum = NULL; file->p2l_offset = -1; + file->p2l_checksum = NULL; file->footer_offset = -1; file->pool = pool; } @@ -176,6 +178,7 @@ open_pack_or_rev_file(svn_fs_fs__revisio /* We failed for the first time. Refresh cache & retry. */ SVN_ERR(svn_fs_fs__update_min_unpacked_rev(fs, scratch_pool)); + file->start_revision = svn_fs_fs__packed_base_rev(fs, rev); retry = TRUE; } @@ -253,8 +256,10 @@ svn_fs_fs__auto_read_footer(svn_fs_fs__r footer->data[footer->len] = '\0'; /* Extract index locations. */ - SVN_ERR(svn_fs_fs__parse_footer(&file->l2p_offset, &file->p2l_offset, - footer, file->start_revision)); + SVN_ERR(svn_fs_fs__parse_footer(&file->l2p_offset, &file->l2p_checksum, + &file->p2l_offset, &file->p2l_checksum, + footer, file->start_revision, + file->pool)); file->footer_offset = filesize - footer_length - 1; } Modified: subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/rev_file.h URL: http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/rev_file.h?rev=1632906&r1=1632905&r2=1632906&view=diff ============================================================================== --- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/rev_file.h (original) +++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/rev_file.h Sun Oct 19 13:55:35 2014 @@ -72,11 +72,19 @@ typedef struct svn_fs_fs__revision_file_ * has not been called, yet. */ apr_off_t l2p_offset; + /* MD5 checksum on the whole on-disk representation of the L2P index. + * NULL if svn_fs_fs__auto_read_footer has not been called, yet. */ + svn_checksum_t *l2p_checksum; + /* Offset within FILE at which the L2P index ends and the P2L index * data starts. Greater than L2P_OFFSET. -1 if svn_fs_fs__auto_read_footer * has not been called, yet. */ apr_off_t p2l_offset; + /* MD5 checksum on the whole on-disk representation of the P2L index. + * NULL if svn_fs_fs__auto_read_footer has not been called, yet. */ + svn_checksum_t *p2l_checksum; + /* Offset within FILE at which the P2L index ends and the footer starts. * Greater than P2L_OFFSET. -1 if svn_fs_fs__auto_read_footer has not * been called, yet. */ Modified: subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/structure URL: http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/structure?rev=1632906&r1=1632905&r2=1632906&view=diff ============================================================================== --- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/structure (original) +++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/structure Sun Oct 19 13:55:35 2014 @@ -37,16 +37,16 @@ repository) is: <shard>.pack/ Pack directory, if the repo has been packed (see below) pack Pack file, if the repository has been packed (see below) manifest Pack manifest file, if a pack file exists (see below) - pack.l2p Log-to-phys index file (format 7+, see below) - pack.p2l Phys-to-log index file (format 7+, see below) revprops/ Subdirectory containing rev-props <shard>/ Shard directory, if sharding is in use (see below) <revnum> File containing rev-props for <revnum> <shard>.pack/ Pack directory, if the repo has been packed (see below) <rev>.<count> Pack file, if the repository has been packed (see below) manifest Pack manifest file, if a pack file exists (see below) - revprops.db SQLite database of the packed revision properties - transactions/ Subdirectory containing transactions + revprops.db SQLite database of the packed revprops (format 5 only) + transactions/ Subdirectory containing transactions (format 1 to 6) + <txnid>.txn/ Directory containing transaction <txnid> + transactions-la/ Subdirectory containing transactions (format 7+) <txnid>.txn/ Directory containing transaction <txnid> txn-protorevs/ Subdirectory containing transaction proto-revision files <txnid>.rev Proto-revision file for transaction <txnid> @@ -66,7 +66,7 @@ repository) is: format File containing the format number of this filesystem fsfs.conf Configuration file min-unpacked-rev File containing the oldest revision not in a pack file - min-unpacked-revprop File containing the oldest revision of unpacked revprop + min-unpacked-revprop Same for revision properties (format 5 only) rep-cache.db SQLite database mapping rep checksums to locations revprop-generation File containing the current revprop generation (f. 7+) @@ -328,8 +328,10 @@ the pack file. The offsets are stored a a newline character. Revision pack files using logical addressing don't use manifest files but -index files instead. The revisions inside a pack file will also get -interleaved to reduce I/O for typical access patterns. +appends index data to the revision contents. The revisions inside a pack +file will also get interleaved to reduce I/O for typical access patterns. +There is no structural difference between packed and non-packed revision +files in that mode. Packing revision properties (format 5: SQLite) @@ -546,7 +548,8 @@ A revision file contains a concatenation * Text and property representations * Node-revisions * The changed-path data - * Two offsets at the very end (physical addressing mode only) + * Index data (logical addressing only) + * Revision / pack file footer (logical addressing only) A representation begins with a line containing either "PLAIN\n" or "DELTA\n" or "DELTA <rev> <item_index> <length>\n", where <rev>, @@ -624,7 +627,7 @@ Starting with FS format 4, <action> may "dir") of the node, after a hyphen; for example, an added directory may be represented as "add-dir". -Before with FS format 7, <mergeinfo-mod> flag is not available. It may +Prior to FS format 7, <mergeinfo-mod> flag is not available. It may also be missing in revisions upgraded from pre-f7 formats. In physical addressing mode, at the very end of a rev file is a pair of @@ -632,6 +635,15 @@ lines containing "\n<root-offset> <cp-of the offset of the root directory node revision and <cp-offset> is the offset of the changed-path data. +In logical addressing mode, the revision footer has the form + + <l2p offset> <l2p checksum> <p2l offset> <p2l checksum><terminal byte> + +The terminal byte contains the length (as plain 8 bit value) of the footer +excluding that length byte. The first offset is the start of the log-to- +phys index, followed by the digest of the MD5 checksum over its content. +The other pair gives the same of for the phys-to-log index. + All numbers in the rev file format are unsigned and are represented as ASCII decimal. @@ -752,11 +764,12 @@ digests, too, so you would simply iterat consult the files they reference for lock information. -Index files ------------ +Index Data +---------- Format 7 introduces logical addressing that requires item indexes to be translated / mapped to physical rev / pack file offsets. +These indexes are appended to the respective rev / pack file. Details of the binary format used by these index files can be found in structure-indexes. Modified: subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/structure-indexes URL: http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/structure-indexes?rev=1632906&r1=1632905&r2=1632906&view=diff ============================================================================== --- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/structure-indexes (original) +++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/structure-indexes Sun Oct 19 13:55:35 2014 @@ -1,17 +1,17 @@ -This file describes the design, data model, and file formats of FSFS -index files. +This file describes the design, data model, and storage formats of FSFS +index data. Design ====== -For each pack and each rev file using logical addressing, there is exactly -two index files. One, the log-to-phys index, maps the (rev, item_index) +Each pack and each rev file using logical addressing contains exactly +two index sections. One, the log-to-phys index, maps the (rev, item_index) pairs to absolute file offsets. The other, phys-to-log, is a reverse index that gives basic information on any file location. This is enough to read and cache any data without traversing DAGs. -Rev and pack files are immutable, so the same is true for index files. +Rev and pack files are immutable, so the same is true for index data. During a transaction or while packing a file, a proto index file gets written (actually, one log-to-phys and one phys-to-log). Its format is a simple concatenation of runtime structs and as such, an implementation @@ -52,7 +52,7 @@ and pre-defined item_index values. Encoding -------- -The final index file format is tuned for space and decoding efficiency. +The final index data format is tuned for space and decoding efficiency. Indexes are stored as a sequence of variable integers. The encoding is as follows: @@ -80,6 +80,14 @@ Most data is unsigned by nature but will signed integers. +Encoding in proto-index files +----------------------------- + +These have a much simpler encoding. Throughout the files, all records have +the same length (but different between L2P and P2L). All records contain +unsigned 64 bit integers only, stored in little endian byte order. + + Log-to-phys index ================= @@ -105,13 +113,13 @@ hierarchy: offset = offsets[item_index % page_size]; Different log-to-phys indexes in the same repository may have different - page sizes but within any given index file, the page size is the same - and immutable. + page sizes but within any given index, the page size is the same and + immutable. header: - <first revision> ... first revision covered by this index file - <revision count> ... number of revision covered by this index file + <first revision> ... first revision covered by this index + <revision count> ... number of revision covered by this index <page size> ... maximum number of entries per page <page table index> ... array, for each revision containing the index in <page table> of the first page that belongs to @@ -123,7 +131,7 @@ header: page table: <offset> ... absolute position of the page contents within the - index file + index <entry count> ... number of offset entries in the page. Must match <header>.<page size> unless this is the last page for the respective revision. @@ -140,10 +148,10 @@ page: pack file. This has <entry count> entries. -Index file format ------------------ +Index on-disk format +-------------------- - file := header revisions pages offsets + index := "L2P-INDEX\n" header revisions pages offsets header := u(<header>.<first revision>) \ u(<header>.<page size>) \ @@ -158,11 +166,13 @@ Index file format u(<header>.<page table>[k].<entry count>), for k in 0 .. s(<header>.<page table>)-1 - offsets := i(<header>.<page table>[k].<offsets>[0]) \ + offsets := page(k), + for k in 0 .. s(<header>.<page table>)-1 + + page(k) := i(<header>.<page table>[k].<offsets>[0]) \ i( <header>.<page table>[k].<offsets>[l] \ - <header>.<page table>[k].<offsets>[l - 1]), - for l in 1 .. s(<header>.<page table>[k].<entry count>)-1, - for k in 0 .. s(<header>.<page table>)-1 + for l in 1 .. s(<header>.<page table>[k].<entry count>)-1 u(x) ... unsigned int x in 7b/8b encoding i(x) ... signed int x in 7b/8b encoding @@ -190,7 +200,7 @@ at the beginning of the file is optional ... <eof> /* end of file. */ -All entries are pairs of 64 bit unsigned integers in machine endianess. +All entries are pairs of 64 bit unsigned integers in little endian order. Phys-to-log index @@ -217,7 +227,7 @@ hierarchy: offset is does not match any actual item start. To simplify the lookup, the last index page will have an "unused item" entry for the section behind EOF. Holes aren't allowed as well, i.e. every byte of the rev / - pack is expected to be covered by the index file. + pack is expected to be covered by the index. Also, there may be items stretching across page borders or even over multiple pages. The data model solves this issue by storing the item @@ -227,7 +237,7 @@ hierarchy: header: - <first revision> ... first revision covered by this index file + <first revision> ... first revision covered by this index <file size> ... size of the rev / pack file in bytes <page size> ... number of bytes in the rev / pack file covered by each index page @@ -253,10 +263,10 @@ entry: <item_index> ... item_index within that revision -Index file format ------------------ +Index on-disk format +-------------------- - file := header pages items + index := "P2L-INDEX\n" header pages items header := u(<header>.<first revision>) \ u(<header>.<file size>) \ @@ -293,7 +303,18 @@ Proto index file format ----------------------- The index will be created from a proto index file containing simple -instances of the in-memory representation of svn_fs_fs__p2l_entry_t. +instances of svn_fs_fs__p2l_entry_t with the following element order: + + item offset as uint64 + item size as uint64 + item type as uint64 + modified FNV1a checksum as uint64 + revision as uint64, with SVN_INVALID_REVNUM mapped to 0 + and revisions >= 0 stored as rev+1 + item index as uint64 + +All values are stored in little endian order. + Page table and header information, except start revision and page size, can easily be derived from that information. @@ -303,7 +324,7 @@ are not allowed; zero-length items are. In transactions, the final revision number may not be known when writing the proto index file (e.g. while still writing the proto rev file). Items with revision set to SVN_INVALID_REVNUM will therefore be automatically -updated when creating the index file. This is possible in conjunction +updated when creating the final index. This is possible in conjunction with rev files but not for pack files. Modified: subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/temp_serializer.c URL: http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/temp_serializer.c?rev=1632906&r1=1632905&r2=1632906&view=diff ============================================================================== --- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/temp_serializer.c (original) +++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/temp_serializer.c Sun Oct 19 13:55:35 2014 @@ -205,7 +205,7 @@ static svn_temp_serializer__context_t * serialize_dir(apr_array_header_t *entries, apr_pool_t *pool) { dir_data_t dir_data; - apr_size_t i = 0; + int i = 0; svn_temp_serializer__context_t *context; /* calculate sizes */ Modified: subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/transaction.c URL: http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/transaction.c?rev=1632906&r1=1632905&r2=1632906&view=diff ============================================================================== --- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/transaction.c (original) +++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/transaction.c Sun Oct 19 13:55:35 2014 @@ -409,7 +409,7 @@ auto_truncate_proto_rev(svn_fs_t *fs, if (indexed_length < actual_length) SVN_ERR(svn_io_file_trunc(proto_rev, indexed_length, pool)); else if (indexed_length > actual_length) - return svn_error_createf(SVN_ERR_FS_ITEM_INDEX_INCONSISTENT, + return svn_error_createf(SVN_ERR_FS_INDEX_INCONSISTENT, NULL, _("p2l proto index offset %s beyond proto" "rev file size %s for TXN %s"), @@ -621,40 +621,29 @@ unparse_dir_entries(apr_array_header_t * return SVN_NO_ERROR; } -/* Copy the contents of NEW_CHANGE into OLD_CHANGE assuming that both - belong to the same path. Allocate copies in POOL. +/* Return a deep copy of SOURCE and allocate it in RESULT_POOL. */ -static void -replace_change(svn_fs_path_change2_t *old_change, - const svn_fs_path_change2_t *new_change, - apr_pool_t *pool) -{ - /* An add at this point must be following a previous delete, - so treat it just like a replace. */ - old_change->node_kind = new_change->node_kind; - old_change->node_rev_id = svn_fs_fs__id_copy(new_change->node_rev_id, - pool); - old_change->text_mod = new_change->text_mod; - old_change->prop_mod = new_change->prop_mod; - old_change->mergeinfo_mod = new_change->mergeinfo_mod; - if (new_change->copyfrom_rev == SVN_INVALID_REVNUM) - { - old_change->copyfrom_rev = SVN_INVALID_REVNUM; - old_change->copyfrom_path = NULL; - } - else - { - old_change->copyfrom_rev = new_change->copyfrom_rev; - old_change->copyfrom_path = apr_pstrdup(pool, - new_change->copyfrom_path); - } +static svn_fs_path_change2_t * +path_change_dup(const svn_fs_path_change2_t *source, + apr_pool_t *result_pool) +{ + svn_fs_path_change2_t *result = apr_pmemdup(result_pool, source, + sizeof(*source)); + result->node_rev_id = svn_fs_fs__id_copy(source->node_rev_id, result_pool); + if (source->copyfrom_path) + result->copyfrom_path = apr_pstrdup(result_pool, source->copyfrom_path); + + return result; } /* Merge the internal-use-only CHANGE into a hash of public-FS svn_fs_path_change2_t CHANGED_PATHS, collapsing multiple changes into a - single summarical (is that real word?) change per path. */ + single summarical (is that real word?) change per path. DELETIONS is + also a path->svn_fs_path_change2_t hash and contains all the deletions + that got turned into a replacement. */ static svn_error_t * fold_change(apr_hash_t *changed_paths, + apr_hash_t *deletions, const change_t *change) { apr_pool_t *pool = apr_hash_pool_get(changed_paths); @@ -686,7 +675,7 @@ fold_change(apr_hash_t *changed_paths, _("Invalid change ordering: new node revision ID " "without delete")); - /* Sanity check: an add, replacement, move, or reset must be the first + /* Sanity check: an add, replacement, or reset must be the first thing to follow a deletion. */ if ((old_change->change_kind == svn_fs_path_change_delete) && (! ((info->change_kind == svn_fs_path_change_replace) @@ -711,7 +700,7 @@ fold_change(apr_hash_t *changed_paths, case svn_fs_path_change_reset: /* A reset here will simply remove the path change from the hash. */ - old_change = NULL; + apr_hash_set(changed_paths, path->data, path->len, NULL); break; case svn_fs_path_change_delete: @@ -719,31 +708,48 @@ fold_change(apr_hash_t *changed_paths, { /* If the path was introduced in this transaction via an add, and we are deleting it, just remove the path - altogether. */ - old_change = NULL; + altogether. (The caller will delete any child paths.) */ + apr_hash_set(changed_paths, path->data, path->len, NULL); + } + else if (old_change->change_kind == svn_fs_path_change_replace) + { + /* A deleting a 'replace' restore the original deletion. */ + new_change = apr_hash_get(deletions, path->data, path->len); + SVN_ERR_ASSERT(new_change); + apr_hash_set(changed_paths, path->data, path->len, new_change); } else { - /* A deletion overrules all previous changes. */ - old_change->change_kind = svn_fs_path_change_delete; - old_change->text_mod = info->text_mod; - old_change->prop_mod = info->prop_mod; - old_change->mergeinfo_mod = info->mergeinfo_mod; - old_change->copyfrom_rev = SVN_INVALID_REVNUM; - old_change->copyfrom_path = NULL; + /* A deletion overrules a previous change (modify). */ + new_change = path_change_dup(info, pool); + apr_hash_set(changed_paths, path->data, path->len, new_change); } break; case svn_fs_path_change_add: case svn_fs_path_change_replace: /* An add at this point must be following a previous delete, - so treat it just like a replace. */ - replace_change(old_change, info, pool); - old_change->change_kind = svn_fs_path_change_replace; + so treat it just like a replace. Remember the original + deletion such that we are able to delete this path again + (the replacement may have changed node kind and id). */ + new_change = path_change_dup(info, pool); + new_change->change_kind = svn_fs_path_change_replace; + + apr_hash_set(changed_paths, path->data, path->len, new_change); + + /* Remember the original change. + * Make sure to allocate the hash key in a durable pool. */ + apr_hash_set(deletions, + apr_pstrmemdup(apr_hash_pool_get(deletions), + path->data, path->len), + path->len, old_change); break; case svn_fs_path_change_modify: default: + /* If the new change modifies some attribute of the node, set + the corresponding flag, whether it already was set or not. + Note: We do not reset a flag to FALSE if a change is undone. */ if (info->text_mod) old_change->text_mod = TRUE; if (info->prop_mod) @@ -752,44 +758,44 @@ fold_change(apr_hash_t *changed_paths, old_change->mergeinfo_mod = svn_tristate_true; break; } - - /* remove old_change from the cache if it is no longer needed. */ - if (old_change == NULL) - apr_hash_set(changed_paths, path->data, path->len, NULL); } else { - /* This change is new to the hash, so make a new public change - structure from the internal one (in the hash's pool), and dup - the path into the hash's pool, too. */ - new_change = apr_pmemdup(pool, info, sizeof(*new_change)); - new_change->node_rev_id = svn_fs_fs__id_copy(info->node_rev_id, pool); - if (info->copyfrom_path) - new_change->copyfrom_path = apr_pstrdup(pool, info->copyfrom_path); - /* Add this path. The API makes no guarantees that this (new) key - will not be retained. Thus, we copy the key into the target pool - to ensure a proper lifetime. */ + will not be retained. Thus, we copy the key into the target pool + to ensure a proper lifetime. */ apr_hash_set(changed_paths, apr_pstrmemdup(pool, path->data, path->len), path->len, - new_change); + path_change_dup(info, pool)); } return SVN_NO_ERROR; } +/* Baton type to be used with process_changes(). */ +typedef struct process_changes_baton_t +{ + /* Folded list of path changes. */ + apr_hash_t *changed_paths; + + /* Path changes that are deletions and have been turned into + replacements. If those replacements get deleted again, this + container contains the record that we have to revert to. */ + apr_hash_t *deletions; +} process_changes_baton_t; + /* An implementation of svn_fs_fs__change_receiver_t. Examine all the changed path entries in CHANGES and store them in *CHANGED_PATHS. Folding is done to remove redundant or unnecessary data. Do all allocations in POOL. */ static svn_error_t * -process_changes(void *baton, +process_changes(void *baton_p, change_t *change, apr_pool_t *scratch_pool) { - apr_hash_t *changed_paths = baton; + process_changes_baton_t *baton = baton_p; - SVN_ERR(fold_change(changed_paths, change)); + SVN_ERR(fold_change(baton->changed_paths, baton->deletions, change)); /* Now, if our change was a deletion or replacement, we have to blow away any changes thus far on paths that are (or, were) @@ -820,14 +826,15 @@ process_changes(void *baton, The number of changes to process may be >> 1000. Therefore, keep the inner loop as tight as possible. */ - for (hi = apr_hash_first(scratch_pool, changed_paths); + for (hi = apr_hash_first(scratch_pool, baton->changed_paths); hi; hi = apr_hash_next(hi)) { /* KEY is the path. */ const void *path; apr_ssize_t klen; - apr_hash_this(hi, &path, &klen, NULL); + svn_fs_path_change2_t *old_change; + apr_hash_this(hi, &path, &klen, (void**)&old_change); /* If we come across a child of our path, remove it. Call svn_fspath__skip_ancestor only if there is a chance that @@ -839,7 +846,9 @@ process_changes(void *baton, child = svn_fspath__skip_ancestor(change->path.data, path); if (child && child[0] != '\0') - apr_hash_set(changed_paths, path, klen, NULL); + { + apr_hash_set(baton->changed_paths, path, klen, NULL); + } } } } @@ -856,6 +865,10 @@ svn_fs_fs__txn_changes_fetch(apr_hash_t apr_file_t *file; apr_hash_t *changed_paths = apr_hash_make(pool); apr_pool_t *scratch_pool = svn_pool_create(pool); + process_changes_baton_t baton; + + baton.changed_paths = changed_paths; + baton.deletions = apr_hash_make(scratch_pool); SVN_ERR(svn_io_file_open(&file, path_txn_changes(fs, txn_id, scratch_pool), @@ -865,7 +878,7 @@ svn_fs_fs__txn_changes_fetch(apr_hash_t SVN_ERR(svn_fs_fs__read_changes_incrementally( svn_stream_from_aprfile2(file, TRUE, scratch_pool), - process_changes, changed_paths, + process_changes, &baton, scratch_pool)); svn_pool_destroy(scratch_pool); @@ -998,12 +1011,7 @@ create_txn_dir(const char **id_p, txn_id->number = cb.txn_number; *id_p = svn_fs_fs__id_txn_unparse(txn_id, pool); - txn_dir = svn_dirent_join_many(pool, - fs->path, - PATH_TXNS_DIR, - apr_pstrcat(pool, *id_p, PATH_EXT_TXN, - SVN_VA_NULL), - SVN_VA_NULL); + txn_dir = svn_fs_fs__path_txn_dir(fs, txn_id, pool); return svn_io_dir_make(txn_dir, APR_OS_DEFAULT, pool); } @@ -1026,8 +1034,8 @@ create_txn_dir_pre_1_5(const char **id_p const char *unique_path, *prefix; /* Try to create directories named "<txndir>/<rev>-<uniqueifier>.txn". */ - prefix = svn_dirent_join_many(pool, fs->path, PATH_TXNS_DIR, - apr_psprintf(pool, "%ld", rev), SVN_VA_NULL); + prefix = svn_dirent_join(svn_fs_fs__path_txns_dir(fs, pool), + apr_psprintf(pool, "%ld", rev), pool); subpool = svn_pool_create(pool); for (i = 1; i <= 99999; i++) @@ -1590,8 +1598,10 @@ svn_fs_fs__add_change(svn_fs_t *fs, ? svn_tristate_true : svn_tristate_false; change->node_kind = node_kind; + change->copyfrom_known = TRUE; change->copyfrom_rev = copyfrom_rev; - change->copyfrom_path = apr_pstrdup(pool, copyfrom_path); + if (copyfrom_path) + change->copyfrom_path = apr_pstrdup(pool, copyfrom_path); svn_hash_sets(changes, path, change); SVN_ERR(svn_fs_fs__write_changes(svn_stream_from_aprfile2(file, TRUE, pool), @@ -1697,7 +1707,7 @@ allocate_item_index(apr_uint64_t *item_i *item_index = SVN_FS_FS__ITEM_INDEX_FIRST_USER; to_write = svn__ui64toa(buffer, *item_index + 1); - SVN_ERR(svn_io_file_seek(file, SEEK_SET, &offset, pool)); + SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, pool)); SVN_ERR(svn_io_file_write_full(file, buffer, to_write, NULL, pool)); SVN_ERR(svn_io_file_close(file, pool)); @@ -2519,7 +2529,7 @@ write_container_rep(representation_t *re collection_writer_t writer, svn_fs_t *fs, apr_hash_t *reps_hash, - int item_type, + apr_uint32_t item_type, svn_revnum_t final_revision, apr_pool_t *scratch_pool) { @@ -2618,7 +2628,7 @@ write_container_delta_rep(representation svn_fs_t *fs, node_revision_t *noderev, apr_hash_t *reps_hash, - int item_type, + apr_uint32_t item_type, svn_revnum_t final_revision, apr_pool_t *scratch_pool) { @@ -2956,9 +2966,9 @@ write_final_rev(const svn_fs_id_t **new_ if (noderev->prop_rep && is_txn_rep(noderev->prop_rep)) { apr_hash_t *proplist; - int item_type = noderev->kind == svn_node_dir - ? SVN_FS_FS__ITEM_TYPE_DIR_PROPS - : SVN_FS_FS__ITEM_TYPE_FILE_PROPS; + apr_uint32_t item_type = noderev->kind == svn_node_dir + ? SVN_FS_FS__ITEM_TYPE_DIR_PROPS + : SVN_FS_FS__ITEM_TYPE_FILE_PROPS; SVN_ERR(svn_fs_fs__get_proplist(&proplist, fs, noderev, pool)); noderev->prop_rep->revision = rev; @@ -3584,20 +3594,25 @@ svn_fs_fs__add_index_data(svn_fs_t *fs, apr_off_t p2l_offset; svn_stringbuf_t *footer; unsigned char footer_length; + svn_checksum_t *l2p_checksum; + svn_checksum_t *p2l_checksum; /* Append the actual index data to the pack file. */ l2p_offset = 0; SVN_ERR(svn_io_file_seek(file, APR_END, &l2p_offset, pool)); - SVN_ERR(svn_fs_fs__l2p_index_append(fs, file, l2p_proto_index, revision, - pool)); + SVN_ERR(svn_fs_fs__l2p_index_append(&l2p_checksum, fs, file, + l2p_proto_index, revision, + pool, pool)); p2l_offset = 0; SVN_ERR(svn_io_file_seek(file, APR_END, &p2l_offset, pool)); - SVN_ERR(svn_fs_fs__p2l_index_append(fs, file, p2l_proto_index, revision, - pool)); + SVN_ERR(svn_fs_fs__p2l_index_append(&p2l_checksum, fs, file, + p2l_proto_index, revision, + pool, pool)); /* Append footer. */ - footer = svn_fs_fs__unparse_footer(l2p_offset, p2l_offset, pool); + footer = svn_fs_fs__unparse_footer(l2p_offset, l2p_checksum, + p2l_offset, p2l_checksum, pool, pool); SVN_ERR(svn_io_file_write_full(file, footer->data, footer->len, NULL, pool)); @@ -3638,6 +3653,22 @@ commit_body(void *baton, apr_pool_t *poo const svn_fs_fs__id_part_t *txn_id = svn_fs_fs__txn_get_id(cb->txn); apr_hash_t *changed_paths; + /* Re-Read the current repository format. All our repo upgrade and + config evaluation strategies are such that existing information in + FS and FFD remains valid. + + Although we don't recommend upgrading hot repositories, people may + still do it and we must make sure to either handle them gracefully + or to error out. + + Committing pre-format 3 txns will fail after upgrade to format 3+ + because the proto-rev cannot be found; no further action needed. + Upgrades from pre-f7 to f7+ means a potential change in addressing + mode for the final rev. We must be sure to detect that cause because + the failure would only manifest once the new revision got committed. + */ + SVN_ERR(svn_fs_fs__read_format_file(cb->fs, pool)); + /* Read the current youngest revision and, possibly, the next available node id and copy id (for old format filesystems). Update the cached value for the youngest revision, because we have just checked it. */ @@ -3889,7 +3920,7 @@ svn_fs_fs__list_transactions(apr_array_h names = apr_array_make(pool, 1, sizeof(const char *)); /* Get the transactions directory. */ - txn_dir = svn_dirent_join(fs->path, PATH_TXNS_DIR, pool); + txn_dir = svn_fs_fs__path_txns_dir(fs, pool); /* Now find a listing of this directory. */ SVN_ERR(svn_io_get_dirents3(&dirents, txn_dir, TRUE, pool, pool)); Modified: subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/tree.c URL: http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/tree.c?rev=1632906&r1=1632905&r2=1632906&view=diff ============================================================================== --- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/tree.c (original) +++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/tree.c Sun Oct 19 13:55:35 2014 @@ -1537,7 +1537,7 @@ fs_change_node_prop(svn_fs_root_t *root, parent_path_t *parent_path; apr_hash_t *proplist; const svn_fs_fs__id_part_t *txn_id; - svn_boolean_t modeinfo_mod = FALSE; + svn_boolean_t mergeinfo_mod = FALSE; if (! root->is_txn_root) return SVN_FS__NOT_TXN(root); @@ -1582,7 +1582,7 @@ fs_change_node_prop(svn_fs_root_t *root, (value != NULL), pool)); } - modeinfo_mod = TRUE; + mergeinfo_mod = TRUE; } /* Set the property. */ @@ -1595,7 +1595,7 @@ fs_change_node_prop(svn_fs_root_t *root, /* Make a record of this modification in the changes table. */ return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(parent_path->node), - svn_fs_path_change_modify, FALSE, TRUE, modeinfo_mod, + svn_fs_path_change_modify, FALSE, TRUE, mergeinfo_mod, svn_fs_fs__dag_node_kind(parent_path->node), SVN_INVALID_REVNUM, NULL, pool); } @@ -4338,10 +4338,12 @@ stringify_node(dag_node_t *node, /* Check metadata sanity on NODE, and on its children. Manually verify information for DAG nodes in revision REV, and trust the metadata - accuracy for nodes belonging to older revisions. */ + accuracy for nodes belonging to older revisions. To detect cycles, + provide all parent dag_node_t * in PARENT_NODES. */ static svn_error_t * verify_node(dag_node_t *node, svn_revnum_t rev, + apr_array_header_t *parent_nodes, apr_pool_t *pool) { svn_boolean_t has_mergeinfo; @@ -4351,6 +4353,18 @@ verify_node(dag_node_t *node, int pred_count; svn_node_kind_t kind; apr_pool_t *iterpool = svn_pool_create(pool); + int i; + + /* Detect (non-)DAG cycles. */ + for (i = 0; i < parent_nodes->nelts; ++i) + { + dag_node_t *parent = APR_ARRAY_IDX(parent_nodes, i, dag_node_t *); + if (svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(parent), + svn_fs_fs__dag_get_id(node))) + return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, + "Node is its own direct or indirect parent '%s'", + stringify_node(node, iterpool)); + } /* Fetch some data. */ SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, node)); @@ -4402,8 +4416,8 @@ verify_node(dag_node_t *node, if (kind == svn_node_dir) { apr_array_header_t *entries; - int i; apr_int64_t children_mergeinfo = 0; + APR_ARRAY_PUSH(parent_nodes, dag_node_t*) = node; SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool)); @@ -4422,7 +4436,7 @@ verify_node(dag_node_t *node, { SVN_ERR(svn_fs_fs__dag_get_node(&child, fs, dirent->id, iterpool)); - SVN_ERR(verify_node(child, rev, iterpool)); + SVN_ERR(verify_node(child, rev, parent_nodes, iterpool)); SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo, child)); } @@ -4447,6 +4461,10 @@ verify_node(dag_node_t *node, stringify_node(node, iterpool), mergeinfo_count, has_mergeinfo, children_mergeinfo); + + /* If we don't make it here, there was an error / corruption. + * In that case, nobody will need PARENT_NODES anymore. */ + apr_array_pop(parent_nodes); } svn_pool_destroy(iterpool); @@ -4459,6 +4477,7 @@ svn_fs_fs__verify_root(svn_fs_root_t *ro { svn_fs_t *fs = root->fs; dag_node_t *root_dir; + apr_array_header_t *parent_nodes; /* Issue #4129: bogus pred-counts and minfo-cnt's on the root node-rev (and elsewhere). This code makes more thorough checks than the @@ -4482,7 +4501,8 @@ svn_fs_fs__verify_root(svn_fs_root_t *ro } /* Recursively verify ROOT_DIR. */ - SVN_ERR(verify_node(root_dir, root->rev, pool)); + parent_nodes = apr_array_make(pool, 16, sizeof(dag_node_t *)); + SVN_ERR(verify_node(root_dir, root->rev, parent_nodes, pool)); /* Verify explicitly the predecessor of the root. */ { Modified: subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/util.c URL: http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/util.c?rev=1632906&r1=1632905&r2=1632906&view=diff ============================================================================== --- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/util.c (original) +++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/util.c Sun Oct 19 13:55:35 2014 @@ -55,6 +55,16 @@ svn_fs_fs__is_packed_revprop(svn_fs_t *f && (ffd->format >= SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT); } +svn_revnum_t +svn_fs_fs__packed_base_rev(svn_fs_t *fs, + svn_revnum_t revision) +{ + fs_fs_data_t *ffd = fs->fsap_data; + return (revision < ffd->min_unpacked_rev) + ? (revision - (revision % ffd->max_files_per_dir)) + : revision; +} + const char * svn_fs_fs__path_txn_current(svn_fs_t *fs, apr_pool_t *pool) @@ -224,15 +234,25 @@ combine_txn_id_string(const svn_fs_fs__i } const char * +svn_fs_fs__path_txns_dir(svn_fs_t *fs, + apr_pool_t *pool) +{ + fs_fs_data_t *ffd = fs->fsap_data; + + return ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT + ? svn_dirent_join(fs->path, PATH_TXNS_LA_DIR, pool) + : svn_dirent_join(fs->path, PATH_TXNS_DIR, pool); +} + +const char * svn_fs_fs__path_txn_dir(svn_fs_t *fs, const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool) { SVN_ERR_ASSERT_NO_RETURN(txn_id != NULL); - return svn_dirent_join_many(pool, fs->path, PATH_TXNS_DIR, - combine_txn_id_string(txn_id, PATH_EXT_TXN, - pool), - SVN_VA_NULL); + return svn_dirent_join(svn_fs_fs__path_txns_dir(fs, pool), + combine_txn_id_string(txn_id, PATH_EXT_TXN, pool), + pool); } const char* @@ -263,16 +283,22 @@ svn_fs_fs__path_txn_item_index(svn_fs_t } const char * +svn_fs_fs__path_txn_proto_revs(svn_fs_t *fs, + apr_pool_t *pool) +{ + return svn_dirent_join(fs->path, PATH_TXN_PROTOS_DIR, pool); +} + +const char * svn_fs_fs__path_txn_proto_rev(svn_fs_t *fs, const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool) { fs_fs_data_t *ffd = fs->fsap_data; if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT) - return svn_dirent_join_many(pool, fs->path, PATH_TXN_PROTOS_DIR, - combine_txn_id_string(txn_id, PATH_EXT_REV, - pool), - SVN_VA_NULL); + return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool), + combine_txn_id_string(txn_id, PATH_EXT_REV, pool), + pool); else return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), PATH_REV, pool); @@ -286,11 +312,10 @@ svn_fs_fs__path_txn_proto_rev_lock(svn_f { fs_fs_data_t *ffd = fs->fsap_data; if (ffd->format >= SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT) - return svn_dirent_join_many(pool, fs->path, PATH_TXN_PROTOS_DIR, - combine_txn_id_string(txn_id, - PATH_EXT_REV_LOCK, - pool), - SVN_VA_NULL); + return svn_dirent_join(svn_fs_fs__path_txn_proto_revs(fs, pool), + combine_txn_id_string(txn_id, PATH_EXT_REV_LOCK, + pool), + pool); else return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool), PATH_REV_LOCK, pool); @@ -387,7 +412,7 @@ svn_fs_fs__read_min_unpacked_rev(svn_rev SVN_ERR(svn_io_read_length_line(file, buf, &len, pool)); SVN_ERR(svn_io_file_close(file, pool)); - *min_unpacked_rev = SVN_STR_TO_REV(buf); + SVN_ERR(svn_revnum_parse(min_unpacked_rev, buf, NULL)); return SVN_NO_ERROR; } @@ -429,7 +454,6 @@ svn_fs_fs__read_current(svn_revnum_t *re { fs_fs_data_t *ffd = fs->fsap_data; svn_stringbuf_t *content; - const char *str; SVN_ERR(svn_fs_fs__read_content(&content, svn_fs_fs__path_current(fs, pool), @@ -437,16 +461,20 @@ svn_fs_fs__read_current(svn_revnum_t *re if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT) { - SVN_ERR(svn_revnum_parse(rev, content->data, &str)); - if (*str != '\n') - return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, - _("Corrupt 'current' file")); + /* When format 1 and 2 filesystems are upgraded, the 'current' file is + left intact. As a consequence, there is a window when a filesystem + has a new format, but this file still contains the IDs left from an + old format, i.e. looks like "359 j5 v\n". Do not be too strict here + and only expect a parseable revision number. */ + SVN_ERR(svn_revnum_parse(rev, content->data, NULL)); *next_node_id = 0; *next_copy_id = 0; } else { + const char *str; + SVN_ERR(svn_revnum_parse(rev, content->data, &str)); if (*str != ' ') return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, Modified: subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/util.h URL: http://svn.apache.org/viewvc/subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/util.h?rev=1632906&r1=1632905&r2=1632906&view=diff ============================================================================== --- subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/util.h (original) +++ subversion/branches/revprop-caching-ng/subversion/libsvn_fs_fs/util.h Sun Oct 19 13:55:35 2014 @@ -76,6 +76,12 @@ svn_boolean_t svn_fs_fs__is_packed_revprop(svn_fs_t *fs, svn_revnum_t rev); +/* Return the first revision in the pack / rev file containing REVISION in + * filesystem FS. For non-packed revs, this will simply be REVISION. */ +svn_revnum_t +svn_fs_fs__packed_base_rev(svn_fs_t *fs, + svn_revnum_t revision); + /* Return the full path of the rev shard directory that will contain * revision REV in FS. Allocate the result in POOL. */ @@ -183,6 +189,13 @@ const char * svn_fs_fs__path_min_unpacked_rev(svn_fs_t *fs, apr_pool_t *pool); +/* Return the path of the 'transactions' directory in FS. + * The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_txns_dir(svn_fs_t *fs, + apr_pool_t *pool); + /* Return the path of the directory containing the transaction TXN_ID in FS. * The result will be allocated in POOL. */ @@ -191,6 +204,13 @@ svn_fs_fs__path_txn_dir(svn_fs_t *fs, const svn_fs_fs__id_part_t *txn_id, apr_pool_t *pool); +/* Return the path of the 'txn-protorevs' directory in FS, even if that + * folder may not exist in FS. The result will be allocated in POOL. + */ +const char * +svn_fs_fs__path_txn_proto_revs(svn_fs_t *fs, + apr_pool_t *pool); + /* Return the path of the proto-revision file for transaction TXN_ID in FS. * The result will be allocated in POOL. */
