ww: enforce internal package visibility
This commit is contained in:
118
cmd/ww/main.c
118
cmd/ww/main.c
@@ -373,6 +373,7 @@ source_has_test_decl(const char *path)
|
|||||||
#define SEP_ROLE_TEST_SUPPORT 1
|
#define SEP_ROLE_TEST_SUPPORT 1
|
||||||
#define SEP_ROLE_GENERATED_MAIN 2
|
#define SEP_ROLE_GENERATED_MAIN 2
|
||||||
#define SEP_TEST_SUPPORT_MODULE "__wwtest"
|
#define SEP_TEST_SUPPORT_MODULE "__wwtest"
|
||||||
|
#define SEP_LOAD_INTERNAL -3
|
||||||
|
|
||||||
/* Package-graph storage grows geometrically. Counts remain signed ints
|
/* Package-graph storage grows geometrically. Counts remain signed ints
|
||||||
* because they are stable action/context indices throughout the existing
|
* because they are stable action/context indices throughout the existing
|
||||||
@@ -994,6 +995,84 @@ sep_forbidden_command_import(const struct sepgraph *g, int importer, int dep)
|
|||||||
&& strcmp(from->canon, to->canon) == 0);
|
&& strcmp(from->canon, to->canon) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
sep_internal_parent_count(const char *path, size_t *parents)
|
||||||
|
{
|
||||||
|
const char *p = path;
|
||||||
|
size_t components = 0;
|
||||||
|
size_t final = 0;
|
||||||
|
int found = 0;
|
||||||
|
while (*p != '\0') {
|
||||||
|
while (*p == '.') p++;
|
||||||
|
if (*p == '\0') break;
|
||||||
|
const char *end = strchr(p, '.');
|
||||||
|
size_t n = end != NULL ? (size_t)(end - p) : strlen(p);
|
||||||
|
if (n == sizeof "internal" - 1
|
||||||
|
&& memcmp(p, "internal", n) == 0) {
|
||||||
|
final = components;
|
||||||
|
found = 1;
|
||||||
|
}
|
||||||
|
components++;
|
||||||
|
if (end == NULL) break;
|
||||||
|
p = end + 1;
|
||||||
|
}
|
||||||
|
if (!found) return 0;
|
||||||
|
*parents = components - final;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
sep_internal_import_allowed(const struct seppkg *from,
|
||||||
|
const char *target_path, const char *target_entry)
|
||||||
|
{
|
||||||
|
size_t parents;
|
||||||
|
if (!sep_internal_parent_count(target_path, &parents)) return 1;
|
||||||
|
const char *importer = from->canon;
|
||||||
|
char *owned = NULL;
|
||||||
|
if (!from->is_dir) {
|
||||||
|
const char *slash = strrchr(from->canon, '/');
|
||||||
|
if (slash == NULL) return -1;
|
||||||
|
size_t n = slash == from->canon ? 1 : (size_t)(slash - from->canon);
|
||||||
|
owned = strndup(from->canon, n);
|
||||||
|
if (owned == NULL) return sep_fail_nomem();
|
||||||
|
importer = owned;
|
||||||
|
}
|
||||||
|
size_t boundary = strlen(target_entry);
|
||||||
|
while (boundary > 1 && target_entry[boundary - 1] == '/') boundary--;
|
||||||
|
for (size_t i = 0; i < parents; i++) {
|
||||||
|
while (boundary > 0 && target_entry[boundary - 1] != '/') boundary--;
|
||||||
|
while (boundary > 1 && target_entry[boundary - 1] == '/') boundary--;
|
||||||
|
}
|
||||||
|
char *lexical = boundary == 0
|
||||||
|
? strdup(".") : strndup(target_entry, boundary);
|
||||||
|
if (lexical == NULL) {
|
||||||
|
free(owned);
|
||||||
|
return sep_fail_nomem();
|
||||||
|
}
|
||||||
|
errno = 0;
|
||||||
|
char *owner = realpath(lexical, NULL);
|
||||||
|
free(lexical);
|
||||||
|
if (owner == NULL) {
|
||||||
|
if (errno == ENOMEM)
|
||||||
|
sep_fail_nomem();
|
||||||
|
else
|
||||||
|
fprintf(stderr, "ww: cannot canonicalize package %s\n",
|
||||||
|
target_entry);
|
||||||
|
free(owned);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
size_t n = strlen(importer);
|
||||||
|
boundary = strlen(owner);
|
||||||
|
int allowed = (n == boundary
|
||||||
|
&& memcmp(importer, owner, boundary) == 0)
|
||||||
|
|| (boundary == 1 && owner[0] == '/' && importer[0] == '/')
|
||||||
|
|| (n > boundary && memcmp(importer, owner, boundary) == 0
|
||||||
|
&& importer[boundary] == '/');
|
||||||
|
free(owner);
|
||||||
|
free(owned);
|
||||||
|
return allowed;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
sep_command_compiler_marker(const struct sepgraph *g, int pi)
|
sep_command_compiler_marker(const struct sepgraph *g, int pi)
|
||||||
{
|
{
|
||||||
@@ -1718,6 +1797,8 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file,
|
|||||||
else
|
else
|
||||||
located = locate_import(searchpath, path_form, ipath,
|
located = locate_import(searchpath, path_form, ipath,
|
||||||
sizeof ipath);
|
sizeof ipath);
|
||||||
|
const char *visibility_entry = bound != NULL
|
||||||
|
? g->pkg[pi].entry : ipath;
|
||||||
if (!located) {
|
if (!located) {
|
||||||
const char *dot = strrchr(name, '.');
|
const char *dot = strrchr(name, '.');
|
||||||
const char *leaf = dot ? dot + 1 : name;
|
const char *leaf = dot ? dot + 1 : name;
|
||||||
@@ -1751,13 +1832,7 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file,
|
|||||||
rc = -1;
|
rc = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (sep_binding_add(bindings, 'D', name, canon) < 0) {
|
|
||||||
free(canon);
|
|
||||||
rc = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
int self = strcmp(canon, g->pkg[pi].canon) == 0;
|
int self = strcmp(canon, g->pkg[pi].canon) == 0;
|
||||||
free(canon);
|
|
||||||
if (self
|
if (self
|
||||||
&& (sep_external_production_name(&g->pkg[pi], name, 1)
|
&& (sep_external_production_name(&g->pkg[pi], name, 1)
|
||||||
|| (g->pkg[pi].variant == SEP_VARIANT_EXTERNAL
|
|| (g->pkg[pi].variant == SEP_VARIANT_EXTERNAL
|
||||||
@@ -1768,6 +1843,7 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file,
|
|||||||
? g->pkg[pi].path : g->pkg[pi].canon;
|
? g->pkg[pi].path : g->pkg[pi].canon;
|
||||||
errorf(u->pos, "self-import: package '%s' cannot import itself",
|
errorf(u->pos, "self-import: package '%s' cannot import itself",
|
||||||
owner[0] ? owner : "(root)");
|
owner[0] ? owner : "(root)");
|
||||||
|
free(canon);
|
||||||
rc = -1;
|
rc = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1775,7 +1851,23 @@ sep_scan_file(struct sepgraph *g, int pi, const char *file,
|
|||||||
* canonical production action for this directory. Discovery role and
|
* canonical production action for this directory. Discovery role and
|
||||||
* the external product's artifact name never create another action. */
|
* the external product's artifact name never create another action. */
|
||||||
int di = sep_find_or_add(g, name, ipath, 1);
|
int di = sep_find_or_add(g, name, ipath, 1);
|
||||||
if (di < 0) { rc = -1; break; }
|
if (di < 0) { free(canon); rc = -1; break; }
|
||||||
|
int allowed = sep_internal_import_allowed(&g->pkg[pi],
|
||||||
|
g->pkg[di].path, visibility_entry);
|
||||||
|
if (allowed < 0) { free(canon); rc = -1; break; }
|
||||||
|
if (!allowed) {
|
||||||
|
errorf(u->pos, "use of internal package %s not allowed",
|
||||||
|
g->pkg[di].path);
|
||||||
|
free(canon);
|
||||||
|
rc = SEP_LOAD_INTERNAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (sep_binding_add(bindings, 'D', name, canon) < 0) {
|
||||||
|
free(canon);
|
||||||
|
rc = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
free(canon);
|
||||||
if (sep_add_dep(g, pi, di) < 0) { rc = -1; break; }
|
if (sep_add_dep(g, pi, di) < 0) { rc = -1; break; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1989,7 +2081,7 @@ sep_prepare_pkg_context(struct sepgraph *g, int pi, int context)
|
|||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
g->pkg[pi].context_state[context] = 2;
|
g->pkg[pi].context_state[context] = 2;
|
||||||
g->pkg[pi].failed = 1;
|
g->pkg[pi].failed = 1;
|
||||||
return -1;
|
return rc;
|
||||||
}
|
}
|
||||||
for (int i = 1; i < g->pkg[pi].ndeps; i++) {
|
for (int i = 1; i < g->pkg[pi].ndeps; i++) {
|
||||||
int v = g->pkg[pi].deps[i];
|
int v = g->pkg[pi].deps[i];
|
||||||
@@ -2026,6 +2118,7 @@ sep_load_pkg(struct sepgraph *g, int pi, int context)
|
|||||||
context = g->support_context;
|
context = g->support_context;
|
||||||
struct seploadframe *frames = NULL;
|
struct seploadframe *frames = NULL;
|
||||||
int nframe = 0, framecap = 0;
|
int nframe = 0, framecap = 0;
|
||||||
|
int result = -1;
|
||||||
if (sep_reserve((void **)&frames, &framecap, 1,
|
if (sep_reserve((void **)&frames, &framecap, 1,
|
||||||
sizeof *frames) < 0)
|
sizeof *frames) < 0)
|
||||||
return -2;
|
return -2;
|
||||||
@@ -2045,8 +2138,11 @@ sep_load_pkg(struct sepgraph *g, int pi, int context)
|
|||||||
nframe--;
|
nframe--;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (sep_prepare_pkg_context(g, f->pkg, f->context) < 0)
|
int prepared = sep_prepare_pkg_context(g, f->pkg, f->context);
|
||||||
|
if (prepared < 0) {
|
||||||
|
result = prepared;
|
||||||
goto failed;
|
goto failed;
|
||||||
|
}
|
||||||
f->next_dep = 0;
|
f->next_dep = 0;
|
||||||
}
|
}
|
||||||
if (f->pending_dep >= 0) {
|
if (f->pending_dep >= 0) {
|
||||||
@@ -2094,7 +2190,7 @@ failed:
|
|||||||
for (int i = 0; i < nframe; i++)
|
for (int i = 0; i < nframe; i++)
|
||||||
g->pkg[frames[i].pkg].failed = 1;
|
g->pkg[frames[i].pkg].failed = 1;
|
||||||
free(frames);
|
free(frames);
|
||||||
return sep_fatal_allocation ? -2 : -1;
|
return sep_fatal_allocation ? -2 : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@@ -3016,6 +3112,7 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
|||||||
}
|
}
|
||||||
int lr = sep_load_pkg(g, root, products[i].context);
|
int lr = sep_load_pkg(g, root, products[i].context);
|
||||||
if (lr == -2) return 1;
|
if (lr == -2) return 1;
|
||||||
|
if (lr == SEP_LOAD_INTERNAL) return 1;
|
||||||
if (lr < 0) {
|
if (lr < 0) {
|
||||||
g->pkg[root].failed = 1;
|
g->pkg[root].failed = 1;
|
||||||
continue;
|
continue;
|
||||||
@@ -3034,6 +3131,7 @@ build_one_sep_impl(const char *src, int entry_is_dir,
|
|||||||
if (support >= 0 && support != variant) {
|
if (support >= 0 && support != variant) {
|
||||||
int lr = sep_load_pkg(g, support, products[i].context);
|
int lr = sep_load_pkg(g, support, products[i].context);
|
||||||
if (lr == -2) return 1;
|
if (lr == -2) return 1;
|
||||||
|
if (lr == SEP_LOAD_INTERNAL) return 1;
|
||||||
if (lr < 0) g->pkg[variant].failed = 1;
|
if (lr < 0) g->pkg[variant].failed = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
lib/os/os.ww
15
lib/os/os.ww
@@ -46,6 +46,7 @@ type nr = enum i64 {
|
|||||||
RMDIR = 84,
|
RMDIR = 84,
|
||||||
UNLINK = 87,
|
UNLINK = 87,
|
||||||
SYMLINK = 88,
|
SYMLINK = 88,
|
||||||
|
READLINK = 89,
|
||||||
SETPGID = 109,
|
SETPGID = 109,
|
||||||
GETDENTS64 = 217,
|
GETDENTS64 = 217,
|
||||||
NEWFSTATAT = 262,
|
NEWFSTATAT = 262,
|
||||||
@@ -132,7 +133,7 @@ export def SFD_NONBLOCK: i32 = O_NONBLOCK;
|
|||||||
export def SFD_CLOEXEC: i32 = O_CLOEXEC;
|
export def SFD_CLOEXEC: i32 = O_CLOEXEC;
|
||||||
|
|
||||||
fn kpath(p: str) *u8 = {
|
fn kpath(p: str) *u8 = {
|
||||||
if (p.len + 1 >= PATH_MAX) { return nil: *u8; }; // ENAMETOOLONG
|
if (p.len + 1 > PATH_MAX) { return nil: *u8; }; // ENAMETOOLONG
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < p.len) { pathbuf[i] = p[i]; i += 1; };
|
for (i < p.len) { pathbuf[i] = p[i]; i += 1; };
|
||||||
pathbuf[p.len] = 0u8;
|
pathbuf[p.len] = 0u8;
|
||||||
@@ -347,7 +348,7 @@ export fn rmdir(path: str) i32 = {
|
|||||||
export fn rename(oldpath: str, newpath: str) i32 = {
|
export fn rename(oldpath: str, newpath: str) i32 = {
|
||||||
let p: *u8 = kpath(oldpath);
|
let p: *u8 = kpath(oldpath);
|
||||||
if (p == nil: *u8) { return -36i32; };
|
if (p == nil: *u8) { return -36i32; };
|
||||||
if (newpath.len + 1 >= PATH_MAX) { return -36i32; };
|
if (newpath.len + 1 > PATH_MAX) { return -36i32; };
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < newpath.len) { pathbuf2[i] = newpath[i]; i += 1; };
|
for (i < newpath.len) { pathbuf2[i] = newpath[i]; i += 1; };
|
||||||
pathbuf2[newpath.len] = 0u8;
|
pathbuf2[newpath.len] = 0u8;
|
||||||
@@ -364,13 +365,21 @@ export fn rename(oldpath: str, newpath: str) i32 = {
|
|||||||
export fn symlink(target: str, path: str) i32 = {
|
export fn symlink(target: str, path: str) i32 = {
|
||||||
let p: *u8 = kpath(target);
|
let p: *u8 = kpath(target);
|
||||||
if (p == nil: *u8) { return -36i32; };
|
if (p == nil: *u8) { return -36i32; };
|
||||||
if (path.len + 1 >= PATH_MAX) { return -36i32; };
|
if (path.len + 1 > PATH_MAX) { return -36i32; };
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < path.len) { pathbuf2[i] = path[i]; i += 1; };
|
for (i < path.len) { pathbuf2[i] = path[i]; i += 1; };
|
||||||
pathbuf2[path.len] = 0u8;
|
pathbuf2[path.len] = 0u8;
|
||||||
return syscall2(nr.SYMLINK, p: i64, (&pathbuf2[0]): i64): i32;
|
return syscall2(nr.SYMLINK, p: i64, (&pathbuf2[0]): i64): i32;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// readlink — read a symlink target without appending NUL. The caller owns the
|
||||||
|
// buffer and uses the returned byte count, matching readlink(2).
|
||||||
|
export fn readlink(path: str, buf: *u8, n: u64) i64 = {
|
||||||
|
let p: *u8 = kpath(path);
|
||||||
|
if (p == nil: *u8) { return -36i64; };
|
||||||
|
return syscall3(nr.READLINK, p: i64, buf: i64, n: i64);
|
||||||
|
};
|
||||||
|
|
||||||
// mkdirs — recursive mkdir. Creates `path` and any non-existent
|
// mkdirs — recursive mkdir. Creates `path` and any non-existent
|
||||||
// parent directories with the given mode. EEXIST is silently
|
// parent directories with the given mode. EEXIST is silently
|
||||||
// accepted (matches Hare's `errors::exists` skip in os::mkdirs);
|
// accepted (matches Hare's `errors::exists` skip in os::mkdirs);
|
||||||
|
|||||||
@@ -157,6 +157,25 @@ fn istype(m: os.mode, t: os.mode) bool = {
|
|||||||
rmtree(&t);
|
rmtree(&t);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@test fn test_readlink_verbatim() void = {
|
||||||
|
let t: tree;
|
||||||
|
mktree(&t);
|
||||||
|
let buf: [16]u8;
|
||||||
|
let i: i32 = 0;
|
||||||
|
for (i < 16) { buf[i] = 90u8; i += 1; }; // 'Z'
|
||||||
|
let n: i64 = os.readlink(t.symlink, &buf[0], 16u64);
|
||||||
|
assert(!(n != 9i64));
|
||||||
|
let want: str = "./regfile";
|
||||||
|
i = 0;
|
||||||
|
for (i < want.len) {
|
||||||
|
assert(!(buf[i] != want[i]));
|
||||||
|
i += 1;
|
||||||
|
};
|
||||||
|
let ni: i32 = n: i32;
|
||||||
|
assert(!(buf[ni] != 90u8));
|
||||||
|
rmtree(&t);
|
||||||
|
};
|
||||||
|
|
||||||
@test fn test_fstat_regfile() void = {
|
@test fn test_fstat_regfile() void = {
|
||||||
let t: tree;
|
let t: tree;
|
||||||
mktree(&t);
|
mktree(&t);
|
||||||
@@ -196,7 +215,7 @@ fn istype(m: os.mode, t: os.mode) bool = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// kpath copies into a single [PATH_MAX]u8 buffer and reserves one byte
|
// kpath copies into a single [PATH_MAX]u8 buffer and reserves one byte
|
||||||
// for the NUL terminator (`p.len + 1 >= PATH_MAX` → reject). The
|
// for the NUL terminator (`p.len + 1 > PATH_MAX` → reject). The
|
||||||
// rejection surfaces as `oserror = -36` (ENAMETOOLONG) on (... |
|
// rejection surfaces as `oserror = -36` (ENAMETOOLONG) on (... |
|
||||||
// oserror)-returning wrappers, and as `false` on [[os.exists]]
|
// oserror)-returning wrappers, and as `false` on [[os.exists]]
|
||||||
// (Hare's os::exists doc: "true if a node exists at the given path,
|
// (Hare's os::exists doc: "true if a node exists at the given path,
|
||||||
|
|||||||
@@ -522,6 +522,7 @@ def SEP_ROLE_NORMAL: i32 = 0;
|
|||||||
def SEP_ROLE_TEST_SUPPORT: i32 = 1;
|
def SEP_ROLE_TEST_SUPPORT: i32 = 1;
|
||||||
def SEP_ROLE_GENERATED_MAIN: i32 = 2;
|
def SEP_ROLE_GENERATED_MAIN: i32 = 2;
|
||||||
def SEP_TEST_SUPPORT_MODULE: str = "__wwtest";
|
def SEP_TEST_SUPPORT_MODULE: str = "__wwtest";
|
||||||
|
def SEP_LOAD_INTERNAL: i32 = -3;
|
||||||
def SEP_INITIAL_CAP: i32 = 8;
|
def SEP_INITIAL_CAP: i32 = 8;
|
||||||
def SEP_COUNT_MAX: i32 = 2147483647;
|
def SEP_COUNT_MAX: i32 = 2147483647;
|
||||||
|
|
||||||
@@ -1345,6 +1346,115 @@ fn sepjoinpath(dir: *u8, name: *u8) *u8 = {
|
|||||||
return sepjoinpathlit(dir, pathstr(name));
|
return sepjoinpathlit(dir, pathstr(name));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn sepphysicaljoin(dir: *u8, name: str) *u8 = {
|
||||||
|
if (cstrlen(dir) == 1u64 && dir[0u64] == '/': u8) {
|
||||||
|
return sepappendlit(dir, name);
|
||||||
|
};
|
||||||
|
return sepjoinpathlit(dir, name);
|
||||||
|
};
|
||||||
|
|
||||||
|
fn seppendingpath(target: str, rest: *u8, requiredir: bool) *u8 = {
|
||||||
|
let restlen: u64 = cstrlen(rest);
|
||||||
|
let need: u64 = 0u64;
|
||||||
|
if (!sepaddbytes(&need, target.len: u64)
|
||||||
|
|| (target.len != 0 && (restlen != 0 || requiredir)
|
||||||
|
&& !sepaddbytes(&need, 1u64))
|
||||||
|
|| !sepaddbytes(&need, restlen)
|
||||||
|
|| !sepaddbytes(&need, 1u64)) { return nil; };
|
||||||
|
let out: []u8;
|
||||||
|
if (!sepmakebytes(need, &out)) { return nil; };
|
||||||
|
let off: u64 = strinto(out.ptr, 0u64, target);
|
||||||
|
if (target.len != 0 && (restlen != 0 || requiredir)) {
|
||||||
|
off = byteinto(out.ptr, off, '/': u8);
|
||||||
|
};
|
||||||
|
off = cstrinto(out.ptr, off, rest);
|
||||||
|
cstrseal(out.ptr, off);
|
||||||
|
return out.ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
fn sepphysicalparent(path: *u8) *u8 = {
|
||||||
|
let n: u64 = cstrlen(path);
|
||||||
|
for (n > 1u64 && path[n - 1u64] == '/': u8) { n -= 1u64; };
|
||||||
|
for (n > 1u64 && path[n - 1u64] != '/': u8) { n -= 1u64; };
|
||||||
|
if (n > 1u64) { n -= 1u64; };
|
||||||
|
return sepdupcstr(path, n);
|
||||||
|
};
|
||||||
|
|
||||||
|
fn sepcanonicalfile(path: *u8) *u8 = {
|
||||||
|
if (path[0u64] == 0u8) { return nil; };
|
||||||
|
let pending: *u8 = sepdupcstr(path, cstrlen(path));
|
||||||
|
if (pending == nil) { return nil; };
|
||||||
|
let resolved: *u8 = nil;
|
||||||
|
if (path[0u64] == '/': u8) {
|
||||||
|
resolved = sepdupcstr("/".ptr, 1u64);
|
||||||
|
} else {
|
||||||
|
let cwd: []u8;
|
||||||
|
if (!sepmakebytes(os.PATH_MAX: u64, &cwd)) { return nil; };
|
||||||
|
let n: i64 = os.getcwd(cwd.ptr, cwd.len: u64);
|
||||||
|
if (n <= 1i64 || n > cwd.len: i64) { return nil; };
|
||||||
|
resolved = sepdupcstr(cwd.ptr, (n - 1i64): u64);
|
||||||
|
};
|
||||||
|
if (resolved == nil) { return nil; };
|
||||||
|
let links: i32 = 0;
|
||||||
|
for (true) {
|
||||||
|
let total: u64 = cstrlen(pending);
|
||||||
|
let start: u64 = 0u64;
|
||||||
|
for (start < total && pending[start] == '/': u8) { start += 1u64; };
|
||||||
|
if (start == total) { return resolved; };
|
||||||
|
let end: u64 = start;
|
||||||
|
for (end < total && pending[end] != '/': u8) { end += 1u64; };
|
||||||
|
let followed: bool = end < total;
|
||||||
|
let rest: u64 = end;
|
||||||
|
for (rest < total && pending[rest] == '/': u8) { rest += 1u64; };
|
||||||
|
let requiredir: bool = followed && rest == total;
|
||||||
|
let name: str;
|
||||||
|
name.ptr = pending + start;
|
||||||
|
name.len = (end - start): i32;
|
||||||
|
if (name.len == 1 && name[0] == '.': u8) {
|
||||||
|
pending = pending + rest;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if (name.len == 2 && name[0] == '.': u8 && name[1] == '.': u8) {
|
||||||
|
resolved = sepphysicalparent(resolved);
|
||||||
|
if (resolved == nil) { return nil; };
|
||||||
|
pending = pending + rest;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let candidate: *u8 = sepphysicaljoin(resolved, name);
|
||||||
|
if (candidate == nil) { return nil; };
|
||||||
|
let fi: os.filestat;
|
||||||
|
match (os.lstat(&fi, pathstr(candidate))) {
|
||||||
|
case void => void;
|
||||||
|
case let e: os.oserror => return nil;
|
||||||
|
};
|
||||||
|
let typ: u32 = (fi.mode: u32) & 61440u32;
|
||||||
|
if (typ == os.mode.LINK: u32) {
|
||||||
|
if (links == 40) { return nil; };
|
||||||
|
links += 1;
|
||||||
|
let target: []u8;
|
||||||
|
if (!sepmakebytes(os.PATH_MAX: u64, &target)) { return nil; };
|
||||||
|
let n: i64 = os.readlink(pathstr(candidate), target.ptr,
|
||||||
|
os.PATH_MAX: u64);
|
||||||
|
if (n < 0i64 || n >= os.PATH_MAX: i64) { return nil; };
|
||||||
|
let ni: i32 = n: i32;
|
||||||
|
target[ni] = 0u8;
|
||||||
|
let targetname: str;
|
||||||
|
targetname.ptr = target.ptr;
|
||||||
|
targetname.len = ni;
|
||||||
|
if (ni > 0 && target[0] == '/': u8) {
|
||||||
|
resolved = sepdupcstr("/".ptr, 1u64);
|
||||||
|
if (resolved == nil) { return nil; };
|
||||||
|
};
|
||||||
|
pending = seppendingpath(targetname, pending + rest, requiredir);
|
||||||
|
if (pending == nil) { return nil; };
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if (followed && typ != os.mode.DIR: u32) { return nil; };
|
||||||
|
resolved = candidate;
|
||||||
|
pending = pending + rest;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
fn sepdirectoryvariant(variant: i32) bool = {
|
fn sepdirectoryvariant(variant: i32) bool = {
|
||||||
return variant == SEP_VARIANT_PRODUCTION
|
return variant == SEP_VARIANT_PRODUCTION
|
||||||
|| variant == SEP_VARIANT_SAME_TEST
|
|| variant == SEP_VARIANT_SAME_TEST
|
||||||
@@ -1422,6 +1532,106 @@ fn sepforbiddencommandimport(g: *sepgraph, importer: i32, dep: i32) bool = {
|
|||||||
&& cstreq(from.canon, to.canon));
|
&& cstreq(from.canon, to.canon));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn sepinternalparentcount(path: *u8, parents: *u64) bool = {
|
||||||
|
let total: u64 = cstrlen(path);
|
||||||
|
let p: u64 = 0u64;
|
||||||
|
let components: u64 = 0u64;
|
||||||
|
let final: u64 = 0u64;
|
||||||
|
let found: bool = false;
|
||||||
|
for (p < total) {
|
||||||
|
for (p < total && path[p] == '.': u8) { p += 1u64; };
|
||||||
|
if (p == total) { break; };
|
||||||
|
let end: u64 = p;
|
||||||
|
for (end < total && path[end] != '.': u8) { end += 1u64; };
|
||||||
|
if (end - p == "internal".len: u64
|
||||||
|
&& bytecmp(path + p, end - p, "internal".ptr,
|
||||||
|
"internal".len: u64) == 0) {
|
||||||
|
final = components;
|
||||||
|
found = true;
|
||||||
|
};
|
||||||
|
components += 1u64;
|
||||||
|
p = end;
|
||||||
|
};
|
||||||
|
if (!found) { return false; };
|
||||||
|
*parents = components - final;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
fn seprawimporterdir(p: *seppkg) *u8 = {
|
||||||
|
let total: u64 = cstrlen(p.canon);
|
||||||
|
let slash: u64 = total;
|
||||||
|
let i: u64 = 0u64;
|
||||||
|
for (i < total) {
|
||||||
|
if (p.canon[i] == '/': u8) { slash = i; };
|
||||||
|
i += 1u64;
|
||||||
|
};
|
||||||
|
if (slash == total) { return nil; };
|
||||||
|
if (slash == 0u64) { slash = 1u64; };
|
||||||
|
return sepdupcstr(p.canon, slash);
|
||||||
|
};
|
||||||
|
|
||||||
|
fn sepcanonicalinternalowner(path: *u8, parents: u64) *u8 = {
|
||||||
|
let boundary: u64 = cstrlen(path);
|
||||||
|
for (boundary > 1u64 && path[boundary - 1u64] == '/': u8) {
|
||||||
|
boundary -= 1u64;
|
||||||
|
};
|
||||||
|
let pi: u64 = 0u64;
|
||||||
|
for (pi < parents) {
|
||||||
|
for (boundary > 0u64 && path[boundary - 1u64] != '/': u8) {
|
||||||
|
boundary -= 1u64;
|
||||||
|
};
|
||||||
|
for (boundary > 1u64 && path[boundary - 1u64] == '/': u8) {
|
||||||
|
boundary -= 1u64;
|
||||||
|
};
|
||||||
|
pi += 1u64;
|
||||||
|
};
|
||||||
|
let lexical: *u8 = nil;
|
||||||
|
if (boundary == 0u64) {
|
||||||
|
lexical = sepdupcstr(".".ptr, 1u64);
|
||||||
|
} else {
|
||||||
|
lexical = sepdupcstr(path, boundary);
|
||||||
|
};
|
||||||
|
if (lexical == nil) { return nil; };
|
||||||
|
return canonicaldir(pathstr(lexical));
|
||||||
|
};
|
||||||
|
|
||||||
|
fn sepinternalimportallowed(from: *seppkg, targetpath: *u8,
|
||||||
|
targetentry: *u8) i32 = {
|
||||||
|
let parents: u64 = 0u64;
|
||||||
|
if (!sepinternalparentcount(targetpath, &parents)) { return 1; };
|
||||||
|
let importer: *u8 = from.canon;
|
||||||
|
if (from.isdir == 0) {
|
||||||
|
importer = seprawimporterdir(from);
|
||||||
|
if (importer == nil) {
|
||||||
|
if (!sepfatalallocation) {
|
||||||
|
cerr("ww: cannot canonicalize package ");
|
||||||
|
cerr(pathstr(from.entry)); cerr("\n");
|
||||||
|
};
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
let owner: *u8 = sepcanonicalinternalowner(targetentry, parents);
|
||||||
|
if (owner == nil) {
|
||||||
|
if (!sepfatalallocation) {
|
||||||
|
cerr("ww: cannot canonicalize package ");
|
||||||
|
cerr(pathstr(targetentry)); cerr("\n");
|
||||||
|
};
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
let boundary: u64 = cstrlen(owner);
|
||||||
|
let n: u64 = cstrlen(importer);
|
||||||
|
if (n == boundary
|
||||||
|
&& bytecmp(importer, boundary, owner, boundary) == 0) {
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
if (boundary == 1u64 && owner[0u64] == '/': u8
|
||||||
|
&& importer[0u64] == '/': u8) { return 1; };
|
||||||
|
if (n > boundary
|
||||||
|
&& bytecmp(importer, boundary, owner, boundary) == 0
|
||||||
|
&& importer[boundary] == '/': u8) { return 1; };
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
fn sepcommandcompilermarker(g: *sepgraph, pi: i32) bool = {
|
fn sepcommandcompilermarker(g: *sepgraph, pi: i32) bool = {
|
||||||
return sepcommanddeclaredname(&g.pkg[pi]) && !g.pkg[pi].linkentry;
|
return sepcommanddeclaredname(&g.pkg[pi]) && !g.pkg[pi].linkentry;
|
||||||
};
|
};
|
||||||
@@ -1494,13 +1704,15 @@ fn sepfindoraddvariant(g: *sepgraph, path: *u8, entry: *u8,
|
|||||||
let canon: *u8 = nil;
|
let canon: *u8 = nil;
|
||||||
if (isdir != 0) {
|
if (isdir != 0) {
|
||||||
canon = canonicaldir(pathstr(entry));
|
canon = canonicaldir(pathstr(entry));
|
||||||
|
} else {
|
||||||
|
canon = sepcanonicalfile(entry);
|
||||||
|
};
|
||||||
if (canon == nil) {
|
if (canon == nil) {
|
||||||
if (sepfatalallocation) { return -1; };
|
if (sepfatalallocation) { return -1; };
|
||||||
cerr("ww: cannot canonicalize package ");
|
cerr("ww: cannot canonicalize package ");
|
||||||
cerr(pathstr(entry)); cerr("\n");
|
cerr(pathstr(entry)); cerr("\n");
|
||||||
return -1;
|
return -1;
|
||||||
};
|
};
|
||||||
};
|
|
||||||
let incoming: *u8 = nil;
|
let incoming: *u8 = nil;
|
||||||
if (isdir != 0 && path[0u64] != 0u8) {
|
if (isdir != 0 && path[0u64] != 0u8) {
|
||||||
incoming = sepvariantpath(variant, path);
|
incoming = sepvariantpath(variant, path);
|
||||||
@@ -2112,19 +2324,19 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, searchpath: *u8,
|
|||||||
};
|
};
|
||||||
let externalproduction: bool = false;
|
let externalproduction: bool = false;
|
||||||
let ipath: *u8 = nil;
|
let ipath: *u8 = nil;
|
||||||
|
let visibilityentry: *u8 = nil;
|
||||||
if (g.pkg[pi].variant == SEP_VARIANT_EXTERNAL
|
if (g.pkg[pi].variant == SEP_VARIANT_EXTERNAL
|
||||||
&& g.pkg[pi].importbase != nil
|
&& g.pkg[pi].importbase != nil
|
||||||
&& cstrlen(g.pkg[pi].importbase) == idn
|
&& cstrlen(g.pkg[pi].importbase) == idn
|
||||||
&& bytecmp(g.pkg[pi].importbase, idn, idp, idn) == 0) {
|
&& bytecmp(g.pkg[pi].importbase, idn, idp, idn) == 0) {
|
||||||
ipath = g.pkg[pi].canon;
|
ipath = g.pkg[pi].canon;
|
||||||
|
visibilityentry = g.pkg[pi].entry;
|
||||||
};
|
};
|
||||||
if (ipath == nil) {
|
if (ipath == nil) {
|
||||||
ipath = locateimport(searchpath, idp, idn);
|
ipath = locateimport(searchpath, idp, idn);
|
||||||
|
visibilityentry = ipath;
|
||||||
};
|
};
|
||||||
if (ipath != nil) {
|
if (ipath != nil) {
|
||||||
if (!sepbindadd(bindings, 'D': u8, u.usepath, ipath)) {
|
|
||||||
return -1;
|
|
||||||
};
|
|
||||||
let self: bool = os.samefile(pathstr(ipath),
|
let self: bool = os.samefile(pathstr(ipath),
|
||||||
pathstr(g.pkg[pi].entry));
|
pathstr(g.pkg[pi].entry));
|
||||||
if (self && (sepexternalname(&g.pkg[pi], idp, idn, true)
|
if (self && (sepexternalname(&g.pkg[pi], idp, idn, true)
|
||||||
@@ -2150,6 +2362,18 @@ fn sepscanfile(g: *sepgraph, pi: i32, file: *u8, searchpath: *u8,
|
|||||||
// action. Discovery role and product artifact never create another.
|
// action. Discovery role and product artifact never create another.
|
||||||
let di: i32 = sepfindoradd(g, nm.ptr, ipath, 1);
|
let di: i32 = sepfindoradd(g, nm.ptr, ipath, 1);
|
||||||
if (di < 0) { return -1; };
|
if (di < 0) { return -1; };
|
||||||
|
let allowed: i32 = sepinternalimportallowed(&g.pkg[pi],
|
||||||
|
g.pkg[di].path, visibilityentry);
|
||||||
|
if (allowed < 0) { return -1; };
|
||||||
|
if (allowed == 0) {
|
||||||
|
cerrpos(u.file, u.line, u.col);
|
||||||
|
cerr(": error: use of internal package ");
|
||||||
|
cerr(pathstr(g.pkg[di].path)); cerr(" not allowed\n");
|
||||||
|
return SEP_LOAD_INTERNAL;
|
||||||
|
};
|
||||||
|
if (!sepbindadd(bindings, 'D': u8, u.usepath, ipath)) {
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
if (!sepadddep(g, pi, di)) { return -1; };
|
if (!sepadddep(g, pi, di)) { return -1; };
|
||||||
} else {
|
} else {
|
||||||
let lstart: u64 = 0u64;
|
let lstart: u64 = 0u64;
|
||||||
@@ -2510,7 +2734,8 @@ fn seploadpkg(g: *sepgraph, pi: i32, context: i32) i32 = {
|
|||||||
nframe -= 1;
|
nframe -= 1;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if (seppreparepkgcontext(g, f.pkg, f.context) < 0) {
|
let prepared: i32 = seppreparepkgcontext(g, f.pkg, f.context);
|
||||||
|
if (prepared < 0) {
|
||||||
let fi: i32 = 0;
|
let fi: i32 = 0;
|
||||||
for (fi < nframe) {
|
for (fi < nframe) {
|
||||||
g.pkg[frames[fi].pkg].failed = true;
|
g.pkg[frames[fi].pkg].failed = true;
|
||||||
@@ -2519,7 +2744,7 @@ fn seploadpkg(g: *sepgraph, pi: i32, context: i32) i32 = {
|
|||||||
if (sepfatalallocation) {
|
if (sepfatalallocation) {
|
||||||
return sepfinishloadframes(frames, -2);
|
return sepfinishloadframes(frames, -2);
|
||||||
};
|
};
|
||||||
return sepfinishloadframes(frames, -1);
|
return sepfinishloadframes(frames, prepared);
|
||||||
};
|
};
|
||||||
f.nextdep = 0;
|
f.nextdep = 0;
|
||||||
};
|
};
|
||||||
@@ -3771,6 +3996,7 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
|||||||
let loadresult: i32 = seploadpkg(g, root,
|
let loadresult: i32 = seploadpkg(g, root,
|
||||||
products[producti].context);
|
products[producti].context);
|
||||||
if (loadresult == -2) { return 1; };
|
if (loadresult == -2) { return 1; };
|
||||||
|
if (loadresult == SEP_LOAD_INTERNAL) { return 1; };
|
||||||
if (loadresult < 0) {
|
if (loadresult < 0) {
|
||||||
g.pkg[root].failed = true;
|
g.pkg[root].failed = true;
|
||||||
producti += 1;
|
producti += 1;
|
||||||
@@ -3793,6 +4019,7 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32,
|
|||||||
let loadresult: i32 = seploadpkg(g, support,
|
let loadresult: i32 = seploadpkg(g, support,
|
||||||
products[producti].context);
|
products[producti].context);
|
||||||
if (loadresult == -2) { return 1; };
|
if (loadresult == -2) { return 1; };
|
||||||
|
if (loadresult == SEP_LOAD_INTERNAL) { return 1; };
|
||||||
if (loadresult < 0) {
|
if (loadresult < 0) {
|
||||||
g.pkg[variant].failed = true;
|
g.pkg[variant].failed = true;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user