fat: long-file-name creation, fast cluster allocation, makePath

createFile/createDirectory now build long-name chains: a mangled STEM~N
8.3 alias (collision-checked per directory), the standard rotate-add
checksum, and 13-UCS-2-per-entry pieces written last-logical-first into
a contiguous free-slot run (found sector-wise, growing the directory as
today). Write order is chain first, 8.3 entry last, so an interrupted
create leaves only skippable orphans. Uppercase-compliant 8.3 names
keep the bare-entry fast path; lowercase names now get a chain so their
exact case survives — matching tools/make-fat-image.py. rename stays
8.3-only (documented; nothing needs more yet).

allocateCluster drops its from-cluster-2 rescan (measured ~1 s/cluster
on a part-full volume, a 37 s shutdown flush in the lost M19 build):
a next-free hint (rewound by frees) plus a FAT-sector LBA cache turn
the scan into one device read per FAT sector.

mkdir now refuses an existing name (no duplicate entries), and
runtime.fs gains makePath (mkdir -p) for the logger's nested per-boot
directories. Engine tests cover the log-directory shape, ~N collisions,
chain unlink/reuse, the 8.3 fast path, and the checksum.
This commit is contained in:
Daniel Samson
2026-07-21 15:59:06 +01:00
parent d0c1b3e45b
commit 30d6ea622a
3 changed files with 356 additions and 17 deletions
+16
View File
@@ -236,6 +236,22 @@ pub fn makeDirectory(path: []const u8) bool {
return pathOperation(.mkdir, path);
}
/// Create every missing directory along `path` (mkdir -p). Probes each prefix
/// with `exists` first — a FAT mkdir of an existing name is refused, and the
/// probe keeps the common "already there" case cheap. Returns true when the
/// whole path exists afterwards.
pub fn makePath(path: []const u8) bool {
var end: usize = 0;
while (end < path.len) {
end += 1;
while (end < path.len and path[end] != '/') end += 1;
const prefix = path[0..end];
if (prefix.len == 0 or (prefix.len == 1 and prefix[0] == '/')) continue;
if (!exists(prefix) and !makeDirectory(prefix)) return false;
}
return true;
}
/// Remove the file at `path`. Returns true on success. Directories are refused
/// (a separate directory-removal would have to check emptiness).
pub fn remove(path: []const u8) bool {