diff --git a/include/fs.h b/include/fs.h index c7b2b4c..4831a71 100644 --- a/include/fs.h +++ b/include/fs.h @@ -14,13 +14,144 @@ #define fsTypeFile 1 #define fsTypeDir 2 +#define fsTypeFifo 3 + +/* Real POSIX permission bits (chmod(2)/open(2)'s access check). This kernel + now has a real (if minimal) uid AND gid model -- sched.h's Thread.uid/ + Thread.gid, vfs.c's vfsPermitted -- so all three (owner/group/other) + triads are genuinely stored AND enforced. A mode value like 0750 is + accepted and echoed back by vfsStat/userStat exactly as given. Enforced + at exactly two real choke points, both uid+gid-aware via vfsPermitted: + resolveOpen (vfs.c) refuses to open an existing file for writing without + the right write bit for the CALLING thread's identity, and + loadImageByPath/jobctlSpawn (syscall.c/jobctl.c) refuse to exec a file + without the right exec bit -- both real, both checked at open/spawn time + only, never re-checked against an fd/process already granted (matching + real Unix). Deliberately no uid-0-bypasses-everything superuser rule + like real Unix's root has -- see vfs.c's vfsPermitted doc comment for + why a from-scratch bypass would have silently broken every permission + test already relying on chmod denying write even to a file's own + creator (every boot-time process starts at uid 0). */ +#define fsModeOwnerRead 0400u +#define fsModeOwnerWrite 0200u +#define fsModeOwnerExec 0100u + +/* The "group" triad -- vfsPermitted's middle fallback, consulted once a + caller's uid fails to match a node's owner uid but its GID (sched.h's + Thread.gid) matches the node's own group gid (fs.h's FbStat.gid). Each + bit is its own owner bit shifted right by 3 (real Unix's own rwxrwxrwx + bit layout: owner at bits 8-6, group at bits 5-3, other at bits 2-0), + the same derivation fsModeOther* below already used for the next shift + down -- vfsPermitted computes both from wantOwnerBit rather than storing + six separate constants per permission. */ +#define fsModeGroupRead 0040u +#define fsModeGroupWrite 0020u +#define fsModeGroupExec 0010u + +/* The "other" (neither owner nor group) triad -- vfsPermitted's final + fallback once a caller's uid AND gid both fail to match. Numerically each + is its own owner bit shifted right by 6, matching the 6-bit gap between + the owner and other triads in a real Unix mode. */ +#define fsModeOtherRead 0004u +#define fsModeOtherWrite 0002u +#define fsModeOtherExec 0001u + +/* Defaults applied at creation time (never retroactive -- an existing + file's mode never changes on its own, only through chmod). Directory + mode bits are stored and reported by stat/ls -l but never enforced: this + kernel's existing per-node `writable` (tmpfs) / dir-emptiness (SFS) gates + already govern directory mutation, a second, redundant enforcement axis + was not worth the complexity for a case no test needs. */ +#define fsModeDefaultFile 0644u +#define fsModeDefaultDir 0755u +#define fsModeDefaultExec 0755u + +/* sysMmap's `prot` argument (ulib.h's userMmap) -- POSIX PROT_NONE/READ/ + WRITE/EXEC by shape. mmapProtExec IS real and hardware-enforced + (mmapFaultHandler, mmap.c, maps every page of a Vma missing this bit + with paging.h's pageNoExecute): jumping into a mapping that never asked + for it takes a real, catchable SIGSEGV (isr.c's I/D-bit short-circuit in + tryResolveUserPageFault), never silently succeeds -- WHEN + paging.h's pagingNxSupported() is true (boot.S enables IA32_EFER.NXE + only after confirming CPUID.80000001H:EDX.NX; unsupported hardware + silently keeps every mapping executable rather than write a reserved PTE + bit, the same honest best-effort this codebase already applies + elsewhere). This mechanism is not limited to mmap: elf.c's own loader + now maps each ELF program header with the same paging.h flags, derived + from that segment's own p_flags (elfSegmentPagingFlags) -- a binary's + .text/.rodata comes up read-only+executable, its .data/.bss read-write+ + non-executable (user/user.ld emits them as two separate PT_LOAD + segments precisely so they can get different flags), and the + fixed-size stack and brk heap (kmain.c/syscall.c/jobctl.c/sched.c) still + ask pagingMapUserRangeFlags for pageNoExecute explicitly the same way + they always have. mmapProtWrite IS also real and enforced + (mmapFaultHandler, mmap.c): a write fault against a mapping missing this + bit is refused and turns into a real SIGSEGV (isr.c), never silently + allowed -- the same real enforcement elf.c's own read-only .text/.rodata + segments now get too (user/elfwritevictim.c). */ +#define mmapProtNone 0x0 +#define mmapProtRead 0x1 +#define mmapProtWrite 0x2 +#define mmapProtExec 0x4 + +/* sysMmap's `flags` argument -- MAP_ANONYMOUS-equivalent: fd/offset are + ignored and every page is demand-zero-filled instead of read from a file + (mmap.c's mmapFaultHandler). There is no MAP_SHARED bit: every mapping + here behaves like MAP_PRIVATE (a file-backed page's own frame is this + mapping's alone -- writing to it, when mmapProtWrite allows that, never + writes back to the underlying file, exactly like a real MAP_PRIVATE + mapping's own copy-on-write semantics, just without ever actually + sharing the frame with the file cache to begin with, since this kernel + has none), so the file-backed default already gets MAP_PRIVATE's own + behavior without a second flag bit to ask for it. */ +#define mmapAnonymous 0x1 #define fsNameMax 64 +/* Shared cap for a full path string (as opposed to fsNameMax's single + component): sched.h's Thread.cwd, vfs.h's vfsResolvePath/vfsGetcwd, and + every syscall.c path-copying stack buffer (sysOpen/sysMkdir/sysChdir/...) + all use this same constant, so a path that fits one always fits the + other -- shell.c's own longstanding local pathCapacity used the same 256 + value before this constant existed. */ +#define fsPathMax 256 + typedef struct FbStat { unsigned long size; unsigned int type; + /* Real owner rwx permission bits (see fsModeOwner* above) for a tmpfs + or SFS node -- a synthetic 0666/0444 derived from `writable` before + chmod(2) existed, now the actual per-node value. Always 0666 for + /dev/disk0 (no real per-file mode concept for a raw block device, + an honest, never-enforced placeholder, unlike the file/dir case). */ unsigned int mode; + /* Real owner identity (POSIX uid): the sched.h Thread.uid value that + was current at creation time for a tmpfs or SFS node -- see vfs.c's + vfsUid/sfs.h's sfsUidOf, the same accessors vfsPermitted itself reads + to decide owner-vs-other access. Never retroactive at creation time, + like mode's own default, and never changed by chmod -- only a real + vfsChown (root-only, real Unix's _POSIX_CHOWN_RESTRICTED default) can + reassign it afterward. Always 0 ("root") for /dev/disk0 and the in-memory + tmpfs root itself, mirroring mtime/ctime's own "0 means never + tracked" placeholder immediately below for cases with no real + creation event of their own. */ + unsigned int uid; + /* Real group identity (POSIX gid): the sched.h Thread.gid value that was + current at creation time for a tmpfs or SFS node -- see vfs.c's + vfsGid/sfs.h's sfsGidOf, the same accessors vfsPermitted itself reads + to decide the group branch of owner-vs-group-vs-other access. Never + retroactive at creation time, like uid's own default, and never + changed by chmod -- only vfsChgrp (owner may only set it to their OWN + current gid, root may set it to anything, see vfs.c's vfsChgrp doc + comment) can reassign it afterward. Always 0 for /dev/disk0 and the + in-memory tmpfs root itself, mirroring uid's own placeholder. */ + unsigned int gid; + /* Real wall-clock timestamps (seconds since the Unix epoch, rtc.h's + rtcNow()) for an SFS ("/mnt") inode -- see sfs.h's sfsMtimeOf/ + sfsCtimeOf. Always 0 for the in-memory tmpfs tree and /dev/disk0, + which have never tracked time (documented gap, not a faked value). */ + unsigned long mtime; + unsigned long ctime; } FbStat; typedef struct FbDirent { diff --git a/include/hmap.h b/include/hmap.h new file mode 100644 index 0000000..2068f0f --- /dev/null +++ b/include/hmap.h @@ -0,0 +1,45 @@ +#ifndef FROSTBYTE_HMAP_H +#define FROSTBYTE_HMAP_H + +#include "types.h" + +/* + * Hmap: a string-keyed hash map with chained buckets, backed by the kernel + * heap. Keys are copied into owned storage on hmapSet (the caller's key + * buffer need not outlive the call); values are stored as opaque void* + * (the map does not take ownership of pointed-to value memory). + */ + +typedef struct HmapEntry { + char* key; + void* value; + struct HmapEntry* next; +} HmapEntry; + +typedef struct Hmap { + HmapEntry** buckets; + usize bucketCount; + usize count; +} Hmap; + +/* FNV-1a. Exposed so tests (and callers who want a custom bucket count + heuristic) can reason about it directly. */ +uint64 hmapHashString(const char* key); + +bool hmapInit(Hmap* h, usize bucketCount); +void hmapFree(Hmap* h); + +/* Inserts key/value, or overwrites value if key is already present. + Returns false only on allocation failure. */ +bool hmapSet(Hmap* h, const char* key, void* value); +void* hmapGet(const Hmap* h, const char* key); +bool hmapHas(const Hmap* h, const char* key); +bool hmapRemove(Hmap* h, const char* key); + +usize hmapCount(const Hmap* h); + +/* Visits every entry in unspecified order; stop early if visit returns + false. userData is passed through unchanged. */ +void hmapForEach(const Hmap* h, bool (*visit)(const char* key, void* value, void* userData), void* userData); + +#endif