[wip, osx, io] reorganizing io_open_restrict, fix opening last element with the correct flags
This commit is contained in:
parent
c71da9e761
commit
d363c8b607
|
@ -12,6 +12,11 @@
|
|||
|
||||
#include"platform_path.c"
|
||||
|
||||
bool path_is_absolute(str8 path)
|
||||
{
|
||||
return(path.len && (path.ptr[0] == '/'));
|
||||
}
|
||||
|
||||
str8 path_executable(mem_arena* arena)
|
||||
{@autoreleasepool{
|
||||
str8 result = {};
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*****************************************************************/
|
||||
|
||||
#include"platform_io_internal.h"
|
||||
#include"platform_path.h"
|
||||
|
||||
file_table __globalFileTable = {0};
|
||||
|
||||
|
@ -63,3 +64,293 @@ io_cmp io_wait_single_req(io_req* req)
|
|||
{
|
||||
return(io_wait_single_req_with_table(req, &__globalFileTable));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// io common primitives
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
typedef struct io_open_restrict_context
|
||||
{
|
||||
io_error error;
|
||||
u64 rootUID;
|
||||
io_file_desc rootFd;
|
||||
io_file_desc fd;
|
||||
|
||||
} io_open_restrict_context;
|
||||
|
||||
io_error io_open_restrict_enter(io_open_restrict_context* context, str8 name, file_access_rights accessRights, file_open_flags openFlags)
|
||||
{
|
||||
io_file_desc nextFd = io_raw_open_at(context->fd, name, accessRights, openFlags);
|
||||
if(io_file_desc_is_nil(nextFd))
|
||||
{
|
||||
context->error = io_raw_last_error();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(context->fd != context->rootFd)
|
||||
{
|
||||
io_raw_close(context->fd);
|
||||
}
|
||||
context->fd = nextFd;
|
||||
}
|
||||
return(context->error);
|
||||
}
|
||||
|
||||
typedef struct io_open_restrict_result
|
||||
{
|
||||
io_error error;
|
||||
io_file_desc fd;
|
||||
} io_open_restrict_result;
|
||||
|
||||
|
||||
io_open_restrict_result io_open_restrict(io_file_desc dirFd, str8 path, file_access_rights accessRights, file_open_flags openFlags)
|
||||
{
|
||||
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);
|
||||
|
||||
io_open_restrict_context context = {
|
||||
.error = IO_OK,
|
||||
.rootFd = dirFd,
|
||||
.fd = dirFd,
|
||||
};
|
||||
|
||||
file_status status;
|
||||
context.error = io_raw_fstat(dirFd, &status);
|
||||
context.rootUID = status.uid;
|
||||
|
||||
for_list(&pathElements.list, elt, str8_elt, listElt)
|
||||
{
|
||||
str8 name = elt->string;
|
||||
|
||||
/*TODO:
|
||||
The last element should be treated a bit differently:
|
||||
|
||||
- if it doesn't exists, and FILE_OPEN_CREATE is set, we can create it
|
||||
- it must be opened with the correct flags
|
||||
*/
|
||||
|
||||
if(!str8_cmp(name, STR8(".")))
|
||||
{
|
||||
if(&elt->listElt == list_last(&pathElements.list))
|
||||
{
|
||||
//NOTE: if we're at the last element, re-enter current directory with correct flags
|
||||
io_open_restrict_enter(&context, name, accessRights, openFlags);
|
||||
}
|
||||
else
|
||||
{
|
||||
//NOTE: else we can just skip the element
|
||||
}
|
||||
}
|
||||
else if(!str8_cmp(name, STR8("..")))
|
||||
{
|
||||
//NOTE: check that we don't escape root dir
|
||||
file_status status;
|
||||
context.error = io_raw_fstat(context.fd, &status);
|
||||
if(context.error)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if(status.uid == context.rootUID)
|
||||
{
|
||||
context.error = IO_ERR_WALKOUT;
|
||||
break;
|
||||
}
|
||||
else if(&elt->listElt == list_last(&pathElements.list))
|
||||
{
|
||||
//NOTE: if we're at the last element, enter parent directory with correct flags
|
||||
io_open_restrict_enter(&context, name, accessRights, openFlags);
|
||||
}
|
||||
else
|
||||
{
|
||||
io_open_restrict_enter(&context, name, FILE_ACCESS_READ, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!io_raw_file_exists_at(context.fd, name, FILE_OPEN_SYMLINK))
|
||||
{
|
||||
//NOTE: if the file doesn't exists, but we're at the last element and FILE_OPEN_CREATE
|
||||
// is set, we create the file. Otherwise it is a IO_ERROR_NO_ENTRY error.
|
||||
if( (&elt->listElt == list_last(&pathElements.list))
|
||||
&&(openFlags & FILE_OPEN_CREATE))
|
||||
{
|
||||
io_open_restrict_enter(&context, name, accessRights, openFlags);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.error = IO_ERR_NO_ENTRY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//NOTE: if the file exists, we check the type of file
|
||||
file_status status = {0};
|
||||
context.error = io_raw_fstat_at(context.fd, name, FILE_OPEN_SYMLINK, &status);
|
||||
if(context.error)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if(status.type == MP_FILE_SYMLINK)
|
||||
{
|
||||
//TODO - do we need a FILE_OPEN_NO_FOLLOW that fails if last element is a symlink?
|
||||
// - do we need a FILE_OPEN_NO_SYMLINKS that fails if _any_ element is a symlink?
|
||||
|
||||
if( (&elt->listElt == list_last(&pathElements.list))
|
||||
&&(openFlags & FILE_OPEN_SYMLINK))
|
||||
{
|
||||
io_open_restrict_enter(&context, name, accessRights, openFlags);
|
||||
}
|
||||
else
|
||||
{
|
||||
io_raw_read_link_result link = io_raw_read_link_at(scratch.arena, context.fd, name);
|
||||
if(link.error)
|
||||
{
|
||||
context.error = link.error;
|
||||
break;
|
||||
}
|
||||
|
||||
if(path_is_absolute(link.target))
|
||||
{
|
||||
context.error = IO_ERR_WALKOUT;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(link.target.len == 0)
|
||||
{
|
||||
//NOTE: treat an empty target as a '.'
|
||||
link.target = STR8(".");
|
||||
}
|
||||
|
||||
str8_list linkElements = str8_split(scratch.arena, link.target, 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(status.type == MP_FILE_DIRECTORY)
|
||||
{
|
||||
//NOTE: descend in directory
|
||||
if(&elt->listElt == list_last(&pathElements.list))
|
||||
{
|
||||
io_open_restrict_enter(&context, name, accessRights, openFlags);
|
||||
}
|
||||
else
|
||||
{
|
||||
io_open_restrict_enter(&context, name, FILE_ACCESS_READ, 0);
|
||||
}
|
||||
}
|
||||
else if(status.type == MP_FILE_REGULAR)
|
||||
{
|
||||
//NOTE: check that we're at the end of path and open that file
|
||||
if(&elt->listElt != list_last(&pathElements.list))
|
||||
{
|
||||
context.error = IO_ERR_NOT_DIR;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
io_open_restrict_enter(&context, name, accessRights, openFlags);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.error = IO_ERR_NO_ENTRY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(context.error)
|
||||
{
|
||||
if(context.fd != context.rootFd)
|
||||
{
|
||||
io_raw_close(context.fd);
|
||||
context.fd = io_file_desc_nil();
|
||||
}
|
||||
}
|
||||
|
||||
io_open_restrict_result result = {
|
||||
.error = context.error,
|
||||
.fd = context.fd
|
||||
};
|
||||
|
||||
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(table);
|
||||
if(!slot)
|
||||
{
|
||||
cmp.error = IO_ERR_MAX_FILES;
|
||||
cmp.result = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
slot->fd = -1;
|
||||
|
||||
cmp.handle = file_handle_from_slot(table, slot);
|
||||
|
||||
slot->rights = req->open.rights;
|
||||
if(atSlot)
|
||||
{
|
||||
slot->rights &= atSlot->rights;
|
||||
}
|
||||
|
||||
if(slot->rights != req->open.rights)
|
||||
{
|
||||
slot->error = IO_ERR_PERM;
|
||||
slot->fatal = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
str8 path = str8_from_buffer(req->size, req->buffer);
|
||||
|
||||
io_file_desc dirFd = atSlot ? atSlot->fd : io_file_desc_nil();
|
||||
slot->fd = -1;
|
||||
|
||||
if(req->open.flags & FILE_OPEN_RESTRICT)
|
||||
{
|
||||
io_open_restrict_result res = io_open_restrict(dirFd, path, slot->rights, req->open.flags);
|
||||
if(res.error == IO_OK)
|
||||
{
|
||||
slot->fd = res.fd;
|
||||
}
|
||||
else
|
||||
{
|
||||
slot->fatal = true;
|
||||
slot->error = res.error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
slot->fd = io_raw_open_at(dirFd, path, slot->rights, req->open.flags);
|
||||
if(slot->fd < 0)
|
||||
{
|
||||
slot->fatal = true;
|
||||
slot->error = io_raw_last_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
cmp.error = slot->error;
|
||||
}
|
||||
return(cmp);
|
||||
}
|
||||
|
|
|
@ -12,11 +12,11 @@
|
|||
#include"platform.h"
|
||||
|
||||
#if PLATFORM_MACOS || PLATFORM_LINUX
|
||||
#define PLATFORM_IO_NATIVE_MEMBER int fd
|
||||
typedef int io_file_desc;
|
||||
#elif PLATFORM_WINDOWS
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include<windows.h>
|
||||
#define PLATFORM_IO_NATIVE_MEMBER HANDLE h
|
||||
typedef HANDLE io_file_desc;
|
||||
#endif
|
||||
|
||||
typedef struct file_slot
|
||||
|
@ -27,7 +27,7 @@ typedef struct file_slot
|
|||
list_elt freeListElt;
|
||||
|
||||
file_access_rights rights;
|
||||
PLATFORM_IO_NATIVE_MEMBER;
|
||||
io_file_desc fd;
|
||||
|
||||
} file_slot;
|
||||
|
||||
|
@ -50,4 +50,38 @@ 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);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// raw io primitives
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
io_file_desc io_file_desc_nil();
|
||||
bool io_file_desc_is_nil(io_file_desc fd);
|
||||
|
||||
/*WARN
|
||||
io_raw_xxx_at functions are similar to posix openat() regarding path resolution,
|
||||
but with some important differences:
|
||||
- If dirFd is a non-nil fd, path is considered relative to dirFd _even if it is an absolute path_
|
||||
- If dirFd is a nil fd, it is _ignored_ (i.e., path can be absolute, or relative to the current directory)
|
||||
|
||||
This means that:
|
||||
- dirFd behaves more like the _root_ of path, ie dirFd won't be ignore if we pass an absolute path,
|
||||
- we don't need a special handle value to use a path relative to the current working directory
|
||||
(we just pass a nil dirFd with a relative path)
|
||||
*/
|
||||
io_file_desc io_raw_open_at(io_file_desc dirFd, str8 path, file_access_rights accessRights, file_open_flags openFlags);
|
||||
void io_raw_close(io_file_desc fd);
|
||||
io_error io_raw_last_error();
|
||||
bool io_raw_file_exists_at(io_file_desc dirFd, str8 path, file_open_flags openFlags);
|
||||
io_error io_raw_fstat(io_file_desc fd, file_status* status);
|
||||
io_error io_raw_fstat_at(io_file_desc dirFd, str8 path, file_open_flags openFlags, file_status* status);
|
||||
|
||||
typedef struct io_raw_read_link_result
|
||||
{
|
||||
io_error error;
|
||||
str8 target;
|
||||
} io_raw_read_link_result;
|
||||
|
||||
io_raw_read_link_result io_raw_read_link_at(mem_arena* arena, io_file_desc dirFd, str8 path);
|
||||
|
||||
#endif //__PLATFORM_IO_INTERNAL_H_
|
||||
|
|
|
@ -17,6 +17,7 @@ MP_API str8_list path_split(mem_arena* arena, str8 path);
|
|||
MP_API str8 path_join(mem_arena* arena, str8_list elements);
|
||||
MP_API str8 path_append(mem_arena* arena, str8 parent, str8 relPath);
|
||||
|
||||
MP_API bool path_is_absolute(str8 path);
|
||||
MP_API str8 path_executable(mem_arena* arena);
|
||||
MP_API str8 path_canonical(mem_arena* arena, str8 path);
|
||||
|
||||
|
|
|
@ -15,10 +15,20 @@
|
|||
#include"platform_io_common.c"
|
||||
#include"platform_io_internal.c"
|
||||
|
||||
io_error io_convert_errno(int e)
|
||||
io_file_desc io_file_desc_nil()
|
||||
{
|
||||
io_error error;
|
||||
switch(e)
|
||||
return(-1);
|
||||
}
|
||||
|
||||
bool io_file_desc_is_nil(io_file_desc fd)
|
||||
{
|
||||
return(fd < 0);
|
||||
}
|
||||
|
||||
io_error io_raw_last_error()
|
||||
{
|
||||
io_error error = IO_OK;
|
||||
switch(errno)
|
||||
{
|
||||
case EPERM:
|
||||
case EACCES:
|
||||
|
@ -178,311 +188,50 @@ int io_update_dir_flags_at(int dirFd, char* path, int flags)
|
|||
return(flags);
|
||||
}
|
||||
|
||||
typedef struct io_open_restrict_result
|
||||
io_file_desc io_raw_open_at(io_file_desc dirFd, str8 path, file_access_rights accessRights, file_open_flags openFlags)
|
||||
{
|
||||
io_error error;
|
||||
int fd;
|
||||
int flags = io_convert_access_rights(accessRights);
|
||||
flags |= io_convert_open_flags(openFlags);
|
||||
|
||||
} 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};
|
||||
mode_t mode = S_IRUSR
|
||||
| S_IWUSR
|
||||
| S_IRGRP
|
||||
| S_IWGRP
|
||||
| S_IROTH
|
||||
| S_IWOTH;
|
||||
|
||||
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 = dup(dirFd);
|
||||
if(result.fd < 0)
|
||||
io_file_desc fd = -1;
|
||||
if(dirFd >= 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(".")))
|
||||
if(path.len && path.ptr[0] == '/')
|
||||
{
|
||||
//NOTE: skip
|
||||
continue;
|
||||
//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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
////////////////////////////////////////////////////////
|
||||
//TODO: else open the file with correct flags
|
||||
////////////////////////////////////////////////////////
|
||||
}
|
||||
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(table);
|
||||
if(!slot)
|
||||
{
|
||||
cmp.error = IO_ERR_MAX_FILES;
|
||||
cmp.result = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
slot->fd = -1;
|
||||
|
||||
cmp.handle = file_handle_from_slot(table, slot);
|
||||
|
||||
slot->rights = req->open.rights;
|
||||
if(atSlot)
|
||||
{
|
||||
slot->rights &= atSlot->rights;
|
||||
}
|
||||
|
||||
if(slot->rights != req->open.rights)
|
||||
{
|
||||
slot->error = IO_ERR_PERM;
|
||||
slot->fatal = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int flags = io_convert_access_rights(slot->rights);
|
||||
flags |= io_convert_open_flags(req->open.flags);
|
||||
|
||||
mode_t mode = S_IRUSR
|
||||
| S_IWUSR
|
||||
| S_IRGRP
|
||||
| S_IWGRP
|
||||
| S_IROTH
|
||||
| S_IWOTH;
|
||||
|
||||
mem_arena_scope scratch = mem_scratch_begin();
|
||||
str8 path = str8_from_buffer(req->size, req->buffer);
|
||||
|
||||
slot->fd = -1;
|
||||
if(atSlot)
|
||||
{
|
||||
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->open.flags & 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
|
||||
{
|
||||
flags = io_update_dir_flags_at(atSlot->fd, pathCStr, flags);
|
||||
|
||||
slot->fd = openat(atSlot->fd, pathCStr, flags, mode);
|
||||
if(slot->fd < 0)
|
||||
{
|
||||
slot->fatal = true;
|
||||
slot->error = io_convert_errno(errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char* pathCStr = str8_to_cstring(scratch.arena, path);
|
||||
|
||||
flags = io_update_dir_flags_at(AT_FDCWD, pathCStr, flags);
|
||||
|
||||
slot->fd = open(pathCStr, flags, mode);
|
||||
if(slot->fd < 0)
|
||||
{
|
||||
slot->fatal = true;
|
||||
slot->error = io_convert_errno(errno);
|
||||
}
|
||||
}
|
||||
mem_scratch_end(scratch);
|
||||
}
|
||||
cmp.error = slot->error;
|
||||
dirFd = AT_FDCWD;
|
||||
}
|
||||
|
||||
return(cmp);
|
||||
char* pathCStr = str8_to_cstring(scratch.arena, path);
|
||||
|
||||
flags = io_update_dir_flags_at(dirFd, pathCStr, flags);
|
||||
|
||||
fd = openat(dirFd, pathCStr, flags, mode);
|
||||
mem_scratch_end(scratch);
|
||||
|
||||
return(fd);
|
||||
}
|
||||
|
||||
io_cmp io_close(file_slot* slot, io_req* req, file_table* table)
|
||||
void io_raw_close(io_file_desc fd)
|
||||
{
|
||||
io_cmp cmp = {0};
|
||||
if(slot->fd >= 0)
|
||||
{
|
||||
close(slot->fd);
|
||||
}
|
||||
file_slot_recycle(table, slot);
|
||||
return(cmp);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
file_perm io_convert_perm_from_stat(u16 mode)
|
||||
|
@ -531,6 +280,148 @@ file_type io_convert_type_from_stat(u16 mode)
|
|||
return(type);
|
||||
}
|
||||
|
||||
io_error io_raw_fstat(io_file_desc fd, file_status* status)
|
||||
{
|
||||
io_error error = IO_OK;
|
||||
struct stat s;
|
||||
if(fstat(fd, &s))
|
||||
{
|
||||
error = io_raw_last_error();
|
||||
}
|
||||
else
|
||||
{
|
||||
status->uid = s.st_ino;
|
||||
status->perm = io_convert_perm_from_stat(s.st_mode);
|
||||
status->type = io_convert_type_from_stat(s.st_mode);
|
||||
status->size = s.st_size;
|
||||
//TODO: times
|
||||
}
|
||||
return(error);
|
||||
}
|
||||
|
||||
io_error io_raw_fstat_at(io_file_desc dirFd, str8 path, file_open_flags flags, file_status* status)
|
||||
{
|
||||
mem_arena_scope scratch = mem_scratch_begin();
|
||||
|
||||
if(dirFd >= 0)
|
||||
{
|
||||
if(path.len && path.ptr[0] == '/')
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dirFd = AT_FDCWD;
|
||||
}
|
||||
|
||||
char* pathCStr = str8_to_cstring(scratch.arena, path);
|
||||
|
||||
int statFlag = (flags & FILE_OPEN_SYMLINK)? AT_SYMLINK_NOFOLLOW : 0;
|
||||
io_error error = IO_OK;
|
||||
struct stat s;
|
||||
if(fstatat(dirFd, pathCStr, &s, statFlag))
|
||||
{
|
||||
error = io_raw_last_error();
|
||||
}
|
||||
else
|
||||
{
|
||||
status->uid = s.st_ino;
|
||||
status->perm = io_convert_perm_from_stat(s.st_mode);
|
||||
status->type = io_convert_type_from_stat(s.st_mode);
|
||||
status->size = s.st_size;
|
||||
//TODO: times
|
||||
}
|
||||
|
||||
mem_scratch_end(scratch);
|
||||
return(error);
|
||||
}
|
||||
|
||||
io_raw_read_link_result io_raw_read_link_at(mem_arena* arena, io_file_desc dirFd, str8 path)
|
||||
{
|
||||
mem_arena_scope scratch = mem_scratch_begin_next(arena);
|
||||
|
||||
if(dirFd >= 0)
|
||||
{
|
||||
if(path.len && path.ptr[0] == '/')
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dirFd = AT_FDCWD;
|
||||
}
|
||||
|
||||
char* pathCStr = str8_to_cstring(scratch.arena, path);
|
||||
|
||||
io_raw_read_link_result result = {0};
|
||||
|
||||
char buffer[PATH_MAX];
|
||||
u64 bufferSize = PATH_MAX;
|
||||
i64 r = readlinkat(dirFd, pathCStr, buffer, bufferSize);
|
||||
|
||||
if(r<0)
|
||||
{
|
||||
result.error = io_raw_last_error();
|
||||
}
|
||||
else
|
||||
{
|
||||
result.target.len = r;
|
||||
result.target.ptr = mem_arena_alloc_array(arena, char, result.target.len);
|
||||
memcpy(result.target.ptr, buffer, result.target.len);
|
||||
}
|
||||
|
||||
mem_scratch_end(scratch);
|
||||
return(result);
|
||||
}
|
||||
|
||||
bool io_raw_file_exists_at(io_file_desc dirFd, str8 path, file_open_flags openFlags)
|
||||
{
|
||||
mem_arena_scope scratch = mem_scratch_begin();
|
||||
|
||||
if(dirFd >= 0)
|
||||
{
|
||||
if(path.len && path.ptr[0] == '/')
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dirFd = AT_FDCWD;
|
||||
}
|
||||
|
||||
char* pathCStr = str8_to_cstring(scratch.arena, path);
|
||||
|
||||
int flags = (openFlags & FILE_OPEN_SYMLINK)? AT_SYMLINK_NOFOLLOW : 0;
|
||||
int r = faccessat(dirFd, pathCStr, F_OK, flags);
|
||||
bool result = (r == 0);
|
||||
|
||||
mem_scratch_end(scratch);
|
||||
return(result);
|
||||
}
|
||||
|
||||
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(table, slot);
|
||||
return(cmp);
|
||||
}
|
||||
|
||||
io_cmp io_fstat(file_slot* slot, io_req* req)
|
||||
{
|
||||
io_cmp cmp = {0};
|
||||
|
@ -544,7 +435,7 @@ io_cmp io_fstat(file_slot* slot, io_req* req)
|
|||
struct stat s;
|
||||
if(fstat(slot->fd, &s))
|
||||
{
|
||||
slot->error = io_convert_errno(errno);
|
||||
slot->error = io_raw_last_error();
|
||||
cmp.error = slot->error;
|
||||
}
|
||||
else
|
||||
|
@ -581,7 +472,7 @@ io_cmp io_seek(file_slot* slot, io_req* req)
|
|||
|
||||
if(cmp.result < 0)
|
||||
{
|
||||
slot->error = io_convert_errno(errno);
|
||||
slot->error = io_raw_last_error();
|
||||
cmp.error = slot->error;
|
||||
}
|
||||
|
||||
|
@ -596,7 +487,7 @@ io_cmp io_read(file_slot* slot, io_req* req)
|
|||
|
||||
if(cmp.result < 0)
|
||||
{
|
||||
slot->error = io_convert_errno(errno);
|
||||
slot->error = io_raw_last_error();
|
||||
cmp.result = 0;
|
||||
cmp.error = slot->error;
|
||||
}
|
||||
|
@ -612,7 +503,7 @@ io_cmp io_write(file_slot* slot, io_req* req)
|
|||
|
||||
if(cmp.result < 0)
|
||||
{
|
||||
slot->error = io_convert_errno(errno);
|
||||
slot->error = io_raw_last_error();
|
||||
cmp.result = 0;
|
||||
cmp.error = slot->error;
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ io_file_desc io_file_desc_nil()
|
|||
return(INVALID_HANDLE_VALUE);
|
||||
}
|
||||
|
||||
bool io_file_desc_invalid(io_file_desc fd)
|
||||
bool io_file_desc_is_nil(io_file_desc fd)
|
||||
{
|
||||
return(fd == NULL || fd == INVALID_HANDLE_VALUE);
|
||||
}
|
||||
|
@ -288,7 +288,7 @@ io_error io_raw_stat_at(io_file_desc dirFd, str8 name, file_open_flags openFlags
|
|||
{
|
||||
io_error error = IO_OK;
|
||||
io_file_desc fd = io_raw_open_at(dirFd, name, FILE_ACCESS_READ, FILE_OPEN_SYMLINK);
|
||||
if(io_file_desc_invalid(fd))
|
||||
if(io_file_desc_is_nil(fd))
|
||||
{
|
||||
error = io_raw_last_error();
|
||||
}
|
||||
|
@ -388,7 +388,7 @@ typedef struct io_open_restrict_context
|
|||
io_error io_open_restrict_enter(io_open_restrict_context* context, str8 name, file_access_rights accessRights, file_open_flags openFlags)
|
||||
{
|
||||
io_file_desc nextFd = io_raw_open_at(context->fd, name, accessRights, openFlags);
|
||||
if(io_file_desc_invalid(nextFd))
|
||||
if(io_file_desc_is_nil(nextFd))
|
||||
{
|
||||
context->error = io_raw_last_error();
|
||||
}
|
||||
|
@ -536,86 +536,12 @@ io_open_restrict_result io_open_path_restrict(io_file_desc dirFd, str8 path, fil
|
|||
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(table);
|
||||
if(!slot)
|
||||
{
|
||||
cmp.error = IO_ERR_MAX_FILES;
|
||||
cmp.result = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmp.handle = file_handle_from_slot(table, slot);
|
||||
|
||||
slot->rights = req->open.rights;
|
||||
if(atSlot)
|
||||
{
|
||||
slot->rights &= atSlot->rights;
|
||||
}
|
||||
|
||||
if(slot->rights != req->open.rights)
|
||||
{
|
||||
slot->error = IO_ERR_PERM;
|
||||
slot->fatal = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mem_arena_scope scratch = mem_scratch_begin();
|
||||
str8 path = str8_from_buffer(req->size, req->buffer);
|
||||
str16 pathW = win32_utf8_to_wide_null_terminated(scratch.arena, path);
|
||||
|
||||
slot->h = INVALID_HANDLE_VALUE;
|
||||
if(atSlot)
|
||||
{
|
||||
if(req->open.flags & FILE_OPEN_RESTRICT)
|
||||
{
|
||||
//TODO: if FILE_OPEN_RESTRICT, do the full path traversal to check that path is in the
|
||||
// subtree rooted at atSlot->fd
|
||||
io_open_restrict_result res = io_open_path_restrict(atSlot->h, path, req->open.rights, req->open.flags);
|
||||
if(res.error)
|
||||
{
|
||||
slot->fatal = true;
|
||||
slot->error = res.error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
slot->h = io_raw_open_at(atSlot->h, path, req->open.rights, req->open.flags);
|
||||
if(slot->h == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
slot->fatal = true;
|
||||
slot->error = io_raw_last_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO: take care of share mode and security attributes, make it consistent with posix impl
|
||||
slot->h = io_raw_open_at(NULL, path, req->open.rights, req->open.flags);
|
||||
if(slot->h == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
slot->fatal = true;
|
||||
slot->error = io_raw_last_error();
|
||||
}
|
||||
}
|
||||
|
||||
mem_scratch_end(scratch);
|
||||
}
|
||||
cmp.error = slot->error;
|
||||
}
|
||||
|
||||
return(cmp);
|
||||
}
|
||||
|
||||
io_cmp io_close(file_slot* slot, io_req* req, file_table* table)
|
||||
{
|
||||
io_cmp cmp = {0};
|
||||
if(slot->h)
|
||||
if(slot->fd)
|
||||
{
|
||||
CloseHandle(slot->h);
|
||||
CloseHandle(slot->fd);
|
||||
}
|
||||
file_slot_recycle(table, slot);
|
||||
return(cmp);
|
||||
|
@ -631,7 +557,7 @@ io_cmp io_fstat(file_slot* slot, io_req* req)
|
|||
}
|
||||
else
|
||||
{
|
||||
slot->error = io_raw_stat(slot->h, (file_status*)req->buffer);
|
||||
slot->error = io_raw_stat(slot->fd, (file_status*)req->buffer);
|
||||
cmp.error = slot->error;
|
||||
}
|
||||
return(cmp);
|
||||
|
@ -659,7 +585,7 @@ io_cmp io_seek(file_slot* slot, io_req* req)
|
|||
LARGE_INTEGER off = {.QuadPart = req->offset};
|
||||
LARGE_INTEGER newPos = {0};
|
||||
|
||||
if(!SetFilePointerEx(slot->h, off, &newPos, whence))
|
||||
if(!SetFilePointerEx(slot->fd, off, &newPos, whence))
|
||||
{
|
||||
slot->error = io_raw_last_error();
|
||||
cmp.error = slot->error;
|
||||
|
@ -678,7 +604,7 @@ io_cmp io_read(file_slot* slot, io_req* req)
|
|||
|
||||
DWORD bytesRead = 0;
|
||||
|
||||
if(!ReadFile(slot->h, req->buffer, req->size, &bytesRead, NULL))
|
||||
if(!ReadFile(slot->fd, req->buffer, req->size, &bytesRead, NULL))
|
||||
{
|
||||
slot->error = io_raw_last_error();
|
||||
cmp.result = 0;
|
||||
|
@ -697,7 +623,7 @@ io_cmp io_write(file_slot* slot, io_req* req)
|
|||
|
||||
DWORD bytesWritten = 0;
|
||||
|
||||
if(!WriteFile(slot->h, req->buffer, req->size, &bytesWritten, NULL))
|
||||
if(!WriteFile(slot->fd, req->buffer, req->size, &bytesWritten, NULL))
|
||||
{
|
||||
slot->error = io_raw_last_error();
|
||||
cmp.result = 0;
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Hello, world!
|
Loading…
Reference in New Issue