@ai-ecoverse/slicc-kernel 1.54.7 → 1.54.8

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 CHANGED
@@ -500,7 +500,7 @@ Programs ask the kernel for their ids:
500
500
 
501
501
  ### `/proc`
502
502
 
503
- Every process of the kernel has `/proc/<pid>/` with `cmdline`, `comm`, `stat`, `statm` and `status`, in the formats procps reads, and `/proc/self` is a link to the `/proc/<pid>` of the process that reads it, as on Linux (`readlink /proc/self` is its pid, the one it was exec'd under included). They come from the kernel's process table when they are opened, so a terminal sees the processes of every client; an exec'd program shows under the pid its parent knows. `/proc/uptime`, `/proc/loadavg`, `/proc/stat` and `/proc/meminfo` are there too, and `/proc/mounts`. A process with no parent in the kernel (one a page, a client or the Node entry started) has parent 1, as children of init do, in `getppid()`, `/proc/<pid>/stat` and `ps` alike, and so does a process whose parent has ended, waited for or not (`getppid()` asks the kernel each time; a shell's `$PPID` keeps the value it started with, as on Linux). Pids, parents, process groups, sessions, command lines, terminals, start and boot times are real, and so are the credentials in `status` (`Uid:`, `Gid:`, `Groups:`) and each process's memory: the size of its wasm memory (`VSZ` and `RSS` alike, as wasm has no paging), which `/proc/meminfo` counts as used. CPU times, load and `MemTotal` (from `navigator.deviceMemory`, else 4 GiB) are placeholders. A process that isn't root sees only its own user's processes (see [Users](#users)). WASI and WASIX programs see the same `/proc`, read-only (opening a file in it for writing fails with `EROFS`), from every thread, also through a symlink that points into it (where every change, `mkdir`, `rename`, `unlink`, `chmod` and so on, fails too: with `EROFS` when the root file system has a physical `/proc`, which stays hidden and untouched, and otherwise with the `ENOENT` of the missing directory, since that check is only paid for when a physical `/proc` exists), and `/` lists it, so a WASI program gets a preopen for it; `/proc/self` is the program's own pid there too (the pid an exec'd program took over). Neither has `/proc/<pid>/fd` yet.
503
+ Every process of the kernel has `/proc/<pid>/` with `cmdline`, `comm`, `stat`, `statm` and `status`, in the formats procps reads, and `/proc/self` is a link to the `/proc/<pid>` of the process that reads it, as on Linux (`readlink /proc/self` is its pid, the one it was exec'd under included). They come from the kernel's process table when they are opened, so a terminal sees the processes of every client; an exec'd program shows under the pid its parent knows. `/proc/uptime`, `/proc/loadavg`, `/proc/stat` and `/proc/meminfo` are there too, and `/proc/mounts`. A process with no parent in the kernel (one a page, a client or the Node entry started) has parent 1, as children of init do, in `getppid()`, `/proc/<pid>/stat` and `ps` alike, and so does a process whose parent has ended, waited for or not (`getppid()` asks the kernel each time; a shell's `$PPID` keeps the value it started with, as on Linux). Pids, parents, process groups, sessions, command lines, terminals, start and boot times are real, and so are the credentials in `status` (`Uid:`, `Gid:`, `Groups:`) and each process's memory: the size of its wasm memory (`VSZ` and `RSS` alike, as wasm has no paging), which `/proc/meminfo` counts as used. CPU times, load and `MemTotal` (from `navigator.deviceMemory`, else 4 GiB) are placeholders. A process that isn't root sees only its own user's processes (see [Users](#users)). WASI and WASIX programs see the same `/proc`, read-only (opening a file in it for writing fails with `EROFS`), from every thread, also through a symlink that points into it (where every change, `mkdir`, `rename`, `unlink`, `chmod` and so on, fails too: with `EROFS` when the root file system has a physical `/proc`, which stays hidden and untouched, and otherwise with the `ENOENT` of the missing directory, since that check is only paid for when a physical `/proc` exists; with one, a symlink whose target passes `..` after a file or a missing name, such as `/f/../proc`, fails with `ENOTDIR` or `ENOENT`, as on Linux), and `/` lists it, so a WASI program gets a preopen for it; `/proc/self` is the program's own pid there too (the pid an exec'd program took over). Neither has `/proc/<pid>/fd` yet.
504
504
 
505
505
  The kernel writes `/etc/passwd` and `/etc/group` when it starts (see [Users](#users)). While the root has none of them, or no `/etc/mtab`, reading them gets per-process ones: root is uid 0 and the realm user, uid 1000, is named after `USER`, or `web_user` (Emscripten's default) without one, so it matches `$USER`, with `HOME` as its home, so `id`, `whoami`, `ls -l` and `ps` show names. Nothing is written to OPFS, and a real file there wins.
506
506
 
@@ -1806,9 +1806,9 @@ var KernelSocket = class _KernelSocket {
1806
1806
  case SO_TYPE:
1807
1807
  return SOCK_STREAM;
1808
1808
  case SO_ERROR: {
1809
- const failed = this.failed;
1809
+ const failed2 = this.failed;
1810
1810
  this.failed = void 0;
1811
- return failed ? wasiErrno(failed) : 0;
1811
+ return failed2 ? wasiErrno(failed2) : 0;
1812
1812
  }
1813
1813
  case SO_ACCEPTCONN:
1814
1814
  return this.state === "listening" ? 1 : 0;
@@ -8022,6 +8022,7 @@ function* procWalk(path, follow2) {
8022
8022
  let done4 = [];
8023
8023
  let todo = path.split("/").filter((part) => part !== "");
8024
8024
  let hops = 0;
8025
+ let broken;
8025
8026
  while (todo.length > 0) {
8026
8027
  const part = todo.shift();
8027
8028
  if (part === ".") continue;
@@ -8031,19 +8032,31 @@ function* procWalk(path, follow2) {
8031
8032
  }
8032
8033
  const at = `/${[...done4, part].join("/")}`;
8033
8034
  const whole = normalize(`/${[...done4, part, ...todo].join("/")}`);
8034
- if (inProc(at) && inProc(whole)) return whole;
8035
+ if (inProc(at) && inProc(whole)) return broken ?? whole;
8035
8036
  if (!follow2 && todo.length === 0) return void 0;
8036
- const target = yield at;
8037
- if (target === null) return void 0;
8038
- if (target === void 0) done4.push(part);
8039
- else {
8037
+ const target = yield { at, check: todo.find((next2) => next2 !== ".") === ".." };
8038
+ if (typeof target === "string") {
8040
8039
  if (++hops > MAX_HOPS2) return void 0;
8041
- if (target.startsWith("/")) done4 = [];
8042
- todo = [...target.split("/").filter((part2) => part2 !== ""), ...todo];
8040
+ [done4, todo] = throughLink(target, done4, todo);
8041
+ } else {
8042
+ broken ??= refusalOf(target);
8043
+ done4.push(part);
8043
8044
  }
8044
8045
  }
8045
8046
  return void 0;
8046
8047
  }
8048
+ function failed(err) {
8049
+ const code = err?.code;
8050
+ return { refused: typeof code === "string" ? code : "ENOENT" };
8051
+ }
8052
+ function refusalOf(found) {
8053
+ if (found === true) return void 0;
8054
+ return found === false ? { refused: "ENOTDIR" } : found;
8055
+ }
8056
+ function throughLink(target, done4, todo) {
8057
+ const parts = target.split("/").filter((part) => part !== "");
8058
+ return [target.startsWith("/") ? [] : done4, [...parts, ...todo]];
8059
+ }
8047
8060
  async function procAlias(fs, path, follow2) {
8048
8061
  const { readlink, lstat } = fs;
8049
8062
  if (!readlink || !lstat) return void 0;
@@ -8055,7 +8068,10 @@ async function procAlias(fs, path, follow2) {
8055
8068
  const walk = procWalk(path, follow2);
8056
8069
  let step = walk.next();
8057
8070
  while (!step.done) {
8058
- const target = await readlink.call(fs, step.value).catch(() => void 0);
8071
+ const { at, check } = step.value;
8072
+ const link2 = await readlink.call(fs, at).catch(() => void 0);
8073
+ const isDir = (st) => st.isDirectory === true;
8074
+ const target = link2 ?? (check ? await lstat.call(fs, at).then(isDir, failed) : true);
8059
8075
  step = walk.next(target);
8060
8076
  }
8061
8077
  return step.value;
@@ -10101,6 +10117,8 @@ var WasmProcess = class {
10101
10117
  async openVfs(req) {
10102
10118
  if (inProc(req.path)) return this.openProc(req, req.path);
10103
10119
  const alias = req.orphan ? void 0 : await procAlias(this.options.fs, req.path, !req.exclusive);
10120
+ if (typeof alias === "object")
10121
+ throw Object.assign(new Error(alias.refused), { code: alias.refused });
10104
10122
  if (alias !== void 0) return this.openProc(req, alias);
10105
10123
  await refuseReadonly(this.options.fs, req.path, req.flags, req);
10106
10124
  const fd = this.fds.install(heldFile(), 3);
@@ -11757,22 +11775,22 @@ async function mountFstab(lines2, mount, signal, retries = FSTAB_RETRIES, discar
11757
11775
  );
11758
11776
  }
11759
11777
  async function mountLine({ line, spec, invalid }, mount, signal, retries, discard) {
11760
- let failed = invalid;
11778
+ let failed2 = invalid;
11761
11779
  for (let attempt = 0; !invalid && attempt <= retries.length && !signal.aborted; attempt++) {
11762
11780
  try {
11763
11781
  const entry = await mount(spec);
11764
11782
  if (!signal.aborted) return { line, target: spec.target, entry };
11765
11783
  discard(entry);
11766
- failed = Object.assign(new Error("the kernel stopped"), { code: "ECANCELED" });
11784
+ failed2 = Object.assign(new Error("the kernel stopped"), { code: "ECANCELED" });
11767
11785
  break;
11768
11786
  } catch (err) {
11769
- failed = err;
11787
+ failed2 = err;
11770
11788
  if (FINAL.has(codeOf3(err)) || attempt === retries.length) break;
11771
11789
  await wait(retries[attempt], signal);
11772
11790
  }
11773
11791
  }
11774
- const code = codeOf3(failed);
11775
- return { line, target: spec.target, code, error: String(failed?.message ?? code) };
11792
+ const code = codeOf3(failed2);
11793
+ return { line, target: spec.target, code, error: String(failed2?.message ?? code) };
11776
11794
  }
11777
11795
 
11778
11796
  // src/mount/hostfs.ts