[io] Store last error in file handle, lockdown on fatal error
This commit is contained in:
parent
25f66b3954
commit
e82225116b
|
@ -69,12 +69,22 @@ typedef struct io_req
|
||||||
typedef i32 io_error;
|
typedef i32 io_error;
|
||||||
enum {
|
enum {
|
||||||
IO_OK = 0,
|
IO_OK = 0,
|
||||||
IO_ERR_INVALID, // invalid argument or argument combination
|
IO_ERR_UNKNOWN,
|
||||||
IO_ERR_PERM, // access denied
|
IO_ERR_OP, // unsupported operation
|
||||||
IO_ERR_PATH, // path does not exist
|
|
||||||
IO_ERR_EXISTS, // file already exists
|
|
||||||
IO_ERR_HANDLE, // invalid handle
|
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
|
||||||
|
|
||||||
//...
|
//...
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
467
src/io_impl.c
467
src/io_impl.c
|
@ -21,6 +21,7 @@ typedef struct file_slot
|
||||||
u32 generation;
|
u32 generation;
|
||||||
int fd;
|
int fd;
|
||||||
io_error error;
|
io_error error;
|
||||||
|
bool fatal;
|
||||||
list_elt freeListElt;
|
list_elt freeListElt;
|
||||||
|
|
||||||
} file_slot;
|
} file_slot;
|
||||||
|
@ -87,70 +88,78 @@ io_cmp io_open(io_req* req)
|
||||||
{
|
{
|
||||||
io_cmp cmp = {0};
|
io_cmp cmp = {0};
|
||||||
|
|
||||||
int flags = 0;
|
file_slot* slot = file_slot_alloc(&__globalFileTable);
|
||||||
if(req->openFlags & IO_OPEN_READ)
|
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)
|
if(req->openFlags & IO_OPEN_TRUNCATE)
|
||||||
{
|
{
|
||||||
flags |= O_TRUNC;
|
flags |= O_TRUNC;
|
||||||
}
|
}
|
||||||
if(req->openFlags & IO_OPEN_APPEND)
|
if(req->openFlags & IO_OPEN_APPEND)
|
||||||
{
|
{
|
||||||
flags |= O_APPEND;
|
flags |= O_APPEND;
|
||||||
}
|
}
|
||||||
if(req->openFlags & IO_OPEN_CREATE)
|
if(req->openFlags & IO_OPEN_CREATE)
|
||||||
{
|
{
|
||||||
flags |= O_CREAT;
|
flags |= O_CREAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
mode_t mode = S_IRUSR
|
mode_t mode = S_IRUSR
|
||||||
| S_IWUSR
|
| S_IWUSR
|
||||||
| S_IRGRP
|
| S_IRGRP
|
||||||
| S_IWGRP
|
| S_IWGRP
|
||||||
| S_IROTH
|
| S_IROTH
|
||||||
| S_IWOTH;
|
| S_IWOTH;
|
||||||
|
|
||||||
//NOTE: build path
|
//NOTE: build path
|
||||||
////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
//TODO: canonicalize directory path & check that it's inside local app folder
|
//TODO: canonicalize directory path & check that it's inside local app folder
|
||||||
////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
mem_arena* scratch = mem_scratch();
|
mem_arena* scratch = mem_scratch();
|
||||||
mem_arena_marker mark = mem_arena_mark(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 execPath = mp_app_get_executable_path(scratch);
|
||||||
str8 execDir = mp_path_directory(execPath);
|
str8 execDir = mp_path_directory(execPath);
|
||||||
|
|
||||||
str8_list_push(scratch, &list, execDir);
|
str8_list_push(scratch, &list, execDir);
|
||||||
str8_list_push(scratch, &list, STR8("/../data/"));
|
str8_list_push(scratch, &list, STR8("/../data/"));
|
||||||
str8_list_push(scratch, &list, str8_from_buffer(req->size, req->buffer));
|
str8_list_push(scratch, &list, str8_from_buffer(req->size, req->buffer));
|
||||||
|
|
||||||
str8 absPath = str8_list_join(scratch, list);
|
str8 absPath = str8_list_join(scratch, list);
|
||||||
char* absCString = str8_to_cstring(scratch, absPath);
|
char* absCString = str8_to_cstring(scratch, absPath);
|
||||||
|
|
||||||
//NOTE: open
|
//NOTE: open
|
||||||
int fd = open(absCString, flags, mode);
|
int fd = open(absCString, flags, mode);
|
||||||
|
|
||||||
if(fd >= 0)
|
if(fd >= 0)
|
||||||
{
|
|
||||||
file_slot* slot = file_slot_alloc(&__globalFileTable);
|
|
||||||
if(slot)
|
|
||||||
{
|
{
|
||||||
slot->fd = fd;
|
slot->fd = fd;
|
||||||
file_handle handle = file_handle_from_slot(&__globalFileTable, slot);
|
file_handle handle = file_handle_from_slot(&__globalFileTable, slot);
|
||||||
|
@ -158,156 +167,243 @@ io_cmp io_open(io_req* req)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cmp.error = IO_ERR_MAX_FILES;
|
slot->fd = -1;
|
||||||
close(fd);
|
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
|
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)
|
switch(errno)
|
||||||
{
|
{
|
||||||
case EACCES:
|
case EINVAL:
|
||||||
cmp.error = IO_ERR_PERM;
|
slot->error = IO_ERR_ARG;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EOVERFLOW:
|
||||||
|
slot->error = IO_ERR_OVERFLOW;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
//TODO: convert open error codes to io_error
|
|
||||||
default:
|
default:
|
||||||
cmp.error = IO_ERR_INVALID;
|
slot->error = IO_ERR_UNKNOWN;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
cmp.error = slot->error;
|
||||||
}
|
}
|
||||||
|
|
||||||
mem_arena_clear_to(scratch, mark);
|
|
||||||
|
|
||||||
return(cmp);
|
return(cmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
io_cmp io_close(io_req* req)
|
io_cmp io_seek(file_slot* slot, io_req* req)
|
||||||
{
|
{
|
||||||
io_cmp cmp = {0};
|
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)
|
int whence;
|
||||||
{
|
switch(req->whence)
|
||||||
io_cmp cmp = {0};
|
|
||||||
file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle);
|
|
||||||
if(slot)
|
|
||||||
{
|
{
|
||||||
struct stat s;
|
case IO_SEEK_CURRENT:
|
||||||
fstat(slot->fd, &s);
|
whence = SEEK_CUR;
|
||||||
cmp.result = s.st_size;
|
break;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cmp.error = IO_ERR_HANDLE;
|
|
||||||
}
|
|
||||||
return(cmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
io_cmp io_pos(io_req* req)
|
case IO_SEEK_SET:
|
||||||
{
|
whence = SEEK_SET;
|
||||||
io_cmp cmp = {0};
|
break;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
io_cmp io_seek(io_req* req)
|
case IO_SEEK_END:
|
||||||
{
|
whence = SEEK_END;
|
||||||
io_cmp cmp = {0};
|
}
|
||||||
file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle);
|
cmp.result = lseek(slot->fd, req->size, whence);
|
||||||
if(slot)
|
|
||||||
|
if(cmp.result < 0)
|
||||||
{
|
{
|
||||||
int whence;
|
switch(errno)
|
||||||
switch(req->whence)
|
|
||||||
{
|
{
|
||||||
case IO_SEEK_CURRENT:
|
case EINVAL:
|
||||||
whence = SEEK_CUR;
|
slot->error = IO_ERR_ARG;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IO_SEEK_SET:
|
case EOVERFLOW:
|
||||||
whence = SEEK_SET;
|
slot->error = IO_ERR_OVERFLOW;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IO_SEEK_END:
|
default:
|
||||||
whence = SEEK_END;
|
slot->error = IO_ERR_UNKNOWN;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
cmp.result = lseek(slot->fd, req->size, whence);
|
cmp.error = slot->error;
|
||||||
//TODO: check for errors
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cmp.error = IO_ERR_HANDLE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return(cmp);
|
return(cmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
io_cmp io_read(io_req* req)
|
io_cmp io_read(file_slot* slot, io_req* req)
|
||||||
{
|
{
|
||||||
io_cmp cmp = {0};
|
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);
|
switch(errno)
|
||||||
//TODO: check for errors
|
{
|
||||||
}
|
case EAGAIN:
|
||||||
else
|
slot->error = IO_ERR_NOT_READY;
|
||||||
{
|
break;
|
||||||
cmp.error = IO_ERR_HANDLE;
|
|
||||||
|
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);
|
return(cmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
io_cmp io_write(io_req* req)
|
io_cmp io_write(file_slot* slot, io_req* req)
|
||||||
{
|
{
|
||||||
io_cmp cmp = {0};
|
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);
|
switch(errno)
|
||||||
//TODO: check for errors
|
{
|
||||||
}
|
case EDQUOT:
|
||||||
else
|
case ENOSPC:
|
||||||
{
|
slot->error = IO_ERR_SPACE;
|
||||||
cmp.error = IO_ERR_HANDLE;
|
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);
|
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};
|
io_cmp cmp = {0};
|
||||||
file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle);
|
cmp.result = slot->error;
|
||||||
if(slot)
|
|
||||||
{
|
|
||||||
//TODO get error from slot
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cmp.error = IO_ERR_HANDLE;
|
|
||||||
}
|
|
||||||
return(cmp);
|
return(cmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
io_cmp io_wait_single_req(io_req* req)
|
io_cmp io_wait_single_req(io_req* req)
|
||||||
{
|
{
|
||||||
mem_arena* scratch = mem_scratch();
|
mem_arena* scratch = mem_scratch();
|
||||||
|
@ -332,50 +428,67 @@ io_cmp io_wait_single_req(io_req* req)
|
||||||
|
|
||||||
if(bufferIndex + req->size > memSize)
|
if(bufferIndex + req->size > memSize)
|
||||||
{
|
{
|
||||||
cmp.error = IO_ERR_INVALID;
|
cmp.error = IO_ERR_ARG;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//TODO: avoid modifying req.
|
//TODO: avoid modifying req.
|
||||||
req->buffer = memory + bufferIndex;
|
req->buffer = memory + bufferIndex;
|
||||||
|
|
||||||
switch(req->op)
|
file_slot* slot = 0;
|
||||||
|
if(req->op != IO_OP_OPEN)
|
||||||
{
|
{
|
||||||
case IO_OP_OPEN:
|
slot = file_slot_from_handle(&__globalFileTable, req->handle);
|
||||||
cmp = io_open(req);
|
if(!slot)
|
||||||
break;
|
{
|
||||||
|
cmp.error = IO_ERR_HANDLE;
|
||||||
|
}
|
||||||
|
else if(slot->fatal && req->op != IO_OP_CLOSE)
|
||||||
|
{
|
||||||
|
cmp.error = IO_ERR_PREV;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case IO_OP_CLOSE:
|
if(cmp.error == IO_OK)
|
||||||
cmp = io_close(req);
|
{
|
||||||
break;
|
switch(req->op)
|
||||||
|
{
|
||||||
|
case IO_OP_OPEN:
|
||||||
|
cmp = io_open(req);
|
||||||
|
break;
|
||||||
|
|
||||||
case IO_OP_SIZE:
|
case IO_OP_CLOSE:
|
||||||
cmp = io_size(req);
|
cmp = io_close(slot, req);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IO_OP_READ:
|
case IO_OP_SIZE:
|
||||||
cmp = io_read(req);
|
cmp = io_size(slot, req);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IO_OP_WRITE:
|
case IO_OP_READ:
|
||||||
cmp = io_write(req);
|
cmp = io_read(slot, req);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IO_OP_POS:
|
case IO_OP_WRITE:
|
||||||
cmp = io_pos(req);
|
cmp = io_write(slot, req);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IO_OP_SEEK:
|
case IO_OP_POS:
|
||||||
cmp = io_seek(req);
|
cmp = io_pos(slot, req);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IO_OP_ERROR:
|
case IO_OP_SEEK:
|
||||||
cmp = io_get_error(req);
|
cmp = io_seek(slot, req);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case IO_OP_ERROR:
|
||||||
cmp.error = IO_ERR_INVALID;
|
cmp = io_get_error(slot, req);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
cmp.error = IO_ERR_OP;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue