diff --git a/include/nuttx/fs/fs.h b/include/nuttx/fs/fs.h index fd6e0157bffab..cfe7107a3d3f1 100644 --- a/include/nuttx/fs/fs.h +++ b/include/nuttx/fs/fs.h @@ -425,6 +425,55 @@ 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. + */ + +typedef ssize_t cookie_read_function_t(void *cookie, char *buf, size_t size); +typedef ssize_t cookie_write_function_t(void *cookie, const char *buf, + size_t size); +typedef off_t cookie_seek_function_t(void *cookie, off_t *offset, + int whence); +typedef int cookie_close_function_t(void *cookie); + +typedef struct +{ + 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; + +struct cookie_s +{ + FAR void *cookie; /* Pointer to the caller's cookie struct */ + cookie_io_functions_t cookie_io; /* Programmer-defined hook functions */ +}; + +/* Definition of internal callbacks for read/write/seek/close operations. + * These are needed to ensure custom callbacks from fopencookie are called + * from user address space and not from kernel address space. These callbacks + * are either routed to fopencookie related callbacks or to internal + * read/write/seek/close operations defined in file system (thus in kernel + * address space) + */ + +typedef ssize_t read_internal_cb_t(FAR void *s, char *buf, size_t size); +typedef ssize_t write_internal_cb_t(FAR void *s, const char *buf, + size_t size); +typedef off_t seek_internal_cb_t(FAR void *s, off_t offset, int whence); +typedef int close_internal_cb_t(FAR void *s); + +typedef struct +{ + FAR read_internal_cb_t *read; + FAR write_internal_cb_t *write; + FAR seek_internal_cb_t *seek; + FAR close_internal_cb_t *close; +} io_callbacks_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,6 +546,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 */ + FAR struct cookie_s fs_cookie; /* Cookie structure for fopencookie */ + io_callbacks_t fs_callbacks; /* Internal callbacks for IO call */ #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..f9708cd6b06cc 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 */ + +ssize_t lib_fread_cb(void *stream, char *buf, size_t size); +ssize_t lib_fwrite_cb(void *stream, const char *buf, size_t size); +off_t lib_fseek_cb(void *stream, off_t offset, int whence); +int lib_fclose_cb(void *stream); + /* Defined in lib_parsehostfile.c */ #ifdef CONFIG_NETDB_HOSTFILE diff --git a/libs/libc/stdio/CMakeLists.txt b/libs/libc/stdio/CMakeLists.txt index 4a26d755b5e45..aedd93e59dbcc 100644 --- a/libs/libc/stdio/CMakeLists.txt +++ b/libs/libc/stdio/CMakeLists.txt @@ -100,7 +100,9 @@ if(CONFIG_FILE_STREAM) lib_setvbuf.c lib_libstream.c lib_libfilelock.c - lib_libgetstreams.c) + lib_libgetstreams.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 4b2de61a569ef..c49095bd0c34e 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 +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..48c13be7c1c5b 100644 --- a/libs/libc/stdio/lib_fclose.c +++ b/libs/libc/stdio/lib_fclose.c @@ -129,7 +129,7 @@ int fclose(FAR FILE *stream) (uintptr_t)stream); status = android_fdsan_close_with_tag(stream->fs_fd, tag); #else - status = close(stream->fs_fd); + status = stream->fs_callbacks.close(stream); #endif /* If close() returns an error but flush() did not then make sure diff --git a/libs/libc/stdio/lib_fopen.c b/libs/libc/stdio/lib_fopen.c index 80b33044c4db3..19d1733151952 100644 --- a/libs/libc/stdio/lib_fopen.c +++ b/libs/libc/stdio/lib_fopen.c @@ -87,6 +87,11 @@ FAR FILE *fdopen(int fd, FAR const char *mode) (uintptr_t)filep)); #endif + filep->fs_callbacks.read = lib_fread_cb; + filep->fs_callbacks.write = lib_fwrite_cb; + filep->fs_callbacks.seek = lib_fseek_cb; + filep->fs_callbacks.close = lib_fclose_cb; + return filep; } @@ -140,6 +145,11 @@ FAR FILE *fopen(FAR const char *path, FAR const char *mode) (uintptr_t)filep)); #endif + filep->fs_callbacks.read = lib_fread_cb; + filep->fs_callbacks.write = lib_fwrite_cb; + filep->fs_callbacks.seek = lib_fseek_cb; + filep->fs_callbacks.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..aa777358a4bb8 --- /dev/null +++ b/libs/libc/stdio/lib_fopencookie.c @@ -0,0 +1,181 @@ +/**************************************************************************** + * 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 + ****************************************************************************/ + +static ssize_t freadcookie(FAR void *s, FAR char *buf, size_t nbytes); +static ssize_t fwritecookie(FAR void *s, FAR const char *buf, size_t nbytes); +static off_t fseekcookie(FAR void *s, off_t offset, int whence); +static int fclosecookie(FAR void *s); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static FAR FILE *fopencookie_init_stream(int oflags) +{ + FAR FILE *stream; + + stream = lib_zalloc(sizeof(FILE)); + if (stream == NULL) + { + set_errno(-ENOMEM); + return NULL; + } + + nxrmutex_init(&stream->fs_lock); + +#ifndef CONFIG_STDIO_DISABLE_BUFFERING +#if CONFIG_STDIO_BUFFER_SIZE > 0 + /* Set up pointers */ + + stream->fs_bufstart = stream->fs_buffer; + stream->fs_bufend = &stream->fs_bufstart[CONFIG_STDIO_BUFFER_SIZE]; + stream->fs_bufpos = stream->fs_bufstart; + stream->fs_bufread = stream->fs_bufstart; + stream->fs_flags = __FS_FLAG_UBF; /* Fake setvbuf and fclose */ + +#ifdef CONFIG_STDIO_LINEBUFFER + /* Setup buffer flags */ + + stream->fs_flags |= __FS_FLAG_LBF; /* Line buffering */ + +#endif /* CONFIG_STDIO_LINEBUFFER */ +#endif /* CONFIG_STDIO_BUFFER_SIZE > 0 */ +#endif /* CONFIG_STDIO_DISABLE_BUFFERING */ + + /* Save the file description and open flags. Setting the + * file descriptor locks this stream. + */ + + stream->fs_fd = -1; + stream->fs_oflags = oflags; + + return stream; +} + +static ssize_t freadcookie(FAR void *s, FAR char *buf, size_t nbytes) +{ + FAR FILE *stream = (FAR FILE *)s; + FAR struct cookie_s *cookie = (FAR struct cookie_s *)&stream->fs_cookie; + int ret; + + DEBUGASSERT(cookie->cookie != NULL && cookie->cookie_io.read != NULL); + + ret = cookie->cookie_io.read(cookie->cookie, buf, nbytes); + return ret; +} + +static ssize_t fwritecookie(FAR void *s, FAR const char *buf, size_t nbytes) +{ + FAR FILE *stream = (FAR FILE *)s; + FAR struct cookie_s *cookie = (FAR struct cookie_s *)&stream->fs_cookie; + int ret; + + DEBUGASSERT(cookie->cookie != NULL && cookie->cookie_io.write != NULL); + + ret = cookie->cookie_io.write(cookie->cookie, buf, nbytes); + return ret; +} + +static off_t fseekcookie(FAR void *s, off_t offset, int whence) +{ + FAR FILE *stream = (FAR FILE *)s; + FAR struct cookie_s *cookie = (FAR struct cookie_s *)&stream->fs_cookie; + int ret; + + DEBUGASSERT(cookie->cookie != NULL && cookie->cookie_io.seek != NULL); + + ret = cookie->cookie_io.seek(cookie->cookie, &offset, whence); + return ret; +} + +static int fclosecookie(FAR void *s) +{ + FAR FILE *stream = (FAR FILE *)s; + FAR struct cookie_s *cookie = (FAR struct cookie_s *)&stream->fs_cookie; + int ret; + + DEBUGASSERT(cookie->cookie != NULL && cookie->cookie_io.close != NULL); + + ret = cookie->cookie_io.close(cookie->cookie); + + free(stream); + + return ret; +} + +/**************************************************************************** + * 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 oflags; + + /* Map the open mode string to open flags */ + + oflags = lib_mode2oflags(mode); + if (oflags < 0) + { + return NULL; + } + + filep = fopencookie_init_stream(oflags); + if (filep == NULL) + { + return NULL; + } + + filep->fs_cookie.cookie = cookie; + filep->fs_cookie.cookie_io = io_funcs; + + filep->fs_callbacks.read = freadcookie; + filep->fs_callbacks.write = fwritecookie; + filep->fs_callbacks.seek = fseekcookie; + filep->fs_callbacks.close = fclosecookie; + + return filep; +} diff --git a/libs/libc/stdio/lib_fseeko.c b/libs/libc/stdio/lib_fseeko.c index 163a79de3890a..4f8f595368729 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_callbacks.seek(stream, 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..33acbf1f7cbc5 100644 --- a/libs/libc/stdio/lib_ftello.c +++ b/libs/libc/stdio/lib_ftello.c @@ -110,7 +110,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_callbacks.seek(stream, 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..384637f2f21e1 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_callbacks.write(stream, + (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_libfread_unlocked.c b/libs/libc/stdio/lib_libfread_unlocked.c index 1fb26b798ddcd..8d5fa102273b4 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_callbacks.read(stream, + (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_callbacks.read(stream, + (char *)stream->fs_bufread, + buffer_available); if (bytes_read < 0) { if (count - remaining > 0) @@ -258,7 +260,8 @@ 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_callbacks.read(stream, (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..4865df7dc8fd6 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_callbacks.write(stream, ptr, count); if (ret < 0) { _NX_SETERRNO(ret); @@ -132,7 +132,7 @@ 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_callbacks.write(stream, (const char *)src, count); if (ret < 0) { _NX_SETERRNO(ret); @@ -163,7 +163,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_callbacks.write(stream, 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..badcf1838f0fd 100644 --- a/libs/libc/stdio/lib_libstream.c +++ b/libs/libc/stdio/lib_libstream.c @@ -69,10 +69,22 @@ 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_callbacks.read = lib_fread_cb; + list->sl_std[0].fs_callbacks.write = lib_fwrite_cb; + list->sl_std[0].fs_callbacks.seek = lib_fseek_cb; + list->sl_std[0].fs_callbacks.close = lib_fclose_cb; nxrmutex_init(&list->sl_std[0].fs_lock); list->sl_std[1].fs_fd = -1; + list->sl_std[1].fs_callbacks.read = lib_fread_cb; + list->sl_std[1].fs_callbacks.write = lib_fwrite_cb; + list->sl_std[1].fs_callbacks.seek = lib_fseek_cb; + list->sl_std[1].fs_callbacks.close = lib_fclose_cb; nxrmutex_init(&list->sl_std[1].fs_lock); list->sl_std[2].fs_fd = -1; + list->sl_std[2].fs_callbacks.read = lib_fread_cb; + list->sl_std[2].fs_callbacks.write = lib_fwrite_cb; + list->sl_std[2].fs_callbacks.seek = lib_fseek_cb; + list->sl_std[2].fs_callbacks.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..97cfb20dd759a 100644 --- a/libs/libc/stdio/lib_rdflush_unlocked.c +++ b/libs/libc/stdio/lib_rdflush_unlocked.c @@ -92,7 +92,7 @@ int lib_rdflush_unlocked(FAR FILE *stream) * user */ - if (lseek(stream->fs_fd, -rdoffset, SEEK_CUR) < 0) + if (stream->fs_callbacks.seek(stream, -rdoffset, SEEK_CUR) < 0) { return ERROR; } diff --git a/libs/libc/stdio/lib_stdio_cb.c b/libs/libc/stdio/lib_stdio_cb.c new file mode 100644 index 0000000000000..bf13cc0b764f4 --- /dev/null +++ b/libs/libc/stdio/lib_stdio_cb.c @@ -0,0 +1,76 @@ +/**************************************************************************** + * 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 "libc.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lib_fseek_cb + ****************************************************************************/ + +off_t lib_fseek_cb(FAR void *s, off_t offset, int whence) +{ + FAR FILE *stream = (FAR FILE *)s; + return lseek(stream->fs_fd, offset, whence); +} + +/**************************************************************************** + * Name: lib_fwrite_cb + ****************************************************************************/ + +ssize_t lib_fwrite_cb(FAR void *s, const char *buf, size_t size) +{ + FAR FILE *stream = (FAR FILE *)s; + return _NX_WRITE(stream->fs_fd, buf, size); +} + +/**************************************************************************** + * Name: lib_fread_cb + ****************************************************************************/ + +ssize_t lib_fread_cb(FAR void *s, char *buf, size_t size) +{ + FAR FILE *stream = (FAR FILE *)s; + return _NX_READ(stream->fs_fd, buf, size); +} + +/**************************************************************************** + * Name: lib_fclose_cb + ****************************************************************************/ + +int lib_fclose_cb(void *s) +{ + FAR FILE *stream = (FAR FILE *)s; + return close(stream->fs_fd); +}