| # Fusebox |
| |
| Fusebox is a ChromeOS-only mechanism for exposing Chrome's in-process virtual |
| file system (its [`storage` C++ |
| API](https://source.chromium.org/chromium/chromium/src/+/main:storage/browser/file_system/)) |
| on the 'real' (kernel-level) virtual file system, via [Linux's |
| FUSE](https://www.kernel.org/doc/html/latest/filesystems/fuse.html) protocol. |
| |
| It enables sharing virtual-file-like things *across processes* (e.g. between |
| ash-chrome and lacros-chrome, although lacros-chrome is now deprecated) or |
| *with Virtual Machines* (e.g. the Android or Crostini VMs) just by sharing a |
| string file name or an integer file descriptor. |
| |
| Fusebox doesn't *replace* the `storage` C++ API. It provides *an alternative |
| mechanism* for accessing those virtual files. Workflows that stay entirely |
| within ash-chrome can continue to use the C++ API. But when the GMail web-app |
| (running in a sandboxed and now deprecated lacros-chrome process) wants to |
| upload files from a phone attached to a Chromebook via USB cable, and the MTP |
| (Media Transfer Protocol) volume (virtual directory) is served by ash-chrome |
| code, that access is facilitated by Fusebox. |
| |
| |
| ## Structure |
| |
| There are multiple processes involved. The two key ones communicate over D-Bus: |
| |
| - ash-chrome is the D-Bus server, also known as the Fusebox Server. This |
| process does not speak FUSE per se. |
| - `/usr/bin/fusebox` is the D-Bus client, also known as the Fusebox Client or |
| the Fusebox Daemon. This is the process that speaks FUSE with the kernel. |
| Like other FUSE daemons on ChromeOS, this is managed by |
| [`cros-disks`](https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform2/cros-disks/) |
| and runs in a [minijail |
| sandbox](https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform2/cros-disks/seccomp_policy). |
| |
| Those are the two key processes. More processes are involved when an arbitrary |
| process (e.g. lacros-chrome or another process running in an Android VM) wants |
| to read a Fusebox file: |
| |
| ``` |
| arbitrary-proc <-libc-> kernel <-FUSE-> FuseboxDaemon <-D-Bus-> ash-chrome |
| ``` |
| |
| Specifically when "an arbitrary process" is lacros-chrome, we could skip some |
| hops with a [direct |
| connection](https://chromium.googlesource.com/chromium/src.git/+/main/chromeos/crosapi) |
| between lacros-chrome and ash-chrome. But that optimization is not implemented |
| yet (as of March 2023). |
| |
| `cros-disks` forks/execs the Fusebox Daemon at user log-in. But after start-up, |
| `cros-disks` is not involved in Fusebox serving virtual files. |
| |
| |
| ### D-Bus |
| |
| The Fusebox Server has some bookkeeping code because D-Bus RPCs are "1 request, |
| 1 response" but some `storage` C++ API calls are "1 request, multiple |
| (streaming) responses". In Fusebox's D-Bus protocol, the `cookie` is the common |
| numeric identifier that groups these request/response pairs. |
| |
| We may move our IPC system from D-Bus to Mojo in the future, for this and other |
| reasons, especially as we don't really use D-Bus' structured types anymore |
| (they're hard to evolve, since the client and server live in different source |
| code repositories). Fusebox only uses D-Bus as a simple pipe for flinging |
| [Fusebox-specific |
| protobufs](https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform2/system_api/dbus/fusebox/fusebox.proto) |
| around. But for now (March 2023), it's D-Bus. |
| |
| The method names on Fusebox' D-Bus interface (e.g. "Open", "Read", "Write", |
| etc.) typically correspond 1:1 with both FUSE methods and Chrome's `storage` |
| C++ API methods, although those two systems sometimes use different names (e.g. |
| "Unlink" and "Rmdir" versus "RemoveFile" and "RemoveDirectory"). |
| |
| Some method names have a "2" suffix, "Read2" versus "Read", because the |
| original version used D-Bus' structured types as arguments. As above, these are |
| hard to evolve (e.g. add a new field) without atomic cross-repository commits. |
| The "2" versions speak protobufs-over-D-Bus instead. |
| |
| |
| ## File Names |
| |
| Fusebox file names (on the kernel-visible file system) look like |
| `/media/fuse/fusebox/abc.1234/foo/bar.txt`. The `abc.1234` is also called the |
| Fusebox Subdir (or just the Subdir), as a single Fusebox daemon process can |
| serve multiple volumes. |
| |
| The `abc` part of the Subdir identifies the volume type: |
| |
| - `adp` = Android Documents Provider, an Android (Java) API. For example, |
| Dropbox has an official Android app, which can run on Chromebooks, making |
| someone's Dropbox folder-in-the-cloud appear in the ChromeOS Files App. |
| - `fsp` = File System Provider, a Chrome (JavaScript) API. For example, Chrome |
| extensions can implement virtual file systems. |
| - `mtp` = Media Transfer Protocol, via ChromeOS' system-global [platform2/mtpd |
| daemon](https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform2/mtpd). |
| For example, phones and tablets that are attached to a Chromebook via USB |
| cable and have opted in to sharing their files. |
| - `tmp` = Temporary filesystem (a subdirectory of `/tmp`), for testing. |
| |
| The `1234` part of the Subdir, typically a base-64 encoded hash code, |
| identifies different volumes of that type. For example, somebody could mount |
| multiple ADP volumes, and they'd get different `adp.*` Subdirs. These hashes |
| (and hence file names) aim to be stable for what's conceptually "the same |
| volume". For example, unplugging a phone from a USB port and plugging the same |
| phone into a different port shouldn't change the Subdir. |
| |
| The `foo/bar.txt` part is the relative path within the volume root. For |
| example, `Download/cake.jpg` could identify a photo in an attached phone's |
| `Download` directory. |
| |
| |
| ### Built-In File Names |
| |
| The Fusebox Client also serves some files under `/media/fuse/fusebox/built_in`, |
| mainly for basic debugging. For example, some of these `built_in` files may |
| still be informative even when the Fusebox Client cannot connect to the Fusebox |
| Server. |
| |
| |
| ## Source code |
| |
| The Fusebox Server (Chrome) code primarily lives in this directory, |
| `chrome/browser/ash/fusebox`. Unsurprisingly, `fusebox_server.cc` is the |
| centerpiece. Part of its code is bureaucracy because D-Bus code and callbacks |
| run on the main (UI) thread while `storage` code and callbacks run on the IO |
| thread. Blocking I/O belongs on yet another thread (or a pool of worker |
| threads). |
| |
| A little bit of Fusebox-specific D-Bus bureaucracy lives in the |
| `fusebox_service_provider.*` files in a sibling directory, |
| `chrome/browser/ash/dbus`. |
| |
| Fusebox integration with the Files App (ChromeOS' graphical file manager), via |
| its "under the hood" Volume Manager, and related "talk to `cros-disks`" code |
| lives in another sibling directory, `chrome/browser/ash/file_manager`. |
| |
| The Fusebox Client code lives in the [`fusebox` |
| directory](https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform2/fusebox/) |
| in the `platform2` repository. |
| |
| |
| ### `storage` C++ API |
| |
| The Fusebox Server layers over Chrome's `storage` C++ API. The interface's |
| source code lives in the `storage/browser/file_system` directory and backing |
| implementations are elsewhere. For example: |
| |
| - the ADP implementation is in `chrome/browser/ash/arc/fileapi`. |
| - the FSP implementation is in `chrome/browser/ash/file_system_provider/fileapi`. |
| - the MTP implementation is in `chrome/browser/media_galleries/fileapi`. |
| - the 'real' file system implementation is in `storage/browser/file_system`. |
| |
| `storage` was historically designed around serving a [cross-browser JS |
| API](https://developer.mozilla.org/en-US/docs/Web/API/FileSystem), allowing |
| multiple, independent web apps (each running untrusted code) to access |
| persistent storage without interfering with each other. Fusebox uses it (e.g. |
| [`storage::FileSystemURL`](https://source.chromium.org/chromium/chromium/src/+/main:storage/browser/file_system/file_system_url.h)) |
| largely because that's how Chrome's 'Virtual File Systems' are implemented. |
| Some `storage::FileSystemURL` concepts such as their `url::Origin` and |
| `blink::StorageKey` are core to the API but less relevant for Fusebox's use. |
| |
| |
| ## FUSE Handles |
| |
| When the kernel sends the FUSE server an "open" request (and a string path), |
| the response contains a numeric FUSE handle (sometimes abbreviated as `fh`, |
| just like how a numeric file descriptor can be `fd`). Subsequent "read" |
| requests contain the handle but not the path. |
| |
| These FUSE Handle numbers are similar to inode numbers, in that they're |
| server-defined and opaque to the client, but they are not the same. Just as the |
| one file can be opened multiple times, the one inode can be associated with |
| multiple file descriptors (on the 'file system client' side) and multiple FUSE |
| handles (on the 'file system server' side). |
| |
| |
| ## Monikers |
| |
| Monikers are a Fusebox concept (but not a FUSE concept). They are similar to |
| symlinks, in that they are an alternative name to an existing thing. They are |
| unlike symlinks in that the link target *does not otherwise exist* on the |
| 'real' file system. The link target is a `storage::FileSystemURL`. |
| |
| Fusebox Monikers are used for ad-hoc sharing on demand, typically for |
| individual files instead of directories or volumes. They are for "share this |
| one (virtual) file with only this one app" rather than "make this |
| folder-in-the-cloud available as an ambient collection of (virtual) files". |
| |
| Moniker file names look like `/media/fuse/fusebox/moniker/123etc789`. `moniker` |
| is the entire Subdir and the `123etc789` is an unguessable random number. |
| |
| See the `fusebox_moniker.h` comments for more detail. |
| |
| |
| ## Testing |
| |
| As the interesting parts of Fusebox involve multiple processes (Fusebox Client, |
| Fusebox Server and more), we rely more on integration tests (tast) than unit |
| tests. The test code lives in the `platform` repository, under |
| [`tast-tests/src/chromiumos/tast/local/bundles/cros/filemanager/`](https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/tast-tests/src/chromiumos/tast/local/bundles/cros/filemanager/). |
| |
| |
| ## More Information |
| |
| Here are some Google-internal slide decks (the speaker notes also link to video |
| recordings): |
| |
| - [Fusebox Overview](https://goto.google.com/fusebox-deck-2022), October 2022. |
| - [Fusebox Code |
| Walkthrough](https://goto.google.com/fusebox-code-walkthrough-2023), February |
| 2023. |
| - [How to Build ChromiumOS' |
| Fusebox](https://goto.google.com/how-to-build-cros-fusebox), January 2024. |
| |
| There's also [the ChromeOS Files Team site](https://goto.google.com/xf-site) |
| for more general information. It is also Google-internal. |