From 663df1528945cb8d788fa7b21b4ea68aac6296fa Mon Sep 17 00:00:00 2001 From: Max Wash Date: Mon, 28 Jul 2025 22:20:20 +0100 Subject: [PATCH] io: implement b_error support for file/directory operations --- io/include/blue/io/directory.h | 19 +++-- io/include/blue/io/file.h | 8 +- io/sys/darwin/directory.c | 152 +++++++++++++++++++++++++++------ io/sys/darwin/file.c | 27 +++--- io/sys/darwin/posix.c | 71 +++++++++++++++ io/sys/darwin/posix.h | 6 ++ 6 files changed, 236 insertions(+), 47 deletions(-) diff --git a/io/include/blue/io/directory.h b/io/include/blue/io/directory.h index 6dd5825..244511a 100644 --- a/io/include/blue/io/directory.h +++ b/io/include/blue/io/directory.h @@ -1,9 +1,9 @@ #ifndef BLUELIB_IO_DIRECTORY_H_ #define BLUELIB_IO_DIRECTORY_H_ +#include #include #include -#include #include #include @@ -19,6 +19,11 @@ typedef enum b_directory_iterator_flags { B_DIRECTORY_ITERATE_PARENT_LAST = 0x02u, } b_directory_iterator_flags; +typedef enum b_directory_open_flags { + B_DIRECTORY_OPEN_CREATE = 0x01u, + B_DIRECTORY_OPEN_CREATE_INTERMEDIATE = 0x03u, +} b_directory_open_flags; + typedef struct b_directory_iterator { b_iterator _base; struct z__b_directory_iterator *_z; @@ -35,17 +40,21 @@ typedef struct b_directory_iterator { for (int z__b_unique_name() = b_directory_iterator_begin(dir, it, flags); \ b_directory_iterator_is_valid(it); b_directory_iterator_next(it)) -BLUE_API b_status b_directory_open( - b_directory *root, const b_path *path, b_directory **out); +BLUE_API b_result b_directory_open( + b_directory *root, const b_path *path, b_directory_open_flags flags, + b_directory **out); BLUE_API const b_path *b_directory_get_path(const b_directory *dir); +BLUE_API const b_path *b_directory_get_rel_path(const b_directory *dir); +BLUE_API const char *b_directory_get_path_cstr(const b_directory *dir); +BLUE_API const char *b_directory_get_rel_path_cstr(const b_directory *dir); BLUE_API bool b_directory_path_exists(const b_directory *root, const b_path *path); BLUE_API bool b_directory_path_is_file(const b_directory *root, const b_path *path); BLUE_API bool b_directory_path_is_directory( const b_directory *root, const b_path *path); -BLUE_API b_status b_directory_path_stat( +BLUE_API b_result b_directory_path_stat( const b_directory *root, const b_path *path, struct b_file_info *out); -BLUE_API b_status b_directory_path_unlink( +BLUE_API b_result b_directory_path_unlink( const b_directory *root, const b_path *path); BLUE_API int b_directory_iterator_begin( diff --git a/io/include/blue/io/file.h b/io/include/blue/io/file.h index 4187cbe..bcf801d 100644 --- a/io/include/blue/io/file.h +++ b/io/include/blue/io/file.h @@ -1,8 +1,8 @@ #ifndef BLUELIB_IO_FILE_H_ #define BLUELIB_IO_FILE_H_ +#include #include -#include #include #include @@ -49,11 +49,11 @@ typedef struct b_file_info { typedef struct b_file b_file; -BLUE_API b_status b_file_open( +BLUE_API b_result b_file_open( struct b_directory *root, const struct b_path *path, b_file_mode mode, b_file **out); -BLUE_API b_status b_file_open_temp(b_file_mode mode, b_file **out); -BLUE_API b_status b_file_open_shadow( +BLUE_API b_result b_file_open_temp(b_file_mode mode, b_file **out); +BLUE_API b_result b_file_open_shadow( b_file *original, b_file_mode mode, b_file **out); BLUE_API b_status b_file_open_stream(b_file *file, b_stream **out); diff --git a/io/sys/darwin/directory.c b/io/sys/darwin/directory.c index e116595..d213f75 100644 --- a/io/sys/darwin/directory.c +++ b/io/sys/darwin/directory.c @@ -1,6 +1,7 @@ #include "posix.h" #include +#include #include #include #include @@ -12,6 +13,7 @@ struct b_directory { struct b_object base; int fd; + struct b_path *rel_path; struct b_path *abs_path; }; @@ -30,12 +32,95 @@ static struct b_object_type directory_type = { .t_release = directory_release, }; -enum b_status b_directory_open( +static b_result create_directory(struct b_directory *root, const char *path) +{ + int root_fd = root->fd; + int err = mkdirat(root_fd, path, 0755); + if (err == 0 || errno == EEXIST) { + return B_RESULT_SUCCESS; + } + + return b_result_from_errno_with_subfilepath( + errno, path, b_directory_get_rel_path_cstr(root), B_ERR_IO_FAILURE); +} + +static b_result create_directory_hierarchy(struct b_directory *root, const char *path) +{ + int root_fd = root->fd; + + char *path_buf = b_strdup(path); + if (!path_buf) { + return B_RESULT_ERR(NO_MEMORY); + } + + b_result result = B_RESULT_SUCCESS; + for (size_t i = 0; path_buf[i]; i++) { + if (path_buf[i] != '/') { + continue; + } + + path_buf[i] = 0; + + int err = mkdirat(root_fd, path_buf, 0755); + if (err != 0 && errno != EEXIST) { + result = b_result_from_errno_with_subfilepath( + errno, path_buf, + b_directory_get_rel_path_cstr(root), + B_ERR_IO_FAILURE); + break; + } + + path_buf[i] = '/'; + } + + int err = mkdirat(root_fd, path_buf, 0755); + if (err != 0 && errno != EEXIST) { + result = b_result_from_errno_with_subfilepath( + errno, path_buf, b_directory_get_rel_path_cstr(root), + B_ERR_IO_FAILURE); + } + + free(path_buf); + return result; +} + +b_result b_directory_open( struct b_directory *root, const struct b_path *path, - struct b_directory **out) + b_directory_open_flags flags, struct b_directory **out) { enum b_status status = B_SUCCESS; + int root_fd = root ? root->fd : AT_FDCWD; + + const char *path_cstr = b_path_ptr(path); + if (root) { + while (*path_cstr == '/') { + path_cstr++; + } + } + + b_result result = B_RESULT_SUCCESS; + if ((flags & B_DIRECTORY_OPEN_CREATE_INTERMEDIATE) + == B_DIRECTORY_OPEN_CREATE_INTERMEDIATE) { + result = create_directory_hierarchy(root, path_cstr); + } else if ((flags & B_DIRECTORY_OPEN_CREATE) == B_DIRECTORY_OPEN_CREATE) { + result = create_directory(root, path_cstr); + } + + if (b_result_is_error(result)) { + return b_result_propagate(result); + } + + int fd = openat(root_fd, path_cstr, O_DIRECTORY); + + if (fd == -1) { + status = b_status_from_errno(errno, B_ERR_IO_FAILURE); + return B_RESULT_STATUS(status); + } + + struct b_directory *dir + = (struct b_directory *)b_object_type_instantiate(&directory_type); + struct b_path *cwd = NULL; if (!root) { @@ -49,41 +134,58 @@ enum b_status b_directory_open( b_path *new_path = b_path_join(parts, sizeof parts / sizeof parts[0]); if (!new_path) { - return B_ERR_NO_MEMORY; + return B_RESULT_ERR(NO_MEMORY); } if (cwd) { b_path_release(cwd); } - int root_fd = root ? root->fd : AT_FDCWD; - - int fd = openat(root_fd, b_path_ptr(path), O_DIRECTORY); - - if (fd == -1) { - status = b_status_from_errno(errno, B_ERR_IO_FAILURE); - - b_path_release(new_path); - - return status; - } - - struct b_directory *dir - = (struct b_directory *)b_object_type_instantiate(&directory_type); - dir->abs_path = new_path; + dir->rel_path = b_path_duplicate(path); dir->fd = fd; *out = dir; - return B_SUCCESS; + return B_RESULT_SUCCESS; } const struct b_path *b_directory_get_path(const struct b_directory *dir) { + if (!dir) { + return NULL; + } + return dir->abs_path; } +const struct b_path *b_directory_get_rel_path(const struct b_directory *dir) +{ + if (!dir) { + return NULL; + } + + return dir->rel_path; +} + +const char *b_directory_get_path_cstr(const struct b_directory *dir) +{ + if (!dir) { + return NULL; + } + + return b_path_ptr(dir->abs_path); +} + +const char *b_directory_get_rel_path_cstr(const struct b_directory *dir) +{ + if (!dir) { + return NULL; + } + + return b_path_ptr(dir->rel_path); +} + bool b_directory_path_exists( const struct b_directory *root, const struct b_path *path) { @@ -144,7 +246,7 @@ bool b_directory_path_is_directory( return result; } -enum b_status b_directory_path_stat( +b_result b_directory_path_stat( const struct b_directory *root, const struct b_path *path, struct b_file_info *out) { @@ -156,16 +258,16 @@ enum b_status b_directory_path_stat( struct b_path *abs_path = b_path_join(parts, sizeof parts / sizeof parts[0]); if (!abs_path) { - return B_ERR_NO_MEMORY; + return B_RESULT_ERR(NO_MEMORY); } enum b_status status = b_path_stat(abs_path, out); b_path_release(abs_path); - return status; + return B_RESULT_STATUS(status); } -enum b_status b_directory_path_unlink( +b_result b_directory_path_unlink( const struct b_directory *root, const struct b_path *path) { const struct b_path *parts[] = { @@ -176,13 +278,13 @@ enum b_status b_directory_path_unlink( struct b_path *abs_path = b_path_join(parts, sizeof parts / sizeof parts[0]); if (!abs_path) { - return B_ERR_NO_MEMORY; + return B_RESULT_ERR(NO_MEMORY); } enum b_status status = b_path_unlink(abs_path); b_path_release(abs_path); - return status; + return B_RESULT_STATUS(status); } static int ftsent_compare(const FTSENT **one, const FTSENT **two) diff --git a/io/sys/darwin/file.c b/io/sys/darwin/file.c index 0f5e72d..0249004 100644 --- a/io/sys/darwin/file.c +++ b/io/sys/darwin/file.c @@ -61,7 +61,7 @@ static unsigned int b_mode_to_unix_mode(enum b_file_mode mode) return result; } -enum b_status b_file_open( +b_result b_file_open( struct b_directory *root, const struct b_path *path, enum b_file_mode mode, struct b_file **out) { @@ -70,7 +70,7 @@ enum b_status b_file_open( if (flags == (unsigned int)-1) { b_path_release(file_path); - return B_ERR_INVALID_ARGUMENT; + return B_RESULT_ERR(INVALID_ARGUMENT); } struct b_path *root_path = NULL; @@ -92,13 +92,14 @@ enum b_status b_file_open( b_path_release(file_path); if (!abs_path) { - return B_ERR_NO_MEMORY; + return B_RESULT_ERR(NO_MEMORY); } int fd = open(b_path_ptr(abs_path), flags, 0644); if (fd == -1) { b_path_release(abs_path); - return b_status_from_errno(errno, B_ERR_IO_FAILURE); + return B_RESULT_STATUS( + b_status_from_errno(errno, B_ERR_IO_FAILURE)); } struct b_file *file @@ -106,7 +107,7 @@ enum b_status b_file_open( if (!file) { close(fd); b_path_release(abs_path); - return B_ERR_NO_MEMORY; + return B_RESULT_ERR(NO_MEMORY); } file->fd = fd; @@ -114,7 +115,7 @@ enum b_status b_file_open( file->mode = mode; *out = file; - return B_SUCCESS; + return B_RESULT_SUCCESS; } static void generate_tmp_filename(char *out, size_t len) @@ -135,7 +136,7 @@ static void generate_tmp_filename(char *out, size_t len) out[len - 1] = 0; } -enum b_status b_file_open_temp(enum b_file_mode mode, struct b_file **out) +b_result b_file_open_temp(enum b_file_mode mode, struct b_file **out) { mode |= B_FILE_DELETE_ON_CLOSE; @@ -147,10 +148,10 @@ enum b_status b_file_open_temp(enum b_file_mode mode, struct b_file **out) snprintf(path, sizeof path, "/tmp/%s", name); struct b_path *rpath = b_path_create_from_cstr(path); - enum b_status status = b_file_open( + b_result status = b_file_open( B_DIRECTORY_ROOT, rpath, mode | B_FILE_CREATE_ONLY, out); - if (status == B_ERR_NAME_EXISTS) { + if (b_error_get_status_code(status) == B_ERR_NAME_EXISTS) { b_path_release(rpath); continue; } @@ -162,7 +163,7 @@ enum b_status b_file_open_temp(enum b_file_mode mode, struct b_file **out) } } -enum b_status b_file_open_shadow( +b_result b_file_open_shadow( struct b_file *original, enum b_file_mode mode, struct b_file **out) { mode |= B_FILE_SHADOW | B_FILE_DELETE_ON_CLOSE | B_FILE_CREATE; @@ -194,16 +195,16 @@ enum b_status b_file_open_shadow( } struct b_file *shadow_file; - enum b_status status = b_file_open( + b_result status = b_file_open( B_DIRECTORY_ROOT, shadow_filepath, mode, &shadow_file); b_path_release(shadow_filepath); - if (!B_OK(status)) { + if (b_result_is_error(status)) { return status; } *out = shadow_file; - return B_SUCCESS; + return B_RESULT_SUCCESS; } static enum b_status stream_close(struct b_stream *stream) diff --git a/io/sys/darwin/posix.c b/io/sys/darwin/posix.c index 2063072..db10e8e 100644 --- a/io/sys/darwin/posix.c +++ b/io/sys/darwin/posix.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -23,6 +24,7 @@ enum b_status b_status_from_errno(int error, enum b_status default_value) case ENOTDIR: return B_ERR_NOT_DIRECTORY; case EPERM: + case EACCES: return B_ERR_PERMISSION_DENIED; case ENOTSUP: case ENOSYS: @@ -32,6 +34,75 @@ enum b_status b_status_from_errno(int error, enum b_status default_value) } } +b_result b_result_from_errno_with_filepath( + int error, const char *path, enum b_status default_value) +{ + switch (error) { + case 0: + return B_RESULT_SUCCESS; + case ENOENT: + return B_RESULT_STATUS_WITH_STRING( + B_ERR_NO_ENTRY, "Path @i{%s} does not exist", path); + case ENOTDIR: + return B_RESULT_STATUS_WITH_STRING( + B_ERR_NOT_DIRECTORY, "Path @i{%s} is not a directory", + path); + case EISDIR: + return B_RESULT_STATUS_WITH_STRING( + B_ERR_IS_DIRECTORY, "Path @i{%s} is a directory", path); + case EPERM: + case EACCES: + return B_RESULT_STATUS_WITH_STRING( + B_ERR_PERMISSION_DENIED, + "Permission denied while accessing path @i{%s}", path); + default: + return B_RESULT_STATUS(b_status_from_errno(error, default_value)); + } + + return B_RESULT_SUCCESS; +} + +b_result b_result_from_errno_with_subfilepath( + int error, const char *path, const char *dir_path, + enum b_status default_value) +{ + if (!dir_path) { + return b_result_propagate(b_result_from_errno_with_filepath( + error, path, default_value)); + } + + switch (error) { + case 0: + return B_RESULT_SUCCESS; + case ENOENT: + return B_RESULT_STATUS_WITH_STRING( + B_ERR_NO_ENTRY, + "Path @i{%s} in directory @i{%s} does not exist", path, + dir_path); + case ENOTDIR: + return B_RESULT_STATUS_WITH_STRING( + B_ERR_NOT_DIRECTORY, + "Path @i{%s} in directory @i{%s} is not a directory", + path, dir_path); + case EISDIR: + return B_RESULT_STATUS_WITH_STRING( + B_ERR_IS_DIRECTORY, + "Path @i{%s} in directory @i{%s} is a directory", path, + dir_path); + case EPERM: + case EACCES: + return B_RESULT_STATUS_WITH_STRING( + B_ERR_PERMISSION_DENIED, + "Permission denied while accessing path @i{%s} in " + "directory @i{%s}", + path, dir_path); + default: + return B_RESULT_STATUS(b_status_from_errno(error, default_value)); + } + + return B_RESULT_SUCCESS; +} + enum b_status b_file_info_from_stat(const struct stat *st, struct b_file_info *out) { out->length = st->st_size; diff --git a/io/sys/darwin/posix.h b/io/sys/darwin/posix.h index 7efffe9..5ce72ba 100644 --- a/io/sys/darwin/posix.h +++ b/io/sys/darwin/posix.h @@ -1,12 +1,18 @@ #ifndef _IO_DARWIN_POSIX_H_ #define _IO_DARWIN_POSIX_H_ +#include #include struct stat; struct b_file_info; extern enum b_status b_status_from_errno(int error, enum b_status default_value); +extern b_result b_result_from_errno_with_filepath( + int error, const char *path, enum b_status default_value); +extern b_result b_result_from_errno_with_subfilepath( + int error, const char *path, const char *dir_path, + enum b_status default_value); extern enum b_status b_file_info_from_stat( const struct stat *in, struct b_file_info *out);