[io, win32] separate 'raw io' primitives from the io_open_restrict() implementation

This commit is contained in:
martinfouilleul 2023-06-14 19:41:51 +02:00
parent 49643520ee
commit c71da9e761
2 changed files with 122 additions and 93 deletions

View File

@ -191,6 +191,7 @@ enum file_perm
typedef struct file_status typedef struct file_status
{ {
u64 uid;
file_type type; file_type type;
file_perm perm; file_perm perm;
u64 size; u64 size;

View File

@ -14,9 +14,11 @@
#include"platform_io_internal.c" #include"platform_io_internal.c"
#include"platform_io_common.c" #include"platform_io_common.c"
io_error io_convert_win32_error(int winError) io_error io_raw_last_error()
{ {
io_error error = 0; io_error error = 0;
int winError = GetLastError();
switch(winError) switch(winError)
{ {
case ERROR_SUCCESS: case ERROR_SUCCESS:
@ -106,7 +108,19 @@ str16 win32_path_from_handle_null_terminated(mem_arena* arena, HANDLE handle)
return(res); return(res);
} }
HANDLE io_open_relative(HANDLE dirHandle, str8 path, file_access_rights accessRights, file_open_flags openFlags) typedef HANDLE io_file_desc;
io_file_desc io_file_desc_nil()
{
return(INVALID_HANDLE_VALUE);
}
bool io_file_desc_invalid(io_file_desc fd)
{
return(fd == NULL || fd == INVALID_HANDLE_VALUE);
}
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; HANDLE handle = INVALID_HANDLE_VALUE;
@ -166,13 +180,13 @@ HANDLE io_open_relative(HANDLE dirHandle, str8 path, file_access_rights accessRi
mem_arena_scope scratch = mem_scratch_begin(); mem_arena_scope scratch = mem_scratch_begin();
str16 pathW = win32_utf8_to_wide_null_terminated(scratch.arena, path); str16 pathW = win32_utf8_to_wide_null_terminated(scratch.arena, path);
if(dirHandle == NULL || dirHandle == INVALID_HANDLE_VALUE) if(dirFd == NULL || dirFd == INVALID_HANDLE_VALUE)
{ {
handle = CreateFileW(pathW.ptr, win32AccessFlags, win32ShareMode, NULL, win32CreateFlags, win32AttributeFlags, NULL); handle = CreateFileW(pathW.ptr, win32AccessFlags, win32ShareMode, NULL, win32CreateFlags, win32AttributeFlags, NULL);
} }
else else
{ {
str16 dirPathW = win32_path_from_handle_null_terminated(scratch.arena, dirHandle); str16 dirPathW = win32_path_from_handle_null_terminated(scratch.arena, dirFd);
if(dirPathW.len && pathW.len) if(dirPathW.len && pathW.len)
{ {
@ -192,26 +206,24 @@ HANDLE io_open_relative(HANDLE dirHandle, str8 path, file_access_rights accessRi
return(handle); return(handle);
} }
u64 io_win32_file_uid(HANDLE h) void io_raw_close(io_file_desc fd)
{ {
BY_HANDLE_FILE_INFORMATION fileInfo; CloseHandle(fd);
GetFileInformationByHandle(h, &fileInfo);
u64 id = ((u64)fileInfo.nFileIndexHigh<<32) | ((u64)fileInfo.nFileIndexLow);
return(id);
} }
io_error io_win32_stat_from_handle(HANDLE h, file_status* status) io_error io_raw_stat(io_file_desc fd, file_status* status)
{ {
io_error error = IO_OK; io_error error = IO_OK;
BY_HANDLE_FILE_INFORMATION info; BY_HANDLE_FILE_INFORMATION info;
if(!GetFileInformationByHandle(h, &info)) if(!GetFileInformationByHandle(fd, &info))
{ {
error = io_convert_win32_error(GetLastError()); error = io_raw_last_error();
} }
else else
{ {
status->size = (((u64)info.nFileSizeHigh)<<32) | ((u64)info.nFileSizeLow); status->size = (((u64)info.nFileSizeHigh)<<32) | ((u64)info.nFileSizeLow);
status->uid = ((u64)info.nFileIndexHigh<<32) | ((u64)info.nFileIndexLow);
DWORD attrRegularSet = FILE_ATTRIBUTE_ARCHIVE DWORD attrRegularSet = FILE_ATTRIBUTE_ARCHIVE
| FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_COMPRESSED
@ -228,9 +240,9 @@ io_error io_win32_stat_from_handle(HANDLE h, file_status* status)
if((info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) if((info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
{ {
FILE_ATTRIBUTE_TAG_INFO tagInfo; FILE_ATTRIBUTE_TAG_INFO tagInfo;
if(!GetFileInformationByHandleEx(h, FileAttributeTagInfo, &tagInfo, sizeof(tagInfo))) if(!GetFileInformationByHandleEx(fd, FileAttributeTagInfo, &tagInfo, sizeof(tagInfo)))
{ {
error = io_convert_win32_error(GetLastError()); error = io_raw_last_error();
} }
else if(tagInfo.ReparseTag == IO_REPARSE_TAG_SYMLINK) else if(tagInfo.ReparseTag == IO_REPARSE_TAG_SYMLINK)
{ {
@ -265,6 +277,35 @@ io_error io_win32_stat_from_handle(HANDLE h, file_status* status)
return(error); 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 error = IO_OK;
io_file_desc fd = io_raw_open_at(dirFd, name, FILE_ACCESS_READ, FILE_OPEN_SYMLINK);
if(io_file_desc_invalid(fd))
{
error = io_raw_last_error();
}
else
{
error = io_raw_stat(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 typedef struct
{ {
ULONG ReparseTag; ULONG ReparseTag;
@ -298,22 +339,16 @@ typedef struct
}; };
} REPARSE_DATA_BUFFER; } REPARSE_DATA_BUFFER;
typedef struct io_win32_read_link_result io_raw_read_link_result io_raw_read_link(mem_arena* arena, io_file_desc fd)
{ {
io_error error; io_raw_read_link_result result = {0};
str8 targetPath;
} io_win32_read_link_result;
io_win32_read_link_result io_win32_read_link(mem_arena* arena, HANDLE handle)
{
io_win32_read_link_result result = {0};
char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
DWORD bytesReturned; DWORD bytesReturned;
if(!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0, buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned, 0)) if(!DeviceIoControl(fd, FSCTL_GET_REPARSE_POINT, NULL, 0, buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned, 0))
{ {
result.error = io_convert_win32_error(GetLastError()); result.error = io_raw_last_error();
} }
else else
{ {
@ -323,7 +358,7 @@ io_win32_read_link_result io_win32_read_link(mem_arena* arena, HANDLE handle)
str16 nameW = {0}; str16 nameW = {0};
nameW.len = reparse->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t); nameW.len = reparse->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
nameW.ptr = (u16*)((char*)reparse->SymbolicLinkReparseBuffer.PathBuffer + reparse->SymbolicLinkReparseBuffer.SubstituteNameOffset); nameW.ptr = (u16*)((char*)reparse->SymbolicLinkReparseBuffer.PathBuffer + reparse->SymbolicLinkReparseBuffer.SubstituteNameOffset);
result.targetPath = win32_wide_to_utf8(arena, nameW); result.target = win32_wide_to_utf8(arena, nameW);
} }
else else
{ {
@ -333,39 +368,49 @@ io_win32_read_link_result io_win32_read_link(mem_arena* arena, HANDLE handle)
return(result); return(result);
} }
io_raw_read_link_result io_raw_read_link_at(mem_arena* arena, io_file_desc dirFd, str8 name)
{
io_file_desc fd = io_raw_open_at(dirFd, name, FILE_ACCESS_READ, FILE_OPEN_SYMLINK);
io_raw_read_link_result result = io_raw_read_link(arena, fd);
io_raw_close(fd);
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_invalid(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 typedef struct io_open_restrict_result
{ {
io_error error; io_error error;
HANDLE h; HANDLE h;
} io_open_restrict_result; } io_open_restrict_result;
typedef struct io_open_restrict_context
{
io_error error;
u64 rootUID;
HANDLE rootHandle;
HANDLE handle;
} io_open_restrict_context; io_open_restrict_result io_open_path_restrict(io_file_desc dirFd, str8 path, file_access_rights accessRights, file_open_flags openFlags)
io_error io_open_restrict_enter(io_open_restrict_context* context, str8 name, file_access_rights accessRights, file_open_flags openFlags)
{
HANDLE nextHandle = io_open_relative(context->handle, name, accessRights, openFlags);
if(nextHandle == INVALID_HANDLE_VALUE)
{
context->error = io_convert_win32_error(GetLastError());
}
else
{
if(context->handle != context->rootHandle)
{
CloseHandle(context->handle);
}
context->handle = nextHandle;
}
}
io_open_restrict_result io_open_path_restrict(HANDLE dirHandle, str8 path, file_access_rights accessRights, file_open_flags openFlags)
{ {
mem_arena_scope scratch = mem_scratch_begin(); mem_arena_scope scratch = mem_scratch_begin();
@ -375,17 +420,13 @@ io_open_restrict_result io_open_path_restrict(HANDLE dirHandle, str8 path, file_
io_open_restrict_context context = { io_open_restrict_context context = {
.error = IO_OK, .error = IO_OK,
.rootHandle = dirHandle, .rootFd = dirFd,
.rootUID = io_win32_file_uid(dirHandle), .rootUID = io_raw_uid(dirFd),
.handle = dirHandle, .fd = dirFd,
}; };
for_list(&pathElements.list, elt, str8_elt, listElt) for_list(&pathElements.list, elt, str8_elt, listElt)
{ {
if(context.error != IO_OK)
{
break;
}
str8 name = elt->string; str8 name = elt->string;
if(!str8_cmp(name, STR8("."))) if(!str8_cmp(name, STR8(".")))
@ -396,7 +437,7 @@ io_open_restrict_result io_open_path_restrict(HANDLE dirHandle, str8 path, file_
else if(!str8_cmp(name, STR8(".."))) else if(!str8_cmp(name, STR8("..")))
{ {
//NOTE: check that we don't escape root dir //NOTE: check that we don't escape root dir
if(io_win32_file_uid(context.handle) == context.rootUID) if(io_raw_uid(context.fd) == context.rootUID)
{ {
context.error = IO_ERR_WALKOUT; context.error = IO_ERR_WALKOUT;
break; break;
@ -408,15 +449,8 @@ io_open_restrict_result io_open_path_restrict(HANDLE dirHandle, str8 path, file_
} }
else else
{ {
HANDLE statHandle = io_open_relative(context.handle, name, FILE_ACCESS_READ, FILE_OPEN_SYMLINK);
if(statHandle == INVALID_HANDLE_VALUE)
{
context.error = io_convert_win32_error(GetLastError());
break;
}
file_status status = {0}; file_status status = {0};
context.error = io_win32_stat_from_handle(statHandle, &status); context.error = io_raw_stat_at(context.fd, name, FILE_OPEN_SYMLINK, &status);
CloseHandle(statHandle);
if(context.error) if(context.error)
{ {
break; break;
@ -425,33 +459,27 @@ io_open_restrict_result io_open_path_restrict(HANDLE dirHandle, str8 path, file_
if(status.type == MP_FILE_SYMLINK) if(status.type == MP_FILE_SYMLINK)
{ {
//NOTE: read link target and add to file //NOTE: read link target and add to file
HANDLE link = io_open_relative(context.handle, name, FILE_ACCESS_READ, FILE_OPEN_SYMLINK); io_raw_read_link_result link = io_raw_read_link_at(scratch.arena, context.fd, name);
if(link == INVALID_HANDLE_VALUE)
{
context.error = io_convert_win32_error(GetLastError());
break;
}
io_win32_read_link_result linkResult = io_win32_read_link(scratch.arena, link);
CloseHandle(link);
if(linkResult.error) if(link.error)
{ {
context.error = linkResult.error; context.error = link.error;
break; break;
} }
if(linkResult.targetPath.len == 0)
if(link.target.len == 0)
{ {
// skip // skip
} }
else if(linkResult.targetPath.ptr[0] == '/' else if( link.target.ptr[0] == '/'
||linkResult.targetPath.ptr[0] == '\\') || link.target.ptr[0] == '\\')
{ {
context.error = IO_ERR_WALKOUT; context.error = IO_ERR_WALKOUT;
break; break;
} }
else else
{ {
str8_list linkElements = str8_split(scratch.arena, linkResult.targetPath, sep); str8_list linkElements = str8_split(scratch.arena, link.target, sep);
if(!list_empty(&linkElements.list)) if(!list_empty(&linkElements.list))
{ {
//NOTE: insert linkElements into pathElements after elt //NOTE: insert linkElements into pathElements after elt
@ -492,16 +520,16 @@ io_open_restrict_result io_open_path_restrict(HANDLE dirHandle, str8 path, file_
if(context.error) if(context.error)
{ {
if(context.handle != context.rootHandle) if(context.fd != context.rootFd)
{ {
CloseHandle(context.handle); io_raw_close(context.fd);
context.handle = INVALID_HANDLE_VALUE; context.fd = io_file_desc_nil();
} }
} }
io_open_restrict_result result = { io_open_restrict_result result = {
.error = context.error, .error = context.error,
.h = context.handle .h = context.fd
}; };
mem_scratch_end(scratch); mem_scratch_end(scratch);
@ -555,22 +583,22 @@ io_cmp io_open_at(file_slot* atSlot, io_req* req, file_table* table)
} }
else else
{ {
slot->h = io_open_relative(atSlot->h, path, req->open.rights, req->open.flags); slot->h = io_raw_open_at(atSlot->h, path, req->open.rights, req->open.flags);
if(slot->h == INVALID_HANDLE_VALUE) if(slot->h == INVALID_HANDLE_VALUE)
{ {
slot->fatal = true; slot->fatal = true;
slot->error = io_convert_win32_error(GetLastError()); slot->error = io_raw_last_error();
} }
} }
} }
else else
{ {
//TODO: take care of share mode and security attributes, make it consistent with posix impl //TODO: take care of share mode and security attributes, make it consistent with posix impl
slot->h = io_open_relative(NULL, path, req->open.rights, req->open.flags); slot->h = io_raw_open_at(NULL, path, req->open.rights, req->open.flags);
if(slot->h == INVALID_HANDLE_VALUE) if(slot->h == INVALID_HANDLE_VALUE)
{ {
slot->fatal = true; slot->fatal = true;
slot->error = io_convert_win32_error(GetLastError()); slot->error = io_raw_last_error();
} }
} }
@ -603,7 +631,7 @@ io_cmp io_fstat(file_slot* slot, io_req* req)
} }
else else
{ {
slot->error = io_win32_stat_from_handle(slot->h, (file_status*)req->buffer); slot->error = io_raw_stat(slot->h, (file_status*)req->buffer);
cmp.error = slot->error; cmp.error = slot->error;
} }
return(cmp); return(cmp);
@ -633,7 +661,7 @@ io_cmp io_seek(file_slot* slot, io_req* req)
if(!SetFilePointerEx(slot->h, off, &newPos, whence)) if(!SetFilePointerEx(slot->h, off, &newPos, whence))
{ {
slot->error = io_convert_win32_error(GetLastError()); slot->error = io_raw_last_error();
cmp.error = slot->error; cmp.error = slot->error;
} }
else else
@ -652,7 +680,7 @@ io_cmp io_read(file_slot* slot, io_req* req)
if(!ReadFile(slot->h, req->buffer, req->size, &bytesRead, NULL)) if(!ReadFile(slot->h, req->buffer, req->size, &bytesRead, NULL))
{ {
slot->error = io_convert_win32_error(GetLastError()); slot->error = io_raw_last_error();
cmp.result = 0; cmp.result = 0;
cmp.error = slot->error; cmp.error = slot->error;
} }
@ -671,7 +699,7 @@ io_cmp io_write(file_slot* slot, io_req* req)
if(!WriteFile(slot->h, req->buffer, req->size, &bytesWritten, NULL)) if(!WriteFile(slot->h, req->buffer, req->size, &bytesWritten, NULL))
{ {
slot->error = io_convert_win32_error(GetLastError()); slot->error = io_raw_last_error();
cmp.result = 0; cmp.result = 0;
cmp.error = slot->error; cmp.error = slot->error;
} }