ww: protect public install outputs
This commit is contained in:
@@ -1515,6 +1515,7 @@ type sepproduct = struct {
|
||||
directoryproduct: bool,
|
||||
notests: bool,
|
||||
buildaction: bool, // loaded product retained in the action list
|
||||
publicout: bool,
|
||||
context: i32,
|
||||
root: i32,
|
||||
variantroot: i32,
|
||||
@@ -6134,6 +6135,7 @@ type septxnentry = struct {
|
||||
backup: *u8,
|
||||
hadold: bool,
|
||||
installed: bool,
|
||||
publicoutput: bool,
|
||||
};
|
||||
|
||||
fn sepalloctxnentries(cap: i32) ([]septxnentry | nomem) = {
|
||||
@@ -6207,8 +6209,8 @@ fn septxnbackup(dst: *u8) *u8 = {
|
||||
return buf.ptr;
|
||||
};
|
||||
|
||||
fn septxnadd(entries: *[]septxnentry, n: *i32,
|
||||
stage: *u8, dst: *u8) bool = {
|
||||
fn septxnaddmode(entries: *[]septxnentry, n: *i32,
|
||||
stage: *u8, dst: *u8, publicoutput: bool) bool = {
|
||||
if (cstreq(stage, dst)) {
|
||||
cerrpath("ww: transaction path collision: ", dst, "\n");
|
||||
return false;
|
||||
@@ -6253,10 +6255,21 @@ fn septxnadd(entries: *[]septxnentry, n: *i32,
|
||||
(*entries)[*n].backup = backup;
|
||||
(*entries)[*n].hadold = false;
|
||||
(*entries)[*n].installed = false;
|
||||
(*entries)[*n].publicoutput = publicoutput;
|
||||
*n += 1;
|
||||
return true;
|
||||
};
|
||||
|
||||
fn septxnadd(entries: *[]septxnentry, n: *i32,
|
||||
stage: *u8, dst: *u8) bool = {
|
||||
return septxnaddmode(entries, n, stage, dst, false);
|
||||
};
|
||||
|
||||
fn septxnaddpublic(entries: *[]septxnentry, n: *i32,
|
||||
stage: *u8, dst: *u8) bool = {
|
||||
return septxnaddmode(entries, n, stage, dst, true);
|
||||
};
|
||||
|
||||
fn septxnaddpkgsuffix(entries: *[]septxnentry, n: *i32,
|
||||
g: *sepgraph, pi: i32, scratch: *u8,
|
||||
stagesuffix: str, dstsuffix: str) bool = {
|
||||
@@ -6271,6 +6284,91 @@ fn septxndiscard(entries: []septxnentry, n: i32) void = {
|
||||
for (i < n) { os.remove(pathstr(entries[i].stage)); i += 1; };
|
||||
};
|
||||
|
||||
fn sepoutputprefix(buf: *u8, n: i64, magic: str) bool = {
|
||||
if (n < 0i64 || n < magic.len: i64) { return false; };
|
||||
let i: i32 = 0;
|
||||
for (i < magic.len) {
|
||||
if (buf[i] != magic.ptr[i]) { return false; };
|
||||
i += 1;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
fn sepoutput2(buf: *u8, n: i64, a: u8, b: u8) bool = {
|
||||
return n >= 2i64 && buf[0u64] == a && buf[1u64] == b;
|
||||
};
|
||||
|
||||
fn sepoutput4(buf: *u8, n: i64, a: u8, b: u8, c: u8, d: u8) bool = {
|
||||
return n >= 4i64 && buf[0u64] == a && buf[1u64] == b
|
||||
&& buf[2u64] == c && buf[3u64] == d;
|
||||
};
|
||||
|
||||
fn sepoutput6(buf: *u8, n: i64, a: u8, b: u8, c: u8, d: u8,
|
||||
e: u8, f: u8) bool = {
|
||||
return n >= 6i64 && buf[0u64] == a && buf[1u64] == b
|
||||
&& buf[2u64] == c && buf[3u64] == d
|
||||
&& buf[4u64] == e && buf[5u64] == f;
|
||||
};
|
||||
|
||||
// Go 1.26.5 work.objectMagic, plus WW's compiler-owned interface prefix.
|
||||
fn sepisobjectoutput(path: *u8) bool = {
|
||||
let fd: i32 = os.open(pathstr(path), os.flag.RDONLY, 0i32);
|
||||
if (fd < 0) { return false; };
|
||||
let buf: [64]u8;
|
||||
let got: u64 = 0u64;
|
||||
let bad: bool = false;
|
||||
for (got < 64u64) {
|
||||
let n: i64 = os.read(fd, &buf[0] + got, 64u64 - got);
|
||||
if (n > 0) { got += n: u64; }
|
||||
else if (n == 0) { break; }
|
||||
else if (n != -4i64) { bad = true; break; };
|
||||
};
|
||||
os.close(fd);
|
||||
if (bad) { return false; };
|
||||
let n: i64 = got: i64;
|
||||
if (sepoutputprefix(&buf[0], n, "!<arch>\n")
|
||||
|| sepoutputprefix(&buf[0], n, "<bigaf>\n")
|
||||
|| sepoutput4(&buf[0], n, 127u8, 69u8, 76u8, 70u8)
|
||||
|| sepoutput4(&buf[0], n, 254u8, 237u8, 250u8, 206u8)
|
||||
|| sepoutput4(&buf[0], n, 254u8, 237u8, 250u8, 207u8)
|
||||
|| sepoutput4(&buf[0], n, 206u8, 250u8, 237u8, 254u8)
|
||||
|| sepoutput4(&buf[0], n, 207u8, 250u8, 237u8, 254u8)
|
||||
|| sepoutput6(&buf[0], n, 77u8, 90u8, 144u8, 0u8, 3u8, 0u8)
|
||||
|| sepoutput6(&buf[0], n, 77u8, 90u8, 120u8, 0u8, 1u8, 0u8)
|
||||
|| sepoutput4(&buf[0], n, 0u8, 0u8, 1u8, 235u8)
|
||||
|| sepoutput4(&buf[0], n, 0u8, 0u8, 138u8, 151u8)
|
||||
|| sepoutput4(&buf[0], n, 0u8, 0u8, 6u8, 71u8)
|
||||
|| sepoutput4(&buf[0], n, 0u8, 97u8, 115u8, 109u8)
|
||||
|| sepoutput2(&buf[0], n, 1u8, 223u8)
|
||||
|| sepoutput2(&buf[0], n, 1u8, 247u8)
|
||||
|| sepoutputprefix(&buf[0], n, "//ww:module ")) {
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
fn sepcheckdstoverwrite(dst: *u8) bool = {
|
||||
let fi: os.filestat;
|
||||
match (os.stat(&fi, pathstr(dst))) {
|
||||
case void => {
|
||||
let typ: u32 = (fi.mode: u32) & 61440u32;
|
||||
if (typ == os.mode.DIR: u32) {
|
||||
cerr("ww: build output "); sepputquoted(dst);
|
||||
cerr(" already exists and is a directory\n");
|
||||
return false;
|
||||
};
|
||||
if (typ == os.mode.REG: u32 && fi.sz != 0u64
|
||||
&& !sepisobjectoutput(dst)) {
|
||||
cerr("ww: build output "); sepputquoted(dst);
|
||||
cerr(" already exists and is not an object file\n");
|
||||
return false;
|
||||
};
|
||||
};
|
||||
case let e: os.oserror => void;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
fn septxncommit(entries: []septxnentry, n: i32) bool = {
|
||||
let i: i32 = 0;
|
||||
let valid: bool = true;
|
||||
@@ -6283,6 +6381,17 @@ fn septxncommit(entries: []septxnentry, n: i32) bool = {
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
if (valid) {
|
||||
i = 0;
|
||||
for (i < n) {
|
||||
if (entries[i].publicoutput
|
||||
&& !sepcheckdstoverwrite(entries[i].dst)) {
|
||||
valid = false;
|
||||
break;
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
};
|
||||
if (valid) {
|
||||
i = 0;
|
||||
for (i < n) {
|
||||
@@ -6614,6 +6723,50 @@ fn septxnrelease(entries: *[]septxnentry, n: i32) void = {
|
||||
entries.cap = 0;
|
||||
};
|
||||
|
||||
// Running retained tests enter this Go-like install action only after their
|
||||
// request-private executable has returned successfully.
|
||||
fn sepinstalltestoutput(stage: *u8, dst: *u8) i32 = {
|
||||
let installstage: *u8 = sepappendlit(stage, ".install");
|
||||
if (installstage == nil) { return 1; };
|
||||
if (cstrlen(installstage) + 1u64 > os.PATH_MAX: u64
|
||||
|| pathexistsnofollow(installstage) != 0
|
||||
|| copyexecutablestage(stage, installstage) != 0) {
|
||||
cerr("ww: cannot stage retained test output\n");
|
||||
os.free(installstage: *void, cstrlen(installstage) + 1u64);
|
||||
return 1;
|
||||
};
|
||||
let parent: *u8 = seplexicalparent(dst);
|
||||
if (parent == nil) {
|
||||
os.remove(pathstr(installstage));
|
||||
os.free(installstage: *void, cstrlen(installstage) + 1u64);
|
||||
return 1;
|
||||
};
|
||||
let created: sepcreateddirs;
|
||||
created.n = 0;
|
||||
if (sepmkdirsrecord(parent, 511, &created) != 0) {
|
||||
cerrpath("ww: cannot create test output directory ", parent, "\n");
|
||||
os.free(parent: *void, cstrlen(parent) + 1u64);
|
||||
os.remove(pathstr(installstage));
|
||||
os.free(installstage: *void, cstrlen(installstage) + 1u64);
|
||||
return 1;
|
||||
};
|
||||
os.free(parent: *void, cstrlen(parent) + 1u64);
|
||||
let entries: []septxnentry;
|
||||
let n: i32 = 0;
|
||||
if (!septxnaddpublic(&entries, &n, installstage, dst)
|
||||
|| !septxncommit(entries, n)) {
|
||||
septxndiscard(entries, n);
|
||||
septxnrelease(&entries, n);
|
||||
seprollbackdirs(&created);
|
||||
os.remove(pathstr(installstage));
|
||||
os.free(installstage: *void, cstrlen(installstage) + 1u64);
|
||||
return 1;
|
||||
};
|
||||
septxnrelease(&entries, n);
|
||||
os.free(installstage: *void, cstrlen(installstage) + 1u64);
|
||||
return 0;
|
||||
};
|
||||
|
||||
fn sepfinishfail(entries: *[]septxnentry, n: i32) i32 = {
|
||||
septxndiscard(*entries, n);
|
||||
septxnrelease(entries, n);
|
||||
@@ -6924,19 +7077,24 @@ fn sepfinishrequest(selfdir: *u8, l6: *u8, c6: *u8, a6: *u8,
|
||||
producti = 0;
|
||||
for (producti < nproducts) {
|
||||
if (products[producti].stageout != nil
|
||||
&& !septxnadd(&entries, &ntxn, products[producti].stageout,
|
||||
products[producti].out)) {
|
||||
return sepfinishfail(&entries, ntxn);
|
||||
&& ((products[producti].publicout
|
||||
&& !septxnaddpublic(&entries, &ntxn,
|
||||
products[producti].stageout, products[producti].out))
|
||||
|| (!products[producti].publicout
|
||||
&& !septxnadd(&entries, &ntxn,
|
||||
products[producti].stageout, products[producti].out)))) {
|
||||
return sepfinishfail(&entries, ntxn);
|
||||
};
|
||||
if (products[producti].stagepublish != nil
|
||||
&& !septxnadd(&entries, &ntxn, products[producti].stagepublish,
|
||||
&& !septxnaddpublic(&entries, &ntxn,
|
||||
products[producti].stagepublish,
|
||||
products[producti].publish)) {
|
||||
return sepfinishfail(&entries, ntxn);
|
||||
};
|
||||
if (products[producti].stageiface != nil) {
|
||||
let outiface: *u8 = sepappendlit(products[producti].out, ".wwi");
|
||||
if (outiface == nil
|
||||
|| !septxnadd(&entries, &ntxn,
|
||||
|| !septxnaddpublic(&entries, &ntxn,
|
||||
products[producti].stageiface, outiface)) {
|
||||
return sepfinishfail(&entries, ntxn);
|
||||
};
|
||||
@@ -8213,7 +8371,8 @@ fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
objstem: *u8, incs: *u8, lf: *lflags, publishpackage: i32,
|
||||
requirecommand: i32, istest: i32,
|
||||
rootvariant: i32, testpackage: *u8, emitasm: i32,
|
||||
keepscratch: i32, workdir: *u8, createoutputdir: *u8,
|
||||
publicoutput: bool, keepscratch: i32, workdir: *u8,
|
||||
createoutputdir: *u8,
|
||||
defaultoutputdir: *u8, outputpatherror: bool) i32 = {
|
||||
let scratch: *u8 = nil;
|
||||
let g: *sepgraph = nil;
|
||||
@@ -8235,6 +8394,7 @@ fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32,
|
||||
product.directoryproduct = false;
|
||||
product.notests = false;
|
||||
product.buildaction = true;
|
||||
product.publicout = publicoutput;
|
||||
product.root = -1;
|
||||
product.variantroot = -1;
|
||||
product.support = -1;
|
||||
@@ -8691,7 +8851,7 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
let rc: i32 = buildonesep(selfdir, resolved, isdir, rootidentity,
|
||||
outp, outp, incs.ptr, &lf,
|
||||
0i32, 0i32, 0i32, SEP_VARIANT_PRODUCTION, nil,
|
||||
emitasm, 0i32, workdir, nil, nil, false);
|
||||
emitasm, false, 0i32, workdir, nil, nil, false);
|
||||
let cleanbad: bool = false;
|
||||
let cleanrc: i32 = os.remove(pathstr(outp));
|
||||
if (cleanrc != 0 && cleanrc != -2i32) {
|
||||
@@ -8710,7 +8870,7 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
return buildonesep(selfdir, resolved, isdir, rootidentity,
|
||||
out, objstem, incs.ptr, &lf,
|
||||
publishpackage, 0i32, 0i32, SEP_VARIANT_PRODUCTION, nil,
|
||||
emitasm, 1i32, workdir, createoutputdir, defaultoutputdir,
|
||||
emitasm, true, 1i32, workdir, createoutputdir, defaultoutputdir,
|
||||
outputpatherror);
|
||||
};
|
||||
|
||||
@@ -8893,7 +9053,7 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
if (buildonesep(selfdir, resolved, isdir, rootidentity,
|
||||
outp, outp, incs.ptr, &lf,
|
||||
0i32, 1i32, 0i32, SEP_VARIANT_PRODUCTION, nil,
|
||||
0i32, 0i32, nil, nil, nil, false) != 0) {
|
||||
0i32, false, 0i32, nil, nil, nil, false) != 0) {
|
||||
let cleanrc: i32 = os.remove(pathstr(outp));
|
||||
if (cleanrc != 0 && cleanrc != -2i32) {
|
||||
cerr("ww: cannot remove temporary output\n");
|
||||
@@ -8958,10 +9118,12 @@ fn runsingletest(selfdir: *u8, src: *u8, incs: *u8, compileonly: i32,
|
||||
let owntmp: bool = false;
|
||||
let retainout: bool = outstem != nil
|
||||
&& !cstreqlit(outstem, "/dev/null");
|
||||
if (retainout) {
|
||||
let deferredinstall: bool = retainout && compileonly == 0
|
||||
&& emitasm == 0;
|
||||
if (retainout && !deferredinstall) {
|
||||
outp = outstem;
|
||||
objstem = outstem;
|
||||
} else { if (workdir != nil) {
|
||||
} else { if (workdir != nil && !deferredinstall) {
|
||||
// The workdir owns the persistent test binary the same way it
|
||||
// owns the package artifacts.
|
||||
outp = joinpathlit(workdir, "main");
|
||||
@@ -8975,6 +9137,7 @@ fn runsingletest(selfdir: *u8, src: *u8, incs: *u8, compileonly: i32,
|
||||
};
|
||||
outp = joinpathlit(tmp.ptr, "main");
|
||||
objstem = outp;
|
||||
if (retainout) { objstem = outstem; };
|
||||
}; };
|
||||
// E3-C1: separate compilation is the sole build path (task #87).
|
||||
let lf: lflags;
|
||||
@@ -8986,7 +9149,8 @@ fn runsingletest(selfdir: *u8, src: *u8, incs: *u8, compileonly: i32,
|
||||
if (retainout) { keep = 1; };
|
||||
let bres: i32 = buildonesep(selfdir, src, 0, nil, outp, objstem, incs, &lf,
|
||||
0i32, 0i32, 1i32, SEP_VARIANT_PRODUCTION, nil,
|
||||
emitasm, keep, workdir, nil, nil, false);
|
||||
emitasm, retainout && !deferredinstall, keep, workdir,
|
||||
nil, nil, false);
|
||||
if (bres != 0) {
|
||||
if (owntmp) {
|
||||
let cleanrc: i32 = os.remove(pathstr(outp));
|
||||
@@ -9037,6 +9201,8 @@ fn runsingletest(selfdir: *u8, src: *u8, incs: *u8, compileonly: i32,
|
||||
rc = -1;
|
||||
};
|
||||
}; };
|
||||
if (rc == 0 && deferredinstall
|
||||
&& sepinstalltestoutput(outp, outstem) != 0) { rc = 1; };
|
||||
if (owntmp) {
|
||||
let cleanrc: i32 = os.remove(pathstr(outp));
|
||||
if (cleanrc != 0 && cleanrc != -2i32) {
|
||||
@@ -9052,6 +9218,10 @@ fn runsingletest(selfdir: *u8, src: *u8, incs: *u8, compileonly: i32,
|
||||
};
|
||||
|
||||
fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
if (argc - start == 3
|
||||
&& cstreqlit(argv[start], "--ww-install-test-output")) {
|
||||
return sepinstalltestoutput(argv[start + 1], argv[start + 2]);
|
||||
};
|
||||
// -I parsing mirrors dobuild/dorun so a single-file test can resolve
|
||||
// transitive imports (e.g. 905_nkname asttest → tok); coupled to the
|
||||
// -T flip (task #5/#10).
|
||||
@@ -9203,7 +9373,10 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
let publish: *u8 = argv[i + 8];
|
||||
let status: *u8 = argv[i + 9];
|
||||
let pn: u64 = cstrlen(name);
|
||||
let buildproduct: bool = cstreqlit(kind, "build");
|
||||
let publicbuildproduct: bool =
|
||||
cstreqlit(kind, "build-public");
|
||||
let buildproduct: bool = cstreqlit(kind, "build")
|
||||
|| publicbuildproduct;
|
||||
let testproduct: bool = cstreqlit(kind, "test");
|
||||
let hasproduction: bool = !cstreqlit(production, "-");
|
||||
let hasinternal: bool = !cstreqlit(internal, "-");
|
||||
@@ -9248,6 +9421,7 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
||||
product.directoryproduct = true;
|
||||
product.notests = testproduct && !hasinternal && !hasexternal;
|
||||
product.buildaction = true;
|
||||
product.publicout = publicbuildproduct;
|
||||
product.root = -1;
|
||||
product.variantroot = -1;
|
||||
product.productionroot = -1;
|
||||
|
||||
Reference in New Issue
Block a user