A userspace union filesystem built with FUSE in Go
- Linux
- FUSE support enabled on the host
- Go 1.26+
From repository root:
make build./onionfs -l <lower_dir> -u <upper_dir> -m <mountpoint> [flags]Required:
| Flag | Description |
|---|---|
-l, --lower |
lower (read-only) directory |
-u, --upper |
read-write layer directory |
-m, --mountpoint |
where the merged view is mounted |
Optional:
| Flag | Description |
|---|---|
--show-meta |
show internal .wh.* whiteout files in listings |
--version |
print version and exit |
-h, --help |
print help |
Note: Currently all three paths must already exist otherwise OnionFS exits with an Error
flowchart LR
U[Upper Layer<br/>read-write]
L[Lower Layer<br/>read-only]
F[OnionFS FUSE]
M[Mounted View]
U --> F
L --> F
F --> M
R[Read path] --> F
F --> RU{Exists in upper?}
RU -->|yes| U
RU -->|no| RL{Exists in lower?}
RL -->|yes| L
RL -->|no| ENOENT[ENOENT]
W[Write path] --> CW[Copy-up if source is lower]
CW --> U
D[Delete path] --> WH[Create .wh.<name> in upper when needed]
WH --> U
Every operation starts by resolving the virtual path through these steps in order:
- If
upper/.wh.<name>exists, file is deleted, returnENOENT - If
upper/<path>exists, serve from upper - If
lower/<path>exists, serve from lower - Otherwise
ENOENT
When a lower-layer file is opened for write or modified via setattr paths, OnionFS copies it to upper first, then applies mutations to the upper copy.
Deleting through the mountpoint:
- removes upper copy if present
- creates
.wh.<name>in upper when lower has the object
This hides the lower object from the merged view without deleting data from lower
- Merges entries from upper and lower
- Excludes whiteout targets
- Hides
.wh.*files by default - Shows
.wh.*files inls -lawhen--show-metais set
This project is open-source and available under the MIT License