feature: implement linux file handling functions including open, close, read, write, and permissions management
This commit is contained in:
parent
710f231a47
commit
7cd3fe2905
2 changed files with 497 additions and 0 deletions
484
Source/DrangPlatform/Source/linux/fs/file.c
Normal file
484
Source/DrangPlatform/Source/linux/fs/file.c
Normal file
|
|
@ -0,0 +1,484 @@
|
|||
|
||||
#include "drang/alloc.h"
|
||||
#include "internal.h"
|
||||
#include <drang/error.h>
|
||||
#include <errno.h>
|
||||
#include <errno_convert.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
static int mode_to_open_flags(const drang_fs_mode_t mode)
|
||||
{
|
||||
int oflags = 0;
|
||||
if (mode & drang_fs_mode_read && mode & drang_fs_mode_write) {
|
||||
oflags |= O_RDWR;
|
||||
} else if (mode & drang_fs_mode_read) {
|
||||
oflags |= O_RDONLY;
|
||||
} else if (mode & drang_fs_mode_write) {
|
||||
oflags |= O_WRONLY;
|
||||
}
|
||||
|
||||
if (mode & drang_fs_mode_append) {
|
||||
oflags |= O_APPEND;
|
||||
}
|
||||
if (mode & drang_fs_mode_create) {
|
||||
oflags |= O_CREAT;
|
||||
}
|
||||
if (mode & drang_fs_mode_truncate) {
|
||||
oflags |= O_TRUNC;
|
||||
}
|
||||
if (mode & drang_fs_mode_exclusive) {
|
||||
oflags |= O_EXCL;
|
||||
}
|
||||
if (oflags == 0)
|
||||
return 0;
|
||||
oflags |= O_LARGEFILE; // Ensure large file support
|
||||
return oflags;
|
||||
}
|
||||
|
||||
static int drang_permissions_to_linux(const drang_fs_permissions_t permissions)
|
||||
{
|
||||
int result = 0;
|
||||
if (permissions & DRANG_FS_PERM_USER_READ) result |= S_IRUSR;
|
||||
if (permissions & DRANG_FS_PERM_USER_WRITE) result |= S_IWUSR;
|
||||
if (permissions & DRANG_FS_PERM_USER_EXEC) result |= S_IXUSR;
|
||||
if (permissions & DRANG_FS_PERM_GROUP_READ) result |= S_IRGRP;
|
||||
if (permissions & DRANG_FS_PERM_GROUP_WRITE) result |= S_IWGRP;
|
||||
if (permissions & DRANG_FS_PERM_GROUP_EXEC) result |= S_IXGRP;
|
||||
if (permissions & DRANG_FS_PERM_OTHER_READ) result |= S_IROTH;
|
||||
if (permissions & DRANG_FS_PERM_OTHER_WRITE) result |= S_IWOTH;
|
||||
if (permissions & DRANG_FS_PERM_OTHER_EXEC) result |= S_IXOTH;
|
||||
return result;
|
||||
}
|
||||
|
||||
int drang_fs_open(const char *path, drang_fs_mode_t mode, drang_fs_file_t **out_file)
|
||||
{
|
||||
struct drang_fs_file* file = NULL;
|
||||
DRANG_BEGIN_TRY()
|
||||
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(out_file, DRANG_EINVAL);
|
||||
|
||||
|
||||
const int oflags = mode_to_open_flags(mode);
|
||||
DRANG_CHECK(oflags != 0, DRANG_EINVAL);
|
||||
|
||||
file = DRANG_TRY_CALLOC_T(struct drang_fs_file);
|
||||
|
||||
DRANG_TRY(drang_rwlock_init(&file->lock));
|
||||
|
||||
const int fd = (oflags & O_CREAT) ? open(path, oflags, 0666) : open(path, oflags);
|
||||
if (fd == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
file->fd = fd;
|
||||
file->mode = mode;
|
||||
DRANG_RETURN_IN(out_file, file);
|
||||
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (file != NULL) {
|
||||
drang_rwlock_fini(&file->lock);
|
||||
drang_free(file);
|
||||
}
|
||||
}
|
||||
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_close(drang_fs_file_t *file)
|
||||
{
|
||||
bool locked = false;
|
||||
if (file == NULL) return DRANG_EOK;
|
||||
DRANG_BEGIN_TRY()
|
||||
|
||||
DRANG_TRY(drang_fs_flush(file));
|
||||
|
||||
DRANG_CHECK(file->fd >= 0, DRANG_EINVAL);
|
||||
|
||||
DRANG_TRY(drang_rwlock_wrlock(&file->lock));
|
||||
locked = true;
|
||||
const int result = close(file->fd);
|
||||
if (result == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
|
||||
drang_rwlock_fini(&file->lock);
|
||||
|
||||
drang_free(file);
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
// TODO: Implement buffering
|
||||
int drang_fs_read(drang_fs_file_t *file, void *buffer, size_t count, size_t size, size_t *bytes_read)
|
||||
{
|
||||
bool locked = false;
|
||||
DRANG_BEGIN_TRY()
|
||||
|
||||
DRANG_FAIL_IF_NULL(file, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(buffer, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->fd >= 0, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->mode & drang_fs_mode_read, DRANG_EPERM);
|
||||
DRANG_CHECK(count > 0 && size > 0, DRANG_EINVAL);
|
||||
|
||||
size_t total_bytes = 0;
|
||||
DRANG_CHECK(!DRANG_MUL_OVERFLOW(count, size, &total_bytes), DRANG_EOVERFLOW);
|
||||
|
||||
|
||||
DRANG_TRY(drang_rwlock_rdlock(&file->lock));
|
||||
locked = true;
|
||||
|
||||
const ssize_t res = read(file->fd, buffer, total_bytes);
|
||||
if (res == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
if (bytes_read != NULL) {
|
||||
*bytes_read = (size_t)res;
|
||||
}
|
||||
|
||||
drang_rwlock_rdunlock(&file->lock);
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_rdunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_write(
|
||||
drang_fs_file_t *file, const void *buffer, size_t count, size_t size, size_t *bytes_written)
|
||||
{
|
||||
bool locked = false;
|
||||
DRANG_BEGIN_TRY()
|
||||
|
||||
DRANG_FAIL_IF_NULL(file, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(buffer, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->fd >= 0, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->mode & (drang_fs_mode_write | drang_fs_mode_append), DRANG_EPERM);
|
||||
DRANG_CHECK(count > 0 && size > 0, DRANG_EINVAL);
|
||||
|
||||
|
||||
size_t total_bytes = 0;
|
||||
DRANG_CHECK(!DRANG_MUL_OVERFLOW(count, size, &total_bytes), DRANG_EOVERFLOW);
|
||||
DRANG_TRY(drang_rwlock_wrlock(&file->lock));
|
||||
locked = true;
|
||||
|
||||
const ssize_t res = write(file->fd, buffer, total_bytes);
|
||||
if (res == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
if (bytes_written != NULL) {
|
||||
*bytes_written = (size_t)res;
|
||||
}
|
||||
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_seek(drang_fs_file_t *file, int64_t offset, drang_seek_origin_t origin)
|
||||
{
|
||||
bool locked = false;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(file, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->fd >= 0, DRANG_EINVAL);
|
||||
int whence = 0;
|
||||
switch (origin) {
|
||||
case drang_seek_origin_begin:
|
||||
whence = SEEK_SET;
|
||||
break;
|
||||
case drang_seek_origin_current:
|
||||
whence = SEEK_CUR;
|
||||
break;
|
||||
case drang_seek_origin_end:
|
||||
whence = SEEK_END;
|
||||
break;
|
||||
default:
|
||||
DRANG_FAIL(DRANG_EINVAL);
|
||||
}
|
||||
|
||||
DRANG_TRY(drang_rwlock_wrlock(&file->lock));
|
||||
locked = true;
|
||||
const off_t res = lseek(file->fd, (off_t)offset, whence);
|
||||
if (res == (off_t)-1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_tell(drang_fs_file_t *file, uint64_t *position)
|
||||
{
|
||||
bool locked = false;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(file, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(position, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->fd >= 0, DRANG_EINVAL);
|
||||
|
||||
DRANG_TRY(drang_rwlock_rdlock(&file->lock));
|
||||
locked = true;
|
||||
const off_t res = lseek(file->fd, 0, SEEK_CUR);
|
||||
if (res == (off_t)-1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
*position = (uint64_t)res;
|
||||
|
||||
drang_rwlock_rdunlock(&file->lock);
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_rdunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
// TODO: Implement buffering
|
||||
int drang_fs_flush(drang_fs_file_t *file)
|
||||
{
|
||||
bool locked = false;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(file, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->fd >= 0, DRANG_EINVAL);
|
||||
|
||||
DRANG_TRY(drang_rwlock_wrlock(&file->lock));
|
||||
locked = true;
|
||||
const int res = fsync(file->fd);
|
||||
if (res == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_truncate(drang_fs_file_t *file, uint64_t size)
|
||||
{
|
||||
bool locked = false;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(file, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->fd >= 0, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->mode & drang_fs_mode_write, DRANG_EPERM);
|
||||
|
||||
DRANG_TRY(drang_rwlock_wrlock(&file->lock));
|
||||
locked = true;
|
||||
|
||||
const int res = ftruncate(file->fd, (off_t)size);
|
||||
if (res == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_create(const char *path, drang_fs_permissions_t permissions)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
const int linux_perms = drang_permissions_to_linux(permissions);
|
||||
const int fd = open(path, O_CREAT | O_EXCL | O_WRONLY | O_LARGEFILE, linux_perms);
|
||||
if (fd == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
const int res = close(fd);
|
||||
if (res == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
int drang_fs_remove(const char *path)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
const int res = unlink(path);
|
||||
if (res == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
int drang_fs_move(const char *old_path, const char *new_path)
|
||||
{
|
||||
int src_fd = -1;
|
||||
int dst_fd = -1;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(old_path, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(new_path, DRANG_EINVAL);
|
||||
if (rename(old_path, new_path) == 0) {
|
||||
DRANG_RETURN();
|
||||
}
|
||||
|
||||
|
||||
if (errno == EXDEV) { // Cross-device link, need to copy+delete
|
||||
struct stat src_stat = {0};
|
||||
|
||||
if (stat(old_path, &src_stat) == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
src_fd = open(old_path, O_RDONLY | O_LARGEFILE);
|
||||
if (src_fd == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
dst_fd = open(new_path, O_CREAT | O_EXCL | O_WRONLY | O_LARGEFILE, src_stat.st_mode);
|
||||
if (dst_fd == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
char buffer[DRANG_LINUX_FS_BUFFER_SIZE];
|
||||
ssize_t bytes_read = 0;
|
||||
while ((bytes_read = read(src_fd, buffer, sizeof(buffer))) > 0) {
|
||||
const ssize_t write_res = write(dst_fd, buffer, (size_t)bytes_read);
|
||||
if (write_res == -1 || write_res != bytes_read) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
}
|
||||
|
||||
if (bytes_read == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
if (close(src_fd) == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
src_fd = -1;
|
||||
|
||||
if (close(dst_fd) == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
dst_fd = -1;
|
||||
|
||||
if (unlink(old_path) == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
DRANG_RETURN();
|
||||
}
|
||||
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (src_fd != -1) {
|
||||
close(src_fd);
|
||||
}
|
||||
if (dst_fd != -1) {
|
||||
close(dst_fd);
|
||||
unlink(new_path);
|
||||
}
|
||||
}
|
||||
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
bool drang_fs_exists(const char *path)
|
||||
{
|
||||
if (path == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct stat buffer;
|
||||
return (stat(path, &buffer) == 0);
|
||||
}
|
||||
|
||||
int drang_fs_stat(const char *path, struct drang_fs_stat *out_stat)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(out_stat, DRANG_EINVAL);
|
||||
|
||||
struct stat st;
|
||||
if (stat(path, &st) == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
out_stat->type = drang_fs_type_file;
|
||||
} else if (S_ISDIR(st.st_mode)) {
|
||||
out_stat->type = drang_fs_type_directory;
|
||||
} else if (S_ISLNK(st.st_mode)) {
|
||||
out_stat->type = drang_fs_type_symlink;
|
||||
} else {
|
||||
out_stat->type = drang_fs_type_unknown;
|
||||
}
|
||||
|
||||
|
||||
out_stat->size = (uint64_t)st.st_size;
|
||||
out_stat->permissions = 0;
|
||||
if (st.st_mode & S_IRUSR) out_stat->permissions |= DRANG_FS_PERM_USER_READ;
|
||||
if (st.st_mode & S_IWUSR) out_stat->permissions |= DRANG_FS_PERM_USER_WRITE;
|
||||
if (st.st_mode & S_IXUSR) out_stat->permissions |= DRANG_FS_PERM_USER_EXEC;
|
||||
if (st.st_mode & S_IRGRP) out_stat->permissions |= DRANG_FS_PERM_GROUP_READ;
|
||||
if (st.st_mode & S_IWGRP) out_stat->permissions |= DRANG_FS_PERM_GROUP_WRITE;
|
||||
if (st.st_mode & S_IXGRP) out_stat->permissions |= DRANG_FS_PERM_GROUP_EXEC;
|
||||
if (st.st_mode & S_IROTH) out_stat->permissions |= DRANG_FS_PERM_OTHER_READ;
|
||||
if (st.st_mode & S_IWOTH) out_stat->permissions |= DRANG_FS_PERM_OTHER_WRITE;
|
||||
if (st.st_mode & S_IXOTH) out_stat->permissions |= DRANG_FS_PERM_OTHER_EXEC;
|
||||
|
||||
out_stat->created_time = (drang_fs_time_t)st.st_ctim.tv_sec * 1000000000 + st.st_ctim.tv_nsec;
|
||||
out_stat->modified_time = (drang_fs_time_t)st.st_mtim.tv_sec * 1000000000 + st.st_mtim.tv_nsec;
|
||||
out_stat->accessed_time = (drang_fs_time_t)st.st_atim.tv_sec * 1000000000 + st.st_atim.tv_nsec;
|
||||
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
int drang_fs_set_permissions(const char *path, drang_fs_permissions_t permissions)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
const int linux_perms = drang_permissions_to_linux(permissions);
|
||||
if (chmod(path, linux_perms) == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
13
Source/DrangPlatform/Source/linux/fs/internal.h
Normal file
13
Source/DrangPlatform/Source/linux/fs/internal.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
#include <drang/fs.h>
|
||||
#include <unistd.h>
|
||||
#include "linux/sync/internal.h"
|
||||
|
||||
#define DRANG_LINUX_FS_BUFFER_SIZE (4096)
|
||||
|
||||
struct drang_fs_file
|
||||
{
|
||||
int fd;
|
||||
drang_fs_mode_t mode;
|
||||
struct drang_rwlock lock;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue