diff --git a/src/io_common.h b/src/io_common.h index a5fbac4..3187b1c 100644 --- a/src/io_common.h +++ b/src/io_common.h @@ -69,12 +69,22 @@ typedef struct io_req typedef i32 io_error; enum { IO_OK = 0, - IO_ERR_INVALID, // invalid argument or argument combination - IO_ERR_PERM, // access denied - IO_ERR_PATH, // path does not exist - IO_ERR_EXISTS, // file already exists + IO_ERR_UNKNOWN, + IO_ERR_OP, // unsupported operation IO_ERR_HANDLE, // invalid handle - IO_ERR_MAX_FILES, + IO_ERR_PREV, // previously had a fatal error (last error stored on handle) + IO_ERR_ARG, // invalid argument or argument combination + IO_ERR_PERM, // access denied + IO_ERR_SPACE, // no space left + IO_ERR_NO_FILE, // file does not exist + IO_ERR_EXISTS, // file already exists + IO_ERR_MAX_FILES, // max open files reached + IO_ERR_PATH_LENGTH, // path too long + IO_ERR_FILE_SIZE, // file too big + IO_ERR_OVERFLOW, // offset too big + IO_ERR_NOT_READY, // no data ready to be read/written + IO_ERR_MEM, // failed to allocate memory + //... }; diff --git a/src/io_impl.c b/src/io_impl.c index 695d359..302fcfb 100644 --- a/src/io_impl.c +++ b/src/io_impl.c @@ -21,6 +21,7 @@ typedef struct file_slot u32 generation; int fd; io_error error; + bool fatal; list_elt freeListElt; } file_slot; @@ -87,70 +88,78 @@ io_cmp io_open(io_req* req) { io_cmp cmp = {0}; - int flags = 0; - if(req->openFlags & IO_OPEN_READ) + file_slot* slot = file_slot_alloc(&__globalFileTable); + if(!slot) { - if(req->openFlags & IO_OPEN_WRITE) + cmp.error = IO_ERR_MAX_FILES; + cmp.result = 0; + } + else + { + file_handle handle = file_handle_from_slot(&__globalFileTable, slot); + cmp.result = handle.h; + + int flags = 0; + if(req->openFlags & IO_OPEN_READ) { - flags = O_RDWR; + if(req->openFlags & IO_OPEN_WRITE) + { + flags = O_RDWR; + } + else + { + flags = O_RDONLY; + } } - else + else if(req->openFlags & IO_OPEN_WRITE) { - flags = O_RDONLY; + flags = O_WRONLY; } - } - else if(req->openFlags & IO_OPEN_WRITE) - { - flags = O_WRONLY; - } - if(req->openFlags & IO_OPEN_TRUNCATE) - { - flags |= O_TRUNC; - } - if(req->openFlags & IO_OPEN_APPEND) - { - flags |= O_APPEND; - } - if(req->openFlags & IO_OPEN_CREATE) - { - flags |= O_CREAT; - } + if(req->openFlags & IO_OPEN_TRUNCATE) + { + flags |= O_TRUNC; + } + if(req->openFlags & IO_OPEN_APPEND) + { + flags |= O_APPEND; + } + if(req->openFlags & IO_OPEN_CREATE) + { + flags |= O_CREAT; + } - mode_t mode = S_IRUSR - | S_IWUSR - | S_IRGRP - | S_IWGRP - | S_IROTH - | S_IWOTH; + mode_t mode = S_IRUSR + | S_IWUSR + | S_IRGRP + | S_IWGRP + | S_IROTH + | S_IWOTH; - //NOTE: build path - //////////////////////////////////////////////////////////////////////////////////// - //TODO: canonicalize directory path & check that it's inside local app folder - //////////////////////////////////////////////////////////////////////////////////// + //NOTE: build path + //////////////////////////////////////////////////////////////////////////////////// + //TODO: canonicalize directory path & check that it's inside local app folder + //////////////////////////////////////////////////////////////////////////////////// - mem_arena* scratch = mem_scratch(); - mem_arena_marker mark = mem_arena_mark(scratch); + mem_arena* scratch = mem_scratch(); + mem_arena_marker mark = mem_arena_mark(scratch); - str8_list list = {0}; + str8_list list = {0}; - str8 execPath = mp_app_get_executable_path(scratch); - str8 execDir = mp_path_directory(execPath); + str8 execPath = mp_app_get_executable_path(scratch); + str8 execDir = mp_path_directory(execPath); - str8_list_push(scratch, &list, execDir); - str8_list_push(scratch, &list, STR8("/../data/")); - str8_list_push(scratch, &list, str8_from_buffer(req->size, req->buffer)); + str8_list_push(scratch, &list, execDir); + str8_list_push(scratch, &list, STR8("/../data/")); + str8_list_push(scratch, &list, str8_from_buffer(req->size, req->buffer)); - str8 absPath = str8_list_join(scratch, list); - char* absCString = str8_to_cstring(scratch, absPath); + str8 absPath = str8_list_join(scratch, list); + char* absCString = str8_to_cstring(scratch, absPath); - //NOTE: open - int fd = open(absCString, flags, mode); + //NOTE: open + int fd = open(absCString, flags, mode); - if(fd >= 0) - { - file_slot* slot = file_slot_alloc(&__globalFileTable); - if(slot) + if(fd >= 0) { slot->fd = fd; file_handle handle = file_handle_from_slot(&__globalFileTable, slot); @@ -158,156 +167,243 @@ io_cmp io_open(io_req* req) } else { - cmp.error = IO_ERR_MAX_FILES; - close(fd); + slot->fd = -1; + slot->fatal = true; + + switch(errno) + { + case EACCES: + case EROFS: + slot->error = IO_ERR_PERM; + break; + + case EDQUOT: + case ENOSPC: + slot->error = IO_ERR_SPACE; + break; + + case EEXIST: + slot->error = IO_ERR_EXISTS; + break; + + case EFAULT: + case EINVAL: + case EISDIR: + slot->error = IO_ERR_ARG; + break; + + case EMFILE: + case ENFILE: + slot->error = IO_ERR_MAX_FILES; + break; + + case ENAMETOOLONG: + slot->error = IO_ERR_PATH_LENGTH; + break; + + case ENOENT: + case ENOTDIR: + slot->error = IO_ERR_NO_FILE; + break; + + case EOVERFLOW: + slot->error = IO_ERR_FILE_SIZE; + break; + + default: + slot->error = IO_ERR_UNKNOWN; + break; + } + cmp.error = slot->error; } + + mem_arena_clear_to(scratch, mark); + } + + return(cmp); +} + +io_cmp io_close(file_slot* slot, io_req* req) +{ + io_cmp cmp = {0}; + if(slot->fd >= 0) + { + close(slot->fd); + } + file_slot_recycle(&__globalFileTable, slot); + return(cmp); +} + +io_cmp io_size(file_slot* slot, io_req* req) +{ + io_cmp cmp = {0}; + + struct stat s; + if(fstat(slot->fd, &s)) + { + slot->error = IO_ERR_UNKNOWN; + cmp.error = slot->error; } else + { + cmp.result = s.st_size; + } + + return(cmp); +} + +io_cmp io_pos(file_slot* slot, io_req* req) +{ + io_cmp cmp = {0}; + cmp.result = lseek(slot->fd, 0, SEEK_CUR); + if(cmp.result < 0) { switch(errno) { - case EACCES: - cmp.error = IO_ERR_PERM; + case EINVAL: + slot->error = IO_ERR_ARG; + break; + + case EOVERFLOW: + slot->error = IO_ERR_OVERFLOW; break; - //TODO: convert open error codes to io_error default: - cmp.error = IO_ERR_INVALID; + slot->error = IO_ERR_UNKNOWN; + break; } + cmp.error = slot->error; } - mem_arena_clear_to(scratch, mark); - return(cmp); } -io_cmp io_close(io_req* req) +io_cmp io_seek(file_slot* slot, io_req* req) { io_cmp cmp = {0}; - file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle); - if(slot) - { - close(slot->fd); - file_slot_recycle(&__globalFileTable, slot); - } - else - { - cmp.error = IO_ERR_HANDLE; - } - return(cmp); -} -io_cmp io_size(io_req* req) -{ - io_cmp cmp = {0}; - file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle); - if(slot) + int whence; + switch(req->whence) { - struct stat s; - fstat(slot->fd, &s); - cmp.result = s.st_size; - } - else - { - cmp.error = IO_ERR_HANDLE; - } - return(cmp); -} + case IO_SEEK_CURRENT: + whence = SEEK_CUR; + break; -io_cmp io_pos(io_req* req) -{ - io_cmp cmp = {0}; - file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle); - if(slot) - { - cmp.result = lseek(slot->fd, 0, SEEK_CUR); - //TODO: check for errors - } - else - { - cmp.error = IO_ERR_HANDLE; - } - return(cmp); -} + case IO_SEEK_SET: + whence = SEEK_SET; + break; -io_cmp io_seek(io_req* req) -{ - io_cmp cmp = {0}; - file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle); - if(slot) + case IO_SEEK_END: + whence = SEEK_END; + } + cmp.result = lseek(slot->fd, req->size, whence); + + if(cmp.result < 0) { - int whence; - switch(req->whence) + switch(errno) { - case IO_SEEK_CURRENT: - whence = SEEK_CUR; + case EINVAL: + slot->error = IO_ERR_ARG; break; - case IO_SEEK_SET: - whence = SEEK_SET; + case EOVERFLOW: + slot->error = IO_ERR_OVERFLOW; break; - case IO_SEEK_END: - whence = SEEK_END; + default: + slot->error = IO_ERR_UNKNOWN; + break; } - cmp.result = lseek(slot->fd, req->size, whence); - //TODO: check for errors - } - else - { - cmp.error = IO_ERR_HANDLE; + cmp.error = slot->error; } + return(cmp); } -io_cmp io_read(io_req* req) +io_cmp io_read(file_slot* slot, io_req* req) { io_cmp cmp = {0}; - file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle); - if(slot) + + cmp.result = read(slot->fd, req->buffer, req->size); + + if(cmp.result < 0) { - cmp.result = read(slot->fd, req->buffer, req->size); - //TODO: check for errors - } - else - { - cmp.error = IO_ERR_HANDLE; + switch(errno) + { + case EAGAIN: + slot->error = IO_ERR_NOT_READY; + break; + + case EFAULT: + case EINVAL: + slot->error = IO_ERR_ARG; + break; + + case ENOBUFS: + case ENOMEM: + slot->error = IO_ERR_MEM; + break; + + default: + slot->error = IO_ERR_UNKNOWN; + break; + } + cmp.error = slot->error; } + return(cmp); } -io_cmp io_write(io_req* req) +io_cmp io_write(file_slot* slot, io_req* req) { io_cmp cmp = {0}; - file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle); - if(slot) + + cmp.result = write(slot->fd, req->buffer, req->size); + + if(cmp.result < 0) { - cmp.result = write(slot->fd, req->buffer, req->size); - //TODO: check for errors - } - else - { - cmp.error = IO_ERR_HANDLE; + switch(errno) + { + case EDQUOT: + case ENOSPC: + slot->error = IO_ERR_SPACE; + break; + + case EFAULT: + case EINVAL: + slot->error = IO_ERR_ARG; + break; + + case EAGAIN: + slot->error = IO_ERR_NOT_READY; + break; + + case EFBIG: + slot->error = IO_ERR_FILE_SIZE; + break; + + case ENOBUFS: + case ENOMEM: + slot->error = IO_ERR_MEM; + break; + + default: + slot->error = IO_ERR_UNKNOWN; + break; + } + cmp.error = slot->error; } + return(cmp); } -io_cmp io_get_error(io_req* req) +io_cmp io_get_error(file_slot* slot, io_req* req) { io_cmp cmp = {0}; - file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle); - if(slot) - { - //TODO get error from slot - } - else - { - cmp.error = IO_ERR_HANDLE; - } + cmp.result = slot->error; return(cmp); } - io_cmp io_wait_single_req(io_req* req) { mem_arena* scratch = mem_scratch(); @@ -332,50 +428,67 @@ io_cmp io_wait_single_req(io_req* req) if(bufferIndex + req->size > memSize) { - cmp.error = IO_ERR_INVALID; + cmp.error = IO_ERR_ARG; } else { //TODO: avoid modifying req. req->buffer = memory + bufferIndex; - switch(req->op) + file_slot* slot = 0; + if(req->op != IO_OP_OPEN) { - case IO_OP_OPEN: - cmp = io_open(req); - break; + slot = file_slot_from_handle(&__globalFileTable, req->handle); + if(!slot) + { + cmp.error = IO_ERR_HANDLE; + } + else if(slot->fatal && req->op != IO_OP_CLOSE) + { + cmp.error = IO_ERR_PREV; + } + } - case IO_OP_CLOSE: - cmp = io_close(req); - break; + if(cmp.error == IO_OK) + { + switch(req->op) + { + case IO_OP_OPEN: + cmp = io_open(req); + break; - case IO_OP_SIZE: - cmp = io_size(req); - break; + case IO_OP_CLOSE: + cmp = io_close(slot, req); + break; - case IO_OP_READ: - cmp = io_read(req); - break; + case IO_OP_SIZE: + cmp = io_size(slot, req); + break; - case IO_OP_WRITE: - cmp = io_write(req); - break; + case IO_OP_READ: + cmp = io_read(slot, req); + break; - case IO_OP_POS: - cmp = io_pos(req); - break; + case IO_OP_WRITE: + cmp = io_write(slot, req); + break; - case IO_OP_SEEK: - cmp = io_seek(req); - break; + case IO_OP_POS: + cmp = io_pos(slot, req); + break; - case IO_OP_ERROR: - cmp = io_get_error(req); - break; + case IO_OP_SEEK: + cmp = io_seek(slot, req); + break; - default: - cmp.error = IO_ERR_INVALID; - break; + case IO_OP_ERROR: + cmp = io_get_error(slot, req); + break; + + default: + cmp.error = IO_ERR_OP; + break; + } } }