From 0478b5bce46a6de4a203f8a8dbced4842904d4f9 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Mon, 19 Mar 2018 07:08:40 -0600 Subject: [PATCH] Addes CROMFS prototype. CROMFS is a commpressed, in-memory read-only file system based on LZF. Squashed commit of the following: fs/cromfs: Now depends on EXPERIMENTAL because it is not yet verified. fs/cromfs: This commit brings the CROMFS file system to code complete. Still completely untested. Next steps: Need a tool to generate CROMFS file system images and a test case under apps/ fs/cromfs: Add logic to traverse the in-memory file system nodes. fs/cromfs: Add initial support for an in-memory, compressed, read-only file system. --- fs/Kconfig | 1 + fs/Makefile | 3 +- fs/cromfs/Kconfig | 15 + fs/cromfs/Make.defs | 47 ++ fs/cromfs/cromfs.h | 127 +++++ fs/cromfs/fs_cromfs.c | 1129 +++++++++++++++++++++++++++++++++++++ fs/mount/fs_mount.c | 9 +- include/nuttx/fs/dirent.h | 20 +- include/sys/stat.h | 4 +- include/sys/statfs.h | 1 + 10 files changed, 1349 insertions(+), 7 deletions(-) create mode 100644 fs/cromfs/Kconfig create mode 100644 fs/cromfs/Make.defs create mode 100644 fs/cromfs/cromfs.h create mode 100644 fs/cromfs/fs_cromfs.c diff --git a/fs/Kconfig b/fs/Kconfig index 1bada41f4a..6fe7911734 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -81,6 +81,7 @@ source fs/fat/Kconfig source fs/nfs/Kconfig source fs/nxffs/Kconfig source fs/romfs/Kconfig +source fs/cromfs/Kconfig source fs/tmpfs/Kconfig source fs/smartfs/Kconfig source fs/binfs/Kconfig diff --git a/fs/Makefile b/fs/Makefile index bba917231c..1ffea7cae0 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -1,7 +1,7 @@ ############################################################################ # fs/Makefile # -# Copyright (C) 2007, 2008, 2011-2014, 2016-2017 Gregory Nutt. All rights +# Copyright (C) 2007, 2008, 2011-2014, 2016-2018 Gregory Nutt. All rights # reserved. # Author: Gregory Nutt # @@ -65,6 +65,7 @@ ifneq ($(CONFIG_DISABLE_MOUNTPOINT),y) include mount/Make.defs include fat/Make.defs include romfs/Make.defs +include cromfs/Make.defs include tmpfs/Make.defs include nxffs/Make.defs include nfs/Make.defs diff --git a/fs/cromfs/Kconfig b/fs/cromfs/Kconfig new file mode 100644 index 0000000000..38fa42e061 --- /dev/null +++ b/fs/cromfs/Kconfig @@ -0,0 +1,15 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config FS_CROMFS + bool "CROMFS file system" + default n + depends on !DISABLE_MOUNTPOINT && EXPERIMENTAL + select FS_READABLE + ---help--- + Enable Compessed Read-Only Filesystem (CROMFS) support + +if FS_CROMFS +endif diff --git a/fs/cromfs/Make.defs b/fs/cromfs/Make.defs new file mode 100644 index 0000000000..d3f1600adf --- /dev/null +++ b/fs/cromfs/Make.defs @@ -0,0 +1,47 @@ +############################################################################ +# fs/cromfs/Make.defs +# +# Copyright (C) 2008 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# 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 Nuttx nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +ifeq ($(CONFIG_FS_CROMFS),y) +# Files required for CROMFS file system support + +ASRCS += +CSRCS += fs_cromfs.c + +# Include ROMFS build support + +DEPPATH += --dep-path cromfs +VPATH += :cromfs + +endif diff --git a/fs/cromfs/cromfs.h b/fs/cromfs/cromfs.h new file mode 100644 index 0000000000..44700e4f84 --- /dev/null +++ b/fs/cromfs/cromfs.h @@ -0,0 +1,127 @@ +/**************************************************************************** + * fs/cromfs/cromfs.h + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __FS_CROMFS_CROMFS_H +#define __FS_CROMFS_CROMFS_H 1 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* This structure describes the CROMFS volume. It provides most of the + * information needed for statfs() including: + * + * f_type - Type of filesystem + * Return cv_magic (CROMFS_MAGIC) + * f_namelen - Maximum length of filenames + * Return NAME_MAX + * f_bsize - Optimal block size for transfers + * Return cv_bsize, the block size used when file system was + * compressed + * f_blocks - Total data blocks in the file system + * - Return cv_nblocks + * f_bfree - Free blocks in the file system + * Return 0 + * f_bavail - Free blocks avail to non-superuser + * Return 0 + * f_files - Total file nodes in the file system + * - Return cv_nnodes + * f_ffree - Free file nodes in the file system + * Return 0 + * + * The volume header is followed immediately by the root directory node. An + * offset to that node is used to permit future variable length data (such as + * a volumne name) which may intervene. + * + * Since this is an in-memory file system, size_t is the most relevant type for + * internal file system offsets. + */ + +struct cromfs_volume_s +{ + uint32_t cv_magic; /* Must be first. Must be CROMFS_MAGIC */ + uint16_t cv_nnodes; /* Total number of nodes in-use */ + uint16_t cv_nblocks; /* Total number of data blocks in-use */ + size_t cv_root; /* Offset to the first node in the root file system */ + size_t cv_fsize; /* Size of the compressed file system image */ + size_t cv_bsize; /* Optimal block size for transfers */ +}; + +/* This describes one node in the CROMFS file system. It holds node meta + * data that provides the information that will be return by stat() or fstat() + * and also provides the information needed by the CROMFS file system to + * access the node data. + * + * Relationship to struct stat: + * + * st_mode - File type, attributes, and access mode bits + * Return cn_mode from the node structure + * st_size - Size of file/directory, in bytes + * Return cn_size from the node structure + * st_blksize - Block size used for filesystem I/O + * Return cv_bsize from the volume header. + * st_blocks - Number of blocks allocated + * Return (cn_size + (cv_bsize - 1)) / cv_bsize + * st_atime - Time of last access + * Return 0 + * st_mtime - Time of last modification + * Return 0 + * st_ctime - Time of last status change + * Return 0 + */ + +struct cromfs_node_s +{ + mode_t cn_mode; /* File type, attributes, and access mode bits */ + size_t cn_name; /* Offset from the beginning of the volume header to the + * node name string. NUL-terminated. */ + size_t cn_size; /* Size of the uncompressed data (in bytes) */ + size_t cn_peer; /* Offset to next node in this directory (for readdir())*/ + union /* Must be last */ + { + size_t cn_child; /* Offset to first node in sub-directory (directories only) */ + size_t cn_link; /* Offset to an arbitrary node (for hard link) */ + size_t cn_blocks; /* Offset to first block of compressed data (for read) */ + } u; +}; + +#endif /* __FS_CROMFS_CROMFS_H */ \ No newline at end of file diff --git a/fs/cromfs/fs_cromfs.c b/fs/cromfs/fs_cromfs.c new file mode 100644 index 0000000000..13a837db43 --- /dev/null +++ b/fs/cromfs/fs_cromfs.c @@ -0,0 +1,1129 @@ +/**************************************************************************** + * fs/cromfs/fs_cromfs.c + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER 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 +#include +#include + +#include +#include +#include +#include +#include + +#include "cromfs.h" + +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_CROMFS) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure represents an open, regular file */ + +struct cromfs_file_s +{ + FAR const struct cromfs_node_s *ff_node; /* The open file node */ + FAR uint8_t *ff_buffer; /* Decompression buffer */ +}; + +/* This is the form of the callback from cromfs_foreach_node(): */ + +typedef CODE int (*cromfs_foreach_t)(FAR const struct cromfs_volume_s *fs, + FAR const struct cromfs_node_s *node, + FAR void *arg); + +/* This is the form of the argument provided to the cromfs_comparenode() + * callback. + */ + +struct cromfs_comparenode_s +{ + FAR const struct cromfs_node_s **node; /* Location to return the node */ + FAR const char *relpath; /* Full relative path */ + FAR const char *segment; /* Reference to start of the + * path segment. */ + uint16_t seglen; /* Length of the next path segment */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Helpers */ + +static FAR void *cromfs_offset2addr(FAR const struct cromfs_volume_s *fs, + size_t offset); +static size_t cromfs_addr2offset(FAR const struct cromfs_volume_s *fs, + FAR const void *addr); +static int cromfs_foreach_node(FAR const struct cromfs_volume_s *fs, + cromfs_foreach_t callback, FAR void *arg); +static uint16_t cromfs_seglen(FAR const char *relpath); +static int cromfs_comparenode(FAR const struct cromfs_volume_s *fs, + FAR const struct cromfs_node_s *node, + FAR void *arg); +static int cromfs_findnode(FAR const struct cromfs_volume_s *fs, + FAR const struct cromfs_node_s **node, + FAR const char *relpath); + +/* Common file system methods */ + +static int cromfs_open(FAR struct file *filep, const char *relpath, + int oflags, mode_t mode); +static int cromfs_close(FAR struct file *filep); +static ssize_t cromfs_read(FAR struct file *filep, char *buffer, size_t buflen); +static int cromfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg); + +static int cromfs_dup(FAR const struct file *oldp, FAR struct file *newp); +static int cromfs_fstat(FAR const struct file *filep, FAR struct stat *buf); + +static int cromfs_opendir(struct inode *mountpt, const char *relpath, + struct fs_dirent_s *dir); +static int cromfs_readdir(FAR struct inode *mountpt, + FAR struct fs_dirent_s *dir); +static int cromfs_rewinddir(FAR struct inode *mountpt, + FAR struct fs_dirent_s *dir); + +static int cromfs_bind(FAR struct inode *blkdriver, FAR const void *data, + FAR void **handle); +static int cromfs_unbind(FAR void *handle, FAR struct inode **blkdriver, + unsigned int flags); +static int cromfs_statfs(FAR struct inode *mountpt, + FAR struct statfs *buf); + +static int cromfs_stat(FAR struct inode *mountpt, FAR const char *relpath, + FAR struct stat *buf); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* See fs_mount.c -- this structure is explicitly extern'ed there. + * We use the old-fashioned kind of initializers so that this will compile + * with any compiler. + */ + +const struct mountpt_operations cromfs_operations = +{ + cromfs_open, /* open */ + cromfs_close, /* close */ + cromfs_read, /* read */ + NULL, /* write */ + NULL, /* seek */ + cromfs_ioctl, /* ioctl */ + + NULL, /* sync */ + cromfs_dup, /* dup */ + cromfs_fstat, /* fstat */ + NULL, /* truncate */ + + cromfs_opendir, /* opendir */ + NULL, /* closedir */ + cromfs_readdir, /* readdir */ + cromfs_rewinddir, /* rewinddir */ + + cromfs_bind, /* bind */ + cromfs_unbind, /* unbind */ + cromfs_statfs, /* statfs */ + + NULL, /* unlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* rename */ + cromfs_stat /* stat */ +}; + +/* The CROMFS uses a global, in-memory instance of the file system image + * rather than a ROMDISK as does, same the ROMFS file system. This is + * primarily because the compression logic needs contiguous, in-memory + * data. One consequence of this is that there can only only be a single + * CROMFS file system in the build. + * + * This is the address of the single CROMFS file system image: + */ + +extern const struct cromfs_volume_s g_cromfs_image; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cromfs_offset2addr + ****************************************************************************/ + +static FAR void *cromfs_offset2addr(FAR const struct cromfs_volume_s *fs, + size_t offset) +{ + /* Zero offset is a specials case: It corresponds to a NULL address */ + + if (offset == 0) + { + return NULL; + } + + /* The root node lies at the beginning of the CROMFS image so we can + * convert the offset into the image by simply adding the the address + * of the root node. + */ + + DEBUGASSERT(offset < fs->cv_fsize); + return (FAR void *)((FAR uint8_t *)fs + offset); +} + +/**************************************************************************** + * Name: cromfs_offset2addr + ****************************************************************************/ + +static size_t cromfs_addr2offset(FAR const struct cromfs_volume_s *fs, + FAR const void *addr) +{ + uintptr_t start; + uintptr_t target; + size_t offset; + + /* NULL is a specials case: It corresponds to offset zero */ + + if (addr == NULL) + { + return 0; + } + + /* Make sure that the address is "after" the start of file system image. */ + + start = (uintptr_t)fs; + target = (uintptr_t)addr; + DEBUGASSERT(target >= start); + + /* Get the non-zero offset and make sure that the offset is within the file + * system image. + */ + + offset = target - start; + DEBUGASSERT(offset < fs->cv_fsize); + return offset; +} + +/**************************************************************************** + * Name: cromfs_foreach_node + ****************************************************************************/ + +static int cromfs_foreach_node(FAR const struct cromfs_volume_s *fs, + cromfs_foreach_t callback, FAR void *arg) +{ + FAR const struct cromfs_node_s *node; + int ret = OK; + + node = (FAR const struct cromfs_node_s *)cromfs_offset2addr(fs, fs->cv_root); + while (node != NULL) + { + ret = callback(fs, node, arg); + if (ret != OK) + { + return ret; + } + + node = (FAR const struct cromfs_node_s *) + cromfs_offset2addr(fs, node->cn_peer); + } + + return ret; +} + +/**************************************************************************** + * Name: cromfs_seglen + ****************************************************************************/ + +static uint16_t cromfs_seglen(FAR const char *relpath) +{ + FAR char *delimiter; + int len; + + delimiter = strchr(relpath, '/'); + if (delimiter == NULL) + { + len = strlen(relpath); + } + else + { + len = (int)((uintptr_t)delimiter - (uintptr_t)relpath); + } + + DEBUGASSERT((unsigned int)len <= UINT16_MAX); + return (uint16_t)len; +} + +/**************************************************************************** + * Name: cromfs_comparenode + ****************************************************************************/ + +static int cromfs_comparenode(FAR const struct cromfs_volume_s *fs, + FAR const struct cromfs_node_s *node, + FAR void *arg) +{ + FAR struct cromfs_comparenode_s *cpnode; + FAR char *name; + int namlen; + + DEBUGASSERT(fs != NULL && node != NULL && arg != NULL); + cpnode = (FAR struct cromfs_comparenode_s *)arg; + + /* Get the name of the node */ + + name = (FAR char *)cromfs_offset2addr(fs, node->cn_name); + namlen = strlen(name); + + /* If the lengths of the name does not match the length of the next path + * segment, then this is not the node we are looking for. + */ + + if (namlen != cpnode->seglen) + { + return 0; /* Keep looking */ + } + + /* The are the same length... are they the same? */ + + if (strncmp(name, cpnode->segment, namlen) == 0) + { + FAR const char *segment = cpnode->segment; + + /* Got it! Was this the last segment of the path? If so, then + * the segment length is equal to the length of the remaining + * relpath and we should find a NUL terminator at that location. + */ + + if (segment[namlen] == '\0') + { + /* We have it. Save the terminal node with the final match + * and return 1 to stop the traversal. + */ + + *cpnode->node = node; + return 1; + } + + /* A specal cas is if the path ends in "/". In this case I suppose + * we need to interpret the as matching as long as it is a directory? + */ + + if (segment[namlen] == '/' && segment[namlen = 1] == '\0') + { + *cpnode->node = node; + return S_ISDIR(node->cn_mode) ? 1 : -ENOENT; + } + + /* If this is a valid, non-terminal segment on the path, then it must + * be a directory. + */ + + if (!S_ISDIR(node->cn_mode)) + { + /* Terminate the traversal with an error */ + + return -ENOTDIR; + } + + /* Set up to continue the traversal in the sub-directory. NOTE that + * this recurses and could potentially eat up a lot of stack. + */ + + segment = cpnode->segment + cpnode->seglen; + + /* Skip over any '/' delimiter */ + + while (*segment == '/') + { + segment++; + } + + cpnode->segment = segment; + cpnode->seglen = cromfs_seglen(segment); + DEBUGASSERT(cpnode->seglen > 0); + + /* Then recurse */ + + return cromfs_foreach_node(fs, cromfs_comparenode, cpnode); + } + else + { + return 0; /* Keep looking in this directory */ + } +} + +/**************************************************************************** + * Name: cromfs_findnode + ****************************************************************************/ + +static int cromfs_findnode(FAR const struct cromfs_volume_s *fs, + FAR const struct cromfs_node_s **node, + FAR const char *relpath) +{ + struct cromfs_comparenode_s cpnode; + int ret; + + /* NULL or empty string refers to the root node */ + + if (relpath == NULL || relpath[0] == '\0') + { + *node = (FAR const struct cromfs_node_s *) + cromfs_offset2addr(fs, fs->cv_root); + return OK; + } + + /* Not the root directory. Relative so it should not begin with '/'. */ + + if (relpath[0] == '/') + { + return -EINVAL; + } + + /* Set up for the traversal */ + + cpnode.node = node; + cpnode.relpath = relpath; + cpnode.segment = relpath; + cpnode.seglen = (uint16_t)cromfs_seglen(relpath); + + ret = cromfs_foreach_node(fs, cromfs_comparenode, &cpnode); + if (ret > 0) + { + return OK; + } + else if (ret == OK) + { + return -ENOENT; + } + else + { + return ret; + } +} + +/**************************************************************************** + * Name: cromfs_open + ****************************************************************************/ + +static int cromfs_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode) +{ + FAR struct inode *inode; + FAR const struct cromfs_volume_s *fs; + FAR const struct cromfs_node_s *node; + FAR struct cromfs_file_s *ff; + int ret; + + finfo("Open '%s'\n", relpath); + + /* Sanity checks */ + + DEBUGASSERT(filep->f_priv == NULL && filep->f_inode != NULL); + + /* Get the mountpoint inode reference from the file structure and the + * volume private data from the inode structure + */ + + inode = filep->f_inode; + fs = inode->i_private; + + DEBUGASSERT(fs != NULL); + + /* CROMFS is read-only. Any attempt to open with any kind of write + * access is not permitted. + */ + + if ((oflags & O_WRONLY) != 0 || (oflags & O_RDONLY) == 0) + { + ferr("ERROR: Only O_RDONLY supported\n"); + return -EACCES; + } + + /* Locate the node for this relative path */ + + node = NULL; + ret = cromfs_findnode(fs, &node, relpath); + if (ret < 0) + { + /* Nothing exists at that relative path (or a really bad error occurred) */ + + return ret; + } + + DEBUGASSERT(node != NULL); + + /* Verify that the node is a regular file */ + + if (!S_ISREG(node->cn_mode)) + { + return -EISDIR; + } + + /* Create an instance of the file private date to describe the opened + * file. + */ + + ff = (FAR struct cromfs_file_s *)kmm_zalloc(sizeof(struct cromfs_file_s)); + if (ff == NULL) + { + return -ENOMEM; + } + + /* Create a file buffer to support partial sector accesses */ + + ff->ff_buffer = (FAR uint8_t *)kmm_malloc(fs->cv_bsize); + if (!ff->ff_buffer) + { + kmm_free(ff); + return -ENOMEM; + } + + /* Save the node in the open file instance */ + + ff->ff_node = node; + + /* Save the index as the open-specific state in filep->f_priv */ + + filep->f_priv = (FAR void *)ff; + return OK; +} + +/**************************************************************************** + * Name: cromfs_close + ****************************************************************************/ + +static int cromfs_close(FAR struct file *filep) +{ + FAR struct cromfs_file_s *ff; + + finfo("Closing\n"); + DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); + + /* Get the open file instance from the file structure */ + + ff = filep->f_priv; + DEBUGASSERT(ff->ff_node != NULL && ff->ff_buffer != NULL); + + /* Free all resources consumed by the opened file */ + + kmm_free(ff->ff_buffer); + kmm_free(ff); + + return OK; +} + +/**************************************************************************** + * Name: cromfs_read + ****************************************************************************/ + +static ssize_t cromfs_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct inode *inode; + FAR const struct cromfs_volume_s *fs; + FAR struct cromfs_file_s *ff; + FAR struct lzf_header_s *currhdr; + FAR struct lzf_header_s *nexthdr; + FAR uint8_t *dest; + FAR const uint8_t *src; + off_t fpos; + size_t remaining; + size_t blkoffs; + uint16_t ulen; + uint16_t clen; + unsigned int copysize; + unsigned int copyoffs; + + finfo("Read %d bytes from offset %d\n", buflen, filep->f_pos); + DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); + + /* Get the mountpoint inode reference from the file structure and the + * volume private data from the inode structure + */ + + inode = filep->f_inode; + fs = inode->i_private; + DEBUGASSERT(fs != NULL); + + /* Get the open file instance from the file structure */ + + ff = (FAR struct cromfs_file_s *)filep->f_priv; + DEBUGASSERT(ff->ff_node != NULL && ff->ff_buffer != NULL); + + /* Check for a read past the end of the file */ + + if (filep->f_pos > ff->ff_node->cn_size) + { + /* Return the end-of-file indication */ + + return 0; + } + + /* Find the compressed block containing the current offset, f_pos */ + + dest = (FAR uint8_t *)buffer; + remaining = buflen; + fpos = filep->f_pos; + blkoffs = 0; + ulen = 0; + nexthdr = (FAR struct lzf_header_s *) + cromfs_offset2addr(fs, ff->ff_node->u.cn_blocks); + + /* Look until we find the compressed block containing the start of the + * requested data. + */ + + while (remaining > 0) + { + /* Search for the next block containing the fpos file offset. This is + * real search on the first time through but the reamining blocks should + * be contiguous. + */ + + do + { + size_t blksize; + + /* Go to the next block */ + + currhdr = nexthdr; + blkoffs += blksize; + + if (currhdr->lzf_type == LZF_TYPE0_HDR) + { + FAR struct lzf_type0_header_s *hdr0 = + (FAR struct lzf_type0_header_s *)currhdr; + + ulen = (uint16_t)hdr0->lzf_len[0] << 8 | + (uint16_t)hdr0->lzf_len[0]; + blksize = (size_t)ulen + LZF_TYPE0_HDR_SIZE; + } + else + { + FAR struct lzf_type1_header_s * hdr1 = + (FAR struct lzf_type1_header_s *)currhdr; + + ulen = (uint16_t)hdr1->lzf_ulen[0] << 8 | + (uint16_t)hdr1->lzf_ulen[0]; + clen = (uint16_t)hdr1->lzf_clen[0] << 8 | + (uint16_t)hdr1->lzf_clen[0]; + blksize = (size_t)clen + LZF_TYPE1_HDR_SIZE; + } + + nexthdr = (FAR struct lzf_header_s *)((FAR uint8_t *)currhdr + blksize); + } + while (blkoffs <= fpos && (blkoffs + ulen) > fpos); + + /* Check if we need to decompress the next block into the user buffer */ + + if (currhdr->lzf_type == LZF_TYPE0_HDR) + { + /* Just copy the uncompressed data copy data from image to the user buffer */ + + copyoffs = blkoffs >= filep->f_pos ? 0 : filep->f_pos - blkoffs; + DEBUGASSERT(ulen > copyoffs); + copysize = ulen - copyoffs; + + if (copysize > remaining) /* Clip to the size really needed */ + { + copysize = remaining; + } + + src = (FAR const uint8_t *)currhdr + LZF_TYPE0_HDR_SIZE; + memcpy(dest, &src[copyoffs], copysize); + } + else + { + unsigned int decomplen; + + /* If the source of the data is at the beginning of the compressed + * data buffer, then we can decompress directly into the user buffer. + */ + + if (filep->f_pos <= blkoffs) + { + copyoffs = 0; + copysize = ulen; + if (copysize > remaining) + { + copysize = remaining; + } + + src = (FAR const uint8_t *)currhdr + LZF_TYPE1_HDR_SIZE; + decomplen = lzf_decompress(src, clen, dest, copysize); + DEBUGASSERT(decomplen = copysize); + } + else + { + unsigned int outsize; + + /* No, we will need to decompress into the our intermediate + * decompression buffer. + */ + + copyoffs = blkoffs >= filep->f_pos ? 0 : filep->f_pos - blkoffs; + DEBUGASSERT(ulen > copyoffs); + copysize = ulen - copyoffs; + + if (copysize > remaining) /* Clip to the size really needed */ + { + copysize = remaining; + } + + outsize = copyoffs + copysize; + DEBUGASSERT(outsize <= fs->cv_bsize); + + src = (FAR const uint8_t *)currhdr + LZF_TYPE1_HDR_SIZE; + decomplen = lzf_decompress(src, clen, ff->ff_buffer, outsize); + DEBUGASSERT(decomplen == outsize); + + /* Then copy to user buffer */ + + memcpy(dest, &ff->ff_buffer[copyoffs], copysize); + } + } + } + + /* Adjust pointers counts and offset */ + + dest += copysize; + remaining -= copysize; + fpos += copysize; + + /* Update the file pointer */ + + filep->f_pos = fpos; + return buflen; +} + +/**************************************************************************** + * Name: cromfs_ioctl + ****************************************************************************/ + +static int cromfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + finfo("cmd: %d arg: %08lx\n", cmd, arg); + + /* No IOCTL commands yet supported */ + + return -ENOTTY; +} + +/**************************************************************************** + * Name: cromfs_dup + * + * Description: + * Duplicate open file data in the new file structure. + * + ****************************************************************************/ + +static int cromfs_dup(FAR const struct file *oldp, FAR struct file *newp) +{ + FAR struct cromfs_volume_s *fs; + FAR struct cromfs_file_s *oldff; + FAR struct cromfs_file_s *newff; + + finfo("Dup %p->%p\n", oldp, newp); + DEBUGASSERT(oldp->f_priv != NULL && oldp->f_inode != NULL && + newp->f_priv == NULL && newp->f_inode != NULL); + + /* Recover our private data from the struct file instance */ + + fs = (FAR struct cromfs_volume_s *)oldp->f_inode->i_private; + DEBUGASSERT(fs != NULL); + + /* Get the open file instance from the file structure */ + + oldff = oldp->f_priv; + DEBUGASSERT(oldff->ff_node != NULL && oldff->ff_buffer != NULL); + + /* Allocate and initialize an new open file instance referring to the + * same node. + */ + + newff = (FAR struct cromfs_file_s *)kmm_zalloc(sizeof(struct cromfs_file_s)); + if (newff == NULL) + { + return -ENOMEM; + } + + /* Create a file buffer to support partial sector accesses */ + + newff->ff_buffer = (FAR uint8_t *)kmm_malloc(fs->cv_bsize); + if (newff->ff_buffer == NULL) + { + kmm_free(newff); + return -ENOMEM; + } + + /* Save the node in the open file instance */ + + newff->ff_node = oldff->ff_node; + + /* Copy the index from the old to the new file structure */ + + newp->f_priv = newff; + return OK; +} + +/**************************************************************************** + * Name: cromfs_fstat + * + * Description: + * Obtain information about an open file associated with the file + * descriptor 'fd', and will write it to the area pointed to by 'buf'. + * + ****************************************************************************/ + +static int cromfs_fstat(FAR const struct file *filep, FAR struct stat *buf) +{ + FAR struct inode *inode; + FAR struct cromfs_volume_s *fs; + FAR struct cromfs_file_s *ff; + size_t fsize; + size_t bsize; + + /* Sanity checks */ + + DEBUGASSERT(filep->f_priv == NULL && filep->f_inode != NULL); + + /* Get the mountpoint inode reference from the file structure and the + * volume private data from the inode structure + */ + + ff = filep->f_priv; + DEBUGASSERT(ff->ff_node != NULL && ff->ff_buffer != NULL); + + inode = filep->f_inode; + fs = inode->i_private; + + /* Return the stat info */ + + fsize = ff->ff_node->cn_size; + bsize = fs->cv_bsize; + + buf->st_mode = ff->ff_node->cn_mode; + buf->st_size = fsize; + buf->st_blksize = bsize; + buf->st_blocks = (fsize + (bsize - 1)) / bsize; + buf->st_atime = 0; + buf->st_mtime = 0; + buf->st_ctime = 0; + + return OK; +} + +/**************************************************************************** + * Name: cromfs_opendir + * + * Description: + * Open a directory for read access + * + ****************************************************************************/ + +static int cromfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, + FAR struct fs_dirent_s *dir) +{ + FAR const struct cromfs_volume_s *fs; + FAR const struct cromfs_node_s *node; + int ret; + + finfo("relpath: \"%s\"\n", relpath ? relpath : "NULL"); + + /* Sanity checks */ + + DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); + + /* Recover our private data from the inode instance */ + + fs = mountpt->i_private; + + /* Locate the node for this relative path */ + + node = NULL; + ret = cromfs_findnode(fs, &node, relpath); + if (ret < 0) + { + /* Nothing exists at that relative path (or a really bad error occurred) */ + + return ret; + } + + DEBUGASSERT(node != NULL); + + /* Verify that the node is a directory */ + + if (!S_ISDIR(node->cn_mode)) + { + return -ENOTDIR; + } + + /* Set the start node and next node to the first entry in the directory */ + + dir->u.cromfs.cr_firstoffset = (size_t)cromfs_addr2offset(fs, node); + dir->u.cromfs.cr_curroffset = dir->u.cromfs.cr_firstoffset; + return OK; +} + +/**************************************************************************** + * Name: cromfs_readdir + * + * Description: Read the next directory entry + * + ****************************************************************************/ + +static int cromfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir) +{ + FAR const struct cromfs_volume_s *fs; + FAR const struct cromfs_node_s *node; + FAR char *name; + size_t offset; + + finfo("mountpt: %p dir: %p\n", mountpt, dir); + + /* Sanity checks */ + + DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); + + /* Recover our private data from the inode instance */ + + fs = mountpt->i_private; + + /* Have we reached the end of the directory */ + + offset = dir->u.cromfs.cr_curroffset; + if (offset == 0) + { + /* We signal the end of the directory by returning the + * special error -ENOENT + */ + + finfo("Entry %d: End of directory\n", offset); + return -ENOENT; + } + + /* Convert the offset to a node address (assuming that everything is in- + * memory) + */ + + node = (FAR const struct cromfs_node_s *)cromfs_offset2addr(fs, offset); + + /* Save the filename and file type */ + + name = (FAR char *)cromfs_offset2addr(fs, node->cn_name); + finfo("Entry %lu: \"%s\"\n", (unsigned long)offset, name); + strncpy(dir->fd_dir.d_name, name, NAME_MAX + 1); + + switch (node->cn_mode & s_IFTGT) + { + case S_IFDIR: /* Directory */ + dir->fd_dir.d_type = DTYPE_DIRECTORY; + break; + + case S_IFREG: /* Regular file */ + dir->fd_dir.d_type = DTYPE_FILE; + break; + + case S_IFIFO: /* FIFO */ + case S_IFCHR: /* Character driver */ + case S_IFBLK: /* Block driver */ + /* case S_IFSOCK: Socket */ + case S_IFMQ: /* Message queue */ + case S_IFSEM: /* Semaphore */ + case S_IFSHM: /* Shared memory */ + default: + DEBUGPANIC(); + dir->fd_dir.d_type = DTYPE_UNKNOWN; + break; + } + + /* Set up the next directory entry offset. NOTE that we could use the + * standard f_pos instead of our own private fb_index. + */ + + dir->u.cromfs.cr_curroffset = node->cn_peer; + return OK; +} + +/**************************************************************************** + * Name: cromfs_rewindir + * + * Description: Reset directory read to the first entry + * + ****************************************************************************/ + +static int cromfs_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir) +{ + finfo("Entry\n"); + + dir->u.cromfs.cr_curroffset = dir->u.cromfs.cr_firstoffset; + return OK; +} + +/**************************************************************************** + * Name: cromfs_bind + * + * Description: This implements a portion of the mount operation. This + * function allocates and initializes the mountpoint private data and + * binds the blockdriver inode to the filesystem private data. The final + * binding of the private data (containing the blockdriver) to the + * mountpoint is performed by mount(). + * + ****************************************************************************/ + +static int cromfs_bind(FAR struct inode *blkdriver, const void *data, + void **handle) +{ + finfo("blkdriver: %p data: %p handle: %p\n", blkdriver, data, handle); + DEBUGASSERT(blkdriver == NULL && handle != NULL); + + /* Return the new file system handle */ + + *handle = (FAR void *)&g_cromfs_image; + return OK; +} + +/**************************************************************************** + * Name: cromfs_unbind + * + * Description: This implements the filesystem portion of the umount + * operation. + * + ****************************************************************************/ + +static int cromfs_unbind(FAR void *handle, FAR struct inode **blkdriver, + unsigned int flags) +{ + finfo("handle: %p blkdriver: %p flags: %02x\n", + handle, blkdriver, flags); + return OK; +} + +/**************************************************************************** + * Name: cromfs_statfs + * + * Description: Return filesystem statistics + * + ****************************************************************************/ + +static int cromfs_statfs(struct inode *mountpt, struct statfs *buf) +{ + FAR struct cromfs_volume_s *fs; + + finfo("mountpt: %p buf: %p\n", mountpt, buf); + + /* Sanity checks */ + + DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); + + /* Recover our private data from the inode instance */ + + fs = mountpt->i_private; + + /* Fill in the statfs info. */ + + memset(buf, 0, sizeof(struct statfs)); + buf->f_type = CROMFS_MAGIC; + buf->f_namelen = NAME_MAX; + buf->f_bsize = fs->cv_bsize; + buf->f_blocks = fs->cv_nblocks; + buf->f_files = fs->cv_nnodes; + return OK; +} + +/**************************************************************************** + * Name: cromfs_stat + * + * Description: Return information about a file or directory + * + ****************************************************************************/ + +static int cromfs_stat(FAR struct inode *mountpt, FAR const char *relpath, + FAR struct stat *buf) +{ + FAR const struct cromfs_volume_s *fs; + FAR const struct cromfs_node_s *node; + int ret; + + finfo("mountptr: %p relpath: %s buf: %p\n", mountpt, relpath, buf); + + /* Sanity checks */ + + DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL && buf != NULL ); + memset(buf, 0, sizeof(struct stat)); + + /* Recover our private data from the inode instance */ + + fs = mountpt->i_private; + + /* Locate the node for this relative path */ + + node = NULL; + ret = cromfs_findnode(fs, &node, relpath); + if (ret >= 0) + { + DEBUGASSERT(node != NULL); + + /* Return the struct stat info associate with this node */ + + buf->st_mode = node->cn_mode; + buf->st_size = node->cn_size; + buf->st_blksize = fs->cv_bsize; + buf->st_blocks = (node->cn_size + (fs->cv_bsize - 1)) / fs->cv_bsize; + ret = OK; + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_CROMFS */ diff --git a/fs/mount/fs_mount.c b/fs/mount/fs_mount.c index 404b700fce..e6a7d1665f 100644 --- a/fs/mount/fs_mount.c +++ b/fs/mount/fs_mount.c @@ -79,7 +79,8 @@ #if defined(CONFIG_FS_NXFFS) || defined(CONFIG_FS_BINFS) || \ defined(CONFIG_FS_PROCFS) || defined(CONFIG_NFS) || \ - defined(CONFIG_FS_TMPFS) || defined(CONFIG_FS_USERFS) + defined(CONFIG_FS_TMPFS) || defined(CONFIG_FS_USERFS) || \ + defined(CONFIG_FS_CROMFS) # define NONBDFS_SUPPORT #endif @@ -145,6 +146,9 @@ extern const struct mountpt_operations userfs_operations; #ifdef CONFIG_FS_HOSTFS extern const struct mountpt_operations hostfs_operations; #endif +#ifdef CONFIG_FS_CROMFS +extern const struct mountpt_operations cromfs_operations; +#endif static const struct fsmap_t g_nonbdfsmap[] = { @@ -168,6 +172,9 @@ static const struct fsmap_t g_nonbdfsmap[] = #endif #ifdef CONFIG_FS_HOSTFS { "hostfs", &hostfs_operations }, +#endif +#ifdef CONFIG_FS_CROMFS + { "cromfs", &cromfs_operations }, #endif { NULL, NULL }, }; diff --git a/include/nuttx/fs/dirent.h b/include/nuttx/fs/dirent.h index ae16f89342..c29d5de50d 100644 --- a/include/nuttx/fs/dirent.h +++ b/include/nuttx/fs/dirent.h @@ -1,7 +1,8 @@ /**************************************************************************** * include/nuttx/fs/dirent.h * - * Copyright (C) 2007, 2009, 2011-2013, 2015 Gregory Nutt. All rights reserved. + * Copyright (C) 2007, 2009, 2011-2013, 2015, 20189 Gregory Nutt. All + * rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -102,16 +103,26 @@ struct fs_romfsdir_s }; #endif /* CONFIG_FS_ROMFS */ +#ifdef CONFIG_FS_CROMFS +/* For CROMFS, we need to return the next compressed node to be examined. */ + +struct fs_cromfsdir_s +{ + off_t cr_firstoffset; /* Offset to the first entry in the directory */ + off_t cr_curroffset; /* Current offset into the directory contents */ +}; +#endif /* CONFIG_FS_ROMFS */ + #ifdef CONFIG_FS_TMPFS /* For TMPFS, we need the directory object and an index into the directory * entries. */ -struct tmpfs_directory_s; /* Forward reference */ +struct tmpfs_directory_s; /* Forward reference */ struct fs_tmpfsdir_s { FAR struct tmpfs_directory_s *tf_tdo; /* Directory being enumerated */ - unsigned int tf_index; /* Directory index */ + unsigned int tf_index; /* Directory index */ }; #endif /* CONFIG_FS_TMPFS */ @@ -244,6 +255,9 @@ struct fs_dirent_s #ifdef CONFIG_FS_ROMFS struct fs_romfsdir_s romfs; #endif +#ifdef CONFIG_FS_ROMFS + struct fs_cromfsdir_s cromfs; +#endif #ifdef CONFIG_FS_TMPFS struct fs_tmpfsdir_s tmpfs; #endif diff --git a/include/sys/stat.h b/include/sys/stat.h index 5d2d3f7fcc..d6c4abb81e 100644 --- a/include/sys/stat.h +++ b/include/sys/stat.h @@ -122,9 +122,9 @@ struct stat { /* Required, standard fields */ - mode_t st_mode; /* File type, atributes, and access mode bits */ + mode_t st_mode; /* File type, attributes, and access mode bits */ off_t st_size; /* Size of file/directory, in bytes */ - blksize_t st_blksize; /* Blocksize used for filesystem I/O */ + blksize_t st_blksize; /* Block size used for filesystem I/O */ blkcnt_t st_blocks; /* Number of blocks allocated */ time_t st_atime; /* Time of last access */ time_t st_mtime; /* Time of last modification */ diff --git a/include/sys/statfs.h b/include/sys/statfs.h index e33226e972..e59c8bb1ef 100644 --- a/include/sys/statfs.h +++ b/include/sys/statfs.h @@ -105,6 +105,7 @@ #define UNIONFS_MAGIC 0x53464e55 #define HOSTFS_MAGIC 0x54534f48 #define USERFS_MAGIC 0x52455355 +#define CROMFS_MAGIC 0x4d4f5243 /**************************************************************************** * Type Definitions