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 in include/fcntl.h).

  • mode (mode_t) – Specifies the mode (permissions). If oflags include O_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:

  1. mmap() is the API that is used to support direct access to random access media under the following very restrictive conditions:

  1. 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.

  2. 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.

  1. 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(). The poll() 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 in include/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 in include/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 the mountpt 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 to mountpt inode, by accessing mountpt->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 file include/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

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 file include/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 or CH_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:

  1. They require a block device driver. They include vfat, romfs, smartfs, and littlefs.

  2. They require MTD drivers. They include romfs, spiffs, littlefs.

  3. 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.