ww: invalidate partial package commits
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user