commit 97e9d95d3f797e67447af025d08c39104081ce45
parent 9b614f0e25ec0d8f43389169ce3863762f7dc8af
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Thu, 17 Sep 2026 14:04:32 -0400
storage: an unmeasurable root reports no free space, not Infinity
getFreeBytes fails open to Number.POSITIVE_INFINITY — right for the download
gate it was written for, wrong to hand onward as a number. Infinity does not
survive JSON, so it crosses the RSC boundary as `null` and every byte
formatter downstream gets a surprise. freeBytes is spread only when finite,
which is what the field being optional already means.
Forcing the fail-open needs a filesystem the unit test cannot conjure, so the
assertion is on the contract instead: a freeBytes that is present is finite.
mountByUuid takes the same ProbeOptions as probeLocation, so the by-uuid
directory is named in one place rather than two.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
2 files changed, 22 insertions(+), 8 deletions(-)
diff --git a/common/lib/storageVolumes.test.ts b/common/lib/storageVolumes.test.ts
@@ -120,6 +120,12 @@ test("available: the root is a directory, with identity and free bytes", async (
assert.equal(p.identity.known && p.identity.label, "PLATTER");
// root === join(mountpoint, relPath), the invariant a re-point relies on.
assert.equal(p.identity.known && p.identity.relPath, "archilyzer-media");
+ // NEVER Infinity. getFreeBytes fails open to it (statfs unsupported, a
+ // permissions error, ENOTDIR) and that value does not survive JSON — it
+ // reaches a client component as `null`. Forcing the fail-open needs a
+ // filesystem this test cannot conjure, so the assertion is on the contract:
+ // a freeBytes that is present is a finite number.
+ assert.ok(Number.isFinite(p.freeBytes));
assert.ok((p.freeBytes ?? 0) > 0);
// In fstab and not an automount: nothing to warn about.
assert.equal(p.warning, undefined);
diff --git a/common/lib/storageVolumes.ts b/common/lib/storageVolumes.ts
@@ -68,11 +68,12 @@ export const MOUNT_TIMEOUT_MS = 15_000;
// difference between "unmounted" and "absent".
export const BY_UUID_DIR = "/dev/disk/by-uuid";
-// Test seams ONLY. Production callers pass nothing and get the constants above;
-// the unit tests shorten the findmnt timeout (so the "fake binary that sleeps"
-// case does not cost the suite three seconds) and point `byUuidDir` at a tmp
-// directory, which is the only way to exercise unmounted-vs-absent without
-// plugging a disk in.
+// Test seams ONLY, shared by probeLocation and mountByUuid so the by-uuid
+// directory is named in one place. Production callers pass nothing and get the
+// constants above; the unit tests shorten the findmnt timeout (so the "fake
+// binary that sleeps" case does not cost the suite three seconds) and point
+// `byUuidDir` at a tmp directory, which is the only way to exercise
+// unmounted-vs-absent without plugging a disk in.
export type ProbeOptions = {
findmntTimeoutMs?: number;
byUuidDir?: string;
@@ -238,7 +239,13 @@ export async function probeLocation(
if (isDir) {
const identity = await identityOfPath(root, bins, timeoutMs);
- const freeBytes = await getFreeBytes(root);
+ // getFreeBytes FAILS OPEN TO Infinity (statfs unsupported, a permissions
+ // error, ENOTDIR) — right for a download gate, wrong to carry as a number:
+ // Infinity does not survive JSON, so it reaches a client component as
+ // `null` and every byte formatter downstream gets a surprise. An
+ // unmeasurable root reports no free space at all, which is the honest
+ // answer and the one the field is already optional for.
+ const free = await getFreeBytes(root);
// Two ways a mount will not be there after a reboot: udisks put it under
// /run/media (or /media) because a human plugged it in, or there is no
// fstab entry naming its UUID. The fstab call is skipped when the
@@ -256,7 +263,7 @@ export async function probeLocation(
status: "available",
identity,
...(warning ? { warning } : {}),
- freeBytes,
+ ...(Number.isFinite(free) ? { freeBytes: free } : {}),
};
}
@@ -332,6 +339,7 @@ export type MountResult = {
export async function mountByUuid(
uuid: string,
bins: VolumeBins,
+ opts: ProbeOptions = {},
): Promise<MountResult> {
if (!(await udisksctlAvailable(bins))) {
return {
@@ -341,7 +349,7 @@ export async function mountByUuid(
}
const res = await run(
bins.udisksctlBin,
- ["mount", "-b", path.join(BY_UUID_DIR, uuid)],
+ ["mount", "-b", path.join(opts.byUuidDir ?? BY_UUID_DIR, uuid)],
MOUNT_TIMEOUT_MS,
);
if (!res.ok) {