Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ members = [
"sdk/icon",
"sdk/keymap",
"sdk/libc/arch",
"sdk/std/copies",
"sdk/osrelease",
"sdk/toyos",
"sdk/window",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ opened: 2026-09-27
# A file held across a file server's restart answers Gone

No volume records an object id, so nothing tells the held file from another
put at its path since, and a handle held across a restart answers `Gone`
(`std`'s `sdk/std/sys/fs.rs`); `logd` loses `/log` for the boot if LOG's server
restarts.
put at its path since, and a file held across a restart answers `Gone`
(`toyos::files`, so std's `File` and a C descriptor alike); `logd` loses
`/log` for the boot if LOG's server restarts.

**Exit**: DATA's entry carries an object id and a generation that nothing
reuses, a held handle reopens only when both match, and a host test that
Expand Down
24 changes: 24 additions & 0 deletions issues/a-full-kernel-handle-table-answers-enomem-not-emfile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
status: open
kind: defect
opened: 2026-10-21
---

# A full kernel handle table answers ENOMEM, not EMFILE

The kernel answers a full handle table `ResourceExhausted`
(`kernel/src/object/handle.rs`, `HandleError::TableFull`), the same word it
answers for no memory. libc can only answer that `ENOMEM`
(`sdk/libc/src/errcode.rs`), and std `OutOfMemory`. So an `open`, `pipe` or
`/dev/null` that a full kernel table refuses says `ENOMEM`, where POSIX has
`EMFILE` for a process that holds as many descriptors as it may. The kernel's
table is the binding limit for descriptors that are kernel handles: it holds
as many slots as `toyos::fd::MAX_FDS`, and the process's own handles take some
of them. Corpus case `212_full_table` asserts the `ENOMEM`.

A word of the kernel's own for a full table would let libc say `EMFILE`. The
owner ruled no kernel change for POSIX alone, so this waits on a reason the
kernel's own callers have to tell the two apart.

**Exit**: a full handle table and no memory are two answers at the kernel
boundary, libc answers the first `EMFILE`, and `212_full_table` expects it.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The kernel resolves a symlink on its own mounts — ROOT and `/tmp` — in its o
tree (`kernel/src/vfs.rs`), and its tree holds nothing under `/apps`,
`/config`, `/home`, `/state`, `/log` or `/boot` but ROOT's empty directories:
those are file servers', reached through a directory capability
(`sdk/std/sys/fs.rs`). So a link in `/tmp` whose target is
(`toyos::files`, which std and libc share). So a link in `/tmp` whose target is
`/home/toy/notes` opens nothing (`NotFound`), where the same link on a served
directory is followed — a file server hands an absolute target back to the
client, which resolves it in its own table.
Expand Down
21 changes: 21 additions & 0 deletions issues/a-served-file-at-stdio-stops-every-spawn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
status: open
kind: defect
opened: 2026-10-21
---

# A served file at stdio stops every spawn

A file a file server holds is a descriptor naming an object of
`toyos::files`', not a kernel handle, and `toyos::fd::copy` refuses one for a
child (`EOPNOTSUPP`, std's `Unsupported`). libc's `posix_spawn` asks for
descriptors 0 to 2 on every spawn (`spawnreq::plan`), so once a C program has
put a served file there, by `dup2` or `freopen`, every `posix_spawn`,
`system` and `popen` it makes is refused. So is an `adddup2` of a served
descriptor and an `addopen` of a served path, and std's `Command` with
inherited stdio in the same case. Before served files were descriptors, no
served file could sit at 0 to 2. Read from the code; no guest case has run it.

**Exit**: a served file can be given to a child, as PR 4 of the descriptor
work gives served kinds (`EXPORT`), and a guest C case redirects stdout to a
file under `/home`, runs `system`, and reads the child's output back from it.
20 changes: 0 additions & 20 deletions issues/a-served-file-panics-when-asked-its-raw-fd.md

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ opened: 2026-10-01

Read from the code, not run. The kernel's `/tmp` (`kernel/src/tmpfs.rs`)
makes a link over whatever file its name held, where POSIX's `symlink` refuses
a name that exists `EEXIST`, so libc's `symlink` refuses `ENOSYS`
(`sdk/libc/src/refused.rs`). Its `list` answers files alone, so no
`readdir` of a directory shows a link it holds.
a name that exists `EEXIST`, so libc's `symlink` refuses `ENOSYS` for a link
the kernel would hold (`sdk/libc/src/posix_io.rs`). Its `list` answers files
alone, so no `readdir` of a directory shows a link it holds.

**Exit**: `symlink` is the file server's, not the kernel's. libc's `symlink`
sends the request to the server of the directory that is to hold the link, as
std's does (`on_path` in `sdk/std/sys/fs.rs`), with nothing
asked first, and answers its `AlreadyExists` `EEXIST`. The file server makes the link in
the one request that refuses a name that exists, and libc's `readdir` of that
directory names the link, each asserted by a test. The kernel's `SYS_SYMLINK`
libc's `symlink` already sends a link a file server is to hold to that
server, through `toyos::files` as std's does, which refuses a name that
exists, answered `EEXIST`, and `readdir` there names the link; corpus case
`211_home_files` asserts both under `/home`. What is left is `/tmp`.

**Exit**: `/tmp` is a file server's, so every `symlink` is one, sent with
nothing asked first and answered `EEXIST` for a name that exists, and libc's
`readdir` of `/tmp` names the link, each asserted by a test. The kernel's `SYS_SYMLINK`
and its symlink code (`kernel/src/vfs.rs`, `kernel/src/tmpfs.rs`) go wherever
nothing calls them. That `EEXIST` is how LLVM's `LockFileManager` takes its
lock (`create_link`, `::symlink` in `llvm/lib/Support/Unix/Path.inc`). An LLVM
Expand Down
22 changes: 22 additions & 0 deletions issues/an-fsync-of-a-served-file-syncs-its-whole-volume.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
status: open
kind: defect
opened: 2026-09-27
---

# An fsync of a served file syncs its whole volume

The file server answers `FSYNC` by syncing the volume, every open file's
entry and every dirty block of the cache (`system/fileserver/src/main.rs`,
`FSYNC`, which calls `sync` with the file's node and syncs the whole volume). So an
`fsync` or `File::sync_all` of one file on `/home` syncs all of DATA, and a
4 KiB create and fsync cost 3.5–3.6 ms p50 on QEMU TCG against the kernel's
1.0–1.3 ms, measured on the branch that moved DATA to the file server.

std's ToyOS `File::flush` is a no-op, as it is on every other platform: a
`File` buffers nothing, since each `write` is the server's when it answers
(`toyos::files`), so a flush has nothing to hand on and `Write::flush` asks
for no durability.

**Exit**: `FSYNC` makes durable the file it names and what that file's entry
depends on, with a guest measurement of one file's fsync beside the volume's.
19 changes: 0 additions & 19 deletions issues/c-programs-name-no-file-a-file-server-holds.md

This file was deleted.

16 changes: 9 additions & 7 deletions issues/create-new-on-a-kernel-path-is-not-exclusive.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ opened: 2026-09-27

# `create_new` on a kernel path is not exclusive

std's `OpenOptions::create_new(true)` promises an open that fails with
`AlreadyExists` when the file is there. std's ToyOS `to_flags`
(`sdk/std/sys/fs.rs`) turns `create_new` into the kernel's
std's `OpenOptions::create_new(true)` and C's `open` with
`O_CREAT|O_EXCL` promise an open that fails when the file is there
(`AlreadyExists`, `EEXIST`). Both reach `toyos_fs_open`'s kernel arm
(`sdk/toyos/src/files/owner.rs`), which turns `create_new` into the kernel's
plain `OpenFlags::CREATE`, and the kernel has no exclusive flag to turn it
into, so on `/tmp` a second `create_new` of one path opens the file the first
made and says nothing. A served path does not share it: the file protocol
carries `O_CREATE_NEW` and the file server refuses. Found when fsd's test actuator took two
into. So on `/tmp` a second exclusive create of one path opens the file the
first made and says nothing, and a C lock file made that way locks nothing. A
served path does not share it: the file protocol carries `O_CREATE_NEW` and
the file server refuses. Found when the file server's test actuator took two
`create_new`s of one `/tmp` mark from two processes as two first times.

**Exit**: an exclusive create reaches the kernel's `/tmp` and is refused
`AlreadyExists` there, with a guest test that makes one path twice with
`create_new` and has the second refused.
`create_new` and with `O_CREAT|O_EXCL` and has each second one refused.
21 changes: 0 additions & 21 deletions issues/every-flush-of-a-served-file-syncs-its-whole-volume.md

This file was deleted.

6 changes: 4 additions & 2 deletions issues/libc-refuses-what-toyos-cannot-yet-answer.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ These answer failure in their POSIX form and do nothing
`tests/corpus/tinycc/206_libc_refusals.c`. Each waits on what it names:

- `link`, `ENOSYS`: no call gives a file a second name.
- `symlink`, `ENOSYS`: `SYS_SYMLINK` displaces what its name held
(`issues/a-symbolic-link-on-tmp-displaces-its-name-and-lists-nowhere.md`).
- `symlink` of a link the kernel would hold, `ENOSYS`: `SYS_SYMLINK`
displaces what its name held
(`issues/a-symbolic-link-on-tmp-displaces-its-name-and-lists-nowhere.md`). A
file server makes one and refuses a name that exists, as POSIX has it.
- `chmod` and `fchmod`, `ENOSYS`: a file has no mode bits to set.
- `utimes`, `ENOSYS`: no call sets a file's times.
- `statvfs` and `fstatvfs`, `ENOSYS`: no call answers a filesystem's size or
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ kernel's part of a restart, and named module tests hold each program's

## What is left, in order

1. **Step 9's file half** (stage 4), once libc is a file server's client
(`issues/c-programs-name-no-file-a-file-server-holds.md`).
1. **Step 9's file half** (stage 4). libc is a file server's client
through `toyos::files`, as std is.
2. **Step 8's exit** (stage 4). Its empty view needs step 9's file half:
until then the kernel serves `/system` and `/tmp` to every process.
3. **P1 of the Wi-Fi track** (`issues/there-is-no-wifi.md`): a BAR stays
Expand Down Expand Up @@ -157,9 +157,8 @@ Beside that order, each waiting on something outside it:
`fs_rename.rs`), the page cache (`file_cache.rs`) and the file
syscalls. `/tmp` leaves the kernel per session, as the owner ruled
("Per session", 2026-10-03, in the isolation track), and `/system` is
read over the file protocol, which
`issues/c-programs-name-no-file-a-file-server-holds.md`'s exit puts
after libc is a file server's client. **Exit**: `tmpfs.rs`,
read over the file protocol, which std and libc both reach through
`toyos::files`. **Exit**: `tmpfs.rs`,
`fs_rename.rs` and `file_cache.rs` are gone, `vfs.rs` resolves ROOT
for exec and `SYS_DLOPEN` alone, and `sdk/abi` has no `SYS_OPEN` (9),
`SYS_STAT` (16), `SYS_READDIR` (17), `SYS_DELETE` (18), `SYS_CHDIR`
Expand Down
8 changes: 5 additions & 3 deletions issues/the-kernel-resolves-a-path-by-rules-of-its-own.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ A path on the kernel's own mounts, ROOT and `/tmp`, is resolved by
A file server resolves its own paths POSIX's way, with a bound of its own
(`system/fileserver/src/resolve.rs`, `MAX_LINKS`), so the same link reads
differently on `/tmp` and on a served directory. libc's `realpath`
(`sdk/libc/src/resolve.rs`) answers by the kernel's rules, so that a
program that canonicalises a path and then opens it opens the same file;
`sdk/libc/arch/src/path_resolution.rs` pins those rules.
(`sdk/libc/src/resolve.rs`, `resolve_path`) resolves the names a file server
holds POSIX's way and hands the kernel the rest at the first name it holds,
answering that part by the kernel's rules, so that a program that
canonicalises a path and then opens it opens the same file;
`sdk/libc/arch/src/path_resolution.rs` pins both.

**Owner**: the kernel VFS, `kernel/src/vfs.rs`.

Expand Down
4 changes: 2 additions & 2 deletions lib/manifest/src/grants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ pub const MAX_STORE_BYTES: usize = HEADER.len() + 1 + MAX_ENTRIES * MAX_LINE;
/// `toyos::fs::MAX_GRANT_ROOT` leaves, which the supervisor asserts.
pub const MAX_ROOT: usize = 54;

/// The deepest folder, in components: std finds a capability by probing at
/// most this many (`MAX_CAPABILITY_DEPTH` in `sdk/std/sys/fs.rs`).
/// The deepest folder, in components: std and libc find a capability by
/// probing at most this many (`toyos::files::MAX_CAPABILITY_DEPTH`).
pub const MAX_DEPTH: usize = 4;

const MAX_LINE: usize = "grant ".len() + crate::USER.len() + 1 + MAX_PROGRAM_NAME + 1 + DIGEST_LEN + 1 + 11 + 1 + MAX_ROOT + 2;
Expand Down
2 changes: 1 addition & 1 deletion rust
Submodule rust updated 1 files
+1 −0 library/Cargo.lock
Loading