diff --git a/external/crashpad b/external/crashpad index 95733c1ee..000e3ad7a 160000 --- a/external/crashpad +++ b/external/crashpad @@ -1 +1 @@ -Subproject commit 95733c1ee6fe77fc82ae6bb1bac2c34b9a6d0c17 +Subproject commit 000e3ad7a51c89e49bc00eca9846aa0485cccc96 diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index a81e552f3..4794136a8 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -364,6 +364,104 @@ attachment_is_placeholder(const sentry_options_t *options, const char *path) return is_placeholder; } +/** + * Reads a legacy JSON attachment manifest (TODO: remove in 1.0) + */ +static sentry_value_t +read_legacy_manifest(const sentry_path_t *manifest_path) +{ + size_t buf_len = 0; + char *buf = sentry__path_read_to_buffer(manifest_path, &buf_len); + if (!buf) { + return sentry_value_new_list(); + } + + const char *start = buf; + const char *end = buf + buf_len; + while (start < end + && (*start == ' ' || *start == '\t' || *start == '\r' + || *start == '\n')) { + start++; + } + const char *trimmed_end = end; + while (trimmed_end > start + && (trimmed_end[-1] == ' ' || trimmed_end[-1] == '\t' + || trimmed_end[-1] == '\r' || trimmed_end[-1] == '\n')) { + trimmed_end--; + } + sentry_value_t legacy + = start < trimmed_end && *start == '[' && trimmed_end[-1] == ']' + ? sentry__value_from_json(start, (size_t)(trimmed_end - start)) + : sentry_value_new_null(); + sentry_free(buf); + if (sentry_value_get_type(legacy) != SENTRY_VALUE_TYPE_LIST) { + sentry_value_decref(legacy); + return sentry_value_new_null(); + } + + sentry_value_t attachments = sentry_value_new_list(); + size_t len = sentry_value_get_length(legacy); + for (size_t i = 0; i < len; i++) { + sentry_value_t info = sentry_value_get_by_index(legacy, i); + const char *path + = sentry_value_as_string(sentry_value_get_by_key(info, "path")); + const char *filename + = sentry_value_as_string(sentry_value_get_by_key(info, "filename")); + if (sentry__string_empty(path) || sentry__string_empty(filename)) { + continue; + } + sentry_value_t attachment = sentry__attachment_from_file(path); + if (sentry_value_is_null(attachment)) { + continue; + } + sentry_attachment_set_filename(attachment, filename); + sentry_attachment_set_type(attachment, + sentry_value_as_string( + sentry_value_get_by_key(info, "attachment_type"))); + sentry_attachment_set_content_type(attachment, + sentry_value_as_string( + sentry_value_get_by_key(info, "content_type"))); + sentry_value_append(attachments, attachment); + } + sentry_value_decref(legacy); + return attachments; +} + +static sentry_value_t +read_attachment_manifest(const sentry_path_t *run_folder) +{ + sentry_path_t *path + = sentry__path_join_str(run_folder, "__sentry-attachments"); + if (!path) { + return sentry_value_new_null(); + } + sentry_value_t attachments = sentry__read_attachment_manifest(path); + if (sentry_value_is_null(attachments)) { + attachments = read_legacy_manifest(path); + } + sentry__path_free(path); + return attachments; +} + +static void +write_attachments_from_manifest( + int fd, const sentry_options_t *options, const sentry_path_t *run_folder) +{ + sentry_value_t attachments = read_attachment_manifest(run_folder); + size_t len = sentry_value_get_length(attachments); + for (size_t i = 0; i < len; i++) { + sentry_value_t attachment = sentry_value_get_by_index(attachments, i); + const char *path = sentry__attachment_get_path(attachment); + if (!attachment_is_placeholder(options, path)) { + write_attachment_to_envelope(fd, path, + sentry__attachment_get_filename(attachment), + sentry__attachment_get_type(attachment), + sentry__attachment_get_content_type(attachment)); + } + } + sentry_value_decref(attachments); +} + // For each large attachment listed in `/__sentry-attachments`, // cache it as an attachment-ref item. Small attachments were already inlined // during envelope writing. @@ -375,23 +473,7 @@ add_attachment_refs(sentry_envelope_t *envelope, || !options->enable_large_attachments || !run_folder) { return; } - sentry_path_t *attach_list_path - = sentry__path_join_str(run_folder, "__sentry-attachments"); - if (!attach_list_path) { - SENTRY_WARN("Failed to resolve attachment manifest path"); - return; - } - size_t attach_json_len = 0; - char *attach_json - = sentry__path_read_to_buffer(attach_list_path, &attach_json_len); - sentry__path_free(attach_list_path); - if (!attach_json) { - return; - } - sentry_value_t list = attach_json_len > 0 - ? sentry__value_from_json(attach_json, attach_json_len) - : sentry_value_new_null(); - sentry_free(attach_json); + sentry_value_t list = read_attachment_manifest(run_folder); if (sentry_value_is_null(list)) { SENTRY_WARN("Failed to parse attachment manifest"); return; @@ -400,34 +482,12 @@ add_attachment_refs(sentry_envelope_t *envelope, bool materialized = false; size_t len = sentry_value_get_length(list); for (size_t i = 0; i < len; i++) { - sentry_value_t info = sentry_value_get_by_index(list, i); - const char *path - = sentry_value_as_string(sentry_value_get_by_key(info, "path")); - const char *filename - = sentry_value_as_string(sentry_value_get_by_key(info, "filename")); - const char *attachment_type = sentry_value_as_string( - sentry_value_get_by_key(info, "attachment_type")); - const char *content_type = sentry_value_as_string( - sentry_value_get_by_key(info, "content_type")); - if (sentry__string_empty(path) || sentry__string_empty(filename)) { - SENTRY_WARN("Skipping malformed attachment manifest entry"); - continue; - } - sentry_value_t attachment = sentry__attachment_from_file(path); - if (sentry_value_is_null(attachment)) { - SENTRY_WARNF("Failed to allocate attachment paths for: %s", path); - continue; - } - sentry_attachment_set_filename(attachment, filename); - sentry_attachment_set_type(attachment, attachment_type); - sentry_attachment_set_content_type(attachment, content_type); + sentry_value_t attachment = sentry_value_get_by_index(list, i); if (!sentry__attachment_is_placeholder(attachment, options)) { - sentry_value_decref(attachment); continue; } if (!materialized && !sentry__envelope_materialize(envelope)) { SENTRY_WARN("Failed to materialize envelope for attachment-refs"); - sentry_value_decref(attachment); break; } materialized = true; @@ -435,7 +495,6 @@ add_attachment_refs(sentry_envelope_t *envelope, envelope, attachment, options->run->cache_path, NULL)) { SENTRY_WARN("failed to cache attachment-ref"); } - sentry_value_decref(attachment); } sentry_value_decref(list); } @@ -3927,54 +3986,7 @@ write_envelope_with_native_stacktrace(const sentry_options_t *options, // Add scope attachments using metadata file if (run_folder) { - sentry_path_t *attach_list_path - = sentry__path_join_str(run_folder, "__sentry-attachments"); - if (attach_list_path) { - size_t attach_json_len = 0; - char *attach_json = sentry__path_read_to_buffer( - attach_list_path, &attach_json_len); - sentry__path_free(attach_list_path); - - if (attach_json && attach_json_len > 0) { - // Parse attachment list JSON - sentry_value_t attach_list - = sentry__value_from_json(attach_json, attach_json_len); - sentry_free(attach_json); - - if (!sentry_value_is_null(attach_list)) { - size_t len = sentry_value_get_length(attach_list); - for (size_t i = 0; i < len; i++) { - sentry_value_t attach_info - = sentry_value_get_by_index(attach_list, i); - sentry_value_t path_val - = sentry_value_get_by_key(attach_info, "path"); - sentry_value_t filename_val - = sentry_value_get_by_key(attach_info, "filename"); - sentry_value_t attachment_type_val - = sentry_value_get_by_key( - attach_info, "attachment_type"); - sentry_value_t content_type_val - = sentry_value_get_by_key( - attach_info, "content_type"); - - const char *path = sentry_value_as_string(path_val); - const char *filename - = sentry_value_as_string(filename_val); - const char *attachment_type - = sentry_value_as_string(attachment_type_val); - const char *content_type - = sentry_value_as_string(content_type_val); - - if (path && filename - && !attachment_is_placeholder(options, path)) { - write_attachment_to_envelope(fd, path, filename, - attachment_type, content_type); - } - } - sentry_value_decref(attach_list); - } - } - } + write_attachments_from_manifest(fd, options, run_folder); } // Add screenshot attachment if captured by the daemon @@ -4204,54 +4216,7 @@ write_envelope_with_minidump(const sentry_options_t *options, // Add scope attachments using metadata file if (run_folder) { - sentry_path_t *attach_list_path - = sentry__path_join_str(run_folder, "__sentry-attachments"); - if (attach_list_path) { - size_t attach_json_len = 0; - char *attach_json = sentry__path_read_to_buffer( - attach_list_path, &attach_json_len); - sentry__path_free(attach_list_path); - - if (attach_json && attach_json_len > 0) { - // Parse attachment list JSON - sentry_value_t attach_list - = sentry__value_from_json(attach_json, attach_json_len); - sentry_free(attach_json); - - if (!sentry_value_is_null(attach_list)) { - size_t len = sentry_value_get_length(attach_list); - for (size_t i = 0; i < len; i++) { - sentry_value_t attach_info - = sentry_value_get_by_index(attach_list, i); - sentry_value_t path_val - = sentry_value_get_by_key(attach_info, "path"); - sentry_value_t filename_val - = sentry_value_get_by_key(attach_info, "filename"); - sentry_value_t attachment_type_val - = sentry_value_get_by_key( - attach_info, "attachment_type"); - sentry_value_t content_type_val - = sentry_value_get_by_key( - attach_info, "content_type"); - - const char *path = sentry_value_as_string(path_val); - const char *filename - = sentry_value_as_string(filename_val); - const char *attachment_type - = sentry_value_as_string(attachment_type_val); - const char *content_type - = sentry_value_as_string(content_type_val); - - if (path && filename - && !attachment_is_placeholder(options, path)) { - write_attachment_to_envelope(fd, path, filename, - attachment_type, content_type); - } - } - sentry_value_decref(attach_list); - } - } - } + write_attachments_from_manifest(fd, options, run_folder); } // Add screenshot attachment if captured by the daemon diff --git a/src/backends/sentry_backend_crashpad.cpp b/src/backends/sentry_backend_crashpad.cpp index 7133fa2f1..33a3d8a29 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -153,8 +153,6 @@ typedef struct { static void crashpad_backend_add_breadcrumb(sentry_backend_t *backend, sentry_value_t breadcrumb, const sentry_options_t *options); -static sentry_path_t *make_attachment_path( - const sentry_path_t *run_path, sentry_value_t attachment); /** * Correctly destruct C++ members of the crashpad state. @@ -405,7 +403,8 @@ prepare_initial_attachment( { size_t bytes_len = 0; const char *bytes = sentry__attachment_get_bytes(attachment, &bytes_len); - sentry_path_t *path = make_attachment_path(run_path, attachment); + sentry_path_t *path + = sentry__attachment_make_run_path(run_path, attachment); if (!path) { return nullptr; } @@ -883,35 +882,6 @@ process_completed_reports( #if defined(SENTRY_PLATFORM_WINDOWS) || defined(SENTRY_PLATFORM_LINUX) \ || defined(SENTRY_PLATFORM_MACOS) -static sentry_path_t * -make_attachment_path(const sentry_path_t *run_path, sentry_value_t attachment) -{ - if (!sentry__attachment_get_bytes(attachment, nullptr)) { - return sentry__attachment_make_path(attachment); - } - - sentry_uuid_t id = sentry__attachment_get_id(attachment); - const char *filename = sentry__attachment_get_filename(attachment); - if (!run_path || sentry_uuid_is_nil(&id) - || sentry__string_empty(filename)) { - return nullptr; - } - - char uuid[37]; - sentry_uuid_as_string(&id, uuid); - sentry_path_t *dir = sentry__path_join_str(run_path, uuid); - sentry_path_t *path = dir ? sentry__path_join_str(dir, filename) : nullptr; - sentry_path_t *parent = path ? sentry__path_dir(path) : nullptr; - bool valid = parent && sentry__path_eq(parent, dir); - sentry__path_free(parent); - sentry__path_free(dir); - if (!valid) { - sentry__path_free(path); - return nullptr; - } - return path; -} - static void add_attachment(void *state, sentry_value_t attachment) { @@ -922,7 +892,8 @@ add_attachment(void *state, sentry_value_t attachment) size_t bytes_len = 0; const char *bytes = sentry__attachment_get_bytes(attachment, &bytes_len); - sentry_path_t *path = make_attachment_path(data->run_path, attachment); + sentry_path_t *path + = sentry__attachment_make_run_path(data->run_path, attachment); if (!path) { const char *filename = sentry__attachment_get_filename(attachment); SENTRY_WARNF("failed to create path for crashpad attachment \"%s\"", @@ -953,7 +924,8 @@ remove_attachment(void *state, sentry_value_t attachment) if (!data || !data->client) { return; } - sentry_path_t *path = make_attachment_path(data->run_path, attachment); + sentry_path_t *path + = sentry__attachment_make_run_path(data->run_path, attachment); if (!path) { return; } diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index 974c66b0e..e70f5502d 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -245,39 +245,6 @@ native_backend_preload_scope( sentry_value_decref(breadcrumbs); } -/** - * Creates an attachment path, deriving a unique path in the run directory for - * buffer attachments. - */ -static sentry_path_t * -make_attachment_path(const sentry_path_t *run_path, sentry_value_t attachment) -{ - if (!sentry__attachment_get_bytes(attachment, NULL)) { - return sentry__attachment_make_path(attachment); - } - - sentry_uuid_t id = sentry__attachment_get_id(attachment); - const char *filename = sentry__attachment_get_filename(attachment); - if (!run_path || sentry_uuid_is_nil(&id) - || sentry__string_empty(filename)) { - return NULL; - } - - char uuid[37]; - sentry_uuid_as_string(&id, uuid); - sentry_path_t *dir = sentry__path_join_str(run_path, uuid); - sentry_path_t *path = dir ? sentry__path_join_str(dir, filename) : NULL; - sentry_path_t *parent = path ? sentry__path_dir(path) : NULL; - bool valid = parent && sentry__path_eq(parent, dir); - sentry__path_free(parent); - sentry__path_free(dir); - if (!valid) { - sentry__path_free(path); - return NULL; - } - return path; -} - static void add_attachment(void *data, sentry_value_t attachment) { @@ -291,7 +258,8 @@ add_attachment(void *data, sentry_value_t attachment) size_t bytes_len = 0; const char *bytes = sentry__attachment_get_bytes(attachment, &bytes_len); if (bytes) { - sentry_path_t *path = make_attachment_path(state->run_path, attachment); + sentry_path_t *path + = sentry__attachment_make_run_path(state->run_path, attachment); if (!path) { const char *filename = sentry__attachment_get_filename(attachment); SENTRY_WARNF("failed to create path for native backend attachment " @@ -1222,53 +1190,7 @@ native_backend_write_attachments(const sentry_path_t *event_path) sentry_path_t *attach_list_path = sentry__path_join_str(run_path, "__sentry-attachments"); if (attach_list_path) { - sentry_value_t attach_list = sentry_value_new_list(); - size_t len = sentry_value_get_length(attachments); - for (size_t i = 0; i < len; i++) { - sentry_value_t attachment - = sentry_value_get_by_index(attachments, i); - sentry_path_t *path - = make_attachment_path(run_path, attachment); - if (!path) { - continue; - } - // skip missing or partially written attachments - size_t bytes_len = 0; - if (sentry__attachment_get_bytes(attachment, &bytes_len) - && sentry__path_get_size(path) != bytes_len) { - sentry__path_free(path); - continue; - } - sentry_value_t attach_info = sentry_value_new_object(); - sentry_value_set_by_key( - attach_info, "path", sentry_value_new_string(path->path)); - const char *filename - = sentry__attachment_get_filename(attachment); - sentry_value_set_by_key( - attach_info, "filename", sentry_value_new_string(filename)); - const char *type = sentry__attachment_get_type(attachment); - if (!sentry__string_empty(type)) { - sentry_value_set_by_key(attach_info, "attachment_type", - sentry_value_new_string(type)); - } - const char *content_type - = sentry__attachment_get_content_type(attachment); - if (content_type) { - sentry_value_set_by_key(attach_info, "content_type", - sentry_value_new_string(content_type)); - } - sentry_value_append(attach_list, attach_info); - sentry__path_free(path); - } - size_t attach_json_len = 0; - char *attach_json - = sentry__value_to_json(attach_list, &attach_json_len); - sentry_value_decref(attach_list); - if (attach_json) { - sentry__path_write_buffer( - attach_list_path, attach_json, attach_json_len); - sentry_free(attach_json); - } + sentry__write_attachment_manifest(attach_list_path, attachments); sentry__path_free(attach_list_path); } sentry__path_free(run_path); diff --git a/src/sentry_attachment.c b/src/sentry_attachment.c index 11a7791d4..a02f2d9f6 100644 --- a/src/sentry_attachment.c +++ b/src/sentry_attachment.c @@ -1,6 +1,7 @@ #include "sentry_attachment.h" #include "sentry_alloc.h" #include "sentry_logger.h" +#include "sentry_mpack.h" #include "sentry_options.h" #include "sentry_path.h" #include "sentry_string.h" @@ -401,6 +402,36 @@ sentry__attachment_make_path(sentry_value_t attachment) return sentry__path_from_str(sentry__attachment_get_path(attachment)); } +sentry_path_t * +sentry__attachment_make_run_path( + const sentry_path_t *run_path, sentry_value_t attachment) +{ + if (!sentry__attachment_get_bytes(attachment, NULL)) { + return sentry__attachment_make_path(attachment); + } + + sentry_uuid_t id = sentry__attachment_get_id(attachment); + const char *filename = sentry__attachment_get_filename(attachment); + if (!run_path || sentry_uuid_is_nil(&id) + || sentry__string_empty(filename)) { + return NULL; + } + + char uuid[37]; + sentry_uuid_as_string(&id, uuid); + sentry_path_t *dir = sentry__path_join_str(run_path, uuid); + sentry_path_t *path = dir ? sentry__path_join_str(dir, filename) : NULL; + sentry_path_t *parent = path ? sentry__path_dir(path) : NULL; + bool valid = parent && sentry__path_eq(parent, dir); + sentry__path_free(parent); + sentry__path_free(dir); + if (!valid) { + sentry__path_free(path); + return NULL; + } + return path; +} + bool sentry__attachment_is_placeholder( sentry_value_t attachment, const sentry_options_t *options) @@ -662,3 +693,117 @@ sentry__attachments_clone(sentry_value_t attachments) } return clone; } + +static sentry_value_t +read_manifest(const char *buf, size_t buf_len) +{ + if (buf_len == 0) { + return sentry_value_new_list(); + } + + sentry_value_t attachments + = sentry__value_from_msgpack_stream(buf, buf_len); + size_t i = 0; + while (i < sentry_value_get_length(attachments)) { + sentry_value_t attachment = sentry_value_get_by_index(attachments, i); + if (sentry_value_get_type(attachment) != SENTRY_VALUE_TYPE_OBJECT + || sentry__string_empty(sentry__attachment_get_path(attachment)) + || sentry__string_empty( + sentry__attachment_get_filename(attachment))) { + if (sentry_value_remove_by_index(attachments, i)) { + sentry_value_decref(attachments); + return sentry_value_new_null(); + } + continue; + } + i++; + } + if (sentry_value_get_length(attachments) == 0) { + sentry_value_decref(attachments); + return sentry_value_new_null(); + } + return attachments; +} + +sentry_value_t +sentry__read_attachment_manifest(const sentry_path_t *path) +{ + if (!path) { + return sentry_value_new_null(); + } + size_t buf_len = 0; + char *buf = sentry__path_read_to_buffer(path, &buf_len); + if (!buf) { + return sentry_value_new_null(); + } + sentry_value_t attachments = read_manifest(buf, buf_len); + sentry_free(buf); + return attachments; +} + +bool +sentry__write_attachment_manifest( + const sentry_path_t *path, sentry_value_t attachments) +{ + if (!path) { + return false; + } + + sentry_path_t *run_path = sentry__path_dir(path); + if (!run_path) { + return false; + } + + const char *keys[] + = { ATTACHMENT_FILENAME, ATTACHMENT_TYPE, ATTACHMENT_CONTENT_TYPE }; + mpack_writer_t writer; + char *buf = NULL; + size_t buf_len = 0; + mpack_writer_init_growable(&writer, &buf, &buf_len); + size_t len = sentry_value_get_length(attachments); + for (size_t i = 0; i < len && mpack_writer_error(&writer) == mpack_ok; + i++) { + sentry_value_t attachment = sentry_value_get_by_index(attachments, i); + sentry_path_t *attachment_path + = sentry__attachment_make_run_path(run_path, attachment); + if (!attachment_path) { + continue; + } + + // skip missing or partially written attachments + size_t bytes_len = 0; + if (sentry__attachment_get_bytes(attachment, &bytes_len) + && sentry__path_get_size(attachment_path) != bytes_len) { + sentry__path_free(attachment_path); + continue; + } + + uint32_t count = 1; + for (size_t j = 0; j < sizeof(keys) / sizeof(keys[0]); j++) { + if (sentry_value_get_type( + sentry_value_get_by_key(attachment, keys[j])) + == SENTRY_VALUE_TYPE_STRING) { + count++; + } + } + mpack_start_map(&writer, count); + mpack_write_cstr(&writer, ATTACHMENT_PATH); + mpack_write_cstr(&writer, attachment_path->path); + sentry__path_free(attachment_path); + for (size_t j = 0; j < sizeof(keys) / sizeof(keys[0]); j++) { + sentry_value_t value = sentry_value_get_by_key(attachment, keys[j]); + if (sentry_value_get_type(value) == SENTRY_VALUE_TYPE_STRING) { + mpack_write_cstr(&writer, keys[j]); + mpack_write_str(&writer, sentry_value_as_string(value), + (uint32_t)sentry_value_get_length(value)); + } + } + mpack_finish_map(&writer); + } + + sentry__path_free(run_path); + bool success = mpack_writer_destroy(&writer) == mpack_ok + && sentry__path_write_buffer(path, buf ? buf : "", buf_len) == 0; + sentry_free(buf); + return success; +} diff --git a/src/sentry_attachment.h b/src/sentry_attachment.h index 1b0668ad0..808c3fe53 100644 --- a/src/sentry_attachment.h +++ b/src/sentry_attachment.h @@ -69,6 +69,13 @@ const char *sentry__attachment_get_path(sentry_value_t attachment); */ sentry_path_t *sentry__attachment_make_path(sentry_value_t attachment); +/** + * Creates an attachment path, deriving a unique path in the run directory for + * buffer attachments. + */ +sentry_path_t *sentry__attachment_make_run_path( + const sentry_path_t *run_path, sentry_value_t attachment); + /** * Returns true if the attachment should be represented as an attachment-ref. */ @@ -142,4 +149,17 @@ sentry_value_t sentry__attachments_find( */ sentry_value_t sentry__attachments_clone(sentry_value_t attachments); +/** + * Reads a list of attachments from a manifest file. + */ +sentry_value_t sentry__read_attachment_manifest(const sentry_path_t *path); + +/** + * Writes attachment metadata to a manifest file in the run directory. + * Byte attachments reference their separately persisted files; missing or + * partially written files are skipped. Does not modify the attachments. + */ +bool sentry__write_attachment_manifest( + const sentry_path_t *path, sentry_value_t attachments); + #endif diff --git a/src/sentry_mpack.h b/src/sentry_mpack.h index 57c36949f..064f232da 100644 --- a/src/sentry_mpack.h +++ b/src/sentry_mpack.h @@ -387,6 +387,28 @@ #define mpack_writer_track_pop sentry__mpack_writer_track_pop #define mpack_writer_track_push sentry__mpack_writer_track_push +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4127) // conditional expression is constant +# if defined(__clang__) // clang-cl +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdocumentation" +# pragma clang diagnostic ignored "-Wpre-c11-compat" +# endif +#elif defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wstatic-in-inline" +#endif + #include "../vendor/mpack.h" +#if defined(_MSC_VER) +# pragma warning(pop) +# ifdef __clang__ // clang-cl +# pragma clang diagnostic pop +# endif +#elif defined(__clang__) +# pragma clang diagnostic pop +#endif + #endif diff --git a/src/sentry_value.c b/src/sentry_value.c index c5aba36fc..6e09804c2 100644 --- a/src/sentry_value.c +++ b/src/sentry_value.c @@ -6,33 +6,10 @@ #include #include -#if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable : 4127) // conditional expression is constant -# if defined(__clang__) // clang-cl -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdocumentation" -# pragma clang diagnostic ignored "-Wpre-c11-compat" -# endif -#elif defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wstatic-in-inline" -#endif - -#include "sentry_mpack.h" - -#if defined(_MSC_VER) -# pragma warning(pop) -# ifdef __clang__ // clang-cl -# pragma clang diagnostic pop -# endif -#elif defined(__clang__) -# pragma clang diagnostic pop -#endif - #include "sentry_alloc.h" #include "sentry_core.h" #include "sentry_json.h" +#include "sentry_mpack.h" #include "sentry_slice.h" #include "sentry_string.h" #include "sentry_sync.h" diff --git a/src/session_replay/sentry_session_replay.c b/src/session_replay/sentry_session_replay.c index e0705636d..bc7492c00 100644 --- a/src/session_replay/sentry_session_replay.c +++ b/src/session_replay/sentry_session_replay.c @@ -4,35 +4,12 @@ #include "sentry_database.h" #include "sentry_envelope.h" #include "sentry_json.h" +#include "sentry_mpack.h" #include "sentry_string.h" #include "sentry_utils.h" #include "sentry_uuid.h" #include "sentry_value.h" -#if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable : 4127) // conditional expression is constant -# if defined(__clang__) // clang-cl -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdocumentation" -# pragma clang diagnostic ignored "-Wpre-c11-compat" -# endif -#elif defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wstatic-in-inline" -#endif - -#include "sentry_mpack.h" - -#if defined(_MSC_VER) -# pragma warning(pop) -# ifdef __clang__ // clang-cl -# pragma clang diagnostic pop -# endif -#elif defined(__clang__) -# pragma clang diagnostic pop -#endif - #include sentry_path_t * diff --git a/tests/assertions.py b/tests/assertions.py index d67943c51..904429d99 100644 --- a/tests/assertions.py +++ b/tests/assertions.py @@ -537,6 +537,7 @@ def _load_crashpad_attachments(msg): bytes_bin = None minidump = None for part in msg.walk(): + assert part.get_filename() != "__sentry-attachments" if part.get_filename() is not None: assert part.get("Content-Type") is None diff --git a/tests/test_integration_native.py b/tests/test_integration_native.py index c06352b05..30940ea6e 100644 --- a/tests/test_integration_native.py +++ b/tests/test_integration_native.py @@ -12,6 +12,7 @@ import time import struct +import msgpack import pytest from . import ( @@ -433,8 +434,10 @@ def manifest_is_current(): if not paths: return False try: - manifest = json.loads(paths[0].read_text()) - except (OSError, json.JSONDecodeError): + unpacker = msgpack.Unpacker(raw=False) + unpacker.feed(paths[0].read_bytes()) + manifest = list(unpacker) + except (OSError, msgpack.UnpackException): # rewritten in place, so a read can catch a partial file return False last_manifest = manifest diff --git a/tests/unit/test_attachments.c b/tests/unit/test_attachments.c index 5240f2133..3ca5f8b84 100644 --- a/tests/unit/test_attachments.c +++ b/tests/unit/test_attachments.c @@ -756,3 +756,139 @@ SENTRY_TEST(attachments_more_than_ten) TEST_CHECK_INT_EQUAL(testdata.called, 1); } + +SENTRY_TEST(attachment_manifest) +{ + sentry_value_t attachment = sentry__attachment_from_file("attachment.bin"); + sentry_attachment_set_filename(attachment, "renamed.bin"); + sentry_value_t attachments = sentry_value_new_list(); + sentry_value_append(attachments, attachment); + + const char bytes[] = { 'a', '\0', 'b' }; + const char *id = "00000000-0000-4000-8000-000000000001"; + sentry_value_t bytes_attachment + = sentry_attachment_from_bytes(bytes, sizeof(bytes), "bytes.bin"); + sentry_value_set_by_key( + bytes_attachment, "id", sentry_value_new_string(id)); + sentry_value_append(attachments, bytes_attachment); + for (size_t i = 0; i < sentry_value_get_length(attachments); i++) { + attachment = sentry_value_get_by_index(attachments, i); + sentry_attachment_set_type(attachment, "event.attachment"); + sentry_attachment_set_content_type( + attachment, "application/octet-stream"); + } + sentry_value_freeze(attachments); + TEST_CHECK(!sentry__write_attachment_manifest(NULL, attachments)); + + sentry_path_t *run_path = sentry__path_from_str( + SENTRY_TEST_PATH_PREFIX ".attachment-manifest-run"); + sentry_path_t *dir = sentry__path_join_str(run_path, id); + sentry_path_t *path = sentry__path_join_str(dir, "bytes.bin"); + sentry_path_t *manifest_path + = sentry__path_join_str(run_path, "__sentry-attachments"); + TEST_ASSERT(sentry__path_create_dir_all(dir) == 0); + sentry__path_remove(path); + + // missing and partially written byte attachments are excluded + for (size_t size = 0; size <= sizeof(bytes); size++) { + if (size) { + TEST_ASSERT(sentry__path_write_buffer(path, bytes, size) == 0); + } + TEST_ASSERT( + sentry__write_attachment_manifest(manifest_path, attachments)); + sentry_value_t parsed = sentry__read_attachment_manifest(manifest_path); + TEST_CHECK(sentry_value_get_type(parsed) == SENTRY_VALUE_TYPE_LIST); + TEST_CHECK_INT_EQUAL( + sentry_value_get_length(parsed), size == sizeof(bytes) ? 2 : 1); + for (size_t i = 0; i < sentry_value_get_length(parsed); i++) { + sentry_value_t info = sentry_value_get_by_index(parsed, i); + attachment = sentry_value_get_by_index(attachments, i); + TEST_CHECK_STRING_EQUAL(sentry__attachment_get_path(info), + i == 0 ? "attachment.bin" : path->path); + TEST_CHECK_STRING_EQUAL(sentry__attachment_get_filename(info), + i == 0 ? "renamed.bin" : "bytes.bin"); + TEST_CHECK_STRING_EQUAL( + sentry__attachment_get_type(info), "event.attachment"); + TEST_CHECK_STRING_EQUAL(sentry__attachment_get_content_type(info), + "application/octet-stream"); + sentry_uuid_t info_id = sentry__attachment_get_id(info); + TEST_CHECK(sentry_uuid_is_nil(&info_id)); + TEST_CHECK(sentry__attachment_get_bytes(info, NULL) == NULL); + } + sentry_value_decref(parsed); + } + + // malformed manifest entries are excluded + sentry_value_t malformed = sentry_value_new_object(); + size_t valid_len = 0; + size_t malformed_len = 0; + char *valid_buf = sentry_value_to_msgpack( + sentry_value_get_by_index(attachments, 0), &valid_len); + char *malformed_buf = sentry_value_to_msgpack(malformed, &malformed_len); + char *manifest_buf = sentry_malloc(valid_len + malformed_len); + TEST_ASSERT(valid_buf != NULL); + TEST_ASSERT(malformed_buf != NULL); + TEST_ASSERT(manifest_buf != NULL); + memcpy(manifest_buf, malformed_buf, malformed_len); + memcpy(manifest_buf + malformed_len, valid_buf, valid_len); + TEST_ASSERT(sentry__path_write_buffer( + manifest_path, manifest_buf, valid_len + malformed_len) + == 0); + sentry_value_t salvaged = sentry__read_attachment_manifest(manifest_path); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(salvaged), 1); + TEST_CHECK_STRING_EQUAL( + sentry__attachment_get_filename(sentry_value_get_by_index(salvaged, 0)), + "renamed.bin"); + sentry_value_decref(salvaged); + sentry_free(manifest_buf); + sentry_free(malformed_buf); + sentry_free(valid_buf); + sentry_value_decref(malformed); + + size_t size = 0; + const char *original + = sentry__attachment_get_bytes(bytes_attachment, &size); + TEST_CHECK(size == sizeof(bytes)); + TEST_CHECK(memcmp(original, bytes, sizeof(bytes)) == 0); + TEST_CHECK(sentry__attachment_get_path(bytes_attachment) == NULL); + TEST_CHECK(sentry_value_is_frozen(bytes_attachment)); + char *persisted = sentry__path_read_to_buffer(path, &size); + TEST_ASSERT(persisted != NULL); + TEST_CHECK(size == sizeof(bytes)); + TEST_CHECK(memcmp(persisted, bytes, sizeof(bytes)) == 0); + sentry_free(persisted); + sentry_value_decref(attachments); + + sentry_value_t parsed = sentry__read_attachment_manifest(NULL); + TEST_CHECK(sentry_value_is_null(parsed)); + sentry_value_decref(parsed); + + sentry_path_t *missing_path = sentry__path_from_str( + SENTRY_TEST_PATH_PREFIX ".missing-attachment-manifest"); + sentry__path_remove(missing_path); + parsed = sentry__read_attachment_manifest(missing_path); + TEST_CHECK(sentry_value_is_null(parsed)); + sentry_value_decref(parsed); + sentry__path_free(missing_path); + + TEST_ASSERT( + sentry__path_write_buffer(manifest_path, "incomplete", 10) == 0); + parsed = sentry__read_attachment_manifest(manifest_path); + TEST_CHECK(sentry_value_is_null(parsed)); + sentry_value_decref(parsed); + + sentry_value_t empty = sentry_value_new_list(); + TEST_ASSERT(sentry__write_attachment_manifest(manifest_path, empty)); + parsed = sentry__read_attachment_manifest(manifest_path); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(parsed), 0); + sentry_value_decref(parsed); + sentry_value_decref(empty); + sentry__path_remove(path); + sentry__path_remove(dir); + sentry__path_remove(manifest_path); + sentry__path_remove(run_path); + sentry__path_free(path); + sentry__path_free(dir); + sentry__path_free(manifest_path); + sentry__path_free(run_path); +} diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index 623e5bc97..98d78647f 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -10,6 +10,7 @@ XX(assert_sdk_name) XX(assert_sdk_user_agent) XX(assert_sdk_version) XX(attachment_bytes_no_copy) +XX(attachment_manifest) XX(attachment_placeholder) XX(attachment_properties) XX(attachment_rate_limit)