/**************************************************************************** * fs/mnemofs/mnemofs.c * mnemofs: Filesystem for NAND Flash storage devices. * * 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. * * Alternatively, the contents of this file may be used under the terms of * the BSD-3-Clause license: * * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2024 Saurav Pal * * 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 of the author nor the names of its contributors may * be used to endorse or promote products derived from this software * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * ****************************************************************************/ /* mnemofs has these components: * - Block Allocator * - Journal * - LRU * - Master Node * * All the files and directories are stored as a reversed CTZ skip list, a * data structure defined by littlefs. CTZs list have multiple nodes * connected by pointers stored in the nodes. So, each node has a pointer * area, which is at the end of the CTZ node (CTZ block), and a data area, * which forms the rest of the area, which stores the actual file data. The * size of the data area can be deterministically found given the index of * the node in the CTZ list. In mnemofs, each CTZ node/CTZ block takes up one * page in the NAND flash. * * Due to the presence of a data area, the mnemofs VFS methods can only view * an abstracted view of the CTZ list they are interacting with. VFS methods * work with CTZ methods (mnemofs_ctz.c), and can not see the existence of * the pointer area, nor do they have to care about it. They work with * offsets in the data area from the beginning of the list, and the CTZ * methods take care of converting that offset into actual NAND flash * coordinates (block number, or page number). * * More info in mnemofs_ctz.c, mnemofs_lru.c, mnemofs_journal.c and * mnemofs_master.c. */ /* TODO: * - LRU and journal store multiple deltas/commits together respectively. * These are supposed to be applied together atomically. * - Journal moves. * - Return values of all functions. */ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include "mnemofs.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /**************************************************************************** * Private Types ****************************************************************************/ /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static int mnemofs_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode); static int mnemofs_close(FAR struct file *filep); static ssize_t mnemofs_read(FAR struct file *filep, FAR char *buffer, size_t buflen); static ssize_t mnemofs_write(FAR struct file *filep, FAR const char *buffer, size_t buflen); static off_t mnemofs_seek(FAR struct file *filep, off_t offset, int whence); static int mnemofs_ioctl(FAR struct file *filep, int cmd, unsigned long arg); static int mnemofs_truncate(FAR struct file *filep, off_t length); static int mnemofs_sync(FAR struct file *filep); static int mnemofs_dup(FAR const struct file *oldp, FAR struct file *newp); static int mnemofs_fstat(FAR const struct file *filep, FAR struct stat *buf); static int mnemofs_opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s **dir); static int mnemofs_closedir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir); static int mnemofs_readdir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir, FAR struct dirent *entry); static int mnemofs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir); static int mnemofs_bind(FAR struct inode *driver, FAR const void *data, FAR void** handle); static int mnemofs_unbind(FAR void *handle, FAR struct inode **driver, unsigned int flags); static int mnemofs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf); static int mnemofs_unlink(FAR struct inode *mountpt, FAR const char *relpath); static int mnemofs_mkdir(FAR struct inode *mountpt, FAR const char *relpath, mode_t mode); static int mnemofs_rmdir(FAR struct inode *mountpt, FAR const char *relpath); static int mnemofs_rename(FAR struct inode *mountpt, FAR const char *oldrelpath, FAR const char *newrelpath); static int mnemofs_stat(FAR struct inode *mountpt, FAR const char *relpath, FAR struct stat *buf); /**************************************************************************** * Private Data ****************************************************************************/ /**************************************************************************** * Public Data ****************************************************************************/ /**************************************************************************** * Name: g_mnemofs_operations * * Description: * The global list of VFS methods implemented by mnemofs * ****************************************************************************/ const struct mountpt_operations g_mnemofs_operations = { mnemofs_open, /* open */ mnemofs_close, /* close */ mnemofs_read, /* read */ mnemofs_write, /* write */ mnemofs_seek, /* seek */ mnemofs_ioctl, /* ioctl */ NULL, /* mmap */ mnemofs_truncate, /* truncate */ NULL, /* poll */ mnemofs_sync, /* sync */ mnemofs_dup, /* dup */ mnemofs_fstat, /* fstat */ NULL, /* fchstat */ mnemofs_opendir, /* opendir */ mnemofs_closedir, /* closedir */ mnemofs_readdir, /* readdir */ mnemofs_rewinddir, /* rewinddir */ mnemofs_bind, /* bind */ mnemofs_unbind, /* unbind */ mnemofs_statfs, /* statfs */ mnemofs_unlink, /* unlink */ mnemofs_mkdir, /* mkdir */ mnemofs_rmdir, /* rmdir */ mnemofs_rename, /* rename */ mnemofs_stat, /* stat */ NULL /* chstat */ }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: mnemofs_open * * Description: * Open a file given its path and populate its file pointer. The file * pointer's private field is filled with information about the open file. * See `open(2)` for details on the work and parameters of this function. * * File pointers' private member have two parts...`mfs_ofd_d` is the actual * entry for the file descriptor (fd), while the inner part is a reference * counted shared portion formed from `mfs_ocom_s`. This way, multiple fds * can point to the same file without problems. The shared portion is * shared among the fds of the same file, while the upper part is different * for each fd, regardless of whether they belong to the same file or not. * A list of upper parts of files is stored in `sb` as a kernel linked * list. * * Input Parameters: * filep - File pointer. * relpath - Relative path to the file from the mount root. * oflags - Flags for opening the file. * mode - Access Mode (User|Group|All) if creating the file. * * Returned Value: * 0 - OK * < 0 - Error * ****************************************************************************/ static int mnemofs_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode) { int ret = OK; int flags; struct mfs_pitr_s pitr; FAR const char *child = NULL; FAR struct inode *inode; FAR struct mfs_sb_s *sb; FAR struct mfs_ofd_s *f; FAR struct mfs_ocom_s *fcom; FAR struct mfs_dirent_s *dirent = NULL; finfo("Mnemofs open on path \"%s\" with flags %x and mode %x.", relpath, oflags, mode); inode = filep->f_inode; DEBUGASSERT(inode != NULL); sb = inode->i_private; DEBUGASSERT(sb != NULL); ret = nxmutex_lock(&MFS_LOCK(sb)); if (predict_false(ret < 0)) { goto errout; } finfo("Lock Acquired."); f = kmm_zalloc(sizeof(*f)); if (predict_false(f == NULL)) { ret = -ENOMEM; goto errout_with_lock; } fcom = kmm_zalloc(sizeof(*fcom)); if (predict_false(fcom == NULL)) { ret = -ENOMEM; goto errout_with_f; } finfo("Memory allocations done."); f->com = fcom; f->com->refcount++; /* Check creation flags. */ flags = mfs_get_patharr(sb, relpath, &f->com->path, &f->com->depth); if ((flags & MFS_NEXIST) != 0) { if ((flags & MFS_P_ISDIR) != 0) { if ((oflags & O_CREAT) != 0) { /* Add direntry to parent's directory file. */ f->com->new_ent = true; mfs_pitr_init(sb, f->com->path, f->com->depth, &pitr, true); child = mfs_path2childname(relpath); mfs_pitr_appendnew(sb, f->com->path, f->com->depth, &pitr, child, mode); mfs_pitr_free(&pitr); finfo("Created new file."); /* OK */ } } else { ret = -EISDIR; goto errout_with_fcom; } } else if ((flags & MFS_ISFILE) != 0) { if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) { ret = -EEXIST; goto errout_with_fcom; } else { /* OK */ } } else { ret = -EISDIR; goto errout_with_fcom; } /* Check r/w permission flags. */ /* TODO: Update mtime and atime. */ mfs_pitr_init(sb, f->com->path, f->com->depth, &pitr, true); mfs_pitr_readdirent(sb, &pitr, &dirent); if (dirent != NULL) { if ((oflags & O_WRONLY) != 0 && (dirent->mode & O_WRONLY) == 0) { ret = -EACCES; goto errout_with_dirent; } mfs_free_dirent(dirent); dirent = NULL; } f->com->sz = mfs_get_fsz(sb, f->com->path, f->com->depth); f->com->off = 0; f->com->oflags = oflags; mfs_pitr_free(&pitr); finfo("Direntry is read and processed."); /* Check Offset flags. */ if ((oflags & (O_TRUNC | O_WRONLY)) == (O_TRUNC | O_WRONLY) || (oflags & (O_TRUNC | O_RDONLY)) == (O_TRUNC | O_RDONLY)) { /* Truncate to size 0. If write and truncate are mentioned only * then it's truncated. Else, the truncate flag is ignored. */ ret = mfs_lru_del(sb, 0, f->com->sz, f->com->sz, f->com->path, f->com->depth); if (predict_false(ret < 0)) { finfo("Error while truncating file. Ret: %d.", ret); goto errout_with_dirent; } } if ((oflags & O_APPEND) != 0) { f->com->off = f->com->sz; } finfo("Offset flags are set."); list_add_tail(&sb->of, &f->list); filep->f_priv = f; nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock Released."); finfo("Mnemofs open exited with %d.", ret); return ret; errout_with_dirent: if (dirent != NULL) { mfs_free_dirent(dirent); } errout_with_fcom: kmm_free(fcom); errout_with_f: kmm_free(f); errout_with_lock: nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock Released."); errout: finfo("Mnemofs open exited with %d.", ret); return ret; } /**************************************************************************** * Name: mnemofs_close * * Description: * Closes a file, and frees up the allocated space for the open file's * representation in the file pointer's private field. mnemofs also syncs * up the on-flash data after closing of a file (unlike a typical fs as * mentioned in `man`). This is to be prepared for random power loss at all * possible times. See `close(2)` for details on the work and parameters * of this function. * * Input Parameters: * filep - File pointer. * * Returned Value: * 0 - OK * < 0 - Error * ****************************************************************************/ static int mnemofs_close(FAR struct file *filep) { int ret = OK; FAR struct inode *inode; FAR struct mfs_sb_s *sb; FAR struct mfs_ofd_s *f; finfo("Mnemofs close."); inode = filep->f_inode; DEBUGASSERT(inode != NULL); sb = inode->i_private; DEBUGASSERT(sb != NULL); ret = nxmutex_lock(&MFS_LOCK(sb)); if (ret < 0) { goto errout; } finfo("Lock Acquired."); f = filep->f_priv; DEBUGASSERT(f != NULL); /* Flushing in-memory data to on-flash journal. */ ret = mfs_lru_ctzflush(sb, f->com->path, f->com->depth, f->com->sz); if (predict_false(ret < 0)) { finfo("Error while flushing file. Ret: %d.", ret); goto errout_with_lock; } f->com->refcount--; if (predict_true(f->com->refcount != 0)) { kmm_free(f->com->path); kmm_free(f->com); kmm_free(f); finfo("Refcount is 0, open file structure freed."); } list_delete(&f->list); filep->f_priv = NULL; finfo("File entry removed from the open files list."); errout_with_lock: nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock Released."); errout: finfo("Mnemofs close exited with %d.", ret); return ret; } /**************************************************************************** * Name: mnemofs_read * * Description: * This reads an open file at the current offset into the provided `buffer` * and puts in at most `buflen` bytes of data from the file. See `read(2)` * for details on the work and parameters of this function. * * Files (and directories) are just inverted CTZ skip lists. The CTZ * methods provide a seamless interface to abstract away the data structure * to provide an interface which allows the caller to only focus on the * offset of the data from the start of the file, and not worry about the * underlying details, pointers, blocks/pages, etc. * * Input Parameters: * filep - File pointer. * buffer - Buffer where to put read data. * buflen - Length of the buffer. * * Returned Value: * 0 - OK * < 0 - Error * ****************************************************************************/ static ssize_t mnemofs_read(FAR struct file *filep, FAR char *buffer, size_t buflen) { int ret = 0; FAR struct inode *inode; FAR struct mfs_sb_s *sb; FAR struct mfs_ofd_s *f; finfo("Mnemofs read."); inode = filep->f_inode; DEBUGASSERT(inode != NULL); sb = inode->i_private; DEBUGASSERT(sb != NULL); ret = nxmutex_lock(&MFS_LOCK(sb)); if (ret < 0) { goto errout; } finfo("Lock acquired."); f = filep->f_priv; DEBUGASSERT(f != NULL); finfo("Mnemofs read %lu bytes from %u offset", buflen, f->com->off); /* Check if allowed to read. */ if ((f->com->oflags & O_RDONLY) == 0) { finfo("Not allowed to read."); ret = -EINVAL; goto errout_with_lock; } /* Read data in CTZ from the current offset. */ ret = mfs_lru_rdfromoff(sb, f->com->off, f->com->path, f->com->depth, buffer, buflen); if (ret < 0) { finfo("Error while reading. Ret: %d.", ret); goto errout_with_lock; } /* Update offset. */ f->com->off += buflen; errout_with_lock: nxmutex_unlock(&MFS_LOCK(sb)); errout: finfo("Mnemofs read exited with %d.", ret); return ret; } /**************************************************************************** * Name: mnemofs_write * * Description: * This writes to an open file at the current offset from the provided * `buffer` and puts in at most `buflen` bytes of data from the buffer. See * `write(2)` for details on the work and parameters of this function. * * Input Parameters: * filep - File pointer. * buffer - Buffer from where to write data. * buflen - Length of the buffer. * * Returned Value: * 0 - OK * < 0 - Error * ****************************************************************************/ static ssize_t mnemofs_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) { int ret = OK; FAR struct inode *inode; FAR struct mfs_sb_s *sb; FAR struct mfs_ofd_s *f; finfo("Mnemofs write."); inode = filep->f_inode; DEBUGASSERT(inode != NULL); sb = inode->i_private; DEBUGASSERT(sb != NULL); ret = nxmutex_lock(&MFS_LOCK(sb)); if (ret < 0) { goto errout; } finfo("Lock acquired."); f = filep->f_priv; DEBUGASSERT(f != NULL); finfo("Mnemofs write %lu bytes at offset %u.", buflen, f->com->off); /* Check if allowed to write. */ if ((f->com->oflags & O_WRONLY) == 0) { ret = -EINVAL; goto errout_with_lock; } /* Write data to CTZ at the current offset. */ ret = mfs_lru_wr(sb, f->com->off, buflen, f->com->sz, f->com->path, f->com->depth, buffer); if (ret < 0) { goto errout_with_lock; } /* Update offset and size. */ f->com->off += buflen; f->com->sz = MAX(f->com->sz, f->com->off); finfo("Offset updated to %u and size to %u", f->com->off, f->com->sz); errout_with_lock: nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); errout: finfo("Mnemofs write exited with %d.", ret); return ret; } /**************************************************************************** * Name: mnemofs_seek * * Description: * Repositions the current offset of the file pointed by the file * descriptor. See `lseek(2)` for details on the work and parameters of * this function. * * The new offset may be beyond the current file size. If that's the case, * then on any subsequent writes, it will be such that there are bytes with * '\0' in the gap/hole. In mnemofs, the whole hole situation is that the * LRU doesn't know about the existence of holes, but comitting a file from * LRU to the journal does write all the holes to the flash. * * Input Parameters: * filep - File pointer. * offset - Offset. * whence - From where the offset should be applied. * * Returned Value: * 0 - OK * < 0 - Error * ****************************************************************************/ static off_t mnemofs_seek(FAR struct file *filep, off_t offset, int whence) { int ret = OK; mfs_t pos; FAR struct inode *inode; FAR struct mfs_sb_s *sb; FAR struct mfs_ofd_s *f; finfo("Mnemofs seek."); inode = filep->f_inode; DEBUGASSERT(inode != NULL); sb = inode->i_private; DEBUGASSERT(sb != NULL); ret = nxmutex_lock(&MFS_LOCK(sb)); if (ret < 0) { goto errout; } finfo("Lock acquired."); f = filep->f_priv; DEBUGASSERT(f != NULL); finfo("Mnemofs seek from %u offset to %u using whence %d.", f->com->off, offset, whence); pos = f->com->off; switch (whence) { case SEEK_SET: pos = offset; break; case SEEK_CUR: pos += offset; break; case SEEK_END: pos = f->com->sz + offset; break; default: ret = -EINVAL; goto errout_with_lock; } /* Check bounds of the position data type. */ if ((pos < f->com->off && offset > 0) || (pos > f->com->off && offset < 0)) { finfo("Out of bounds seek."); ret = -EINVAL; goto errout_with_lock; } f->com->off = pos; finfo("Final position %u.", pos); errout_with_lock: nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); errout: return ret; } /**************************************************************************** * Name: mnemofs_ioctl * * Description: * Allows caller to directly control the underlying storage device. See * `ioctl(2)` for details on the work and parameters of this function. * * Input Parameters: * filep - File pointer. * cmd - Command. * arg - Arguments to the command. * * Returned Value: * 0 - OK * < 0 - Error * ****************************************************************************/ static int mnemofs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { int ret = OK; FAR struct inode *inode; FAR struct inode *drv; FAR struct mfs_sb_s *sb; finfo("Mnemofs ioctl with cmd %u and arg %ld", cmd, arg); inode = filep->f_inode; sb = inode->i_private; drv = sb->drv; ret = nxmutex_lock(&MFS_LOCK(sb)); if (ret < 0) { goto errout; } finfo("Lock acquired."); if (INODE_IS_MTD(drv)) { ret = MTD_IOCTL(drv->u.i_mtd, cmd, arg); } else { finfo("Not an MTD driver."); ret = -ENOTTY; } nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); errout: finfo("Mnemofs ioctl exited with %d.", ret); return ret; } /**************************************************************************** * Name: mnemofs_truncate * * Description: * Truncates file specified to specified length. See `truncate(2)` for * details on the work and parameters of this function. * * Input Parameters: * filep - File pointer. * length - Final size of the truncated file. * * Returned Value: * 0 - OK * < 0 - Error * * Assumptions/Limitations: * If the length specified is bigger than the file, it won't increase the * file size in itself. Only when there is a subsequent write to the offset * will there be an increase in the file size. This can be changed, but is * kept for efficiency. * ****************************************************************************/ static int mnemofs_truncate(FAR struct file *filep, off_t length) { int ret = OK; FAR struct inode *inode; FAR struct mfs_sb_s *sb; FAR struct mfs_ofd_s *f; finfo("Mnemofs truncate to length %d.", length); inode = filep->f_inode; DEBUGASSERT(inode != NULL); sb = inode->i_private; DEBUGASSERT(sb != NULL); ret = nxmutex_lock(&MFS_LOCK(sb)); if (ret < 0) { goto errout; } finfo("Lock acquired."); f = filep->f_priv; DEBUGASSERT(f != NULL); if (length < f->com->sz) { ret = mfs_lru_del(sb, length, f->com->sz - length, f->com->sz, f->com->path, f->com->depth); if (predict_false(ret < 0)) { finfo("Error during truncate. Ret: %d.", ret); goto errout_with_lock; } } errout_with_lock: nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); errout: finfo("Mnemofs truncate exited with %d.", ret); return ret; } /**************************************************************************** * Name: mnemofs_sync * * Description: * Causes all pending modifications that are in memory for a file * descriptor to be written to the device. See `sync(2)` for details on * the work and parameters of this function. * * For mnemofs, this means that the LRU entry for the file corresponding to * the fd is committed to the journal. * * Input Parameters: * filep - File pointer. * * Returned Value: * 0 - OK * < 0 - Error * ****************************************************************************/ static int mnemofs_sync(FAR struct file *filep) { int ret = OK; FAR struct inode *inode; FAR struct mfs_sb_s *sb; FAR struct mfs_ofd_s *f; finfo("Mnemofs sync."); inode = filep->f_inode; DEBUGASSERT(inode != NULL); sb = inode->i_private; DEBUGASSERT(sb != NULL); ret = nxmutex_lock(&MFS_LOCK(sb)); if (ret < 0) { goto errout; } finfo("Lock acquired."); f = filep->f_priv; DEBUGASSERT(f != NULL); ret = mfs_lru_ctzflush(sb, f->com->path, f->com->depth, f->com->sz); nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); errout: finfo("Mnemofs sync exited with %d.", ret); return ret; } /**************************************************************************** * Name: mnemofs_dup * * Description: * Creates a duplicate file descriptor. The new and old fds must be * interchangeable and they share file offsets and file status flags. * See `dup(2)` for details on the work and parameters of this function. * * The file descriptors have file pointers behind them. In mnemofs, * the file pointers have different upper halves. But duplicate file * pointers share a reference counted common part among themselves. * * Input Parameters: * oldp - Old file pointer. * newp - New file pointer. * * Returned Value: * 0 - OK * < 0 - Error * ****************************************************************************/ static int mnemofs_dup(FAR const struct file *oldp, FAR struct file *newp) { int ret = OK; FAR struct inode *inode; FAR struct mfs_sb_s *sb; FAR struct mfs_ofd_s *of; FAR struct mfs_ofd_s *nf; finfo("Mnemofs dup."); inode = oldp->f_inode; DEBUGASSERT(inode != NULL); sb = inode->i_private; DEBUGASSERT(sb != NULL); ret = nxmutex_lock(&MFS_LOCK(sb)); if (ret < 0) { goto errout; } finfo("Lock acquired."); of = oldp->f_priv; DEBUGASSERT(of != NULL); nf = kmm_zalloc(sizeof(*nf)); if (predict_false(nf == NULL)) { finfo("No memory left."); ret = -ENOMEM; goto errout_with_lock; } nf->com = of->com; /* Refcount limit. */ if (nf->com->refcount == UINT8_MAX) { finfo("Refcount limit reached."); ret = -EMFILE; goto errout_with_nf; } nf->com->refcount++; /* Add the new upper half to the list of open fds. */ list_add_tail(&sb->of, &nf->list); finfo("New file descriptor added to the end of the list of open files."); nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); return ret; errout_with_nf: kmm_free(nf); errout_with_lock: nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); errout: finfo("Mnemofs dup exited with %d.", ret); return ret; } /**************************************************************************** * Name: mnemofs_fstat * * Description: * Get file's status. See `fstat(2)` for details on the work and * parameters of this function. * * In mnemofs, most of the metadata of a file is stored in the directory * entry of the file inside the parent directories. The directory entries * are stored in the form of a CTZ, and this is what a directory refers to. * * Input Parameters: * filep - File pointer. * buf - Stat result. * * Returned Value: * 0 - OK * < 0 - Error * ****************************************************************************/ static int mnemofs_fstat(FAR const struct file *filep, FAR struct stat *buf) { int ret = OK; struct mfs_pitr_s pitr; FAR struct inode *inode; FAR struct mfs_sb_s *sb; FAR struct mfs_ofd_s *f; FAR struct mfs_dirent_s *dirent = NULL; finfo("Mnemofs fstat."); inode = filep->f_inode; DEBUGASSERT(inode != NULL); sb = inode->i_private; DEBUGASSERT(sb != NULL); ret = nxmutex_lock(&MFS_LOCK(sb)); if (ret < 0) { goto errout; } finfo("Lock acquired."); f = filep->f_priv; DEBUGASSERT(f != NULL); mfs_pitr_init(sb, f->com->path, f->com->depth, &pitr, true); mfs_pitr_adv_tochild(&pitr, f->com->path, f->com->depth); mfs_pitr_readdirent(sb, &pitr, &dirent); mfs_pitr_free(&pitr); DEBUGASSERT(dirent != NULL); buf->st_mode = dirent->mode; buf->st_nlink = 1; /* mnemofs doesn't yet support links. */ buf->st_size = dirent->sz; buf->st_atim = dirent->st_atim; buf->st_ctim = dirent->st_ctim; buf->st_blksize = sb->pg_sz; buf->st_blocks = ((dirent->sz + (sb->pg_sz - 1)) / sb->pg_sz); /* Ceil */ mfs_free_dirent(dirent); nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); errout: finfo("Mnemofs fstat exited with %d.", ret); return ret; } /**************************************************************************** * Name: mnemofs_opendir * * Description: * Opens a directory for traversal. See `opendir(3)` for details on the * work and parameters of this function. * * In mnemofs, the directories are in the form of a CTZ list like files, * but the content in the list is the directory entries of its children. * There is mfs_fsdirent->count which exists due to the fact that the first * entry in readdir is `.`, second is `..` and then the actual contents. To * save space, this count never goes above 2 because it's not needed as * above 2, the offset in the CTZ list is sufficient for traversal. * * Input Parameters: * mountpt - Mount point of the file system. * relpath - Relative path of the directory. * dir - To populate this with open directory data structure. * * Returned Value: * 0 - OK * < 0 - Error * * Assumptions/Limitations: * There is no attempt to `unlink` or `rmdir` when a directory is open. * This will mess up the iterator, and mnemofs does not yet store a list * of open directories to prevent unlink or rmdir when open or something * like that. This is yet to be tested if mnemofs can handle such a case. * This bug is marked in code in `readdir`. * ****************************************************************************/ static int mnemofs_opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s **dir) { int ret = OK; int flags; mfs_t depth; FAR struct mfs_sb_s *sb; FAR struct mfs_path_s *path; FAR struct mfs_pitr_s *pitr; FAR struct mfs_fsdirent *fsdirent; finfo("Mnemofs opendir for directory %s.", relpath); DEBUGASSERT(mountpt != NULL); sb = mountpt->i_private; DEBUGASSERT(mountpt->i_private); ret = nxmutex_lock(&MFS_LOCK(sb)); if (ret < 0) { goto errout; } finfo("Lock acquired."); flags = mfs_get_patharr(sb, relpath, &path, &depth); if ((flags & MFS_ISDIR) == 0) { ret = -ENOTDIR; goto errout_with_lock; } pitr = kmm_zalloc(sizeof(*pitr)); if (predict_false(pitr == NULL)) { ret = -ENOMEM; goto errout_with_lock; } fsdirent = kmm_zalloc(sizeof(*fsdirent)); if (predict_false(fsdirent == NULL)) { ret = -ENOMEM; goto errout_with_pitr; } mfs_pitr_init(sb, path, depth, pitr, false); fsdirent->idx = 0; fsdirent->path = path; fsdirent->depth = depth; fsdirent->pitr = pitr; *dir = (FAR struct fs_dirent_s *) fsdirent; finfo("Opened directory with index %u at depth %u.", fsdirent->idx, fsdirent->depth); nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); return ret; errout_with_pitr: kmm_free(pitr); errout_with_lock: nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); errout: finfo("Mnemofs opendir exited with %d.", ret); return ret; } /**************************************************************************** * Name: mnemofs_closedir * * Description: * Closes an opened directory. See `closedir(3)` for details on the * work and parameters of this function. * * Input Parameters: * mountpt - Mount point of the file system. * dir - Open directory data structure. * * Returned Value: * 0 - OK * ****************************************************************************/ static int mnemofs_closedir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir) { struct mfs_fsdirent *fsdirent = (struct mfs_fsdirent *) dir; finfo("Mnemofs closedir."); mfs_free_patharr(fsdirent->path); mfs_pitr_free(fsdirent->pitr); kmm_free(fsdirent->pitr); kmm_free(fsdirent); return OK; } /**************************************************************************** * Name: mnemofs_readdir * * Description: * Gives the directory entry of where the directory iterator currently * points to and advances the iterator to the next directory entry. See * `readdir(3)` for details on the work and parameters of this function. * * The first two entries are `.` and `..` respectively. The way mnemofs * keeps a track of the current directory entry is through offset in the * directory entry file. This offset is an unsigned integer, and to know * when to return the "dots" and when to return actual directory entries, * a counter is used that gives `.` when 0, `..` when 1, and the actual * directory entries when 2. The offset increases only when counter is 2. * The counter never increases above 2. * * Input Parameters: * mountpt - Mount point of the file system. * dir - Open directory data structure. * entry - To populate structure with directory entry. * * Returned Value: * 0 - OK * ****************************************************************************/ static int mnemofs_readdir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir, FAR struct dirent *entry) { int ret = OK; FAR struct mfs_sb_s *sb; FAR struct mfs_dirent_s *dirent; FAR struct mfs_fsdirent *fsdirent = (FAR struct mfs_fsdirent *) dir; DEBUGASSERT(mountpt != NULL); sb = mountpt->i_private; DEBUGASSERT(sb != NULL); finfo("Mnemofs readdir with dirent idx %u.", fsdirent->idx); ret = nxmutex_lock(&MFS_LOCK(sb)); if (ret < 0) { goto errout; } finfo("Lock acquired."); if (fsdirent->idx == 0) { /* . */ snprintf(entry->d_name, NAME_MAX + 1, "."); entry->d_type = DTYPE_DIRECTORY; fsdirent->idx++; goto errout_with_lock; } else if (fsdirent->idx == 1) { /* .. */ snprintf(entry->d_name, NAME_MAX + 1, ".."); entry->d_type = DTYPE_DIRECTORY; fsdirent->idx++; goto errout_with_lock; } /* Regular direntries from here. */ /* Here lies the `unlink` and `rmdir` bug, as sync can only update the * current CTZ. */ /* mfs_pitr_sync(sb, fsdirent->pitr, fsdirent->path, fsdirent->depth); */ mfs_pitr_readdirent(sb, fsdirent->pitr, &dirent); if (predict_false(ret < 0)) { goto errout_with_lock; } else if (dirent == NULL) { /* End of Directory. */ finfo("End of directory."); ret = -ENOENT; goto errout_with_lock; } memset(entry->d_name, 0, NAME_MAX + 1); memcpy(entry->d_name, dirent->name, NAME_MAX); entry->d_type = (S_ISDIR(dirent->mode) ? DTYPE_DIRECTORY: DTYPE_FILE); mfs_pitr_adv(sb, fsdirent->pitr); mfs_free_dirent(dirent); errout_with_lock: nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); errout: finfo("Mnemofs readdir exited with %d.", ret); return ret; } /**************************************************************************** * Name: mnemofs_rewinddir * * Description: * Rewinds the directory iterator to the start of the directory. See * `rewinddir(3)` for details on the work and parameters of this function. * * In mnemofs, this means resetting offset of CTZ list of directory to 0 and * counter to 0; * * Input Parameters: * mountpt - Mount point of the file system. * dir - Open directory data structure. * * Returned Value: * 0 - OK * ****************************************************************************/ static int mnemofs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir) { int ret = OK; FAR struct mfs_sb_s *sb; struct mfs_fsdirent *fsdirent = (struct mfs_fsdirent *) dir; finfo("Rewind dir."); DEBUGASSERT(mountpt != NULL); sb = mountpt->i_private; DEBUGASSERT(sb != NULL); ret = nxmutex_lock(&MFS_LOCK(sb)); if (ret < 0) { goto errout; } finfo("Lock acquired."); mfs_pitr_reset(fsdirent->pitr); fsdirent->idx = 0; nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); errout: finfo("Mnemofs rewinddir exited with %d.", ret); return ret; } /**************************************************************************** * Name: mnemofs_bind * * Description: * Mounts the file system. This is responsible for initializing all the * in-memory data structures, which may including scanning the storage * for serialized information, or formatting the storage, depending on the * options provided during mount. * * See `mount(2)` and `mount(8)` for more information. * * In mnemofs, the superblock is not stored on disk. It does not contain * any information about the current state of the device, but rather just * the information about the storage device, which is obtained from the * driver anyway. To know if the device is formatted, the entire device * is scanned, block by block, for the existence of the first block of the * journal. This is quicker than searching page by page, as there are much * fewer blocks than pages. The first 8 bytes of the first journal block * are a special sequence. The journal contains the location of the master * node (and vice versa, but it's easier to find/use journal and then * master node). * * Input Parameters: * driver - MTD driver * data - Mount options * handle - To be updated with file system information. * * Returned Value: * 0 - OK * < 0 - Error * ****************************************************************************/ static int mnemofs_bind(FAR struct inode *driver, FAR const void *data, FAR void** handle) { int ret = OK; bool format = false; FAR char buf[8]; mfs_t i = 0; FAR struct mfs_sb_s *sb = NULL; struct mtd_geometry_s geo; finfo("Mnemofs bind."); memset(buf, 0, 8); sb = kmm_zalloc(sizeof(*sb)); if (!sb) { ret = -ENOMEM; goto errout; } /* Currently only supports NAND flashes (MTD devices). */ if (driver && INODE_IS_MTD(driver)) { if (!driver || !driver->u.i_mtd || !driver->u.i_mtd->ioctl) { ret = -ENODEV; finfo("MTD driver not supported.\n"); goto errout_with_sb; } ret = MTD_IOCTL(driver->u.i_mtd, MTDIOC_GEOMETRY, (unsigned long) &geo); finfo("MTD Driver Geometry read. " "Page size: %d, Block size: %d, Pages/Block: %d, Blocks: %d\n", geo.blocksize, geo.erasesize, geo.erasesize / geo.blocksize, geo.neraseblocks); } else { finfo("Not an MTD device.\n"); ret = -ENODEV; goto errout_with_sb; } nxmutex_init(&MFS_LOCK(sb)); ret = nxmutex_lock(&MFS_LOCK(sb)); if (ret < 0) { goto errout_with_sb; } finfo("Lock acquired."); sb->drv = driver; sb->pg_sz = geo.blocksize; sb->blk_sz = geo.erasesize; sb->n_blks = geo.neraseblocks; sb->pg_in_blk = MFS_BLKSZ(sb) / sb->pg_sz; #ifdef CONFIG_MNEMOFS_JOURNAL_NBLKS sb->j_nblks = CONFIG_MNEMOFS_JOURNAL_NBLKS; #else sb->j_nblks = MIN(5, MFS_NBLKS(sb) / 2); #endif sb->log_blk_sz = log2(MFS_BLKSZ(sb)); sb->log_pg_sz = log2(sb->pg_sz); sb->log_pg_in_blk = log2(sb->pg_in_blk); sb->log_n_blks = log2(MFS_NBLKS(sb)); sb->rw_buf = kmm_zalloc(MFS_PGSZ(sb)); if (predict_false(sb->rw_buf == NULL)) { goto errout_with_lock; } /* TODO: Print the super block in Block 0. */ srand(time(NULL)); mfs_ba_init(sb); mfs_lru_init(sb); if (!strncmp(data, "autoformat", 11)) { /* Format if not formatted already. */ finfo("Auto format.\n"); /* Look for journal and maybe hopefully, the master node * if it comes first. */ for (i = 0; i < MFS_NBLKS(sb); i++) { mfs_read_page(sb, buf, 8, MFS_BLK2PG(sb, i), 0); if (!strncmp(buf, MFS_JRNL_MAGIC, 8)) { /* Found journal first block. */ mfs_jrnl_init(sb, i); } } if (predict_false(sb->mn.pg == 0)) { format = true; finfo("Device needs to formatted.\n"); } else { finfo("Device already formatted.\n"); } } if (format || !strncmp(data, "forceformat", 12)) { /* Format. */ if (!format) { finfo("Force format.\n"); } ret = mfs_jrnl_fmt(sb, 0, 0); if (ret != OK) { goto errout_with_sb; } finfo("Device formatted.\n"); } *handle = (FAR void *)sb; finfo("Successfully mounted mnemofs! Super Block %p\n", sb); nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); return ret; errout_with_lock: nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); errout_with_sb: kmm_free(sb); errout: finfo("Mnemofs bind exited with %d.", ret); return ret; } /**************************************************************************** * Name: mnemofs_unbind * * Description: * Unmounts the file system. * * See `umount(2)` and `umount(8)` for more information. * * Input Parameters: * handle - File system information. * driver - To be populated with the MTD driver * flags - Flags for unmounting. * * Returned Value: * 0 - OK * ****************************************************************************/ static int mnemofs_unbind(FAR void *handle, FAR struct inode **driver, unsigned int flags) { FAR struct mfs_sb_s *sb; finfo("Mnemofs unbind."); DEBUGASSERT(handle); sb = handle; *driver = sb->drv; mfs_jrnl_free(sb); mfs_ba_free(sb); kmm_free(sb); finfo("Successfully unmounted mnemofs!"); return OK; } /**************************************************************************** * Name: mnemofs_statfs * * Description: * Get file system statistics. See `statfs(2)` for more information. * * Input Parameters: * mountpt - Mount point of the file system. * buf - To populate with file system statistics. * * Returned Value: * 0 - OK * < 0 - Error * ****************************************************************************/ static int mnemofs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf) { int ret = OK; FAR struct mfs_sb_s *sb; finfo("Mnemofs statfs."); DEBUGASSERT(mountpt != NULL); sb = mountpt->i_private; DEBUGASSERT(sb != NULL); ret = nxmutex_lock(&MFS_LOCK(sb)); if (ret < 0) { goto errout; } finfo("Lock acquired."); buf->f_type = MNEMOFS_SUPER_MAGIC; buf->f_bsize = sb->pg_sz; buf->f_blocks = MFS_NBLKS(sb) * sb->pg_in_blk; buf->f_bavail = mfs_ba_getavailpgs(sb); buf->f_namelen = UINT8_MAX; nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); errout: finfo("Mnemofs statfs exited with %d.", ret); return ret; } /**************************************************************************** * Name: mnemofs_unlink * * Description: * Remove a file's entry from the file system. This might delete the file * as well. See `unlink(2)` for more information. * * In mnemofs, the directory entry of the file is removed from its parent's * directory file (CTZ list). The pages used by the file are marked for * delete to the block allocator. When the master node is moved, and if the * blocks of which the pages are part of is ready for erase, then the * block allocator erases those blocks. * * Input Parameters: * mountpt - Mount point of the file system. * buf - To populate with file system statistics. * * Returned Value: * 0 - OK * < 0 - Error * ****************************************************************************/ static int mnemofs_unlink(FAR struct inode *mountpt, FAR const char *relpath) { int ret = OK; int ret_flags; mfs_t depth; FAR struct mfs_sb_s *sb; FAR struct mfs_path_s *path; finfo("Mnemofs unlink at path \"%s\".", relpath); DEBUGASSERT(mountpt != NULL); sb = mountpt->i_private; DEBUGASSERT(sb != NULL); ret = nxmutex_lock(&MFS_LOCK(sb)); if (ret < 0) { goto errout; } finfo("Lock acquired."); ret_flags = mfs_get_patharr(sb, relpath, &path, &depth); if ((ret_flags & MFS_ISFILE) == 0) { ret = -EISDIR; goto errout_with_lock; } mfs_pitr_rm(sb, path, depth); mfs_free_patharr(path); errout_with_lock: nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); errout: finfo("Mnemofs unlink exited with %d.", ret); return ret; } /**************************************************************************** * Name: mnemofs_mkdir * * Description: * Create a directory at given relpath. See `mkdir(2)` for more * information. * * In mnemofs, the directory entry of the file is appended to its parent's * directory file (CTZ list). * * Input Parameters: * mountpt - Mount point of the file system. * relpath - Relative path of the new directory. * mode - Mode of the new directory (ACL). * * Returned Value: * 0 - OK * < 0 - Error * ****************************************************************************/ static int mnemofs_mkdir(FAR struct inode *mountpt, FAR const char *relpath, mode_t mode) { int ret = OK; int flags; mfs_t depth; FAR const char *child; struct mfs_pitr_s pitr; FAR struct mfs_sb_s *sb; FAR struct mfs_path_s *path; finfo("Mnemofs mkdir at %s.", relpath); mode |= S_IFDIR; DEBUGASSERT(mountpt != NULL); sb = mountpt->i_private; DEBUGASSERT(sb != NULL); ret = nxmutex_lock(&MFS_LOCK(sb)); if (ret < 0) { goto errout; } finfo("Lock acquired."); flags = mfs_get_patharr(sb, relpath, &path, &depth); if ((flags & MFS_NEXIST) == 0) { if ((flags & MFS_P_EXIST) != 0) { if ((flags & MFS_P_ISDIR) != 0) { /* OK */ finfo("OK"); } else { ret = -ENOTDIR; goto errout_with_lock; } } else { ret = -ENOENT; goto errout_with_lock; } } else { ret = -EEXIST; goto errout_with_lock; } mfs_pitr_init(sb, path, depth, &pitr, true); child = relpath; finfo("Mode %x", mode); ret = mfs_pitr_appendnew(sb, path, depth, &pitr, child, mode); if (predict_false(ret < 0)) { goto errout_with_pitr; } mfs_pitr_free(&pitr); finfo("Directory created at %s", relpath); mfs_free_patharr(path); nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); finfo("Mnemofs mkdir exited with ret %d.", ret); return ret; errout_with_pitr: mfs_pitr_free(&pitr); errout_with_lock: nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); errout: finfo("Mnemofs mkdir exited with ret %d.", ret); return ret; } /**************************************************************************** * Name: mnemofs_rmdir * * Description: * Removes a directory from given relpath. See `rmdir(2)` for more * information. * * Input Parameters: * mountpt - Mount point of the file system. * relpath - Relative path of the new directory. * mode - Mode of the new directory (ACL). * * Returned Value: * 0 - OK * < 0 - Error * ****************************************************************************/ static int mnemofs_rmdir(FAR struct inode *mountpt, FAR const char *relpath) { int ret = OK; int flags; mfs_t depth; struct mfs_pitr_s pitr; FAR struct mfs_sb_s *sb; FAR struct mfs_path_s *path; finfo("Mnemofs rmdir for path \"%s\".", relpath); DEBUGASSERT(mountpt != NULL); sb = mountpt->i_private; DEBUGASSERT(sb != NULL); ret = nxmutex_lock(&MFS_LOCK(sb)); if (ret < 0) { goto errout; } finfo("Lock acquired."); flags = mfs_get_patharr(sb, relpath, &path, &depth); if ((flags & MFS_ISDIR) == 0) { ret = -EISDIR; goto errout_with_lock; } mfs_pitr_init(sb, path, depth, &pitr, true); mfs_pitr_adv_tochild(&pitr, path, depth); if (!mfs_obj_isempty(sb, &pitr)) { ret = -ENOTEMPTY; goto errout_with_pitr; } mfs_pitr_free(&pitr); mfs_pitr_rm(sb, path, depth); errout_with_pitr: mfs_free_patharr(path); errout_with_lock: nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); errout: finfo("Mnemofs rmdir exited with ret %d.", ret); return ret; } /**************************************************************************** * Name: mnemofs_rename * * Description: * Moves a file or directory between paths. See `rename(2)` for more * information. * * In mnemofs, this involves erasing a direntry from old path, and adding * it to the new path. There are some further complications regarding * directories and files, as mentioned in `rename(2)`. * * Input Parameters: * mountpt - Mount point of the file system. * relpath - Relative path of the new directory. * mode - Mode of the new directory (ACL). * * Returned Value: * 0 - OK * < 0 - Error * ****************************************************************************/ static int mnemofs_rename(FAR struct inode *mountpt, FAR const char *oldrelpath, FAR const char *newrelpath) { int ret = OK; int oflags; int nflags; bool nexists; bool odir = false; bool ndir = false; mfs_t odepth; mfs_t ndepth; struct mfs_pitr_s opitr; struct mfs_pitr_s npitr; FAR struct mfs_sb_s *sb; FAR struct mfs_path_s *opath; FAR struct mfs_path_s *npath; FAR struct mfs_dirent_s *odirent = NULL; finfo("Mnemofs rename \"%s\" to \"%s\".", oldrelpath, newrelpath); DEBUGASSERT(mountpt != NULL); sb = mountpt->i_private; DEBUGASSERT(sb != NULL); ret = nxmutex_lock(&MFS_LOCK(sb)); if (ret < 0) { goto errout; } finfo("Lock acquired."); oflags = mfs_get_patharr(sb, oldrelpath, &opath, &odepth); if ((oflags & MFS_NEXIST) != 0) { ret = -ENOENT; goto errout_with_opath; } nflags = mfs_get_patharr(sb, newrelpath, &npath, &ndepth); if ((nflags & MFS_P_EXIST) == 0) { ret = -ENONET; goto errout_with_npath; } odir = ((oflags & MFS_ISDIR) != 0); ndir = ((nflags & MFS_ISDIR) != 0); nexists = ((nflags & MFS_NEXIST) == 0); if (nexists && odir && !ndir) { ret = -ENOTDIR; goto errout_with_npath; } if (nexists && !odir && ndir) { ret = -EISDIR; goto errout_with_npath; } mfs_pitr_init(sb, npath, ndepth, &npitr, nexists); mfs_pitr_init(sb, opath, odepth, &opitr, true); /* If new path already exists, remove the direntry. If it's a non-empty * directory, then raise error. */ if (nexists) { mfs_pitr_adv_tochild(&opitr, opath, odepth); mfs_pitr_adv_tochild(&npitr, npath, ndepth); mfs_pitr_readdirent(sb, &opitr, &odirent); if (ndir && !mfs_obj_isempty(sb, &npitr)) { ret = -ENOTEMPTY; goto errout_with_pitr; } mfs_pitr_reset(&npitr); mfs_pitr_rm(sb, npath, ndepth); } mfs_pitr_adv_tochild(&npitr, npath, ndepth); mfs_pitr_appenddirent(sb, npath, ndepth, &npitr, odirent); mfs_pitr_rmdirent(sb, opath, odepth, &opitr, odirent); errout_with_pitr: mfs_free_dirent(odirent); mfs_pitr_free(&opitr); mfs_pitr_free(&npitr); errout_with_npath: mfs_free_patharr(npath); errout_with_opath: mfs_free_patharr(opath); nxmutex_unlock(&MFS_LOCK(sb)); finfo("Lock released."); errout: finfo("Mnemofs rename exited with ret %d.", ret); return ret; } /**************************************************************************** * Name: mnemofs_stat * * Description: * Get stats of a file. See `stat(2)` for more information. * * In mnemofs, most of the relevant information is stored in the dirent * of the file in its parent's directory file. The stats of the root are * available in the master node. * * Input Parameters: * mountpt - Mount point of the file system. * relpath - Relative path of the new directory. * buf - File stats to populate. * * Returned Value: * 0 - OK * < 0 - Error * ****************************************************************************/ static int mnemofs_stat(FAR struct inode *mountpt, FAR const char *relpath, FAR struct stat *buf) { int ret = OK; int ret_flags; mfs_t depth; struct mfs_pitr_s pitr; FAR struct mfs_sb_s *sb; FAR struct mfs_path_s *path; FAR struct mfs_dirent_s *dirent = NULL; finfo("Mnemofs stat for path %s.", relpath); DEBUGASSERT(mountpt != NULL); sb = mountpt->i_private; DEBUGASSERT(sb != NULL); ret = nxmutex_lock(&MFS_LOCK(sb)); if (ret < 0) { goto errout; } finfo("Lock acquired."); ret_flags = mfs_get_patharr(sb, relpath, &path, &depth); if (ret_flags & MFS_NEXIST) { ret = -ENOENT; goto errout_with_lock; } finfo("Got path array. Depth %u for path \"%s\"", depth, relpath); mfs_pitr_init(sb, path, depth, &pitr, true); mfs_pitr_adv_tochild(&pitr, path, depth); ret = mfs_pitr_readdirent(sb, &pitr, &dirent); if (predict_false(ret < 0)) { goto errout_with_lock; } else if (dirent == NULL) { ret = -ENOENT; goto errout_with_lock; } finfo("Read stats."); buf->st_nlink = 1; buf->st_blksize = sb->pg_sz; buf->st_size = dirent->sz; buf->st_mode = dirent->mode; buf->st_atim = dirent->st_atim; buf->st_ctim = dirent->st_ctim; buf->st_mtim = dirent->st_mtim; buf->st_blocks = dirent->ctz.idx_e + 1; mfs_free_dirent(dirent); mfs_pitr_free(&pitr); mfs_free_patharr(path); errout_with_lock: finfo("Lock released."); nxmutex_unlock(&MFS_LOCK(sb)); errout: finfo("ret %d", ret); return ret; }