diff --git a/include/nuttx/fs/fs.h b/include/nuttx/fs/fs.h index cdc3aa224a66f..fed454fc7d210 100644 --- a/include/nuttx/fs/fs.h +++ b/include/nuttx/fs/fs.h @@ -426,6 +426,33 @@ 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(FAR void *cookie, FAR char *buf, + size_t size); +typedef CODE ssize_t cookie_write_function_t(FAR void *cookie, + FAR const char *buf, + size_t size); +typedef CODE off_t cookie_seek_function_t(FAR void *cookie, + FAR off_t *offset, + int whence); +typedef CODE int cookie_close_function_t(FAR 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 +524,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/stdio/CMakeLists.txt b/libs/libc/stdio/CMakeLists.txt index f914b6ef4cfbb..a3524352a9fe1 100644 --- a/libs/libc/stdio/CMakeLists.txt +++ b/libs/libc/stdio/CMakeLists.txt @@ -103,7 +103,8 @@ if(CONFIG_FILE_STREAM) lib_libgetstreams.c lib_fputwc.c lib_putwc.c - lib_fputws.c) + lib_fputws.c + lib_fopencookie.c) endif() target_sources(c PRIVATE ${SRCS}) diff --git a/libs/libc/stdio/Make.defs b/libs/libc/stdio/Make.defs index d94658b647893..c692bb1c25184 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_libfilelock.c lib_libgetstreams.c CSRCS += lib_setbuffer.c lib_fputwc.c lib_putwc.c lib_fputws.c +CSRCS += lib_fopencookie.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..357f7f9f93fa1 100644 --- a/libs/libc/stdio/lib_fclose.c +++ b/libs/libc/stdio/lib_fclose.c @@ -115,32 +115,33 @@ int fclose(FAR FILE *stream) nxmutex_unlock(&slist->sl_lock); - /* Check that the underlying file descriptor corresponds to an an open - * file. - */ + /* Call user custom callback if it is not NULL. */ - if (stream->fs_fd >= 0) + if (stream->fs_iofunc.close != NULL) { - /* Close the file descriptor and save the return status */ - + status = stream->fs_iofunc.close(stream->fs_cookie); + } + else + { + int fd = (int)(intptr_t)stream->fs_cookie; #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); + status = android_fdsan_close_with_tag(fd, tag); #else - status = close(stream->fs_fd); + status = close(fd); #endif + } - /* 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 8e53b8683118b..d446e0960cb72 100644 --- a/libs/libc/stdio/lib_fopen.c +++ b/libs/libc/stdio/lib_fopen.c @@ -141,7 +141,7 @@ FAR FILE *fdopen(int fd, FAR const char *mode) * file descriptor locks this stream. */ - filep->fs_fd = fd; + filep->fs_cookie = (FAR void *)(intptr_t)fd; filep->fs_oflags = oflags; #ifdef CONFIG_FDSAN @@ -150,6 +150,13 @@ FAR FILE *fdopen(int fd, FAR const char *mode) (uintptr_t)filep)); #endif + /* Assign custom callbacks to NULL. */ + + filep->fs_iofunc.read = NULL; + filep->fs_iofunc.write = NULL; + filep->fs_iofunc.seek = NULL; + filep->fs_iofunc.close = NULL; + return filep; errout: diff --git a/libs/libc/stdio/lib_fopencookie.c b/libs/libc/stdio/lib_fopencookie.c new file mode 100644 index 0000000000000..aaca71a4d836c --- /dev/null +++ b/libs/libc/stdio/lib_fopencookie.c @@ -0,0 +1,156 @@ +/**************************************************************************** + * 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 + ****************************************************************************/ + +/**************************************************************************** + * Name: cookie_read_cb + ****************************************************************************/ + +static ssize_t cookie_read_cb(FAR void *cookie, FAR char *buf, size_t size) +{ + /* Per specification: if *read is a null pointer, then reads from the + * custom stream always return end of file. + */ + + return 0; +} + +/**************************************************************************** + * Name: cookie_write_cb + ****************************************************************************/ + +static ssize_t cookie_write_cb(FAR void *cookie, FAR const char *buf, + size_t size) +{ + /* Per specification: if *write is a null pointer, then output to the + * stream is discarded. + */ + + return 0; +} + +/**************************************************************************** + * Name: cookie_seek_cb + ****************************************************************************/ + +static off_t cookie_seek_cb(FAR void *cookie, FAR off_t *offset, int whence) +{ + /* Per specification: if *seek is a null pointer, then it is not + * possible to perform seek operations on the stream. + */ + + errno = -ENOTSUP; + return -1; +} + +/**************************************************************************** + * Name: cookie_close_cb + ****************************************************************************/ + +static int cookie_close_cb(FAR void *cookie) +{ + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: fopencookie + ****************************************************************************/ + +FAR FILE *fopencookie(FAR void *cookie, FAR const char *mode, + cookie_io_functions_t io_funcs) +{ + FAR FILE *filep = NULL; + + /* Call fdopen to initialize the stream. The function is called with + * first possible file descriptor (0-2 are reserved). This is only + * to pass the required checks, file descriptor is then rewritten to + * cookie. + */ + + filep = fdopen(3, mode); + if (filep == NULL) + { + return NULL; + } + + /* Assign cookie to file descriptor (fs_cookie) and assign user + * defined callbacks. + */ + + filep->fs_cookie = cookie; + filep->fs_iofunc = io_funcs; + + /* Fopencookie Linux specification allows cookie_io_functions_t to be + * filled only partially and thus some callbacks might be NULL and not + * used. For this reason we add internal callbacks that are assigned to + * prevent undefined behaviour and handle correct return value per + * fopencookie specification. + */ + + if (filep->fs_iofunc.read == NULL) + { + filep->fs_iofunc.read = cookie_read_cb; + } + + if (filep->fs_iofunc.write == NULL) + { + filep->fs_iofunc.write = cookie_write_cb; + } + + if (filep->fs_iofunc.seek == NULL) + { + filep->fs_iofunc.seek = cookie_seek_cb; + } + + if (filep->fs_iofunc.close == NULL) + { + filep->fs_iofunc.close = cookie_close_cb; + } + + return filep; +} diff --git a/libs/libc/stdio/lib_fseeko.c b/libs/libc/stdio/lib_fseeko.c index 163a79de3890a..cf8062840f8c2 100644 --- a/libs/libc/stdio/lib_fseeko.c +++ b/libs/libc/stdio/lib_fseeko.c @@ -87,5 +87,14 @@ 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; + if (stream->fs_iofunc.seek != NULL) + { + return stream->fs_iofunc.seek(stream->fs_cookie, &offset, + whence) == (off_t)-1 ? ERROR : OK; + } + else + { + return lseek((int)(intptr_t)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..735302105e5b9 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,16 @@ off_t ftello(FAR FILE *stream) * file pointer, but will return its current setting */ - position = lseek(stream->fs_fd, 0, SEEK_CUR); + if (stream->fs_iofunc.seek != NULL) + { + position = stream->fs_iofunc.seek(stream->fs_cookie, &offset, + SEEK_CUR); + } + else + { + position = lseek((int)(intptr_t)stream->fs_cookie, 0, 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..4502ca31d10e9 100644 --- a/libs/libc/stdio/lib_libfflush.c +++ b/libs/libc/stdio/lib_libfflush.c @@ -59,13 +59,13 @@ ssize_t lib_fflush_unlocked(FAR FILE *stream, bool bforce) { #ifndef CONFIG_STDIO_DISABLE_BUFFERING - FAR const unsigned char *src; + FAR const char *src; ssize_t bytes_written; ssize_t nbuffer; /* 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; } @@ -102,12 +102,23 @@ ssize_t lib_fflush_unlocked(FAR FILE *stream, bool bforce) /* Try to write that amount */ - src = stream->fs_bufstart; + src = (FAR const char *)stream->fs_bufstart; do { /* Perform the write */ - bytes_written = _NX_WRITE(stream->fs_fd, src, nbuffer); + if (stream->fs_iofunc.write != NULL) + { + bytes_written = stream->fs_iofunc.write(stream->fs_cookie, + src, + nbuffer); + } + else + { + bytes_written = _NX_WRITE((int)(intptr_t)stream->fs_cookie, + 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..cb07f262cecdc 100644 --- a/libs/libc/stdio/lib_libfgets.c +++ b/libs/libc/stdio/lib_libfgets.c @@ -100,7 +100,7 @@ FAR char *lib_fgets_unlocked(FAR char *buf, size_t buflen, FILE *stream, /* Sanity checks */ - if (!stream || !buf || stream->fs_fd < 0) + if (!stream || !buf) { return NULL; } diff --git a/libs/libc/stdio/lib_libfread_unlocked.c b/libs/libc/stdio/lib_libfread_unlocked.c index 1fb26b798ddcd..ca2535a2b9483 100644 --- a/libs/libc/stdio/lib_libfread_unlocked.c +++ b/libs/libc/stdio/lib_libfread_unlocked.c @@ -46,13 +46,14 @@ ssize_t lib_fread_unlocked(FAR void *ptr, size_t count, FAR FILE *stream) { - FAR unsigned char *dest = (FAR unsigned char *)ptr; + FAR char *dest = (FAR char *)ptr; ssize_t bytes_read; size_t remaining = count; #ifndef CONFIG_STDIO_DISABLE_BUFFERING size_t gulp_size; int ret; #endif + int fd; /* Make sure that reading from this stream is allowed */ @@ -69,6 +70,7 @@ ssize_t lib_fread_unlocked(FAR void *ptr, size_t count, FAR FILE *stream) } else { + fd = (int)(intptr_t)stream->fs_cookie; #if CONFIG_NUNGET_CHARS > 0 /* First, re-read any previously ungotten characters */ @@ -170,7 +172,17 @@ 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); + if (stream->fs_iofunc.read != NULL) + { + bytes_read = stream->fs_iofunc.read( + stream->fs_cookie, + dest, remaining); + } + else + { + bytes_read = _NX_READ(fd, dest, remaining); + } + if (bytes_read < 0) { if (count - remaining > 0) @@ -213,9 +225,19 @@ 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); + if (stream->fs_iofunc.read != NULL) + { + bytes_read = stream->fs_iofunc.read( + stream->fs_cookie, + (FAR char *)stream->fs_bufread, + buffer_available); + } + else + { + bytes_read = _NX_READ(fd, stream->fs_bufread, + buffer_available); + } + if (bytes_read < 0) { if (count - remaining > 0) @@ -258,7 +280,17 @@ 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); + if (stream->fs_iofunc.read != NULL) + { + bytes_read = stream->fs_iofunc.read(stream->fs_cookie, + dest, + remaining); + } + else + { + bytes_read = _NX_READ(fd, 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..8150b1cff6f14 100644 --- a/libs/libc/stdio/lib_libfwrite.c +++ b/libs/libc/stdio/lib_libfwrite.c @@ -47,8 +47,8 @@ ssize_t lib_fwrite_unlocked(FAR const void *ptr, size_t count, FAR FILE *stream) #ifndef CONFIG_STDIO_DISABLE_BUFFERING { - FAR const unsigned char *start = ptr; - FAR const unsigned char *src = ptr; + FAR const char *start = ptr; + FAR const char *src = ptr; ssize_t ret = ERROR; size_t gulp_size; @@ -72,7 +72,15 @@ 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); + if (stream->fs_iofunc.write != NULL) + { + ret = stream->fs_iofunc.write(stream->fs_cookie, ptr, count); + } + else + { + ret = _NX_WRITE((int)(intptr_t)stream->fs_cookie, ptr, count); + } + if (ret < 0) { _NX_SETERRNO(ret); @@ -132,7 +140,15 @@ 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); + if (stream->fs_iofunc.write != NULL) + { + ret = stream->fs_iofunc.write(stream->fs_cookie, src, count); + } + else + { + ret = _NX_WRITE((int)(intptr_t)stream->fs_cookie, src, count); + } + if (ret < 0) { _NX_SETERRNO(ret); @@ -163,7 +179,16 @@ 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; + if (stream->fs_iofunc.write != NULL) + { + ret = stream->fs_iofunc.write(stream->fs_cookie, ptr, count); + } + else + { + ret = _NX_WRITE((int)(intptr_t)stream->fs_cookie, ptr, count); + } + if (ret < 0) { stream->fs_flags |= __FS_FLAG_ERROR; diff --git a/libs/libc/stdio/lib_rdflush_unlocked.c b/libs/libc/stdio/lib_rdflush_unlocked.c index 70c4758660de2..5b5f275cf84a4 100644 --- a/libs/libc/stdio/lib_rdflush_unlocked.c +++ b/libs/libc/stdio/lib_rdflush_unlocked.c @@ -48,6 +48,8 @@ int lib_rdflush_unlocked(FAR FILE *stream) { + int ret; + /* Sanity checking */ if (stream == NULL) @@ -92,7 +94,18 @@ int lib_rdflush_unlocked(FAR FILE *stream) * user */ - if (lseek(stream->fs_fd, -rdoffset, SEEK_CUR) < 0) + rdoffset = -rdoffset; + if (stream->fs_iofunc.seek != NULL) + { + ret = stream->fs_iofunc.seek(stream->fs_cookie, &rdoffset, + SEEK_CUR); + } + else + { + ret = lseek((int)(intptr_t)stream->fs_cookie, rdoffset, SEEK_CUR); + } + + if (ret < 0) { return ERROR; } diff --git a/libs/libc/stdio/lib_setvbuf.c b/libs/libc/stdio/lib_setvbuf.c index 8d5e5e57037fd..a0cbd9c4d6065 100644 --- a/libs/libc/stdio/lib_setvbuf.c +++ b/libs/libc/stdio/lib_setvbuf.c @@ -131,14 +131,6 @@ int setvbuf(FAR FILE *stream, FAR char *buffer, int mode, size_t size) * BEFORE any operations have been performed on the stream. */ - /* Return EBADF if the file is not open */ - - if (stream->fs_fd < 0) - { - errcode = EBADF; - goto errout_with_lock; - } - /* Return EBUSY if operations have already been performed on the buffer. * Here we really only verify that there is no valid data in the existing * buffer. diff --git a/libs/libc/stdio/lib_ungetc.c b/libs/libc/stdio/lib_ungetc.c index f5ebea1e39452..1b9827c0b143f 100644 --- a/libs/libc/stdio/lib_ungetc.c +++ b/libs/libc/stdio/lib_ungetc.c @@ -52,7 +52,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 ((stream->fs_oflags & O_RDOK) == 0) { return EOF; } diff --git a/sched/tls/task_initinfo.c b/sched/tls/task_initinfo.c index b1fbafcdd5c1d..15f2380ecb261 100644 --- a/sched/tls/task_initinfo.c +++ b/sched/tls/task_initinfo.c @@ -81,8 +81,15 @@ static void task_init_stream(FAR struct streamlist *list) * file descriptor locks this stream. */ - stream[i].fs_fd = i; + stream[i].fs_cookie = (FAR void *)(intptr_t)i; stream[i].fs_oflags = i ? O_WROK : O_RDONLY; + + /* Assign custom callbacks to NULL. */ + + stream[i].fs_iofunc.read = NULL; + stream[i].fs_iofunc.write = NULL; + stream[i].fs_iofunc.seek = NULL; + stream[i].fs_iofunc.close = NULL; #endif /* !CONFIG_STDIO_DISABLE_BUFFERING && CONFIG_STDIO_BUFFER_SIZE > 0 */ } }