nuttx/fs/mnemofs/mnemofs.c
Saurav Pal 0be6dfb552 fs/mnemofs: Refactor path logic, direntry size bug fix, open free bug fix
Refactoring path logic to prevent logic flaws, direntry size bug fix to allow proper direntry traversal, open free bug fix to prevent memory leak after close.

Signed-off-by: Saurav Pal <resyfer.dev@gmail.com>
2024-08-09 09:00:17 +02:00

2190 lines
58 KiB
C

/****************************************************************************
* 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 <fcntl.h>
#include <math.h>
#include <nuttx/fs/fs.h>
#include <nuttx/kmalloc.h>
#include <stdio.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#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;
FAR const char *child = NULL;
FAR struct inode *inode;
struct mfs_pitr_s pitr;
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_EXIST) == 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);
finfo("Child is: %s.", child);
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, f->com->path, &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 processing done.");
/* 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->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("[TMP1] %p %p", &sb->of, &f->list);
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);
if (predict_false(ret < 0))
{
finfo("Error while flushing file. Ret: %d.", ret);
goto errout_with_lock;
}
f->com->refcount--;
if (f->com->refcount == 0)
{
kmm_free(f->com->path);
kmm_free(f->com);
finfo("Refcount is 0, open file structure freed.");
}
list_delete(&f->list);
kmm_free(f);
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->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->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);
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);
mfs_pitr_readdirent(sb, f->com->path, &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;
}
ret = mfs_lru_updatedinfo(sb, path, depth);
if (predict_false(ret < 0))
{
goto errout_with_path;
}
pitr = kmm_zalloc(sizeof(*pitr));
if (predict_false(pitr == NULL))
{
ret = -ENOMEM;
goto errout_with_path;
}
fsdirent = kmm_zalloc(sizeof(*fsdirent));
if (predict_false(fsdirent == NULL))
{
ret = -ENOMEM;
goto errout_with_pitr;
}
ret = mfs_pitr_init(sb, path, depth, pitr, false);
if (predict_false(ret < 0))
{
finfo("Failed PITR initialization.");
goto errout_with_fsdirent;
}
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_fsdirent:
kmm_free(fsdirent);
errout_with_pitr:
kmm_free(pitr);
errout_with_path:
mfs_free_patharr(path);
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. */
/* TODO: Need to think why *exactly* below line is needed. The LRU node
* seems to contain wrong size during opendir, but updating it here
* updates it to correct size, even though this updatedinfo is also
* called in opendir?
*/
ret = mfs_lru_updatedinfo(sb, fsdirent->path, fsdirent->depth);
if (predict_false(ret < 0))
{
goto errout_with_lock;
}
ret = mfs_pitr_readdirent(sb, fsdirent->path, 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, dirent->namelen);
entry->d_type = (S_ISDIR(dirent->mode) ? DTYPE_DIRECTORY: DTYPE_FILE);
finfo("Size of direntry %u, current off %u.", MFS_DIRENTSZ(dirent),
fsdirent->pitr->c_off);
mfs_pitr_adv_bydirent(fsdirent->pitr, dirent);
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));
list_initialize(&sb->of);
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;
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_EXIST) != 0)
{
finfo("File exists.");
ret = -EEXIST;
goto errout_with_path;
}
else
{
if ((flags & MFS_P_EXIST) != 0)
{
if ((flags & MFS_P_ISDIR) != 0)
{
/* OK */
finfo("OK");
}
else
{
ret = -ENOTDIR;
goto errout_with_path;
}
}
else
{
ret = -ENOENT;
goto errout_with_path;
}
}
memset(&path[depth - 1], 0, sizeof(struct mfs_path_s));
mfs_pitr_init(sb, path, depth, &pitr, true);
/* The last incomplete direntry will be added by mfs_pitr_appendnew. */
ret = mfs_pitr_appendnew(sb, path, depth, &pitr, relpath, mode);
if (predict_false(ret < 0))
{
goto errout_with_path;
}
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_path:
mfs_free_patharr(path);
mfs_pitr_free(&pitr);
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);
if (!mfs_obj_isempty(sb, path, &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_EXIST) == 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_EXIST) != 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);
mfs_pitr_adv_tochild(&npitr, npath);
mfs_pitr_readdirent(sb, opath, &opitr, &odirent);
if (ndir && !mfs_obj_isempty(sb, npath, &npitr))
{
ret = -ENOTEMPTY;
goto errout_with_pitr;
}
mfs_pitr_reset(&npitr);
mfs_pitr_rm(sb, npath, ndepth);
}
mfs_pitr_adv_tochild(&npitr, npath);
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_EXIST) == 0)
{
ret = -ENOENT;
goto errout_with_path;
}
finfo("Got path array. Depth %u for path \"%s\". Return flags %u.", depth,
relpath, ret_flags);
ret = mfs_lru_updatedinfo(sb, path, depth);
if (predict_false(ret < 0))
{
goto errout_with_path;
}
mfs_pitr_init(sb, path, depth, &pitr, true);
mfs_pitr_adv_tochild(&pitr, path);
ret = mfs_pitr_readdirent(sb, path, &pitr, &dirent);
if (predict_false(ret < 0))
{
goto errout_with_path;
}
else if (dirent == NULL)
{
ret = -ENOENT;
goto errout_with_path;
}
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);
errout_with_path:
mfs_free_patharr(path);
finfo("Lock released.");
nxmutex_unlock(&MFS_LOCK(sb));
errout:
finfo("Ret %d", ret);
return ret;
}