From 23b7dc0651dd5cdf1f6e42cb5ef157a1d8462ae7 Mon Sep 17 00:00:00 2001 From: Saurav Pal Date: Tue, 9 Jul 2024 18:57:16 +0000 Subject: [PATCH] fs/mnemofs: Setup and VFS methods The Setup and VFS methods for mnemofs, a NAND Flash File System. Signed-off-by: Saurav Pal --- Documentation/components/filesystem/index.rst | 1 + .../components/filesystem/mnemofs.rst | 118 + fs/Kconfig | 1 + fs/Makefile | 1 + fs/mnemofs/CMakeLists.txt | 53 + fs/mnemofs/Kconfig | 45 + fs/mnemofs/Make.defs | 66 + fs/mnemofs/mnemofs.c | 2148 +++++++++++++++++ fs/mnemofs/mnemofs.h | 1106 +++++++++ fs/mnemofs/mnemofs_blkalloc.c | 128 + fs/mnemofs/mnemofs_fsobj.c | 250 ++ fs/mnemofs/mnemofs_journal.c | 131 + fs/mnemofs/mnemofs_lru.c | 152 ++ fs/mnemofs/mnemofs_rw.c | 91 + fs/mount/fs_mount.c | 10 +- include/sys/statfs.h | 1 + 16 files changed, 4300 insertions(+), 2 deletions(-) create mode 100644 Documentation/components/filesystem/mnemofs.rst create mode 100644 fs/mnemofs/CMakeLists.txt create mode 100644 fs/mnemofs/Kconfig create mode 100644 fs/mnemofs/Make.defs create mode 100644 fs/mnemofs/mnemofs.c create mode 100644 fs/mnemofs/mnemofs.h create mode 100644 fs/mnemofs/mnemofs_blkalloc.c create mode 100644 fs/mnemofs/mnemofs_fsobj.c create mode 100644 fs/mnemofs/mnemofs_journal.c create mode 100644 fs/mnemofs/mnemofs_lru.c create mode 100644 fs/mnemofs/mnemofs_rw.c diff --git a/Documentation/components/filesystem/index.rst b/Documentation/components/filesystem/index.rst index 943e3f54e9..a2495cfcba 100644 --- a/Documentation/components/filesystem/index.rst +++ b/Documentation/components/filesystem/index.rst @@ -538,6 +538,7 @@ NuttX provides support for a variety of file systems out of the box. hostfs.rst littlefs.rst mmap.rst + mnemofs.rst nfs.rst nxffs.rst partition.rst diff --git a/Documentation/components/filesystem/mnemofs.rst b/Documentation/components/filesystem/mnemofs.rst new file mode 100644 index 0000000000..1994ec8f89 --- /dev/null +++ b/Documentation/components/filesystem/mnemofs.rst @@ -0,0 +1,118 @@ +======= +MNEMOFS +======= + +Mnemofs is a NAND Flash File System built for NuttX. + +Usage +===== + +If there's a NAND flash available at a location, for example, ``/dev/nand``, +you can mount it with ``mnemofs`` to a location like ``/mydir`` using:: + + mount -t mnemofs /dev/nand /mydir + +The above command will only work if the device was already formatted using +mnemofs. For a brand new device, or if you want to switch from an existing +file system, this won't work, and would need a format. + +Instead try this:: + + mount -t mnemofs -o forceformat /dev/nand /mydir + +Unsure of whether you need to do a format? This will help:: + + mount -t mnemofs -o autoformat /dev/nand /mydir + +This will format the device only if it can not detect mnemofs being already +formatted onto it. Do note this includes cases where mnemofs is formatted to +the device, but it's been mutilated to the point of being unrecognizable. + +After this, use it like a regular file system. That's the job of a file +system after all...to hide the storage device's pecularities behind an +abstraction. A file system is considered good if you don't have to think +about its existence during regular usage. + +Design +====== + +mnemofs is designed to be a middle ground between flash storage consumption, +memory consumption, wear and speed. It sacrifices a little bit of everything, +and ends up being acceptably good in all of them, instead of sacrificing +multiple aspects, and being good in one. + +mnemofs consists of several components, however, a walkthrough of the process +where a change requested by a user ends up being written to the NAND flash +would serve well for an introduction. The details will be explained further +below. + +The user requests some changes, say, add ``x`` bytes to ``y`` offset in a file. +This change is copied into the LRU cache of mnemofs. This LRU cache exists +in-memory, and serves as a tool for wear reduction. + +This LRU cache is a kernel list of nodes. Each node represents a file or a +directory. When the LRU is full, the last node is popped from this list and +the changes it contains, which is an accumulation of changes requested by +the user for that particular file or directory since the node was added to +the LRU cache, is written to the flash. + +Each file or directory is represented by a `CTZ skip list `_, +and the only attributes required to access the list is the index of the last +CTZ skip list block, the page number of that CTZ skip list block, and the +size of the file. In mnemofs, CTZ skip list blocks take up exactly one page +on the flash. + +Mnemofs works in a Copy-On-Write manner, similar to littlefs. When a CTZ +skip list is updated, the new location is added to the Journal of mnemofs +as a log. This log contains some information about the location of the new +CTZ list, the path it belongs to, etc. and then the updated location is +added as an update to its parent's CTZ skip list, and it undergoes the same +process. This log is appended with a checksum of the entire log, which +gives an assurance that the saved log was indeed saved completely before a +power loos. + +The journal is a modified singly linked list of blocks on the flash that +contains logs of changes in the file system. The last two blocks of the +journal is reserved for master blocks, hence the number of blocks in the +journal will be referred to as ``n + 2`` blocks. + +The area on storage other than the journal contains a certain "base" state of +the file system. All changes to the base state since is written to the +journal. The first block of the journal starts with an 8 byte magic sequence +to identify the start of the journal (on mount), followed by the number of +blocks in the journal and then finally an array of all the ``n + 2`` block +numbers that are part of the journal. After this part, the entire area in the +``n`` blocks contain logs and their checksums. + +The last two blocks of a journal are called the master blocks, and they store +multiple instances of the master node. They are duplicates of each other, and +each instance of the master node takes one page each, and are written to +these master blocks in a sequential manner. The master node points to the +root. + +When the first ``n`` blocks of the journal are full, then they are flushed +and since the root updates here as well, a new master node is written. Once +the new master node is written, the file system's base state is updated and +thus the old obsolete pages can be erased (if possible). The first ``n`` +blocks of the journal move more than the master nodes. + +The block allocator of mnemofs is havily inspired from littlefs. It starts +from a random block, and starts allocating pages or blocks sequentially in a +circular manner. It skips pages upon block requirement, but since block +requirements are only required by internal structures, they are always +requested in bulk, and minimize wastage. However, unlike in littlefs, mnemofs +keeps a bitmap in memory about the pages that are currently being used, as +well as the count of pages inside each block that want to be erased. + +In mnemofs, the bind might take a lot of time in the worst possible +theoretical case, as it's an ``O(n)`` mounting process, however, it's not the +case in real life. Mnemofs only needs to scan the first page of every block +in the device to look for the start of the journal. Leaving the actual +location of the page aside, this will be pretty fast in real life as the +larger the storage capacity is, the larger are the pages and the larger are +the number of pages per block, and thus the number of blocks in the device +do not increase at a rate similar to the increase in storage capacity of the +device. Further, the journal has the journal array, which contains block +numbers of each block in it, very close to the start of the array, and +mnemofs can quickly jump from there to the latest master node, and scan +the file system for used pages. \ No newline at end of file diff --git a/fs/Kconfig b/fs/Kconfig index 6115ed19c7..17f5f52749 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -134,3 +134,4 @@ source "fs/userfs/Kconfig" source "fs/hostfs/Kconfig" source "fs/rpmsgfs/Kconfig" source "fs/zipfs/Kconfig" +source "fs/mnemofs/Kconfig" \ No newline at end of file diff --git a/fs/Makefile b/fs/Makefile index 2f62d7bea8..db747317bd 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -57,6 +57,7 @@ include hostfs/Make.defs include littlefs/Make.defs include rpmsgfs/Make.defs include zipfs/Make.defs +include mnemofs/Make.defs endif diff --git a/fs/mnemofs/CMakeLists.txt b/fs/mnemofs/CMakeLists.txt new file mode 100644 index 0000000000..c360c3eb9c --- /dev/null +++ b/fs/mnemofs/CMakeLists.txt @@ -0,0 +1,53 @@ +# ############################################################################## +# fs/mnemofs/CMakeLists.txt +# +# 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. +# +# ############################################################################## + +if(CONFIG_FS_MNEMOFS) + target_sources( + fs PRIVATE mnemofs_blkalloc.c mnemofs_fsobj.c mnemofs_journal.c + mnemofs_lru.c mnemofs_rw.c mnemofs.c) +endif() diff --git a/fs/mnemofs/Kconfig b/fs/mnemofs/Kconfig new file mode 100644 index 0000000000..68416b9938 --- /dev/null +++ b/fs/mnemofs/Kconfig @@ -0,0 +1,45 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config FS_MNEMOFS + bool "MNEMOFS NAND Flash File System" + default n + depends on !DISABLE_MOUNTPOINT && MTD_NAND + ---help--- + Build the mnemofs NAND flash file system. + +if FS_MNEMOFS +config MNEMOFS_JOURNAL_NBLKS + int "MNEMOFS Journal Block Count" + default 20 + range 4 65536 + depends on FS_MNEMOFS + ---help--- + Number of blocks that mnemofs will use for the journal. Specifying + this will only work on formatting a NAND flash using mnemofs. If the + device is already formatted, the on-flash journal block count will + be considered instead. Two additional blocks will be allocated for + the master blocks. + +config MNEMOFS_NLRU + int "MNEMOFS LRU Node Count" + default 20 + range 1 255 + depends on FS_MNEMOFS + ---help--- + Number of nodes used by mnemofs for LRU. The higher the value is, + the lesser would be the wear on device with higher RAM + consumption. + +config MNEMOFS_NLRUDELTA + int "MNEMOFS LRU Delta Count" + default 20 + range 1 255 + depends on FS_MNEMOFS + ---help--- + Number of deltas used by mnemofs for LRU for every node. The higher + the value is, the lesser would be the wear on device with higher RAM + consumption. +endif # FS_MNEMOFS diff --git a/fs/mnemofs/Make.defs b/fs/mnemofs/Make.defs new file mode 100644 index 0000000000..4b6d8fbc79 --- /dev/null +++ b/fs/mnemofs/Make.defs @@ -0,0 +1,66 @@ +############################################################################ +# fs/mnemofs/Make.defs +# +# 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. +# +############################################################################ + +ifeq ($(CONFIG_FS_MNEMOFS),y) + +# Add the mnemofs C files to the build + +CSRCS += mnemofs_blkalloc.c +CSRCS += mnemofs_fsobj.c +CSRCS += mnemofs_journal.c +CSRCS += mnemofs_lru.c +CSRCS += mnemofs_rw.c +CSRCS += mnemofs.c + +# Add the mnemofs directory to the build + +DEPPATH += --dep-path mnemofs +VPATH += :mnemofs +endif \ No newline at end of file diff --git a/fs/mnemofs/mnemofs.c b/fs/mnemofs/mnemofs.c new file mode 100644 index 0000000000..2009098fa3 --- /dev/null +++ b/fs/mnemofs/mnemofs.c @@ -0,0 +1,2148 @@ +/**************************************************************************** + * 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; +} diff --git a/fs/mnemofs/mnemofs.h b/fs/mnemofs/mnemofs.h new file mode 100644 index 0000000000..7ef33e15a6 --- /dev/null +++ b/fs/mnemofs/mnemofs.h @@ -0,0 +1,1106 @@ +/**************************************************************************** + * fs/mnemofs/mnemofs.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __FS_MNEMOFS_MNEMOFS_H +#define __FS_MNEMOFS_MNEMOFS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MFS_JRNL_MAGIC "-mfs!j!-" +#define MFS_MN_MAGIC "-mfs!m!-" + +#define MFS_CEILDIVIDE(num, denom) (((num) + ((denom) - 1)) / (denom)) +#define MFS_UPPER8(num) (((num) + 7) & (-8)) + +#define MFS_BLK2PG(sb, blk) ((blk) << (sb)->log_pg_in_blk) +#define MFS_PG2BLK(sb, pg) ((pg) >> (sb)->log_pg_in_blk) +#define MFS_PG2BLKPGOFF(sb, pg) ((pg) % (1 << (sb)->log_pg_in_blk)) + +#define MFS_LOCK(sb) ((sb)->fs_lock) +#define MFS_PGSZ(sb) ((sb)->pg_sz) +#define MFS_LRU(sb) ((sb)->lru) +#define MFS_BLKSZ(sb) ((sb)->blk_sz) +#define MFS_MN(sb) ((sb)->mn) +#define MFS_JRNL(sb) ((sb)->j_state) +#define MFS_LOGPGSZ(sb) (MFS_CEILDIVIDE((sb)->log_pg_sz, 8)) +#define MFS_PGINBLK(sb) ((sb)->pg_in_blk) +#define MFS_MTD(sb) ((sb)->drv->u.i_mtd) +#define MFS_RWBUF(sb) ((sb)->rw_buf) +#define MFS_BA(sb) ((sb)->ba_state) +#define MFS_NBLKS(sb) ((sb)->n_blks) +#define MFS_NPGS(sb) (MFS_NBLKS(sb) * MFS_PGINBLK(sb)) + +#define MFS_CTZ_SZ(l) ((l)->sz) +#define MFS_DIRENTSZ(dirent) (sizeof(struct mfs_dirent_s) \ + + (dirent)->namelen) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef uint32_t mfs_t; + +enum +{ + MFS_PG_USED, + MFS_PG_FREE, + MFS_BLK_BAD, + MFS_BLK_ERASABLE, + MFS_BLK_FREE, + MFS_BLK_USED, +}; + +enum MFS_PATH_FLAGS +{ + MFS_ISDIR = (1 << 0), /* Path is a directory. */ + MFS_ISFILE = (1 << 1), /* Path is a file. */ + MFS_NEXIST = (1 << 2), /* Path No Exist */ + MFS_FINPATH = (1 << 3), /* File in midele of path before bottom most + * child. Not reachable. + */ + MFS_P_EXIST = (1 << 4), /* Parent of the bottom most element exists. */ + MFS_P_ISDIR = (1 << 5), /* Parent is a directory. */ +}; + +struct mfs_ctz_s +{ + mfs_t pg_e; + mfs_t idx_e; +}; + +struct mfs_path_s +{ + struct mfs_ctz_s ctz; + mfs_t off; + mfs_t sz; +}; + +struct mfs_ba_state_s +{ + mfs_t s_blk; /* Start block */ + mfs_t c_pg; /* Current page */ + FAR mfs_t *k_del; /* Delete counter for blocks. */ + size_t n_bmap_upgs; + FAR uint8_t *bmap_upgs; /* Bitmap of used pages. */ +}; + +struct mfs_mn_s +{ + mfs_t pg; /* Only mblk1's pg will be used here. */ + mfs_t jrnl_blk; /* Start of journal. */ + mfs_t mblk_idx; + struct mfs_ctz_s root_ctz; + mfs_t root_sz; + struct timespec ts; + mode_t root_mode; + struct timespec root_st_atim; + struct timespec root_st_ctim; + struct timespec root_st_mtim; +}; + +struct mfs_jrnl_state_s +{ + mfs_t mblk1; + mfs_t mblk2; + mfs_t n_logs; + mfs_t log_cpg; /* Current (last) page */ + mfs_t log_cblkidx; /* Current (last) block index. */ + mfs_t log_spg; /* First log's page */ + mfs_t log_sblkidx; /* First jrnl blk index. TODO: jrnlarr > 1 blk. */ + mfs_t jrnlarr_pg; + mfs_t jrnlarr_pgoff; + uint16_t n_blks; +}; + +struct mfs_sb_s +{ + FAR uint8_t *rw_buf; + FAR struct inode *drv; + mutex_t fs_lock; + mfs_t sb_blk; /* Block number of the superblock */ + mfs_t pg_sz; + uint8_t log_pg_sz; + mfs_t blk_sz; + uint8_t log_blk_sz; + mfs_t n_blks; + uint8_t log_n_blks; + mfs_t n_lru; + uint16_t pg_in_blk; + uint8_t log_pg_in_blk; + uint8_t j_nblks; + struct mfs_mn_s mn; /* Master Node */ + struct mfs_jrnl_state_s j_state; /* Journal State */ + struct mfs_ba_state_s ba_state; /* Block Allocator State */ + struct list_node lru; + struct list_node of; /* open files. */ +}; + +/* This is for *dir VFS methods. */ + +struct mfs_fsdirent +{ + struct fs_dirent_s base; /* VFS directory structure */ + uint8_t idx; /* This only goes from 0 for ., 1 for .. and + * 2 for others. + */ + FAR struct mfs_pitr_s *pitr; + FAR struct mfs_path_s *path; + mfs_t depth; +}; + +/* LRU Delta */ + +struct mfs_delta_s +{ + struct list_node list; + mfs_t off; + mfs_t n_b; + FAR char *upd; +}; + +/* LRU Node */ + +struct mfs_node_s +{ + struct list_node list; + struct list_node delta; + mfs_t n_list; + mfs_t depth; + mfs_t sz; + mfs_t range_min; + mfs_t range_max; + FAR struct mfs_path_s path[]; +}; + +/* Common Part Open File Descriptor */ + +struct mfs_ocom_s +{ + bool new_ent; + mfs_t sz; /* Current file size. */ + mfs_t off; + mfs_t depth; + uint8_t refcount; + int oflags; + FAR struct mfs_path_s *path; +}; + +/* Open part for file descriptors. */ + +struct mfs_ofd_s +{ + struct list_node list; + FAR struct mfs_ocom_s *com; +}; + +struct mfs_dirent_s +{ + uint8_t name_hash; /* Should be at start to improve efficiency. */ + mfs_t sz; + uint16_t mode; + struct timespec st_atim; /* Time of last access */ + struct timespec st_mtim; /* Time of last modification */ + struct timespec st_ctim; /* Time of last status change */ + struct mfs_ctz_s ctz; + uint8_t namelen; + FAR char name[]; +}; + +/* Parent iterator */ + +struct mfs_pitr_s +{ + struct mfs_path_s p; /* Parent representation */ + mfs_t depth; + mfs_t c_off; /* Current offset. */ + mfs_t sz; /* Parent's size. */ +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mfs_blkremsz + * + * Description: + * Given a page and a page offset, it returns the bytes left in the entire + * block after the offset location. + * + * Input Parameters: + * sb - Superblock instance of the device. + * pg - Page number. + * pgoff - Page offset. + * + * Returned Value: + * Bytes left in the block. + * + ****************************************************************************/ + +static mfs_t inline mfs_blkremsz(FAR const struct mfs_sb_s * const sb, + mfs_t pg, mfs_t pgoff) +{ + return MFS_BLKSZ(sb) - (MFS_PG2BLKPGOFF(sb, pg) * sb->pg_sz + pgoff); +} + +static inline mfs_t mfs_ctz(const uint32_t x) +{ + if (predict_false(x == 0)) + { +/* Special case, since we're using this for the CTZ skip list. The 0th + * block has no pointers. + */ + + return 0; + } + +#if defined(__GNUC__) + return __builtin_ctz(x); +#else + uint32_t c; + +/* Credits: + * http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightBinSearch + */ + + if (x & 0x1) + { + /* special case for odd x (assumed to happen half of the time) */ + + c = 0; + } + else + { + c = 1; + if ((x & 0xffff) == 0) + { + x >>= 16; + c += 16; + } + if ((x & 0xff) == 0) + { + x >>= 8; + c += 8; + } + if ((x & 0xf) == 0) + { + x >>= 4; + c += 4; + } + if ((x & 0x3) == 0) + { + x >>= 2; + c += 2; + } + c -= x & 0x1; + } + return c; +#endif +} + +static inline mfs_t mfs_clz(const uint32_t x) +{ + if (predict_false(x == UINT32_MAX)) + { +/* Special case, since we're using this for the CTZ skip list. The 0th + * block has no pointers. + */ + + return 0; + } + +#if defined(__GNUC__) + return __builtin_clz(x); +#else + return 0; /* TODO */ +#endif +} + +static inline mfs_t mfs_popcnt(mfs_t x) +{ +#if defined(__GNUC__) + return __builtin_popcount(x); +#else +/* Can be found at: + * http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan + */ + + mfs_t c; + for (c = 0; x; c++) + { + x &= x - 1; + } + +#endif +} + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* mnemofs_journal.c */ + +/**************************************************************************** + * Name: mfs_jrnl_init + * + * Description: + * Initialize journal if device is already formatted. + * + * Input Parameters: + * sb - Superblock instance of the device. + * blk - First block of the journal. + * + * Returned Value: + * 0 - OK + * < 0 - Error + * + ****************************************************************************/ + +int mfs_jrnl_init(FAR struct mfs_sb_s * const sb, mfs_t blk); + +/**************************************************************************** + * Name: mfs_jrnl_fmt + * + * Description: + * Format a journal to the device. + * + * Input Parameters: + * sb - Superblock instance of the device. + * blk1 - First master block for the journal. + * blk2 - Second master block for the journal. + * + * Returned Value: + * 0 - OK + * < 0 - Error + * + * Assumptions/Limitations: + * If blk1 == 0 and blk2 == 0, this means that this will also format in the + * master blocks. If this is not satisfied, the provided values will be + * taken to denote the master nodes. + * + ****************************************************************************/ + +int mfs_jrnl_fmt(FAR struct mfs_sb_s * const sb, mfs_t blk1, mfs_t blk2); + +/**************************************************************************** + * Name: mfs_jrnl_free + * + * Description: + * Free the journal. + * + * Input Parameters: + * sb - Superblock instance of the device. + * + ****************************************************************************/ + +void mfs_jrnl_free(FAR struct mfs_sb_s * const sb); + +/* mnemofs_blkalloc.c */ + +/**************************************************************************** + * Name: mfs_ba_init + * + * Description: + * Initializes the block allocator. + * + * Input Parameters: + * sb - Superblock instance of the device. + * + * Returned Value: + * 0 - OK + * -ENOMEM - No memory left. + * + ****************************************************************************/ + +int mfs_ba_init(FAR struct mfs_sb_s * const sb); + +/**************************************************************************** + * Name: mfs_ba_free + * + * Description: + * Free the block allocator + * + * Input Parameters: + * sb - Superblock instance of the device. + * + * Assumptions/Limitations: + * This assumes a locked environment when called. + * + ****************************************************************************/ + +void mfs_ba_free(FAR struct mfs_sb_s * const sb); + +/**************************************************************************** + * Name: mfs_ba_getavailpgs + * + * Description: + * Get number of available pages. + * + * Input Parameters: + * sb - Superblock instance of the device. + * pg - Page number + * + * Assumptions/Limitations: + * This assumes a locked environment when called. + * + ****************************************************************************/ + +mfs_t mfs_ba_getavailpgs(FAR const struct mfs_sb_s * const sb); + +/* mnemofs_rw.c */ + +/**************************************************************************** + * Name: mfs_read_page + * + * Description: + * Read a page. + * + * Input Parameters: + * sb - Superblock instance of the device. + * data - Buffer + * datalen - Length of buffer. + * pg - Page number. + * pgoff - Offset into the page. + * + * Assumptions/Limitations: + * This assumes a locked environment when called. + * + ****************************************************************************/ + +ssize_t mfs_read_page(FAR const struct mfs_sb_s * const sb, + FAR char *data, const mfs_t datalen, const off_t page, + const mfs_t pgoff); + +/* mnemofs_lru.c */ + +/**************************************************************************** + * Name: mfs_lru_del + * + * Description: + * Delete instruction to LRU. + * + * Input Parameters: + * sb - Superblock instance of the device. + * off - Offset into the data. + * bytes - Number of bytes to delete. + * ctz_sz - Number of bytes in the data of the CTZ list. + * path - CTZ representation of the relpath. + * depth - Depth of path. + * + ****************************************************************************/ + +int mfs_lru_del(FAR struct mfs_sb_s * const sb, const mfs_t off, + mfs_t bytes, mfs_t ctz_sz, + FAR struct mfs_path_s * const path, const mfs_t depth); + +/**************************************************************************** + * Name: mfs_lru_wr + * + * Description: + * Write to LRU + * + * Input Parameters: + * sb - Superblock instance of the device. + * data_off - Offset into the data. + * bytes - Number of bytes to delete. + * ctz_sz - Number of bytes in the data of the CTZ list. + * path - CTZ representation of the relpath. + * depth - Depth of path. + * buf - Buffer. + * + ****************************************************************************/ + +int mfs_lru_wr(FAR struct mfs_sb_s * const sb, const mfs_t data_off, + mfs_t bytes, mfs_t ctz_sz, FAR struct mfs_path_s * const path, + const mfs_t depth, FAR const char *buf); + +/**************************************************************************** + * Name: mfs_lru_ctzflush + * + * Description: + * Flush the updates of a CTZ list inside the LRU to the flash. + * + * Input Parameters: + * sb - Superblock instance of the device. + * path - CTZ representation of the relpath. + * depth - Depth of path. + * ctz_sz - Size of the CTZ file. + * + ****************************************************************************/ + +int mfs_lru_ctzflush(FAR struct mfs_sb_s * const sb, + FAR struct mfs_path_s * const path, const mfs_t depth, + const mfs_t ctz_sz); + +/**************************************************************************** + * Name: mfs_lru_rdfromoff + * + * Description: + * Read updated data from an offset upto a certain number of bytes. + * + * Input Parameters: + * sb - Superblock instance of the device. + * data_off - Offset into the data. + * path - CTZ representation of the relpath. + * depth - Depth of path. + * buf - Buffer to populate with data. + * buflen - Length of data to read. + * + ****************************************************************************/ + +int mfs_lru_rdfromoff(FAR struct mfs_sb_s * const sb, const mfs_t data_off, + FAR struct mfs_path_s * const path, const mfs_t depth, + FAR char *buf, const mfs_t buflen); + +/**************************************************************************** + * Name: mfs_lru_init + * + * Description: + * Initialize LRU. + * + * Input Parameters: + * sb - Superblock instance of the device. + * + ****************************************************************************/ + +void mfs_lru_init(FAR struct mfs_sb_s * const sb); + +/**************************************************************************** + * Name: mfs_lru_updatedsz + * + * Description: + * Update size of a CTZ list. + * + * Input Parameters: + * sb - Superblock instance of the device. + * path - CTZ representation of the relpath. + * depth - Depth of path. + * n_sz - New size. + * + ****************************************************************************/ + +void mfs_lru_updatedsz(FAR struct mfs_sb_s * const sb, + FAR const struct mfs_path_s * const path, + const mfs_t depth, mfs_t *n_sz); + +/* mnemofs_fsobj.c */ + +/**************************************************************************** + * Name: mfs_path2childname + * + * Description: + * Given a path, point to the start of the name of the last fs object in + * that path (child). + * + * Input Parameters: + * relpath - Path. + * + * Returned Value: + * The pointer pointing to where the name of the child starts. + * + * Assumptions/Limitations: + * This does not allocate a new array for the name, but rather just points + * to the place in `relpath` where the name starts. + * + ****************************************************************************/ + +FAR const char * mfs_path2childname(FAR const char *relpath); + +/**************************************************************************** + * Name: mfs_get_fsz + * + * Description: + * Get updated file size of a file. + * + * Input Parameters: + * sb - Superblock instance of the device. + * path - CTZ representation of the path. + * depth - Depth of the path. + * + * Returned Value: + * The size of the file. + * + ****************************************************************************/ + +mfs_t mfs_get_fsz(FAR struct mfs_sb_s * const sb, + FAR const struct mfs_path_s * const path, + const mfs_t depth); + +/**************************************************************************** + * Name: mfs_get_patharr + * + * Description: + * Takes a relpath, and returns the CTZ location of every file system + * object that occurs in the relpath. This also returns a bit flag + * consisting of items from `MFS_PATH_FLAGS`. + * + * Input Parameters: + * sb - Superblock instance of the device. + * relpath - Path of the file. + * path - To populate with CTZ representation of the path. + * depth - To populate with depth of the path. + * + * Returned Value: + * 0 - OK + * < - Error + * + * Assumptions/Limitations: + * This allocates the `path` array in heap, and transfers the ownership + * of this array to the caller. It's the caller's reponsibility to use this + * with `mfs_free_patharr`. + * + ****************************************************************************/ + +int mfs_get_patharr(FAR struct mfs_sb_s *const sb, + FAR const char *relpath, FAR struct mfs_path_s **path, + FAR mfs_t *depth); + +/**************************************************************************** + * Name: mfs_free_patharr + * + * Description: + * Frees up a CTZ representation of the relpath. + * + * Input Parameters: + * path - CTZ representation of the relpath. + * + ****************************************************************************/ + +void mfs_free_patharr(FAR struct mfs_path_s *path); + +/**************************************************************************** + * Name: mfs_obj_isempty + * + * Description: + * Checks if a fs object is empty by reading its directory entry. + * + * Input Parameters: + * sb - Superblock instance of the device. + * pitr - Parent iterator pointing to the fs object. + * + * Returned Value: + * Is the file or directory pointer by the direntry empty. + * + * Assumptions/Limitations: + * The `pitr` should point to the fs object being checked. + * + ****************************************************************************/ + +bool mfs_obj_isempty(FAR struct mfs_sb_s * const sb, + FAR struct mfs_pitr_s * const pitr); + +/**************************************************************************** + * Name: mfs_pitr_init + * + * Description: + * Initialize a parent iterator (pitr). This iterator starts at the start + * of the parent's directory file, and is used to iterate over the + * directory entries (direntries) the file has. + * + * Input Parameters: + * sb - Superblock instance of the device. + * path - CTZ representation of the relpath. + * depth - Depth of CTZ representation of the path. + * pitr - To initialize Parent iterator. + * child - If the child exists in the path. + * + * Assumptions/Limitations: + * - The `pitr` should point to the fs object being checked. + * + * - If the `child` is set to true, it assumes the child is present in the + * directory file. If not, it assumes it's not present in the path (due + * to whatever circumstances like it's not known if the child exists in + * the parent or not, search in parent file, etc.). + * + * - This assumes the pitr is initialized and freed in the same locked + * context. If, at all, this is not the case, then at the start of every + * new locked context till it's freed, this needs to be synced. + * + * - This also assumes that a CTZ will not change location before the pitr + * is destroyed. If at all a CTZ changes location, the pitr needs to be + * updated. + * + * - This contains a pitr with all fields set to 0 when it's invalid. + * + ****************************************************************************/ + +void mfs_pitr_init(FAR struct mfs_sb_s * const sb, + FAR const struct mfs_path_s * const path, + const mfs_t depth, FAR struct mfs_pitr_s *pitr, + bool child); + +/**************************************************************************** + * Name: mfs_pitr_free + * + * Description: + * Free an initialized pitr. + * + * Input Parameters: + * pitr - Parent iterator. + * + * Assumptions/Limitations: + * It's mainly for symbolic purpose, and the only thing it does is make all + * the members reset to 0. + * + ****************************************************************************/ + +void mfs_pitr_free(FAR struct mfs_pitr_s * const pitr); + +/**************************************************************************** + * Name: mfs_pitr_adv + * + * Description: + * Advance a pitr to the next direntry. + * + * Input Parameters: + * sb - Superblock instance of the device. + * pitr - Parent iterator. + * + * Assumptions/Limitations: + * This is best used if we have not already read the value of a dirent. If + * we have, then we do it the `mfs_pitr_adv_dirent` way. + * + ****************************************************************************/ + +void mfs_pitr_adv(FAR struct mfs_sb_s * const sb, + FAR struct mfs_pitr_s * const pitr); + +/**************************************************************************** + * Name: mfs_pitr_adv_dirent + * + * Description: + * Advance a pitr to the next direntry by using current direntry. + * + * Input Parameters: + * pitr - Parent iterator. + * dirent - Current directory entry. + * + * Assumptions/Limitations: + * This is best used if we have already got the dirent by which we want to + * advance entry (the one pointed by pitr). If we don't, then we do it the + * `mfs_pitr_adv` way. + * + ****************************************************************************/ + +void mfs_pitr_adv_dirent(FAR struct mfs_pitr_s * const pitr, + FAR const struct mfs_dirent_s * const dirent); + +/**************************************************************************** + * Name: mfs_pitr_adv_off + * + * Description: + * Advance a pitr by an offset. + * + * Input Parameters: + * pitr - Parent iterator. + * + * Assumptions/Limitations: + * This is best used if we have already got the offset by which we want to + * advance entry. This assumes that once the offset is applied, the new + * pitr location is valid, and start of a direntry. + * + ****************************************************************************/ + +void mfs_pitr_adv_off(FAR struct mfs_pitr_s * const pitr, + const mfs_t off); + +/**************************************************************************** + * Name: mfs_pitr_adv_tochild + * + * Description: + * Advance the pitr to point to the child mentioned in the path. + * + * Input Parameters: + * pitr - Parent iterator. + * path - CTZ representation of the relpath + * depth - Depth of path. + * + * Assumptions/Limitations: + * This assumes that the pitr is initialized for the immediate parent of the + * child. + * + ****************************************************************************/ + +void mfs_pitr_adv_tochild(FAR struct mfs_pitr_s * const pitr, + FAR const struct mfs_path_s * const path, + const mfs_t depth); + +/**************************************************************************** + * Name: mfs_pitr_reset + * + * Description: + * Reset a pitr to point to the start of the CTZ file. + * + * Input Parameters: + * pitr - Parent iterator. + * + ****************************************************************************/ + +void mfs_pitr_reset(FAR struct mfs_pitr_s * const pitr); + +/**************************************************************************** + * Name: mfs_pitr_sync + * + * Description: + * Sync a pitr. + * + * In mnemofs, when moving between locked contexts, it may happen that a + * a CTZ is moved to some place is between. This updates the location by + * reading the journal to check for any related logs in the journal. + * Updates contained in the LRU do not update the location of a CTZ list. + * + * Input Parameters: + * sb - Superblock instance of the device. + * pitr - Parent iterator. + * path - CTZ representation of the relpath + * depth - Depth of path. + * + ****************************************************************************/ + +void mfs_pitr_sync(FAR struct mfs_sb_s * const sb, + FAR struct mfs_pitr_s * const pitr, + FAR const struct mfs_path_s * const path, + const mfs_t depth); + +/**************************************************************************** + * Name: mfs_pitr_readdirent + * + * Description: + * Reads the direntry pointed to by the pitr. + * + * Input Parameters: + * sb - Superblock instance of the device. + * pitr - Parent Iterator. + * dirent - Direntry to populate. + * + * Returned Value: + * Length of the name of the FS object in the direntry. + * + * Assumptions/Limitations: + * This alloctes `dirent`, and transfers the ownership to the caller. It's + * the caller's responsibility to get it freed using mfs_free_dirent. + * + ****************************************************************************/ + +int mfs_pitr_readdirent(FAR struct mfs_sb_s * const sb, + FAR struct mfs_pitr_s * const pitr, + FAR struct mfs_dirent_s **dirent); + +/**************************************************************************** + * Name: mfs_pitr_readdirent + * + * Description: + * Free an allocated direntry. + * + * Input Parameters: + * dirent - Direntry. + * + ****************************************************************************/ + +void mfs_free_dirent(FAR struct mfs_dirent_s *dirent); + +/**************************************************************************** + * Name: mfs_searchfopen + * + * Description: + * Checks if a file is already open. + * + * In mnemofs, this is done by iterating through the kernel list of open + * files. + * + * Input Parameters: + * sb - Superblock instance of the device. + * path - CTZ representation of the relpath. + * depth - Depth of path. + * + * Returned Value: + * True - File is already open. + * False - File is not open. + * + ****************************************************************************/ + +bool mfs_searchfopen(FAR const struct mfs_sb_s * const sb, + FAR const struct mfs_path_s * const path, + const mfs_t depth); + +/**************************************************************************** + * Name: mfs_pitr_appenddirent + * + * Description: + * Appeend a direntry at the end of the directory file of parent that's + * initialized in pitr. + * + * In mnemofs, this is done by iterating through the kernel list of open + * files. + * + * Input Parameters: + * sb - Superblock instance of the device. + * path - CTZ representation of the relpath. + * depth - Depth of path. + * pitr - Parent Iterator. + * dirent - Directory entry to be added. + * + * Returned Value: + * 0 - OK + * < 0 - Error + * + ****************************************************************************/ + +int mfs_pitr_appenddirent(FAR struct mfs_sb_s * const sb, + FAR struct mfs_path_s * const path, + const mfs_t depth, + FAR const struct mfs_pitr_s * const pitr, + FAR const struct mfs_dirent_s * const dirent); + +/**************************************************************************** + * Name: mfs_pitr_appendnew + * + * Description: + * Appeend a new entry at the end of the directory file of parent that's + * initialized in pitr. This creates the direntry. + * + * Input Parameters: + * sb - Superblock instance of the device. + * path - CTZ representation of the relpath. + * depth - Depth of path. + * pitr - Parent Iterator. + * dirent - Directory entry to be added. + * mode - Mode of the file/directory. + * + * Returned Value: + * 0 - OK + * < 0 - Error + * + ****************************************************************************/ + +int mfs_pitr_appendnew(FAR struct mfs_sb_s * const sb, + FAR struct mfs_path_s * const path, + const mfs_t depth, + FAR const struct mfs_pitr_s * const pitr, + FAR const char * const child_name, const mode_t mode); + +/**************************************************************************** + * Name: mfs_pitr_rmdirent + * + * Description: + * Removes the dirent from its parent's directory file. + * + * Input Parameters: + * sb - Superblock instance of the device. + * path - CTZ representation of the relpath. + * depth - Depth of path. + * pitr - Parent Iterator. + * dirent - Directory entry to be added. + * + * Returned Value: + * 0 - OK + * < 0 - Error + * + ****************************************************************************/ + +int mfs_pitr_rmdirent(FAR struct mfs_sb_s * const sb, + FAR struct mfs_path_s * const path, + const mfs_t depth, FAR struct mfs_pitr_s * const pitr, + FAR const struct mfs_dirent_s * const dirent); + +/**************************************************************************** + * Name: mfs_pitr_rm + * + * Description: + * Removes the fs object pointed by path from its parent's directory file. + * + * Input Parameters: + * sb - Superblock instance of the device. + * path - CTZ representation of the relpath. + * depth - Depth of path. + * + * Returned Value: + * 0 - OK + * < 0 - Error + * + * Assumptions/Limitations: + * This does not require any form of pitr initialization. It does it inside + * itself. + * + ****************************************************************************/ + +int mfs_pitr_rm(FAR struct mfs_sb_s * const sb, + FAR struct mfs_path_s * const path, + const mfs_t depth); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __FS_MNEMOFS_MNEMOFS_H */ diff --git a/fs/mnemofs/mnemofs_blkalloc.c b/fs/mnemofs/mnemofs_blkalloc.c new file mode 100644 index 0000000000..0fc04110b1 --- /dev/null +++ b/fs/mnemofs/mnemofs_blkalloc.c @@ -0,0 +1,128 @@ +/**************************************************************************** + * fs/mnemofs/mnemofs_blkalloc.c + * Block Allocator for mnemofs + * + * 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 block allocator takes some inspiration from littlefs's block + * allocator. + * + * It has two primary jobs...provide a block and ensure wear levelling. The + * block allocator of mnemofs tries to provide a block that will more or less + * ensure wear levelling. We'll call the block allocator as BA. + * + * The block allocator starts at a random block in the device and starts a + * circular allocation from there, ie. it allocated sequentially till it + * reaches the end, at which point it cycles back to the beginning and then + * continues allocating sequentially. If a page is requested it will check if + * the page has been written to (being used). If a page is being written to + * but all the pages in a block are ready to be erased, then the block is + * erased and page is allocated. If none of these two conditions match, it + * moves on to check the next page and so on. If the block that contains the + * page is a bad block, the BA skips all the pages in the entire block. + * + * The BA can also grant a request for an entire block. If the BA is + * currently in the middle of a block, it will skip the remaining pages till + * it reaches the start of the next block. These pages won't be reflected as + * being used, and can be allocated the next time the BA cycles back to these + * pages. Even though skipped pages will be eventually utilized later anyway, + * block allocation requests are made by very few critical data structures + * in mnemofs, and they all do it in bulk, and thus skipped pages are + * minimal. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "mnemofs.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int mfs_ba_init(FAR struct mfs_sb_s * const sb) +{ + /* TODO */ + + return OK; +} + +void mfs_ba_free(FAR struct mfs_sb_s * const sb) +{ + /* TODO */ +} diff --git a/fs/mnemofs/mnemofs_fsobj.c b/fs/mnemofs/mnemofs_fsobj.c new file mode 100644 index 0000000000..d6e8ce0eb8 --- /dev/null +++ b/fs/mnemofs/mnemofs_fsobj.c @@ -0,0 +1,250 @@ +/**************************************************************************** + * fs/mnemofs/mnemofs_fsobj.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * In mnemofs, all the FS object methods (ie. methods in this file), + * interface directly with the LRU. To these methods, only the methods + * exposed by the LRU are visible, nothing else. The LRU will give them the + * most updated data, which includes data from the flash, the updates from + * the journal and the LRU deltas as well. + * + * TODO: The above menetioned concept. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "mnemofs.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +FAR const char * mfs_path2childname(FAR const char *relpath) +{ + /* TODO */ + + return NULL; +} + +mfs_t mfs_get_fsz(FAR struct mfs_sb_s * const sb, + FAR const struct mfs_path_s * const path, + const mfs_t depth) +{ + /* TODO */ + + return 0; +} + +int mfs_get_patharr(FAR struct mfs_sb_s *const sb, + FAR const char *relpath, FAR struct mfs_path_s **path, + FAR mfs_t *depth) +{ + /* TODO */ + + return OK; +} + +void mfs_free_patharr(FAR struct mfs_path_s *path) +{ + /* TODO */ +} + +bool mfs_obj_isempty(FAR struct mfs_sb_s * const sb, + FAR struct mfs_pitr_s * const pitr) +{ + /* TODO */ + + return false; +} + +void mfs_pitr_init(FAR struct mfs_sb_s * const sb, + FAR const struct mfs_path_s * const path, + const mfs_t depth, FAR struct mfs_pitr_s *pitr, bool child) +{ + /* TODO */ +} + +void mfs_pitr_free(FAR struct mfs_pitr_s * const pitr) +{ + /* TODO */ +} + +void mfs_pitr_adv(FAR struct mfs_sb_s * const sb, + FAR struct mfs_pitr_s * const pitr) +{ + /* TODO */ +} + +void mfs_pitr_adv_dirent(FAR struct mfs_pitr_s * const pitr, + FAR const struct mfs_dirent_s * const dirent) +{ + /* TODO */ +} + +void mfs_pitr_adv_off(FAR struct mfs_pitr_s * const pitr, + const mfs_t off) +{ + /* TODO */ +} + +void mfs_pitr_adv_tochild(FAR struct mfs_pitr_s * const pitr, + FAR const struct mfs_path_s * const path, + const mfs_t depth) +{ + /* TODO */ +} + +void mfs_pitr_reset(FAR struct mfs_pitr_s * const pitr) +{ + /* TODO */ +} + +void mfs_pitr_sync(FAR struct mfs_sb_s * const sb, + FAR struct mfs_pitr_s * const pitr, + FAR const struct mfs_path_s * const path, + const mfs_t depth) +{ + /* TODO */ +} + +int mfs_pitr_readdirent(FAR struct mfs_sb_s * const sb, + FAR struct mfs_pitr_s * const pitr, + FAR struct mfs_dirent_s **dirent) +{ + /* TODO */ + + return OK; +} + +void mfs_free_dirent(FAR struct mfs_dirent_s *dirent) +{ + /* TODO */ +} + +bool mfs_searchfopen(FAR const struct mfs_sb_s * const sb, + FAR const struct mfs_path_s * const path, + const mfs_t depth) +{ + /* TODO */ + + return false; +} + +int mfs_pitr_appendnew(FAR struct mfs_sb_s * const sb, + FAR struct mfs_path_s * const path, + const mfs_t depth, + FAR const struct mfs_pitr_s * const pitr, + FAR const char * const child_name, + const mode_t mode) +{ + /* TODO */ + + return OK; +} + +int mfs_pitr_appenddirent(FAR struct mfs_sb_s * const sb, + FAR struct mfs_path_s * const path, + const mfs_t depth, + FAR const struct mfs_pitr_s * const pitr, + FAR const struct mfs_dirent_s * const dirent) +{ + /* TODO */ + + return OK; +} + +int mfs_pitr_rmdirent(FAR struct mfs_sb_s * const sb, + FAR struct mfs_path_s * const path, + const mfs_t depth, + FAR struct mfs_pitr_s * const pitr, + FAR const struct mfs_dirent_s * const dirent) +{ + /* TODO */ + + return OK; +} + +int mfs_pitr_rm(FAR struct mfs_sb_s * const sb, + FAR struct mfs_path_s * const path, + const mfs_t depth) +{ + /* TODO */ + + return OK; +} diff --git a/fs/mnemofs/mnemofs_journal.c b/fs/mnemofs/mnemofs_journal.c new file mode 100644 index 0000000000..4f010a51de --- /dev/null +++ b/fs/mnemofs/mnemofs_journal.c @@ -0,0 +1,131 @@ +/**************************************************************************** + * fs/mnemofs/mnemofs_journal.c + * Journal of mnemofs. + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * In mnemofs, the journal stores the path, depth and the new location of the + * CTZ file called logs, and also the location of the master block. The first + * n blocks of the journal store the logs, while the last two blocks contain + * master nodes, and the blocks are called as master blocks. The two master + * blocks are identical copies for backup. + * + * Due to LRU, and the structure of mnemofs, the first n blocks of the + * journal get filled up much faster than the master blocks, and move more. + * There will be certain point where the entire journal (the n+2 blocks) + * move, but mostly, its the first n blocks that move. + * + * The first block starts with an 8 byte magic sequence, a 2 bytes long + * number denoting number of blocks in the journal, and then follows up + * with an array containing the block numbers of all blocks in the journal + * including the first block. Then the logs start. + * + * The logs take up size in multiples of pages. There might be unitilzed + * space at the end of a log. + * + * All logs are followed by a byte-long hash of the log. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "mnemofs.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int mfs_jrnl_init(FAR struct mfs_sb_s * const sb, mfs_t blk) +{ + /* TODO */ + + return OK; +} + +void mfs_jrnl_free(FAR struct mfs_sb_s * const sb) +{ + /* TODO */ +} + +int mfs_jrnl_fmt(FAR struct mfs_sb_s * const sb, mfs_t blk1, mfs_t blk2) +{ + /* TODO */ + + return OK; +} diff --git a/fs/mnemofs/mnemofs_lru.c b/fs/mnemofs/mnemofs_lru.c new file mode 100644 index 0000000000..0776ff58ee --- /dev/null +++ b/fs/mnemofs/mnemofs_lru.c @@ -0,0 +1,152 @@ +/**************************************************************************** + * fs/mnemofs/mnemofs_lru.c + * LRU cache of mnemofs. + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * LRU (Least Recently Used) cache takes in all the changes the user wants + * to do to the on-flash storage, and stores them in memory. When a + * significant amount of changes are accumulated, the LRU writes the new + * information to the flash. + * + * LRU is a kernel list of nodes. Each node represents a CTZ list. Each node + * contains a kernel list of changes requested for the CTZ list, called as + * deltas. + * + * When LRU is full the last node is flushed (it can be explicitly flushed as + * well) and all the changes are written at once on the flash, and the new + * location is noted down in the journal, and an entry for the location + * update is added to the LRU for the parent. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "mnemofs.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int mfs_lru_ctzflush(FAR struct mfs_sb_s * const sb, + FAR struct mfs_path_s * const path, const mfs_t depth, + const mfs_t ctz_sz) +{ + /* TODO */ + + return OK; +} + +int mfs_lru_del(FAR struct mfs_sb_s * const sb, const mfs_t data_off, + mfs_t bytes, mfs_t ctz_sz, + FAR struct mfs_path_s * const path, const mfs_t depth) +{ + /* TODO */ + + return OK; +} + +int mfs_lru_wr(FAR struct mfs_sb_s * const sb, const mfs_t data_off, + mfs_t bytes, mfs_t ctz_sz, FAR struct mfs_path_s * const path, + const mfs_t depth, FAR const char *buf) +{ + /* TODO */ + + return OK; +} + +int mfs_lru_rdfromoff(FAR struct mfs_sb_s * const sb, const mfs_t data_off, + FAR struct mfs_path_s * const path, const mfs_t depth, + FAR char *buf, const mfs_t buflen) +{ + /* TODO */ + + return OK; +} + +void mfs_lru_init(FAR struct mfs_sb_s * const sb) +{ + /* TODO */ +} + +void mfs_lru_updatedsz(FAR struct mfs_sb_s * const sb, + FAR const struct mfs_path_s * const path, + const mfs_t depth, mfs_t *n_sz) +{ + /* TODO */ +} diff --git a/fs/mnemofs/mnemofs_rw.c b/fs/mnemofs/mnemofs_rw.c new file mode 100644 index 0000000000..0607c95bef --- /dev/null +++ b/fs/mnemofs/mnemofs_rw.c @@ -0,0 +1,91 @@ +/**************************************************************************** + * fs/mnemofs/mnemofs_rw.c + * Read/Write utilities for mnemofs + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "mnemofs.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +ssize_t mfs_read_page(FAR const struct mfs_sb_s * const sb, + FAR char *data, const mfs_t datalen, const off_t page, + const mfs_t pgoff) +{ + return OK; +} diff --git a/fs/mount/fs_mount.c b/fs/mount/fs_mount.c index 2bbef28958..4780f34613 100644 --- a/fs/mount/fs_mount.c +++ b/fs/mount/fs_mount.c @@ -57,8 +57,8 @@ /* These file systems require MTD drivers */ -#if (defined(CONFIG_FS_SPIFFS) || defined(CONFIG_FS_LITTLEFS)) && \ - defined(CONFIG_MTD) +#if (defined(CONFIG_FS_SPIFFS) || defined(CONFIG_FS_LITTLEFS) || \ + defined(CONFIG_FS_MNEMOFS)) && defined(CONFIG_MTD) # define MDFS_SUPPORT 1 #endif @@ -132,6 +132,9 @@ extern const struct mountpt_operations g_spiffs_operations; #ifdef CONFIG_FS_LITTLEFS extern const struct mountpt_operations g_littlefs_operations; #endif +#ifdef CONFIG_FS_MNEMOFS +extern const struct mountpt_operations g_mnemofs_operations; +#endif static const struct fsmap_t g_mdfsmap[] = { @@ -143,6 +146,9 @@ static const struct fsmap_t g_mdfsmap[] = #endif #ifdef CONFIG_FS_LITTLEFS { "littlefs", &g_littlefs_operations }, +#endif +#ifdef CONFIG_FS_MNEMOFS + { "mnemofs", &g_mnemofs_operations }, #endif { NULL, NULL }, }; diff --git a/include/sys/statfs.h b/include/sys/statfs.h index 68c711cde4..83cc8a0db9 100644 --- a/include/sys/statfs.h +++ b/include/sys/statfs.h @@ -82,6 +82,7 @@ #define _XIAFS_SUPER_MAGIC 0x012fd16d #define SPIFFS_SUPER_MAGIC 0x20090315 #define LITTLEFS_SUPER_MAGIC 0x0a732923 +#define MNEMOFS_SUPER_MAGIC 0x704b8e4d /* NuttX specific file-systems */