From 0fa6dcd2ea049c7240ff372afbfec535fa9a33a5 Mon Sep 17 00:00:00 2001 From: martinfouilleul Date: Wed, 14 Jun 2023 12:03:50 +0200 Subject: [PATCH] [win32, io] - Added str16 functions for dealing with win32 wide char strings - Added io_open_relative to open a path relative to an already opened handle (with no escape checking) --- src/platform/win32_io.c | 171 ++++++++++++++++++++++++++++++++++------ src/util/strings.c | 84 ++++++++++++++++++++ src/util/strings.h | 33 ++++++++ 3 files changed, 262 insertions(+), 26 deletions(-) diff --git a/src/platform/win32_io.c b/src/platform/win32_io.c index bf49e92..1a1ea5a 100644 --- a/src/platform/win32_io.c +++ b/src/platform/win32_io.c @@ -69,6 +69,131 @@ io_error io_convert_win32_error(int winError) return(error); } +typedef struct io_open_restrict_result +{ + io_error error; + HANDLE h; +} io_open_restrict_result; + + +str16 win32_utf8_to_wide_null_terminated(mem_arena* arena, str8 s) +{ + str16 res = {0}; + res.len = 1 + MultiByteToWideChar(CP_UTF8, 0, s.ptr, s.len, NULL, 0); + res.ptr = mem_arena_alloc_array(arena, u16, res.len); + MultiByteToWideChar(CP_UTF8, 0, s.ptr, s.len, res.ptr, res.len); + res.ptr[res.len-1] = '\0'; + return(res); +} + +str16 win32_path_from_handle_null_terminated(mem_arena* arena, HANDLE handle) +{ + str16 res = {0}; + + res.len = GetFinalPathNameByHandleW(handle, NULL, 0, FILE_NAME_NORMALIZED); + if(res.len) + { + res.ptr = mem_arena_alloc_array(arena, u16, res.len); + if(!GetFinalPathNameByHandleW(handle, res.ptr, res.len, FILE_NAME_NORMALIZED)) + { + res.len = 0; + res.ptr = 0; + } + } + return(res); +} + +HANDLE io_open_relative(HANDLE dirHandle, str8 path, DWORD accessFlags, DWORD createFlags, DWORD attributesFlags) +{ + HANDLE handle = INVALID_HANDLE_VALUE; + + mem_arena_scope scratch = mem_scratch_begin(); + + str16 dirPathW = win32_path_from_handle_null_terminated(scratch.arena, dirHandle); + str16 pathW = win32_utf8_to_wide_null_terminated(scratch.arena, path); + + if(dirPathW.len && pathW.len) + { + u64 fullPathWSize = dirPathW.len + pathW.len; + LPWSTR fullPathW = mem_arena_alloc_array(scratch.arena, u16, fullPathWSize); + memcpy(fullPathW, dirPathW.ptr, (dirPathW.len-1)*sizeof(u16)); + fullPathW[dirPathW.len-1] = '\\'; + memcpy(fullPathW + dirPathW.len, pathW.ptr, pathW.len*sizeof(u16)); + + LPWSTR canonical = mem_arena_alloc_array(scratch.arena, wchar_t, fullPathWSize); + PathCanonicalizeW(canonical, fullPathW); + + handle = CreateFileW(canonical, accessFlags, 0, NULL, createFlags, attributesFlags, NULL); + } + return(handle); +} + +/* +io_open_restrict_result io_open_path_restrict(HANDLE dirHandle, str8 path, DWORD accessFlags, DWORD createFlags, DWORD attributesFalgs) +{ + io_open_restrict_result res = {0}; + + mem_arena_scope scratch = mem_scratch_begin(); + + str8_list sep = {0}; + str8_list_push(scratch.arena, &sep, STR8("/")); + str8_list pathElements = str8_split(scratch.arena, path, sep); + + HANDLE handle = NULL; + DuplicateHandle(GetCurrentProcess(), dirHandle, GetCurrentProcess(), &handle); + + for_list(&pathElements.list, elt, str8_elt, listElt) + { + str8 name = elt->string; + + if(!str8_cmp(name, STR8("."))) + { + //NOTE: skip; + continue + } + else if(!str8_cmp(name, STR8(".."))) + { + //NOTE: check that we don't escape root dir + if(CompareObjectHandles(dirHandle, handle)) + { + result.error = IO_ERR_WALKOUT; + goto error; + } + else + { + // Open parent and continue + + HANDLE nextHandle = NULL; + OBJECT_ATTRIBUTES attributes; + //TODO initialize attributes + + IO_STATUS_BLOCK status; + + NtCreateFile(&nextHandle, + FILE_LIST_DIRECTORY|FILE_TRAVERSE, + &attributes, + &status, + 0, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, + FILE_OPEN, + FILE_DIRECTORY_FILE, + NULL, + 0); + } + } + else + { + //NOTE: open that dir, check if dir/symlink/etc + + } + } + + error: + return(res); +} +*/ + io_cmp io_open_at(file_slot* atSlot, io_req* req, file_table* table) { io_cmp cmp = {0}; @@ -99,10 +224,8 @@ io_cmp io_open_at(file_slot* atSlot, io_req* req, file_table* table) //NOTE: open mem_arena_scope scratch = mem_scratch_begin(); - int pathWideSize = 1 + MultiByteToWideChar(CP_UTF8, 0, req->buffer, req->size, NULL, 0); - LPWSTR pathWide = mem_arena_alloc_array(scratch.arena, wchar_t, pathWideSize); - MultiByteToWideChar(CP_UTF8, 0, req->buffer, req->size, pathWide, pathWideSize); - pathWide[pathWideSize-1] = '\0'; + str8 path = str8_from_buffer(req->size, req->buffer); + str16 pathW = win32_utf8_to_wide_null_terminated(scratch.arena, path); DWORD accessFlags = 0; DWORD createFlags = 0; @@ -157,45 +280,41 @@ io_cmp io_open_at(file_slot* atSlot, io_req* req, file_table* table) slot->h = INVALID_HANDLE_VALUE; if(atSlot) { - /* + /* if(req->open.flags & FILE_OPEN_RESTRICT) { //TODO: if FILE_OPEN_RESTRICT, do the full path traversal to check that path is in the // subtree rooted at atSlot->fd + io_open_restrict_result res = io_open_path_restrict(atSlot->h, path, accessFlags, createFlags, attributesFalgs); + if(res.error) + { + slot->fatal = true; + slot->error = res.error; + } } else - */ + */ { - DWORD atPathWideSize = GetFinalPathNameByHandleW(atSlot->h, NULL, 0, FILE_NAME_NORMALIZED); - if(atPathWideSize) + slot->h = io_open_relative(atSlot->h, path, accessFlags, createFlags, attributesFlags); + if(slot->h == INVALID_HANDLE_VALUE) { - LPWSTR fullPathWide = mem_arena_alloc_array(scratch.arena, wchar_t, atPathWideSize + pathWideSize + 1); - if(GetFinalPathNameByHandleW(atSlot->h, fullPathWide, atPathWideSize, FILE_NAME_NORMALIZED)) - { - fullPathWide[atPathWideSize-1] = '\\'; - memcpy(fullPathWide + atPathWideSize, pathWide, pathWideSize*sizeof(wchar_t)); - - LPWSTR canonical = mem_arena_alloc_array(scratch.arena, wchar_t, atPathWideSize + pathWideSize + 1); - PathCanonicalizeW(canonical, fullPathWide); - - slot->h = CreateFileW(canonical, accessFlags, 0, NULL, createFlags, attributesFlags, NULL); - } + slot->fatal = true; + slot->error = io_convert_win32_error(GetLastError()); } } } else { //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); + slot->h = CreateFileW(pathW.ptr, accessFlags, 0, NULL, createFlags, attributesFlags, NULL); + if(slot->h == INVALID_HANDLE_VALUE) + { + slot->fatal = true; + slot->error = io_convert_win32_error(GetLastError()); + } } mem_scratch_end(scratch); - - if(slot->h == INVALID_HANDLE_VALUE) - { - slot->fatal = true; - slot->error = io_convert_win32_error(GetLastError()); - } } cmp.error = slot->error; } diff --git a/src/util/strings.c b/src/util/strings.c index 511e7e2..aad5239 100644 --- a/src/util/strings.c +++ b/src/util/strings.c @@ -221,6 +221,90 @@ str8_list str8_split(mem_arena* arena, str8 str, str8_list separators) return(list); } +//---------------------------------------------------------------------------------- +// u16 strings +//---------------------------------------------------------------------------------- +str16 str16_from_buffer(u64 len, u16* buffer) +{ + return((str16){.len = len, .ptr = buffer}); +} + +str16 str16_slice(str16 s, u64 start, u64 end) +{ + ASSERT(start <= end && start <= s.len && end <= s.len); + return((str16){.len = end - start, .ptr = s.ptr + start}); +} + +str16 str16_push_buffer(mem_arena* arena, u64 len, u16* buffer) +{ + str16 str = {0}; + str.len = len; + str.ptr = mem_arena_alloc_array(arena, u16, len); + memcpy(str.ptr, buffer, len*sizeof(u16)); + return(str); +} + +str16 str16_push_copy(mem_arena* arena, str16 s) +{ + return(str16_push_buffer(arena, s.len, s.ptr)); +} + +str16 str16_push_slice(mem_arena* arena, str16 s, u64 start, u64 end) +{ + str16 slice = str16_slice(s, start, end); + return(str16_push_copy(arena, slice)); +} + +void str16_list_init(str16_list* list) +{ + list_init(&list->list); + list->eltCount = 0; + list->len = 0; +} + +void str16_list_push(mem_arena* arena, str16_list* list, str16 str) +{ + str16_elt* elt = mem_arena_alloc_type(arena, str16_elt); + elt->string = str; + list_append(&list->list, &elt->listElt); + list->eltCount++; + list->len += str.len; +} + +str16 str16_list_collate(mem_arena* arena, str16_list list, str16 prefix, str16 separator, str16 postfix) +{ + str16 str = {0}; + str.len = prefix.len + list.len + list.eltCount*separator.len + postfix.len; + str.ptr = mem_arena_alloc_array(arena, u16, str.len); + char* dst = (char*)str.ptr; + memcpy(dst, prefix.ptr, prefix.len*sizeof(u16)); + dst += prefix.len*sizeof(u16); + + str16_elt* elt = list_first_entry(&list.list, str16_elt, listElt); + if(elt) + { + memcpy(dst, elt->string.ptr, elt->string.len*sizeof(u16)); + dst += elt->string.len*sizeof(u16); + elt = list_next_entry(&list.list, elt, str16_elt, listElt); + } + + for( ; elt != 0; elt = list_next_entry(&list.list, elt, str16_elt, listElt)) + { + memcpy(dst, separator.ptr, separator.len*sizeof(u16)); + dst += separator.len*sizeof(u16); + memcpy(dst, elt->string.ptr, elt->string.len*sizeof(u16)); + dst += elt->string.len*sizeof(u16); + } + memcpy(dst, postfix.ptr, postfix.len*sizeof(u16)); + return(str); +} + +str16 str16_list_join(mem_arena* arena, str16_list list) +{ + str16 empty = {.len = 0, .ptr = 0}; + return(str16_list_collate(arena, list, empty, empty, empty)); +} + //---------------------------------------------------------------------------------- // u32 strings //---------------------------------------------------------------------------------- diff --git a/src/util/strings.h b/src/util/strings.h index 04cfd4c..4951a5c 100644 --- a/src/util/strings.h +++ b/src/util/strings.h @@ -70,6 +70,39 @@ MP_API str8 str8_list_collate(mem_arena* arena, str8_list list, str8 prefix, str MP_API str8 str8_list_join(mem_arena* arena, str8_list list); MP_API str8_list str8_split(mem_arena* arena, str8 str, str8_list separators); +//---------------------------------------------------------------------------------- +// u16 strings +//---------------------------------------------------------------------------------- +typedef struct str16 +{ + u64 len; + u16* ptr; +} str16; + +MP_API str16 str16_from_buffer(u64 len, u16* buffer); +MP_API str16 str16_slice(str16 s, u64 start, u64 end); + +MP_API str16 str16_push_buffer(mem_arena* arena, u64 len, u16* buffer); +MP_API str16 str16_push_copy(mem_arena* arena, str16 s); +MP_API str16 str16_push_slice(mem_arena* arena, str16 s, u64 start, u64 end); + +typedef struct str16_elt +{ + list_elt listElt; + str16 string; +} str16_elt; + +typedef struct str16_list +{ + list_info list; + u64 eltCount; + u64 len; +} str16_list; + +MP_API void str16_list_push(mem_arena* arena, str16_list* list, str16 str); +MP_API str16 str16_list_join(mem_arena* arena, str16_list list); +MP_API str16_list str16_split(mem_arena* arena, str16 str, str16_list separators); + //---------------------------------------------------------------------------------- // u32 strings //----------------------------------------------------------------------------------