From 6e9039bb08e963cd502d102fabee9c292f20fd05 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Mon, 30 Oct 2017 18:04:28 -0600 Subject: [PATCH] Squashed commit of the following: fs/userfs: This completes coding of the UserFS client and of the UserFS feature in general. This feature is being merged to main now because I believe it is innocuous. It is, however, untesed. The next step will be to develop a test case to verify the feature. fs/userfs: Completes the request logic for the UserFS client. Still need the logic that receives the responses. fs/userfs: Completes coding for most of the server side of the user filesystem logic. fs/userfs: Big design changes, simplications. Use Unix domain local sockets instead of message queues. Easier to transfer big data in local sockets than message queues. Remove character drvier 'factory' it is not necessary. fs/userfs: Minor reparitioning; volume private info does not need to be held on the OS client side. libc/userfs: Add some of the server side logic. fs/userfs: Add some UserFS initialization logic. fs/userfs: Add frame work for the UserFS proxy. Remove all references to a block driver. There is no block dricer... what was I thinking? fs/userfs: Add some initialization of the character driver, 'factory' device. fs/userfs: Rename from fusefs to userfs to that we don't stomp on someone else's cool name. Add a header file describing the fusefs interface. --- Directories.mk | 2 +- arch/risc-v/src/common/up_initialize.c | 15 +- fs/Kconfig | 1 + fs/Makefile | 4 +- fs/mount/fs_gettype.c | 8 +- fs/mount/fs_mount.c | 8 +- fs/userfs/Kconfig | 14 + fs/userfs/Make.defs | 46 + fs/userfs/fs_userfs.c | 1625 ++++++++++++++++++++++++ fs/userfs/userfs.h | 52 + include/nuttx/fs/dirent.h | 16 +- include/nuttx/fs/ioctl.h | 5 + include/nuttx/fs/userfs.h | 598 +++++++++ include/sys/statfs.h | 1 + libc/Kconfig | 1 + libc/Makefile | 1 + libc/userfs/.gitignore | 10 + libc/userfs/Kconfig | 4 + libc/userfs/Make.defs | 47 + libc/userfs/lib_userfs.c | 1058 +++++++++++++++ libc/zoneinfo/README.txt | 2 +- 21 files changed, 3509 insertions(+), 9 deletions(-) create mode 100644 fs/userfs/Kconfig create mode 100644 fs/userfs/Make.defs create mode 100644 fs/userfs/fs_userfs.c create mode 100644 fs/userfs/userfs.h create mode 100644 include/nuttx/fs/userfs.h create mode 100644 libc/userfs/.gitignore create mode 100644 libc/userfs/Kconfig create mode 100644 libc/userfs/Make.defs create mode 100644 libc/userfs/lib_userfs.c 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