[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;
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
	//...
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										275
									
								
								src/io_impl.c
								
								
								
								
							
							
						
						
									
										275
									
								
								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,6 +88,17 @@ io_cmp io_open(io_req* req)
 | 
			
		|||
{
 | 
			
		||||
	io_cmp cmp = {0};
 | 
			
		||||
 | 
			
		||||
	file_slot* slot = file_slot_alloc(&__globalFileTable);
 | 
			
		||||
	if(!slot)
 | 
			
		||||
	{
 | 
			
		||||
		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)
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -148,9 +160,6 @@ io_cmp io_open(io_req* req)
 | 
			
		|||
		int fd = open(absCString, flags, mode);
 | 
			
		||||
 | 
			
		||||
		if(fd >= 0)
 | 
			
		||||
	{
 | 
			
		||||
		file_slot* slot = file_slot_alloc(&__globalFileTable);
 | 
			
		||||
		if(slot)
 | 
			
		||||
		{
 | 
			
		||||
			slot->fd = fd;
 | 
			
		||||
			file_handle handle = file_handle_from_slot(&__globalFileTable, slot);
 | 
			
		||||
| 
						 | 
				
			
			@ -158,84 +167,121 @@ io_cmp io_open(io_req* req)
 | 
			
		|||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			cmp.error = IO_ERR_MAX_FILES;
 | 
			
		||||
			close(fd);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
			slot->fd = -1;
 | 
			
		||||
			slot->fatal = true;
 | 
			
		||||
 | 
			
		||||
			switch(errno)
 | 
			
		||||
			{
 | 
			
		||||
				case EACCES:
 | 
			
		||||
				cmp.error = IO_ERR_PERM;
 | 
			
		||||
				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;
 | 
			
		||||
 | 
			
		||||
			//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_close(file_slot* slot, io_req* req)
 | 
			
		||||
{
 | 
			
		||||
	io_cmp cmp = {0};
 | 
			
		||||
	file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle);
 | 
			
		||||
	if(slot)
 | 
			
		||||
	if(slot->fd >= 0)
 | 
			
		||||
	{
 | 
			
		||||
		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 io_size(file_slot* slot, io_req* req)
 | 
			
		||||
{
 | 
			
		||||
	io_cmp cmp = {0};
 | 
			
		||||
	file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle);
 | 
			
		||||
	if(slot)
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
	struct stat s;
 | 
			
		||||
		fstat(slot->fd, &s);
 | 
			
		||||
	if(fstat(slot->fd, &s))
 | 
			
		||||
	{
 | 
			
		||||
		slot->error = IO_ERR_UNKNOWN;
 | 
			
		||||
		cmp.error = slot->error;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		cmp.result = s.st_size;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		cmp.error = IO_ERR_HANDLE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return(cmp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
io_cmp io_pos(io_req* req)
 | 
			
		||||
io_cmp io_pos(file_slot* slot, 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
 | 
			
		||||
	if(cmp.result < 0)
 | 
			
		||||
	{
 | 
			
		||||
		cmp.error = IO_ERR_HANDLE;
 | 
			
		||||
		switch(errno)
 | 
			
		||||
		{
 | 
			
		||||
			case EINVAL:
 | 
			
		||||
				slot->error = IO_ERR_ARG;
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			case EOVERFLOW:
 | 
			
		||||
				slot->error = IO_ERR_OVERFLOW;
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				slot->error = IO_ERR_UNKNOWN;
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
		cmp.error = slot->error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return(cmp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
io_cmp io_seek(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)
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
	int whence;
 | 
			
		||||
	switch(req->whence)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -251,63 +297,113 @@ io_cmp io_seek(io_req* req)
 | 
			
		|||
			whence = SEEK_END;
 | 
			
		||||
	}
 | 
			
		||||
	cmp.result = lseek(slot->fd, req->size, whence);
 | 
			
		||||
		//TODO: check for errors
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
 | 
			
		||||
	if(cmp.result < 0)
 | 
			
		||||
	{
 | 
			
		||||
		cmp.error = IO_ERR_HANDLE;
 | 
			
		||||
		switch(errno)
 | 
			
		||||
		{
 | 
			
		||||
			case EINVAL:
 | 
			
		||||
				slot->error = IO_ERR_ARG;
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			case EOVERFLOW:
 | 
			
		||||
				slot->error = IO_ERR_OVERFLOW;
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				slot->error = IO_ERR_UNKNOWN;
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
		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);
 | 
			
		||||
		//TODO: check for errors
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
 | 
			
		||||
	if(cmp.result < 0)
 | 
			
		||||
	{
 | 
			
		||||
		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);
 | 
			
		||||
		//TODO: check for errors
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
 | 
			
		||||
	if(cmp.result < 0)
 | 
			
		||||
	{
 | 
			
		||||
		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,13 +428,29 @@ 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;
 | 
			
		||||
 | 
			
		||||
		file_slot* slot = 0;
 | 
			
		||||
		if(req->op != IO_OP_OPEN)
 | 
			
		||||
		{
 | 
			
		||||
			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;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(cmp.error == IO_OK)
 | 
			
		||||
		{
 | 
			
		||||
			switch(req->op)
 | 
			
		||||
			{
 | 
			
		||||
				case IO_OP_OPEN:
 | 
			
		||||
| 
						 | 
				
			
			@ -346,38 +458,39 @@ io_cmp io_wait_single_req(io_req* req)
 | 
			
		|||
					break;
 | 
			
		||||
 | 
			
		||||
				case IO_OP_CLOSE:
 | 
			
		||||
				cmp = io_close(req);
 | 
			
		||||
					cmp = io_close(slot, req);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case IO_OP_SIZE:
 | 
			
		||||
				cmp = io_size(req);
 | 
			
		||||
					cmp = io_size(slot, req);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case IO_OP_READ:
 | 
			
		||||
				cmp = io_read(req);
 | 
			
		||||
					cmp = io_read(slot, req);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case IO_OP_WRITE:
 | 
			
		||||
				cmp = io_write(req);
 | 
			
		||||
					cmp = io_write(slot, req);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case IO_OP_POS:
 | 
			
		||||
				cmp = io_pos(req);
 | 
			
		||||
					cmp = io_pos(slot, req);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case IO_OP_SEEK:
 | 
			
		||||
				cmp = io_seek(req);
 | 
			
		||||
					cmp = io_seek(slot, req);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				case IO_OP_ERROR:
 | 
			
		||||
				cmp = io_get_error(req);
 | 
			
		||||
					cmp = io_get_error(slot, req);
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				default:
 | 
			
		||||
				cmp.error = IO_ERR_INVALID;
 | 
			
		||||
					cmp.error = IO_ERR_OP;
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return(cmp);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue