/**************************************************************************** * fs/v9fs/v9fs.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 "inode/inode.h" #include "client.h" /**************************************************************************** * Private Type ****************************************************************************/ struct v9fs_vfs_file_s { uint32_t fid; mutex_t lock; }; struct v9fs_vfs_dirent_s { struct fs_dirent_s base; uint32_t fid; mutex_t lock; off_t offset; off_t head; size_t size; uint8_t buffer[1]; }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static int v9fs_vfs_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode); static int v9fs_vfs_close(FAR struct file *filep); static ssize_t v9fs_vfs_read(FAR struct file *filep, FAR char *buffer, size_t buflen); static ssize_t v9fs_vfs_write(FAR struct file *filep, FAR const char *buffer, size_t buflen); static off_t v9fs_vfs_seek(FAR struct file *filep, off_t offset, int whence); static int v9fs_vfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg); static int v9fs_vfs_sync(FAR struct file *filep); static int v9fs_vfs_dup(FAR const struct file *oldp, FAR struct file *newp); static int v9fs_vfs_fstat(FAR const struct file *filep, FAR struct stat *buf); static int v9fs_vfs_fchstat(FAR const struct file *filep, FAR const struct stat *buf, int flags); static int v9fs_vfs_truncate(FAR struct file *filep, off_t length); static int v9fs_vfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s **dir); static int v9fs_vfs_closedir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir); static int v9fs_vfs_readdir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir, FAR struct dirent *entry); static int v9fs_vfs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir); static int v9fs_vfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf); static int v9fs_vfs_unlink(FAR struct inode *mountpt, FAR const char *relpath); static int v9fs_vfs_mkdir(FAR struct inode *mountpt, FAR const char *relpath, mode_t mode); static int v9fs_vfs_rmdir(FAR struct inode *mountpt, FAR const char *relpath); static int v9fs_vfs_rename(FAR struct inode *mountpt, FAR const char *oldrelpath, FAR const char *newrelpath); static int v9fs_vfs_stat(FAR struct inode *mountpt, FAR const char *relpath, FAR struct stat *buf); static int v9fs_vfs_chstat(FAR struct inode *mountpt, FAR const char *relpath, FAR const struct stat *buf, int flags); static int v9fs_vfs_bind(FAR struct inode *driver, FAR const void *data, FAR void **handle); static int v9fs_vfs_unbind(FAR void *handle, FAR struct inode **blkdriver, unsigned int flags); /**************************************************************************** * Public Data ****************************************************************************/ const struct mountpt_operations g_v9fs_operations = { v9fs_vfs_open, /* open */ v9fs_vfs_close, /* close */ v9fs_vfs_read, /* read */ v9fs_vfs_write, /* write */ v9fs_vfs_seek, /* seek */ v9fs_vfs_ioctl, /* ioctl */ NULL, /* mmap */ v9fs_vfs_truncate, /* truncate */ NULL, /* poll */ v9fs_vfs_sync, /* sync */ v9fs_vfs_dup, /* dup */ v9fs_vfs_fstat, /* fstat */ v9fs_vfs_fchstat, /* fchstat */ v9fs_vfs_opendir, /* opendir */ v9fs_vfs_closedir, /* closedir */ v9fs_vfs_readdir, /* readdir */ v9fs_vfs_rewinddir, /* rewinddir */ v9fs_vfs_bind, /* bind */ v9fs_vfs_unbind, /* unbind */ v9fs_vfs_statfs, /* statfs */ v9fs_vfs_unlink, /* unlink */ v9fs_vfs_mkdir, /* mkdir */ v9fs_vfs_rmdir, /* rmdir */ v9fs_vfs_rename, /* rename */ v9fs_vfs_stat, /* stat */ v9fs_vfs_chstat /* chstat */ }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: v9fs_vfs_open ****************************************************************************/ static int v9fs_vfs_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode) { FAR struct v9fs_vfs_file_s *file; FAR struct v9fs_client_s *client; int ret; /* Sanity checks */ DEBUGASSERT(filep != NULL && filep->f_inode != NULL); /* Recover out private data from the struct file instance */ client = filep->f_inode->i_private; file = kmm_zalloc(sizeof(struct v9fs_vfs_file_s)); if (file == NULL) { return -ENOMEM; } ret = v9fs_client_walk(client, relpath, NULL); if (ret >= 0) { file->fid = ret; ret = v9fs_client_open(client, file->fid, oflags); if (ret < 0) { ferr("ERROR: Failed to open the fid: %d\n", ret); goto err_put; } } else if ((oflags & O_CREAT) != 0) { /* We expect to create this file */ ret = v9fs_client_walk(client, relpath, &relpath); if (ret < 0) { ferr("ERROR: Can't find the parent fid of relpath: %d\n", ret); goto err_free; } file->fid = ret; ret = v9fs_client_create(client, file->fid, relpath, oflags, mode); if (ret < 0) { ferr("ERROR: Failed to create the file: %d\n", ret); goto err_put; } } else { /* We expect to open this file */ ferr("ERROR: Can't find the fid of relpath: %d\n", ret); ret = -ENOENT; goto err_free; } if ((oflags & O_APPEND) != 0) { filep->f_pos = v9fs_client_getsize(client, file->fid); if (filep->f_pos < 0) { ret = filep->f_pos; ferr("ERROR: Failed to get the file status: %d\n", ret); goto err_put; } } nxmutex_init(&file->lock); filep->f_priv = file; return 0; err_put: v9fs_fid_put(client, file->fid); err_free: kmm_free(file); return ret; } /**************************************************************************** * Name: v9fs_vfs_close ****************************************************************************/ static int v9fs_vfs_close(FAR struct file *filep) { FAR struct v9fs_client_s *client; FAR struct v9fs_vfs_file_s *file; /* Sanity checks */ DEBUGASSERT(filep != NULL && filep->f_inode != NULL); /* Recover out private data from the struct file instance */ client = filep->f_inode->i_private; file = filep->f_priv; v9fs_fid_put(client, file->fid); nxmutex_destroy(&file->lock); kmm_free(file); return 0; } /**************************************************************************** * Name: v9fs_vfs_read ****************************************************************************/ static ssize_t v9fs_vfs_read(FAR struct file *filep, FAR char *buffer, size_t buflen) { FAR struct v9fs_vfs_file_s *file; FAR struct v9fs_client_s *client; ssize_t ret; /* Sanity checks */ DEBUGASSERT(filep != NULL && filep->f_inode != NULL); /* Recover out private data from the struct file instance */ client = filep->f_inode->i_private; file = filep->f_priv; nxmutex_lock(&file->lock); ret = v9fs_client_read(client, file->fid, buffer, filep->f_pos, buflen); if (ret > 0) { filep->f_pos += ret; } nxmutex_unlock(&file->lock); return ret; } /**************************************************************************** * Name: v9fs_vfs_write ****************************************************************************/ static ssize_t v9fs_vfs_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) { FAR struct v9fs_vfs_file_s *file; FAR struct v9fs_client_s *client; ssize_t ret; /* Sanity checks */ DEBUGASSERT(filep != NULL && filep->f_inode != NULL); /* Recover out private data from the struct file instance */ client = filep->f_inode->i_private; file = filep->f_priv; nxmutex_lock(&file->lock); ret = v9fs_client_write(client, file->fid, buffer, filep->f_pos, buflen); if (ret > 0) { filep->f_pos += ret; } nxmutex_unlock(&file->lock); return ret; } /**************************************************************************** * Name: v9fs_vfs_seek ****************************************************************************/ static off_t v9fs_vfs_seek(FAR struct file *filep, off_t offset, int whence) { FAR struct v9fs_vfs_file_s *file; FAR struct v9fs_client_s *client; off_t ret; /* Sanity checks */ DEBUGASSERT(filep != NULL && filep->f_inode != NULL); /* Recover out private data from the struct file instance */ client = filep->f_inode->i_private; file = filep->f_priv; nxmutex_lock(&file->lock); switch (whence) { case SEEK_SET: ret = offset; break; case SEEK_CUR: ret = filep->f_pos + offset; break; case SEEK_END: ret = v9fs_client_getsize(client, file->fid); if (ret >= 0) { ret += offset; } break; default: ferr("ERROR: Invalid whence: %d\n", whence); ret = -EINVAL; break; } if (ret >= 0) { filep->f_pos = ret; } nxmutex_unlock(&file->lock); return ret; } /**************************************************************************** * Name: v9fs_vfs_ioctl ****************************************************************************/ static int v9fs_vfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { FAR struct v9fs_vfs_file_s *file; FAR struct v9fs_client_s *client; int ret = -ENOTTY; client = filep->f_inode->i_private; file = filep->f_priv; if (cmd == FIOC_FILEPATH) { FAR char *ptr = (FAR char *)((uintptr_t)arg); inode_getpath(filep->f_inode, ptr, PATH_MAX); ret = v9fs_client_getname(client, file->fid, ptr); } return ret; } /**************************************************************************** * Name: v9fs_vfs_sync ****************************************************************************/ static int v9fs_vfs_sync(FAR struct file *filep) { FAR struct v9fs_vfs_file_s *file; FAR struct v9fs_client_s *client; client = filep->f_inode->i_private; file = filep->f_priv; return v9fs_client_fsync(client, file->fid); } /**************************************************************************** * Name: v9fs_vfs_dup ****************************************************************************/ static int v9fs_vfs_dup(FAR const struct file *oldp, FAR struct file *newp) { FAR struct v9fs_vfs_file_s *newfile; FAR struct v9fs_vfs_file_s *file; FAR struct v9fs_client_s *client; int ret; /* Sanity checks */ DEBUGASSERT(oldp != NULL && oldp->f_inode != NULL && newp != NULL && newp->f_inode != NULL); /* Recover out private data from the struct file instance */ client = oldp->f_inode->i_private; file = oldp->f_priv; newfile = kmm_zalloc(sizeof(struct v9fs_vfs_file_s)); if (newfile == NULL) { return -ENOMEM; } ret = v9fs_fid_get(client, file->fid); if (ret < 0) { kmm_free(newfile); return ret; } nxmutex_init(&newfile->lock); newfile->fid = file->fid; newp->f_priv = newfile; return ret; } /**************************************************************************** * Name: v9fs_vfs_fstat ****************************************************************************/ static int v9fs_vfs_fstat(FAR const struct file *filep, FAR struct stat *buf) { FAR struct v9fs_vfs_file_s *file; FAR struct v9fs_client_s *client; /* Sanity checks */ DEBUGASSERT(filep != NULL && filep->f_inode != NULL); /* Recover out private data from the struct file instance */ client = filep->f_inode->i_private; file = filep->f_priv; memset(buf, 0, sizeof(struct stat)); return v9fs_client_stat(client, file->fid, buf); } /**************************************************************************** * Name: v9fs_vfs_fchstat ****************************************************************************/ static int v9fs_vfs_fchstat(FAR const struct file *filep, FAR const struct stat *buf, int flags) { FAR struct v9fs_vfs_file_s *file; FAR struct v9fs_client_s *client; /* Sanity checks */ DEBUGASSERT(filep != NULL && filep->f_inode != NULL); /* Recover out private data from the struct file instance */ client = filep->f_inode->i_private; file = filep->f_priv; return v9fs_client_chstat(client, file->fid, buf, flags); } /**************************************************************************** * Name: v9fs_vfs_truncate ****************************************************************************/ static int v9fs_vfs_truncate(FAR struct file *filep, off_t length) { struct stat buf; buf.st_size = length; return v9fs_vfs_fchstat(filep, &buf, CH_STAT_SIZE); } /**************************************************************************** * Name: v9fs_vfs_opendir ****************************************************************************/ static int v9fs_vfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s **dir) { FAR struct v9fs_vfs_dirent_s *fsdir; FAR struct v9fs_client_s *client; uint32_t fid; int ret; /* Sanity checks */ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); /* Recover out private data from the struct file instance */ client = mountpt->i_private; fsdir = kmm_zalloc(sizeof(struct v9fs_vfs_dirent_s) + client->msize); if (fsdir == NULL) { return -ENOMEM; } ret = v9fs_client_walk(client, relpath, NULL); if (ret < 0) { ferr("ERROR: Can't find the fid of the relpath: %d\n", ret); goto err; } fid = ret; ret = v9fs_client_open(client, fid, 0); if (ret < 0) { ferr("ERROR: Failed to open the fid: %d\n", ret); v9fs_fid_put(client, fid); goto err; } nxmutex_init(&fsdir->lock); fsdir->fid = fid; *dir = &fsdir->base; return 0; err: kmm_free(fsdir); return ret; } /**************************************************************************** * Name: v9fs_vfs_closedir ****************************************************************************/ static int v9fs_vfs_closedir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir) { FAR struct v9fs_vfs_dirent_s *fsdir; FAR struct v9fs_client_s *client; /* Sanity checks */ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); /* Recover out private data from the struct file instance */ fsdir = (FAR struct v9fs_vfs_dirent_s *)dir; client = mountpt->i_private; v9fs_fid_put(client, fsdir->fid); nxmutex_destroy(&fsdir->lock); kmm_free(fsdir); return 0; } /**************************************************************************** * Name: v9fs_readdir ****************************************************************************/ static int v9fs_vfs_readdir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir, FAR struct dirent *entry) { FAR struct v9fs_vfs_dirent_s *fsdir; FAR struct v9fs_client_s *client; ssize_t ret; /* Sanity checks */ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); /* Recover out private data from the struct file instance */ fsdir = (FAR struct v9fs_vfs_dirent_s *)dir; client = mountpt->i_private; nxmutex_lock(&fsdir->lock); for (; ; ) { if (fsdir->head == fsdir->size) { ret = v9fs_client_readdir(client, fsdir->fid, fsdir->buffer, fsdir->offset, client->msize); if (ret < 0) { break; } fsdir->head = 0; fsdir->size = ret; } ret = v9fs_client_convertdir(fsdir->buffer, fsdir->size, fsdir->head, &fsdir->offset, entry); if (ret < 0) { break; } fsdir->head += ret; if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) { break; } } nxmutex_unlock(&fsdir->lock); return ret < 0 ? ret : 0; } /**************************************************************************** * Name: v9fs_vfs_rewinddir ****************************************************************************/ static int v9fs_vfs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir) { FAR struct v9fs_vfs_dirent_s *fsdir; /* Sanity checks */ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); /* Recover out private data from the struct file instance */ fsdir = (FAR struct v9fs_vfs_dirent_s *)dir; nxmutex_lock(&fsdir->lock); fsdir->head = 0; fsdir->offset = 0; fsdir->size = 0; nxmutex_unlock(&fsdir->lock); return 0; } /**************************************************************************** * Name: v9fs_vfs_statfs ****************************************************************************/ static int v9fs_vfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf) { FAR struct v9fs_client_s *client; /* Sanity checks */ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); /* Recover out private data from the struct file instance */ client = mountpt->i_private; memset(buf, 0, sizeof(struct statfs)); return v9fs_client_statfs(client, buf); } /**************************************************************************** * Name: v9fs_vfs_unlink ****************************************************************************/ static int v9fs_vfs_unlink(FAR struct inode *mountpt, FAR const char *relpath) { FAR struct v9fs_client_s *client; FAR const char *filename; uint32_t fid; int ret; /* Sanity checks */ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); /* Recover out private data from the struct file instance */ client = mountpt->i_private; ret = v9fs_client_walk(client, relpath, &filename); if (ret < 0) { ferr("ERROR: Can't find the parent fid of relpath: %d\n", ret); return ret; } fid = ret; ret = v9fs_client_unlink(client, fid, filename, false); v9fs_fid_put(client, fid); if (ret == -EOPNOTSUPP) { /* Maybe the server does not support this method of deletion. * Let's change the protocol to send */ ret = v9fs_client_walk(client, relpath, NULL); if (ret < 0) { ferr("ERROR: Can't find the fid of relpath: %d\n", ret); return ret; } fid = ret; ret = v9fs_client_remove(client, fid); v9fs_fid_put(client, fid); } return ret; } /**************************************************************************** * Name: v9fs_vfs_mkdir ****************************************************************************/ static int v9fs_vfs_mkdir(FAR struct inode *mountpt, FAR const char *relpath, mode_t mode) { FAR struct v9fs_client_s *client; uint32_t fid; int ret; /* Sanity checks */ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); /* Recover out private data from the struct file instance */ client = mountpt->i_private; ret = v9fs_client_walk(client, relpath, &relpath); if (ret < 0) { ferr("ERROR: Can't find the parent fid of relpath: %d\n", ret); return ret; } fid = ret; ret = v9fs_client_mkdir(client, fid, relpath, mode); v9fs_fid_put(client, fid); return ret; } /**************************************************************************** * Name: v9fs_vfs_rmdir ****************************************************************************/ static int v9fs_vfs_rmdir(FAR struct inode *mountpt, FAR const char *relpath) { FAR struct v9fs_client_s *client; FAR const char *dirname; uint32_t fid; int ret; /* Sanity checks */ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); /* Recover out private data from the struct file instance */ client = mountpt->i_private; ret = v9fs_client_walk(client, relpath, &dirname); if (ret < 0) { ferr("ERROR: Can't find the parent fid of relpath: %d\n", ret); return ret; } fid = ret; ret = v9fs_client_unlink(client, fid, dirname, true); v9fs_fid_put(client, fid); if (ret == -EOPNOTSUPP) { /* Maybe the server does not support this method of deletion. * Let's change the protocol to send */ ret = v9fs_client_walk(client, relpath, NULL); if (ret < 0) { ferr("ERROR: Can't find the fid of relpath: %d\n", ret); return ret; } fid = ret; ret = v9fs_client_remove(client, fid); v9fs_fid_put(client, fid); } return ret; } /**************************************************************************** * Name: v9fs_vfs_rename ****************************************************************************/ static int v9fs_vfs_rename(FAR struct inode *mountpt, FAR const char *oldrelpath, FAR const char *newrelpath) { FAR struct v9fs_client_s *client; uint32_t oldfid; uint32_t newpfid; int ret; /* Sanity checks */ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); /* Recover out private data from the struct file instance */ client = mountpt->i_private; ret = v9fs_client_walk(client, oldrelpath, NULL); if (ret < 0) { ferr("ERROR: Can't find the fid of the oldrelpath: %d\n", ret); return ret; } oldfid = ret; ret = v9fs_client_walk(client, newrelpath, &newrelpath); if (ret < 0) { ferr("ERROR: Can't find the new parent fid of the newrelpath: %d\n", ret); v9fs_fid_put(client, oldfid); return ret; } newpfid = ret; ret = v9fs_client_rename(client, oldfid, newpfid, newrelpath); v9fs_fid_put(client, oldfid); v9fs_fid_put(client, newpfid); return ret; } /**************************************************************************** * Name: v9fs_vfs_stat ****************************************************************************/ static int v9fs_vfs_stat(FAR struct inode *mountpt, FAR const char *relpath, FAR struct stat *buf) { FAR struct v9fs_client_s *client; uint32_t fid; int ret; /* Sanity checks */ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); /* Recover out private data from the struct file instance */ client = mountpt->i_private; memset(buf, 0, sizeof(struct stat)); ret = v9fs_client_walk(client, relpath, NULL); if (ret < 0) { ferr("ERROR: Can't find the fid of the relpath: %d\n", ret); return ret; } fid = ret; ret = v9fs_client_stat(client, fid, buf); v9fs_fid_put(client, fid); return ret; } /**************************************************************************** * Name: v9fs_vfs_chstat ****************************************************************************/ static int v9fs_vfs_chstat(FAR struct inode *mountpt, FAR const char *relpath, FAR const struct stat *buf, int flags) { FAR struct v9fs_client_s *client; uint32_t fid; int ret; /* Sanity checks */ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); /* Recover out private data from the struct file instance */ client = mountpt->i_private; ret = v9fs_client_walk(client, relpath, NULL); if (ret < 0) { ferr("ERROR: Can't find the fid of the relpath %d\n", ret); return ret; } fid = ret; ret = v9fs_client_chstat(client, fid, buf, flags); v9fs_fid_put(client, fid); return ret; } /**************************************************************************** * Name: v9fs_vfs_unbind ****************************************************************************/ static int v9fs_vfs_unbind(FAR void *handle, FAR struct inode **blkdriver, unsigned int flags) { FAR struct v9fs_client_s *client; int ret; client = handle; ret = v9fs_client_uninit(client); if (ret < 0) { ferr("ERROR: Failed to clunk root fid\n"); return ret; } kmm_free(client); return ret; } /**************************************************************************** * Name: v9fs_vfs_bind ****************************************************************************/ static int v9fs_vfs_bind(FAR struct inode *driver, FAR const void *data, FAR void **handle) { FAR struct v9fs_client_s *client; int ret; client = kmm_zalloc(sizeof(struct v9fs_client_s)); if (client == NULL) { return -ENOMEM; } ret = v9fs_client_init(client, data); if (ret < 0) { ferr("ERROR: Failed to initialize for client: %d\n", ret); kmm_free(client); return ret; } *handle = client; return ret; }