pkgcache: reject 0-byte artifacts on store and lookup, self-heal torn writes (#10)
A torn producer write (e.g. disk-full mid-copy) could leave a 0-byte P.wwi or
P.o in out/.pkgcache under a self-consistent key; cache_lookup checked only
existence, so every later build HIT and served the empty artifact forever
(silent serve-wrong). Reject size==0 on both sides, symmetric across stages:
store refuses to commit a 0-byte temp before the key write, lookup treats a
0-byte cached artifact as a MISS so existing poison self-heals on re-derive.
A valid .wwi/.o is never 0 bytes, so the guard cannot misfire.
Regression: test/wcc/989_pkgcache_poison_run.c, table-driven over
{poison P.wwi | P.o | both}, non-vacuity proven by guard-neuter.
This commit is contained in:
@@ -1241,6 +1241,22 @@ fn copyfile(src: *u8, dst: *u8) i32 = {
|
||||
return 0;
|
||||
};
|
||||
|
||||
// cachefilesize — open+filesize+close; -1 on any error. A torn producer write
|
||||
// (e.g. disk-full mid-copy) can leave a 0-byte P.wwi/P.o under a self-consistent
|
||||
// key; size==0 is unambiguous poison (wwi_emit always writes >=1 line, a valid
|
||||
// .o is never empty), so both cachelookup and cachestore reject it — self-healing
|
||||
// (#10). Twin of cstage filenonempty.
|
||||
fn cachefilesize(path: *u8) i64 = {
|
||||
let fd: i32 = os.open(pathstr(path), os.flag.RDONLY, 0i32);
|
||||
if (fd < 0) { return -1i64; };
|
||||
let szr: (i64 | os.oserror) = os.filesize(fd);
|
||||
os.close(fd);
|
||||
match (szr) {
|
||||
case let v: i64 => return v;
|
||||
case let e: os.oserror => return -1i64;
|
||||
};
|
||||
};
|
||||
|
||||
// cachelookup — HIT iff the manifest equals the stored P.key byte-for-byte
|
||||
// AND both cached artifacts exist; on HIT copy them into the scratch
|
||||
// wwi/objf so the producer loop can skip compose+w6c+w6a.
|
||||
@@ -1260,6 +1276,8 @@ fn cachelookup(cacheroot: *u8, g: *sepgraph, pi: i32, manifest: *u8,
|
||||
};
|
||||
if (os.access(pathstr(cwwi), 0i32) != 0) { return 0; };
|
||||
if (os.access(pathstr(cobj), 0i32) != 0) { return 0; };
|
||||
if (cachefilesize(cwwi) <= 0i64) { return 0; };
|
||||
if (cachefilesize(cobj) <= 0i64) { return 0; };
|
||||
if (copyfile(cwwi, wwi) != 0) { return 0; };
|
||||
if (copyfile(cobj, objf) != 0) { return 0; };
|
||||
return 1;
|
||||
@@ -1340,6 +1358,8 @@ fn cachestore(cacheroot: *u8, g: *sepgraph, pi: i32, manifest: *u8,
|
||||
let tkey: *u8 = cachetmp(dir, "P.key", pid);
|
||||
if (copyfile(wwi, twwi) != 0) { cachermtmp(twwi, tobj, tkey); return; };
|
||||
if (copyfile(objf, tobj) != 0) { cachermtmp(twwi, tobj, tkey); return; };
|
||||
if (cachefilesize(twwi) <= 0i64) { cachermtmp(twwi, tobj, tkey); return; };
|
||||
if (cachefilesize(tobj) <= 0i64) { cachermtmp(twwi, tobj, tkey); return; };
|
||||
let fd: i32 = os.open(pathstr(tkey),
|
||||
os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC, 420i32); // 0o644
|
||||
if (fd < 0) { cachermtmp(twwi, tobj, tkey); return; };
|
||||
|
||||
Reference in New Issue
Block a user