ww: preserve compiler sink outputs

This commit is contained in:
2026-08-14 19:10:36 +09:00
parent 763bbc8f25
commit 25da54936d
3 changed files with 84 additions and 16 deletions

View File

@@ -13,6 +13,7 @@ struct publication {
char *backup; char *backup;
int had_old; int had_old;
int installed; int installed;
int passthrough;
}; };
static char * static char *
@@ -30,9 +31,37 @@ publish_path(const char *dst, const char *kind)
return p; return p;
} }
static int
copy_stream(FILE *src, FILE *out)
{
int bad = fflush(src) != 0 || fseek(src, 0, SEEK_SET) != 0;
unsigned char buf[65536];
while (!bad) {
size_t n = fread(buf, 1, sizeof buf, src);
if (n != 0 && fwrite(buf, 1, n, out) != n) bad = 1;
if (n < sizeof buf) {
if (ferror(src)) bad = 1;
break;
}
}
return bad ? -1 : 0;
}
static int static int
materialize_stream(FILE *src, struct publication *p) materialize_stream(FILE *src, struct publication *p)
{ {
struct stat st;
if (lstat(p->dst, &st) == 0 && S_ISCHR(st.st_mode)) {
FILE *out = fopen(p->dst, "wb");
int bad = out == NULL || copy_stream(src, out) < 0;
if (out != NULL && fclose(out) != 0) bad = 1;
if (bad) {
fprintf(stderr, "w6c: cannot write %s\n", p->dst);
return -1;
}
p->passthrough = 1;
return 0;
}
p->stage = publish_path(p->dst, "new"); p->stage = publish_path(p->dst, "new");
p->backup = publish_path(p->dst, "old"); p->backup = publish_path(p->dst, "old");
if (p->stage == NULL || p->backup == NULL) { if (p->stage == NULL || p->backup == NULL) {
@@ -50,16 +79,7 @@ materialize_stream(FILE *src, struct publication *p)
unlink(p->stage); unlink(p->stage);
return -1; return -1;
} }
int bad = fflush(src) != 0 || fseek(src, 0, SEEK_SET) != 0; int bad = copy_stream(src, out) < 0;
unsigned char buf[65536];
while (!bad) {
size_t n = fread(buf, 1, sizeof buf, src);
if (n != 0 && fwrite(buf, 1, n, out) != n) bad = 1;
if (n < sizeof buf) {
if (ferror(src)) bad = 1;
break;
}
}
if (fclose(out) != 0) bad = 1; if (fclose(out) != 0) bad = 1;
if (bad) { if (bad) {
unlink(p->stage); unlink(p->stage);
@@ -100,6 +120,7 @@ static int
publish_all(struct publication *p, int n) publish_all(struct publication *p, int n)
{ {
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
if (p[i].passthrough) continue;
if (!path_is_regular_nofollow(p[i].stage)) { if (!path_is_regular_nofollow(p[i].stage)) {
fprintf(stderr, "w6c: publication stage is not a regular file for %s\n", fprintf(stderr, "w6c: publication stage is not a regular file for %s\n",
p[i].dst); p[i].dst);
@@ -120,6 +141,7 @@ publish_all(struct publication *p, int n)
} }
} }
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
if (p[i].passthrough) continue;
int exists = path_exists_nofollow(p[i].backup); int exists = path_exists_nofollow(p[i].backup);
if (exists != 0) { if (exists != 0) {
if (exists < 0) if (exists < 0)
@@ -132,6 +154,7 @@ publish_all(struct publication *p, int n)
} }
} }
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
if (p[i].passthrough) continue;
if (rename(p[i].dst, p[i].backup) == 0) if (rename(p[i].dst, p[i].backup) == 0)
p[i].had_old = 1; p[i].had_old = 1;
else if (errno != ENOENT) { else if (errno != ENOENT) {
@@ -140,6 +163,7 @@ publish_all(struct publication *p, int n)
} }
} }
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
if (p[i].passthrough) continue;
if (rename(p[i].stage, p[i].dst) != 0) { if (rename(p[i].stage, p[i].dst) != 0) {
fprintf(stderr, "w6c: cannot publish %s\n", p[i].dst); fprintf(stderr, "w6c: cannot publish %s\n", p[i].dst);
goto rollback; goto rollback;
@@ -149,13 +173,15 @@ publish_all(struct publication *p, int n)
/* Installation is the commit point. Backup cleanup cannot truthfully /* Installation is the commit point. Backup cleanup cannot truthfully
* turn a completely installed pair into a rejected compilation. */ * turn a completely installed pair into a rejected compilation. */
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
if (p[i].had_old && unlink(p[i].backup) != 0) { if (!p[i].passthrough && p[i].had_old
&& unlink(p[i].backup) != 0) {
fprintf(stderr, "w6c: cannot remove backup for %s\n", p[i].dst); fprintf(stderr, "w6c: cannot remove backup for %s\n", p[i].dst);
} }
return 0; return 0;
rollback: rollback:
for (int i = n - 1; i >= 0; i--) { for (int i = n - 1; i >= 0; i--) {
if (p[i].passthrough) continue;
if (p[i].installed) (void)unlink(p[i].dst); if (p[i].installed) (void)unlink(p[i].dst);
if (p[i].had_old) (void)rename(p[i].backup, p[i].dst); if (p[i].had_old) (void)rename(p[i].backup, p[i].dst);
if (p[i].stage != NULL) (void)unlink(p[i].stage); if (p[i].stage != NULL) (void)unlink(p[i].stage);

View File

@@ -5147,10 +5147,12 @@ and is never followed or removed. A committed dispatcher voucher must itself
be a regular file before it can authorize reuse. Compiler assembly and be a regular file before it can authorize reuse. Compiler assembly and
interface bytes are first generated through anonymous files with checked full interface bytes are first generated through anonymous files with checked full
writes and then published as their own rollback group. A non-regular compiler writes and then published as their own rollback group. A non-regular compiler
destination is rejected before preservation, and installation—not cleanup of destination is rejected before preservation, except that an already existing
a recoverable old backup—is the commit point. Cold rejection removes the exact character-device sink such as `/dev/null` receives a checked passthrough and is
request-owned scratch tree. Stale init code, stale closure metadata, and mixed never renamed or treated as a persistent artifact. Installation—not cleanup
committed generations therefore cannot be reused. of a recoverable old backup—is the commit point. Cold rejection removes the
exact request-owned scratch tree. Stale init code, stale closure metadata, and
mixed committed generations therefore cannot be reused.
#### Stage and observer ownership #### Stage and observer ownership

View File

@@ -14,6 +14,7 @@ type publication = struct {
backup: str, backup: str,
hadold: bool, hadold: bool,
installed: bool, installed: bool,
passthrough: bool,
}; };
fn publicationpath(dst: *u8, kind: str) str = { fn publicationpath(dst: *u8, kind: str) str = {
@@ -21,6 +22,17 @@ fn publicationpath(dst: *u8, kind: str) str = {
strconv.i32tos(os.getpid(), strconv.base.DEC), ".", kind); strconv.i32tos(os.getpid(), strconv.base.DEC), ".", kind);
}; };
fn pathischardevice(path: *u8) bool = {
let fi: os.filestat;
match (os.lstat(&fi, pathstr(path))) {
case void => {
return (((fi.mode: u32) & 61440u32) == (os.mode.CHR: u32));
};
case let e: os.oserror => return false;
};
return false;
};
// Open a destination-adjacent file and immediately unlink its name. Cgen's // Open a destination-adjacent file and immediately unlink its name. Cgen's
// fatal exit then leaves only a kernel-owned fd, never a partial destination // fatal exit then leaves only a kernel-owned fd, never a partial destination
// or named staging artifact. // or named staging artifact.
@@ -28,6 +40,12 @@ fn anonymousfd(dst: *u8, kind: str) i32 = {
let path: str = publicationpath(dst, kind); let path: str = publicationpath(dst, kind);
let fd: i32 = os.open(path, let fd: i32 = os.open(path,
os.flag.RDWR | os.flag.CREATE | os.flag.EXCL, 384i32); os.flag.RDWR | os.flag.CREATE | os.flag.EXCL, 384i32);
if (fd < 0 && pathischardevice(dst)) {
path = strings.concat("/tmp/ww-w6c.",
strconv.i32tos(os.getpid(), strconv.base.DEC), ".", kind);
fd = os.open(path,
os.flag.RDWR | os.flag.CREATE | os.flag.EXCL, 384i32);
};
if (fd < 0) { if (fd < 0) {
os.write(2, "w6c: cannot create anonymous output\n".ptr, os.write(2, "w6c: cannot create anonymous output\n".ptr,
"w6c: cannot create anonymous output\n".len: u64); "w6c: cannot create anonymous output\n".len: u64);
@@ -60,6 +78,21 @@ fn copystream(src: i32, dst: i32) bool = {
fn materializestream(src: i32, dst: *u8, p: *publication) bool = { fn materializestream(src: i32, dst: *u8, p: *publication) bool = {
p.dst = dst; p.dst = dst;
p.passthrough = false;
let fi: os.filestat;
match (os.lstat(&fi, pathstr(dst))) {
case void => {
if (((fi.mode: u32) & 61440u32) == (os.mode.CHR: u32)) {
let sink: i32 = os.open(pathstr(dst), os.flag.WRONLY, 0i32);
if (sink < 0) { return false; };
let sinkok: bool = copystream(src, sink);
if (os.close(sink) != 0) { sinkok = false; };
if (sinkok) { p.passthrough = true; };
return sinkok;
};
};
case let e: os.oserror => void;
};
p.stage = publicationpath(dst, "new"); p.stage = publicationpath(dst, "new");
p.backup = publicationpath(dst, "old"); p.backup = publicationpath(dst, "old");
p.hadold = false; p.hadold = false;
@@ -110,6 +143,7 @@ fn publishall(p: *publication, n: i32) bool = {
let i: i32 = 0; let i: i32 = 0;
let ok: bool = true; let ok: bool = true;
for (i < n) { for (i < n) {
if (p[i].passthrough) { i += 1; continue; };
if (!pathisregularnofollow(p[i].stage)) { if (!pathisregularnofollow(p[i].stage)) {
publishdiag("w6c: publication stage is not a regular file for ", publishdiag("w6c: publication stage is not a regular file for ",
p[i].dst); p[i].dst);
@@ -140,6 +174,7 @@ fn publishall(p: *publication, n: i32) bool = {
if (ok) { if (ok) {
i = 0; i = 0;
for (i < n) { for (i < n) {
if (p[i].passthrough) { i += 1; continue; };
let exists: i32 = pathexistsnofollow(p[i].backup); let exists: i32 = pathexistsnofollow(p[i].backup);
if (exists != 0) { if (exists != 0) {
if (exists < 0) { if (exists < 0) {
@@ -157,6 +192,7 @@ fn publishall(p: *publication, n: i32) bool = {
if (ok) { if (ok) {
i = 0; i = 0;
for (i < n) { for (i < n) {
if (p[i].passthrough) { i += 1; continue; };
let rc: i32 = os.rename(pathstr(p[i].dst), p[i].backup); let rc: i32 = os.rename(pathstr(p[i].dst), p[i].backup);
if (rc == 0) { p[i].hadold = true; } if (rc == 0) { p[i].hadold = true; }
else { if (rc != -2) { else { if (rc != -2) {
@@ -170,6 +206,7 @@ fn publishall(p: *publication, n: i32) bool = {
if (ok) { if (ok) {
i = 0; i = 0;
for (i < n) { for (i < n) {
if (p[i].passthrough) { i += 1; continue; };
if (os.rename(p[i].stage, pathstr(p[i].dst)) != 0) { if (os.rename(p[i].stage, pathstr(p[i].dst)) != 0) {
publishdiag("w6c: cannot publish ", p[i].dst); publishdiag("w6c: cannot publish ", p[i].dst);
ok = false; ok = false;
@@ -183,7 +220,8 @@ fn publishall(p: *publication, n: i32) bool = {
// truthfully turn a completely installed pair into rejection. // truthfully turn a completely installed pair into rejection.
i = 0; i = 0;
for (i < n) { for (i < n) {
if (p[i].hadold && os.remove(p[i].backup) != 0) { if (!p[i].passthrough && p[i].hadold
&& os.remove(p[i].backup) != 0) {
publishdiag("w6c: cannot remove backup for ", p[i].dst); publishdiag("w6c: cannot remove backup for ", p[i].dst);
}; };
i += 1; i += 1;
@@ -193,6 +231,7 @@ fn publishall(p: *publication, n: i32) bool = {
}; };
i = n - 1; i = n - 1;
for (i >= 0) { for (i >= 0) {
if (p[i].passthrough) { i -= 1; continue; };
if (p[i].installed) { os.remove(pathstr(p[i].dst)); }; if (p[i].installed) { os.remove(pathstr(p[i].dst)); };
if (p[i].hadold) { os.rename(p[i].backup, pathstr(p[i].dst)); }; if (p[i].hadold) { os.rename(p[i].backup, pathstr(p[i].dst)); };
if (p[i].stage.len != 0) { os.remove(p[i].stage); }; if (p[i].stage.len != 0) { os.remove(p[i].stage); };
@@ -882,6 +921,7 @@ export fn main(argc: i32, argv: **u8) i32 = {
pubs[pi].backup = ""; pubs[pi].backup = "";
pubs[pi].hadold = false; pubs[pi].hadold = false;
pubs[pi].installed = false; pubs[pi].installed = false;
pubs[pi].passthrough = false;
pi += 1; pi += 1;
}; };
let npub: i32 = 0; let npub: i32 = 0;