From 3aa1471ac55a437ca56919a67865cfd72d20caad Mon Sep 17 00:00:00 2001 From: Lephe Date: Thu, 6 Jan 2022 14:05:52 +0100 Subject: [PATCH] fs: track offset of Fugue files manually This helped locate some bugs: * read() could read past EOF due to BFile_Read() allowing you to read up until the end of the last sector, beyond the file size * pread() did not restore the file offset because the negative seek at the end is not relative (that was the CASIOWIN fs API), so pread() could not actually be written without knowing the current position * lseek() would clamp you to EOF but still return its out-of-bounds arguments, as a direct result of BFile_Seek() doing that Benefits: * Made pread() a generic function --- include/gint/fs.h | 9 +----- include/gint/rtc.h | 6 +++- src/fs/fs.c | 2 -- src/fs/fugue/fugue.c | 62 +++++++++++++++++++-------------------- src/fs/fugue/fugue.h | 7 +++++ src/fs/fugue/fugue_dir.c | 8 ----- src/fs/fugue/fugue_open.c | 15 ++++++++-- src/fs/pread.c | 22 +++++++++----- src/fs/pwrite.c | 2 -- 9 files changed, 69 insertions(+), 64 deletions(-) diff --git a/include/gint/fs.h b/include/gint/fs.h index 77cec45..6f553ba 100644 --- a/include/gint/fs.h +++ b/include/gint/fs.h @@ -20,17 +20,10 @@ extern "C" { This structure provides the base functions for every type of file descriptor. The prototypes are standard, except that the first argument (int fd) is replaced by (void *data) which points to the custom data - allocator for the descriptor. - - pread() is normally implemented in terms of lseek() and read(), however on - Fugue file descriptors on the G-III series there is no way to determine the - current position so this method won't work, and pread() is instead provided - by a non-standard parameter of BFile_Read(). The overloaded entry supports - this alternative. */ + allocator for the descriptor. */ typedef struct { /* See for a description of these functions */ ssize_t (*read)(void *data, void *buf, size_t size); - ssize_t (*pread)(void *data, void *buf, size_t size, off_t offset); ssize_t (*write)(void *data, void const *buf, size_t size); off_t (*lseek)(void *data, off_t offset, int whence); int (*close)(void *data); diff --git a/include/gint/rtc.h b/include/gint/rtc.h index 7ea8162..f1bd374 100644 --- a/include/gint/rtc.h +++ b/include/gint/rtc.h @@ -17,7 +17,11 @@ extern "C" { // Time management //--- -/* rtc_time_t: A point in time, representable in the RTC registers */ +/* rtc_time_t: A point in time, representable in the RTC registers + + WARNING: A copy of this definition is used in fxlibc, make sure to keep it + in sync. (We don't install kernel headers before compiling the fxlibc, it's + kind of a nightmare for the modest build system.) */ typedef struct { uint16_t year; /* Years (exact value, e.g. 2018) */ diff --git a/src/fs/fs.c b/src/fs/fs.c index 939ff03..57ca51b 100644 --- a/src/fs/fs.c +++ b/src/fs/fs.c @@ -76,7 +76,6 @@ int open_generic(fs_descriptor_type_t *type, void *data, int fd) static fs_descriptor_type_t devnull = { .read = NULL, - .pread = NULL, .write = NULL, .lseek = NULL, .close = NULL, @@ -92,5 +91,4 @@ GCONSTRUCTOR static void init_standard_streams(void) fdtable[STDERR_FILENO].type = &devnull; fdtable[STDERR_FILENO].data = NULL; - } diff --git a/src/fs/fugue/fugue.c b/src/fs/fugue/fugue.c index 3795968..c7d1cd8 100644 --- a/src/fs/fugue/fugue.c +++ b/src/fs/fugue/fugue.c @@ -1,91 +1,89 @@ #include #include #include +#include #include #include +#include "fugue.h" #include "util.h" -ssize_t fugue_read(void *data, void *buf, size_t size) +ssize_t fugue_read(void *data0, void *buf, size_t size) { - int fugue_fd = (int)data; + fugue_fd_t *data = data0; + int fugue_fd = data->fd; + + /* Fugue allows to read past EOF up to the end of the sector */ + int filesize = BFile_Size(fugue_fd); + if(data->pos + (int)size > filesize) + size = filesize - data->pos; int rc = BFile_Read(fugue_fd, buf, size, -1); if(rc < 0) { errno = bfile_error_to_errno(rc); return -1; } + data->pos += rc; return size; } -ssize_t fugue_pread(void *data, void *buf, size_t size, off_t offset) +ssize_t fugue_write(void *data0, const void *buf, size_t size) { - int fugue_fd = (int)data; - - /* 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(fugue_fd, buf, size, offset); - if(rc < 0) { - errno = bfile_error_to_errno(rc); - return -1; - } - BFile_Seek(fugue_fd, -size); - return rc; -} - -ssize_t fugue_write(void *data, const void *buf, size_t size) -{ - int fugue_fd = (int)data; + fugue_fd_t *data = data0; + int fugue_fd = data->fd; int rc = BFile_Write(fugue_fd, buf, size); if(rc < 0) { errno = bfile_error_to_errno(rc); return -1; } + data->pos += rc; return rc; } -off_t fugue_lseek(void *data, off_t offset, int whence) +off_t fugue_lseek(void *data0, off_t offset, int whence) { - int fugue_fd = (int)data; + fugue_fd_t *data = data0; + int fugue_fd = data->fd; - /* 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; - } + int filesize = BFile_Size(fugue_fd); if(whence == SEEK_CUR) - offset += BFile_GetPos(fugue_fd); + offset += data->pos; else if(whence == SEEK_END) - offset += BFile_Size(fugue_fd); + offset += filesize; + + /* BFile_Seek() clamps to the file size, but still returns the argument + when it does... */ + offset = min(offset, filesize); int rc = BFile_Seek(fugue_fd, offset); - if(rc < 0) { errno = bfile_error_to_errno(rc); return -1; } + data->pos = offset; /* rc is the amount of space left in the file (including pre-allocated space), so instead just return offset directly */ return offset; } -int fugue_close(void *data) +int fugue_close(void *data0) { - int fugue_fd = (int)data; + fugue_fd_t *data = data0; + int fugue_fd = data->fd; int rc = BFile_Close(fugue_fd); if(rc < 0) { errno = bfile_error_to_errno(rc); return -1; } + free(data); return 0; } const fs_descriptor_type_t fugue_descriptor_type = { .read = fugue_read, - .pread = fugue_pread, .write = fugue_write, .lseek = fugue_lseek, .close = fugue_close, diff --git a/src/fs/fugue/fugue.h b/src/fs/fugue/fugue.h index 3848779..1a4d4d8 100644 --- a/src/fs/fugue/fugue.h +++ b/src/fs/fugue/fugue.h @@ -12,6 +12,13 @@ extern const fs_descriptor_type_t fugue_descriptor_type; /* Directory descriptor type */ extern const fs_descriptor_type_t fugue_dir_descriptor_type; +/* Since on Graph 35+E II / fx-9860G III there is no (known) syscall to update + the file position, we need to track it ourselves, hopefully faithfully. */ +typedef struct { + int fd; + int pos; +} fugue_fd_t; + /* Specific implementations of some standard functions */ int fugue_open(char const *path, int flags, mode_t mode); diff --git a/src/fs/fugue/fugue_dir.c b/src/fs/fugue/fugue_dir.c index 4d89296..69c9467 100644 --- a/src/fs/fugue/fugue_dir.c +++ b/src/fs/fugue/fugue_dir.c @@ -41,13 +41,6 @@ ssize_t fugue_dir_read(void *data, void *buf, GUNUSED size_t size) return sizeof *dirent_ptr; } -ssize_t fugue_dir_pread(GUNUSED void *data, GUNUSED void *buf, - GUNUSED size_t size, GUNUSED off_t offset) -{ - errno = EISDIR; - return -1; -} - ssize_t fugue_dir_write(GUNUSED void *data, GUNUSED const void *buf, GUNUSED size_t size) { @@ -90,7 +83,6 @@ int fugue_dir_close(void *data) const fs_descriptor_type_t fugue_dir_descriptor_type = { .read = fugue_dir_read, - .pread = fugue_dir_pread, .write = fugue_dir_write, .lseek = fugue_dir_lseek, .close = fugue_dir_close, diff --git a/src/fs/fugue/fugue_open.c b/src/fs/fugue/fugue_open.c index aa1cdc7..e1c4684 100644 --- a/src/fs/fugue/fugue_open.c +++ b/src/fs/fugue/fugue_open.c @@ -113,14 +113,23 @@ int fugue_open(char const *path, int flags, GUNUSED mode_t mode) BFile_Seek(fugue_fd, BFile_Size(fugue_fd)); /* Return the now-open file descriptor */ - fs_descriptor_t data = { + fugue_fd_t *data = malloc(sizeof *data); + if(!data) { + BFile_Close(fugue_fd); + goto end; + } + data->fd = fugue_fd; + data->pos = 0; + + fs_descriptor_t fd_data = { .type = &fugue_descriptor_type, - .data = (void *)fugue_fd, + .data = data, }; - rc = fd = fs_create_descriptor(&data); + rc = fd = fs_create_descriptor(&fd_data); if(fd == -1) { BFile_Close(fugue_fd); + free(data); errno = ENFILE; goto end; } diff --git a/src/fs/pread.c b/src/fs/pread.c index 42aa7d6..d06f777 100644 --- a/src/fs/pread.c +++ b/src/fs/pread.c @@ -4,15 +4,21 @@ ssize_t pread(int fd, void *buf, size_t size, off_t offset) { - fs_descriptor_t const *d = fs_get_descriptor(fd); - if(!d) { - errno = EBADF; + off_t current = lseek(fd, 0, SEEK_CUR); + if(current == (off_t)-1) return (ssize_t)-1; - } - if(d->type->pread) - return d->type->pread(d->data, buf, size, offset); + ssize_t rc = -1; - /* No seek function: cannot seek */ - return 0; + if(lseek(fd, offset, SEEK_SET) == (off_t)-1) + goto end; + + rc = read(fd, buf, size); + if(rc < 0) + 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/pwrite.c b/src/fs/pwrite.c index bcbcb5f..10b8ff7 100644 --- a/src/fs/pwrite.c +++ b/src/fs/pwrite.c @@ -3,8 +3,6 @@ ssize_t pwrite(int fd, const void *buf, size_t size, off_t offset) { off_t current = lseek(fd, 0, SEEK_CUR); - - /* This fails on G-III BFile and non-seekable file descriptors */ if(current == (off_t)-1) return (ssize_t)-1;