[io] implementing IO_OP_OPEN_AT with FILE_OPEN_RESTRICT flag for sandboxing file access
This commit is contained in:
parent
c041b212ab
commit
b2d2d2a587
|
@ -7,7 +7,6 @@
|
||||||
*****************************************************************/
|
*****************************************************************/
|
||||||
|
|
||||||
#include"platform_io.h"
|
#include"platform_io.h"
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// File stream read/write API
|
// File stream read/write API
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
/************************************************************//**
|
||||||
|
*
|
||||||
|
* @file: platform_io_internal.c
|
||||||
|
* @author: Martin Fouilleul
|
||||||
|
* @date: 11/06/2023
|
||||||
|
*
|
||||||
|
*****************************************************************/
|
||||||
|
|
||||||
|
#include"platform_io_internal.h"
|
||||||
|
|
||||||
|
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->fd = -1;
|
||||||
|
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_cmp io_wait_single_req(io_req* req)
|
||||||
|
{
|
||||||
|
return(io_wait_single_req_with_table(req, &__globalFileTable));
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/************************************************************//**
|
||||||
|
*
|
||||||
|
* @file: platform_io_internal.h
|
||||||
|
* @author: Martin Fouilleul
|
||||||
|
* @date: 10/06/2023
|
||||||
|
*
|
||||||
|
*****************************************************************/
|
||||||
|
#ifndef __PLATFORM_IO_INTERNAL_H_
|
||||||
|
#define __PLATFORM_IO_INTERNAL_H_
|
||||||
|
|
||||||
|
#include"platform_io.h"
|
||||||
|
#include"platform.h"
|
||||||
|
|
||||||
|
#if PLATFORM_MACOS || PLATFORM_LINUX
|
||||||
|
#define PLATFORM_IO_NATIVE_MEMBER int fd
|
||||||
|
#elif PLATFORM_WINDOWS
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include<windows>
|
||||||
|
#define PLATFORM_IO_NATIVE_MEMBER HANDLE h
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct file_slot
|
||||||
|
{
|
||||||
|
u32 generation;
|
||||||
|
io_error error;
|
||||||
|
bool fatal;
|
||||||
|
list_elt freeListElt;
|
||||||
|
|
||||||
|
PLATFORM_IO_NATIVE_MEMBER;
|
||||||
|
|
||||||
|
} 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_slot* file_slot_alloc(file_table* table);
|
||||||
|
void file_slot_recycle(file_table* table, file_slot* slot);
|
||||||
|
file_handle file_handle_from_slot(file_table* table, file_slot* slot);
|
||||||
|
file_slot* file_slot_from_handle(file_table* table, file_handle handle);
|
||||||
|
|
||||||
|
io_cmp io_wait_single_req_with_table(io_req* req, file_table* table);
|
||||||
|
|
||||||
|
#endif //__PLATFORM_IO_INTERNAL_H_
|
|
@ -13,75 +13,7 @@
|
||||||
#include<limits.h>
|
#include<limits.h>
|
||||||
|
|
||||||
#include"platform_io_common.c"
|
#include"platform_io_common.c"
|
||||||
|
#include"platform_io_internal.c"
|
||||||
typedef struct file_slot
|
|
||||||
{
|
|
||||||
u32 generation;
|
|
||||||
int fd;
|
|
||||||
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->fd = -1;
|
|
||||||
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_errno(int e)
|
io_error io_convert_errno(int e)
|
||||||
{
|
{
|
||||||
|
@ -219,11 +151,203 @@ int io_convert_open_flags(file_open_flags flags)
|
||||||
return(oflags);
|
return(oflags);
|
||||||
}
|
}
|
||||||
|
|
||||||
io_cmp io_open_at(file_slot* atSlot, io_req* req)
|
typedef struct io_open_restrict_result
|
||||||
|
{
|
||||||
|
io_error error;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
} io_open_restrict_result;
|
||||||
|
|
||||||
|
io_open_restrict_result io_open_restrict(int dirFd, str8 path, int flags, mode_t mode)
|
||||||
|
{
|
||||||
|
io_open_restrict_result result = {.fd = -1};
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
result.fd = dirFd;
|
||||||
|
if(result.fd < 0)
|
||||||
|
{
|
||||||
|
result.error = io_convert_errno(errno);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
struct stat dirStat;
|
||||||
|
if(fstat(result.fd, &dirStat))
|
||||||
|
{
|
||||||
|
result.error = io_convert_errno(errno);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
ino_t baseInode = dirStat.st_ino;
|
||||||
|
ino_t currentInode = baseInode;
|
||||||
|
|
||||||
|
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(currentInode == baseInode)
|
||||||
|
{
|
||||||
|
result.error = IO_ERR_WALKOUT;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// open that dir and continue
|
||||||
|
int nextFd = openat(result.fd, "..", O_RDONLY);
|
||||||
|
if(!nextFd)
|
||||||
|
{
|
||||||
|
result.error = io_convert_errno(errno);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
close(result.fd);
|
||||||
|
result.fd = nextFd;
|
||||||
|
if(fstat(result.fd, &dirStat))
|
||||||
|
{
|
||||||
|
result.error = io_convert_errno(errno);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
currentInode = dirStat.st_ino;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char* nameCStr = str8_to_cstring(scratch.arena, name);
|
||||||
|
|
||||||
|
if(faccessat(result.fd, nameCStr, F_OK, AT_SYMLINK_NOFOLLOW))
|
||||||
|
{
|
||||||
|
//NOTE: file does not exist. if we're not at the end of path, or we don't have create flag,
|
||||||
|
// return a IO_ERR_NO_ENTRY
|
||||||
|
if(&elt->listElt != list_last(&pathElements.list) && !(flags & O_CREAT))
|
||||||
|
{
|
||||||
|
result.error = IO_ERR_NO_ENTRY;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//NOTE create the flag and return
|
||||||
|
int nextFd = openat(result.fd, nameCStr, flags);
|
||||||
|
if(!nextFd)
|
||||||
|
{
|
||||||
|
result.error = io_convert_errno(errno);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
close(result.fd);
|
||||||
|
result.fd = nextFd;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if(fstatat(result.fd, nameCStr, &st, AT_SYMLINK_NOFOLLOW))
|
||||||
|
{
|
||||||
|
result.error = io_convert_errno(errno);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int type = st.st_mode & S_IFMT;
|
||||||
|
|
||||||
|
if(type == S_IFLNK)
|
||||||
|
{
|
||||||
|
// symlink, check that it's relative, and insert it in elements
|
||||||
|
char buff[PATH_MAX+1];
|
||||||
|
int r = readlinkat(result.fd, nameCStr, buff, PATH_MAX);
|
||||||
|
if(r<0)
|
||||||
|
{
|
||||||
|
result.error = io_convert_errno(errno);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
else if(r == 0)
|
||||||
|
{
|
||||||
|
//NOTE: skip
|
||||||
|
}
|
||||||
|
else if(buff[0] == '/')
|
||||||
|
{
|
||||||
|
result.error = IO_ERR_WALKOUT;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buff[r] = '\0';
|
||||||
|
|
||||||
|
str8_list linkElements = str8_split(scratch.arena, str8_from_buffer(r, buff), 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(type == S_IFDIR)
|
||||||
|
{
|
||||||
|
// dir, open it and continue
|
||||||
|
int nextFd = openat(result.fd, nameCStr, O_RDONLY);
|
||||||
|
if(!nextFd)
|
||||||
|
{
|
||||||
|
result.error = io_convert_errno(errno);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
close(result.fd);
|
||||||
|
result.fd = nextFd;
|
||||||
|
currentInode = st.st_ino;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(type == S_IFREG)
|
||||||
|
{
|
||||||
|
// regular file, check that we're at the last element
|
||||||
|
if(&elt->listElt != list_last(&pathElements.list))
|
||||||
|
{
|
||||||
|
result.error = IO_ERR_NOT_DIR;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.error = IO_ERR_NOT_DIR;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
error:
|
||||||
|
{
|
||||||
|
close(result.fd);
|
||||||
|
result.fd = -1;
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
mem_scratch_end(scratch);
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
io_cmp io_open_at(file_slot* atSlot, io_req* req, file_table* table)
|
||||||
{
|
{
|
||||||
io_cmp cmp = {0};
|
io_cmp cmp = {0};
|
||||||
|
|
||||||
file_slot* slot = file_slot_alloc(&__globalFileTable);
|
file_slot* slot = file_slot_alloc(table);
|
||||||
if(!slot)
|
if(!slot)
|
||||||
{
|
{
|
||||||
cmp.error = IO_ERR_MAX_FILES;
|
cmp.error = IO_ERR_MAX_FILES;
|
||||||
|
@ -231,7 +355,7 @@ io_cmp io_open_at(file_slot* atSlot, io_req* req)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cmp.handle = file_handle_from_slot(&__globalFileTable, slot);
|
cmp.handle = file_handle_from_slot(table, slot);
|
||||||
|
|
||||||
int flags = io_convert_open_flags(req->openFlags);
|
int flags = io_convert_open_flags(req->openFlags);
|
||||||
|
|
||||||
|
@ -247,46 +371,72 @@ io_cmp io_open_at(file_slot* atSlot, io_req* req)
|
||||||
mem_arena_scope scratch = mem_scratch_begin();
|
mem_arena_scope scratch = mem_scratch_begin();
|
||||||
|
|
||||||
str8 path = str8_from_buffer(req->size, req->buffer);
|
str8 path = str8_from_buffer(req->size, req->buffer);
|
||||||
char* pathCStr = str8_to_cstring(scratch.arena, path);
|
|
||||||
|
|
||||||
//TODO: if FILE_OPEN_RESTRICT, do the file traversal to check that path is in the
|
//TODO: if FILE_OPEN_RESTRICT, do the file traversal to check that path is in the
|
||||||
// subtree rooted at atSlot->fd
|
// subtree rooted at atSlot->fd
|
||||||
int fd = -1;
|
slot->fd = -1;
|
||||||
if(atSlot)
|
if(atSlot)
|
||||||
{
|
{
|
||||||
fd = openat(atSlot->fd, pathCStr, flags, mode);
|
if(path.len && path.ptr[0] == '/')
|
||||||
|
{
|
||||||
|
//NOTE: if path is absolute, change for a relative one, otherwise openat ignores fd.
|
||||||
|
str8_list list = {0};
|
||||||
|
str8_list_push(scratch.arena, &list, STR8("."));
|
||||||
|
str8_list_push(scratch.arena, &list, path);
|
||||||
|
path = str8_list_join(scratch.arena, list);
|
||||||
|
}
|
||||||
|
char* pathCStr = str8_to_cstring(scratch.arena, path);
|
||||||
|
|
||||||
|
if(req->openFlags & FILE_OPEN_RESTRICT)
|
||||||
|
{
|
||||||
|
io_open_restrict_result res = io_open_restrict(atSlot->fd, path, flags, mode);
|
||||||
|
if(res.error == IO_OK)
|
||||||
|
{
|
||||||
|
slot->fd = res.fd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
slot->fatal = true;
|
||||||
|
slot->error = res.error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
slot->fd = openat(atSlot->fd, pathCStr, flags, mode);
|
||||||
|
if(slot->fd < 0)
|
||||||
|
{
|
||||||
|
slot->fatal = true;
|
||||||
|
slot->error = io_convert_errno(errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fd = open(pathCStr, flags, mode);
|
char* pathCStr = str8_to_cstring(scratch.arena, path);
|
||||||
|
slot->fd = open(pathCStr, flags, mode);
|
||||||
|
if(slot->fd < 0)
|
||||||
|
{
|
||||||
|
slot->fatal = true;
|
||||||
|
slot->error = io_convert_errno(errno);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mem_scratch_end(scratch);
|
mem_scratch_end(scratch);
|
||||||
|
|
||||||
if(fd >= 0)
|
|
||||||
{
|
|
||||||
slot->fd = fd;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
slot->fd = -1;
|
|
||||||
slot->fatal = true;
|
|
||||||
slot->error = io_convert_errno(errno);
|
|
||||||
}
|
|
||||||
cmp.error = slot->error;
|
cmp.error = slot->error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return(cmp);
|
return(cmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
io_cmp io_close(file_slot* slot, io_req* req)
|
io_cmp io_close(file_slot* slot, io_req* req, file_table* table)
|
||||||
{
|
{
|
||||||
io_cmp cmp = {0};
|
io_cmp cmp = {0};
|
||||||
if(slot->fd >= 0)
|
if(slot->fd >= 0)
|
||||||
{
|
{
|
||||||
close(slot->fd);
|
close(slot->fd);
|
||||||
}
|
}
|
||||||
file_slot_recycle(&__globalFileTable, slot);
|
file_slot_recycle(table, slot);
|
||||||
return(cmp);
|
return(cmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,11 +580,11 @@ io_cmp io_get_error(file_slot* slot, io_req* req)
|
||||||
return(cmp);
|
return(cmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
io_cmp io_wait_single_req(io_req* req)
|
io_cmp io_wait_single_req_with_table(io_req* req, file_table* table)
|
||||||
{
|
{
|
||||||
io_cmp cmp = {0};
|
io_cmp cmp = {0};
|
||||||
|
|
||||||
file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle);
|
file_slot* slot = file_slot_from_handle(table, req->handle);
|
||||||
if(!slot)
|
if(!slot)
|
||||||
{
|
{
|
||||||
if(req->op != IO_OP_OPEN_AT)
|
if(req->op != IO_OP_OPEN_AT)
|
||||||
|
@ -442,7 +592,9 @@ io_cmp io_wait_single_req(io_req* req)
|
||||||
cmp.error = IO_ERR_HANDLE;
|
cmp.error = IO_ERR_HANDLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(slot->fatal && req->op != IO_OP_CLOSE)
|
else if( slot->fatal
|
||||||
|
&& req->op != IO_OP_CLOSE
|
||||||
|
&& req->op != IO_OP_ERROR)
|
||||||
{
|
{
|
||||||
cmp.error = IO_ERR_PREV;
|
cmp.error = IO_ERR_PREV;
|
||||||
}
|
}
|
||||||
|
@ -452,7 +604,7 @@ io_cmp io_wait_single_req(io_req* req)
|
||||||
switch(req->op)
|
switch(req->op)
|
||||||
{
|
{
|
||||||
case IO_OP_OPEN_AT:
|
case IO_OP_OPEN_AT:
|
||||||
cmp = io_open_at(slot, req);
|
cmp = io_open_at(slot, req, table);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IO_OP_FSTAT:
|
case IO_OP_FSTAT:
|
||||||
|
@ -460,7 +612,7 @@ io_cmp io_wait_single_req(io_req* req)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IO_OP_CLOSE:
|
case IO_OP_CLOSE:
|
||||||
cmp = io_close(slot, req);
|
cmp = io_close(slot, req, table);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IO_OP_READ:
|
case IO_OP_READ:
|
||||||
|
|
|
@ -9,77 +9,9 @@
|
||||||
#include<errno.h>
|
#include<errno.h>
|
||||||
#include<limits.h>
|
#include<limits.h>
|
||||||
|
|
||||||
|
#include"platform_io_internal.c"
|
||||||
#include"platform_io_common.c"
|
#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 io_convert_win32_error(int winError)
|
||||||
{
|
{
|
||||||
io_error error = 0;
|
io_error error = 0;
|
||||||
|
@ -136,11 +68,11 @@ io_error io_convert_win32_error(int winError)
|
||||||
return(error);
|
return(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
io_cmp io_open_at(file_slot* atSlot, io_req* req)
|
io_cmp io_open_at(file_slot* atSlot, io_req* req, file_table* table)
|
||||||
{
|
{
|
||||||
io_cmp cmp = {0};
|
io_cmp cmp = {0};
|
||||||
|
|
||||||
file_slot* slot = file_slot_alloc(&__globalFileTable);
|
file_slot* slot = file_slot_alloc(table);
|
||||||
if(!slot)
|
if(!slot)
|
||||||
{
|
{
|
||||||
cmp.error = IO_ERR_MAX_FILES;
|
cmp.error = IO_ERR_MAX_FILES;
|
||||||
|
@ -148,7 +80,7 @@ io_cmp io_open_at(file_slot* atSlot, io_req* req)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cmp.handle = file_handle_from_slot(&__globalFileTable, slot);
|
cmp.handle = file_handle_from_slot(table, slot);
|
||||||
|
|
||||||
//NOTE: open
|
//NOTE: open
|
||||||
mem_arena_scope scratch = mem_scratch_begin();
|
mem_arena_scope scratch = mem_scratch_begin();
|
||||||
|
@ -232,14 +164,14 @@ io_cmp io_open_at(file_slot* atSlot, io_req* req)
|
||||||
return(cmp);
|
return(cmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
io_cmp io_close(file_slot* slot, io_req* req)
|
io_cmp io_close(file_slot* slot, io_req* req, file_table* table)
|
||||||
{
|
{
|
||||||
io_cmp cmp = {0};
|
io_cmp cmp = {0};
|
||||||
if(slot->h)
|
if(slot->h)
|
||||||
{
|
{
|
||||||
CloseHandle(slot->h);
|
CloseHandle(slot->h);
|
||||||
}
|
}
|
||||||
file_slot_recycle(&__globalFileTable, slot);
|
file_slot_recycle(table, slot);
|
||||||
return(cmp);
|
return(cmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,11 +333,11 @@ io_cmp io_get_error(file_slot* slot, io_req* req)
|
||||||
return(cmp);
|
return(cmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
io_cmp io_wait_single_req(io_req* req)
|
io_cmp io_wait_single_req_with_table(io_req* req, file_table* table)
|
||||||
{
|
{
|
||||||
io_cmp cmp = {0};
|
io_cmp cmp = {0};
|
||||||
|
|
||||||
file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle);
|
file_slot* slot = file_slot_from_handle(table, req->handle);
|
||||||
if(!slot)
|
if(!slot)
|
||||||
{
|
{
|
||||||
if(req->op != IO_OP_OPEN_AT)
|
if(req->op != IO_OP_OPEN_AT)
|
||||||
|
@ -423,7 +355,7 @@ io_cmp io_wait_single_req(io_req* req)
|
||||||
switch(req->op)
|
switch(req->op)
|
||||||
{
|
{
|
||||||
case IO_OP_OPEN_AT:
|
case IO_OP_OPEN_AT:
|
||||||
cmp = io_open_at(slot, req);
|
cmp = io_open_at(slot, req, table);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IO_OP_FSTAT:
|
case IO_OP_FSTAT:
|
||||||
|
@ -431,7 +363,7 @@ io_cmp io_wait_single_req(io_req* req)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IO_OP_CLOSE:
|
case IO_OP_CLOSE:
|
||||||
cmp = io_close(slot, req);
|
cmp = io_close(slot, req, table);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IO_OP_READ:
|
case IO_OP_READ:
|
||||||
|
|
Loading…
Reference in New Issue