117 lines
10 KiB
Markdown
117 lines
10 KiB
Markdown
# DanOS Filesystem Hierarchy Standard (DFHS)
|
||
|
||
Most modern Unix and Unix-like operating systems follow the FHS. DanOS has its own FHS structure which extends the unix FHS. This is provided by virtual file system driver (VFS).
|
||
|
||
## Directory structure
|
||
|
||
| Path | Description |
|
||
|------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||
| / | Primary hierarchy root and root directory of the entire file system hierarchy. |
|
||
| /bin | Essential command binaries that need to be available in single-user mode, including to bring up the system or repair it, for all users (e.g., cat, ls, cp). |
|
||
| /boot | Boot loader files (e.g., EFI, initial-ramdisk.img ). |
|
||
| /dev | POSIX Device files (e.g., /dev/null, /dev/disk0, /dev/tty, /dev/random). |
|
||
| /etc | Host-specific system-wide configuration files. |
|
||
| /home | Users' home directories, containing saved files, personal settings, etc. |
|
||
| /lib | Libraries essential for the binaries in /bin and /sbin. eg realtime, system, ipc etc. |
|
||
| /sbin | Essential system binaries (e.g init) |
|
||
| /srv | Site-specific data served by this system, such as data and scripts for web servers, data offered by FTP servers, and repositories for version control systems |
|
||
| /system | DanOS operating system files (similar idea to C:\Windows). A true representation of danos — its layout mirrors the source tree, so `/system` is what danos *is*. |
|
||
| /system/devices | danos virtual device tree e.g. similar to /sys on linux but with danos device tree conventions (the structures in the devices module) |
|
||
| /system/drivers | driver binaries, one sub-project each (e.g. /system/drivers/hpet) |
|
||
| /system/services | system-service binaries — the VFS server, init, and other user-mode servers (e.g. /system/services/vfs, /system/services/init) |
|
||
| /system/kernel | the kernel image |
|
||
| /tmp | Directory for temporary files (see also /var/tmp). Often not preserved between system reboots and may be severely size-restricted. |
|
||
| /usr | Secondary hierarchy for read-only user data; contains the majority of (multi-)user utilities and applications. Should be shareable and read-only. |
|
||
| /var | Variable files: files whose content is expected to continually change during normal operation of the system, such as logs, spool files, and temporary e-mail files. |
|
||
|
||
## File types
|
||
|
||
POSIX specifies the long format of the ls command to represent the Unix file type as the first letter for an entry.
|
||
|
||
| type | symbol | Description |
|
||
|-------------------|--------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||
| regular | - | An ordinary file holding an uninterpreted byte stream. Reads and writes are positional, and the file grows on demand (e.g., a binary in /bin, a config file in /etc). |
|
||
| directory | d | A container mapping names to other files. It may only be modified through directory operations, never written to directly. |
|
||
| symbolic link | l | A file whose contents are a path that is resolved in its place. The target need not exist, and may cross mount points. |
|
||
| FIFO special | p | A named pipe: an in-order byte stream between processes, where writers block until a reader opens the other end. |
|
||
| block special | b | A device node addressed in fixed-size blocks with the kernel free to buffer and reorder access (e.g., /dev/disk0). |
|
||
| character special | c | A device node addressed as an unbuffered byte stream, delivered to the driver in order (e.g., /dev/tty, /dev/null). |
|
||
| socket | s | A named endpoint for bidirectional message-passing between processes, bound to a path rather than an address. |
|
||
|
||
## /dev
|
||
|
||
`/dev` holds the names through which processes reach devices. It is deliberately not
|
||
the device tree: the tree — every node discovered by ACPI or PCI enumeration, with its
|
||
resources and its parent — lives under [/system/devices](#directory-structure) and is
|
||
addressed by device id. `/dev` is the much smaller set of devices that have a driver
|
||
willing to serve them, addressed by name.
|
||
|
||
A device node is not a file the VFS can read. The bytes live in a driver process
|
||
([drivers.md](drivers.md)), so opening a `/dev` name has to resolve to that driver's
|
||
IPC endpoint, and subsequent reads and writes are calls against it. This is what
|
||
`system/services/vfs/vfs.zig` reserves for M10 and what the `Stat.kind` field is for; **none of it is
|
||
implemented today.** The current VFS is a flat, in-memory ramfs of eight nodes, with no
|
||
directories at all and `kind` hardcoded to zero. The three sections below describe the
|
||
intended shape, and are honest about which parts the kernel can already support.
|
||
|
||
### Character devices
|
||
|
||
A character device is a byte stream with no addressable position: bytes are delivered
|
||
to the driver in the order written, and a read consumes what is there. Terminals,
|
||
serial lines, keyboards and mice are all of this shape. These are the natural first
|
||
device nodes in danos, because a character driver needs nothing the kernel doesn't
|
||
already provide — it claims its device, maps its registers with `mmio_map`, and blocks
|
||
on `replyWait` for either an interrupt or a client request. `system/drivers/hpet/hpet.zig` is already
|
||
that program, minus the client half.
|
||
|
||
The obstacle is not the file type, it is which hardware a ring-3 driver can actually
|
||
drive. Port I/O is unavailable to user space — the TSS I/O permission bitmap is absent
|
||
and IOPL is never raised — so `in`/`out` from a driver is a #GP. That excludes the
|
||
16550 UART at `0x3F8` and PS/2 at `0x60`/`0x64`, which is to say it excludes the
|
||
obvious implementations of `/dev/tty`, `/dev/ttyS0` and a keyboard node. Until either
|
||
port I/O grants or a memory-mapped UART exist, serial output stays a kernel service
|
||
reached through the `write` system call rather than a file. A memory-mapped device such
|
||
as the framebuffer has no such problem and is the more likely first real entry here.
|
||
|
||
### Block devices
|
||
|
||
A block device is addressed in fixed-size blocks and, unlike a character device, the
|
||
layer above is free to buffer, reorder, coalesce and retry requests against it. Disks
|
||
and other persistent storage are the whole population of this class.
|
||
|
||
**danos cannot host a block driver at all today,** and the reason is worth stating
|
||
plainly because it is not a matter of unwritten code. Every storage controller worth
|
||
naming is a bus master: it is programmed by handing it the physical address of a
|
||
descriptor ring and left to read and write memory on its own. A ring-3 driver cannot
|
||
build such a ring, because `mmap` returns writeback-cached, physically discontiguous
|
||
pages and never discloses their physical address. Nor should it be allowed to: a device
|
||
programmed with an arbitrary physical address writes to arbitrary physical memory, and
|
||
page tables do not sit between a device and RAM — an IOMMU does. Granting a DMA-capable
|
||
device to a driver process, with no IOMMU programmed, is equivalent to granting ring 0,
|
||
which would forfeit the isolation that motivates user-space drivers in the first place.
|
||
|
||
Block devices therefore wait on DMA-capable memory, memory barriers, and VT-d/DMAR —
|
||
the M14–M16 work in [driver-model.md](driver-model.md). A ramdisk over the initial ramdisk is
|
||
the one block-shaped thing implementable now, and it needs no driver process.
|
||
|
||
### Pseudo-devices
|
||
|
||
A pseudo-device has the interface of a device and no hardware behind it: `/dev/null`
|
||
discarding writes and reading as end-of-file, `/dev/zero` reading as an endless run of
|
||
zero bytes, `/dev/full` failing writes with `ENOSPC`, `/dev/random` and `/dev/urandom`
|
||
yielding unpredictable bytes.
|
||
|
||
These are the only `/dev` entries danos can implement immediately, and they are the
|
||
sensible place to start, because they are exactly the entries that need no driver
|
||
process, no `device_claim`, no MMIO grant and no interrupt. The VFS server answers them
|
||
out of its own address space — `null` and `zero` are a few lines each in
|
||
`system/services/vfs/vfs.zig`'s `read` and `write` handlers. Doing so forces the two pieces of
|
||
structure that every later device node depends on and that the flat ramfs currently
|
||
lacks: a directory, so that `/dev/null` is a path rather than a name; and a populated
|
||
`Stat.kind`, so that a caller can tell a character device from a regular file.
|
||
|
||
`/dev/random` is the one that is not free. It needs an entropy source, and the honest
|
||
options on this kernel are `RDRAND`/`RDSEED` where CPUID advertises them, and the HPET
|
||
counter's low bits as a poor fallback. Neither is a seeded CSPRNG, and a `/dev/random`
|
||
that is merely unpredictable-looking is worse than none — nothing should be keyed from
|
||
it until it is a real one. |