diff options
author | Peter Wemm <peter@FreeBSD.org> | 2018-05-08 03:44:38 +0000 |
---|---|---|
committer | Peter Wemm <peter@FreeBSD.org> | 2018-05-08 03:44:38 +0000 |
commit | 3faf8d6bffc5d0fb2525ba37bb504c53366caf9d (patch) | |
tree | 7e47911263e75034b767fe34b2f8d3d17e91f66d /subversion/libsvn_fs_x/low_level.c | |
parent | a55fb3c0d5eca7d887798125d5b95942b1f01d4b (diff) |
Diffstat (limited to 'subversion/libsvn_fs_x/low_level.c')
-rw-r--r-- | subversion/libsvn_fs_x/low_level.c | 195 |
1 files changed, 158 insertions, 37 deletions
diff --git a/subversion/libsvn_fs_x/low_level.c b/subversion/libsvn_fs_x/low_level.c index 76f3fd2f38721..5c7e3ad26610f 100644 --- a/subversion/libsvn_fs_x/low_level.c +++ b/subversion/libsvn_fs_x/low_level.c @@ -1,4 +1,4 @@ -/* low_level.c --- low level r/w access to fs_x file structures +/* low_level.c --- low level r/w access to FSX file structures * * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one @@ -56,7 +56,6 @@ #define ACTION_ADD "add" #define ACTION_DELETE "delete" #define ACTION_REPLACE "replace" -#define ACTION_RESET "reset" /* True and False flags. */ #define FLAG_TRUE "true" @@ -103,6 +102,19 @@ parse_revnum(svn_revnum_t *rev, return SVN_NO_ERROR; } +/* 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_x__parse_footer(apr_off_t *l2p_offset, svn_checksum_t **l2p_checksum, @@ -110,6 +122,7 @@ svn_fs_x__parse_footer(apr_off_t *l2p_offset, 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; @@ -118,17 +131,20 @@ svn_fs_x__parse_footer(apr_off_t *l2p_offset, /* 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)); @@ -136,17 +152,33 @@ svn_fs_x__parse_footer(apr_off_t *l2p_offset, /* 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)); @@ -797,14 +829,6 @@ read_change(svn_fs_x__change_t **change_p, change = apr_pcalloc(result_pool, sizeof(*change)); last_str = line->data; - /* Get the node-id of the change. */ - str = svn_cstring_tokenize(" ", &last_str); - if (str == NULL) - return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, - _("Invalid changes line in rev-file")); - - SVN_ERR(svn_fs_x__id_parse(&change->noderev_id, str)); - /* Get the change type. */ str = svn_cstring_tokenize(" ", &last_str); if (str == NULL) @@ -845,10 +869,6 @@ read_change(svn_fs_x__change_t **change_p, { change->change_kind = svn_fs_path_change_replace; } - else if (strcmp(str, ACTION_RESET) == 0) - { - change->change_kind = svn_fs_path_change_reset; - } else { return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, @@ -954,10 +974,10 @@ read_change(svn_fs_x__change_t **change_p, svn_error_t * svn_fs_x__read_changes(apr_array_header_t **changes, svn_stream_t *stream, + int max_count, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - svn_fs_x__change_t *change; apr_pool_t *iterpool; /* Pre-allocate enough room for most change lists. @@ -970,13 +990,16 @@ svn_fs_x__read_changes(apr_array_header_t **changes, */ *changes = apr_array_make(result_pool, 63, sizeof(svn_fs_x__change_t *)); - SVN_ERR(read_change(&change, stream, result_pool, scratch_pool)); iterpool = svn_pool_create(scratch_pool); - while (change) + for (; max_count > 0; --max_count) { - APR_ARRAY_PUSH(*changes, svn_fs_x__change_t*) = change; - SVN_ERR(read_change(&change, stream, result_pool, iterpool)); + svn_fs_x__change_t *change; svn_pool_clear(iterpool); + SVN_ERR(read_change(&change, stream, result_pool, iterpool)); + if (!change) + break; + + APR_ARRAY_PUSH(*changes, svn_fs_x__change_t*) = change; } svn_pool_destroy(iterpool); @@ -1016,7 +1039,6 @@ write_change_entry(svn_stream_t *stream, svn_fs_x__change_t *change, apr_pool_t *scratch_pool) { - const char *idstr; const char *change_string = NULL; const char *kind_string = ""; svn_stringbuf_t *buf; @@ -1036,17 +1058,12 @@ write_change_entry(svn_stream_t *stream, case svn_fs_path_change_replace: change_string = ACTION_REPLACE; break; - case svn_fs_path_change_reset: - change_string = ACTION_RESET; - break; default: return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, _("Invalid change type %d"), change->change_kind); } - idstr = svn_fs_x__id_unparse(&change->noderev_id, scratch_pool)->data; - SVN_ERR_ASSERT(change->node_kind == svn_node_dir || change->node_kind == svn_node_file); kind_string = apr_psprintf(scratch_pool, "-%s", @@ -1054,8 +1071,8 @@ write_change_entry(svn_stream_t *stream, ? SVN_FS_X__KIND_DIR : SVN_FS_X__KIND_FILE); - buf = svn_stringbuf_createf(scratch_pool, "%s %s%s %s %s %s %s\n", - idstr, change_string, kind_string, + buf = svn_stringbuf_createf(scratch_pool, "%s%s %s %s %s %s\n", + change_string, kind_string, change->text_mod ? FLAG_TRUE : FLAG_FALSE, change->prop_mod ? FLAG_TRUE : FLAG_FALSE, change->mergeinfo_mod == svn_tristate_true @@ -1121,3 +1138,107 @@ svn_fs_x__write_changes(svn_stream_t *stream, return SVN_NO_ERROR; } +svn_error_t * +svn_fs_x__parse_properties(apr_hash_t **properties, + const svn_string_t *content, + apr_pool_t *result_pool) +{ + const apr_byte_t *p = (const apr_byte_t *)content->data; + const apr_byte_t *end = p + content->len; + apr_uint64_t count; + + *properties = apr_hash_make(result_pool); + + /* Extract the number of properties we are expected to read. */ + p = svn__decode_uint(&count, p, end); + + /* Read all the properties we find. + Because prop-name and prop-value are nicely NUL-terminated + sub-strings of CONTENT, we can simply reference them there. + I.e. there is no need to copy them around. + */ + while (p < end) + { + apr_uint64_t value_len; + svn_string_t *value; + + const char *key = (const char *)p; + + /* Note that this may never overflow / segfault because + CONTENT itself is NUL-terminated. */ + apr_size_t key_len = strlen(key); + p += key_len + 1; + if (key[key_len]) + return svn_error_createf(SVN_ERR_FS_CORRUPT_PROPLIST, NULL, + "Property name not NUL terminated"); + + if (p >= end) + return svn_error_createf(SVN_ERR_FS_CORRUPT_PROPLIST, NULL, + "Property value missing"); + p = svn__decode_uint(&value_len, p, end); + if (value_len >= (end - p)) + return svn_error_createf(SVN_ERR_FS_CORRUPT_PROPLIST, NULL, + "Property value too long"); + + value = apr_pcalloc(result_pool, sizeof(*value)); + value->data = (const char *)p; + value->len = (apr_size_t)value_len; + if (p[value->len]) + return svn_error_createf(SVN_ERR_FS_CORRUPT_PROPLIST, NULL, + "Property value not NUL terminated"); + + p += value->len + 1; + + apr_hash_set(*properties, key, key_len, value); + } + + /* Check that we read the expected number of properties. */ + if ((apr_uint64_t)apr_hash_count(*properties) != count) + return svn_error_createf(SVN_ERR_FS_CORRUPT_PROPLIST, NULL, + "Property count mismatch"); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_fs_x__write_properties(svn_stream_t *stream, + apr_hash_t *proplist, + apr_pool_t *scratch_pool) +{ + apr_byte_t buffer[SVN__MAX_ENCODED_UINT_LEN]; + apr_size_t len; + apr_hash_index_t *hi; + + /* Write the number of properties in this list. */ + len = svn__encode_uint(buffer, apr_hash_count(proplist)) - buffer; + SVN_ERR(svn_stream_write(stream, (const char *)buffer, &len)); + + /* Serialize each property as follows: + <Prop-name> <NUL> + <Value-len> <Prop-value> <NUL> + */ + for (hi = apr_hash_first(scratch_pool, proplist); + hi; + hi = apr_hash_next(hi)) + { + const char *key; + apr_size_t key_len; + svn_string_t *value; + apr_hash_this(hi, (const void **)&key, (apr_ssize_t *)&key_len, + (void **)&value); + + /* Include the terminating NUL. */ + ++key_len; + SVN_ERR(svn_stream_write(stream, key, &key_len)); + + len = svn__encode_uint(buffer, value->len) - buffer; + SVN_ERR(svn_stream_write(stream, (const char *)buffer, &len)); + SVN_ERR(svn_stream_write(stream, value->data, &value->len)); + + /* Terminate with NUL. */ + len = 1; + SVN_ERR(svn_stream_write(stream, "", &len)); + } + + return SVN_NO_ERROR; +} |