Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add error codes for file read/write/open failures #1061

Merged
merged 1 commit into from
Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions include/aws/common/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,14 @@ void aws_register_error_info(const struct aws_error_info_list *error_info);
AWS_COMMON_API
void aws_unregister_error_info(const struct aws_error_info_list *error_info);

/**
* Convert a c library io error into an aws error, and raise it.
* If no conversion is found, fallback_aws_error_code is raised.
* Always returns AWS_OP_ERR.
*/
AWS_COMMON_API
int aws_translate_and_raise_io_error_or(int error_no, int fallback_aws_error_code);

/**
* Convert a c library io error into an aws error, and raise it.
* If no conversion is found, AWS_ERROR_SYS_CALL_FAILURE is raised.
Expand Down Expand Up @@ -202,6 +210,9 @@ enum aws_common_error {
AWS_ERROR_INVALID_UTF8,
AWS_ERROR_GET_HOME_DIRECTORY_FAILED,
AWS_ERROR_INVALID_XML,
AWS_ERROR_FILE_OPEN_FAILURE,
AWS_ERROR_FILE_READ_FAILURE,
AWS_ERROR_FILE_WRITE_FAILURE,
AWS_ERROR_END_COMMON_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_COMMON_PACKAGE_ID)
};

Expand Down
2 changes: 2 additions & 0 deletions include/aws/common/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ bool aws_path_exists(const struct aws_string *path);
* fseeko() on linux
*
* whence can either be SEEK_SET or SEEK_END
*
* Returns AWS_OP_SUCCESS, or AWS_OP_ERR (after an error has been raised).
*/
AWS_COMMON_API
int aws_fseek(FILE *file, int64_t offset, int whence);
Expand Down
23 changes: 16 additions & 7 deletions source/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ static struct aws_error_info errors[] = {
),
AWS_DEFINE_ERROR_INFO_COMMON(
AWS_ERROR_SYS_CALL_FAILURE,
"System call failure"),
"System call failure."),
AWS_DEFINE_ERROR_INFO_COMMON(
AWS_ERROR_FILE_INVALID_PATH,
"Invalid file path."),
Expand All @@ -232,7 +232,7 @@ static struct aws_error_info errors[] = {
"User does not have permission to perform the requested action."),
AWS_DEFINE_ERROR_INFO_COMMON(
AWS_ERROR_STREAM_UNSEEKABLE,
"Stream does not support seek operations"),
"Stream does not support seek operations."),
AWS_DEFINE_ERROR_INFO_COMMON(
AWS_ERROR_C_STRING_BUFFER_NOT_NULL_TERMINATED,
"A c-string like buffer was passed but a null terminator was not found within the bounds of the buffer."),
Expand All @@ -244,7 +244,7 @@ static struct aws_error_info errors[] = {
"Attempt to divide a number by zero."),
AWS_DEFINE_ERROR_INFO_COMMON(
AWS_ERROR_INVALID_FILE_HANDLE,
"Invalid file handle"),
"Invalid file handle."),
AWS_DEFINE_ERROR_INFO_COMMON(
AWS_ERROR_OPERATION_INTERUPTED,
"The operation was interrupted."
Expand All @@ -255,16 +255,25 @@ static struct aws_error_info errors[] = {
),
AWS_DEFINE_ERROR_INFO_COMMON(
AWS_ERROR_PLATFORM_NOT_SUPPORTED,
"Feature not supported on this platform"),
"Feature not supported on this platform."),
AWS_DEFINE_ERROR_INFO_COMMON(
AWS_ERROR_INVALID_UTF8,
"Invalid UTF-8"),
"Invalid UTF-8."),
AWS_DEFINE_ERROR_INFO_COMMON(
AWS_ERROR_GET_HOME_DIRECTORY_FAILED,
"Failed to get home directory"),
"Failed to get home directory."),
AWS_DEFINE_ERROR_INFO_COMMON(
AWS_ERROR_INVALID_XML,
"Invalid XML document"),
"Invalid XML document."),
AWS_DEFINE_ERROR_INFO_COMMON(
AWS_ERROR_FILE_OPEN_FAILURE,
"Failed opening file."),
AWS_DEFINE_ERROR_INFO_COMMON(
AWS_ERROR_FILE_READ_FAILURE,
"Failed reading from file."),
AWS_DEFINE_ERROR_INFO_COMMON(
AWS_ERROR_FILE_WRITE_FAILURE,
"Failed writing to file."),
};
/* clang-format on */

Expand Down
16 changes: 12 additions & 4 deletions source/error.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,19 @@ void aws_unregister_error_info(const struct aws_error_info_list *error_info) {
}

int aws_translate_and_raise_io_error(int error_no) {
return aws_translate_and_raise_io_error_or(error_no, AWS_ERROR_SYS_CALL_FAILURE);
}

int aws_translate_and_raise_io_error_or(int error_no, int fallback_aws_error_code) {
switch (error_no) {
case EINVAL:
return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
case ESPIPE:
return aws_raise_error(AWS_ERROR_STREAM_UNSEEKABLE);
/* If useful fallback code provided, raise that instead of AWS_ERROR_INVALID_ARGUMENT,
* which isn't very useful when it bubbles out from deep within some complex system. */
if (fallback_aws_error_code != AWS_ERROR_SYS_CALL_FAILURE) {
return aws_raise_error(fallback_aws_error_code);
} else {
return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
}
case EPERM:
case EACCES:
return aws_raise_error(AWS_ERROR_NO_PERMISSION);
Expand All @@ -217,6 +225,6 @@ int aws_translate_and_raise_io_error(int error_no) {
case ENOTEMPTY:
return aws_raise_error(AWS_ERROR_DIRECTORY_NOT_EMPTY);
default:
return aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE);
return aws_raise_error(fallback_aws_error_code);
}
}
83 changes: 47 additions & 36 deletions source/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,48 +38,59 @@ int aws_byte_buf_init_from_file(struct aws_byte_buf *out_buf, struct aws_allocat

AWS_ZERO_STRUCT(*out_buf);
FILE *fp = aws_fopen(filename, "rb");
if (fp == NULL) {
goto error;
}

if (fp) {
if (fseek(fp, 0L, SEEK_END)) {
int errno_value = errno; /* Always cache errno before potential side-effect */
AWS_LOGF_ERROR(AWS_LS_COMMON_IO, "static: Failed to seek file %s with errno %d", filename, errno_value);
fclose(fp);
return aws_translate_and_raise_io_error(errno_value);
}
int64_t len64 = 0;
if (aws_file_get_length(fp, &len64)) {
AWS_LOGF_ERROR(
AWS_LS_COMMON_IO,
"static: Failed to get file length. file:'%s' error:%s",
filename,
aws_error_name(aws_last_error()));
goto error;
}

size_t allocation_size = (size_t)ftell(fp) + 1;
/* Tell the user that we allocate here and if success they're responsible for the free. */
if (aws_byte_buf_init(out_buf, alloc, allocation_size)) {
fclose(fp);
return AWS_OP_ERR;
}
if (len64 >= SIZE_MAX) {
aws_raise_error(AWS_ERROR_OVERFLOW_DETECTED);
AWS_LOGF_ERROR(
AWS_LS_COMMON_IO,
"static: File too large to read into memory. file:'%s' error:%s",
filename,
aws_error_name(aws_last_error()));
goto error;
}

/* 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;

if (fseek(fp, 0L, SEEK_SET)) {
int errno_value = errno; /* Always cache errno before potential side-effect */
AWS_LOGF_ERROR(AWS_LS_COMMON_IO, "static: Failed to seek file %s with errno %d", filename, errno_value);
aws_byte_buf_clean_up(out_buf);
fclose(fp);
return aws_translate_and_raise_io_error(errno_value);
}
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;
}

size_t read = fread(out_buf->buffer, 1, out_buf->len, fp);
int errno_cpy = errno; /* Always cache errno before potential side-effect */
fclose(fp);
if (read < out_buf->len) {
AWS_LOGF_ERROR(AWS_LS_COMMON_IO, "static: Failed to read file %s with errno %d", filename, errno_cpy);
aws_secure_zero(out_buf->buffer, out_buf->len);
aws_byte_buf_clean_up(out_buf);
return aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE);
}
fclose(fp);
return AWS_OP_SUCCESS;

return AWS_OP_SUCCESS;
error:
if (fp) {
fclose(fp);
}

aws_byte_buf_clean_up_secure(out_buf);
return AWS_OP_ERR;
}

Expand Down
4 changes: 2 additions & 2 deletions source/log_writer.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ static int s_aws_file_writer_write(struct aws_log_writer *writer, const struct a

size_t length = output->len;
if (fwrite(output->bytes, 1, length, impl->log_file) < length) {
int errno_value = errno; /* Always cache errno before potential side-effect */
return aws_translate_and_raise_io_error(errno_value);
int errno_value = ferror(impl->log_file) ? errno : 0; /* Always cache errno before potential side-effect */
return aws_translate_and_raise_io_error_or(errno_value, AWS_ERROR_FILE_WRITE_FAILURE);
}

return AWS_OP_SUCCESS;
Expand Down
4 changes: 2 additions & 2 deletions source/logging.c
Original file line number Diff line number Diff line change
Expand Up @@ -502,8 +502,8 @@ static int s_noalloc_stderr_logger_log(

int write_result = AWS_OP_SUCCESS;
if (fwrite(format_buffer, 1, format_data.amount_written, impl->file) < format_data.amount_written) {
int errno_value = errno; /* Always cache errno before potential side-effect */
aws_translate_and_raise_io_error(errno_value);
int errno_value = ferror(impl->file) ? errno : 0; /* Always cache errno before potential side-effect */
aws_translate_and_raise_io_error_or(errno_value, AWS_ERROR_FILE_WRITE_FAILURE);
write_result = AWS_OP_ERR;
}

Expand Down
4 changes: 2 additions & 2 deletions source/posix/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ FILE *aws_fopen_safe(const struct aws_string *file_path, const struct aws_string
FILE *f = fopen(aws_string_c_str(file_path), aws_string_c_str(mode));
if (!f) {
int errno_cpy = errno; /* Always cache errno before potential side-effect */
aws_translate_and_raise_io_error(errno_cpy);
aws_translate_and_raise_io_error_or(errno_cpy, AWS_ERROR_FILE_OPEN_FAILURE);
AWS_LOGF_ERROR(
AWS_LS_COMMON_IO,
"static: Failed to open file. path:'%s' mode:'%s' errno:%d aws-error:%d(%s)",
Expand Down Expand Up @@ -285,7 +285,7 @@ int aws_fseek(FILE *file, int64_t offset, int whence) {
int errno_value = errno; /* Always cache errno before potential side-effect */

if (result != 0) {
return aws_translate_and_raise_io_error(errno_value);
return aws_translate_and_raise_io_error_or(errno_value, AWS_ERROR_STREAM_UNSEEKABLE);
}

return AWS_OP_SUCCESS;
Expand Down
4 changes: 2 additions & 2 deletions source/windows/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ FILE *aws_fopen_safe(const struct aws_string *file_path, const struct aws_string
aws_wstring_destroy(w_file_path);

if (error) {
aws_translate_and_raise_io_error(error);
aws_translate_and_raise_io_error_or(error, AWS_ERROR_FILE_OPEN_FAILURE);
AWS_LOGF_ERROR(
AWS_LS_COMMON_IO,
"static: Failed to open file. path:'%s' mode:'%s' errno:%d aws-error:%d(%s)",
Expand Down Expand Up @@ -508,7 +508,7 @@ bool aws_path_exists(const struct aws_string *path) {
int aws_fseek(FILE *file, int64_t offset, int whence) {
if (_fseeki64(file, offset, whence)) {
int errno_value = errno; /* Always cache errno before potential side-effect */
return aws_translate_and_raise_io_error(errno_value);
return aws_translate_and_raise_io_error_or(errno_value, AWS_ERROR_STREAM_UNSEEKABLE);
}

return AWS_OP_SUCCESS;
Expand Down