blob: 4ef99217faf33adcd0fa66f0cbbad9f67ebd8859 [file] [log] [blame] [view]
# Launching Filesystems on Fuchsia
Filesystems on Fuchsia are regular programs that run in userspace. They consume a block device and
a set of options, and produce an export directory with several protocols, first and foremost a
fuchsia.io.Directory representing the root of the filesystem at `/root`.
There are currently two ways to launch a filesystem. One way, the old way which is being
deprecated, is to launch the raw filesystem binary, and the other way, which filesystems are moving
to, is to launch as a component via the component framework. Both of these methods are supported by
fs_management libraries in C++ and Rust (the Rust implementation is pending - https://fxbug.dev/42178109).
No matter how the filesystem is launched (the old way or the new way), all our platform filesystems
have the same structure to the export directory. The basic entries are -
```
/diagnostics
/fuchsia.fs.Admin
/root
```
`diagnostics` provides the inspect tree implementation, which has a bunch of statistics and
metrics. `fuchsia.fs.Admin` provides the ability to shut down a filesystem instance. `root` is the
root of the filesystem. Other platform filesystems may include additional entries. For example,
blobfs provides `/fuchsia.update.verify.ComponentOtaHealthVerification`.
## Launching filesystems with fs_management
Filesystems are launched using the `fs_management::Mount`, which takes a block device file
descriptor, an optional mount path, the disk format, options, and a launch callback. Mount returns
an RAII MountedFilesystem object with which you can Unmount and get access to the export directory.
The block device should be opened as read-write unless you configure the filesystem to be in
read-only mode. One option for block device paths to open is in `/dev/class/block`, which is an
enumerated list of all the block devices Fuchsia knows about (starting at `000` and counting up).
It's populated in the order that the drivers are bound, which is _not_ consistent across boots. The
other option is to use the topological path, which will depend on the drivers the storage uses (but
is otherwise consistent). Both of these can be found using `lsblk` on the command line. A couple of
examples -
```
/dev/sys/platform/00:00:2d/ramctl/ramdisk-0/block
/dev/sys/platform/pci/00:1f.2/ahci/sata0/block
```
If you are launching a filesystem on a ramdisk, you can use the ramdisk functions to get the
ramdisk path.
The `mount_path` is expected to be a non-existing path in your namespace. fs_management uses
fdio_namespace_bind to place the root of the filesystem at that path in your local namespace. Note:
This path is process local! Only your process sees the namespace, and a new one is created for
every process. See //docs/concepts/process/namespaces.md for more information.
The options struct, `fs_management::MountOptions`, contains a union of all possible filesystem
configuration. That is, not all filesystems support all the options. Make sure that the filesystem
you are launching supports the option you are using, or else it may fail to launch.
If both `component_child_name` and `component_collection_name` are provided, then fs_management
will assume the component is a dynamic child in the realm. It attempts to connect to it's exposed
directory via `fuchsia.component.Realm`, and if it fails because it can't find a component with the
given name, it attempts to launch a new instance, using a component url based on the disk format.
If only `component_child_name` is provided, then fs_management will assume the component is a static
child in the realm. It attempts to connect to it's exposed directory via
`fuchsia.component.Realm`.
A shard is provided for cml files to include if they plan to launch filesystems as dynamic children
at `//src/storage/lib/fs_management/client.shard.cml`. The `component_collection_name` using this
shard is `fs-collection`.
Launching a filesystem using regular processes -
```cpp
fbl::unique_fd device_fd(open("/dev/class/block/001", O_RDWR));
ASSERT_TRUE(device_fd);
fs_management::MountOptions options;
auto fs = fs_management::Mount(std::move(device_fd), fs_management::kDiskFormatMinfs, options);
ASSERT_EQ(fs.status(), ZX_OK);
auto data = fs->DataRoot();
ASSERT_EQ(data.status(), ZX_OK);
auto binding = fs_management::NamespaceBinding::Create("/fs", std::move(*data));
ASSERT_EQ(binding.status(), ZX_OK);
// Now /fs points at the root of the filesystem.
```
Launching a filesystem using a component collection -
```cpp
fbl::unique_fd device_fd(open("/dev/class/block/001", O_RDWR));
ASSERT_TRUE(device_fd);
fs_management::MountOptions options {
.component_child_name = "minfs",
.component_collection_name = "fs-collection",
};
auto fs = fs_management::Mount(std::move(device_fd), fs_management::kDiskFormatMinfs, options);
ASSERT_EQ(fs.status(), ZX_OK);
auto data = fs->DataRoot();
ASSERT_EQ(data.status(), ZX_OK);
auto binding = fs_management::NamespaceBinding::Create("/fs", std::move(*data));
ASSERT_EQ(binding.status(), ZX_OK);
// Now /fs points at the root of the filesystem.
```
## Launching a filesystem as a process
This section has a detailed description of how fs_management launches platform filesystem
processes. It's intended for filesystem developers - if you just want to mount a filesystem, in
code, see the previous section.
Platform filesystem processes take 2 startup handles and a handful of command line arguments. The
two startup handles are the server end of the export directory (`PA_DIRECTORY_REQUEST`), and a
handle to the block device (`PA_HND(PA_USER0, 1)`).
The export directory should have the structure described at the beginning of this document.
The block device should be a handle to something that speaks `fuchsia.hardware.block.Block`. Most
block devices speak a handful of other protocols as well.
Filesystems can use protocols from their namespace, but they should fail gracefully if they are not
there. This is because many test environments don't construct consistent namespaces for the
filesystems they run. The most common production environment is fshost, which copies all it's
services to filesystems it launches, so using anything it uses in it's cml is probably fine.
## Launching a filesystem as a component
This section has a detailed description of how fs_management launches platform filesystem
components. It's intended for filesystem developers - if you just want to mount a filesystem, in
code, see the previous section.
There is currently one filesystem that supports being launched as a component, blobfs. The cml file
for blobfs is in //src/storage/blobfs/bin-component/meta/blobfs.cml.
When a filesystem component is launched, it starts in a partially configured state. They serve one
protocol - `fuchsia.fs.startup.Startup`. This is served from the path
`/<fs_component>/svc/fuchsia.fs.startup.Startup`. Filesystems need two things to run - a set of
options and a block device handle. This protocol provides methods for `Start`, as well as `Format`
and `Check`, which take the block device and a set of options as arguments.
`Start` returns once the filesystem is launched. Before this point, it's expected that filesystems
queue incoming open requests to be processed once `Start` is called. In blobfs, this is done by
selectively calling Serve on parts of the export directory at a time. If a `Start` call fails,
fs_management will attempt to destroy and restart the lifecycle component.
Filesystems that are launched as components are also expected to consume an extra startup handle,
PA_LIFECYCLE. This handle is the server end of a fuchsia.process.lifecycle.Lifecycle protocol,
which the component framework can use to send shutdown requests.