diff --git a/src/platform/platform_io_common.c b/src/platform/platform_io_common.c index 1fd314d..303a9e8 100644 --- a/src/platform/platform_io_common.c +++ b/src/platform/platform_io_common.c @@ -7,7 +7,6 @@ *****************************************************************/ #include"platform_io.h" - //------------------------------------------------------------------------------ // File stream read/write API //------------------------------------------------------------------------------ diff --git a/src/platform/platform_io_internal.c b/src/platform/platform_io_internal.c new file mode 100644 index 0000000..8be45e9 --- /dev/null +++ b/src/platform/platform_io_internal.c @@ -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)); +} diff --git a/src/platform/platform_io_internal.h b/src/platform/platform_io_internal.h new file mode 100644 index 0000000..69181de --- /dev/null +++ b/src/platform/platform_io_internal.h @@ -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 + #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_ diff --git a/src/platform/posix_io.c b/src/platform/posix_io.c index 12cff2d..a11d62b 100644 --- a/src/platform/posix_io.c +++ b/src/platform/posix_io.c @@ -13,75 +13,7 @@ #include #include"platform_io_common.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); -} +#include"platform_io_internal.c" io_error io_convert_errno(int e) { @@ -219,11 +151,203 @@ int io_convert_open_flags(file_open_flags flags) 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}; - file_slot* slot = file_slot_alloc(&__globalFileTable); + file_slot* slot = file_slot_alloc(table); if(!slot) { cmp.error = IO_ERR_MAX_FILES; @@ -231,7 +355,7 @@ io_cmp io_open_at(file_slot* atSlot, io_req* req) } 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); @@ -247,46 +371,72 @@ io_cmp io_open_at(file_slot* atSlot, io_req* req) mem_arena_scope scratch = mem_scratch_begin(); 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 // subtree rooted at atSlot->fd - int fd = -1; + slot->fd = -1; 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 { - 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); - - if(fd >= 0) - { - slot->fd = fd; - } - else - { - slot->fd = -1; - slot->fatal = true; - slot->error = io_convert_errno(errno); - } cmp.error = slot->error; } 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}; if(slot->fd >= 0) { close(slot->fd); } - file_slot_recycle(&__globalFileTable, slot); + file_slot_recycle(table, slot); return(cmp); } @@ -430,11 +580,11 @@ io_cmp io_get_error(file_slot* slot, io_req* req) 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}; - file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle); + file_slot* slot = file_slot_from_handle(table, req->handle); if(!slot) { 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; } } - 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; } @@ -452,7 +604,7 @@ io_cmp io_wait_single_req(io_req* req) switch(req->op) { case IO_OP_OPEN_AT: - cmp = io_open_at(slot, req); + cmp = io_open_at(slot, req, table); break; case IO_OP_FSTAT: @@ -460,7 +612,7 @@ io_cmp io_wait_single_req(io_req* req) break; case IO_OP_CLOSE: - cmp = io_close(slot, req); + cmp = io_close(slot, req, table); break; case IO_OP_READ: diff --git a/src/platform/win32_io.c b/src/platform/win32_io.c index c14cdd5..54263ad 100644 --- a/src/platform/win32_io.c +++ b/src/platform/win32_io.c @@ -9,77 +9,9 @@ #include #include +#include"platform_io_internal.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 error = 0; @@ -136,11 +68,11 @@ io_error io_convert_win32_error(int winError) 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}; - file_slot* slot = file_slot_alloc(&__globalFileTable); + file_slot* slot = file_slot_alloc(table); if(!slot) { cmp.error = IO_ERR_MAX_FILES; @@ -148,7 +80,7 @@ io_cmp io_open_at(file_slot* atSlot, io_req* req) } else { - cmp.handle = file_handle_from_slot(&__globalFileTable, slot); + cmp.handle = file_handle_from_slot(table, slot); //NOTE: open mem_arena_scope scratch = mem_scratch_begin(); @@ -232,14 +164,14 @@ io_cmp io_open_at(file_slot* atSlot, io_req* req) 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}; if(slot->h) { CloseHandle(slot->h); } - file_slot_recycle(&__globalFileTable, slot); + file_slot_recycle(table, slot); return(cmp); } @@ -401,11 +333,11 @@ io_cmp io_get_error(file_slot* slot, io_req* req) 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}; - file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle); + file_slot* slot = file_slot_from_handle(table, req->handle); if(!slot) { if(req->op != IO_OP_OPEN_AT) @@ -423,7 +355,7 @@ io_cmp io_wait_single_req(io_req* req) switch(req->op) { case IO_OP_OPEN_AT: - cmp = io_open_at(slot, req); + cmp = io_open_at(slot, req, table); break; case IO_OP_FSTAT: @@ -431,7 +363,7 @@ io_cmp io_wait_single_req(io_req* req) break; case IO_OP_CLOSE: - cmp = io_close(slot, req); + cmp = io_close(slot, req, table); break; case IO_OP_READ: