ww: invalidate partial package commits

This commit is contained in:
2026-08-14 05:12:45 +09:00
parent 402f50ac8d
commit 351f4a25bc
5 changed files with 282 additions and 17 deletions

View File

@@ -4220,6 +4220,8 @@ build_one_sep_impl(const char *src, int entry_is_dir,
return 1;
}
int any_failed = 0;
int commit_open = 0;
int commit_integrity_failed = 0;
for (int i = 0; i < nproducts; i++)
if (g->pkg[products[i].root].failed) any_failed = 1;
for (int oi = 0; oi < norder; oi++) {
@@ -4255,6 +4257,8 @@ build_one_sep_impl(const char *src, int entry_is_dir,
const char *ca = warm ? anew : apath;
if (sep_discard_action_staging(warm, unitnew, wwinew, asmnew,
objnew, anew) < 0) {
if (warm && unlink(unitf) != 0 && errno != ENOENT)
commit_integrity_failed = 1;
g->pkg[pi].failed = 1;
any_failed = 1;
continue;
@@ -4262,6 +4266,8 @@ build_one_sep_impl(const char *src, int entry_is_dir,
if (sep_compose_unit(g, pi, cu) < 0) {
(void)sep_discard_action_staging(warm, unitnew, wwinew,
asmnew, objnew, anew);
if (warm && unlink(unitf) != 0 && errno != ENOENT)
commit_integrity_failed = 1;
g->pkg[pi].failed = 1;
any_failed = 1;
continue;
@@ -4271,6 +4277,7 @@ build_one_sep_impl(const char *src, int entry_is_dir,
if (g->pkg[g->pkg[pi].deps[k]].export_changed)
deps_changed = 1;
if (warm && !stale_all && !deps_changed
&& !commit_integrity_failed
&& file_equal(unitnew, unitf)
&& file_is_reg(asmf)
&& file_is_reg(wwi)
@@ -4284,6 +4291,20 @@ build_one_sep_impl(const char *src, int entry_is_dir,
}
continue;
}
/* Once an action is known not to be reusable, its old unit must no
* longer vouch for artifacts if any later producer or commit step
* fails. This is especially important when a dependency already
* committed a changed export during the same request. */
if (warm && unlink(unitf) != 0 && errno != ENOENT) {
fprintf(stderr, "ww: cannot invalidate package unit %s\n",
unitf);
(void)sep_discard_action_staging(warm, unitnew, wwinew,
asmnew, objnew, anew);
g->pkg[pi].failed = 1;
any_failed = 1;
commit_integrity_failed = 1;
continue;
}
int nmaps = 0;
for (int k = 0; k < g->pkg[pi].bindings.n; k++) {
if (!sep_binding_first_map(g, &g->pkg[pi].bindings, k))
@@ -4418,8 +4439,22 @@ build_one_sep_impl(const char *src, int entry_is_dir,
}
}
/* Commit order: artifacts before the unit that vouches for
* them, unit strictly last. */
* them, unit strictly last. Remove the workdir identity before the
* first artifact rename; failure to do so is a pre-commit rejection
* that leaves all committed artifacts untouched. */
if (warm) {
if (!commit_open) {
if (unlink(stampf) != 0 && errno != ENOENT) {
fprintf(stderr,
"ww: cannot invalidate package workdir\n");
g->pkg[pi].failed = 1;
any_failed = 1;
(void)sep_discard_action_staging(warm, unitnew,
wwinew, asmnew, objnew, anew);
continue;
}
commit_open = 1;
}
if (rename(wwinew, wwi) != 0
|| rename(asmnew, asmf) != 0
|| (!emit_asm && rename(objnew, obj) != 0)
@@ -4429,6 +4464,12 @@ build_one_sep_impl(const char *src, int entry_is_dir,
g->pkg[pi].path[0] ? g->pkg[pi].path : "(root)");
g->pkg[pi].failed = 1;
any_failed = 1;
/* A failed rename sequence may already have replaced the
* interface or another artifact. The stamp is already absent;
* also invalidate every unit voucher and force later actions in
* this invocation through their producers. */
commit_integrity_failed = 1;
(void)invalidate_workdir_units(scratch);
(void)sep_discard_action_staging(warm, unitnew, wwinew,
asmnew, objnew, anew);
continue;
@@ -4437,9 +4478,9 @@ build_one_sep_impl(const char *src, int entry_is_dir,
}
/* Stale passes removed every old unit voucher before compiling. Current
* successful units are therefore safe to vouch for even when a sibling
* root failed; a killed pass leaves the old identity and forces another
* invalidating pass, never false reuse. */
if (warm) {
* compiler rejects. A partial artifact commit invalidates all vouchers
* and suppresses the workdir identity so the next pass starts stale. */
if (warm && !commit_integrity_failed) {
if (!file_equal(toolw, self_path)
&& copy_file_atomic(self_path, toolw) != 0) {
fprintf(stderr, "ww: cannot record %s\n", toolw);
@@ -4455,7 +4496,7 @@ build_one_sep_impl(const char *src, int entry_is_dir,
fprintf(stderr, "ww: cannot record %s\n", toola);
free(order); return 1;
}
if (!stampok) {
if (!stampok || commit_open) {
char stampnew[PATH_MAX];
int sn = snprintf(stampnew, sizeof stampnew, "%s.new", stampf);
if (sn < 0 || (size_t)sn >= sizeof stampnew) {