Skip to content

Commit 62e6517

Browse files
authored
Merge pull request #4 from stupendoussuperpowers/imfs_grate
Add Example Grate: In Memory File System
2 parents 6bb38ae + d1ad2c6 commit 62e6517

File tree

7 files changed

+2710
-0
lines changed

7 files changed

+2710
-0
lines changed

examples/imfs-grate/README.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
## In Memory File System
2+
3+
[Upstream Repository](https://github.com/stupendoussuperpowers/imfs)
4+
5+
The In Memory File System (IMFS) provides a self-contained implementation of a POSIX-like FS backed by memory. It serves as a backbone that can later be integrated as a grate to sandbox any FS calls made by a cage. IMFS exposes POSIX-like APIs and maintains its own inode and file descriptor tables to provide an end-to-end FS interface.
6+
7+
New implementations to IMFS are usually tested in a sandboxed manner on Linux natively, before being tested in `lind-3i` with a grate function wrapping the new functionality.
8+
9+
### File System APIs
10+
11+
IMFS mirrors POSIX system calls with an added `cageid` parameter. For example:
12+
13+
```
14+
open(const char* pathname, int flags, mode_t mode)
15+
->
16+
imfs_open(int cageid, const char* pathname, int flags, mode_t mode)
17+
```
18+
19+
The behaviours of these APIs closely match those of their corresponding Linux system calls. They follow the semantics described in man pages including types, return values, and error codes. This allows easy integration of IMFS into a grate, and allows for easy testing on native environments.
20+
21+
When running this module on Linux, the `cageid` parameter should be stubbed as a constant between `[0,128)`, like so:
22+
23+
```
24+
#define CAGEID 0
25+
26+
int fd = imfs_open(CAGEID, "/testfile.txt", O_RDONLY, 0);
27+
imfs_close(CAGEID, fd);
28+
```
29+
### Utility Functions.
30+
31+
In addition to POSIX APIs, IMFS also provides helper functions for moving files in and out of memory.
32+
33+
- `load_file(char *path)` Load a single file into IMFS at `path`, recursively creating any required folders.
34+
35+
- `dump_file(char *path, char *actual_path)` Copy IMFS file at `path` to the host filesystem at `actual_path`
36+
37+
- `preloads(char *preload_files)` Copy files from host to IMFS, `preload_files` being a `:` separated list of filenames.
38+
39+
These utility functions are called before executing any child cages, and after they exit. The IMFS grate is responsible for calling these to stage files into memory (`load_file`, `preloads`) and to persist results back (`dump_file`).
40+
41+
In the accompanying example grate, the grate reads the environment variables `"PRELOADS"` to determine which files are meant to be staged.
42+
43+
## Implementation
44+
45+
### Inodes
46+
47+
IMFS maintains an array of `Node` objects each of which serve as an inode to represent an FS object (file, directory, symlink, or pipe). Allocation of nodes is performed using a free-list mechanism along with a pointer that tracks the next available slot within the array.
48+
49+
The structure of the node is specialized according to its type:
50+
51+
- Directories contain references to child nodes.
52+
- Symlinks maintain a pointer to the target node.
53+
- Regular files store data in fixed-sized `Chunk`s, each of which store 1024 bytes of data. These chunks are organized as a singly linked list.
54+
55+
### File Descriptors
56+
57+
Each cage has its own array of `FileDesc` objects that represent a file descriptor. The file descriptors used by these FS calls are indices into this array.
58+
59+
File descriptor allocation begins at index 3. The management of standard descriptors (`stdin`, `stdout`, `stderr`) are delegated to the enclosing grate.
60+
61+
Descriptors are allocated using `imfs_open` or `imfs_openat`. Each file descriptor object stores:
62+
63+
- A pointer to the associated node.
64+
- The current file offset.
65+
- Open flags
66+
67+
## Building
68+
69+
Build Requirements:
70+
71+
- `make`
72+
- Python3 for tests
73+
74+
### Native Build
75+
76+
- `make lib` to build as a library
77+
- `make imfs` to build with the main function
78+
- `make debug` build with debug symbols
79+
80+
### Lind Integration Build
81+
82+
The following compile flags are required to compile IMFS for a Lind build:
83+
84+
- `-DLIB` omit the main function
85+
- `-DDIAG` to enable diagnostic logging
86+
- `-D_GNU_SOURCE` needed to support `SEEK_HOLE` and `SEEK_DATA` operations in `imfs_lseek()`
87+
88+
## Grate Integration
89+
90+
The grate implementation currently provides syscall wrappers for the following FS syscalls:
91+
92+
- [`open`](https://man7.org/linux/man-pages/man2/open.2.html)
93+
- [`close`](https://man7.org/linux/man-pages/man2/close.2.html)
94+
- [`read`](https://man7.org/linux/man-pages/man2/read.2.html)
95+
- [`write`](https://man7.org/linux/man-pages/man2/write.2.html)
96+
- [`fcntl`](https://man7.org/linux/man-pages/man2/fcntl.2.html)
97+
98+
## Testing
99+
100+
POSIX compliance is validated through `pjdfstest`, a widely adopted test suite for file systems for both BSD and Linux file systems. The tests are executed natively on Linux, which required modifications to `pjdfstest` in order to support a persistent test runner capable of maintaining FS state in memory.
101+
102+
`pdjfstest` provides a comprehensive list of assertions each designed to verify a specific FS property. This approach allows for easier detection of edge-cases.
103+
104+
The test suite is invoked using:
105+
106+
- `make test` run all tests
107+
- `make test-<feature>` run all tests in a particular feature
108+
109+
## Example Usage: Running `tcc` with IMFS Grate
110+
111+
Check out the documentation [here](https://github.com/stupendoussuperpowers/lind-wasm/tree/ea95e1742c4c497ae7d859603869d8612f695ad7/imfs_grate).
112+
113+
## Future Work
114+
115+
- Currently only a handful of the most common logical branches are supported for most syscalls. For example, not all flags are supported for `open`.
116+
- Access control is not implemented, by default all nodes are created with mode `0755` allowing for any user or group to access them.
117+
- `mmap` is yet to be implemented.
118+
- Performance testing for reading and writing.
119+
- Integrating FD table management with `fdtables` crate.
120+

examples/imfs-grate/build.conf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
ENTRY=imfs_grate.c
2+
MAX_MEMORY=1570242560
3+
EXTRA_CFLAGS="-D_GNU_SOURCE -DLIB"
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/usr/bin/env bash
2+
3+
# Usage: ./compile_grate.sh <example-dir>
4+
#
5+
# Builds and outputs a WebAssembly binary for lind.
6+
#
7+
# Expected directory structure:
8+
# <example-dir>/
9+
# ├── build.conf (Required: ENTRY, Optional: MAX_MEMORY, EXTRA_CFLAGS)
10+
# └── src/
11+
# └── *.c (Source files to compile)
12+
#
13+
# Outputs to <example-dir>/output/:
14+
# - <entry>.wasm
15+
# - <entry>.cwasm
16+
17+
set -euo pipefail
18+
19+
if [[ $# -ne 1 ]]; then
20+
echo "Usage: $0 <example-dir>"
21+
exit 1
22+
fi
23+
24+
TARGET="$1"
25+
26+
# Enter the example directory
27+
pushd "$TARGET" >/dev/null
28+
29+
# Now everything is relative to the example dir
30+
echo "[cwd] $(pwd)"
31+
32+
# Load per-example config
33+
if [[ ! -f build.conf ]]; then
34+
echo "missing build.conf"
35+
exit 1
36+
fi
37+
source build.conf
38+
39+
CLANG_DIR="${CLANG:-/home/lind/lind-wasm/clang+llvm-18.1.8-x86_64-linux-gnu-ubuntu-18.04}"
40+
CLANG="$CLANG_DIR/bin/clang"
41+
SYSROOT="${SYSROOT:-/home/lind/lind-wasm/src/glibc/sysroot}"
42+
WASM_OPT="${WASM_OPT:-/home/lind/lind-wasm/tools/binaryen/bin/wasm-opt}"
43+
WASMTIME="${WASMTIME:-/home/lind/lind-wasm/src/wasmtime/target/release/wasmtime}"
44+
45+
SRC_DIR="src"
46+
mkdir -p output
47+
OUT="output/${ENTRY%.c}"
48+
49+
MAX_MEMORY="${MAX_MEMORY:-268435456}"
50+
EXTRA_CFLAGS="${EXTRA_CFLAGS:-}"
51+
EXTRA_WASM_OPT="${EXTRA_WASM_OPT:-}"
52+
53+
echo "[build] $OUT (max-mem=$MAX_MEMORY)"
54+
55+
"$CLANG" -pthread \
56+
--target=wasm32-unknown-wasi \
57+
--sysroot "$SYSROOT" \
58+
-Wl,--import-memory,--export-memory,--max-memory="$MAX_MEMORY",\
59+
--export=__stack_pointer,--export=__stack_low,--export=pass_fptr_to_wt \
60+
$EXTRA_CFLAGS \
61+
"$SRC_DIR"/*.c \
62+
-g -O0 -o "$OUT.wasm"
63+
64+
"$WASM_OPT" \
65+
--asyncify \
66+
--epoch-injection \
67+
--debuginfo \
68+
$EXTRA_WASM_OPT \
69+
"$OUT.wasm" -o "$OUT.wasm"
70+
71+
"$WASMTIME" compile "$OUT.wasm" -o "$OUT.cwasm"
72+
73+
# Return to original directory
74+
popd >/dev/null

0 commit comments

Comments
 (0)