diff --git a/include/nuttx/fs/fs.h b/include/nuttx/fs/fs.h index fd6e0157bffab..0aea17b606204 100644 --- a/include/nuttx/fs/fs.h +++ b/include/nuttx/fs/fs.h @@ -425,6 +425,27 @@ 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 int 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; + /* 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. 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/Make.defs b/libs/libc/stdio/Make.defs index 4b2de61a569ef..a531566d67351 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 endif # Add the stdio directory to the build diff --git a/libs/libc/stdio/lib_fopencookie.c b/libs/libc/stdio/lib_fopencookie.c new file mode 100644 index 0000000000000..a6f91d97dccba --- /dev/null +++ b/libs/libc/stdio/lib_fopencookie.c @@ -0,0 +1,229 @@ +/**************************************************************************** + * 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 "libc.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct cookie_s +{ + FAR void *cookie; /* Pointer to the caller's cookie struct */ + cookie_io_functions_t cookie_io; /* Programmer-defined hook functions */ + int cookie_fd; /* File descriptor */ +}; + +static ssize_t freadcookie(FAR struct file *filep, FAR char *buf, + size_t nbytes); +static ssize_t fwritecookie(FAR struct file *filep, FAR const char *buf, + size_t nbytes); +static off_t fseekcookie(FAR struct file *filep, off_t offset, int whence); +static int fclosecookie(FAR struct file *filep); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations cookie_fops = +{ + NULL, /* open */ + fclosecookie, /* close */ + freadcookie, /* read */ + fwritecookie, /* write */ + fseekcookie, /* seek */ + NULL, /* ioctl */ + NULL, /* mmap */ + NULL, /* truncate */ + NULL, /* poll */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static ssize_t freadcookie(FAR struct file *filep, FAR char *buf, + size_t nbytes) +{ + FAR struct cookie_s *cookie = (FAR struct cookie_s *)filep->f_priv; + 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 struct file *filep, FAR const char *buf, + size_t nbytes) +{ + FAR struct cookie_s *cookie = (FAR struct cookie_s *)filep->f_priv; + int ret; + + DEBUGASSERT(cookie->cookie != NULL && cookie->cookie_io.write != NULL); + + ret = cookie->cookie_io.write(cookie->cookie, (FAR const char *)buf, + nbytes); + return ret; +} + +static off_t fseekcookie(FAR struct file *filep, off_t offset, int whence) +{ + FAR struct cookie_s *cookie = (FAR struct cookie_s *)filep->f_priv; + 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 struct file *filep) +{ + FAR struct cookie_s *cookie = (FAR struct cookie_s *)filep->f_priv; + int ret; + + DEBUGASSERT(cookie->cookie != NULL && cookie->cookie_io.close != NULL); + + ret = cookie->cookie_io.close(cookie->cookie); + + free(filep->f_priv); + free(filep->f_inode); + close(cookie->cookie_fd); + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: fopencookie + ****************************************************************************/ + +FAR FILE *fopencookie(FAR void *cookie, FAR const char *mode, + cookie_io_functions_t io_funcs) +{ + FAR FILE *filestructp = NULL; + FAR struct file filep; + FAR struct inode *inode; + FAR struct cookie_s *cookie_priv; + int oflags; + int ret; + int fd; + + /* Map the open mode string to open flags */ + + oflags = lib_mode2oflags(mode); + if (oflags < 0) + { + return NULL; + } + + /* Allocate necessary structures. */ + + inode = lib_zalloc(sizeof(struct inode)); + if (inode == NULL) + { + set_errno(-ENOMEM); + goto fopencookie_return; + } + + cookie_priv = lib_zalloc(sizeof(struct cookie_s)); + if (cookie_priv == NULL) + { + free(inode); + set_errno(-ENOMEM); + goto fopencookie_return; + } + + memset(&filep, 0, sizeof(filep)); + + /* Fill cookie_priv structure. We need to add cookie provided by user + * and functions providd by user. + */ + + cookie_priv->cookie = cookie; + cookie_priv->cookie_io.read = io_funcs.read; + cookie_priv->cookie_io.write = io_funcs.write; + cookie_priv->cookie_io.seek = io_funcs.seek; + cookie_priv->cookie_io.close = io_funcs.close; + + /* Add file operations */ + + inode->u.i_ops = &cookie_fops; + + filep.f_oflags = oflags; + filep.f_inode = inode; + filep.f_priv = cookie_priv; + + /* And get file descriptor. */ + + fd = file_allocate_from_tcb(nxsched_self(), filep.f_inode, filep.f_oflags, + 0, filep.f_priv, 0, false); + if (fd < 0) + { + set_errno(fd); + filestructp = NULL; + goto fopencookie_return_err; + } + + cookie_priv->cookie_fd = fd; + + /* Call fs_fdopen to ensure initialization of file structure. */ + + ret = fs_fdopen(fd, oflags, NULL, &filestructp); + if (ret < 0) + { + /* Don't forget to close the file descriptor if any other + * failures are reported by fdopen(). + */ + + close(fd); + + set_errno(-ret); + filestructp = NULL; + goto fopencookie_return_err; + } + + goto fopencookie_return; + +fopencookie_return_err: + free(inode); + free(cookie_priv); + +fopencookie_return: + return filestructp; +}