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:
@@ -780,6 +780,17 @@ pkgcache_dir(struct sepgraph *g, int pi, char *out, size_t outsz)
|
||||
snprintf(out, outsz, "%s/%s", pkgcache_root(), base);
|
||||
}
|
||||
|
||||
/* 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 store and lookup reject
|
||||
* it — self-healing, Go-build-cache style (#10). */
|
||||
static int
|
||||
filenonempty(const char *p)
|
||||
{
|
||||
struct stat st;
|
||||
return stat(p, &st) == 0 && st.st_size > 0;
|
||||
}
|
||||
|
||||
/* HIT iff a freshly recomputed manifest equals the stored P.key byte-for-byte
|
||||
* AND both cached artifacts exist; on HIT copy them into the scratch wwi/obj
|
||||
* paths so the producer loop can skip compose+w6c+w6a. */
|
||||
@@ -802,6 +813,7 @@ cache_lookup(struct sepgraph *g, int pi, const char *manifest,
|
||||
if (more || sn != strlen(manifest) || memcmp(stored, manifest, sn) != 0)
|
||||
return 0;
|
||||
if (access(cwwi, 0) != 0 || access(cobj, 0) != 0) return 0;
|
||||
if (!filenonempty(cwwi) || !filenonempty(cobj)) return 0;
|
||||
snprintf(cmd, sizeof cmd, "cp -f '%s' '%s'", cwwi, wwi);
|
||||
if (run(cmd) != 0) return 0;
|
||||
snprintf(cmd, sizeof cmd, "cp -f '%s' '%s'", cobj, obj);
|
||||
@@ -847,6 +859,7 @@ cache_store(struct sepgraph *g, int pi, const char *manifest,
|
||||
if (run(cmd) != 0) goto cleanup;
|
||||
snprintf(cmd, sizeof cmd, "cp -f '%s' '%s'", obj, tobj);
|
||||
if (run(cmd) != 0) goto cleanup;
|
||||
if (!filenonempty(twwi) || !filenonempty(tobj)) goto cleanup;
|
||||
f = fopen(tkey, "wb");
|
||||
if (f == NULL) goto cleanup;
|
||||
fputs(manifest, f);
|
||||
|
||||
Reference in New Issue
Block a user