NuttX File System
NuttX includes an optional, scalable file system. This file-system may be omitted altogether; NuttX does not depend on the presence of any file system.
Pseudo Root File System. A simple in-memory, pseudo file
system can be enabled by default. This is an in-memory file
system because it does not require any storage medium or block
driver support. Rather, file system contents are generated
on-the-fly as referenced via standard file system operations
(open, close, read, write, etc.). In this sense, the file system
is pseudo file system (in the same sense that the Linux
/proc
file system is also referred to as a pseudo file
system).
Any user supplied data or logic can be accessed via the
pseudo-file system. Built in support is provided for character and
block drivers in the /dev
pseudo file system directory.
Mounted File Systems The simple in-memory file system can be
extended my mounting block devices that provide access to true
file systems backed up via some mass storage device. NuttX
supports the standard mount()
command that allows a block
driver to be bound to a mountpoint within the pseudo file system
and to a file system. At present, NuttX supports the standard VFAT
and ROMFS file systems, a special, wear-leveling NuttX FLASH File
System (NXFFS), as well as a Network File System client (NFS
version 3, UDP).
Comparison to Linux From a programming perspective, the NuttX file system appears very similar to a Linux file system. However, there is a fundamental difference: The NuttX root file system is a pseudo file system and true file systems may be mounted in the pseudo file system. In the typical Linux installation by comparison, the Linux root file system is a true file system and pseudo file systems may be mounted in the true, root file system. The approach selected by NuttX is intended to support greater scalability from the very tiny platform to the moderate platform.
Virtual File System (VFS)
Virtual File System provides a unified interface for various file systems to be able to co-exist together by exposing a blueprint that each file system needs to implement. This also allows the file system to be free from worry about the device driver implementations for storage devices, as they also expose a unified way of accessing the underlying devices.
How VFS works
Threads are controllable sequences of instruction execution with their own
stacks. Each task in NuttX is represented by a Task Control Block (TCB) (TCB
is defined in include/nuttx/sched.h
) and tasks are organized in task
lists.
All threads that are created by pthread_create()
are part of the same task
group. A task group (defined in include/nuttx/sched.h
) is a shared
structure pointed to by the TCBs of all the threads that belong to the same
task group, and this task group contains all the resources shared across the
task group which includes file descriptors in the form of a file list.
A file list (defined in include/nuttx/fs/fs.h
) contains file structures
that denote open files (along with a spinlock to manage access to the file
list). With the devices listed in the root file system (on
points like /dev/led
, /dev/mmcsd0
, etc. which are henceforth called
blockdriver mount points) in an unmounted state, storage devices can be
mounted using the mount()
command (to any point like /dir/abcd
) with
any specific supported file system, which internally calls its implemented
mountpt_operations->bind()
method and passes the blockdriver’s mount
point inode to it, thus creating a mount point. The blockdriver mount
point inode will have a mountpt->i_private
which contains any (file system
dependent) information about the mount and is to be filled by the file system
during the execution of mountpt_operations->bind()
(and usually this data
includes a pointer to the blockdriver mount point as well). After that,
according to system calls, the other exposed functions of the filesystem
are called as per need.
VFS Interface
VFS allows file systems to expose their own implementations of methods belonging to a unified interface:
File operations
-
int open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode)
Opens a file. Files are required to be opened before any other file operations are performed on it.
- Parameters:
filep (FAR struct file*) – Open file’s file structure pointer. The
filep->f_priv
member needs to be set here with the file system specific data that represents an open file.relpath (FAR const char*) – Relative path of the file from the root of the mounted file system.
oflags (int) – Flags in a bit field that specify the mode for openning the file (eg.
O_RDONLY
,O_RDWR
, etc. defined ininclude/fcntl.h
).mode (mode_t) – Specifies the mode (permissions). If
oflags
includeO_CREAT
, then this contains the mode for the file to be created.
- Returns:
Status of openning a file.
- Return values:
OK (0) – Success.
< 0 – Error.
-
int close(FAR struct file *filep)
This closes the opened file, and ideally syncs all the changes to the file to be written to the disk, as well as free the memory allocated to store the open file’s data.
- Parameters:
filep (FAR struct file*) – Open file’s file structure pointer.
- Returns:
Status of closing a file.
- Return values:
OK (0) – Success.
< 0 – Error.
-
ssize_t read(FAR struct file *filep, FAR char *buffer, size_t buflen)
Reads maximum
buflen
bytes from an opened file (from the current offset the opened file descriptor is pointing at if the file system supports seeking).- Parameters:
filep (FAR struct file*) – Open file’s file structure pointer.
buffer (FAR char*) – Buffer to store the read data.
buflen (size_t) – Length of the maximum number of bytes to be read.
- Returns:
Number of bytes read.
- Return values:
> 0 – Size of bytes read.
< 0 – Error.
-
ssize_t write(FAR struct file *filep, FAR const char *buffer, size_t buflen)
Writes maximum
buflen
bytes to an opened file (from the current offset the opened file is at if the file system supports seeking).- Parameters:
filep (FAR struct file*) – Open file’s file structure pointer.
buffer (FAR char*) – Buffer which contains the data to be written.
buflen (size_t) – Length of the maximum number of bytes to be written.
- Returns:
Number of bytes written.
- Return values:
> 0 – Size of bytes written.
< buflen – Insufficient storage or file size limit reached
< 0 – Error.
Note
POSIX requires that a read()
after a write()
should get the newly
written data, but not all file systems conform to POSIX, especially as
POSIX requires atomic writes, which is not usually implemented
as it can impact performance.
To be POSIX compliant in concurrent situations, either the writes have to be atomic, or read is blocked with a lock until an on-going write is finished, which, as stated, would impact performance.
-
off_t seek(FAR struct file *filep, off_t offset, int whence)
Underlying implementation of
lseek()
, it allows the open file’s file structure to point to any particular location in the file.- Parameters:
filep (FAR struct file*) – Open file’s file structure pointer.
offset (off_t) – The offset required.
whence (int) –
This controls how the offset it applied. It can have values (defined in
/include/sys/types.h
):SEEK_SET: Offset from start of file.
SEEK_CUR: Offset from current location in file.
SEEK_END: Offset after end of file.
Note
According to POSIX, lseek()
to any point after the end of the file
does not by itself increase the size of the file. Later writes to this
part will, however, increase it to at least the end of the written data, and
the “gap” before this written data should be filled with \0
in case of
any reads after such a write operation.
-
int ioctl(FAR struct file *filep, int cmd, unsigned long arg)
It is the underlying implementation of
ioctl()
(I/O Control).ioctl()
manipulates the underlying device parameters of files.- Parameters:
filep (FAR struct file*) – Open file’s file structure pointer.
cmd (int) – It can take a variety of values (which are defined in
include/nuttx/fs/ioctl.h
). It represents the command that will be carried out on the file. Both the filesystem, as well as the device driver needs to support the command in order for the function to run.arg (unsigned long) – Additional argument that may be required for ioctl. Details for what is required is written in the comments beside the desired ioctl command in
include/nuttx/fs/ioctl.h
.
- Returns:
Status of ioctl operation.
- Return values:
OK (0) – Success.
< 0 – Error.
-
int mmap(FAR struct file *filep, FAR struct mm_map_entry_s *map)
Underlying implementation of
mmap()
.mmap()
creates a new mapping in the virtual address space of the calling process.- Parameters:
filep (FAR struct file*) – Open file’s file structure pointer.
map (FAR struct mm_map_entry_s*) – mmap entry strucutre pointer, which includes the virtual address.
- Returns:
Status of mmap operation.
- Return values:
OK (0) – Success.
< 0 – Error.
Note
NuttX operates in a flat open address space. Therefore, it generally does
not require mmap()
functionality. There are two notable exceptions where
mmap()
functionality is required:
mmap()
is the API that is used to support direct access to random access media under the following very restrictive conditions:
The filesystem implements the mmap file operation. Any file system that maps files contiguously on the media should support this ioctl. (vs. file system that scatter files over the media in non-contiguous sectors). As of this writing, ROMFS is the only file system that meets this requirement.
The underlying block driver supports the BIOC_XIPBASE ioctl command that maps the underlying media to a randomly accessible address. At present, only the RAM/ROM disk driver does this.
If CONFIG_FS_RAMMAP is defined in the configuration, then mmap() will support simulation of memory mapped files by copying files whole into RAM.
-
int truncate(FAR struct file *filep, off_t length)
Shrinks or expands the file to be of the desired size.
- Parameters:
filep (FAR struct file*) – Open file’s file structure pointer.
length (off_t) – Final size of the file.
- Returns:
Status of truncate operation.
- Return values:
OK (0) – Success.
< 0 – Error.
-
int poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup)
Underlying implementation of
poll()
. Thepoll()
function provides applications with a mechanism for multiplexing input/output over a set of file descriptors.- Parameters:
filep (FAR struct file*) – Open file’s pointer.
fds (FAR struct pollfd*) – The structure describing the events to be monitored, OR NULL if this is a request to stop monitoring events.
setup (bool) – true: Setup up the poll; false: Teardown the poll
- Returns:
Status of poll operation.
- Return values:
OK (0) – Success.
< 0 – Error.
Additional open file specific operations
-
int sync(FAR struct file *filep)
This synchronizes the on-disk file system state of the file with the in-memory file system state, ie. commits the file system caches to the disk.
- Parameters:
filep (FAR struct file*) – Open file’s
struct file
(defined ininclude/nuttx/fs/fs.h
) pointer.
- Returns:
Status of syncing a file.
- Return values:
OK (0) – Success.
< 0 – Error.
-
int dup(FAR const struct file *oldp, FAR struct file *newp)
Duplicate an open file structure.
- Parameters:
oldp (FAR const struct file*) – Pointer to structure that is to be duplicated.
newp (FAR struct file*) – Pointer to structure in which the duplicate data will be stored.
- Returns:
Status of duplicating open file’s structure.
- Return values:
OK (0) – Success.
< 0 – Error.
-
int fstat(FAR const struct file *filep, FAR struct stat *buf)
Obtain information about an open file.
- Parameters:
filep (FAR const struct file*) – Open file’s pointer.
buf (FAR struct stat*) – Pointer to the
struct stat
(defined ininclude/sys/stat.h
).
- Returns:
Status of obtaining open file’s information.
- Return values:
OK (0) – Success.
< 0 – Error.
-
int fchstat(FAR const struct file *filep, FAR const struct stat *buf, int flags)
Change file stats. It can change the mode, timestamps and ownership.
- Parameters:
filep (FAR struct file*) – Open file’s pointer.
buf (FAR const struct stat*) – Pointer to stat structure describing the values that need to be updated.
flags (int) –
Bit field that can include (defined in
include/nuttx/fs/fs.h
):CH_STAT_MODE
CH_STAT_UID
CH_STAT_GID
CH_STAT_ATIME
CH_STAT_MTIME
This describes what needs to be updated.
- Returns:
Status of changin open file’s stats.
- Return values:
OK (0) – Success.
< 0 – Error.
Directory operations
-
int opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s **dir)
Opens a directory stream for the provided directory. Other directory operations can be used after this to do various directory related operations . We say the directory stream points to the first entry, but you need
readdir()
to read the first entry.- Parameters:
mountpt (FAR struct inode*) – Mount point inode of the file system.
relpath (FAR const char*) – Relative path from the root of the point point of the directory.
dir (FAR struct fs_dirent_s**) – A directory stream structure pointer which needs to be populated with the required fields (defined in
include/nuttx/fs/fs.h
).
- Returns:
Status of openning the directory.
- Return values:
OK (0) – Success.
< 0 – Error.
-
int closedir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir)
Closes a directory stream, as well as deallocates any memory used while while openning a directory stream.
- Parameters:
mountpt (FAR struct inode*) – Mount point inode of the file system.
dir (FAR struct fs_dirent_s**) – A directory stream structure pointer which was previously allocated (and needs to be freed).
- Returns:
Status of closing the directory.
- Return values:
OK (0) – Success.
-
int readdir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir, FAR struct dirent *entry)
This reads the next directory entry in a directory stream. If the stream points to the base of the directory, then the first directory entry in the directory is given.
- Parameters:
mountpt (FAR struct inode*) – Mount point inode of the file system.
dir (FAR struct fs_dirent_s**) – A directory stream structure pointer.
entry (FAR struct dirent*) – Pointer to the directory entry. This will be modified to point to the directory entry after it in the directory.
- Returns:
Status of reading the directory.
- Return values:
OK (0) – Success.
< 0 – Error.
-
int rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir)
Resets the directory stream back to the first entry, like it was after openning.
- Parameters:
mountpt (FAR struct inode*) – Mount point inode of the file system.
dir (FAR struct fs_dirent_s**) – A directory stream structure pointer.
- Returns:
Status of rewinding the directory.
- Return values:
OK (0) – Success.
< 0 – Error.
Volume-relations operations
-
int bind(FAR struct inode *blkdriver, FAR const void *data, FAR void **handle)
This is where the file system related data is initialized, and is part of the mount process.
- Parameters:
blkdriver (FAR struct inode*) – Pointer to the block driver’s device inode. This needs to be opened in this function.
data (FAR const void*) – The options provided during mount.
handle (FAR void**) – Whatever data
handle
points to is attached to themountpt
inode after this function is called during the mount process. This way, this file system’s other methods can receive this information if they have access tomountpt
inode, by accessingmountpt->i_private
.
- Returns:
Status of binding operation.
- Return values:
OK (0) – Success.
< 0 – Error.
-
int unbind(FAR void *handle, FAR struct inode **blkdriver, unsigned int flags)
This is part of the unmounting process. The file system first needs to assess the flags passed to it and appropriately do the tasks required by these flags, and then it needs to free the private data (
handle
and any allocated members), as well as close the previously-opened (during mount) block driver’s inode.- Parameters:
handle (FAR void*) – Private data of the file-system.
blkdriver (FAR struct inode**) – The device inode of the block driver’s device inode.
flags (unsigned int) –
Flags dictate the actions needed to be carried out before the file system data is removed and the block driver inode is closed. The values can be (as defined in
include/sys/mount.h
):MNT_FORCE
MNT_DETACH
MNT_EXPIRE
UMOUNT_NOFOLLOW
- Returns:
Status of unbinding operation.
- Return values:
OK (0) – Success.
< 0 – Error.
-
int statfs(FAR struct inode *mountpt, FAR struct statfs *buf)
Provides stats for that instance of the file system. The exact stats that are provided can be viewed in the members of
struct statfs
(in fileinclude/sys/statfs.h
).- Parameters:
mountpt (FAR struct inode*) – Mount point inode of the file system.
buf (FAR struct statfs*) – Buffer that needs to be filled with the relevant file system information.
- Returns:
Status of finding the filesystem stats operation.
- Return values:
OK (0) – Success.
< 0 – Error.
Path operations
-
int unlink(FAR struct inode *mountpt, FAR const char *relpath)
Removes a file, specifically, removes a name from the file system.
- Parameters:
mountpt (FAR struct inode*) – Mount point inode of the file system.
relpath (FAR const char*) – The relative path of the file from the root of the file system.
- Returns:
Status of unlinking operation.
- Return values:
OK (0) – Success.
< 0 – Error.
-
int mkdir(FAR struct inode *mountpt, FAR const char *relpath, mode_t mode)
Creates a directory.
- Parameters:
mountpt (FAR struct inode*) – Mount point inode of the file system.
relpath (FAR const char*) – Relative path of the new directory from the root of the file system.
mode (mode_t) – The mode (permissions) for the directory.
- Returns:
Status of creating a directory operation.
- Return values:
OK (0) – Success.
< 0 – Error.
-
int rmdir(FAR struct inode *mountpt, FAR const char *relpath)
Removes a directory.
- Parameters:
mountpt (FAR struct inode*) – Mount point inode of the file system.
relpath (FAR const char*) – Relative path of the directory from the root of the file system.
- Returns:
Status of removing a directory operation.
- Return values:
OK (0) – Success.
< 0 – Error.
-
int rename(FAR struct inode *mountpt, FAR const char *oldrelpath, FAR const char *newrelpath)
Renames a file or a directory
- Parameters:
mountpt (FAR struct inode*) – Mount point inode of the file system.
oldrelpath (FAR const char*) – Existing path of the file or directory.
newrelpath (FAR const char*) – New path of the file or directory.
- Returns:
Status of renaming a file or a directory operation.
- Return values:
OK (0) – Success.
< 0 – Error.
-
int stat(FAR struct inode *mountpt, FAR const char *relpath, FAR struct stat *buf)
Information about a file or a directory. The exact information that is provided can be viewed in the members of
struct stat
(in fileinclude/sys/stat.h
).- Parameters:
mountpt (FAR struct inode*) – Mount point inode of the file system.
relpath (FAR const char*) – Relative path of the file or directory from the root of the file system.
buf (FAR struct stat*) – Buffer that needs to be filled with the relevant file or directory information.
- Returns:
Status of finding information about a file or directory.1 operation.
- Return values:
OK (0) – Success.
< 0 – Error.
-
int chstat(FAR struct inode *mountpt, FAR const char *relpath, FAR const struct stat *buf, int flags)
Change the stats of a file or directory.
- Parameters:
mountpt (FAR struct inode*) – Mount point inode of the file system.
relpath (FAR const char*) – Relative path of the file or directory from the root of the file system.
buf (FAR const struct stat*) – Contains the new stat information. Access only the ones that are required according to the flags.
flags (int) – A bit field that can have values including
CH_STAT_MODE
,CH_STAT_UID
,CH_STAT_GID
,CH_STAT_ATIME
orCH_STAT_MTIME
which are or-ed together.
-
int syncfs(FAR struct inode *mountpt)
This works like
sync()
but instead of the file, it syncs the entire filesystem’s metadata.- Parameters:
mountpt (FAR struct inode*) – Mount point inode of the file system.
- Returns:
Status of syncing file system metadata operation.
- Return values:
OK (0) – Success.
< 0 – Error.
The file systems can have their own implementations for these functions under-the-hood, but the user does not have to worry about the underlying file system during file I/O, as the file system has to expose its implementations in a unified interface.
Note
Each file system has to globally expose their implementations of the unified
interface as defined by struct mountpt_operations
(in
include/fs/fs.h
) to one of the lists defined in fs/mount/fs_mount.c
depending on the type of the file system.
They also need their own magic number
to be listed in include/sys
and in fs_gettype
function (in
fs/mount/fs_gettype.c
) for identification of the filesystem.
File systems
NuttX provides support for a variety of file systems out of the box.
FS Categories
File systems can be divided into these categories on the basis of the drivers they require:
They require a block device driver. They include vfat, romfs, smartfs, and littlefs.
They require MTD drivers. They include romfs, spiffs, littlefs.
They require neither block nor MTD drivers. They include nxffs, tmpfs, nfs binfs, procfs, userfs, hostfs, cromfs, unionfs, rpmsgfs, and zipfs.
The requirements are specified by declaring the filesystem in the proper
array in fs/mount/fs_mount.c
.