From 8635880bbbb12735a30516d6895cfeaa7b2a400f Mon Sep 17 00:00:00 2001 From: Lephe Date: Fri, 10 Dec 2021 07:25:00 +0100 Subject: [PATCH] fs: basic filesystem support over BFile --- CMakeLists.txt | 30 ++++++++++++++++ src/fs/close.c | 14 ++++++++ src/fs/creat.c | 6 ++++ src/fs/lseek.c | 37 +++++++++++++++++++ src/fs/open.c | 74 ++++++++++++++++++++++++++++++++++++++ src/fs/pread.c | 17 +++++++++ src/fs/pwrite.c | 28 +++++++++++++++ src/fs/read.c | 14 ++++++++ src/fs/unlink.c | 21 +++++++++++ src/fs/util.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++ src/fs/util.h | 41 +++++++++++++++++++++ src/fs/write.c | 14 ++++++++ 12 files changed, 390 insertions(+) create mode 100644 src/fs/close.c create mode 100644 src/fs/creat.c create mode 100644 src/fs/lseek.c create mode 100644 src/fs/open.c create mode 100644 src/fs/pread.c create mode 100644 src/fs/pwrite.c create mode 100644 src/fs/read.c create mode 100644 src/fs/unlink.c create mode 100644 src/fs/util.c create mode 100644 src/fs/util.h create mode 100644 src/fs/write.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 86cc4aa..5781a11 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,18 +22,34 @@ endif() configure_file(include/gint/config.h.in include/gint/config.h) set(SOURCES_COMMON + # Clock Pulse Generator driver src/cpg/cpg.c + # CPU driver src/cpu/atomic.c src/cpu/cpu.c src/cpu/ics.s src/cpu/registers.s src/cpu/sleep.c + # Direct Memory Access driver src/dma/dma.c src/dma/inth.s src/dma/memcpy.c src/dma/memset.c + # Filesystem interface (Fugue only) + src/fs/close.c + src/fs/creat.c + src/fs/lseek.c + src/fs/open.c + src/fs/pread.c + src/fs/pwrite.c + src/fs/read.c + src/fs/unlink.c + src/fs/util.c + src/fs/write.c + # Interrupt Controller driver src/intc/intc.c src/intc/inth.s + # Kernel src/kernel/exch.c src/kernel/exch.s src/kernel/hardware.c @@ -44,15 +60,19 @@ set(SOURCES_COMMON src/kernel/syscalls.S src/kernel/tlbh.S src/kernel/world.c + # Key Scan Interface driver src/keysc/getkey.c src/keysc/iokbd.c src/keysc/keycodes.c src/keysc/keydev.c src/keysc/keysc.c + # Memory allocator src/kmalloc/arena_gint.c src/kmalloc/arena_osheap.c src/kmalloc/kmalloc.c + # MMU driver src/mmu/mmu.c + # Rendering src/render/dhline.c src/render/dimage.c src/render/dline.c @@ -62,13 +82,17 @@ set(SOURCES_COMMON src/render/dupdate_hook.c src/render/dvline.c src/render/topti.c + # RTC driver src/rtc/rtc.c src/rtc/rtc_ticks.c + # Sound Processing Unit driver src/spu/spu.c + # Timer Unit driver src/tmu/inth-etmu.s src/tmu/inth-tmu.s src/tmu/sleep.c src/tmu/tmu.c + # USB driver src/usb/classes/ff-bulk.c src/usb/configure.c src/usb/pipes.c @@ -77,6 +101,7 @@ set(SOURCES_COMMON src/usb/usb.c ) set(SOURCES_FX + # Gray engine src/gray/engine.c src/gray/gclear.c src/gray/gint_gline.c @@ -84,6 +109,7 @@ set(SOURCES_FX src/gray/grect.c src/gray/gsubimage.c src/gray/gtext.c + # Rendering src/render-fx/bopti-asm-gray-scsp.s src/render-fx/bopti-asm-gray.s src/render-fx/bopti-asm-mono-scsp.s @@ -98,11 +124,15 @@ set(SOURCES_FX src/render-fx/masks.c src/render-fx/topti-asm.s src/render-fx/topti.c + # T6K11 driver src/t6k11/t6k11.c + src/usb/classes/ff-bulk-gray.c ) set(SOURCES_CG + # R61524 driver src/r61524/r61524.c + # Rendering src/render-cg/bopti-asm.s src/render-cg/bopti.c src/render-cg/dclear.c diff --git a/src/fs/close.c b/src/fs/close.c new file mode 100644 index 0000000..fe0e584 --- /dev/null +++ b/src/fs/close.c @@ -0,0 +1,14 @@ +#include +#include "util.h" + +int close(int fd) +{ + ENOTSUP_IF_NOT_FUGUE(-1); + + int err = BFile_Close(fd); + if(err < 0) { + errno = bfile_error_to_errno(err); + return -1; + } + return 0; +} diff --git a/src/fs/creat.c b/src/fs/creat.c new file mode 100644 index 0000000..c9e637c --- /dev/null +++ b/src/fs/creat.c @@ -0,0 +1,6 @@ +#include + +int creat(char const *path, mode_t mode) +{ + return open(path, O_CREAT | O_WRONLY | O_TRUNC, mode); +} diff --git a/src/fs/lseek.c b/src/fs/lseek.c new file mode 100644 index 0000000..304c79f --- /dev/null +++ b/src/fs/lseek.c @@ -0,0 +1,37 @@ +#include +#include "util.h" + +off_t lseek(int fd, off_t offset, int whence) +{ + ENOTSUP_IF_NOT_FUGUE((off_t)-1); + + if(whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) { + errno = EINVAL; + return (off_t)-1; + } + + /* On Graph 35+E II, there is no documented way to know the offset. */ + if(gint[HWCALC] == HWCALC_G35PE2 && whence == SEEK_CUR) { + errno = ENOTSUP; + return (off_t)-1; + } + + off_t destination; + if(whence == SEEK_SET) + destination = offset; + else if(whence == SEEK_CUR) + destination = BFile_GetPos(fd) + offset; + else if(whence == SEEK_END) + destination = BFile_Size(fd) - offset; + + int rc = BFile_Seek(fd, destination); + + if(rc < 0) { + errno = bfile_error_to_errno(rc); + return -1; + } + + /* rc is the amount of space left in the file (including pre-allocated + space), so instead just return destination directly */ + return (off_t)destination; +} diff --git a/src/fs/open.c b/src/fs/open.c new file mode 100644 index 0000000..ea438f7 --- /dev/null +++ b/src/fs/open.c @@ -0,0 +1,74 @@ +#include +#include "util.h" + +int open(char const *path, int flags, ...) +{ + ENOTSUP_IF_NOT_FUGUE(-1); + + uint16_t *fcpath = utf8_to_fc_alloc(path, u"\\\\fls0\\"); + int fd, err, rc = -1; + + if(!fcpath) { + errno = ENOMEM; + return -1; + } + + /* Open mode */ + int bfile_mode = BFile_ReadOnly; + if(flags & O_WRONLY) + bfile_mode = BFile_WriteOnly; + else if(flags & O_RDWR) + bfile_mode = BFile_ReadWrite; + + /* Exclusive creation requires the file to be created by the call */ + bool excl = (flags & O_EXCL) && (flags & O_CREAT); + /* Truncation requires the file to be removed/recreated */ + bool trunc = (flags & O_TRUNC) && (flags & O_CREAT); + + /* Try and open the file normally, unless O_TRUNC is specified without + O_EXCL, in which case we simply delete and recreate the file. */ + fd = BFile_EntryNotFound; + if(excl || !trunc) + fd = BFile_Open(fcpath, bfile_mode); + + /* If the file exists and O_EXCL was requested, fail. */ + if(fd >= 0 && excl) { + BFile_Close(fd); + errno = EEXIST; + return -1; + } + + /* If O_TRUNC is requested and either the file exists or O_CREAT is + set, temporarily remove it. */ + if((flags & O_TRUNC) && (fd >= 0 || (flags & O_CREAT))) { + if(fd >= 0) BFile_Close(fd); + BFile_Remove(fcpath); + fd = BFile_EntryNotFound; + } + + /* If the file does not exist and O_CREAT is set, create it */ + if((flags & O_CREAT) && ((flags & O_TRUNC) || fd < 0)) { + int size = 0; + err = BFile_Create(fcpath, BFile_File, &size); + if(err < 0) { + errno = bfile_error_to_errno(err); + goto end; + } + fd = BFile_Open(fcpath, bfile_mode); + } + + if(fd < 0) { + errno = bfile_error_to_errno(fd); + goto end; + } + + /* If O_APPEND is set, move to the end of the file */ + if((flags & O_APPEND)) + BFile_Seek(fd, BFile_Size(fd)); + + /* Return the now-open file descriptor */ + rc = fd; +end: + free(fcpath); + return rc; +} diff --git a/src/fs/pread.c b/src/fs/pread.c new file mode 100644 index 0000000..c6ce809 --- /dev/null +++ b/src/fs/pread.c @@ -0,0 +1,17 @@ +#include +#include "util.h" + +ssize_t pread(int fd, void *buf, size_t size, off_t offset) +{ + ENOTSUP_IF_NOT_FUGUE(-1); + + /* Thanks to the extra argument to BFile_Read(), we can perform this + call without knowing the current offset, even on G-III models */ + int rc = BFile_Read(fd, buf, size, offset); + if(rc < 0) { + errno = bfile_error_to_errno(rc); + return -1; + } + BFile_Seek(fd, -size); + return rc; +} diff --git a/src/fs/pwrite.c b/src/fs/pwrite.c new file mode 100644 index 0000000..d3b0e33 --- /dev/null +++ b/src/fs/pwrite.c @@ -0,0 +1,28 @@ +#include +#include "util.h" + +ssize_t pwrite(int fd, const void *buf, size_t size, off_t offset) +{ + ENOTSUP_IF_NOT_FUGUE((off_t)-1); + + off_t current = lseek(fd, 0, SEEK_CUR); + + if(current == (off_t)-1) + return (ssize_t)-1; + + ssize_t rc = -1; + + if(lseek(fd, 0, offset) == (off_t)-1) + goto end; + + rc = BFile_Write(fd, buf, size); + if(rc < 0) { + errno = bfile_error_to_errno(rc); + goto end; + } + +end: + /* At the end, always try to restore the current position */ + lseek(fd, current, SEEK_CUR); + return rc; +} diff --git a/src/fs/read.c b/src/fs/read.c new file mode 100644 index 0000000..20a0332 --- /dev/null +++ b/src/fs/read.c @@ -0,0 +1,14 @@ +#include +#include "util.h" + +ssize_t read(int fd, void *buf, size_t size) +{ + ENOTSUP_IF_NOT_FUGUE(-1); + + int err = BFile_Read(fd, buf, size, -1); + if(err < 0) { + errno = bfile_error_to_errno(err); + return -1; + } + return size; +} diff --git a/src/fs/unlink.c b/src/fs/unlink.c new file mode 100644 index 0000000..ae00609 --- /dev/null +++ b/src/fs/unlink.c @@ -0,0 +1,21 @@ +#include +#include "util.h" + +int unlink(char const *path) +{ + ENOTSUP_IF_NOT_FUGUE(-1); + + uint16_t *fcpath = utf8_to_fc_alloc(path, u"\\\\fls0\\"); + if(!fcpath) { + errno = ENOMEM; + return -1; + } + + int err = BFile_Remove(fcpath); + if(err < 0) { + errno = bfile_error_to_errno(err); + return -1; + } + + return 0; +} diff --git a/src/fs/util.c b/src/fs/util.c new file mode 100644 index 0000000..07890ac --- /dev/null +++ b/src/fs/util.c @@ -0,0 +1,94 @@ +#include "util.h" +#include +#include + +int bfile_error_to_errno(int e) +{ + /* TODO: Find BFile code for too many fds and map it to ENFILE. */ + switch(e) { + case BFile_EntryNotFound: return ENOENT; + case BFile_IllegalPath: return EINVAL; + case BFile_DeviceFull: return EDQUOT; + case BFile_IllegalDevice: return EINVAL; + case BFile_AccessDenied: return EACCES; + case BFile_PermissionError: return EACCES; + case BFile_EntryFull: return EDQUOT; + case BFile_AlreadyExists: return EEXIST; + case BFile_ReadOnlyFile: return EACCES; + case BFile_EnumerateEnd: return ENOENT; + case BFile_IllegalSeekPos: return EINVAL; + case BFile_NotMountDevice: return ENOENT; + case BFile_DeviceNotFound: return ENOENT; + default: return errno; + } +} + +/* Length of FONTCHARACTER and UTF-8 strings, counting only ASCII characters */ +static size_t utf8_len(char const *utf8) +{ + size_t len = 0; + for(size_t i = 0; utf8[i] != 0; i++) + len += (utf8[i] >= 0 && (uint8_t)utf8[i] <= 0x7f); + return len; +} +static size_t fc_len(uint16_t const *fc) +{ + size_t len = 0; + for(size_t i = 0; fc[i] != 0 && fc[i] != 0xffff; i++) + len += (fc[i] <= 0x7f); + return len; +} + +void utf8_to_fc(uint16_t *fc, char const *utf8, size_t fc_len) +{ + size_t j = 0; + + for(size_t i = 0; j < fc_len && utf8[i] != 0; i++) { + if(utf8[i] >= 0 && (uint8_t)utf8[i] <= 0x7f) + fc[j++] = utf8[i]; + } + + while(j < fc_len) + fc[j++] = 0; +} + +void fc_to_utf8(char *utf8, uint16_t const *fc, size_t utf8_len) +{ + size_t j = 0; + + for(size_t i = 0; j < utf8_len && fc[i] != 0 && fc[i] != 0xffff; i++) { + if(fc[i] <= 0x7f) + utf8[j++] = fc[i]; + } + + while(j < utf8_len) + utf8[j++] = 0; +} + +uint16_t *utf8_to_fc_alloc(char const *utf8, uint16_t *prefix) +{ + size_t lenp = 0; + if(prefix) { + while(prefix[lenp] != 0 && prefix[lenp] != 0xffff) + lenp++; + } + + size_t len = utf8_len(utf8); + uint16_t *fc = malloc((lenp+len+1) * sizeof *fc); + + if(fc != NULL) { + if(prefix) + memcpy(fc, prefix, lenp * sizeof *prefix); + utf8_to_fc(fc + lenp, utf8, len+1); + } + return fc; +} + +char *fc_to_utf8_alloc(uint16_t const *fc) +{ + size_t len = fc_len(fc); + char *utf8 = malloc(len+1); + if(utf8 != NULL) + fc_to_utf8(utf8, fc, len+1); + return utf8; +} diff --git a/src/fs/util.h b/src/fs/util.h new file mode 100644 index 0000000..64fce5c --- /dev/null +++ b/src/fs/util.h @@ -0,0 +1,41 @@ +#ifndef FS_UTIL_H +#define FS_UTIL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include + +#define ENOTSUP_IF_NOT_FUGUE(rc) \ + if(gint[HWFS] != HWFS_FUGUE) { errno = ENOTSUP; return rc; } + +/* Translate common BFile error codes to errno values. */ +extern int bfile_error_to_errno(int bfile_error_code); + +/* TODO: These functions do not actually translate special characters between + encodings, they simply strip them. */ + +/* Convert UTF-8 to FONTCHARACTER; outputs fc_len characters with padding. If + fc[fc_len-1] is not 0 after the call, then fc is too short. */ +extern void utf8_to_fc(uint16_t *fc, char const *utf8, size_t fc_len); + +/* Same in the other direction. */ +extern void fc_to_utf8(char *utf8, uint16_t const *fc, size_t utf8_len); + +/* Same as utf8_to_fc() but allocates a string with malloc(). */ +extern uint16_t *utf8_to_fc_alloc(char const *utf8, uint16_t *prefix); + +/* Same as fc_to_utf8() but allocates a string with malloc(). */ +extern char *fc_to_utf8_alloc(uint16_t const *fc); + +#ifdef __cplusplus +} +#endif + +#endif /* FS_UTIL_H */ diff --git a/src/fs/write.c b/src/fs/write.c new file mode 100644 index 0000000..3f4a3ad --- /dev/null +++ b/src/fs/write.c @@ -0,0 +1,14 @@ +#include +#include "util.h" + +ssize_t write(int fd, const void *buf, size_t size) +{ + ENOTSUP_IF_NOT_FUGUE(-1); + + int rc = BFile_Write(fd, buf, size); + if(rc < 0) { + errno = bfile_error_to_errno(rc); + return -1; + } + return rc; +}