ww: preserve compiler sink outputs
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user