| # Starnix virtual file system (VFS) |
| |
| The page examines the key concepts and underlying structure of [Starnix][starnix]'s |
| virtual file system (VFS): |
| |
| - [Motivation for Starnix's own virtual file system](#motivation-for-starnix-vfs) |
| - [Building blocks of the Starnix VFS](#building-blocks-of-starnix-vfs) |
| - [Mounted file systems in the Starnix VFS](#mounted-file-systems-in-starnix-vfs) |
| |
| ## Motivation for Starnix's own virtual file system {:#motivation-for-starnix-vfs} |
| |
| Starnix has its own virtual file system (which is separate from |
| [Fuchsia's VFS][fuchsia-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`][tmpfs]{:.external} and [`procfs`][procfs]{:.external}). |
| |
| - **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 an |
| `open()` 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 {:#building-blocks-of-starnix-vfs} |
| |
| This section constructs a simple file system using the fundamental building |
| blocks of the Starnix VFS: |
| |
| - [Files and directories](#files-and-directories) |
| - [A file system](#a-file-system) |
| |
| ### Files and directories {:#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} |
| |
| 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 {:#mounted-file-systems-in-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](#mount) |
| - [NamespaceNode](#namespacenode) |
| |
| ### Mount {:#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`](#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](#symbolic-links)) to be used to reach the same file in the mounted |
| `FileSystem`. |
| |
| ### NamespaceNode {:#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} |
| |
| 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`. |
| |
| <!-- Reference links --> |
| |
| [starnix]: /docs/concepts/starnix/syscalls.md |
| [fuchsia-vfs]: /docs/concepts/filesystems/filesystems.md |
| [tmpfs]: https://en.wikipedia.org/wiki/Tmpfs |
| [procfs]: https://en.wikipedia.org/wiki/Procfs |