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
This commit is contained in:
Lephe 2022-01-06 14:05:52 +01:00
parent 9b02f5f1db
commit 3aa1471ac5
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
9 changed files with 69 additions and 64 deletions

View File

@ -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 <unistd.h> 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);

View File

@ -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) */

View File

@ -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;
}

View File

@ -1,91 +1,89 @@
#include <gint/fs.h>
#include <gint/hardware.h>
#include <gint/bfile.h>
#include <gint/defs/util.h>
#include <fcntl.h>
#include <errno.h>
#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,

View File

@ -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);

View File

@ -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,

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;