doos 1.0.0: images, size cap, subuid ids, working apt in host mode
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
+47
-16
@@ -7,11 +7,14 @@ How doos works internally. Behavior contract is `SPEC.md`; this explains the cod
|
||||
| File | Class / role |
|
||||
|---|---|
|
||||
| `src/spec.hpp` | `spec::` constants: caps, mount lists, device list, paths. Single source of truth with `SPEC.md`. |
|
||||
| `src/util.{hpp,cpp}` | `SysError` (errno-carrying exception), `Fd` (RAII fd), `FileLock` (bounded flock), `fsutil` (mkdirs, read/write, bounded `removeTree`). |
|
||||
| `src/util.{hpp,cpp}` | `SysError`, `Fd` (RAII fd), `FileLock` (bounded flock), `parseSize`/`formatSize`, `runProcess` (fork/exec, capped capture), `fsutil` (mkdirs, read/write, bounded `removeTree`, `mirrorDirs` skeleton, `copyTree`, `copyFiles`, `diskUsage` with hardlink dedupe). |
|
||||
| `src/idmap.{hpp,cpp}` | `IdMap` (single or subuid mapping via newuidmap/newgidmap, subuid range lookup), `UserNsJob` (run a function as root of a user namespace with the same map: image extraction, deleting subuid-owned trees). |
|
||||
| `src/image.{hpp,cpp}` | `Catalog` (alpine, ubuntu resolution from publishers), `ImageStore` (add with download, sha256 check, extraction; list; delete). |
|
||||
| `src/cgroup.{hpp,cpp}` | `Cgroup`: one cgroup v2 leaf per running container under the user's delegated cgroup, used for `cgroup.freeze`. |
|
||||
| `src/network.{hpp,cpp}` | `NetMode` + `Network` (parse, resolv.conf per mode), `SlirpSession` (owns the slirp4netns process). |
|
||||
| `src/container.{hpp,cpp}` | `Container` (one container's paths, meta, pid file, layout), `Store` (list, get_or_create, remove). |
|
||||
| `src/sandbox.{hpp,cpp}` | `Sandbox`: start a container (clone + mounts + pivot_root + init) or join a running one (setns). |
|
||||
| `src/cli.{hpp,cpp}` | `Cli`: argument parsing and the four commands. |
|
||||
| `src/container.{hpp,cpp}` | `Options` (what the user asked for), `Container` (paths, meta, pid file, layout, one-time seeding, usage), `Store` (list, get_or_create with on-demand image fetch, remove). |
|
||||
| `src/sandbox.{hpp,cpp}` | `Sandbox`: start a container (clone + id map + mounts + pivot_root + init), watch it (size cap), or join a running one (setns). |
|
||||
| `src/cli.{hpp,cpp}` | `Cli`: argument parsing, the four container commands and `image list/add/delete`. |
|
||||
| `src/main.cpp` | Catches exceptions, prints `doos: <message>`, exit 1. |
|
||||
| `tests/run.sh` | End-to-end suite. |
|
||||
| `scripts/release.sh` | Bump, build, test, commit, tag. |
|
||||
@@ -28,13 +31,15 @@ Cli::cmdAttach
|
||||
|
||||
start():
|
||||
clone(NEWUSER|NEWNS|NEWPID|NEWUTS|NEWIPC[|NEWNET]) child = container pid 1
|
||||
parent writes /proc/<child>/{setgroups,uid_map,gid_map} 0 -> invoking uid, size 1
|
||||
IdMap::apply(child, ids) single: write /proc maps; subuid: newuidmap + newgidmap
|
||||
slirp mode: SlirpSession.start(child) waits for ready-fd
|
||||
Cgroup::create + add(child) best effort; without it the size cap only warns
|
||||
recordPid(child) (pid + starttime, defeats pid reuse)
|
||||
lock.unlock() joiners must not wait for container lifetime
|
||||
write 'G' on sync pipe child continues
|
||||
parent: ignore SIGINT/SIGQUIT, forward SIGTERM/SIGHUP, waitpid(child)
|
||||
on exit: pid file cleared, slirp stopped (RAII)
|
||||
parent: ignore SIGINT/SIGQUIT, forward SIGTERM/SIGHUP, watch(child)
|
||||
watch: poll(pidfd, 10 s); each wake scans usage -> warn 80%, freeze 100%, thaw <95%
|
||||
on exit: pid file cleared, slirp stopped, cgroup removed (RAII)
|
||||
|
||||
child (containerInit, pid 1):
|
||||
PDEATHSIG=SIGKILL, close inherited lock fd, wait for 'G'
|
||||
@@ -45,15 +50,41 @@ child (containerInit, pid 1):
|
||||
|
||||
## setupRoot order
|
||||
|
||||
1. `MS_REC|MS_PRIVATE` on `/` so nothing propagates to the host.
|
||||
2. Bind the container `rootfs/` onto itself (pivot_root needs a mountpoint).
|
||||
3. Write `resolv.conf` into the `/etc` upper dir (matches network mode every start).
|
||||
4. Overlay mounts (`lowerdir=<host dir>,upperdir,workdir,userxattr`, nosuid, nodev).
|
||||
5. Compat dirs: recreate host symlinks (usr-merge) or overlay real dirs.
|
||||
6. Bind private dirs (`/root`, `/var`), mount tmpfs (`/tmp`, `/run`).
|
||||
7. `/dev`: tmpfs, bind host device nodes, new devpts, `/dev/shm`, symlinks.
|
||||
8. `/proc`, then read-only remount and masks from `spec.hpp`.
|
||||
9. `chdir(rootfs)`, `pivot_root(".", ".")`, lazy unmount of old root.
|
||||
Common start: `MS_REC|MS_PRIVATE` on `/`, then `resolv.conf` is written into the container's `/etc` location
|
||||
(`Container::etcDir`: `private/etc` in host mode, the root upper layer in image mode), and on a single-id
|
||||
container with apt present an `APT::Sandbox::User "root"` override is written next to it.
|
||||
|
||||
Host mode:
|
||||
1. Bind the container `rootfs/` onto itself (pivot_root needs a mountpoint).
|
||||
2. Overlay mounts for `/usr`, `/opt` and real compat dirs (`lowerdir=<host dir>,upperdir,workdir,userxattr`).
|
||||
3. Compat symlinks recreated (usr-merge).
|
||||
4. Bind the private dirs `/root`, `/var`, `/etc`; then the overlay for `/var/lib/dpkg` inside the private `/var`.
|
||||
|
||||
Image mode:
|
||||
1. One overlay on `rootfs/`: lower = `images/NAME/rootfs`, upper = `layers/root/upper`.
|
||||
2. Mountpoints (`/proc /dev /tmp /run ...`) created inside the merged tree.
|
||||
|
||||
Both modes then: tmpfs `/tmp` and `/run`; `/dev` (tmpfs, bound host nodes, new devpts, `/dev/shm`, symlinks);
|
||||
`/proc` with read-only remounts and masks from `spec.hpp`; `chdir(rootfs)`, `pivot_root(".", ".")`, lazy
|
||||
unmount of the old root.
|
||||
|
||||
## Seeding (host mode, once per container)
|
||||
|
||||
`Container::prepareLayout` runs on create and as a heal on every later load; each step leaves a
|
||||
`seeded.<name>` marker so it runs once and prints what it did and what it skipped:
|
||||
1. Directory skeleton of every overlaid host dir into its upper layer (`fsutil::mirrorDirs`).
|
||||
2. Regular files of `/var/lib/dpkg`, `triggers`, `alternatives` copied into the upper (`copyFiles`), then empty lock files.
|
||||
3. `/etc` copied into `private/etc` (`copyTree`).
|
||||
|
||||
Reasons are in `SPEC.md` (host root-owned files appear as `nobody`).
|
||||
|
||||
## Images
|
||||
|
||||
`ImageStore::add`: lock-free staging dir `images/.tmp-NAME`, download with curl (https only, size and time caps),
|
||||
sha256 check, `tar -x -p --numeric-owner` executed by `UserNsJob` with the image's id mode (`--no-same-owner`
|
||||
for single), usage measured, `meta` written, atomic `rename` into place. Any failure removes the staging dir
|
||||
from inside the id map, so a failed add leaves nothing. `Store::getOrCreate` calls it when a catalog image is
|
||||
missing, and pins the container's id mode to the image's.
|
||||
|
||||
## join flow
|
||||
|
||||
|
||||
Reference in New Issue
Block a user