diff --git a/Directories.mk b/Directories.mk index 2e55b91a940..271e8aba516 100644 --- a/Directories.mk +++ b/Directories.mk @@ -131,7 +131,7 @@ else OTHERDIRS += audio endif -ifeq ($(CONFIG_DRIVERS_WIRELESS),y) +ifeq ($(CONFIG_WIRELESS),y) NONFSDIRS += wireless else OTHERDIRS += wireless diff --git a/arch/risc-v/src/common/up_initialize.c b/arch/risc-v/src/common/up_initialize.c index acf400f8268..83effe7494b 100644 --- a/arch/risc-v/src/common/up_initialize.c +++ b/arch/risc-v/src/common/up_initialize.c @@ -44,6 +44,7 @@ #include #include #include +#include #include @@ -188,11 +189,19 @@ void up_initialize(void) ramlog_consoleinit(); #endif - /* Initialize the system logging device */ +#if CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_PSEUDOTERM_SUSV1) + /* Register the master pseudo-terminal multiplexor device */ -#ifdef CONFIG_SYSLOG_CHAR - syslog_initialize(); + (void)ptmx_register(); #endif + + /* Early initialization of the system logging device. Some SYSLOG channel + * can be initialized early in the initialization sequence because they + * depend on only minimal OS initialization. + */ + + syslog_initialize(SYSLOG_INIT_EARLY); + #ifdef CONFIG_RAMLOG_SYSLOG ramlog_sysloginit(); #endif diff --git a/fs/Kconfig b/fs/Kconfig index cb9963187c0..1bada41f4aa 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -86,4 +86,5 @@ source fs/smartfs/Kconfig source fs/binfs/Kconfig source fs/procfs/Kconfig source fs/unionfs/Kconfig +source fs/userfs/Kconfig source fs/hostfs/Kconfig diff --git a/fs/Makefile b/fs/Makefile index 6ab682d28f2..bba917231c3 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -1,7 +1,8 @@ ############################################################################ # fs/Makefile # -# Copyright (C) 2007, 2008, 2011-2014, 2016-2017 Gregory Nutt. All rights reserved. +# Copyright (C) 2007, 2008, 2011-2014, 2016-2017 Gregory Nutt. All rights +# reserved. # Author: Gregory Nutt # # Redistribution and use in source and binary forms, with or without @@ -71,6 +72,7 @@ include smartfs/Make.defs include binfs/Make.defs include procfs/Make.defs include unionfs/Make.defs +include userfs/Make.defs include hostfs/Make.defs endif diff --git a/fs/mount/fs_gettype.c b/fs/mount/fs_gettype.c index 5db7a8d2862..4c97c9d75b7 100644 --- a/fs/mount/fs_gettype.c +++ b/fs/mount/fs_gettype.c @@ -132,6 +132,12 @@ FAR const char *fs_gettype(FAR struct statfs *statbuf) break; #endif +#ifdef CONFIG_FS_USERFS + case USERFS_MAGIC: + fstype = "userfs"; + break; +#endif + default: fstype = "Unrecognized"; break; @@ -140,4 +146,4 @@ FAR const char *fs_gettype(FAR struct statfs *statbuf) return fstype; } -#endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS */ \ No newline at end of file +#endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS */ diff --git a/fs/mount/fs_mount.c b/fs/mount/fs_mount.c index 2fc9d7376fd..62a982b40e8 100644 --- a/fs/mount/fs_mount.c +++ b/fs/mount/fs_mount.c @@ -79,7 +79,7 @@ #if defined(CONFIG_FS_NXFFS) || defined(CONFIG_FS_BINFS) || \ defined(CONFIG_FS_PROCFS) || defined(CONFIG_NFS) || \ - defined(CONFIG_FS_TMPFS) + defined(CONFIG_FS_TMPFS) || defined(CONFIG_FS_USERFS) # define NONBDFS_SUPPORT #endif @@ -139,6 +139,9 @@ extern const struct mountpt_operations binfs_operations; #ifdef CONFIG_FS_PROCFS extern const struct mountpt_operations procfs_operations; #endif +#ifdef CONFIG_FS_USERFS +extern const struct mountpt_operations userfs_operations; +#endif #ifdef CONFIG_FS_HOSTFS extern const struct mountpt_operations hostfs_operations; #endif @@ -160,6 +163,9 @@ static const struct fsmap_t g_nonbdfsmap[] = #ifdef CONFIG_FS_PROCFS { "procfs", &procfs_operations }, #endif +#ifdef CONFIG_FS_USERFS + { "userfs", &userfs_operations }, +#endif #ifdef CONFIG_FS_HOSTFS { "hostfs", &hostfs_operations }, #endif diff --git a/fs/userfs/Kconfig b/fs/userfs/Kconfig new file mode 100644 index 00000000000..9e6bdf917bf --- /dev/null +++ b/fs/userfs/Kconfig @@ -0,0 +1,14 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config FS_USERFS + bool "User file system" + default n + depends on NET_LOCAL + ---help--- + Enable support for user file system. See include/nuttx/fs/userfs.h + +if FS_USERFS +endif diff --git a/fs/userfs/Make.defs b/fs/userfs/Make.defs new file mode 100644 index 00000000000..8432d8d3d03 --- /dev/null +++ b/fs/userfs/Make.defs @@ -0,0 +1,46 @@ +############################################################################ +# fs/userfs/Make.defs +# +# Copyright (C) 2017 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +ifeq ($(CONFIG_FS_USERFS),y) + +# Add the userfs C files to the build + +CSRCS += fs_userfs.c + +# Add the userfs directory to the build + +DEPPATH += --dep-path userfs +VPATH += :userfs +endif diff --git a/fs/userfs/fs_userfs.c b/fs/userfs/fs_userfs.c new file mode 100644 index 00000000000..fbdc216eaf5 --- /dev/null +++ b/fs/userfs/fs_userfs.c @@ -0,0 +1,1625 @@ +/**************************************************************************** + * fs/userfs/fs_userfs.c + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define IOBUFFER_SIZE(p) (USERFS_REQ_MAXSIZE + (p)->mxwrite) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure holds the internal state of the UserFS proxy */ + +struct userfs_state_s +{ + /* Fields copied from struct userfs_config_s */ + + size_t mxwrite; /* The max size of a write data */ + + /* Internal state */ + + uint16_t instance; /* UserFS instance number */ + struct socket psock; /* Client socket instance */ + struct sockaddr_un server; /* Server address */ + socklen_t addrlen; /* Size of server address */ + + /* I/O Buffer (actual size depends on USERFS_REQ_MAXSIZE and the configured + * mxwrite). + */ + + uint8_t iobuffer[1]; +}; + +#define SIZEOF_USERFS_STATE_S(n) (sizeof(struct userfs_state_s) + (n) - 1) + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int userfs_open(FAR struct file *filep, const char *relpath, + int oflags, mode_t mode); +static int userfs_close(FAR struct file *filep); +static ssize_t userfs_read(FAR struct file *filep, char *buffer, + size_t buflen); +static ssize_t userfs_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static ssize_t userfs_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static off_t userfs_seek(FAR struct file *filep, off_t offset, int whence); +static int userfs_ioctl(FAR struct file *filep, int cmd, + unsigned long arg); + +static int userfs_sync(FAR struct file *filep); +static int userfs_dup(FAR const struct file *oldp, FAR struct file *newp); +static int userfs_fstat(FAR const struct file *filep, + FAR struct stat *buf); + +static int userfs_opendir(FAR struct inode *mountpt, + FAR const char *relpath, FAR struct fs_dirent_s *dir); +static int userfs_closedir(FAR struct inode *mountpt, + FAR struct fs_dirent_s *dir); +static int userfs_readdir(FAR struct inode *mountpt, + FAR struct fs_dirent_s *dir); +static int userfs_rewinddir(FAR struct inode *mountpt, + FAR struct fs_dirent_s *dir); + +static int userfs_bind(FAR struct inode *blkdriver, FAR const void *data, + FAR void **handle); +static int userfs_unbind(FAR void *handle, FAR struct inode **blkdriver, + unsigned int flags); +static int userfs_statfs(FAR struct inode *mountpt, + FAR struct statfs *buf); + +static int userfs_unlink(FAR struct inode *mountpt, + FAR const char *relpath); +static int userfs_mkdir(FAR struct inode *mountpt, + FAR const char *relpath, mode_t mode); +static int userfs_rmdir(FAR struct inode *mountpt, + FAR const char *relpath); +static int userfs_rename(FAR struct inode *mountpt, + FAR const char *oldrelpath, FAR const char *newrelpath); +static int userfs_stat(FAR struct inode *mountpt, FAR const char *relpath, + FAR struct stat *buf); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* See fs_mount.c -- this structure is explicitly extern'ed there. + * We use the old-fashioned kind of initializers so that this will compile + * with any compiler. + */ + +const struct mountpt_operations userfs_operations = +{ + userfs_open, /* open */ + userfs_close, /* close */ + userfs_read, /* read */ + userfs_write, /* write */ + userfs_seek, /* seek */ + userfs_ioctl, /* ioctl */ + + userfs_sync, /* sync */ + userfs_dup, /* dup */ + userfs_fstat, /* fstat */ + + userfs_opendir, /* opendir */ + userfs_closedir, /* closedir */ + userfs_readdir, /* readdir */ + userfs_rewinddir, /* rewinddir */ + + userfs_bind, /* bind */ + userfs_unbind, /* unbind */ + userfs_statfs, /* statfs */ + + userfs_unlink, /* unlink */ + userfs_mkdir, /* mkdir */ + userfs_rmdir, /* rmdir */ + userfs_rename, /* rename */ + userfs_stat /* stat */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: userfs_open + ****************************************************************************/ + +static int userfs_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode) +{ + FAR struct userfs_state_s *priv; + FAR struct userfs_open_request_s *req; + FAR struct userfs_open_response_s *resp; + ssize_t nsent; + ssize_t nrecvd; + int pathlen; + + finfo("Open '%s'\n", relpath); + + DEBUGASSERT(filep != NULL && + filep->f_inode != NULL && + filep->f_inode->i_private != NULL); + priv = filep->f_inode->i_private; + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_open_request_s *)priv->iobuffer; + req->req = USERFS_REQ_OPEN; + req->oflags = oflags; + req->mode = mode; + + DEBUGASSERT(relpath != NULL); + pathlen = strlen(relpath); + if (pathlen > priv->mxwrite) + { + return -E2BIG; + } + + strncpy(req->relpath, relpath, priv->mxwrite); + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + SIZEOF_USERFS_OPEN_REQUEST_S(pathlen + 1), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd != sizeof(struct userfs_open_response_s)) + { + ferr("ERROR: Response size incorrect: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + /* Save the returned openinfo as the filep private data. */ + + resp = (FAR struct userfs_open_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_OPEN) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + filep->f_priv = resp->openinfo; + + return resp->ret; +} + +/**************************************************************************** + * Name: userfs_close + ****************************************************************************/ + +static int userfs_close(FAR struct file *filep) +{ + FAR struct userfs_state_s *priv; + FAR struct userfs_close_request_s *req; + FAR struct userfs_close_response_s *resp; + ssize_t nsent; + ssize_t nrecvd; + + DEBUGASSERT(filep != NULL && + filep->f_inode != NULL && + filep->f_inode->i_private != NULL); + priv = filep->f_inode->i_private; + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_close_request_s *)priv->iobuffer; + req->req = USERFS_REQ_CLOSE; + req->openinfo = filep->f_priv; + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + sizeof(struct userfs_close_request_s), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd != sizeof(struct userfs_close_response_s)) + { + ferr("ERROR: Response size incorrect: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + resp = (FAR struct userfs_close_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_CLOSE) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + return resp->ret; +} + +/**************************************************************************** + * Name: userfs_read + ****************************************************************************/ + +static ssize_t userfs_read(FAR struct file *filep, char *buffer, + size_t buflen) +{ + FAR struct userfs_state_s *priv; + FAR struct userfs_read_request_s *req; + FAR struct userfs_read_response_s *resp; + ssize_t nsent; + ssize_t nrecvd; + int respsize; + + finfo("Read %d bytes from offset %d\n", buflen, filep->f_pos); + + DEBUGASSERT(filep != NULL && + filep->f_inode != NULL && + filep->f_inode->i_private != NULL); + priv = filep->f_inode->i_private; + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_read_request_s *)priv->iobuffer; + req->req = USERFS_REQ_READ; + req->openinfo = filep->f_priv; + req->readlen = buflen; + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + sizeof(struct userfs_read_request_s), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd < SIZEOF_USERFS_READ_RESPONSE_S(0)) + { + ferr("ERROR: Response too small: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + resp = (FAR struct userfs_read_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_CLOSE) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + if (resp->nread > buflen) + { + ferr("ERROR: Response size too large: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + respsize = SIZEOF_USERFS_READ_RESPONSE_S(resp->nread); + if (respsize != nrecvd) + { + ferr("ERROR: Incorrect response size: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + /* Copy the received data to the user buffer */ + + memcpy(buffer, resp->rddata, resp->nread); + return resp->nread; +} + +/**************************************************************************** + * Name: userfs_write + ****************************************************************************/ + +static ssize_t userfs_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen) +{ + FAR struct userfs_state_s *priv; + FAR struct userfs_write_request_s *req; + FAR struct userfs_write_response_s *resp; + ssize_t nsent; + ssize_t nrecvd; + + finfo("Write %d bytes to offset %d\n", buflen, filep->f_pos); + + DEBUGASSERT(filep != NULL && + filep->f_inode != NULL && + filep->f_inode->i_private != NULL); + priv = filep->f_inode->i_private; + + /* Perform multiple writes if the write length exceeds the configured maximum (mxwrite). + */ + + if (buflen > priv->mxwrite) + { + return -E2BIG; /* No implememented yet */ + } + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_write_request_s *)priv->iobuffer; + req->req = USERFS_REQ_WRITE; + req->openinfo = filep->f_priv; + req->writelen = buflen; + memcpy(req->wrdata, buffer, buflen); + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + SIZEOF_USERFS_WRITE_REQUEST_S(buflen), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd != sizeof(struct userfs_write_response_s)) + { + ferr("ERROR: Response size incorrect: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + resp = (FAR struct userfs_write_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_WRITE) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + return resp->nwritten; +} + +/**************************************************************************** + * Name: userfs_seek + ****************************************************************************/ + +static off_t userfs_seek(FAR struct file *filep, off_t offset, int whence) +{ + FAR struct userfs_state_s *priv; + FAR struct userfs_seek_request_s *req; + FAR struct userfs_seek_response_s *resp; + ssize_t nsent; + ssize_t nrecvd; + + finfo("Offset %lu bytes to whence=%d\n", (unsigned long)offset, whence); + + DEBUGASSERT(filep != NULL && + filep->f_inode != NULL && + filep->f_inode->i_private != NULL); + priv = filep->f_inode->i_private; + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_seek_request_s *)priv->iobuffer; + req->req = USERFS_REQ_SEEK; + req->openinfo = filep->f_priv; + req->offset = offset; + req->whence = whence; + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + sizeof(struct userfs_seek_request_s), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd != sizeof(struct userfs_seek_response_s)) + { + ferr("ERROR: Response size incorrect: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + resp = (FAR struct userfs_seek_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_SEEK) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + return resp->ret; +} + +/**************************************************************************** + * Name: userfs_ioctl + ****************************************************************************/ + +static int userfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct userfs_state_s *priv; + FAR struct userfs_ioctl_request_s *req; + FAR struct userfs_ioctl_response_s *resp; + ssize_t nsent; + ssize_t nrecvd; + + finfo("cmd: %d arg: %08lx\n", cmd, arg); + + DEBUGASSERT(filep != NULL && + filep->f_inode != NULL && + filep->f_inode->i_private != NULL); + priv = filep->f_inode->i_private; + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_ioctl_request_s *)priv->iobuffer; + req->req = USERFS_REQ_IOCTL; + req->openinfo = filep->f_priv; + req->cmd = cmd; + req->arg = arg; + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + sizeof(struct userfs_ioctl_request_s), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd != sizeof(struct userfs_ioctl_response_s)) + { + ferr("ERROR: Response size incorrect: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + resp = (FAR struct userfs_ioctl_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_IOCTL) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + return resp->ret; +} + +/**************************************************************************** + * Name: userfs_sync + ****************************************************************************/ + +static int userfs_sync(FAR struct file *filep) +{ + FAR struct userfs_state_s *priv; + FAR struct userfs_sync_request_s *req; + FAR struct userfs_sync_response_s *resp; + ssize_t nsent; + ssize_t nrecvd; + + DEBUGASSERT(filep != NULL && + filep->f_inode != NULL && + filep->f_inode->i_private != NULL); + priv = filep->f_inode->i_private; + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_sync_request_s *)priv->iobuffer; + req->req = USERFS_REQ_SYNC; + req->openinfo = filep->f_priv; + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + sizeof(struct userfs_sync_request_s), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd != sizeof(struct userfs_sync_response_s)) + { + ferr("ERROR: Response size incorrect: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + resp = (FAR struct userfs_sync_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_SYNC) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + return resp->ret; +} + +/**************************************************************************** + * Name: userfs_dup + * + * Description: + * Duplicate open file data in the new file structure. + * + ****************************************************************************/ + +static int userfs_dup(FAR const struct file *oldp, FAR struct file *newp) +{ + FAR struct userfs_state_s *priv; + FAR struct userfs_dup_request_s *req; + FAR struct userfs_dup_response_s *resp; + ssize_t nsent; + ssize_t nrecvd; + + finfo("Dup %p->%p\n", oldp, newp); + + DEBUGASSERT(oldp != NULL && + oldp->f_inode != NULL && + oldp->f_inode->i_private != NULL); + priv = oldp->f_inode->i_private; + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_dup_request_s *)priv->iobuffer; + req->req = USERFS_REQ_DUP; + req->openinfo = oldp->f_priv; + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + sizeof(struct userfs_dup_request_s), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd != sizeof(struct userfs_dup_response_s)) + { + ferr("ERROR: Response size incorrect: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + resp = (FAR struct userfs_dup_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_DUP) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + newp->f_priv = resp->openinfo; + return resp->ret; +} + +/**************************************************************************** + * Name: userfs_fstat + * + * Description: + * Obtain information about an open file associated with the file + * descriptor 'fd', and will write it to the area pointed to by 'buf'. + * + ****************************************************************************/ + +static int userfs_fstat(FAR const struct file *filep, FAR struct stat *buf) +{ + FAR struct userfs_state_s *priv; + FAR struct userfs_fstat_request_s *req; + FAR struct userfs_fstat_response_s *resp; + ssize_t nsent; + ssize_t nrecvd; + + DEBUGASSERT(filep != NULL && + filep->f_inode != NULL && + filep->f_inode->i_private != NULL); + priv = filep->f_inode->i_private; + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_fstat_request_s *)priv->iobuffer; + req->req = USERFS_REQ_FSTAT; + req->openinfo = filep->f_priv; + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + sizeof(struct userfs_fstat_request_s), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd != sizeof(struct userfs_fstat_response_s)) + { + ferr("ERROR: Response size incorrect: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + resp = (FAR struct userfs_fstat_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_FSTAT) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + /* Return the status of the directory entry */ + + DEBUGASSERT(buf != NULL); + memcpy(buf, &resp->buf, sizeof(struct stat)); + return resp->ret; +} + +/**************************************************************************** + * Name: userfs_opendir + * + * Description: + * Open a directory + * + ****************************************************************************/ + +static int userfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, + FAR struct fs_dirent_s *dir) +{ + FAR struct userfs_state_s *priv; + FAR struct userfs_opendir_request_s *req; + FAR struct userfs_opendir_response_s *resp; + ssize_t nsent; + ssize_t nrecvd; + int pathlen; + + finfo("relpath: \"%s\"\n", relpath ? relpath : "NULL"); + + DEBUGASSERT(mountpt != NULL && + mountpt->i_private != NULL); + priv = mountpt->i_private; + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_opendir_request_s *)priv->iobuffer; + req->req = USERFS_REQ_OPENDIR; + + DEBUGASSERT(relpath != NULL); + pathlen = strlen(relpath); + if (pathlen > priv->mxwrite) + { + return -E2BIG; + } + + strncpy(req->relpath, relpath, priv->mxwrite); + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + SIZEOF_USERFS_OPENDIR_REQUEST_S(pathlen + 1), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd != sizeof(struct userfs_opendir_response_s)) + { + ferr("ERROR: Response size incorrect: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + resp = (FAR struct userfs_opendir_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_OPENDIR) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + /* Save the opaque dir reference in struct fs_dirent_s */ + + DEBUGASSERT(dir != NULL); + dir->u.userfs.fs_dir = resp->dir; + return resp->ret; +} + +/**************************************************************************** + * Name: userfs_closedir + * + * Description: + * Close a directory + * + ****************************************************************************/ + +static int userfs_closedir(FAR struct inode *mountpt, + FAR struct fs_dirent_s *dir) +{ + FAR struct userfs_state_s *priv; + FAR struct userfs_closedir_request_s *req; + FAR struct userfs_closedir_response_s *resp; + ssize_t nsent; + ssize_t nrecvd; + + DEBUGASSERT(mountpt != NULL && + mountpt->i_private != NULL); + priv = mountpt->i_private; + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_closedir_request_s *)priv->iobuffer; + req->req = USERFS_REQ_CLOSEDIR; + req->dir = dir->u.userfs.fs_dir; + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + sizeof(struct userfs_closedir_request_s), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd != sizeof(struct userfs_closedir_response_s)) + { + ferr("ERROR: Response size incorrect: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + resp = (FAR struct userfs_closedir_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_CLOSEDIR) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + return resp->ret; +} + +/**************************************************************************** + * Name: userfs_readdir + * + * Description: Read the next directory entry + * + ****************************************************************************/ + +static int userfs_readdir(FAR struct inode *mountpt, + FAR struct fs_dirent_s *dir) +{ + FAR struct userfs_state_s *priv; + FAR struct userfs_readdir_request_s *req; + FAR struct userfs_readdir_response_s *resp; + ssize_t nsent; + ssize_t nrecvd; + + DEBUGASSERT(mountpt != NULL && + mountpt->i_private != NULL); + priv = mountpt->i_private; + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_readdir_request_s *)priv->iobuffer; + req->req = USERFS_REQ_READDIR; + req->dir = dir->u.userfs.fs_dir; + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + sizeof(struct userfs_readdir_request_s), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd != sizeof(struct userfs_readdir_response_s)) + { + ferr("ERROR: Response size incorrect: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + resp = (FAR struct userfs_readdir_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_READDIR) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + /* Return the dirent */ + + DEBUGASSERT(dir != NULL); + memcpy(&dir->fd_dir, &resp->entry, sizeof(struct dirent)); + return resp->ret; +} + +/**************************************************************************** + * Name: userfs_rewindir + * + * Description: Reset directory read to the first entry + * + ****************************************************************************/ + +static int userfs_rewinddir(FAR struct inode *mountpt, + FAR struct fs_dirent_s *dir) +{ + FAR struct userfs_state_s *priv; + FAR struct userfs_rewinddir_request_s *req; + FAR struct userfs_rewinddir_response_s *resp; + ssize_t nsent; + ssize_t nrecvd; + + DEBUGASSERT(mountpt != NULL && + mountpt->i_private != NULL); + priv = mountpt->i_private; + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_rewinddir_request_s *)priv->iobuffer; + req->req = USERFS_REQ_REWINDDIR; + req->dir = dir->u.userfs.fs_dir; + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + sizeof(struct userfs_rewinddir_request_s), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd != sizeof(struct userfs_rewinddir_response_s)) + { + ferr("ERROR: Response size incorrrect: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + resp = (FAR struct userfs_rewinddir_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_REWINDDIR) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + return resp->ret; +} + +/**************************************************************************** + * Name: userfs_bind + * + * Description: This implements a portion of the mount operation. This + * function allocates and initializes the mountpoint private data and + * binds the blockdriver inode to the filesystem private data. The final + * binding of the private data (containing the blockdriver) to the + * mountpoint is performed by mount(). + * + ****************************************************************************/ + +static int userfs_bind(FAR struct inode *blkdriver, FAR const void *data, + FAR void **handle) +{ + FAR struct userfs_state_s *priv; + FAR const struct userfs_config_s *config; + unsigned int iolen; + int ret; + + DEBUGASSERT(data != NULL && handle != NULL); + config = (FAR const struct userfs_config_s *)data; + DEBUGASSERT(config->instance >= 0 && config->instance <= UINT16_MAX); + + /* Allocate an instance of the UserFS state structure */ + + iolen = USERFS_REQ_MAXSIZE + config->mxwrite; + priv = (FAR struct userfs_state_s *)kmm_malloc(SIZEOF_USERFS_STATE_S(iolen)); + if (priv == NULL) + { + ferr("ERROR: Failed to allocate state structure\n"); + return -ENOMEM; + } + + /* Copy the configuration data into the allocated structure. Why? First + * we can't be certain of the life time of the memory underlying the config + * reference. Also, in the KERNEL build, the config data will like in + * process-specific memory and cannot be shared across processes. + */ + + priv->mxwrite = config->mxwrite; + priv->instance = config->instance; + + /* Preset the server address */ + + priv->server.sun_family = AF_LOCAL; + snprintf(priv->server.sun_path, UNIX_PATH_MAX, USERFS_SERVER_FMT, + config->instance); + priv->server.sun_path[UNIX_PATH_MAX - 1] = '\0'; + + priv->addrlen = strlen(priv->server.sun_path) + sizeof(sa_family_t) + 1; + + /* Create a new Unix domain datagram server socket */ + + ret = psock_socket(PF_LOCAL, SOCK_DGRAM, 0, &priv->psock); + if (ret < 0) + { + printf("client: ERROR socket failure %d\n", ret); + goto errout_with_alloc; + } + + /* Mounted! */ + + *handle = (FAR void *)priv; + return OK; + +errout_with_alloc: + kmm_free(priv); + return ret; +} + +/**************************************************************************** + * Name: userfs_unbind + * + * Description: This implements the filesystem portion of the umount + * operation. + * + ****************************************************************************/ + +static int userfs_unbind(FAR void *handle, FAR struct inode **blkdriver, + unsigned int flags) +{ + FAR struct userfs_state_s *priv = (FAR struct userfs_state_s *)handle; + FAR struct userfs_destroy_request_s *req; + FAR struct userfs_destroy_response_s *resp; + ssize_t nsent; + ssize_t nrecvd; + + DEBUGASSERT(priv != NULL); + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_destroy_request_s *)priv->iobuffer; + req->req = USERFS_REQ_DESTROY; + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + sizeof(struct userfs_destroy_request_s), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd != sizeof(struct userfs_destroy_response_s)) + { + ferr("ERROR: Response size incorrect: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + resp = (FAR struct userfs_destroy_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_DESTROY) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + /* If the destruction failed, then refuse to unmount at this time */ + + if (resp->ret < 0) + { + return resp->ret; + } + + /* Free resources and return success */ + + psock_close(&priv->psock); + kmm_free(priv); + return OK; +} + +/**************************************************************************** + * Name: userfs_statfs + * + * Description: + * Return filesystem statistics + * + ****************************************************************************/ + +static int userfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf) +{ + FAR struct userfs_state_s *priv; + FAR struct userfs_statfs_request_s *req; + FAR struct userfs_statfs_response_s *resp; + ssize_t nsent; + ssize_t nrecvd; + + DEBUGASSERT(mountpt != NULL && + mountpt->i_private != NULL); + priv = mountpt->i_private; + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_statfs_request_s *)priv->iobuffer; + req->req = USERFS_REQ_STATFS; + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + sizeof(struct userfs_statfs_request_s), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd != sizeof(struct userfs_unlink_response_s)) + { + ferr("ERROR: Response size incorrect: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + resp = (FAR struct userfs_statfs_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_STATFS) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + /* Return the status of the file system */ + + DEBUGASSERT(buf != NULL); + memcpy(buf, &resp->buf, sizeof(struct statfs)); + return resp->ret; +} + +/**************************************************************************** + * Name: userfs_unlink + * + * Description: + * Remove a directory entry + * + ****************************************************************************/ + +static int userfs_unlink(FAR struct inode *mountpt, + FAR const char *relpath) +{ + FAR struct userfs_state_s *priv; + FAR struct userfs_unlink_request_s *req; + FAR struct userfs_unlink_response_s *resp; + ssize_t nsent; + ssize_t nrecvd; + int pathlen; + + DEBUGASSERT(mountpt != NULL && + mountpt->i_private != NULL); + priv = mountpt->i_private; + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_unlink_request_s *)priv->iobuffer; + req->req = USERFS_REQ_UNLINK; + + DEBUGASSERT(relpath != NULL); + pathlen = strlen(relpath); + if (pathlen > priv->mxwrite) + { + return -E2BIG; + } + + strncpy(req->relpath, relpath, priv->mxwrite); + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + SIZEOF_USERFS_UNLINK_REQUEST_S(pathlen + 1), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd != sizeof(struct userfs_unlink_response_s)) + { + ferr("ERROR: Response size incorrect: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + resp = (FAR struct userfs_unlink_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_UNLINK) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + return resp->ret; +} + +/**************************************************************************** + * Name: userfs_mkdir + * + * Description: + * Create a new directory + * + ****************************************************************************/ + +static int userfs_mkdir(FAR struct inode *mountpt, + FAR const char *relpath, mode_t mode) +{ + FAR struct userfs_state_s *priv; + FAR struct userfs_mkdir_request_s *req; + FAR struct userfs_mkdir_response_s *resp; + ssize_t nsent; + ssize_t nrecvd; + int pathlen; + + DEBUGASSERT(mountpt != NULL && + mountpt->i_private != NULL); + priv = mountpt->i_private; + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_mkdir_request_s *)priv->iobuffer; + req->req = USERFS_REQ_MKDIR; + req->mode = mode; + + DEBUGASSERT(relpath != NULL); + pathlen = strlen(relpath); + if (pathlen > priv->mxwrite) + { + return -E2BIG; + } + + strncpy(req->relpath, relpath, priv->mxwrite); + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + SIZEOF_USERFS_MKDIR_REQUEST_S(pathlen + 1), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd != sizeof(struct userfs_mkdir_response_s)) + { + ferr("ERROR: Response size incorrect: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + resp = (FAR struct userfs_mkdir_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_MKDIR) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + return resp->ret; +} + +/**************************************************************************** + * Name: userfs_rmdir + * + * Description: + * Remove a directory + * + ****************************************************************************/ + +static int userfs_rmdir(FAR struct inode *mountpt, + FAR const char *relpath) +{ + FAR struct userfs_state_s *priv; + FAR struct userfs_rmdir_request_s *req; + FAR struct userfs_rmdir_response_s *resp; + ssize_t nsent; + ssize_t nrecvd; + int pathlen; + + DEBUGASSERT(mountpt != NULL && + mountpt->i_private != NULL); + priv = mountpt->i_private; + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_rmdir_request_s *)priv->iobuffer; + req->req = USERFS_REQ_RMDIR; + + DEBUGASSERT(relpath != NULL); + pathlen = strlen(relpath); + if (pathlen > priv->mxwrite) + { + return -E2BIG; + } + + strncpy(req->relpath, relpath, priv->mxwrite); + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + SIZEOF_USERFS_RMDIR_REQUEST_S(pathlen + 1), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd != sizeof(struct userfs_rmdir_response_s)) + { + ferr("ERROR: Response size incorrect: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + resp = (FAR struct userfs_rmdir_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_RMDIR) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + return resp->ret; +} + +/**************************************************************************** + * Name: userfs_rename + * + * Description: + * Rename a directory entry + * + ****************************************************************************/ + +static int userfs_rename(FAR struct inode *mountpt, + FAR const char *oldrelpath, + FAR const char *newrelpath) +{ + FAR struct userfs_state_s *priv; + FAR struct userfs_rename_request_s *req; + FAR struct userfs_rename_response_s *resp; + int oldpathlen; + int newpathlen; + ssize_t nsent; + ssize_t nrecvd; + + DEBUGASSERT(mountpt != NULL && + mountpt->i_private != NULL); + priv = mountpt->i_private; + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_rename_request_s *)priv->iobuffer; + req->req = USERFS_REQ_RENAME; + + DEBUGASSERT(oldrelpath != NULL && newrelpath != NULL); + oldpathlen = strlen(oldrelpath) + 1; + newpathlen = strlen(newrelpath) + 1; + + if ((oldpathlen + newpathlen) > priv->mxwrite) + { + return -E2BIG; + } + + req->newoffset = oldpathlen; + strncpy(req->oldrelpath, oldrelpath, oldpathlen); + strncpy(&req->oldrelpath[oldpathlen], newrelpath, newpathlen); + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + SIZEOF_USERFS_RENAME_REQUEST_S(oldpathlen, newpathlen), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd != sizeof(struct userfs_rename_response_s)) + { + ferr("ERROR: Response size incorrect: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + resp = (FAR struct userfs_rename_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_RENAME) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + return resp->ret; +} + +/**************************************************************************** + * Name: userfs_stat + * + * Description: + * Return information about a file or directory + * + ****************************************************************************/ + +static int userfs_stat(FAR struct inode *mountpt, FAR const char *relpath, + FAR struct stat *buf) +{ + FAR struct userfs_state_s *priv; + FAR struct userfs_stat_request_s *req; + FAR struct userfs_stat_response_s *resp; + ssize_t nsent; + ssize_t nrecvd; + int pathlen; + + DEBUGASSERT(mountpt != NULL && + mountpt->i_private != NULL); + priv = mountpt->i_private; + + /* Construct and send the request to the server */ + + req = (FAR struct userfs_stat_request_s *)priv->iobuffer; + req->req = USERFS_REQ_STAT; + + DEBUGASSERT(relpath != NULL); + pathlen = strlen(relpath); + if (pathlen > priv->mxwrite) + { + return -E2BIG; + } + + strncpy(req->relpath, relpath, priv->mxwrite); + + nsent = psock_sendto(&priv->psock, priv->iobuffer, + SIZEOF_USERFS_STAT_REQUEST_S(pathlen + 1), 0, + (FAR struct sockaddr *)&priv->server, priv->addrlen); + if (nsent < 0) + { + ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); + return (int)nsent; + } + + /* Then get the response from the server */ + + nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), + 0, NULL, NULL); + if (nrecvd < 0) + { + ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); + return (int)nrecvd; + } + + if (nrecvd != sizeof(struct userfs_stat_response_s)) + { + ferr("ERROR: Response size incorrect: %u\n", (unsigned int)nrecvd); + return -EIO; + } + + resp = (FAR struct userfs_stat_response_s *)priv->iobuffer; + if (resp->resp != USERFS_RESP_STAT) + { + ferr("ERROR: Incorrect response: %u\n", resp->resp); + return -EIO; + } + + /* Return the directory entry status */ + + DEBUGASSERT(buf != NULL); + memcpy(buf, &resp->buf, sizeof(struct stat)); + return resp->ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/fs/userfs/userfs.h b/fs/userfs/userfs.h new file mode 100644 index 00000000000..5007eb385c8 --- /dev/null +++ b/fs/userfs/userfs.h @@ -0,0 +1,52 @@ +/**************************************************************************** + * fs/userfs/userfs.h + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __FS_USERFS_USERFS_H +#define __FS_USERFS_USERFS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifdef CONFIG_FS_USERFS + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#endif /* CONFIG_FS_USERFS */ +#endif /* __FS_USERFS_USERFS_H */ \ No newline at end of file diff --git a/include/nuttx/fs/dirent.h b/include/nuttx/fs/dirent.h index f616f734737..ae16f893422 100644 --- a/include/nuttx/fs/dirent.h +++ b/include/nuttx/fs/dirent.h @@ -177,6 +177,17 @@ struct fs_unionfsdir_s }; #endif +#ifdef CONFIG_FS_USERFS +/* The UserFS uses an opaque representation since the actual userspace representation + * of the directory state structure is unknowable. + */ + +struct fs_userfsdir_s +{ + FAR void *fs_dir; /* Opaque pointer to UserFS DIR */ +}; +#endif + #ifdef CONFIG_FS_HOSTFS /* HOSTFS provides mapping to directories on the host machine in the * sim environment. @@ -184,7 +195,7 @@ struct fs_unionfsdir_s struct fs_hostfsdir_s { - FAR void *fs_dir; /* Opaque pointer to host DIR * */ + FAR void *fs_dir; /* Opaque pointer to host DIR */ }; #endif @@ -254,6 +265,9 @@ struct fs_dirent_s #ifdef CONFIG_FS_UNIONFS struct fs_unionfsdir_s unionfs; #endif +#ifdef CONFIG_FS_USERFS + struct fs_userfsdir_s userfs; +#endif #ifdef CONFIG_FS_HOSTFS struct fs_hostfsdir_s hostfs; #endif diff --git a/include/nuttx/fs/ioctl.h b/include/nuttx/fs/ioctl.h index c7391e5cef6..90341218a7c 100644 --- a/include/nuttx/fs/ioctl.h +++ b/include/nuttx/fs/ioctl.h @@ -149,6 +149,11 @@ #define FIONSPACE _FIOC(0x0007) /* IN: Location to return value (int *) * OUT: Free space in send queue. */ +#define FIONUSERFS _FIOC(0x0008) /* IN: Pointer to struct usefs_config_s + * holding userfs configuration. + * OUT: Instance number is returned on + * success. + */ /* NuttX file system ioctl definitions **************************************/ diff --git a/include/nuttx/fs/userfs.h b/include/nuttx/fs/userfs.h new file mode 100644 index 00000000000..0fc30704013 --- /dev/null +++ b/include/nuttx/fs/userfs.h @@ -0,0 +1,598 @@ +/**************************************************************************** + * include/nuttx/fs/userfs.h + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_FS_USERFSFS_H +#define __INCLUDE_NUTTX_FS_USERFSFS_H + +/* UserFS is implemented by two components: + * + * 1. A component as part of the internal OS file system logic. This + * file system receives the file system requests and marshals the request + * as described by the following structures, and sends this marshaled + * request on a FIFO that was created by userfs_run(). It also receives + * the marshal led response by the application file system, unmarshals the + * response, and provides the file system response to the caller. + * 2. userfs_run() is part of the NuttX C library. It receives the marshaled + * operating system requests on the FIFO, unmarshals it, and calls the + * application file system methods on behalf of the OS. It the marshals + * the application response data and sends this back to the waiting + * OS file system logic. + * + * Overview of general operation flow: + * + * 1. The UserFS OS support will be instantiated when the UserFS is mounted + * based upon the configuration passed in the optional data of the + * mount command. + * 2. The UserFS instance N will be configured to communicate on a Unix + * domain local socket with address: /dev/userfsN where N is the same + * value as was when file system was created. The Unix domain socket + * handles both client to server requests and server-to-client responses. + * 3. The UserFs will receive system file system requests and forward them + * on the the MqUfsReqN to the user-space file system server + * (userfs_run()). These requests may be accompanied by additional data in + * an provided request buffer that was provided when the UserFS was + * created. This buffer would hold, for example, the data to be + * written that would accompany a write request. + * 4. The user-space logic of userfs_run() listens at the other end of the + * Unix domain socket. It will receive the requests and forward them + * to the user file system implementation via the methods of struct + * userfs_operations_s + * 5. Responses generated by the struct userfs_operations_s method will be + * returned to UserFS via the Unix domain socket. + * 6. The UserFS kernel thread will listen on the Unix local domain socket + * and will receive the user file system responses and forward them to + * the kernel-space file system client. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef CONFIG_FS_USERFS + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* UserFS IOCTLs are defined in included/nuttx/fs/ioctl.h. There is only one: + * + * FIONUSERFS. The is the IOCTL that is used with the dev/userfs factory to + * create a UserFS instance. + * + * Input: This function receives an pointer to a read-only instance of + * struct userfs_config_s that contains information needed to + * configure the UserFS instance. + * Output: On success the UserFS N instance is created. N is non-negative + * and will be provided as the IOCTL return value on success. On + * failure, ioctl() will return -1 with the errno variable set to + * indicate the cause of the failure. + */ + +/* Format statements that should be used in creating Unix domain addresses */ + +#define USERFS_SERVER_FMT "/dev/userver%u" +#define USERFS_SERVER_MAXLEN (18) + +/* It looks like the maximum size of a request is 16 bytes. We will allow a + * little more for the maximum size of a request structure. + */ + +#define USERFS_REQ_MAXSIZE (32) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* This enumeration provides the type of each request sent from the OS file + * system client to the user file system. + */ + +enum userfs_req_e +{ + USERFS_REQ_OPEN = 0, + USERFS_REQ_CLOSE, + USERFS_REQ_READ, + USERFS_REQ_WRITE, + USERFS_REQ_SEEK, + USERFS_REQ_IOCTL, + USERFS_REQ_SYNC, + USERFS_REQ_DUP, + USERFS_REQ_FSTAT, + USERFS_REQ_OPENDIR, + USERFS_REQ_CLOSEDIR, + USERFS_REQ_READDIR, + USERFS_REQ_REWINDDIR, + USERFS_REQ_STATFS, + USERFS_REQ_UNLINK, + USERFS_REQ_MKDIR, + USERFS_REQ_RMDIR, + USERFS_REQ_RENAME, + USERFS_REQ_STAT, + USERFS_REQ_DESTROY +}; + +/* This enumeration provides the type of each response returned from the + * user file system to OS file system client to the . + */ + +enum userfs_resp_e +{ + USERFS_RESP_OPEN = 0, + USERFS_RESP_CLOSE, + USERFS_RESP_READ, + USERFS_RESP_WRITE, + USERFS_RESP_SEEK, + USERFS_RESP_IOCTL, + USERFS_RESP_SYNC, + USERFS_RESP_DUP, + USERFS_RESP_FSTAT, + USERFS_RESP_OPENDIR, + USERFS_RESP_CLOSEDIR, + USERFS_RESP_READDIR, + USERFS_RESP_REWINDDIR, + USERFS_RESP_STATFS, + USERFS_RESP_UNLINK, + USERFS_RESP_MKDIR, + USERFS_RESP_RMDIR, + USERFS_RESP_RENAME, + USERFS_RESP_STAT, + USERFS_RESP_DESTROY +}; + +/* These structures are used by internal UserFS implementation and should not + * be of interest to application level logic. + * + * This is passed to the mount() function as optional data when the UserFS + * file system is mounted. + */ + +struct userfs_config_s +{ + size_t mxwrite; /* The max size of a write data */ + int instance; /* Instance number used to create unique naming */ +}; + +/* This structure identifies the user-space file system operations. */ + +struct stat; /* Forward reference */ +struct statfs; /* Forward reference */ + +struct userfs_operations_s +{ + int (*open)(FAR void *volinfo, FAR const char *relpath, + int oflags, mode_t mode, FAR void **openinfo); + int (*close)(FAR void *volinfo, FAR void *openinfo); + ssize_t (*read)(FAR void *volinfo, FAR void *openinfo, + FAR char *buffer, size_t buflen); + ssize_t (*write)(FAR void *volinfo, FAR void *openinfo, + FAR const char *buffer, size_t buflen); + off_t (*seek)(FAR void *volinfo, FAR void *openinfo, off_t offset, + int whence); + int (*ioctl)(FAR void *volinfo, FAR void *openinfo, int cmd, + unsigned long arg); + int (*sync)(FAR void *volinfo, FAR void *openinfo); + int (*dup)(FAR void *volinfo, FAR void *oldinfo, FAR void *newinfo); + int (*fstat)(FAR void *volinfo, FAR void *openinfo, + FAR struct stat *buf); + int (*opendir)(FAR void *volinfo, FAR const char *relpath, + FAR void **dir); + int (*closedir)(FAR void *volinfo, FAR void *dir); + int (*readdir)(FAR void *volinfo, FAR void *dir, + FAR struct dirent *entry); + int (*rewinddir)(FAR void *volinfo, FAR void *dir); + int (*statfs)(FAR void *volinfo, FAR struct statfs *buf); + int (*unlink)(FAR void *volinfo, FAR const char *relpath); + int (*mkdir)(FAR void *volinfo, FAR const char *relpath, mode_t mode); + int (*rmdir)(FAR void *volinfo, FAR const char *relpath); + int (*rename)(FAR void *volinfo, FAR const char *oldrelpath, + FAR const char *newrelpath); + int (*stat)(FAR void *volinfo, FAR const char *relpath, + FAR struct stat *buf); + int (*destroy)(FAR void *volinfo); +}; + +/* The following structures describe the header on the marshaled data sent + * on the FIFOs. See struct userfs_operations_s for the form of the + * marshaled function calls. + */ + +struct userfs_open_request_s +{ + uint8_t req; /* Must be USERFS_REQ_OPEN */ + int oflags; /* Open flags */ + mode_t mode; /* Open mode */ + char relpath[1]; /* Mountpoint relative path to the file to open */ +}; + +#define SIZEOF_USERFS_OPEN_REQUEST_S(n) (sizeof(struct userfs_open_request_s) + (n) - 1) + +struct userfs_open_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_OPEN */ + FAR void *openinfo; /* Open file info for use in other operations */ + int ret; /* Result of the operation */ +}; + +struct userfs_close_request_s +{ + uint8_t req; /* Must be USERFS_REQ_CLOSE */ + FAR void *openinfo; /* Open file info as returned by open() */ +}; + +struct userfs_close_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_CLOSE */ + int ret; /* Result of the operation */ +}; + +struct userfs_read_request_s +{ + uint8_t req; /* Must be USERFS_REQ_READ */ + FAR void *openinfo; /* Open file info as returned by open() */ + size_t readlen; /* Maximum number of bytes to read */ +}; + +struct userfs_read_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_READ */ + ssize_t nread; /* Result of the operation */ + char rddata[1]; /* Read data follows. Actual size is nread */ +}; + +#define SIZEOF_USERFS_READ_RESPONSE_S(n) (sizeof(struct userfs_read_response_s) + (n) - 1) + +struct userfs_write_request_s +{ + uint8_t req; /* Must be USERFS_REQ_WRITE */ + FAR void *openinfo; /* Open file info as returned by open() */ + size_t writelen; /* Number of bytes to write */ + char wrdata[1]; /* Read data follows. Actual size is wrsize */ +}; + +#define SIZEOF_USERFS_WRITE_REQUEST_S(n) (sizeof(struct userfs_write_request_s) + (n) - 1) + +struct userfs_write_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_WRITE */ + ssize_t nwritten; /* Result of the operation */ +}; + +struct userfs_seek_request_s +{ + uint8_t req; /* Must be USERFS_REQ_SEEK */ + FAR void *openinfo; /* Open file info as returned by open() */ + off_t offset; /* Seek offset */ + int whence; /* Determines how offset is interpreted */ +}; + +struct userfs_seek_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_SEEK */ + off_t ret; /* Result of the operation */ +}; + +struct userfs_ioctl_request_s +{ + uint8_t req; /* Must be USERFS_REQ_IOCTL */ + FAR void *openinfo; /* Open file info as returned by open() */ + int cmd; /* IOCTL command */ + unsigned long arg; /* Argument that accompanies the command */ +}; + +struct userfs_ioctl_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_IOCTL */ + int ret; /* Result of the operation */ +}; + +struct userfs_sync_request_s +{ + uint8_t req; /* Must be USERFS_REQ_SYNC */ + FAR void *openinfo; /* Open file info as returned by open() */ +}; + +struct userfs_sync_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_SYNC */ + int ret; /* Result of the operation */ +}; + +struct userfs_dup_request_s +{ + uint8_t req; /* Must be USERFS_REQ_DUP */ + FAR void *openinfo; /* Open file info as returned by open() */ +}; + +struct userfs_dup_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_DUP */ + FAR void *openinfo; /* Open file info for the dup'ed file */ + int ret; /* Result of the operation */ +}; + +struct userfs_fstat_request_s +{ + uint8_t req; /* Must be USERFS_REQ_FSTAT */ + FAR void *openinfo; /* Open file info for the dup'ed file */ +}; + +struct userfs_fstat_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_FSTAT */ + int ret; /* Result of the operation */ + FAR struct stat buf; /* Returned file system status */ +}; + +struct userfs_opendir_request_s +{ + uint8_t req; /* Must be USERFS_REQ_OPENDIR */ + char relpath[1]; /* Mountpoint relative path to the directory to open */ +}; + +#define SIZEOF_USERFS_OPENDIR_REQUEST_S(n) (sizeof(struct userfs_opendir_request_s) + (n) - 1) + +struct userfs_opendir_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_OPENDIR */ + int ret; /* Result of the operation */ + FAR void *dir; /* Opaque pointer to directory information */ +}; + +struct userfs_closedir_request_s +{ + uint8_t req; /* Must be USERFS_REQ_CLOSEDIR */ + FAR void *dir; /* Opaque pointer to directory information */ +}; + +struct userfs_closedir_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_CLOSEDIR */ + int ret; /* Result of the operation */ +}; + +struct userfs_readdir_request_s +{ + uint8_t req; /* Must be USERFS_REQ_READDIR */ + FAR void *dir; /* Opaque pointer to directory information */ +}; + +struct userfs_readdir_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_READDIR */ + int ret; /* Result of the operation */ + struct dirent entry; /* Directory entry that was read */ +}; + +struct userfs_rewinddir_request_s +{ + uint8_t req; /* Must be USERFS_REQ_REWINDDIR */ + FAR void *dir; /* Opaque pointer to directory information */ +}; + +struct userfs_rewinddir_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_REWINDDIR */ + int ret; /* Result of the operation */ +}; + +struct userfs_statfs_request_s +{ + uint8_t req; /* Must be USERFS_REQ_STATFS */ +}; + +struct userfs_statfs_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_STATFS */ + int ret; /* Result of the operation */ + FAR struct statfs buf; /* Returned file system status */ +}; + +struct userfs_unlink_request_s +{ + uint8_t req; /* Must be USERFS_REQ_UNLINK */ + char relpath[1]; /* Relative path to the entry to unlink */ +}; + +#define SIZEOF_USERFS_UNLINK_REQUEST_S(n) (sizeof(struct userfs_unlink_request_s) + (n) - 1) + +struct userfs_unlink_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_UNLINK */ + int ret; /* Result of the operation */ +}; + +struct userfs_mkdir_request_s +{ + uint8_t req; /* Must be USERFS_REQ_MKDIR */ + mode_t mode; /* Directory mode flags */ + char relpath[1]; /* Relative path to the directory to create */ +}; + +#define SIZEOF_USERFS_MKDIR_REQUEST_S(n) (sizeof(struct userfs_mkdir_request_s) + (n) - 1) + +struct userfs_mkdir_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_MKDIR */ + int ret; /* Result of the operation */ +}; + +struct userfs_rmdir_request_s +{ + uint8_t req; /* Must be USERFS_REQ_RMDIR */ + char relpath[1]; /* Relative path to the directory to remove */ +}; + +#define SIZEOF_USERFS_RMDIR_REQUEST_S(n) (sizeof(struct userfs_rmdir_request_s) + (n) - 1) + +struct userfs_rmdir_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_RMDIR */ + int ret; /* Result of the operation */ +}; + +struct userfs_rename_request_s +{ + uint8_t req; /* Must be USERFS_REQ_RENAME */ + uint16_t newoffset; /* Offset from old to new relpath */ + char oldrelpath[1]; /* Old relative path to be renamed */ +}; + +#define SIZEOF_USERFS_RENAME_REQUEST_S(o,n) (sizeof(struct userfs_rename_request_s) + (o) + (n)) + +struct userfs_rename_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_RENAME */ + int ret; /* Result of the operation */ +}; + +struct userfs_stat_request_s +{ + uint8_t req; /* Must be USERFS_REQ_STAT */ + char relpath[1]; /* Relative path to the directory entry to be queried */ +}; + +#define SIZEOF_USERFS_STAT_REQUEST_S(n) (sizeof(struct userfs_stat_request_s) + (n) - 1) + +struct userfs_stat_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_STAT */ + int ret; /* Result of the operation */ + FAR struct stat buf; /* Returned status of the directory entry */ +}; + +struct userfs_destroy_request_s +{ + uint8_t req; /* Must be USERFS_REQ_DESTROY */ +}; + +struct userfs_destroy_response_s +{ + uint8_t resp; /* Must be USERFS_RESP_DESTROY */ + int ret; /* Result of the operation */ +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: userfs_register + * + * Description: + * Register the UserFS factory driver at dev/userfs. + * + * NOTE: This is an OS internal function that should not be called from + * appliation logic. + * + * Input parameters: + * None + * + * Returned Value: + * Zero (OK) is returned if the dev/userfs driver was intitialized and + * registered properly. Otherwise, a negated errno value is returned + * to indicate the nature of the failure. + * + ****************************************************************************/ + +int userfs_register(void); + +/**************************************************************************** + * Name: userfs_run + * + * Description: + * Start the UserFS server on the current thread. This function will mount + * the UserFS file system and will not return until that file system has + * been unmounted. + * + * userfs_run() implements the UserFS server. It performs there operations: + * + * 1. It configures and creates the UserFS file system and + * 2. Mounts the user file system at the provide mount point path. + * 2. Receives file system requests on the Unix doamin local socket with + * address /dev/userfsN where N is the same as above, + * 3. Received file system requests are marshaled and dispatch to the + * user file system via callbacks to the operations provided by + * "userops", and + * 3. Returns file system responses generated by the callbacks via the + * same Unix domain local socket. + * + * NOTE: This is a user function that is implemented as part of the + * NuttX C library and is intended to be called by appliation logic. + * + * Input parameters: + * mountpt - Mountpoint path + * userops - The caller operations that implement the file system + * interface. + * volinfo - Private volume data that will be provided in all struct + * userfs_operations_s methods. + * mxwrite - The max size of a write data + * + * Returned Value: + * This function does not return unless the file system is unmounted (OK) + * or unless an error is encountered. In the event of an error, the + * returned value is a negated errno value indicating the nature of the + * error. + * + ****************************************************************************/ + +int userfs_run(FAR const char *mountpt, + FAR const struct userfs_operations_s *userops, + FAR void *volinfo, size_t mxwrite); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_FS_USERFS */ +#endif /* __INCLUDE_NUTTX_FS_USERFSFS_H */ diff --git a/include/sys/statfs.h b/include/sys/statfs.h index 1d1786a3f81..e33226e972c 100644 --- a/include/sys/statfs.h +++ b/include/sys/statfs.h @@ -104,6 +104,7 @@ #define SMARTFS_MAGIC 0x54524D53 #define UNIONFS_MAGIC 0x53464e55 #define HOSTFS_MAGIC 0x54534f48 +#define USERFS_MAGIC 0x52455355 /**************************************************************************** * Type Definitions diff --git a/libc/Kconfig b/libc/Kconfig index 407e9dead78..0e558863dbf 100644 --- a/libc/Kconfig +++ b/libc/Kconfig @@ -22,3 +22,4 @@ source libc/netdb/Kconfig source libc/misc/Kconfig source libc/wqueue/Kconfig source libc/hex2bin/Kconfig +source libc/userfs/Kconfig diff --git a/libc/Makefile b/libc/Makefile index 4b23e458c1b..58d56a0adbe 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -94,6 +94,7 @@ include unistd/Make.defs include wchar/Make.defs include wctype/Make.defs include wqueue/Make.defs +include userfs/Make.defs # REVISIT: Backslash causes problems in $(COBJS) target DELIM := $(strip /) diff --git a/libc/userfs/.gitignore b/libc/userfs/.gitignore new file mode 100644 index 00000000000..91d1ddb7d02 --- /dev/null +++ b/libc/userfs/.gitignore @@ -0,0 +1,10 @@ +/.built +/.tzbuilt +/.tzunpack +/romfs_zoneinfo.img +/romfs_zoneinfo.h +/tzbin +/tzcode-latest.tar.gz +/tzcode +/tzdata-latest.tar.gz + diff --git a/libc/userfs/Kconfig b/libc/userfs/Kconfig new file mode 100644 index 00000000000..f72f3c094ce --- /dev/null +++ b/libc/userfs/Kconfig @@ -0,0 +1,4 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# diff --git a/libc/userfs/Make.defs b/libc/userfs/Make.defs new file mode 100644 index 00000000000..6c34026c56f --- /dev/null +++ b/libc/userfs/Make.defs @@ -0,0 +1,47 @@ +############################################################################ +# libc/userfs/Make.defs +# +# Copyright (C) 2017 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +ifeq ($(CONFIG_FS_USERFS),y) + +# Add the internal C files to the build + +CSRCS += lib_userfs.c + +# Add the userfs directory to the build + +DEPPATH += --dep-path userfs +VPATH += :userfs + +endif diff --git a/libc/userfs/lib_userfs.c b/libc/userfs/lib_userfs.c new file mode 100644 index 00000000000..11c2584b44b --- /dev/null +++ b/libc/userfs/lib_userfs.c @@ -0,0 +1,1058 @@ +/**************************************************************************** + * libc/userfs/lib_userfs.c + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct userfs_info_s +{ + FAR const struct userfs_operations_s *userops; /* File system callbacks */ + FAR void *volinfo; /* Data that accompanies the user callbacks */ + struct sockaddr_un client; /* Client to send response back to */ + int16_t sockfd; /* Server socket */ + uint16_t iolen; /* Size of I/O buffer */ + uint16_t mxwrite; /* The max size of a write data */ + uint8_t iobuffer[1]; /* I/O buffer. Actual size is iolen. */ +}; + +#define SIZEOF_USERFS_INFO_S(n) (sizeof(struct userfs_info_s) + (n) - 1) + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* REVISIT: This is insufficient in the KERNEL build. In that build mode, + * there will be multiple instances of these variables and the logic will + * not generate unique instance numbers. + */ + +static sem_t g_userfs_exclsem = SEM_INITIALIZER(1); +static uint16_t g_userfs_next_instance; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: userfs_instance + ****************************************************************************/ + +static inline uint16_t userfs_instance(void) +{ + int ret; + + ret = nxsem_wait(&g_userfs_exclsem); + if (ret >= 0) + { + /* Get the next instance number. + * + * REVISIT: Here we really should verify that other UserFs + * exists with the same instance number. That could + * happen if g_userfs_next_instance were to wrap around. + */ + + ret = g_userfs_next_instance++; + nxsem_post(&g_userfs_exclsem); + } + + return ret; +} + +/**************************************************************************** + * Name: userfs_*_dispatch + ****************************************************************************/ + +static inline int userfs_open_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_open_request_s *req, size_t reqlen) +{ + struct userfs_open_response_s resp; + int pathlen; + size_t expected; + ssize_t nsent; + int ret; + + /* Verify the request size */ + + if (reqlen < SIZEOF_USERFS_OPEN_REQUEST_S(0)) + { + return -EINVAL; + } + + pathlen = strlen(req->relpath); + if (pathlen > info->mxwrite) + { + return -EINVAL; + } + + expected = SIZEOF_USERFS_OPEN_REQUEST_S(pathlen); + if (expected >= reqlen) + { + return -EINVAL; + } + + /* Dispatch the request. + * + * REVISIT: In the kernel build openinfo will be valid only in the + * context of this process. + */ + + DEBUGASSERT(info->userops != NULL && info->userops->open != NULL); + resp.ret = info->userops->open(info->volinfo, req->relpath, req->oflags, + req->mode, &resp.openinfo); + + /* Send the response */ + + resp.resp = USERFS_RESP_OPEN; + nsent = sendto(info->sockfd, &resp, sizeof(struct userfs_open_response_s), + 0, (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); + if (nsent < 0) + { + ret = -errno; + ferr("ERROR: Send open response failed: %d\n", ret) + return ret; + } + + /* REVISIT: Partial sends are not supported */ + + DEBUGASSERT(nsent == sizeof(struct userfs_open_response_s)); + return OK; +} + +static inline int userfs_close_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_close_request_s *req, size_t reqlen) +{ + struct userfs_close_response_s resp; + + /* Verify the request size */ + + if (reqlen != sizeof(struct userfs_close_request_s)) + { + return -EINVAL; + } + + /* Dispatch the request */ + + DEBUGASSERT(info->userops != NULL && info->userops->close != NULL); + resp.ret = info->userops->close(info->volinfo, req->openinfo); + + /* Send the response */ + + resp.resp = USERFS_RESP_CLOSE; + return sendto(info->sockfd, &resp, sizeof(struct userfs_close_response_s), + 0, (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); +} + +static inline int userfs_read_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_read_request_s *req, size_t reqlen) +{ + FAR struct userfs_read_response_s *resp; + size_t readlen; + size_t resplen; + + /* Verify the request size */ + + if (reqlen != sizeof(struct userfs_read_request_s)) + { + return -EINVAL; + } + + /* Dispatch the request */ + + readlen = req->readlen; + if (readlen > info->mxwrite) + { + readlen = info->mxwrite; + } + + resp = (FAR struct userfs_read_response_s *)info->iobuffer; + + DEBUGASSERT(info->userops != NULL && info->userops->read != NULL); + resp->nread = info->userops->read(info->volinfo, req->openinfo, + resp->rddata, readlen); + + /* Send the response */ + + resp->resp = USERFS_RESP_READ; + resplen = SIZEOF_USERFS_READ_RESPONSE_S(resp->nread); + return sendto(info->sockfd, resp, resplen, 0, + (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); +} + +static inline int userfs_write_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_write_request_s *req, size_t reqlen) +{ + struct userfs_write_response_s resp; + size_t writelen; + size_t expected; + + /* Verify the request size */ + + if (reqlen < SIZEOF_USERFS_WRITE_REQUEST_S(0)) + { + return -EINVAL; + } + + writelen = req->writelen; + if (writelen > info->mxwrite) + { + return -EINVAL; + } + + expected = SIZEOF_USERFS_WRITE_REQUEST_S(writelen); + if (expected != reqlen) + { + return -EINVAL; + } + + /* Dispatch the request */ + + DEBUGASSERT(info->userops != NULL && info->userops->write != NULL); + resp.nwritten = info->userops->write(info->volinfo, req->openinfo, + req->wrdata, writelen); + + /* Send the response */ + + resp.resp = USERFS_RESP_WRITE; + return sendto(info->sockfd, &resp, sizeof(struct userfs_write_response_s), + 0, (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); +} + +static inline int userfs_seek_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_seek_request_s *req, size_t reqlen) +{ + struct userfs_seek_response_s resp; + + /* Verify the request size */ + + if (reqlen != sizeof(struct userfs_seek_request_s)) + { + return -EINVAL; + } + + /* Dispatch the request */ + + DEBUGASSERT(info->userops != NULL && info->userops->seek != NULL); + resp.ret = info->userops->seek(info->volinfo, req->openinfo, req->offset, + req->whence); + + /* Send the response */ + + resp.resp = USERFS_RESP_SEEK; + return sendto(info->sockfd, &resp, sizeof(struct userfs_seek_response_s), + 0, (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); +} + +static inline int userfs_ioctl_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_ioctl_request_s *req, size_t reqlen) +{ + struct userfs_ioctl_response_s resp; + + /* Verify the request size */ + + if (reqlen != sizeof(struct userfs_ioctl_request_s)) + { + return -EINVAL; + } + + /* Dispatch the request */ + + DEBUGASSERT(info->userops != NULL && info->userops->ioctl != NULL); + resp.ret = info->userops->ioctl(info->volinfo, req->openinfo, req->cmd, + req->arg); + + /* Send the response */ + + resp.resp = USERFS_RESP_IOCTL; + return sendto(info->sockfd, &resp, sizeof(struct userfs_ioctl_response_s), + 0, (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); +} + +static inline int userfs_sync_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_sync_request_s *req, size_t reqlen) +{ + struct userfs_sync_response_s resp; + + /* Verify the request size */ + + if (reqlen != sizeof(struct userfs_sync_request_s)) + { + return -EINVAL; + } + + /* Dispatch the request */ + + DEBUGASSERT(info->userops != NULL && info->userops->sync != NULL); + resp.ret = info->userops->sync(info->volinfo, req->openinfo); + + /* Send the response */ + + resp.resp = USERFS_RESP_SYNC; + return sendto(info->sockfd, &resp, sizeof(struct userfs_sync_response_s), + 0, (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); +} + +static inline int userfs_dup_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_dup_request_s *req, size_t reqlen) +{ + struct userfs_dup_response_s resp; + + /* Verify the request size */ + + if (reqlen != sizeof(struct userfs_dup_request_s)) + { + return -EINVAL; + } + + /* Dispatch the request */ + + DEBUGASSERT(info->userops != NULL && info->userops->dup != NULL); + resp.ret = info->userops->dup(info->volinfo, req->openinfo, &resp.openinfo); + + /* Send the response */ + + resp.resp = USERFS_RESP_DUP; + return sendto(info->sockfd, &resp, sizeof(struct userfs_dup_response_s), + 0, (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); +} + +static inline int userfs_fstat_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_fstat_request_s *req, size_t reqlen) +{ + struct userfs_fstat_response_s resp; + + /* Verify the request size */ + + if (reqlen != sizeof(struct userfs_fstat_request_s)) + { + return -EINVAL; + } + + /* Dispatch the request */ + + DEBUGASSERT(info->userops != NULL && info->userops->fstat != NULL); + resp.ret = info->userops->fstat(info->volinfo, req->openinfo, &resp.buf); + + /* Send the response */ + + resp.resp = USERFS_RESP_FSTAT; + return sendto(info->sockfd, &resp, sizeof(struct userfs_fstat_response_s), + 0, (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); +} + +static inline int userfs_opendir_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_opendir_request_s *req, size_t reqlen) +{ + struct userfs_opendir_response_s resp; + int pathlen; + size_t expected; + + /* Verify the request size */ + + if (reqlen < SIZEOF_USERFS_OPENDIR_REQUEST_S(0)) + { + return -EINVAL; + } + + pathlen = strlen(req->relpath); + if (pathlen > info->mxwrite) + { + return -EINVAL; + } + + expected = SIZEOF_USERFS_OPENDIR_REQUEST_S(pathlen); + if (expected >= reqlen) + { + return -EINVAL; + } + + /* Dispatch the request */ + + DEBUGASSERT(info->userops != NULL && info->userops->opendir != NULL); + resp.ret = info->userops->opendir(info->volinfo, req->relpath, &resp.dir); + + /* Send the response */ + + resp.resp = USERFS_RESP_OPENDIR; + return sendto(info->sockfd, &resp, sizeof(struct userfs_opendir_response_s), + 0, (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); +} + +static inline int userfs_closedir_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_closedir_request_s *req, size_t reqlen) +{ + struct userfs_closedir_response_s resp; + + /* Verify the request size */ + + if (reqlen != sizeof(struct userfs_closedir_request_s)) + { + return -EINVAL; + } + + /* Dispatch the request */ + + DEBUGASSERT(info->userops != NULL && info->userops->closedir != NULL); + resp.ret = info->userops->closedir(info->volinfo, req->dir); + + /* Send the response */ + + resp.resp = USERFS_RESP_CLOSEDIR; + return sendto(info->sockfd, &resp, sizeof(struct userfs_open_response_s), + 0, (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); +} + +static inline int userfs_readdir_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_readdir_request_s *req, size_t reqlen) +{ + struct userfs_readdir_response_s resp; + + /* Verify the request size */ + + if (reqlen != sizeof(struct userfs_readdir_request_s)) + { + return -EINVAL; + } + + /* Dispatch the request */ + + DEBUGASSERT(info->userops != NULL && info->userops->readdir != NULL); + resp.ret = info->userops->readdir(info->volinfo, req->dir, &resp.entry); + + /* Send the response */ + + resp.resp = USERFS_RESP_READDIR; + return sendto(info->sockfd, &resp, sizeof(struct userfs_readdir_response_s), + 0, (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); +} + +static inline int userfs_rewinddir_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_rewinddir_request_s *req, size_t reqlen) +{ + struct userfs_rewinddir_response_s resp; + + /* Verify the request size */ + + if (reqlen != sizeof(struct userfs_rewinddir_request_s)) + { + return -EINVAL; + } + + /* Dispatch the request */ + + DEBUGASSERT(info->userops != NULL && info->userops->rewinddir != NULL); + resp.ret = info->userops->rewinddir(info->volinfo, req->dir); + + /* Send the response */ + + resp.resp = USERFS_RESP_REWINDDIR; + return sendto(info->sockfd, &resp, sizeof(struct userfs_rewinddir_response_s), + 0, (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); +} + +static inline int userfs_statfs_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_statfs_request_s *req, size_t reqlen) +{ + struct userfs_statfs_response_s resp; + + /* Verify the request size */ + + if (reqlen != sizeof(struct userfs_statfs_request_s)) + { + return -EINVAL; + } + + /* Dispatch the request */ + + DEBUGASSERT(info->userops != NULL && info->userops->statfs != NULL); + resp.ret = info->userops->statfs(info->volinfo, &resp.buf); + + /* Send the response */ + + resp.resp = USERFS_RESP_STATFS; + return sendto(info->sockfd, &resp, sizeof(struct userfs_statfs_response_s), + 0, (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); +} + +static inline int userfs_unlink_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_unlink_request_s *req, size_t reqlen) +{ + struct userfs_unlink_response_s resp; + int pathlen; + size_t expected; + + /* Verify the request size */ + + if (reqlen < SIZEOF_USERFS_UNLINK_REQUEST_S(0)) + { + return -EINVAL; + } + + pathlen = strlen(req->relpath); + if (pathlen > info->mxwrite) + { + return -EINVAL; + } + + expected = SIZEOF_USERFS_UNLINK_REQUEST_S(pathlen); + if (expected >= reqlen) + { + return -EINVAL; + } + + /* Dispatch the request */ + + DEBUGASSERT(info->userops != NULL && info->userops->unlink != NULL); + resp.ret = info->userops->unlink(info->volinfo, req->relpath); + + /* Send the response */ + + resp.resp = USERFS_RESP_UNLINK; + return sendto(info->sockfd, &resp, sizeof(struct userfs_unlink_response_s), + 0, (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); +} + +static inline int userfs_mkdir_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_mkdir_request_s *req, size_t reqlen) +{ + struct userfs_mkdir_response_s resp; + int pathlen; + size_t expected; + + /* Verify the request size */ + + if (reqlen < SIZEOF_USERFS_MKDIR_REQUEST_S(0)) + { + return -EINVAL; + } + + pathlen = strlen(req->relpath); + if (pathlen > info->mxwrite) + { + return -EINVAL; + } + + expected = SIZEOF_USERFS_MKDIR_REQUEST_S(pathlen); + if (expected >= reqlen) + { + return -EINVAL; + } + + /* Dispatch the request */ + + DEBUGASSERT(info->userops != NULL && info->userops->mkdir != NULL); + resp.ret = info->userops->mkdir(info->volinfo, req->relpath, req->mode); + + /* Send the response */ + + resp.resp = USERFS_RESP_MKDIR; + return sendto(info->sockfd, &resp, sizeof(struct userfs_mkdir_response_s), + 0, (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); +} + +static inline int userfs_rmdir_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_rmdir_request_s *req, size_t reqlen) +{ + struct userfs_rmdir_response_s resp; + int pathlen; + size_t expected; + + /* Verify the request size */ + + if (reqlen < SIZEOF_USERFS_MKDIR_REQUEST_S(0)) + { + return -EINVAL; + } + + pathlen = strlen(req->relpath); + if (pathlen > info->mxwrite) + { + return -EINVAL; + } + + expected = SIZEOF_USERFS_MKDIR_REQUEST_S(pathlen); + if (expected >= reqlen) + { + return -EINVAL; + } + + /* Dispatch the request */ + + DEBUGASSERT(info->userops != NULL && info->userops->rmdir != NULL); + resp.ret = info->userops->rmdir(info->volinfo, req->relpath); + + /* Send the response */ + + resp.resp = USERFS_RESP_RMDIR; + return sendto(info->sockfd, &resp, sizeof(struct userfs_rmdir_response_s), + 0, (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); +} + +static inline int userfs_rename_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_rename_request_s *req, size_t reqlen) +{ + struct userfs_rename_response_s resp; + FAR char *newrelpath; + unsigned int newoffset; + int oldpathlen; + int newpathlen; + size_t expected; + + /* Verify the request size */ + + if (reqlen < SIZEOF_USERFS_RENAME_REQUEST_S(0,0)) + { + return -EINVAL; + } + + oldpathlen = strlen(req->oldrelpath); + newoffset = req->newoffset; + + if (oldpathlen >= newoffset) + { + return -EINVAL; + } + + newrelpath = &req->oldrelpath[newoffset]; + newpathlen = strlen(newrelpath); + + if ((newpathlen + newoffset) > info->mxwrite) + { + return -EINVAL; + } + + expected = SIZEOF_USERFS_RENAME_REQUEST_S(newoffset, newpathlen); + if (expected >= reqlen) + { + return -EINVAL; + } + + /* Dispatch the request */ + + DEBUGASSERT(info->userops != NULL && info->userops->rename != NULL); + resp.ret = info->userops->rename(info->volinfo, req->oldrelpath, + newrelpath); + + /* Send the response */ + + resp.resp = USERFS_RESP_RENAME; + return sendto(info->sockfd, &resp, sizeof(struct userfs_rename_response_s), + 0, (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); +} + +static inline int userfs_stat_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_stat_request_s *req, size_t reqlen) +{ + struct userfs_stat_response_s resp; + int pathlen; + size_t expected; + + /* Verify the request size */ + + if (reqlen < SIZEOF_USERFS_STAT_REQUEST_S(0)) + { + return -EINVAL; + } + + pathlen = strlen(req->relpath); + if (pathlen > info->mxwrite) + { + return -EINVAL; + } + + expected = SIZEOF_USERFS_STAT_REQUEST_S(pathlen); + if (expected >= reqlen) + { + return -EINVAL; + } + + /* Dispatch the request */ + + DEBUGASSERT(info->userops != NULL && info->userops->stat != NULL); + resp.ret = info->userops->stat(info->volinfo, req->relpath, &resp.buf); + + /* Send the response */ + + resp.resp = USERFS_RESP_STAT; + return sendto(info->sockfd, &resp, sizeof(struct userfs_stat_response_s), + 0, (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); +} + +static inline int userfs_destroy_dispatch(FAR struct userfs_info_s *info, + FAR struct userfs_destroy_request_s *req, size_t reqlen) +{ + struct userfs_destroy_response_s resp; + ssize_t nsent; + + /* Verify the request size */ + + if (reqlen != sizeof(struct userfs_destroy_request_s)) + { + return -EINVAL; + } + + /* Dispatch the request */ + + DEBUGASSERT(info->userops != NULL && info->userops->destroy != NULL); + resp.ret = info->userops->destroy(info->volinfo); + + /* Send the response */ + + resp.resp = USERFS_RESP_DESTROY; + nsent = sendto(info->sockfd, &resp, + sizeof(struct userfs_destroy_response_s), + 0, (FAR struct sockaddr *)&info->client, + sizeof(struct sockaddr_un)); + if (nsent < 0) + { + int ret = -errno; + ferr("ERROR: sendto failed: %d\n", ret); + return ret; + } + + /* Speical case of resp.ret indicates an error, the destruction was + * refused. So we need to return success in this case so that we + * continue processing requests. + */ + + return resp.ret < 0 ? OK : -ENOTCONN; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: userfs_run + * + * Description: + * Start the UserFS server on the current thread. This function will mount + * the UserFS file system and will not return until that file system has + * been unmounted. + * + * userfs_run() implements the UserFS server. It performs there operations: + * + * 1. It configures and creates the UserFS file system and + * 2. Mounts the user file system at the provide mount point path. + * 2. Receives file system requests on the Unix doamin local socket with + * address /dev/userfsN where N is the same as above, + * 3. Received file system requests are marshaled and dispatch to the + * user file system via callbacks to the operations provided by + * "userops", and + * 3. Returns file system responses generated by the callbacks via the + * same Unix domain local socket. + * + * NOTE: This is a user function that is implemented as part of the + * NuttX C library and is intended to be called by appliation logic. + * + * Input parameters: + * mountpt - Mountpoint path + * userops - The caller operations that implement the file system + * interface. + * volinfo - Private volume data that will be provided in all struct + * userfs_operations_s methods. + * mxwrite - The max size of a write data + * + * Returned Value: + * This function does not return unless the file system is unmounted (OK) + * or unless an error is encountered. In the event of an error, the + * returned value is a negated errno value indicating the nature of the + * error. + * + ****************************************************************************/ + +int userfs_run(FAR const char *mountpt, + FAR const struct userfs_operations_s *userops, + FAR void *volinfo, size_t mxwrite) +{ + FAR struct userfs_info_s *info; + FAR struct userfs_config_s *config; + struct sockaddr_un server; + unsigned int iolen; + socklen_t addrlen; + socklen_t recvlen; + ssize_t nread; + int ret; + + DEBUGASSERT(mountpt != NULL && userops != NULL && mxwrite <= UINT16_MAX); + DEBUGASSERT(mxwrite > 0 && mxwrite <= (UINT16_MAX - USERFS_REQ_MAXSIZE)); + + /* Allocate a state structrue with an I/O buffer to receive UserFS requests */ + + iolen = USERFS_REQ_MAXSIZE + mxwrite; + info = (FAR struct userfs_info_s *)zalloc(SIZEOF_USERFS_INFO_S(iolen)); + if (info == NULL) + { + ferr("ERROR: Failed to allocate state structure\n"); + return -ENOMEM; + } + + /* Initialize the state structure */ + + info->userops = userops; + info->volinfo = volinfo; + info->iolen = iolen; + info->mxwrite = mxwrite; + + /* Create the UserFS configuration that will be provided as optional + * data when the UserFS is mounted. + */ + + config->mxwrite = mxwrite; + config->instance = userfs_instance(); + + /* Mounts the user file system at the provided mount point path. */ + + ret = mount(NULL, mountpt, "userfs", 0, (FAR const void *)&config); + if (ret < 0) + { + ret = -get_errno(); + ferr("ERROR: mount() failued: %d\n", ret); + goto errout_with_info; + } + + /* Create a new Unix domain datagram server socket */ + + info->sockfd = socket(PF_LOCAL, SOCK_DGRAM, 0); + if (info->sockfd < 0) + { + ret = -get_errno(); + ferr("ERROR: socket() failed: %d\n", ret); + goto errout_with_info; + } + + /* Bind the socket to a local server address */ + + server.sun_family = AF_LOCAL; + snprintf(server.sun_path, UNIX_PATH_MAX, USERFS_SERVER_FMT, + config->instance); + server.sun_path[UNIX_PATH_MAX - 1] = '\0'; + + addrlen = strlen(server.sun_path) + sizeof(sa_family_t) + 1; + ret = bind(info->sockfd, (struct sockaddr*)&server, addrlen); + if (ret < 0) + { + ret = -get_errno(); + ferr("ERROR: bind() failed: %d\n", ret); + goto errout_with_sockfd; + } + + /* Receive file system requests on the POSIX message queue as long + * as the mount persists. + */ + + do + { + /* Receive the next file system request */ + + finfo("Receiving up %u bytes\n", info->iolen); + recvlen = addrlen; + nread = recvfrom(info->sockfd, info->iobuffer, info->iolen, 0, + (FAR struct sockaddr *)&info->client, &recvlen); + + if (nread < 0) + { + ret = -get_errno(); + ferr("ERROR: recvfrom failed: %d\n", ret); + goto errout_with_sockfd; + } + + DEBUGASSERT(recvlen >= sizeof(sa_family_t) && + recvlen <= sizeof(struct sockaddr_un)); + + /* Process the request according to its request ID */ + + DEBUGASSERT(nread >= sizeof(uint8_t)); + switch (*info->iobuffer) + { + case USERFS_REQ_OPEN: + ret = userfs_open_dispatch(info, + (FAR struct userfs_open_request_s *)info->iobuffer, nread); + break; + + case USERFS_REQ_CLOSE: + ret = userfs_close_dispatch(info, + (FAR struct userfs_close_request_s *)info->iobuffer, nread); + break; + + case USERFS_REQ_READ: + ret = userfs_read_dispatch(info, + (FAR struct userfs_read_request_s *)info->iobuffer, nread); + break; + + case USERFS_REQ_WRITE: + ret = userfs_write_dispatch(info, + (FAR struct userfs_write_request_s *)info->iobuffer, nread); + break; + + case USERFS_REQ_SEEK: + ret = userfs_seek_dispatch(info, + (FAR struct userfs_seek_request_s *)info->iobuffer, nread); + break; + + case USERFS_REQ_IOCTL: + ret = userfs_ioctl_dispatch(info, + (FAR struct userfs_ioctl_request_s *)info->iobuffer, nread); + break; + + case USERFS_REQ_SYNC: + ret = userfs_sync_dispatch(info, + (FAR struct userfs_sync_request_s *)info->iobuffer, nread); + break; + + case USERFS_REQ_DUP: + ret = userfs_dup_dispatch(info, + (FAR struct userfs_dup_request_s *)info->iobuffer, nread); + break; + + case USERFS_REQ_FSTAT: + ret = userfs_fstat_dispatch(info, + (FAR struct userfs_fstat_request_s *)info->iobuffer, nread); + break; + + case USERFS_REQ_OPENDIR: + ret = userfs_opendir_dispatch(info, + (FAR struct userfs_opendir_request_s *)info->iobuffer, nread); + break; + + case USERFS_REQ_CLOSEDIR: + ret = userfs_closedir_dispatch(info, + (FAR struct userfs_closedir_request_s *)info->iobuffer, nread); + break; + + case USERFS_REQ_READDIR: + ret = userfs_readdir_dispatch(info, + (FAR struct userfs_readdir_request_s *)info->iobuffer, nread); + break; + + case USERFS_REQ_REWINDDIR: + ret = userfs_rewinddir_dispatch(info, + (FAR struct userfs_rewinddir_request_s *)info->iobuffer, nread); + break; + + case USERFS_REQ_STATFS: + ret = userfs_statfs_dispatch(info, + (FAR struct userfs_statfs_request_s *)info->iobuffer, nread); + break; + + case USERFS_REQ_UNLINK: + ret = userfs_unlink_dispatch(info, + (FAR struct userfs_unlink_request_s *)info->iobuffer, nread); + break; + + case USERFS_REQ_MKDIR: + ret = userfs_mkdir_dispatch(info, + (FAR struct userfs_mkdir_request_s *)info->iobuffer, nread); + break; + + case USERFS_REQ_RMDIR: + ret = userfs_rmdir_dispatch(info, + (FAR struct userfs_rmdir_request_s *)info->iobuffer, nread); + break; + + case USERFS_REQ_RENAME: + ret = userfs_rename_dispatch(info, + (FAR struct userfs_rename_request_s *)info->iobuffer, nread); + break; + + case USERFS_REQ_STAT: + ret = userfs_stat_dispatch(info, + (FAR struct userfs_stat_request_s *)info->iobuffer, nread); + break; + + case USERFS_REQ_DESTROY: + ret = userfs_destroy_dispatch(info, + (FAR struct userfs_destroy_request_s *)info->iobuffer, nread); + break; + + default: + ferr("ERROR: Unrecognized request received: %u\n", *info->iobuffer); + ret = -EINVAL; + break; + } + } + while (ret == OK); + + /* Close the Unix domain socket */ + +errout_with_sockfd: + close(info->sockfd); + + /* Free the IO Buffer */ + +errout_with_info: + free(info); + return ret; +} diff --git a/libc/zoneinfo/README.txt b/libc/zoneinfo/README.txt index 9ceb5de8093..406d1cce58c 100644 --- a/libc/zoneinfo/README.txt +++ b/libc/zoneinfo/README.txt @@ -1,4 +1,4 @@ -apps/system/zoninfo/README.txt +apps/system/zoneinfo/README.txt Author: Gregory Nutt Directory Contents