# 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.