win32/src/file_win32.c (352 lines of code) (raw):
// Copyright (C) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
#include "windows.h"
#include "c_logging/logger.h"
#include "c_pal/execution_engine_win32.h"
#include "c_pal/gballoc_hl.h"
#include "c_pal/gballoc_hl_redirect.h"
#include "c_pal/file.h"
typedef struct FILE_HANDLE_DATA_TAG
{
EXECUTION_ENGINE_HANDLE execution_engine;
PTP_POOL ptp_pool;
HANDLE h_file;
TP_CALLBACK_ENVIRON cbe;
PTP_CLEANUP_GROUP ptp_cleanup_group; /*the cleanup group of IO operations*/
PTP_IO ptp_io;
FILE_REPORT_FAULT user_report_fault_callback;
void* user_report_fault_context;
}FILE_HANDLE_DATA;
typedef struct FILE_WIN32_IO_TAG
{
OVERLAPPED ov;
FILE_HANDLE handle;
FILE_CB user_callback;
void* user_context;
uint32_t size;
}FILE_WIN32_IO;
static VOID CALLBACK 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;
if (overlapped == NULL)
{
LogError("invalid argument PTP_CALLBACK_INSTANCE instance=%p, PVOID context=%p, PVOID overlapped=%p, ULONG io_result=%" PRIu32 ", ULONG_PTR number_of_bytes_transferred=%" PRIuPTR ", PTP_IO io=%p",
instance, context, overlapped, io_result, number_of_bytes_transferred, io);
}
else
{
/*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);
FILE_CB user_callback = io_context->user_callback;
void* user_callback_context = io_context->user_context;
bool all_bytes_were_transferred = (uint32_t)number_of_bytes_transferred == io_context->size;
if (io_result != NO_ERROR)
{
LogError("Error in asynchronous operation. io_result=%" PRIu32 "", io_result);
}
if (!all_bytes_were_transferred)
{
LogError("All bytes were not transferred. number_of_bytes_transferred=%" PRIuPTR "; io_context->size=%" PRIu32 "",
number_of_bytes_transferred, io_context->size);
}
if (!CloseHandle(io_context->ov.hEvent))
{
LogLastError("failure in CloseHandle(io_context->ov.hEvent=%p)", io_context->ov.hEvent);
}
free(io_context);
/*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. ]*/
user_callback(user_callback_context, io_result == NO_ERROR && all_bytes_were_transferred);
}
}
static VOID NTAPI on_close_threadpool_group_member(
PVOID object_context,
PVOID cleanup_context
)
{
(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 result;
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. ]*/
(full_file_name[0] == '\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, MU_P_OR_NULL(full_file_name), user_report_fault_callback, user_report_fault_context);
result = NULL;
}
else
{
/*Codes_SRS_FILE_WIN32_43_041: [ file_create shall allocate a FILE_HANDLE. ]*/
result = malloc(sizeof(FILE_HANDLE_DATA));
if (result == NULL)
{
/*Codes_SRS_FILE_43_034: [ If there are any failures, file_create shall fail and return NULL. ]*/
/*Codes_SRS_FILE_WIN32_43_008: [ If there are any failures, file_create shall return NULL. ]*/
LogError("Failure in malloc");
}
else
{
bool succeeded;
/* Codes_SRS_FILE_WIN32_01_001: [ file_create shall increment the reference count of execution_engine in order to hold on to it. ]*/
execution_engine_inc_ref(execution_engine);
result->execution_engine = execution_engine;
/*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. ]*/
result->h_file = CreateFileA(
full_file_name, /* LPCTSTR lpFileName*/
GENERIC_READ | GENERIC_WRITE, /* DWORD dwDesiredAccess*/
FILE_SHARE_READ, /* DWORD dwShareMode*/
NULL, /* LPSECURITY_ATTRIBUTES lpSecurityAttributes*/
OPEN_ALWAYS, /* DWORD dwCreationDisposition*/
FILE_FLAG_OVERLAPPED | FILE_FLAG_WRITE_THROUGH, /* DWORD dwFlagsAndAttributes*/
NULL /* HANDLE hTemplateFile*/
);
if ( result->h_file == INVALID_HANDLE_VALUE)
{
/*Codes_SRS_FILE_43_034: [ If there are any failures, file_create shall fail and return NULL. ]*/
/*Codes_SRS_FILE_WIN32_43_008: [ If there are any failures, file_create shall return NULL. ]*/
LogLastError("Failure in CreateFileA, full_file_name=%s", full_file_name);
succeeded = 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(result->h_file, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS))
{
/*Codes_SRS_FILE_43_034: [ If there are any failures, file_create shall fail and return NULL. ]*/
/*Codes_SRS_FILE_WIN32_43_008: [ If there are any failures, file_create shall return NULL. ]*/
LogLastError("Failure in SetFileCompletionNotificationModes, full_file_name=%s", full_file_name);
succeeded = false;
}
else
{
/*Codes_SRS_FILE_WIN32_43_003: [ file_create shall initialize a threadpool environment by calling InitializeThreadpolEnvironment.]*/
InitializeThreadpoolEnvironment(&result->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.]*/
result->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(&result->cbe, result->ptp_pool);
/*Codes_SRS_FILE_WIN32_43_006: [ file_create shall create a cleanup group by calling CreateThreadpoolCleanupGroup.]*/
result->ptp_cleanup_group = CreateThreadpoolCleanupGroup();
if (result->ptp_cleanup_group == NULL)
{
/*Codes_SRS_FILE_43_034: [ If there are any failures, file_create shall fail and return NULL. ]*/
LogLastError("Failure in CreateThreadpoolCleanupGroup, full_file_name=%s", full_file_name);
succeeded = false;
}
else
{
/*Codes_SRS_FILE_WIN32_43_007: [ file_create shall register the cleanup group with the threadpool environment by calling SetThreadpoolCallbackCleanupGroup.]*/
SetThreadpoolCallbackCleanupGroup(&result->cbe, result->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]*/
result->ptp_io = CreateThreadpoolIo(result->h_file, on_file_io_complete_win32, NULL, &result->cbe);
if (result->ptp_io == NULL)
{
/*Codes_SRS_FILE_43_034: [ If there are any failures, file_create shall fail and return NULL. ]*/
/*Codes_SRS_FILE_WIN32_43_008: [ If there are any failures, file_create shall return NULL. ]*/
LogLastError("Failure in CreateThreadpoolIo, full_file_name=%s", full_file_name);
succeeded = false;
}
else
{
/*Codes_SRS_FILE_WIN32_43_009: [ file_create shall succeed and return a non - NULL value.]*/
succeeded = true;
result->user_report_fault_callback = user_report_fault_callback;
result->user_report_fault_context = user_report_fault_context;
}
if (!succeeded)
{
CloseThreadpoolCleanupGroup(result->ptp_cleanup_group);
}
}
if (!succeeded)
{
DestroyThreadpoolEnvironment(&result->cbe);
}
}
if (!succeeded)
{
if (!CloseHandle(result->h_file))
{
LogLastError("Failure in CloseHandle, full_file_name=%s", full_file_name);
}
}
}
/*Codes_SRS_FILE_43_034: [ If there are any failures, file_create shall fail and return NULL. ]*/
if (!succeeded)
{
execution_engine_dec_ref(result->execution_engine);
free(result);
result = NULL;
}
}
}
return result;
}
IMPLEMENT_MOCKABLE_FUNCTION(, void, file_destroy, FILE_HANDLE, handle)
{
if (handle == NULL)
{
/*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.]*/
LogError("Invalid argument to file_destroy: FILE_HANDLE=%p", handle);
}
else
{
/*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 the handle returned by CreateFileA. ]*/
if (!CloseHandle(handle->h_file))
{
LogLastError("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_01_002: [ file_destroy shall decrement the reference count for the execution engine. ]*/
execution_engine_dec_ref(handle->execution_engine);
/*Codes_SRS_FILE_WIN32_43_042: [ file_destroy shall free the handle.]*/
free(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_context = malloc(sizeof(FILE_WIN32_IO));
if (io_context == 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(sizeof(FILE_WIN32_IO)=%zu)", sizeof(FILE_WIN32_IO));
result = FILE_WRITE_ASYNC_ERROR;
}
else
{
io_context->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_context->ov, 0, sizeof(OVERLAPPED));
/*Codes_SRS_FILE_WIN32_43_054: [ file_write_async shall create an event by calling CreateEvent.]*/
io_context->ov.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (io_context->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.]*/
LogLastError("failure in CreateEvent");
result = FILE_WRITE_ASYNC_ERROR;
}
else
{
io_context->ov.Offset = position & 0xFFFFFFFFULL;
io_context->ov.OffsetHigh = position >> 32;
io_context->user_callback = user_callback;
io_context->user_context = user_context;
io_context->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 successfull, 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_context->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.]*/
LogLastError("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_context->ov.hEvent))
{
LogLastError("Failure in CloseHandle");
}
}
}
if (!callback_will_be_called)
{
free(io_context);
}
}
}
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_context = malloc(sizeof(FILE_WIN32_IO));
if (io_context == 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_context->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_context->ov, 0, sizeof(OVERLAPPED));
/*Codes_SRS_FILE_WIN32_43_055: [ file_read_async shall create an event by calling CreateEvent. ]*/
io_context->ov.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (io_context->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. ]*/
LogLastError("Failure in CreateEvent");
result = FILE_READ_ASYNC_ERROR;
}
else
{
io_context->ov.Offset = position & 0xFFFFFFFFULL;
io_context->ov.OffsetHigh = position >> 32;
io_context->user_callback = user_callback;
io_context->user_context = user_context;
io_context->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_context->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. ]*/
LogLastError("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_context->ov.hEvent))
{
LogLastError("Failure in CloseHandle");
}
}
}
if (!callback_will_be_called)
{
free(io_context);
}
}
}
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. ]*/
/*TODO: Task number 7732885*/
return 0;
}