s3_checksum_stream.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include "aws/s3/private/s3_checksums.h"
  6. #include <aws/common/encoding.h>
  7. #include <aws/io/stream.h>
  8. struct aws_checksum_stream {
  9. struct aws_input_stream base;
  10. struct aws_allocator *allocator;
  11. struct aws_input_stream *old_stream;
  12. struct aws_s3_checksum *checksum;
  13. struct aws_byte_buf checksum_result;
  14. /* base64 encoded checksum of the stream, updated on destruction of stream */
  15. struct aws_byte_buf *encoded_checksum_output;
  16. };
  17. static int s_aws_input_checksum_stream_seek(
  18. struct aws_input_stream *stream,
  19. int64_t offset,
  20. enum aws_stream_seek_basis basis) {
  21. (void)stream;
  22. (void)offset;
  23. (void)basis;
  24. AWS_LOGF_ERROR(
  25. AWS_LS_S3_CLIENT,
  26. "Cannot seek on checksum stream, as it will cause the checksum output to mismatch the checksum of the stream "
  27. "contents");
  28. AWS_ASSERT(false);
  29. return aws_raise_error(AWS_ERROR_UNSUPPORTED_OPERATION);
  30. }
  31. static int s_aws_input_checksum_stream_read(struct aws_input_stream *stream, struct aws_byte_buf *dest) {
  32. struct aws_checksum_stream *impl = AWS_CONTAINER_OF(stream, struct aws_checksum_stream, base);
  33. size_t original_len = dest->len;
  34. if (aws_input_stream_read(impl->old_stream, dest)) {
  35. return AWS_OP_ERR;
  36. }
  37. struct aws_byte_cursor to_sum = aws_byte_cursor_from_buf(dest);
  38. /* Move the cursor to the part to calculate the checksum */
  39. aws_byte_cursor_advance(&to_sum, original_len);
  40. /* If read failed, `aws_input_stream_read` will handle the error to restore the dest. No need to handle error here
  41. */
  42. return aws_checksum_update(impl->checksum, &to_sum);
  43. }
  44. static int s_aws_input_checksum_stream_get_status(struct aws_input_stream *stream, struct aws_stream_status *status) {
  45. struct aws_checksum_stream *impl = AWS_CONTAINER_OF(stream, struct aws_checksum_stream, base);
  46. return aws_input_stream_get_status(impl->old_stream, status);
  47. }
  48. static int s_aws_input_checksum_stream_get_length(struct aws_input_stream *stream, int64_t *out_length) {
  49. struct aws_checksum_stream *impl = AWS_CONTAINER_OF(stream, struct aws_checksum_stream, base);
  50. return aws_input_stream_get_length(impl->old_stream, out_length);
  51. }
  52. /* We take ownership of the old input stream, and destroy it with this input stream. This is because we want to be able
  53. * to substitute in the chunk_stream for the cursor stream currently used in s_s3_meta_request_default_prepare_request
  54. * which returns the new stream. So in order to prevent the need of keeping track of two input streams we instead
  55. * consume the cursor stream and destroy it with this one */
  56. static void s_aws_input_checksum_stream_destroy(struct aws_checksum_stream *impl) {
  57. if (!impl) {
  58. return;
  59. }
  60. int result = aws_checksum_finalize(impl->checksum, &impl->checksum_result, 0);
  61. if (result != AWS_OP_SUCCESS) {
  62. aws_byte_buf_reset(&impl->checksum_result, true);
  63. }
  64. AWS_ASSERT(result == AWS_OP_SUCCESS);
  65. struct aws_byte_cursor checksum_result_cursor = aws_byte_cursor_from_buf(&impl->checksum_result);
  66. AWS_FATAL_ASSERT(aws_base64_encode(&checksum_result_cursor, impl->encoded_checksum_output) == AWS_OP_SUCCESS);
  67. aws_checksum_destroy(impl->checksum);
  68. aws_input_stream_release(impl->old_stream);
  69. aws_byte_buf_clean_up(&impl->checksum_result);
  70. aws_mem_release(impl->allocator, impl);
  71. }
  72. static struct aws_input_stream_vtable s_aws_input_checksum_stream_vtable = {
  73. .seek = s_aws_input_checksum_stream_seek,
  74. .read = s_aws_input_checksum_stream_read,
  75. .get_status = s_aws_input_checksum_stream_get_status,
  76. .get_length = s_aws_input_checksum_stream_get_length,
  77. };
  78. struct aws_input_stream *aws_checksum_stream_new(
  79. struct aws_allocator *allocator,
  80. struct aws_input_stream *existing_stream,
  81. enum aws_s3_checksum_algorithm algorithm,
  82. struct aws_byte_buf *checksum_output) {
  83. AWS_PRECONDITION(existing_stream);
  84. struct aws_checksum_stream *impl = aws_mem_calloc(allocator, 1, sizeof(struct aws_checksum_stream));
  85. impl->allocator = allocator;
  86. impl->base.vtable = &s_aws_input_checksum_stream_vtable;
  87. impl->checksum = aws_checksum_new(allocator, algorithm);
  88. if (impl->checksum == NULL) {
  89. goto on_error;
  90. }
  91. aws_byte_buf_init(&impl->checksum_result, allocator, impl->checksum->digest_size);
  92. impl->old_stream = aws_input_stream_acquire(existing_stream);
  93. impl->encoded_checksum_output = checksum_output;
  94. aws_ref_count_init(
  95. &impl->base.ref_count, impl, (aws_simple_completion_callback *)s_aws_input_checksum_stream_destroy);
  96. return &impl->base;
  97. on_error:
  98. aws_mem_release(impl->allocator, impl);
  99. return NULL;
  100. }