From a64c86ca4e405ca2ec4b7dfaeb96317981cf1e76 Mon Sep 17 00:00:00 2001 From: Martin Fouilleul Date: Fri, 16 Jun 2023 11:57:53 +0200 Subject: [PATCH] [io, test] Added more file io tests --- src/platform/platform_io.h | 3 + src/platform/platform_io_common.c | 10 + src/platform/platform_io_internal.c | 266 ++++++++-------- test/files/data/directory/dummy.txt | 0 test/files/data/directory/test.txt | 1 + test/files/data/jail/dir/test.txt | 1 + test/files/data/jail/dir1/dummy.txt | 0 test/files/data/jail/{escape => dir_escape} | 0 test/files/data/jail/file_escape | 1 + test/files/data/jail/test.txt | 1 + test/files/data/regular.txt | 2 +- test/files/data/{posix_symlink => symlink} | 0 test/files/data/win32_symlink | 1 - test/files/main.c | 323 +++++++++++++++++--- 14 files changed, 438 insertions(+), 171 deletions(-) delete mode 100644 test/files/data/directory/dummy.txt create mode 100644 test/files/data/directory/test.txt create mode 100644 test/files/data/jail/dir/test.txt delete mode 100644 test/files/data/jail/dir1/dummy.txt rename test/files/data/jail/{escape => dir_escape} (100%) create mode 120000 test/files/data/jail/file_escape rename test/files/data/{posix_symlink => symlink} (100%) delete mode 120000 test/files/data/win32_symlink diff --git a/src/platform/platform_io.h b/src/platform/platform_io.h index 26fe09e..a8136d3 100644 --- a/src/platform/platform_io.h +++ b/src/platform/platform_io.h @@ -140,6 +140,9 @@ MP_API io_cmp io_wait_single_req(io_req* req); // File IO wrapper API //---------------------------------------------------------------- +MP_API file_handle file_handle_nil(); +MP_API bool file_handle_is_nil(file_handle handle); + MP_API file_handle file_open(str8 path, file_access_rights rights, file_open_flags flags); MP_API file_handle file_open_at(file_handle dir, str8 path, file_access_rights rights, file_open_flags flags); MP_API void file_close(file_handle file); diff --git a/src/platform/platform_io_common.c b/src/platform/platform_io_common.c index c01366e..199c69d 100644 --- a/src/platform/platform_io_common.c +++ b/src/platform/platform_io_common.c @@ -11,6 +11,16 @@ // File stream read/write API //------------------------------------------------------------------------------ +file_handle file_handle_nil() +{ + return((file_handle){0}); +} + +bool file_handle_is_nil(file_handle handle) +{ + return(handle.h == 0); +} + file_handle file_open(str8 path, file_access_rights rights, file_open_flags flags) { io_req req = {.op = IO_OP_OPEN_AT, diff --git a/src/platform/platform_io_internal.c b/src/platform/platform_io_internal.c index 37d6898..17d27fa 100644 --- a/src/platform/platform_io_internal.c +++ b/src/platform/platform_io_internal.c @@ -117,124 +117,134 @@ io_open_restrict_result io_open_restrict(io_file_desc dirFd, str8 path, file_acc .fd = dirFd, }; - file_status status; - context.error = io_raw_fstat(dirFd, &status); - context.rootUID = status.uid; - - for_list(&pathElements.list, elt, str8_elt, listElt) + if(io_file_desc_is_nil(dirFd)) { - str8 name = elt->string; - file_access_rights eltAccessRights = FILE_ACCESS_READ; - file_open_flags eltOpenFlags = 0; + context.error = IO_ERR_HANDLE; + } + else + { + file_status status; + context.error = io_raw_fstat(dirFd, &status); + context.rootUID = status.uid; + } - bool atLastElement = (&elt->listElt == list_last(&pathElements.list)); - if(atLastElement) + if(context.error == IO_OK) + { + for_list(&pathElements.list, elt, str8_elt, listElt) { - eltAccessRights = accessRights; - eltOpenFlags = openFlags; - } + str8 name = elt->string; + file_access_rights eltAccessRights = FILE_ACCESS_READ; + file_open_flags eltOpenFlags = 0; - if( !str8_cmp(name, STR8(".")) - && !atLastElement) - { - //NOTE: if we're not at the last element we can just skip '.' elements - continue; - } - 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) + bool atLastElement = (&elt->listElt == list_last(&pathElements.list)); + if(atLastElement) { - break; - } - else if(status.uid == context.rootUID) - { - context.error = IO_ERR_WALKOUT; - break; - } - } - 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( !atLastElement - || !(openFlags & FILE_OPEN_CREATE)) - { - 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; + eltAccessRights = accessRights; + eltOpenFlags = openFlags; } - if(status.type == MP_FILE_REGULAR) + if( !str8_cmp(name, STR8(".")) + && !atLastElement) { - if(!atLastElement) + //NOTE: if we're not at the last element we can just skip '.' elements + continue; + } + 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(!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( !atLastElement + || !(openFlags & FILE_OPEN_CREATE)) + { + 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_REGULAR) + { + if(!atLastElement) + { + context.error = IO_ERR_NOT_DIR; + break; + } + } + else 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( !atLastElement + || !(openFlags & FILE_OPEN_SYMLINK)) + { + 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(link.target.len == 0) + { + //NOTE: treat an empty target as a '.' + link.target = STR8("."); + } + else if(path_is_absolute(link.target)) + { + context.error = IO_ERR_WALKOUT; + break; + } + + 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; + } + } + continue; + } + } + else if(status.type != MP_FILE_DIRECTORY) { context.error = IO_ERR_NOT_DIR; break; } } - else 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( !atLastElement - || !(openFlags & FILE_OPEN_SYMLINK)) - { - 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(link.target.len == 0) - { - //NOTE: treat an empty target as a '.' - link.target = STR8("."); - } - else if(path_is_absolute(link.target)) - { - context.error = IO_ERR_WALKOUT; - break; - } - - 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; - } - } - continue; - } - } - else if(status.type != MP_FILE_DIRECTORY) - { - context.error = IO_ERR_NOT_DIR; - break; - } + //NOTE: if we arrive here, we have no errors and the correct flags are set, + // so we can enter the element + DEBUG_ASSERT(context.error == IO_OK); + io_open_restrict_enter(&context, name, eltAccessRights, eltOpenFlags); } - - //NOTE: if we arrive here, we have no errors and the correct flags are set, - // so we can enter the element - DEBUG_ASSERT(context.error == IO_OK); - io_open_restrict_enter(&context, name, eltAccessRights, eltOpenFlags); } if(context.error && !io_file_desc_is_nil(context.fd)) @@ -270,38 +280,50 @@ io_cmp io_open_at(file_slot* atSlot, io_req* req, file_table* table) 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) + str8 path = str8_from_buffer(req->size, req->buffer); + + if(!path.len) { - slot->error = IO_ERR_PERM; - slot->fatal = true; + slot->error = IO_ERR_ARG; + } + else if(!atSlot && !file_handle_is_nil(req->handle)) + { + slot->error = IO_ERR_HANDLE; } else { - str8 path = str8_from_buffer(req->size, req->buffer); - - io_file_desc dirFd = atSlot ? atSlot->fd : io_file_desc_nil(); - - if(req->open.flags & FILE_OPEN_RESTRICT) + slot->rights = req->open.rights; + if(atSlot) { - io_open_restrict_result res = io_open_restrict(dirFd, path, slot->rights, req->open.flags); - slot->error = res.error; - slot->fd = res.fd; + slot->rights &= atSlot->rights; + } + + if(slot->rights != req->open.rights) + { + slot->error = IO_ERR_PERM; } else { - slot->fd = io_raw_open_at(dirFd, path, slot->rights, req->open.flags); - if(io_file_desc_is_nil(slot->fd)) + io_file_desc dirFd = atSlot ? atSlot->fd : io_file_desc_nil(); + + if(req->open.flags & FILE_OPEN_RESTRICT) { - slot->error = io_raw_last_error(); + io_open_restrict_result res = io_open_restrict(dirFd, path, slot->rights, req->open.flags); + slot->error = res.error; + slot->fd = res.fd; + } + else + { + slot->fd = io_raw_open_at(dirFd, path, slot->rights, req->open.flags); + if(io_file_desc_is_nil(slot->fd)) + { + slot->error = io_raw_last_error(); + } } } } + if(slot->error) { slot->fatal = true; diff --git a/test/files/data/directory/dummy.txt b/test/files/data/directory/dummy.txt deleted file mode 100644 index e69de29..0000000 diff --git a/test/files/data/directory/test.txt b/test/files/data/directory/test.txt new file mode 100644 index 0000000..389b335 --- /dev/null +++ b/test/files/data/directory/test.txt @@ -0,0 +1 @@ +Hello from directory/test.txt \ No newline at end of file diff --git a/test/files/data/jail/dir/test.txt b/test/files/data/jail/dir/test.txt new file mode 100644 index 0000000..f1e120f --- /dev/null +++ b/test/files/data/jail/dir/test.txt @@ -0,0 +1 @@ +Hello from jail/dir/test.txt \ No newline at end of file diff --git a/test/files/data/jail/dir1/dummy.txt b/test/files/data/jail/dir1/dummy.txt deleted file mode 100644 index e69de29..0000000 diff --git a/test/files/data/jail/escape b/test/files/data/jail/dir_escape similarity index 100% rename from test/files/data/jail/escape rename to test/files/data/jail/dir_escape diff --git a/test/files/data/jail/file_escape b/test/files/data/jail/file_escape new file mode 120000 index 0000000..98f61ca --- /dev/null +++ b/test/files/data/jail/file_escape @@ -0,0 +1 @@ +../regular.txt \ No newline at end of file diff --git a/test/files/data/jail/test.txt b/test/files/data/jail/test.txt index e69de29..d225ff4 100644 --- a/test/files/data/jail/test.txt +++ b/test/files/data/jail/test.txt @@ -0,0 +1 @@ +Hello from jail/test.txt \ No newline at end of file diff --git a/test/files/data/regular.txt b/test/files/data/regular.txt index 5dd01c1..fac204f 100644 --- a/test/files/data/regular.txt +++ b/test/files/data/regular.txt @@ -1 +1 @@ -Hello, world! \ No newline at end of file +Hello from regular.txt \ No newline at end of file diff --git a/test/files/data/posix_symlink b/test/files/data/symlink similarity index 100% rename from test/files/data/posix_symlink rename to test/files/data/symlink diff --git a/test/files/data/win32_symlink b/test/files/data/win32_symlink deleted file mode 120000 index 397e107..0000000 --- a/test/files/data/win32_symlink +++ /dev/null @@ -1 +0,0 @@ -regular.txt \ No newline at end of file diff --git a/test/files/main.c b/test/files/main.c index a6ce32e..45601cf 100644 --- a/test/files/main.c +++ b/test/files/main.c @@ -9,10 +9,15 @@ #include #include"milepost.h" -int test_write(mem_arena* arena, str8 path, str8 test_string) +int test_write() { log_info("writing\n"); + mem_arena* arena = mem_scratch(); + + str8 path = STR8("./data/write_test.txt"); + str8 test_string = STR8("Hello from write_test.txt"); + file_handle f = file_open(path, FILE_ACCESS_WRITE, FILE_OPEN_CREATE|FILE_OPEN_TRUNCATE); if(file_last_error(f)) { @@ -48,10 +53,31 @@ int test_write(mem_arena* arena, str8 path, str8 test_string) return(0); } -int test_read(mem_arena* arena, str8 path, str8 test_string) +int check_string(file_handle f, str8 test_string) +{ + char buffer[256]; + i64 n = file_read(f, 256, buffer); + if(file_last_error(f)) + { + log_error("Error while reading test string\n"); + return(-1); + } + + if(str8_cmp(test_string, str8_from_buffer(n, buffer))) + { + return(-1); + } + + return(0); +} + +int test_read() { log_info("reading\n"); + str8 path = STR8("./data/regular.txt"); + str8 test_string = STR8("Hello from regular.txt"); + file_handle f = file_open(path, FILE_ACCESS_READ, 0); if(file_last_error(f)) { @@ -59,28 +85,24 @@ int test_read(mem_arena* arena, str8 path, str8 test_string) return(-1); } - char buffer[256]; - i64 n = file_read(f, 256, buffer); - if(file_last_error(f)) + if(check_string(f, test_string)) { - log_error("Error while reading %.*s\n", (int)path.len, path.ptr); + log_error("Check string failed\n"); return(-1); } + file_close(f); - - if(str8_cmp(test_string, str8_from_buffer(n, buffer))) - { - log_error("Didn't recover test string\n"); - return(-1); - } - return(0); } -int test_stat_size(str8 path, u64 size) +int test_stat_size() { log_info("stat size\n"); + str8 path = STR8("./data/regular.txt"); + str8 test_string = STR8("Hello from regular.txt"); + u64 size = test_string.len; + file_handle f = file_open(path, 0, 0); if(file_last_error(f)) { @@ -106,16 +128,11 @@ int test_stat_size(str8 path, u64 size) return(0); } -int test_stat_type(mem_arena* arena, str8 dataDir) +int test_stat_type() { - str8 regular = path_append(arena, dataDir, STR8("regular.txt")); - str8 dir = path_append(arena, dataDir, STR8("directory")); - - #if PLATFORM_WINDOWS - str8 link = path_append(arena, dataDir, STR8("win32_symlink")); - #else - str8 link = path_append(arena, dataDir, STR8("posix_symlink")); - #endif + str8 regular = STR8("./data/regular.txt"); + str8 dir = STR8("./data/directory"); + str8 link = STR8("./data/symlink"); log_info("stat type, regular\n"); @@ -186,6 +203,120 @@ int test_stat_type(mem_arena* arena, str8 dataDir) return(0); } +int test_symlinks() +{ + // open symlink target + log_info("open symlink target\n"); + file_handle f = file_open_at(file_handle_nil(), STR8("./data/symlink"), FILE_ACCESS_READ, 0); + if(file_last_error(f)) + { + log_error("failed to open ./data/symlink\n"); + return(-1); + } + if(check_string(f, STR8("Hello from regular.txt"))) + { + log_error("Check string failed\n"); + return(-1); + } + file_close(f); + + // open symlink file + log_info("open symlink file\n"); + f = file_open_at(file_handle_nil(), STR8("./data/symlink"), FILE_ACCESS_READ, FILE_OPEN_SYMLINK); + if(file_last_error(f)) + { + log_error("failed to open ./data/symlink\n"); + return(-1); + } + file_status status = file_get_status(f); + if(file_last_error(f)) + { + log_error("Error while retrieving file status\n"); + return(-1); + } + if(status.type != MP_FILE_SYMLINK) + { + log_error("file type doesn't match\n"); + return(-1); + } + + char buffer[512]; + int n = file_read(f, 512, buffer); + + if(n || (file_last_error(f) == IO_OK)) + { + log_error("file read should fail on symlinks\n"); + return(-1); + } + + file_close(f); + + + return(0); +} + +int test_args() +{ + //NOTE: nil handle + log_info("check open_at with nil handle\n"); + file_handle f = file_open_at(file_handle_nil(), STR8("./data/regular.txt"), FILE_ACCESS_READ, 0); + if(file_last_error(f)) + { + log_error("file_open_at() with nil handle failed\n"); + return(-1); + } + if(check_string(f, STR8("Hello from regular.txt"))) + { + log_error("Check string failed\n"); + return(-1); + } + file_close(f); + + //NOTE: invalid handle + log_info("check open_at with nil handle\n"); + file_handle wrongHandle = {.h = 123456789 }; + + f = file_open_at(wrongHandle, STR8("./data/regular.txt"), FILE_ACCESS_READ, 0); + if(file_last_error(f) != IO_ERR_HANDLE) + { + log_error("file_open_at() with non-nil invalid handle should return IO_ERR_HANDLE\n"); + return(-1); + } + file_close(f); + + //NOTE: nil/wrong handle and FILE_OPEN_RESTRICT + log_info("check open_at with nil handle and FILE_OPEN_RESTRICT\n"); + + f = file_open_at(file_handle_nil(), STR8("./data/regular.txt"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT); + if(file_last_error(f) != IO_ERR_HANDLE) + { + log_error("file_open_at() with nil handle and FILE_OPEN_RESTRICT should return IO_ERR_HANDLE\n"); + return(-1); + } + file_close(f); + + f = file_open_at(wrongHandle, STR8("./data/regular.txt"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT); + if(file_last_error(f) != IO_ERR_HANDLE) + { + log_error("file_open_at() with invalid handle and FILE_OPEN_RESTRICT should return IO_ERR_HANDLE\n"); + return(-1); + } + file_close(f); + + //NOTE: empty path + log_info("check empty path\n"); + + f = file_open_at(file_handle_nil(), STR8(""), FILE_ACCESS_READ, 0); + if(file_last_error(f) != IO_ERR_ARG) + { + log_error("empty path should return IO_ERR_ARG\n"); + return(-1); + } + file_close(f); + + return(0); +} + int test_jail() { log_info("test jail\n"); @@ -197,8 +328,22 @@ int test_jail() return(-1); } - // Check escapes - file_handle f = file_open_at(jail, STR8(".."), FILE_ACCESS_READ, FILE_OPEN_RESTRICT); + //----------------------------------------------------------- + //NOTE: Check escapes + //----------------------------------------------------------- + log_info("check potential escapes\n"); + + //NOTE: escape with absolute path + file_handle f = file_open_at(jail, STR8("/tmp"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT); + if(file_last_error(f) != IO_ERR_NO_ENTRY) + { + log_error("Escaped jail with absolute path /tmp\n"); + return(-1); + } + file_close(f); + + //NOTE: escape with .. + f = file_open_at(jail, STR8(".."), FILE_ACCESS_READ, FILE_OPEN_RESTRICT); if(file_last_error(f) != IO_ERR_WALKOUT) { log_error("Escaped jail with relative path ..\n"); @@ -206,47 +351,134 @@ int test_jail() } file_close(f); - f = file_open_at(jail, STR8(".."), FILE_ACCESS_READ, FILE_OPEN_RESTRICT); + //NOTE: escape with dir/../.. + f = file_open_at(jail, STR8("dir/../.."), FILE_ACCESS_READ, FILE_OPEN_RESTRICT); if(file_last_error(f) != IO_ERR_WALKOUT) { - log_error("Escaped jail with relative path dir1/../..\n"); + log_error("Escaped jail with relative path dir/../..\n"); return(-1); } file_close(f); - f = file_open_at(jail, STR8("/escape"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT); + //NOTE: escape with symlink to parent + f = file_open_at(jail, STR8("/dir_escape"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT); if(file_last_error(f) != IO_ERR_WALKOUT) { - log_error("Escaped jail with symlink\n"); + log_error("Escaped jail with symlink to parent\n"); return(-1); } file_close(f); - // Check legitimates open + //NOTE: escape to file with symlink to parent + f = file_open_at(jail, STR8("/dir_escape/regular.txt"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT); + if(file_last_error(f) != IO_ERR_WALKOUT) + { + log_error("Escaped jail to regular.txt with symlink to parent\n"); + return(-1); + } + file_close(f); + + //NOTE: escape with symlink to file + f = file_open_at(jail, STR8("/file_escape"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT); + if(file_last_error(f) != IO_ERR_WALKOUT) + { + log_error("Escaped jail with symlink to file regular.txt\n"); + return(-1); + } + file_close(f); + + //NOTE: escape with bad root handle + file_handle wrong_handle = {0}; + f = file_open_at(wrong_handle, STR8("./data/regular.txt"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT); + if(file_last_error(f) == IO_OK) + { + log_error("Escaped jail with nil root handle\n"); + return(-1); + } + if(file_last_error(f) != IO_ERR_HANDLE) + { + log_error("FILE_OPEN_RESTRICT with invalid root handle should return IO_ERR_HANDLE\n"); + return(-1); + } + file_close(f); + + //----------------------------------------------------------- + //NOTE: empty path + //----------------------------------------------------------- + log_info("check empty path\n"); + + f = file_open_at(jail, STR8(""), FILE_ACCESS_READ, 0); + if(file_last_error(f) != IO_ERR_ARG) + { + log_error("empty path should return IO_ERR_ARG\n"); + return(-1); + } + file_close(f); + + //----------------------------------------------------------- + //NOTE: Check legitimates open + //----------------------------------------------------------- + log_info("check legitimates open\n"); + + //NOTE: regular file jail/test.txt f = file_open_at(jail, STR8("/test.txt"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT); if(file_last_error(f) != IO_OK) { log_error("Can't open jail/test.txt\n"); return(-1); } - file_close(f); - - f = file_open_at(jail, STR8("/dir1/../test.txt"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT); - if(file_last_error(f) != IO_OK) + if(check_string(f, STR8("Hello from jail/test.txt"))) { - log_error("Can't open jail/dir1/../test.txt\n"); + log_error("Check string failed\n"); return(-1); } file_close(f); + //NOTE: valid file traversal to jail/test.txt + f = file_open_at(jail, STR8("/dir/../test.txt"), FILE_ACCESS_READ, FILE_OPEN_RESTRICT); + if(file_last_error(f) != IO_OK) + { + log_error("Can't open jail/dir/../test.txt\n"); + return(-1); + } + if(check_string(f, STR8("Hello from jail/test.txt"))) + { + log_error("Check string failed\n"); + return(-1); + } + file_close(f); + + //NOTE: re-open root directory + f = file_open_at(jail, STR8("."), FILE_ACCESS_READ, FILE_OPEN_RESTRICT); + if(file_last_error(f) != IO_OK) + { + log_error("Can't open jail/.\n"); + return(-1); + } + { + //NOTE: access regular file test.txt inside reopened root + file_handle f2 = file_open_at(f, STR8("test.txt"), FILE_ACCESS_READ, 0); + + if(check_string(f2, STR8("Hello from jail/test.txt"))) + { + log_error("Check string failed\n"); + return(-1); + } + file_close(f2); + } + file_close(f); + + return(0); } -int test_rights(mem_arena* arena, str8 dirPath) +int test_rights() { log_info("test rights\n"); + str8 dirPath = STR8("./data"); + //-------------------------------------------------------------------------------------- // base dir with no access //-------------------------------------------------------------------------------------- @@ -395,19 +627,16 @@ int main(int argc, char** argv) mem_arena* arena = mem_scratch(); - str8 dataDir = STR8("./data"); - str8 path = STR8("./test.txt"); - - str8 test_string = STR8("Hello, world!"); - - if(test_write(arena, path, test_string)) { return(-1); } - if(test_read(arena, path, test_string)) { return(-1); } - if(test_stat_size(path, test_string.len)) { return(-1); } - if(test_stat_type(arena, dataDir)) { return(-1); } - if(test_rights(arena, dataDir)) { return(-1); } + if(test_write()) { return(-1); } + if(test_read()) { return(-1); } + if(test_stat_size()) { return(-1); } + if(test_stat_type()) { return(-1); } + if(test_args()) { return(-1); } + if(test_symlinks()) { return(-1); } + if(test_rights()) { return(-1); } if(test_jail()) { return(-1); } - remove("./test.txt"); + remove("./data/write_test.txt"); log_info("OK\n");