[win32, io] win32 passes files io test with new common io_open_at implementation

This commit is contained in:
martinfouilleul 2023-06-16 15:30:48 +02:00
parent a64c86ca4e
commit debcffce2a
7 changed files with 159 additions and 226 deletions

View File

@ -17,8 +17,9 @@
#if PLATFORM_WINDOWS
#include"platform/win32_memory.c"
#include"platform/win32_clock.c"
#include"platform/win32_io.c"
#include"platform/win32_string_helpers.c"
#include"platform/win32_path.c"
#include"platform/win32_io.c"
//TODO
#elif PLATFORM_MACOS
#include"platform/unix_memory.c"

View File

@ -109,6 +109,7 @@ io_open_restrict_result io_open_restrict(io_file_desc dirFd, str8 path, file_acc
str8_list sep = {0};
str8_list_push(scratch.arena, &sep, STR8("/"));
str8_list_push(scratch.arena, &sep, STR8("\\"));
str8_list pathElements = str8_split(scratch.arena, path, sep);
io_open_restrict_context context = {
@ -277,7 +278,7 @@ io_cmp io_open_at(file_slot* atSlot, io_req* req, file_table* table)
}
else
{
slot->fd = -1;
slot->fd = io_file_desc_nil();
cmp.handle = file_handle_from_slot(table, slot);
@ -324,6 +325,16 @@ io_cmp io_open_at(file_slot* atSlot, io_req* req, file_table* table)
}
}
if(slot->error == IO_OK)
{
file_status status;
slot->error = io_raw_fstat(slot->fd, &status);
if(slot->error == IO_OK)
{
slot->type = status.type;
}
}
if(slot->error)
{
slot->fatal = true;

View File

@ -26,6 +26,7 @@ typedef struct file_slot
bool fatal;
list_elt freeListElt;
file_type type;
file_access_rights rights;
io_file_desc fd;

View File

@ -11,6 +11,7 @@
#include<shlwapi.h>
#include<winioctl.h>
#include"win32_string_helpers.h"
#include"platform_io_internal.c"
#include"platform_io_common.c"
@ -72,25 +73,6 @@ io_error io_raw_last_error()
return(error);
}
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);
}
str8 win32_wide_to_utf8(mem_arena* arena, str16 s)
{
str8 res = {0};
res.len = WideCharToMultiByte(CP_UTF8, 0, s.ptr, s.len, NULL, 0, NULL, NULL);
res.ptr = mem_arena_alloc_array(arena, u8, res.len);
WideCharToMultiByte(CP_UTF8, 0, s.ptr, s.len, res.ptr, res.len, NULL, NULL);
return(res);
}
str16 win32_path_from_handle_null_terminated(mem_arena* arena, HANDLE handle)
{
str16 res = {0};
@ -120,6 +102,40 @@ bool io_file_desc_is_nil(io_file_desc fd)
return(fd == NULL || fd == INVALID_HANDLE_VALUE);
}
str16 win32_get_path_at_null_terminated(mem_arena* arena, io_file_desc dirFd, str8 path)
{
str16 result = {0};
mem_arena_scope scratch = mem_scratch_begin_next(arena);
str16 dirPathW = win32_path_from_handle_null_terminated(scratch.arena, dirFd);
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));
result.len = fullPathWSize;
result.ptr = mem_arena_alloc_array(arena, wchar_t, result.len);
if(PathCanonicalizeW(result.ptr, fullPathW))
{
result.len = lstrlenW(result.ptr);
}
else
{
result.ptr = 0;
result.len = 0;
}
}
mem_scratch_end(scratch);
return(result);
}
io_file_desc io_raw_open_at(io_file_desc dirFd, str8 path, file_access_rights accessRights, file_open_flags openFlags)
{
HANDLE handle = INVALID_HANDLE_VALUE;
@ -186,20 +202,10 @@ io_file_desc io_raw_open_at(io_file_desc dirFd, str8 path, file_access_rights ac
}
else
{
str16 dirPathW = win32_path_from_handle_null_terminated(scratch.arena, dirFd);
if(dirPathW.len && pathW.len)
str16 fullPathW = win32_get_path_at_null_terminated(scratch.arena, dirFd, path);
if(fullPathW.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, win32AccessFlags, win32ShareMode, NULL, win32CreateFlags, win32AttributeFlags, NULL);
handle = CreateFileW(fullPathW.ptr, win32AccessFlags, win32ShareMode, NULL, win32CreateFlags, win32AttributeFlags, NULL);
}
}
mem_scratch_end(scratch);
@ -211,7 +217,19 @@ void io_raw_close(io_file_desc fd)
CloseHandle(fd);
}
io_error io_raw_stat(io_file_desc fd, file_status* status)
bool io_raw_file_exists_at(io_file_desc dirFd, str8 path, file_open_flags openFlags)
{
bool result = false;
io_file_desc fd = io_raw_open_at(dirFd, path, FILE_ACCESS_NONE, (openFlags & FILE_OPEN_SYMLINK));
if(!io_file_desc_is_nil(fd))
{
result = true;
io_raw_close(fd);
}
return(result);
}
io_error io_raw_fstat(io_file_desc fd, file_status* status)
{
io_error error = IO_OK;
@ -277,35 +295,22 @@ io_error io_raw_stat(io_file_desc fd, file_status* status)
return(error);
}
u64 io_raw_uid(io_file_desc fd)
{
file_status status;
io_raw_stat(fd, &status);
return(status.uid);
}
io_error io_raw_stat_at(io_file_desc dirFd, str8 name, file_open_flags openFlags, file_status* status)
io_error io_raw_fstat_at(io_file_desc dirFd, str8 name, file_open_flags openFlags, file_status* status)
{
io_error error = IO_OK;
io_file_desc fd = io_raw_open_at(dirFd, name, FILE_ACCESS_READ, FILE_OPEN_SYMLINK);
io_file_desc fd = io_raw_open_at(dirFd, name, FILE_ACCESS_NONE, FILE_OPEN_SYMLINK);
if(io_file_desc_is_nil(fd))
{
error = io_raw_last_error();
}
else
{
error = io_raw_stat(fd, status);
error = io_raw_fstat(fd, status);
io_raw_close(fd);
}
return(error);
}
typedef struct io_raw_read_link_result
{
io_error error;
str8 target;
} io_raw_read_link_result;
typedef struct
{
ULONG ReparseTag;
@ -376,166 +381,6 @@ io_raw_read_link_result io_raw_read_link_at(mem_arena* arena, io_file_desc dirFd
return(result);
}
typedef struct io_open_restrict_context
{
io_error error;
u64 rootUID;
io_file_desc rootFd;
io_file_desc fd;
} io_open_restrict_context;
io_error io_open_restrict_enter(io_open_restrict_context* context, str8 name, file_access_rights accessRights, file_open_flags openFlags)
{
io_file_desc nextFd = io_raw_open_at(context->fd, name, accessRights, openFlags);
if(io_file_desc_is_nil(nextFd))
{
context->error = io_raw_last_error();
}
else
{
if(context->fd != context->rootFd)
{
io_raw_close(context->fd);
}
context->fd = nextFd;
}
return(context->error);
}
typedef struct io_open_restrict_result
{
io_error error;
HANDLE h;
} io_open_restrict_result;
io_open_restrict_result io_open_path_restrict(io_file_desc dirFd, str8 path, file_access_rights accessRights, file_open_flags openFlags)
{
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);
io_open_restrict_context context = {
.error = IO_OK,
.rootFd = dirFd,
.rootUID = io_raw_uid(dirFd),
.fd = dirFd,
};
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(io_raw_uid(context.fd) == context.rootUID)
{
context.error = IO_ERR_WALKOUT;
break;
}
else
{
io_open_restrict_enter(&context, name, FILE_ACCESS_READ, 0);
}
}
else
{
file_status status = {0};
context.error = io_raw_stat_at(context.fd, name, FILE_OPEN_SYMLINK, &status);
if(context.error)
{
break;
}
if(status.type == MP_FILE_SYMLINK)
{
//NOTE: read link target and add to file
io_raw_read_link_result link = io_raw_read_link_at(scratch.arena, context.fd, name);
if(link.error)
{
context.error = link.error;
break;
}
if(link.target.len == 0)
{
// skip
}
else if( link.target.ptr[0] == '/'
|| link.target.ptr[0] == '\\')
{
context.error = IO_ERR_WALKOUT;
break;
}
else
{
str8_list linkElements = str8_split(scratch.arena, link.target, sep);
if(!list_empty(&linkElements.list))
{
//NOTE: insert linkElements into pathElements after elt
list_elt* tmp = elt->listElt.next;
elt->listElt.next = linkElements.list.first;
linkElements.list.last->next = tmp;
if(!tmp)
{
pathElements.list.last = linkElements.list.last;
}
}
}
}
else if(status.type == MP_FILE_DIRECTORY)
{
//NOTE: descend in directory
io_open_restrict_enter(&context, name, FILE_ACCESS_READ, 0);
}
else if(status.type == MP_FILE_REGULAR)
{
//NOTE: check that we're at the end of path and open that file
if(&elt->listElt != list_last(&pathElements.list))
{
context.error = IO_ERR_NOT_DIR;
break;
}
else
{
io_open_restrict_enter(&context, name, accessRights, openFlags);
}
}
else
{
context.error = IO_ERR_NO_ENTRY;
}
}
}
if(context.error)
{
if(context.fd != context.rootFd)
{
io_raw_close(context.fd);
context.fd = io_file_desc_nil();
}
}
io_open_restrict_result result = {
.error = context.error,
.h = context.fd
};
mem_scratch_end(scratch);
return(result);
}
io_cmp io_close(file_slot* slot, io_req* req, file_table* table)
{
io_cmp cmp = {0};
@ -557,7 +402,7 @@ io_cmp io_fstat(file_slot* slot, io_req* req)
}
else
{
slot->error = io_raw_stat(slot->fd, (file_status*)req->buffer);
slot->error = io_raw_fstat(slot->fd, (file_status*)req->buffer);
cmp.error = slot->error;
}
return(cmp);
@ -602,6 +447,13 @@ io_cmp io_read(file_slot* slot, io_req* req)
{
io_cmp cmp = {0};
if(slot->type != MP_FILE_REGULAR)
{
slot->error = IO_ERR_PERM;
cmp.error = slot->error;
}
else
{
DWORD bytesRead = 0;
if(!ReadFile(slot->fd, req->buffer, req->size, &bytesRead, NULL))
@ -614,6 +466,7 @@ io_cmp io_read(file_slot* slot, io_req* req)
{
cmp.result = bytesRead;
}
}
return(cmp);
}
@ -621,6 +474,13 @@ io_cmp io_write(file_slot* slot, io_req* req)
{
io_cmp cmp = {0};
if(slot->type != MP_FILE_REGULAR)
{
slot->error = IO_ERR_PERM;
cmp.error = slot->error;
}
else
{
DWORD bytesWritten = 0;
if(!WriteFile(slot->fd, req->buffer, req->size, &bytesWritten, NULL))
@ -633,7 +493,7 @@ io_cmp io_write(file_slot* slot, io_req* req)
{
cmp.result = bytesWritten;
}
}
return(cmp);
}

View File

@ -5,9 +5,21 @@
* @date: 24/05/2023
*
*****************************************************************/
#include<shlwapi.h> // PathIsRelative()
#include"win32_string_helpers.h"
#include"platform_path.c"
bool path_is_absolute(str8 path)
{
mem_arena_scope scratch = mem_scratch_begin();
str16 pathW = win32_utf8_to_wide_null_terminated(scratch.arena, path);
bool result = !PathIsRelativeW(pathW.ptr);
mem_scratch_end(scratch);
return(result);
}
str8 path_executable(mem_arena* arena)
{
//TODO use wide chars

View File

@ -0,0 +1,31 @@
/************************************************************//**
*
* @file: win32_string_helpers.c
* @author: Martin Fouilleul
* @date: 24/05/2023
*
*****************************************************************/
#define WIN32_LEAN_AND_MEAN
#include<windows.h>
#include"win32_string_helpers.h"
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);
}
str8 win32_wide_to_utf8(mem_arena* arena, str16 s)
{
str8 res = {0};
res.len = WideCharToMultiByte(CP_UTF8, 0, s.ptr, s.len, NULL, 0, NULL, NULL);
res.ptr = mem_arena_alloc_array(arena, u8, res.len);
WideCharToMultiByte(CP_UTF8, 0, s.ptr, s.len, res.ptr, res.len, NULL, NULL);
return(res);
}

View File

@ -0,0 +1,17 @@
/************************************************************//**
*
* @file: win32_string_helpers.h
* @author: Martin Fouilleul
* @date: 24/05/2023
*
*****************************************************************/
#ifndef __WIN32_STRING_HELPERS_H_
#define __WIN32_STRING_HELPERS_H_
#include"util/strings.h"
str16 win32_utf8_to_wide_null_terminated(mem_arena* arena, str8 s);
str8 win32_wide_to_utf8(mem_arena* arena, str16 s);
#endif // __WIN32_STRING_HELPERS_H_