/**************************************************************************** * 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_READ) { 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; struct sockaddr_un client; socklen_t addrlen; 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; } priv->psock.s_crefs = 1; /* Bind the socket to the client address */ client.sun_family = AF_LOCAL; snprintf(client.sun_path, UNIX_PATH_MAX, USERFS_CLIENT_FMT, config->instance); client.sun_path[UNIX_PATH_MAX - 1] = '\0'; addrlen = strlen(client.sun_path) + sizeof(sa_family_t) + 1; ret = psock_bind(&priv->psock, (struct sockaddr*)&client, addrlen); if (ret < 0) { ferr("ERROR: bind() failed: %d\n", ret); goto errout_with_psock; } priv->psock.s_crefs = 1; /* Mounted! */ *handle = (FAR void *)priv; return OK; errout_with_psock: psock_close(&priv->psock); 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 ****************************************************************************/