diff --git a/source/file.c b/source/file.c index 00723555f7..a3d5b0e7e4 100644 --- a/source/file.c +++ b/source/file.c @@ -62,25 +62,43 @@ int aws_byte_buf_init_from_file(struct aws_byte_buf *out_buf, struct aws_allocat goto error; } - size_t allocation_size = (size_t)len64 + 1; - aws_byte_buf_init(out_buf, alloc, allocation_size); - - /* Ensure compatibility with null-terminated APIs, but don't consider - * the null terminator part of the length of the payload */ - out_buf->len = out_buf->capacity - 1; - out_buf->buffer[out_buf->len] = 0; - - size_t read = fread(out_buf->buffer, 1, out_buf->len, fp); - if (read < out_buf->len) { - int errno_value = ferror(fp) ? errno : 0; /* Always cache errno before potential side-effect */ - aws_translate_and_raise_io_error_or(errno_value, AWS_ERROR_FILE_READ_FAILURE); - AWS_LOGF_ERROR( - AWS_LS_COMMON_IO, - "static: Failed reading file:'%s' errno:%d aws-error:%s", - filename, - errno_value, - aws_error_name(aws_last_error())); - goto error; + /* For "special" files, size may be a lie. For example, on Amazon Linux 2: + * /proc/net/tcp: says 0, but it's larger if you read it. + * /sys/devices/virtual/dmi/id/product_name: says 4096, but it's smaller if you read it. + * + * So we'll use file-size as a hint, but read() in a loop until we hit EOF, + * resizing the buffer if necessary as we go. + * + * If file-size == 0, add a bit of capacity to start. + * If file-size != 0, add 1 more to capacity, in case we need to do a 2nd read to learn it's EOF. */ + size_t initial_capacity = len64 == 0 ? 128 : (size_t)len64 + 1; + aws_byte_buf_init(out_buf, alloc, initial_capacity); + while (true) { + /* Add more capacity if necessary */ + if (out_buf->len == out_buf->capacity) { + size_t additional_capacity = aws_min_size(4096, aws_mul_size_saturating(out_buf->capacity, 2)); + aws_byte_buf_reserve_relative(out_buf, additional_capacity); + } + + size_t space_available = out_buf->capacity - out_buf->len; + size_t read = fread(out_buf->buffer + out_buf->len, 1, space_available, fp); + out_buf->len += read; + + if (feof(fp)) { + break; + } + + if (read == 0) { + int errno_value = ferror(fp) ? errno : 0; /* Always cache errno before potential side-effect */ + aws_translate_and_raise_io_error_or(errno_value, AWS_ERROR_FILE_READ_FAILURE); + AWS_LOGF_ERROR( + AWS_LS_COMMON_IO, + "static: Failed reading file:'%s' errno:%d aws-error:%s", + filename, + errno_value, + aws_error_name(aws_last_error())); + goto error; + } } fclose(fp);