feat: extend fs with HmapEntry and Hmap

This commit is contained in:
dualfroz committed 2026-09-01 18:47:32 +02:00
1 parent 0c36d813e5
commit afbefed8da
2 files changed
+176

No files matched your search

+131
View File
@@ -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 {
+45
View File
@@ -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