aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorZyX <kp-pav@yandex.ru>2015-08-08 13:27:55 +0300
committerZyX <kp-pav@yandex.ru>2015-10-08 22:00:29 +0300
commit1542fc221e52f31d5102730a222b5dbbae782dcd (patch)
treea9de4e5f891d3c68da0319f10a02bb744313c401 /src
parent1e067920994a5dd02df8f71f21afa29f0d4bcd21 (diff)
downloadrneovim-1542fc221e52f31d5102730a222b5dbbae782dcd.tar.gz
rneovim-1542fc221e52f31d5102730a222b5dbbae782dcd.tar.bz2
rneovim-1542fc221e52f31d5102730a222b5dbbae782dcd.zip
shada,functests: Improve detection of invalid ShaDa files
It appears that large portion of non-ShaDa ASCII text files may be parsed as a ShaDa file because it is mostly recognized as a sequence of unknown entries: all ASCII non-control characters are recognized as FIXUINT shada objects, so text like #!/bin/sh powerline "$@" 2>&1 | tee -a powerline (with trailing newline) will be recognized as a correct ShaDa file containing single unknown entry with type 0x23 (dec 35, '#'), timestamp 0x21 (dec 33, '!') and length 0x2F (dec 47, '/') without this commit. With it parsing this entry will fail.
Diffstat (limited to 'src')
-rw-r--r--src/nvim/shada.c200
1 files changed, 130 insertions, 70 deletions
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 03dc10c533..bd3fd731e3 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -2001,6 +2001,93 @@ static int compare_file_marks(const void *a, const void *b)
: 1));
}
+/// Parse msgpack object that has given length
+///
+/// @param[in] sd_reader Structure containing file reader definition.
+/// @param[in] length Object length.
+/// @param[out] ret_unpacked Location where read result should be saved. If
+/// NULL then unpacked data will be freed. Must be
+/// NULL if `ret_buf` is NULL.
+/// @param[out] ret_buf Buffer containing parsed string.
+///
+/// @return kSDReadStatusNotShaDa, kSDReadStatusReadError or
+/// kSDReadStatusSuccess.
+static inline ShaDaReadResult shada_parse_msgpack(
+ ShaDaReadDef *const sd_reader, const size_t length,
+ msgpack_unpacked *ret_unpacked, char **const ret_buf)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1)
+{
+ const uintmax_t initial_fpos = sd_reader->fpos;
+ char *const buf = xmalloc(length);
+
+ const ShaDaReadResult fl_ret = fread_len(sd_reader, buf, length);
+ if (fl_ret != kSDReadStatusSuccess) {
+ xfree(buf);
+ return fl_ret;
+ }
+ bool did_try_to_free = false;
+shada_parse_msgpack_read_next: {}
+ size_t off = 0;
+ msgpack_unpacked unpacked;
+ msgpack_unpacked_init(&unpacked);
+ const msgpack_unpack_return result =
+ msgpack_unpack_next(&unpacked, buf, length, &off);
+ ShaDaReadResult ret = kSDReadStatusSuccess;
+ switch (result) {
+ case MSGPACK_UNPACK_SUCCESS: {
+ if (off < length) {
+ goto shada_parse_msgpack_extra_bytes;
+ }
+ break;
+ }
+ case MSGPACK_UNPACK_PARSE_ERROR: {
+ emsgu(_(RCERR "Failed to parse ShaDa file due to a msgpack parser error "
+ "at position %" PRIu64),
+ (uint64_t) initial_fpos);
+ ret = kSDReadStatusNotShaDa;
+ break;
+ }
+ case MSGPACK_UNPACK_NOMEM_ERROR: {
+ if (!did_try_to_free) {
+ did_try_to_free = true;
+ try_to_free_memory();
+ goto shada_parse_msgpack_read_next;
+ }
+ EMSG(_(e_outofmem));
+ ret = kSDReadStatusReadError;
+ break;
+ }
+ case MSGPACK_UNPACK_CONTINUE: {
+ emsgu(_(RCERR "Failed to parse ShaDa file: incomplete msgpack string "
+ "at position %" PRIu64),
+ (uint64_t) initial_fpos);
+ ret = kSDReadStatusNotShaDa;
+ break;
+ }
+ case MSGPACK_UNPACK_EXTRA_BYTES: {
+shada_parse_msgpack_extra_bytes:
+ emsgu(_(RCERR "Failed to parse ShaDa file: extra bytes in msgpack string "
+ "at position %" PRIu64),
+ (uint64_t) initial_fpos);
+ ret = kSDReadStatusNotShaDa;
+ break;
+ }
+ }
+ if (ret_buf != NULL && ret == kSDReadStatusSuccess) {
+ if (ret_unpacked == NULL) {
+ msgpack_unpacked_destroy(&unpacked);
+ } else {
+ *ret_unpacked = unpacked;
+ }
+ *ret_buf = buf;
+ } else {
+ assert(ret_buf == NULL || ret != kSDReadStatusSuccess);
+ msgpack_unpacked_destroy(&unpacked);
+ xfree(buf);
+ }
+ return ret;
+}
+
/// Write ShaDa file
///
/// @param[in] sd_writer Structure containing file writer definition.
@@ -3258,9 +3345,23 @@ shada_read_next_item_start:
? !(flags & kSDReadUnknown)
: !((unsigned) (1 << type_u64) & flags))
|| (max_kbyte && length > max_kbyte * 1024)) {
- const ShaDaReadResult fl_ret = fread_len(sd_reader, NULL, length);
- if (fl_ret != kSDReadStatusSuccess) {
- return fl_ret;
+ // First entry is unknown or equal to "\n" (10)? Most likely this means that
+ // current file is not a ShaDa file because first item should normally be
+ // a header (excluding tests where first item is tested item). Check this by
+ // parsing entry contents: in non-ShaDa files this will most likely result
+ // in incomplete MessagePack string.
+ if (initial_fpos == 0
+ && (type_u64 == '\n' || type_u64 > SHADA_LAST_ENTRY)) {
+ const ShaDaReadResult spm_ret = shada_parse_msgpack(sd_reader, length,
+ NULL, NULL);
+ if (spm_ret != kSDReadStatusSuccess) {
+ return spm_ret;
+ }
+ } else {
+ const ShaDaReadResult fl_ret = fread_len(sd_reader, NULL, length);
+ if (fl_ret != kSDReadStatusSuccess) {
+ return fl_ret;
+ }
}
goto shada_read_next_item_start;
}
@@ -3269,72 +3370,33 @@ shada_read_next_item_start:
entry->type = kSDItemUnknown;
entry->data.unknown_item.size = length;
entry->data.unknown_item.type = type_u64;
- entry->data.unknown_item.contents = xmalloc(length);
- const ShaDaReadResult fl_ret = fread_len(sd_reader,
- entry->data.unknown_item.contents,
- length);
- if (fl_ret != kSDReadStatusSuccess) {
- shada_free_shada_entry(entry);
- entry->type = kSDItemMissing;
- }
- return fl_ret;
- }
-
- char *const buf = xmalloc(length);
-
- {
- const ShaDaReadResult fl_ret = fread_len(sd_reader, buf, length);
- if (fl_ret != kSDReadStatusSuccess) {
- xfree(buf);
+ if (initial_fpos == 0) {
+ const ShaDaReadResult spm_ret = shada_parse_msgpack(
+ sd_reader, length, NULL, &entry->data.unknown_item.contents);
+ if (spm_ret != kSDReadStatusSuccess) {
+ entry->type = kSDItemMissing;
+ }
+ return spm_ret;
+ } else {
+ entry->data.unknown_item.contents = xmalloc(length);
+ const ShaDaReadResult fl_ret = fread_len(
+ sd_reader, entry->data.unknown_item.contents, length);
+ if (fl_ret != kSDReadStatusSuccess) {
+ shada_free_shada_entry(entry);
+ entry->type = kSDItemMissing;
+ }
return fl_ret;
}
}
msgpack_unpacked unpacked;
- msgpack_unpacked_init(&unpacked);
+ char *buf = NULL;
- bool did_try_to_free = false;
-shada_read_next_item_read_next: {}
- size_t off = 0;
- const msgpack_unpack_return result =
- msgpack_unpack_next(&unpacked, buf, length, &off);
- ret = kSDReadStatusNotShaDa;
- switch (result) {
- case MSGPACK_UNPACK_SUCCESS: {
- if (off < length) {
- goto shada_read_next_item_extra_bytes;
- }
- break;
- }
- case MSGPACK_UNPACK_PARSE_ERROR: {
- emsgu(_(RCERR "Failed to parse ShaDa file due to a msgpack parser error "
- "at position %" PRIu64),
- (uint64_t) initial_fpos);
- goto shada_read_next_item_error;
- }
- case MSGPACK_UNPACK_NOMEM_ERROR: {
- if (!did_try_to_free) {
- did_try_to_free = true;
- try_to_free_memory();
- goto shada_read_next_item_read_next;
- }
- EMSG(_(e_outofmem));
- ret = kSDReadStatusReadError;
- goto shada_read_next_item_error;
- }
- case MSGPACK_UNPACK_CONTINUE: {
- emsgu(_(RCERR "Failed to parse ShaDa file: incomplete msgpack string "
- "at position %" PRIu64),
- (uint64_t) initial_fpos);
- goto shada_read_next_item_error;
- }
- case MSGPACK_UNPACK_EXTRA_BYTES: {
-shada_read_next_item_extra_bytes:
- emsgu(_(RCERR "Failed to parse ShaDa file: extra bytes in msgpack string "
- "at position %" PRIu64),
- (uint64_t) initial_fpos);
- goto shada_read_next_item_error;
- }
+ const ShaDaReadResult spm_ret = shada_parse_msgpack(sd_reader, length,
+ &unpacked, &buf);
+ if (spm_ret != kSDReadStatusSuccess) {
+ ret = spm_ret;
+ goto shada_read_next_item_error;
}
ret = kSDReadStatusMalformed;
#define CHECK_KEY(key, expected) \
@@ -3968,7 +4030,6 @@ shada_read_next_item_hist_no_conv:
}
}
entry->type = (ShadaEntryType) type_u64;
- goto shada_read_next_item_end;
#undef BIN_CONVERTED
#undef CONVERTED
#undef CHECK_KEY
@@ -3989,17 +4050,16 @@ shada_read_next_item_hist_no_conv:
#undef TOSIZE
#undef SET_ADDITIONAL_DATA
#undef SET_ADDITIONAL_ELEMENTS
-shada_read_next_item_error:
+ ret = kSDReadStatusSuccess;
+shada_read_next_item_end:
msgpack_unpacked_destroy(&unpacked);
xfree(buf);
+ return ret;
+shada_read_next_item_error:
entry->type = (ShadaEntryType) type_u64;
shada_free_shada_entry(entry);
entry->type = kSDItemMissing;
- return ret;
-shada_read_next_item_end:
- msgpack_unpacked_destroy(&unpacked);
- xfree(buf);
- return kSDReadStatusSuccess;
+ goto shada_read_next_item_end;
}
/// Check whether "name" is on removable media (according to 'shada')