@ai-ecoverse/slicc-kernel 1.63.0 → 1.64.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/kernel-worker.js +43 -2
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node-process-worker.js +1 -0
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +43 -2
- package/dist/node.js.map +2 -2
- package/dist/process-worker.js +1 -0
- package/dist/process-worker.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -560,7 +560,7 @@ With `users: 'enforce'` the kernel checks every file call that reaches it agains
|
|
|
560
560
|
- **Opening a file:** an open needs `r` to read and `w` to write or truncate, checked once when the file is opened, in every runtime: Emscripten, WASI (a second open of a path the program already has open is checked as well), JS programs' `ctx.open` and `ctx.fs.writeFile`, and the kernel's own opens. A file the open itself creates is not checked (only its directory is), so `open(O_CREAT | O_WRONLY, 0444)` works. An open descriptor keeps the access it was opened with, through a later `chmod`, and so do descriptors a child inherits. Reads and writes without a descriptor (`ctx.fs.readFile`, `ctx.sync.fs.readFile`) are checked each time.
|
|
561
561
|
- **`access(2)`:** `faccessat` (bash's `[ -r ]`, `test -w`) answers from the same checks, with the real uid and gid, or the effective ones with `AT_EACCESS`, for Emscripten programs, and for WASI programs through `slicc_fs.path_access` (see [File modes and the umask](#file-modes-and-the-umask)).
|
|
562
562
|
- **Exec:** running a program by path needs `x` on it, and `r` too when it's a script (`#!` or a `binfmt` suffix); a WASI or Emscripten binary needs only `x`. Root needs an `x` bit as well. Commands found by name, and `/usr/bin` and `/bin`, are always executable; a user's own commands in `~/.local/share/pnpm/bin` are reached through the user's home, so another user gets `EACCES` there, as for any file. A refused exec is `EACCES` (126 in bash).
|
|
563
|
-
-
|
|
563
|
+
- **The layout:** with `users: 'report'` or `'enforce'`, the kernel gives its own paths an owner and a mode when they have no owner record yet: on a fresh root, and once on a root from before owners. These are `/` `0755`, `/root` `0700`, `/home` `0755`, `/etc` `0755`, `/etc/passwd` and `/etc/group` `0644`, `/usr` and `/usr/local` `0755`, `/node_modules` (or the `modules` option) and the system `PNPM_HOME` `0755`, all root's. A path that has a record keeps it, so an admin's `chmod 711 /home` or `chown cone /node_modules` lasts across restarts; a mounted path is left to its mount, and a path below a symlinked directory is stamped where it really is. Only a root that records owners is stamped: the OPFS root with its sidecar, which the page and the Node entry (in memory) both use; a custom root file system without owner records is left as it is. The directory for files removed while open, `/.slicc-unlinked`, is made root's `0700`. `/tmp` is the exception: it is set to `1777` at every start. Emscripten programs see `/` at `0755`, `/dev` at `0755`, `null`, `zero`, `full`, `random`, `urandom` and `tty` at `0666`, and `/dev/shm` at `1777`. With `users: 'off'` nothing is stamped. Paths the embedder makes (bios's `/opt` and `/var/lib/slicc`) are the embedder's to set, and `/etc/sudoers` comes with `sudo`.
|
|
564
564
|
- **A client's `fs`:** checked as the client's user; a page's client (root) passes.
|
|
565
565
|
|
|
566
566
|
With `users: 'enforce'` not yet the default, no release turns it on before `sudo` exists.
|
package/dist/kernel-worker.js
CHANGED
|
@@ -427,6 +427,9 @@ var OpfsFs = class {
|
|
|
427
427
|
trackOwners() {
|
|
428
428
|
this.owners = true;
|
|
429
429
|
}
|
|
430
|
+
async hasOwner(path) {
|
|
431
|
+
return ownOf((await this.meta.get([path]))[0]) !== void 0;
|
|
432
|
+
}
|
|
430
433
|
async ancestry(path) {
|
|
431
434
|
const entries = await this.meta.get(chain(path));
|
|
432
435
|
const out = [];
|
|
@@ -6427,6 +6430,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
|
|
|
6427
6430
|
...fs.realpath ? { realpath: fs.realpath.bind(fs) } : {},
|
|
6428
6431
|
...fs.chown ? { chown: fs.chown.bind(fs) } : {},
|
|
6429
6432
|
...fs.ancestry ? { ancestry: fs.ancestry.bind(fs) } : {},
|
|
6433
|
+
...fs.hasOwner ? { hasOwner: fs.hasOwner.bind(fs) } : {},
|
|
6430
6434
|
chmod: (path, mode) => fs.chmod(path, mode),
|
|
6431
6435
|
utimes: (path, atime, mtime) => fs.utimes(path, atime, mtime),
|
|
6432
6436
|
lutimes: (path, atime, mtime) => lutimesOf(fs, path, atime, mtime),
|
|
@@ -6526,8 +6530,10 @@ var UnlinkedKeeper = class {
|
|
|
6526
6530
|
count = 0;
|
|
6527
6531
|
links = /* @__PURE__ */ new Map();
|
|
6528
6532
|
linked = /* @__PURE__ */ new Map();
|
|
6533
|
+
dirOwner;
|
|
6529
6534
|
constructor(fs, holders, rootOf, options = {}) {
|
|
6530
6535
|
const { locks, owner } = options;
|
|
6536
|
+
this.dirOwner = options.dirOwner;
|
|
6531
6537
|
this.fs = fs;
|
|
6532
6538
|
this.holders = holders;
|
|
6533
6539
|
this.rootOf = rootOf;
|
|
@@ -6638,7 +6644,8 @@ var UnlinkedKeeper = class {
|
|
|
6638
6644
|
const dir = dirOf(this.rootOf(path));
|
|
6639
6645
|
const hidden = `${dir}/${this.instance}.${++this.count}`;
|
|
6640
6646
|
try {
|
|
6641
|
-
|
|
6647
|
+
const owner = this.dirOwner;
|
|
6648
|
+
await this.fs.mkdir(dir, { recursive: true, ...owner ? { owner } : {} });
|
|
6642
6649
|
await this.fs.rename(path, hidden);
|
|
6643
6650
|
await this.linkedName(hidden);
|
|
6644
6651
|
return hidden;
|
|
@@ -8017,6 +8024,10 @@ var MountFs = class {
|
|
|
8017
8024
|
if (this.at(real)) throw errnoError("ENOSYS", path);
|
|
8018
8025
|
return real;
|
|
8019
8026
|
}
|
|
8027
|
+
async hasOwner(path) {
|
|
8028
|
+
if (this.at(path)) return true;
|
|
8029
|
+
return await this.base.hasOwner?.(path) ?? true;
|
|
8030
|
+
}
|
|
8020
8031
|
async ancestry(path) {
|
|
8021
8032
|
const names = chain(normalizePath(path));
|
|
8022
8033
|
const first = names.findIndex((name2) => this.at(name2));
|
|
@@ -14126,6 +14137,16 @@ var NOT_FOUND = 127;
|
|
|
14126
14137
|
var DECIDE_MS = 100;
|
|
14127
14138
|
var INIT_PID = 1;
|
|
14128
14139
|
var SHARED_DIRS = ["/tmp", "/home"];
|
|
14140
|
+
var LAYOUT = [
|
|
14141
|
+
["/", 493],
|
|
14142
|
+
["/root", 448],
|
|
14143
|
+
["/home", 493],
|
|
14144
|
+
["/etc", 493],
|
|
14145
|
+
[PASSWD, 420],
|
|
14146
|
+
[GROUP, 420],
|
|
14147
|
+
["/usr", 493],
|
|
14148
|
+
["/usr/local", 493]
|
|
14149
|
+
];
|
|
14129
14150
|
var encoder3 = new TextEncoder();
|
|
14130
14151
|
var USERS_MODES = ["off", "report", "enforce"];
|
|
14131
14152
|
function usersMode(mode = "off") {
|
|
@@ -14319,7 +14340,8 @@ var Launcher = class {
|
|
|
14319
14340
|
{
|
|
14320
14341
|
locks: webLocks(),
|
|
14321
14342
|
busy: (path) => [...this.openFiles].some((nodes) => nodes.holds(path)),
|
|
14322
|
-
owner: options.unlinkOwner
|
|
14343
|
+
owner: options.unlinkOwner,
|
|
14344
|
+
...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
|
|
14323
14345
|
}
|
|
14324
14346
|
);
|
|
14325
14347
|
this.fs = this.unlinked.wrap(keepingOpen(fs, this.nodes));
|
|
@@ -14944,6 +14966,24 @@ var Launcher = class {
|
|
|
14944
14966
|
await this.provisionPnpmHome();
|
|
14945
14967
|
return entry;
|
|
14946
14968
|
}
|
|
14969
|
+
async stampLayout() {
|
|
14970
|
+
const layout = [
|
|
14971
|
+
...LAYOUT,
|
|
14972
|
+
[this.modulesDir, 493],
|
|
14973
|
+
[this.pnpmHome, 493],
|
|
14974
|
+
[`/${UNLINKED_DIR}`, 448]
|
|
14975
|
+
];
|
|
14976
|
+
for (const [path, mode] of layout) {
|
|
14977
|
+
const real = await this.realLayoutPath(path);
|
|
14978
|
+
const fresh = real !== void 0 && !await this.base.hasOwner?.(real);
|
|
14979
|
+
if (fresh) await this.base.chown?.(real, { uid: 0, gid: 0, mode, follow: false });
|
|
14980
|
+
}
|
|
14981
|
+
}
|
|
14982
|
+
async realLayoutPath(path) {
|
|
14983
|
+
if (!await this.base.exists(path)) return void 0;
|
|
14984
|
+
if (path === "/") return path;
|
|
14985
|
+
return joinName(await realDir(this.base, parentOf3(path)), baseName(path));
|
|
14986
|
+
}
|
|
14947
14987
|
async provisionPnpmHome() {
|
|
14948
14988
|
await this.base.mkdir(this.pnpmHome, { recursive: true }).catch(() => void 0);
|
|
14949
14989
|
}
|
|
@@ -15017,6 +15057,7 @@ var Launcher = class {
|
|
|
15017
15057
|
if (this.owners) await this.base.chmod("/tmp", 1023);
|
|
15018
15058
|
await this.provisionPnpmHome();
|
|
15019
15059
|
await this.users.ensure();
|
|
15060
|
+
if (this.owners) await this.stampLayout();
|
|
15020
15061
|
await writeCaFile(this.base, this.ca).catch(() => void 0);
|
|
15021
15062
|
this.fstab = this.mountFstab();
|
|
15022
15063
|
}
|