/**************************************************************************** * fs/romfs/fs_romfs.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 #include #include #include #include "fs_romfs.h" #include "fs_heap.h" /**************************************************************************** * Private Types ****************************************************************************/ /* This structure represents one entry node in the romfs file system */ struct romfs_dir_s { struct fs_dirent_s base; /* Vfs directory structure */ #ifdef CONFIG_FS_ROMFS_CACHE_NODE FAR struct romfs_nodeinfo_s **firstnode; /* The address of first node in the directory */ FAR struct romfs_nodeinfo_s **currnode; /* The address of current node into the directory */ #else off_t firstoffset; /* Offset to the first entry in the directory */ off_t curroffset; /* Current offset into the directory contents */ #endif }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static int romfs_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode); static int romfs_close(FAR struct file *filep); static ssize_t romfs_read(FAR struct file *filep, FAR char *buffer, size_t buflen); static off_t romfs_seek(FAR struct file *filep, off_t offset, int whence); static int romfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg); static int romfs_mmap(FAR struct file *filep, FAR struct mm_map_entry_s *map); static int romfs_dup(FAR const struct file *oldp, FAR struct file *newp); static int romfs_fstat(FAR const struct file *filep, FAR struct stat *buf); static int romfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s **dir); static int romfs_closedir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir); static int romfs_readdir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir, FAR struct dirent *entry); static int romfs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir); static int romfs_bind(FAR struct inode *blkdriver, FAR const void *data, FAR void **handle); static int romfs_unbind(FAR void *handle, FAR struct inode **blkdriver, unsigned int flags); static int romfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf); static int romfs_stat_common(uint8_t type, uint32_t size, uint16_t sectorsize, FAR struct stat *buf); static int romfs_stat(FAR struct inode *mountpt, FAR const char *relpath, FAR struct stat *buf); /**************************************************************************** * Public Data ****************************************************************************/ /* See fs_mount.c -- this structure is explicitly externed there. * We use the old-fashioned kind of initializers so that this will compile * with any compiler. */ const struct mountpt_operations g_romfs_operations = { romfs_open, /* open */ romfs_close, /* close */ romfs_read, /* read */ NULL, /* write */ romfs_seek, /* seek */ romfs_ioctl, /* ioctl */ romfs_mmap, /* mmap */ NULL, /* truncate */ NULL, /* poll */ NULL, /* sync */ romfs_dup, /* dup */ romfs_fstat, /* fstat */ NULL, /* fchstat */ romfs_opendir, /* opendir */ romfs_closedir, /* closedir */ romfs_readdir, /* readdir */ romfs_rewinddir, /* rewinddir */ romfs_bind, /* bind */ romfs_unbind, /* unbind */ romfs_statfs, /* statfs */ NULL, /* unlink */ NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* rename */ romfs_stat, /* stat */ NULL /* chstat */ }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: romfs_open ****************************************************************************/ static int romfs_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode) { struct romfs_nodeinfo_s nodeinfo; FAR struct romfs_mountpt_s *rm; FAR struct romfs_file_s *rf; size_t len; int ret; finfo("Open '%s'\n", relpath); /* Sanity checks */ DEBUGASSERT(filep->f_priv == NULL); /* Get mountpoint private data from the inode reference from the file * structure */ rm = filep->f_inode->i_private; DEBUGASSERT(rm != NULL); /* Check if the mount is still healthy */ ret = nxrmutex_lock(&rm->rm_lock); if (ret < 0) { return ret; } ret = romfs_checkmount(rm); if (ret != OK) { ferr("ERROR: romfs_checkmount failed: %d\n", ret); goto errout_with_lock; } /* ROMFS 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"); ret = -EACCES; goto errout_with_lock; } /* Locate the directory entry for this path */ ret = romfs_finddirentry(rm, &nodeinfo, relpath); if (ret < 0) { ferr("ERROR: Failed to find directory directory entry for '%s': %d\n", relpath, ret); goto errout_with_lock; } /* The full path exists -- but is the final component a file * or a directory? Or some other Unix file type that is not * appropriate in this context. * * REVISIT: This logic should follow hard/soft link file * types. At present, it returns the ENXIO. */ if (IS_DIRECTORY(nodeinfo.rn_next)) { /* It is a directory */ ret = -EISDIR; ferr("ERROR: '%s' is a directory\n", relpath); goto errout_with_lock; } else if (!IS_FILE(nodeinfo.rn_next)) { /* ENXIO indicates "The named file is a character special or * block special file, and the device associated with this * special file does not exist." * * Here we also return ENXIO if the file is not a directory * or a regular file. */ ret = -ENXIO; ferr("ERROR: '%s' is a special file\n", relpath); goto errout_with_lock; } /* Create an instance of the file private data to describe the opened * file. */ len = strlen(relpath); rf = fs_heap_zalloc(sizeof(struct romfs_file_s) + len); if (!rf) { ferr("ERROR: Failed to allocate private data\n"); ret = -ENOMEM; goto errout_with_lock; } /* Initialize the file private data (only need to initialize * non-zero elements) */ rf->rf_size = nodeinfo.rn_size; rf->rf_type = (uint8_t)(nodeinfo.rn_next & RFNEXT_ALLMODEMASK); strlcpy(rf->rf_path, relpath, len + 1); /* Get the start of the file data */ ret = romfs_datastart(rm, &nodeinfo, &rf->rf_startoffset); if (ret < 0) { ferr("ERROR: Failed to locate start of file data: %d\n", ret); fs_heap_free(rf); goto errout_with_lock; } /* Configure buffering to support access to this file */ ret = romfs_fileconfigure(rm, rf); if (ret < 0) { ferr("ERROR: Failed configure buffering: %d\n", ret); fs_heap_free(rf); goto errout_with_lock; } /* Attach the private date to the struct file instance */ filep->f_priv = rf; rm->rm_refs++; errout_with_lock: nxrmutex_unlock(&rm->rm_lock); return ret; } /**************************************************************************** * Name: romfs_close ****************************************************************************/ static int romfs_close(FAR struct file *filep) { FAR struct romfs_mountpt_s *rm; FAR struct romfs_file_s *rf; int ret; finfo("Closing\n"); /* Sanity checks */ DEBUGASSERT(filep->f_priv != NULL); /* Recover our private data from the struct file instance */ rf = filep->f_priv; rm = filep->f_inode->i_private; DEBUGASSERT(rm != NULL); ret = nxrmutex_lock(&rm->rm_lock); if (ret < 0) { return ret; } rm->rm_refs--; nxrmutex_unlock(&rm->rm_lock); /* Do not check if the mount is healthy. We must support closing of * the file even when there is healthy mount. */ /* Deallocate the memory structures created when the open method * was called. * * Free the sector buffer that was used to manage partial sector * accesses. */ if (!rm->rm_xipbase && rf->rf_buffer) { fs_heap_free(rf->rf_buffer); } /* Then free the file structure itself. */ fs_heap_free(rf); filep->f_priv = NULL; return ret; } /**************************************************************************** * Name: romfs_read ****************************************************************************/ static ssize_t romfs_read(FAR struct file *filep, FAR char *buffer, size_t buflen) { FAR struct romfs_mountpt_s *rm; FAR struct romfs_file_s *rf; unsigned int bytesread; unsigned int readsize = 0; unsigned int nsectors; uint32_t offset; size_t bytesleft; off_t sector; FAR uint8_t *userbuffer = (FAR uint8_t *)buffer; int sectorndx; int ret; finfo("Read %zu bytes from offset %jd\n", buflen, (intmax_t)filep->f_pos); /* Sanity checks */ DEBUGASSERT(filep->f_priv != NULL); /* Recover our private data from the struct file instance */ rf = filep->f_priv; rm = filep->f_inode->i_private; DEBUGASSERT(rm != NULL); /* Make sure that the mount is still healthy */ ret = nxrmutex_lock(&rm->rm_lock); if (ret < 0) { return (ssize_t)ret; } ret = romfs_checkmount(rm); if (ret != OK) { ferr("ERROR: romfs_checkmount failed: %d\n", ret); goto errout_with_lock; } /* Get the number of bytes left in the file */ bytesleft = rf->rf_size - filep->f_pos; /* Truncate read count so that it does not exceed the number * of bytes left in the file. */ if (buflen > bytesleft) { buflen = bytesleft; } /* Loop until either (1) all data has been transferred, or (2) an * error occurs. */ while (buflen > 0) { /* Get the first sector and index to read from. */ offset = rf->rf_startoffset + filep->f_pos; sector = SEC_NSECTORS(rm, offset); sectorndx = offset & SEC_NDXMASK(rm); /* Check if the user has provided a buffer large enough to * hold one or more complete sectors -AND- the read is * aligned to a sector boundary. */ nsectors = SEC_NSECTORS(rm, buflen); if (nsectors >= rf->rf_ncachesector && sectorndx == 0) { /* Read maximum contiguous sectors directly to the user's * buffer without using our tiny read buffer. */ /* Read all of the sectors directly into user memory */ finfo("Read %d sectors starting with %jd\n", nsectors, (intmax_t)sector); ret = romfs_hwread(rm, userbuffer, sector, nsectors); if (ret < 0) { ferr("ERROR: romfs_hwread failed: %d\n", ret); goto errout_with_lock; } bytesread = nsectors * rm->rm_hwsectorsize; } else { /* We are reading a partial sector. First, read the whole sector * into the file data buffer. This is a caching buffer so if * it is already there then all is well. */ finfo("Read sector %jd\n", (intmax_t)sector); ret = romfs_filecacheread(rm, rf, sector); if (ret < 0) { ferr("ERROR: romfs_filecacheread failed: %d\n", ret); goto errout_with_lock; } /* Copy the partial sector into the user buffer */ bytesread = (rf->rf_cachesector + rf->rf_ncachesector - sector) * rm->rm_hwsectorsize - sectorndx; sectorndx = rf->rf_ncachesector * rm->rm_hwsectorsize - bytesread; if (bytesread > buflen) { /* We will not read to the end of the buffer */ bytesread = buflen; } finfo("Return %d bytes from sector offset %d\n", bytesread, sectorndx); memcpy(userbuffer, &rf->rf_buffer[sectorndx], bytesread); } /* Set up for the next sector read */ userbuffer += bytesread; filep->f_pos += bytesread; readsize += bytesread; buflen -= bytesread; } errout_with_lock: nxrmutex_unlock(&rm->rm_lock); return ret < 0 ? ret : readsize; } /**************************************************************************** * Name: romfs_seek ****************************************************************************/ static off_t romfs_seek(FAR struct file *filep, off_t offset, int whence) { FAR struct romfs_mountpt_s *rm; FAR struct romfs_file_s *rf; off_t position; int ret; finfo("Seek to offset: %jd whence: %d\n", (intmax_t)offset, whence); /* Sanity checks */ DEBUGASSERT(filep->f_priv != NULL); /* Recover our private data from the struct file instance */ rf = filep->f_priv; rm = filep->f_inode->i_private; DEBUGASSERT(rm != NULL); /* Map the offset according to the whence option */ switch (whence) { case SEEK_SET: /* The offset is set to offset bytes. */ position = offset; break; case SEEK_CUR: /* The offset is set to its current location plus * offset bytes. */ position = offset + filep->f_pos; break; case SEEK_END: /* The offset is set to the size of the file plus * offset bytes. */ position = offset + rf->rf_size; break; default: ferr("ERROR: Whence is invalid: %d\n", whence); return -EINVAL; } /* Make sure that the mount is still healthy */ ret = nxrmutex_lock(&rm->rm_lock); if (ret < 0) { return (off_t)ret; } ret = romfs_checkmount(rm); if (ret != OK) { ferr("ERROR: romfs_checkmount failed: %d\n", ret); goto errout_with_lock; } /* Limit positions to the end of the file. */ if (position > rf->rf_size) { /* Otherwise, the position is limited to the file size */ position = rf->rf_size; } /* Set file position and return success */ filep->f_pos = position; finfo("New file position: %jd\n", (intmax_t)filep->f_pos); errout_with_lock: nxrmutex_unlock(&rm->rm_lock); return ret; } /**************************************************************************** * Name: romfs_ioctl ****************************************************************************/ static int romfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { FAR struct romfs_file_s *rf; finfo("cmd: %d arg: %08lx\n", cmd, arg); /* Sanity checks */ DEBUGASSERT(filep->f_priv != NULL); /* Recover our private data from the struct file instance */ rf = filep->f_priv; /* Only one ioctl command is supported */ if (cmd == FIOC_FILEPATH) { FAR char *ptr = (FAR char *)((uintptr_t)arg); inode_getpath(filep->f_inode, ptr, PATH_MAX); strlcat(ptr, rf->rf_path, PATH_MAX); return OK; } return -ENOTTY; } static int romfs_mmap(FAR struct file *filep, FAR struct mm_map_entry_s *map) { FAR struct romfs_mountpt_s *rm; FAR struct romfs_file_s *rf; /* Sanity checks */ DEBUGASSERT(filep->f_priv != NULL); /* Recover our private data from the struct file instance */ rf = filep->f_priv; rm = filep->f_inode->i_private; /* Return the address on the media corresponding to the start of * the file. */ if (rm->rm_xipbase && map->offset >= 0 && map->offset < rf->rf_size && map->length != 0 && map->offset + map->length <= rf->rf_size) { map->vaddr = rm->rm_xipbase + rf->rf_startoffset + map->offset; return OK; } return -ENOTTY; } /**************************************************************************** * Name: romfs_dup ****************************************************************************/ static int romfs_dup(FAR const struct file *oldp, FAR struct file *newp) { FAR struct romfs_mountpt_s *rm; FAR struct romfs_file_s *oldrf; FAR struct romfs_file_s *newrf; size_t len; int ret; finfo("Dup %p->%p\n", oldp, newp); /* Sanity checks */ DEBUGASSERT(oldp->f_priv != NULL && newp->f_priv == NULL && newp->f_inode != NULL); /* Get mountpoint private data from the inode reference from the file * structure */ rm = newp->f_inode->i_private; DEBUGASSERT(rm != NULL); /* Check if the mount is still healthy */ ret = nxrmutex_lock(&rm->rm_lock); if (ret < 0) { return ret; } ret = romfs_checkmount(rm); if (ret != OK) { ferr("ERROR: romfs_checkmount failed: %d\n", ret); goto errout_with_lock; } /* Recover the old private data from the old struct file instance */ oldrf = oldp->f_priv; /* Create an new instance of the file private data to describe the new * dup'ed file. */ len = strlen(oldrf->rf_path); newrf = fs_heap_malloc(sizeof(struct romfs_file_s) + len); if (!newrf) { ferr("ERROR: Failed to allocate private data\n"); ret = -ENOMEM; goto errout_with_lock; } /* Copy all file private data (except for the buffer) */ newrf->rf_startoffset = oldrf->rf_startoffset; newrf->rf_size = oldrf->rf_size; newrf->rf_type = oldrf->rf_type; strlcpy(newrf->rf_path, oldrf->rf_path, len + 1); /* Configure buffering to support access to this file */ ret = romfs_fileconfigure(rm, newrf); if (ret < 0) { fs_heap_free(newrf); ferr("ERROR: Failed configure buffering: %d\n", ret); goto errout_with_lock; } /* Attach the new private date to the new struct file instance */ newp->f_priv = newrf; rm->rm_refs++; errout_with_lock: nxrmutex_unlock(&rm->rm_lock); return ret; } /**************************************************************************** * Name: romfs_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 romfs_fstat(FAR const struct file *filep, FAR struct stat *buf) { FAR struct romfs_mountpt_s *rm; FAR struct romfs_file_s *rf; int ret; finfo("fstat\n"); /* Sanity checks */ DEBUGASSERT(filep->f_priv != NULL); /* Get mountpoint private data from the inode reference from the file * structure */ rf = filep->f_priv; rm = filep->f_inode->i_private; DEBUGASSERT(rm != NULL); /* Check if the mount is still healthy */ ret = nxrmutex_lock(&rm->rm_lock); if (ret < 0) { return ret; } ret = romfs_checkmount(rm); if (ret >= 0) { /* Return information about the directory entry */ ret = romfs_stat_common(rf->rf_type, rf->rf_size, rm->rm_hwsectorsize, buf); } nxrmutex_unlock(&rm->rm_lock); return ret; } /**************************************************************************** * Name: romfs_opendir * * Description: * Open a directory for read access * ****************************************************************************/ static int romfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s **dir) { FAR struct romfs_mountpt_s *rm; FAR struct romfs_dir_s *rdir; struct romfs_nodeinfo_s nodeinfo; int ret; finfo("relpath: '%s'\n", relpath); /* Sanity checks */ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); /* Recover our private data from the inode instance */ rm = mountpt->i_private; rdir = fs_heap_zalloc(sizeof(*rdir)); if (rdir == NULL) { return -ENOMEM; } /* Make sure that the mount is still healthy */ ret = nxrmutex_lock(&rm->rm_lock); if (ret < 0) { goto errout_with_rdir; } ret = romfs_checkmount(rm); if (ret != OK) { ferr("ERROR: romfs_checkmount failed: %d\n", ret); goto errout_with_lock; } /* Find the requested directory */ ret = romfs_finddirentry(rm, &nodeinfo, relpath); if (ret < 0) { ferr("ERROR: Failed to find directory '%s': %d\n", relpath, ret); goto errout_with_lock; } /* Verify that it is some kind of directory */ if (!IS_DIRECTORY(nodeinfo.rn_next)) { /* The entry is not a directory */ ferr("ERROR: '%s' is not a directory\n", relpath); ret = -ENOTDIR; goto errout_with_lock; } /* The entry is a directory */ #ifdef CONFIG_FS_ROMFS_CACHE_NODE rdir->firstnode = nodeinfo.rn_child; rdir->currnode = nodeinfo.rn_child; #else rdir->firstoffset = nodeinfo.rn_offset; rdir->curroffset = nodeinfo.rn_offset; #endif *dir = &rdir->base; nxrmutex_unlock(&rm->rm_lock); return OK; errout_with_lock: nxrmutex_unlock(&rm->rm_lock); errout_with_rdir: fs_heap_free(rdir); return ret; } /**************************************************************************** * Name: romfs_closedir * * Description: Close the directory * ****************************************************************************/ static int romfs_closedir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir) { DEBUGASSERT(dir); fs_heap_free(dir); return 0; } /**************************************************************************** * Name: romfs_readdir * * Description: Read the next directory entry * ****************************************************************************/ static int romfs_readdir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir, FAR struct dirent *entry) { FAR struct romfs_mountpt_s *rm; FAR struct romfs_dir_s *rdir; #ifndef CONFIG_FS_ROMFS_CACHE_NODE uint32_t linkoffset; uint32_t info; uint32_t size; #endif uint32_t next; int ret; finfo("Entry\n"); /* Sanity checks */ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); /* Recover our private data from the inode instance */ rm = mountpt->i_private; rdir = (FAR struct romfs_dir_s *)dir; /* Make sure that the mount is still healthy */ ret = nxrmutex_lock(&rm->rm_lock); if (ret < 0) { return ret; } ret = romfs_checkmount(rm); if (ret != OK) { ferr("ERROR: omfs_checkmount failed: %d\n", ret); goto errout_with_lock; } /* Loop, skipping over unsupported items in the file system */ for (; ; ) { /* Have we reached the end of the directory */ #ifdef CONFIG_FS_ROMFS_CACHE_NODE if (!rdir->currnode || !(*rdir->currnode)) #else if (!rdir->curroffset) #endif { /* We signal the end of the directory by returning the * special error -ENOENT */ finfo("End of directory\n"); ret = -ENOENT; goto errout_with_lock; } #ifdef CONFIG_FS_ROMFS_CACHE_NODE next = (*rdir->currnode)->rn_next; strlcpy(entry->d_name, (*rdir->currnode)->rn_name, sizeof(entry->d_name)); rdir->currnode++; #else /* Parse the directory entry */ ret = romfs_parsedirentry(rm, rdir->curroffset, &linkoffset, &next, &info, &size); if (ret < 0) { ferr("ERROR: romfs_parsedirentry failed: %d\n", ret); goto errout_with_lock; } /* Save the filename */ ret = romfs_parsefilename(rm, rdir->curroffset, entry->d_name); if (ret < 0) { ferr("ERROR: romfs_parsefilename failed: %d\n", ret); goto errout_with_lock; } /* Set up the next directory entry offset */ rdir->curroffset = next & RFNEXT_OFFSETMASK; #endif /* Check the file type */ if (IS_DIRECTORY(next)) { entry->d_type = DTYPE_DIRECTORY; break; } else if (IS_FILE(next)) { entry->d_type = DTYPE_FILE; break; } else if (IS_SOFTLINK(next)) { entry->d_type = DTYPE_LINK; break; } } errout_with_lock: nxrmutex_unlock(&rm->rm_lock); return ret; } /**************************************************************************** * Name: romfs_rewindir * * Description: Reset directory read to the first entry * ****************************************************************************/ static int romfs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir) { FAR struct romfs_mountpt_s *rm; FAR struct romfs_dir_s *rdir; int ret; finfo("Entry\n"); /* Sanity checks */ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); /* Recover our private data from the inode instance */ rm = mountpt->i_private; rdir = (FAR struct romfs_dir_s *)dir; /* Make sure that the mount is still healthy */ ret = nxrmutex_lock(&rm->rm_lock); if (ret < 0) { return ret; } ret = romfs_checkmount(rm); if (ret == OK) { #ifdef CONFIG_FS_ROMFS_CACHE_NODE rdir->currnode = rdir->firstnode; #else rdir->curroffset = rdir->firstoffset; #endif } nxrmutex_unlock(&rm->rm_lock); return ret; } /**************************************************************************** * Name: romfs_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 romfs_bind(FAR struct inode *blkdriver, FAR const void *data, FAR void **handle) { struct romfs_mountpt_s *rm; int ret; finfo("Entry\n"); /* Open the block driver */ if (blkdriver == NULL) { ferr("ERROR: No block driver/ops\n"); return -ENODEV; } if (INODE_IS_BLOCK(blkdriver) && blkdriver->u.i_bops->open != NULL && blkdriver->u.i_bops->open(blkdriver) != OK) { ferr("ERROR: No open method\n"); return -ENODEV; } /* Create an instance of the mountpt state structure */ rm = fs_heap_zalloc(sizeof(struct romfs_mountpt_s)); if (!rm) { ferr("ERROR: Failed to allocate mountpoint structure\n"); return -ENOMEM; } /* Initialize the allocated mountpt state structure. The filesystem is * responsible for one reference ont the blkdriver inode and does not * have to addref() here (but does have to release in ubind(). */ nxrmutex_init(&rm->rm_lock); /* Initialize the mutex that controls access */ rm->rm_blkdriver = blkdriver; /* Save the block driver reference */ /* Get the hardware configuration and setup buffering appropriately */ ret = romfs_hwconfigure(rm); if (ret < 0) { ferr("ERROR: romfs_hwconfigure failed: %d\n", ret); goto errout; } /* Then complete the mount by getting the ROMFS configuratrion from * the ROMF header */ ret = romfs_fsconfigure(rm); if (ret < 0) { ferr("ERROR: romfs_fsconfigure failed: %d\n", ret); goto errout_with_buffer; } /* Mounted! */ *handle = rm; return OK; errout_with_buffer: if (!rm->rm_xipbase) { fs_heap_free(rm->rm_buffer); } errout: nxrmutex_destroy(&rm->rm_lock); fs_heap_free(rm); return ret; } /**************************************************************************** * Name: romfs_unbind * * Description: This implements the filesystem portion of the umount * operation. * ****************************************************************************/ static int romfs_unbind(FAR void *handle, FAR struct inode **blkdriver, unsigned int flags) { FAR struct romfs_mountpt_s *rm = handle; int ret; finfo("Entry\n"); #ifdef CONFIG_DEBUG_FEATURES if (!rm) { return -EINVAL; } #endif /* Check if there are sill any files opened on the filesystem. */ ret = nxrmutex_lock(&rm->rm_lock); if (ret < 0) { return ret; } if (rm->rm_refs) { /* We cannot unmount now.. there are open files */ fwarn("WARNING: There are open files\n"); /* This implementation currently only supports unmounting if there are * no open file references. */ ret = (flags != 0) ? -ENOSYS : -EBUSY; } else { /* Unmount ... close the block driver */ if (rm->rm_blkdriver) { FAR struct inode *inode = rm->rm_blkdriver; if (inode) { if (INODE_IS_BLOCK(inode) && inode->u.i_bops->close != NULL) { inode->u.i_bops->close(inode); } /* We hold a reference to the block driver but should * not but mucking with inodes in this context. So, we will * just return our contained reference to the block driver * inode and let the umount logic dispose of it. */ if (blkdriver) { *blkdriver = inode; } } } /* Release the mountpoint private data */ if (!rm->rm_xipbase && rm->rm_buffer) { fs_heap_free(rm->rm_buffer); } #ifdef CONFIG_FS_ROMFS_CACHE_NODE romfs_freenode(rm->rm_root); #endif nxrmutex_destroy(&rm->rm_lock); fs_heap_free(rm); return OK; } nxrmutex_unlock(&rm->rm_lock); return ret; } /**************************************************************************** * Name: romfs_statfs * * Description: Return filesystem statistics * ****************************************************************************/ static int romfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf) { FAR struct romfs_mountpt_s *rm; int ret; finfo("Entry\n"); /* Sanity checks */ DEBUGASSERT(mountpt && mountpt->i_private); /* Get the mountpoint private data from the inode structure */ rm = mountpt->i_private; /* Check if the mount is still healthy */ ret = nxrmutex_lock(&rm->rm_lock); if (ret < 0) { return ret; } ret = romfs_checkmount(rm); if (ret < 0) { ferr("ERROR: romfs_checkmount failed: %d\n", ret); goto errout_with_lock; } /* Fill in the statfs info */ buf->f_type = ROMFS_MAGIC; /* We will claim that the optimal transfer size is the size of one sector */ buf->f_bsize = rm->rm_hwsectorsize; /* Everything else follows in units of sectors */ buf->f_blocks = SEC_NSECTORS(rm, rm->rm_volsize + SEC_NDXMASK(rm)); buf->f_bfree = 0; buf->f_bavail = 0; buf->f_namelen = NAME_MAX; errout_with_lock: nxrmutex_unlock(&rm->rm_lock); return ret; } /**************************************************************************** * Name: romfs_stat_common * * Description: * Return information about a file or directory * ****************************************************************************/ static int romfs_stat_common(uint8_t type, uint32_t size, uint16_t sectorsize, FAR struct stat *buf) { memset(buf, 0, sizeof(struct stat)); if (IS_DIRECTORY(type)) { /* It's a read-execute directory name */ buf->st_mode = S_IFDIR | S_IROTH | S_IXOTH | S_IRGRP | S_IXGRP | S_IRUSR | S_IXUSR; } else if (IS_FILE(type) || IS_SOFTLINK(type)) { if (IS_FILE(type)) { buf->st_mode = S_IFREG; } else { buf->st_mode = S_IFLNK; } /* It's a read-only file name */ buf->st_mode |= S_IROTH | S_IRGRP | S_IRUSR; if (IS_EXECUTABLE(type)) { /* It's a read-execute file name */ buf->st_mode |= S_IXOTH | S_IXGRP | S_IXUSR; } } else { /* Otherwise, pretend like the unsupported type does not exist */ finfo("Unsupported type: %d\n", type); return -ENOENT; } /* File/directory size, access block size */ buf->st_size = size; buf->st_blksize = sectorsize; buf->st_blocks = (buf->st_size + sectorsize - 1) / sectorsize; return OK; } /**************************************************************************** * Name: romfs_stat * * Description: Return information about a file or directory * ****************************************************************************/ static int romfs_stat(FAR struct inode *mountpt, FAR const char *relpath, FAR struct stat *buf) { FAR struct romfs_mountpt_s *rm; struct romfs_nodeinfo_s nodeinfo; uint8_t type; int ret; finfo("Entry\n"); /* Sanity checks */ DEBUGASSERT(mountpt && mountpt->i_private); /* Get the mountpoint private data from the inode structure */ rm = mountpt->i_private; /* Check if the mount is still healthy */ ret = nxrmutex_lock(&rm->rm_lock); if (ret < 0) { return ret; } ret = romfs_checkmount(rm); if (ret != OK) { ferr("ERROR: romfs_checkmount failed: %d\n", ret); goto errout_with_lock; } /* Find the directory entry corresponding to relpath. */ ret = romfs_finddirentry(rm, &nodeinfo, relpath); /* If nothing was found, then we fail with EEXIST */ if (ret < 0) { finfo("Failed to find directory: %d\n", ret); goto errout_with_lock; } /* Return information about the directory entry */ type = (uint8_t)(nodeinfo.rn_next & RFNEXT_ALLMODEMASK); ret = romfs_stat_common(type, nodeinfo.rn_size, rm->rm_hwsectorsize, buf); errout_with_lock: nxrmutex_unlock(&rm->rm_lock); return ret; } /**************************************************************************** * Public Functions ****************************************************************************/