io: implement b_error support for file/directory operations

This commit is contained in:
2025-07-28 22:20:20 +01:00
parent 8554541f3a
commit 663df15289
6 changed files with 236 additions and 47 deletions

View File

@@ -1,9 +1,9 @@
#ifndef BLUELIB_IO_DIRECTORY_H_ #ifndef BLUELIB_IO_DIRECTORY_H_
#define BLUELIB_IO_DIRECTORY_H_ #define BLUELIB_IO_DIRECTORY_H_
#include <blue/core/error.h>
#include <blue/core/iterator.h> #include <blue/core/iterator.h>
#include <blue/core/misc.h> #include <blue/core/misc.h>
#include <blue/core/status.h>
#include <blue/io/file.h> #include <blue/io/file.h>
#include <blue/io/path.h> #include <blue/io/path.h>
@@ -19,6 +19,11 @@ typedef enum b_directory_iterator_flags {
B_DIRECTORY_ITERATE_PARENT_LAST = 0x02u, B_DIRECTORY_ITERATE_PARENT_LAST = 0x02u,
} b_directory_iterator_flags; } 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 { typedef struct b_directory_iterator {
b_iterator _base; b_iterator _base;
struct z__b_directory_iterator *_z; 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); \ for (int z__b_unique_name() = b_directory_iterator_begin(dir, it, flags); \
b_directory_iterator_is_valid(it); b_directory_iterator_next(it)) b_directory_iterator_is_valid(it); b_directory_iterator_next(it))
BLUE_API b_status b_directory_open( BLUE_API b_result b_directory_open(
b_directory *root, const b_path *path, b_directory **out); 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_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_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_file(const b_directory *root, const b_path *path);
BLUE_API bool b_directory_path_is_directory( BLUE_API bool b_directory_path_is_directory(
const b_directory *root, const b_path *path); 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); 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); const b_directory *root, const b_path *path);
BLUE_API int b_directory_iterator_begin( BLUE_API int b_directory_iterator_begin(

View File

@@ -1,8 +1,8 @@
#ifndef BLUELIB_IO_FILE_H_ #ifndef BLUELIB_IO_FILE_H_
#define BLUELIB_IO_FILE_H_ #define BLUELIB_IO_FILE_H_
#include <blue/core/error.h>
#include <blue/core/misc.h> #include <blue/core/misc.h>
#include <blue/core/status.h>
#include <blue/core/stream.h> #include <blue/core/stream.h>
#include <blue/object/object.h> #include <blue/object/object.h>
@@ -49,11 +49,11 @@ typedef struct b_file_info {
typedef struct b_file b_file; 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, struct b_directory *root, const struct b_path *path, b_file_mode mode,
b_file **out); b_file **out);
BLUE_API b_status b_file_open_temp(b_file_mode mode, b_file **out); BLUE_API b_result 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_shadow(
b_file *original, b_file_mode mode, b_file **out); b_file *original, b_file_mode mode, b_file **out);
BLUE_API b_status b_file_open_stream(b_file *file, b_stream **out); BLUE_API b_status b_file_open_stream(b_file *file, b_stream **out);

View File

@@ -1,6 +1,7 @@
#include "posix.h" #include "posix.h"
#include <blue/io/directory.h> #include <blue/io/directory.h>
#include <blue/object/string.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <fts.h> #include <fts.h>
@@ -12,6 +13,7 @@
struct b_directory { struct b_directory {
struct b_object base; struct b_object base;
int fd; int fd;
struct b_path *rel_path;
struct b_path *abs_path; struct b_path *abs_path;
}; };
@@ -30,12 +32,95 @@ static struct b_object_type directory_type = {
.t_release = directory_release, .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 *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; 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; struct b_path *cwd = NULL;
if (!root) { 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]); b_path *new_path = b_path_join(parts, sizeof parts / sizeof parts[0]);
if (!new_path) { if (!new_path) {
return B_ERR_NO_MEMORY; return B_RESULT_ERR(NO_MEMORY);
} }
if (cwd) { if (cwd) {
b_path_release(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->abs_path = new_path;
dir->rel_path = b_path_duplicate(path);
dir->fd = fd; dir->fd = fd;
*out = dir; *out = dir;
return B_SUCCESS; return B_RESULT_SUCCESS;
} }
const struct b_path *b_directory_get_path(const struct b_directory *dir) const struct b_path *b_directory_get_path(const struct b_directory *dir)
{ {
if (!dir) {
return NULL;
}
return dir->abs_path; 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( bool b_directory_path_exists(
const struct b_directory *root, const struct b_path *path) const struct b_directory *root, const struct b_path *path)
{ {
@@ -144,7 +246,7 @@ bool b_directory_path_is_directory(
return result; 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, const struct b_directory *root, const struct b_path *path,
struct b_file_info *out) struct b_file_info *out)
{ {
@@ -156,16 +258,16 @@ enum b_status b_directory_path_stat(
struct b_path *abs_path struct b_path *abs_path
= b_path_join(parts, sizeof parts / sizeof parts[0]); = b_path_join(parts, sizeof parts / sizeof parts[0]);
if (!abs_path) { if (!abs_path) {
return B_ERR_NO_MEMORY; return B_RESULT_ERR(NO_MEMORY);
} }
enum b_status status = b_path_stat(abs_path, out); enum b_status status = b_path_stat(abs_path, out);
b_path_release(abs_path); 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_directory *root, const struct b_path *path)
{ {
const struct b_path *parts[] = { const struct b_path *parts[] = {
@@ -176,13 +278,13 @@ enum b_status b_directory_path_unlink(
struct b_path *abs_path struct b_path *abs_path
= b_path_join(parts, sizeof parts / sizeof parts[0]); = b_path_join(parts, sizeof parts / sizeof parts[0]);
if (!abs_path) { if (!abs_path) {
return B_ERR_NO_MEMORY; return B_RESULT_ERR(NO_MEMORY);
} }
enum b_status status = b_path_unlink(abs_path); enum b_status status = b_path_unlink(abs_path);
b_path_release(abs_path); b_path_release(abs_path);
return status; return B_RESULT_STATUS(status);
} }
static int ftsent_compare(const FTSENT **one, const FTSENT **two) static int ftsent_compare(const FTSENT **one, const FTSENT **two)

View File

@@ -61,7 +61,7 @@ static unsigned int b_mode_to_unix_mode(enum b_file_mode mode)
return result; return result;
} }
enum b_status b_file_open( b_result b_file_open(
struct b_directory *root, const struct b_path *path, struct b_directory *root, const struct b_path *path,
enum b_file_mode mode, struct b_file **out) enum b_file_mode mode, struct b_file **out)
{ {
@@ -70,7 +70,7 @@ enum b_status b_file_open(
if (flags == (unsigned int)-1) { if (flags == (unsigned int)-1) {
b_path_release(file_path); b_path_release(file_path);
return B_ERR_INVALID_ARGUMENT; return B_RESULT_ERR(INVALID_ARGUMENT);
} }
struct b_path *root_path = NULL; struct b_path *root_path = NULL;
@@ -92,13 +92,14 @@ enum b_status b_file_open(
b_path_release(file_path); b_path_release(file_path);
if (!abs_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); int fd = open(b_path_ptr(abs_path), flags, 0644);
if (fd == -1) { if (fd == -1) {
b_path_release(abs_path); 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 struct b_file *file
@@ -106,7 +107,7 @@ enum b_status b_file_open(
if (!file) { if (!file) {
close(fd); close(fd);
b_path_release(abs_path); b_path_release(abs_path);
return B_ERR_NO_MEMORY; return B_RESULT_ERR(NO_MEMORY);
} }
file->fd = fd; file->fd = fd;
@@ -114,7 +115,7 @@ enum b_status b_file_open(
file->mode = mode; file->mode = mode;
*out = file; *out = file;
return B_SUCCESS; return B_RESULT_SUCCESS;
} }
static void generate_tmp_filename(char *out, size_t len) 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; 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; 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); snprintf(path, sizeof path, "/tmp/%s", name);
struct b_path *rpath = b_path_create_from_cstr(path); 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); 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); b_path_release(rpath);
continue; 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) 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; 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; 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_DIRECTORY_ROOT, shadow_filepath, mode, &shadow_file);
b_path_release(shadow_filepath); b_path_release(shadow_filepath);
if (!B_OK(status)) { if (b_result_is_error(status)) {
return status; return status;
} }
*out = shadow_file; *out = shadow_file;
return B_SUCCESS; return B_RESULT_SUCCESS;
} }
static enum b_status stream_close(struct b_stream *stream) static enum b_status stream_close(struct b_stream *stream)

View File

@@ -1,3 +1,4 @@
#include <blue/core/error.h>
#include <blue/core/status.h> #include <blue/core/status.h>
#include <blue/io/file.h> #include <blue/io/file.h>
#include <errno.h> #include <errno.h>
@@ -23,6 +24,7 @@ enum b_status b_status_from_errno(int error, enum b_status default_value)
case ENOTDIR: case ENOTDIR:
return B_ERR_NOT_DIRECTORY; return B_ERR_NOT_DIRECTORY;
case EPERM: case EPERM:
case EACCES:
return B_ERR_PERMISSION_DENIED; return B_ERR_PERMISSION_DENIED;
case ENOTSUP: case ENOTSUP:
case ENOSYS: 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) enum b_status b_file_info_from_stat(const struct stat *st, struct b_file_info *out)
{ {
out->length = st->st_size; out->length = st->st_size;

View File

@@ -1,12 +1,18 @@
#ifndef _IO_DARWIN_POSIX_H_ #ifndef _IO_DARWIN_POSIX_H_
#define _IO_DARWIN_POSIX_H_ #define _IO_DARWIN_POSIX_H_
#include <blue/core/error.h>
#include <blue/core/status.h> #include <blue/core/status.h>
struct stat; struct stat;
struct b_file_info; struct b_file_info;
extern enum b_status b_status_from_errno(int error, enum b_status default_value); 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( extern enum b_status b_file_info_from_stat(
const struct stat *in, struct b_file_info *out); const struct stat *in, struct b_file_info *out);