From 3667ab30e02dcac3b0e47618eeedafc4951aeffc Mon Sep 17 00:00:00 2001 From: martinfouilleul Date: Fri, 26 May 2023 16:03:23 +0200 Subject: [PATCH] [win32, io, wip] add win32 io impl. for open/close/seek/read/write --- src/milepost.c | 2 + src/platform/platform_io.h | 65 ++-- src/platform/platform_io_common.c | 4 +- src/platform/platform_path.c | 2 +- src/platform/posix_io.c | 18 +- src/platform/win32_io.c | 484 ++++++++++++++++++++++++++++++ src/platform/win32_path.c | 28 ++ src/win32_app.c | 45 --- test/files/build.bat | 4 + test/files/main.c | 6 +- 10 files changed, 566 insertions(+), 92 deletions(-) create mode 100644 src/platform/win32_io.c create mode 100644 src/platform/win32_path.c create mode 100644 test/files/build.bat diff --git a/src/milepost.c b/src/milepost.c index 78de00c..f0a8403 100644 --- a/src/milepost.c +++ b/src/milepost.c @@ -17,6 +17,8 @@ #if PLATFORM_WINDOWS #include"platform/win32_memory.c" #include"platform/win32_clock.c" + #include"platform/win32_io.c" + #include"platform/win32_path.c" //TODO #elif PLATFORM_MACOS #include"platform/unix_memory.c" diff --git a/src/platform/platform_io.h b/src/platform/platform_io.h index db6c9af..763447b 100644 --- a/src/platform/platform_io.h +++ b/src/platform/platform_io.h @@ -121,58 +121,59 @@ typedef struct io_cmp //---------------------------------------------------------------- //TODO: complete io queue api //---------------------------------------------------------------- -io_cmp io_wait_single_req(io_req* req); +MP_API io_cmp io_wait_single_req(io_req* req); //---------------------------------------------------------------- // File IO wrapper API //---------------------------------------------------------------- -file_handle file_open(str8 path, file_open_flags flags); -file_handle file_open_relative(file_handle base, str8 path, file_open_flags flags); -void file_close(file_handle file); +MP_API file_handle file_open(str8 path, file_open_flags flags); +MP_API file_handle file_open_relative(file_handle base, str8 path, file_open_flags flags); +MP_API void file_close(file_handle file); -i64 file_pos(file_handle file); -i64 file_seek(file_handle file, long offset, file_whence whence); +MP_API i64 file_pos(file_handle file); +MP_API i64 file_seek(file_handle file, i64 offset, file_whence whence); -u64 file_write(file_handle file, u64 size, char* buffer); -u64 file_read(file_handle file, u64 size, char* buffer); +MP_API u64 file_write(file_handle file, u64 size, char* buffer); +MP_API u64 file_read(file_handle file, u64 size, char* buffer); -io_error file_last_error(file_handle handle); +MP_API io_error file_last_error(file_handle handle); //---------------------------------------------------------------- // File System wrapper API //---------------------------------------------------------------- -typedef enum +typedef enum file_type { - FILE_UNKNOWN, - FILE_REGULAR, - FILE_DIRECTORY, - FILE_SYMLINK, - FILE_BLOCK, - FILE_CHARACTER, - FILE_FIFO, - FILE_SOCKET, + MP_FILE_UNKNOWN, + MP_FILE_REGULAR, + MP_FILE_DIRECTORY, + MP_FILE_SYMLINK, + MP_FILE_BLOCK, + MP_FILE_CHARACTER, + MP_FILE_FIFO, + MP_FILE_SOCKET, + } file_type; typedef u16 file_perm; enum file_perm { - FILE_OTHER_EXEC = 1<<0, - FILE_OTHER_WRITE = 1<<1, - FILE_OTHER_READ = 1<<2, + MP_FILE_OTHER_EXEC = 1<<0, + MP_FILE_OTHER_WRITE = 1<<1, + MP_FILE_OTHER_READ = 1<<2, - FILE_GROUP_EXEC = 1<<3, - FILE_GROUP_WRITE = 1<<4, - FILE_GROUP_READ = 1<<5, + MP_FILE_GROUP_EXEC = 1<<3, + MP_FILE_GROUP_WRITE = 1<<4, + MP_FILE_GROUP_READ = 1<<5, - FILE_OWNER_EXEC = 1<<6, - FILE_OWNER_WRITE = 1<<7, - FILE_OWNER_READ = 1<<8, + MP_FILE_OWNER_EXEC = 1<<6, + MP_FILE_OWNER_WRITE = 1<<7, + MP_FILE_OWNER_READ = 1<<8, - FILE_STICKY_BIT = 1<<9, - FILE_SET_GID = 1<<10, - FILE_SET_UID = 1<<11, + MP_FILE_STICKY_BIT = 1<<9, + MP_FILE_SET_GID = 1<<10, + MP_FILE_SET_UID = 1<<11, }; typedef struct file_status @@ -185,8 +186,8 @@ typedef struct file_status } file_status; -file_status file_get_status(file_handle file); -u64 file_size(file_handle file); +MP_API file_status file_get_status(file_handle file); +MP_API u64 file_size(file_handle file); //TODO: Complete as needed... diff --git a/src/platform/platform_io_common.c b/src/platform/platform_io_common.c index 9bb3fd3..af7d0d2 100644 --- a/src/platform/platform_io_common.c +++ b/src/platform/platform_io_common.c @@ -42,11 +42,11 @@ i64 file_pos(file_handle file) return(cmp.offset); } -i64 file_seek(file_handle file, long offset, file_whence whence) +i64 file_seek(file_handle file, i64 offset, file_whence whence) { io_req req = {.op = IO_OP_SEEK, .handle = file, - .size = offset, + .offset = offset, .whence = whence}; io_cmp cmp = io_wait_single_req(&req); diff --git a/src/platform/platform_path.c b/src/platform/platform_path.c index 72de1f7..699bc80 100644 --- a/src/platform/platform_path.c +++ b/src/platform/platform_path.c @@ -92,7 +92,7 @@ str8 path_append(mem_arena* arena, str8 parent, str8 relPath) str8 path_executable_relative(mem_arena* arena, str8 relPath) { - str8_list list = {}; + str8_list list = {0}; mem_arena_scope scratch = mem_scratch_begin_next(arena); str8 executablePath = path_executable(scratch.arena); diff --git a/src/platform/posix_io.c b/src/platform/posix_io.c index b6e9aa5..da429a9 100644 --- a/src/platform/posix_io.c +++ b/src/platform/posix_io.c @@ -302,35 +302,35 @@ file_type io_convert_type_from_stat(u16 mode) switch(mode & S_IFMT) { case S_IFIFO: - type = FILE_FIFO; + type = MP_FILE_FIFO; break; case S_IFCHR: - type = FILE_CHARACTER; + type = MP_FILE_CHARACTER; break; case S_IFDIR: - type = FILE_DIRECTORY; + type = MP_FILE_DIRECTORY; break; case S_IFBLK: - type = FILE_BLOCK; + type = MP_FILE_BLOCK; break; case S_IFREG: - type = FILE_REGULAR; + type = MP_FILE_REGULAR; break; case S_IFLNK: - type = FILE_SYMLINK; + type = MP_FILE_SYMLINK; break; case S_IFSOCK: - type = FILE_SOCKET; + type = MP_FILE_SOCKET; break; default: - type = FILE_UNKNOWN; + type = MP_FILE_UNKNOWN; break; } return(type); @@ -395,7 +395,7 @@ io_cmp io_seek(file_slot* slot, io_req* req) case FILE_SEEK_END: whence = SEEK_END; } - cmp.result = lseek(slot->fd, req->size, whence); + cmp.result = lseek(slot->fd, req->offset, whence); if(cmp.result < 0) { diff --git a/src/platform/win32_io.c b/src/platform/win32_io.c new file mode 100644 index 0000000..5ba82a7 --- /dev/null +++ b/src/platform/win32_io.c @@ -0,0 +1,484 @@ +/************************************************************//** +* +* @file: win32_io.c +* @author: Martin Fouilleul +* @date: 25/05/2023 +* +*****************************************************************/ + +#include +#include + +#include"platform_io_common.c" + +typedef struct file_slot +{ + u32 generation; + HANDLE h; + io_error error; + bool fatal; + list_elt freeListElt; + +} file_slot; + +enum +{ + ORCA_MAX_FILE_SLOTS = 256, +}; + +typedef struct file_table +{ + file_slot slots[ORCA_MAX_FILE_SLOTS]; + u32 nextSlot; + list_info freeList; +} file_table; + +file_table __globalFileTable = {0}; + +file_slot* file_slot_alloc(file_table* table) +{ + file_slot* slot = list_pop_entry(&table->freeList, file_slot, freeListElt); + if(!slot && table->nextSlot < ORCA_MAX_FILE_SLOTS) + { + slot = &table->slots[table->nextSlot]; + slot->generation = 1; + slot->h = NULL; + table->nextSlot++; + } + return(slot); +} + +void file_slot_recycle(file_table* table, file_slot* slot) +{ + slot->generation++; + list_push(&table->freeList, &slot->freeListElt); +} + +file_handle file_handle_from_slot(file_table* table, file_slot* slot) +{ + u64 index = slot - table->slots; + u64 generation = slot->generation; + file_handle handle = {.h = (generation<<32) | index }; + return(handle); +} + +file_slot* file_slot_from_handle(file_table* table, file_handle handle) +{ + file_slot* slot = 0; + + u64 index = handle.h & 0xffffffff; + u64 generation = handle.h>>32; + + if(index < table->nextSlot) + { + file_slot* candidate = &table->slots[index]; + if(candidate->generation == generation) + { + slot = candidate; + } + } + return(slot); +} + +io_error io_convert_win32_error(int winError) +{ + io_error error = 0; + switch(winError) + { + case ERROR_SUCCESS: + error = IO_OK; + break; + + case ERROR_ACCESS_DENIED: + error = IO_ERR_PERM; + break; + + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + case ERROR_INVALID_DRIVE: + case ERROR_DIRECTORY: + error = IO_ERR_NO_ENTRY; + break; + + case ERROR_TOO_MANY_OPEN_FILES: + error = IO_ERR_MAX_FILES; + break; + + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_OUTOFMEMORY: + error = IO_ERR_MEM; + break; + + case ERROR_DEV_NOT_EXIST: + error = IO_ERR_NO_DEVICE; + break; + + case ERROR_FILE_EXISTS: + case ERROR_ALREADY_EXISTS: + error = IO_ERR_EXISTS; + break; + + case ERROR_BUFFER_OVERFLOW: + case ERROR_FILENAME_EXCED_RANGE: + error = IO_ERR_PATH_LENGTH; + break; + + case ERROR_FILE_TOO_LARGE: + error = IO_ERR_FILE_SIZE; + break; + + //TODO: complete + + default: + error = IO_ERR_UNKNOWN; + break; + } + return(error); +} + +io_cmp io_open_at(file_slot* atSlot, io_req* req) +{ + io_cmp cmp = {0}; + + file_slot* slot = file_slot_alloc(&__globalFileTable); + if(!slot) + { + cmp.error = IO_ERR_MAX_FILES; + cmp.result = 0; + } + else + { + cmp.handle = file_handle_from_slot(&__globalFileTable, slot); + + //NOTE: open + mem_arena_scope scratch = mem_scratch_begin(); + + int sizeWide = 1 + MultiByteToWideChar(CP_UTF8, 0, req->buffer, req->size, NULL, 0); + LPWSTR pathWide = mem_arena_alloc_array(scratch.arena, wchar_t, sizeWide); + MultiByteToWideChar(CP_UTF8, 0, req->buffer, req->size, pathWide, sizeWide); + pathWide[sizeWide-1] = '\0'; + + DWORD accessFlags = 0; + DWORD createFlags = 0; + DWORD attributesFlags = FILE_ATTRIBUTE_NORMAL; + + if(req->openFlags & FILE_OPEN_READ) + { + accessFlags |= GENERIC_READ; + } + if(req->openFlags & FILE_OPEN_WRITE) + { + if(req->openFlags & FILE_OPEN_APPEND) + { + accessFlags |= FILE_APPEND_DATA; + } + else + { + accessFlags |= GENERIC_WRITE; + } + } + + if(req->openFlags & FILE_OPEN_TRUNCATE) + { + if(req->openFlags & FILE_OPEN_CREATE) + { + createFlags |= CREATE_ALWAYS; + } + else + { + createFlags |= TRUNCATE_EXISTING; + } + } + if(req->openFlags & FILE_OPEN_CREATE) + { + if(!createFlags & CREATE_ALWAYS) + { + createFlags |= OPEN_ALWAYS; + } + } + if( !(createFlags & OPEN_ALWAYS) + && !(createFlags & CREATE_ALWAYS) + && !(createFlags & TRUNCATE_EXISTING)) + { + createFlags |= OPEN_EXISTING; + } + + if(req->openFlags & FILE_OPEN_SYMLINK) + { + attributesFlags |= FILE_FLAG_OPEN_REPARSE_POINT; + } + + if(req->openFlags & FILE_OPEN_RESTRICT) + { + //TODO: if FILE_OPEN_RESTRICT, do the file traversal to check that path is in the + // subtree rooted at atSlot->fd + } + + //TODO: open at + + //TODO: take care of share mode and security attributes, make it consistent with posix impl + slot->h = CreateFileW(pathWide, accessFlags, 0, NULL, createFlags, attributesFlags, NULL); + + mem_scratch_end(scratch); + + if(slot->h == INVALID_HANDLE_VALUE) + { + slot->fatal = true; + slot->error = io_convert_win32_error(GetLastError()); + } + cmp.error = slot->error; + } + + return(cmp); +} + +io_cmp io_close(file_slot* slot, io_req* req) +{ + io_cmp cmp = {0}; + if(slot->h) + { + CloseHandle(slot->h); + } + file_slot_recycle(&__globalFileTable, slot); + return(cmp); +} + +/* +file_perm io_convert_perm_from_stat(u16 mode) +{ + file_perm perm = mode & 07777; + return(perm); +} + +file_type io_convert_type_from_stat(u16 mode) +{ + file_type type; + switch(mode & S_IFMT) + { + case S_IFIFO: + type = FILE_FIFO; + break; + + case S_IFCHR: + type = FILE_CHARACTER; + break; + + case S_IFDIR: + type = FILE_DIRECTORY; + break; + + case S_IFBLK: + type = FILE_BLOCK; + break; + + case S_IFREG: + type = FILE_REGULAR; + break; + + case S_IFLNK: + type = FILE_SYMLINK; + break; + + case S_IFSOCK: + type = FILE_SOCKET; + break; + + default: + type = FILE_UNKNOWN; + break; + } + return(type); +} + +io_cmp io_fstat(file_slot* slot, io_req* req) +{ + io_cmp cmp = {0}; + + if(req->size < sizeof(file_status)) + { + cmp.error = IO_ERR_ARG; + } + else + { + struct stat s; + if(fstat(slot->fd, &s)) + { + slot->error = io_convert_errno(errno); + cmp.error = slot->error; + } + else + { + file_status* status = (file_status*)req->buffer; + status->perm = io_convert_perm_from_stat(s.st_mode); + status->type = io_convert_type_from_stat(s.st_mode); + status->size = s.st_size; + //TODO: times + } + } + return(cmp); +} +*/ + +io_cmp io_pos(file_slot* slot, io_req* req) +{ + io_cmp cmp = {0}; + LARGE_INTEGER off = {.QuadPart = req->offset}; + LARGE_INTEGER newPos = {0}; + + if(!SetFilePointerEx(slot->h, off, &newPos, FILE_CURRENT)) + { + slot->error = io_convert_win32_error(GetLastError()); + cmp.error = slot->error; + } + else + { + cmp.result = newPos.QuadPart; + } + return(cmp); +} + +io_cmp io_seek(file_slot* slot, io_req* req) +{ + io_cmp cmp = {0}; + + DWORD whence; + switch(req->whence) + { + case FILE_SEEK_CURRENT: + whence = FILE_CURRENT; + break; + + case FILE_SEEK_SET: + whence = FILE_BEGIN; + break; + + case FILE_SEEK_END: + whence = FILE_END; + } + + LARGE_INTEGER off = {.QuadPart = req->offset}; + LARGE_INTEGER newPos = {0}; + + if(!SetFilePointerEx(slot->h, off, &newPos, whence)) + { + slot->error = io_convert_win32_error(GetLastError()); + cmp.error = slot->error; + } + else + { + cmp.result = newPos.QuadPart; + } + + return(cmp); +} + +io_cmp io_read(file_slot* slot, io_req* req) +{ + io_cmp cmp = {0}; + + DWORD bytesRead = 0; + + if(!ReadFile(slot->h, req->buffer, req->size, &bytesRead, NULL)) + { + cmp.result = -1; + slot->error = io_convert_win32_error(GetLastError()); + cmp.error = slot->error; + } + else + { + cmp.result = bytesRead; + } + return(cmp); +} + +io_cmp io_write(file_slot* slot, io_req* req) +{ + io_cmp cmp = {0}; + + DWORD bytesWritten = 0; + + if(!WriteFile(slot->h, req->buffer, req->size, &bytesWritten, NULL)) + { + cmp.result = -1; + slot->error = io_convert_win32_error(GetLastError()); + cmp.error = slot->error; + } + else + { + cmp.result = bytesWritten; + } + + return(cmp); +} + +io_cmp io_get_error(file_slot* slot, io_req* req) +{ + io_cmp cmp = {0}; + cmp.result = slot->error; + return(cmp); +} + +io_cmp io_wait_single_req(io_req* req) +{ + io_cmp cmp = {0}; + + file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle); + if(!slot) + { + if(req->op != IO_OP_OPEN_AT) + { + cmp.error = IO_ERR_HANDLE; + } + } + else if(slot->fatal && req->op != IO_OP_CLOSE) + { + cmp.error = IO_ERR_PREV; + } + + if(cmp.error == IO_OK) + { + switch(req->op) + { + case IO_OP_OPEN_AT: + cmp = io_open_at(slot, req); + break; +/* + case IO_OP_FSTAT: + cmp = io_fstat(slot, req); + break; +*/ + case IO_OP_CLOSE: + cmp = io_close(slot, req); + break; + + case IO_OP_READ: + cmp = io_read(slot, req); + break; + + case IO_OP_WRITE: + cmp = io_write(slot, req); + break; +/* + case IO_OP_POS: + cmp = io_pos(slot, req); + break; + + case IO_OP_SEEK: + cmp = io_seek(slot, req); + break; +*/ + case IO_OP_ERROR: + cmp = io_get_error(slot, req); + break; + + default: + cmp.error = IO_ERR_OP; + if(slot) + { + slot->error = cmp.error; + } + break; + } + } + return(cmp); +} diff --git a/src/platform/win32_path.c b/src/platform/win32_path.c new file mode 100644 index 0000000..bed8929 --- /dev/null +++ b/src/platform/win32_path.c @@ -0,0 +1,28 @@ +/************************************************************//** +* +* @file: win32_path.c +* @author: Martin Fouilleul +* @date: 24/05/2023 +* +*****************************************************************/ + +#include"platform_path.c" + +str8 path_executable(mem_arena* arena) +{ + //TODO use wide chars + char* buffer = mem_arena_alloc_array(arena, char, MAX_PATH+1); + int size = GetModuleFileName(NULL, buffer, MAX_PATH+1); + //TODO: check for errors... + for(int i=0; i