/**************************************************************************** * fs/userfs/fs_userfs.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fs_heap.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define IOBUFFER_SIZE(p) (USERFS_REQ_MAXSIZE + (p)->mxwrite) /**************************************************************************** * Private Types ****************************************************************************/ struct userfs_dir_s { struct fs_dirent_s base; FAR void *dir; }; /* 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 */ struct socket psock; /* Client socket instance */ struct sockaddr_in server; /* Server address */ mutex_t lock; /* Exclusive access for request-response sequence */ /* 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_truncate(FAR struct file *filep, off_t length); 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, FAR struct dirent *entry); 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); static int userfs_fchstat(FAR const struct file *filep, FAR const struct stat *buf, int flags); static int userfs_chstat(FAR struct inode *mountpt, FAR const char *relpath, FAR const struct stat *buf, int flags); /**************************************************************************** * 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 g_userfs_operations = { userfs_open, /* open */ userfs_close, /* close */ userfs_read, /* read */ userfs_write, /* write */ userfs_seek, /* seek */ userfs_ioctl, /* ioctl */ NULL, /* mmap */ userfs_truncate, /* truncate */ NULL, /* poll */ userfs_sync, /* sync */ userfs_dup, /* dup */ userfs_fstat, /* fstat */ userfs_fchstat, /* fchstat */ 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 */ userfs_chstat /* chstat */ }; /**************************************************************************** * 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; int ret; finfo("Open '%s'\n", relpath); DEBUGASSERT( filep->f_inode != NULL && filep->f_inode->i_private != NULL); priv = filep->f_inode->i_private; /* Check the path length */ DEBUGASSERT(relpath != NULL); pathlen = strlen(relpath); if (pathlen > priv->mxwrite) { return -E2BIG; } /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* 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; strlcpy(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, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); 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; int ret; DEBUGASSERT( filep->f_inode != NULL && filep->f_inode->i_private != NULL); priv = filep->f_inode->i_private; /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* 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, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); 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; } if (resp->ret >= 0) { filep->f_priv = NULL; } 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; int ret; finfo("Read %zu bytes from offset %jd\n", buflen, (intmax_t)filep->f_pos); DEBUGASSERT( filep->f_inode != NULL && filep->f_inode->i_private != NULL); priv = filep->f_inode->i_private; /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* 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, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); 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; int ret; finfo("Write %zu bytes to offset %jd\n", buflen, (intmax_t)filep->f_pos); DEBUGASSERT( 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 implemented yet */ } /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* 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, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); 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; int ret; finfo("Offset %lu bytes to whence=%d\n", (unsigned long)offset, whence); DEBUGASSERT( filep->f_inode != NULL && filep->f_inode->i_private != NULL); priv = filep->f_inode->i_private; /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* 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, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); 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; int ret; finfo("cmd: %d arg: %08lx\n", cmd, arg); DEBUGASSERT( filep->f_inode != NULL && filep->f_inode->i_private != NULL); priv = filep->f_inode->i_private; /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* 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, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); 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; int ret; DEBUGASSERT( filep->f_inode != NULL && filep->f_inode->i_private != NULL); priv = filep->f_inode->i_private; /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* 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, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); 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; int ret; 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; /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* 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, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); 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; int ret; DEBUGASSERT( filep->f_inode != NULL && filep->f_inode->i_private != NULL); priv = filep->f_inode->i_private; /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* 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, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); 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_fchstat * * Description: * Change information about an open file associated with the file * descriptor 'filep'. * ****************************************************************************/ static int userfs_fchstat(FAR const struct file *filep, FAR const struct stat *buf, int flags) { FAR struct userfs_state_s *priv; FAR struct userfs_fchstat_request_s *req; FAR struct userfs_fchstat_response_s *resp; ssize_t nsent; ssize_t nrecvd; int ret; DEBUGASSERT( filep->f_inode != NULL && filep->f_inode->i_private != NULL); priv = filep->f_inode->i_private; /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* Construct and send the request to the server */ req = (FAR struct userfs_fchstat_request_s *)priv->iobuffer; req->req = USERFS_REQ_FCHSTAT; req->openinfo = filep->f_priv; req->buf = *buf; req->flags = flags; nsent = psock_sendto(&priv->psock, priv->iobuffer, sizeof(struct userfs_fchstat_request_s), 0, (FAR struct sockaddr *)&priv->server, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %zd\n", nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); if (nrecvd < 0) { ferr("ERROR: psock_recvfrom failed: %zd\n", nrecvd); return (int)nrecvd; } if (nrecvd != sizeof(struct userfs_fchstat_response_s)) { ferr("ERROR: Response size incorrect: %zd\n", nrecvd); return -EIO; } resp = (FAR struct userfs_fchstat_response_s *)priv->iobuffer; if (resp->resp != USERFS_RESP_FCHSTAT) { ferr("ERROR: Incorrect response: %u\n", resp->resp); return -EIO; } return resp->ret; } /**************************************************************************** * Name: userfs_truncate * * Description: * Set the size of the regular file referred to by 'filep' to 'length' * ****************************************************************************/ static int userfs_truncate(FAR struct file *filep, off_t length) { FAR struct userfs_state_s *priv; FAR struct userfs_truncate_request_s *req; FAR struct userfs_truncate_response_s *resp; ssize_t nsent; ssize_t nrecvd; int ret; DEBUGASSERT( filep->f_inode != NULL && filep->f_inode->i_private != NULL); priv = filep->f_inode->i_private; /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* Construct and send the request to the server */ req = (FAR struct userfs_truncate_request_s *)priv->iobuffer; req->req = USERFS_REQ_TRUNCATE; req->openinfo = filep->f_priv; req->length = length; nsent = psock_sendto(&priv->psock, priv->iobuffer, sizeof(struct userfs_truncate_request_s), 0, (FAR struct sockaddr *)&priv->server, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); if (nrecvd < 0) { ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); return (int)nrecvd; } if (nrecvd != sizeof(struct userfs_truncate_response_s)) { ferr("ERROR: Response size incorrect: %u\n", (unsigned int)nrecvd); return -EIO; } resp = (FAR struct userfs_truncate_response_s *)priv->iobuffer; if (resp->resp != USERFS_RESP_FSTAT) { ferr("ERROR: Incorrect response: %u\n", resp->resp); return -EIO; } /* Return the result of truncate operation */ 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_dir_s *udir; 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; int ret; finfo("relpath: \"%s\"\n", relpath ? relpath : "NULL"); DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); priv = mountpt->i_private; /* Check the path length */ DEBUGASSERT(relpath != NULL); pathlen = strlen(relpath); if (pathlen > priv->mxwrite) { return -E2BIG; } /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* Construct and send the request to the server */ req = (FAR struct userfs_opendir_request_s *)priv->iobuffer; req->req = USERFS_REQ_OPENDIR; strlcpy(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, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); 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); udir = fs_heap_zalloc(sizeof(struct userfs_dir_s)); if (udir == NULL) { return -ENOMEM; } udir->dir = resp->dir; *dir = (FAR struct fs_dirent_s *)udir; 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_dir_s *udir; 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; int ret; DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); priv = mountpt->i_private; udir = (FAR struct userfs_dir_s *)dir; /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* Construct and send the request to the server */ req = (FAR struct userfs_closedir_request_s *)priv->iobuffer; req->req = USERFS_REQ_CLOSEDIR; req->dir = udir->dir; nsent = psock_sendto(&priv->psock, priv->iobuffer, sizeof(struct userfs_closedir_request_s), 0, (FAR struct sockaddr *)&priv->server, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); 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; } fs_heap_free(udir); 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 dirent *entry) { FAR struct userfs_dir_s *udir; 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; int ret; DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); priv = mountpt->i_private; udir = (FAR struct userfs_dir_s *)dir; /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* Construct and send the request to the server */ req = (FAR struct userfs_readdir_request_s *)priv->iobuffer; req->req = USERFS_REQ_READDIR; req->dir = udir->dir; nsent = psock_sendto(&priv->psock, priv->iobuffer, sizeof(struct userfs_readdir_request_s), 0, (FAR struct sockaddr *)&priv->server, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); 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(entry, &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_dir_s *udir; 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; int ret; DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); priv = mountpt->i_private; udir = (FAR struct userfs_dir_s *)dir; /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* Construct and send the request to the server */ req = (FAR struct userfs_rewinddir_request_s *)priv->iobuffer; req->req = USERFS_REQ_REWINDDIR; req->dir = udir->dir; nsent = psock_sendto(&priv->psock, priv->iobuffer, sizeof(struct userfs_rewinddir_request_s), 0, (FAR struct sockaddr *)&priv->server, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); 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 incorrect: %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_in client; unsigned int iolen; int ret; DEBUGASSERT(data != NULL && handle != NULL); config = (FAR const struct userfs_config_s *)data; /* Allocate an instance of the UserFS state structure */ iolen = USERFS_REQ_MAXSIZE + config->mxwrite; priv = fs_heap_malloc(SIZEOF_USERFS_STATE_S(iolen)); if (priv == NULL) { ferr("ERROR: Failed to allocate state structure\n"); return -ENOMEM; } /* Initialize the mutex that assures mutually exclusive access through * the entire request-response sequence. */ nxmutex_init(&priv->lock); /* 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; /* Preset the server address */ priv->server.sin_family = AF_INET; priv->server.sin_port = HTONS(config->portno); priv->server.sin_addr.s_addr = HTONL(INADDR_LOOPBACK); /* Create a LocalHost UDP client socket */ ret = psock_socket(PF_INET, SOCK_DGRAM, 0, &priv->psock); if (ret < 0) { ferr("ERROR: socket() failed: %d\n", ret); goto errout_with_alloc; } /* Bind the socket to the client address */ client.sin_family = AF_INET; client.sin_port = 0; client.sin_addr.s_addr = HTONL(INADDR_LOOPBACK); ret = psock_bind(&priv->psock, (FAR struct sockaddr *)&client, sizeof(struct sockaddr_in)); if (ret < 0) { ferr("ERROR: bind() failed: %d\n", ret); goto errout_with_psock; } /* Mounted! */ *handle = (FAR void *)priv; return OK; errout_with_psock: psock_close(&priv->psock); errout_with_alloc: nxmutex_destroy(&priv->lock); fs_heap_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; int ret; DEBUGASSERT(priv != NULL); /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* 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, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); 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); nxmutex_destroy(&priv->lock); fs_heap_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; int ret; DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); priv = mountpt->i_private; /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* 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, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); if (nrecvd < 0) { ferr("ERROR: psock_recvfrom failed: %d\n", (int)nrecvd); return (int)nrecvd; } if (nrecvd != sizeof(struct userfs_statfs_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; int ret; DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); priv = mountpt->i_private; /* Check the path length */ DEBUGASSERT(relpath != NULL); pathlen = strlen(relpath); if (pathlen > priv->mxwrite) { return -E2BIG; } /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* Construct and send the request to the server */ req = (FAR struct userfs_unlink_request_s *)priv->iobuffer; req->req = USERFS_REQ_UNLINK; strlcpy(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, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); 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; int ret; DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); priv = mountpt->i_private; /* Check the path length */ DEBUGASSERT(relpath != NULL); pathlen = strlen(relpath); if (pathlen > priv->mxwrite) { return -E2BIG; } /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* 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; strlcpy(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, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); 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; int ret; DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); priv = mountpt->i_private; /* Check the path length */ DEBUGASSERT(relpath != NULL); pathlen = strlen(relpath); if (pathlen > priv->mxwrite) { return -E2BIG; } /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* Construct and send the request to the server */ req = (FAR struct userfs_rmdir_request_s *)priv->iobuffer; req->req = USERFS_REQ_RMDIR; strlcpy(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, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); 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; int ret; DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); priv = mountpt->i_private; /* Check the path lengths */ DEBUGASSERT(oldrelpath != NULL && newrelpath != NULL); oldpathlen = strlen(oldrelpath) + 1; newpathlen = strlen(newrelpath) + 1; if ((oldpathlen + newpathlen) > priv->mxwrite) { return -E2BIG; } /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* Construct and send the request to the server */ req = (FAR struct userfs_rename_request_s *)priv->iobuffer; req->req = USERFS_REQ_RENAME; req->newoffset = oldpathlen; strlcpy(req->oldrelpath, oldrelpath, oldpathlen); strlcpy(&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, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); 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; int ret; DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); priv = mountpt->i_private; /* Check the path length */ DEBUGASSERT(relpath != NULL); pathlen = strlen(relpath); if (pathlen > priv->mxwrite) { return -E2BIG; } /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* Construct and send the request to the server */ req = (FAR struct userfs_stat_request_s *)priv->iobuffer; req->req = USERFS_REQ_STAT; strlcpy(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, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %d\n", (int)nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); 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; } /**************************************************************************** * Name: userfs_chstat * * Description: * Change information about a file or directory * ****************************************************************************/ static int userfs_chstat(FAR struct inode *mountpt, FAR const char *relpath, FAR const struct stat *buf, int flags) { FAR struct userfs_state_s *priv; FAR struct userfs_chstat_request_s *req; FAR struct userfs_chstat_response_s *resp; ssize_t nsent; ssize_t nrecvd; int pathlen; int ret; DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); priv = mountpt->i_private; /* Check the path length */ DEBUGASSERT(relpath != NULL); pathlen = strlen(relpath); if (pathlen > priv->mxwrite) { return -E2BIG; } /* Get exclusive access */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* Construct and send the request to the server */ req = (FAR struct userfs_chstat_request_s *)priv->iobuffer; req->req = USERFS_REQ_CHSTAT; req->buf = *buf; req->flags = flags; strlcpy(req->relpath, relpath, priv->mxwrite); nsent = psock_sendto(&priv->psock, priv->iobuffer, SIZEOF_USERFS_CHSTAT_REQUEST_S(pathlen + 1), 0, (FAR struct sockaddr *)&priv->server, sizeof(struct sockaddr_in)); if (nsent < 0) { ferr("ERROR: psock_sendto failed: %zd\n", nsent); nxmutex_unlock(&priv->lock); return (int)nsent; } /* Then get the response from the server */ nrecvd = psock_recvfrom(&priv->psock, priv->iobuffer, IOBUFFER_SIZE(priv), 0, NULL, NULL); nxmutex_unlock(&priv->lock); if (nrecvd < 0) { ferr("ERROR: psock_recvfrom failed: %zd\n", nrecvd); return (int)nrecvd; } if (nrecvd != sizeof(struct userfs_chstat_response_s)) { ferr("ERROR: Response size incorrect: %zd\n", nrecvd); return -EIO; } resp = (FAR struct userfs_chstat_response_s *)priv->iobuffer; if (resp->resp != USERFS_RESP_STAT) { ferr("ERROR: Incorrect response: %u\n", resp->resp); return -EIO; } return resp->ret; } /**************************************************************************** * Public Functions ****************************************************************************/