diff --git a/fs/vfs/fs_fdopen.c b/fs/vfs/fs_fdopen.c index 5f7bf7005ad47..7a8f06e21c00d 100644 --- a/fs/vfs/fs_fdopen.c +++ b/fs/vfs/fs_fdopen.c @@ -221,8 +221,8 @@ int fs_fdopen(int fd, int oflags, FAR struct tcb_s *tcb, * file descriptor locks this stream. */ - stream->fs_fd = fd; - stream->fs_oflags = oflags; + stream->fs_cookie = (FAR void *)(intptr_t)fd; + stream->fs_oflags = oflags; if (filep != NULL) { diff --git a/include/nuttx/fs/fs.h b/include/nuttx/fs/fs.h index 7c8f0a53e46f1..b1bbe1140722f 100644 --- a/include/nuttx/fs/fs.h +++ b/include/nuttx/fs/fs.h @@ -426,6 +426,31 @@ struct inode #define FSNODE_SIZE(n) (sizeof(struct inode) + (n)) +/* Definitions for custom stream operations with fopencookie. The + * implementation is as defined in Standard C library (libc). The only + * difference is that we use off_t instead of off64_t. This means + * off_t is int64_t if CONFIG_FS_LARGEFILE is defined and int32_t if not. + * + * These callbacks can either lead to custom functions if fopencookie is used + * or to standard file system functions if not. + */ + +typedef CODE ssize_t cookie_read_function_t(void *cookie, char *buf, + size_t size); +typedef CODE ssize_t cookie_write_function_t(void *cookie, const char *buf, + size_t size); +typedef CODE off_t cookie_seek_function_t(void *cookie, off_t *offset, + int whence); +typedef CODE int cookie_close_function_t(void *cookie); + +typedef struct cookie_io_functions_t +{ + FAR cookie_read_function_t *read; + FAR cookie_write_function_t *write; + FAR cookie_seek_function_t *seek; + FAR cookie_close_function_t *close; +} cookie_io_functions_t; + /* This is the underlying representation of an open file. A file * descriptor is an index into an array of such types. The type associates * the file descriptor to the file state and to a set of inode operations. @@ -497,7 +522,8 @@ struct file_struct { FAR struct file_struct *fs_next; /* Pointer to next file stream */ rmutex_t fs_lock; /* Recursive lock */ - int fs_fd; /* File descriptor associated with stream */ + cookie_io_functions_t fs_iofunc; /* Callbacks to user / system functions */ + FAR void *fs_cookie; /* Pointer to file descriptor / cookie struct */ #ifndef CONFIG_STDIO_DISABLE_BUFFERING FAR unsigned char *fs_bufstart; /* Pointer to start of buffer */ FAR unsigned char *fs_bufend; /* Pointer to 1 past end of buffer */ diff --git a/include/stdio.h b/include/stdio.h index 7aaa814d351f5..b7a4c3dde47a2 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -244,6 +244,11 @@ int dprintf(int fd, FAR const IPTR char *fmt, ...) printf_like(2, 3); int vdprintf(int fd, FAR const IPTR char *fmt, va_list ap) printf_like(2, 0); +/* Custom stream operation fopencookie. */ + +FAR FILE *fopencookie(FAR void *cookie, FAR const char *mode, + cookie_io_functions_t io_funcs); + /* Operations on paths */ FAR FILE *tmpfile(void) fopen_like; diff --git a/libs/libc/libc.h b/libs/libc/libc.h index 332556feb7916..c7516786fb4bd 100644 --- a/libs/libc/libc.h +++ b/libs/libc/libc.h @@ -245,6 +245,13 @@ bool lib_isbasedigit(int ch, int base, FAR int *value); int lib_checkbase(int base, FAR const char **pptr); +/* Defined in lib_stdio_cb.c */ + +ssize_t lib_fread_cb(FAR void *cookie, FAR char *buf, size_t size); +ssize_t lib_fwrite_cb(FAR void *cookie, FAR const char *buf, size_t size); +off_t lib_fseek_cb(FAR void *cookie, FAR off_t *offset, int whence); +int lib_fclose_cb(FAR void *cookie); + /* Defined in lib_parsehostfile.c */ #ifdef CONFIG_NETDB_HOSTFILE diff --git a/libs/libc/stdio/CMakeLists.txt b/libs/libc/stdio/CMakeLists.txt index b1f0cb926e393..9244c0b08d715 100644 --- a/libs/libc/stdio/CMakeLists.txt +++ b/libs/libc/stdio/CMakeLists.txt @@ -104,7 +104,9 @@ if(CONFIG_FILE_STREAM) lib_libgetstreams.c lib_fputwc.c lib_putwc.c - lib_fputws.c) + lib_fputws.c + lib_fopencookie.c + lib_stdio_cb.c) endif() target_sources(c PRIVATE ${SRCS}) diff --git a/libs/libc/stdio/Make.defs b/libs/libc/stdio/Make.defs index d438ceaf6e178..ad0f2387d1dee 100644 --- a/libs/libc/stdio/Make.defs +++ b/libs/libc/stdio/Make.defs @@ -48,6 +48,7 @@ CSRCS += lib_feof.c lib_ferror.c lib_rewind.c lib_clearerr.c CSRCS += lib_scanf.c lib_vscanf.c lib_fscanf.c lib_vfscanf.c lib_tmpfile.c CSRCS += lib_setbuf.c lib_setvbuf.c lib_libstream.c lib_libfilelock.c CSRCS += lib_libgetstreams.c lib_setbuffer.c lib_fputwc.c lib_putwc.c lib_fputws.c +CSRCS += lib_fopencookie.c lib_stdio_cb.c endif # Add the stdio directory to the build diff --git a/libs/libc/stdio/lib_fclose.c b/libs/libc/stdio/lib_fclose.c index b85a497e7d05f..6d3fa1d0a6e60 100644 --- a/libs/libc/stdio/lib_fclose.c +++ b/libs/libc/stdio/lib_fclose.c @@ -115,32 +115,18 @@ int fclose(FAR FILE *stream) nxmutex_unlock(&slist->sl_lock); - /* Check that the underlying file descriptor corresponds to an an open - * file. - */ - - if (stream->fs_fd >= 0) - { - /* Close the file descriptor and save the return status */ + /* Close the file descriptor and save the return status */ -#ifdef CONFIG_FDSAN - uint64_t tag; - tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_FILE, - (uintptr_t)stream); - status = android_fdsan_close_with_tag(stream->fs_fd, tag); -#else - status = close(stream->fs_fd); -#endif + status = stream->fs_iofunc.close(stream->fs_cookie); - /* If close() returns an error but flush() did not then make sure - * that we return the close() error condition. - */ + /* If close() returns an error but flush() did not then make sure + * that we return the close() error condition. + */ - if (ret == OK) - { - ret = status; - errcode = get_errno(); - } + if (ret == OK) + { + ret = status; + errcode = get_errno(); } #ifndef CONFIG_STDIO_DISABLE_BUFFERING diff --git a/libs/libc/stdio/lib_fileno.c b/libs/libc/stdio/lib_fileno.c index 803890b0d2d01..c90d6bdc986a3 100644 --- a/libs/libc/stdio/lib_fileno.c +++ b/libs/libc/stdio/lib_fileno.c @@ -41,7 +41,7 @@ int fileno(FAR FILE *stream) if (stream) { - ret = stream->fs_fd; + ret = (int)(intptr_t)stream->fs_cookie; } if (ret < 0) diff --git a/libs/libc/stdio/lib_fopen.c b/libs/libc/stdio/lib_fopen.c index 80b33044c4db3..c13f317b3012f 100644 --- a/libs/libc/stdio/lib_fopen.c +++ b/libs/libc/stdio/lib_fopen.c @@ -87,6 +87,13 @@ FAR FILE *fdopen(int fd, FAR const char *mode) (uintptr_t)filep)); #endif + /* Assign internal callbacks. */ + + filep->fs_iofunc.read = lib_fread_cb; + filep->fs_iofunc.write = lib_fwrite_cb; + filep->fs_iofunc.seek = lib_fseek_cb; + filep->fs_iofunc.close = lib_fclose_cb; + return filep; } @@ -140,6 +147,13 @@ FAR FILE *fopen(FAR const char *path, FAR const char *mode) (uintptr_t)filep)); #endif + /* Assign internal callbacks. */ + + filep->fs_iofunc.read = lib_fread_cb; + filep->fs_iofunc.write = lib_fwrite_cb; + filep->fs_iofunc.seek = lib_fseek_cb; + filep->fs_iofunc.close = lib_fclose_cb; + return filep; } diff --git a/libs/libc/stdio/lib_fopencookie.c b/libs/libc/stdio/lib_fopencookie.c new file mode 100644 index 0000000000000..30610a5978940 --- /dev/null +++ b/libs/libc/stdio/lib_fopencookie.c @@ -0,0 +1,83 @@ +/**************************************************************************** + * libs/libc/stdio/lib_fopencookie.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libc.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: fopencookie + ****************************************************************************/ + +FAR FILE *fopencookie(FAR void *cookie, FAR const char *mode, + cookie_io_functions_t io_funcs) +{ + FAR FILE *filep = NULL; + int ret; + int oflags; + + /* Map the open mode string to open flags */ + + oflags = lib_mode2oflags(mode); + if (oflags < 0) + { + return NULL; + } + + /* Call fs_fdopen with invalid file descriptor and cookie flag set to 1 */ + + ret = fs_fdopen(-1, oflags, NULL, &filep); + if (ret < 0) + { + return NULL; + } + + /* Assign cookie and user defined callbacks. */ + + filep->fs_cookie = cookie; + filep->fs_iofunc = io_funcs; + + return filep; +} diff --git a/libs/libc/stdio/lib_fseeko.c b/libs/libc/stdio/lib_fseeko.c index 163a79de3890a..70cc6fad54721 100644 --- a/libs/libc/stdio/lib_fseeko.c +++ b/libs/libc/stdio/lib_fseeko.c @@ -87,5 +87,6 @@ int fseeko(FAR FILE *stream, off_t offset, int whence) /* Perform the fseeko on the underlying file descriptor */ - return lseek(stream->fs_fd, offset, whence) == (off_t)-1 ? ERROR : OK; + return stream->fs_iofunc.seek(stream->fs_cookie, &offset, + whence) == (off_t)-1 ? ERROR : OK; } diff --git a/libs/libc/stdio/lib_ftello.c b/libs/libc/stdio/lib_ftello.c index 0052a50e80157..a7f68b03b1b1f 100644 --- a/libs/libc/stdio/lib_ftello.c +++ b/libs/libc/stdio/lib_ftello.c @@ -97,6 +97,7 @@ static off_t lib_getoffset(FAR FILE *stream) off_t ftello(FAR FILE *stream) { off_t position; + off_t offset = 0; /* Verify that we were provided with a stream */ @@ -110,7 +111,7 @@ off_t ftello(FAR FILE *stream) * file pointer, but will return its current setting */ - position = lseek(stream->fs_fd, 0, SEEK_CUR); + position = stream->fs_iofunc.seek(stream->fs_cookie, &offset, SEEK_CUR); if (position != (off_t)-1) { return position - lib_getoffset(stream); diff --git a/libs/libc/stdio/lib_libfflush.c b/libs/libc/stdio/lib_libfflush.c index bdc0250673647..a62d73bf2b13f 100644 --- a/libs/libc/stdio/lib_libfflush.c +++ b/libs/libc/stdio/lib_libfflush.c @@ -65,7 +65,7 @@ ssize_t lib_fflush_unlocked(FAR FILE *stream, bool bforce) /* Return EBADF if the file is not opened for writing */ - if (stream->fs_fd < 0 || (stream->fs_oflags & O_WROK) == 0) + if ((stream->fs_oflags & O_WROK) == 0) { return -EBADF; } @@ -107,7 +107,9 @@ ssize_t lib_fflush_unlocked(FAR FILE *stream, bool bforce) { /* Perform the write */ - bytes_written = _NX_WRITE(stream->fs_fd, src, nbuffer); + bytes_written = stream->fs_iofunc.write(stream->fs_cookie, + (const char *)src, + nbuffer); if (bytes_written < 0) { /* Write failed. The cause of the failure is in 'errno'. diff --git a/libs/libc/stdio/lib_libfgets.c b/libs/libc/stdio/lib_libfgets.c index 29f4420d90b88..b49868dc8f737 100644 --- a/libs/libc/stdio/lib_libfgets.c +++ b/libs/libc/stdio/lib_libfgets.c @@ -97,10 +97,11 @@ FAR char *lib_fgets_unlocked(FAR char *buf, size_t buflen, FILE *stream, bool keepnl, bool consume) { size_t nch = 0; + int fd = (int)(intptr_t)stream->fs_cookie; /* Sanity checks */ - if (!stream || !buf || stream->fs_fd < 0) + if (!stream || !buf || fd < 0) { return NULL; } diff --git a/libs/libc/stdio/lib_libfread_unlocked.c b/libs/libc/stdio/lib_libfread_unlocked.c index 1fb26b798ddcd..369ed999ffcb0 100644 --- a/libs/libc/stdio/lib_libfread_unlocked.c +++ b/libs/libc/stdio/lib_libfread_unlocked.c @@ -170,7 +170,9 @@ ssize_t lib_fread_unlocked(FAR void *ptr, size_t count, FAR FILE *stream) if (remaining > buffer_available) { - bytes_read = _NX_READ(stream->fs_fd, dest, remaining); + bytes_read = stream->fs_iofunc.read(stream->fs_cookie, + (char *)dest, + remaining); if (bytes_read < 0) { if (count - remaining > 0) @@ -213,9 +215,9 @@ ssize_t lib_fread_unlocked(FAR void *ptr, size_t count, FAR FILE *stream) * into the buffer. */ - bytes_read = _NX_READ(stream->fs_fd, - stream->fs_bufread, - buffer_available); + bytes_read = stream->fs_iofunc.read(stream->fs_cookie, + (char *)stream->fs_bufread, + buffer_available); if (bytes_read < 0) { if (count - remaining > 0) @@ -258,7 +260,9 @@ ssize_t lib_fread_unlocked(FAR void *ptr, size_t count, FAR FILE *stream) while (remaining > 0) { - bytes_read = _NX_READ(stream->fs_fd, dest, remaining); + bytes_read = stream->fs_iofunc.read(stream->fs_cookie, + (char *)dest, + remaining); if (bytes_read < 0) { if (count - remaining > 0) diff --git a/libs/libc/stdio/lib_libfwrite.c b/libs/libc/stdio/lib_libfwrite.c index 265a59d85540e..4a186a28d642f 100644 --- a/libs/libc/stdio/lib_libfwrite.c +++ b/libs/libc/stdio/lib_libfwrite.c @@ -72,7 +72,7 @@ ssize_t lib_fwrite_unlocked(FAR const void *ptr, size_t count, if (stream->fs_bufstart == NULL) { - ret = _NX_WRITE(stream->fs_fd, ptr, count); + ret = stream->fs_iofunc.write(stream->fs_cookie, ptr, count); if (ret < 0) { _NX_SETERRNO(ret); @@ -132,7 +132,8 @@ ssize_t lib_fwrite_unlocked(FAR const void *ptr, size_t count, if (count >= CONFIG_STDIO_BUFFER_SIZE) { - ret = _NX_WRITE(stream->fs_fd, src, count); + ret = stream->fs_iofunc.write(stream->fs_cookie, (const char *)src, + count); if (ret < 0) { _NX_SETERRNO(ret); @@ -163,7 +164,7 @@ ssize_t lib_fwrite_unlocked(FAR const void *ptr, size_t count, } #else { - ssize_t ret = _NX_WRITE(stream->fs_fd, ptr, count); + ssize_t ret = stream->fs_iofunc.write(stream->fs_cookie, ptr, count); if (ret < 0) { stream->fs_flags |= __FS_FLAG_ERROR; diff --git a/libs/libc/stdio/lib_libstream.c b/libs/libc/stdio/lib_libstream.c index be6a2a1c02115..8f750dc4174f1 100644 --- a/libs/libc/stdio/lib_libstream.c +++ b/libs/libc/stdio/lib_libstream.c @@ -68,11 +68,23 @@ void lib_stream_initialize(FAR struct task_group_s *group) /* Initialize stdin, stdout and stderr stream */ - list->sl_std[0].fs_fd = -1; + list->sl_std[0].fs_cookie = (FAR void *)(intptr_t)-1; + list->sl_std[0].fs_iofunc.read = lib_fread_cb; + list->sl_std[0].fs_iofunc.write = lib_fwrite_cb; + list->sl_std[0].fs_iofunc.seek = lib_fseek_cb; + list->sl_std[0].fs_iofunc.close = lib_fclose_cb; nxrmutex_init(&list->sl_std[0].fs_lock); - list->sl_std[1].fs_fd = -1; + list->sl_std[1].fs_cookie = (FAR void *)(intptr_t)-1; + list->sl_std[1].fs_iofunc.read = lib_fread_cb; + list->sl_std[1].fs_iofunc.write = lib_fwrite_cb; + list->sl_std[1].fs_iofunc.seek = lib_fseek_cb; + list->sl_std[1].fs_iofunc.close = lib_fclose_cb; nxrmutex_init(&list->sl_std[1].fs_lock); - list->sl_std[2].fs_fd = -1; + list->sl_std[2].fs_cookie = (FAR void *)(intptr_t)-1; + list->sl_std[2].fs_iofunc.read = lib_fread_cb; + list->sl_std[2].fs_iofunc.write = lib_fwrite_cb; + list->sl_std[2].fs_iofunc.seek = lib_fseek_cb; + list->sl_std[2].fs_iofunc.close = lib_fclose_cb; nxrmutex_init(&list->sl_std[2].fs_lock); } diff --git a/libs/libc/stdio/lib_rdflush_unlocked.c b/libs/libc/stdio/lib_rdflush_unlocked.c index 70c4758660de2..5294143fb556d 100644 --- a/libs/libc/stdio/lib_rdflush_unlocked.c +++ b/libs/libc/stdio/lib_rdflush_unlocked.c @@ -92,7 +92,8 @@ int lib_rdflush_unlocked(FAR FILE *stream) * user */ - if (lseek(stream->fs_fd, -rdoffset, SEEK_CUR) < 0) + rdoffset = -rdoffset; + if (stream->fs_iofunc.seek(stream->fs_cookie, &rdoffset, SEEK_CUR) < 0) { return ERROR; } diff --git a/libs/libc/stdio/lib_setvbuf.c b/libs/libc/stdio/lib_setvbuf.c index 8d5e5e57037fd..8e8e765f41248 100644 --- a/libs/libc/stdio/lib_setvbuf.c +++ b/libs/libc/stdio/lib_setvbuf.c @@ -78,6 +78,7 @@ int setvbuf(FAR FILE *stream, FAR char *buffer, int mode, size_t size) { #ifndef CONFIG_STDIO_DISABLE_BUFFERING FAR unsigned char *newbuf = NULL; + int fd = (int)(intptr_t)stream->fs_cookie; uint8_t flags; int errcode; @@ -133,7 +134,7 @@ int setvbuf(FAR FILE *stream, FAR char *buffer, int mode, size_t size) /* Return EBADF if the file is not open */ - if (stream->fs_fd < 0) + if (fd < 0) { errcode = EBADF; goto errout_with_lock; diff --git a/libs/libc/stdio/lib_stdio_cb.c b/libs/libc/stdio/lib_stdio_cb.c new file mode 100644 index 0000000000000..6f7a0fefb8c2e --- /dev/null +++ b/libs/libc/stdio/lib_stdio_cb.c @@ -0,0 +1,93 @@ +/**************************************************************************** + * libs/libc/stdio/lib_stdio_cb.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#ifdef CONFIG_FDSAN +# include +#endif + +#include "libc.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lib_fseek_cb + ****************************************************************************/ + +off_t lib_fseek_cb(FAR void *cookie, FAR off_t *offset, int whence) +{ + int fd = (int)(intptr_t)cookie; + return lseek(fd, *offset, whence); +} + +/**************************************************************************** + * Name: lib_fwrite_cb + ****************************************************************************/ + +ssize_t lib_fwrite_cb(FAR void *cookie, FAR const char *buf, size_t size) +{ + int fd = (int)(intptr_t)cookie; + return _NX_WRITE(fd, buf, size); +} + +/**************************************************************************** + * Name: lib_fread_cb + ****************************************************************************/ + +ssize_t lib_fread_cb(FAR void *cookie, FAR char *buf, size_t size) +{ + int fd = (int)(intptr_t)cookie; + return _NX_READ(fd, buf, size); +} + +/**************************************************************************** + * Name: lib_fclose_cb + ****************************************************************************/ + +int lib_fclose_cb(FAR void *cookie) +{ + int fd = (int)(intptr_t)cookie; + +#ifdef CONFIG_FDSAN + uint64_t tag; + int ret = ioctl(fd, FIOC_GETTAG, &tag); + if (ret < 0) + { + return errno; + } + + return android_fdsan_close_with_tag(fd, tag); +#else + return close(fd); +#endif +} diff --git a/libs/libc/stdio/lib_ungetc.c b/libs/libc/stdio/lib_ungetc.c index f5ebea1e39452..f7d906da285e5 100644 --- a/libs/libc/stdio/lib_ungetc.c +++ b/libs/libc/stdio/lib_ungetc.c @@ -39,6 +39,7 @@ int ungetc(int c, FAR FILE *stream) { + int fd = (int)(intptr_t)stream->fs_cookie; #if CONFIG_NUNGET_CHARS > 0 int nungotten; #endif @@ -52,7 +53,7 @@ int ungetc(int c, FAR FILE *stream) /* Stream must be open for read access */ - if ((stream->fs_fd < 0) || ((stream->fs_oflags & O_RDOK) == 0)) + if ((fd < 0) || ((stream->fs_oflags & O_RDOK) == 0)) { return EOF; }