From 6903bd58d5b265afdd767a21f45e35c837836219 Mon Sep 17 00:00:00 2001 From: Lephe Date: Mon, 13 Dec 2021 18:38:47 +0100 Subject: [PATCH] fs: cast BFile support into generic file descriptors This paves the way for standard streams, USB streams, and some more. --- CMakeLists.txt | 7 +- include/gint/bfile.h | 5 + include/gint/fs.h | 94 +++++++++++++++++ src/fs/close.c | 21 ++-- src/fs/fs.c | 96 +++++++++++++++++ src/fs/fugue/fugue.c | 209 ++++++++++++++++++++++++++++++++++++++ src/fs/fugue/fugue.h | 13 +++ src/fs/{ => fugue}/util.c | 2 + src/fs/{ => fugue}/util.h | 6 -- src/fs/lseek.c | 39 ++----- src/fs/open.c | 76 ++------------ src/fs/pread.c | 23 +++-- src/fs/pwrite.c | 12 +-- src/fs/read.c | 20 ++-- src/fs/unlink.c | 19 +--- src/fs/write.c | 20 ++-- 16 files changed, 498 insertions(+), 164 deletions(-) create mode 100644 include/gint/fs.h create mode 100644 src/fs/fs.c create mode 100644 src/fs/fugue/fugue.c create mode 100644 src/fs/fugue/fugue.h rename src/fs/{ => fugue}/util.c (98%) rename src/fs/{ => fugue}/util.h (85%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5781a11..c7b88d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,17 +35,20 @@ set(SOURCES_COMMON src/dma/inth.s src/dma/memcpy.c src/dma/memset.c - # Filesystem interface (Fugue only) + # Filesystem interface src/fs/close.c src/fs/creat.c + src/fs/fs.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 + # Filesystem interface to Fugue + src/fs/fugue/fugue.c + src/fs/fugue/util.c # Interrupt Controller driver src/intc/intc.c src/intc/inth.s diff --git a/include/gint/bfile.h b/include/gint/bfile.h index 0a8ab1f..a6acd53 100644 --- a/include/gint/bfile.h +++ b/include/gint/bfile.h @@ -129,6 +129,11 @@ int BFile_Write(int fd, void const *data, int even_size); * If [whence >= 0], it is taken as an absolute location within the file; * If [whence == -1], BFile_Read() reads from the current position. + With Fugue this function can read past end of file and return the requested + amount of bytes even when the file does not have enough data to read that + much. It seems that extra bytes read as zeros. Reading past the end does + *not* extend the file size. + With CASIOWIN, returns how much data can be read from the updated file offset (ie. how many bytes until end of file), or an error code. With Fugue, returns the amount of data read (or an error code). */ diff --git a/include/gint/fs.h b/include/gint/fs.h new file mode 100644 index 0000000..f427b5c --- /dev/null +++ b/include/gint/fs.h @@ -0,0 +1,94 @@ +//--- +// gint:fs - Filesystem abstraction +//--- + +#ifndef GINT_FS +#define GINT_FS + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* Maximum number of file descriptors */ +#define FS_FD_MAX 32 + +/* fs_descriptor_type_t: Overloaded file descriptor functions + + 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. */ +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); + +} fs_descriptor_type_t; + +/* fs_descriptor_t: File descriptor information + This internal type describes the entries of the descriptor table. */ +typedef struct { + /* Interface functions */ + fs_descriptor_type_t const *type; + /* Custom data (can also be an integer cast to (void *)) */ + void *data; + +} fs_descriptor_t; + +/* fs_get_descriptor(): Get a file descriptor's data + + This function is used internally in order to implement read(), write(), and + other standard functions functions. It could be useful for other APIs that + want to present their resources as file descriptors but still implement + extra non-standard functions; this allows them to use the file descriptor as + input and still access the custom data. + + Returns NULL if there is no file descriptor with this number, in which case + the caller probably needs to set errno = EBADF. */ +fs_descriptor_t const *fs_get_descriptor(int fd); + +/* fs_create_descriptor(): Create a new file descriptor + + This function is used in open() and its variants to allocate new file + descriptors. The descriptor's data is created with a copy of the provided + structure, which must include a non-NULL type attribute. + + This function always returns the smallest file descriptor that is unused by + the application and is not 0, 1 or 2; unless it runs out of file + descriptors, in which case it returns -1 and the caller might want to set + errno = ENFILE. */ +int fs_create_descriptor(fs_descriptor_t const *data); + +/* fs_free_descriptor(): Clsoe a file descriptor's slot + + This function frees the specified file descriptor. It is automatically + called by close() after the descriptor type's close() function, so there is + normally no need to call this function directly. */ +void fs_free_descriptor(int fd); + +/* open_generic(): Open a file descriptor using custom file functions + + Opens a new file descriptor of the specified type with the provided user + data. If reuse_fd < 0, a new file descriptor is allocated, otherwise the + exact file descriptor reuse_fd is used. (This is useful to reopen standard + streams.) In this case, the only possible return values are -1 and reuse_fd + itself. */ +int open_generic(fs_descriptor_type_t *type, void *data, int reuse_fd); + +#ifdef __cplusplus +} +#endif + +#endif /* GINT_FS */ diff --git a/src/fs/close.c b/src/fs/close.c index fe0e584..88c96bf 100644 --- a/src/fs/close.c +++ b/src/fs/close.c @@ -1,14 +1,19 @@ #include -#include "util.h" +#include +#include 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; + fs_descriptor_t const *d = fs_get_descriptor(fd); + if(!d) { + errno = EBADF; + return (ssize_t)-1; } - return 0; + + int rc = 0; + if(d->type->close) + rc = d->type->close(d->data); + + fs_free_descriptor(fd); + return rc; } diff --git a/src/fs/fs.c b/src/fs/fs.c new file mode 100644 index 0000000..939ff03 --- /dev/null +++ b/src/fs/fs.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include + +/* File descriptor table */ +static fs_descriptor_t fdtable[FS_FD_MAX] = { 0 }; + +fs_descriptor_t const *fs_get_descriptor(int fd) +{ + if((unsigned)fd >= FS_FD_MAX) + return NULL; + if(fdtable[fd].type == NULL) + return NULL; + + return &fdtable[fd]; +} + +int fs_create_descriptor(fs_descriptor_t const *data) +{ + if(data->type == NULL) + return -1; + + /* Leave 0/1/2 for stdin, stdout and stderr */ + for(int i = 3; i < FS_FD_MAX; i++) { + if(fdtable[i].type == NULL) { + fdtable[i] = *data; + return i; + } + } + + return -1; +} + +void fs_free_descriptor(int fd) +{ + if((unsigned)fd >= FS_FD_MAX) + return; + + fdtable[fd].type = NULL; + fdtable[fd].data = NULL; +} + +int open_generic(fs_descriptor_type_t *type, void *data, int fd) +{ + if(!type) { + errno = EINVAL; + return -1; + } + fs_descriptor_t d = { + .type = type, + .data = data + }; + + /* Re-use file descriptor mode */ + if(fd >= 0) { + if(fd >= FS_FD_MAX) { + errno = EBADF; + return -1; + } + if(fdtable[fd].type) { + errno = ENFILE; + return -1; + } + + fdtable[fd] = d; + return fd; + } + /* Normal allocation mode */ + else { + return fs_create_descriptor(&d); + } +} + +/* Standard streams */ + +static fs_descriptor_type_t devnull = { + .read = NULL, + .pread = NULL, + .write = NULL, + .lseek = NULL, + .close = NULL, +}; + +GCONSTRUCTOR static void init_standard_streams(void) +{ + fdtable[STDIN_FILENO].type = &devnull; + fdtable[STDIN_FILENO].data = NULL; + + fdtable[STDOUT_FILENO].type = &devnull; + fdtable[STDOUT_FILENO].data = NULL; + + fdtable[STDERR_FILENO].type = &devnull; + fdtable[STDERR_FILENO].data = NULL; + +} diff --git a/src/fs/fugue/fugue.c b/src/fs/fugue/fugue.c new file mode 100644 index 0000000..84fb13f --- /dev/null +++ b/src/fs/fugue/fugue.c @@ -0,0 +1,209 @@ +#include +#include +#include +#include +#include +#include "util.h" + +ssize_t fugue_read(void *data, void *buf, size_t size) +{ + int fugue_fd = (int)data; + + int rc = BFile_Read(fugue_fd, buf, size, -1); + if(rc < 0) { + errno = bfile_error_to_errno(rc); + return -1; + } + return size; +} + +ssize_t fugue_pread(void *data, void *buf, size_t size, off_t offset) +{ + 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; + + int rc = BFile_Write(fugue_fd, buf, size); + if(rc < 0) { + errno = bfile_error_to_errno(rc); + return -1; + } + return rc; +} + +off_t fugue_lseek(void *data, off_t offset, int whence) +{ + int fugue_fd = (int)data; + + 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(fugue_fd) + offset; + else if(whence == SEEK_END) + destination = BFile_Size(fugue_fd) + offset; + + int rc = BFile_Seek(fugue_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; +} + +int fugue_close(void *data) +{ + int fugue_fd = (int)data; + + int rc = BFile_Close(fugue_fd); + if(rc < 0) { + errno = bfile_error_to_errno(rc); + return -1; + } + return 0; +} + +static const fs_descriptor_type_t fugue_descriptor_type = { + .read = fugue_read, + .pread = fugue_pread, + .write = fugue_write, + .lseek = fugue_lseek, + .close = fugue_close, +}; + +int fugue_open(char const *path, int flags, GUNUSED mode_t mode) +{ + if(gint[HWFS] != HWFS_FUGUE) { + errno = ENOTSUP; + return -1; + } + + uint16_t *fcpath = utf8_to_fc_alloc(path, u"\\\\fls0\\"); + int fugue_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. */ + fugue_fd = BFile_EntryNotFound; + if(excl || !trunc) + fugue_fd = BFile_Open(fcpath, bfile_mode); + + /* If the file exists and O_EXCL was requested, fail. */ + if(fugue_fd >= 0 && excl) { + BFile_Close(fugue_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) && (fugue_fd >= 0 || (flags & O_CREAT))) { + if(fugue_fd >= 0) BFile_Close(fugue_fd); + BFile_Remove(fcpath); + fugue_fd = BFile_EntryNotFound; + } + + /* If the file does not exist and O_CREAT is set, create it */ + if((flags & O_CREAT) && ((flags & O_TRUNC) || fugue_fd < 0)) { + int size = 0; + err = BFile_Create(fcpath, BFile_File, &size); + if(err < 0) { + errno = bfile_error_to_errno(err); + goto end; + } + fugue_fd = BFile_Open(fcpath, bfile_mode); + } + + if(fugue_fd < 0) { + errno = bfile_error_to_errno(fugue_fd); + goto end; + } + + /* If O_APPEND is set, move to the end of the file */ + if((flags & O_APPEND)) + BFile_Seek(fugue_fd, BFile_Size(fugue_fd)); + + /* Return the now-open file descriptor */ + fs_descriptor_t data = { + .type = &fugue_descriptor_type, + .data = (void *)fugue_fd, + }; + int fd = fs_create_descriptor(&data); + + if(fd == -1) { + BFile_Close(fugue_fd); + errno = ENFILE; + goto end; + } + + rc = fd; +end: + free(fcpath); + return rc; +} + +int fugue_unlink(char const *path) +{ + if(gint[HWFS] != HWFS_FUGUE) { + errno = ENOTSUP; + return -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/fugue/fugue.h b/src/fs/fugue/fugue.h new file mode 100644 index 0000000..1b139b4 --- /dev/null +++ b/src/fs/fugue/fugue.h @@ -0,0 +1,13 @@ +#ifndef FS_FUGUE_FUGUE_H +#define FS_FUGUE_FUGUE_H + +#include +#include + +/* Specific implementation of open() */ +int fugue_open(char const *path, int flags, mode_t mode); + +/* Specific implementation of unlink() */ +int fugue_unlink(char const *path); + +#endif /* FS_FUGUE_FUGUE_H */ diff --git a/src/fs/util.c b/src/fs/fugue/util.c similarity index 98% rename from src/fs/util.c rename to src/fs/fugue/util.c index 07890ac..292d287 100644 --- a/src/fs/util.c +++ b/src/fs/fugue/util.c @@ -1,6 +1,8 @@ #include "util.h" #include #include +#include +#include int bfile_error_to_errno(int e) { diff --git a/src/fs/util.h b/src/fs/fugue/util.h similarity index 85% rename from src/fs/util.h rename to src/fs/fugue/util.h index 64fce5c..22b4175 100644 --- a/src/fs/util.h +++ b/src/fs/fugue/util.h @@ -7,13 +7,7 @@ extern "C" { #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); diff --git a/src/fs/lseek.c b/src/fs/lseek.c index 304c79f..c08fb35 100644 --- a/src/fs/lseek.c +++ b/src/fs/lseek.c @@ -1,37 +1,18 @@ #include -#include "util.h" +#include +#include 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; + fs_descriptor_t const *d = fs_get_descriptor(fd); + if(!d) { + errno = EBADF; + return (ssize_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; - } + if(d->type->lseek) + return d->type->lseek(d->data, offset, whence); - 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; + /* No seek function: cannot seek */ + return 0; } diff --git a/src/fs/open.c b/src/fs/open.c index ea438f7..03b82f9 100644 --- a/src/fs/open.c +++ b/src/fs/open.c @@ -1,74 +1,14 @@ #include -#include "util.h" +#include +#include "fugue/fugue.h" int open(char const *path, int flags, ...) { - ENOTSUP_IF_NOT_FUGUE(-1); + va_list args; + va_start(args, flags); + mode_t mode = va_arg(args, int); + va_end(args); - 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; + /* Standard open() is the Fugue filesystem only */ + return fugue_open(path, flags, mode); } diff --git a/src/fs/pread.c b/src/fs/pread.c index c6ce809..42aa7d6 100644 --- a/src/fs/pread.c +++ b/src/fs/pread.c @@ -1,17 +1,18 @@ #include -#include "util.h" +#include +#include 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; + fs_descriptor_t const *d = fs_get_descriptor(fd); + if(!d) { + errno = EBADF; + return (ssize_t)-1; } - BFile_Seek(fd, -size); - return rc; + + if(d->type->pread) + return d->type->pread(d->data, buf, size, offset); + + /* No seek function: cannot seek */ + return 0; } diff --git a/src/fs/pwrite.c b/src/fs/pwrite.c index d3b0e33..bcbcb5f 100644 --- a/src/fs/pwrite.c +++ b/src/fs/pwrite.c @@ -1,25 +1,21 @@ #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); + /* This fails on G-III BFile and non-seekable file descriptors */ if(current == (off_t)-1) return (ssize_t)-1; ssize_t rc = -1; - if(lseek(fd, 0, offset) == (off_t)-1) + if(lseek(fd, offset, SEEK_SET) == (off_t)-1) goto end; - rc = BFile_Write(fd, buf, size); - if(rc < 0) { - errno = bfile_error_to_errno(rc); + rc = write(fd, buf, size); + if(rc < 0) goto end; - } end: /* At the end, always try to restore the current position */ diff --git a/src/fs/read.c b/src/fs/read.c index 20a0332..e214719 100644 --- a/src/fs/read.c +++ b/src/fs/read.c @@ -1,14 +1,18 @@ #include -#include "util.h" +#include +#include 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; + fs_descriptor_t const *d = fs_get_descriptor(fd); + if(!d) { + errno = EBADF; + return (ssize_t)-1; } - return size; + + if(d->type->read) + return d->type->read(d->data, buf, size); + + /* No read function: we can't read anything */ + return 0; } diff --git a/src/fs/unlink.c b/src/fs/unlink.c index ae00609..cb43104 100644 --- a/src/fs/unlink.c +++ b/src/fs/unlink.c @@ -1,21 +1,8 @@ #include -#include "util.h" +#include "fugue/fugue.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; + /* Standard unlink() is the Fugue filesystem only */ + return fugue_unlink(path); } diff --git a/src/fs/write.c b/src/fs/write.c index 3f4a3ad..8ce994c 100644 --- a/src/fs/write.c +++ b/src/fs/write.c @@ -1,14 +1,18 @@ #include -#include "util.h" +#include +#include 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; + fs_descriptor_t const *d = fs_get_descriptor(fd); + if(!d) { + errno = EBADF; + return (ssize_t)-1; } - return rc; + + if(d->type->write) + return d->type->write(d->data, buf, size); + + /* No write function: discard the contents but show no error */ + return size; }