From 63a9d497a2d91776945b0aa9c40b3408a88d210f Mon Sep 17 00:00:00 2001 From: Parth Aggarwal Date: Fri, 17 Jul 2020 16:55:23 -0400 Subject: [PATCH] file_win32 implementation, unit tests and int tests --- interfaces/devdoc/file_requirements.md | 12 +- interfaces/inc/azure_c_pal/file.h | 7 +- interfaces/tests/CMakeLists.txt | 1 + linux/devdoc/file_linux_requirements.md | 19 +- win32/devdoc/file_win32_requirements.md | 67 ++-- win32/src/file_win32.c | 513 ++++++++++++++++++++++-- win32/tests/CMakeLists.txt | 1 + 7 files changed, 542 insertions(+), 78 deletions(-) diff --git a/interfaces/devdoc/file_requirements.md b/interfaces/devdoc/file_requirements.md index da9ee8b..4dfc783 100644 --- a/interfaces/devdoc/file_requirements.md +++ b/interfaces/devdoc/file_requirements.md @@ -38,7 +38,7 @@ MOCKABLE_FUNCTION(, FILE_HANDLE, file_create, EXECUTION_ENGINE_HANDLE, execution MOCKABLE_FUNCTION(, void, file_destroy, FILE_HANDLE, handle); MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_WRITE_ASYNC_RESULT, file_write_async, FILE_HANDLE, handle, const unsigned char*, source, uint32_t, size, uint64_t, position, FILE_WRITE_CB, user_callback, void*, user_context)(FILE_WRITE_ASYNC_OK, FILE_WRITE_ASYNC_ERROR); -MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_READ_ASYNC_RESULT, file_read_async, FILE_HANDLE, handle, uint64_t, position, unsigned char*, destination, uint32_t, size,FILE_READ_CB, user_callback, void*, user_context)(FILE_READ_ASYNC_OK, FILE_READ_ASYNC_ERROR); +MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_READ_ASYNC_RESULT, file_read_async, FILE_HANDLE, handle, unsigned char*, destination, uint64_t, position, uint32_t, size, FILE_CB, user_callback, void*, user_context)(FILE_READ_ASYNC_OK, FILE_READ_ASYNC_ERROR); MOCKABLE_FUNCTION_WITH_RETURNS(, int, file_extend, FILE_HANDLE, handle, uint64_t, desired_size)(0, MU_FAILURE); ``` @@ -49,7 +49,7 @@ MOCKABLE_FUNCTION_WITH_RETURNS(, int, file_extend, FILE_HANDLE, handle, uint64_t MOCKABLE_FUNCTION(, FILE_HANDLE, file_create,FILE_REPORT_FAULT, user_report_fault_callback, void*, user_report_fault_context, EXECUTION_ENGINE_HANDLE, execution_engine, const char*, full_file_name); ``` -If a file by the name `full_file_name` does not exist, `file_create` creates a file by that name, opens it and returns its handle. If the file does exist, `file_create` opens the file and returns its handle. +If a file by the name `full_file_name` does not exist, `file_create` creates a file by that name, opens it and returns its handle. If the file does exist, `file_create` opens the file and returns its handle. `file_create` shall register `user_report_fault_callback` with argument `user_report_fault_context` as the callback function to be called when the callback specified by the user for a specific asynchronous operation cannot be called. **SRS_FILE_43_033: [** If `execution_engine` is `NULL`, `file_create` shall fail and return `NULL`. **]** @@ -61,8 +61,6 @@ If a file by the name `full_file_name` does not exist, `file_create` creates a f **SRS_FILE_43_001: [** `file_create` shall open the file named `full_file_name` for asynchronous operations and return its handle. **]** -**SRS_FILE_43_038: [** `file_create` shall register `user_report_fault_callback` with argument `user_report_fault_context` as the callback function to be called when the callback specified by the user for a specific asynchronous operation cannot be called. **]** - **SRS_FILE_43_034: [** If there are any failures, `file_create` shall fail and return `NULL`. **]** ## file_destroy @@ -91,9 +89,11 @@ MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_WRITE_ASYNC_RESULT, file_write_async, FILE **SRS_FILE_43_010: [** If `source` is `NULL` then `file_write_async` shall fail and return `FILE_WRITE_ASYNC_INVALID_ARGS`. **]** +**SRS_FILE_43_012: [** If `user_callback` is `NULL` then `file_write_async` shall fail and return `FILE_WRITE_ASYNC_INVALID_ARGS`. **]** + **SRS_FILE_43_040: [** If `position` + `size` is greater than `INT64_MAX`, then `file_write_async` shall fail and return `FILE_WRITE_ASYNC_INVALID_ARGS`. **]** -**SRS_FILE_43_012: [** If `user_callback` is `NULL` then `file_write_async` shall fail and return `FILE_WRITE_ASYNC_INVALID_ARGS`. **]** +**SRS_FILE_43_042: [** If `size` is 0 then `file_write_async` shall fail and return `FILE_WRITE_ASYNC_INVALID_ARGS`. **]** **SRS_FILE_43_014: [** `file_write_async` shall enqueue a write request to write `source`'s content to the `position` offset in the file. **]** @@ -121,6 +121,8 @@ MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_READ_ASYNC_RESULT, file_read_async, FILE_H **SRS_FILE_43_020: [** If `user_callback` is `NULL` then `file_read_async` shall fail and return `FILE_READ_ASYNC_INVALID_ARGS`. **]** +**SRS_FILE_43_043: [** If `size` is 0 then `file_read_async` shall fail and return `FILE_READ_ASYNC_INVALID_ARGS`. **]** + **SRS_FILE_43_021: [** `file_read_async` shall enqueue a read request to read `handle`'s content at `position` offset and write it to `destination`. **]** **SRS_FILE_43_036: [** If the call to read the file fails, `file_read_async` shall fail and return `FILE_READ_ASYNC_READ_ERROR`. **]** diff --git a/interfaces/inc/azure_c_pal/file.h b/interfaces/inc/azure_c_pal/file.h index a9c210d..c9e84f6 100644 --- a/interfaces/inc/azure_c_pal/file.h +++ b/interfaces/inc/azure_c_pal/file.h @@ -35,14 +35,13 @@ MU_DEFINE_ENUM(FILE_READ_ASYNC_RESULT, FILE_READ_ASYNC_VALUES); typedef struct FILE_HANDLE_DATA_TAG* FILE_HANDLE; typedef void(*FILE_REPORT_FAULT)(void* user_report_fault_context, const char* information); -typedef void(*FILE_WRITE_CB)(void* user_context, bool is_successful); -typedef void(*FILE_READ_CB)(void* user_context, bool is_successful); +typedef void(*FILE_CB)(void* user_context, bool is_successful); MOCKABLE_FUNCTION(, FILE_HANDLE, file_create, EXECUTION_ENGINE_HANDLE, execution_engine, const char*, full_file_name, FILE_REPORT_FAULT, user_report_fault_callback, void*, user_report_fault_context); MOCKABLE_FUNCTION(, void, file_destroy, FILE_HANDLE, handle); -MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_WRITE_ASYNC_RESULT, file_write_async, FILE_HANDLE, handle, const unsigned char*, source, uint32_t, size, uint64_t, position, FILE_WRITE_CB, user_callback, void*, user_context)(FILE_WRITE_ASYNC_OK, FILE_WRITE_ASYNC_ERROR); -MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_READ_ASYNC_RESULT, file_read_async, FILE_HANDLE, handle, uint64_t, position, unsigned char*, destination, uint32_t, size,FILE_READ_CB, user_callback, void*, user_context)(FILE_READ_ASYNC_OK, FILE_READ_ASYNC_ERROR); +MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_WRITE_ASYNC_RESULT, file_write_async, FILE_HANDLE, handle, const unsigned char*, source, uint32_t, size, uint64_t, position, FILE_CB, user_callback, void*, user_context)(FILE_WRITE_ASYNC_OK, FILE_WRITE_ASYNC_ERROR); +MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_READ_ASYNC_RESULT, file_read_async, FILE_HANDLE, handle, unsigned char*, destination, uint32_t, size, uint64_t, position, FILE_CB, user_callback, void*, user_context)(FILE_READ_ASYNC_OK, FILE_READ_ASYNC_ERROR); MOCKABLE_FUNCTION_WITH_RETURNS(, int, file_extend, FILE_HANDLE, handle, uint64_t, desired_size)(0, MU_FAILURE); #ifdef __cplusplus diff --git a/interfaces/tests/CMakeLists.txt b/interfaces/tests/CMakeLists.txt index 0f7ca83..170e1dd 100644 --- a/interfaces/tests/CMakeLists.txt +++ b/interfaces/tests/CMakeLists.txt @@ -8,6 +8,7 @@ endif() if(${run_int_tests}) add_subdirectory(interlocked_int) + add_subdirectory(file_int) if(MSVC) # waiting for timer to be implemented on linux add_subdirectory(sync_int) diff --git a/linux/devdoc/file_linux_requirements.md b/linux/devdoc/file_linux_requirements.md index 534c9c2..0f19c6e 100644 --- a/linux/devdoc/file_linux_requirements.md +++ b/linux/devdoc/file_linux_requirements.md @@ -31,14 +31,13 @@ MU_DEFINE_ENUM(FILE_READ_ASYNC_RESULT, FILE_READ_ASYNC_VALUES); typedef struct FILE_HANDLE_DATA_TAG* FILE_HANDLE; typedef void(*FILE_REPORT_FAULT)(void* user_report_fault_context, const char* information); -typedef void(*FILE_WRITE_CB)(void* user_context, bool is_successful); -typedef void(*FILE_READ_CB)(void* user_context, bool is_successful); +typedef void(*FILE_CB)(void* user_context, bool is_successful); MOCKABLE_FUNCTION(, FILE_HANDLE, file_create, EXECUTION_ENGINE_HANDLE, execution_engine, const char*, full_file_name, FILE_REPORT_FAULT, user_report_fault_callback, void*, user_report_fault_context); MOCKABLE_FUNCTION(, void, file_destroy, FILE_HANDLE, handle); MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_WRITE_ASYNC_RESULT, file_write_async, FILE_HANDLE, handle, const unsigned char*, source, uint32_t, size, uint64_t, position, FILE_WRITE_CB, user_callback, void*, user_context)(FILE_WRITE_ASYNC_OK, FILE_WRITE_ASYNC_ERROR); -MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_READ_ASYNC_RESULT, file_read_async, FILE_HANDLE, handle, uint64_t, position, unsigned char*, destination, uint32_t, size,FILE_READ_CB, user_callback, void*, user_context)(FILE_READ_ASYNC_OK, FILE_READ_ASYNC_ERROR); +MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_READ_ASYNC_RESULT, file_read_async, FILE_HANDLE, handle, unsigned char*, destination, uint32_t, size, uint64_t, position, FILE_CB, user_callback, void*, user_context)(FILE_READ_ASYNC_OK, FILE_READ_ASYNC_ERROR); MOCKABLE_FUNCTION_WITH_RETURNS(, int, file_extend, FILE_HANDLE, handle, uint64_t, desired_size)(0, MU_FAILURE); ``` @@ -53,6 +52,8 @@ MOCKABLE_FUNCTION(, FILE_HANDLE, file_create, EXECUTION_ENGINE_HANDLE, execution **SRS_FILE_LINUX_43_038: [** If `full_file_name` is `NULL` then `file_create` shall fail and return `NULL`. **]** +**SRS_FILE_LINUX_43_050: [** If full_file_name is an empty string, `file_create` shall fail and return `NULL`. **]** + **SRS_FILE_LINUX_43_029: [** `file_create` shall allocate a `FILE_HANDLE`. **]** **SRS_FILE_LINUX_43_001: [** `file_create` shall call `open` with `full_file_name` as `pathname` and flags `O_CREAT`, `O_RDWR`, `O_DIRECT` and `O_LARGEFILE`. **]** @@ -82,10 +83,12 @@ MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_WRITE_ASYNC_RESULT, file_write_async, FILE **SRS_FILE_LINUX_43_032: [** If `source` is `NULL` then `file_write_async` shall fail and return `FILE_WRITE_ASYNC_INVALID_ARGS`. **]** -**SRS_FILE_LINUX_43_048: [** If `size` is 0 then `file_write_async` shall fail and return `FILE_WRITE_ASYNC_INVALID_ARGS`. **]** +**SRS_FILE_LINUX_43_051: [** If `position` + `size` is greater than `INT64_MAX`, then `file_write_async` shall fail and return `FILE_WRITE_ASYNC_INVALID_ARGS`. **]** **SRS_FILE_LINUX_43_049: [** **SRS_FILE_LINUX_43_033: [** If `user_callback` is `NULL` then `file_write_async` shall fail and return `FILE_WRITE_ASYNC_INVALID_ARGS`. **]** **]** +**SRS_FILE_LINUX_43_048: [** If `size` is 0 then `file_write_async` shall fail and return `FILE_WRITE_ASYNC_INVALID_ARGS`. **]** + **SRS_FILE_LINUX_43_019: [** `file_write_async` shall allocate a struct to hold `handle`, `source`, `size`, `user_callback` and `user_context`. **]** **SRS_FILE_LINUX_43_004: [** `file_write_async` shall allocate a `sigevent` struct with `SIGEV_THREAD` as `sigev_notify`, the allocated struct as `sigev_value`, `on_file_write_complete_linux` as `sigev_notify_function`, `NULL` as `sigev_notify_attributes`. **]** @@ -103,7 +106,7 @@ MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_WRITE_ASYNC_RESULT, file_write_async, FILE ## file_read_async ```c -MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_READ_ASYNC_RESULT, file_read_async, FILE_HANDLE, handle, uint64_t, position, unsigned char*, destination, uint32_t, size,FILE_READ_CB, user_callback, void*, user_context)(FILE_READ_ASYNC_OK, FILE_READ_ASYNC_ERROR); +MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_READ_ASYNC_RESULT, file_read_async, FILE_HANDLE, handle, unsigned char*, destination, uint32_t, size, uint64_t, position, FILE_CB, user_callback, void*, user_context)(FILE_READ_ASYNC_OK, FILE_READ_ASYNC_ERROR); ``` **SRS_FILE_LINUX_43_034: [** If `handle` is `NULL` then `file_read_async` shall fail and return `FILE_READ_ASYNC_INVALID_ARGS`. **]** @@ -112,6 +115,8 @@ MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_READ_ASYNC_RESULT, file_read_async, FILE_H **SRS_FILE_LINUX_43_035: [** If `user_callback` is `NULL` then `file_read_async` shall fail and return `FILE_READ_ASYNC_INVALID_ARGS`. **]** +**SRS_FILE_LINUX_43_052: [** If `size` is 0 then `file_read_async` shall fail and return `FILE_READ_ASYNC_INVALID_ARGS`. **]** + **SRS_FILE_LINUX_43_045: [** `file_read_async` shall allocate a struct to hold `handle`, `destination`, `user_callback` and `user_context`. **]** **SRS_FILE_LINUX_43_008: [** `file_read_async` shall allocate a `sigevent` struct with `SIGEV_THREAD` as `sigev_notify`, `user_context` as `sigev_value`, `on_file_read_complete_linux` as `sigev_notify_function`, `NULL` as `sigev_notify_attributes`. **]** @@ -142,7 +147,7 @@ Will be implemented later. static void on_file_write_complete_linux( FILE_LINUX_WRITE* write_info); ``` -`on_file_write_complete_linux` is called when an asynchronous write operation completes. +`on_file_write_complete_linux` is called when an asynchronous write operation completes. `on_file_write_complete_linux` calls the user-specified callback with the user-specified context and a bool indicating the success or failure of the asynchronous operation. **SRS_FILE_LINUX_43_047: [** If `write_info` is `NULL`, `on_file_write_complete_linux` shall call `user_report_fault_callback` with `user_report_fault_context`. **]** @@ -160,7 +165,7 @@ static void on_file_write_complete_linux( FILE_LINUX_WRITE* write_info); static void on_file_read_complete_linux( FILE_LINUX_READ* read_info); ``` -`on_file_read_complete_linux` is called when an asynchronous read operation completes. +`on_file_read_complete_linux` is called when an asynchronous read operation completes. `on_file_write_complete_linux` calls the user-specified callback with the user-specified context and a bool indicating the success or failure of the asynchronous operation. **SRS_FILE_LINUX_43_046: [** If `read_info` is `NULL`, `on_file_write_complete_linux` shall call `user_report_fault_callback` with `user_report_fault_context`. **]** diff --git a/win32/devdoc/file_win32_requirements.md b/win32/devdoc/file_win32_requirements.md index 90bff9e..e4be355 100644 --- a/win32/devdoc/file_win32_requirements.md +++ b/win32/devdoc/file_win32_requirements.md @@ -25,14 +25,13 @@ MU_DEFINE_ENUM(FILE_READ_ASYNC_RESULT, FILE_READ_ASYNC_VALUES); typedef struct FILE_HANDLE_DATA_TAG* FILE_HANDLE; typedef void(*FILE_REPORT_FAULT)(void* user_report_fault_context, const char* information); -typedef void(*FILE_WRITE_CB)(void* user_context, bool is_successful); -typedef void(*FILE_READ_CB)(void* user_context, bool is_successful); +typedef void(*FILE_CB)(void* user_context, bool is_successful); MOCKABLE_FUNCTION(, FILE_HANDLE, file_create, EXECUTION_ENGINE_HANDLE, execution_engine, const char*, full_file_name, FILE_REPORT_FAULT, user_report_fault_callback, void*, user_report_fault_context); MOCKABLE_FUNCTION(, void, file_destroy, FILE_HANDLE, handle); MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_WRITE_ASYNC_RESULT, file_write_async, FILE_HANDLE, handle, const unsigned char*, source, uint32_t, size, uint64_t, position, FILE_WRITE_CB, user_callback, void*, user_context)(FILE_WRITE_ASYNC_OK, FILE_WRITE_ASYNC_ERROR); -MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_READ_ASYNC_RESULT, file_read_async, FILE_HANDLE, handle, uint64_t, position, unsigned char*, destination, uint32_t, size,FILE_READ_CB, user_callback, void*, user_context)(FILE_READ_ASYNC_OK, FILE_READ_ASYNC_ERROR); +MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_READ_ASYNC_RESULT, file_read_async, FILE_HANDLE, handle, unsigned char*, destination, uint32_t, size, uint64_t, position, FILE_CB, user_callback, void*, user_context)(FILE_READ_ASYNC_OK, FILE_READ_ASYNC_ERROR); MOCKABLE_FUNCTION_WITH_RETURNS(, int, file_extend, FILE_HANDLE, handle, uint64_t, desired_size)(0, MU_FAILURE); ``` @@ -47,23 +46,25 @@ MOCKABLE_FUNCTION(, FILE_HANDLE, file_create, EXECUTION_ENGINE_HANDLE, execution **SRS_FILE_WIN32_43_048: [** If `full_file_name` is `NULL` then `file_create` shall fail and return `NULL`. **]** +**SRS_FILE_WIN32_43_059: [** If full_file_name is an empty string, `file_create` shall fail and return `NULL`. **]** + **SRS_FILE_WIN32_43_041: [** `file_create` shall allocate a `FILE_HANDLE`. **]** **SRS_FILE_WIN32_43_001: [** `file_create` shall call `CreateFileA` with `full_file_name` as `lpFileName`, `GENERIC_READ|GENERIC_WRITE` as `dwDesiredAccess`, `FILE_SHARED_READ` as `dwShareMode`, `NULL` as `lpSecurityAttributes`, `OPEN_ALWAYS` as `dwCreationDisposition`, `FILE_FLAG_OVERLAPPED|FILE_FLAG_WRITE_THROUGH` as `dwFlagsAndAttributes` and `NULL` as `hTemplateFile`. **]** -**SRS_FILE_WIN32_43_002: [** `file_create` shall call `SetFileCompletionNotificationModes` to disable calling the completion port when an async operations finishes synchrounously. **]** +**SRS_FILE_WIN32_43_002: [** `file_create` shall call `SetFileCompletionNotificationModes` to disable calling the completion port when an async operations finishes synchronously. **]** -**SRS_FILE_WIN32_43_003: [** `file_create` shall allocate a threadpool environment. **]** +**SRS_FILE_WIN32_43_003: [** `file_create` shall initialize a threadpool environment by calling `InitializeThreadpolEnvironment`. **]** **SRS_FILE_WIN32_43_004: [** `file_create` shall obtain a `PTP_POOL` struct by calling `execution_engine_win32_get_threadpool` on `execution_engine`. **]** -**SRS_FILE_WIN32_43_005: [** `file_create` shall register the threadpool environment with `ptpPool` **]** +**SRS_FILE_WIN32_43_005: [** `file_create` shall register the threadpool environment by calling `SetThreadpoolCallbackPool` on the initialized threadpool environment and the obtained `ptp_pool` **]** -**SRS_FILE_WIN32_43_006: [** `file_create` shall create a cleanup group. **]** +**SRS_FILE_WIN32_43_006: [** `file_create` shall create a cleanup group by calling `CreateThreadpoolCleanupGroup`. **]** -**SRS_FILE_WIN32_43_007: [** `file_create` shall register the cleanup group with the threadpool environment. **]** +**SRS_FILE_WIN32_43_007: [** `file_create` shall register the cleanup group with the threadpool environment by calling `SetThreadpoolCallbackCleanupGroup`. **]** -**SRS_FILE_WIN32_43_033: [** `file_create` shall create a threadpool io with the allocated `FILE_HANDLE` and `on_file_io_complete_win32` as a callback. **]** +**SRS_FILE_WIN32_43_033: [** `file_create` shall create a threadpool io with the allocated `FILE_HANDLE` and `on_file_io_complete_win32` as a callback by calling `CreateThreadpoolIo` **]** **SRS_FILE_WIN32_43_008: [** If there are any failures, `file_create` shall return `NULL`. **]** @@ -77,17 +78,17 @@ MOCKABLE_FUNCTION(, void, file_destroy, FILE_HANDLE, handle); **SRS_FILE_WIN32_43_049: [** If `handle` is `NULL`, `file_destroy` shall return. **]** -**SRS_FILE_WIN32_43_011: [** `file_destroy` shall wait for all I/O to complete. **]** +**SRS_FILE_WIN32_43_011: [** `file_destroy` shall wait for all I/O to complete by calling `WaitForThreadpoolIoCallbacks`. **]** -**SRS_FILE_WIN32_43_012: [** `file_destroy` shall close the cleanup group. **]** +**SRS_FILE_WIN32_43_012: [** `file_destroy` shall close the cleanup group by calling `CloseThreadpoolCleanupGroup`. **]** -**SRS_FILE_WIN32_43_013: [** `file_destroy` shall destroy the environment. **]** +**SRS_FILE_WIN32_43_013: [** `file_destroy` shall destroy the environment by calling `DestroyThreadpoolEnvironment`. **]** -**SRS_FILE_WIN32_43_016: [** `file_destroy` shall call `CloseHandle` on `handle` **]** +**SRS_FILE_WIN32_43_016: [** `file_destroy` shall call `CloseHandle` on `handle->h_file` **]** -**SRS_FILE_WIN32_43_015: [** `file_destroy` shall close the threadpool IO. **]** +**SRS_FILE_WIN32_43_015: [** `file_destroy` shall close the threadpool IO by calling `CloseThreadPoolIo`. **]** -**SRS_FILE_WIN32_43_042: [** `file_destroy` shall free the `FILE_HANDLE`. **]** +**SRS_FILE_WIN32_43_042: [** `file_destroy` shall free the `handle`. **]** ## file_write_async @@ -102,13 +103,17 @@ MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_WRITE_ASYNC_RESULT, file_write_async, FILE **SRS_FILE_WIN32_43_045: [** If `user_callback` is `NULL` then `file_write_async` shall fail and return `FILE_WRITE_ASYNC_INVALID_ARGS`. **]** +**SRS_FILE_WIN32_43_060: [** If `position` + `size` is greater than `INT64_MAX`, then `file_write_async` shall fail and return `FILE_WRITE_ASYNC_INVALID_ARGS`. **]** + +**SRS_FILE_WIN32_43_061: [** If `size` is 0 then `file_write_async` shall fail and return `FILE_WRITE_ASYNC_INVALID_ARGS`. **]** + **SRS_FILE_WIN32_43_017: [** `file_write_async` shall call `StartThreadpoolIo`. **]** **SRS_FILE_WIN32_43_054: [** `file_write_async` shall create an event by calling `CreateEvent`. **]** **SRS_FILE_WIN32_43_020: [** `file_write_async` shall allocate an `OVERLAPPED` struct and populate it with the created event and `position`. **]** -**SRS_FILE_WIN32_43_018: [** `file_write_async` shall allocate a context to store the allocated `OVERLAPPED` struct, `handle`, write as the type of asynchronous operation, `source`, `user_callback` and `user_context`. **]** +**SRS_FILE_WIN32_43_018: [** `file_write_async` shall allocate a context to store the allocated `OVERLAPPED` struct, `handle`, `size`, `user_callback` and `user_context`. **]** **SRS_FILE_WIN32_43_021: [** `file_write_async` shall call `WriteFile` with `handle`, `source`, `size` and the allocated `OVERLAPPED` struct. **]** @@ -118,10 +123,12 @@ MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_WRITE_ASYNC_RESULT, file_write_async, FILE **SRS_FILE_WIN32_43_024: [** If `WriteFile` succeeds synchronously then `file_write_async` shall succeed, call `CancelThreadpoolIo`, call `user_callback` and return `FILE_WRITE_ASYNC_OK`. **]** +**SRS_FILE_WIN32_43_057: [** If there are any other failures, `file_write_async` shall fail and return `FILE_WRITE_ASYNC_ERROR`. **]** + ## file_read_async ```c -MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_READ_ASYNC_RESULT, file_read_async, FILE_HANDLE, handle, uint64_t, position, unsigned char*, destination, uint32_t, size,FILE_READ_CB, user_callback, void*, user_context)(FILE_READ_ASYNC_OK, FILE_READ_ASYNC_ERROR); +MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_READ_ASYNC_RESULT, file_read_async, FILE_HANDLE, handle, unsigned char*, destination, uint32_t, size, uint64_t, position, FILE_CB, user_callback, void*, user_context)(FILE_READ_ASYNC_OK, FILE_READ_ASYNC_ERROR); ``` **SRS_FILE_WIN32_43_046: [** If `handle` is `NULL` then `file_read_async` shall fail and return `FILE_READ_ASYNC_INVALID_ARGS`. **]** @@ -130,22 +137,26 @@ MOCKABLE_FUNCTION_WITH_RETURNS(, FILE_READ_ASYNC_RESULT, file_read_async, FILE_H **SRS_FILE_WIN32_43_047: [** If `user_callback` is `NULL` then `file_read_async` shall fail and return `FILE_READ_ASYNC_INVALID_ARGS`. **]** +**SRS_FILE_WIN32_43_062: [** If `size` is 0 then `file_read_async` shall fail and return `FILE_READ_ASYNC_INVALID_ARGS`. **]** + **SRS_FILE_WIN32_43_025: [** `file_read_async` shall call `StartThreadpoolIo`. **]** -**SRS_FILE_WIN32_43_055: [** `file_write_async` shall create an event by calling `CreateEvent`. **]** +**SRS_FILE_WIN32_43_055: [** `file_read_async` shall create an event by calling `CreateEvent`. **]** **SRS_FILE_WIN32_43_028: [** `file_read_async` shall allocate an `OVERLAPPED` struct and populate it with the created event and `position`. **]** -**SRS_FILE_WIN32_43_026: [** `file_read_async` shall allocate a context to store the allocated `OVERLAPPED` struct, `destination`, `handle`, read as the type of asynchronous operation, `user_callback` and `user_context` **]** +**SRS_FILE_WIN32_43_026: [** `file_read_async` shall allocate a context to store the allocated `OVERLAPPED` struct, `destination`, `handle`, `size`, `user_callback` and `user_context` **]** **SRS_FILE_WIN32_43_029: [** `file_read_async` shall call `ReadFile` with `handle`, `destination`, `size` and the allocated `OVERLAPPED` struct. **]** **SRS_FILE_WIN32_43_030: [** If `ReadFile` fails synchronously and `GetLastError` indicates `ERROR_IO_PENDING` then `file_read_async` shall succeed and return `FILE_READ_ASYNC_OK`. **]** -**SRS_FILE_WIN32_43_031: [** If `ReadFile` fails synchronously and `GetLastError` does not indicate `ERROR_IO_PENDING` then `file_read_async` shall fail, call `CancelThreadpoolIo` and return `FILE_READ_ASYNC_WRITE_ERROR`. **]** +**SRS_FILE_WIN32_43_031: [** If `ReadFile` fails synchronously and `GetLastError` does not indicate `ERROR_IO_PENDING` then `file_read_async` shall fail, call `CancelThreadpoolIo` and return `FILE_READ_ASYNC_READ_ERROR`. **]** **SRS_FILE_WIN32_43_032: [** If `ReadFile` succeeds synchronously then `file_read_async` shall succeed, call `CancelThreadpoolIo`, call `user_callback` and return `FILE_READ_ASYNC_OK`. **]** +**SRS_FILE_WIN32_43_058: [** If there are any other failures, `file_read_async` shall fail and return `FILE_READ_ASYNC_ERROR`. **]** + ## file_extend ```c @@ -166,18 +177,14 @@ static void on_file_io_complete_win32( PTP_IO io ); ``` -`on_file_io_complete_win32` is called when a file operation completes asynchronously. +`on_file_io_complete_win32` is called when a file operation completes asynchronously. `on_file_io_complete_win32` calls the user-specified callback with the user-specified context and a bool indicating the success or failure of the asynchronous operation. If `on_file_io_complete_win32` is not able to recover the user-specified callback, it shall call the fault callback specified while creating the file. -**SRS_FILE_WIN32_43_057: [** If `overlapped` is `NULL`, `on_file_io_complete_win32` shall call `user_report_fault_callback` with `user_report_fault_context`. **]** +**SRS_FILE_WIN32_43_034: [** `on_file_io_complete_win32` shall recover the file handle, the number of bytes requested by the user, `user_callback` and `user_context` from the context containing `overlapped`. **]** -**SRS_FILE_WIN32_43_034: [** `on_file_io_complete_win32` shall recover the context containing `overlapped`. **]** +**SRS_FILE_WIN32_43_063: [** `on_file_io_complete_win32` shall call `GetOverlappedResult` to determine if the asynchronous operation was successful. **]** -**SRS_FILE_WIN32_43_053: [** `on_file_io_complete_win32` shall determine the type of asynchronous operation from the context. **]** +**SRS_FILE_WIN32_43_067: [** `on_file_io_complete_win32` shall compare `number_of_bytes_transferred` to the number of bytes requested by the user. **]** -**SRS_FILE_WIN32_43_056: [** If the type of asynchronous operation is neither read nor write, `on_file_io_complete_win32` shall call `user_report_fault_callback` with `user_report_fault_context`. **]** +**SRS_FILE_WIN32_43_066: [** `on_file_io_complete_win32` shall call `user_callback` with `is_successful` as `true` if and only if `GetOverlappedResult` returns `true` and `number_of_bytes_transferred` is equal to the number of bytes requested by the user. **]** -**SRS_FILE_WIN32_43_035: [** If `io_result` is not `NO_ERROR`, `on_file_io_complete_win32` shall call `user_callback` with `false` as `is_successful`. **]** - -**SRS_FILE_WIN32_43_036: [** If the type of the asynchronous operation is read, `on_file_io_complete_win32` shall call `user_callback` with `user_context` and `true` as `is_successful`. **]** - -**SRS_FILE_WIN32_43_039: [** If the type of the asynchronous operation is write, `on_file_io_complete_win32` shall call `user_callback` with `user_context` and `true` as `is_successful`. **]** \ No newline at end of file +**SRS_FILE_WIN32_43_068: [** If either `GetOverlappedResult` returns `false` or `number_of_bytes_transferred` is not equal to the bytes requested by the user, `on_file_io_complete_win32` shall return `false`. **]** diff --git a/win32/src/file_win32.c b/win32/src/file_win32.c index 46d2219..85782dc 100644 --- a/win32/src/file_win32.c +++ b/win32/src/file_win32.c @@ -1,32 +1,18 @@ // Copyright (C) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +#include +#include +#include +#include + #include "windows.h" + +#include "azure_c_logging/xlogging.h" +#include "execution_engine_win32.h" +#include "azure_c_pal/gballoc.h" #include "azure_c_pal/file.h" -typedef struct FILE_WRITE_DATA_CONTEXT_TAG -{ - FILE_WRITE_CB user_callback; - void* user_context; - const unsigned char* source; - uint32_t size; -}FILE_WRITE_DATA_CONTEXT; - -typedef struct FILE_READ_DATA_CONTEXT_TAG -{ - FILE_READ_CB user_callback; - void* user_context; - unsigned char* destination; - uint32_t size; -}FILE_READ_DATA_CONTEXT; - -#define FILE_ASYNC_OPERATION_VALUES \ - FILE_ASYNC_WRITE, \ - FILE_ASYNC_READ - -MU_DEFINE_ENUM(FILE_ASYNC_OPERATION, FILE_ASYNC_OPERATION_VALUES) -MU_DEFINE_ENUM_STRINGS(FILE_ASYNC_OPERATION, FILE_ASYNC_OPERATION_VALUES) - typedef struct FILE_HANDLE_DATA_TAG { PTP_POOL ptp_pool; @@ -39,18 +25,13 @@ typedef struct FILE_HANDLE_DATA_TAG }FILE_HANDLE_DATA; -typedef union FILE_IO_DATA_TAG -{ - FILE_READ_DATA_CONTEXT read_data; - FILE_WRITE_DATA_CONTEXT write_data; -}FILE_IO_DATA; - typedef struct FILE_WIN32_IO_TAG { OVERLAPPED ov; FILE_HANDLE handle; - FILE_ASYNC_OPERATION type; - FILE_IO_DATA data; + FILE_CB user_callback; + void* user_context; + uint32_t size; }FILE_WIN32_IO; static void on_file_io_complete_win32( @@ -60,4 +41,472 @@ static void on_file_io_complete_win32( ULONG io_result, ULONG_PTR number_of_bytes_transferred, PTP_IO io -); \ No newline at end of file +); + +static VOID NTAPI on_close_threadpool_group_member( + PVOID object_context, /*what was passed @ CreateThreadpoolIo*/ + PVOID cleanup_context /*what was passed @ CloseThreadpoolCleanupGroupMembers*/ +) +{ + (void)object_context; + (void)cleanup_context; +} + +IMPLEMENT_MOCKABLE_FUNCTION( + , + FILE_HANDLE, file_create, + EXECUTION_ENGINE_HANDLE, execution_engine, + const char*, full_file_name, + FILE_REPORT_FAULT, user_report_fault_callback, + void*, user_report_fault_context +) +{ + FILE_HANDLE handle = (FILE_HANDLE)NULL; + bool result = true; + if + ( + /*Codes_SRS_FILE_43_033: [ If execution_engine is NULL, file_create shall fail and return NULL. ]*/ + /*Codes_SRS_FILE_WIN32_43_040: [ If execution_engine is NULL, file_create shall fail and return NULL. ]*/ + (execution_engine == NULL) || + /*Codes_SRS_FILE_43_002: [ If full_file_name is NULL then file_create shall fail and return NULL. ]*/ + /*Codes_SRS_FILE_WIN32_43_048: [ If full_file_name is NULL then file_create shall fail and return NULL. ]*/ + (full_file_name == NULL) || + /*Codes_SRS_FILE_43_037: [ If full_file_name is an empty string, file_create shall fail and return NULL. ]*/ + /*Codes_SRS_FILE_WIN32_43_059: [ If full_file_name is an empty string, file_create shall fail and return NULL. ]*/ + (strlen(full_file_name) == 0) + ) + { + LogError("Invalid arguments to file_create: EXECUTION_ENGINE_HANDLE execution_engine=%p, const char* full_file_name=%s, FILE_REPORT_FAULT user_report_callback=%p, void* user_report_faul_context=%p", + execution_engine, full_file_name, user_report_fault_callback, user_report_fault_context); + result = false; + } + else + { + /*Codes_SRS_FILE_WIN32_43_041: [ file_create shall allocate a FILE_HANDLE. ]*/ + handle = malloc(sizeof(FILE_HANDLE_DATA)); + if (handle == NULL) { + LogError("Failure in malloc"); + result = false; + } + else + { + /*Codes_SRS_FILE_43_003: [ If a file with name full_file_name does not exist, file_create shall create a file with that name.]*/ + /*Codes_SRS_FILE_43_001: [ file_create shall open the file named full_file_name for asynchronous operations and return its handle. ]*/ + /*Codes_SRS_FILE_WIN32_43_001: [ file_create shall call CreateFileA with full_file_name as lpFileName, GENERIC_READ|GENERIC_WRITE as dwDesiredAccess, FILE_SHARED_READ as dwShareMode, NULL as lpSecurityAttributes, OPEN_ALWAYS as dwCreationDisposition, FILE_FLAG_OVERLAPPED|FILE_FLAG_WRITE_THROUGH as dwFlagsAndAttributes and NULL as hTemplateFile. ]*/ + if ((handle->h_file = CreateFileA( + full_file_name, /*_In_ LPCTSTR lpFileName*/ + GENERIC_READ | GENERIC_WRITE, /*_In_ DWORD dwDesiredAccess*/ + FILE_SHARE_READ, /*_In_ DWORD dwShareMode*/ + NULL, /*_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes*/ + OPEN_ALWAYS, /*_In_ DWORD dwCreationDisposition*/ + FILE_FLAG_OVERLAPPED | FILE_FLAG_WRITE_THROUGH, /*_In_ DWORD dwFlagsAndAttributes*/ + NULL /*_In_opt_ HANDLE hTemplateFile*/ + )) == INVALID_HANDLE_VALUE) + { + LogError("Failure in CreateFileA, full_file_name=%s", full_file_name); + result = false; + } + else + { + /*Codes_SRS_FILE_WIN32_43_002: [ file_create shall call SetFileCompletionNotificationModes to disable calling the completion port when an async operations finishes synchrounously.]*/ + if (!SetFileCompletionNotificationModes(handle->h_file, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) + { + LogError("Failure in SetFileCompletionNotificationModes, full_file_name=%s", full_file_name); + result = false; + } + else + { + /*Codes_SRS_FILE_WIN32_43_003: [ file_create shall initialize a threadpool environment by calling InitializeThreadpolEnvironment.]*/ + InitializeThreadpoolEnvironment(&handle->cbe); + + /*Codes_SRS_FILE_WIN32_43_004: [ file_create shall obtain a PTP_POOL struct by calling execution_engine_win32_get_threadpool on execution_engine.]*/ + handle->ptp_pool = execution_engine_win32_get_threadpool(execution_engine); + + /*Codes_SRS_FILE_WIN32_43_005: [ file_create shall register the threadpool environment by calling SetThreadpoolCallbackPool on the initialized threadpool environment and the obtained ptp_pool ]*/ + SetThreadpoolCallbackPool(&handle->cbe, handle->ptp_pool); + + /*Codes_SRS_FILE_WIN32_43_006: [ file_create shall create a cleanup group by calling CreateThreadpoolCleanupGroup.]*/ + handle->ptp_cleanup_group = CreateThreadpoolCleanupGroup(); + + if (handle->ptp_cleanup_group == NULL) + { + LogError("Failure in CreateThreadpoolCleanupGroup, full_file_name=%s", full_file_name); + result = false; + } + else + { + /*Codes_SRS_FILE_WIN32_43_007: [ file_create shall register the cleanup group with the threadpool environment by calling SetThreadpoolCallbackCleanupGroup.]*/ + SetThreadpoolCallbackCleanupGroup(&handle->cbe, handle->ptp_cleanup_group, on_close_threadpool_group_member); + + /*Codes_SRS_FILE_WIN32_43_033: [ file_create shall create a threadpool io with the allocated FILE_HANDLE and on_file_io_complete_win32 as a callback by calling CreateThreadpoolIo]*/ + handle->ptp_io = CreateThreadpoolIo(handle->h_file, on_file_io_complete_win32, NULL, &handle->cbe); + + if (handle->ptp_io == NULL) + { + LogError("Failure in CreateThreadpoolIo, full_file_name=%s", full_file_name); + result = false; + } + if (!result) + { + CloseThreadpoolCleanupGroup(handle->ptp_cleanup_group); + } + } + if (!result) + { + DestroyThreadpoolEnvironment(&handle->cbe); + } + } + } + /*Codes_SRS_FILE_43_034: [ If there are any failures, file_create shall fail and return NULL. ]*/ + if (!result) + { + free(handle); + } + } + } + /*Codes_SRS_FILE_WIN32_43_008: [ If there are any failures, file_create shall return NULL.]*/ + if (!result) + { + return NULL; + } + + /*Codes_SRS_FILE_43_038: [ file_create shall register user_report_fault_callback with argument user_report_fault_context as the callback function to be called when the callback specified by the user for a specific asynchronous operation cannot be called. ]*/ + handle->user_report_fault_callback = user_report_fault_callback; + handle->user_report_fault_context = user_report_fault_context; + /*Codes_SRS_FILE_WIN32_43_009: [ file_create shall succeed and return a non - NULL value.]*/ + return handle; +} + + +IMPLEMENT_MOCKABLE_FUNCTION(, void, file_destroy, FILE_HANDLE, handle) +{ + /*Codes_SRS_FILE_43_005: [ If handle is NULL, file_destroy shall return. ]*/ + /*Codes_SRS_FILE_WIN32_43_049: [ If handle is NULL, file_destroy shall return.]*/ + if (handle != NULL) + { + /*Codes_SRS_FILE_43_006: [ file_destroy shall wait for all pending I/O operations to complete. ]*/ + /*Codes_SRS_FILE_WIN32_43_011: [ file_destroy shall wait for all I/O to complete by calling WaitForThreadpoolIoCallbacks. ]*/ + WaitForThreadpoolIoCallbacks(handle->ptp_io, FALSE); + /*Codes_SRS_FILE_WIN32_43_012: [ file_destroy shall close the cleanup group by calling CloseThreadpoolCleanupGroup. ]*/ + CloseThreadpoolCleanupGroup(handle->ptp_cleanup_group); + /*Codes_SRS_FILE_WIN32_43_013: [ file_destroy shall destroy the environment by calling DestroyThreadpoolEnvironment. ]*/ + DestroyThreadpoolEnvironment(&(handle->cbe)); + /*Codes_SRS_FILE_43_007: [ file_destroy shall close the file handle handle. ]*/ + /*Codes_SRS_FILE_WIN32_43_016: [ file_destroy shall call CloseHandle on handle->h_file ]*/ + if (!CloseHandle(handle->h_file)) + { + LogError("failure in CloseHandle"); + } + /*Codes_SRS_FILE_WIN32_43_015: [ file_destroy shall close the threadpool IO by calling CloseThreadPoolIo. ]*/ + CloseThreadpoolIo(handle->ptp_io); + /*Codes_SRS_FILE_WIN32_43_042: [ file_destroy shall free the handle.]*/ + free(handle); + } + else + { + LogError("Invalid argument to file_destroy: FILE_HANDLE=%p", handle); + } +} + +IMPLEMENT_MOCKABLE_FUNCTION(, FILE_WRITE_ASYNC_RESULT, file_write_async, FILE_HANDLE, handle, const unsigned char*, source, uint32_t, size, uint64_t, position, FILE_CB, user_callback, void*, user_context) +{ + FILE_WRITE_ASYNC_RESULT result; + if + ( + /*Codes_SRS_FILE_43_009: [ If handle is NULL then file_write_async shall fail and return FILE_WRITE_ASYNC_INVALID_ARGS. ]*/ + /*Codes_SRS_FILE_WIN32_43_043: [ If handle is NULL then file_write_async shall fail and return FILE_WRITE_ASYNC_INVALID_ARGS.]*/ + (handle == NULL) || + /*Codes_SRS_FILE_43_010: [ If source is NULL then file_write_async shall fail and return FILE_WRITE_ASYNC_INVALID_ARGS. ]*/ + /*Codes_SRS_FILE_WIN32_43_044: [ If source is NULL then file_write_async shall fail and return FILE_WRITE_ASYNC_INVALID_ARGS.]*/ + (source == NULL) || + /*Codes_SRS_FILE_43_012: [ If user_callback is NULL then file_write_async shall fail and return FILE_WRITE_ASYNC_INVALID_ARGS. ]*/ + /*Codes_SRS_FILE_WIN32_43_045: [ If user_callback is NULL then file_write_async shall fail and return FILE_WRITE_ASYNC_INVALID_ARGS.]*/ + (user_callback == NULL)|| + /*Codes_SRS_FILE_43_040: [ If position + size is greater than INT64_MAX, then file_write_async shall fail and return FILE_WRITE_ASYNC_INVALID_ARGS. ]*/ + /*Codes_SRS_FILE_WIN32_43_060: [ If position + size is greater than INT64_MAX, then file_write_async shall fail and return FILE_WRITE_ASYNC_INVALID_ARGS. ]*/ + ((position + size) > INT64_MAX)|| + /*Codes_SRS_FILE_43_042: [ If size is 0 then file_write_async shall fail and return FILE_WRITE_ASYNC_INVALID_ARGS. ]*/ + /*Codes_SRS_FILE_WIN32_43_061: [ If size is 0 then file_write_async shall fail and return FILE_WRITE_ASYNC_INVALID_ARGS. ]*/ + (size == 0) + ) + { + LogError("Invalid arguments to file_write_async: FILE_HANDLE file_handle=%p, const unsigned char* source=%p, uin32_t size=%" PRIu32 ", uint64_t position=%" PRIu64 ", FILE_CB user_callback=%p, void* user_context=%p", + handle, source, size, position, user_callback, user_context); + result = FILE_WRITE_ASYNC_INVALID_ARGS; + } + else + { + bool callback_will_be_called = false; + /*Codes_SRS_FILE_WIN32_43_018: [ file_write_async shall allocate a context to store the allocated OVERLAPPED struct, handle, size, user_callback and user_context. ]*/ + FILE_WIN32_IO* io = malloc(sizeof(FILE_WIN32_IO)); + if (io == NULL) + { + /*Codes_SRS_FILE_43_015: [ If there are any failures, file_write_async shall fail and return FILE_WRITE_ASYNC_ERROR. ]*/ + /*Codes_SRS_FILE_WIN32_43_057: [ If there are any other failures, file_write_async shall fail and return FILE_WRITE_ASYNC_ERROR.]*/ + LogError("failure in malloc"); + result = FILE_WRITE_ASYNC_ERROR; + } + else + { + io->handle = handle; + /*Codes_SRS_FILE_WIN32_43_020: [ file_write_async shall allocate an OVERLAPPED struct and populate it with the created event and position. ]*/ + (void)memset(&(io->ov), 0, sizeof(OVERLAPPED)); + /*Codes_SRS_FILE_WIN32_43_054: [ file_write_async shall create an event by calling CreateEvent.]*/ + io->ov.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (io->ov.hEvent == NULL) + { + /*Codes_SRS_FILE_43_015: [ If there are any failures, file_write_async shall fail and return FILE_WRITE_ASYNC_ERROR. ]*/ + /*Codes_SRS_FILE_WIN32_43_057: [ If there are any other failures, file_write_async shall fail and return FILE_WRITE_ASYNC_ERROR.]*/ + LogError("failure in CreateEvent"); + result = FILE_WRITE_ASYNC_ERROR; + } + else + { + io->ov.Offset = position & 0xFFFFFFFFULL; + io->ov.OffsetHigh = position >> 32; + + io->user_callback = user_callback; + io->user_context = user_context; + + io->size = size; + + /*Codes_SRS_FILE_WIN32_43_017: [ file_write_async shall call StartThreadpoolIo.]*/ + StartThreadpoolIo(handle->ptp_io); + + /*Codes_SRS_FILE_43_014: [ file_write_async shall enqueue a write request to write source's content to the position offset in the file. ]*/ + /*Codes_SRS_FILE_43_041: [ If position + size is greater than the size of the file and the call to write is succesfull, file_write_async shall grow the file to accomodate the write. ]*/ + /*Codes_SRS_FILE_WIN32_43_021: [ file_write_async shall call WriteFile with handle, source, size and the allocated OVERLAPPED struct.]*/ + if (WriteFile(handle->h_file, source, size, NULL, &(io->ov)) == FALSE) + { + if (GetLastError() == ERROR_IO_PENDING) + { + /*Codes_SRS_FILE_43_008: [ file_write_async shall call user_call_back passing user_context and success depending on the success of the asynchronous write operation.]*/ + /*Codes_SRS_FILE_43_030: [ file_write_async shall succeed and return FILE_WRITE_ASYNC_OK. ]*/ + /*Codes_SRS_FILE_WIN32_43_022: [ If WriteFile fails synchronously and GetLastError indicates ERROR_IO_PENDING then file_write_async shall succeed and return FILE_WRITE_ASYNC_OK.]*/ + callback_will_be_called = true; + result = FILE_WRITE_ASYNC_OK; + } + else + { + /*Codes_SRS_FILE_43_035: [ If the call to write the file fails, file_write_async shall fail and return FILE_WRITE_ASYNC_WRITE_ERROR. ]*/ + /*Codes_SRS_FILE_WIN32_43_023: [ If WriteFile fails synchronously and GetLastError does not indicate ERROR_IO_PENDING then file_write_async shall fail, call CancelThreadpoolIo and return FILE_WRITE_ASYNC_WRITE_ERROR.]*/ + LogError("failure in WriteFile"); + CancelThreadpoolIo(handle->ptp_io); + result = FILE_WRITE_ASYNC_WRITE_ERROR; + } + } + else + { + /*Codes_SRS_FILE_43_008: [ file_write_async shall call user_call_back passing user_context and success depending on the success of the asynchronous write operation.]*/ + /*Codes_SRS_FILE_43_030: [ file_write_async shall succeed and return FILE_WRITE_ASYNC_OK. ]*/ + /*Codes_SRS_FILE_WIN32_43_024: [ If WriteFile succeeds synchronously then file_write_async shall succeed, call CancelThreadpoolIo, call user_callback and return FILE_WRITE_ASYNC_OK.]*/ + CancelThreadpoolIo(handle->ptp_io); + user_callback(user_context, true); + result = FILE_WRITE_ASYNC_OK; + } + if (!callback_will_be_called) + { + if (!CloseHandle(io->ov.hEvent)) + { + LogError("Failure in CloseHandle"); + } + } + } + if (!callback_will_be_called) + { + free(io); + } + } + } + return result; +} + +IMPLEMENT_MOCKABLE_FUNCTION(, FILE_READ_ASYNC_RESULT, file_read_async, FILE_HANDLE, handle, unsigned char*, destination, uint32_t, size, uint64_t, position, FILE_CB, user_callback, void*, user_context) +{ + FILE_READ_ASYNC_RESULT result; + if + ( + /*Codes_SRS_FILE_43_017: [ If handle is NULL then file_read_async shall fail and return FILE_READ_ASYNC_INVALID_ARGS. ]*/ + /*Codes_SRS_FILE_WIN32_43_046: [ If handle is NULL then file_read_async shall fail and return FILE_READ_ASYNC_INVALID_ARGS. ]*/ + (handle == NULL) || + /*Codes_SRS_FILE_43_032: [ If destination is NULL then file_read_async shall fail and return FILE_READ_ASYNC_INVALID_ARGS. ]*/ + /*Codes_SRS_FILE_WIN32_43_051: [ If destination is NULL then file_read_async shall fail and return FILE_READ_ASYNC_INVALID_ARGS. ]*/ + (destination == NULL) || + /*Codes_SRS_FILE_43_020: [ If user_callback is NULL then file_read_async shall fail and return FILE_READ_ASYNC_INVALID_ARGS. ]*/ + /*Codes_SRS_FILE_WIN32_43_047: [ If user_callback is NULL then file_read_async shall fail and return FILE_READ_ASYNC_INVALID_ARGS. ]*/ + (user_callback == NULL) || + /*Codes_SRS_FILE_43_043: [ If size is 0 then file_read_async shall fail and return FILE_READ_ASYNC_INVALID_ARGS. ]*/ + /*Codes_SRS_FILE_WIN32_43_062: [ If size is 0 then file_read_async shall fail and return FILE_READ_ASYNC_INVALID_ARGS. ]*/ + (size == 0) + ) + { + LogError("Invalid arguments to file_read_async: FILE_HANDLE file_handle=%p, unsigned char* destination=%p, uin32_t size=%" PRIu32 ", uint64_t position=%" PRIu64 ", FILE_CB user_callback=%p, void* user_context=%p", + handle, destination, size, position, user_callback, user_context); + result = FILE_READ_ASYNC_INVALID_ARGS; + } + else + { + bool callback_will_be_called = false; + /*Codes_SRS_FILE_WIN32_43_026: [ file_read_async shall allocate a context to store the allocated OVERLAPPED struct, destination, handle, size, user_callback and user_context ]*/ + FILE_WIN32_IO* io = malloc(sizeof(FILE_WIN32_IO)); + if (io == NULL) + { + /*Codes_SRS_FILE_43_022: [ If there are any failures then file_read_async shall fail and return FILE_READ_ASYNC_ERROR. ]*/ + /*Codes_SRS_FILE_WIN32_43_058: [ If there are any other failures, file_read_async shall fail and return FILE_READ_ASYNC_ERROR. ]*/ + LogError("Failure in malloc"); + result = FILE_READ_ASYNC_ERROR; + } + else + { + io->handle = handle; + /*Codes_SRS_FILE_WIN32_43_028: [ file_read_async shall allocate an OVERLAPPED struct and populate it with the created event and position. ]*/ + (void)memset(&(io->ov), 0, sizeof(OVERLAPPED)); + /*Codes_SRS_FILE_WIN32_43_055: [ file_read_async shall create an event by calling CreateEvent. ]*/ + io->ov.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (io->ov.hEvent == NULL) + { + /*Codes_SRS_FILE_43_022: [ If there are any failures then file_read_async shall fail and return FILE_READ_ASYNC_ERROR. ]*/ + /*Codes_SRS_FILE_WIN32_43_058: [ If there are any other failures, file_read_async shall fail and return FILE_READ_ASYNC_ERROR. ]*/ + LogError("Failure in CreateEvent"); + result = FILE_READ_ASYNC_ERROR; + } + else + { + io->ov.Offset = position & 0xFFFFFFFFULL; + io->ov.OffsetHigh = position >> 32; + + io->user_callback = user_callback; + io->user_context = user_context; + + io->size = size; + + /*Codes_SRS_FILE_43_021: [ file_read_async shall enqueue a read request to read handle's content at position offset and write it to destination. ]*/ + /*Codes_SRS_FILE_43_039: [ If position + size exceeds the size of the file, user_callback shall be called with success as false. ]*/ + /*Codes_SRS_FILE_WIN32_43_025: [ file_read_async shall call StartThreadpoolIo. ]*/ + StartThreadpoolIo(handle->ptp_io); + /*Codes_SRS_FILE_WIN32_43_029: [ file_read_async shall call ReadFile with handle, destination, size and the allocated OVERLAPPED struct.]*/ + if (ReadFile(handle->h_file, destination, size, NULL, &(io->ov)) == FALSE) + { + if (GetLastError() == ERROR_IO_PENDING) + { + /*Codes_SRS_FILE_43_016: [ file_read_async shall call user_callback passing user_context and success depending on the success of the asynchronous read operation.]*/ + /*Codes_SRS_FILE_43_031: [ file_read_async shall succeed and return FILE_READ_ASYNC_OK. ]*/ + /*Codes_SRS_FILE_WIN32_43_030: [ If ReadFile fails synchronously and GetLastError indicates ERROR_IO_PENDING then file_read_async shall succeed and return FILE_READ_ASYNC_OK. ]*/ + callback_will_be_called = true; + result = FILE_READ_ASYNC_OK; + } + else + { + /*Codes_SRS_FILE_43_036: [ If the call to read the file fails, file_read_async shall fail and return FILE_READ_ASYNC_READ_ERROR. ]*/ + /*Codes_SRS_FILE_WIN32_43_031: [ If ReadFile fails synchronously and GetLastError does not indicate ERROR_IO_PENDING then file_read_async shall fail, call CancelThreadpoolIo and return FILE_READ_ASYNC_WRITE_ERROR. ]*/ + LogError("Failure in ReadFile"); + CancelThreadpoolIo(handle->ptp_io); + result = FILE_READ_ASYNC_READ_ERROR; + } + } + else + { + /*Codes_SRS_FILE_43_016: [ file_read_async shall call user_callback passing user_context and success depending on the success of the asynchronous read operation.]*/ + /*Codes_SRS_FILE_43_031: [ file_read_async shall succeed and return FILE_READ_ASYNC_OK. ]*/ + /*Codes_SRS_FILE_WIN32_43_032: [ If ReadFile succeeds synchronously then file_read_async shall succeed, call CancelThreadpoolIo, call user_callback and return FILE_READ_ASYNC_OK. ]*/ + CancelThreadpoolIo(handle->ptp_io); + user_callback(user_context, true); + result = FILE_READ_ASYNC_OK; + } + if (!callback_will_be_called) + { + if (!CloseHandle(io->ov.hEvent)) + { + LogError("Failure in CloseHandle"); + } + } + } + if (!callback_will_be_called) + { + free(io); + } + } + } + return result; +} + +IMPLEMENT_MOCKABLE_FUNCTION(, int, file_extend, FILE_HANDLE, handle, uint64_t, desired_size) +{ + (void)handle; + (void)desired_size; + /*Codes_SRS_FILE_WIN32_43_050: [ file_extend shall return 0. ]*/ + return 0; +} + +static void on_file_io_complete_win32( + PTP_CALLBACK_INSTANCE instance, + PVOID context, + PVOID overlapped, + ULONG io_result, + ULONG_PTR number_of_bytes_transferred, + PTP_IO io +) +{ + (void)instance; + (void)context; + (void)io_result; + (void)number_of_bytes_transferred; + (void)io; + + void (*callback_function)(); + void* callback_context; + HANDLE handle = NULL; + bool report_fault = false; + + /*Codes_SRS_FILE_WIN32_43_034: [ on_file_io_complete_win32 shall recover the file handle, the number of bytes requested by the user, user_callback and user_context from the context containing overlapped. ]*/ + FILE_WIN32_IO* io_context = CONTAINING_RECORD(overlapped, FILE_WIN32_IO, ov); + if (io_context->user_callback == NULL) + { + LogError("Could not recover user-specified callback"); + report_fault = true; + callback_function = (FILE_REPORT_FAULT)(io_context->handle->user_report_fault_callback); + callback_context = io_context->handle->user_report_fault_context; + } + else + { + callback_function = (FILE_CB)(io_context->user_callback); + callback_context = io_context->user_context; + handle = io_context->handle->h_file; + } + + DWORD num_bytes; + /*Codes_SRS_FILE_WIN32_43_063: [ on_file_io_complete_win32 shall call GetOverlappedResult to determine if the asynchronous operation was successful. ]*/ + bool overlapped_result = GetOverlappedResult(handle, overlapped, &num_bytes, FALSE); + (void)num_bytes; + + /*Codes_SRS_FILE_WIN32_43_067: [ on_file_io_complete_win32 shall compare number_of_bytes_transferred to the number of bytes requested by the user. ]*/ + bool all_bytes_were_transferred = (uint32_t)number_of_bytes_transferred == io_context->size; + + CloseHandle(io_context->ov.hEvent); + free(io_context); + + if (!overlapped_result) + { + DWORD error = GetLastError(); + LogError("Error in asynchronous operation. Error code =%" PRIu32 "", error); + } + if (!all_bytes_were_transferred) + { + LogError("All bytes were not transferred."); + } + + + if (report_fault) + { + if(callback_function != NULL) + { + ((FILE_REPORT_FAULT)callback_function)(callback_context, "Could not determine recover user_callback."); + } + } + else + { + /*Codes_SRS_FILE_WIN32_43_066: [ on_file_io_complete_win32 shall call user_callback with is_successful as true if and only if GetOverlappedResult returns true and number_of_bytes_transferred is equal to the number of bytes requested by the user. ]*/ + /*Codes_SRS_FILE_WIN32_43_068: [ If either GetOverlappedResult returns false or number_of_bytes_transferred is not equal to the bytes requested by the user, on_file_io_complete_win32 shall return false. ]*/ + ((FILE_CB)callback_function)(callback_context, overlapped_result && all_bytes_were_transferred); + } +} diff --git a/win32/tests/CMakeLists.txt b/win32/tests/CMakeLists.txt index 7e78bf3..f4169ce 100644 --- a/win32/tests/CMakeLists.txt +++ b/win32/tests/CMakeLists.txt @@ -15,6 +15,7 @@ if(${run_unittests}) add_subdirectory(srw_lock_win32_ut) add_subdirectory(reals_win32_ut) add_subdirectory(sync_win32_ut) + add_subdirectory(file_win32_ut) endif() if(${run_int_tests})