From 4f6e695f7c56aa5a321008ca8e5ddd47e1959e4d Mon Sep 17 00:00:00 2001 From: Saurav Pal Date: Fri, 15 May 2026 23:14:23 +0530 Subject: [PATCH] fs/mnemofs: Add mnemofs version 1 support. Split mnemofs into allocation, directory, file, CTZ, and read/write modules, and update direntry traversal and file handling. Add superblock format version 1 support, reject newer on-flash versions, and preserve the mounted version when rewriting metadata. Update the NAND simulator drivers for the new mnemofs behavior by fixing spare writes, exposing the erase state, allowing raw reads, and documenting the background-task startup flow. Signed-off-by: Saurav Pal --- .../applications/testing/nand_sim/index.rst | 17 +- drivers/mtd/mtd_nandram.c | 16 +- drivers/mtd/mtd_nandwrapper.c | 24 +- fs/mnemofs/CMakeLists.txt | 13 +- fs/mnemofs/Kconfig | 41 - fs/mnemofs/Make.defs | 13 +- fs/mnemofs/mnemofs.c | 4414 +++++++---------- fs/mnemofs/mnemofs.h | 2161 +------- fs/mnemofs/mnemofs_alloc.c | 1343 +++++ fs/mnemofs/mnemofs_blkalloc.c | 580 --- fs/mnemofs/mnemofs_ctz.c | 881 ++-- fs/mnemofs/mnemofs_dirent.c | 3249 ++++++++++++ fs/mnemofs/mnemofs_file.c | 1732 +++++++ fs/mnemofs/mnemofs_fsobj.c | 1136 ----- fs/mnemofs/mnemofs_journal.c | 887 ---- fs/mnemofs/mnemofs_lru.c | 1068 ---- fs/mnemofs/mnemofs_master.c | 453 -- fs/mnemofs/mnemofs_rw.c | 552 ++- fs/mnemofs/mnemofs_util.c | 263 - 19 files changed, 9246 insertions(+), 9597 deletions(-) create mode 100644 fs/mnemofs/mnemofs_alloc.c delete mode 100644 fs/mnemofs/mnemofs_blkalloc.c create mode 100644 fs/mnemofs/mnemofs_dirent.c create mode 100644 fs/mnemofs/mnemofs_file.c delete mode 100644 fs/mnemofs/mnemofs_fsobj.c delete mode 100644 fs/mnemofs/mnemofs_journal.c delete mode 100644 fs/mnemofs/mnemofs_lru.c delete mode 100644 fs/mnemofs/mnemofs_master.c delete mode 100644 fs/mnemofs/mnemofs_util.c diff --git a/Documentation/applications/testing/nand_sim/index.rst b/Documentation/applications/testing/nand_sim/index.rst index 61472adf0b9..fb0ac4847ea 100644 --- a/Documentation/applications/testing/nand_sim/index.rst +++ b/Documentation/applications/testing/nand_sim/index.rst @@ -152,15 +152,18 @@ is still the wrapper, not the actual upper half, as the upper half methods may call the other methods as well internally and we might want to log them as well*. -Registering Device & Daemon -=========================== +Registering Device & Background Task +==================================== This wrapper is then registered using ``register_mtddriver``, and this -whole thing is converted to be a daemon, so that the device can keep running -in the background. +whole thing is launched as a background task, so that the device can keep +running after the ``nand`` command returns to the shell. -Making it a daemon is achieved by using ``fork()``, killing the parent, and -using ``daemon()`` in child. +The ``nand`` command starts a dedicated NuttX task with ``task_create()``, +waits until the device registration finishes, and then returns control to +the shell. This keeps the simulator alive in the background without relying +on ``fork()``, whose post-fork execution model is not appropriate for this +kind of long-running initialization in the flat build used by the simulator. Known Issues ============ @@ -168,4 +171,4 @@ Known Issues * ECC is not implemented yet. * MLC NAND Flash is not implemented yet. * Due to the fixed name of the device, there can't be more than one instance - of this virtual device. \ No newline at end of file + of this virtual device. diff --git a/drivers/mtd/mtd_nandram.c b/drivers/mtd/mtd_nandram.c index 1b92dc8e6a6..0a719b62237 100644 --- a/drivers/mtd/mtd_nandram.c +++ b/drivers/mtd/mtd_nandram.c @@ -332,12 +332,10 @@ int nand_ram_eraseblock(FAR struct nand_raw_s *raw, off_t block) int nand_ram_rawread(FAR struct nand_raw_s *raw, off_t block, unsigned int page, FAR void *data, FAR void *spare) { - int ret; uint32_t read_page; struct nand_ram_data_s *read_page_data; struct nand_ram_spare_s *read_page_spare; - ret = OK; read_page = (block << NAND_RAM_LOG_PAGES_PER_BLOCK) + page; read_page_data = nand_ram_flash_data + read_page; read_page_spare = nand_ram_flash_spare + read_page; @@ -349,14 +347,6 @@ int nand_ram_rawread(FAR struct nand_raw_s *raw, off_t block, nand_ram_ins_i, "rawread", read_page); nand_ram_status(); - if (nand_ram_flash_spare[read_page].bad != NAND_RAM_BLOCK_GOOD) - { - ret = -EFAULT; - NAND_RAM_LOG("[LOWER %" PRIu64 " | %s] Failed: %s\n", - nand_ram_ins_i, "rawread", EFAULT_STR); - goto errout; - } - nand_ram_flash_spare[read_page].n_read++; if (data != NULL) @@ -378,11 +368,9 @@ int nand_ram_rawread(FAR struct nand_raw_s *raw, off_t block, } NAND_RAM_LOG("[LOWER %" PRIu64 " | %s] Done\n", nand_ram_ins_i, "rawread"); - -errout: nxmutex_unlock(&nand_ram_dev_mut); - return ret; + return OK; } /**************************************************************************** @@ -444,7 +432,7 @@ int nand_ram_rawwrite(FAR struct nand_raw_s *raw, off_t block, if (spare != NULL) { - memcpy((FAR void *)write_page_spare, data, NAND_RAM_SPARE_SIZE); + memcpy((FAR void *)write_page_spare, spare, NAND_RAM_SPARE_SIZE); } NAND_RAM_LOG("[LOWER %" PRIu64 " | %s] Done\n", nand_ram_ins_i, diff --git a/drivers/mtd/mtd_nandwrapper.c b/drivers/mtd/mtd_nandwrapper.c index 248db39e4a0..2844c3f7c8d 100644 --- a/drivers/mtd/mtd_nandwrapper.c +++ b/drivers/mtd/mtd_nandwrapper.c @@ -272,6 +272,7 @@ int nand_wrapper_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg) { int ret; + FAR uint8_t *erasestate; FAR struct nand_wrapper_dev_s *nand_dev; nand_dev = (struct nand_wrapper_dev_s *)dev; @@ -282,7 +283,28 @@ int nand_wrapper_ioctl(FAR struct mtd_dev_s *dev, int cmd, ", Arg : %zu\n", nand_wrapper_ins_i, "ioctl", cmd, arg); DEBUGASSERT(nand_dev && nand_dev->under.mtd.ioctl); - ret = nand_dev->under.mtd.ioctl(dev, cmd, arg); + if (cmd == MTDIOC_ERASESTATE) + { + erasestate = (FAR uint8_t *)arg; + if (erasestate == NULL) + { + ret = -EINVAL; + } + else + { + /* The wrapped bread path exposes blank NAND pages as zero-filled + * buffers after ECC handling, so report the same visible erase + * state to higher layers. + */ + + *erasestate = 0x00; + ret = OK; + } + } + else + { + ret = nand_dev->under.mtd.ioctl(dev, cmd, arg); + } if (ret >= 0) { diff --git a/fs/mnemofs/CMakeLists.txt b/fs/mnemofs/CMakeLists.txt index 059b0ec1024..fa304483a54 100644 --- a/fs/mnemofs/CMakeLists.txt +++ b/fs/mnemofs/CMakeLists.txt @@ -49,15 +49,6 @@ # ############################################################################## if(CONFIG_FS_MNEMOFS) - target_sources( - fs - PRIVATE mnemofs_blkalloc.c - mnemofs_ctz.c - mnemofs_fsobj.c - mnemofs_journal.c - mnemofs_lru.c - mnemofs_master.c - mnemofs_rw.c - mnemofs_util.c - mnemofs.c) + target_sources(fs PRIVATE mnemofs.c mnemofs_alloc.c mnemofs_ctz.c + mnemofs_dirent.c mnemofs_file.c mnemofs_rw.c) endif() diff --git a/fs/mnemofs/Kconfig b/fs/mnemofs/Kconfig index 5b917613791..833665531c4 100644 --- a/fs/mnemofs/Kconfig +++ b/fs/mnemofs/Kconfig @@ -9,44 +9,3 @@ config FS_MNEMOFS depends on !DISABLE_MOUNTPOINT && MTD_NAND ---help--- Build the mnemofs NAND flash file system. - -if FS_MNEMOFS -config MNEMOFS_EXTRA_DEBUG - bool "MNEMOFS Extra Debug Logs" - default n - depends on FS_MNEMOFS - ---help--- - Prints extra log information related to 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 index b3daca808da..c30d4c0cf95 100644 --- a/fs/mnemofs/Make.defs +++ b/fs/mnemofs/Make.defs @@ -54,15 +54,12 @@ ifeq ($(CONFIG_FS_MNEMOFS),y) # Add the mnemofs C files to the build -CSRCS += mnemofs_blkalloc.c -CSRCS += mnemofs_ctz.c -CSRCS += mnemofs_fsobj.c -CSRCS += mnemofs_journal.c -CSRCS += mnemofs_lru.c -CSRCS += mnemofs_master.c -CSRCS += mnemofs_rw.c -CSRCS += mnemofs_util.c CSRCS += mnemofs.c +CSRCS += mnemofs_alloc.c +CSRCS += mnemofs_ctz.c +CSRCS += mnemofs_dirent.c +CSRCS += mnemofs_file.c +CSRCS += mnemofs_rw.c # Add the mnemofs directory to the build diff --git a/fs/mnemofs/mnemofs.c b/fs/mnemofs/mnemofs.c index bf1a4326780..5a3065cc2cf 100644 --- a/fs/mnemofs/mnemofs.c +++ b/fs/mnemofs/mnemofs.c @@ -50,134 +50,1878 @@ * ****************************************************************************/ -/* 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 + +#include +#include #include #include "mnemofs.h" -#include "fs_heap.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 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_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); +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_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 + * Pre-processor Definitions ****************************************************************************/ +#define MFS_MOUNTOPT_NONE 0 +#define MFS_MOUNTOPT_AUTOFORMAT 1 +#define MFS_MOUNTOPT_FORCEFORMAT 2 + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mnemofs_mountpt_to_sb + * + * Description: + * Return the mounted file system state associated with one mountpoint + * inode. + * + * Input Parameters: + * mountpt - The mountpoint inode to inspect. + * + * Returned Value: + * A pointer to the mounted file system state is returned on success. + * NULL is returned if mountpt is NULL. + * + ****************************************************************************/ + +static FAR struct mfs_sb_s *mnemofs_mountpt_to_sb(FAR struct inode *mountpt) +{ + if (mountpt == NULL) + { + return NULL; + } + + return mountpt->i_private; +} + +/**************************************************************************** + * Name: mnemofs_file_to_sb + * + * Description: + * Return the mounted file system state associated with one VFS file + * structure. + * + * Input Parameters: + * filep - The VFS file structure to inspect. + * + * Returned Value: + * A pointer to the mounted file system state is returned on success. + * NULL is returned if no file-system state can be resolved. + * + ****************************************************************************/ + +static FAR struct mfs_sb_s *mnemofs_file_to_sb(FAR const struct file *filep) +{ + FAR struct mfs_ofd_s *ofd; + + if (filep == NULL) + { + return NULL; + } + + ofd = filep->f_priv; + if (ofd != NULL) + { + return ofd->sb; + } + + if (filep->f_inode == NULL) + { + return NULL; + } + + return filep->f_inode->i_private; +} + +/**************************************************************************** + * Name: mnemofs_lock + * + * Description: + * Acquire the per-mount mnemofs mutex. + * + * Input Parameters: + * sb - The mounted file system instance. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * invalid input or lock failure. + * + ****************************************************************************/ + +static int mnemofs_lock(FAR struct mfs_sb_s *sb) +{ + if (sb == NULL) + { + return -EINVAL; + } + + return nxmutex_lock(&sb->lock); +} + +/**************************************************************************** + * Name: mnemofs_unlock + * + * Description: + * Flush and invalidate the shared read/write buffer, then release the + * per-mount mutex. + * + * Input Parameters: + * sb - The mounted file system instance. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void mnemofs_unlock(FAR struct mfs_sb_s *sb) +{ + int ret; + + DEBUGASSERT(sb != NULL); + ret = mfs_rwbuf_sync(sb); + if (ret < 0) + { + ferr("Could not sync read/write buffer: %d\n", ret); + } + + mfs_rwbuf_invalidate(sb); + nxmutex_unlock(&sb->lock); +} + +/**************************************************************************** + * Name: mnemofs_dirmeta_checksum + * + * Description: + * Compute the checksum stored in an on-flash directory metadata record. + * + * Input Parameters: + * dirmeta - The directory metadata record to checksum. + * + * Returned Value: + * The computed checksum value is returned. + * + ****************************************************************************/ + +static uint8_t mnemofs_dirmeta_checksum( + FAR const struct mfs_dirmeta_s *dirmeta) +{ + return crc8((FAR const uint8_t *)dirmeta, sizeof(*dirmeta) - 1); +} + +/**************************************************************************** + * Name: mnemofs_dirmeta_is_valid + * + * Description: + * Verify the magic and checksum stored in a directory metadata record. + * + * Input Parameters: + * dirmeta - The directory metadata record to validate. + * + * Returned Value: + * true is returned if dirmeta contains a valid directory metadata record. + * false is returned otherwise. + * + ****************************************************************************/ + +static bool mnemofs_dirmeta_is_valid( + FAR const struct mfs_dirmeta_s *dirmeta) +{ + if (dirmeta == NULL) + { + return false; + } + + return dirmeta->magic == MFS_DIR_MAGIC && + dirmeta->checksum == mnemofs_dirmeta_checksum(dirmeta); +} + +/**************************************************************************** + * Name: mnemofs_superblock_init + * + * Description: + * Initialize one in-memory superblock image for writing to flash. + * + * Input Parameters: + * sb - The mounted file system instance. + * rootdir - The root directory block to store. + * superblock - The location to receive the initialized image. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void mnemofs_superblock_init(FAR const struct mfs_sb_s *sb, + mfs_t rootdir, + FAR struct mfs_superblock_s *superblock) +{ + DEBUGASSERT(sb != NULL && superblock != NULL); + + memset(superblock, 0, sizeof(*superblock)); + superblock->magic = MNEMOFS_SUPER_MAGIC; + superblock->npages = MFS_PAGE_COUNT(sb); + superblock->pagesize = MFS_PAGE_SIZE(sb); + superblock->pagesperblock = MFS_PAGES_PER_BLOCK(sb); + superblock->nblocks = MFS_BLOCK_COUNT(sb); + superblock->rootdir = rootdir; + superblock->version = sb->version; +} + +/**************************************************************************** + * Name: mnemofs_superblock_is_valid + * + * Description: + * Verify that an on-flash superblock matches the current device geometry + * and references a plausible root directory block. + * + * Input Parameters: + * sb - The mounted file system instance. + * superblock - The on-flash superblock image to validate. + * + * Returned Value: + * true is returned if the superblock is valid. false is returned + * otherwise. + * + ****************************************************************************/ + +static bool mnemofs_superblock_is_valid( + FAR const struct mfs_sb_s *sb, + FAR const struct mfs_superblock_s *superblock) +{ + if (sb == NULL || superblock == NULL) + { + return false; + } + + return superblock->magic == MNEMOFS_SUPER_MAGIC && + superblock->npages == MFS_PAGE_COUNT(sb) && + superblock->pagesize == MFS_PAGE_SIZE(sb) && + superblock->pagesperblock == MFS_PAGES_PER_BLOCK(sb) && + superblock->nblocks == MFS_BLOCK_COUNT(sb) && + superblock->rootdir < MFS_BLOCK_COUNT(sb); +} + +/**************************************************************************** + * Name: mnemofs_find_next_good_block + * + * Description: + * Scan forward from start until a non-bad block is found. + * + * Input Parameters: + * sb - The mounted file system instance. + * start - The first block number to inspect. + * block - The location to receive the next usable block. + * + * Returned Value: + * Zero (OK) is returned on success. -ENOSPC is returned if no good block + * exists at or after start. A negated errno value is returned on other + * failures. + * + ****************************************************************************/ + +static int mnemofs_find_next_good_block(FAR struct mfs_sb_s *sb, mfs_t start, + FAR mfs_t *block) +{ + int ret; + + if (sb == NULL || block == NULL) + { + return -EINVAL; + } + + for (; start < MFS_BLOCK_COUNT(sb); start++) + { + ret = mfs_is_bad_block(sb, start); + if (ret < 0) + { + return ret; + } + + if (ret == 0) + { + * block = start; + return OK; + } + } + + return -ENOSPC; +} + +/**************************************************************************** + * Name: mnemofs_find_initial_rootdir + * + * Description: + * Find the first usable root-directory block after the chosen superblock. + * + * Input Parameters: + * sb - The mounted file system instance. + * superblock - The block number reserved for the superblock. + * rootdir - The location to receive the candidate root directory + * block. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_find_initial_rootdir(FAR struct mfs_sb_s *sb, + mfs_t superblock, + FAR mfs_t *rootdir) +{ + if (sb == NULL || rootdir == NULL) + { + return -EINVAL; + } + + return mnemofs_find_next_good_block(sb, superblock + 1, rootdir); +} + +/**************************************************************************** + * Name: mnemofs_validate_rootdir + * + * Description: + * Verify that rootdir is usable and contains a valid self-parenting + * directory metadata page. + * + * Input Parameters: + * sb - The mounted file system instance. + * superblock - The superblock block number. + * rootdir - The root directory block number to validate. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_validate_rootdir(FAR struct mfs_sb_s *sb, + mfs_t superblock, mfs_t rootdir) +{ + struct mfs_dirmeta_s dirmeta; + ssize_t nread; + mfs_t page; + int ret; + + if (sb == NULL) + { + return -EINVAL; + } + + if (rootdir <= superblock || rootdir >= MFS_BLOCK_COUNT(sb)) + { + return -EINVAL; + } + + ret = mfs_is_bad_block(sb, rootdir); + if (ret < 0) + { + return ret; + } + + if (ret != 0) + { + return -EINVAL; + } + + page = MFS_BLOCK_TO_PAGE(sb, rootdir); + nread = mfs_read_page(sb, page, sb->rwbuf); + if (nread < 0) + { + return nread; + } + + if (nread != 1) + { + return -EIO; + } + + memcpy(&dirmeta, sb->rwbuf, sizeof(dirmeta)); + if (!mnemofs_dirmeta_is_valid(&dirmeta) || dirmeta.parent != rootdir) + { + return -EINVAL; + } + + return OK; +} + +/**************************************************************************** + * Name: mnemofs_read_superblock + * + * Description: + * Read and validate the on-flash superblock, then return its root + * directory block number. + * + * Input Parameters: + * sb - The mounted file system instance. + * superblock - The block number that stores the superblock. + * rootdir - The location to receive the validated root directory + * block. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_read_superblock(FAR struct mfs_sb_s *sb, mfs_t superblock, + FAR mfs_t *rootdir) +{ + struct mfs_superblock_s onflash; + ssize_t nread; + mfs_t page; + int ret; + + if (sb == NULL || rootdir == NULL) + { + return -EINVAL; + } + + page = MFS_BLOCK_TO_PAGE(sb, superblock); + nread = mfs_read_page(sb, page, sb->rwbuf); + if (nread < 0) + { + return nread; + } + + if (nread != 1) + { + return -EIO; + } + + memcpy(&onflash, sb->rwbuf, sizeof(onflash)); + if (!mnemofs_superblock_is_valid(sb, &onflash)) + { + return -EINVAL; + } + + ret = mnemofs_validate_rootdir(sb, superblock, onflash.rootdir); + if (ret < 0) + { + return ret; + } + + if (onflash.version > MFS_FORMAT_VERSION_MAJOR) + { + ferr("higher version present on flash: %u > %u\n", + onflash.version, MFS_FORMAT_VERSION_MAJOR); + return -EPROTONOSUPPORT; + } + + sb->version = onflash.version; + * rootdir = onflash.rootdir; + return OK; +} + +/**************************************************************************** + * Name: mnemofs_write_superblock + * + * Description: + * Write a freshly initialized superblock image to flash. + * + * Input Parameters: + * sb - The mounted file system instance. + * superblock - The block number that stores the superblock. + * rootdir - The root directory block number to record. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_write_superblock(FAR struct mfs_sb_s *sb, + mfs_t superblock, mfs_t rootdir) +{ + struct mfs_superblock_s onflash; + int ret; + ssize_t nwritten; + mfs_t page; + + if (sb == NULL) + { + return -EINVAL; + } + + mnemofs_superblock_init(sb, rootdir, &onflash); + ret = mfs_rwbuf_prepare_write(sb); + if (ret < 0) + { + return ret; + } + + memset(sb->rwbuf, 0, MFS_PAGE_SIZE(sb)); + memcpy(sb->rwbuf, &onflash, sizeof(onflash)); + + page = MFS_BLOCK_TO_PAGE(sb, superblock); + nwritten = mfs_write_page(sb, page, sb->rwbuf); + if (nwritten < 0) + { + return nwritten; + } + + return nwritten == 1 ? OK : -EIO; +} + +/**************************************************************************** + * Name: mnemofs_format_rootdir + * + * Description: + * Write the metadata page for a freshly formatted root directory block. + * + * Input Parameters: + * sb - The mounted file system instance. + * rootdir - The root directory block number to initialize. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_format_rootdir(FAR struct mfs_sb_s *sb, mfs_t rootdir) +{ + struct mfs_dirmeta_s dirmeta; + int ret; + ssize_t nwritten; + mfs_t page; + + if (sb == NULL) + { + return -EINVAL; + } + + memset(&dirmeta, 0, sizeof(dirmeta)); + dirmeta.magic = MFS_DIR_MAGIC; + dirmeta.parent = rootdir; + dirmeta.checksum = mnemofs_dirmeta_checksum(&dirmeta); + + ret = mfs_rwbuf_prepare_write(sb); + if (ret < 0) + { + return ret; + } + + memset(sb->rwbuf, 0, MFS_PAGE_SIZE(sb)); + memcpy(sb->rwbuf, &dirmeta, sizeof(dirmeta)); + + page = MFS_BLOCK_TO_PAGE(sb, rootdir); + nwritten = mfs_write_page(sb, page, sb->rwbuf); + if (nwritten < 0) + { + return nwritten; + } + + return nwritten == 1 ? OK : -EIO; +} + +/**************************************************************************** + * Name: mnemofs_erase_good_blocks + * + * Description: + * Erase every usable block on the backing device while leaving bad blocks + * untouched. + * + * Input Parameters: + * sb - The mounted file system instance. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_erase_good_blocks(FAR struct mfs_sb_s *sb) +{ + mfs_t block; + int bad; + int ret; + + if (sb == NULL) + { + return -EINVAL; + } + + for (block = 0; block < MFS_BLOCK_COUNT(sb); block++) + { + bad = mfs_is_bad_block(sb, block); + if (bad < 0) + { + return bad; + } + + if (bad != 0) + { + continue; + } + + ret = mfs_erase_blocks(sb, block, 1); + if (ret < 0) + { + return ret; + } + + if (ret != 1) + { + return -EIO; + } + } + + return OK; +} + +/**************************************************************************** + * Name: mnemofs_format + * + * Description: + * Format the device as a fresh mnemofs volume with the specified + * superblock and root directory blocks. + * + * Input Parameters: + * sb - The mounted file system instance. + * superblock - The superblock block number. + * rootdir - The root directory block number. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_format(FAR struct mfs_sb_s *sb, mfs_t superblock, + mfs_t rootdir) +{ + int ret; + + sb->version = MFS_FORMAT_VERSION_MAJOR; + ret = mnemofs_erase_good_blocks(sb); + if (ret < 0) + { + return ret; + } + + ret = mnemofs_format_rootdir(sb, rootdir); + if (ret < 0) + { + return ret; + } + + return mnemofs_write_superblock(sb, superblock, rootdir); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mfs_update_rootdir + * + * Description: + * Persist a replacement root directory block into both the root directory + * metadata page and the on-flash superblock. + * + * Input Parameters: + * sb - The mounted file system instance. + * rootdir - The new root directory block number. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int mfs_update_rootdir(FAR struct mfs_sb_s *sb, mfs_t rootdir) +{ + int ret; + + if (sb == NULL) + { + ferr("invalid sb\n"); + return -EINVAL; + } + + ret = mnemofs_format_rootdir(sb, rootdir); + if (ret < 0) + { + ferr("mnemofs_format_rootdir failed: %d\n", ret); + return ret; + } + + ret = mnemofs_write_superblock(sb, sb->superblock, rootdir); + if (ret < 0) + { + ferr("mnemofs_write_superblock failed: %d\n", ret); + return ret; + } + + sb->rootdir = rootdir; + finfo("updated rootdir\n"); + return OK; +} + +/**************************************************************************** + * Name: mnemofs_bind + * + * Description: + * Bind an MTD device to mnemofs. This captures geometry, allocates the + * shared read/write buffer, parses mount options, validates or formats + * the on-flash superblock, and initializes allocator state for the + * mounted volume. + * + * Input Parameters: + * driver - The backing MTD inode. + * data - Optional mount options string. Supported options (strings) are + * "autoformat" and "forceformat". + * handle - To be updated with the file system's state (sb). + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_bind(FAR struct inode *driver, FAR const void *data, + FAR void **handle) +{ + FAR struct mfs_sb_s *sb; + FAR const char *mountopt; + int mountoptid; + mfs_t superblock; + mfs_t rootdir; + int ret; + + if (driver == NULL) + { + ferr("The driver is NULL\n"); + return -EINVAL; + } + + if (handle == NULL) + { + ferr("The handle is NULL\n"); + return -EINVAL; + } + + if (!INODE_IS_MTD(driver) || driver->u.i_mtd == NULL) + { + ferr("The required driver not found.\n"); + return -ENODEV; + } + + sb = kmm_zalloc(sizeof(*sb)); + if (sb == NULL) + { + ferr("Could not allocated sb\n"); + return -ENOMEM; + } + + finfo("sb allocated: %p\n", sb); + + ret = nxmutex_init(&sb->lock); + if (ret < 0) + { + ferr("Could not initialize fs mutex\n"); + goto errout_with_sb; + } + + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("Could not acquire fs mutex\n"); + goto errout_with_mutex; + } + + sb->driver = driver; + sb->mtd = driver->u.i_mtd; + ret = MTD_IOCTL(sb->mtd, MTDIOC_GEOMETRY, + (unsigned long)(uintptr_t)&sb->geo); + if (ret < 0) + { + ferr("Could not get device geometry\n"); + goto errout_with_lock; + } + + sb->erasestate = 0xff; + ret = MTD_IOCTL(sb->mtd, MTDIOC_ERASESTATE, + (unsigned long)(uintptr_t)&sb->erasestate); + if (ret < 0 && ret != -ENOTTY) + { + ferr("Could not get erase state\n"); + goto errout_with_lock; + } + + if (sb->geo.blocksize == 0 || sb->geo.erasesize == 0 || + sb->geo.erasesize % sb->geo.blocksize != 0) + { + ferr("Invalid geometry\n"); + + ret = -EINVAL; + goto errout_with_lock; + } + + sb->pagesperblk = sb->geo.erasesize / sb->geo.blocksize; + if (sb->pagesperblk < 2) + { + ferr("Number of pages is too low in MTD device\n"); + ret = -EINVAL; + goto errout_with_lock; + } + + sb->rwbuf = kmm_malloc(MFS_PAGE_SIZE(sb)); + if (sb->rwbuf == NULL) + { + ferr("Could not allocated read/write buffer for fs\n"); + ret = -ENOMEM; + goto errout_with_lock; + } + + memset(sb->rwbuf, 0, MFS_PAGE_SIZE(sb)); + sb->rwpage = MFS_LOCATION_INVALID; + sb->rwvalid = false; + sb->rwdirty = false; + sb->version = MFS_FORMAT_VERSION_MAJOR; + + mountopt = data; + mountoptid = MFS_MOUNTOPT_NONE; + if (mountopt != NULL && mountopt[0] != '\0') + { + if (strcmp(mountopt, "autoformat") == 0) + { + finfo("Mount option set: autoformat\n"); + mountoptid = MFS_MOUNTOPT_AUTOFORMAT; + } + else if (strcmp(mountopt, "forceformat") == 0) + { + finfo("Mount option set: forceformat\n"); + mountoptid = MFS_MOUNTOPT_FORCEFORMAT; + } + else + { + ferr("Invalid mount option\n"); + ret = -EINVAL; + goto errout_with_lock; + } + } + else + { + finfo("Mount option set: none (default)\n"); + } + + ret = mnemofs_find_next_good_block(sb, 0, &superblock); + if (ret < 0) + { + ferr("Could not locate superblock\n"); + goto errout_with_lock; + } + + sb->superblock = superblock; + if (mountoptid == MFS_MOUNTOPT_FORCEFORMAT) + { + /* Force format the disk with mnemofs */ + + ret = mnemofs_find_initial_rootdir(sb, superblock, &rootdir); + if (ret < 0) + { + ferr("Could not locate initial root directory block\n"); + goto errout_with_lock; + } + + sb->rootdir = rootdir; + ret = mnemofs_format(sb, superblock, rootdir); + if (ret < 0) + { + ferr("Could not force format volume\n"); + goto errout_with_lock; + } + } + else + { + ret = mnemofs_read_superblock(sb, superblock, &rootdir); + if (ret < 0) + { + if (ret == -EINVAL && mountoptid == MFS_MOUNTOPT_AUTOFORMAT) + { + ret = mnemofs_find_initial_rootdir(sb, superblock, &rootdir); + if (ret < 0) + { + ferr("Could not locate initial root directory block\n"); + goto errout_with_lock; + } + + sb->rootdir = rootdir; + ret = mnemofs_format(sb, superblock, rootdir); + if (ret < 0) + { + ferr("Could not auto format volume\n"); + goto errout_with_lock; + } + } + else + { + if (ret != -EPROTONOSUPPORT) + { + ferr("Volume is not formatted as mnemofs\n"); + } + + goto errout_with_lock; + } + } + else + { + sb->rootdir = rootdir; + } + } + + ret = mfs_alloc_init(sb); + if (ret < 0) + { + ferr("Could not initialize page allocator\n"); + goto errout_with_lock; + } + + list_initialize(&sb->ofiles); + * handle = sb; + mnemofs_unlock(sb); + return OK; + +errout_with_lock: + mfs_alloc_uninit(sb); + mnemofs_unlock(sb); + kmm_free(sb->rwbuf); +errout_with_mutex: + nxmutex_destroy(&sb->lock); +errout_with_sb: + kmm_free(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_unbind + * + * Description: + * Tear down a mounted mnemofs instance once no open files remain. + * + * Input Parameters: + * handle - The bound file system state. + * driver - The location to receive the backing MTD inode. + * flags - Unbind flags from the VFS. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_unbind(FAR void *handle, FAR struct inode **driver, + unsigned int flags) +{ + FAR struct mfs_sb_s *sb = handle; + int ret; + + switch (flags) + { + default: + break; + } + + if (sb == NULL || driver == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %d\n", ret); + return ret; + } + + if (!list_is_empty(&sb->ofiles)) + { + ferr("open files still active\n"); + ret = -EBUSY; + goto errout_with_lock; + } + + * driver = sb->driver; + mfs_alloc_uninit(sb); + mnemofs_unlock(sb); + kmm_free(sb->rwbuf); + nxmutex_destroy(&sb->lock); + kmm_free(sb); + return OK; + +errout_with_lock: + mnemofs_unlock(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_statfs + * + * Description: + * Return file-system-wide statistics for one mounted mnemofs instance. + * + * Input Parameters: + * mountpt - The mountpoint inode. + * buf - The location to receive the translated statfs data. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf) +{ + FAR struct mfs_sb_s *sb; + int ret; + + if (buf == NULL) + { + ferr("invalid statfs buffer\n"); + return -EINVAL; + } + + sb = mnemofs_mountpt_to_sb(mountpt); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %d\n", ret); + return ret; + } + + memset(buf, 0, sizeof(*buf)); + buf->f_type = MNEMOFS_SUPER_MAGIC; + buf->f_bsize = MFS_PAGE_SIZE(sb); + buf->f_blocks = MFS_BLOCK_COUNT(sb); + buf->f_bfree = MFS_BLOCK_COUNT(sb) > sb->superblock + 1 ? + MFS_BLOCK_COUNT(sb) - sb->superblock - 1 : 0; + buf->f_bavail = buf->f_bfree; + buf->f_namelen = MFS_NAME_MAX; + mnemofs_unlock(sb); + return OK; +} + +/**************************************************************************** + * Name: mnemofs_open + * + * Description: + * VFS open wrapper for regular files in mnemofs. + * + * Input Parameters: + * filep - The VFS file structure to initialize. + * relpath - The relative path to open. + * oflags - Open flags. + * mode - Create mode used with O_CREAT. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode) +{ + FAR struct mfs_sb_s *sb; + int ret; + + sb = mnemofs_file_to_sb(filep); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %d\n", ret); + return ret; + } + + ret = mfs_file_open(sb, filep, relpath, oflags, mode); + if (ret < 0) + { + ferr("mfs_file_open failed: %d\n", ret); + } + + mnemofs_unlock(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_close + * + * Description: + * VFS close wrapper for regular files in mnemofs. + * + * Input Parameters: + * filep - The VFS file structure to close. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_close(FAR struct file *filep) +{ + FAR struct mfs_sb_s *sb; + int ret; + + sb = mnemofs_file_to_sb(filep); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %d\n", ret); + return ret; + } + + ret = mfs_file_close(sb, filep); + if (ret < 0) + { + ferr("mfs_file_close failed: %d\n", ret); + } + + mnemofs_unlock(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_read + * + * Description: + * VFS read wrapper for regular files in mnemofs. + * + * Input Parameters: + * filep - The VFS file structure to read from. + * buffer - The caller's read buffer. + * buflen - The number of bytes requested. + * + * Returned Value: + * A non-negative byte count is returned on success. A negated errno + * value is returned on failure. + * + ****************************************************************************/ + +static ssize_t mnemofs_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct mfs_sb_s *sb; + ssize_t ret; + + sb = mnemofs_file_to_sb(filep); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %zd\n", ret); + return ret; + } + + ret = mfs_file_read(sb, filep, buffer, buflen); + if (ret < 0) + { + ferr("mfs_file_read failed: %zd\n", ret); + } + + mnemofs_unlock(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_write + * + * Description: + * VFS write wrapper for regular files in mnemofs. + * + * Input Parameters: + * filep - The VFS file structure to write to. + * buffer - The source bytes to write. + * buflen - The number of bytes to write. + * + * Returned Value: + * A non-negative byte count is returned on success. A negated errno + * value is returned on failure. + * + ****************************************************************************/ + +static ssize_t mnemofs_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen) +{ + FAR struct mfs_sb_s *sb; + ssize_t ret; + + sb = mnemofs_file_to_sb(filep); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %zd\n", ret); + return ret; + } + + ret = mfs_file_write(sb, filep, buffer, buflen); + if (ret < 0) + { + ferr("mfs_file_write failed: %zd\n", ret); + } + + mnemofs_unlock(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_seek + * + * Description: + * VFS seek wrapper for regular files in mnemofs. + * + * Input Parameters: + * filep - The VFS file structure to reposition. + * offset - The seek offset. + * whence - The seek base selector. + * + * Returned Value: + * The new file position is returned on success. A negated errno value is + * returned on failure. + * + ****************************************************************************/ + +static off_t mnemofs_seek(FAR struct file *filep, off_t offset, int whence) +{ + FAR struct mfs_sb_s *sb; + off_t ret; + + sb = mnemofs_file_to_sb(filep); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %lld\n", (long long)ret); + return ret; + } + + ret = mfs_file_seek(sb, filep, offset, whence); + if (ret < 0) + { + ferr("mfs_file_seek failed: %lld\n", (long long)ret); + } + + mnemofs_unlock(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_ioctl + * + * Description: + * VFS ioctl wrapper for regular files in mnemofs. + * + * Input Parameters: + * filep - The VFS file structure to operate on. + * cmd - The ioctl command. + * arg - The ioctl argument. + * + * Returned Value: + * Zero (OK) or another command-specific value is returned on success. A + * negated errno value is returned on failure. + * + ****************************************************************************/ + +static int mnemofs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct mfs_sb_s *sb; + int ret; + + sb = mnemofs_file_to_sb(filep); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %d\n", ret); + return ret; + } + + ret = mfs_file_ioctl(sb, filep, cmd, arg); + if (ret < 0) + { + ferr("mfs_file_ioctl failed: %d\n", ret); + } + + mnemofs_unlock(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_truncate + * + * Description: + * VFS truncate wrapper for regular files in mnemofs. + * + * Input Parameters: + * filep - The VFS file structure to truncate. + * length - The new stored file length. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_truncate(FAR struct file *filep, off_t length) +{ + FAR struct mfs_sb_s *sb; + int ret; + + sb = mnemofs_file_to_sb(filep); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %d\n", ret); + return ret; + } + + ret = mfs_file_truncate(sb, filep, length); + if (ret < 0) + { + ferr("mfs_file_truncate failed: %d\n", ret); + } + + mnemofs_unlock(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_sync + * + * Description: + * VFS sync wrapper for regular files in mnemofs. + * + * Input Parameters: + * filep - The VFS file structure to sync. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_sync(FAR struct file *filep) +{ + FAR struct mfs_sb_s *sb; + int ret; + + sb = mnemofs_file_to_sb(filep); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %d\n", ret); + return ret; + } + + ret = mfs_file_sync(sb, filep); + if (ret < 0) + { + ferr("mfs_file_sync failed: %d\n", ret); + } + + mnemofs_unlock(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_dup + * + * Description: + * VFS dup wrapper for regular files in mnemofs. + * + * Input Parameters: + * oldp - The existing VFS file structure. + * newp - The new VFS file structure to initialize. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_dup(FAR const struct file *oldp, FAR struct file *newp) +{ + FAR struct mfs_sb_s *sb; + int ret; + + sb = mnemofs_file_to_sb(oldp); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %d\n", ret); + return ret; + } + + ret = mfs_file_dup(sb, oldp, newp); + if (ret < 0) + { + ferr("mfs_file_dup failed: %d\n", ret); + } + + mnemofs_unlock(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_fstat + * + * Description: + * VFS fstat wrapper for regular files in mnemofs. + * + * Input Parameters: + * filep - The VFS file structure to inspect. + * buf - The location to receive the translated stat data. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_fstat(FAR const struct file *filep, FAR struct stat *buf) +{ + FAR struct mfs_sb_s *sb; + int ret; + + sb = mnemofs_file_to_sb(filep); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %d\n", ret); + return ret; + } + + ret = mfs_file_fstat(sb, filep, buf); + if (ret < 0) + { + ferr("mfs_file_fstat failed: %d\n", ret); + } + + mnemofs_unlock(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_opendir + * + * Description: + * VFS opendir wrapper for directories in mnemofs. + * + * Input Parameters: + * mountpt - The mountpoint inode. + * relpath - The relative path of the directory to open. + * dir - The location to receive the opened directory stream. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_opendir(FAR struct inode *mountpt, + FAR const char *relpath, + FAR struct fs_dirent_s **dir) +{ + FAR struct mfs_sb_s *sb; + int ret; + + sb = mnemofs_mountpt_to_sb(mountpt); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %d\n", ret); + return ret; + } + + ret = mfs_dir_opendir(sb, relpath, dir); + if (ret < 0) + { + ferr("mfs_dir_opendir failed: %d\n", ret); + } + + mnemofs_unlock(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_closedir + * + * Description: + * VFS closedir wrapper for directories in mnemofs. + * + * Input Parameters: + * mountpt - The mountpoint inode. + * dir - The directory stream to close. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_closedir(FAR struct inode *mountpt, + FAR struct fs_dirent_s *dir) +{ + FAR struct mfs_sb_s *sb; + int ret; + + sb = mnemofs_mountpt_to_sb(mountpt); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %d\n", ret); + return ret; + } + + ret = mfs_dir_closedir(sb, dir); + if (ret < 0) + { + ferr("mfs_dir_closedir failed: %d\n", ret); + } + + mnemofs_unlock(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_readdir + * + * Description: + * VFS readdir wrapper for directories in mnemofs. + * + * Input Parameters: + * mountpt - The mountpoint inode. + * dir - The directory stream to read. + * entry - The location to receive the next dirent record. + * + * Returned Value: + * Zero (OK) is returned on success. -ENOENT is returned at end of + * directory. A negated errno value is returned on other failures. + * + ****************************************************************************/ + +static int mnemofs_readdir(FAR struct inode *mountpt, + FAR struct fs_dirent_s *dir, + FAR struct dirent *entry) +{ + FAR struct mfs_sb_s *sb; + int ret; + + sb = mnemofs_mountpt_to_sb(mountpt); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %d\n", ret); + return ret; + } + + ret = mfs_dir_readdir(sb, dir, entry); + if (ret < 0 && ret != -ENOENT) + { + ferr("mfs_dir_readdir failed: %d\n", ret); + } + + mnemofs_unlock(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_rewinddir + * + * Description: + * VFS rewinddir wrapper for directories in mnemofs. + * + * Input Parameters: + * mountpt - The mountpoint inode. + * dir - The directory stream to rewind. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_rewinddir(FAR struct inode *mountpt, + FAR struct fs_dirent_s *dir) +{ + FAR struct mfs_sb_s *sb; + int ret; + + sb = mnemofs_mountpt_to_sb(mountpt); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %d\n", ret); + return ret; + } + + ret = mfs_dir_rewinddir(sb, dir); + if (ret < 0) + { + ferr("mfs_dir_rewinddir failed: %d\n", ret); + } + + mnemofs_unlock(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_unlink + * + * Description: + * VFS unlink wrapper for regular files in mnemofs. + * + * Input Parameters: + * mountpt - The mountpoint inode. + * relpath - The relative path of the file to remove. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_unlink(FAR struct inode *mountpt, FAR const char *relpath) +{ + FAR struct mfs_sb_s *sb; + int ret; + + sb = mnemofs_mountpt_to_sb(mountpt); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %d\n", ret); + return ret; + } + + ret = mfs_dir_unlink(sb, relpath); + if (ret < 0) + { + ferr("mfs_dir_unlink failed: %d\n", ret); + } + + mnemofs_unlock(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_mkdir + * + * Description: + * VFS mkdir wrapper for directories in mnemofs. + * + * Input Parameters: + * mountpt - The mountpoint inode. + * relpath - The relative path of the directory to create. + * mode - The requested directory mode. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_mkdir(FAR struct inode *mountpt, FAR const char *relpath, + mode_t mode) +{ + FAR struct mfs_sb_s *sb; + int ret; + + sb = mnemofs_mountpt_to_sb(mountpt); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %d\n", ret); + return ret; + } + + ret = mfs_dir_mkdir(sb, relpath, mode); + if (ret < 0) + { + ferr("mfs_dir_mkdir failed: %d\n", ret); + } + + mnemofs_unlock(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_rmdir + * + * Description: + * VFS rmdir wrapper for directories in mnemofs. + * + * Input Parameters: + * mountpt - The mountpoint inode. + * relpath - The relative path of the directory to remove. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_rmdir(FAR struct inode *mountpt, FAR const char *relpath) +{ + FAR struct mfs_sb_s *sb; + int ret; + + sb = mnemofs_mountpt_to_sb(mountpt); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %d\n", ret); + return ret; + } + + ret = mfs_dir_rmdir(sb, relpath); + if (ret < 0) + { + ferr("mfs_dir_rmdir failed: %d\n", ret); + } + + mnemofs_unlock(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_rename + * + * Description: + * VFS rename wrapper for file system objects in mnemofs. + * + * Input Parameters: + * mountpt - The mountpoint inode. + * oldrelpath - The current relative path. + * newrelpath - The replacement relative path. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_rename(FAR struct inode *mountpt, + FAR const char *oldrelpath, + FAR const char *newrelpath) +{ + FAR struct mfs_sb_s *sb; + int ret; + + sb = mnemofs_mountpt_to_sb(mountpt); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %d\n", ret); + return ret; + } + + ret = mfs_dir_rename(sb, oldrelpath, newrelpath); + if (ret < 0) + { + ferr("mfs_dir_rename failed: %d\n", ret); + } + + mnemofs_unlock(sb); + return ret; +} + +/**************************************************************************** + * Name: mnemofs_stat + * + * Description: + * VFS stat wrapper for file system objects in mnemofs. + * + * Input Parameters: + * mountpt - The mountpoint inode. + * relpath - The relative path to inspect. + * buf - The location to receive the translated stat data. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mnemofs_stat(FAR struct inode *mountpt, FAR const char *relpath, + FAR struct stat *buf) +{ + FAR struct mfs_sb_s *sb; + int ret; + + sb = mnemofs_mountpt_to_sb(mountpt); + ret = mnemofs_lock(sb); + if (ret < 0) + { + ferr("mnemofs_lock failed: %d\n", ret); + return ret; + } + + ret = mfs_dir_stat(sb, relpath, buf); + if (ret < 0) + { + ferr("mfs_dir_stat failed: %d\n", ret); + } + + mnemofs_unlock(sb); + return ret; +} + /**************************************************************************** * 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 */ @@ -191,21 +1935,17 @@ const struct mountpt_operations g_mnemofs_operations = NULL, /* poll */ NULL, /* readv */ NULL, /* writev */ - 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 */ @@ -213,2463 +1953,3 @@ const struct mountpt_operations g_mnemofs_operations = mnemofs_stat, /* stat */ NULL /* chstat */ }; - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: mnemofs_open - * - * Description: - * Open a file given its path and populate its file pointer. The file - * pointer's private field is filled with information about the open file. - * See `open(2)` for details on the work and parameters of this function. - * - * File pointers' private member have two parts...`mfs_ofd_d` is the actual - * entry for the file descriptor (fd), while the inner part is a reference - * counted shared portion formed from `mfs_ocom_s`. This way, multiple fds - * can point to the same file without problems. The shared portion is - * shared among the fds of the same file, while the upper part is different - * for each fd, regardless of whether they belong to the same file or not. - * A list of upper parts of files is stored in `sb` as a kernel linked - * list. - * - * Input Parameters: - * filep - File pointer. - * relpath - Relative path to the file from the mount root. - * oflags - Flags for opening the file. - * mode - Access Mode (User|Group|All) if creating the file. - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - ****************************************************************************/ - -static int mnemofs_open(FAR struct file *filep, FAR const char *relpath, - int oflags, mode_t mode) -{ - int ret = OK; - int flags; - FAR const char *child = NULL; - FAR struct inode *inode; - struct mfs_pitr_s pitr; - FAR struct mfs_sb_s *sb; - FAR struct mfs_ofd_s *f; - FAR struct mfs_ocom_s *fcom; - FAR struct mfs_dirent_s *dirent = NULL; - - MFS_LOG("OPEN", "Entry."); - MFS_LOG("OPEN", "Requested path is %s.", relpath); - MFS_LOG("OPEN", "Flags set as 0x%x.", oflags); - MFS_LOG("OPEN", "Mode set as 0x%x.", 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)) - { - MFS_LOG("OPEN", "Failed to acquire mutex."); - goto errout; - } - else - { - MFS_EXTRA_LOG("OPEN", "Mutex acquired."); - } - - f = fs_heap_zalloc(sizeof(*f)); - if (predict_false(f == NULL)) - { - MFS_LOG("OPEN", "Failed to allocate file structure."); - ret = -ENOMEM; - goto errout_with_lock; - } - else - { - MFS_EXTRA_LOG("OPEN", "Allocated file structure at %p.", f); - } - - fcom = fs_heap_zalloc(sizeof(*fcom)); - if (predict_false(fcom == NULL)) - { - MFS_LOG("OPEN", "Failed to allocate common file structure."); - ret = -ENOMEM; - goto errout_with_f; - } - else - { - MFS_EXTRA_LOG("OPEN", "Allocated common file structure at %p.", fcom); - } - - f->com = fcom; - f->com->refcount++; - - /* Check creation flags. */ - - flags = mfs_get_patharr(sb, relpath, &f->com->path, &f->com->depth); - MFS_EXTRA_LOG("OPEN", "Retrieved flags are 0x%x.", flags); - - if ((flags & MFS_EXIST) == 0) - { - MFS_EXTRA_LOG("OPEN", "Path doesn't exists."); - - if ((flags & MFS_P_ISDIR) != 0) - { - MFS_EXTRA_LOG("OPEN", "Parent is a directory."); - - if ((oflags & O_CREAT) != 0) - { - MFS_EXTRA_LOG("OPEN", "Creation flag is set."); - - /* 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_EXTRA_LOG("OPEN", "The final child is \"%s\".", child); - - mfs_pitr_appendnew(sb, f->com->path, f->com->depth, &pitr, - child, mode); - mfs_pitr_free(&pitr); - - MFS_EXTRA_LOG("OPEN", "Added child direntry."); - - /* OK */ - - MFS_EXTRA_LOG("OPEN", "All OK"); - } - } - else - { - MFS_LOG("OPEN", "Ancestor is not a directory."); - - ret = -ENOTDIR; - goto errout_with_fcom; - } - } - else - { - MFS_EXTRA_LOG("OPEN", "Path exists."); - - if ((flags & MFS_ISFILE) != 0) - { - MFS_EXTRA_LOG("OPEN", "Path points to a file."); - - /* NOTE: O_DIRECTORY is not supported. Use opendir. */ - - if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) - { - MFS_LOG("OPEN", "O_CREAT and O_EXCL flag are set."); - MFS_LOG("OPEN", "Operation failed as file exists with O_EXCL" - "flag set."); - - ret = -EEXIST; - goto errout_with_fcom; - } - else - { - /* OK */ - - MFS_EXTRA_LOG("OPEN", "All OK"); - } - } - else - { - MFS_EXTRA_LOG("OPEN", "Path points to a directory."); - - ret = -EISDIR; - goto errout_with_fcom; - } - } - - /* Check r/w permission flags. */ - - /* TODO: Update mtime and atime. */ - - mfs_pitr_init(sb, f->com->path, f->com->depth, &pitr, true); - mfs_pitr_readdirent(sb, f->com->path, &pitr, &dirent); - - if (dirent != NULL) - { - MFS_EXTRA_LOG_DIRENT(dirent); - - DEBUGASSERT((flags & MFS_EXIST) != 0); - - MFS_EXTRA_LOG("OPEN", "Direntry exists at %p.", dirent); - - if ((oflags & O_WRONLY) != 0 && (dirent->mode & O_WRONLY) == 0) - { - MFS_LOG("OPEN", "Write is not allowed."); - - ret = -EACCES; - goto errout_with_dirent; - } - - /* man page says: The argument flags must include one of the following - * access modes: O_RDONLY, O_WRONLY, or O_RDWR. - */ - - mfs_free_dirent(dirent); - dirent = NULL; - } - else - { - DEBUGASSERT((flags & MFS_EXIST) == 0); - } - - 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); - - MFS_EXTRA_LOG("OPEN", "Direntry processing is done."); - - /* Check Offset flags. */ - - if ((oflags & (O_TRUNC | O_WRONLY)) == (O_TRUNC | O_WRONLY) || - (oflags & (O_TRUNC | O_RDWR)) == (O_TRUNC | O_RDWR)) - { - /* Truncate to size 0. If write and truncate are mentioned only - * then it's truncated. Else, the truncate flag is ignored. - */ - - MFS_EXTRA_LOG("OPEN", "O_TRUNC is set."); - - ret = mfs_lru_del(sb, 0, f->com->sz, f->com->path, f->com->depth); - if (predict_false(ret < 0)) - { - MFS_LOG("OPEN", "Could not truncate file."); - goto errout_with_dirent; - } - else - { - MFS_EXTRA_LOG("OPEN", "File truncated."); - } - } - - if ((oflags & O_APPEND) != 0) - { - MFS_EXTRA_LOG("OPEN", "Append flag is set."); - f->com->off = f->com->sz; - } - - MFS_EXTRA_LOG_F(f); - - list_add_tail(&MFS_OFILES(sb), &f->list); - filep->f_priv = f; - MFS_EXTRA_LOG("OPEN", "File structure is set at %p.", filep); - - nxmutex_unlock(&MFS_LOCK(sb)); - MFS_EXTRA_LOG("OPEN", "Mutex released."); - - finfo("Mnemofs open exited with %d.", ret); - return ret; - -errout_with_dirent: - if (dirent != NULL) - { - mfs_free_dirent(dirent); - } - - mfs_free_patharr(fcom->path); - -errout_with_fcom: - fs_heap_free(fcom); - -errout_with_f: - fs_heap_free(f); - -errout_with_lock: - nxmutex_unlock(&MFS_LOCK(sb)); - MFS_EXTRA_LOG("OPEN", "Mutex released."); - -errout: - MFS_LOG("OPEN", "Exit | Return: %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; - - MFS_LOG("CLOSE", "Entry."); - MFS_LOG("CLOSE", "File structure is at %p.", filep); - - 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)) - { - MFS_LOG("CLOSE", "Failed to acquire mutex."); - goto errout; - } - else - { - MFS_EXTRA_LOG("CLOSE", "Mutex acquired."); - } - - f = filep->f_priv; - DEBUGASSERT(f != NULL); - - MFS_EXTRA_LOG_F(f); - - /* Flushing in-memory data to on-flash journal. */ - - f->com->refcount--; - - MFS_EXTRA_LOG("CLOSE", "Reference Counter updated."); - MFS_EXTRA_LOG_F(f); - - if (f->com->refcount == 0) - { - MFS_EXTRA_LOG("CLOSE", "Reference Counter is 0."); - - ret = mnemofs_flush(sb); - if (predict_false(ret < 0)) - { - MFS_LOG("CLOSE", "Could not flush file system."); - - /* This could be problematic. - * TODO: For now, this is same as no-op. - */ - - f->com->refcount++; /* Revert to old refcount. */ - goto errout_with_lock; - } - else - { - MFS_EXTRA_LOG("CLOSE", "File system flushed."); - } - - fs_heap_free(f->com->path); - MFS_EXTRA_LOG("CLOSE", "Freed file structure path."); - - fs_heap_free(f->com); - MFS_EXTRA_LOG("CLOSE", "Freed common file structure."); - } - - list_delete(&f->list); - MFS_EXTRA_LOG("CLOSE", "Removed file structure from open files list."); - - fs_heap_free(f); - MFS_EXTRA_LOG("CLOSE", "Freed file structure."); - - filep->f_priv = NULL; - -errout_with_lock: - nxmutex_unlock(&MFS_LOCK(sb)); - MFS_EXTRA_LOG("CLOSE", "Mutex released."); - -errout: - MFS_LOG("CLOSE", "Exit | Return: %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 - Bytes read. - * - ****************************************************************************/ - -static ssize_t mnemofs_read(FAR struct file *filep, FAR char *buffer, - size_t buflen) -{ - ssize_t ret = 0; - FAR struct inode *inode; - FAR struct mfs_sb_s *sb; - FAR struct mfs_ofd_s *f; - - MFS_LOG("READ", "Entry."); - MFS_LOG("READ", "File structure is at %p.", filep); - MFS_LOG("READ", "Buffer is at %p.", buffer); - MFS_LOG("READ", "Length of the buffer is %zu.", buflen); - - 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)) - { - MFS_LOG("READ", "Failed to acquire mutex."); - goto errout; - } - else - { - MFS_EXTRA_LOG("READ", "Mutex acquired."); - } - - f = filep->f_priv; - DEBUGASSERT(f != NULL); - - MFS_EXTRA_LOG_F(f); - - /* Check if allowed to read. */ - - if ((f->com->oflags & O_RDONLY) == 0) - { - MFS_EXTRA_LOG("READ", "Not allowed to read file."); - - ret = -EINVAL; - goto errout_with_lock; - } - - /* Read data in CTZ from the current offset. */ - - buflen = MIN(buflen, f->com->sz - f->com->off); /* TODO: Need to consider - * if this needs to be - * lower down the chain. - */ - - MFS_EXTRA_LOG("READ", "Final buffer length is %zu.", buflen); - - ret = mfs_lru_rdfromoff(sb, f->com->off, f->com->path, f->com->depth, - buffer, buflen); - if (ret < 0) - { - MFS_EXTRA_LOG("READ", "Could not read."); - goto errout_with_lock; - } - - ret = buflen; - - /* Update offset. */ - - f->com->off += buflen; - MFS_EXTRA_LOG("READ", "File structure offset updated."); - MFS_EXTRA_LOG_F(f); - -errout_with_lock: - nxmutex_unlock(&MFS_LOCK(sb)); - MFS_EXTRA_LOG("READ", "Mutex released."); - -errout: - MFS_LOG("READ", "Exit | Return: %zd.", 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 - Bytes written. - * - ****************************************************************************/ - -static ssize_t mnemofs_write(FAR struct file *filep, FAR const char *buffer, - size_t buflen) -{ - ssize_t ret = OK; - FAR struct inode *inode; - FAR struct mfs_sb_s *sb; - FAR struct mfs_ofd_s *f; - - MFS_LOG("WRITE", "Entry."); - MFS_LOG("WRITE", "File structure is at %p.", filep); - MFS_LOG("WRITE", "Buffer is at %p.", buffer); - MFS_LOG("WRITE", "Length of the buffer is %zu.", buflen); - - 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)) - { - MFS_LOG("WRITE", "Failed to acquire mutex."); - goto errout; - } - else - { - MFS_EXTRA_LOG("WRITE", "Mutex acquired."); - } - - f = filep->f_priv; - DEBUGASSERT(f != NULL); - - MFS_EXTRA_LOG_F(f); - - /* Check if allowed to write. */ - - if ((f->com->oflags & O_WRONLY) == 0) - { - MFS_EXTRA_LOG("WRITE", "Write not allowed."); - ret = -EINVAL; - goto errout_with_lock; - } - - /* Write data to CTZ at the current offset. */ - - ret = mfs_lru_wr(sb, f->com->off, buflen, f->com->path, f->com->depth, - buffer); - if (predict_false(ret < 0)) - { - MFS_LOG("WRITE", "Could not write to LRU."); - goto errout_with_lock; - } - else - { - MFS_EXTRA_LOG("WRITE", "Completed writing to LRU."); - } - - /* Update offset and size. */ - - f->com->off += buflen; - f->com->sz = MAX(f->com->sz, f->com->off); - ret = buflen; - - MFS_EXTRA_LOG("WRITE", "Updated file offset and size."); - MFS_EXTRA_LOG_F(f); - -errout_with_lock: - nxmutex_unlock(&MFS_LOCK(sb)); - MFS_EXTRA_LOG("WRITE", "Mutex released."); - -errout: - MFS_LOG("WRITE", "Exit | Return: %zd.", 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 committing 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 - Final position. - * - ****************************************************************************/ - -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; - - MFS_LOG("SEEK", "Entry."); - MFS_LOG("SEEK", "Offset: %u, Whence: %d", offset, whence); - - inode = filep->f_inode; - DEBUGASSERT(inode != NULL); - sb = inode->i_private; - DEBUGASSERT(sb != NULL); - - ret = nxmutex_lock(&MFS_LOCK(sb)); - if (ret < 0) - { - MFS_LOG("SEEK", "Failed to acquire mutex."); - goto errout; - } - else - { - MFS_EXTRA_LOG("SEEK", "Mutex acquired."); - } - - f = filep->f_priv; - DEBUGASSERT(f != NULL); - - MFS_EXTRA_LOG("SEEK", "File offset: %u, Command offset: %u, 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; - } - - MFS_EXTRA_LOG("SEEK", "Proposed final position: %" PRIu32, pos); - - /* Check bounds of the position data type. */ - - if (pos > f->com->off && offset < 0) - { - MFS_LOG("SEEK", "Proposed final position (%" PRIu32 ") out of bounds.", - pos); - ret = -EINVAL; - goto errout_with_lock; - } - - f->com->off = pos; - ret = pos; - - MFS_EXTRA_LOG("SEEK", "Final position: %" PRIu32, pos); - -errout_with_lock: - nxmutex_unlock(&MFS_LOCK(sb)); - MFS_EXTRA_LOG("SEEK", "Mutex released."); - -errout: - MFS_LOG("SEEK", "Exit | Return: %d.", ret); - 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 %" PRIu32 "and arg %zu.", cmd, arg); - - inode = filep->f_inode; - sb = inode->i_private; - drv = sb->drv; - - ret = nxmutex_lock(&MFS_LOCK(sb)); - if (ret < 0) - { - goto errout; - } - - finfo("Lock acquired."); - - if (INODE_IS_MTD(drv)) - { - ret = MTD_IOCTL(drv->u.i_mtd, cmd, arg); - } - else - { - finfo("Not an MTD driver."); - ret = -ENOTTY; - } - - nxmutex_unlock(&MFS_LOCK(sb)); - finfo("Lock released."); - -errout: - finfo("Mnemofs ioctl exited with %d.", ret); - return ret; -} - -/**************************************************************************** - * Name: mnemofs_truncate - * - * Description: - * Truncates file specified to specified length. See `truncate(2)` for - * details on the work and parameters of this function. - * - * Input Parameters: - * filep - File pointer. - * length - Final size of the truncated file. - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - * Assumptions/Limitations: - * If the length specified is bigger than the file, it won't increase the - * file size in itself. Only when there is a subsequent write to the offset - * will there be an increase in the file size. This can be changed, but is - * kept for efficiency. - * - ****************************************************************************/ - -static int mnemofs_truncate(FAR struct file *filep, off_t length) -{ - int ret = OK; - FAR struct inode *inode; - FAR struct mfs_sb_s *sb; - FAR struct mfs_ofd_s *f; - - finfo("Mnemofs truncate to length %d.", length); - - inode = filep->f_inode; - DEBUGASSERT(inode != NULL); - sb = inode->i_private; - DEBUGASSERT(sb != NULL); - - ret = nxmutex_lock(&MFS_LOCK(sb)); - if (ret < 0) - { - goto errout; - } - - finfo("Lock acquired."); - - f = filep->f_priv; - DEBUGASSERT(f != NULL); - - if (length < f->com->sz) - { - ret = mfs_lru_del(sb, length, f->com->sz - length, f->com->path, - f->com->depth); - if (predict_false(ret < 0)) - { - finfo("Error during truncate. Ret: %d.", ret); - goto errout_with_lock; - } - } - -errout_with_lock: - nxmutex_unlock(&MFS_LOCK(sb)); - finfo("Lock released."); - -errout: - finfo("Mnemofs truncate exited with %d.", ret); - return ret; -} - -/**************************************************************************** - * Name: mnemofs_sync - * - * Description: - * Causes all pending modifications that are in memory for a file - * descriptor to be written to the device. See `sync(2)` for details on - * the work and parameters of this function. - * - * For mnemofs, this means that the LRU entry for the file corresponding to - * the fd is committed to the journal. - * - * Input Parameters: - * filep - File pointer. - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - ****************************************************************************/ - -static int mnemofs_sync(FAR struct file *filep) -{ - int ret = OK; - FAR struct inode *inode; - FAR struct mfs_sb_s *sb; - FAR struct mfs_ofd_s *f; - - finfo("Mnemofs sync."); - - inode = filep->f_inode; - DEBUGASSERT(inode != NULL); - sb = inode->i_private; - DEBUGASSERT(sb != NULL); - - ret = nxmutex_lock(&MFS_LOCK(sb)); - if (ret < 0) - { - goto errout; - } - - finfo("Lock acquired."); - - f = filep->f_priv; - DEBUGASSERT(f != NULL); - - ret = mnemofs_flush(sb); - - 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 = fs_heap_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(&MFS_OFILES(sb), &nf->list); - newp->f_priv = nf; - 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: - fs_heap_free(nf); - -errout_with_lock: - nxmutex_unlock(&MFS_LOCK(sb)); - finfo("Lock released."); - -errout: - finfo("Mnemofs dup exited with %d.", ret); - return ret; -} - -/**************************************************************************** - * Name: mnemofs_fstat - * - * Description: - * Get file's status. See `fstat(2)` for details on the work and - * parameters of this function. - * - * In mnemofs, most of the metadata of a file is stored in the directory - * entry of the file inside the parent directories. The directory entries - * are stored in the form of a CTZ, and this is what a directory refers to. - * - * Input Parameters: - * filep - File pointer. - * buf - Stat result. - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - ****************************************************************************/ - -static int mnemofs_fstat(FAR const struct file *filep, FAR struct stat *buf) -{ - int ret = OK; - struct mfs_pitr_s pitr; - FAR struct inode *inode; - FAR struct mfs_sb_s *sb; - FAR struct mfs_ofd_s *f; - FAR struct mfs_dirent_s *dirent = NULL; - - finfo("Mnemofs fstat."); - - inode = filep->f_inode; - DEBUGASSERT(inode != NULL); - sb = inode->i_private; - DEBUGASSERT(sb != NULL); - - ret = nxmutex_lock(&MFS_LOCK(sb)); - if (ret < 0) - { - goto errout; - } - - finfo("Lock acquired."); - - f = filep->f_priv; - DEBUGASSERT(f != NULL); - - mfs_pitr_init(sb, f->com->path, f->com->depth, &pitr, true); - mfs_pitr_adv_tochild(&pitr, f->com->path); - mfs_pitr_readdirent(sb, f->com->path, &pitr, &dirent); - mfs_pitr_free(&pitr); - - DEBUGASSERT(dirent != NULL); - - buf->st_mode = dirent->mode; - buf->st_nlink = 1; /* mnemofs doesn't yet support links. */ - buf->st_size = dirent->sz; - buf->st_atim = dirent->st_atim; - buf->st_ctim = dirent->st_ctim; - buf->st_blksize = sb->pg_sz; - buf->st_blocks = ((dirent->sz + (sb->pg_sz - 1)) / sb->pg_sz); /* Ceil */ - - mfs_free_dirent(dirent); - - nxmutex_unlock(&MFS_LOCK(sb)); - finfo("Lock released."); - -errout: - finfo("Mnemofs fstat exited with %d.", ret); - return ret; -} - -/**************************************************************************** - * Name: mnemofs_opendir - * - * Description: - * Opens a directory for traversal. See `opendir(3)` for details on the - * work and parameters of this function. - * - * In mnemofs, the directories are in the form of a CTZ list like files, - * but the content in the list is the directory entries of its children. - * There is mfs_fsdirent->count which exists due to the fact that the first - * entry in readdir is `.`, second is `..` and then the actual contents. To - * save space, this count never goes above 2 because it's not needed as - * above 2, the offset in the CTZ list is sufficient for traversal. - * - * Input Parameters: - * mountpt - Mount point of the file system. - * relpath - Relative path of the directory. - * dir - To populate this with open directory data structure. - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - * Assumptions/Limitations: - * There is no attempt to `unlink` or `rmdir` when a directory is open. - * This will mess up the iterator, and mnemofs does not yet store a list - * of open directories to prevent unlink or rmdir when open or something - * like that. This is yet to be tested if mnemofs can handle such a case. - * This bug is marked in code in `readdir`. - * - ****************************************************************************/ - -static int mnemofs_opendir(FAR struct inode *mountpt, - FAR const char *relpath, - FAR struct fs_dirent_s **dir) -{ - int ret = OK; - int flags; - mfs_t depth; - FAR struct mfs_sb_s *sb; - FAR struct mfs_path_s *path; - FAR struct mfs_pitr_s *pitr; - FAR struct mfs_fsdirent_s *fsdirent; - - MFS_LOG("OPENDIR", "Entry."); - MFS_LOG("OPENDIR", "Requested path is \"%s\".", relpath); - - DEBUGASSERT(mountpt != NULL); - sb = mountpt->i_private; - DEBUGASSERT(mountpt->i_private); - - ret = nxmutex_lock(&MFS_LOCK(sb)); - if (ret < 0) - { - MFS_LOG("OPENDIR", "Failed to acquire mutex."); - goto errout; - } - else - { - MFS_EXTRA_LOG("OPENDIR", "Mutex acquired."); - } - - flags = mfs_get_patharr(sb, relpath, &path, &depth); - if ((flags & MFS_ISDIR) == 0) - { - MFS_LOG("OPENDIR", "Not a directory."); - ret = -ENOTDIR; - goto errout_with_lock; - } - else - { - MFS_EXTRA_LOG("OPENDIR", "Path is at %p.", path); - MFS_EXTRA_LOG("OPENDIR", "Path is at %" PRIu32, depth); - MFS_EXTRA_LOG("OPENDIR", "Retrieved flags is %d.", flags); - } - - ret = mfs_lru_getupdatedinfo(sb, path, depth); - if (predict_false(ret < 0)) - { - MFS_LOG("OPENDIR", "Failed to get updated information from LRU."); - goto errout_with_path; - } - else - { - MFS_EXTRA_LOG("OPENDIR", "Got updated information from LRU."); - } - - pitr = fs_heap_zalloc(sizeof(*pitr)); - if (predict_false(pitr == NULL)) - { - MFS_LOG("OPENDIR", "Could not allocate space for pitr."); - ret = -ENOMEM; - goto errout_with_path; - } - else - { - MFS_EXTRA_LOG("OPENDIR", "Space allocated for pitr at %p.", pitr); - } - - fsdirent = fs_heap_zalloc(sizeof(*fsdirent)); - if (predict_false(fsdirent == NULL)) - { - MFS_LOG("OPENDIR", "Could not allocate space for FS Direntry."); - ret = -ENOMEM; - goto errout_with_pitr; - } - else - { - MFS_EXTRA_LOG("OPENDIR", "Space allocated for FS Direntry at %p.", - fsdirent); - } - - ret = mfs_pitr_init(sb, path, depth, pitr, false); - if (predict_false(ret < 0)) - { - MFS_LOG("OPENDIR", "Failed to initialize pitr."); - goto errout_with_fsdirent; - } - else - { - MFS_EXTRA_LOG("OPENDIR", "Pitr initialized successfully."); - } - - fsdirent->idx = 0; - fsdirent->path = path; - fsdirent->depth = depth; - fsdirent->pitr = pitr; - - MFS_EXTRA_LOG_FSDIRENT(fsdirent); - - *dir = (FAR struct fs_dirent_s *) fsdirent; - MFS_EXTRA_LOG("OPENDIR", "Directory structure is %p.", dir); - - nxmutex_unlock(&MFS_LOCK(sb)); - MFS_EXTRA_LOG("OPENDIR", "Mutex released."); - - MFS_LOG("OPENDIR", "Exit | Return: %d.", ret); - return ret; - -errout_with_fsdirent: - fs_heap_free(fsdirent); - -errout_with_pitr: - fs_heap_free(pitr); - -errout_with_path: - mfs_free_patharr(path); - -errout_with_lock: - nxmutex_unlock(&MFS_LOCK(sb)); - MFS_EXTRA_LOG("OPENDIR", "Mutex released."); - -errout: - MFS_LOG("OPENDIR", "Exit | Return: %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_s *fsdirent = (struct mfs_fsdirent_s *) dir; - - MFS_LOG("CLOSEDIR", "Entry."); - MFS_LOG("CLOSEDIR", "FS Direntry at %p.", fsdirent); - - MFS_EXTRA_LOG_FSDIRENT(fsdirent); - - mfs_free_patharr(fsdirent->path); - mfs_pitr_free(fsdirent->pitr); - fs_heap_free(fsdirent->pitr); - fs_heap_free(fsdirent); - - MFS_LOG("CLOSEDIR", "Exit | Return: %d.", OK); - 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_s *fsdirent = (FAR struct mfs_fsdirent_s *) dir; - - MFS_LOG("READDIR", "Entry."); - MFS_LOG("READDIR", "FS Direntry at %p.", fsdirent); - MFS_EXTRA_LOG_FSDIRENT(fsdirent); - - DEBUGASSERT(mountpt != NULL); - sb = mountpt->i_private; - DEBUGASSERT(sb != NULL); - - ret = nxmutex_lock(&MFS_LOCK(sb)); - if (ret < 0) - { - MFS_LOG("READDIR", "Failed to acquire mutex."); - goto errout; - } - else - { - MFS_EXTRA_LOG("READDIR", "Mutex acquired."); - } - - MFS_EXTRA_LOG("READDIR", "Current direntry index is %" PRIu8, - fsdirent->idx); - - if (fsdirent->idx == 0) - { - /* . */ - - MFS_EXTRA_LOG("READDIR", "Direntry for \".\""); - - snprintf(entry->d_name, NAME_MAX + 1, "."); - entry->d_type = DTYPE_DIRECTORY; - fsdirent->idx++; - - MFS_EXTRA_LOG("READDIR", "Direntry index updated to %" PRIu8, - fsdirent->idx); - goto errout_with_lock; - } - else if (fsdirent->idx == 1) - { - /* .. */ - - MFS_EXTRA_LOG("READDIR", "Direntry for \"..\""); - - snprintf(entry->d_name, NAME_MAX + 1, ".."); - entry->d_type = DTYPE_DIRECTORY; - fsdirent->idx++; - - MFS_EXTRA_LOG("READDIR", "Direntry index updated to %" PRIu8, - fsdirent->idx); - goto errout_with_lock; - } - else - { - MFS_EXTRA_LOG("READDIR", "Direntry for regular directory items."); - } - - /* TODO: Need to think why *exactly* below line is needed. The LRU node - * seems to contain wrong size during opendir, but updating it here - * updates it to correct size, even though this updatedinfo is also - * called in opendir? - */ - - ret = mfs_lru_getupdatedinfo(sb, fsdirent->path, fsdirent->depth); - if (predict_false(ret < 0)) - { - MFS_LOG("READDIR", "Failed to get updated information from LRU."); - goto errout_with_lock; - } - else - { - MFS_EXTRA_LOG("READDIR", "Got updated information from LRU."); - } - - ret = mfs_pitr_readdirent(sb, fsdirent->path, fsdirent->pitr, &dirent); - if (predict_false(ret < 0)) - { - MFS_LOG("READDIR", "Could not read direntry."); - goto errout_with_lock; - } - else if (dirent == NULL) - { - MFS_LOG("READDIR", "No more direntries left."); - MFS_LOG("READDIR", "End of directory."); - - ret = -ENOENT; - goto errout_with_lock; - } - else - { - MFS_LOG("READDIR", "Direntry retrieved."); - MFS_EXTRA_LOG_DIRENT(dirent); - } - - memset(entry->d_name, 0, NAME_MAX + 1); - MFS_EXTRA_LOG("READDIR", "Resetting entry name."); - - memcpy(entry->d_name, dirent->name, dirent->namelen); - MFS_EXTRA_LOG("READDIR", "Setting entry name to be \"%.*s\".", - dirent->namelen, dirent->name); - - entry->d_type = (S_ISDIR(dirent->mode) ? DTYPE_DIRECTORY: DTYPE_FILE); - MFS_EXTRA_LOG("READDIR", "Setting entry d_type to %" PRIu8, entry->d_type); - - mfs_pitr_adv_bydirent(fsdirent->pitr, dirent); - mfs_free_dirent(dirent); - -errout_with_lock: - nxmutex_unlock(&MFS_LOCK(sb)); - MFS_EXTRA_LOG("READDIR", "Mutex released."); - -errout: - MFS_LOG("READDIR", "Exit | Return: %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_s *fsdirent = (struct mfs_fsdirent_s *) dir; - - MFS_LOG("REWINDDIR", "Entry."); - MFS_LOG("REWINDDIR", "FS Direntry at %p.", fsdirent); - MFS_EXTRA_LOG_FSDIRENT(fsdirent); - - DEBUGASSERT(mountpt != NULL); - sb = mountpt->i_private; - DEBUGASSERT(sb != NULL); - - ret = nxmutex_lock(&MFS_LOCK(sb)); - if (ret < 0) - { - MFS_LOG("REWINDDIR", "Failed to acquire mutex."); - goto errout; - } - else - { - MFS_EXTRA_LOG("REWINDDIR", "Mutex acquired."); - } - - mfs_pitr_reset(fsdirent->pitr); - - fsdirent->idx = 0; - MFS_EXTRA_LOG("REWINDDIR", "Direntry index reset to 0."); - - MFS_EXTRA_LOG_FSDIRENT(fsdirent); - - nxmutex_unlock(&MFS_LOCK(sb)); - MFS_EXTRA_LOG("REWINDDIR", "Mutex released."); - -errout: - MFS_LOG("REWINDDIR", "Exit | Return: %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 yet. It does not have - * 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; - mfs_t j = 0; - mfs_t mnblk1; - mfs_t mnblk2; - mfs_t jrnl_blk; - FAR struct mfs_sb_s *sb = NULL; - struct mtd_geometry_s geo; - - MFS_LOG("BIND", "Entry."); - - MFS_EXTRA_LOG("BIND", "Resetting temporary buffer."); - memset(buf, 0, 8); - - MFS_EXTRA_LOG("BIND", "Allocating superblock in memory."); - sb = fs_heap_zalloc(sizeof(*sb)); - if (!sb) - { - MFS_LOG("BIND", "SB in-memory allocation error."); - ret = -ENOMEM; - goto errout; - } - else - { - MFS_EXTRA_LOG("BIND", "Superblock allocated at %p", sb); - } - - /* Currently only supports NAND flashes (MTD devices). */ - - if (driver && INODE_IS_MTD(driver)) - { - if (!driver || !driver->u.i_mtd || !driver->u.i_mtd->ioctl) - { - MFS_LOG("BIND", "Unsupported device."); - ret = -ENODEV; - goto errout_with_sb; - } - else - { - MFS_EXTRA_LOG("BIND", "Device is of MTD type."); - } - - ret = MTD_IOCTL(driver->u.i_mtd, MTDIOC_GEOMETRY, - (unsigned long) &geo); - - MFS_LOG("BIND", "MTD Driver Geometry read."); - MFS_EXTRA_LOG("BIND", "MTD Driver Geometry details." - " Page size: %d, Block size: %d," - " Pages/Block: %d, Blocks: %d\n", - geo.blocksize, geo.erasesize, - geo.erasesize / geo.blocksize, geo.neraseblocks); - } - else - { - MFS_LOG("BIND", "Device is not an MTD device."); - ret = -ENODEV; - goto errout_with_sb; - } - - ret = nxmutex_init(&MFS_LOCK(sb)); - if (predict_false(ret < 0)) - { - MFS_LOG("BIND", "FS-wide Mutex failed to initialize."); - goto errout_with_sb; - } - else - { - MFS_EXTRA_LOG("BIND", "FS-wide Mutex Initialized."); - } - - ret = nxmutex_lock(&MFS_LOCK(sb)); - if (ret < 0) - { - MFS_LOG("BIND", "Mutex failed to lock. Return %d.", ret); - goto errout_with_lockinit; - } - else - { - MFS_EXTRA_LOG("BIND", "Mutex 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 - MFS_JRNL(sb).n_blks = CONFIG_MNEMOFS_JOURNAL_NBLKS; -#else - MFS_JRNL(sb).n_blks = 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)); - MFS_FLUSH(sb) = false; - - list_initialize(&MFS_OFILES(sb)); - - MFS_EXTRA_LOG("BIND", "SB initialized in-memory."); - MFS_EXTRA_LOG("BIND", "SB Details."); - MFS_EXTRA_LOG("BIND", "\tDriver: %p", driver); - MFS_EXTRA_LOG("BIND", "\tPage Size: %" PRIu32, sb->pg_sz); - MFS_EXTRA_LOG("BIND", "\tLog Page Size: %" PRIu8, sb->log_pg_sz); - MFS_EXTRA_LOG("BIND", "\tBlock Size: %" PRIu32, sb->blk_sz); - MFS_EXTRA_LOG("BIND", "\tLog Block Size: %" PRIu8, - sb->log_blk_sz); - MFS_EXTRA_LOG("BIND", "\tPages Per Block: %" PRIu16, - sb->pg_in_blk); - MFS_EXTRA_LOG("BIND", "\tBlocks: %" PRIu32, sb->n_blks); - MFS_EXTRA_LOG("BIND", "\tLog Blocks: %" PRIu8, sb->log_n_blks); - MFS_EXTRA_LOG("BIND", "\tJournal Blocks: %" PRIu16, - MFS_JRNL(sb).n_blks); - MFS_EXTRA_LOG("BIND", "\tFlush State: %" PRIu8, MFS_FLUSH(sb)); - - sb->rw_buf = fs_heap_zalloc(MFS_PGSZ(sb)); - if (predict_false(sb->rw_buf == NULL)) - { - MFS_LOG("BIND", "RW Buffer in-memory allocation error."); - goto errout_with_lock; - } - else - { - MFS_EXTRA_LOG("BIND", "RW Buffer allocated."); - } - - /* TODO: Format the superblock in Block 0. */ - - srand(time(NULL)); - - if (!MFS_STRLITCMP(data, "autoformat")) - { - MFS_LOG("BIND", "Autoformat is ON."); - - /* Look for journal and maybe hopefully, the master node - * if it comes first. - */ - - MFS_LOG("BIND", "Checking for valid mnemofs formatting."); - - for (i = 0; i < MFS_NBLKS(sb); i++) - { - MFS_EXTRA_LOG("BIND", "Checking start of Block %" PRIu32, - i + 1); - mfs_read_page(sb, buf, 8, MFS_BLK2PG(sb, i), 0); - - for (j = 0; j < 8; j++) - { - MFS_EXTRA_LOG("BIND", "\tBlock %" PRIu32 - ", Offset %" PRIu32 ": %x", i, j, buf[j]); - } - - if (!MFS_STRLITCMP(buf, MFS_JRNL_MAGIC)) - { - MFS_LOG("BIND", "Found Journal at Block %" PRIu32, - i + 1); - - ret = mfs_jrnl_init(sb, i); - if (predict_false(ret < 0)) - { - MFS_LOG("BIND", "Error initializing journal."); - goto errout_with_rwbuf; - } - else - { - MFS_LOG("BIND", "Journal initialized."); - } - - ret = mfs_mn_init(sb, i); - if (predict_false(ret < 0)) - { - MFS_LOG("BIND", "Error initializing masternode."); - goto errout_with_rwbuf; - } - else - { - MFS_LOG("BIND", "Master node initialized."); - } - - break; - } - - MFS_EXTRA_LOG("BIND", "Resetting temporary buffer."); - memset(buf, 0, 8); - } - - if (predict_false(sb->mn.pg == 0)) - { - MFS_LOG("BIND", "Journal not found on device."); - MFS_LOG("BIND", "Device needs formatting."); - - format = true; - memset(&MFS_JRNL(sb), 0, sizeof(struct mfs_jrnl_state_s)); - memset(&MFS_MN(sb), 0, sizeof(struct mfs_mn_s)); - } - else - { - MFS_LOG("BIND", "Device already formatted."); - - mfs_lru_init(sb); - mfs_ba_init(sb); - } - } - - if (format || !strncmp(data, "forceformat", 12)) - { - /* Format. */ - - if (format) - { - MFS_LOG("BIND", "Device format necessary."); - } - else - { - MFS_EXTRA_LOG("BIND", "Device formatting configured."); - } - - mfs_ba_fmt(sb); - mfs_lru_init(sb); - - mnblk1 = 0; - mnblk2 = 0; - - ret = mfs_jrnl_fmt(sb, &mnblk1, &mnblk2, &jrnl_blk); - if (predict_false(ret < 0)) - { - MFS_LOG("BIND", "Error formatting Journal"); - goto errout_with_rwbuf; - } - else - { - MFS_LOG("BIND", "Journal format completed."); - } - - ret = mfs_mn_fmt(sb, mnblk1, mnblk2, jrnl_blk); - if (predict_false(ret < 0)) - { - MFS_LOG("BIND", "Error formatting Master Node"); - goto errout_with_rwbuf; - } - else - { - MFS_LOG("BIND", "Master node format completed."); - } - - MFS_LOG("BIND", "Device formatted."); - } - - *handle = (FAR void *)sb; - MFS_LOG("BIND", "Mount Successful. Super Block %p.", sb); - - nxmutex_unlock(&MFS_LOCK(sb)); - MFS_LOG("BIND", "Mutex released."); - - MFS_LOG("BIND", "Exit | Return: %d.", ret); - return ret; - -errout_with_rwbuf: - fs_heap_free(sb->rw_buf); - MFS_LOG("BIND", "RW Buffer freed."); - -errout_with_lock: - nxmutex_unlock(&MFS_LOCK(sb)); - MFS_EXTRA_LOG("BIND", "Mutex released."); - -errout_with_sb: - fs_heap_free(sb); - MFS_LOG("BIND", "Superblock freed."); - -errout_with_lockinit: - nxmutex_destroy(&MFS_LOCK(sb)); - MFS_EXTRA_LOG("BIND", "Mutex destroyed."); - -errout: - MFS_LOG("BIND", "Exit | Return: %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; - - MFS_LOG("UNBIND", "Entry."); - - DEBUGASSERT(handle); - sb = handle; - MFS_LOG("UNBIND", "Superblock %p.", sb); - - *driver = sb->drv; - MFS_LOG("UNBIND", "Driver %p.", driver); - - mfs_jrnl_free(sb); - mfs_ba_free(sb); - - nxmutex_destroy(&MFS_LOCK(sb)); - MFS_EXTRA_LOG("UNBIND", "Mutex destroyed."); - - fs_heap_free(sb->rw_buf); - MFS_LOG("UNBIND", "RW Buffer freed."); - - fs_heap_free(sb); - MFS_LOG("UNBIND", "Superblock freed."); - - MFS_LOG("UNBIND", "Exit."); - 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 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."); - - flags = mfs_get_patharr(sb, relpath, &path, &depth); - if ((flags & MFS_ISFILE) == 0) - { - ret = -EISDIR; - goto errout_with_lock; - } - - mfs_pitr_rm(sb, path, depth, true); - - mfs_free_patharr(path); - -errout_with_lock: - nxmutex_unlock(&MFS_LOCK(sb)); - finfo("Lock released."); - -errout: - finfo("Mnemofs unlink exited with %d.", ret); - return ret; -} - -/**************************************************************************** - * Name: mnemofs_mkdir - * - * Description: - * Create a directory at given relpath. See `mkdir(2)` for more - * information. - * - * In mnemofs, the directory entry of the file is appended to its parent's - * directory file (CTZ list). - * - * Input Parameters: - * mountpt - Mount point of the file system. - * relpath - Relative path of the new directory. - * mode - Mode of the new directory (ACL). - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - ****************************************************************************/ - -static int mnemofs_mkdir(FAR struct inode *mountpt, FAR const char *relpath, - mode_t mode) -{ - int ret = OK; - int flags; - mfs_t depth; - struct mfs_pitr_s pitr; - FAR struct mfs_sb_s *sb; - FAR struct mfs_path_s *path; - - MFS_LOG("MKDIR", "Entry."); - MFS_LOG("MKDIR", "New directory at \"%s\".", relpath); - - mode |= S_IFDIR; - - MFS_LOG("MKDIR", "Mode is 0x%x.", mode); - - DEBUGASSERT(mountpt != NULL); - sb = mountpt->i_private; - MFS_EXTRA_LOG("MKDIR", "Superblock is %p.", sb); - DEBUGASSERT(sb != NULL); - - ret = nxmutex_lock(&MFS_LOCK(sb)); - if (ret < 0) - { - goto errout; - } - else - { - MFS_EXTRA_LOG("MKDIR", "Mutex lock acquired."); - } - - flags = mfs_get_patharr(sb, relpath, &path, &depth); - MFS_EXTRA_LOG("MKDIR", "Retrieved flags are 0x%x.", flags); - MFS_EXTRA_LOG("MKDIR", "Path received is at %p.", path); - MFS_EXTRA_LOG("MKDIR", "Depth of path is %" PRIu32 ".", depth); - - if ((flags & MFS_EXIST) != 0) - { - MFS_LOG("MKDIR", "The requested directory already exists."); - ret = -EEXIST; - goto errout_with_path; - } - else - { - MFS_LOG("MKDIR", "The requested directory does not exist."); - if ((flags & MFS_P_EXIST) != 0) - { - MFS_EXTRA_LOG("MKDIR", "Parent exists."); - if ((flags & MFS_P_ISDIR) != 0) - { - /* OK */ - - MFS_EXTRA_LOG("MKDIR", "Parent is all right."); - } - else - { - MFS_EXTRA_LOG("MKDIR", "Ancestor is not a directory."); - ret = -ENOTDIR; - goto errout_with_path; - } - } - else - { - MFS_EXTRA_LOG("MKDIR", "Parent not found."); - ret = -ENOENT; - goto errout_with_path; - } - } - - memset(&path[depth - 1], 0, sizeof(struct mfs_path_s)); - MFS_EXTRA_LOG("MKDIR", "Resetting, at index %u, path array %p.", - depth - 1, path); - - mfs_pitr_init(sb, path, depth, &pitr, true); - MFS_EXTRA_LOG("MKDIR", "The path contains the child."); - MFS_EXTRA_LOG("MKDIR", "Parent iterator initialized."); - MFS_EXTRA_LOG("MKDIR", "\tDepth of parent %" PRIu32 ".", - pitr.depth); - MFS_EXTRA_LOG("MKDIR", "\tCurrent iteration offset %" PRIu32 ".", - pitr.c_off); - MFS_EXTRA_LOG("MKDIR", "\tParent's offset %" PRIu32 ".", - pitr.p.off); - MFS_EXTRA_LOG("MKDIR", "\tParent's size %" PRIu32 ".", pitr.p.sz); - MFS_EXTRA_LOG("MKDIR", "\tParent's CTZ (%" PRIu32 ", %" PRIu32 ")" - , pitr.p.ctz.idx_e, pitr.p.ctz.pg_e); - - /* The last incomplete direntry will be added by mfs_pitr_appendnew. */ - - ret = mfs_pitr_appendnew(sb, path, depth, &pitr, relpath, mode); - if (predict_false(ret < 0)) - { - MFS_LOG("MKDIR", "Could not append direntry. Return %d", ret); - goto errout_with_path; - } - else - { - MFS_EXTRA_LOG("MKDIR", "Direntry append successful."); - MFS_EXTRA_LOG("MKDIR", "\tDepth of parent %" PRIu32 ".", pitr.depth); - MFS_EXTRA_LOG("MKDIR", "\tCurrent iteration offset %" PRIu32 ".", - pitr.c_off); - MFS_EXTRA_LOG("MKDIR", "\tParent's offset %" PRIu32 ".", pitr.p.off); - MFS_EXTRA_LOG("MKDIR", "\tParent's size %" PRIu32 ".", pitr.p.sz); - MFS_EXTRA_LOG("MKDIR", "\tParent's CTZ (%" PRIu32 ", %" PRIu32 ")", - pitr.p.ctz.idx_e, pitr.p.ctz.pg_e); - } - - mfs_pitr_free(&pitr); - - MFS_LOG("MKDIR", "Directory created at \"%s\".", relpath); - - mfs_free_patharr(path); - - nxmutex_unlock(&MFS_LOCK(sb)); - MFS_EXTRA_LOG("MKDIR", "Mutex released."); - - MFS_LOG("MKDIR", "Exit | Return: %d.", ret); - return ret; - -errout_with_path: - mfs_free_patharr(path); - - mfs_pitr_free(&pitr); - - nxmutex_unlock(&MFS_LOCK(sb)); - MFS_EXTRA_LOG("MKDIR", "Mutex released."); - - /* TODO: The flush operation does not work properly, and causes memory - * leaks by most likely not flushing out anything and keeping it in - * memory. - */ - -errout: - MFS_LOG("MKDIR", "Exit | Return: %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; - - MFS_LOG("RMDIR", "Entry"); - MFS_LOG("RMDIR", "Directory \"%s\" to be removed.", relpath); - - DEBUGASSERT(mountpt != NULL); - sb = mountpt->i_private; - DEBUGASSERT(sb != NULL); - - ret = nxmutex_lock(&MFS_LOCK(sb)); - if (ret < 0) - { - MFS_LOG("RMDIR", "Mutex could not be acquired."); - goto errout; - } - else - { - MFS_EXTRA_LOG("RMDIR", "Mutex acquired."); - } - - flags = mfs_get_patharr(sb, relpath, &path, &depth); - if ((flags & MFS_ISDIR) == 0) - { - MFS_LOG("RMDIR", "FS Object is not a directory."); - ret = -ENOTDIR; - goto errout_with_lock; - } - else - { - MFS_EXTRA_LOG("RMDIR", "Path is at %p with depth %" PRIu32, path, - depth); - MFS_EXTRA_LOG("RMDIR", "FS Object is a directory."); - MFS_EXTRA_LOG("RMDIR", "Retrieved flags are 0x%x.", flags); - } - - mfs_pitr_init(sb, path, depth, &pitr, true); - mfs_pitr_adv_tochild(&pitr, path); - - if (!mfs_obj_isempty(sb, path, &pitr)) - { - MFS_EXTRA_LOG("RMDIR", "Directory is not empty."); - ret = -ENOTEMPTY; - goto errout_with_pitr; - } - - mfs_pitr_free(&pitr); - - mfs_pitr_rm(sb, path, depth, true); - -errout_with_pitr: - mfs_free_patharr(path); - -errout_with_lock: - nxmutex_unlock(&MFS_LOCK(sb)); - MFS_EXTRA_LOG("RMDIR", "Mutex released."); - -errout: - MFS_LOG("RMDIR", "Exit | Return: %d.", ret); - return ret; -} - -/**************************************************************************** - * Name: mnemofs_rename - * - * Description: - * Moves a file or directory between paths. See `rename(2)` for more - * information. - * - * In mnemofs, this involves erasing a direntry from old path, and adding - * it to the new path. There are some further complications regarding - * directories and files, as mentioned in `rename(2)`. - * - * Input Parameters: - * mountpt - Mount point of the file system. - * relpath - Relative path of the new directory. - * mode - Mode of the new directory (ACL). - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - ****************************************************************************/ - -static int mnemofs_rename(FAR struct inode *mountpt, - FAR const char *oldrelpath, - FAR const char *newrelpath) -{ - int ret = OK; - int oflags; - int nflags; - bool nexists; - bool odir = false; - bool ndir = false; - mfs_t odepth; - mfs_t ndepth; - struct mfs_pitr_s opitr; - struct mfs_pitr_s npitr; - FAR struct mfs_sb_s *sb; - FAR struct mfs_path_s *opath; - FAR struct mfs_path_s *npath; - FAR struct mfs_dirent_s *odirent = NULL; - - finfo("Mnemofs rename \"%s\" to \"%s\".", oldrelpath, newrelpath); - - DEBUGASSERT(mountpt != NULL); - sb = mountpt->i_private; - DEBUGASSERT(sb != NULL); - - ret = nxmutex_lock(&MFS_LOCK(sb)); - if (ret < 0) - { - goto errout; - } - - finfo("Lock acquired."); - - oflags = mfs_get_patharr(sb, oldrelpath, &opath, &odepth); - if ((oflags & MFS_EXIST) == 0) - { - ret = -ENOENT; - goto errout_with_opath; - } - - nflags = mfs_get_patharr(sb, newrelpath, &npath, &ndepth); - if ((nflags & MFS_P_EXIST) == 0) - { - ret = -ENONET; - goto errout_with_npath; - } - - odir = ((oflags & MFS_ISDIR) != 0); - ndir = ((nflags & MFS_ISDIR) != 0); - nexists = ((nflags & MFS_EXIST) != 0); - - if (nexists && odir && !ndir) - { - ret = -ENOTDIR; - goto errout_with_npath; - } - - if (nexists && !odir && ndir) - { - ret = -EISDIR; - goto errout_with_npath; - } - - mfs_pitr_init(sb, npath, ndepth, &npitr, nexists); - mfs_pitr_init(sb, opath, odepth, &opitr, true); - - /* If new path already exists, remove the direntry. If it's a non-empty - * directory, then raise error. - */ - - if (nexists) - { - mfs_pitr_adv_tochild(&opitr, opath); - mfs_pitr_adv_tochild(&npitr, npath); - - mfs_pitr_readdirent(sb, opath, &opitr, &odirent); - if (ndir && !mfs_obj_isempty(sb, npath, &npitr)) - { - ret = -ENOTEMPTY; - goto errout_with_pitr; - } - - mfs_pitr_reset(&npitr); - - mfs_pitr_rm(sb, npath, ndepth, false); - } - - mfs_pitr_adv_tochild(&npitr, npath); - - mfs_pitr_appenddirent(sb, npath, ndepth, &npitr, odirent); - mfs_pitr_rmdirent(sb, opath, odepth, &opitr, odirent); - -errout_with_pitr: - mfs_free_dirent(odirent); - mfs_pitr_free(&opitr); - mfs_pitr_free(&npitr); - -errout_with_npath: - mfs_free_patharr(npath); - -errout_with_opath: - mfs_free_patharr(opath); - nxmutex_unlock(&MFS_LOCK(sb)); - finfo("Lock released."); - -errout: - finfo("Mnemofs rename exited with ret %d.", ret); - return ret; -} - -/**************************************************************************** - * Name: mnemofs_stat - * - * Description: - * Get stats of a file. See `stat(2)` for more information. - * - * In mnemofs, most of the relevant information is stored in the dirent - * of the file in its parent's directory file. The stats of the root are - * available in the master node. - * - * Input Parameters: - * mountpt - Mount point of the file system. - * relpath - Relative path of the new directory. - * buf - File stats to populate. - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - ****************************************************************************/ - -static int mnemofs_stat(FAR struct inode *mountpt, FAR const char *relpath, - FAR struct stat *buf) -{ - int ret = OK; - int 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; - - MFS_LOG("STAT", "Entry."); - MFS_LOG("STAT", "Requested path is \"%s\".", relpath); - - DEBUGASSERT(mountpt != NULL); - sb = mountpt->i_private; - DEBUGASSERT(sb != NULL); - - ret = nxmutex_lock(&MFS_LOCK(sb)); - if (ret < 0) - { - MFS_LOG("STAT", "Could not acquire mutex."); - goto errout; - } - else - { - MFS_EXTRA_LOG("STAT", "Mutex acquired."); - } - - MFS_EXTRA_LOG_MN(&MFS_MN(sb)); - - flags = mfs_get_patharr(sb, relpath, &path, &depth); - if ((flags & MFS_EXIST) == 0) - { - MFS_LOG("STAT", "File does not exist."); - ret = -ENOENT; - goto errout_with_path; - } - else - { - MFS_EXTRA_LOG("STAT", "Path is at %p.", path); - MFS_EXTRA_LOG("STAT", "Depth is %" PRIu32, depth); - MFS_EXTRA_LOG("STAT", "Retrieved flags are 0x%x.", flags); - } - - ret = mfs_lru_getupdatedinfo(sb, path, depth); - if (predict_false(ret < 0)) - { - MFS_LOG("STAT", "Could not get updated information."); - goto errout_with_path; - } - else - { - MFS_EXTRA_LOG("STAT", "Updated information received from LRU."); - } - - mfs_pitr_init(sb, path, depth, &pitr, true); - mfs_pitr_adv_tochild(&pitr, path); - - ret = mfs_pitr_readdirent(sb, path, &pitr, &dirent); - if (predict_false(ret < 0)) - { - MFS_LOG("STAT", "Could not read from direntry."); - goto errout_with_path; - } - else if (dirent == NULL) - { - MFS_LOG("STAT", "No entry found."); - ret = -ENOENT; - goto errout_with_path; - } - else - { - MFS_EXTRA_LOG("STAT", "Direntry read successfully."); - } - - MFS_EXTRA_LOG_DIRENT(dirent); - - buf->st_nlink = 1; - buf->st_blksize = sb->pg_sz; - buf->st_size = dirent->sz; - buf->st_mode = dirent->mode; - buf->st_atim = dirent->st_atim; - buf->st_ctim = dirent->st_ctim; - buf->st_mtim = dirent->st_mtim; - buf->st_blocks = dirent->ctz.idx_e + 1; - - mfs_free_dirent(dirent); - mfs_pitr_free(&pitr); - -errout_with_path: - mfs_free_patharr(path); - - nxmutex_unlock(&MFS_LOCK(sb)); - MFS_EXTRA_LOG("STAT", "Mutex released."); - -errout: - MFS_LOG("STAT", "Exit | Return: %d.", ret); - return ret; -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -int mnemofs_flush(FAR struct mfs_sb_s *sb) -{ - int ret = OK; - bool change; - - /* Empty the LRU, and maybe the journal as well. */ - - finfo("Flush operation started."); - - for (; ; ) - { - change = false; - if (!mfs_lru_isempty(sb)) - { - finfo("LRU needs to be flushed."); - - change = true; - ret = mfs_lru_flush(sb); - if (predict_false(ret < 0)) - { - goto errout; - } - } - - if (!mfs_jrnl_isempty(sb) && - MFS_JRNL(sb).log_cblkidx >= MFS_JRNL_LIM(sb)) - { - finfo("Journal needs to be flushed."); - - change = true; - - ret = mfs_jrnl_flush(sb); - if (predict_false(ret < 0)) - { - goto errout; - } - } - - if (!change) - { - break; - } - - finfo("Finished Iteration."); - } - -errout: - return ret; -} diff --git a/fs/mnemofs/mnemofs.h b/fs/mnemofs/mnemofs.h index 201f8719463..dbe571920c1 100644 --- a/fs/mnemofs/mnemofs.h +++ b/fs/mnemofs/mnemofs.h @@ -57,2021 +57,242 @@ * Included Files ****************************************************************************/ -#include +#include +#include +#include +#include #include #include +#include #include +#include +#include +#include + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ -#define MFS_JRNL_MAGIC "-mfs!j!-" -#define MFS_MN_MAGIC "-mfs!m!-" +typedef uint32_t mfs_t; +typedef uint64_t mfs_time_t; -#define MFS_CEILDIVIDE(num, denom) (((num) + ((denom) - 1)) / (denom)) -#define MFS_UPPER8(num) (((num) + 7) & (-8)) +#define MFS_DIRENT_MAGIC 131 +#define MFS_DIR_MAGIC 197 +#define MFS_NAME_MAX 30 +#define MFS_NAME_LEN (MFS_NAME_MAX + 1) +#define MFS_LOCATION_INVALID 0 +#define MFS_DIRENT_OFF_INVALID UINT32_MAX +#define MFS_FORMAT_VERSION_MAJOR 1 -#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_OFILES(sb) ((sb)->of) -#define MFS_FLUSH(sb) ((sb)->flush) -#define MFS_NPGS(sb) (MFS_NBLKS(sb) * MFS_PGINBLK(sb)) - -#define MFS_HASHSZ 16 -#define MFS_CTZ_SZ(l) ((l)->sz) -#define MFS_DIRENTSZ(dirent) ((2 * 2) + 4 + (16 * 3) + 8 + 1 \ - + (dirent)->namelen) - -#define MFS_JRNL_LIM(sb) (MFS_JRNL(sb).n_blks / 2) -#define MFS_TRAVERSE_INITSZ 8 - -#define MFS_LOG(fn, fmt, ...) finfo("[mnemofs | " fn "] " fmt, ##__VA_ARGS__) -#ifdef CONFIG_MNEMOFS_EXTRA_DEBUG -#define MFS_EXTRA_LOG(fn, fmt, ...) MFS_LOG(fn, fmt, ##__VA_ARGS__) -#else -#define MFS_EXTRA_LOG(fmt, ...) { } -#endif -#define MFS_STRLITCMP(a, lit) strncmp(a, lit, strlen(lit)) +#define MFS_PAGE_SIZE(sb) ((sb)->geo.blocksize) +#define MFS_BLOCK_SIZE(sb) ((sb)->geo.erasesize) +#define MFS_BLOCK_COUNT(sb) ((sb)->geo.neraseblocks) +#define MFS_PAGES_PER_BLOCK(sb) ((sb)->pagesperblk) +#define MFS_PAGE_COUNT(sb) \ + (MFS_BLOCK_COUNT(sb) * MFS_PAGES_PER_BLOCK(sb)) +#define MFS_BLOCK_TO_PAGE(sb, blk) \ + ((blk) * MFS_PAGES_PER_BLOCK(sb)) +#define MFS_DIRENTS_PER_PAGE(sb) \ + (MFS_PAGE_SIZE(sb) / sizeof(struct mfs_direntry_s)) /**************************************************************************** * Public Types ****************************************************************************/ -typedef uint32_t mfs_t; - -enum +begin_packed_struct struct mfs_direntry_s { - MFS_PG_USED, - MFS_PG_FREE, - MFS_BLK_BAD, - MFS_BLK_ERASABLE, - MFS_BLK_FREE, - MFS_BLK_USED, -}; + uint8_t magic; + uint16_t mode; + mfs_t oldloc; + mfs_t newloc; + uint16_t namehash; + char name[MFS_NAME_MAX]; + mfs_time_t ctime; + mfs_time_t mtime; + union + { + uint32_t size; + uint32_t reserved; + } meta; + uint8_t checksum; +} end_packed_struct; -enum MFS_PATH_FLAGS +begin_packed_struct struct mfs_dirmeta_s { - MFS_ISDIR = (1 << 0), /* Path is a directory. */ - MFS_ISFILE = (1 << 1), /* Path is a file. */ - MFS_EXIST = (1 << 2), /* Path Exists */ - 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. */ -}; + uint8_t magic; + mfs_t parent; + uint8_t checksum; +} end_packed_struct; -struct mfs_ctz_s +begin_packed_struct struct mfs_superblock_s { - mfs_t pg_e; - mfs_t idx_e; -}; + uint32_t magic; + uint32_t npages; + uint32_t pagesize; + uint32_t pagesperblock; + uint32_t nblocks; + mfs_t rootdir; + uint16_t version; +} end_packed_struct; -struct mfs_path_s +struct mfs_dirloc_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; /* Index for next MN entry inside blk. */ - 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 jrnl_start; - 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; /* TODO: Does not include the master node. */ + mfs_t page; + mfs_t offset; }; 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; - uint16_t pg_in_blk; - uint8_t log_pg_in_blk; - 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. */ - bool flush; + FAR struct inode *driver; + FAR struct mtd_dev_s *mtd; + struct mtd_geometry_s geo; + uint8_t erasestate; + FAR uint8_t *rwbuf; + mfs_t rwpage; + bool rwvalid; + bool rwdirty; + FAR uint8_t *freepages; + FAR uint8_t *delpages; + size_t bitmapsize; + mfs_t nextpage; + mfs_t pagesperblk; + mfs_t superblock; + mfs_t rootdir; + uint16_t version; + mutex_t lock; + struct list_node ofiles; }; -/* This is for *dir VFS methods. */ - -struct mfs_fsdirent_s +struct mfs_dirfs_s { - 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; + struct fs_dirent_s dir; + mfs_t location; + struct mfs_direntry_s direntry; + struct mfs_dirloc_s nextloc; }; -/* LRU Delta */ - -struct mfs_delta_s +struct mfs_ctz_s { - struct list_node list; - mfs_t off; - mfs_t n_b; - FAR char *upd; + mfs_t page; + mfs_t index; }; -/* LRU Node */ - -struct mfs_node_s +struct mfs_ofd_common_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; - struct timespec st_mtim; - struct timespec st_atim; - struct timespec st_ctim; - FAR struct mfs_path_s *path; + struct mfs_direntry_s direntry; + struct mfs_dirloc_s direntloc; + FAR char *relpath; + mfs_t fileloc; + off_t pos; + unsigned int refs; }; -/* 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 list_node entry; + FAR struct mfs_sb_s *sb; + FAR struct mfs_ofd_common_s *common; }; -struct mfs_dirent_s -{ - uint16_t name_hash; /* Should be at start to improve efficiency. */ - uint16_t mode; - mfs_t sz; - 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; - char name[]; -}; - -/* Parent iterator */ - -struct mfs_pitr_s -{ - struct mfs_path_s p; /* Parent's path representation */ - mfs_t depth; - mfs_t c_off; /* Current iteration offset. */ -}; - -/* TODO: depth >= 1 */ - -/* IMP TODO: sizeof(x) != size of buffer required to store it. Need to fix. */ - -/**************************************************************************** - * 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 -} - -/**************************************************************************** - * Name: mfs_clz - * - * Description: - * Count Leading Zeros. Returns the number of leading zeros in a 32-bit - * integer. - * - * Input Parameters: - * x - 32-bit integer to check. - * - * Returned Value: - * The number of leading zeros. - * - ****************************************************************************/ - -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 - uint32_t n = 0; - uint32_t x_tmp = x; - - if (x_tmp == 0) - { - return 32; - } - - if (x_tmp <= 0x0000ffff) - { - n += 16; - x_tmp <<= 16; - } - - if (x_tmp <= 0x00ffffff) - { - n += 8; - x_tmp <<= 8; - } - - if (x_tmp <= 0x0fffffff) - { - n += 4; - x_tmp <<= 4; - } - - if (x_tmp <= 0x3fffffff) - { - n += 2; - x_tmp <<= 2; - } - - if (x_tmp <= 0x7fffffff) - { - n += 1; - } - - return n; -#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 -} - -static inline void MFS_EXTRA_LOG_DIRENT(FAR const struct mfs_dirent_s * const - dirent) -{ - MFS_EXTRA_LOG("EXTRA_LOG_DIRENT", "Direntry details."); - MFS_EXTRA_LOG("EXTRA_LOG_DIRENT", "\tDirent location %p", dirent); - MFS_EXTRA_LOG("EXTRA_LOG_DIRENT", "\tMode is %" PRIu16, dirent->mode); - MFS_EXTRA_LOG("EXTRA_LOG_DIRENT", "\tName is \"%.*s\"", dirent->namelen, - dirent->name); - MFS_EXTRA_LOG("EXTRA_LOG_DIRENT", "\tNamelen is %" PRIu32, - dirent->namelen); - MFS_EXTRA_LOG("EXTRA_LOG_DIRENT", "\tName Hash is %" PRIu16, - dirent->name_hash); - MFS_EXTRA_LOG("EXTRA_LOG_DIRENT", "\tSize is %" PRIu16, - dirent->sz); - - /* TODO: Timespecs */ -} - -static inline void MFS_EXTRA_LOG_FSDIRENT(FAR const struct mfs_fsdirent_s * - const fsdirent) -{ - MFS_EXTRA_LOG("EXTRA_LOG_FSDIRENT", "FS Direntry details."); - MFS_EXTRA_LOG("EXTRA_LOG_FSDIRENT", "\tDirentry depth %" PRIu32, - fsdirent->depth); - MFS_EXTRA_LOG("EXTRA_LOG_FSDIRENT", "\tRead index %" PRIu32, - fsdirent->idx); - MFS_EXTRA_LOG("EXTRA_LOG_FSDIRENT", "\tPath %p.", fsdirent->path); - MFS_EXTRA_LOG("EXTRA_LOG_FSDIRENT", "\tPitr %p.", fsdirent->pitr); - MFS_EXTRA_LOG("EXTRA_LOG_FSDIRENT", "\tDepth %" PRIu32, fsdirent->depth); -} - -static inline void MFS_EXTRA_LOG_PITR(FAR const struct mfs_pitr_s * const - pitr) -{ - MFS_EXTRA_LOG("EXTRA_LOG_PITR", "Pitr details."); - MFS_EXTRA_LOG("EXTRA_LOG_PITR", "\tDepth %" PRIu32, pitr->depth); - MFS_EXTRA_LOG("EXTRA_LOG_PITR", "\tCurrent Offset %" PRIu32, pitr->c_off); - MFS_EXTRA_LOG("EXTRA_LOG_PITR", "\tParent CTZ (%" PRIu32 ", %" PRIu32 ")", - pitr->p.ctz.idx_e, pitr->p.ctz.pg_e); - MFS_EXTRA_LOG("EXTRA_LOG_PITR", "\tParent Size %" PRIu32, pitr->p.sz); - MFS_EXTRA_LOG("EXTRA_LOG_PITR", "\tParent Offset %" PRIu32, pitr->p.off); -} - -static inline void MFS_EXTRA_LOG_MN(FAR const struct mfs_mn_s * const mn) -{ - MFS_EXTRA_LOG("EXTRA_LOG_MN", "Master node details."); - MFS_EXTRA_LOG("EXTRA_LOG_MN", "\tFirst journal block is %" PRIu32, - mn->jrnl_blk); - MFS_EXTRA_LOG("EXTRA_LOG_MN", "\tNext MN entry index %" PRIu32, - mn->mblk_idx); - MFS_EXTRA_LOG("EXTRA_LOG_MN", "\tRoot CTZ (%" PRIu32 ", %" PRIu32 ")", - mn->root_ctz.idx_e, mn->root_ctz.pg_e); - MFS_EXTRA_LOG("EXTRA_LOG_MN", "\tRoot Size %" PRIu32, mn->root_sz); - MFS_EXTRA_LOG("EXTRA_LOG_MN", "\tRoot Mode is %u.", mn->root_mode); - - /* TODO: Timespecs */ -} - -static inline void MFS_EXTRA_LOG_F(FAR struct mfs_ofd_s *f) -{ - MFS_EXTRA_LOG("EXTRA_LOG_F", "File structure details."); - MFS_EXTRA_LOG("EXTRA_LOG_F", "\tList details."); - MFS_EXTRA_LOG("EXTRA_LOG_F", "\t\tPrevious node %p.", f->list.prev); - MFS_EXTRA_LOG("EXTRA_LOG_F", "\t\tNext node %p.", f->list.next); - MFS_EXTRA_LOG("EXTRA_LOG_F", "\tCommon structure details."); - MFS_EXTRA_LOG("EXTRA_LOG_F", "\t\tDepth: %" PRIu32, f->com->depth); - MFS_EXTRA_LOG("EXTRA_LOG_F", "\t\tNew Entry: %s.", - f->com->new_ent ? "true" : "false"); - MFS_EXTRA_LOG("EXTRA_LOG_F", "\t\tOffset: %" PRIu32, f->com->off); - MFS_EXTRA_LOG("EXTRA_LOG_F", "\t\tFlags: 0x%x.", f->com->oflags); - MFS_EXTRA_LOG("EXTRA_LOG_F", "\t\tReference Counter: %" PRIu8, - f->com->refcount); - MFS_EXTRA_LOG("EXTRA_LOG_F", "\t\tFile Size: %" PRIu32, f->com->sz); - MFS_EXTRA_LOG("EXTRA_LOG_F", "\t\tPath details."); - MFS_EXTRA_LOG("EXTRA_LOG_F", "\t\t\tOffset: %" PRIu32, f->com->path->off); - MFS_EXTRA_LOG("EXTRA_LOG_F", "\t\t\tSize: %" PRIu32, f->com->path->sz); - MFS_EXTRA_LOG("EXTRA_LOG_F", "\t\t\tCTZ (%" PRIu32 ", %" PRIu32 ").", - f->com->path->ctz.idx_e, f->com->path->ctz.pg_e); -} +static_assert(sizeof(struct mfs_direntry_s) == 64, + "mnemofs direntry layout mismatch"); +static_assert(sizeof(struct mfs_dirmeta_s) == 6, + "mnemofs dirmeta layout mismatch"); +static_assert(sizeof(struct mfs_superblock_s) == 26, + "mnemofs superblock layout mismatch"); /**************************************************************************** * Public Function Prototypes ****************************************************************************/ -/* mnemofs.c */ - -int mnemofs_flush(FAR struct mfs_sb_s *sb); - -/* mnemofs_journal.c */ - -/**************************************************************************** - * Name: mfs_jrnl_init - * - * Description: - * Initializes journal when it's already formatted into the device. - * - * Input Parameters: - * sb - Superblock instance of the device. - * blk - First block of the journal. - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - * Assumptions/Limitations: - * Does not initialize the master node. - * - ****************************************************************************/ - -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. - * - * Does not format the master node. - * - ****************************************************************************/ - -int mfs_jrnl_fmt(FAR struct mfs_sb_s * const sb, mfs_t *blk1, mfs_t *blk2, - FAR mfs_t *jrnl_blk); - -/**************************************************************************** - * 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); - -/**************************************************************************** - * Name: mfs_jrnl_blkidx2blk - * - * Description: - * Gets the block number of a journal block at an index. - * - * Input Parameters: - * sb - Superblock instance of the device. - * blk_idx - Index of the block who's block number to find. - * - * Returned Value: - * Block number. - * - * Assumptions/Limitations: - * Assumes valid index. This index can also include master blocks. Also - * assumes, for now, that the entire journal array can fit in the first - * block. - * - ****************************************************************************/ - -mfs_t mfs_jrnl_blkidx2blk(FAR const struct mfs_sb_s * const sb, - const mfs_t blk_idx); - -/**************************************************************************** - * Name: mfs_jrnl_updatedinfo - * - * Description: - * Update the path information from the journal. - * - * Input Parameters: - * sb - Superblock instance of the device. - * path - "Base state" path. - * depth - Path depth. - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - * Assumptions/Limitations: - * This applies updates over data that is already gathered from the data - * section of the flash. The data section is the "base state" over which - * the updates in the journal are applied. - * - ****************************************************************************/ - -int mfs_jrnl_updatedinfo(FAR const struct mfs_sb_s * const sb, - FAR struct mfs_path_s * const path, - const mfs_t depth); - -/**************************************************************************** - * Name: mfs_jrnl_wrlog - * - * Description: - * Write a log for LRU node when its popped from the LRU. - * - * Input Parameters: - * sb - Superblock instance of the device. - * node - LRU node. - * loc_new - New location of the CTZ skip list. - * sz_new - New size of the CTZ skip list. - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - * Assumptions/Limitations: - * When a node from LRU is flushed, it's written to the data in any space - * that is available according to the block allocator, and the new location - * is recorded as a log along with the old location. Any log of this same - * CTZ skip list in the journal will use this "snapshot" of the location, - * ie. the updated path will be used until another log of the same CTZ - * skip list is stored, after which the path will be updated again, and - * so on. - * - ****************************************************************************/ - -int mfs_jrnl_wrlog(FAR struct mfs_sb_s * const sb, - FAR const struct mfs_node_s *node, - const struct mfs_ctz_s loc_new, const mfs_t sz_new); - -/**************************************************************************** - * Name: mfs_jrnl_flush - * - * Description: - * Flush the entire journal. This is the entry point of the entire flush - * operation and includes messing with the master node as well. - * - * Input Parameters: - * sb - Superblock instance of the device. - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - ****************************************************************************/ - -int mfs_jrnl_flush(FAR struct mfs_sb_s * const sb); - -/**************************************************************************** - * Name: mfs_jrnl_isempty - * - * Description: - * Check if the journal is empty. - * - * Input Parameters: - * sb - Superblock instance of the device. - * - * Returned Value: - * True is the journal is empty, false otherwise. - * - ****************************************************************************/ - -bool mfs_jrnl_isempty(FAR const struct mfs_sb_s * const sb); - -struct mfs_jrnl_log_s -{ - /* A field denoting the size of the below elements is added here on-flash. - */ - - mfs_t depth; - mfs_t sz_new; - struct mfs_ctz_s loc_new; - struct timespec st_mtim_new; - struct timespec st_atim_new; - struct timespec st_ctim_new; - FAR struct mfs_path_s *path; - uint16_t hash; -}; - -int mfs_jrnl_rdlog(FAR const struct mfs_sb_s *const sb, - FAR mfs_t *blkidx, FAR mfs_t *pg_in_blk, - FAR struct mfs_jrnl_log_s *log); - -void mfs_jrnl_log_free(FAR const struct mfs_jrnl_log_s * const log); - -/* mnemofs_blkalloc.c */ - -/**************************************************************************** - * Name: mfs_ba_init - * - * Description: - * Formats and initializes the block allocator. - * - * Input Parameters: - * sb - Superblock instance of the device. - * - * Returned Value: - * 0 - OK - * -ENOMEM - No memory left. - * - ****************************************************************************/ - -int mfs_ba_fmt(FAR struct mfs_sb_s * const sb); - -/**************************************************************************** - * 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_getpg - * - * Description: - * Returns an allocated page. - * - * Input Parameters: - * sb - Superblock instance of the device. - * - * Returned Value: - * 0 - No more space left - * > 0 - Page number - * - * Assumptions/Limitations: - * This assumes a locked environment when called. - * - ****************************************************************************/ - -mfs_t mfs_ba_getpg(FAR struct mfs_sb_s * const sb); - -/**************************************************************************** - * Name: mfs_ba_getblk - * - * Description: - * Returns an allocated block. - * - * Input Parameters: - * sb - Superblock instance of the device. - * - * Returned Value: - * 0 - No more space left - * > 0 - Block number - * - * Assumptions/Limitations: - * This assumes a locked environment when called. - * - ****************************************************************************/ - -mfs_t mfs_ba_getblk(FAR struct mfs_sb_s * const sb); - -/**************************************************************************** - * Name: mfs_ba_pgmarkdel - * - * Description: - * Mark a page as being ready for deletion. - * - * Input Parameters: - * sb - Superblock instance of the device. - * pg - Page number. - * - * Assumptions/Limitations: - * This assumes a locked environment when called. - * - ****************************************************************************/ - -void mfs_ba_pgmarkdel(FAR struct mfs_sb_s * const sb, mfs_t pg); - -/**************************************************************************** - * Name: mfs_ba_blkmarkdel - * - * Description: - * Mark a block as being ready for deletion. - * - * Input Parameters: - * sb - Superblock instance of the device. - * blk - Block number. - * - * Assumptions/Limitations: - * This assumes a locked environment when called. - * - ****************************************************************************/ - -void mfs_ba_blkmarkdel(FAR struct mfs_sb_s * const sb, mfs_t blk); - -/**************************************************************************** - * Name: mfs_ba_delmarked - * - * Description: - * Delete all marked for deletion blocks. - * - * Input Parameters: - * sb - Superblock instance of the device. - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - * Assumptions/Limitations: - * This assumes a locked environment when called. - * - ****************************************************************************/ - -int mfs_ba_delmarked(FAR struct mfs_sb_s * const sb); - -/**************************************************************************** - * Name: mfs_ba_markusedpg - * - * Description: - * Marked page as being used. - * - * Input Parameters: - * sb - Superblock instance of the device. - * pg - Page number - * - * Assumptions/Limitations: - * This assumes a locked environment when called. - * - ****************************************************************************/ - -void mfs_ba_markusedpg(FAR struct mfs_sb_s * const sb, mfs_t pg); - -/**************************************************************************** - * Name: mfs_ba_markusedblk - * - * Description: - * Marked block as being used. - * - * Input Parameters: - * sb - Superblock instance of the device. - * blk - Block number - * - * Assumptions/Limitations: - * This assumes a locked environment when called. - * - ****************************************************************************/ - -void mfs_ba_markusedblk(FAR struct mfs_sb_s * const sb, mfs_t blk); - -/**************************************************************************** - * 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); - -/**************************************************************************** - * 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); - -/* mnemofs_rw.c */ - -/**************************************************************************** - * Name: mfs_isbadblk - * - * Description: - * Is a block bad. - * - * Input Parameters: - * sb - Superblock instance of the device. - * blk - Block Number - * - * Assumptions/Limitations: - * This assumes a locked environment when called. - * - ****************************************************************************/ - -int mfs_isbadblk(FAR const struct mfs_sb_s * const sb, mfs_t blk); - -/**************************************************************************** - * Name: mfs_markbadblk - * - * Description: - * Mark a block as bad. - * - * Input Parameters: - * sb - Superblock instance of the device. - * blk - Block Number - * - * Assumptions/Limitations: - * This assumes a locked environment when called. - * - ****************************************************************************/ - -int mfs_markbadblk(FAR const struct mfs_sb_s * const sb, mfs_t blk); - -/**************************************************************************** - * Name: mfs_write_page - * - * Description: - * Write 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_write_page(FAR const struct mfs_sb_s * const sb, - FAR const char *data, const mfs_t datalen, - const off_t page, const mfs_t pgoff); - -/**************************************************************************** - * 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); - -/**************************************************************************** - * Name: mfs_erase_blk - * - * Description: - * Erase a block. - * - * Input Parameters: - * sb - Superblock instance of the device. - * blk - Block Number - * - * Assumptions/Limitations: - * This assumes a locked environment when called. - * - ****************************************************************************/ - -int mfs_erase_blk(FAR const struct mfs_sb_s * const sb, const off_t blk); - -/**************************************************************************** - * Name: mfs_erase_nblks - * - * Description: - * Erase consecutive blocks. - * - * Input Parameters: - * sb - Superblock instance of the device. - * blk - Block Number - * - * Assumptions/Limitations: - * This assumes a locked environment when called. - * - ****************************************************************************/ - -int mfs_erase_nblks(FAR const struct mfs_sb_s * const sb, const off_t blk, - const size_t n); - -/* mnemofs_util.c */ - -/**************************************************************************** - * Name: mfs_arrhash - * - * Description: - * Returns an 8-bit hash of an entire array of data. - * - * Input Parameters: - * arr - Data array. - * len - Length of the array. - * - * Returned Value: - * 16-bit hash of the array. - * - ****************************************************************************/ - -uint16_t mfs_hash(FAR const char *arr, ssize_t len); - -/**************************************************************************** - * Name: mfs_ser_8 - * - * Description: - * Serialize a 8 bit type into output. - * - * Input Parameters: - * n - 8 bit to serialize - * out - Output array where to serialize. - * - * Returned Value: - * Pointer to byte after the end of serialized value. - * - ****************************************************************************/ - -FAR char *mfs_ser_8(const uint8_t n, FAR char * const out); - -/**************************************************************************** - * Name: mfs_deser_8 - * - * Description: - * Deserialize a 8 bit type from input. - * - * Input Parameters: - * in - Input array from where to deserialize. - * n - 8 bit to deserialize - * - * Returned Value: - * Pointer to byte after the end of serialized value. - * - ****************************************************************************/ - -FAR const char *mfs_deser_8(FAR const char * const in, uint8_t *n); - -/**************************************************************************** - * Name: mfs_ser_str - * - * Description: - * Serialize a string into output. - * - * Input Parameters: - * str - String to serialize - * len - Length of string - * out - Output array where to serialize. - * - * Returned Value: - * Pointer to byte after the end of serialized value. - * - ****************************************************************************/ - -FAR char *mfs_ser_str(FAR const char * const str, const mfs_t len, - FAR char * const out); - -/**************************************************************************** - * Name: mfs_deser_str - * - * Description: - * Deserialize a string from input. - * - * Input Parameters: - * in - Input array from where to deserialize. - * str - String to deserialize - * len - Length of string - * - * Returned Value: - * Pointer to byte after the end of serialized value. - * - ****************************************************************************/ - -FAR const char *mfs_deser_str(FAR const char * const in, - FAR char * const str, const mfs_t len); - -/**************************************************************************** - * Name: mfs_ser_mfs - * - * Description: - * Serialize a mfs_t type into output. - * - * Input Parameters: - * n - mfs_t to serialize - * out - Output array where to serialize. - * - * Returned Value: - * Pointer to byte after the end of serialized value. - * - ****************************************************************************/ - -FAR char *mfs_ser_mfs(const mfs_t n, FAR char * const out); - -/**************************************************************************** - * Name: mfs_deser_mfs - * - * Description: - * Deserialize a mfs_t type from input.. - * - * Input Parameters: - * in - Input array from where to deserialize. - * n - mfs_t to deserialize - * - * Returned Value: - * Pointer to byte after the end of serialized value. - * - ****************************************************************************/ - -FAR const char *mfs_deser_mfs(FAR const char * const in, - FAR mfs_t * const n); - -/**************************************************************************** - * Name: mfs_ser_ctz - * - * Description: - * Serialize a mfs_ctz_store_s type into output. - * - * Input Parameters: - * x - mfs_ctz_s to serialize - * out - Output array where to serialize. - * - * Returned Value: - * Pointer to byte after the end of serialized value. - * - ****************************************************************************/ - -FAR char *mfs_ser_ctz(FAR const struct mfs_ctz_s * const x, - FAR char * const out); - -/**************************************************************************** - * Name: mfs_deser_ctz - * - * Description: - * Deserialize a mfs_ctz_store_s type into output. - * - * Input Parameters: - * in - Input array from where to deserialize. - * x - mfs_ctz_s to deserialize - * - * Returned Value: - * Pointer to byte after the end of serialized value. - * - ****************************************************************************/ - -FAR const char *mfs_deser_ctz(FAR const char * const in, - FAR struct mfs_ctz_s * const x); - -FAR char *mfs_ser_path(FAR const struct mfs_path_s * const x, - FAR char * const out); - -FAR const char *mfs_deser_path(FAR const char * const in, - FAR struct mfs_path_s * const x); - -/**************************************************************************** - * Name: mfs_ser_timespec - * - * Description: - * Serialize timespec. - * - * Input Parameters: - * x - Value to serialize - * out - Output array where to serialize. - * - * Returned Value: - * Pointer to byte after the end of serialized value. - * - ****************************************************************************/ - -FAR char *mfs_ser_timespec(FAR const struct timespec * const x, - FAR char * const out); - -/**************************************************************************** - * Name: mfs_deser_timespec - * - * Description: - * Deserialize timespec. - * - * Input Parameters: - * in - Input array from where to deserialize. - * x - Value to deserialize - * - * Returned Value: - * Pointer to byte after the end of serialized value. - * - ****************************************************************************/ - -FAR const char *mfs_deser_timespec(FAR const char * const in, - FAR struct timespec * const x); - -/**************************************************************************** - * Name: mfs_ser_16 - * - * Description: - * Serialize 16 bit values. - * - * Input Parameters: - * x - Value to serialize - * out - Output array where to serialize. - * - * Returned Value: - * Pointer to byte after the end of serialized value. - * - ****************************************************************************/ - -FAR char *mfs_ser_16(const uint16_t n, FAR char * const out); - -/**************************************************************************** - * Name: mfs_deser_16 - * - * Description: - * Deserialize 16 bit value. - * - * Input Parameters: - * in - Input array from where to deserialize. - * x - Value to deserialize - * - * Returned Value: - * Pointer to byte after the end of serialized value. - * - ****************************************************************************/ - -FAR const char *mfs_deser_16(FAR const char * const in, FAR uint16_t *n); - -/**************************************************************************** - * Name: mfs_v2n - * - * Description: - * v2n(n) math function. - * - * Input Parameters: - * n - Number. - * - * Returned Value: - * v2n(n). - * - ****************************************************************************/ - -mfs_t mfs_v2n(mfs_t n); - -/**************************************************************************** - * Name: mfs_set_msb - * - * Description: - * The mosr significant set bit location. - * - * Input Parameters: - * n - Number. - * - * Returned Value: - * Number after setting the bit. - * - ****************************************************************************/ - -mfs_t mfs_set_msb(mfs_t n); - -bool mfs_ctz_eq(FAR const struct mfs_ctz_s * const a, - FAR const struct mfs_ctz_s * const b); - -bool mfs_path_eq(FAR const struct mfs_path_s * const a, - FAR const struct mfs_path_s * const b); - -/* mnemofs_ctz.c */ - -/**************************************************************************** - * Name: mfs_ctz_rdfromoff_new - * - * Description: - * Read from a specific offset into the data in a CTZ file. New version. - * - * Input Parameters: - * sb - Superblock instance of the device. - * ctz - CTZ list. - * data_off - Offset into the data. - * len - Length of the buffer. - * buf - Buffer to store read contents. - * - * Assumptions/Limitations: - * The CTZ list provided should be the updated location from the journal. - * - ****************************************************************************/ - -int mfs_ctz_rdfromoff(FAR const struct mfs_sb_s * const sb, - const struct mfs_ctz_s ctz, mfs_t data_off, - mfs_t len, FAR char * buf); - -/**************************************************************************** - * Name: mfs_ctz_wrtnode - * - * Description: - * Write an LRU node to the flash. It also adds a log of it to the journal. - * - * Input Parameters: - * sb - Superblock instance of the device. - * node - LRU node - * - * Assumptions/Limitations: - * - Assumes path is updated by journal. This will also write corresponding - * journal log. - * - * - This is the most computationally heavy part of the entire file system - * from a human POV, and one of the most for MCU or computer (as init - * methods are heavier). - * - ****************************************************************************/ - -int mfs_ctz_wrtnode(FAR struct mfs_sb_s * const sb, - FAR const struct mfs_node_s * const node, - FAR struct mfs_ctz_s *new_loc); - -/**************************************************************************** - * Name: mfs_ctz_travel - * - * Description: - * From CTZ block at page `pg_src` and index `idx_src`, give the page - * number of index `idx_dest`. - * - * The source is preferably the last CTZ block in the CTZ list, but it can - * realistically be any CTZ block in the CTZ list whose position is known. - * However, `idx_dest <= idx_src` has to be followed. Takes O(log(n)) - * complexity to travel. - * - * Input Parameters: - * sb - Superblock instance of the device. - * idx_src - Index of the source ctz block. - * pg_src - Page number of the source ctz block. - * idx_dest - Index of the destination ctz block. - * - * Returned Value: - * The page number corresponding to `idx_dest`. - * - * Assumptions/Limitations: - * `idx_dest <= idx_src`. - * - ****************************************************************************/ - -mfs_t mfs_ctz_travel(FAR const struct mfs_sb_s * const sb, - mfs_t idx_src, mfs_t pg_src, mfs_t idx_dest); - -/* mnemofs_lru.c */ - -/**************************************************************************** - * 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. - * - ****************************************************************************/ - -int mfs_lru_ctzflush(FAR struct mfs_sb_s * const sb, - FAR struct mfs_path_s * const path, const mfs_t depth); - -/**************************************************************************** - * 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. - * path - CTZ representation of the relpath. - * depth - Depth of path. - * - ****************************************************************************/ - -int mfs_lru_del(FAR struct mfs_sb_s * const sb, const mfs_t data_off, - mfs_t bytes, 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. - * 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, FAR struct mfs_path_s * const path, - const mfs_t depth, FAR const char *buf); - -/**************************************************************************** - * 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_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 const 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_getupdatedinfo - * - * Description: - * Update information of the path from the LRU. - * - * Input Parameters: - * sb - Superblock instance of the device. - * path - CTZ representation of the relpath. - * depth - Depth of path. - * - * Assumptions/Limitations: - * The will update path. - * - ****************************************************************************/ - -int mfs_lru_getupdatedinfo(FAR const struct mfs_sb_s * const sb, - FAR struct mfs_path_s * const path, - const mfs_t depth); - -/**************************************************************************** - * Name: mfs_lru_updatectz - * - * Description: - * Update CTZ location of an fs object. - * - * Input Parameters: - * sb - Superblock instance of the device. - * path - Old CTZ representation of the relpath. - * depth - Depth of path. - * new_ctz - New CTZ location. - * - * Assumptions/Limitations: - * The update the CTZ portion of the direntry in the parent. - * This updates the path as well. - * - ****************************************************************************/ - -int mfs_lru_updatectz(FAR struct mfs_sb_s * sb, - FAR struct mfs_path_s * const path, const mfs_t depth, - const struct mfs_ctz_s new_ctz, mfs_t new_sz); - -bool mfs_lru_isempty(FAR struct mfs_sb_s * const sb); -int mfs_lru_flush(FAR struct mfs_sb_s * const sb); - -/* mnemofs_master.c */ - -/**************************************************************************** - * Name: mfs_mn_init - * - * Description: - * Initialize master node by reading from the flash. - * - * Input Parameters: - * sb - Superblock instance of the device. - * jrnl_blk - First block of the journal. - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - * Assumptions/Limitations: - * The journal will have to be initialized before this. - * - ****************************************************************************/ - -int mfs_mn_init(FAR struct mfs_sb_s * const sb, const mfs_t jrnl_blk); - -/**************************************************************************** - * Name: mfs_mn_fmt - * - * Description: - * Format a master node. - * - * Input Parameters: - * sb - Superblock instance of the device. - * jrnl_blk - First block of the journal. - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - * Assumptions/Limitations: - * The journal will have to be formatted before this. - * - ****************************************************************************/ - -int mfs_mn_fmt(FAR struct mfs_sb_s * const sb, const mfs_t blk1, - const mfs_t blk2, const mfs_t jrnl_blk); - -/**************************************************************************** - * Name: mfs_mn_move - * - * Description: - * Move the master node. - * - * Input Parameters: - * sb - Superblock instance of the device. - * root - New location of the root of the file system. - * root_sz - New size of the CTZ list of the root of the file system. - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - * Assumptions/Limitations: - * This is called when the root is updated to a new location. - * - ****************************************************************************/ - -int mfs_mn_move(FAR struct mfs_sb_s * const sb, struct mfs_ctz_s root, - const mfs_t root_sz); - -int mfs_mn_sync(FAR struct mfs_sb_s *sb, - FAR struct mfs_path_s * const new_loc, - const mfs_t blk1, const mfs_t blk2, const mfs_t jrnl_blk); - -/* 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 responsibility to use - * this with `mfs_free_patharr`. - * - ****************************************************************************/ - -int mfs_get_patharr(FAR const 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_path_s *path, - 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. - * - ****************************************************************************/ - -int mfs_pitr_init(FAR const struct mfs_sb_s * const sb, - FAR const struct mfs_path_s * const path, - const mfs_t depth, FAR struct mfs_pitr_s * const 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 const 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. - * - ****************************************************************************/ - -int mfs_pitr_adv(FAR struct mfs_sb_s * const sb, - FAR struct mfs_path_s *path, - FAR struct mfs_pitr_s * const pitr); - -/**************************************************************************** - * Name: mfs_pitr_adv_bydirent - * - * 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_bydirent(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 - * - * 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); - -/**************************************************************************** - * 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_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 const struct mfs_sb_s * const sb, - FAR struct mfs_path_s *path, - 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 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. - * relpath - Relative path of the fs object. - * mode - Mode of the file/directory. - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - * Assumptions/Limitations: - * This assumes that the fs object desired to be appended appears at the - * end of the entire relpath. - * - ****************************************************************************/ - -int mfs_pitr_appendnew(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 char * const relpath, 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, bool rm_child); - -int mfs_pitr_traversefs(FAR struct mfs_sb_s * sb, const struct mfs_ctz_s ctz, - int type); - -#undef EXTERN -#ifdef __cplusplus -} -#endif +int mfs_is_bad_block(FAR const struct mfs_sb_s *sb, mfs_t blk); +int mfs_rwbuf_sync(FAR struct mfs_sb_s *sb); +void mfs_rwbuf_invalidate(FAR struct mfs_sb_s *sb); +int mfs_rwbuf_prepare_write(FAR struct mfs_sb_s *sb); +void mfs_rwbuf_discard_page(FAR struct mfs_sb_s *sb, mfs_t page); +void mfs_rwbuf_discard_block(FAR struct mfs_sb_s *sb, mfs_t block); +ssize_t mfs_write_page(FAR struct mfs_sb_s *sb, mfs_t page, + FAR const uint8_t *buffer); +ssize_t mfs_read_page(FAR struct mfs_sb_s *sb, mfs_t page, + FAR uint8_t *buffer); +int mfs_erase_blocks(FAR struct mfs_sb_s *sb, mfs_t startblk, + size_t nblocks); +int mfs_update_rootdir(FAR struct mfs_sb_s *sb, mfs_t rootdir); + +int mfs_alloc_init(FAR struct mfs_sb_s *sb); +void mfs_alloc_uninit(FAR struct mfs_sb_s *sb); +int mfs_alloc_page(FAR struct mfs_sb_s *sb, FAR mfs_t *page); +int mfs_alloc_block(FAR struct mfs_sb_s *sb, FAR mfs_t *block); +int mfs_release_page(FAR struct mfs_sb_s *sb, mfs_t page); +int mfs_report_page_deleted(FAR struct mfs_sb_s *sb, mfs_t page); +int mfs_report_block_deleted(FAR struct mfs_sb_s *sb, mfs_t block); + +mfs_t mfs_ctz_unit_data_area(FAR struct mfs_sb_s *sb, mfs_t index); +int mfs_ctz_traverse(FAR struct mfs_sb_s *sb, mfs_t startidx, + mfs_t endidx, mfs_t startpage, FAR mfs_t *endpage); +int mfs_ctz_index_from_off(FAR struct mfs_sb_s *sb, mfs_t offset, + FAR mfs_t *index, FAR mfs_t *pageoff); +int mfs_ctz_fill_next_ptrs(FAR struct mfs_sb_s *sb, mfs_t page, + mfs_t index, FAR uint8_t *buffer); + +mfs_t mfs_page_to_block(FAR const struct mfs_sb_s *sb, mfs_t page); + +int mfs_dirent_traverse(FAR struct mfs_sb_s *sb, FAR const char *relpath, + FAR mfs_t *location, + FAR struct mfs_direntry_s *direntry, + FAR struct mfs_dirloc_s *dirloc); +int mfs_dir_create_file(FAR struct mfs_sb_s *sb, FAR const char *relpath, + mode_t mode, FAR mfs_t *location, + FAR struct mfs_direntry_s *direntry, + FAR struct mfs_dirloc_s *dirloc); +int mfs_dir_update_file(FAR struct mfs_sb_s *sb, mfs_t oldloc, mfs_t newloc, + FAR const struct mfs_dirloc_s *dirloc, + FAR struct mfs_direntry_s *direntry, + off_t length); +int mfs_dir_truncate_file(FAR struct mfs_sb_s *sb, mfs_t location, + FAR const struct mfs_dirloc_s *dirloc, + FAR struct mfs_direntry_s *direntry, + off_t length); + +bool mfs_file_is_open(FAR const struct mfs_sb_s *sb, mfs_t fileloc); + +int mfs_file_open(FAR struct mfs_sb_s *sb, FAR struct file *filep, + FAR const char *relpath, int oflags, mode_t mode); +int mfs_file_close(FAR struct mfs_sb_s *sb, FAR struct file *filep); +ssize_t mfs_file_read(FAR struct mfs_sb_s *sb, FAR struct file *filep, + FAR char *buffer, size_t buflen); +ssize_t mfs_file_write(FAR struct mfs_sb_s *sb, FAR struct file *filep, + FAR const char *buffer, size_t buflen); +off_t mfs_file_seek(FAR struct mfs_sb_s *sb, FAR struct file *filep, + off_t offset, int whence); +int mfs_file_ioctl(FAR struct mfs_sb_s *sb, FAR struct file *filep, + int cmd, unsigned long arg); +int mfs_file_truncate(FAR struct mfs_sb_s *sb, FAR struct file *filep, + off_t length); +int mfs_file_sync(FAR struct mfs_sb_s *sb, FAR struct file *filep); +int mfs_file_dup(FAR struct mfs_sb_s *sb, FAR const struct file *oldp, + FAR struct file *newp); +int mfs_file_fstat(FAR struct mfs_sb_s *sb, FAR const struct file *filep, + FAR struct stat *buf); + +int mfs_dir_opendir(FAR struct mfs_sb_s *sb, FAR const char *relpath, + FAR struct fs_dirent_s **dir); +int mfs_dir_closedir(FAR struct mfs_sb_s *sb, + FAR struct fs_dirent_s *dir); +int mfs_dir_readdir(FAR struct mfs_sb_s *sb, FAR struct fs_dirent_s *dir, + FAR struct dirent *entry); +int mfs_dir_rewinddir(FAR struct mfs_sb_s *sb, + FAR struct fs_dirent_s *dir); +int mfs_dir_unlink(FAR struct mfs_sb_s *sb, FAR const char *relpath); +int mfs_dir_mkdir(FAR struct mfs_sb_s *sb, FAR const char *relpath, + mode_t mode); +int mfs_dir_rmdir(FAR struct mfs_sb_s *sb, FAR const char *relpath); +int mfs_dir_rename(FAR struct mfs_sb_s *sb, FAR const char *oldrelpath, + FAR const char *newrelpath); +int mfs_dir_stat(FAR struct mfs_sb_s *sb, FAR const char *relpath, + FAR struct stat *buf); + +extern const struct mountpt_operations g_mnemofs_operations; #endif /* __FS_MNEMOFS_MNEMOFS_H */ diff --git a/fs/mnemofs/mnemofs_alloc.c b/fs/mnemofs/mnemofs_alloc.c new file mode 100644 index 00000000000..44cda72ea26 --- /dev/null +++ b/fs/mnemofs/mnemofs_alloc.c @@ -0,0 +1,1343 @@ +/**************************************************************************** + * fs/mnemofs/mnemofs_alloc.c + * + * SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + * + * 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 + +#include +#include +#include + +#include "fs_heap.h" +#include "mnemofs.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static bool mfs_bitmap_get(FAR const uint8_t *bitmap, mfs_t page); +static void mfs_bitmap_set(FAR uint8_t *bitmap, mfs_t page); +static void mfs_bitmap_clear(FAR uint8_t *bitmap, mfs_t page); +static mfs_t mfs_first_data_block(FAR const struct mfs_sb_s *sb); +static mfs_t mfs_first_data_page(FAR const struct mfs_sb_s *sb); +static mfs_t mfs_next_data_block(FAR const struct mfs_sb_s *sb, mfs_t block); +static mfs_t mfs_next_data_page(FAR const struct mfs_sb_s *sb, mfs_t page); +static int mfs_alloc_validate_block(FAR const struct mfs_sb_s *sb, + mfs_t block); +static int mfs_alloc_validate_page(FAR const struct mfs_sb_s *sb, + mfs_t page); +static void mfs_alloc_mark_page_used(FAR struct mfs_sb_s *sb, mfs_t page); +static void mfs_alloc_mark_page_free(FAR struct mfs_sb_s *sb, mfs_t page); +static void mfs_alloc_mark_page_deleted(FAR struct mfs_sb_s *sb, mfs_t page); +static void mfs_alloc_mark_block_used(FAR struct mfs_sb_s *sb, mfs_t block); +static void mfs_alloc_mark_block_free(FAR struct mfs_sb_s *sb, mfs_t block); +static void mfs_alloc_mark_block_deleted(FAR struct mfs_sb_s *sb, + mfs_t block); +static bool mfs_alloc_block_is_free(FAR const struct mfs_sb_s *sb, + mfs_t block); +static bool mfs_alloc_block_is_deleted(FAR const struct mfs_sb_s *sb, + mfs_t block); +static int mfs_alloc_reclaim_block(FAR struct mfs_sb_s *sb, mfs_t block); +static int mfs_alloc_page_erased(FAR struct mfs_sb_s *sb, mfs_t page, + FAR bool *erased); +static FAR char *mfs_alloc_child_path(FAR const char *parent, + FAR const char *name); +static int mfs_alloc_reserve_live_tree(FAR struct mfs_sb_s *sb, + FAR const char *relpath); +static int mfs_alloc_choose_start_page(FAR struct mfs_sb_s *sb); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mfs_bitmap_get + * + * Description: + * Return the state of one bit in a page bitmap. + * + * Input Parameters: + * bitmap - The bitmap to inspect. + * page - The page number whose bit is requested. + * + * Returned Value: + * true is returned if the bit is set. false is returned otherwise. + * + ****************************************************************************/ + +static bool mfs_bitmap_get(FAR const uint8_t *bitmap, mfs_t page) +{ + return (bitmap[page >> 3] & (1u << (page & 7))) != 0; +} + +/**************************************************************************** + * Name: mfs_bitmap_set + * + * Description: + * Set one bit in a page bitmap. + * + * Input Parameters: + * bitmap - The bitmap to modify. + * page - The page number whose bit should be set. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void mfs_bitmap_set(FAR uint8_t *bitmap, mfs_t page) +{ + bitmap[page >> 3] |= (1u << (page & 7)); +} + +/**************************************************************************** + * Name: mfs_bitmap_clear + * + * Description: + * Clear one bit in a page bitmap. + * + * Input Parameters: + * bitmap - The bitmap to modify. + * page - The page number whose bit should be cleared. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void mfs_bitmap_clear(FAR uint8_t *bitmap, mfs_t page) +{ + bitmap[page >> 3] &= ~(1u << (page & 7)); +} + +/**************************************************************************** + * Name: mfs_first_data_block + * + * Description: + * Return the first block that may hold live file system data. + * + * Input Parameters: + * sb - The mounted file system instance. + * + * Returned Value: + * The first usable data block is returned on success. + * MFS_LOCATION_INVALID is returned if the geometry is not usable. + * + ****************************************************************************/ + +static mfs_t mfs_first_data_block(FAR const struct mfs_sb_s *sb) +{ + if (sb == NULL || sb->superblock + 1 >= MFS_BLOCK_COUNT(sb)) + { + return MFS_LOCATION_INVALID; + } + + return sb->superblock + 1; +} + +/**************************************************************************** + * Name: mfs_first_data_page + * + * Description: + * Return the first page that may hold live file system data. + * + * Input Parameters: + * sb - The mounted file system instance. + * + * Returned Value: + * The first usable data page is returned on success. + * MFS_LOCATION_INVALID is returned if no usable data block exists. + * + ****************************************************************************/ + +static mfs_t mfs_first_data_page(FAR const struct mfs_sb_s *sb) +{ + mfs_t block; + + block = mfs_first_data_block(sb); + if (block == MFS_LOCATION_INVALID) + { + return MFS_LOCATION_INVALID; + } + + return MFS_BLOCK_TO_PAGE(sb, block); +} + +/**************************************************************************** + * Name: mfs_next_data_block + * + * Description: + * Return the next usable data block in circular allocation order. + * + * Input Parameters: + * sb - The mounted file system instance. + * block - The current block in the scan. + * + * Returned Value: + * The next data block is returned on success. + * MFS_LOCATION_INVALID is returned if no usable data range exists. + * + ****************************************************************************/ + +static mfs_t mfs_next_data_block(FAR const struct mfs_sb_s *sb, mfs_t block) +{ + mfs_t firstblock; + + firstblock = mfs_first_data_block(sb); + if (firstblock == MFS_LOCATION_INVALID) + { + return MFS_LOCATION_INVALID; + } + + block++; + if (block >= MFS_BLOCK_COUNT(sb)) + { + return firstblock; + } + + if (block < firstblock) + { + return firstblock; + } + + return block; +} + +/**************************************************************************** + * Name: mfs_next_data_page + * + * Description: + * Return the next usable data page in circular allocation order. + * + * Input Parameters: + * sb - The mounted file system instance. + * page - The current page in the scan. + * + * Returned Value: + * The next data page is returned on success. + * MFS_LOCATION_INVALID is returned if no usable data range exists. + * + ****************************************************************************/ + +static mfs_t mfs_next_data_page(FAR const struct mfs_sb_s *sb, mfs_t page) +{ + mfs_t firstpage; + + firstpage = mfs_first_data_page(sb); + if (firstpage == MFS_LOCATION_INVALID) + { + return MFS_LOCATION_INVALID; + } + + if (page == MFS_LOCATION_INVALID) + { + return firstpage; + } + + page++; + if (page >= MFS_PAGE_COUNT(sb)) + { + return firstpage; + } + + if (mfs_page_to_block(sb, page) < mfs_first_data_block(sb)) + { + return firstpage; + } + + return page; +} + +/**************************************************************************** + * Name: mfs_alloc_validate_block + * + * Description: + * Validate that block lies inside the allocator-managed data range. + * + * Input Parameters: + * sb - The mounted file system instance. + * block - The block number to validate. + * + * Returned Value: + * Zero (OK) is returned if block is valid. -EINVAL is returned + * otherwise. + * + ****************************************************************************/ + +static int mfs_alloc_validate_block(FAR const struct mfs_sb_s *sb, + mfs_t block) +{ + mfs_t firstblock; + + if (sb == NULL || sb->freepages == NULL || sb->delpages == NULL) + { + ferr("allocator not initialized\n"); + return -EINVAL; + } + + firstblock = mfs_first_data_block(sb); + if (firstblock == MFS_LOCATION_INVALID) + { + ferr("no usable data block\n"); + return -EINVAL; + } + + if (block == MFS_LOCATION_INVALID || block >= MFS_BLOCK_COUNT(sb) || + block < firstblock) + { + ferr("invalid block\n"); + return -EINVAL; + } + + return OK; +} + +/**************************************************************************** + * Name: mfs_alloc_validate_page + * + * Description: + * Validate that page lies inside the allocator-managed data range. + * + * Input Parameters: + * sb - The mounted file system instance. + * page - The page number to validate. + * + * Returned Value: + * Zero (OK) is returned if page is valid. -EINVAL is returned + * otherwise. + * + ****************************************************************************/ + +static int mfs_alloc_validate_page(FAR const struct mfs_sb_s *sb, mfs_t page) +{ + mfs_t block; + + if (sb == NULL || sb->freepages == NULL || sb->delpages == NULL) + { + ferr("allocator not initialized\n"); + return -EINVAL; + } + + if (page == MFS_LOCATION_INVALID || page >= MFS_PAGE_COUNT(sb)) + { + ferr("invalid page\n"); + return -EINVAL; + } + + block = mfs_page_to_block(sb, page); + if (block == MFS_LOCATION_INVALID) + { + ferr("page has no valid block\n"); + return -EINVAL; + } + + return mfs_alloc_validate_block(sb, block); +} + +/**************************************************************************** + * Name: mfs_alloc_mark_page_used + * + * Description: + * Mark one page as allocated in the allocator bitmaps. + * + * Input Parameters: + * sb - The mounted file system instance. + * page - The page number to mark. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void mfs_alloc_mark_page_used(FAR struct mfs_sb_s *sb, mfs_t page) +{ + mfs_bitmap_clear(sb->freepages, page); + mfs_bitmap_clear(sb->delpages, page); +} + +/**************************************************************************** + * Name: mfs_alloc_mark_page_free + * + * Description: + * Mark one page as free in the allocator bitmaps. + * + * Input Parameters: + * sb - The mounted file system instance. + * page - The page number to mark. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void mfs_alloc_mark_page_free(FAR struct mfs_sb_s *sb, mfs_t page) +{ + mfs_bitmap_set(sb->freepages, page); + mfs_bitmap_clear(sb->delpages, page); +} + +/**************************************************************************** + * Name: mfs_alloc_mark_page_deleted + * + * Description: + * Mark one page as pending deletion in the allocator bitmaps. + * + * Input Parameters: + * sb - The mounted file system instance. + * page - The page number to mark. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void mfs_alloc_mark_page_deleted(FAR struct mfs_sb_s *sb, mfs_t page) +{ + mfs_bitmap_clear(sb->freepages, page); + mfs_bitmap_set(sb->delpages, page); +} + +/**************************************************************************** + * Name: mfs_alloc_mark_block_used + * + * Description: + * Mark every page in one block as allocated. + * + * Input Parameters: + * sb - The mounted file system instance. + * block - The block number to mark. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void mfs_alloc_mark_block_used(FAR struct mfs_sb_s *sb, mfs_t block) +{ + mfs_t page; + mfs_t endpage; + + page = MFS_BLOCK_TO_PAGE(sb, block); + endpage = page + MFS_PAGES_PER_BLOCK(sb); + for (; page < endpage; page++) + { + mfs_alloc_mark_page_used(sb, page); + } +} + +/**************************************************************************** + * Name: mfs_alloc_mark_block_free + * + * Description: + * Mark every page in one block as free. + * + * Input Parameters: + * sb - The mounted file system instance. + * block - The block number to mark. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void mfs_alloc_mark_block_free(FAR struct mfs_sb_s *sb, mfs_t block) +{ + mfs_t page; + mfs_t endpage; + + page = MFS_BLOCK_TO_PAGE(sb, block); + endpage = page + MFS_PAGES_PER_BLOCK(sb); + for (; page < endpage; page++) + { + mfs_alloc_mark_page_free(sb, page); + } +} + +/**************************************************************************** + * Name: mfs_alloc_mark_block_deleted + * + * Description: + * Mark every page in one block as pending deletion. + * + * Input Parameters: + * sb - The mounted file system instance. + * block - The block number to mark. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void mfs_alloc_mark_block_deleted(FAR struct mfs_sb_s *sb, + mfs_t block) +{ + mfs_t page; + mfs_t endpage; + + page = MFS_BLOCK_TO_PAGE(sb, block); + endpage = page + MFS_PAGES_PER_BLOCK(sb); + for (; page < endpage; page++) + { + mfs_alloc_mark_page_deleted(sb, page); + } +} + +/**************************************************************************** + * Name: mfs_alloc_block_is_free + * + * Description: + * Check whether every page in block is currently marked free. + * + * Input Parameters: + * sb - The mounted file system instance. + * block - The block number to inspect. + * + * Returned Value: + * true is returned if the whole block is free. false is returned + * otherwise. + * + ****************************************************************************/ + +static bool mfs_alloc_block_is_free(FAR const struct mfs_sb_s *sb, + mfs_t block) +{ + mfs_t page; + mfs_t endpage; + + page = MFS_BLOCK_TO_PAGE(sb, block); + endpage = page + MFS_PAGES_PER_BLOCK(sb); + for (; page < endpage; page++) + { + if (!mfs_bitmap_get(sb->freepages, page)) + { + return false; + } + } + + return true; +} + +/**************************************************************************** + * Name: mfs_alloc_block_is_deleted + * + * Description: + * Check whether every page in block is currently marked deleted. + * + * Input Parameters: + * sb - The mounted file system instance. + * block - The block number to inspect. + * + * Returned Value: + * true is returned if the whole block is pending deletion. false is + * returned otherwise. + * + ****************************************************************************/ + +static bool mfs_alloc_block_is_deleted(FAR const struct mfs_sb_s *sb, + mfs_t block) +{ + mfs_t page; + mfs_t endpage; + + page = MFS_BLOCK_TO_PAGE(sb, block); + endpage = page + MFS_PAGES_PER_BLOCK(sb); + for (; page < endpage; page++) + { + if (!mfs_bitmap_get(sb->delpages, page)) + { + return false; + } + } + + return true; +} + +/**************************************************************************** + * Name: mfs_alloc_reclaim_block + * + * Description: + * Erase one block if every page in it has been marked deleted, then + * return it to the free bitmap. + * + * Input Parameters: + * sb - The mounted file system instance. + * block - The block number to reclaim. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * validation or erase failure. + * + ****************************************************************************/ + +static int mfs_alloc_reclaim_block(FAR struct mfs_sb_s *sb, mfs_t block) +{ + int ret; + + ret = mfs_alloc_validate_block(sb, block); + if (ret < 0) + { + ferr("mfs_alloc_validate_block failed: %d\n", ret); + return ret; + } + + if (!mfs_alloc_block_is_deleted(sb, block)) + { + return OK; + } + + ret = mfs_erase_blocks(sb, block, 1); + if (ret < 0) + { + ferr("mfs_erase_blocks failed: %d\n", ret); + return ret; + } + + if (ret != 1) + { + ferr("short erase: %d\n", ret); + return -EIO; + } + + mfs_alloc_mark_block_free(sb, block); + return OK; +} + +/**************************************************************************** + * Name: mfs_alloc_page_erased + * + * Description: + * Read a page and report whether it is still in the erased state. + * + * Input Parameters: + * sb - The mounted file system instance. + * page - The page number to inspect. + * erased - The location to receive the erased-state result. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mfs_alloc_page_erased(FAR struct mfs_sb_s *sb, mfs_t page, + FAR bool *erased) +{ + ssize_t nread; + size_t i; + + if (sb == NULL || erased == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + nread = mfs_read_page(sb, page, sb->rwbuf); + if (nread < 0) + { + ferr("mfs_read_page failed: %zd\n", nread); + return nread; + } + + if (nread != 1) + { + ferr("short read: %zd\n", nread); + return -EIO; + } + + * erased = true; + for (i = 0; i < MFS_PAGE_SIZE(sb); i++) + { + if (sb->rwbuf[i] != sb->erasestate) + { + * erased = false; + break; + } + } + + return OK; +} + +/**************************************************************************** + * Name: mfs_alloc_child_path + * + * Description: + * Allocate `parent/name` as one new relative path string. + * + * Input Parameters: + * parent - The parent path prefix. + * name - The child name to append. + * + * Returned Value: + * A newly allocated path string is returned on success. NULL is returned + * on invalid input or allocation failure. + * + ****************************************************************************/ + +static FAR char *mfs_alloc_child_path(FAR const char *parent, + FAR const char *name) +{ + FAR char *path; + size_t parentlen; + size_t namelen; + + if (parent == NULL || name == NULL) + { + ferr("invalid args\n"); + return NULL; + } + + parentlen = strlen(parent); + namelen = strlen(name); + path = kmm_malloc(parentlen + namelen + 2); + if (path == NULL) + { + ferr("kmm_malloc failed\n"); + return NULL; + } + + if (parentlen == 0) + { + memcpy(path, name, namelen + 1); + return path; + } + + memcpy(path, parent, parentlen); + path[parentlen] = '/'; + memcpy(path + parentlen + 1, name, namelen + 1); + return path; +} + +/**************************************************************************** + * Name: mfs_alloc_reserve_live_tree + * + * Description: + * Walk the live directory tree rooted at relpath and mark each reachable + * page or block as allocated in the allocator bitmaps. + * + * Input Parameters: + * sb - The mounted file system instance. + * relpath - The relative path that roots the subtree to reserve. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * traversal, allocation, or directory-read failure. + * + ****************************************************************************/ + +static int mfs_alloc_reserve_live_tree(FAR struct mfs_sb_s *sb, + FAR const char *relpath) +{ + struct mfs_direntry_s direntry; + struct mfs_dirloc_s dirloc; + FAR struct fs_dirent_s *dir = NULL; + FAR char *childpath = NULL; + FAR char *dirpath = NULL; + struct dirent entry; + mfs_t location; + int ret; + int closeret; + + ret = mfs_dirent_traverse(sb, relpath, &location, &direntry, &dirloc); + if (ret < 0) + { + ferr("mfs_dirent_traverse failed: %d\n", ret); + return ret; + } + + if (!S_ISDIR(direntry.mode)) + { + mfs_alloc_mark_page_used(sb, location); + return OK; + } + + mfs_alloc_mark_block_used(sb, location); + ret = mfs_dir_opendir(sb, relpath, &dir); + if (ret < 0) + { + ferr("mfs_dir_opendir failed: %d\n", ret); + return ret; + } + + for (; ; ) + { + ret = mfs_dir_readdir(sb, dir, &entry); + if (ret == -ENOENT) + { + ret = OK; + goto errout_with_dir; + } + + if (ret < 0) + { + ferr("mfs_dir_readdir failed: %d\n", ret); + goto errout_with_dir; + } + + childpath = mfs_alloc_child_path(relpath, entry.d_name); + if (childpath == NULL) + { + ferr("mfs_alloc_child_path failed\n"); + ret = -ENOMEM; + goto errout_with_dir; + } + + ret = mfs_alloc_reserve_live_tree(sb, childpath); + kmm_free(childpath); + childpath = NULL; + if (ret < 0) + { + ferr("reserve child failed: %d\n", ret); + goto errout_with_dir; + } + } + +errout_with_dir: + kmm_free(childpath); + if (dir != NULL) + { + dirpath = dir->fd_path; + closeret = mfs_dir_closedir(sb, dir); + fs_heap_free(dirpath); + if (ret >= 0) + { + ret = closeret; + } + } + + if (ret < 0) + { + ferr("failed: %d\n", ret); + } + + return ret; +} + +/**************************************************************************** + * Name: mfs_alloc_choose_start_page + * + * Description: + * Choose a random starting block for future allocation scans. The first + * usable data block found from that random point becomes the next + * allocation position. + * + * Input Parameters: + * sb - The mounted file system instance. + * + * Returned Value: + * Zero (OK) is returned on success. -ENOSPC is returned if no usable + * data block exists. A negated errno value is returned on other + * failures. + * + ****************************************************************************/ + +static int mfs_alloc_choose_start_page(FAR struct mfs_sb_s *sb) +{ + mfs_t firstblock; + mfs_t nblocks; + mfs_t start; + mfs_t block; + mfs_t i; + int bad; + + if (sb == NULL) + { + ferr("invalid sb\n"); + return -EINVAL; + } + + firstblock = mfs_first_data_block(sb); + if (firstblock == MFS_LOCATION_INVALID) + { + ferr("no usable data block\n"); + return -ENOSPC; + } + + nblocks = MFS_BLOCK_COUNT(sb) - firstblock; + start = firstblock + (arc4random() % nblocks); + + for (i = 0, block = start; i < nblocks; i++) + { + bad = mfs_is_bad_block(sb, block); + if (bad < 0) + { + ferr("mfs_is_bad_block failed: %d\n", bad); + return bad; + } + + if (bad == 0) + { + sb->nextpage = MFS_BLOCK_TO_PAGE(sb, block); + return OK; + } + + block = mfs_next_data_block(sb, block); + } + + ferr("no usable start block\n"); + return -ENOSPC; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mfs_alloc_init + * + * Description: + * Initialize allocator state for the mounted volume. This builds the + * free and pending-delete bitmaps, reserves the currently live file + * system tree, and selects a random starting point for future scans. + * + * Input Parameters: + * sb - The mounted file system instance. + * + * Returned Value: + * Zero (OK) is returned on success. -ENOMEM or -ENOSPC may be returned + * for resource exhaustion. A negated errno value is returned on other + * failures. + * + ****************************************************************************/ + +int mfs_alloc_init(FAR struct mfs_sb_s *sb) +{ + mfs_t firstblock; + mfs_t block; + mfs_t page; + mfs_t endpage; + bool erased; + int ret; + int bad; + + if (sb == NULL || sb->rwbuf == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + firstblock = mfs_first_data_block(sb); + if (firstblock == MFS_LOCATION_INVALID) + { + ferr("no usable data block\n"); + return -ENOSPC; + } + + sb->bitmapsize = (MFS_PAGE_COUNT(sb) + 7) / 8; + sb->nextpage = MFS_LOCATION_INVALID; + sb->freepages = kmm_zalloc(sb->bitmapsize); + if (sb->freepages == NULL) + { + ferr("free bitmap allocation failed\n"); + ret = -ENOMEM; + goto errout; + } + + sb->delpages = kmm_zalloc(sb->bitmapsize); + if (sb->delpages == NULL) + { + ferr("deleted bitmap allocation failed\n"); + ret = -ENOMEM; + goto errout; + } + + for (block = 0; block < MFS_BLOCK_COUNT(sb); block++) + { + bad = mfs_is_bad_block(sb, block); + if (bad < 0) + { + ferr("mfs_is_bad_block failed: %d\n", bad); + ret = bad; + goto errout; + } + + if (bad != 0 || block < firstblock) + { + mfs_alloc_mark_block_used(sb, block); + continue; + } + + page = MFS_BLOCK_TO_PAGE(sb, block); + endpage = page + MFS_PAGES_PER_BLOCK(sb); + for (; page < endpage; page++) + { + ret = mfs_alloc_page_erased(sb, page, &erased); + if (ret < 0) + { + ferr("mfs_alloc_page_erased failed: %d\n", ret); + goto errout; + } + + if (erased) + { + mfs_alloc_mark_page_free(sb, page); + } + } + } + + ret = mfs_alloc_reserve_live_tree(sb, ""); + if (ret < 0) + { + ferr("mfs_alloc_reserve_live_tree failed: %d\n", ret); + goto errout; + } + + ret = mfs_alloc_choose_start_page(sb); + if (ret < 0) + { + ferr("mfs_alloc_choose_start_page failed: %d\n", ret); + goto errout; + } + + return OK; + +errout: + ferr("failed: %d\n", ret); + mfs_alloc_uninit(sb); + return ret; +} + +/**************************************************************************** + * Name: mfs_alloc_uninit + * + * Description: + * Release allocator-owned bitmap state. + * + * Input Parameters: + * sb - The mounted file system instance. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void mfs_alloc_uninit(FAR struct mfs_sb_s *sb) +{ + if (sb == NULL) + { + return; + } + + kmm_free(sb->freepages); + kmm_free(sb->delpages); + sb->freepages = NULL; + sb->delpages = NULL; + sb->bitmapsize = 0; + sb->nextpage = MFS_LOCATION_INVALID; +} + +/**************************************************************************** + * Name: mfs_alloc_page + * + * Description: + * Allocate one page using a circular scan that begins at the current + * allocator position. + * + * Input Parameters: + * sb - The mounted file system instance. + * page - The location to receive the allocated page number. + * + * Returned Value: + * Zero (OK) is returned on success. -ENOSPC is returned if no free page + * is available. A negated errno value is returned on other failures. + * + ****************************************************************************/ + +int mfs_alloc_page(FAR struct mfs_sb_s *sb, FAR mfs_t *page) +{ + mfs_t firstblock; + mfs_t candidate; + mfs_t npages; + mfs_t i; + + if (sb == NULL || page == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + * page = MFS_LOCATION_INVALID; + if (sb->freepages == NULL || sb->delpages == NULL || + sb->nextpage == MFS_LOCATION_INVALID) + { + ferr("allocator not initialized\n"); + return -EINVAL; + } + + firstblock = mfs_first_data_block(sb); + if (firstblock == MFS_LOCATION_INVALID) + { + ferr("no usable data block\n"); + return -ENOSPC; + } + + npages = (MFS_BLOCK_COUNT(sb) - firstblock) * + MFS_PAGES_PER_BLOCK(sb); + candidate = sb->nextpage; + + for (i = 0; i < npages; i++) + { + if (mfs_bitmap_get(sb->freepages, candidate)) + { + mfs_alloc_mark_page_used(sb, candidate); + * page = candidate; + sb->nextpage = mfs_next_data_page(sb, candidate); + return OK; + } + + candidate = mfs_next_data_page(sb, candidate); + } + + ferr("no free page\n"); + return -ENOSPC; +} + +/**************************************************************************** + * Name: mfs_alloc_block + * + * Description: + * Allocate one whole directory block. The scan follows the allocator's + * circular position, but accepts only blocks whose every page is free. + * + * Input Parameters: + * sb - The mounted file system instance. + * block - The location to receive the allocated block number. + * + * Returned Value: + * Zero (OK) is returned on success. -ENOSPC is returned if no free + * block is available. A negated errno value is returned on other + * failures. + * + ****************************************************************************/ + +int mfs_alloc_block(FAR struct mfs_sb_s *sb, FAR mfs_t *block) +{ + mfs_t firstblock; + mfs_t candidate; + mfs_t nblocks; + mfs_t i; + + if (sb == NULL || block == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + * block = MFS_LOCATION_INVALID; + if (sb->freepages == NULL || sb->delpages == NULL || + sb->nextpage == MFS_LOCATION_INVALID) + { + ferr("allocator not initialized\n"); + return -EINVAL; + } + + firstblock = mfs_first_data_block(sb); + if (firstblock == MFS_LOCATION_INVALID) + { + ferr("no usable data block\n"); + return -ENOSPC; + } + + candidate = mfs_page_to_block(sb, sb->nextpage); + if (candidate == MFS_LOCATION_INVALID) + { + ferr("invalid next page\n"); + return -EINVAL; + } + + if (sb->nextpage != MFS_BLOCK_TO_PAGE(sb, candidate)) + { + candidate = mfs_next_data_block(sb, candidate); + } + + nblocks = MFS_BLOCK_COUNT(sb) - firstblock; + for (i = 0; i < nblocks; i++) + { + if (mfs_alloc_block_is_free(sb, candidate)) + { + mfs_alloc_mark_block_used(sb, candidate); + * block = candidate; + sb->nextpage = MFS_BLOCK_TO_PAGE(sb, + mfs_next_data_block(sb, + candidate)); + return OK; + } + + candidate = mfs_next_data_block(sb, candidate); + } + + ferr("no free block\n"); + return -ENOSPC; +} + +/**************************************************************************** + * Name: mfs_release_page + * + * Description: + * Return a page to the free bitmap immediately. This is intended for + * rollback of allocations that were not yet committed into directory + * state. + * + * Input Parameters: + * sb - The mounted file system instance. + * page - The page number to release. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int mfs_release_page(FAR struct mfs_sb_s *sb, mfs_t page) +{ + int ret; + + ret = mfs_alloc_validate_page(sb, page); + if (ret < 0) + { + ferr("mfs_alloc_validate_page failed: %d\n", ret); + return ret; + } + + mfs_rwbuf_discard_page(sb, page); + mfs_alloc_mark_page_free(sb, page); + return OK; +} + +/**************************************************************************** + * Name: mfs_report_page_deleted + * + * Description: + * Mark one committed page as obsolete. If this makes every page in the + * containing block obsolete, then the block is erased immediately and + * returned to the free bitmap. + * + * Input Parameters: + * sb - The mounted file system instance. + * page - The page number that became obsolete. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int mfs_report_page_deleted(FAR struct mfs_sb_s *sb, mfs_t page) +{ + mfs_t block; + int ret; + + ret = mfs_alloc_validate_page(sb, page); + if (ret < 0) + { + ferr("mfs_alloc_validate_page failed: %d\n", ret); + return ret; + } + + if (mfs_bitmap_get(sb->freepages, page)) + { + ferr("page already free\n"); + return -EINVAL; + } + + mfs_rwbuf_discard_page(sb, page); + block = mfs_page_to_block(sb, page); + mfs_alloc_mark_page_deleted(sb, page); + ret = mfs_alloc_reclaim_block(sb, block); + if (ret < 0) + { + ferr("mfs_alloc_reclaim_block failed: %d\n", ret); + } + + return ret; +} + +/**************************************************************************** + * Name: mfs_report_block_deleted + * + * Description: + * Mark every page in a committed block as obsolete and reclaim the block + * immediately once all pages in it are scheduled for deletion. + * + * Input Parameters: + * sb - The mounted file system instance. + * block - The block number that became obsolete. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int mfs_report_block_deleted(FAR struct mfs_sb_s *sb, mfs_t block) +{ + int ret; + + ret = mfs_alloc_validate_block(sb, block); + if (ret < 0) + { + ferr("mfs_alloc_validate_block failed: %d\n", ret); + return ret; + } + + mfs_rwbuf_discard_block(sb, block); + mfs_alloc_mark_block_deleted(sb, block); + ret = mfs_alloc_reclaim_block(sb, block); + if (ret < 0) + { + ferr("mfs_alloc_reclaim_block failed: %d\n", ret); + } + + return ret; +} diff --git a/fs/mnemofs/mnemofs_blkalloc.c b/fs/mnemofs/mnemofs_blkalloc.c deleted file mode 100644 index fdf972125b3..00000000000 --- a/fs/mnemofs/mnemofs_blkalloc.c +++ /dev/null @@ -1,580 +0,0 @@ -/**************************************************************************** - * fs/mnemofs/mnemofs_blkalloc.c - * - * SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause - * - * 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" -#include "fs_heap.h" - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -#define BMAP_GET(bmap, idx, off) (((bmap)[(idx)] & (1 << (off))) != 0) -#define BMAP_SET(bmap, idx, off) ((bmap)[(idx)] |= (1 << (off))) -#define DEL_ARR_BLK(sb, blk) (MFS_BA((sb)).k_del[(blk) * sizeof(size_t)]) -#define DEL_ARR_PG(sb, pg) (DEL_ARR_BLK(sb, MFS_PG2BLK((sb), (pg)))) - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -static inline void pg2bmap(mfs_t pg, FAR mfs_t *idx, FAR uint8_t *off); -static int is_pg_writeable(FAR struct mfs_sb_s * const sb, mfs_t pg, - FAR mfs_t *idx, FAR uint8_t *off); -static int is_blk_writeable(FAR struct mfs_sb_s * const sb, - const mfs_t blk); - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/**************************************************************************** - * Public Data - ****************************************************************************/ - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: pg2bmap - * - * Description: - * Gets the bitmap location of a page. The page in the bitmap will be in - * bmap[idx] byte at (1 << off) position in the byte. - * - * Input Parameters: - * pg - Page number to check. - * idx - Populated later with the index of page in MFS_BA(sb).bmap_upgs - * off - Populated later with the offset of page in MFS_BA(sb).bmap_upgs - * - * Assumptions/Limitations: - * Does not check validity of the index. - * - ****************************************************************************/ - -static inline void pg2bmap(mfs_t pg, FAR mfs_t *idx, FAR uint8_t *off) -{ - /* The compiler should automatically use shift operation for division. */ - - *idx = pg / 8; - *off = pg % 8; -} - -/**************************************************************************** - * Name: is_pg_writeable - * - * Description: - * Checks if a page is writeable by checking if the page is either free, or - * it's being used but the entire block is ready for erase. - * - * Input Parameters: - * sb - Superblock instance of the device. - * pg - Page number to check. - * idx - Populated later with the index of page in MFS_BA(sb).bmap_upgs - * off - Populated later with the offset of page in MFS_BA(sb).bmap_upgs - * - * Returned Value: - * MFS_BLK_BAD - If the block of the page is a bad block. - * MFS_PG_USED - If the page is being used. - * MFS_BLK_ERASABLE - If page can be allocated, but block needs erase. - * MFS_PG_FREE - If the page is free. - * -ENOSYS - Not supported. - * - * Assumptions/Limitations: - * Assumes this is run in a locked environment. - * - ****************************************************************************/ - -static int is_pg_writeable(FAR struct mfs_sb_s * const sb, mfs_t pg, - FAR mfs_t *idx, FAR uint8_t *off) -{ - int blkbad_status; - - /* Bad block check. */ - - blkbad_status = mfs_isbadblk(sb, MFS_PG2BLK(sb, pg)); - if (predict_false(blkbad_status == -ENOSYS)) - { - return blkbad_status; - } - - if (predict_false(blkbad_status < 0) || blkbad_status == 1) - { - return MFS_BLK_BAD; - } - - pg2bmap(MFS_BA(sb).c_pg, idx, off); - - if (BMAP_GET(MFS_BA(sb).bmap_upgs, *idx, *off)) - { - if (DEL_ARR_PG(sb, MFS_BA(sb).c_pg) == MFS_PGINBLK(sb)) - { - return MFS_BLK_ERASABLE; - } - else - { - return MFS_PG_USED; - } - } - else - { - return MFS_PG_FREE; - } -} - -/**************************************************************************** - * Name: is_blk_writeable - * - * Description: - * Checks if an entire block is allocatable, either because none of the - * pages in it have been allocated, or because the entire block can be - * erased. - * - * Input Parameters: - * sb - Superblock instance of the device. - * pg - Page number to check. - * idx - Populated later with the index of page in MFS_BA(sb).bmap_upgs - * off - Populated later with the offset of page in MFS_BA(sb).bmap_upgs - * - * Returned Value: - * MFS_BLK_BAD - If the block is a bad block. - * MFS_BLK_USED - If the block is being used. - * MFS_BLK_ERASABLE - If block can be allocated, but block needs erase. - * MFS_BLK_FREE - If the block is free. - * - * Assumptions/Limitations: - * Assumes this is run in a locked environment. - * - ****************************************************************************/ - -static int is_blk_writeable(FAR struct mfs_sb_s * const sb, const mfs_t blk) -{ - int blkbad_status; - mfs_t i; - mfs_t pg = MFS_BLK2PG(sb, blk); - mfs_t idx; - uint8_t off; - - /* Bad block check. */ - - blkbad_status = mfs_isbadblk(sb, blk); - if (predict_false(blkbad_status == -ENOSYS)) - { - return blkbad_status; - } - - if (predict_false(blkbad_status < 0) || blkbad_status == 1) - { - return MFS_BLK_BAD; - } - - for (i = 0; i < MFS_PGINBLK(sb); i++) - { - pg2bmap(pg + i, &idx, &off); - - if (BMAP_GET(MFS_BA(sb).bmap_upgs, idx, off)) - { - if (DEL_ARR_PG(sb, MFS_BA(sb).c_pg) == MFS_PGINBLK(sb)) - { - return MFS_BLK_ERASABLE; - } - else - { - return MFS_BLK_USED; - } - } - } - - return MFS_BLK_FREE; -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -int mfs_ba_fmt(FAR struct mfs_sb_s * const sb) -{ - int ret = OK; - - /* We need at least 5 blocks, as one is occupied by superblock, at least - * one for the journal, 2 for journal's master blocks, and at least one for - * actual data. - */ - - if (MFS_NBLKS(sb) < 5) - { - ret = -ENOSPC; - goto errout; - } - - memset(&MFS_BA(sb), 0, sizeof(MFS_BA(sb))); - - MFS_BA(sb).s_blk = rand() % MFS_NBLKS(sb); - if (MFS_PG2BLK(sb, MFS_BA(sb).s_blk) == sb->sb_blk) - { - MFS_BA(sb).s_blk++; - MFS_BA(sb).s_blk %= MFS_NBLKS(sb); - } - - MFS_BA(sb).c_pg = MFS_BLK2PG(sb, MFS_BA(sb).s_blk); - - /* MFS_BA(sb).k_del_elemsz = ((log + 7) & (-8)) / 8; */ - - MFS_BA(sb).k_del = fs_heap_zalloc(sizeof(size_t) * MFS_NBLKS(sb)); - if (predict_false(MFS_BA(sb).k_del == NULL)) - { - ret = -ENOMEM; - goto errout; - } - - MFS_BA(sb).n_bmap_upgs = MFS_UPPER8(MFS_NPGS(sb)); - - MFS_BA(sb).bmap_upgs = fs_heap_zalloc(MFS_BA(sb).n_bmap_upgs); - if (predict_false(MFS_BA(sb).bmap_upgs == NULL)) - { - ret = -ENOMEM; - goto errout_with_k_del; - } - - /* TODO: Do not start from journal blocks. */ - - finfo("mnemofs: Block Allocator initialized, starting at page %d.\n", - MFS_BLK2PG(sb, MFS_BA(sb).s_blk)); - return ret; - -errout_with_k_del: - fs_heap_free(MFS_BA(sb).k_del); - -errout: - return ret; -} - -int mfs_ba_init(FAR struct mfs_sb_s * const sb) -{ - /* TODO: Ensure journal and master node are initialized before this. */ - - int ret = OK; - - ret = mfs_ba_fmt(sb); - if (predict_false(ret < 0)) - { - goto errout; - } - - /* Traverse the FS tree. */ - - ret = mfs_pitr_traversefs(sb, MFS_MN(sb).root_ctz, MFS_ISDIR); - if (predict_false(ret < 0)) - { - goto errout_with_ba; - } - - return ret; - -errout_with_ba: - mfs_ba_free(sb); - -errout: - return ret; -} - -void mfs_ba_free(FAR struct mfs_sb_s * const sb) -{ - fs_heap_free(MFS_BA(sb).k_del); - fs_heap_free(MFS_BA(sb).bmap_upgs); - - finfo("Block Allocator Freed."); -} - -mfs_t mfs_ba_getpg(FAR struct mfs_sb_s * const sb) -{ - bool inc = true; - bool found = false; - mfs_t i = MFS_BA(sb).c_pg; - mfs_t pg = 0; - mfs_t idx; - mfs_t tpgs = MFS_NBLKS(sb) * MFS_PGINBLK(sb); - uint8_t off; - - for (; i != tpgs; i++) - { - switch (is_pg_writeable(sb, MFS_BA(sb).c_pg, &idx, &off)) - { - case MFS_PG_USED: - finfo("Used %d\n", MFS_BA(sb).c_pg); - break; - - case MFS_PG_FREE: - finfo("Free %d\n", MFS_BA(sb).c_pg); - pg = MFS_BA(sb).c_pg; - mfs_ba_markusedpg(sb, pg); - found = true; - break; - - case MFS_BLK_BAD: - finfo("Bad %d\n", MFS_BA(sb).c_pg); - - /* Skip pages to next block. */ - - MFS_BA(sb).c_pg = MFS_BLK2PG(sb, - (MFS_PG2BLK(sb, MFS_BA(sb).c_pg) + 1) % - MFS_NBLKS(sb)); - inc = false; - break; - - case MFS_BLK_ERASABLE: - finfo("Erasable %d\n", MFS_BA(sb).c_pg); - pg = MFS_BA(sb).c_pg; - mfs_erase_blk(sb, MFS_PG2BLK(sb, MFS_BA(sb).c_pg)); - DEL_ARR_PG(sb, MFS_BA(sb).c_pg) = 0; - mfs_ba_markusedpg(sb, pg); - found = true; - break; - - case -ENOSYS: - - /* TODO: Manually check for bad blocks. */ - - return 0; - } - - if (inc) - { - MFS_BA(sb).c_pg++; - MFS_BA(sb).c_pg %= tpgs; - } - else - { - i--; - inc = true; - } - - if (found) - { - break; - } - } - - if (!found) - { - DEBUGASSERT(pg == 0); - finfo("No more pages found. Page: %u.", pg); - } - - return pg; -} - -mfs_t mfs_ba_getblk(FAR struct mfs_sb_s * const sb) -{ - bool found = false; - mfs_t i = 0; - mfs_t blk; - mfs_t ret = 0; - - blk = MFS_PG2BLK(sb, MFS_BA(sb).c_pg); - if (MFS_BA(sb).c_pg % MFS_PGINBLK(sb)) - { - /* Skipped pages are not updated in used. */ - - blk++; - blk %= MFS_NBLKS(sb); - i++; - } - - for (; i < MFS_NBLKS(sb); i++) - { - switch (is_blk_writeable(sb, blk)) - { - case MFS_BLK_BAD: - break; - - case MFS_BLK_USED: - break; - - case MFS_BLK_ERASABLE: - mfs_ba_blkmarkdel(sb, blk); - mfs_ba_markusedblk(sb, blk); - found = true; - break; - - case MFS_BLK_FREE: - mfs_ba_markusedblk(sb, blk); - found = true; - break; - - case -ENOSYS: - - /* TODO: Manually check for bad blocks. */ - - return 0; - } - - if (found) - { - break; - } - - blk++; - blk %= MFS_NBLKS(sb); - } - - if (found) - { - ret = blk; - MFS_BA(sb).c_pg = MFS_BLK2PG(sb, (++blk) % MFS_NBLKS(sb)); - } - - finfo("Block number: %u. Found: %d.", ret, found); - - return ret; -} - -void mfs_ba_pgmarkdel(FAR struct mfs_sb_s * const sb, mfs_t pg) -{ - DEL_ARR_PG(sb, MFS_BA(sb).c_pg)++; -} - -void mfs_ba_blkmarkdel(FAR struct mfs_sb_s * const sb, mfs_t blk) -{ - DEL_ARR_BLK(sb, blk) = MFS_PGINBLK(sb); -} - -int mfs_ba_delmarked(FAR struct mfs_sb_s * const sb) -{ - int ret = OK; - mfs_t i; - - for (i = 1; i < MFS_NBLKS(sb); i++) - { - if (DEL_ARR_BLK(sb, i) == MFS_PGINBLK(sb)) - { - ret = mfs_erase_blk(sb, i); - - if (ret != OK) - { - return ret; - } - } - } - - return ret; -} - -/* Mark a page as being used. Used by master node during initial format and - */ - -void mfs_ba_markusedpg(FAR struct mfs_sb_s * const sb, mfs_t pg) -{ - mfs_t idx; - uint8_t off; - - pg2bmap(pg, &idx, &off); - BMAP_SET(MFS_BA(sb).bmap_upgs, idx, off); /* Set as used */ -} - -void mfs_ba_markusedblk(FAR struct mfs_sb_s * const sb, mfs_t blk) -{ - mfs_t i = 0; - mfs_t pg = MFS_BLK2PG(sb, blk); - - for (i = 0; i < MFS_PGINBLK(sb); i++) - { - mfs_ba_markusedpg(sb, pg + i); - } -} - -mfs_t mfs_ba_getavailpgs(FAR const struct mfs_sb_s * const sb) -{ - /* TODO */ - - return 0; -} diff --git a/fs/mnemofs/mnemofs_ctz.c b/fs/mnemofs/mnemofs_ctz.c index 5f84dcd4427..96595d77ce6 100644 --- a/fs/mnemofs/mnemofs_ctz.c +++ b/fs/mnemofs/mnemofs_ctz.c @@ -50,613 +50,490 @@ * ****************************************************************************/ -/**************************************************************************** - * In mnemofs, the files and directories use the CTZ skip list data structure - * defined by littlefs. These are reverse skip lists with a specific number - * of pointers for each block. The number of pointers for a block at index - * `x` is `ctz(x) + 1`. There are no pointers if the index is 0. - * - * The pointers all point to some CTZ block other than the CTZ block they are - * part of. The `k`th pointer of a CTZ block at index `x` points to the - * CTZ block at index `x - 2^k`. - * - * For example, CTZ block at index 2 has 2 pointers, and they point to the - * block at index 1, and index 0 respectively. - * - * File/Dir Ptr - * | - * V - * +------+ +------+ +------+ +------+ +------+ +------+ - * | |<--| |---| |---| |---| | | | - * | Node |<--| Node |---| Node |<--| Node |---| Node | | Node | - * | 0 |<--| 1 |<--| 2 |<--| 3 |<--| 4 |<--| 5 | - * +------+ +------+ +------+ +------+ +------+ +------+ - * - * In mnemofs, each CTZ block is stored in a page on the flash. All code in - * this entire file will call CTZ blocks as blocks to honour the original - * naming, and will specify wherever it deviates from this assumption. - * - * Littlefs's design documentation lists all the benefits that this data - * structure brings to the table when it comes to storing large pieces of - * data that will be modified considerably frequently, while being in a - * Copy On Write (CoW) environment. - * - * In mnemofs, the CTZ methods only interface with the underlying R/W methods - * , journal on the lower side and on the upper side, the LRU, and ensures - * that whatever data it provides considers both the on-flash data, as well - * the journal logs. - * - * The pointers are stored such that the first pointer, which points to - * (x - 2^0), is stored at the very end of the CTZ block. The second pointer - * is stored second last, and so on. - * - ****************************************************************************/ - /**************************************************************************** * Included Files ****************************************************************************/ -#include -#include +#include #include -#include -#include -#include +#include +#include #include "mnemofs.h" -#include "fs_heap.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ -#define MFS_CTZ_PTRSZ (sizeof(mfs_t)) - -/**************************************************************************** - * Private Types - ****************************************************************************/ +#define MFS_CTZ_PTR_SIZE sizeof(mfs_t) /**************************************************************************** * Private Function Prototypes ****************************************************************************/ -static mfs_t ctz_idx_nptrs(const mfs_t idx); -static void ctz_off2loc(FAR const struct mfs_sb_s * const sb, mfs_t off, - FAR mfs_t *idx, FAR mfs_t *pgoff); -static mfs_t ctz_blkdatasz(FAR const struct mfs_sb_s * const sb, - const mfs_t idx); -static void ctz_copyidxptrs(FAR const struct mfs_sb_s * const sb, - struct mfs_ctz_s ctz, const mfs_t idx, - FAR char *buf); - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/**************************************************************************** - * Public Data - ****************************************************************************/ +static int mfs_ctz_nptrs(mfs_t index); +static int mfs_ctz_read_ptr(FAR struct mfs_sb_s *sb, mfs_t page, + unsigned int pow, FAR uint8_t *scratch, + FAR mfs_t *ptrpage); +static void mfs_ctz_write_ptr(FAR struct mfs_sb_s *sb, FAR uint8_t *buffer, + unsigned int pow, mfs_t page); +static int mfs_ctz_follow(FAR struct mfs_sb_s *sb, mfs_t startidx, + mfs_t endidx, mfs_t startpage, + FAR uint8_t *scratch, + FAR mfs_t *endpage); /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** - * Name: ctz_idx_nptrs + * Name: mfs_ctz_nptrs * * Description: - * Gives the numbers of pointers that a CTZ block of given index should - * have. + * Return the number of backward pointers stored in CTZ unit index. * * Input Parameters: - * idx - Index of the ctz block. + * index - The CTZ unit index to inspect. * * Returned Value: - * The number of pointers in the CTZ block. + * The number of pointers stored in the unit is returned. * ****************************************************************************/ -static mfs_t ctz_idx_nptrs(const mfs_t idx) +static int mfs_ctz_nptrs(mfs_t index) { - mfs_t ret; + int nptrs; - ret = (idx == 0) ? 0 : mfs_ctz(idx) + 1; - finfo("Number of pointers for %u index is %u.", idx, ret); - return ret; + if (index == 0) + { + return 0; + } + + nptrs = 1; + while ((index & 1) == 0) + { + nptrs++; + index >>= 1; + } + + return nptrs; } /**************************************************************************** - * Name: ctz_off2loc + * Name: mfs_ctz_read_ptr * * Description: - * Converts ctz offset (which is the offset of the data stored in the ctz - * list, which is unaware of the presence of pointers) into the CTZ - * block index and the offset in that CTZ block. + * Read one backward pointer from the pointer area of a CTZ unit. * * Input Parameters: - * sb - Superblock instance of the device. - * off - Offset of the data stored in the CTZ list. - * idx - Index of the CTZ block, to be populated. - * pgoff - Offset inside the CTZ block, to be populated. - * - ****************************************************************************/ - -static void ctz_off2loc(FAR const struct mfs_sb_s * const sb, mfs_t off, - FAR mfs_t *idx, FAR mfs_t *pgoff) -{ - const mfs_t wb = sizeof(mfs_t); - const mfs_t den = MFS_PGSZ(sb) - 2 * wb; - - if (off < den) - { - *idx = 0; - *pgoff = off; - return; - } - - if (idx != NULL) - { - *idx = (off - wb * (__builtin_popcount((off / den) - 1) + 2)) / den; - } - - if (pgoff != NULL) - { - *pgoff = off - den * (*idx) - wb * __builtin_popcount(*idx) - - (ctz_idx_nptrs(*idx) * wb); - } - - finfo("Offset %u. Calculated index %u and page offset %u.", off, *idx, - *pgoff); -} - -/**************************************************************************** - * Name: ctz_blkdatasz - * - * Description: - * The size of data in B that can be fit inside a CTZ block at index `idx`. - * - * Input Parameters: - * sb - Superblock instance of the device. - * idx - Index of the ctz block. + * sb - The mounted file system instance. + * page - The CTZ unit page to read. + * pow - The pointer slot number to decode. + * scratch - A page-sized scratch buffer. + * ptrpage - The location to receive the decoded pointer target page. * * Returned Value: - * The size of data in the CTZ block. + * Zero (OK) is returned on success. A negated errno value is returned if + * the inputs are invalid, the page cannot be read, or the decoded pointer + * is outside the valid page range. * ****************************************************************************/ -static mfs_t ctz_blkdatasz(FAR const struct mfs_sb_s * const sb, - const mfs_t idx) +static int mfs_ctz_read_ptr(FAR struct mfs_sb_s *sb, mfs_t page, + unsigned int pow, FAR uint8_t *scratch, + FAR mfs_t *ptrpage) { - mfs_t ret; + size_t offset; + ssize_t nread; + mfs_t raw; - ret = MFS_PGSZ(sb) - (ctz_idx_nptrs(idx) * MFS_LOGPGSZ(sb)); - finfo("Block data size for index %u is %u.", idx, ret); - return ret; + if (sb == NULL || scratch == NULL || ptrpage == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + if (page == MFS_LOCATION_INVALID || page >= MFS_PAGE_COUNT(sb)) + { + ferr("invalid page\n"); + return -EINVAL; + } + + if ((pow + 1) * MFS_CTZ_PTR_SIZE > MFS_PAGE_SIZE(sb)) + { + ferr("invalid pointer slot\n"); + return -EINVAL; + } + + nread = mfs_read_page(sb, page, scratch); + if (nread < 0) + { + ferr("mfs_read_page failed: %zd\n", nread); + return nread; + } + + if (nread != 1) + { + ferr("short read: %zd\n", nread); + return -EIO; + } + + offset = MFS_PAGE_SIZE(sb) - ((pow + 1) * MFS_CTZ_PTR_SIZE); + memcpy(&raw, scratch + offset, sizeof(raw)); + * ptrpage = le32toh(raw); + if (*ptrpage == MFS_LOCATION_INVALID || *ptrpage >= MFS_PAGE_COUNT(sb)) + { + ferr("invalid pointer target\n"); + return -EIO; + } + + return OK; } /**************************************************************************** - * Name: ctz_copyidxptrs + * Name: mfs_ctz_write_ptr * * Description: - * This is used for cases when you want to expand a CTZ list from any point - * in the list. If we want to expand the CTZ list from a particular index, - * say `start_idx`, while keeping all indexes before it untouched, we - * would need to first allocate new blocks on the flash, and then copy - * the pointers to the location. - * - * Usage of this function is, the caller needs to first allocate a CTZ - * block (a page on flash), allocate buffer which is the size of a CTZ - * block (a page on flash), and use this method to copy the pointers to the - * buffer, then write the data to the flash. + * Encode one backward pointer into the pointer area of a CTZ unit buffer. * * Input Parameters: - * sb - Superblock instance of the device. - * ctz - CTZ list to use as a reference. - * idx - Index of the block who's supposed pointers are to be copied. - * buf - Buffer representing the entire CTZ block where pointers are - * copied to. + * sb - The mounted file system instance. + * buffer - The page-sized CTZ unit buffer to modify. + * pow - The pointer slot number to write. + * page - The target page to encode. * - * Assumptions/Limitations: - * This assumes `idx` is not more than `ctz->idx_e + 1`. + * Returned Value: + * None. * ****************************************************************************/ -static void ctz_copyidxptrs(FAR const struct mfs_sb_s * const sb, - struct mfs_ctz_s ctz, const mfs_t idx, - FAR char *buf) +static void mfs_ctz_write_ptr(FAR struct mfs_sb_s *sb, FAR uint8_t *buffer, + unsigned int pow, mfs_t page) { - mfs_t i; - mfs_t n_ptrs; - mfs_t prev_pg; - mfs_t prev_idx; + size_t offset; + mfs_t raw; - if (idx == 0) + DEBUGASSERT(sb != NULL && buffer != NULL); + DEBUGASSERT((pow + 1) * MFS_CTZ_PTR_SIZE <= MFS_PAGE_SIZE(sb)); + + offset = MFS_PAGE_SIZE(sb) - ((pow + 1) * MFS_CTZ_PTR_SIZE); + raw = htole32(page); + memcpy(buffer + offset, &raw, sizeof(raw)); +} + +/**************************************************************************** + * Name: mfs_ctz_follow + * + * Description: + * Walk backward through a CTZ chain from startidx/startpage until endidx + * is reached. + * + * Input Parameters: + * sb - The mounted file system instance. + * startidx - The index of the known CTZ unit. + * endidx - The target earlier index to reach. + * startpage - The page of the known CTZ unit. + * scratch - A page-sized scratch buffer. + * endpage - The location to receive the page at endidx. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * invalid input or if any pointer read fails. + * + ****************************************************************************/ + +static int mfs_ctz_follow(FAR struct mfs_sb_s *sb, mfs_t startidx, + mfs_t endidx, mfs_t startpage, + FAR uint8_t *scratch, + FAR mfs_t *endpage) +{ + mfs_t diff; + mfs_t page; + mfs_t idx; + unsigned int pow; + unsigned int maxpow; + int ret; + + if (sb == NULL || scratch == NULL || endpage == NULL) { - /* No pointers for first block. */ - - return; + ferr("invalid args\n"); + return -EINVAL; } - n_ptrs = ctz_idx_nptrs(idx); - - if (idx != ctz.idx_e + 1) + if (startpage == MFS_LOCATION_INVALID || startpage >= MFS_PAGE_COUNT(sb) || + startidx < endidx) { - /* We travel to the second last "known" CTZ block. */ - - ctz.pg_e = mfs_ctz_travel(sb, ctz.idx_e, ctz.pg_e, idx - 1); - ctz.idx_e = idx - 1; + ferr("invalid traversal request\n"); + return -EINVAL; } - buf += MFS_PGSZ(sb); /* Go to buf + pg_sz */ - - DEBUGASSERT(idx == ctz.idx_e + 1); - - finfo("Copying %u pointers for CTZ (%u, %u) at index %u.", n_ptrs, - ctz.idx_e, ctz.pg_e, idx); - - for (i = 0; i < n_ptrs; i++) + page = startpage; + idx = startidx; + while (idx > endidx) { - if (predict_false(i == 0)) + diff = idx - endidx; + pow = (sizeof(diff) * 8) - 1 - __builtin_clz(diff); + maxpow = __builtin_ctz(idx); + if (pow > maxpow) { - prev_idx = ctz.idx_e; - prev_pg = ctz.pg_e; - } - else - { - prev_pg = mfs_ctz_travel(sb, prev_idx, prev_pg, prev_idx - 1); - prev_idx--; + pow = maxpow; } - ctz.idx_e = prev_idx; + DEBUGASSERT(pow < (unsigned int)mfs_ctz_nptrs(idx)); + ret = mfs_ctz_read_ptr(sb, page, pow, scratch, &page); + if (ret < 0) + { + ferr("mfs_ctz_read_ptr failed: %d\n", ret); + return ret; + } - /* Do buf + pg_sz - (idx * sizeof(mfs_t)) iteratively. */ - - buf -= MFS_CTZ_PTRSZ; - mfs_ser_mfs(prev_pg, buf); - - finfo("Copied %u page number to %uth pointer.", prev_pg, i); + idx -= (mfs_t)1 << pow; } + + * endpage = page; + return OK; } /**************************************************************************** * Public Functions ****************************************************************************/ -int mfs_ctz_rdfromoff(FAR const struct mfs_sb_s * const sb, - const struct mfs_ctz_s ctz, mfs_t data_off, - mfs_t len, FAR char * buf) +/**************************************************************************** + * Name: mfs_ctz_traverse + * + * Description: + * Traverse a CTZ chain from one known unit index/page to an earlier + * index. + * + * Input Parameters: + * sb - The mounted file system instance. + * startidx - The index of the known CTZ unit. + * endidx - The earlier index to reach. + * startpage - The page of the known CTZ unit. + * endpage - The location to receive the resolved page. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * invalid input, allocation failure, or traversal failure. + * + ****************************************************************************/ + +int mfs_ctz_traverse(FAR struct mfs_sb_s *sb, mfs_t startidx, mfs_t endidx, + mfs_t startpage, FAR mfs_t *endpage) { - int ret = OK; - mfs_t i; - mfs_t cur_pg; - mfs_t cur_idx; - mfs_t cur_pgoff; - mfs_t end_idx; - mfs_t end_pgoff; - mfs_t pg_rd_sz; + FAR uint8_t *scratch; + int ret; - finfo("Reading (%u, %u) CTZ from %u offset for %u bytes.", ctz.idx_e, - ctz.pg_e, data_off, len); - - if (ctz.idx_e == 0 && ctz.pg_e == 0) + if (sb == NULL || endpage == NULL || startpage == MFS_LOCATION_INVALID || + startpage >= MFS_PAGE_COUNT(sb) || startidx < endidx) { - goto errout; + ferr("invalid args\n"); + return -EINVAL; } - ctz_off2loc(sb, data_off + len, &cur_idx, &cur_pgoff); - ctz_off2loc(sb, data_off, &end_idx, &end_pgoff); - - DEBUGASSERT(ctz.idx_e < cur_idx); /* TODO: Need to consider this. For now, there is a temporary fix in read(). */ - if (ctz.idx_e < end_idx) + scratch = kmm_malloc(MFS_PAGE_SIZE(sb)); + if (scratch == NULL) { - goto errout; + ferr("kmm_malloc failed\n"); + return -ENOMEM; } - cur_pg = mfs_ctz_travel(sb, ctz.idx_e, ctz.pg_e, cur_idx); - - if (predict_false(cur_pg == 0)) + ret = mfs_ctz_follow(sb, startidx, endidx, startpage, scratch, endpage); + if (ret < 0) { - goto errout; + ferr("mfs_ctz_follow failed: %d\n", ret); } - /* O(n) read by reading in reverse. */ - - finfo("Started reading. Current Idx: %u, End Idx: %u.", cur_idx, end_idx); - - if (cur_idx != end_idx) - { - for (i = cur_idx; i >= end_idx; i--) - { - finfo("Current index %u, Current Page %u.", i, cur_pg); - - if (predict_false(i == cur_idx)) - { - pg_rd_sz = cur_pgoff; - ret = mfs_read_page(sb, buf - pg_rd_sz, pg_rd_sz, cur_pg, - 0); - cur_pgoff = 0; - } - else if (predict_false(i == end_idx)) - { - pg_rd_sz = ctz_blkdatasz(sb, i) - end_pgoff; - ret = mfs_read_page(sb, buf - pg_rd_sz, pg_rd_sz, cur_pg, - end_pgoff); - } - else - { - pg_rd_sz = ctz_blkdatasz(sb, i); - ret = mfs_read_page(sb, buf - pg_rd_sz, pg_rd_sz, cur_pg, - 0); - } - - if (predict_false(ret == 0)) - { - ret = -EINVAL; - goto errout; - } - - buf -= pg_rd_sz; - } - - cur_pg = mfs_ctz_travel(sb, cur_idx, cur_pg, cur_idx - 1); - if (predict_false(cur_pg == 0)) - { - ret = -EINVAL; - goto errout; - } - } - else - { - ret = mfs_read_page(sb, buf, len, cur_pg, end_pgoff); - if (predict_false(ret < 0)) - { - goto errout; - } - - ret = OK; - } - - finfo("Reading finished."); - -errout: + kmm_free(scratch); return ret; } -int mfs_ctz_wrtnode(FAR struct mfs_sb_s * const sb, - FAR const struct mfs_node_s * const node, - FAR struct mfs_ctz_s *new_loc) +/**************************************************************************** + * Name: mfs_ctz_unit_data_area + * + * Description: + * Return the number of payload bytes available in CTZ unit index after + * its backward pointers are reserved. + * + * Input Parameters: + * sb - The mounted file system instance. + * index - The CTZ unit index to inspect. + * + * Returned Value: + * The number of payload bytes available in the unit is returned. + * + ****************************************************************************/ + +mfs_t mfs_ctz_unit_data_area(FAR struct mfs_sb_s *sb, mfs_t index) { - int ret = OK; - bool written = false; - mfs_t prev; - mfs_t rem_sz; - mfs_t new_pg; - mfs_t cur_pg; - mfs_t cur_idx; - mfs_t cur_pgoff; - mfs_t lower; - mfs_t upper; - mfs_t upper_og; - mfs_t lower_upd; - mfs_t upper_upd; - mfs_t del_bytes; - FAR char *buf = NULL; - FAR char *tmp = NULL; - struct mfs_ctz_s ctz; - FAR struct mfs_delta_s *delta; - - finfo("Write LRU node %p at depth %u.", node, node->depth); - - /* Traverse common CTZ blocks. */ - - ctz_off2loc(sb, node->range_min, &cur_idx, &cur_pgoff); - ctz = node->path[node->depth - 1].ctz; - cur_pg = mfs_ctz_travel(sb, ctz.idx_e, ctz.pg_e, cur_idx); - - /* So, till cur_idx - 1, the CTZ blocks are common. */ - - buf = fs_heap_zalloc(MFS_PGSZ(sb)); - if (predict_false(buf == NULL)) - { - ret = -ENOMEM; - goto errout; - } - - /* Initially, there might be some offset in cur_idx CTZ blocks that is - * unmodified as well. - */ - - finfo("Initial read."); - tmp = buf; - mfs_read_page(sb, tmp, cur_pgoff, cur_pg, 0); - tmp += cur_pgoff; - - /* Modifications. */ - - prev = 0; - rem_sz = node->sz; - lower = node->range_min; - del_bytes = 0; - - /* [lower, upper) range. Two pointer approach. Window gets narrower - * for every delete falling inside it. - */ - - while (rem_sz > 0) - { - upper = MIN(prev + lower + ctz_blkdatasz(sb, cur_idx), rem_sz); - upper_og = upper; - - finfo("Remaining Size %" PRIu32 ". Lower %" PRIu32 ", Upper %" PRIu32 - ", Current Offset %zd.", rem_sz, lower, upper, tmp - buf); - - /* Retrieving original data. */ - - ret = mfs_ctz_rdfromoff(sb, ctz, lower + del_bytes, upper - lower, - tmp); - if (predict_false(ret < 0)) - { - goto errout_with_buf; - } - - list_for_every_entry(&node->delta, delta, struct mfs_delta_s, list) - { - finfo("Checking delta %p in node %p. Offset %" PRIu32 ", bytes %" - PRIu32, delta, node, delta->off, delta->n_b); - - lower_upd = MAX(lower, delta->off); - upper_upd = MIN(upper, delta->off + delta->n_b); - - if (lower_upd >= upper_upd) - { - /* Skip this delta. */ - - continue; - } - - if (delta->upd == NULL) - { - finfo("Node type: Delete"); - - /* Delete */ - - del_bytes += upper_upd - lower_upd; - memmove(tmp + (lower_upd - lower), tmp + (upper_upd - lower), - upper - upper_upd); - upper -= upper_upd; - } - else - { - finfo("Node type: Update"); - - /* Update */ - - memcpy(tmp + (lower_upd - lower), - delta->upd + (lower_upd - delta->off), - upper_upd - lower_upd); - } - } - - /* rem_sz check for final write. */ - - if (upper == upper_og || rem_sz == upper - lower) - { - prev = 0; - - /* Time to write a page for new CTZ list. */ - - new_pg = mfs_ba_getpg(sb); - if (predict_false(new_pg == 0)) - { - ret = -ENOSPC; - goto errout_with_buf; - } - - ctz_copyidxptrs(sb, ctz, cur_idx, buf); - - ret = mfs_write_page(sb, buf, MFS_PGSZ(sb), new_pg, 0); - if (predict_false(ret == 0)) - { - ret = -EINVAL; - goto errout_with_buf; - } - - memset(buf, 0, MFS_PGSZ(sb)); - tmp = buf; - ctz.idx_e = cur_idx; - ctz.pg_e = new_pg; - cur_idx++; - - written = true; - - finfo("Written data to page %" PRIu32, new_pg); - } - else - { - tmp += upper - lower; - - written = false; - } - - prev = upper - lower; - rem_sz -= upper - lower; - lower = upper; - } - - DEBUGASSERT(written); - - /* TODO: Need to verify for cases where the delete extends outside, etc. */ - - /* Write log. Assumes journal has enough space due to the limit. */ - - finfo("Writing log."); - *new_loc = ctz; - ret = mfs_jrnl_wrlog(sb, node, ctz, node->sz); - if (predict_false(ret < 0)) - { - goto errout_with_buf; - } - -errout_with_buf: - fs_heap_free(buf); - -errout: - return ret; + return (mfs_t)MFS_PAGE_SIZE(sb) - ((mfs_t)mfs_ctz_nptrs(index) * 4); } -mfs_t mfs_ctz_travel(FAR const struct mfs_sb_s * const sb, - mfs_t idx_src, mfs_t pg_src, mfs_t idx_dest) +/**************************************************************************** + * Name: mfs_ctz_index_from_off + * + * Description: + * Convert a logical file offset into the CTZ unit index and byte offset + * inside that unit. + * + * Input Parameters: + * sb - The mounted file system instance. + * offset - The logical file offset to translate. + * index - The location to receive the CTZ unit index. + * pageoff - The location to receive the byte offset within that unit. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * invalid input or overflow. + * + ****************************************************************************/ + +int mfs_ctz_index_from_off(FAR struct mfs_sb_s *sb, mfs_t offset, + FAR mfs_t *index, + FAR mfs_t *pageoff) { - char buf[4]; - mfs_t pg; mfs_t idx; - mfs_t pow; - mfs_t diff; - mfs_t max_pow; + mfs_t area; - /* Rising phase. */ - - max_pow = (sizeof(mfs_t) * 8) - mfs_clz(idx_src ^ idx_dest); - idx = idx_src; - pow = 1; - pg = pg_src; - - for (pow = mfs_ctz(idx); pow < max_pow - 1; pow = mfs_ctz(idx)) + if (sb == NULL || index == NULL || pageoff == NULL) { - mfs_read_page(sb, buf, 4, pg, MFS_PGSZ(sb) - (4 * pow)); - mfs_deser_mfs(buf, &pg); - idx -= (1 << pow); + ferr("invalid args\n"); + return -EINVAL; + } - if (pg == 0) + idx = 0; + while (true) + { + area = mfs_ctz_unit_data_area(sb, idx); + if (area == 0) { - return 0; + ferr("zero data area\n"); + return -EINVAL; } - } - if (idx == idx_dest) - { - return pg; - } - - /* Falling phase. */ - - diff = idx - idx_dest; - - for (pow = mfs_set_msb(diff); diff != 0; pow = mfs_set_msb(diff)) - { - mfs_read_page(sb, buf, 4, pg, MFS_PGSZ(sb) - (4 * pow)); - mfs_deser_mfs(buf, &pg); - idx -= (1 << pow); - diff -= (1 << pow); - - if (pg == 0) + if (offset < area) { - return 0; + * index = idx; + * pageoff = offset; + return OK; } + + offset -= area; + if (idx == UINT32_MAX) + { + ferr("offset overflow\n"); + return -EOVERFLOW; + } + + idx++; } - - finfo("Travel from index %" PRIu32 " at page %" PRIu32 " to index %" PRIu32 - " at page %" PRIu32 ".", idx_src, pg_src, idx_dest, pg); - - return pg; +} + +/**************************************************************************** + * Name: mfs_ctz_fill_next_ptrs + * + * Description: + * Populate the pointer area in a page-sized buffer as though the buffer + * were the CTZ unit immediately after the known unit at `page`/`index`. + * + * For every pointer required by unit `index + 1`, this walks backward from + * the known unit `index` to the target `(index + 1) - 2^pow`. + * + * Input Parameters: + * sb - The mounted file system instance. + * page - The location of the known CTZ unit at `index`. + * index - The index of the known CTZ unit. + * buffer - Caller-supplied page-sized buffer to receive the speculative + * pointer bytes. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int mfs_ctz_fill_next_ptrs(FAR struct mfs_sb_s *sb, mfs_t page, + mfs_t index, FAR uint8_t *buffer) +{ + FAR uint8_t *scratch; + mfs_t targetidx; + mfs_t targetpage; + mfs_t nextidx; + unsigned int pow; + unsigned int nptrs; + int ret; + + if (sb == NULL || buffer == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + if (page == MFS_LOCATION_INVALID || page >= MFS_PAGE_COUNT(sb)) + { + ferr("invalid page\n"); + return -EINVAL; + } + + if (index == UINT32_MAX) + { + ferr("index overflow\n"); + return -EOVERFLOW; + } + + scratch = kmm_malloc(MFS_PAGE_SIZE(sb)); + if (scratch == NULL) + { + ferr("kmm_malloc failed\n"); + return -ENOMEM; + } + + nextidx = index + 1; + nptrs = mfs_ctz_nptrs(nextidx); + for (pow = 0; pow < nptrs; pow++) + { + targetidx = index - (((mfs_t)1 << pow) - 1); + ret = mfs_ctz_follow(sb, index, targetidx, page, scratch, &targetpage); + if (ret < 0) + { + ferr("mfs_ctz_follow failed: %d\n", ret); + goto errout; + } + + mfs_ctz_write_ptr(sb, buffer, pow, targetpage); + } + + kmm_free(scratch); + return OK; + +errout: + kmm_free(scratch); + return ret; +} + +/**************************************************************************** + * Name: mfs_page_to_block + * + * Description: + * Convert a page number into the block that contains it. + * + * Input Parameters: + * sb - The mounted file system instance. + * page - The page number to translate. + * + * Returned Value: + * The containing block number is returned on success. + * MFS_LOCATION_INVALID is returned if the inputs are invalid. + * + ****************************************************************************/ + +mfs_t mfs_page_to_block(FAR const struct mfs_sb_s *sb, mfs_t page) +{ + if (sb == NULL || page == MFS_LOCATION_INVALID || + page >= MFS_PAGE_COUNT(sb)) + { + return MFS_LOCATION_INVALID; + } + + return page / MFS_PAGES_PER_BLOCK(sb); } diff --git a/fs/mnemofs/mnemofs_dirent.c b/fs/mnemofs/mnemofs_dirent.c new file mode 100644 index 00000000000..0011699cfe1 --- /dev/null +++ b/fs/mnemofs/mnemofs_dirent.c @@ -0,0 +1,3249 @@ +/**************************************************************************** + * fs/mnemofs/mnemofs_dirent.c + * + * SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + * + * 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 + +#include +#include +#include + +#include "fs_heap.h" +#include "mnemofs.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static FAR const char *mfs_direntry_name(FAR const char *relpath); +static size_t mfs_direntry_namelen( + FAR const struct mfs_direntry_s *direntry); +static bool mfs_direntry_name_equal( + FAR const struct mfs_direntry_s *direntry, + FAR const char *name); +static uint32_t mfs_deterministic_hash(FAR const uint8_t *arr, size_t sz); +static uint8_t mfs_folded_hash8(FAR const uint8_t *arr, size_t sz); +static uint8_t mfs_dirmeta_checksum(FAR const struct mfs_dirmeta_s *dirmeta); +static bool mfs_dirmeta_is_valid(FAR const struct mfs_dirmeta_s *dirmeta); +static void mfs_dirmeta_init(FAR struct mfs_dirmeta_s *dirmeta, + mfs_t parent); +static int mfs_dirmeta_read(FAR struct mfs_sb_s *sb, mfs_t directory, + FAR struct mfs_dirmeta_s *dirmeta); +static int mfs_dirmeta_write(FAR struct mfs_sb_s *sb, mfs_t directory, + mfs_t parent); +static FAR const char *mfs_skip_slashes(FAR const char *path); +static int mfs_next_path_component(FAR const char *path, FAR char *name, + FAR const char **next); +static FAR char *mfs_dir_pathdup(FAR const char *relpath); +static void mfs_dirloc_invalidate(FAR struct mfs_dirloc_s *dirloc); +static bool mfs_dirloc_is_valid(FAR const struct mfs_sb_s *sb, mfs_t block, + FAR const struct mfs_dirloc_s *dirloc); +static void mfs_dirloc_start(FAR const struct mfs_sb_s *sb, mfs_t block, + FAR struct mfs_dirloc_s *dirloc); +static int mfs_dirloc_next(FAR const struct mfs_sb_s *sb, mfs_t block, + FAR struct mfs_dirloc_s *dirloc); +static int mfs_direntry_read(FAR struct mfs_sb_s *sb, + FAR const struct mfs_dirloc_s *dirloc, + FAR struct mfs_direntry_s *direntry); +static void mfs_root_direntry(FAR const struct mfs_sb_s *sb, + FAR struct mfs_direntry_s *direntry); +static void mfs_stat_from_direntry(FAR const struct mfs_sb_s *sb, + mfs_t location, + FAR const struct mfs_direntry_s *direntry, + FAR struct stat *buf); +static void mfs_direntry_to_dirent(FAR const struct mfs_direntry_s *direntry, + FAR struct dirent *entry); +static int mfs_dirloc_parent_block(FAR const struct mfs_sb_s *sb, + FAR const struct mfs_dirloc_s *dirloc, + FAR mfs_t *block); +static int mfs_dir_follow(FAR struct mfs_sb_s *sb, mfs_t directory, + FAR const struct mfs_dirloc_s *startloc, + FAR const struct mfs_direntry_s *startentry, + FAR struct mfs_direntry_s *latest, + FAR struct mfs_dirloc_s *latestloc); +static int mfs_dir_lookup(FAR struct mfs_sb_s *sb, mfs_t directory, + FAR const char *name, FAR mfs_t *location, + FAR struct mfs_direntry_s *direntry, + FAR struct mfs_dirloc_s *dirloc); +static int mfs_dir_lookup_location(FAR struct mfs_sb_s *sb, mfs_t directory, + mfs_t location, + FAR struct mfs_direntry_s *direntry, + FAR struct mfs_dirloc_s *dirloc); +static int mfs_path_parent_dir(FAR struct mfs_sb_s *sb, + FAR const char *relpath, + FAR mfs_t *directory); +static int mfs_parent_dir(FAR struct mfs_sb_s *sb, mfs_t location, + FAR const struct mfs_dirloc_s *dirloc, + FAR mfs_t *directory); +static int mfs_same_directory(FAR struct mfs_sb_s *sb, + FAR const char *newrelpath, + mfs_t location, + FAR const struct mfs_dirloc_s *dirloc, + FAR mfs_t *directory); +static uint16_t mfs_direntry_namehash(FAR const char *name, size_t namelen); +static uint8_t mfs_direntry_checksum( + FAR const struct mfs_direntry_s *direntry); +static bool mfs_direntry_is_valid(FAR const struct mfs_direntry_s *direntry); +static int mfs_direntry_init(FAR struct mfs_direntry_s *direntry, + mode_t mode, FAR const char *relpath); +static int mfs_direntry_set_name(FAR struct mfs_direntry_s *direntry, + FAR const char *relpath); +static void mfs_direntry_seal(FAR struct mfs_direntry_s *direntry); +static int mfs_direntry_add_noflush( + FAR struct mfs_sb_s *sb, mfs_t directory, + FAR const struct mfs_direntry_s *direntry); +static int mfs_dir_reparent_children(FAR struct mfs_sb_s *sb, + mfs_t directory, mfs_t parent); +static int mfs_dir_refresh_open_files(FAR struct mfs_sb_s *sb, + mfs_t olddir, mfs_t newdir); +static int mfs_direntry_flush(FAR struct mfs_sb_s *sb, mfs_t directory, + FAR const struct mfs_direntry_s *direntry); +static int mfs_direntry_add(FAR struct mfs_sb_s *sb, mfs_t directory, + FAR const struct mfs_direntry_s *direntry); +static int mfs_dir_is_empty(FAR struct mfs_sb_s *sb, mfs_t directory); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mfs_direntry_name + * + * Description: + * Return a pointer to the final path component in relpath. + * + * Input Parameters: + * relpath - The relative path to inspect. + * + * Returned Value: + * A pointer to the last path component is returned on success. NULL is + * returned if relpath is NULL. + * + ****************************************************************************/ + +static FAR const char *mfs_direntry_name(FAR const char *relpath) +{ + FAR const char *name; + + if (relpath == NULL) + { + return NULL; + } + + name = strrchr(relpath, '/'); + return name == NULL ? relpath : name + 1; +} + +/**************************************************************************** + * Name: mfs_direntry_namelen + * + * Description: + * Return the bounded length of the name stored in a direntry. + * + * Input Parameters: + * direntry - The direntry whose name length is requested. + * + * Returned Value: + * The string length of the direntry name is returned. Zero is returned + * if direntry is NULL. + * + ****************************************************************************/ + +static size_t mfs_direntry_namelen(FAR const struct mfs_direntry_s *direntry) +{ + if (direntry == NULL) + { + return 0; + } + + return strnlen(direntry->name, sizeof(direntry->name)); +} + +/**************************************************************************** + * Name: mfs_direntry_name_equal + * + * Description: + * Compare the stored direntry name with a caller-provided name. + * + * Input Parameters: + * direntry - The direntry to compare. + * name - The candidate name. + * + * Returned Value: + * true is returned if both names are valid and equal. false is returned + * if either input is invalid or the names differ. + * + ****************************************************************************/ + +static bool mfs_direntry_name_equal( + FAR const struct mfs_direntry_s *direntry, + FAR const char *name) +{ + size_t dirnamelen; + size_t namelen; + + if (direntry == NULL || name == NULL) + { + return false; + } + + namelen = strnlen(name, MFS_NAME_LEN); + if (namelen == 0 || namelen > MFS_NAME_MAX) + { + return false; + } + + dirnamelen = mfs_direntry_namelen(direntry); + return dirnamelen == namelen && + memcmp(direntry->name, name, namelen) == 0; +} + +/**************************************************************************** + * Name: mfs_deterministic_hash + * + * Description: + * Compute a stable 32-bit hash over a byte array. + * + * Input Parameters: + * arr - The byte array to hash. + * sz - The number of bytes in arr. + * + * Returned Value: + * The computed hash value is returned. Zero is returned if arr is NULL. + * + ****************************************************************************/ + +static uint32_t mfs_deterministic_hash(FAR const uint8_t *arr, size_t sz) +{ + uint32_t distance; + uint32_t hash; + uint32_t product; + uint32_t reverse; + uint32_t shift_amount; + uint32_t term1; + uint32_t term2; + size_t midpoint; + size_t reverse_shift; + size_t i; + + if (arr == NULL) + { + return 0; + } + + midpoint = sz / 2; + hash = 0; + + for (i = 0; i < sz; i++) + { + term1 = arr[i] + (uint32_t)i; + reverse = arr[sz - i - 1]; + reverse_shift = sz - i - 1; + term2 = reverse_shift >= 32 ? 0 : reverse >> reverse_shift; + product = term1 * term2; + distance = (uint32_t)(midpoint > i ? midpoint - i : i - midpoint); + shift_amount = i % 32; + hash += (product ^ distance) << shift_amount; + } + + return hash; +} + +/**************************************************************************** + * Name: mfs_folded_hash8 + * + * Description: + * Fold the deterministic 32-bit hash down to one byte. + * + * Input Parameters: + * arr - The byte array to hash. + * sz - The number of bytes in arr. + * + * Returned Value: + * The folded 8-bit hash value is returned. + * + ****************************************************************************/ + +static uint8_t mfs_folded_hash8(FAR const uint8_t *arr, size_t sz) +{ + return (uint8_t)(mfs_deterministic_hash(arr, sz) % + ((uint32_t)UINT8_MAX + 1)); +} + +/**************************************************************************** + * Name: mfs_dirmeta_checksum + * + * Description: + * Compute the checksum stored in a directory metadata record. + * + * Input Parameters: + * dirmeta - The directory metadata record to checksum. + * + * Returned Value: + * The computed checksum value is returned. + * + ****************************************************************************/ + +static uint8_t mfs_dirmeta_checksum(FAR const struct mfs_dirmeta_s *dirmeta) +{ + return mfs_folded_hash8((FAR const uint8_t *)dirmeta, + sizeof(*dirmeta) - 1); +} + +/**************************************************************************** + * Name: mfs_dirmeta_is_valid + * + * Description: + * Verify the magic and checksum stored in a directory metadata record. + * + * Input Parameters: + * dirmeta - The directory metadata record to validate. + * + * Returned Value: + * true is returned if dirmeta contains a valid directory metadata + * record. false is returned otherwise. + * + ****************************************************************************/ + +static bool mfs_dirmeta_is_valid(FAR const struct mfs_dirmeta_s *dirmeta) +{ + if (dirmeta == NULL) + { + return false; + } + + if (dirmeta->magic != MFS_DIR_MAGIC) + { + return false; + } + + if (dirmeta->checksum != mfs_dirmeta_checksum(dirmeta)) + { + return false; + } + + return true; +} + +/**************************************************************************** + * Name: mfs_dirmeta_init + * + * Description: + * Initialize one in-memory directory metadata record. + * + * Input Parameters: + * dirmeta - The metadata record to initialize. + * parent - The parent directory block number to store. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void mfs_dirmeta_init(FAR struct mfs_dirmeta_s *dirmeta, + mfs_t parent) +{ + if (dirmeta == NULL) + { + return; + } + + memset(dirmeta, 0, sizeof(*dirmeta)); + dirmeta->magic = MFS_DIR_MAGIC; + dirmeta->parent = parent; + dirmeta->checksum = mfs_dirmeta_checksum(dirmeta); +} + +/**************************************************************************** + * Name: mfs_dirmeta_read + * + * Description: + * Read directory metadata from the first page of a directory block. + * + * Input Parameters: + * sb - The mounted file system instance. + * directory - The directory block number. + * dirmeta - The location to receive the decoded metadata. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mfs_dirmeta_read(FAR struct mfs_sb_s *sb, mfs_t directory, + FAR struct mfs_dirmeta_s *dirmeta) +{ + mfs_t page; + ssize_t nread; + + if (sb == NULL || dirmeta == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + if (directory == MFS_LOCATION_INVALID || directory >= MFS_BLOCK_COUNT(sb)) + { + ferr("invalid directory block\n"); + return -EINVAL; + } + + page = MFS_BLOCK_TO_PAGE(sb, directory); + nread = mfs_read_page(sb, page, sb->rwbuf); + if (nread < 0) + { + ferr("mfs_read_page failed: %zd\n", nread); + return nread; + } + + if (nread != 1) + { + ferr("short read: %zd\n", nread); + return -EIO; + } + + memcpy(dirmeta, sb->rwbuf, sizeof(*dirmeta)); + if (!mfs_dirmeta_is_valid(dirmeta)) + { + ferr("invalid directory metadata\n"); + return -EIO; + } + + return OK; +} + +/**************************************************************************** + * Name: mfs_dirmeta_write + * + * Description: + * Write directory metadata into the first page of a directory block. + * + * Input Parameters: + * sb - The mounted file system instance. + * directory - The directory block number. + * parent - The parent directory block number. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mfs_dirmeta_write(FAR struct mfs_sb_s *sb, mfs_t directory, + mfs_t parent) +{ + struct mfs_dirmeta_s dirmeta; + mfs_t page; + int ret; + ssize_t nwritten; + + if (sb == NULL) + { + ferr("invalid sb\n"); + return -EINVAL; + } + + if (directory == MFS_LOCATION_INVALID || directory >= MFS_BLOCK_COUNT(sb)) + { + ferr("invalid directory block\n"); + return -EINVAL; + } + + mfs_dirmeta_init(&dirmeta, parent); + ret = mfs_rwbuf_prepare_write(sb); + if (ret < 0) + { + ferr("mfs_rwbuf_prepare_write failed: %d\n", ret); + return ret; + } + + memset(sb->rwbuf, 0, MFS_PAGE_SIZE(sb)); + memcpy(sb->rwbuf, &dirmeta, sizeof(dirmeta)); + + page = MFS_BLOCK_TO_PAGE(sb, directory); + nwritten = mfs_write_page(sb, page, sb->rwbuf); + if (nwritten < 0) + { + ferr("mfs_write_page failed: %zd\n", nwritten); + return nwritten; + } + + if (nwritten != 1) + { + ferr("short write: %zd\n", nwritten); + return -EIO; + } + + return OK; +} + +/**************************************************************************** + * Name: mfs_skip_slashes + * + * Description: + * Advance past any leading slash characters in path. + * + * Input Parameters: + * path - The path pointer to normalize. + * + * Returned Value: + * A pointer to the first non-slash character is returned. If path is + * NULL, then NULL is returned. + * + ****************************************************************************/ + +static FAR const char *mfs_skip_slashes(FAR const char *path) +{ + while (path != NULL && *path == '/') + { + path++; + } + + return path; +} + +/**************************************************************************** + * Name: mfs_next_path_component + * + * Description: + * Decode one path component and advance next to the following component. + * + * Input Parameters: + * path - The path string to parse. + * name - The buffer that receives the decoded component. + * next - The location that receives the pointer to the remaining path. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned + * if the inputs are invalid or the component exceeds MFS_NAME_MAX. + * + ****************************************************************************/ + +static int mfs_next_path_component(FAR const char *path, FAR char *name, + FAR const char **next) +{ + size_t len; + + if (path == NULL || name == NULL || next == NULL) + { + return -EINVAL; + } + + memset(name, 0, MFS_NAME_LEN); + path = mfs_skip_slashes(path); + if (path == NULL) + { + return -EINVAL; + } + + if (*path == '\0') + { + * next = path; + return OK; + } + + len = strcspn(path, "/"); + if (len > MFS_NAME_MAX) + { + return -ENAMETOOLONG; + } + + memcpy(name, path, len); + name[len] = '\0'; + * next = mfs_skip_slashes(path + len); + return OK; +} + +/**************************************************************************** + * Name: mfs_dir_pathdup + * + * Description: + * Duplicate a directory path string for an open directory handle. + * + * Input Parameters: + * relpath - The relative path to duplicate. + * + * Returned Value: + * A heap-allocated copy of relpath is returned on success. If relpath is + * empty, then a copy of "/" is returned. NULL is returned on allocation + * failure. + * + ****************************************************************************/ + +static FAR char *mfs_dir_pathdup(FAR const char *relpath) +{ + FAR const char *src; + FAR char *dst; + size_t len; + + src = relpath; + if (src == NULL || *src == '\0') + { + src = "/"; + } + + len = strlen(src) + 1; + dst = fs_heap_malloc(len); + if (dst == NULL) + { + return NULL; + } + + memcpy(dst, src, len); + return dst; +} + +/**************************************************************************** + * Name: mfs_dirloc_invalidate + * + * Description: + * Mark a dirloc as invalid. + * + * Input Parameters: + * dirloc - The dirloc to invalidate. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void mfs_dirloc_invalidate(FAR struct mfs_dirloc_s *dirloc) +{ + if (dirloc == NULL) + { + return; + } + + dirloc->page = MFS_LOCATION_INVALID; + dirloc->offset = MFS_DIRENT_OFF_INVALID; +} + +/**************************************************************************** + * Name: mfs_dirloc_is_valid + * + * Description: + * Check whether a dirloc refers to a valid slot inside one directory + * block. + * + * Input Parameters: + * sb - The mounted file system instance. + * block - The directory block that should own dirloc. + * dirloc - The dirloc to validate. + * + * Returned Value: + * true is returned if dirloc references a valid direntry slot inside the + * directory block. false is returned otherwise. + * + ****************************************************************************/ + +static bool mfs_dirloc_is_valid(FAR const struct mfs_sb_s *sb, mfs_t block, + FAR const struct mfs_dirloc_s *dirloc) +{ + mfs_t firstpage; + mfs_t lastpage; + + if (sb == NULL || dirloc == NULL) + { + return false; + } + + if (block == MFS_LOCATION_INVALID || block >= MFS_BLOCK_COUNT(sb)) + { + return false; + } + + if (dirloc->page == MFS_LOCATION_INVALID || + dirloc->offset == MFS_DIRENT_OFF_INVALID) + { + return false; + } + + if (dirloc->offset + sizeof(struct mfs_direntry_s) > MFS_PAGE_SIZE(sb)) + { + return false; + } + + firstpage = MFS_BLOCK_TO_PAGE(sb, block) + 1; + lastpage = MFS_BLOCK_TO_PAGE(sb, block) + MFS_PAGES_PER_BLOCK(sb) - 1; + return firstpage <= lastpage && + dirloc->page >= firstpage && dirloc->page <= lastpage; +} + +/**************************************************************************** + * Name: mfs_dirloc_start + * + * Description: + * Initialize a dirloc so it points at the first direntry slot in a + * directory block. + * + * Input Parameters: + * sb - The mounted file system instance. + * block - The directory block to scan. + * dirloc - The dirloc to initialize. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void mfs_dirloc_start(FAR const struct mfs_sb_s *sb, mfs_t block, + FAR struct mfs_dirloc_s *dirloc) +{ + if (dirloc == NULL) + { + return; + } + + if (sb == NULL || MFS_PAGES_PER_BLOCK(sb) < 2) + { + mfs_dirloc_invalidate(dirloc); + return; + } + + dirloc->page = MFS_BLOCK_TO_PAGE(sb, block) + 1; + dirloc->offset = 0; +} + +/**************************************************************************** + * Name: mfs_dirloc_next + * + * Description: + * Advance a dirloc to the next direntry slot within the same directory + * block. + * + * Input Parameters: + * sb - The mounted file system instance. + * block - The directory block being scanned. + * dirloc - The dirloc to advance. + * + * Returned Value: + * Zero (OK) is returned on success. -ENOENT is returned if dirloc is + * already at the end of the directory block or otherwise invalid. + * + ****************************************************************************/ + +static int mfs_dirloc_next(FAR const struct mfs_sb_s *sb, mfs_t block, + FAR struct mfs_dirloc_s *dirloc) +{ + mfs_t nextoff; + mfs_t lastpage; + + if (!mfs_dirloc_is_valid(sb, block, dirloc)) + { + return -ENOENT; + } + + nextoff = dirloc->offset + sizeof(struct mfs_direntry_s); + if (nextoff + sizeof(struct mfs_direntry_s) <= MFS_PAGE_SIZE(sb)) + { + dirloc->offset = nextoff; + return OK; + } + + lastpage = MFS_BLOCK_TO_PAGE(sb, block) + MFS_PAGES_PER_BLOCK(sb) - 1; + if (dirloc->page >= lastpage) + { + mfs_dirloc_invalidate(dirloc); + return -ENOENT; + } + + dirloc->page++; + dirloc->offset = 0; + return OK; +} + +/**************************************************************************** + * Name: mfs_direntry_read + * + * Description: + * Read one direntry record from the location identified by dirloc. + * + * Input Parameters: + * sb - The mounted file system instance. + * dirloc - The direntry location to read. + * direntry - The location that receives the decoded direntry. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned if + * dirloc is invalid or the underlying page read fails. + * + ****************************************************************************/ + +static int mfs_direntry_read(FAR struct mfs_sb_s *sb, + FAR const struct mfs_dirloc_s *dirloc, + FAR struct mfs_direntry_s *direntry) +{ + ssize_t nread; + + if (sb == NULL || dirloc == NULL || direntry == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + if (dirloc->page == MFS_LOCATION_INVALID || + dirloc->offset + sizeof(*direntry) > MFS_PAGE_SIZE(sb)) + { + ferr("invalid dirloc\n"); + return -EINVAL; + } + + nread = mfs_read_page(sb, dirloc->page, sb->rwbuf); + if (nread < 0) + { + ferr("mfs_read_page failed: %zd\n", nread); + return nread; + } + + if (nread != 1) + { + ferr("short read: %zd\n", nread); + return -EIO; + } + + memcpy(direntry, sb->rwbuf + dirloc->offset, sizeof(*direntry)); + return OK; +} + +/**************************************************************************** + * Name: mfs_page_is_erased + * + * Description: + * Check whether a page buffer is still entirely in the device erase + * state. + * + * Input Parameters: + * sb - The mounted file system instance. + * pagebuf - The page buffer to inspect. + * + * Returned Value: + * true is returned if every byte matches sb->erasestate. false is + * returned otherwise. + * + ****************************************************************************/ + +static bool mfs_page_is_erased(FAR const struct mfs_sb_s *sb, + FAR const uint8_t *pagebuf) +{ + size_t i; + + if (sb == NULL || pagebuf == NULL) + { + return false; + } + + for (i = 0; i < MFS_PAGE_SIZE(sb); i++) + { + if (pagebuf[i] != sb->erasestate) + { + return false; + } + } + + return true; +} + +/**************************************************************************** + * Name: mfs_root_direntry + * + * Description: + * Build the synthetic direntry that represents the mounted root + * directory. + * + * Input Parameters: + * sb - The mounted file system instance. + * direntry - The location that receives the synthetic root direntry. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void mfs_root_direntry(FAR const struct mfs_sb_s *sb, + FAR struct mfs_direntry_s *direntry) +{ + memset(direntry, 0, sizeof(*direntry)); + direntry->magic = MFS_DIRENT_MAGIC; + direntry->mode = S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | + S_IROTH | S_IXOTH; + direntry->newloc = sb->rootdir; + direntry->meta.size = MFS_BLOCK_SIZE(sb); + mfs_direntry_seal(direntry); +} + +/**************************************************************************** + * Name: mfs_stat_from_direntry + * + * Description: + * Populate a struct stat from one live direntry record. + * + * Input Parameters: + * sb - The mounted file system instance. + * location - The live object location. + * direntry - The direntry that describes the object. + * buf - The location that receives the converted stat data. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void mfs_stat_from_direntry(FAR const struct mfs_sb_s *sb, + mfs_t location, + FAR const struct mfs_direntry_s *direntry, + FAR struct stat *buf) +{ + memset(buf, 0, sizeof(*buf)); + buf->st_ino = location; + buf->st_mode = (mode_t)direntry->mode; + buf->st_nlink = 1; + buf->st_size = S_ISDIR(direntry->mode) ? + MFS_BLOCK_SIZE(sb) : direntry->meta.size; + buf->st_atim.tv_sec = direntry->mtime; + buf->st_mtim.tv_sec = direntry->mtime; + buf->st_ctim.tv_sec = direntry->ctime; + buf->st_blksize = MFS_PAGE_SIZE(sb); + buf->st_blocks = (buf->st_size + buf->st_blksize - 1) / + buf->st_blksize; +} + +/**************************************************************************** + * Name: mfs_direntry_to_dirent + * + * Description: + * Convert one direntry record into a POSIX dirent. + * + * Input Parameters: + * direntry - The direntry to convert. + * entry - The location that receives the dirent view. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void mfs_direntry_to_dirent(FAR const struct mfs_direntry_s *direntry, + FAR struct dirent *entry) +{ + size_t namelen; + + memset(entry, 0, sizeof(*entry)); + namelen = mfs_direntry_namelen(direntry); + if (namelen >= sizeof(entry->d_name)) + { + namelen = sizeof(entry->d_name) - 1; + } + + memcpy(entry->d_name, direntry->name, namelen); + entry->d_name[namelen] = '\0'; + entry->d_type = S_ISDIR(direntry->mode) ? DTYPE_DIRECTORY : DTYPE_FILE; +} + +/**************************************************************************** + * Name: mfs_dirloc_parent_block + * + * Description: + * Derive the parent directory block number from a direntry location. + * + * Input Parameters: + * sb - The mounted file system instance. + * dirloc - The direntry location inside the parent directory block. + * block - The location that receives the parent block number. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned if + * the inputs are invalid or dirloc does not map to a valid block. + * + ****************************************************************************/ + +static int mfs_dirloc_parent_block(FAR const struct mfs_sb_s *sb, + FAR const struct mfs_dirloc_s *dirloc, + FAR mfs_t *block) +{ + if (sb == NULL || dirloc == NULL || block == NULL) + { + return -EINVAL; + } + + * block = mfs_page_to_block(sb, dirloc->page); + if (*block == MFS_LOCATION_INVALID) + { + return -EINVAL; + } + + return OK; +} + +/**************************************************************************** + * Name: mfs_dir_follow + * + * Description: + * Follow one directory-entry history chain from its first record to the + * latest reachable record. + * + * Input Parameters: + * sb - The mounted file system instance. + * directory - The directory block that owns the chain. + * startloc - The location of the first record in the chain. + * startentry - The first record in the chain. + * latest - The location to receive the latest record contents. + * latestloc - The location to receive the latest record address. + * + * Returned Value: + * Zero (OK) is returned on success. -ENOENT is returned when the chain + * does not lead to a live location. A negated errno value is returned on + * other failures. + * + ****************************************************************************/ + +static int mfs_dir_follow(FAR struct mfs_sb_s *sb, mfs_t directory, + FAR const struct mfs_dirloc_s *startloc, + FAR const struct mfs_direntry_s *startentry, + FAR struct mfs_direntry_s *latest, + FAR struct mfs_dirloc_s *latestloc) +{ + struct mfs_dirloc_s scanloc; + struct mfs_direntry_s current; + struct mfs_direntry_s candidate; + int ret; + + if (sb == NULL || startloc == NULL || startentry == NULL || + latest == NULL || latestloc == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + memcpy(¤t, startentry, sizeof(current)); + memcpy(latest, startentry, sizeof(*latest)); + memcpy(latestloc, startloc, sizeof(*latestloc)); + + /* A tombstone ends this history chain. Later direntries with + * oldloc == MFS_LOCATION_INVALID are fresh heads, not successors. + */ + + if (current.newloc == MFS_LOCATION_INVALID) + { + return -ENOENT; + } + + scanloc = *startloc; + ret = mfs_dirloc_next(sb, directory, &scanloc); + while (ret == OK) + { + ret = mfs_direntry_read(sb, &scanloc, &candidate); + if (ret < 0) + { + ferr("mfs_direntry_read failed: %d\n", ret); + return ret; + } + + if (mfs_direntry_is_valid(&candidate) && + candidate.oldloc == current.newloc) + { + memcpy(¤t, &candidate, sizeof(current)); + memcpy(latest, &candidate, sizeof(*latest)); + memcpy(latestloc, &scanloc, sizeof(*latestloc)); + + if (current.newloc == MFS_LOCATION_INVALID) + { + return -ENOENT; + } + } + + ret = mfs_dirloc_next(sb, directory, &scanloc); + } + + return OK; +} + +/**************************************************************************** + * Name: mfs_dir_lookup + * + * Description: + * Locate the latest live direntry for one name inside a directory block. + * The search scans only chain starting points and then follows each chain + * according to the oldloc/newloc rule. + * + * Input Parameters: + * sb - The mounted file system instance. + * directory - The directory block number. + * name - The entry name to search for. + * location - The location to receive the latest object location. + * direntry - The location to receive the latest direntry contents. + * dirloc - The location to receive the latest direntry address. + * + * Returned Value: + * Zero (OK) is returned on success. -ENOENT is returned if the named + * entry is not found. A negated errno value is returned on other + * failures. + * + ****************************************************************************/ + +static int mfs_dir_lookup(FAR struct mfs_sb_s *sb, mfs_t directory, + FAR const char *name, FAR mfs_t *location, + FAR struct mfs_direntry_s *direntry, + FAR struct mfs_dirloc_s *dirloc) +{ + struct mfs_dirloc_s scanloc; + struct mfs_direntry_s candidate; + struct mfs_direntry_s latest; + struct mfs_dirloc_s latestloc; + uint16_t namehash; + int ret; + + if (sb == NULL || name == NULL || location == NULL || + direntry == NULL || dirloc == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + if (directory == MFS_LOCATION_INVALID || directory >= MFS_BLOCK_COUNT(sb)) + { + return -ENOENT; + } + + namehash = mfs_direntry_namehash(name, strlen(name)); + mfs_dirloc_start(sb, directory, &scanloc); + + while (mfs_dirloc_is_valid(sb, directory, &scanloc)) + { + ret = mfs_direntry_read(sb, &scanloc, &candidate); + if (ret < 0) + { + ferr("mfs_direntry_read failed: %d\n", ret); + return ret; + } + + if (mfs_direntry_is_valid(&candidate) && + candidate.oldloc == MFS_LOCATION_INVALID) + { + ret = mfs_dir_follow(sb, directory, &scanloc, &candidate, + &latest, &latestloc); + if (ret < 0 && ret != -ENOENT) + { + ferr("mfs_dir_follow failed: %d\n", ret); + } + + if (ret == OK && + latest.namehash == namehash && + mfs_direntry_name_equal(&latest, name)) + { + * location = latest.newloc; + memcpy(direntry, &latest, sizeof(*direntry)); + memcpy(dirloc, &latestloc, sizeof(*dirloc)); + return OK; + } + } + + ret = mfs_dirloc_next(sb, directory, &scanloc); + if (ret < 0) + { + break; + } + } + + return -ENOENT; +} + +/**************************************************************************** + * Name: mfs_dir_lookup_location + * + * Description: + * Locate the latest live direntry whose resolved location matches the + * requested location. + * + * Input Parameters: + * sb - The mounted file system instance. + * directory - The directory block number to search. + * location - The live object location to match. + * direntry - The location that receives the latest direntry contents. + * dirloc - The location that receives the latest direntry address. + * + * Returned Value: + * Zero (OK) is returned on success. -ENOENT is returned if no live + * direntry resolves to location. A negated errno value is returned on + * other failures. + * + ****************************************************************************/ + +static int mfs_dir_lookup_location(FAR struct mfs_sb_s *sb, mfs_t directory, + mfs_t location, + FAR struct mfs_direntry_s *direntry, + FAR struct mfs_dirloc_s *dirloc) +{ + struct mfs_dirloc_s scanloc; + struct mfs_direntry_s candidate; + struct mfs_direntry_s latest; + struct mfs_dirloc_s latestloc; + int ret; + + if (sb == NULL || direntry == NULL || dirloc == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + if (directory == MFS_LOCATION_INVALID || + directory >= MFS_BLOCK_COUNT(sb) || + location == MFS_LOCATION_INVALID) + { + return -ENOENT; + } + + mfs_dirloc_start(sb, directory, &scanloc); + while (mfs_dirloc_is_valid(sb, directory, &scanloc)) + { + ret = mfs_direntry_read(sb, &scanloc, &candidate); + if (ret < 0) + { + ferr("mfs_direntry_read failed: %d\n", ret); + return ret; + } + + if (mfs_direntry_is_valid(&candidate) && + candidate.oldloc == MFS_LOCATION_INVALID) + { + ret = mfs_dir_follow(sb, directory, &scanloc, &candidate, + &latest, &latestloc); + if (ret == OK && latest.newloc == location) + { + memcpy(direntry, &latest, sizeof(*direntry)); + memcpy(dirloc, &latestloc, sizeof(*dirloc)); + return OK; + } + + if (ret < 0 && ret != -ENOENT) + { + ferr("mfs_dir_follow failed: %d\n", ret); + return ret; + } + } + + ret = mfs_dirloc_next(sb, directory, &scanloc); + if (ret < 0) + { + break; + } + } + + return -ENOENT; +} + +/**************************************************************************** + * Name: mfs_path_parent_dir + * + * Description: + * Resolve the parent directory block for a relative path. + * + * Input Parameters: + * sb - The mounted file system instance. + * relpath - The relative path whose parent is requested. + * directory - The location to receive the parent directory block number. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mfs_path_parent_dir(FAR struct mfs_sb_s *sb, + FAR const char *relpath, + FAR mfs_t *directory) +{ + FAR const char *path; + FAR const char *next; + struct mfs_direntry_s direntry; + struct mfs_dirloc_s dirloc; + char component[MFS_NAME_LEN]; + mfs_t current; + int ret; + + if (sb == NULL || relpath == NULL || directory == NULL) + { + return -EINVAL; + } + + memset(component, 0, sizeof(component)); + path = mfs_skip_slashes(relpath); + if (path == NULL || *path == '\0') + { + return -EINVAL; + } + + current = sb->rootdir; + for (; ; ) + { + ret = mfs_next_path_component(path, component, &next); + if (ret < 0) + { + return ret; + } + + if (component[0] == '\0') + { + return -EINVAL; + } + + if (*next == '\0') + { + * directory = current; + return OK; + } + + ret = mfs_dir_lookup(sb, current, component, ¤t, + &direntry, &dirloc); + if (ret < 0) + { + return ret; + } + + if (!S_ISDIR(direntry.mode)) + { + return -ENOTDIR; + } + + path = next; + } +} + +/**************************************************************************** + * Name: mfs_parent_dir + * + * Description: + * Resolve the parent directory block for a live file system object. If + * the direntry location is known, then the parent directory can be + * derived directly from that page. Otherwise the function falls back to + * reading the directory metadata stored in the object's own block. + * + * Input Parameters: + * sb - The mounted file system instance. + * location - The live object location. + * dirloc - Optional latest direntry location for that object. + * directory - The location to receive the parent directory block number. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mfs_parent_dir(FAR struct mfs_sb_s *sb, mfs_t location, + FAR const struct mfs_dirloc_s *dirloc, + FAR mfs_t *directory) +{ + struct mfs_dirmeta_s dirmeta; + int ret; + + if (sb == NULL || directory == NULL) + { + return -EINVAL; + } + + if (location == sb->rootdir) + { + return -EBUSY; + } + + if (dirloc != NULL) + { + ret = mfs_dirloc_parent_block(sb, dirloc, directory); + if (ret == OK) + { + return OK; + } + } + + ret = mfs_dirmeta_read(sb, location, &dirmeta); + if (ret < 0) + { + return ret; + } + + if (dirmeta.parent == MFS_LOCATION_INVALID || + dirmeta.parent >= MFS_BLOCK_COUNT(sb)) + { + return -EIO; + } + + * directory = dirmeta.parent; + return OK; +} + +/**************************************************************************** + * Name: mfs_same_directory + * + * Description: + * Verify that location and newrelpath belong to the same parent + * directory. + * + * Input Parameters: + * sb - The mounted file system instance. + * newrelpath - The replacement path to check. + * location - The live object location being examined. + * dirloc - Optional latest direntry location for location. + * directory - The location that receives the shared parent directory. + * + * Returned Value: + * Zero (OK) is returned if both objects share the same parent directory. + * -EXDEV is returned if the parent directories differ. A negated errno + * value is returned on other failures. + * + ****************************************************************************/ + +static int mfs_same_directory(FAR struct mfs_sb_s *sb, + FAR const char *newrelpath, + mfs_t location, + FAR const struct mfs_dirloc_s *dirloc, + FAR mfs_t *directory) +{ + mfs_t olddir; + mfs_t newdir; + int ret; + + if (sb == NULL || newrelpath == NULL || directory == NULL) + { + return -EINVAL; + } + + ret = mfs_parent_dir(sb, location, dirloc, &olddir); + if (ret < 0) + { + return ret; + } + + ret = mfs_path_parent_dir(sb, newrelpath, &newdir); + if (ret < 0) + { + return ret; + } + + if (olddir != newdir) + { + return -EXDEV; + } + + * directory = olddir; + return OK; +} + +/**************************************************************************** + * Name: mfs_direntry_namehash + * + * Description: + * Compute the 16-bit name hash stored in direntry records. + * + * Input Parameters: + * name - The name to hash. + * namelen - The number of characters in name to hash. + * + * Returned Value: + * The computed 16-bit hash value is returned. Zero is returned if name + * is NULL. + * + ****************************************************************************/ + +static uint16_t mfs_direntry_namehash(FAR const char *name, size_t namelen) +{ + FAR const uint8_t *arr; + uint32_t ret; + uint32_t idx_mult; + uint32_t elem; + size_t i; + + if (name == NULL) + { + return 0; + } + + arr = (FAR const uint8_t *)name; + ret = 0; + + for (i = 0; i < namelen; i++) + { + idx_mult = ((uint32_t)(i + 1) % UINT16_MAX) + 1; + elem = ((uint32_t)arr[i] + 1) % ((uint32_t)UINT16_MAX + 1); + elem = (uint32_t)(((uint64_t)elem * idx_mult * idx_mult) % + ((uint32_t)UINT16_MAX + 1)); + ret = (ret + elem) % ((uint32_t)UINT16_MAX + 1); + } + + return (uint16_t)ret; +} + +/**************************************************************************** + * Name: mfs_direntry_checksum + * + * Description: + * Compute the checksum stored in one direntry record. + * + * Input Parameters: + * direntry - The direntry to checksum. + * + * Returned Value: + * The computed checksum value is returned. + * + ****************************************************************************/ + +static uint8_t +mfs_direntry_checksum(FAR const struct mfs_direntry_s *direntry) +{ + return mfs_folded_hash8((FAR const uint8_t *)direntry, + sizeof(*direntry) - 1); +} + +/**************************************************************************** + * Name: mfs_direntry_is_valid + * + * Description: + * Verify the checksum, mode, and stored name fields of a direntry + * record. + * + * Input Parameters: + * direntry - The direntry to validate. + * + * Returned Value: + * true is returned if direntry contains a valid record. false is + * returned otherwise. + * + ****************************************************************************/ + +static bool mfs_direntry_is_valid(FAR const struct mfs_direntry_s *direntry) +{ + size_t namelen; + + if (direntry == NULL) + { + return false; + } + + if (direntry->magic != MFS_DIRENT_MAGIC) + { + return false; + } + + if (direntry->checksum != mfs_direntry_checksum(direntry)) + { + return false; + } + + if (!S_ISDIR(direntry->mode) && !S_ISREG(direntry->mode)) + { + return false; + } + + namelen = mfs_direntry_namelen(direntry); + if (namelen == 0) + { + return false; + } + + if (direntry->namehash != mfs_direntry_namehash(direntry->name, namelen)) + { + return false; + } + + return true; +} + +/**************************************************************************** + * Name: mfs_direntry_init + * + * Description: + * Initialize a brand-new direntry record from one relative path and mode. + * + * Input Parameters: + * direntry - The direntry to initialize. + * mode - The file type and permission bits to store. + * relpath - The relative path whose final component becomes the name. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned if + * the inputs are invalid or the final path component is too long. + * + ****************************************************************************/ + +static int mfs_direntry_init(FAR struct mfs_direntry_s *direntry, + mode_t mode, FAR const char *relpath) +{ + FAR const char *name; + size_t namelen; + + if (direntry == NULL) + { + return -EINVAL; + } + + if (!S_ISDIR(mode) && !S_ISREG(mode)) + { + return -EINVAL; + } + + name = mfs_direntry_name(relpath); + if (name == NULL || *name == '\0') + { + return -EINVAL; + } + + namelen = strnlen(name, MFS_NAME_LEN); + if (namelen == 0 || namelen > MFS_NAME_MAX) + { + return -ENAMETOOLONG; + } + + memset(direntry, 0, sizeof(*direntry)); + direntry->magic = MFS_DIRENT_MAGIC; + direntry->mode = (uint16_t)mode; + memcpy(direntry->name, name, namelen); + direntry->namehash = mfs_direntry_namehash(direntry->name, namelen); + mfs_direntry_seal(direntry); + return OK; +} + +/**************************************************************************** + * Name: mfs_direntry_set_name + * + * Description: + * Replace the stored direntry name with the final component of relpath. + * + * Input Parameters: + * direntry - The direntry to update. + * relpath - The relative path whose final component becomes the new + * name. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned if + * the inputs are invalid or the final path component is too long. + * + ****************************************************************************/ + +static int mfs_direntry_set_name(FAR struct mfs_direntry_s *direntry, + FAR const char *relpath) +{ + FAR const char *name; + size_t namelen; + + if (direntry == NULL) + { + return -EINVAL; + } + + name = mfs_direntry_name(relpath); + if (name == NULL || *name == '\0') + { + return -EINVAL; + } + + namelen = strnlen(name, MFS_NAME_LEN); + if (namelen == 0 || namelen > MFS_NAME_MAX) + { + return -ENAMETOOLONG; + } + + memset(direntry->name, 0, sizeof(direntry->name)); + memcpy(direntry->name, name, namelen); + direntry->namehash = mfs_direntry_namehash(direntry->name, namelen); + mfs_direntry_seal(direntry); + return OK; +} + +/**************************************************************************** + * Name: mfs_direntry_seal + * + * Description: + * Refresh the checksum field after a direntry record is modified. + * + * Input Parameters: + * direntry - The direntry to seal. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void mfs_direntry_seal(FAR struct mfs_direntry_s *direntry) +{ + if (direntry == NULL) + { + return; + } + + direntry->checksum = mfs_direntry_checksum(direntry); +} + +/**************************************************************************** + * Name: mfs_direntry_add_noflush + * + * Description: + * Append one new direntry record into the first blank page found in the + * directory block. Directory metadata occupies the first page, so the + * scan begins at the second page. Even though legacy images may contain + * multiple packed direntries per page, new writes only consume fully + * erased pages so one NAND page is never programmed twice. + * + * Input Parameters: + * sb - The mounted file system instance. + * directory - The directory block number. + * direntry - The new direntry record to store. + * + * Returned Value: + * Zero (OK) is returned on success. -ENOSPC is returned when the + * directory block has no blank page left for a new direntry. A negated + * errno value is returned on other failures. + * + ****************************************************************************/ + +static int mfs_direntry_add_noflush( + FAR struct mfs_sb_s *sb, mfs_t directory, + FAR const struct mfs_direntry_s *direntry) +{ + struct mfs_direntry_s candidate; + mfs_t page; + mfs_t endpage; + size_t offset; + ssize_t nread; + ssize_t nwritten; + bool occupied; + + if (sb == NULL || direntry == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + if (directory == MFS_LOCATION_INVALID || directory >= MFS_BLOCK_COUNT(sb)) + { + ferr("invalid directory block\n"); + return -EINVAL; + } + + if (!mfs_direntry_is_valid(direntry)) + { + ferr("invalid direntry\n"); + return -EINVAL; + } + + page = MFS_BLOCK_TO_PAGE(sb, directory) + 1; + endpage = MFS_BLOCK_TO_PAGE(sb, directory) + MFS_PAGES_PER_BLOCK(sb); + for (; page < endpage; page++) + { + nread = mfs_read_page(sb, page, sb->rwbuf); + if (nread < 0) + { + ferr("mfs_read_page failed: %zd\n", nread); + return nread; + } + + if (nread != 1) + { + ferr("short read: %zd\n", nread); + return -EIO; + } + + occupied = false; + for (offset = 0; + offset + sizeof(candidate) <= MFS_PAGE_SIZE(sb); + offset += sizeof(candidate)) + { + memcpy(&candidate, sb->rwbuf + offset, sizeof(candidate)); + if (mfs_direntry_is_valid(&candidate)) + { + occupied = true; + break; + } + } + + if (occupied || !mfs_page_is_erased(sb, sb->rwbuf)) + { + continue; + } + + memcpy(sb->rwbuf, direntry, sizeof(*direntry)); + nwritten = mfs_write_page(sb, page, sb->rwbuf); + if (nwritten < 0) + { + ferr("mfs_write_page failed: %zd\n", nwritten); + return nwritten; + } + + if (nwritten != 1) + { + ferr("short write: %zd\n", nwritten); + return -EIO; + } + + return OK; + } + + ferr("directory full\n"); + return -ENOSPC; +} + +/**************************************************************************** + * Name: mfs_dir_reparent_children + * + * Description: + * Rewrite the metadata page of each live child directory so its stored + * parent block number becomes parent. + * + * Input Parameters: + * sb - The mounted file system instance. + * directory - The directory block whose live children are scanned. + * parent - The parent block number to store into child metadata. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned if + * scanning or rewriting a child directory metadata page fails. + * + ****************************************************************************/ + +static int mfs_dir_reparent_children(FAR struct mfs_sb_s *sb, + mfs_t directory, mfs_t parent) +{ + struct mfs_dirloc_s scanloc; + struct mfs_direntry_s candidate; + struct mfs_direntry_s latest; + struct mfs_dirloc_s latestloc; + int ret; + + if (sb == NULL) + { + ferr("invalid sb\n"); + return -EINVAL; + } + + if (directory == MFS_LOCATION_INVALID || + directory >= MFS_BLOCK_COUNT(sb) || + parent == MFS_LOCATION_INVALID || + parent >= MFS_BLOCK_COUNT(sb)) + { + ferr("invalid directory or parent\n"); + return -EINVAL; + } + + mfs_dirloc_start(sb, directory, &scanloc); + while (mfs_dirloc_is_valid(sb, directory, &scanloc)) + { + ret = mfs_direntry_read(sb, &scanloc, &candidate); + if (ret < 0) + { + ferr("mfs_direntry_read failed: %d\n", ret); + return ret; + } + + if (mfs_direntry_is_valid(&candidate) && + candidate.oldloc == MFS_LOCATION_INVALID) + { + ret = mfs_dir_follow(sb, directory, &scanloc, &candidate, + &latest, &latestloc); + if (ret == OK && S_ISDIR(latest.mode)) + { + ret = mfs_dirmeta_write(sb, latest.newloc, parent); + if (ret < 0) + { + ferr("mfs_dirmeta_write failed: %d\n", ret); + return ret; + } + } + else if (ret < 0 && ret != -ENOENT) + { + ferr("mfs_dir_follow failed: %d\n", ret); + return ret; + } + } + + ret = mfs_dirloc_next(sb, directory, &scanloc); + if (ret < 0) + { + break; + } + } + + return OK; +} + +/**************************************************************************** + * Name: mfs_dir_refresh_open_files + * + * Description: + * Update open regular-file descriptors after a directory block has been + * relocated. + * + * Input Parameters: + * sb - The mounted file system instance. + * olddir - The original directory block number. + * newdir - The replacement directory block number. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned if + * scanning the replacement directory fails. + * + ****************************************************************************/ + +static int mfs_dir_refresh_open_files(FAR struct mfs_sb_s *sb, + mfs_t olddir, mfs_t newdir) +{ + struct mfs_dirloc_s scanloc; + struct mfs_direntry_s candidate; + struct mfs_direntry_s latest; + struct mfs_dirloc_s latestloc; + FAR struct list_node *node; + FAR struct mfs_ofd_s *ofd; + int ret; + + if (sb == NULL) + { + ferr("invalid sb\n"); + return -EINVAL; + } + + if (olddir == MFS_LOCATION_INVALID || olddir >= MFS_BLOCK_COUNT(sb) || + newdir == MFS_LOCATION_INVALID || newdir >= MFS_BLOCK_COUNT(sb)) + { + ferr("invalid directory block\n"); + return -EINVAL; + } + + mfs_dirloc_start(sb, newdir, &scanloc); + while (mfs_dirloc_is_valid(sb, newdir, &scanloc)) + { + ret = mfs_direntry_read(sb, &scanloc, &candidate); + if (ret < 0) + { + ferr("mfs_direntry_read failed: %d\n", ret); + return ret; + } + + if (mfs_direntry_is_valid(&candidate) && + candidate.oldloc == MFS_LOCATION_INVALID) + { + ret = mfs_dir_follow(sb, newdir, &scanloc, &candidate, + &latest, &latestloc); + if (ret == OK && S_ISREG(latest.mode)) + { + list_for_every(&sb->ofiles, node) + { + ofd = list_entry(node, struct mfs_ofd_s, entry); + if (ofd->common == NULL || + ofd->common->direntloc.page == MFS_LOCATION_INVALID || + mfs_page_to_block(sb, + ofd->common->direntloc.page) != + olddir || + (ofd->common->fileloc != latest.oldloc && + ofd->common->fileloc != latest.newloc)) + { + continue; + } + + memcpy(&ofd->common->direntry, &latest, + sizeof(ofd->common->direntry)); + memcpy(&ofd->common->direntloc, &latestloc, + sizeof(ofd->common->direntloc)); + ofd->common->fileloc = latest.newloc; + } + } + else if (ret < 0 && ret != -ENOENT) + { + ferr("mfs_dir_follow failed: %d\n", ret); + return ret; + } + } + + ret = mfs_dirloc_next(sb, newdir, &scanloc); + if (ret < 0) + { + break; + } + } + + return OK; +} + +/**************************************************************************** + * Name: mfs_direntry_flush + * + * Description: + * Relocate a full directory block into a freshly allocated block and then + * append direntry into the new copy. + * + * Input Parameters: + * sb - The mounted file system instance. + * directory - The original directory block number. + * direntry - The new direntry record to append. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned if + * allocation, copying, or parent bookkeeping fails. + * + ****************************************************************************/ + +static int mfs_direntry_flush(FAR struct mfs_sb_s *sb, mfs_t directory, + FAR const struct mfs_direntry_s *direntry) +{ + struct mfs_dirloc_s scanloc; + struct mfs_direntry_s candidate; + struct mfs_direntry_s latest; + struct mfs_direntry_s copied; + struct mfs_direntry_s parententry; + struct mfs_dirloc_s latestloc; + struct mfs_dirmeta_s dirmeta; + mfs_t newdir; + mfs_t parent; + int ret; + int cleanup; + bool moved_root; + bool children_reparented; + + if (sb == NULL || direntry == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + if (directory == MFS_LOCATION_INVALID || + directory >= MFS_BLOCK_COUNT(sb) || + !mfs_direntry_is_valid(direntry)) + { + ferr("invalid directory or direntry\n"); + return -EINVAL; + } + + moved_root = directory == sb->rootdir; + if (moved_root) + { + parent = directory; + } + else + { + ret = mfs_dirmeta_read(sb, directory, &dirmeta); + if (ret < 0) + { + ferr("mfs_dirmeta_read failed: %d\n", ret); + return ret; + } + + parent = dirmeta.parent; + } + + ret = mfs_alloc_block(sb, &newdir); + if (ret < 0) + { + ferr("mfs_alloc_block failed: %d\n", ret); + return ret; + } + + children_reparented = false; + if (!moved_root) + { + ret = mfs_dirmeta_write(sb, newdir, parent); + if (ret < 0) + { + ferr("mfs_dirmeta_write failed: %d\n", ret); + goto errout_with_newdir; + } + } + + mfs_dirloc_start(sb, directory, &scanloc); + while (mfs_dirloc_is_valid(sb, directory, &scanloc)) + { + ret = mfs_direntry_read(sb, &scanloc, &candidate); + if (ret < 0) + { + ferr("mfs_direntry_read failed: %d\n", ret); + goto errout_with_newdir; + } + + if (mfs_direntry_is_valid(&candidate) && + candidate.oldloc == MFS_LOCATION_INVALID) + { + ret = mfs_dir_follow(sb, directory, &scanloc, &candidate, + &latest, &latestloc); + if (ret == OK) + { + memcpy(&copied, &latest, sizeof(copied)); + copied.oldloc = MFS_LOCATION_INVALID; + mfs_direntry_seal(&copied); + ret = mfs_direntry_add_noflush(sb, newdir, &copied); + if (ret < 0) + { + ferr("copy direntry add failed: %d\n", ret); + goto errout_with_newdir; + } + } + else if (ret != -ENOENT) + { + ferr("mfs_dir_follow failed: %d\n", ret); + goto errout_with_newdir; + } + } + + ret = mfs_dirloc_next(sb, directory, &scanloc); + if (ret < 0) + { + break; + } + } + + ret = mfs_direntry_add_noflush(sb, newdir, direntry); + if (ret < 0) + { + ferr("append to new directory failed: %d\n", ret); + goto errout_with_newdir; + } + + ret = mfs_dir_reparent_children(sb, newdir, newdir); + if (ret < 0) + { + ferr("mfs_dir_reparent_children failed: %d\n", ret); + cleanup = mfs_dir_reparent_children(sb, newdir, directory); + if (cleanup < 0) + { + ferr("child reparent rollback failed: %d\n", cleanup); + ret = cleanup; + } + + goto errout_with_newdir; + } + + children_reparented = true; + if (moved_root) + { + ret = mfs_update_rootdir(sb, newdir); + if (ret < 0) + { + ferr("mfs_update_rootdir failed: %d\n", ret); + goto errout_with_children; + } + } + else + { + ret = mfs_dir_lookup_location(sb, parent, directory, &parententry, + &latestloc); + if (ret < 0) + { + ferr("mfs_dir_lookup_location failed: %d\n", ret); + goto errout_with_children; + } + + parententry.oldloc = directory; + parententry.newloc = newdir; + mfs_direntry_seal(&parententry); + + ret = mfs_direntry_add(sb, parent, &parententry); + if (ret < 0) + { + ferr("parent update direntry add failed: %d\n", ret); + goto errout_with_children; + } + } + + ret = mfs_dir_refresh_open_files(sb, directory, newdir); + if (ret < 0) + { + ferr("mfs_dir_refresh_open_files failed: %d\n", ret); + return ret; + } + + ret = mfs_report_block_deleted(sb, directory); + if (ret < 0) + { + ferr("mfs_report_block_deleted failed: %d\n", ret); + } + + return ret; + +errout_with_children: + if (children_reparented) + { + cleanup = mfs_dir_reparent_children(sb, newdir, directory); + if (cleanup < 0) + { + ferr("child reparent rollback failed: %d\n", cleanup); + ret = cleanup; + } + } + +errout_with_newdir: + cleanup = mfs_report_block_deleted(sb, newdir); + if (cleanup < 0) + { + ferr("newdir cleanup failed: %d\n", cleanup); + } + + return cleanup < 0 ? cleanup : ret; +} + +/**************************************************************************** + * Name: mfs_direntry_add + * + * Description: + * Append one direntry record to a directory, relocating the directory + * block if it is already full. + * + * Input Parameters: + * sb - The mounted file system instance. + * directory - The directory block number. + * direntry - The new direntry record to append. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mfs_direntry_add(FAR struct mfs_sb_s *sb, mfs_t directory, + FAR const struct mfs_direntry_s *direntry) +{ + int ret; + + ret = mfs_direntry_add_noflush(sb, directory, direntry); + if (ret != -ENOSPC) + { + if (ret < 0) + { + ferr("mfs_direntry_add_noflush failed: %d\n", ret); + } + + return ret; + } + + ret = mfs_direntry_flush(sb, directory, direntry); + if (ret < 0) + { + ferr("mfs_direntry_flush failed: %d\n", ret); + } + + return ret; +} + +/**************************************************************************** + * Name: mfs_dir_is_empty + * + * Description: + * Determine whether a directory contains any live entry chains. + * + * Input Parameters: + * sb - The mounted file system instance. + * directory - The directory block number. + * + * Returned Value: + * Zero (OK) is returned if the directory is empty. -ENOTEMPTY is + * returned if a live entry exists. A negated errno value is returned on + * other failures. + * + ****************************************************************************/ + +static int mfs_dir_is_empty(FAR struct mfs_sb_s *sb, mfs_t directory) +{ + struct mfs_dirloc_s scanloc; + struct mfs_direntry_s candidate; + struct mfs_direntry_s latest; + struct mfs_dirloc_s latestloc; + int ret; + + if (sb == NULL) + { + return -EINVAL; + } + + if (directory == MFS_LOCATION_INVALID || directory >= MFS_BLOCK_COUNT(sb)) + { + return -EINVAL; + } + + mfs_dirloc_start(sb, directory, &scanloc); + while (mfs_dirloc_is_valid(sb, directory, &scanloc)) + { + ret = mfs_direntry_read(sb, &scanloc, &candidate); + if (ret < 0) + { + return ret; + } + + if (mfs_direntry_is_valid(&candidate) && + candidate.oldloc == MFS_LOCATION_INVALID) + { + ret = mfs_dir_follow(sb, directory, &scanloc, &candidate, + &latest, &latestloc); + if (ret == OK) + { + return -ENOTEMPTY; + } + + if (ret != -ENOENT) + { + return ret; + } + } + + ret = mfs_dirloc_next(sb, directory, &scanloc); + if (ret < 0) + { + break; + } + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mfs_dirent_traverse + * + * Description: + * Resolve a relative path to its latest live direntry and location. + * + * Input Parameters: + * sb - The mounted file system instance. + * relpath - The relative path to resolve. + * location - The location to receive the resolved object location. + * direntry - The location to receive the resolved direntry contents. + * dirloc - The location to receive the resolved direntry address. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int mfs_dirent_traverse(FAR struct mfs_sb_s *sb, FAR const char *relpath, + FAR mfs_t *location, + FAR struct mfs_direntry_s *direntry, + FAR struct mfs_dirloc_s *dirloc) +{ + FAR const char *path; + FAR const char *next; + char component[MFS_NAME_LEN]; + mfs_t current; + int ret; + + if (sb == NULL || location == NULL || direntry == NULL || dirloc == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + memset(component, 0, sizeof(component)); + if (relpath == NULL) + { + ferr("relpath is NULL\n"); + return -EINVAL; + } + + path = mfs_skip_slashes(relpath); + if (path == NULL || *path == '\0') + { + * location = sb->rootdir; + mfs_root_direntry(sb, direntry); + mfs_dirloc_invalidate(dirloc); + return OK; + } + + current = sb->rootdir; + for (; ; ) + { + ret = mfs_next_path_component(path, component, &next); + if (ret < 0) + { + ferr("mfs_next_path_component failed: %d\n", ret); + return ret; + } + + if (component[0] == '\0') + { + ferr("empty path component\n"); + return -EINVAL; + } + + ret = mfs_dir_lookup(sb, current, component, ¤t, + direntry, dirloc); + if (ret < 0) + { + if (ret != -ENOENT) + { + ferr("mfs_dir_lookup failed: %d\n", ret); + } + + return ret; + } + + if (*next == '\0') + { + * location = current; + return OK; + } + + if (!S_ISDIR(direntry->mode)) + { + ferr("non-directory in path: %s\n", component); + return -ENOTDIR; + } + + path = next; + } +} + +/**************************************************************************** + * Name: mfs_dir_create_file + * + * Description: + * Create a new empty regular file. The function allocates a data page, + * emits the initial direntry record, and then resolves the freshly + * created path back to its live state. + * + * Input Parameters: + * sb - The mounted file system instance. + * relpath - The relative path of the file to create. + * mode - The requested file mode. + * location - The location to receive the created file location. + * direntry - The location to receive the created direntry contents. + * dirloc - The location to receive the created direntry address. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int mfs_dir_create_file(FAR struct mfs_sb_s *sb, FAR const char *relpath, + mode_t mode, FAR mfs_t *location, + FAR struct mfs_direntry_s *direntry, + FAR struct mfs_dirloc_s *dirloc) +{ + struct mfs_direntry_s newentry; + struct mfs_direntry_s existing; + struct mfs_dirloc_s existingloc; + mfs_t directory; + mfs_t page; + int ret; + int cleanup; + + if (sb == NULL || relpath == NULL || location == NULL || + direntry == NULL || dirloc == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + ret = mfs_path_parent_dir(sb, relpath, &directory); + if (ret < 0) + { + ferr("mfs_path_parent_dir failed: %d\n", ret); + return ret; + } + + ret = mfs_dir_lookup(sb, directory, mfs_direntry_name(relpath), &page, + &existing, &existingloc); + if (ret == OK) + { + ferr("file already exists: %s\n", relpath); + return -EEXIST; + } + + if (ret != -ENOENT) + { + ferr("mfs_dir_lookup failed: %d\n", ret); + return ret; + } + + ret = mfs_direntry_init(&newentry, (mode & ~S_IFMT) | S_IFREG, relpath); + if (ret < 0) + { + ferr("mfs_direntry_init failed: %d\n", ret); + return ret; + } + + ret = mfs_alloc_page(sb, &page); + if (ret < 0) + { + ferr("mfs_alloc_page failed: %d\n", ret); + return ret; + } + + newentry.newloc = page; + newentry.meta.size = 0; + mfs_direntry_seal(&newentry); + + ret = mfs_direntry_add(sb, directory, &newentry); + if (ret < 0) + { + ferr("mfs_direntry_add failed: %d\n", ret); + goto errout_with_page; + } + + ret = mfs_dirent_traverse(sb, relpath, location, direntry, dirloc); + if (ret < 0) + { + ferr("verify traverse failed: %d\n", ret); + goto errout_with_page; + } + + finfo("created file %s\n", relpath); + return OK; + +errout_with_page: + cleanup = mfs_release_page(sb, page); + if (cleanup < 0) + { + ferr("page cleanup failed: %d\n", cleanup); + } + + return cleanup < 0 ? cleanup : ret; +} + +/**************************************************************************** + * Name: mfs_dir_update_file + * + * Description: + * Emit a new direntry record that updates a regular file's stored + * location and size. + * + * Input Parameters: + * sb - The mounted file system instance. + * oldloc - The current file location. + * newloc - The revised file location. + * dirloc - The latest direntry location for the file. + * direntry - The latest direntry contents. Updated in-place on success. + * length - The new stored file size. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int mfs_dir_update_file(FAR struct mfs_sb_s *sb, mfs_t oldloc, mfs_t newloc, + FAR const struct mfs_dirloc_s *dirloc, + FAR struct mfs_direntry_s *direntry, + off_t length) +{ + struct mfs_direntry_s newentry; + mfs_t directory; + int ret; + + if (sb == NULL || dirloc == NULL || direntry == NULL || length < 0) + { + ferr("invalid args\n"); + return -EINVAL; + } + + if (!S_ISREG(direntry->mode)) + { + ferr("direntry is not regular file\n"); + return -EINVAL; + } + + if (oldloc == MFS_LOCATION_INVALID || newloc == MFS_LOCATION_INVALID) + { + ferr("invalid file location\n"); + return -EINVAL; + } + + if ((uint64_t)length > UINT32_MAX) + { + ferr("file length too large\n"); + return -EFBIG; + } + + if (oldloc == newloc && direntry->meta.size == (uint32_t)length) + { + return OK; + } + + ret = mfs_parent_dir(sb, oldloc, dirloc, &directory); + if (ret < 0) + { + ferr("mfs_parent_dir failed: %d\n", ret); + return ret; + } + + memcpy(&newentry, direntry, sizeof(newentry)); + newentry.oldloc = oldloc; + newentry.newloc = newloc; + newentry.meta.size = (uint32_t)length; + mfs_direntry_seal(&newentry); + + ret = mfs_direntry_add(sb, directory, &newentry); + if (ret < 0) + { + ferr("mfs_direntry_add failed: %d\n", ret); + return ret; + } + + memcpy(direntry, &newentry, sizeof(*direntry)); + return OK; +} + +/**************************************************************************** + * Name: mfs_dir_truncate_file + * + * Description: + * Emit a new direntry record that updates the stored size of a regular + * file without moving the file's current location. + * + * Input Parameters: + * sb - The mounted file system instance. + * location - The current file location. + * dirloc - The latest direntry location for the file. + * direntry - The latest direntry contents. Updated in-place on success. + * length - The new stored file size. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int mfs_dir_truncate_file(FAR struct mfs_sb_s *sb, mfs_t location, + FAR const struct mfs_dirloc_s *dirloc, + FAR struct mfs_direntry_s *direntry, + off_t length) +{ + return mfs_dir_update_file(sb, location, location, dirloc, direntry, + length); +} + +/**************************************************************************** + * Name: mfs_dir_opendir + * + * Description: + * Open a directory stream positioned at the first direntry slot in the + * resolved directory block. + * + * Input Parameters: + * sb - The mounted file system instance. + * relpath - The relative path of the directory to open. + * dir - The location to receive the opened directory stream. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int mfs_dir_opendir(FAR struct mfs_sb_s *sb, FAR const char *relpath, + FAR struct fs_dirent_s **dir) +{ + FAR struct mfs_dirfs_s *mfsdir; + struct mfs_dirloc_s dirloc; + int ret; + + if (sb == NULL || dir == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + mfsdir = fs_heap_zalloc(sizeof(*mfsdir)); + if (mfsdir == NULL) + { + ferr("fs_heap_zalloc failed\n"); + return -ENOMEM; + } + + ret = mfs_dirent_traverse(sb, relpath, &mfsdir->location, + &mfsdir->direntry, &dirloc); + if (ret < 0) + { + ferr("mfs_dirent_traverse failed: %d\n", ret); + goto errout_with_mfsdir; + } + + if (!S_ISDIR(mfsdir->direntry.mode)) + { + ferr("path is not directory\n"); + ret = -ENOTDIR; + goto errout_with_mfsdir; + } + + mfsdir->dir.fd_path = mfs_dir_pathdup(relpath); + if (mfsdir->dir.fd_path == NULL) + { + ferr("mfs_dir_pathdup failed\n"); + ret = -ENOMEM; + goto errout_with_mfsdir; + } + + mfs_dirloc_start(sb, mfsdir->location, &mfsdir->nextloc); + * dir = &mfsdir->dir; + return OK; + +errout_with_mfsdir: + fs_heap_free(mfsdir); + return ret; +} + +/**************************************************************************** + * Name: mfs_dir_closedir + * + * Description: + * Release one directory stream previously opened by mfs_dir_opendir(). + * + * Input Parameters: + * sb - The mounted file system instance. + * dir - The directory stream to release. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * invalid input. + * + ****************************************************************************/ + +int mfs_dir_closedir(FAR struct mfs_sb_s *sb, FAR struct fs_dirent_s *dir) +{ + if (sb == NULL || dir == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + fs_heap_free(dir); + return OK; +} + +/**************************************************************************** + * Name: mfs_dir_readdir + * + * Description: + * Return the next live directory entry from an open directory stream. + * + * Input Parameters: + * sb - The mounted file system instance. + * dir - The directory stream to advance. + * entry - The location to receive the translated dirent record. + * + * Returned Value: + * Zero (OK) is returned on success. -ENOENT is returned when the end of + * the directory is reached. A negated errno value is returned on other + * failures. + * + ****************************************************************************/ + +int mfs_dir_readdir(FAR struct mfs_sb_s *sb, FAR struct fs_dirent_s *dir, + FAR struct dirent *entry) +{ + FAR struct mfs_dirfs_s *mfsdir; + struct mfs_dirloc_s marked; + struct mfs_dirloc_s nextloc; + struct mfs_direntry_s candidate; + struct mfs_direntry_s latest; + struct mfs_dirloc_s latestloc; + int ret; + + if (sb == NULL || dir == NULL || entry == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + mfsdir = (FAR struct mfs_dirfs_s *)dir; + while (mfs_dirloc_is_valid(sb, mfsdir->location, &mfsdir->nextloc)) + { + marked = mfsdir->nextloc; + nextloc = marked; + ret = mfs_dirloc_next(sb, mfsdir->location, &nextloc); + if (ret < 0) + { + mfs_dirloc_invalidate(&nextloc); + } + + ret = mfs_direntry_read(sb, &marked, &candidate); + if (ret < 0) + { + ferr("mfs_direntry_read failed: %d\n", ret); + return ret; + } + + mfsdir->nextloc = nextloc; + if (!mfs_direntry_is_valid(&candidate) || + candidate.oldloc != MFS_LOCATION_INVALID) + { + continue; + } + + ret = mfs_dir_follow(sb, mfsdir->location, &marked, &candidate, + &latest, &latestloc); + if (ret == -ENOENT) + { + continue; + } + + if (ret < 0) + { + ferr("mfs_dir_follow failed: %d\n", ret); + return ret; + } + + memcpy(&mfsdir->direntry, &latest, sizeof(mfsdir->direntry)); + mfs_direntry_to_dirent(&latest, entry); + return OK; + } + + memset(entry, 0, sizeof(*entry)); + return -ENOENT; +} + +/**************************************************************************** + * Name: mfs_dir_rewinddir + * + * Description: + * Reset an open directory stream so the next read begins at the first + * direntry slot again. + * + * Input Parameters: + * sb - The mounted file system instance. + * dir - The directory stream to rewind. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * invalid input. + * + ****************************************************************************/ + +int mfs_dir_rewinddir(FAR struct mfs_sb_s *sb, FAR struct fs_dirent_s *dir) +{ + FAR struct mfs_dirfs_s *mfsdir; + + if (sb == NULL || dir == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + mfsdir = (FAR struct mfs_dirfs_s *)dir; + mfs_dirloc_start(sb, mfsdir->location, &mfsdir->nextloc); + return OK; +} + +/**************************************************************************** + * Name: mfs_dir_unlink + * + * Description: + * Remove a regular file by appending a tombstone direntry and then + * reporting the file page as deleted. + * + * Input Parameters: + * sb - The mounted file system instance. + * relpath - The relative path of the file to remove. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int mfs_dir_unlink(FAR struct mfs_sb_s *sb, FAR const char *relpath) +{ + struct mfs_direntry_s direntry; + struct mfs_dirloc_s dirloc; + mfs_t directory; + mfs_t location; + int ret; + + if (sb == NULL || relpath == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + ret = mfs_dirent_traverse(sb, relpath, &location, &direntry, &dirloc); + if (ret < 0) + { + ferr("mfs_dirent_traverse failed: %d\n", ret); + return ret; + } + + if (S_ISDIR(direntry.mode)) + { + ferr("path is directory: %s\n", relpath); + return -EISDIR; + } + + if (mfs_file_is_open(sb, location)) + { + ferr("file is open: %s\n", relpath); + return -EBUSY; + } + + ret = mfs_parent_dir(sb, location, &dirloc, &directory); + if (ret < 0) + { + ferr("mfs_parent_dir failed: %d\n", ret); + return ret; + } + + direntry.oldloc = location; + direntry.newloc = MFS_LOCATION_INVALID; + mfs_direntry_seal(&direntry); + + ret = mfs_direntry_add(sb, directory, &direntry); + if (ret < 0) + { + ferr("mfs_direntry_add failed: %d\n", ret); + return ret; + } + + ret = mfs_report_page_deleted(sb, location); + if (ret < 0) + { + ferr("mfs_report_page_deleted failed: %d\n", ret); + return ret; + } + + finfo("removed file %s\n", relpath); + return OK; +} + +/**************************************************************************** + * Name: mfs_dir_mkdir + * + * Description: + * Create a new directory block, initialize its metadata page, and emit + * the initial direntry record in the parent directory. + * + * Input Parameters: + * sb - The mounted file system instance. + * relpath - The relative path of the directory to create. + * mode - The requested directory mode. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int mfs_dir_mkdir(FAR struct mfs_sb_s *sb, FAR const char *relpath, + mode_t mode) +{ + struct mfs_direntry_s direntry; + struct mfs_direntry_s existing; + struct mfs_dirloc_s existingloc; + mfs_t directory; + mfs_t block; + int ret; + int cleanup; + + if (sb == NULL || relpath == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + ret = mfs_path_parent_dir(sb, relpath, &directory); + if (ret < 0) + { + ferr("mfs_path_parent_dir failed: %d\n", ret); + return ret; + } + + ret = mfs_dir_lookup(sb, directory, mfs_direntry_name(relpath), &block, + &existing, &existingloc); + if (ret == OK) + { + ferr("directory already exists: %s\n", relpath); + return -EEXIST; + } + + if (ret != -ENOENT) + { + ferr("mfs_dir_lookup failed: %d\n", ret); + return ret; + } + + ret = mfs_direntry_init(&direntry, (mode & ~S_IFMT) | S_IFDIR, relpath); + if (ret < 0) + { + ferr("mfs_direntry_init failed: %d\n", ret); + return ret; + } + + ret = mfs_alloc_block(sb, &block); + if (ret < 0) + { + ferr("mfs_alloc_block failed: %d\n", ret); + return ret; + } + + ret = mfs_dirmeta_write(sb, block, directory); + if (ret < 0) + { + ferr("mfs_dirmeta_write failed: %d\n", ret); + goto errout_with_block; + } + + direntry.newloc = block; + direntry.meta.size = MFS_BLOCK_SIZE(sb); + mfs_direntry_seal(&direntry); + + ret = mfs_direntry_add(sb, directory, &direntry); + if (ret < 0) + { + ferr("mfs_direntry_add failed: %d\n", ret); + goto errout_with_block; + } + + finfo("created dir %s\n", relpath); + return OK; + +errout_with_block: + cleanup = mfs_report_block_deleted(sb, block); + if (cleanup < 0) + { + ferr("block cleanup failed: %d\n", cleanup); + } + + return cleanup < 0 ? cleanup : ret; +} + +/**************************************************************************** + * Name: mfs_dir_rmdir + * + * Description: + * Remove an empty directory by appending a tombstone direntry and then + * reporting the directory block as deleted. + * + * Input Parameters: + * sb - The mounted file system instance. + * relpath - The relative path of the directory to remove. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int mfs_dir_rmdir(FAR struct mfs_sb_s *sb, FAR const char *relpath) +{ + struct mfs_direntry_s direntry; + struct mfs_dirloc_s dirloc; + mfs_t directory; + mfs_t location; + int ret; + + if (sb == NULL || relpath == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + ret = mfs_dirent_traverse(sb, relpath, &location, &direntry, &dirloc); + if (ret < 0) + { + ferr("mfs_dirent_traverse failed: %d\n", ret); + return ret; + } + + if (!S_ISDIR(direntry.mode)) + { + ferr("path is not directory: %s\n", relpath); + return -ENOTDIR; + } + + ret = mfs_dir_is_empty(sb, location); + if (ret < 0) + { + ferr("mfs_dir_is_empty failed: %d\n", ret); + return ret; + } + + ret = mfs_parent_dir(sb, location, &dirloc, &directory); + if (ret < 0) + { + ferr("mfs_parent_dir failed: %d\n", ret); + return ret; + } + + direntry.oldloc = location; + direntry.newloc = MFS_LOCATION_INVALID; + mfs_direntry_seal(&direntry); + + ret = mfs_direntry_add(sb, directory, &direntry); + if (ret < 0) + { + ferr("mfs_direntry_add failed: %d\n", ret); + return ret; + } + + ret = mfs_report_block_deleted(sb, location); + if (ret < 0) + { + ferr("mfs_report_block_deleted failed: %d\n", ret); + return ret; + } + + finfo("removed dir %s\n", relpath); + return OK; +} + +/**************************************************************************** + * Name: mfs_dir_rename + * + * Description: + * Rename a file system object within the same parent directory by + * appending a new direntry record with the revised name. + * + * Input Parameters: + * sb - The mounted file system instance. + * oldrelpath - The current relative path. + * newrelpath - The replacement relative path. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int mfs_dir_rename(FAR struct mfs_sb_s *sb, FAR const char *oldrelpath, + FAR const char *newrelpath) +{ + struct mfs_direntry_s direntry; + struct mfs_dirloc_s dirloc; + mfs_t directory; + mfs_t location; + int ret; + + if (sb == NULL || oldrelpath == NULL || newrelpath == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + ret = mfs_dirent_traverse(sb, oldrelpath, &location, &direntry, &dirloc); + if (ret < 0) + { + ferr("mfs_dirent_traverse failed: %d\n", ret); + return ret; + } + + if (S_ISREG(direntry.mode) && mfs_file_is_open(sb, location)) + { + ferr("file is open: %s\n", oldrelpath); + return -EBUSY; + } + + ret = mfs_same_directory(sb, newrelpath, location, &dirloc, &directory); + if (ret < 0) + { + ferr("mfs_same_directory failed: %d\n", ret); + return ret; + } + + ret = mfs_direntry_set_name(&direntry, newrelpath); + if (ret < 0) + { + ferr("mfs_direntry_set_name failed: %d\n", ret); + return ret; + } + + direntry.oldloc = location; + direntry.newloc = location; + mfs_direntry_seal(&direntry); + ret = mfs_direntry_add(sb, directory, &direntry); + if (ret < 0) + { + ferr("mfs_direntry_add failed: %d\n", ret); + return ret; + } + + finfo("renamed %s -> %s\n", oldrelpath, newrelpath); + return OK; +} + +/**************************************************************************** + * Name: mfs_dir_stat + * + * Description: + * Resolve one path and translate its live direntry into a struct stat. + * + * Input Parameters: + * sb - The mounted file system instance. + * relpath - The relative path to inspect. + * buf - The location to receive the translated stat data. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int mfs_dir_stat(FAR struct mfs_sb_s *sb, FAR const char *relpath, + FAR struct stat *buf) +{ + struct mfs_direntry_s direntry; + struct mfs_dirloc_s dirloc; + mfs_t location; + int ret; + + if (sb == NULL || relpath == NULL || buf == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + ret = mfs_dirent_traverse(sb, relpath, &location, &direntry, &dirloc); + if (ret < 0) + { + ferr("mfs_dirent_traverse failed: %d\n", ret); + return ret; + } + + mfs_stat_from_direntry(sb, location, &direntry, buf); + return OK; +} diff --git a/fs/mnemofs/mnemofs_file.c b/fs/mnemofs/mnemofs_file.c new file mode 100644 index 00000000000..e55a55fabe9 --- /dev/null +++ b/fs/mnemofs/mnemofs_file.c @@ -0,0 +1,1732 @@ +/**************************************************************************** + * fs/mnemofs/mnemofs_file.c + * + * SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "inode/inode.h" +#include "mnemofs.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static FAR struct mfs_ofd_s *mfs_file_ofd(FAR const struct mfs_sb_s *sb, + FAR const struct file *filep); +static void mfs_file_stat(FAR const struct mfs_sb_s *sb, + FAR const struct mfs_ofd_common_s *com, + FAR struct stat *buf); +static int mfs_file_bounds_from_size(FAR struct mfs_sb_s *sb, mfs_t filesize, + FAR mfs_t *eofidx, + FAR mfs_t *eofpgoff, + FAR mfs_t *finalidx); +static mfs_t mfs_file_unit_off(FAR struct mfs_sb_s *sb, mfs_t index); +static int mfs_file_unit_page(FAR struct mfs_sb_s *sb, mfs_t headpage, + mfs_t headidx, mfs_t targetidx, + FAR mfs_t *targetpage); +static int mfs_file_apply_range(FAR struct mfs_sb_s *sb, mfs_t headpage, + mfs_t headidx, mfs_t startidx, + mfs_t endidx, + int (*action)(FAR struct mfs_sb_s *, + mfs_t)); +static int mfs_file_reset_empty(FAR struct mfs_sb_s *sb, + FAR mfs_t *fileloc, + FAR const struct mfs_dirloc_s *dirloc, + FAR struct mfs_direntry_s *direntry); +static ssize_t mfs_file_read_from_off(FAR struct mfs_sb_s *sb, + FAR struct mfs_ofd_s *ofd, + mfs_t offset, FAR char *buf, + size_t buflen); +static ssize_t mfs_file_write_from_off(FAR struct mfs_sb_s *sb, + FAR struct mfs_ofd_s *ofd, + mfs_t offset, + FAR const char *buf, size_t buflen, + FAR mfs_t *newloc, + FAR mfs_t *newsize); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mfs_file_ofd + * + * Description: + * Return the open-file descriptor state associated with filep for this + * mounted file system. + * + * Input Parameters: + * sb - The mounted file system instance. + * filep - The VFS file structure to inspect. + * + * Returned Value: + * A pointer to the matching open-file descriptor is returned on success. + * NULL is returned if no matching descriptor exists. + * + ****************************************************************************/ + +static FAR struct mfs_ofd_s *mfs_file_ofd(FAR const struct mfs_sb_s *sb, + FAR const struct file *filep) +{ + FAR struct mfs_ofd_s *ofd; + + if (sb == NULL || filep == NULL) + { + return NULL; + } + + ofd = filep->f_priv; + if (ofd == NULL || ofd->sb != sb) + { + return NULL; + } + + return ofd; +} + +/**************************************************************************** + * Name: mfs_file_stat + * + * Description: + * Populate a struct stat from one open-file descriptor snapshot. + * + * Input Parameters: + * sb - The mounted file system instance. + * com - The shared open-file descriptor state. + * buf - The location to receive the translated stat data. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void mfs_file_stat(FAR const struct mfs_sb_s *sb, + FAR const struct mfs_ofd_common_s *com, + FAR struct stat *buf) +{ + memset(buf, 0, sizeof(*buf)); + buf->st_ino = com->fileloc; + buf->st_mode = (mode_t)com->direntry.mode; + buf->st_nlink = 1; + buf->st_size = com->direntry.meta.size; + buf->st_atim.tv_sec = com->direntry.mtime; + buf->st_mtim.tv_sec = com->direntry.mtime; + buf->st_ctim.tv_sec = com->direntry.ctime; + buf->st_blksize = MFS_PAGE_SIZE(sb); + buf->st_blocks = (buf->st_size + buf->st_blksize - 1) / + buf->st_blksize; +} + +/**************************************************************************** + * Name: mfs_file_bounds_from_size + * + * Description: + * Convert a stored file size into CTZ end-of-file indices used by the + * file read and write paths. + * + * Input Parameters: + * sb - The mounted file system instance. + * filesize - The file size to decode. + * eofidx - The location to receive the CTZ unit index at EOF. + * eofpgoff - The location to receive the byte offset inside that unit. + * finalidx - Optional location to receive the final allocated unit index. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mfs_file_bounds_from_size(FAR struct mfs_sb_s *sb, mfs_t filesize, + FAR mfs_t *eofidx, + FAR mfs_t *eofpgoff, + FAR mfs_t *finalidx) +{ + int ret; + + if (sb == NULL || eofidx == NULL || eofpgoff == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + ret = mfs_ctz_index_from_off(sb, filesize, eofidx, eofpgoff); + if (ret < 0) + { + ferr("mfs_ctz_index_from_off failed: %d\n", ret); + return ret; + } + + if (finalidx != NULL) + { + if (filesize == 0 || *eofpgoff != 0) + { + * finalidx = *eofidx; + } + else + { + DEBUGASSERT(*eofidx > 0); + * finalidx = *eofidx - 1; + } + } + + return OK; +} + +/**************************************************************************** + * Name: mfs_file_unit_off + * + * Description: + * Return the logical file offset where CTZ unit index begins. + * + * Input Parameters: + * sb - The mounted file system instance. + * index - The CTZ unit index to translate. + * + * Returned Value: + * The logical file offset of the unit is returned. + * + ****************************************************************************/ + +static mfs_t mfs_file_unit_off(FAR struct mfs_sb_s *sb, mfs_t index) +{ + mfs_t off; + mfs_t idx; + mfs_t datasz; + + DEBUGASSERT(sb != NULL); + + off = 0; + for (idx = 0; idx < index; idx++) + { + datasz = mfs_ctz_unit_data_area(sb, idx); + DEBUGASSERT(datasz > 0); + off += datasz; + } + + return off; +} + +/**************************************************************************** + * Name: mfs_file_unit_page + * + * Description: + * Resolve the page that stores targetidx within one CTZ file chain. + * + * Input Parameters: + * sb - The mounted file system instance. + * headpage - The current head page of the file chain. + * headidx - The index of headpage. + * targetidx - The CTZ unit index to resolve. + * targetpage - The location to receive the resolved page. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * invalid input or traversal failure. + * + ****************************************************************************/ + +static int mfs_file_unit_page(FAR struct mfs_sb_s *sb, mfs_t headpage, + mfs_t headidx, mfs_t targetidx, + FAR mfs_t *targetpage) +{ + int ret; + + if (sb == NULL || targetpage == NULL || headpage == MFS_LOCATION_INVALID || + targetidx > headidx) + { + ferr("invalid args\n"); + return -EINVAL; + } + + ret = mfs_ctz_traverse(sb, headidx, targetidx, headpage, targetpage); + if (ret < 0) + { + ferr("mfs_ctz_traverse failed: %d\n", ret); + } + + return ret; +} + +/**************************************************************************** + * Name: mfs_file_apply_range + * + * Description: + * Resolve each CTZ unit in an inclusive index range and apply action to + * its page. + * + * Input Parameters: + * sb - The mounted file system instance. + * headpage - The current head page of the file chain. + * headidx - The index of headpage. + * startidx - The first CTZ unit index in the range. + * endidx - The last CTZ unit index in the range. + * action - The callback to invoke for each resolved page. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * invalid input, traversal failure, or callback failure. + * + ****************************************************************************/ + +static int mfs_file_apply_range(FAR struct mfs_sb_s *sb, mfs_t headpage, + mfs_t headidx, mfs_t startidx, + mfs_t endidx, + int (*action)(FAR struct mfs_sb_s *, + mfs_t)) +{ + mfs_t idx; + mfs_t page; + int ret; + + if (sb == NULL || action == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + if (startidx > endidx) + { + return OK; + } + + if (endidx > headidx) + { + ferr("invalid range\n"); + return -EINVAL; + } + + idx = startidx; + while (true) + { + ret = mfs_file_unit_page(sb, headpage, headidx, idx, &page); + if (ret < 0) + { + ferr("mfs_file_unit_page failed: %d\n", ret); + return ret; + } + + ret = action(sb, page); + if (ret < 0) + { + ferr("range action failed: %d\n", ret); + return ret; + } + + if (idx == endidx) + { + return OK; + } + + idx++; + } +} + +/**************************************************************************** + * Name: mfs_file_reset_empty + * + * Description: + * Replace one regular file with a fresh empty single-page image and mark + * the old committed pages obsolete. + * + * Input Parameters: + * sb - The mounted file system instance. + * fileloc - The current file head location. Updated on success. + * dirloc - The latest known direntry location for the file. + * direntry - The latest direntry contents. Updated in-place on success. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +static int mfs_file_reset_empty(FAR struct mfs_sb_s *sb, + FAR mfs_t *fileloc, + FAR const struct mfs_dirloc_s *dirloc, + FAR struct mfs_direntry_s *direntry) +{ + FAR uint8_t *pagebuf; + mfs_t oldloc; + mfs_t oldsize; + mfs_t old_eof_idx; + mfs_t old_eof_pgoff; + mfs_t old_final_idx; + mfs_t newloc; + ssize_t nwritten; + int ret; + int cleanup; + + if (sb == NULL || fileloc == NULL || dirloc == NULL || direntry == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + if (!S_ISREG(direntry->mode)) + { + ferr("direntry is not regular file\n"); + return -EINVAL; + } + + oldloc = *fileloc; + oldsize = direntry->meta.size; + if (oldloc == MFS_LOCATION_INVALID) + { + ferr("invalid file location\n"); + return -EINVAL; + } + + if (oldsize == 0) + { + return OK; + } + + ret = mfs_file_bounds_from_size(sb, oldsize, &old_eof_idx, &old_eof_pgoff, + &old_final_idx); + if (ret < 0) + { + ferr("old bounds decode failed: %d\n", ret); + return ret; + } + + UNUSED(old_eof_idx); + UNUSED(old_eof_pgoff); + + pagebuf = kmm_zalloc(MFS_PAGE_SIZE(sb)); + if (pagebuf == NULL) + { + ferr("kmm_zalloc failed\n"); + return -ENOMEM; + } + + newloc = MFS_LOCATION_INVALID; + ret = mfs_alloc_page(sb, &newloc); + if (ret < 0) + { + ferr("mfs_alloc_page failed: %d\n", ret); + goto errout_with_buf; + } + + nwritten = mfs_write_page(sb, newloc, pagebuf); + if (nwritten < 0) + { + ferr("mfs_write_page failed: %zd\n", nwritten); + ret = nwritten; + goto errout_with_page; + } + + if (nwritten != 1) + { + ferr("short write: %zd\n", nwritten); + ret = -EIO; + goto errout_with_page; + } + + ret = mfs_dir_update_file(sb, oldloc, newloc, dirloc, direntry, 0); + if (ret < 0) + { + ferr("mfs_dir_update_file failed: %d\n", ret); + goto errout_with_page; + } + + * fileloc = newloc; + kmm_free(pagebuf); + + ret = mfs_file_apply_range(sb, oldloc, old_final_idx, 0, old_final_idx, + mfs_report_page_deleted); + if (ret < 0) + { + ferr("old page cleanup failed: %d\n", ret); + } + + return ret; + +errout_with_page: + cleanup = mfs_release_page(sb, newloc); + if (cleanup < 0) + { + ferr("page cleanup failed: %d\n", cleanup); + ret = cleanup; + } + +errout_with_buf: + kmm_free(pagebuf); + return ret; +} + +/**************************************************************************** + * Name: mfs_file_read_from_off + * + * Description: + * Read file data starting at one logical offset without updating the + * caller's open-file position. + * + * Input Parameters: + * sb - The mounted file system instance. + * ofd - The open-file descriptor state. + * offset - The logical file offset to start reading from. + * buf - The caller's buffer. + * buflen - The number of bytes requested. + * + * Returned Value: + * A non-negative byte count is returned on success. A negated errno + * value is returned on failure. + * + ****************************************************************************/ + +static ssize_t mfs_file_read_from_off(FAR struct mfs_sb_s *sb, + FAR struct mfs_ofd_s *ofd, + mfs_t offset, FAR char *buf, + size_t buflen) +{ + mfs_t filesize; + mfs_t final_idx; + mfs_t eof_idx; + mfs_t eof_pgoff; + mfs_t s_idx; + mfs_t s_pgoff; + mfs_t e_idx; + mfs_t e_pgoff; + mfs_t c_idx; + mfs_t c_pgoff; + mfs_t c_pg; + mfs_t readlen; + mfs_t off; + mfs_t datasz; + mfs_t rd_sz; + ssize_t nread; + int ret; + + if (sb == NULL || ofd == NULL || ofd->sb != sb || ofd->common == NULL || + (buf == NULL && buflen != 0)) + { + ferr("invalid args\n"); + return -EINVAL; + } + + filesize = ofd->common->direntry.meta.size; + if (buflen == 0 || offset >= filesize) + { + return 0; + } + + readlen = MIN((mfs_t)buflen, filesize - offset); + ret = mfs_file_bounds_from_size(sb, filesize, &eof_idx, &eof_pgoff, + &final_idx); + if (ret < 0) + { + ferr("mfs_file_bounds_from_size failed: %d\n", ret); + return ret; + } + + ret = mfs_ctz_index_from_off(sb, offset, &s_idx, &s_pgoff); + if (ret < 0) + { + ferr("start offset decode failed: %d\n", ret); + return ret; + } + + ret = mfs_ctz_index_from_off(sb, offset + readlen - 1, &e_idx, &e_pgoff); + if (ret < 0) + { + ferr("end offset decode failed: %d\n", ret); + return ret; + } + + UNUSED(eof_idx); + UNUSED(eof_pgoff); + UNUSED(e_pgoff); + + c_idx = s_idx; + c_pgoff = s_pgoff; + off = 0; + while (off < readlen) + { + datasz = mfs_ctz_unit_data_area(sb, c_idx); + if (datasz == 0 || c_pgoff > datasz) + { + ferr("invalid data area\n"); + return -EINVAL; + } + + rd_sz = MIN(datasz - c_pgoff, readlen - off); + ret = mfs_file_unit_page(sb, ofd->common->fileloc, final_idx, c_idx, + &c_pg); + if (ret < 0) + { + ferr("mfs_file_unit_page failed: %d\n", ret); + return ret; + } + + nread = mfs_read_page(sb, c_pg, sb->rwbuf); + if (nread < 0) + { + ferr("mfs_read_page failed: %zd\n", nread); + return nread; + } + + if (nread != 1) + { + ferr("short read: %zd\n", nread); + return -EIO; + } + + memcpy(buf + off, sb->rwbuf + c_pgoff, rd_sz); + off += rd_sz; + + if (c_idx == e_idx) + { + break; + } + + c_idx++; + c_pgoff = 0; + } + + return off; +} + +/**************************************************************************** + * Name: mfs_file_write_from_off + * + * Description: + * Materialize a rewritten CTZ file image for a write at one logical + * offset without updating directory state yet. + * + * Input Parameters: + * sb - The mounted file system instance. + * ofd - The open-file descriptor state. + * offset - The logical file offset to start writing at. + * buffer - The source bytes to write. + * buflen - The number of bytes to write. + * newloc - The location to receive the new file head page. + * newsize - The location to receive the resulting file size. + * + * Returned Value: + * A non-negative byte count is returned on success. A negated errno + * value is returned on failure. + * + ****************************************************************************/ + +static ssize_t mfs_file_write_from_off(FAR struct mfs_sb_s *sb, + FAR struct mfs_ofd_s *ofd, + mfs_t offset, + FAR const char *buffer, size_t buflen, + FAR mfs_t *newloc, + FAR mfs_t *newsize) +{ + FAR uint8_t *pagebuf; + mfs_t oldsize; + mfs_t writeend; + mfs_t filesize; + mfs_t old_eof_idx; + mfs_t old_eof_pgoff; + mfs_t old_final_idx; + mfs_t new_eof_idx; + mfs_t new_eof_pgoff; + mfs_t new_final_idx; + mfs_t first_new_idx; + mfs_t s_idx; + mfs_t s_pgoff; + mfs_t prev_page; + mfs_t last_new_page; + mfs_t last_new_idx; + mfs_t cur_idx; + mfs_t cur_page; + mfs_t cur_old_page; + mfs_t unit_off; + mfs_t unit_end; + mfs_t copy_start; + mfs_t copy_end; + mfs_t datasz; + ssize_t nread; + ssize_t nwritten; + int ret; + int cleanup; + + if (sb == NULL || ofd == NULL || ofd->sb != sb || ofd->common == NULL || + newloc == NULL || newsize == NULL || + (buffer == NULL && buflen != 0)) + { + ferr("invalid args\n"); + return -EINVAL; + } + + * newloc = MFS_LOCATION_INVALID; + * newsize = ofd->common->direntry.meta.size; + + if (buflen == 0) + { + * newloc = ofd->common->fileloc; + * newsize = ofd->common->direntry.meta.size; + return 0; + } + + if ((uint64_t)offset + (uint64_t)buflen > UINT32_MAX) + { + ferr("write would overflow file size\n"); + return -EFBIG; + } + + oldsize = ofd->common->direntry.meta.size; + writeend = offset + (mfs_t)buflen; + filesize = MAX(oldsize, writeend); + + ret = mfs_ctz_index_from_off(sb, offset, &s_idx, &s_pgoff); + if (ret < 0) + { + ferr("start offset decode failed: %d\n", ret); + return ret; + } + + ret = mfs_file_bounds_from_size(sb, oldsize, &old_eof_idx, &old_eof_pgoff, + &old_final_idx); + if (ret < 0) + { + ferr("old bounds decode failed: %d\n", ret); + return ret; + } + + ret = mfs_file_bounds_from_size(sb, filesize, &new_eof_idx, &new_eof_pgoff, + &new_final_idx); + if (ret < 0) + { + ferr("new bounds decode failed: %d\n", ret); + return ret; + } + + UNUSED(old_eof_pgoff); + UNUSED(new_eof_idx); + UNUSED(new_eof_pgoff); + UNUSED(s_pgoff); + + first_new_idx = MIN(s_idx, old_eof_idx); + prev_page = MFS_LOCATION_INVALID; + last_new_page = MFS_LOCATION_INVALID; + last_new_idx = MFS_LOCATION_INVALID; + unit_off = mfs_file_unit_off(sb, first_new_idx); + + if (first_new_idx > 0) + { + ret = mfs_file_unit_page(sb, ofd->common->fileloc, old_final_idx, + first_new_idx - 1, &prev_page); + if (ret < 0) + { + ferr("mfs_file_unit_page failed: %d\n", ret); + return ret; + } + } + + pagebuf = kmm_malloc(MFS_PAGE_SIZE(sb)); + if (pagebuf == NULL) + { + ferr("kmm_malloc failed\n"); + return -ENOMEM; + } + + cur_idx = first_new_idx; + while (true) + { + datasz = mfs_ctz_unit_data_area(sb, cur_idx); + if (datasz == 0) + { + ferr("invalid data area\n"); + ret = -EINVAL; + goto errout; + } + + unit_end = unit_off + datasz; + memset(pagebuf, 0, MFS_PAGE_SIZE(sb)); + + if (cur_idx > 0) + { + ret = mfs_ctz_fill_next_ptrs(sb, prev_page, cur_idx - 1, pagebuf); + if (ret < 0) + { + ferr("mfs_ctz_fill_next_ptrs failed: %d\n", ret); + goto errout; + } + } + + if (cur_idx <= old_final_idx) + { + ret = mfs_file_unit_page(sb, ofd->common->fileloc, old_final_idx, + cur_idx, &cur_old_page); + if (ret < 0) + { + ferr("mfs_file_unit_page failed: %d\n", ret); + goto errout; + } + + nread = mfs_read_page(sb, cur_old_page, sb->rwbuf); + if (nread < 0) + { + ferr("mfs_read_page failed: %zd\n", nread); + ret = nread; + goto errout; + } + + if (nread != 1) + { + ferr("short read: %zd\n", nread); + ret = -EIO; + goto errout; + } + } + + if (cur_idx <= old_final_idx) + { + copy_start = unit_off; + copy_end = MIN(unit_end, MIN(offset, oldsize)); + if (copy_start < copy_end) + { + memcpy(pagebuf + (copy_start - unit_off), sb->rwbuf, + copy_end - copy_start); + } + } + + copy_start = MAX(unit_off, offset); + copy_end = MIN(unit_end, writeend); + if (copy_start < copy_end) + { + memcpy(pagebuf + (copy_start - unit_off), + buffer + (copy_start - offset), copy_end - copy_start); + } + + if (cur_idx <= old_final_idx) + { + copy_start = MAX(unit_off, writeend); + copy_end = MIN(unit_end, oldsize); + if (copy_start < copy_end) + { + memcpy(pagebuf + (copy_start - unit_off), + sb->rwbuf + (copy_start - unit_off), + copy_end - copy_start); + } + } + + ret = mfs_alloc_page(sb, &cur_page); + if (ret < 0) + { + ferr("mfs_alloc_page failed: %d\n", ret); + goto errout; + } + + nwritten = mfs_write_page(sb, cur_page, pagebuf); + if (nwritten < 0) + { + ferr("mfs_write_page failed: %zd\n", nwritten); + ret = nwritten; + cleanup = mfs_release_page(sb, cur_page); + if (cleanup < 0) + { + ferr("mfs_release_page failed: %d\n", cleanup); + ret = cleanup; + } + + goto errout; + } + + if (nwritten != 1) + { + ferr("short write: %zd\n", nwritten); + ret = -EIO; + cleanup = mfs_release_page(sb, cur_page); + if (cleanup < 0) + { + ferr("mfs_release_page failed: %d\n", cleanup); + ret = cleanup; + } + + goto errout; + } + + prev_page = cur_page; + last_new_page = cur_page; + last_new_idx = cur_idx; + + if (cur_idx == new_final_idx) + { + break; + } + + unit_off = unit_end; + cur_idx++; + } + + kmm_free(pagebuf); + * newloc = last_new_page; + * newsize = filesize; + return (ssize_t)buflen; + +errout: + kmm_free(pagebuf); + if (last_new_page != MFS_LOCATION_INVALID) + { + cleanup = mfs_file_apply_range(sb, last_new_page, last_new_idx, + first_new_idx, last_new_idx, + mfs_release_page); + if (cleanup < 0) + { + ferr("rollback release failed: %d\n", cleanup); + return cleanup; + } + } + + ferr("failed: %d\n", ret); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mfs_file_is_open + * + * Description: + * Check whether a live file location already has an open descriptor. + * + * Input Parameters: + * sb - The mounted file system instance. + * fileloc - The file location to test. + * + * Returned Value: + * true is returned if the file is already open. false is returned + * otherwise. + * + ****************************************************************************/ + +bool mfs_file_is_open(FAR const struct mfs_sb_s *sb, mfs_t fileloc) +{ + FAR struct list_node *node; + FAR struct mfs_ofd_s *ofd; + + if (sb == NULL || fileloc == MFS_LOCATION_INVALID) + { + return false; + } + + list_for_every(&sb->ofiles, node) + { + ofd = list_entry(node, struct mfs_ofd_s, entry); + if (ofd->common != NULL && ofd->common->fileloc == fileloc) + { + return true; + } + } + + return false; +} + +/**************************************************************************** + * Name: mfs_file_open + * + * Description: + * Open a regular file. If O_CREAT is present and the path is absent, + * then an initial direntry record and page allocation are performed + * before the open-file state is created. + * + * Input Parameters: + * filep - The file structure to initialize. + * relpath - The relative path of the file. + * oflags - Open flags. + * mode - Create mode used with O_CREAT. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int mfs_file_open(FAR struct mfs_sb_s *sb, FAR struct file *filep, + FAR const char *relpath, int oflags, mode_t mode) +{ + FAR struct mfs_ofd_s *ofd; + FAR struct mfs_ofd_common_s *common; + FAR char *pathdup; + struct mfs_direntry_s direntry; + struct mfs_dirloc_s dirloc; + mfs_t location; + int ret; + + if (sb == NULL || filep == NULL || relpath == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + if ((oflags & O_CREAT) != 0) + { + ret = mfs_dirent_traverse(sb, relpath, &location, &direntry, &dirloc); + if (ret == OK) + { + if (!S_ISREG(direntry.mode)) + { + ferr("path is directory: %s\n", relpath); + return -EISDIR; + } + + if ((oflags & O_EXCL) != 0) + { + ferr("file exists with O_EXCL: %s\n", relpath); + return -EEXIST; + } + } + else + { + if (ret != -ENOENT) + { + ferr("mfs_dirent_traverse failed: %d\n", ret); + return ret; + } + + if ((mode & S_IFMT) == S_IFDIR) + { + ferr("create requested directory mode: %s\n", relpath); + return -EISDIR; + } + + ret = mfs_dir_create_file(sb, relpath, mode, &location, + &direntry, &dirloc); + if (ret < 0) + { + ferr("mfs_dir_create_file failed: %d\n", ret); + return ret; + } + } + } + else + { + ret = mfs_dirent_traverse(sb, relpath, &location, &direntry, &dirloc); + if (ret < 0) + { + ferr("mfs_dirent_traverse failed: %d\n", ret); + return ret; + } + + if (!S_ISREG(direntry.mode)) + { + ferr("path is directory: %s\n", relpath); + return -EISDIR; + } + } + + common = kmm_zalloc(sizeof(*common)); + if (common == NULL) + { + ferr("common allocation failed\n"); + return -ENOMEM; + } + + ofd = kmm_zalloc(sizeof(*ofd)); + if (ofd == NULL) + { + ferr("ofd allocation failed\n"); + ret = -ENOMEM; + goto errout_with_common; + } + + pathdup = kmm_malloc(strlen(relpath) + 1); + if (pathdup == NULL) + { + ferr("path allocation failed\n"); + ret = -ENOMEM; + goto errout_with_ofd; + } + + strlcpy(pathdup, relpath, strlen(relpath) + 1); + + if ((oflags & O_TRUNC) != 0 && (oflags & O_ACCMODE) != O_RDONLY) + { + ret = mfs_file_reset_empty(sb, &location, &dirloc, &direntry); + if (ret < 0) + { + ferr("mfs_file_reset_empty failed: %d\n", ret); + goto errout_with_pathdup; + } + } + + memcpy(&common->direntry, &direntry, sizeof(common->direntry)); + memcpy(&common->direntloc, &dirloc, sizeof(common->direntloc)); + common->relpath = pathdup; + common->fileloc = location; + common->refs = 1; + + ofd->sb = sb; + ofd->common = common; + list_clear_node(&ofd->entry); + list_add_tail(&sb->ofiles, &ofd->entry); + filep->f_priv = ofd; + filep->f_pos = 0; + return OK; + +errout_with_pathdup: + kmm_free(pathdup); +errout_with_ofd: + kmm_free(ofd); +errout_with_common: + kmm_free(common); + return ret; +} + +/**************************************************************************** + * Name: mfs_file_close + * + * Description: + * Close one open-file descriptor, releasing shared state when the last + * reference disappears. + * + * Input Parameters: + * sb - The mounted file system instance. + * filep - The VFS file structure to close. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * invalid input. + * + ****************************************************************************/ + +int mfs_file_close(FAR struct mfs_sb_s *sb, FAR struct file *filep) +{ + FAR struct mfs_ofd_s *ofd; + + ofd = mfs_file_ofd(sb, filep); + if (ofd == NULL || ofd->common == NULL) + { + ferr("invalid file handle\n"); + return -EINVAL; + } + + list_delete(&ofd->entry); + ofd->common->refs--; + if (ofd->common->refs == 0) + { + kmm_free(ofd->common->relpath); + kmm_free(ofd->common); + } + + kmm_free(ofd); + filep->f_priv = NULL; + return OK; +} + +/**************************************************************************** + * Name: mfs_file_read + * + * Description: + * Read from an open regular file at its current file position and advance + * that position by the number of bytes read. + * + * Input Parameters: + * sb - The mounted file system instance. + * filep - The VFS file structure to read from. + * buffer - The caller's read buffer. + * buflen - The number of bytes requested. + * + * Returned Value: + * A non-negative byte count is returned on success. A negated errno + * value is returned on failure. + * + ****************************************************************************/ + +ssize_t mfs_file_read(FAR struct mfs_sb_s *sb, FAR struct file *filep, + FAR char *buffer, size_t buflen) +{ + FAR struct mfs_ofd_s *ofd; + ssize_t ret; + + ofd = mfs_file_ofd(sb, filep); + if (ofd == NULL || ofd->common == NULL || (buffer == NULL && buflen != 0)) + { + ferr("invalid args\n"); + return -EINVAL; + } + + if (ofd->common->pos < 0) + { + ferr("negative file position\n"); + return -EINVAL; + } + + if ((uint64_t)ofd->common->pos > UINT32_MAX) + { + return 0; + } + + ret = mfs_file_read_from_off(sb, ofd, (mfs_t)ofd->common->pos, buffer, + buflen); + if (ret < 0) + { + ferr("mfs_file_read_from_off failed: %zd\n", ret); + } + + if (ret > 0) + { + ofd->common->pos += ret; + filep->f_pos = ofd->common->pos; + } + + return ret; +} + +/**************************************************************************** + * Name: mfs_file_write + * + * Description: + * Write to an open regular file, update directory state to the new CTZ + * chain, and advance the current file position. + * + * Input Parameters: + * sb - The mounted file system instance. + * filep - The VFS file structure to write to. + * buffer - The source bytes to write. + * buflen - The number of bytes to write. + * + * Returned Value: + * A non-negative byte count is returned on success. A negated errno + * value is returned on failure. + * + ****************************************************************************/ + +ssize_t mfs_file_write(FAR struct mfs_sb_s *sb, FAR struct file *filep, + FAR const char *buffer, size_t buflen) +{ + FAR struct mfs_ofd_s *ofd; + off_t offset; + mfs_t writeoff; + mfs_t newloc; + mfs_t newsize; + mfs_t oldloc; + mfs_t oldsize; + mfs_t s_idx; + mfs_t s_pgoff; + mfs_t old_eof_idx; + mfs_t old_eof_pgoff; + mfs_t old_final_idx; + mfs_t new_eof_idx; + mfs_t new_eof_pgoff; + mfs_t new_final_idx; + mfs_t first_new_idx; + int cleanupret; + int updateret; + ssize_t ret; + + ofd = mfs_file_ofd(sb, filep); + if (ofd == NULL || ofd->common == NULL || (buffer == NULL && buflen != 0)) + { + ferr("invalid args\n"); + return -EINVAL; + } + + offset = ofd->common->pos; + if ((filep->f_oflags & O_APPEND) != 0) + { + offset = ofd->common->direntry.meta.size; + } + + if (offset < 0) + { + ferr("negative file position\n"); + return -EINVAL; + } + + if ((uint64_t)offset > UINT32_MAX) + { + ferr("file offset too large\n"); + return -EFBIG; + } + + writeoff = (mfs_t)offset; + oldloc = ofd->common->fileloc; + oldsize = ofd->common->direntry.meta.size; + + ret = mfs_file_write_from_off(sb, ofd, writeoff, buffer, buflen, &newloc, + &newsize); + if (ret <= 0) + { + if (ret < 0) + { + ferr("mfs_file_write_from_off failed: %zd\n", ret); + } + + return ret; + } + + if (newloc == MFS_LOCATION_INVALID) + { + ferr("write returned invalid new location\n"); + ret = -EIO; + goto errout; + } + + updateret = mfs_dir_update_file(sb, oldloc, newloc, + &ofd->common->direntloc, + &ofd->common->direntry, (off_t)newsize); + if (updateret < 0) + { + ferr("mfs_dir_update_file failed: %d\n", updateret); + ret = updateret; + goto errout_with_newpages; + } + + ofd->common->fileloc = newloc; + ofd->common->pos = offset + ret; + filep->f_pos = ofd->common->pos; + + cleanupret = mfs_ctz_index_from_off(sb, writeoff, &s_idx, &s_pgoff); + if (cleanupret < 0) + { + ferr("start offset decode failed: %d\n", cleanupret); + ret = cleanupret; + goto errout; + } + + cleanupret = mfs_file_bounds_from_size(sb, oldsize, &old_eof_idx, + &old_eof_pgoff, &old_final_idx); + if (cleanupret < 0) + { + ferr("old bounds decode failed: %d\n", cleanupret); + ret = cleanupret; + goto errout; + } + + UNUSED(s_pgoff); + UNUSED(old_eof_pgoff); + first_new_idx = MIN(s_idx, old_eof_idx); + cleanupret = mfs_file_apply_range(sb, oldloc, old_final_idx, + first_new_idx, old_final_idx, + mfs_report_page_deleted); + if (cleanupret < 0) + { + ferr("old page cleanup failed: %d\n", cleanupret); + ret = cleanupret; + } + + return ret; + +errout_with_newpages: + cleanupret = mfs_ctz_index_from_off(sb, writeoff, &s_idx, &s_pgoff); + if (cleanupret >= 0) + { + cleanupret = mfs_file_bounds_from_size(sb, newsize, + &new_eof_idx, + &new_eof_pgoff, + &new_final_idx); + } + + if (cleanupret >= 0) + { + cleanupret = mfs_file_bounds_from_size(sb, oldsize, + &old_eof_idx, + &old_eof_pgoff, + &old_final_idx); + } + + if (cleanupret >= 0) + { + UNUSED(s_pgoff); + UNUSED(old_eof_pgoff); + UNUSED(new_eof_idx); + UNUSED(new_eof_pgoff); + first_new_idx = MIN(s_idx, old_eof_idx); + cleanupret = mfs_file_apply_range(sb, newloc, new_final_idx, + first_new_idx, new_final_idx, + mfs_release_page); + } + + if (cleanupret < 0) + { + ferr("rollback cleanup failed: %d\n", cleanupret); + } + + return cleanupret < 0 ? cleanupret : ret; + +errout: + ferr("failed: %zd\n", ret); + return ret; +} + +/**************************************************************************** + * Name: mfs_file_seek + * + * Description: + * Update the current logical file position for an open file. + * + * Input Parameters: + * sb - The mounted file system instance. + * filep - The VFS file structure to reposition. + * offset - The seek offset argument. + * whence - The seek base selector. + * + * Returned Value: + * The new file position is returned on success. A negated errno value is + * returned on failure. + * + ****************************************************************************/ + +off_t mfs_file_seek(FAR struct mfs_sb_s *sb, FAR struct file *filep, + off_t offset, int whence) +{ + FAR struct mfs_ofd_s *ofd; + off_t nextpos; + + ofd = mfs_file_ofd(sb, filep); + if (ofd == NULL || ofd->common == NULL) + { + ferr("invalid file handle\n"); + return -EINVAL; + } + + if (whence == SEEK_SET) + { + nextpos = offset; + } + else if (whence == SEEK_CUR) + { + nextpos = ofd->common->pos + offset; + } + else if (whence == SEEK_END) + { + nextpos = ofd->common->direntry.meta.size + offset; + } + else + { + ferr("invalid whence\n"); + return -EINVAL; + } + + if (nextpos < 0) + { + ferr("negative target position\n"); + return -EINVAL; + } + + ofd->common->pos = nextpos; + filep->f_pos = nextpos; + return nextpos; +} + +/**************************************************************************** + * Name: mfs_file_ioctl + * + * Description: + * Handle file-specific ioctl requests for an open mnemofs file. + * + * Input Parameters: + * sb - The mounted file system instance. + * filep - The VFS file structure to operate on. + * cmd - The ioctl command. + * arg - The ioctl argument. + * + * Returned Value: + * Zero (OK) or another command-specific value is returned on success. A + * negated errno value is returned on failure. + * + ****************************************************************************/ + +int mfs_file_ioctl(FAR struct mfs_sb_s *sb, FAR struct file *filep, + int cmd, unsigned long arg) +{ + FAR struct mfs_ofd_s *ofd; + FAR char *path; + size_t len; + int ret; + + ofd = mfs_file_ofd(sb, filep); + if (ofd == NULL || ofd->common == NULL) + { + ferr("invalid file handle\n"); + return -EINVAL; + } + + switch (cmd) + { + case FIOC_FILEPATH: + { + path = (FAR char *)(uintptr_t)arg; + if (path == NULL || ofd->common->relpath == NULL) + { + ferr("invalid filepath buffer\n"); + return -EINVAL; + } + + ret = inode_getpath(filep->f_inode, path, PATH_MAX); + if (ret < 0) + { + ferr("inode_getpath failed: %d\n", ret); + return ret; + } + + len = strlen(path); + if (len > 0 && path[len - 1] != '/') + { + path[len++] = '/'; + path[len] = '\0'; + } + + if (strlcat(path, ofd->common->relpath, PATH_MAX) >= PATH_MAX) + { + ferr("path too long\n"); + return -ENAMETOOLONG; + } + + return OK; + } + + case BIOC_FLUSH: + { + if (sb->mtd == NULL) + { + ferr("missing mtd\n"); + return -ENODEV; + } + + ret = MTD_IOCTL(sb->mtd, BIOC_FLUSH, arg); + if (ret < 0) + { + ferr("BIOC_FLUSH failed: %d\n", ret); + } + + return ret; + } + + default: + { + ferr("unsupported ioctl: %d\n", cmd); + return -ENOTTY; + } + } +} + +/**************************************************************************** + * Name: mfs_file_truncate + * + * Description: + * Update a file's stored length by emitting a new direntry record that + * points to the same file location with a different size. + * + * Input Parameters: + * filep - The open file instance. + * length - The new stored file size. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int mfs_file_truncate(FAR struct mfs_sb_s *sb, FAR struct file *filep, + off_t length) +{ + FAR struct mfs_ofd_s *ofd; + int ret; + + ofd = mfs_file_ofd(sb, filep); + if (ofd == NULL || ofd->common == NULL || length < 0) + { + ferr("invalid args\n"); + return -EINVAL; + } + + if (length == 0) + { + ret = mfs_file_reset_empty(sb, &ofd->common->fileloc, + &ofd->common->direntloc, + &ofd->common->direntry); + } + else + { + ret = mfs_dir_truncate_file(sb, ofd->common->fileloc, + &ofd->common->direntloc, + &ofd->common->direntry, length); + } + + if (ret < 0) + { + ferr("truncate failed: %d\n", ret); + return ret; + } + + if (ofd->common->pos > length) + { + ofd->common->pos = length; + filep->f_pos = length; + } + + return OK; +} + +/**************************************************************************** + * Name: mfs_file_sync + * + * Description: + * Validate one open-file descriptor for sync. mnemofs updates metadata + * eagerly, so no extra flush work is needed here. + * + * Input Parameters: + * sb - The mounted file system instance. + * filep - The VFS file structure to sync. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * invalid input. + * + ****************************************************************************/ + +int mfs_file_sync(FAR struct mfs_sb_s *sb, FAR struct file *filep) +{ + FAR struct mfs_ofd_s *ofd; + + ofd = mfs_file_ofd(sb, filep); + if (ofd == NULL || ofd->common == NULL) + { + ferr("invalid file handle\n"); + return -EINVAL; + } + + return OK; +} + +/**************************************************************************** + * Name: mfs_file_dup + * + * Description: + * Duplicate one open-file descriptor so both VFS file structures share + * the same underlying open-file state. + * + * Input Parameters: + * sb - The mounted file system instance. + * oldp - The existing VFS file structure. + * newp - The new VFS file structure to initialize. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int mfs_file_dup(FAR struct mfs_sb_s *sb, FAR const struct file *oldp, + FAR struct file *newp) +{ + FAR struct mfs_ofd_s *oldofd; + FAR struct mfs_ofd_s *newofd; + + oldofd = mfs_file_ofd(sb, oldp); + if (oldofd == NULL || oldofd->common == NULL || newp == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + newofd = kmm_zalloc(sizeof(*newofd)); + if (newofd == NULL) + { + ferr("ofd allocation failed\n"); + return -ENOMEM; + } + + newofd->sb = oldofd->sb; + newofd->common = oldofd->common; + newofd->common->refs++; + list_clear_node(&newofd->entry); + list_add_tail(&newofd->sb->ofiles, &newofd->entry); + newp->f_priv = newofd; + newp->f_pos = newofd->common->pos; + return OK; +} + +/**************************************************************************** + * Name: mfs_file_fstat + * + * Description: + * Return file status information for one open regular file. + * + * Input Parameters: + * sb - The mounted file system instance. + * filep - The VFS file structure to inspect. + * buf - The location to receive the translated stat data. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned on + * failure. + * + ****************************************************************************/ + +int mfs_file_fstat(FAR struct mfs_sb_s *sb, FAR const struct file *filep, + FAR struct stat *buf) +{ + FAR struct mfs_ofd_s *ofd; + + ofd = mfs_file_ofd(sb, filep); + if (ofd == NULL || ofd->common == NULL || buf == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + mfs_file_stat(sb, ofd->common, buf); + return OK; +} diff --git a/fs/mnemofs/mnemofs_fsobj.c b/fs/mnemofs/mnemofs_fsobj.c deleted file mode 100644 index e6c43814125..00000000000 --- a/fs/mnemofs/mnemofs_fsobj.c +++ /dev/null @@ -1,1136 +0,0 @@ -/**************************************************************************** - * fs/mnemofs/mnemofs_fsobj.c - * - * SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause - * - * 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 mentioned concept. - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include -#include -#include - -#include "mnemofs.h" -#include "fs_heap.h" - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -static mfs_t nobjs_in_path(FAR const char * relpath); -static const char *next_child(FAR const char *relpath); -static const char *last_child(FAR const char *relpath); -static FAR char *mfs_ser_dirent(FAR const struct mfs_dirent_s * const x, - FAR char * const out); - -static FAR const char *mfs_deser_dirent(FAR const char * const in, - FAR struct mfs_dirent_s * const x); - -static int search_ctz_by_name(FAR const struct mfs_sb_s * const sb, - FAR struct mfs_path_s * const path, - const mfs_t depth, FAR const char * const name, - const mfs_t namelen, FAR mfs_t *off, - FAR struct mfs_dirent_s **dirent); - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -const struct mfs_path_s empty_fsobj = -{ - 0 -}; - -/**************************************************************************** - * Public Data - ****************************************************************************/ - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: nobjs_in_path - * - * Description: - * Count number of file system objects in path. This includes root in the - * count. - * - * Input Parameters: - * relpath - Relative Path. - * - * Returned Value: - * The number of file system objects in the path including the root. - * - ****************************************************************************/ - -static mfs_t nobjs_in_path(FAR const char *relpath) -{ - mfs_t count; - - /* If mount point is "/hi", then operations on "/hi/bye" and "/hi/hello/a" - * give respective relpaths as "bye" and "hello/a". Since mnemofs counts - * the root as 1 FS object, these respectively contain 2 and 3 FS objects - * in the path. The last FS object might be a file or a directory, but - * everything else is a directory. The number of FS objects in the path - * is the depth of the FS object the path refers to. - */ - - if (*relpath == 0) - { - return 1; - } - - count = 2; - - while (*relpath != 0) - { - if (*relpath == '/') - { - count++; - } - - relpath++; - } - - return count; -} - -/**************************************************************************** - * Name: next_child - * - * Description: - * Give the pointer to next child that appears in the path. - * - * Input Parameters: - * relpath - Relative Path. - * - * Returned Value: - * The pointer to the next child. This is not allocated, but points to the - * inside relpath itself. - * - ****************************************************************************/ - -static const char *next_child(FAR const char *path) -{ - mfs_t inc = 0; - FAR const char *tmp = path; - - MFS_EXTRA_LOG("NEXT_CHILD", "Requested string is \"%s\" (%p),", path, - path); - - while (*path != 0) - { - if (*path == '/') - { - MFS_EXTRA_LOG("NEXT_CHILD", "Length is %" PRIu32, inc); - DEBUGASSERT(inc == path - tmp); - return path + 1; - } - - path++; - inc++; - } - - MFS_EXTRA_LOG("NEXT_CHILD", "Length is %" PRIu32, inc); - DEBUGASSERT(inc == path - tmp); - MFS_EXTRA_LOG("NEXT_CHILD", "Last FS Object in string."); - return path; -} - -/**************************************************************************** - * Name: next_child - * - * Description: - * Give the pointer to next child that appears in the path. - * - * Input Parameters: - * relpath - Relative Path. - * - * Returned Value: - * The pointer to the next child. This is not allocated, but points to the - * inside relpath itself. - * - ****************************************************************************/ - -static const char *last_child(FAR const char *relpath) -{ - const mfs_t len = strlen(relpath); - mfs_t i; - - for (i = len - 1; i > 0; i--) - { - if (relpath[i - 1] == '/') - { - return relpath + i; - } - } - - return relpath; -} - -/**************************************************************************** - * Name: mfs_ser_dirent - * - * Description: - * Serialize a direntry. - * - * Input Parameters: - * x - Direntry. - * out - Buffer to populate. - * - * Returned Value: - * Pointer to after the end of serialized content in out. - * - ****************************************************************************/ - -static FAR char *mfs_ser_dirent(FAR const struct mfs_dirent_s * const x, - FAR char * const out) -{ - FAR char *o = out; - - o = mfs_ser_16(x->name_hash, o); - o = mfs_ser_16(x->mode, o); - o = mfs_ser_mfs(x->sz, o); - o = mfs_ser_timespec(&x->st_atim, o); - o = mfs_ser_timespec(&x->st_mtim, o); - o = mfs_ser_timespec(&x->st_ctim, o); - o = mfs_ser_ctz(&x->ctz, o); - o = mfs_ser_8(x->namelen, o); - o = mfs_ser_str(x->name, x->namelen, o); - - return o; -} - -/**************************************************************************** - * Name: mfs_deser_dirent - * - * Description: - * Deserialize a direntry. - * - * Input Parameters: - * in - Buffer. - * x - Direntry to populate. - * - * Returned Value: - * Pointer to after the end of deserialized content in in. - * - ****************************************************************************/ - -static FAR const char *mfs_deser_dirent(FAR const char * const in, - FAR struct mfs_dirent_s * const x) -{ - FAR const char *i = in; - - i = mfs_deser_16(i, &x->name_hash); - i = mfs_deser_16(i, &x->mode); - i = mfs_deser_mfs(i, &x->sz); - i = mfs_deser_timespec(i, &x->st_atim); - i = mfs_deser_timespec(i, &x->st_mtim); - i = mfs_deser_timespec(i, &x->st_ctim); - i = mfs_deser_ctz(i, &x->ctz); - i = mfs_deser_8(i, &x->namelen); - i = mfs_deser_str(i, x->name, x->namelen); - - return i; -} - -int pitr_traverse(FAR struct mfs_sb_s *sb, FAR struct mfs_path_s *path, - mfs_t depth, FAR mfs_t *cap) -{ - int ret = OK; - mfs_t i; - mfs_t pg; - struct mfs_pitr_s pitr; - struct mfs_ctz_s ctz; - FAR struct mfs_dirent_s *dirent = NULL; - - /* TODO: Double traversal can be made faster into a single traversal. */ - - ctz = path[depth - 1].ctz; - - if (ctz.idx_e == 0 && ctz.pg_e == 0) - { - /* Not a valid one. TODO: Does this happens? */ - - goto errout; - } - - for (i = ctz.idx_e; i > 0; i--) - { - mfs_ba_markusedpg(sb, pg); - - pg = mfs_ctz_travel(sb, i, pg, i - 1); - if (pg == 0) - { - break; - } - } - - memset(path + depth, 0, *cap - depth); - - if (depth == *cap) - { - *cap = (*cap * 3) / 2; /* Don't want to double it for memory. */ - - path = fs_heap_realloc(path, (*cap) * sizeof(struct mfs_path_s)); - if (predict_false(path == NULL)) - { - ret = -ENOMEM; - goto errout; - } - } - - mfs_pitr_init(sb, path, depth, &pitr, true); - - while (true) - { - mfs_pitr_readdirent(sb, path, &pitr, &dirent); - if (dirent == NULL) - { - break; - } - - if (S_ISDIR(dirent->mode)) - { - path[(depth + 1) - 1].ctz = dirent->ctz; - - ret = pitr_traverse(sb, path, depth + 1, cap); - if (predict_false(ret < 0)) - { - mfs_free_dirent(dirent); - goto errout; - } - } - - mfs_pitr_adv_bydirent(&pitr, dirent); - mfs_free_dirent(dirent); - } - -errout: - return ret; -} - -/**************************************************************************** - * Public Function Prototypes - ****************************************************************************/ - -FAR const char *mfs_path2childname(FAR const char *relpath) -{ - FAR const char *last = relpath + strlen(relpath) - 1; - - while (last >= relpath && *last != '/') - { - last--; - } - - return last + 1; -} - -mfs_t mfs_get_fsz(FAR struct mfs_sb_s * const sb, - FAR const struct mfs_path_s * const path, - const mfs_t depth) -{ - mfs_t sz; - - if (depth == 0) - { - /* Master node. */ - - return 0; - } - else if (depth == 1) - { - sz = MFS_MN(sb).root_sz; /* Updated size. */ - - /* Journal updated to the root creates a new master node entry. TODO - * this and moving of the journal. - */ - - finfo("File size got as %u for root.", sz); - return sz; - } - - return path[depth - 1].sz; -} - -bool mfs_obj_isempty(FAR struct mfs_sb_s * const sb, - FAR struct mfs_path_s *path, - FAR struct mfs_pitr_s * const pitr) -{ - bool ret; - FAR struct mfs_dirent_s *dirent = NULL; - - mfs_pitr_readdirent(sb, path, pitr, &dirent); - ret = (dirent->sz == 0); - mfs_free_dirent(dirent); - - return ret; -} - -void mfs_free_dirent(FAR struct mfs_dirent_s *dirent) -{ - fs_heap_free(dirent); - - finfo("Dirent freed."); -} - -bool mfs_searchfopen(FAR const struct mfs_sb_s * const sb, - FAR const struct mfs_path_s * const path, - const mfs_t depth) -{ - FAR struct mfs_ofd_s *ofd = NULL; - - list_for_every_entry(&sb->of, ofd, struct mfs_ofd_s, list) - { - if (ofd->com->depth != depth) - { - continue; - } - - if (mfs_path_eq(&ofd->com->path[depth - 1], &path[depth - 1])) - { - return true; - } - } - - return false; -} - -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) -{ - int ret = OK; - struct mfs_pitr_s p_pitr; - FAR struct mfs_dirent_s *p_dirent = NULL; - - mfs_pitr_init(sb, path, depth - 1, &p_pitr, true); - mfs_pitr_adv_tochild(&p_pitr, path); - mfs_pitr_readdirent(sb, path, &p_pitr, &p_dirent); - - ret = mfs_lru_del(sb, pitr->c_off, MFS_DIRENTSZ(dirent), path, depth); - - mfs_free_dirent(p_dirent); - mfs_pitr_free(&p_pitr); - - return ret; -} - -int mfs_pitr_rm(FAR struct mfs_sb_s * const sb, - FAR struct mfs_path_s * const path, - const mfs_t depth, bool rm_child) -{ - int ret = OK; - struct mfs_pitr_s pitr; - FAR struct mfs_dirent_s *dirent = NULL; - - /* TODO: MFS_LOG */ - - mfs_pitr_init(sb, path, depth, &pitr, true); - mfs_pitr_readdirent(sb, path, &pitr, &dirent); - - ret = mfs_pitr_rmdirent(sb, path, depth, &pitr, dirent); - if (predict_false(ret < 0)) - { - goto errout; - } - - if (rm_child) - { - ret = mfs_lru_del(sb, 0, path[depth - 1].sz, path, depth); - if (predict_false(ret < 0)) - { - goto errout; - } - } - -errout: - mfs_free_dirent(dirent); - mfs_pitr_free(&pitr); - - return ret; -} - -int mfs_pitr_init(FAR const struct mfs_sb_s * const sb, - FAR const struct mfs_path_s * const path, - const mfs_t depth, FAR struct mfs_pitr_s * const pitr, - bool child) -{ - /* Ensure updated CTZ location from the journal before this. */ - - int ret = OK; - const int diff = child ? 1 : 0; - const mfs_t p_depth = depth - diff; - - if (predict_false(depth < diff)) - { - ret = -EINVAL; - goto errout; - } - - pitr->c_off = 0; - pitr->depth = p_depth; - - if (predict_true(p_depth != 0)) - { - pitr->p = path[p_depth - 1]; - } - else - { - /* 0 or garbage value is fine for master node, not required. */ - - pitr->p.ctz.idx_e = 0; - pitr->p.ctz.pg_e = 0; - pitr->p.off = 0; - pitr->p.sz = 1; /* For 1 traversal to get root. */ - } - - finfo("Pitr initialized at depth %u, with CTZ (%u, %u) and size %u.", - p_depth, pitr->p.ctz.idx_e, pitr->p.ctz.pg_e, pitr->p.sz); - -errout: - return ret; -} - -void mfs_pitr_free(FAR const struct mfs_pitr_s * const pitr) -{ - MFS_EXTRA_LOG("MFS_PITR_FREE", "Parent iterator at %p freed.", pitr); - MFS_EXTRA_LOG_PITR(pitr); -} - -void mfs_pitr_adv_off(FAR struct mfs_pitr_s * const pitr, - const mfs_t off) -{ - pitr->c_off += off; - - finfo("Pitr at depth %u with CTZ (%u, %u) advanced by %u to %u offset.", - pitr->depth, pitr->p.ctz.idx_e, pitr->p.ctz.pg_e, off, pitr->c_off); -} - -void mfs_pitr_adv_bydirent(FAR struct mfs_pitr_s * const pitr, - FAR const struct mfs_dirent_s * const dirent) -{ - mfs_pitr_adv_off(pitr, MFS_DIRENTSZ(dirent)); - - finfo("Pitr at depth %u with CTZ (%u, %u) advanced by %u to %u offset.", - pitr->depth, pitr->p.ctz.idx_e, pitr->p.ctz.pg_e, - MFS_DIRENTSZ(dirent), pitr->c_off); -} - -void mfs_pitr_adv_tochild(FAR struct mfs_pitr_s * const pitr, - FAR const struct mfs_path_s * const path) -{ - /* (pitr->depth + 1) - 1 is the child's index. */ - - MFS_EXTRA_LOG("MFS_PITR_ADV_TOCHILD", "Advance pitr to child's offset."); - MFS_EXTRA_LOG_PITR(pitr); - MFS_EXTRA_LOG("MFS_PITR_ADV_TOCHILD", "New offset %" PRIu32, pitr->c_off); - pitr->c_off = path[pitr->depth].off; -} - -int mfs_pitr_readdirent(FAR const struct mfs_sb_s * const sb, - FAR struct mfs_path_s *path, - FAR struct mfs_pitr_s * const pitr, - FAR struct mfs_dirent_s **dirent) -{ - int ret = OK; - mfs_t sz; - const mfs_t len = sizeof(struct mfs_dirent_s) \ - + NAME_MAX + 1; - char rd[len]; - FAR struct mfs_dirent_s *d = NULL; - FAR struct mfs_dirent_s *tmp = NULL; - - if (dirent == NULL) - { - return ret; - } - - *dirent = NULL; - memset(rd, 0, len); - - d = fs_heap_zalloc(len); - if (predict_false(d == NULL)) - { - ret = -ENOMEM; - goto errout; - } - else if (pitr->c_off >= pitr->p.sz) - { - goto errout_with_d; - } - else if (pitr->depth == 0) - { - d->name[0] = 0; - d->namelen = 0; - d->ctz = MFS_MN(sb).root_ctz; - d->mode = MFS_MN(sb).root_mode; - d->name_hash = mfs_hash(d->name, d->namelen); - d->st_atim = MFS_MN(sb).root_st_atim; - d->st_ctim = MFS_MN(sb).root_st_ctim; - d->st_mtim = MFS_MN(sb).root_st_mtim; - d->sz = MFS_MN(sb).root_sz; - - pitr->c_off = 1; /* To prevent infinite loop. */ - } - else - { - ret = mfs_lru_rdfromoff(sb, pitr->c_off, path, pitr->depth, rd, len); - if (predict_false(ret < 0)) - { - goto errout_with_d; - } - - mfs_deser_dirent(rd, d); - } - - sz = MFS_DIRENTSZ(d); - tmp = fs_heap_realloc(d, sz); - if (predict_true(tmp != NULL)) - { - d = tmp; - } - - *dirent = d; - DEBUGASSERT(pitr->depth == 0 || strcmp(d->name, "")); - finfo("Read direntry at %u offset, %u depth for CTZ (%u, %u). " \ - "Direntry name: \"%.*s\" with name length %u and size %u.", - pitr->c_off, pitr->depth, pitr->p.ctz.idx_e, pitr->p.ctz.pg_e, - d->namelen, d->name, d->namelen, d->sz); - - return ret; - -errout_with_d: - fs_heap_free(d); - - if (ret < 0) - { - finfo("Direntry could not be allocated."); - } - else if (*dirent == NULL) - { - finfo("No direntry found."); - } - -errout: - return ret; -} - -int mfs_pitr_adv(FAR struct mfs_sb_s * const sb, - FAR struct mfs_path_s *path, - FAR struct mfs_pitr_s * const pitr) -{ - int ret = OK; - FAR struct mfs_dirent_s *dirent; - - ret = mfs_pitr_readdirent(sb, path, pitr, &dirent); - if (predict_false(ret < 0)) - { - goto errout; - } - - mfs_pitr_adv_bydirent(pitr, dirent); - mfs_free_dirent(dirent); - - finfo("Pitr for CTZ (%u, %u) advanced to offset %u.", - pitr->p.ctz.idx_e, pitr->p.ctz.pg_e, pitr->c_off); - -errout: - return ret; -} - -static int search_ctz_by_name(FAR const struct mfs_sb_s * const sb, - FAR struct mfs_path_s * const path, - const mfs_t depth, FAR const char * const name, - const mfs_t namelen, FAR mfs_t *off, - FAR struct mfs_dirent_s **dirent) -{ - /* NOTE: depth is of the parent here. */ - - /* Applies LRU updates. */ - - int ret = OK; - bool found = false; - uint16_t name_hash; - struct mfs_pitr_s pitr; - FAR struct mfs_dirent_s *nd; - - *dirent = NULL; - - if (depth == 0) - { - DEBUGASSERT(namelen == 0); - - nd = fs_heap_zalloc(sizeof(struct mfs_dirent_s)); - if (predict_false(nd == NULL)) - { - ret = -ENOMEM; - goto errout; - } - - *off = 0; - nd->namelen = namelen; - nd->ctz = MFS_MN(sb).root_ctz; - nd->mode = MFS_MN(sb).root_mode; - nd->name_hash = mfs_hash(nd->name, nd->namelen); - nd->st_atim = MFS_MN(sb).root_st_atim; - nd->st_ctim = MFS_MN(sb).root_st_ctim; - nd->st_mtim = MFS_MN(sb).root_st_mtim; - nd->sz = MFS_MN(sb).root_sz; - - *dirent = nd; - goto errout; - } - - name_hash = mfs_hash(name, namelen); - - ret = mfs_lru_getupdatedinfo(sb, path, depth); - if (predict_false(ret < 0)) - { - goto errout; - } - - ret = mfs_pitr_init(sb, path, depth, &pitr, false); - if (predict_false(ret < 0)) - { - goto errout; - } - - for (; ; ) - { - /* Readdirent takes care of LRU updates. */ - - ret = mfs_pitr_readdirent(sb, path, &pitr, &nd); - if (predict_false(ret < 0 || nd == NULL)) - { - ret = -ENONET; - goto errout; - } - - if (nd->name_hash == name_hash && - !strncmp(nd->name, name, MIN(nd->namelen, namelen))) - { - found = true; - path[depth].sz = nd->sz; - path[depth].ctz = nd->ctz; - path[depth].off = pitr.c_off; - *off = pitr.c_off; - break; - } - - mfs_pitr_adv_bydirent(&pitr, nd); - mfs_free_dirent(nd); - } - -errout: - if (found) - { - finfo("Searched \"%.*s\" direntry inside CTZ (%u, %u) at depth %u," - " size %u.", namelen, name, path[depth - 1].ctz.idx_e, - path[depth - 1].ctz.pg_e, depth, path[depth - 1].sz); - *dirent = nd; - } - else - { - ret = -ENOENT; - finfo("Can not find requested direntry in parent. Ret: %d.", ret); - } - - return ret; -} - -int mfs_get_patharr(FAR const struct mfs_sb_s * const sb, - FAR const char *relpath, FAR struct mfs_path_s **path, - FAR mfs_t *depth) -{ - int ret = OK; - int ret_flags = 0; - mfs_t i; - mfs_t sz; - mfs_t off; - mfs_t n_objs; - mfs_t name_len; - FAR const char *cur = NULL; - FAR const char *next = NULL; - struct mfs_ctz_s ctz; - FAR struct mfs_path_s *np = NULL; - FAR struct mfs_dirent_s *dirent = NULL; - - MFS_LOG("MFS_GET_PATHARR", "Entry."); - MFS_EXTRA_LOG("MFS_GET_PATHARR", "Relpath is \"%s\".", relpath); - - *path = NULL; - MFS_EXTRA_LOG("MFS_GET_PATHARR", "Path is %p.", path); - - n_objs = nobjs_in_path(relpath); - MFS_EXTRA_LOG("MFS_GET_PATHARR", "There are %" PRIu32 " objects in path.", - n_objs); - - np = fs_heap_zalloc(n_objs * sizeof(struct mfs_path_s)); - if (predict_false(np == NULL)) - { - MFS_EXTRA_LOG("MFS_GET_PATHARR", "Could not allocate Path array."); - ret = -ENOMEM; - goto errout; - } - else - { - MFS_EXTRA_LOG("MFS_GET_PATHARR", "Path array is allocated at %p.", np); - } - - DEBUGASSERT(*cur != '/'); /* Relpath should not start with a '/' */ - - ctz = MFS_MN(sb).root_ctz; - sz = MFS_MN(sb).root_sz; - np[0].sz = sz; - np[0].ctz = ctz; - np[0].off = 0; - cur = relpath; - next = next_child(cur); - - DEBUGASSERT(*next != 0 || n_objs == 2); - - name_len = *next == 0 ? next - cur : next - cur - 1; - - MFS_EXTRA_LOG("MFS_GET_PATHARR", "Root Master Node."); - MFS_EXTRA_LOG("MFS_GET_PATHARR", "\tCTZ is (%" PRIu32 ", %" PRIu32 ")", - MFS_MN(sb).root_ctz.idx_e, MFS_MN(sb).root_ctz.pg_e); - MFS_EXTRA_LOG("MFS_GET_PATHARR", "\tSize is %" PRIu32, sz); - - if (predict_false(n_objs == 1)) - { - MFS_EXTRA_LOG("MFS_GET_PATHARR", "There is only one object (root)."); - ret_flags |= MFS_ISDIR | MFS_EXIST; - - /* This will not go into the loop. */ - } - else if (predict_false(n_objs == 2)) - { - MFS_EXTRA_LOG("MFS_GET_PATHARR", "There are only 2 objects."); - ret_flags |= MFS_P_EXIST | MFS_P_ISDIR; - } - - /* MFS_MN(sb).root_* is always up to date, no need for journal update. */ - - for (i = 1; i < n_objs; i++) - { - MFS_EXTRA_LOG("MFS_GET_PATHARR", "Looking at depth %" PRIu32, i); - - /* np[i] is the fs object at depth i + 1. */ - - /* Need to update journal for every level in the path as, for eg., the - * child can be deleted, etc. Same goes for LRU, which is taken care of - * by search_ctz_by_name function. - */ - - MFS_EXTRA_LOG("MFS_GET_PATHARR", "Current String is \"%.*s\" (%p)", - name_len, cur, cur); - MFS_EXTRA_LOG("MFS_GET_PATHARR", "Name length is %" PRIu32, name_len); - MFS_EXTRA_LOG("MFS_GET_PATHARR", "Next String is \"%s\"", next); - - ret = search_ctz_by_name(sb, np, i, cur, name_len, &off, &dirent); - if (predict_false(ret < 0)) - { - MFS_EXTRA_LOG("MFS_GET_PATHARR", "Could not find CTZ."); - goto errout_with_ret_flags; - } - else - { - MFS_EXTRA_LOG("MFS_GET_PATHARR", "Found CTZ."); - MFS_EXTRA_LOG("MFS_GET_PATHARR", "New Offset is %" PRIu32, off); - MFS_EXTRA_LOG_DIRENT(dirent); - } - - if (i < n_objs - 2 && !S_ISDIR(dirent->mode)) - { - MFS_EXTRA_LOG("MFS_GET_PATHARR", "Depth %" PRIu32 " contains file", - i); - ret_flags |= MFS_FINPATH; - goto errout_with_ret_flags; - } - else if (i == n_objs - 2) - { - MFS_EXTRA_LOG("MFS_GET_PATHARR", "Parent exists."); - ret_flags |= MFS_P_EXIST; - if (S_ISDIR(dirent->mode)) - { - MFS_EXTRA_LOG("MFS_GET_PATHARR", "Parent is a directory."); - ret_flags |= MFS_P_ISDIR; - } - else - { - MFS_EXTRA_LOG("MFS_GET_PATHARR", "Parent is a file."); - ret_flags |= MFS_FINPATH; - goto errout_with_ret_flags; - } - } - else if (i == n_objs - 1) - { - MFS_EXTRA_LOG("MFS_GET_PATHARR", "Child exists."); - ret_flags |= MFS_EXIST; - if (S_ISDIR(dirent->mode)) - { - MFS_EXTRA_LOG("MFS_GET_PATHARR", "Child is a directory."); - ret_flags |= MFS_ISDIR; - } - else - { - MFS_EXTRA_LOG("MFS_GET_PATHARR", "Child is a file."); - ret_flags |= MFS_ISFILE; - } - } - else - { - /* OK */ - } - - np[i].ctz = dirent->ctz; - np[i].off = off; - np[i].sz = dirent->sz; - - ctz = dirent->ctz; - - mfs_free_dirent(dirent); - - cur = next; - next = next_child(cur); - name_len = *next == 0 ? next - cur : next - cur - 1; - - DEBUGASSERT(cur != next); - } - - ret = ret_flags; - *depth = n_objs; - *path = np; - - MFS_EXTRA_LOG("MFS_GET_PATHARR", "Child is a file."); - - MFS_LOG("MKDIR", "Exit | Flags: %u, Depth %u.", ret, n_objs); - return ret; - -errout_with_ret_flags: - ret = ret_flags; - *depth = n_objs; - *path = np; - - /* mfs_free_patharr(np) : All callers will free np (ie. path) when done - * with it. - */ - -errout: - MFS_LOG("MKDIR", "Exit | Flags: %u, Depth %u.", ret, n_objs); - return ret; -} - -void mfs_free_patharr(FAR struct mfs_path_s *path) -{ - MFS_EXTRA_LOG("MFS_FREE_PATHARR", "Path array at %p freed.", path); - fs_heap_free(path); -} - -void mfs_pitr_reset(FAR struct mfs_pitr_s * const pitr) -{ - pitr->c_off = 0; - - finfo("Pitr for CTZ (%u, %u) reset.", - pitr->p.ctz.idx_e, pitr->p.ctz.pg_e); -} - -int mfs_pitr_appenddirent(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) -{ - /* Depth is depth of the child to be appended. */ - - int ret = OK; - const mfs_t len = MFS_DIRENTSZ(dirent); - char wd[len]; - - if (pitr->depth == 0) - { - /* Root is the only child of the master node. */ - - ret = -EINVAL; - goto errout; - } - - /* TODO: If the parent directory is newly formed (ie. size is 0), then - * allocate space for it. This can be done better. Just allocate page when - * its created and added first to LRU, and then add a check to ensure it - * doesn't get re-allocated when written. A field like "new" would be - * helpful in the LRU node for this. - */ - - memset(wd, 0, len); - - mfs_ser_dirent(dirent, wd); - ret = mfs_lru_wr(sb, pitr->p.sz, len, path, pitr->depth, wd); - if (predict_false(ret < 0)) - { - goto errout; - } - -errout: - return ret; -} - -int mfs_pitr_appendnew(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 char * const relpath, const mode_t mode) -{ - /* Depth is depth of the child to be appended. */ - - int ret = OK; - FAR const char *cur = last_child(relpath); - FAR const char *next = next_child(cur); - const mfs_t len = *next == 0 ? next - cur : next - cur - 1; - struct timespec ts; - FAR struct mfs_dirent_s *d = NULL; - - DEBUGASSERT(depth > 0); - - d = fs_heap_zalloc(sizeof(struct mfs_dirent_s) + len); - if (predict_false(d == NULL)) - { - ret = -ENOMEM; - goto errout; - } - - clock_gettime(CLOCK_REALTIME, &ts); - - d->ctz = empty_fsobj.ctz; - d->mode = mode; - d->st_atim = ts; - d->st_mtim = ts; - d->st_ctim = ts; - d->namelen = len; - strncpy(d->name, cur, d->namelen); - d->name_hash = mfs_hash(d->name, d->namelen); - d->sz = 0; - - /* Add the new direntry in this path. */ - - path[depth - 1].ctz = d->ctz; - path[depth - 1].off = pitr->p.sz; - path[depth - 1].sz = d->sz; - - ret = mfs_pitr_appenddirent(sb, path, depth, pitr, d); - if (predict_false(ret < 0)) - { - goto errout_with_d; - } - - finfo("Direntry appended to Pitr with %u depth, and CTZ (%u, %u). " \ - "Direntry name: \"%.*s\" with name length %u at offset %u.", - pitr->depth, pitr->p.ctz.idx_e, pitr->p.ctz.pg_e, d->namelen, - d->name, d->namelen, path[depth - 1].off); - -errout_with_d: - mfs_free_dirent(d); - -errout: - return ret; -} - -/* Only for initialization of the block allocator. */ - -int mfs_pitr_traversefs(FAR struct mfs_sb_s * sb, const struct mfs_ctz_s ctz, - int type) -{ - /* type takes in MFS_ISFILE & MFS_ISDIR. */ - - int ret = OK; - mfs_t capacity; - FAR struct mfs_path_s *path = NULL; - - capacity = MFS_TRAVERSE_INITSZ; - path = fs_heap_zalloc(capacity * sizeof(struct mfs_path_s)); - if (predict_false(path == NULL)) - { - ret = -ENOMEM; - goto errout; - } - - path[0].off = 0; - path[0].ctz = MFS_MN(sb).root_ctz; - path[0].sz = MFS_MN(sb).root_sz; - - ret = pitr_traverse(sb, path, 1, &capacity); - - mfs_free_patharr(path); - -errout: - return ret; -} diff --git a/fs/mnemofs/mnemofs_journal.c b/fs/mnemofs/mnemofs_journal.c deleted file mode 100644 index 8a3159259fa..00000000000 --- a/fs/mnemofs/mnemofs_journal.c +++ /dev/null @@ -1,887 +0,0 @@ -/**************************************************************************** - * fs/mnemofs/mnemofs_journal.c - * - * SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause - * - * 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" -#include "fs_heap.h" - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -#define MFS_JRNL_SUFFIXSZ (8 + 2) /* 8 byte magic sequence + no. of blks */ -#define MFS_LOGSZ(depth) (sizeof(mfs_t) * 2 + sizeof(struct mfs_ctz_s) + \ - sizeof(struct mfs_path_s) * depth + \ - sizeof(struct timespec) * 3 + sizeof(uint16_t)) - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -/* NOTE: Even if higher level functions use path as struct mfs_path_s, - * journal only uses struct mfs_ctz_s to avoid problems during write and - * read of logs. The offsets in struct mfs_path_s will be applied by the - * search methods of higher functions. - */ - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -FAR static const char *deser_log(FAR const char * const in, - FAR struct mfs_jrnl_log_s * const x); -FAR static char *ser_log(FAR const struct mfs_jrnl_log_s * const x, - FAR char * const out); - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/**************************************************************************** - * Public Data - ****************************************************************************/ - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: mfs_jrnl_rdlog - * - * Description: - * Read a log to the journal from given location, and update location to - * point to next log location. - * - * Input Parameters: - * sb - Superblock instance of the device. - * blkidx - Journal Block Index of the current block. - * pg_in_blk - Page offset in the block. - * log - To populate with the log. - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - * Assumptions/Limitations: - * This function does NOT care about start of the journal, or even, if - * the initial requested area is inside the journal. It will malfunction - * if not used properly. Usually this is used in an iterative manner, and - * hence the first time blkidx and pg_in_blk are initialized, they should - * be derived from the values in MFS_JRNL(sb) respectively. - * - * This updates the blkidx and pg_in_blk to point to the next log, and - * returns an -ENOSPC when end of journal is reached in traversal. - * - * Free the log after use. - * - ****************************************************************************/ - -int mfs_jrnl_rdlog(FAR const struct mfs_sb_s *const sb, - FAR mfs_t *blkidx, FAR mfs_t *pg_in_blk, - FAR struct mfs_jrnl_log_s *log) -{ - int ret = OK; - char tmp[4]; - mfs_t log_sz; - mfs_t jrnl_pg; - mfs_t jrnl_blk; - FAR char *buf = NULL; - - DEBUGASSERT(*pg_in_blk % MFS_PGSZ(sb) == 0); - - jrnl_blk = mfs_jrnl_blkidx2blk(sb, *blkidx); - jrnl_pg = MFS_BLK2PG(sb, jrnl_blk) + *pg_in_blk; - - /* First 4 bytes contain the size of the entire log. */ - - ret = mfs_read_page(sb, tmp, 4, jrnl_pg, 0); - if (predict_false(ret < 0)) - { - goto errout; - } - - mfs_deser_mfs(tmp, &log_sz); - if (log_sz == 0) - { - ret = -ENOSPC; - goto errout; - } - - buf = fs_heap_zalloc(log_sz); - if (predict_false(buf == NULL)) - { - ret = -ENOMEM; - goto errout; - } - - ret = mfs_read_page(sb, buf, log_sz, jrnl_pg, 4); - if (predict_false(ret < 0)) - { - goto errout_with_buf; - } - - ret = OK; - - if (predict_false(deser_log(buf, log) == NULL)) - { - ret = -ENOMEM; - goto errout_with_buf; - } - - (*pg_in_blk)++; - - if (*pg_in_blk >= MFS_PGINBLK(sb)) - { - *pg_in_blk = 0; - (*blkidx)++; - } - -errout_with_buf: - fs_heap_free(buf); - -errout: - return ret; -} - -/**************************************************************************** - * Name: ser_log - * - * Description: - * Serialize a log. - * - * Input Parameters: - * n - Log to serialize - * out - Output array where to serialize. - * - * Returned Value: - * Pointer to byte after the end of serialized value. - * - * Assumptions/Limitations: - * This assumes the out buffer has enough space to hold the inline path. - * - * This doesn't require the hash to be pre-calculated. - * - ****************************************************************************/ - -FAR static char *ser_log(FAR const struct mfs_jrnl_log_s * const x, - FAR char * const out) -{ - FAR char *o = out; - mfs_t i; - - o = mfs_ser_mfs(x->depth, o); - o = mfs_ser_mfs(x->sz_new, o); - o = mfs_ser_ctz(&x->loc_new, o); - o = mfs_ser_timespec(&x->st_mtim_new, o); - o = mfs_ser_timespec(&x->st_atim_new, o); - o = mfs_ser_timespec(&x->st_ctim_new, o); - - for (i = 0; i < x->depth; i++) - { - o = mfs_ser_path(&x->path[i], o); - } - - o = mfs_ser_16(mfs_hash(out, o - out), o); - return o; -} - -/**************************************************************************** - * Name: deser_log - * - * Description: - * Deserialize a log. - * - * Input Parameters: - * in - Input array from where to deserialize. - * n - Log to deserialize - * - * Returned Value: - * NULL - Error. - * Pointer to byte after the end of serialized value. - * - * Assumptions/Limitations: - * This allocates space for the path, and the log should freed after use. - * - ****************************************************************************/ - -FAR static const char *deser_log(FAR const char * const in, - FAR struct mfs_jrnl_log_s * const x) -{ - FAR const char *i = in; - mfs_t k; - - i = mfs_deser_mfs(i, &x->depth); - i = mfs_deser_mfs(i, &x->sz_new); - i = mfs_deser_ctz(i, &x->loc_new); - i = mfs_deser_timespec(i, &x->st_mtim_new); - i = mfs_deser_timespec(i, &x->st_atim_new); - i = mfs_deser_timespec(i, &x->st_ctim_new); - - /* Allocates path. Deallocate using mfs_jrnl_log_free. */ - - x->path = fs_heap_zalloc(sizeof(struct mfs_jrnl_log_s) * x->depth); - if (predict_false(x->path == NULL)) - { - return NULL; - } - - for (k = 0; k < x->depth; k++) - { - i = mfs_deser_path(i, &x->path[k]); - } - - i = mfs_deser_16(i, &x->hash); - return i; -} - -/**************************************************************************** - * Name: mfs_jrnl_log_free - * - * Description: - * Free the log. - * - * Input Parameters: - * log - Log - * - * Assumptions/Limitations: - * This allocates space for the path, and the log should freed after use. - * - ****************************************************************************/ - -void mfs_jrnl_log_free(FAR const struct mfs_jrnl_log_s * const log) -{ - fs_heap_free(log->path); -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -int mfs_jrnl_init(FAR struct mfs_sb_s * const sb, mfs_t blk) -{ - char buftmp[2]; - int ret = OK; - mfs_t sz; - mfs_t blkidx; - mfs_t pg_in_blk; - struct mfs_jrnl_log_s log; - - /* Magic sequence is already used to find the block, so not required. */ - - mfs_read_page(sb, buftmp, 2, MFS_BLK2PG(sb, blk), 8); - mfs_deser_16(buftmp, &MFS_JRNL(sb).n_blks); - - if (MFS_JRNL(sb).n_blks == 0) - { - ret = -EINVAL; - goto errout; - } - - sz = MFS_JRNL_SUFFIXSZ + ((CONFIG_MNEMOFS_JOURNAL_NBLKS + 2) * 4); - - MFS_JRNL(sb).jrnl_start = blk; - MFS_JRNL(sb).log_cpg = (sz + (MFS_PGSZ(sb) - 1)) / MFS_PGSZ(sb); - MFS_JRNL(sb).log_spg = MFS_JRNL(sb).log_cpg; - MFS_JRNL(sb).log_cblkidx = 0; - MFS_JRNL(sb).log_sblkidx = MFS_JRNL(sb).log_cblkidx; - MFS_JRNL(sb).jrnlarr_pg = MFS_BLK2PG(sb, blk); /* Assuming pgsz > 10 */ - MFS_JRNL(sb).jrnlarr_pgoff = MFS_JRNL_SUFFIXSZ; - - /* Number of logs */ - - MFS_JRNL(sb).n_logs = 0; - blkidx = MFS_JRNL(sb).log_sblkidx; - pg_in_blk = MFS_JRNL(sb).log_spg % MFS_PGINBLK(sb); - - while (true) - { - ret = mfs_jrnl_rdlog(sb, &blkidx, &pg_in_blk, &log); - if (predict_false(ret < 0 && ret != -ENOSPC)) - { - goto errout; - } - else if (ret == -ENOSPC) - { - ret = OK; - break; - } - - /* Assumes checking the depth is enough to check if it's empty, as - * theoretically there are no blocks with depth 0, as root has a - * depth of 1. - */ - - if (log.depth == 0) - { - DEBUGASSERT(log.path == NULL); - break; - } - - MFS_JRNL(sb).n_logs++; - mfs_jrnl_log_free(&log); - } - - /* Master node */ - - MFS_JRNL(sb).mblk1 = mfs_jrnl_blkidx2blk(sb, MFS_JRNL(sb).n_blks); - MFS_JRNL(sb).mblk2 = mfs_jrnl_blkidx2blk(sb, MFS_JRNL(sb).n_blks + 1); - - /* TODO: Read all pages in master blocks to find the last master node - * update. - */ - -errout: - return ret; -} - -int mfs_jrnl_fmt(FAR struct mfs_sb_s * const sb, FAR mfs_t *blk1, - FAR mfs_t *blk2, FAR mfs_t *jrnl_blk) -{ - int i; - int ret = OK; - mfs_t sz; - mfs_t pg; - mfs_t blk; - mfs_t alloc_blk; - FAR char *tmp; - FAR char *buf = NULL; - - /* TODO: Replace jrnl_blk with MFS_JRNL(sb).jrnl_start if possible. */ - - /* Write magic sequence, size of jrnlarr, and then the jrnlarr. */ - - sz = MFS_JRNL_SUFFIXSZ + ((CONFIG_MNEMOFS_JOURNAL_NBLKS + 2) * 4); - - buf = fs_heap_zalloc(sz); - if (predict_false(buf == NULL)) - { - ret = -ENOMEM; - goto errout; - } - - if (*blk1 == 0 && *blk2 == 0) - { - *blk1 = mfs_ba_getblk(sb); - if (predict_false(blk1 == 0)) - { - ret = -ENOSPC; - goto errout_with_buf; - } - - finfo("Allocated Master Block 1: %d.", *blk1); - - *blk2 = mfs_ba_getblk(sb); - if (predict_false(blk2 == 0)) - { - ret = -ENOSPC; - goto errout_with_buf; - } - - finfo("Allocated Master Block 1: %d.", *blk2); - finfo("New locations for Master Blocks %d & %d.", *blk1, *blk2); - } - - tmp = buf; - - tmp = mfs_ser_str(MFS_JRNL_MAGIC, 8, tmp); - tmp = mfs_ser_16(CONFIG_MNEMOFS_JOURNAL_NBLKS, tmp); - for (i = 0; i < CONFIG_MNEMOFS_JOURNAL_NBLKS; i++) - { - alloc_blk = mfs_ba_getblk(sb); - tmp = mfs_ser_mfs(alloc_blk, tmp); - - if (predict_false(i == 0)) - { - blk = alloc_blk; - *jrnl_blk = alloc_blk; - MFS_JRNL(sb).jrnl_start = alloc_blk; - } - - finfo("Allocated Journal Block %d at Block %d.", i, alloc_blk); - } - - tmp = mfs_ser_mfs(*blk1, tmp); - tmp = mfs_ser_mfs(*blk2, tmp); - - finfo("All Journal Blocks allocated."); - - pg = MFS_BLK2PG(sb, blk); - ret = mfs_write_page(sb, buf, sz, pg, 0); /* Assuming array fits in a - * single page. - */ - if (predict_false(ret < 0)) - { - goto errout_with_buf; - } - - ret = OK; /* We reach here, we OK. */ - - finfo("Written magic sequence, size and journal array into the journal."); - - MFS_JRNL(sb).n_logs = 0; - MFS_JRNL(sb).n_blks = CONFIG_MNEMOFS_JOURNAL_NBLKS; - MFS_JRNL(sb).log_cpg = pg + 1; /* Assumes 1 page for jrnl_arr. */ - MFS_JRNL(sb).log_cblkidx = 0; - MFS_JRNL(sb).log_spg = MFS_JRNL(sb).log_cpg; - MFS_JRNL(sb).log_sblkidx = MFS_JRNL(sb).log_cblkidx; - MFS_JRNL(sb).jrnlarr_pg = MFS_BLK2PG(sb, blk); - MFS_JRNL(sb).jrnlarr_pgoff = MFS_JRNL_SUFFIXSZ; - MFS_JRNL(sb).mblk1 = *blk1; - MFS_JRNL(sb).mblk2 = *blk2; - -errout_with_buf: - fs_heap_free(buf); - -errout: - return ret; -} - -void mfs_jrnl_free(FAR struct mfs_sb_s * const sb) -{ - if (!mfs_jrnl_isempty(sb) && - MFS_JRNL(sb).log_cblkidx >= MFS_JRNL_LIM(sb)) - { - mfs_jrnl_flush(sb); - } - - finfo("Journal Freed."); -} - -mfs_t mfs_jrnl_blkidx2blk(FAR const struct mfs_sb_s * const sb, - const mfs_t blk_idx) -{ - int ret = OK; - mfs_t pg; - mfs_t idx; - mfs_t blk; - mfs_t pgoff; - char buf[4]; - - pg = MFS_JRNL(sb).jrnlarr_pg; - pgoff = MFS_JRNL(sb).jrnlarr_pgoff; - blk = MFS_PG2BLK(sb, pg); - pgoff += blk_idx * 4; - - if (pgoff > MFS_PGSZ(sb)) - { - pg += pgoff / MFS_PGSZ(sb); - pgoff %= MFS_PGSZ(sb); - } - - /* No pg overflow. The blocks have to be big enough. */ - - DEBUGASSERT(pg < (MFS_BLK2PG(sb, blk) + MFS_PGINBLK(sb))); - - ret = mfs_read_page(sb, buf, 4, pg, pgoff); - if (predict_false(ret < 0)) - { - return 0; - } - - mfs_deser_mfs(buf, &idx); - - /* FUTURE TODO: Make it such that the entire jrnlarr doesn't need to be in - * a single block. - */ - - return idx; -} - -int mfs_jrnl_updatedinfo(FAR const struct mfs_sb_s * const sb, - FAR struct mfs_path_s * const path, - const mfs_t depth) -{ - int ret = OK; - mfs_t blkidx; - mfs_t counter = 0; - mfs_t pg_in_block; - struct mfs_jrnl_log_s tmplog; - - /* TODO: Allow optional filling of updated timestamps, etc. */ - - DEBUGASSERT(depth > 0); - - blkidx = MFS_JRNL(sb).log_sblkidx; - pg_in_block = MFS_JRNL(sb).log_spg % MFS_PGINBLK(sb); - - while (blkidx < MFS_JRNL(sb).n_blks && counter < MFS_JRNL(sb).n_logs) - { - ret = mfs_jrnl_rdlog(sb, &blkidx, &pg_in_block, &tmplog); - if (predict_false(ret < 0 && ret != -ENOSPC)) - { - goto errout; - } - else if (ret == -ENOSPC) - { - break; - } - - DEBUGASSERT(tmplog.depth > 0); - - if (tmplog.depth > depth) - { - /* Not suitable. */ - } - else - { - DEBUGASSERT(tmplog.depth > 0); - - if (mfs_path_eq(&tmplog.path[tmplog.depth - 1], - &path[tmplog.depth - 1])) - { - path[tmplog.depth - 1].ctz = tmplog.loc_new; - path[tmplog.depth - 1].sz = tmplog.sz_new; - } - } - - mfs_jrnl_log_free(&tmplog); - counter++; - } - -errout: - return ret; -} - -int mfs_jrnl_wrlog(FAR struct mfs_sb_s * const sb, - FAR const struct mfs_node_s *node, - const struct mfs_ctz_s loc_new, const mfs_t sz_new) -{ - int ret = OK; - mfs_t jrnl_pg; - FAR char *buf = NULL; - FAR char *tmp = NULL; - const mfs_t log_sz = sizeof(mfs_t) + MFS_LOGSZ(node->depth); - struct mfs_jrnl_log_s log; - - buf = fs_heap_zalloc(log_sz); /* For size before log. */ - if (predict_false(buf == NULL)) - { - ret = -ENOMEM; - goto errout; - } - - /* Serialize */ - - log.depth = node->depth; - log.sz_new = sz_new; - log.loc_new = loc_new; - log.st_mtim_new = node->st_mtim; - log.st_atim_new = node->st_atim; - log.st_ctim_new = node->st_ctim; - log.path = node->path; /* Fine as temporarily usage. */ - - tmp = buf; - tmp = mfs_ser_mfs(log_sz - sizeof(mfs_t), tmp); /* First 4 bytes have sz */ - tmp = ser_log(&log, tmp); - - /* Store */ - - jrnl_pg = MFS_JRNL(sb).log_cpg; - - /* TODO: It assumes it takes only one page per log. */ - - ret = mfs_write_page(sb, buf, log_sz, jrnl_pg, 0); - if (predict_false(ret < 0)) - { - goto errout_with_buf; - } - - ret = OK; - - jrnl_pg++; - - if (jrnl_pg % MFS_PGINBLK(sb) == 0) - { - MFS_JRNL(sb).log_cblkidx++; - } - - MFS_JRNL(sb).log_cpg = jrnl_pg; - MFS_JRNL(sb).n_logs++; - -errout_with_buf: - fs_heap_free(buf); - -errout: - return ret; -} - -int mfs_jrnl_flush(FAR struct mfs_sb_s * const sb) -{ - /* When a file or a directory is deleted. - * - * It will be modified to an entry in the LRU which details the deletion - * of all bytes from the child... as in, offset 0, deleted bytes is the - * size of the file. - * - * The new "location" can be used as (0, 0) to signify a deletion, even in - * its journal log. - * - * Also ensure if the size gets updated to 0. - * - * Then the flush operation problem will be solved for removal of files or - * directories. - * - * Move operation will not empty the child, but only the parent from the - * old parent. - */ - - /* Time complexity is going to be horrendous. Hint: O(n^2). HOWEVER, as - * littlefs points out....if n is constant, it's essentially a O(k), or - * O(1) :D - */ - - /* TODO: Need to consider how the LRU and Journal interact with each other - * for newly created fs object's entries. - */ - - /* We're using updatectz to update the LRU inside the journal. Think - * about how that might affect the iteration attempts. - */ - - int ret = OK; - mfs_t blkidx = MFS_JRNL(sb).log_sblkidx; - mfs_t log_itr = 0; - mfs_t pg_in_blk = MFS_JRNL(sb).log_spg \ - % MFS_PGINBLK(sb); - mfs_t tmp_blkidx; - mfs_t tmp_pg_in_blk; - mfs_t mn_blk1; - mfs_t mn_blk2; - mfs_t i; - mfs_t jrnl_blk; - mfs_t blk; - struct mfs_jrnl_log_s log; - struct mfs_jrnl_log_s tmp_log; - FAR struct mfs_path_s *path = NULL; - struct mfs_jrnl_state_s j_state; - struct mfs_mn_s mn_state; - - while (log_itr < MFS_JRNL(sb).n_logs) - { - ret = mfs_jrnl_rdlog(sb, &blkidx, &pg_in_blk, &log); - if (predict_false(ret < 0)) - { - DEBUGASSERT(ret != -ENOSPC); /* While condition is sufficient. */ - goto errout; - } - - if (log.loc_new.pg_e == 0 && log.loc_new.idx_e == 0) - { - /* Entry is deleted, do not bother with it. */ - - break; - } - - tmp_blkidx = blkidx; - tmp_pg_in_blk = pg_in_blk; - - path = fs_heap_zalloc(log.depth * sizeof(struct mfs_path_s)); - if (predict_false(path == NULL)) - { - goto errout; - } - - memcpy(path, log.path, log.depth * sizeof(struct mfs_path_s)); - path[log.depth - 1].ctz = log.loc_new; - - for (; ; ) - { - ret = mfs_jrnl_rdlog(sb, &tmp_blkidx, &tmp_pg_in_blk, &tmp_log); - if (ret == -ENOSPC) - { - break; - } - else if (predict_false(ret < 0)) - { - mfs_jrnl_log_free(&log); - mfs_free_patharr(path); - goto errout; - } - - if (tmp_log.depth > log.depth) - { - mfs_jrnl_log_free(&tmp_log); - continue; - } - - if (!mfs_path_eq(&path[tmp_log.depth - 1], - &tmp_log.path[tmp_log.depth - 1])) - { - mfs_jrnl_log_free(&tmp_log); - continue; - } - - path[tmp_log.depth - 1] = tmp_log.path[tmp_log.depth - 1]; - - if (tmp_log.loc_new.pg_e == 0 && tmp_log.loc_new.idx_e == 0) - { - /* Entry is deleted, do not bother with it. */ - - break; - } - } - - if (log.depth == 1) - { - MFS_MN(sb).root_ctz = path[log.depth - 1].ctz; - MFS_MN(sb).root_sz = path[log.depth - 1].sz; - - /* TODO: Other parameters. */ - } - else - { - ret = mfs_lru_updatectz(sb, path, log.depth, - path[log.depth - 1].ctz, - path[log.depth - 1].sz); - if (predict_false(ret < 0)) - { - mfs_free_patharr(path); - mfs_jrnl_log_free(&log); - goto errout; - } - } - - mfs_free_patharr(path); - mfs_jrnl_log_free(&log); - } - - if (MFS_MN(sb).mblk_idx == MFS_PGINBLK(sb)) - { - mn_blk1 = 0; - mn_blk2 = 0; - } - else - { - /* FUTURE TODO: Save the two block numbers in master node structure to - * be faster. - */ - - mn_blk1 = mfs_jrnl_blkidx2blk(sb, MFS_JRNL(sb).n_blks); - mn_blk2 = mfs_jrnl_blkidx2blk(sb, MFS_JRNL(sb).n_blks + 1); - } - - /* Reallocate journal. */ - - j_state = MFS_JRNL(sb); - mn_state = MFS_MN(sb); - - ret = mfs_jrnl_fmt(sb, &mn_blk1, &mn_blk2, &jrnl_blk); - if (predict_false(ret < 0)) - { - MFS_JRNL(sb) = j_state; - goto errout; - } - - /* Write master node entry. */ - - ret = mfs_mn_sync(sb, &path[0], mn_blk1, mn_blk2, jrnl_blk); - if (predict_false(ret < 0)) - { - MFS_MN(sb) = mn_state; - goto errout; - } - - /* Mark all old blocks of journal (and master blocks) as deletable. */ - - for (i = 0; i < MFS_JRNL(sb).n_blks + 2; i++) - { - blk = mfs_jrnl_blkidx2blk(sb, i); - mfs_ba_blkmarkdel(sb, blk); - } - - /* Delete outdated blocks. */ - - ret = mfs_ba_delmarked(sb); - if (predict_false(ret < 0)) - { - goto errout; - } - -errout: - return ret; -} - -bool mfs_jrnl_isempty(FAR const struct mfs_sb_s * const sb) -{ - return MFS_JRNL(sb).n_logs == 0; -} diff --git a/fs/mnemofs/mnemofs_lru.c b/fs/mnemofs/mnemofs_lru.c deleted file mode 100644 index fbed366c60d..00000000000 --- a/fs/mnemofs/mnemofs_lru.c +++ /dev/null @@ -1,1068 +0,0 @@ -/**************************************************************************** - * fs/mnemofs/mnemofs_lru.c - * - * SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause - * - * 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" -#include "fs_heap.h" - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -enum -{ - MFS_LRU_UPD, - MFS_LRU_DEL, -}; - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -static void lru_nodesearch(FAR const struct mfs_sb_s * const sb, - FAR const struct mfs_path_s * const path, - const mfs_t depth, FAR struct mfs_node_s **node); -static bool lru_islrufull(FAR struct mfs_sb_s * const sb); -static bool lru_isnodefull(FAR struct mfs_sb_s * const sb, - FAR struct mfs_node_s *node); -static int lru_nodeflush(FAR struct mfs_sb_s * const sb, - FAR struct mfs_path_s * const path, - const mfs_t depth, FAR struct mfs_node_s *node, - bool rm_node); -static int lru_wrtooff(FAR struct mfs_sb_s * const sb, const mfs_t data_off, - mfs_t bytes, int op, - FAR struct mfs_path_s * const path, - const mfs_t depth, FAR const char *buf); -static int lru_updatesz(FAR struct mfs_sb_s * sb, - FAR struct mfs_path_s * const path, - const mfs_t depth, const mfs_t new_sz); -static void lru_node_free(FAR struct mfs_node_s *node); - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/**************************************************************************** - * Public Data - ****************************************************************************/ - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: lru_nodesearch - * - * Description: - * Searches a node by the `path` and `depth` in the LRU. - * - * Input Parameters: - * sb - Superblock instance of the device. - * path - CTZ representation of the relpath. - * depth - Depth of `path`. - * node - To populate with the node corresponding to the CTZ. - * - ****************************************************************************/ - -static void lru_nodesearch(FAR const struct mfs_sb_s * const sb, - FAR const struct mfs_path_s * const path, - const mfs_t depth, FAR struct mfs_node_s **node) -{ - bool found = false; - mfs_t i; - FAR struct mfs_node_s *n; - - *node = NULL; - - list_for_every_entry(&MFS_LRU(sb), n, struct mfs_node_s, list) - { - /* We need this loop to specifically check the parents in case these - * entries are all new, and have not been allocated any pages for - * being stored in the flash. Also we know that the root (depth 1) will - * be at least common in their paths. - */ - - DEBUGASSERT(depth > 0); - - if (n->depth != depth) - { - continue; - } - - found = true; - for (i = n->depth; i >= 1 && found; i--) - { - if (path[i - 1].ctz.idx_e == 0 && path[i - 1].ctz.pg_e == 0 && - mfs_ctz_eq(&n->path[i - 1].ctz, &path[i - 1].ctz) && - n->path[i - 1].off == path[i - 1].off) - { - /* OK */ - } - else if (path[i - 1].ctz.pg_e != 0 && - mfs_ctz_eq(&n->path[i - 1].ctz, &path[i - 1].ctz)) - { - /* OK */ - } - else - { - found = false; - } - } - - if (found) - { - *node = n; - break; - } - } - - if (found) - { - finfo("Node search ended with match of node %p at depth %u" - " for CTZ of %" PRIu32 " size with range [%" PRIu32 ", %" PRIi32 - ").", n, n->depth, n->sz, n->range_min, n->range_max); - } - else - { - finfo("Node search ended without match."); - *node = NULL; - } -} - -/**************************************************************************** - * Name: lru_islrufull - * - * Description: - * Check whether the number of nodes in the LRU has reaches its limit. - * - * Input Parameters: - * sb - Superblock instance of the device. - * - * Returned Value: - * true - LRU is full - * false - LRU is not full. - * - * Assumptions/Limitations: - * When the journal is being flushed, LRU memory limiters will be turned - * off. - * - ****************************************************************************/ - -static bool lru_islrufull(FAR struct mfs_sb_s * const sb) -{ - return !MFS_FLUSH(sb) && list_length(&MFS_LRU(sb)) == CONFIG_MNEMOFS_NLRU; -} - -/**************************************************************************** - * Name: lru_isnodefull - * - * Description: - * Check whether the number of deltas in an LRU node has reaches its limit. - * - * Input Parameters: - * node - LRU node. - * - * Returned Value: - * true - LRU node is full - * false - LRU node is not full. - * - * Assumptions/Limitations: - * When the journal is being flushed, LRU memory limiters will be turned - * off. - * - ****************************************************************************/ - -static bool lru_isnodefull(FAR struct mfs_sb_s * const sb, - FAR struct mfs_node_s *node) -{ - return !MFS_FLUSH(sb) && node->n_list == CONFIG_MNEMOFS_NLRUDELTA; -} - -/**************************************************************************** - * Name: lru_free_delta - * - * Description: - * Free a node's delta. - * - * Input Parameters: - * delta - LRU delta. - * - ****************************************************************************/ - -static void lru_free_delta(FAR struct mfs_delta_s *delta) -{ - fs_heap_free(delta->upd); - fs_heap_free(delta); -} - -/**************************************************************************** - * Name: lru_nodeflush - * - * Description: - * Clear out the deltas in a node by writing them to the flash, and adding - * a log about it to the journal. Does not flush the journal, and assumes - * enough space is in the journal to handle a log. - * - * Input Parameters: - * sb - Superblock instance of the device. - * path - CTZ representation of the relpath. - * depth - Depth of `path`. - * node - LRU node to flush. - * rm_node - To remove node out of LRU (true), or just clear the deltas - * (false). - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - ****************************************************************************/ - -static int lru_nodeflush(FAR struct mfs_sb_s * const sb, - FAR struct mfs_path_s * const path, - const mfs_t depth, FAR struct mfs_node_s *node, - bool rm_node) -{ - int ret = OK; - struct mfs_ctz_s loc; - FAR struct mfs_delta_s *delta = NULL; - FAR struct mfs_delta_s *tmp = NULL; - - if (predict_false(node == NULL)) - { - return -EINVAL; - } - - ret = mfs_ctz_wrtnode(sb, node, &loc); - if (predict_false(ret < 0)) - { - goto errout; - } - - /* Free deltas after flush. */ - - finfo("Removing Deltas."); - - list_for_every_entry_safe(&node->delta, delta, tmp, struct mfs_delta_s, - list) - { - list_delete_init(&delta->list); - lru_free_delta(delta); - } - - if (rm_node) - { - finfo("Deleting node. Old size: %zu.", list_length(&MFS_LRU(sb))); - list_delete_init(&node->list); - finfo("Deleted node. New size: %zu.", list_length(&MFS_LRU(sb))); - } - else - { - /* Reset node stats. */ - - finfo("Resetting node."); - memset(node, 0, sizeof(struct mfs_node_s)); - node->range_min = UINT32_MAX; - } - - finfo("Updating CTZ in parent."); - ret = mfs_lru_updatectz(sb, node->path, node->depth, loc, node->sz); - - if (rm_node) - { - finfo("Freeing node."); - lru_node_free(node); - } - -errout: - return ret; -} - -/**************************************************************************** - * Name: lru_wrtooff - * - * Description: - * Write to offset in LRU. - * - * Input Parameters: - * sb - Superblock instance of the device. - * data_off - Offset into the data in the CTZ skip list. - * bytes - Number of bytes to write. - * ctz_sz - Size of the CTZ skip list. - * op - Operation (MFS_LRU_UPD or MFS_LRU_DEL). - * path - CTZ representation of the path. - * depth - Depth of path. - * buf - Buffer containing data. - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - ****************************************************************************/ - -static int lru_wrtooff(FAR struct mfs_sb_s * const sb, const mfs_t data_off, - mfs_t bytes, int op, - FAR struct mfs_path_s * const path, const mfs_t depth, - FAR const char *buf) -{ - int ret = OK; - bool found = true; - mfs_t old_sz; - FAR struct mfs_node_s *node = NULL; - FAR struct mfs_node_s *last_node = NULL; - FAR struct mfs_delta_s *delta = NULL; - - DEBUGASSERT(depth > 0); - - lru_nodesearch(sb, path, depth, &node); - - if (node == NULL) - { - node = fs_heap_zalloc(sizeof(*node)); - if (predict_false(node == NULL)) - { - found = false; - ret = -ENOMEM; - goto errout; - } - - node->path = fs_heap_zalloc(depth * sizeof(struct mfs_path_s)); - if (predict_false(node->path == NULL)) - { - found = false; - ret = -ENOMEM; - goto errout_with_node; - } - - node->sz = path[depth - 1].sz; - node->depth = depth; - node->n_list = 0; - node->range_max = 0; - node->range_min = UINT32_MAX; - list_initialize(&node->delta); - memcpy(node->path, path, depth * sizeof(struct mfs_path_s)); - found = false; - - finfo("Node not found. Allocated at %p.", node); - } - - if (!found) - { - if (lru_islrufull(sb)) - { - finfo("LRU is full, need to flush a node."); - last_node = list_container_of(list_peek_tail(&MFS_LRU(sb)), - struct mfs_node_s, list); - list_delete_init(&last_node->list); - list_add_tail(&MFS_LRU(sb), &node->list); - finfo("LRU flushing node complete, now only %zu nodes", - list_length(&MFS_LRU(sb))); - } - else - { - list_add_tail(&MFS_LRU(sb), &node->list); - finfo("Node inserted into LRU, and it now %zu node(s).", - list_length(&MFS_LRU(sb))); - } - } - else if (found && lru_isnodefull(sb, node)) - { - /* This can be optimized further if needed, but for now, for safety of - * the data, I think it's better to flush the entire thing. It won't - * flush ALL of it, just, whatever's required. - */ - - ret = mnemofs_flush(sb); - if (predict_false(ret < 0)) - { - goto errout_with_node; - } - } - - /* Add delta to node. */ - - finfo("Adding delta to the node."); - delta = fs_heap_zalloc(sizeof(*delta)); - if (predict_false(delta == NULL)) - { - ret = -ENOMEM; - goto errout_with_node; - } - - finfo("Delta allocated."); - - if (op == MFS_LRU_UPD) - { - delta->upd = fs_heap_zalloc(bytes); - if (predict_false(delta->upd == NULL)) - { - ret = -ENOMEM; - goto errout_with_delta; - } - - finfo("Delta is of the update type, has %u bytes at offset %u.", - bytes, data_off); - } - - delta->n_b = bytes; - delta->off = data_off; - list_add_tail(&node->delta, &delta->list); - if (op == MFS_LRU_UPD) - { - memcpy(delta->upd, buf, bytes); - } - - node->n_list++; - node->range_min = MIN(node->range_min, data_off); - node->range_max = MAX(node->range_max, data_off + bytes); - - old_sz = node->sz; - node->sz = MAX(node->range_max, path[depth - 1].sz); - node->path[node->depth - 1].sz = node->sz; - - if (old_sz != node->sz) - { - ret = lru_updatesz(sb, node->path, node->depth, node->sz); - if (predict_false(ret < 0)) - { - goto errout_with_delta; - } - } - - finfo("Delta attached to node %p. Now there are %zu nodes and the" - " node has %zu deltas. Node with range [%" PRIu32 ", %" - PRIu32 ").", node, list_length(&MFS_LRU(sb)), - list_length(&node->delta), node->range_min, node->range_max); - - return ret; - -errout_with_delta: - list_delete(&delta->list); - fs_heap_free(delta); - -errout_with_node: - if (!found && node != NULL) - { - list_delete(&node->list); - fs_heap_free(node); - } - -errout: - return ret; -} - -/**************************************************************************** - * Name: lru_updatesz - * - * Description: - * Updates size of an fs object in its parent. - * - * Input Parameters: - * sb - Superblock instance of the device. - * path - CTZ representation of the path. - * depth - Depth of path. - * new_sz - New size - * - * Returned Value: - * 0 - OK - * < 0 - Error - * - * Assumptions/Limitations: - * Adds an entry for the target's parent to update the child's size, and - * updates the size in path of everyone that has this child. - * - ****************************************************************************/ - -static int lru_updatesz(FAR struct mfs_sb_s * sb, - FAR struct mfs_path_s * const path, - const mfs_t depth, const mfs_t new_sz) -{ - int ret = OK; - struct mfs_node_s *n = NULL; - mfs_t i; - bool found; - char buf[sizeof(mfs_t)]; - - DEBUGASSERT(depth > 0); - - list_for_every_entry(&MFS_LRU(sb), n, struct mfs_node_s, list) - { - if (n->depth < depth) - { - continue; - } - - found = false; - for (i = depth; i >= 1; i--) - { - if (path[i - 1].ctz.idx_e == 0 && path[i - 1].ctz.pg_e == 0 && - mfs_ctz_eq(&n->path[i - 1].ctz, &path[i - 1].ctz) && - n->path[i - 1].off == path[i - 1].off) - { - found = true; - } - else if (path[i - 1].ctz.pg_e != 0 && - mfs_ctz_eq(&n->path[i - 1].ctz, &path[i - 1].ctz)) - { - found = true; - } - else - { - break; - } - } - - if (found) - { - n->path[depth - 1].sz = new_sz; - } - } - - if (depth == 1) - { - MFS_MN(sb).root_sz = new_sz; - goto errout; - } - - memset(buf, 0, sizeof(mfs_t)); - mfs_ser_mfs(new_sz, buf); - - /* This function will be used by mfs_lru_wr itself, but given that if - * there is no change in size, this won't cause an infinite loop (or, in - * reality, a recursion till it reaches the top of the tree), this should - * be fine. - */ - - ret = mfs_lru_wr(sb, path[depth - 2].off + offsetof(struct mfs_dirent_s, - sz), sizeof(mfs_t), path, depth - 1, buf); - if (predict_false(ret < 0)) - { - goto errout; - } - - path[depth - 1].sz = new_sz; - -errout: - return ret; -} - -static void lru_node_free(FAR struct mfs_node_s *node) -{ - mfs_free_patharr(node->path); - fs_heap_free(node); -} - -static bool lru_sort_cmp(FAR struct mfs_node_s * const node, - FAR struct mfs_node_s * const pivot) -{ - return node->depth < pivot->depth; -} - -static void lru_sort(FAR struct mfs_sb_s * const sb, - FAR struct list_node *left, - FAR struct list_node *right) -{ - FAR struct mfs_node_s *node = NULL; - FAR struct mfs_node_s *next = NULL; - FAR struct mfs_node_s *pivot = NULL; - FAR struct list_node *aend = NULL; /* After end. */ - FAR struct list_node *bfirst = NULL; /* Before first. */ - - if (left == right) - { - return; - } - - /* If left or right is NULL, it means that refers to MFS_LRU(sb). */ - - aend = right->next; - bfirst = left->prev; - - node = list_container_of(left, struct mfs_node_s, list); - pivot = list_container_of(right, struct mfs_node_s, list); - - if (node->list.next == &pivot->list) - { - /* Only two items in the window...node and pivot, so insertion sort. */ - - if (lru_sort_cmp(node, pivot)) - { - /* Add node after the pivot. */ - - list_delete_init(&node->list); - list_add_after(&pivot->list, &node->list); - - DEBUGASSERT(pivot->list.prev == bfirst); - } - - DEBUGASSERT(!lru_sort_cmp(node, pivot)); - - return; - } - - list_for_every_entry_safe_from(&MFS_LRU(sb), node, next, struct mfs_node_s, - list) - { - if (node == pivot) - { - break; - } - - if (lru_sort_cmp(node, pivot)) - { - /* Add node after the pivot. */ - - list_delete_init(&node->list); - list_add_after(&pivot->list, &node->list); - } - } - - if (bfirst->next != &pivot->list) - { - lru_sort(sb, bfirst->next, pivot->list.prev); - } - - if (aend->prev != &pivot->list) - { - lru_sort(sb, pivot->list.next, aend->prev); - } -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -int mfs_lru_flush(FAR struct mfs_sb_s * const sb) -{ - int ret = OK; - FAR struct mfs_node_s *tmp = NULL; - FAR struct mfs_node_s *tmp2 = NULL; - FAR struct mfs_node_s *node = NULL; - FAR struct mfs_node_s *next = NULL; - -/* Modified quick sort in linked lists. What is wanted is like inverted - * topological sort, but all the files (no children) are at the front, - * their depths don't matter. BUT, when it comes to directories, they need - * to be sorted in a decreasing order of their depths to reduce updates due - * to CoW. This will trickle up to the root, such that the root will be the - * last to get updated, and then the master node. - * - * However, since passing the mode all the way requires a lot of change, and - * is a redundant piece of information in most cases, the quick sort can - * simply be done on the basis of depth, and this adventure can be left as a - * TODO. - * - * This involves recursion, but given the LRU size is a constant, the depth - * of recursion will be log2(n). For an LRU size of even 128 (which is quite - * big), the stack depth for this will be 7. - */ - - finfo("Sorting the LRU. No. of nodes: %zu", list_length(&MFS_LRU(sb))); - - lru_sort(sb, MFS_LRU(sb).next, MFS_LRU(sb).prev); - MFS_FLUSH(sb) = true; - - list_for_every_entry_safe(&MFS_LRU(sb), node, next, struct mfs_node_s, - list) - { - finfo("Current node depth: %u.", node->depth); - - if (node->depth != 1) - { - finfo("Checking for parent."); - - /* Ensuring parent is either present, or inserted into the LRU. - * No need of doing this before removing current node from LRU, - * however, this allows us to possibly skip allocating path again - * after freeing the current node. - */ - - /* We can not rely on normal LRU node insertions, as they will - * not be inserted in a sorted manner, and would need the entire - * LRU to be sorted again, so we insert it manually. - */ - - lru_nodesearch(sb, node->path, node->depth - 1, &tmp); - - if (tmp == NULL) - { - finfo("Adding parent to LRU"); - - tmp = fs_heap_zalloc(sizeof(struct mfs_node_s)); - if (predict_false(tmp == NULL)) - { - ret = -ENOMEM; - goto errout; - } - - tmp->range_max = 0; - tmp->range_min = UINT32_MAX; - - /* TODO: Time fields. in tmp. */ - - tmp->depth = node->depth - 1; - tmp->path = fs_heap_zalloc((node->depth - 1) - * sizeof(struct mfs_path_s)); - if (predict_false(tmp->path == NULL)) - { - ret = -ENOMEM; - goto errout_with_tmp; - } - - memcpy(tmp->path, node->path, - sizeof(struct mfs_path_s) * tmp->depth); - list_initialize(&tmp->list); - list_initialize(&tmp->delta); - - /* Insert into sorted. */ - - list_for_every_entry(&MFS_LRU(sb), tmp2, struct mfs_node_s, - list) - { - if (!lru_sort_cmp(tmp, tmp2)) - { - list_add_before(&tmp2->list, &tmp->list); - - if (tmp2->list.prev == &node->list) - { - next = tmp2; - } - - break; - } - } - } - else - { - finfo("Parent already in LRU."); - } - } - else - { - finfo("Root node from LRU."); - } - - /* Parent gets updated inside the LRU in the function below. */ - - finfo("Flushing node."); - ret = lru_nodeflush(sb, node->path, node->depth, node, true); - if (predict_true(ret < 0)) - { - goto errout; - } - } - - return ret; - -errout_with_tmp: - lru_node_free(node); - -errout: - MFS_FLUSH(sb) = false; - return ret; -} - -int mfs_lru_del(FAR struct mfs_sb_s * const sb, const mfs_t data_off, - mfs_t bytes, FAR struct mfs_path_s * const path, - const mfs_t depth) -{ - return lru_wrtooff(sb, data_off, bytes, MFS_LRU_DEL, path, depth, NULL); -} - -int mfs_lru_wr(FAR struct mfs_sb_s * const sb, const mfs_t data_off, - mfs_t bytes, FAR struct mfs_path_s * const path, - const mfs_t depth, FAR const char *buf) -{ - return lru_wrtooff(sb, data_off, bytes, MFS_LRU_UPD, path, depth, buf); -} - -void mfs_lru_init(FAR struct mfs_sb_s * const sb) -{ - list_initialize(&MFS_LRU(sb)); - - finfo("LRU Initialized\n"); -} - -int mfs_lru_rdfromoff(FAR const 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) -{ - /* Requires updated path from the journal. */ - - int ret = OK; - mfs_t upper; - mfs_t lower; - mfs_t rem_sz; - mfs_t upper_og; - mfs_t upper_upd; - mfs_t lower_upd; - FAR char *tmp; - struct mfs_ctz_s ctz; - FAR struct mfs_node_s *node = NULL; - FAR struct mfs_delta_s *delta = NULL; - - /* Node is NOT supposed to be freed by the caller, it's a reference to - * the actual node in the LRU and freeing it could break the entire LRU. - */ - - tmp = buf; - ctz = path[depth - 1].ctz; - lower = data_off; - upper_og = lower + buflen; - upper = upper_og; - rem_sz = buflen; - - lru_nodesearch(sb, path, depth, &node); - if (node == NULL) - { - mfs_ctz_rdfromoff(sb, ctz, 0, buflen, tmp); - goto errout; - } - - while (rem_sz > 0) - { - mfs_ctz_rdfromoff(sb, ctz, lower, rem_sz, tmp); - - list_for_every_entry(&node->delta, delta, struct mfs_delta_s, list) - { - if (delta->upd == NULL) - { - /* Delete */ - - lower_upd = MAX(lower, delta->off); - upper_upd = MIN(upper, delta->off + delta->n_b); - - if (lower_upd >= upper_upd) - { - /* Outside range */ - } - else - { - memmove(tmp + (lower - lower_upd), - tmp + (upper_upd - lower), upper - upper_upd); - - upper -= (upper_upd - lower_upd); - } - } - else - { - /* Update */ - - lower_upd = MAX(lower, delta->off); - upper_upd = MIN(upper, delta->off + delta->n_b); - - if (lower_upd >= upper_upd) - { - /* Outside range */ - } - else - { - memcpy(tmp + (lower_upd - lower), - delta->upd + (lower_upd - delta->off), - upper_upd - lower_upd); - } - } - } - - tmp += upper - lower; - rem_sz -= upper - lower; - lower = upper; - upper = upper_og; - } - -errout: - return ret; -} - -int mfs_lru_getupdatedinfo(FAR const struct mfs_sb_s * const sb, - FAR struct mfs_path_s * const path, - const mfs_t depth) -{ - int ret = OK; - bool found; - mfs_t i; - FAR struct mfs_node_s *node = NULL; - - DEBUGASSERT(depth > 0); - - list_for_every_entry(&MFS_LRU(sb), node, struct mfs_node_s, list) - { - /* TODO: When a directory is newly created, and still in the LRU, its - * CTZ is (0, 0), and this can match others as well if at same depth, - * so, in these cases, match the parents, and so on up. - */ - - DEBUGASSERT(node->depth > 0); - - if (node->depth > depth) - { - continue; - } - - /* We need this loop to specifically check the parents in case these - * entries are all new, and have not been allocated any pages for - * being stored in the flash. Also we know that the root (depth 1) will - * be at least common in their paths. - */ - - found = true; - for (i = node->depth; i >= 1 && found; i--) - { - if (path[i - 1].ctz.idx_e == 0 && path[i - 1].ctz.pg_e == 0 && - mfs_ctz_eq(&node->path[i - 1].ctz, &path[i - 1].ctz) && - node->path[i - 1].off == path[i - 1].off) - { - /* OK */ - } - else if (path[i - 1].ctz.pg_e != 0 && - mfs_ctz_eq(&node->path[i - 1].ctz, &path[i - 1].ctz)) - { - /* OK */ - } - else - { - found = false; - } - } - - if (found) - { - path[node->depth - 1].sz = node->path[node->depth - 1].sz; - } - } - - return ret; -} - -int mfs_lru_updatectz(FAR struct mfs_sb_s * sb, - FAR struct mfs_path_s * const path, const mfs_t depth, - const struct mfs_ctz_s new_ctz, mfs_t new_sz) -{ - int ret = OK; - char buf[sizeof(struct mfs_ctz_s)]; - FAR struct mfs_node_s *node = NULL; - - /* TODO: Other attributes like time stamps to be updated as well. */ - - list_for_every_entry(&MFS_LRU(sb), node, struct mfs_node_s, list) - { - if (node->depth >= depth && - mfs_ctz_eq(&node->path[depth - 1].ctz, &path[depth - 1].ctz)) - { - node->path[depth - 1].ctz = new_ctz; - node->path[depth - 1].sz = path[depth - 1].sz; - } - } - - if (depth == 1) - { - MFS_MN(sb).root_sz = new_sz; - MFS_MN(sb).root_ctz = new_ctz; - - goto errout; - } - - /* Write to LRU. */ - - memset(buf, 0, sizeof(struct mfs_ctz_s)); - mfs_ser_ctz(&new_ctz, buf); - ret = mfs_lru_wr(sb, path[depth - 1].off + offsetof(struct mfs_dirent_s, - ctz), sizeof(struct mfs_ctz_s), path, depth - 1, buf); - if (predict_false(ret < 0)) - { - goto errout; - } - - ret = lru_updatesz(sb, path, depth, new_sz); - if (predict_false(ret < 0)) - { - goto errout; - } - - path[depth - 1].ctz = new_ctz; - path[depth - 1].sz = new_sz; - -errout: - return ret; -} - -bool mfs_lru_isempty(FAR struct mfs_sb_s * const sb) -{ - return list_length(&MFS_LRU(sb)) == 0; -} diff --git a/fs/mnemofs/mnemofs_master.c b/fs/mnemofs/mnemofs_master.c deleted file mode 100644 index 54c1b2da0ec..00000000000 --- a/fs/mnemofs/mnemofs_master.c +++ /dev/null @@ -1,453 +0,0 @@ -/**************************************************************************** - * fs/mnemofs/mnemofs_master.c - * - * SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause - * - * 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 master node points to the root of the file system. It - * contains the information about the root, and when the root is updated, - * the master node needs to point to the updated location, and thus, needs to - * update the master node. - * - * Master nodes sit at the very end of the journal. The last two blocks of - * the journal are called master blocks, and they are filled with a new - * entry for a master node every time it is updated. They are filled in a - * sequential manner, and thus, the latest master node can be found easily. - * The two master blocks contain identical information, and exist to be as a - * backup. - * - * The stored master nodes are basically `struct mfs_mn_s` without the - * redundant `pg` member. - * - * The master node also points to the start of the journal, and thus, when - * the journal moves, a new master node entry is added. - * - * A master node update, when written to the file system, marks the end of - * an update of the file system tree. Thus, at this point, any obsolete data - * that can be erased, will be erased by the block allocator. Only after - * writing the master block is the file system tree updated. Before this, - * the old file system tree is accessible through the older master node, and - * can be accessed again during power loss. - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include -#include - -#include "mnemofs.h" - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -static FAR char *ser_mn(const struct mfs_mn_s mn, - FAR char * const out); -static FAR const char *deser_mn(FAR const char * const in, - FAR struct mfs_mn_s *mn, FAR uint16_t *hash); - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/**************************************************************************** - * Public Data - ****************************************************************************/ - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: ser_mn - * - * Description: - * Serialize master node. - * - * Input Parameters: - * mn - Master node. - * out - Out buffer. - * - * Returned Value: - * Pointer to the end of the serialized data in `out`. - * - * Assumptions/Limitations: - * Out should contain enough space for `mn` and 1 byte extra for the hash. - * - ****************************************************************************/ - -static FAR char *ser_mn(const struct mfs_mn_s mn, FAR char * const out) -{ - FAR char *tmp = out; - - tmp = mfs_ser_mfs(mn.jrnl_blk, tmp); - tmp = mfs_ser_mfs(mn.mblk_idx, tmp); - tmp = mfs_ser_ctz(&mn.root_ctz, tmp); - tmp = mfs_ser_mfs(mn.root_sz, tmp); - tmp = mfs_ser_timespec(&mn.ts, tmp); - tmp = mfs_ser_16(mfs_hash(out, tmp - out), tmp); - - /* TODO: Update this, and the make a macro for size of MN. */ - - return tmp; -} - -/**************************************************************************** - * Name: ser_mn - * - * Description: - * Deserialize master node. - * - * Input Parameters: - * in - In buffer. - * mn - Master node to populate. - * hash - Stored hash (of serialized data) to populate. - * - * Returned Value: - * Pointer to the end of the serialized data in `in`. - * - * Assumptions/Limitations: - * In should contain enough space for `mn` and 1 byte extra for the hash. - * - ****************************************************************************/ - -static FAR const char *deser_mn(FAR const char * const in, - FAR struct mfs_mn_s *mn, FAR uint16_t *hash) -{ - FAR const char *tmp = in; - - tmp = mfs_deser_mfs(tmp, &mn->jrnl_blk); - tmp = mfs_deser_mfs(tmp, &mn->mblk_idx); - tmp = mfs_deser_ctz(tmp, &mn->root_ctz); - tmp = mfs_deser_mfs(tmp, &mn->root_sz); - tmp = mfs_deser_timespec(tmp, &mn->ts); - tmp = mfs_deser_16(tmp, hash); - - /* TODO: Update this, and the make a macro for size of MN. */ - - return tmp; -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -int mfs_mn_init(FAR struct mfs_sb_s * const sb, const mfs_t jrnl_blk) -{ - int ret = OK; - bool found = false; - mfs_t i = 0; - mfs_t mblk1; - mfs_t blkidx; - mfs_t pg_in_blk; - mfs_t jrnl_blk_tmp; - uint16_t hash; - struct mfs_mn_s mn; - const mfs_t sz = sizeof(struct mfs_mn_s) - sizeof(mn.pg); - char buftmp[4]; - char buf[sz + 1]; - struct mfs_jrnl_log_s log; - - mblk1 = mfs_jrnl_blkidx2blk(sb, MFS_JRNL(sb).n_blks); - - mn.jrnl_blk = jrnl_blk; - mn.mblk_idx = 0; - mn.pg = MFS_BLK2PG(sb, mblk1); - - for (i = 0; i < MFS_PGINBLK(sb); i++) - { - mfs_read_page(sb, buftmp, 4, mn.pg, 0); - mfs_deser_mfs(buftmp, &jrnl_blk_tmp); - - if (jrnl_blk_tmp == 0) - { - break; - } - - found = true; - mn.mblk_idx++; - mn.pg++; - } - - if (found == false) - { - ret = -EINVAL; - goto errout; - } - - if (i == MFS_PGINBLK(sb)) - { - ret = -ENOSPC; - goto errout; - } - else - { - mn.pg--; - } - - mfs_read_page(sb, buf, sz + 1, mn.pg, 0); - deser_mn(buf, &mn, &hash); - if (hash != mfs_hash(buf, sz)) - { - ret = -EINVAL; - goto errout; - } - - blkidx = MFS_JRNL(sb).log_sblkidx; - pg_in_blk = MFS_JRNL(sb).log_spg % MFS_PGINBLK(sb); - - while (true) - { - ret = mfs_jrnl_rdlog(sb, &blkidx, &pg_in_blk, &log); - if (predict_false(ret < 0 && ret != -ENOSPC)) - { - goto errout; - } - else if (ret == -ENOSPC) - { - ret = OK; - break; - } - - /* Assumes checking the depth is enough to check if it's empty, as - * theoretically there are no blocks with depth 0, as root has a - * depth of 1. - */ - - if (log.depth == 0) - { - DEBUGASSERT(log.path == NULL); - break; - } - - if (log.depth == 1) - { - mn.root_ctz = log.loc_new; - mn.root_sz = log.sz_new; - } - - mfs_jrnl_log_free(&log); - } - - /* FUTURE TODO: Recovery in case of hash not matching, or page not - * readable. - */ - - mn.root_mode = 0777 | S_IFDIR; - - MFS_MN(sb) = mn; -errout: - return ret; -} - -int mfs_mn_fmt(FAR struct mfs_sb_s * const sb, const mfs_t mblk1, - const mfs_t mblk2, const mfs_t jrnl_blk) -{ - int ret = OK; - mfs_t pg; - struct mfs_mn_s mn; - struct timespec ts; - const mfs_t sz = sizeof(struct mfs_mn_s) - sizeof(mn.pg); - char buf[sz + 1]; - - clock_gettime(CLOCK_REALTIME, &ts); - - memset(buf, 0, sz + 1); - - pg = mfs_ba_getpg(sb); - if (predict_false(pg == 0)) - { - ret = -ENOSPC; - goto errout; - } - - finfo("Root formatted to be at Page %u", pg); - - mn.root_ctz.idx_e = 0; - mn.root_ctz.pg_e = pg; - mn.jrnl_blk = jrnl_blk; - mn.mblk_idx = 0; - mn.pg = MFS_BLK2PG(sb, mblk1); - mn.root_sz = 0; - mn.ts = ts; - mn.root_st_atim = ts; - mn.root_st_ctim = ts; - mn.root_st_mtim = ts; - mn.root_mode = 0777 | S_IFDIR; - - /* Serialize. */ - - ser_mn(mn, buf); - - ret = mfs_write_page(sb, buf, sz, MFS_BLK2PG(sb, mblk1), 0); - if (predict_false(ret < 0)) - { - goto errout; - } - else - { - ret = OK; - } - - ret = mfs_write_page(sb, buf, sz, MFS_BLK2PG(sb, mblk2), 0); - if (predict_false(ret < 0)) - { - goto errout; - } - else - { - ret = OK; - } - - mn.mblk_idx = 1; - MFS_MN(sb) = mn; - finfo("Master node written. Now at page %d, timestamp %lld.%.9ld.", - MFS_MN(sb).pg, (long long)MFS_MN(sb).ts.tv_sec, - MFS_MN(sb).ts.tv_nsec); - -errout: - return ret; -} - -int mfs_mn_move(FAR struct mfs_sb_s * const sb, struct mfs_ctz_s root, - const mfs_t root_sz) -{ - int ret = OK; - struct mfs_mn_s mn; - const mfs_t sz = sizeof(struct mfs_mn_s) - sizeof(mn.pg); - char buf[sz + 1]; - - if (MFS_MN(sb).mblk_idx == MFS_PGINBLK(sb) - 1) - { - /* TODO: Move journal. Master blocks are full. */ - } - - mn = MFS_MN(sb); - - mn.root_ctz = root; - mn.root_sz = root_sz; - mn.mblk_idx++; /* TODO */ - mn.pg++; - - ser_mn(mn, buf); - - ret = mfs_write_page(sb, buf, sz + 1, mn.pg, 0); - if (predict_false(ret < 0)) - { - goto errout; - } - - MFS_MN(sb) = mn; - -errout: - return ret; -} - -int mfs_mn_sync(FAR struct mfs_sb_s *sb, - FAR struct mfs_path_s * const new_loc, - const mfs_t blk1, const mfs_t blk2, const mfs_t jrnl_blk) -{ - int ret = OK; - struct timespec ts; - struct mfs_mn_s mn; - const mfs_t sz = sizeof(struct mfs_mn_s) - sizeof(mn.pg); - char buf[sz + 1]; - - mn = MFS_MN(sb); - - clock_gettime(CLOCK_REALTIME, &ts); - - if (mn.mblk_idx == MFS_PGINBLK(sb)) - { - /* New blocks have been already allocated by the journal. */ - - mn.mblk_idx = 0; - mn.pg = MFS_BLK2PG(sb, blk1); - } - - mn.ts = ts; - mn.root_sz = new_loc->sz; - mn.root_ctz = new_loc->ctz; - mn.root_mode = 0777 | S_IFDIR; - - /* TODO: Root timestamps. */ - - /* Serialize. */ - - ser_mn(mn, buf); - - ret = mfs_write_page(sb, buf, sz, MFS_BLK2PG(sb, blk1) + mn.mblk_idx, 0); - if (predict_false(ret < 0)) - { - goto errout; - } - - ret = mfs_write_page(sb, buf, sz, MFS_BLK2PG(sb, blk2) + mn.mblk_idx, 0); - if (predict_false(ret < 0)) - { - goto errout; - } - - mn.mblk_idx++; - MFS_MN(sb) = mn; - -errout: - return ret; -} diff --git a/fs/mnemofs/mnemofs_rw.c b/fs/mnemofs/mnemofs_rw.c index bc596937e86..7b21805bb01 100644 --- a/fs/mnemofs/mnemofs_rw.c +++ b/fs/mnemofs/mnemofs_rw.c @@ -54,129 +54,503 @@ * Included Files ****************************************************************************/ -#include +#include +#include #include "mnemofs.h" -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/**************************************************************************** - * Public Data - ****************************************************************************/ - /**************************************************************************** * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: mfs_rwbuf_check_page + * + * Description: + * Verify that page belongs to a good NAND block before it is accessed + * through the shared read/write buffer. + * + * Input Parameters: + * sb - The mounted file system instance. + * page - The page number to validate. + * + * Returned Value: + * Zero (OK) is returned if page is valid and its block is not marked bad. + * A negated errno value is returned on invalid input or if the page maps + * to a bad block. + * + ****************************************************************************/ + +static int mfs_rwbuf_check_page(FAR struct mfs_sb_s *sb, mfs_t page) +{ + mfs_t block; + int ret; + + if (sb == NULL || sb->mtd == NULL) + { + return -EINVAL; + } + + if (page >= MFS_PAGE_COUNT(sb)) + { + return -EINVAL; + } + + block = page / MFS_PAGES_PER_BLOCK(sb); + + ret = mfs_is_bad_block(sb, block); + if (ret < 0) + { + return ret; + } + + return ret == 0 ? OK : -EIO; +} + +/**************************************************************************** + * Name: mfs_rwbuf_load + * + * Description: + * Load one page into the shared read/write buffer, syncing any dirty + * contents already cached there first. + * + * Input Parameters: + * sb - The mounted file system instance. + * page - The page number to load. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned if + * validation, syncing, or the underlying read fails. + * + ****************************************************************************/ + +static int mfs_rwbuf_load(FAR struct mfs_sb_s *sb, mfs_t page) +{ + ssize_t nread; + int ret; + + if (sb == NULL || sb->rwbuf == NULL) + { + return -EINVAL; + } + + if (sb->rwvalid && sb->rwpage == page) + { + return OK; + } + + ret = mfs_rwbuf_sync(sb); + if (ret < 0) + { + return ret; + } + + ret = mfs_rwbuf_check_page(sb, page); + if (ret < 0) + { + return ret; + } + + nread = MTD_BREAD(sb->mtd, page, 1, sb->rwbuf); + if (nread < 0) + { + return (int)nread; + } + + if (nread != 1) + { + return -EIO; + } + + sb->rwpage = page; + sb->rwvalid = true; + sb->rwdirty = false; + return OK; +} + /**************************************************************************** * Public Functions ****************************************************************************/ -int mfs_isbadblk(FAR const struct mfs_sb_s * const sb, mfs_t blk) +/**************************************************************************** + * Name: mfs_is_bad_block + * + * Description: + * Query the backing MTD device to learn whether blk is marked bad. + * + * Input Parameters: + * sb - The mounted file system instance. + * blk - The block number to test. + * + * Returned Value: + * Zero (OK) is returned if blk is usable. A positive non-zero value may + * be returned if the MTD reports blk as bad. A negated errno value is + * returned on invalid input or device failure. + * + ****************************************************************************/ + +int mfs_is_bad_block(FAR const struct mfs_sb_s *sb, mfs_t blk) { - if (predict_false(blk > MFS_NBLKS(sb))) + int ret; + + if (sb == NULL || sb->mtd == NULL) { + ferr("invalid args\n"); return -EINVAL; } - return MTD_ISBAD(MFS_MTD(sb), blk); + if (blk >= MFS_BLOCK_COUNT(sb)) + { + ferr("invalid block\n"); + return -EINVAL; + } + + ret = MTD_ISBAD(sb->mtd, blk); + if (ret < 0 && ret != -ENOSYS) + { + ferr("MTD_ISBAD failed: %d\n", ret); + } + + return ret == -ENOSYS ? 0 : ret; } -int mfs_markbadblk(FAR const struct mfs_sb_s * const sb, mfs_t blk) +/**************************************************************************** + * Name: mfs_rwbuf_sync + * + * Description: + * Flush dirty contents from the shared read/write buffer to its cached + * page. + * + * Input Parameters: + * sb - The mounted file system instance. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned if + * the buffered page cannot be written back. + * + ****************************************************************************/ + +int mfs_rwbuf_sync(FAR struct mfs_sb_s *sb) { - if (predict_false(blk > MFS_NBLKS(sb))) + ssize_t nwritten; + + if (sb == NULL) { + ferr("invalid sb\n"); return -EINVAL; } - return MTD_MARKBAD(MFS_MTD(sb), blk); + if (sb->rwbuf == NULL || !sb->rwvalid || !sb->rwdirty) + { + return OK; + } + + nwritten = MTD_BWRITE(sb->mtd, sb->rwpage, 1, sb->rwbuf); + if (nwritten < 0) + { + ferr("MTD_BWRITE failed: %zd\n", nwritten); + return (int)nwritten; + } + + if (nwritten != 1) + { + ferr("short write: %zd\n", nwritten); + return -EIO; + } + + sb->rwdirty = false; + return OK; } -/* NOTE: These functions do not update the block allocator's state nor do - * they enforce it. - */ +/**************************************************************************** + * Name: mfs_rwbuf_invalidate + * + * Description: + * Drop any cached page association from the shared read/write buffer. + * + * Input Parameters: + * sb - The mounted file system instance. + * + * Returned Value: + * None. + * + ****************************************************************************/ -ssize_t mfs_write_page(FAR const struct mfs_sb_s * const sb, - FAR const char *data, const mfs_t datalen, - const off_t page, const mfs_t pgoff) +void mfs_rwbuf_invalidate(FAR struct mfs_sb_s *sb) { - int ret = OK; - - if (predict_false(page > MFS_NPGS(sb) || pgoff >= MFS_PGSZ(sb))) + if (sb == NULL) { + return; + } + + sb->rwpage = MFS_LOCATION_INVALID; + sb->rwvalid = false; + sb->rwdirty = false; +} + +/**************************************************************************** + * Name: mfs_rwbuf_prepare_write + * + * Description: + * Sync any dirty buffered page and then invalidate the cache so the next + * write starts from a clean state. + * + * Input Parameters: + * sb - The mounted file system instance. + * + * Returned Value: + * Zero (OK) is returned on success. A negated errno value is returned if + * syncing the buffered page fails. + * + ****************************************************************************/ + +int mfs_rwbuf_prepare_write(FAR struct mfs_sb_s *sb) +{ + int ret; + + if (sb == NULL) + { + ferr("invalid sb\n"); return -EINVAL; } - memcpy(MFS_RWBUF(sb) + pgoff, data, MIN(datalen, MFS_PGSZ(sb) - pgoff)); - - ret = MTD_BWRITE(MFS_MTD(sb), page, 1, MFS_RWBUF(sb)); - if (predict_false(ret < 0)) + ret = mfs_rwbuf_sync(sb); + if (ret < 0) { - goto errout_with_reset; + ferr("mfs_rwbuf_sync failed: %d\n", ret); + return ret; } -errout_with_reset: - memset(MFS_RWBUF(sb), 0, MFS_PGSZ(sb)); + mfs_rwbuf_invalidate(sb); + return OK; +} + +/**************************************************************************** + * Name: mfs_rwbuf_discard_page + * + * Description: + * Invalidate the shared read/write buffer if it currently caches page. + * + * Input Parameters: + * sb - The mounted file system instance. + * page - The page whose cached state should be discarded. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void mfs_rwbuf_discard_page(FAR struct mfs_sb_s *sb, mfs_t page) +{ + if (sb == NULL || !sb->rwvalid) + { + return; + } + + if (sb->rwpage == page) + { + mfs_rwbuf_invalidate(sb); + } +} + +/**************************************************************************** + * Name: mfs_rwbuf_discard_block + * + * Description: + * Invalidate the shared read/write buffer if it currently caches any page + * that belongs to block. + * + * Input Parameters: + * sb - The mounted file system instance. + * block - The block whose cached state should be discarded. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void mfs_rwbuf_discard_block(FAR struct mfs_sb_s *sb, mfs_t block) +{ + if (sb == NULL || block >= MFS_BLOCK_COUNT(sb) || !sb->rwvalid) + { + return; + } + + if (sb->rwpage / MFS_PAGES_PER_BLOCK(sb) == block) + { + mfs_rwbuf_invalidate(sb); + } +} + +/**************************************************************************** + * Name: mfs_write_page + * + * Description: + * Stage one page for writing through the shared read/write buffer. The + * page becomes dirty and is written later by mfs_rwbuf_sync(). + * + * Input Parameters: + * sb - The mounted file system instance. + * page - The page number to update. + * buffer - The page-sized data to stage. + * + * Returned Value: + * One is returned on success. A negated errno value is returned if the + * inputs are invalid, the page cannot be cached, or an earlier dirty page + * cannot be synced. + * + ****************************************************************************/ + +ssize_t mfs_write_page(FAR struct mfs_sb_s *sb, mfs_t page, + FAR const uint8_t *buffer) +{ + int ret; + + if (sb == NULL || sb->mtd == NULL || sb->rwbuf == NULL || buffer == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + if (page >= MFS_PAGE_COUNT(sb)) + { + ferr("invalid page\n"); + return -EINVAL; + } + + if (!sb->rwvalid || sb->rwpage != page) + { + ret = mfs_rwbuf_sync(sb); + if (ret < 0) + { + ferr("mfs_rwbuf_sync failed: %d\n", ret); + return ret; + } + + ret = mfs_rwbuf_check_page(sb, page); + if (ret < 0) + { + ferr("mfs_rwbuf_check_page failed: %d\n", ret); + return ret; + } + + sb->rwpage = page; + sb->rwvalid = true; + sb->rwdirty = false; + } + + if (buffer != sb->rwbuf) + { + memcpy(sb->rwbuf, buffer, MFS_PAGE_SIZE(sb)); + } + + sb->rwdirty = true; + return 1; +} + +/**************************************************************************** + * Name: mfs_read_page + * + * Description: + * Load one page through the shared read/write buffer and copy it to the + * caller if needed. + * + * Input Parameters: + * sb - The mounted file system instance. + * page - The page number to read. + * buffer - The page-sized buffer that receives the data. + * + * Returned Value: + * One is returned on success. A negated errno value is returned if the + * inputs are invalid or the page cannot be loaded. + * + ****************************************************************************/ + +ssize_t mfs_read_page(FAR struct mfs_sb_s *sb, mfs_t page, + FAR uint8_t *buffer) +{ + int ret; + + if (sb == NULL || sb->mtd == NULL || sb->rwbuf == NULL || buffer == NULL) + { + ferr("invalid args\n"); + return -EINVAL; + } + + if (page >= MFS_PAGE_COUNT(sb)) + { + ferr("invalid page\n"); + return -EINVAL; + } + + ret = mfs_rwbuf_load(sb, page); + if (ret < 0) + { + ferr("mfs_rwbuf_load failed: %d\n", ret); + return ret; + } + + if (buffer != sb->rwbuf) + { + memcpy(buffer, sb->rwbuf, MFS_PAGE_SIZE(sb)); + } + + return 1; +} + +/**************************************************************************** + * Name: mfs_erase_blocks + * + * Description: + * Erase a contiguous range of blocks and discard any cached page that + * falls inside that range. + * + * Input Parameters: + * sb - The mounted file system instance. + * startblk - The first block to erase. + * nblocks - The number of blocks to erase. + * + * Returned Value: + * The underlying MTD erase result is returned on success. A negated + * errno value is returned on invalid input. + * + ****************************************************************************/ + +int mfs_erase_blocks(FAR struct mfs_sb_s *sb, mfs_t startblk, + size_t nblocks) +{ + int ret; + + if (sb == NULL || sb->mtd == NULL || nblocks == 0) + { + ferr("invalid args\n"); + return -EINVAL; + } + + if (startblk >= MFS_BLOCK_COUNT(sb) || + nblocks > MFS_BLOCK_COUNT(sb) || + startblk > MFS_BLOCK_COUNT(sb) - nblocks) + { + ferr("invalid erase range\n"); + return -EINVAL; + } + + if (sb->rwvalid) + { + mfs_t endblk = startblk + nblocks; + mfs_t curblk = sb->rwpage / MFS_PAGES_PER_BLOCK(sb); + + if (curblk >= startblk && curblk < endblk) + { + mfs_rwbuf_invalidate(sb); + } + } + + ret = MTD_ERASE(sb->mtd, startblk, nblocks); + if (ret < 0) + { + ferr("MTD_ERASE failed: %d\n", ret); + } return ret; } - -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) -{ - int ret = OK; - - if (predict_false(page > MFS_NPGS(sb) || pgoff >= MFS_PGSZ(sb))) - { - return -EINVAL; - } - - ret = MTD_BREAD(MFS_MTD(sb), page, 1, MFS_RWBUF(sb)); - if (predict_false(ret < 0)) - { - goto errout_with_reset; - } - - memcpy(data, MFS_RWBUF(sb) + pgoff, MIN(datalen, MFS_PGSZ(sb) - pgoff)); - -errout_with_reset: - memset(MFS_RWBUF(sb), 0, MFS_PGSZ(sb)); - - return ret; -} - -int mfs_erase_blk(FAR const struct mfs_sb_s * const sb, const off_t blk) -{ - if (predict_false(blk > MFS_NBLKS(sb))) - { - return -EINVAL; - } - - return MTD_ERASE(MFS_MTD(sb), blk, 1); -} - -int mfs_erase_nblks(FAR const struct mfs_sb_s * const sb, const off_t blk, - const size_t n) -{ - if (predict_false(blk + n > MFS_NBLKS(sb))) - { - return -EINVAL; - } - - return MTD_ERASE(MFS_MTD(sb), blk, n); -} diff --git a/fs/mnemofs/mnemofs_util.c b/fs/mnemofs/mnemofs_util.c deleted file mode 100644 index b98661ee88e..00000000000 --- a/fs/mnemofs/mnemofs_util.c +++ /dev/null @@ -1,263 +0,0 @@ -/**************************************************************************** - * fs/mnemofs/mnemofs_util.c - * - * SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause - * - * 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" - -#include -#include - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/**************************************************************************** - * Public Data - ****************************************************************************/ - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -uint16_t mfs_hash(FAR const char *arr, ssize_t len) -{ - ssize_t l = 0; - ssize_t r = len - 1; - uint32_t hash = 0; - - while (l <= r) - { - hash += arr[l] * arr[r] * (l + 1) * (r + 1); - l++; - r--; - hash %= (1 << MFS_HASHSZ); - } - - finfo("Hash calculated for size %zd to be %" PRIi32, len, hash); - - return hash; -} - -FAR char *mfs_ser_8(const uint8_t n, FAR char * const out) -{ - *out = n; - return out + 1; -} - -FAR const char *mfs_deser_8(FAR const char * const in, FAR uint8_t *n) -{ - *n = in[0]; - return in + 1; -} - -FAR char *mfs_ser_16(const uint16_t n, FAR char * const out) -{ - memcpy(out, &n, 2); - return out + 2; -} - -FAR const char *mfs_deser_16(FAR const char * const in, FAR uint16_t *n) -{ - memcpy(n, in, 2); - return in + 2; -} - -FAR char *mfs_ser_str(FAR const char * const str, const mfs_t len, - FAR char * const out) -{ - memcpy(out, str, len); - return out + len; -} - -FAR const char *mfs_deser_str(FAR const char * const in, - FAR char * const str, const mfs_t len) -{ - memcpy(str, in, len); - str[len] = 0; - return in + len; -} - -FAR char *mfs_ser_mfs(const mfs_t n, FAR char * const out) -{ - memcpy(out, &n, 4); - return out + 4; -} - -FAR const char *mfs_deser_mfs(FAR const char * const in, FAR mfs_t * const n) -{ - memcpy(n, in, 4); - return in + 4; -} - -FAR char *mfs_ser_64(const uint64_t n, FAR char * const out) -{ - memcpy(out, &n, 8); - return out + 8; -} - -FAR const char *mfs_deser_64(FAR const char * const in, - FAR uint64_t * const n) -{ - memcpy(n, in, 8); - return in + 8; -} - -FAR char *mfs_ser_ctz(FAR const struct mfs_ctz_s * const x, - FAR char * const out) -{ - FAR char *o = out; - - o = mfs_ser_mfs(x->pg_e, o); - o = mfs_ser_mfs(x->idx_e, o); - return o; -} - -FAR const char *mfs_deser_ctz(FAR const char * const in, - FAR struct mfs_ctz_s * const x) -{ - FAR const char *i = in; - - i = mfs_deser_mfs(i, &x->pg_e); - i = mfs_deser_mfs(i, &x->idx_e); - return i; -} - -FAR char *mfs_ser_path(FAR const struct mfs_path_s * const x, - FAR char * const out) -{ - FAR char *o = out; - - o = mfs_ser_ctz(&x->ctz, o); - o = mfs_ser_mfs(x->off, o); - o = mfs_ser_mfs(x->sz, o); - return o; -} - -FAR const char *mfs_deser_path(FAR const char * const in, - FAR struct mfs_path_s * const x) -{ - FAR const char *i = in; - - i = mfs_deser_ctz(i, &x->ctz); - i = mfs_deser_mfs(i, &x->off); - i = mfs_deser_mfs(i, &x->sz); - return i; -} - -FAR char *mfs_ser_timespec(FAR const struct timespec * const x, - FAR char * const out) -{ - FAR char *o = out; - - o = mfs_ser_64(x->tv_sec, o); - o = mfs_ser_64(x->tv_nsec, o); - - return o; -} - -FAR const char *mfs_deser_timespec(FAR const char * const in, - FAR struct timespec * const x) -{ - uint64_t tmp; - FAR const char *i = in; - - i = mfs_deser_64(i, &tmp); - x->tv_sec = tmp; - i = mfs_deser_64(i, &tmp); - x->tv_nsec = tmp; - - return i; -} - -mfs_t mfs_v2n(mfs_t n) -{ - /* https://math.stackexchange.com/a/1835555 */ - - return (n & (~(n - 1))); -} - -mfs_t mfs_set_msb(mfs_t n) -{ - return 31 - mfs_clz(n); -} - -bool mfs_ctz_eq(FAR const struct mfs_ctz_s * const a, - FAR const struct mfs_ctz_s * const b) -{ - return a->idx_e == b->idx_e && a->pg_e == b->pg_e; -} - -bool mfs_path_eq(FAR const struct mfs_path_s * const a, - FAR const struct mfs_path_s * const b) -{ - return mfs_ctz_eq(&a->ctz, &b->ctz) && (a->off == b->off) - && (a->sz == b->sz); -}