diff --git a/fs/inode/inode.h b/fs/inode/inode.h index f5f8727be6..3e79ad1857 100644 --- a/fs/inode/inode.h +++ b/fs/inode/inode.h @@ -418,6 +418,28 @@ int foreach_inode(foreach_inode_t handler, FAR void *arg); int dir_allocate(FAR struct file *filep, FAR const char *relpath); +/**************************************************************************** + * Name: pseudofile_create + * + * Description: + * Create the pseudo-file with specified path and mode, and alloc inode + * of this pseudo-file. + * + ****************************************************************************/ + +int pseudofile_create(FAR struct inode **node, FAR const char *path, + mode_t mode); + +/**************************************************************************** + * Name: inode_is_pseudofile + * + * Description: + * Check inode whether is a pseudo file. + * + ****************************************************************************/ + +bool inode_is_pseudofile(FAR struct inode *inode); + #undef EXTERN #if defined(__cplusplus) } diff --git a/fs/vfs/Make.defs b/fs/vfs/Make.defs index 92bf939c98..3a2b31efea 100644 --- a/fs/vfs/Make.defs +++ b/fs/vfs/Make.defs @@ -25,7 +25,7 @@ CSRCS += fs_fchstat.c fs_fstat.c fs_fstatfs.c fs_ioctl.c fs_lseek.c CSRCS += fs_mkdir.c fs_open.c fs_poll.c fs_pread.c fs_pwrite.c fs_read.c CSRCS += fs_rename.c fs_rmdir.c fs_select.c fs_sendfile.c fs_stat.c CSRCS += fs_statfs.c fs_unlink.c fs_write.c fs_dir.c fs_fsync.c -CSRCS += fs_truncate.c +CSRCS += fs_truncate.c fs_pseudofile.c # Certain interfaces are not available if there is no mountpoint support diff --git a/fs/vfs/fs_open.c b/fs/vfs/fs_open.c index b0ca901666..966e35e2c3 100644 --- a/fs/vfs/fs_open.c +++ b/fs/vfs/fs_open.c @@ -75,7 +75,7 @@ static int file_vopen(FAR struct file *filep, FAR const char *path, { struct inode_search_s desc; FAR struct inode *inode; -#ifndef CONFIG_DISABLE_MOUNTPOINT +#if !defined(CONFIG_DISABLE_MOUNTPOINT) || defined(CONFIG_PSEUDOFS_FILE) mode_t mode = 0666; #endif int ret; @@ -104,12 +104,22 @@ static int file_vopen(FAR struct file *filep, FAR const char *path, ret = inode_find(&desc); if (ret < 0) { - /* "O_CREAT is not set and the named file does not exist. Or, a - * directory component in pathname does not exist or is a dangling - * symbolic link." - */ +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + if ((oflags & O_CREAT) != 0) + { + ret = pseudofile_create(&desc.node, path, mode); + } +#endif - goto errout_with_search; + if (ret < 0) + { + /* "O_CREAT is not set and the named file does not exist. Or, a + * directory component in pathname does not exist or is a dangling + * symbolic link." + */ + + goto errout_with_search; + } } /* Get the search results */ diff --git a/fs/vfs/fs_pseudofile.c b/fs/vfs/fs_pseudofile.c new file mode 100644 index 0000000000..3e2dc23f92 --- /dev/null +++ b/fs/vfs/fs_pseudofile.c @@ -0,0 +1,454 @@ +/**************************************************************************** + * fs/vfs/fs_pseudofile.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "inode/inode.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct fs_pseudofile_s +{ + mutex_t lock; + uint8_t crefs; +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + bool unlinked; +#endif + FAR char *content; +}; + +/**************************************************************************** + * Private Functions Prototypes + ****************************************************************************/ + +static int pseudofile_open(FAR struct file *filep); +static int pseudofile_close(FAR struct file *filep); +static ssize_t pseudofile_write(FAR struct file *filep, + FAR const char *buffer, size_t buflen); +static ssize_t pseudofile_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static off_t pseudofile_seek(FAR struct file *filep, off_t offset, + int whence); +static int pseudofile_mmap(FAR struct file *filep, + FAR struct mm_map_entry_s *map); +static int pseudofile_truncate(FAR struct file *filep, off_t length); +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS +static int pseudofile_unlink(FAR struct inode *inode); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_pseudofile_ops = +{ + pseudofile_open, /* open */ + pseudofile_close, /* close */ + pseudofile_read, /* read */ + pseudofile_write, /* write */ + pseudofile_seek, /* seek */ + NULL, /* ioctl */ + pseudofile_mmap, /* mmap */ + pseudofile_truncate, /* truncate */ + NULL, /* poll */ +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + pseudofile_unlink, /* unlink */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int pseudofile_open(FAR struct file *filep) +{ + FAR struct inode *node = filep->f_inode; + FAR struct fs_pseudofile_s *pf = node->i_private; + int ret; + + ret = nxmutex_lock(&pf->lock); + if (ret < 0) + { + return ret; + } + + if (pf->crefs >= 255) + { + ret = -EMFILE; + } + else + { + pf->crefs += 1; + ret = OK; + } + +#ifdef CONFIG_PSEUDOFS_ATTRIBUTES + node->i_atime.tv_sec = time(NULL); +#endif + nxmutex_unlock(&pf->lock); + return ret; +} + +static void pseudofile_remove(FAR struct fs_pseudofile_s *pf) +{ + nxmutex_unlock(&pf->lock); + nxmutex_destroy(&pf->lock); + kmm_free(pf->content); + kmm_free(pf); +} + +static int pseudofile_close(FAR struct file *filep) +{ + FAR struct inode *node = filep->f_inode; + FAR struct fs_pseudofile_s *pf = node->i_private; + int ret; + + ret = nxmutex_lock(&pf->lock); + if (ret < 0) + { + return ret; + } + + pf->crefs--; +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + if (pf->crefs <= 0 && pf->unlinked) +#else + if (pf->crefs <= 0) +#endif + { + pseudofile_remove(pf); + return OK; + } + + nxmutex_unlock(&pf->lock); + return OK; +} + +static int pseudofile_expand(FAR struct inode *node, + size_t size) +{ + FAR struct fs_pseudofile_s *pf = node->i_private; + FAR void *tmp; + + if (pf->content && kmm_malloc_size(pf->content) >= size) + { + node->i_size = size; + return 0; + } + + tmp = kmm_realloc(pf->content, 1 << LOG2_CEIL(size)); + if (tmp == NULL) + { + return -ENOMEM; + } + + pf->content = tmp; + node->i_size = size; + return 0; +} + +static ssize_t pseudofile_write(FAR struct file *filep, + FAR const char *buffer, size_t buflen) +{ + FAR struct inode *node = filep->f_inode; + FAR struct fs_pseudofile_s *pf = node->i_private; + int ret; + + ret = nxmutex_lock(&pf->lock); + if (ret < 0) + { + return ret; + } + + if (filep->f_oflags & O_APPEND) + { + ret = pseudofile_expand(node, node->i_size + buflen); + if (ret < 0) + { + nxmutex_unlock(&pf->lock); + return ret; + } + + filep->f_pos = node->i_size - buflen; + } + else + { + ret = pseudofile_expand(node, filep->f_pos + buflen); + if (ret < 0) + { + nxmutex_unlock(&pf->lock); + return ret; + } + } + + memcpy(pf->content + filep->f_pos, buffer, buflen); + filep->f_pos += buflen; +#ifdef CONFIG_PSEUDOFS_ATTRIBUTES + node->i_mtime.tv_sec = time(NULL); +#endif + + nxmutex_unlock(&pf->lock); + return buflen; +} + +static ssize_t pseudofile_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct inode *node = filep->f_inode; + FAR struct fs_pseudofile_s *pf = node->i_private; + int ret; + + if (buffer == NULL || node->i_size < filep->f_pos) + { + return -EINVAL; + } + + ret = nxmutex_lock(&pf->lock); + if (ret < 0) + { + return ret; + } + + buflen = MIN(node->i_size - filep->f_pos, buflen); + memcpy(buffer, pf->content + filep->f_pos, buflen); + filep->f_pos += buflen; + +#ifdef CONFIG_PSEUDOFS_ATTRIBUTES + node->i_atime.tv_sec = time(NULL); +#endif + nxmutex_unlock(&pf->lock); + + return buflen; +} + +static off_t pseudofile_seek(FAR struct file *filep, off_t offset, + int whence) +{ + FAR struct inode *node = filep->f_inode; + FAR struct fs_pseudofile_s *pf = node->i_private; + int ret; + + ret = nxmutex_lock(&pf->lock); + if (ret < 0) + { + return ret; + } + + /* Map the offset according to the whence option */ + + switch (whence) + { + case SEEK_SET: + break; + + case SEEK_CUR: + offset += filep->f_pos; + break; + + case SEEK_END: + offset += node->i_size; + break; + + default: + nxmutex_unlock(&pf->lock); + return -EINVAL; + } + + if (offset < 0) + { + nxmutex_unlock(&pf->lock); + return -EINVAL; + } + + filep->f_pos = offset; +#ifdef CONFIG_PSEUDOFS_ATTRIBUTES + node->i_atime.tv_sec = time(NULL); +#endif + nxmutex_unlock(&pf->lock); + return offset; +} + +static int pseudofile_mmap(FAR struct file *filep, + FAR struct mm_map_entry_s *map) +{ + FAR struct inode *node = filep->f_inode; + FAR struct fs_pseudofile_s *pf = node->i_private; + + if (map->offset >= 0 && map->offset < node->i_size && + map->length != 0 && map->offset + map->length <= node->i_size) + { + map->vaddr = pf->content + map->offset; + return OK; + } + + return -EINVAL; +} + +static int pseudofile_truncate(FAR struct file *filep, off_t length) +{ + FAR struct inode *node = filep->f_inode; + FAR struct fs_pseudofile_s *pf = node->i_private; + int ret; + + ret = nxmutex_lock(&pf->lock); + if (ret < 0) + { + return ret; + } + + if (length < node->i_size) + { + FAR void *tmp; + + tmp = kmm_realloc(pf->content, length); + if (tmp == NULL) + { + ret = -ENOMEM; + goto out; + } + + pf->content = tmp; + node->i_size = length; + } + else + { + ret = pseudofile_expand(node, length); + if (ret < 0) + { + goto out; + } + + memset(pf->content + node->i_size, 0, length - node->i_size); + } + +#ifdef CONFIG_PSEUDOFS_ATTRIBUTES + node->i_mtime.tv_sec = time(NULL); +#endif + +out: + nxmutex_unlock(&pf->lock); + return ret; +} + +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS +static int pseudofile_unlink(FAR struct inode *node) +{ + FAR struct fs_pseudofile_s *pf = node->i_private; + int ret; + + ret = nxmutex_lock(&pf->lock); + if (ret < 0) + { + return ret; + } + + if (pf->crefs <= 0) + { + pseudofile_remove(pf); + return OK; + } + + pf->unlinked = true; + nxmutex_unlock(&pf->lock); + return OK; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pseudofile_create + * + * Description: + * Create the pseudo-file with specified path and mode, and alloc inode + * of this pseudo-file. + * + ****************************************************************************/ + +int pseudofile_create(FAR struct inode **node, FAR const char *path, + mode_t mode) +{ + FAR struct fs_pseudofile_s *pf; + int ret; + + if (node == NULL || path == NULL) + { + return -EINVAL; + } + + pf = kmm_zalloc(sizeof(struct fs_pseudofile_s)); + if (pf == NULL) + { + return -ENOMEM; + } + + nxmutex_init(&pf->lock); + + ret = inode_lock(); + if (ret < 0) + { + nxmutex_destroy(&pf->lock); + kmm_free(pf); + return ret; + } + + inode_reserve(path, mode, node); + (*node)->i_crefs = 0; + (*node)->i_flags = 1; + (*node)->u.i_ops = &g_pseudofile_ops; + (*node)->i_private = pf; + + inode_unlock(); + + return 0; +} + +/**************************************************************************** + * Name: inode_is_pseudofile + * + * Description: + * Check inode whether is a pseudo file. + * + ****************************************************************************/ + +bool inode_is_pseudofile(FAR struct inode *inode) +{ + return inode != NULL && inode->u.i_ops == &g_pseudofile_ops; +} diff --git a/fs/vfs/fs_stat.c b/fs/vfs/fs_stat.c index aaa3dae383..ad65b98227 100644 --- a/fs/vfs/fs_stat.c +++ b/fs/vfs/fs_stat.c @@ -416,7 +416,15 @@ int inode_stat(FAR struct inode *inode, FAR struct stat *buf, int resolve) { /* What is it if it also has child inodes? */ - buf->st_mode |= S_IFCHR; + buf->st_size = inode->i_size; + if (inode_is_pseudofile(inode)) + { + buf->st_mode |= S_IFREG; + } + else + { + buf->st_mode |= S_IFCHR; + } } } else diff --git a/include/nuttx/fs/fs.h b/include/nuttx/fs/fs.h index a9012eb1a2..ca965bfba0 100644 --- a/include/nuttx/fs/fs.h +++ b/include/nuttx/fs/fs.h @@ -405,6 +405,7 @@ struct inode uint16_t i_flags; /* Flags for inode */ union inode_ops_u u; /* Inode operations */ ino_t i_ino; /* Inode serial number */ + size_t i_size; /* The size of per inode driver */ #ifdef CONFIG_PSEUDOFS_ATTRIBUTES mode_t i_mode; /* Access mode flags */ uid_t i_owner; /* Owner */