From 62048f543bb3b98b84ffe2aadf8c4955b9da1c10 Mon Sep 17 00:00:00 2001 From: Dengke Date: Fri, 2 Aug 2024 16:42:50 -0700 Subject: [PATCH] initial pass --- include/aws/common/file.h | 14 ++++++++++++++ source/posix/file.c | 18 ++++++++++++++++++ source/windows/file.c | 34 ++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 1 + tests/file_test.c | 35 ++++++++++++++++++++++++++++++----- 5 files changed, 97 insertions(+), 5 deletions(-) diff --git a/include/aws/common/file.h b/include/aws/common/file.h index 23937e866..d5ac5f53a 100644 --- a/include/aws/common/file.h +++ b/include/aws/common/file.h @@ -47,6 +47,8 @@ struct aws_directory_entry { int64_t file_size; }; +struct aws_file_handle; + /** * Invoked during calls to aws_directory_traverse() as an entry is encountered. entry will contain * the parsed directory entry info. @@ -207,6 +209,18 @@ int aws_fseek(FILE *file, int64_t offset, int whence); AWS_COMMON_API int aws_file_get_length(FILE *file, int64_t *length); +/** + * @brief Wrapper for os-specific pwrite implementation. + * + * @param fd + * @param buf + * @param count + * @param offset + * @return AWS_COMMON_API + */ +AWS_COMMON_API +int aws_pwrite(int fd, const void *buf, size_t count, uint64_t offset, size_t *bytes_written); + AWS_EXTERN_C_END AWS_POP_SANE_WARNING_LEVEL diff --git a/source/posix/file.c b/source/posix/file.c index 868112955..6cfcc4639 100644 --- a/source/posix/file.c +++ b/source/posix/file.c @@ -309,3 +309,21 @@ int aws_file_get_length(FILE *file, int64_t *length) { return AWS_OP_SUCCESS; } + +int aws_pwrite(int fd, const void *buf, size_t count, uint64_t offset, size_t *bytes_written) { + if (fd == -1) { + return aws_raise_error(AWS_ERROR_INVALID_FILE_HANDLE); + } +#ifdef AWS_OS_MACOS + /* MacOS doesn't have pwrite64. */ + *bytes_written = pwrite(fd, buf, count, offset); +#else + *bytes_written = pwrite64(fd, buf, count, offset); +#endif + int errno_value = errno; /* Always cache errno before potential side-effect */ + if (*bytes_written == -1) { + /* TODO: maybe some other default error? */ + return aws_translate_and_raise_io_error_or(errno_value, AWS_ERROR_UNKNOWN); + } + return AWS_OP_SUCCESS; +} diff --git a/source/windows/file.c b/source/windows/file.c index b3dfdf92c..fea08751b 100644 --- a/source/windows/file.c +++ b/source/windows/file.c @@ -544,3 +544,37 @@ int aws_file_get_length(FILE *file, int64_t *length) { return AWS_OP_SUCCESS; } + +int aws_pwrite(int fd, const void *buf, size_t count, uint64_t offset, size_t *bytes_written) { + if (fd == -1) { + return aws_raise_error(AWS_ERROR_INVALID_FILE_HANDLE); + } + + HANDLE os_file = (HANDLE)_get_osfhandle(fd); + if (os_file == INVALID_HANDLE_VALUE) { + int errno_value = errno; /* Always cache errno before potential side-effect */ + return aws_translate_and_raise_io_error(errno_value); + } + DWORD bytesWritten; + OVERLAPPED ol = {0}; + LARGE_INTEGER li; + + /* Convert uint64_t offset to OVERLAPPED */ + li.QuadPart = offset; + ol.Offset = li.LowPart; + ol.OffsetHigh = li.HighPart; + + if (!WriteFile(os_file, buf, (DWORD)count, &bytesWritten, &ol)) { + int error = GetLastError(); + + if (error == ERROR_IO_PENDING) { + /* TODO: check for different errors. */ + return aws_raise_error(AWS_ERROR_INVALID_FILE_HANDLE); + } + return aws_raise_error(AWS_ERROR_UNKNOWN); + } + *bytes_written = (size_t)bytesWritten; + /* TODO: verify the fd offset is not affected. */ + /* Never call CloseHandle on the return value of _get_osfhandle */ + return AWS_OP_SUCCESS; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d3556c5f7..41fd172f8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -490,6 +490,7 @@ add_test_case(test_home_directory_not_null) add_test_case(test_normalize_posix_directory_separator) add_test_case(test_normalize_windows_directory_separator) add_test_case(test_byte_buf_init_from_file) +add_test_case(test_aws_pwrite) add_test_case(test_json_parse_from_string) add_test_case(test_json_parse_to_string) diff --git a/tests/file_test.c b/tests/file_test.c index 6a3f4fe8b..0fce44aa6 100644 --- a/tests/file_test.c +++ b/tests/file_test.c @@ -9,14 +9,11 @@ #include #include +#include -static int s_aws_fopen_test_helper(char *file_path, char *content) { +static int s_read_and_verify_helper(char *file_path, char *content) { char read_result[100]; AWS_ZERO_ARRAY(read_result); - FILE *file = aws_fopen(file_path, "w+"); - ASSERT_NOT_NULL(file); - fprintf(file, "%s", content); - fclose(file); FILE *readfile = aws_fopen(file_path, "r"); ASSERT_NOT_NULL(readfile); size_t read_len = fread(read_result, sizeof(char), strlen(content), readfile); @@ -36,6 +33,14 @@ static int s_aws_fopen_test_helper(char *file_path, char *content) { return AWS_OP_SUCCESS; } +static int s_aws_fopen_test_helper(char *file_path, char *content) { + FILE *file = aws_fopen(file_path, "w+"); + ASSERT_NOT_NULL(file); + fprintf(file, "%s", content); + fclose(file); + return s_read_and_verify_helper(file_path, content); +} + static int s_aws_fopen_content_matches(char *file_path, char *content) { char read_result[100]; AWS_ZERO_ARRAY(read_result); @@ -542,3 +547,23 @@ static int s_test_byte_buf_init_from_file(struct aws_allocator *allocator, void } AWS_TEST_CASE(test_byte_buf_init_from_file, s_test_byte_buf_init_from_file) + +static int s_test_aws_pwrite(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + char file_path[] = "sample.txt"; + int fd = open(file_path, O_RDWR | O_NONBLOCK | O_CREAT, 0666); + struct aws_byte_cursor contents = aws_byte_cursor_from_c_str("hello world"); + + size_t byte_written = 0; + aws_pwrite(fd, contents.ptr, contents.len, 0, &byte_written); + aws_pwrite(fd, contents.ptr, contents.len, contents.len, &byte_written); + close(fd); + + /* Verify the written content match expected */ + ASSERT_SUCCESS(s_read_and_verify_helper(file_path, "hello worldhello world")); + + /* TODO: test error handling. */ + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(test_aws_pwrite, s_test_aws_pwrite)