The page examines the key concepts and underlying structure of Starnix's virtual file system (VFS):
- Motivation for Starnix's own virtual file system
- Building blocks of the Starnix VFS
- Mounted file systems in the Starnix VFS
Motivation for Starnix's own virtual file system
Starnix has its own virtual file system (which is separate from Fuchsia's VFS) for the reasons below:
Performance – The Starnix VFS allows many file system operations to avoid making FIDL calls. The Starnix VFS does this by caching the results from previous FIDL calls and implementing the entire file system in a process (similar to
tmpfs
andprocfs
).Compatibility – A file system for Starnix must support several Linux file system operations that are not supported by Fuchsia's VFS libraries, which many Linux-based operating systems rely on. In particular, the following two operations are hard to retrofit into Fuchsia's existing VFS design:
Mounting directories – A Linux file system allows a directory of one file system to be mounted to a directory of another file system.
Tracking a file's path after
open()
– A Linux file system can use a file descriptor to determine the path of the file that is opened by anopen()
call. If the file or one of its parent directories gets moved or renamed, the file's path must account for this change.
Building blocks of the Starnix VFS
This section constructs a simple file system using the fundamental building blocks of the Starnix VFS:
Files and directories
At the core of the Starnix VFS is a collection of FsNode
instances. Each FsNode
contains information about a file or a directory (see Figure 1). File system
operations, such as reading, writing, and retrieving file information, are
performed on FsNode
instances.
Figure 1. A simple file system with a directory and a file.
A DirEntry
assigns a name to an FsNode
. This mapping of DirEntry
to FsNode
is referred to as a hard link. Multiple DirEntry
instances can be mapped to a
single file-typed FsNode
(see Figure 2). This allows multiple paths in a file system
to be resolved to the same FsNode
. As a result, multiple paths can be resolved to the
same file content and attributes in a file system. However, while a file can have
multiple hard links, a directory (that is, a directory-typed FsNode
) can only have
one hard link.
Figure 2. Multiple DirEntry
instances can be mapped to the same FsNode
.
A file system
A file system tracks information on a group of related files and directories. In
the Starnix VFS, a file system (that is, a single instance of FileSystem
)
represents a collection of DirEntry
instances, and each FileSystem
instance
contains the following items:
- A
DirEntry
pointing to the root directory - A cache of
FsNode
instances (representing files and directories under the root directory)
Figure 3. A FileSystem
tracks DirEntry
and FsNode
instances.
Mounted file systems in the Starnix VFS
Mounting allows users to seamlessly move from one file system to another file
system. A file system and its contents become available to users (or tasks) once
the file system is mounted to a certain directory. This "mount point" directory
is often a directory of another file system that the users already have access to.
For example, in Figure 4, users can reach the file1.txt
file in the Example FS
(file system) by using the path /example/file1.txt
from the Parent FS.
Figure 4. The root directory of the Example FS is mounted to the example
directory of the Parent FS.
Mounting a file system and tracking mount points in the Starnix VFS involve the following instances:
Mount
A Mount
makes a FileSystem
accessible at the mount point directory of
another FileSystem
. To put it differently, a Mount
is used to link the
following two directories:
- A mount point directory in the parent
FileSystem
- The root directory of the child
FileSystem
In Figure 4, the Parent FS has the example
directory as a subdirectory and the
Example FS is mounted on this example
directory (which is the mount point
directory). In this setup, when you change your working directory to the
example
directory in the Parent FS (for instance, cd example
is run), you
"enter the mount," that is, you are now in the root directory of the Example FS.
Figure 5. A Mount
tracks the root directory of a mounted FileSystem
.
To enable the feature of seamlessly moving from the parent FileSystem
into the
child FileSystem
and vice versa, a Mount
tracks a DirEntry
that points to the
root directory of the mounted FileSystem
(see Figure 5). And a Mount
also tracks
its parent Mount
using the mountpoint
pointer (see
NamespaceNode
) and its child Mount
instances using the
submounts
pointer. (see Figure 6).
Figure 6. Mount
's mountpoint
and submounts
pointers are used to move
between mounted file systems.
It's important to note that a FileSystem
can be mounted to a number of different
FileSystem
instances at once. This allows the same FileSystem
to be reached from
multiple parent FileSystem
instances, enabling various paths (or
symlinks) to be used to reach the same file in the mounted
FileSystem
.
NamespaceNode
Every task in Starnix contains an instance of FsContext
. An FsContext
holds
information about the task's association with the Starnix VFS, except for the
file descriptor table.
Importantly, an FsContext
contains a Namespace
, which
enables the task to identify all FileSystem
instances that are mounted under
this namespace. This is possible because each Namespace
contains a Mount
that
tracks the "root mount" in the namespace (see Figure 7). Using the root mount's
submounts
pointer (which points to a map of all DirEntry
instances and their
respective Mount
instances in the namespace), the task can reach all of the
other mounted FileSystem
instances under this namespace.
Figure 7. An FsContext
has a Namespace
, which has a Mount
for tracking the
root mount.
A NamespaceNode
is useful for traversing paths in a Namespace
. Each
NamespaceNode
tracks a Mount
and its respective DirEntry
.
However, because the same FileSystem
can be mounted multiple times in a
Namespace
(for instance, at different mount point directories in the same parent
FileSystem
), multiple NamespaceNode
instances may be necessary to track these
different Mount
instances that lead to the same underlying FileSystem
.
Figure 8. A FsContext
has two NamespaceNode
instances for tracking specific
directories: root and cwd.
For instance, each FsContext
contains, at a minimum, two NamespaceNode
instances for tracking the following directories (see Figure 8):
- The root directory
- The current working directory (cwd)
Both NamespaceNode
instances are used for looking up paths in this Namespace
. A
path starting with /
(for example, /example/file1.txt
) is looked up respective
to the root NamespaceNode
, and a path without a starting /
(for example,
file1.txt
) is looked up using the cwd NamespaceNode
.
Figure 9. A simple namespace with a single (root) Mount
.
The diagram in Figure 9 illustrates a namespace with a single Mount
where the
current working directory (cwd) also happens to be the root directory. The diagram
in Figure 10 shows that the current working directory is changed to the example
directory (for instance, cd example
is run), which causes the entry
pointer of
the cwd NamespaceNode
to be updated to the example
directory.
Figure 10. The current working directory is changed to the example
directory
in the namespace.
The diagram in Figure 11 shows a namespace where the Example FS is mounted to the
Parent FS. Under this scenario, when cd example
is run, the mount
pointer of
the cwd NamespaceNode
is updated to a new Mount
(labeled "Example Mount") that
tracks the root directory of the Example FS. Using this new Mount
, the task in
the FsContext
can seamlessly move from the Parent FS to the Example FS when
traversing paths.
Figure 11. A namespace where the Example FS is mounted to the example
directory of
the Parent FS.
Symbolic links
Symbolic links (or symlinks) are handled while walking NamespaceNode
instances.
If the task hits a symlink and asks for its path, the path of this symlink is
resolved recursively from the NamespaceNode
.