@ai-ecoverse/slicc-kernel 1.57.0 → 1.58.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 +4 -3
- package/dist/driver-worker.js +3 -0
- package/dist/driver-worker.js.map +2 -2
- package/dist/driver.js +3 -0
- package/dist/driver.js.map +2 -2
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +290 -111
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-driver-worker.js +3 -0
- package/dist/node-driver-worker.js.map +2 -2
- package/dist/node-process-worker.js +459 -107
- package/dist/node-process-worker.js.map +3 -3
- package/dist/node.js +290 -111
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +459 -107
- package/dist/process-worker.js.map +3 -3
- package/package.json +1 -1
package/dist/kernel-worker.js
CHANGED
|
@@ -188,6 +188,9 @@ async function lutimesOf(fs, path, atime, mtime) {
|
|
|
188
188
|
if ((await fs.lstat(path)).isSymbolicLink) throw fsError("EOPNOTSUPP", path);
|
|
189
189
|
return fs.utimes(path, atime, mtime);
|
|
190
190
|
}
|
|
191
|
+
function mayLink(fs, path) {
|
|
192
|
+
return fs.links?.(path) ?? true;
|
|
193
|
+
}
|
|
191
194
|
function fsError(code, path) {
|
|
192
195
|
return Object.assign(new Error(`${code}: ${path}`), { code });
|
|
193
196
|
}
|
|
@@ -791,12 +794,12 @@ var OpfsFs = class {
|
|
|
791
794
|
const base = dir === "/" ? "" : dir;
|
|
792
795
|
const metas = await this.meta.get(children2.map(([name2]) => `${base}/${name2}`));
|
|
793
796
|
const handed2 = this.owners ? await this.handedTo(dir) : void 0;
|
|
794
|
-
const
|
|
797
|
+
const owned2 = (stat, entry) => handed2 ? { ...stat, ...ownOf(entry) ?? handed2 } : stat;
|
|
795
798
|
return Promise.all(
|
|
796
799
|
children2.map(async ([name2, child], i) => {
|
|
797
|
-
if (!isHandle(child)) return [name2,
|
|
800
|
+
if (!isHandle(child)) return [name2, owned2(this.linkStat(child), child)];
|
|
798
801
|
const stat = await this.statOf(`${base}/${name2}`, child, metas[i]).catch(() => null);
|
|
799
|
-
return [name2, stat &&
|
|
802
|
+
return [name2, stat && owned2(stat, metas[i])];
|
|
800
803
|
})
|
|
801
804
|
);
|
|
802
805
|
}
|
|
@@ -855,6 +858,12 @@ var OpfsFs = class {
|
|
|
855
858
|
await this.meta.remove([found.path]);
|
|
856
859
|
this.touch(parentOf2(found.path));
|
|
857
860
|
}
|
|
861
|
+
links() {
|
|
862
|
+
return false;
|
|
863
|
+
}
|
|
864
|
+
async link(from) {
|
|
865
|
+
throw fsError("EPERM", `${from}: this file system has no hard links`);
|
|
866
|
+
}
|
|
858
867
|
async rename(from, to) {
|
|
859
868
|
if (from === to) return;
|
|
860
869
|
const source = await this.locate(from, false);
|
|
@@ -5814,6 +5823,8 @@ function withCommandDirs(fs, names, userDir = async () => void 0) {
|
|
|
5814
5823
|
}
|
|
5815
5824
|
},
|
|
5816
5825
|
writeFile: (path, content, owner) => fs.writeFile(path, content, owner),
|
|
5826
|
+
link: (from, to, follow2) => fs.link(from, to, follow2),
|
|
5827
|
+
links: (path) => mayLink(fs, path),
|
|
5817
5828
|
async exists(path) {
|
|
5818
5829
|
return await fs.exists(path) || await linked(path) !== void 0;
|
|
5819
5830
|
},
|
|
@@ -5952,6 +5963,8 @@ var UnlinkedKeeper = class {
|
|
|
5952
5963
|
gate = new PathGate();
|
|
5953
5964
|
queue = Promise.resolve();
|
|
5954
5965
|
count = 0;
|
|
5966
|
+
links = /* @__PURE__ */ new Map();
|
|
5967
|
+
linked = /* @__PURE__ */ new Map();
|
|
5955
5968
|
constructor(fs, holders, rootOf, options = {}) {
|
|
5956
5969
|
const { locks, owner } = options;
|
|
5957
5970
|
this.fs = fs;
|
|
@@ -5971,8 +5984,15 @@ var UnlinkedKeeper = class {
|
|
|
5971
5984
|
wrap(fs = this.fs) {
|
|
5972
5985
|
const keeper = this;
|
|
5973
5986
|
const visible = (names) => names.filter((name2) => name2 !== UNLINKED_DIR);
|
|
5987
|
+
const counted = (st) => {
|
|
5988
|
+
const hidden = keeper.links.get(`${st.dev ?? 0}:${st.ino}`) ?? 0;
|
|
5989
|
+
return hidden === 0 || st.nlink === void 0 ? st : { ...st, nlink: Math.max(0, st.nlink - hidden) };
|
|
5990
|
+
};
|
|
5974
5991
|
return {
|
|
5975
5992
|
...fs,
|
|
5993
|
+
links: (path) => mayLink(fs, path),
|
|
5994
|
+
stat: async (path) => counted(await fs.stat(path)),
|
|
5995
|
+
lstat: async (path) => counted(await fs.lstat(path)),
|
|
5976
5996
|
async rm(path, options) {
|
|
5977
5997
|
const target = fs.resolvePath("/", path);
|
|
5978
5998
|
const pinned = await keeper.pin(target);
|
|
@@ -5986,7 +6006,7 @@ var UnlinkedKeeper = class {
|
|
|
5986
6006
|
await keeper.settle(pinned, () => fs.rename(from, to));
|
|
5987
6007
|
},
|
|
5988
6008
|
readdir: async (path) => visible(await fs.readdir(path)),
|
|
5989
|
-
readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR)
|
|
6009
|
+
readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR).map(([name2, st]) => [name2, st && counted(st)])
|
|
5990
6010
|
};
|
|
5991
6011
|
}
|
|
5992
6012
|
async settle(pinned, op) {
|
|
@@ -6036,6 +6056,7 @@ var UnlinkedKeeper = class {
|
|
|
6036
6056
|
for (const holder of held.get(path)) holder.unkeep(path, file);
|
|
6037
6057
|
await this.serial(async () => {
|
|
6038
6058
|
await Promise.allSettled([this.fs.rename(file.hidden, path)]);
|
|
6059
|
+
this.unlinkedName(file.hidden);
|
|
6039
6060
|
await this.tidy(parentOf3(file.hidden));
|
|
6040
6061
|
});
|
|
6041
6062
|
}
|
|
@@ -6058,6 +6079,7 @@ var UnlinkedKeeper = class {
|
|
|
6058
6079
|
try {
|
|
6059
6080
|
await this.fs.mkdir(dir, { recursive: true });
|
|
6060
6081
|
await this.fs.rename(path, hidden);
|
|
6082
|
+
await this.linkedName(hidden);
|
|
6061
6083
|
return hidden;
|
|
6062
6084
|
} catch {
|
|
6063
6085
|
await this.tidy(dir);
|
|
@@ -6074,8 +6096,25 @@ var UnlinkedKeeper = class {
|
|
|
6074
6096
|
return;
|
|
6075
6097
|
}
|
|
6076
6098
|
await this.fs.rm(hidden).catch(() => void 0);
|
|
6099
|
+
this.unlinkedName(hidden);
|
|
6077
6100
|
await this.tidy(parentOf3(hidden));
|
|
6078
6101
|
}
|
|
6102
|
+
async linkedName(hidden) {
|
|
6103
|
+
if (!mayLink(this.fs, hidden)) return;
|
|
6104
|
+
const st = await this.fs.lstat(hidden).catch(() => void 0);
|
|
6105
|
+
if (!st || (st.nlink ?? 1) < 2) return;
|
|
6106
|
+
const id2 = `${st.dev ?? 0}:${st.ino}`;
|
|
6107
|
+
this.linked.set(hidden, id2);
|
|
6108
|
+
this.links.set(id2, (this.links.get(id2) ?? 0) + 1);
|
|
6109
|
+
}
|
|
6110
|
+
unlinkedName(hidden) {
|
|
6111
|
+
const id2 = this.linked.get(hidden);
|
|
6112
|
+
if (id2 === void 0) return;
|
|
6113
|
+
this.linked.delete(hidden);
|
|
6114
|
+
const left = (this.links.get(id2) ?? 1) - 1;
|
|
6115
|
+
if (left > 0) this.links.set(id2, left);
|
|
6116
|
+
else this.links.delete(id2);
|
|
6117
|
+
}
|
|
6079
6118
|
async tidy(dir) {
|
|
6080
6119
|
const left = await this.fs.readdir(dir).catch(() => [UNLINKED_DIR]);
|
|
6081
6120
|
if (left.length === 0) await this.fs.rm(dir, { recursive: true }).catch(() => void 0);
|
|
@@ -6169,6 +6208,10 @@ var FsWatchers = class {
|
|
|
6169
6208
|
},
|
|
6170
6209
|
rm: after(fs.rm.bind(fs), (path) => [path]),
|
|
6171
6210
|
rename: after(fs.rename.bind(fs), (from, to) => [from, to]),
|
|
6211
|
+
link: after(
|
|
6212
|
+
(from, to, follow2) => fs.link(from, to, follow2),
|
|
6213
|
+
(_from, to) => [to]
|
|
6214
|
+
),
|
|
6172
6215
|
symlink: after(fs.symlink.bind(fs), (_target, path) => [path]),
|
|
6173
6216
|
chmod: after(fs.chmod.bind(fs), (path) => [path]),
|
|
6174
6217
|
...fs.chown ? { chown: after(fs.chown.bind(fs), (path) => [path]) } : {},
|
|
@@ -6382,6 +6425,62 @@ var ChildTable = class {
|
|
|
6382
6425
|
}
|
|
6383
6426
|
};
|
|
6384
6427
|
|
|
6428
|
+
// src/fs/walk.ts
|
|
6429
|
+
var MAX_LINKS4 = 40;
|
|
6430
|
+
function parentOf5(path) {
|
|
6431
|
+
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
6432
|
+
}
|
|
6433
|
+
async function mustBeDir(fs, path) {
|
|
6434
|
+
if (!fs.lstat) return;
|
|
6435
|
+
const st = await fs.lstat(path);
|
|
6436
|
+
if (st.isDirectory !== true) throw fsError("ENOTDIR", path);
|
|
6437
|
+
}
|
|
6438
|
+
async function linkOf(fs, path) {
|
|
6439
|
+
return fs.readlink?.(path).catch(() => void 0);
|
|
6440
|
+
}
|
|
6441
|
+
async function realDir(fs, dir, budget = { hops: 0 }) {
|
|
6442
|
+
const queue = dir.split("/").filter(Boolean);
|
|
6443
|
+
let at = "/";
|
|
6444
|
+
while (queue.length > 0) {
|
|
6445
|
+
const part = queue.shift();
|
|
6446
|
+
if (part === ".") continue;
|
|
6447
|
+
if (part === "..") {
|
|
6448
|
+
at = parentOf5(at);
|
|
6449
|
+
continue;
|
|
6450
|
+
}
|
|
6451
|
+
const next2 = at === "/" ? `/${part}` : `${at}/${part}`;
|
|
6452
|
+
const link2 = await linkOf(fs, next2);
|
|
6453
|
+
if (link2 === void 0) {
|
|
6454
|
+
if (queue.find((p) => p !== ".") === "..") await mustBeDir(fs, next2);
|
|
6455
|
+
at = next2;
|
|
6456
|
+
continue;
|
|
6457
|
+
}
|
|
6458
|
+
if (budget.hops >= MAX_LINKS4) throw fsError("ELOOP", dir);
|
|
6459
|
+
budget.hops++;
|
|
6460
|
+
queue.unshift(...link2.split("/").filter(Boolean));
|
|
6461
|
+
if (link2.startsWith("/")) at = "/";
|
|
6462
|
+
}
|
|
6463
|
+
return at;
|
|
6464
|
+
}
|
|
6465
|
+
function joinName(dir, name2) {
|
|
6466
|
+
return dir === "/" ? `/${name2}` : `${dir}/${name2}`;
|
|
6467
|
+
}
|
|
6468
|
+
async function realTarget(fs, path) {
|
|
6469
|
+
const budget = { hops: 0 };
|
|
6470
|
+
let dir = await realDir(fs, parentOf5(path), budget);
|
|
6471
|
+
let name2 = path.slice(path.lastIndexOf("/") + 1);
|
|
6472
|
+
for (; ; ) {
|
|
6473
|
+
const link2 = await linkOf(fs, joinName(dir, name2));
|
|
6474
|
+
if (link2 === void 0) break;
|
|
6475
|
+
if (budget.hops >= MAX_LINKS4) throw fsError("ELOOP", path);
|
|
6476
|
+
budget.hops++;
|
|
6477
|
+
const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
|
|
6478
|
+
dir = await realDir(fs, parentOf5(target), budget);
|
|
6479
|
+
name2 = target.slice(target.lastIndexOf("/") + 1);
|
|
6480
|
+
}
|
|
6481
|
+
return joinName(dir, name2);
|
|
6482
|
+
}
|
|
6483
|
+
|
|
6385
6484
|
// src/mount/protocol.ts
|
|
6386
6485
|
var DRIVER_PROTOCOL = [1, 1];
|
|
6387
6486
|
function driverVersionError(theirs) {
|
|
@@ -6485,7 +6584,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
6485
6584
|
};
|
|
6486
6585
|
|
|
6487
6586
|
// src/mount/mount-fs.ts
|
|
6488
|
-
var
|
|
6587
|
+
var MAX_LINKS5 = 40;
|
|
6489
6588
|
var STALE_RETRIES = 3;
|
|
6490
6589
|
var SETTLE_ROUNDS = 4;
|
|
6491
6590
|
var DEFAULT_IO = 8 * 1024 * 1024;
|
|
@@ -6649,8 +6748,13 @@ var HeldPaths = class extends Map {
|
|
|
6649
6748
|
return this.revoked.has(path);
|
|
6650
6749
|
}
|
|
6651
6750
|
};
|
|
6652
|
-
var
|
|
6751
|
+
var parentOf6 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
6653
6752
|
var readonly = (mount) => mount.caps.readonly === true || mount.options.ro !== void 0;
|
|
6753
|
+
function inodeless(mount, attr) {
|
|
6754
|
+
if (attr.ino !== void 0) return false;
|
|
6755
|
+
if (mount.caps.links) mount.caps = { ...mount.caps, links: false };
|
|
6756
|
+
return true;
|
|
6757
|
+
}
|
|
6654
6758
|
function statOf(attr, path, mount) {
|
|
6655
6759
|
const mtime = new Date(attr.mtime);
|
|
6656
6760
|
const fallback = attr.kind === "directory" ? 16877 : attr.kind === "symlink" ? 41471 : 33188;
|
|
@@ -6664,10 +6768,12 @@ function statOf(attr, path, mount) {
|
|
|
6664
6768
|
atime: mtime,
|
|
6665
6769
|
ctime: mtime,
|
|
6666
6770
|
ino: attr.ino ?? inodeOf(path),
|
|
6771
|
+
...!inodeless(mount, attr) && attr.nlink !== void 0 ? { nlink: attr.nlink } : {},
|
|
6667
6772
|
dev: mount.dev,
|
|
6668
6773
|
...readonly(mount) ? { readonly: true } : {},
|
|
6669
6774
|
...mount.maxFile > 0 ? { maxFile: mount.maxFile } : {},
|
|
6670
6775
|
...mount.caps.ranges ? { ranged: true } : {},
|
|
6776
|
+
...mount.caps.links ? { linkable: true } : {},
|
|
6671
6777
|
...attr.etag !== void 0 ? { version: attr.etag } : {}
|
|
6672
6778
|
};
|
|
6673
6779
|
}
|
|
@@ -6916,7 +7022,7 @@ var MountTable = class {
|
|
|
6916
7022
|
mount.epoch++;
|
|
6917
7023
|
for (const key of [...mount.attrs.keys()]) if (within4(key, path)) mount.attrs.delete(key);
|
|
6918
7024
|
mount.lists.delete(path);
|
|
6919
|
-
mount.lists.delete(
|
|
7025
|
+
mount.lists.delete(parentOf6(path));
|
|
6920
7026
|
for (const key of [...mount.lists.keys()]) if (within4(key, path)) mount.lists.delete(key);
|
|
6921
7027
|
}
|
|
6922
7028
|
async getattr(mount, path, rel2, settled = false) {
|
|
@@ -6990,9 +7096,9 @@ var MountFs = class {
|
|
|
6990
7096
|
throw err;
|
|
6991
7097
|
}
|
|
6992
7098
|
if (attr.kind !== "symlink") return real;
|
|
6993
|
-
if (hops >=
|
|
7099
|
+
if (hops >= MAX_LINKS5) throw errnoError("ELOOP", path);
|
|
6994
7100
|
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
6995
|
-
return this.follow(this.base.resolvePath(
|
|
7101
|
+
return this.follow(this.base.resolvePath(parentOf6(real), target), hops + 1, create);
|
|
6996
7102
|
}
|
|
6997
7103
|
async open(mount, rel2, write) {
|
|
6998
7104
|
return await mount.conn.call({
|
|
@@ -7322,6 +7428,34 @@ var MountFs = class {
|
|
|
7322
7428
|
this.table.forget(a.mount, normalizePath(to));
|
|
7323
7429
|
}
|
|
7324
7430
|
}
|
|
7431
|
+
links(path) {
|
|
7432
|
+
const found = this.at(path);
|
|
7433
|
+
return found ? found.mount.caps.links === true : mayLink(this.base, path);
|
|
7434
|
+
}
|
|
7435
|
+
async named(path) {
|
|
7436
|
+
const at = normalizePath(path);
|
|
7437
|
+
return joinName(await realDir(this, parentOf6(at)), at.slice(at.lastIndexOf("/") + 1));
|
|
7438
|
+
}
|
|
7439
|
+
async link(source, target, follow2 = false) {
|
|
7440
|
+
const from = follow2 ? await realTarget(this, normalizePath(source)) : await this.named(source);
|
|
7441
|
+
const to = await this.named(target);
|
|
7442
|
+
const a = this.at(from);
|
|
7443
|
+
const b = this.at(to);
|
|
7444
|
+
if (!a && !b) return this.base.link(from, to);
|
|
7445
|
+
if (!a || !b || a.mount !== b.mount) throw errnoError("EXDEV", `${from} -> ${to}`);
|
|
7446
|
+
this.writable(a.mount, normalizePath(to));
|
|
7447
|
+
if (a.mount.caps.links) await this.lstat(from);
|
|
7448
|
+
if (!a.mount.caps.links) {
|
|
7449
|
+
throw errnoError("EPERM", `${from}: this file system has no hard links`);
|
|
7450
|
+
}
|
|
7451
|
+
await this.table.commitUnder(from);
|
|
7452
|
+
try {
|
|
7453
|
+
await a.mount.conn.call({ op: "link", from: a.rel, to: b.rel });
|
|
7454
|
+
} finally {
|
|
7455
|
+
this.table.forget(a.mount, normalizePath(from));
|
|
7456
|
+
this.table.forget(a.mount, normalizePath(to));
|
|
7457
|
+
}
|
|
7458
|
+
}
|
|
7325
7459
|
async symlink(target, path, owner) {
|
|
7326
7460
|
const found = this.at(path);
|
|
7327
7461
|
if (!found) return this.base.symlink(target, path, owner);
|
|
@@ -7421,10 +7555,12 @@ function statJson(s) {
|
|
|
7421
7555
|
atimeMs: s.atime.getTime(),
|
|
7422
7556
|
ctimeMs: s.ctime.getTime(),
|
|
7423
7557
|
ino: s.ino,
|
|
7558
|
+
...s.nlink !== void 0 ? { nlink: s.nlink } : {},
|
|
7424
7559
|
...s.dev !== void 0 ? { dev: s.dev } : {},
|
|
7425
7560
|
...s.readonly ? { readonly: true } : {},
|
|
7426
7561
|
...s.maxFile ? { maxFile: s.maxFile } : {},
|
|
7427
7562
|
...s.ranged ? { ranged: true } : {},
|
|
7563
|
+
...s.linkable ? { linkable: true } : {},
|
|
7428
7564
|
...s.version !== void 0 ? { version: s.version } : {},
|
|
7429
7565
|
...s.uid !== void 0 ? { uid: s.uid } : {},
|
|
7430
7566
|
...s.gid !== void 0 ? { gid: s.gid } : {}
|
|
@@ -7493,6 +7629,9 @@ async function run(entry, path, req) {
|
|
|
7493
7629
|
entry.renamed?.(path, to);
|
|
7494
7630
|
return done;
|
|
7495
7631
|
}
|
|
7632
|
+
case "link":
|
|
7633
|
+
await fs.link(path, fs.resolvePath(cwd, req.arg2 ?? ""), req.mode === 1);
|
|
7634
|
+
return done;
|
|
7496
7635
|
case "unlink":
|
|
7497
7636
|
if ((await fs.lstat(path)).isDirectory) throw syncError("EISDIR", path);
|
|
7498
7637
|
await fs.rm(path);
|
|
@@ -7554,12 +7693,28 @@ async function ranged(fs, path, req) {
|
|
|
7554
7693
|
return done;
|
|
7555
7694
|
}
|
|
7556
7695
|
var UNLINKED_OPS = /* @__PURE__ */ new Set(["read", "pread", "pwrite", "truncate"]);
|
|
7557
|
-
var OWN_OPS = /* @__PURE__ */ new Set(["rm", "unlink", "rmdir", "rename"]);
|
|
7696
|
+
var OWN_OPS = /* @__PURE__ */ new Set(["rm", "unlink", "rmdir", "rename", "link"]);
|
|
7697
|
+
var TWO_PATHS = /* @__PURE__ */ new Set(["rename", "link"]);
|
|
7698
|
+
var UNLINKING_OPS = /* @__PURE__ */ new Set(["rm", "unlink", "rename"]);
|
|
7699
|
+
async function linkedElsewhere(fs, path, from) {
|
|
7700
|
+
if (!mayLink(fs, path)) return false;
|
|
7701
|
+
const st = await fs.lstat(path).catch(() => void 0);
|
|
7702
|
+
if (!st || (st.nlink ?? 1) < 2) return false;
|
|
7703
|
+
const source = from === void 0 ? void 0 : await fs.lstat(from).catch(() => void 0);
|
|
7704
|
+
return !(source && source.ino === st.ino && source.dev === st.dev);
|
|
7705
|
+
}
|
|
7558
7706
|
async function keptOr(entry, path, handle) {
|
|
7559
7707
|
if (handle !== void 0) return entry.keptFor?.(handle)?.hidden ?? path;
|
|
7560
7708
|
const kept = entry.unlinked?.(path);
|
|
7561
7709
|
return kept && !await entry.fs.exists(path) ? kept.hidden : path;
|
|
7562
7710
|
}
|
|
7711
|
+
async function owned(entry, own, req, path, target) {
|
|
7712
|
+
const source = target === path ? void 0 : path;
|
|
7713
|
+
if (UNLINKING_OPS.has(req.op) && await linkedElsewhere(entry.fs, target, source)) {
|
|
7714
|
+
return run(entry, path, req);
|
|
7715
|
+
}
|
|
7716
|
+
return own(target, () => run(entry, path, req));
|
|
7717
|
+
}
|
|
7563
7718
|
async function dispatchSyncFs(req) {
|
|
7564
7719
|
const entry = resolveSyncFsToken(req.token);
|
|
7565
7720
|
if (!entry) return { ok: false, errno: "EACCES", message: "sync-fs: unknown or revoked token" };
|
|
@@ -7583,8 +7738,8 @@ async function dispatchSyncFs(req) {
|
|
|
7583
7738
|
return await (entry.gate ? entry.gate(path, serve) : serve());
|
|
7584
7739
|
}
|
|
7585
7740
|
if (!entry.own || !OWN_OPS.has(req.op)) return await run(entry, path, req);
|
|
7586
|
-
const target = req.op
|
|
7587
|
-
return await entry.own
|
|
7741
|
+
const target = TWO_PATHS.has(req.op) ? fs.resolvePath(cwd, req.arg2 ?? "") : path;
|
|
7742
|
+
return await owned(entry, entry.own, req, path, target);
|
|
7588
7743
|
} catch (err) {
|
|
7589
7744
|
return toErrno(err);
|
|
7590
7745
|
}
|
|
@@ -8159,6 +8314,10 @@ function normalize(path) {
|
|
|
8159
8314
|
}
|
|
8160
8315
|
return `/${out.join("/")}`;
|
|
8161
8316
|
}
|
|
8317
|
+
var RING = 8;
|
|
8318
|
+
var RING_SIZE = 64;
|
|
8319
|
+
var SLOT = 3;
|
|
8320
|
+
var SHARED_IDS_BYTES = (RING + RING_SIZE * SLOT) * 4;
|
|
8162
8321
|
|
|
8163
8322
|
// src/process/wasi/wasi-procfs.ts
|
|
8164
8323
|
var DIR_MODE = 16749;
|
|
@@ -9527,6 +9686,7 @@ var VfsNode = class {
|
|
|
9527
9686
|
fs;
|
|
9528
9687
|
path;
|
|
9529
9688
|
orphaned;
|
|
9689
|
+
id;
|
|
9530
9690
|
constructor(fs, path, contents2, orphaned = false, dirty = false, pin) {
|
|
9531
9691
|
this.fs = fs;
|
|
9532
9692
|
this.pin = pin;
|
|
@@ -9696,6 +9856,7 @@ var VfsNode = class {
|
|
|
9696
9856
|
var VfsNodes = class {
|
|
9697
9857
|
byPath = /* @__PURE__ */ new Map();
|
|
9698
9858
|
kept = /* @__PURE__ */ new Map();
|
|
9859
|
+
byId = /* @__PURE__ */ new Map();
|
|
9699
9860
|
keepsOnDisk = false;
|
|
9700
9861
|
fs;
|
|
9701
9862
|
entries = /* @__PURE__ */ new Map();
|
|
@@ -9715,7 +9876,7 @@ var VfsNodes = class {
|
|
|
9715
9876
|
return run2;
|
|
9716
9877
|
}
|
|
9717
9878
|
async entryKey(path) {
|
|
9718
|
-
const parent =
|
|
9879
|
+
const parent = parentOf5(path);
|
|
9719
9880
|
const dir = await this.real(parent, true, () => realDir(this.fs, parent));
|
|
9720
9881
|
return joinName(dir, path.slice(path.lastIndexOf("/") + 1));
|
|
9721
9882
|
}
|
|
@@ -9740,21 +9901,66 @@ var VfsNodes = class {
|
|
|
9740
9901
|
await this.fs.writeFile(path, new Uint8Array(0), owner);
|
|
9741
9902
|
});
|
|
9742
9903
|
}
|
|
9743
|
-
|
|
9744
|
-
|
|
9745
|
-
if (!
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
|
|
9904
|
+
async identity(path) {
|
|
9905
|
+
const st = await this.fs.stat?.(path).catch(() => void 0);
|
|
9906
|
+
if (!st || st.ino === void 0 || (st.nlink ?? 1) < 2) return void 0;
|
|
9907
|
+
return `${st.dev ?? 0}:${st.ino}`;
|
|
9908
|
+
}
|
|
9909
|
+
async identify(path) {
|
|
9910
|
+
return this.byPath.has(path) || !mayLink(this.fs, path) ? void 0 : this.identity(path);
|
|
9911
|
+
}
|
|
9912
|
+
async linked(from, to) {
|
|
9913
|
+
const node = this.byPath.get(from);
|
|
9914
|
+
if (!node || node.orphaned) return;
|
|
9915
|
+
const id2 = node.id ?? await this.identity(to);
|
|
9916
|
+
if (id2 === void 0 || node.orphaned) return;
|
|
9917
|
+
this.know(node, id2);
|
|
9918
|
+
if (!this.byPath.has(to)) this.byPath.set(to, node);
|
|
9919
|
+
}
|
|
9920
|
+
know(node, id2) {
|
|
9921
|
+
node.id = id2;
|
|
9922
|
+
if (!this.byId.has(id2)) this.byId.set(id2, node);
|
|
9923
|
+
}
|
|
9924
|
+
namesOf(node) {
|
|
9925
|
+
const out = [];
|
|
9926
|
+
for (const [p, n] of this.byPath) if (n === node) out.push(p);
|
|
9927
|
+
return out;
|
|
9928
|
+
}
|
|
9929
|
+
forget(node) {
|
|
9930
|
+
for (const p of this.namesOf(node)) this.byPath.delete(p);
|
|
9931
|
+
if (node.id !== void 0 && this.byId.get(node.id) === node) this.byId.delete(node.id);
|
|
9932
|
+
}
|
|
9933
|
+
orphan(node) {
|
|
9934
|
+
node.orphaned = true;
|
|
9935
|
+
this.forget(node);
|
|
9936
|
+
}
|
|
9937
|
+
dropName(path, node) {
|
|
9938
|
+
const others = this.namesOf(node).filter((p) => p !== path);
|
|
9939
|
+
if (others.length === 0) return false;
|
|
9940
|
+
this.byPath.delete(path);
|
|
9941
|
+
if (node.path === path) node.renamedTo(others[0]);
|
|
9942
|
+
return true;
|
|
9943
|
+
}
|
|
9944
|
+
named(path, id2) {
|
|
9945
|
+
const node = this.byPath.get(path) ?? (id2 === void 0 ? void 0 : this.byId.get(id2));
|
|
9946
|
+
if (node && !this.byPath.has(path)) this.byPath.set(path, node);
|
|
9947
|
+
return node;
|
|
9948
|
+
}
|
|
9949
|
+
create(path, id2, node) {
|
|
9950
|
+
this.byPath.set(path, node);
|
|
9951
|
+
if (id2 !== void 0) this.know(node, id2);
|
|
9952
|
+
return node;
|
|
9953
|
+
}
|
|
9954
|
+
open(path, pin, writable = false, id2) {
|
|
9955
|
+
const node = this.named(path, id2) ?? this.create(path, id2, new VfsNode(this.fs, path, void 0, false, false, pin));
|
|
9749
9956
|
node.opens++;
|
|
9750
9957
|
if (writable) node.writers++;
|
|
9751
9958
|
return node;
|
|
9752
9959
|
}
|
|
9753
|
-
adopt(path, contents2, dirty, writable = false) {
|
|
9754
|
-
let node = this.
|
|
9960
|
+
adopt(path, contents2, dirty, writable = false, id2) {
|
|
9961
|
+
let node = this.named(path, id2);
|
|
9755
9962
|
if (!node) {
|
|
9756
|
-
node = new VfsNode(this.fs, path, contents2, false, dirty);
|
|
9757
|
-
this.byPath.set(path, node);
|
|
9963
|
+
node = this.create(path, id2, new VfsNode(this.fs, path, contents2, false, dirty));
|
|
9758
9964
|
} else if (dirty) {
|
|
9759
9965
|
node.replace(contents2);
|
|
9760
9966
|
}
|
|
@@ -9778,7 +9984,7 @@ var VfsNodes = class {
|
|
|
9778
9984
|
if (writable && node.writers === 0) this.onClosed?.(node.path);
|
|
9779
9985
|
return;
|
|
9780
9986
|
}
|
|
9781
|
-
|
|
9987
|
+
this.forget(node);
|
|
9782
9988
|
this.kept.get(node)?.release();
|
|
9783
9989
|
this.kept.delete(node);
|
|
9784
9990
|
this.onClosed?.(node.path);
|
|
@@ -9794,7 +10000,7 @@ var VfsNodes = class {
|
|
|
9794
10000
|
}
|
|
9795
10001
|
keep(path, file) {
|
|
9796
10002
|
const node = this.byPath.get(path);
|
|
9797
|
-
if (!node) {
|
|
10003
|
+
if (!node || this.dropName(path, node)) {
|
|
9798
10004
|
file.release();
|
|
9799
10005
|
return;
|
|
9800
10006
|
}
|
|
@@ -9823,33 +10029,32 @@ var VfsNodes = class {
|
|
|
9823
10029
|
}
|
|
9824
10030
|
}
|
|
9825
10031
|
revoke(prefix) {
|
|
9826
|
-
for (const [path, node] of this.byPath) {
|
|
10032
|
+
for (const [path, node] of [...this.byPath]) {
|
|
9827
10033
|
if (!within5(path, prefix)) continue;
|
|
9828
10034
|
node.revoke();
|
|
9829
|
-
this.
|
|
10035
|
+
this.forget(node);
|
|
9830
10036
|
}
|
|
9831
10037
|
}
|
|
9832
10038
|
unlinked(path) {
|
|
9833
10039
|
for (const [held, node] of [...this.byPath]) {
|
|
9834
|
-
if (!within5(held, path)) continue;
|
|
9835
|
-
|
|
9836
|
-
this.byPath.delete(held);
|
|
10040
|
+
if (!within5(held, path) || this.byPath.get(held) !== node) continue;
|
|
10041
|
+
if (!this.dropName(held, node)) this.orphan(node);
|
|
9837
10042
|
}
|
|
9838
10043
|
}
|
|
9839
10044
|
renamed(from, to) {
|
|
9840
10045
|
if (from === to) return;
|
|
9841
10046
|
const moved2 = [];
|
|
9842
|
-
for (const [p, node] of this.byPath) {
|
|
9843
|
-
if (within5(p, from)) moved2.push(node);
|
|
9844
|
-
else if (within5(p, to)) {
|
|
9845
|
-
node
|
|
9846
|
-
this.byPath.delete(p);
|
|
10047
|
+
for (const [p, node] of [...this.byPath]) {
|
|
10048
|
+
if (within5(p, from)) moved2.push([p, node]);
|
|
10049
|
+
else if (within5(p, to) && this.byPath.get(p) === node && !this.dropName(p, node)) {
|
|
10050
|
+
this.orphan(node);
|
|
9847
10051
|
}
|
|
9848
10052
|
}
|
|
9849
|
-
for (const
|
|
9850
|
-
|
|
9851
|
-
|
|
9852
|
-
|
|
10053
|
+
for (const [p] of moved2) this.byPath.delete(p);
|
|
10054
|
+
for (const [p, node] of moved2) {
|
|
10055
|
+
const next2 = to + p.slice(from.length);
|
|
10056
|
+
if (node.path === p) node.renamedTo(next2);
|
|
10057
|
+
this.byPath.set(next2, node);
|
|
9853
10058
|
}
|
|
9854
10059
|
}
|
|
9855
10060
|
};
|
|
@@ -9863,65 +10068,11 @@ async function present(fs, path, follow2 = false) {
|
|
|
9863
10068
|
throw err;
|
|
9864
10069
|
}
|
|
9865
10070
|
}
|
|
9866
|
-
var MAX_LINKS5 = 40;
|
|
9867
|
-
function parentOf6(path) {
|
|
9868
|
-
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
9869
|
-
}
|
|
9870
|
-
async function mustBeDir(fs, path) {
|
|
9871
|
-
if (!fs.lstat) return;
|
|
9872
|
-
const st = await fs.lstat(path);
|
|
9873
|
-
if (st.isDirectory !== true) throw fsError("ENOTDIR", path);
|
|
9874
|
-
}
|
|
9875
|
-
async function linkOf(fs, path) {
|
|
9876
|
-
return fs.readlink?.(path).catch(() => void 0);
|
|
9877
|
-
}
|
|
9878
|
-
async function realDir(fs, dir, budget = { hops: 0 }) {
|
|
9879
|
-
const queue = dir.split("/").filter(Boolean);
|
|
9880
|
-
let at = "/";
|
|
9881
|
-
while (queue.length > 0) {
|
|
9882
|
-
const part = queue.shift();
|
|
9883
|
-
if (part === ".") continue;
|
|
9884
|
-
if (part === "..") {
|
|
9885
|
-
at = parentOf6(at);
|
|
9886
|
-
continue;
|
|
9887
|
-
}
|
|
9888
|
-
const next2 = at === "/" ? `/${part}` : `${at}/${part}`;
|
|
9889
|
-
const link2 = await linkOf(fs, next2);
|
|
9890
|
-
if (link2 === void 0) {
|
|
9891
|
-
if (queue.find((p) => p !== ".") === "..") await mustBeDir(fs, next2);
|
|
9892
|
-
at = next2;
|
|
9893
|
-
continue;
|
|
9894
|
-
}
|
|
9895
|
-
if (budget.hops >= MAX_LINKS5) throw fsError("ELOOP", dir);
|
|
9896
|
-
budget.hops++;
|
|
9897
|
-
queue.unshift(...link2.split("/").filter(Boolean));
|
|
9898
|
-
if (link2.startsWith("/")) at = "/";
|
|
9899
|
-
}
|
|
9900
|
-
return at;
|
|
9901
|
-
}
|
|
9902
10071
|
function errCode(err) {
|
|
9903
10072
|
return err?.code;
|
|
9904
10073
|
}
|
|
9905
|
-
function joinName(dir, name2) {
|
|
9906
|
-
return dir === "/" ? `/${name2}` : `${dir}/${name2}`;
|
|
9907
|
-
}
|
|
9908
|
-
async function realTarget(fs, path) {
|
|
9909
|
-
const budget = { hops: 0 };
|
|
9910
|
-
let dir = await realDir(fs, parentOf6(path), budget);
|
|
9911
|
-
let name2 = path.slice(path.lastIndexOf("/") + 1);
|
|
9912
|
-
for (; ; ) {
|
|
9913
|
-
const link2 = await linkOf(fs, joinName(dir, name2));
|
|
9914
|
-
if (link2 === void 0) break;
|
|
9915
|
-
if (budget.hops >= MAX_LINKS5) throw fsError("ELOOP", path);
|
|
9916
|
-
budget.hops++;
|
|
9917
|
-
const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
|
|
9918
|
-
dir = await realDir(fs, parentOf6(target), budget);
|
|
9919
|
-
name2 = target.slice(target.lastIndexOf("/") + 1);
|
|
9920
|
-
}
|
|
9921
|
-
return joinName(dir, name2);
|
|
9922
|
-
}
|
|
9923
10074
|
async function targetDir(fs, path) {
|
|
9924
|
-
return
|
|
10075
|
+
return parentOf5(await realTarget(fs, path).catch(() => path));
|
|
9925
10076
|
}
|
|
9926
10077
|
async function refuseReadonly(fs, path, flags, opts = {}) {
|
|
9927
10078
|
const writes = (flags & O_ACCMODE) !== 0 || opts.truncate === true;
|
|
@@ -9957,6 +10108,15 @@ function keepingOpen(fs, nodes) {
|
|
|
9957
10108
|
writeFile: (path, content, owner) => nodes.onEntry(fs.resolvePath("/", path), () => fs.writeFile(path, content, owner)),
|
|
9958
10109
|
createExclusive: async (path, owner) => nodes.createExclusive(await nodes.entryKey(fs.resolvePath("/", path)), owner),
|
|
9959
10110
|
symlink: (target, path, owner) => nodes.onEntry(fs.resolvePath("/", path), () => fs.symlink(target, path, owner)),
|
|
10111
|
+
links: (path) => mayLink(fs, path),
|
|
10112
|
+
link: async (from, to, follow2) => {
|
|
10113
|
+
const source = fs.resolvePath("/", from);
|
|
10114
|
+
const at = await (follow2 ? nodes.targetKey(source) : nodes.entryKey(source));
|
|
10115
|
+
await nodes.flush(at);
|
|
10116
|
+
const key = await nodes.entryKey(fs.resolvePath("/", to));
|
|
10117
|
+
await nodes.onEntry(key, () => fs.link(from, to, follow2));
|
|
10118
|
+
await nodes.linked(at, key);
|
|
10119
|
+
},
|
|
9960
10120
|
mkdir: (path, options) => nodes.onEntry(fs.resolvePath("/", path), () => fs.mkdir(path, options))
|
|
9961
10121
|
};
|
|
9962
10122
|
}
|
|
@@ -9964,7 +10124,7 @@ function vfsFile(fs, opts, nodes) {
|
|
|
9964
10124
|
const access = opts.flags & O_ACCMODE;
|
|
9965
10125
|
const readable = access !== O_WRONLY;
|
|
9966
10126
|
const writable = access === O_WRONLY || access === O_RDWR;
|
|
9967
|
-
const node = !nodes || opts.orphan ? new VfsNode(fs, opts.path, opts.contents, opts.orphan === true, opts.dirty === true) : opts.contents !== void 0 ? nodes.adopt(opts.path, opts.contents, opts.dirty === true, writable) : nodes.open(opts.path, opts.pin, writable);
|
|
10127
|
+
const node = !nodes || opts.orphan ? new VfsNode(fs, opts.path, opts.contents, opts.orphan === true, opts.dirty === true) : opts.contents !== void 0 ? nodes.adopt(opts.path, opts.contents, opts.dirty === true, writable, opts.id) : nodes.open(opts.path, opts.pin, writable, opts.id);
|
|
9968
10128
|
let openError;
|
|
9969
10129
|
const atOpen = (op) => {
|
|
9970
10130
|
void node.serial(op).catch((err) => {
|
|
@@ -10399,12 +10559,12 @@ var WasmProcess = class {
|
|
|
10399
10559
|
} else if (req.create) {
|
|
10400
10560
|
await nodes.onEntry(path, async () => {
|
|
10401
10561
|
const fresh = this.options.owners && !await present(this.options.fs, path, true);
|
|
10402
|
-
this.fds.installAt(fd, this.openVfsFile(open));
|
|
10562
|
+
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
10403
10563
|
await this.fds.get(fd).file.stat?.();
|
|
10404
10564
|
if (fresh) await this.options.fs.chown?.(path, { ...this.newOwner(), kind: "new" });
|
|
10405
10565
|
});
|
|
10406
10566
|
} else {
|
|
10407
|
-
this.fds.installAt(fd, this.openVfsFile(open));
|
|
10567
|
+
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
10408
10568
|
}
|
|
10409
10569
|
if (req.existing && !await present(this.options.fs, path, true)) {
|
|
10410
10570
|
throw new KernelError("ENOENT");
|
|
@@ -10435,7 +10595,7 @@ var WasmProcess = class {
|
|
|
10435
10595
|
);
|
|
10436
10596
|
return memoryFile(tree.read(path), path, req.position);
|
|
10437
10597
|
}
|
|
10438
|
-
openVfsFile(req) {
|
|
10598
|
+
openVfsFile(req, id2) {
|
|
10439
10599
|
return vfsFile(
|
|
10440
10600
|
this.options.fs,
|
|
10441
10601
|
{
|
|
@@ -10446,7 +10606,8 @@ var WasmProcess = class {
|
|
|
10446
10606
|
...req.orphan ? { orphan: true } : {},
|
|
10447
10607
|
...req.truncate ? { truncate: true } : {},
|
|
10448
10608
|
...req.create ? { create: true } : {},
|
|
10449
|
-
...req.pin ? { pin: req.pin } : {}
|
|
10609
|
+
...req.pin ? { pin: req.pin } : {},
|
|
10610
|
+
...id2 !== void 0 && !req.orphan ? { id: id2 } : {}
|
|
10450
10611
|
},
|
|
10451
10612
|
this.nodes
|
|
10452
10613
|
);
|
|
@@ -10594,6 +10755,7 @@ var WasmProcess = class {
|
|
|
10594
10755
|
}
|
|
10595
10756
|
if (req.path === void 0) throw new KernelError("EINVAL");
|
|
10596
10757
|
const path = req.orphan ? req.path : await this.keptOr(await this.nodes.targetKey(req.path), req.handle);
|
|
10758
|
+
const id2 = req.orphan ? void 0 : await this.nodes.identify(path);
|
|
10597
10759
|
if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
|
|
10598
10760
|
const file = vfsFile(
|
|
10599
10761
|
this.options.fs,
|
|
@@ -10604,7 +10766,8 @@ var WasmProcess = class {
|
|
|
10604
10766
|
...req.contents !== void 0 ? { contents: req.contents } : {},
|
|
10605
10767
|
...req.orphan ? { orphan: true } : {},
|
|
10606
10768
|
...req.dirty ? { dirty: true } : {},
|
|
10607
|
-
...req.pin ? { pin: req.pin } : {}
|
|
10769
|
+
...req.pin ? { pin: req.pin } : {},
|
|
10770
|
+
...id2 !== void 0 ? { id: id2 } : {}
|
|
10608
10771
|
},
|
|
10609
10772
|
this.nodes
|
|
10610
10773
|
);
|
|
@@ -11676,6 +11839,9 @@ async function dispatch(h, req) {
|
|
|
11676
11839
|
return h.unlink(req.path);
|
|
11677
11840
|
case "rename":
|
|
11678
11841
|
return h.rename(req.from, req.to);
|
|
11842
|
+
case "link":
|
|
11843
|
+
if (!h.link) throw fsError2("EPERM", "this file system has no hard links");
|
|
11844
|
+
return h.link(req.from, req.to);
|
|
11679
11845
|
case "symlink":
|
|
11680
11846
|
if (!h.symlink) throw fsError2("EPERM", "this file system has no symlinks");
|
|
11681
11847
|
return h.symlink(req.target, req.path);
|
|
@@ -12758,8 +12924,8 @@ var ChunkedBytes = class {
|
|
|
12758
12924
|
}
|
|
12759
12925
|
return out;
|
|
12760
12926
|
}
|
|
12761
|
-
write(offset, bytes,
|
|
12762
|
-
const whole =
|
|
12927
|
+
write(offset, bytes, owned2 = false) {
|
|
12928
|
+
const whole = owned2 && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
|
|
12763
12929
|
for (let done4 = 0; done4 < bytes.length; ) {
|
|
12764
12930
|
const at = offset + done4;
|
|
12765
12931
|
const index = Math.floor(at / CHUNK3);
|
|
@@ -12801,12 +12967,17 @@ function tmpfs() {
|
|
|
12801
12967
|
mtime: Date.now(),
|
|
12802
12968
|
data: new ChunkedBytes(),
|
|
12803
12969
|
target: "",
|
|
12804
|
-
children: /* @__PURE__ */ new Map()
|
|
12970
|
+
children: /* @__PURE__ */ new Map(),
|
|
12971
|
+
links: 1
|
|
12805
12972
|
});
|
|
12806
12973
|
const root = make("directory", 16877);
|
|
12807
12974
|
const handles = /* @__PURE__ */ new Map();
|
|
12808
12975
|
let nextFh = 0;
|
|
12809
12976
|
const parts = (path) => path.split("/").filter(Boolean);
|
|
12977
|
+
const place = (dir, name2, node) => {
|
|
12978
|
+
dir.children.set(name2, node);
|
|
12979
|
+
dir.mtime = Date.now();
|
|
12980
|
+
};
|
|
12810
12981
|
function find(path) {
|
|
12811
12982
|
let node = root;
|
|
12812
12983
|
for (const name2 of parts(path)) {
|
|
@@ -12830,7 +13001,8 @@ function tmpfs() {
|
|
|
12830
13001
|
size: node.kind === "file" ? node.data.size : node.kind === "symlink" ? node.target.length : 4096,
|
|
12831
13002
|
mtime: node.mtime,
|
|
12832
13003
|
mode: node.mode,
|
|
12833
|
-
ino: node.ino
|
|
13004
|
+
ino: node.ino,
|
|
13005
|
+
...node.kind === "directory" ? {} : { nlink: node.links }
|
|
12834
13006
|
});
|
|
12835
13007
|
const handle = (fh) => {
|
|
12836
13008
|
const found = handles.get(fh);
|
|
@@ -12845,8 +13017,7 @@ function tmpfs() {
|
|
|
12845
13017
|
if (existing) return existing;
|
|
12846
13018
|
if (!flags.create) throw fsError2("ENOENT", path);
|
|
12847
13019
|
const node = make("file", 33188);
|
|
12848
|
-
dir
|
|
12849
|
-
dir.mtime = Date.now();
|
|
13020
|
+
place(dir, name2, node);
|
|
12850
13021
|
return node;
|
|
12851
13022
|
}
|
|
12852
13023
|
return {
|
|
@@ -12885,8 +13056,7 @@ function tmpfs() {
|
|
|
12885
13056
|
async mkdir(path) {
|
|
12886
13057
|
const { dir, name: name2 } = parent(path);
|
|
12887
13058
|
if (dir.children.has(name2)) throw fsError2("EEXIST", path);
|
|
12888
|
-
dir
|
|
12889
|
-
dir.mtime = Date.now();
|
|
13059
|
+
place(dir, name2, make("directory", 16877));
|
|
12890
13060
|
},
|
|
12891
13061
|
async rmdir(path) {
|
|
12892
13062
|
const { dir, name: name2 } = parent(path);
|
|
@@ -12903,6 +13073,7 @@ function tmpfs() {
|
|
|
12903
13073
|
if (!node) throw fsError2("ENOENT", path);
|
|
12904
13074
|
if (node.kind === "directory") throw fsError2("EISDIR", path);
|
|
12905
13075
|
dir.children.delete(name2);
|
|
13076
|
+
node.links--;
|
|
12906
13077
|
dir.mtime = Date.now();
|
|
12907
13078
|
},
|
|
12908
13079
|
async rename(from, to) {
|
|
@@ -12919,17 +13090,25 @@ function tmpfs() {
|
|
|
12919
13090
|
throw fsError2(node.kind === "directory" ? "ENOTEMPTY" : "EISDIR", to);
|
|
12920
13091
|
}
|
|
12921
13092
|
source.dir.children.delete(source.name);
|
|
13093
|
+
if (replaced) replaced.links--;
|
|
12922
13094
|
target.dir.children.set(target.name, node);
|
|
12923
13095
|
source.dir.mtime = Date.now();
|
|
12924
13096
|
target.dir.mtime = source.dir.mtime;
|
|
12925
13097
|
},
|
|
13098
|
+
async link(from, to) {
|
|
13099
|
+
const node = find(from);
|
|
13100
|
+
if (node.kind === "directory") throw fsError2("EPERM", `${from} is a directory`);
|
|
13101
|
+
const { dir, name: name2 } = parent(to);
|
|
13102
|
+
if (dir.children.has(name2)) throw fsError2("EEXIST", to);
|
|
13103
|
+
place(dir, name2, node);
|
|
13104
|
+
node.links++;
|
|
13105
|
+
},
|
|
12926
13106
|
async symlink(target, path) {
|
|
12927
13107
|
const { dir, name: name2 } = parent(path);
|
|
12928
13108
|
if (dir.children.has(name2)) throw fsError2("EEXIST", path);
|
|
12929
13109
|
const node = make("symlink", 41471);
|
|
12930
13110
|
node.target = target;
|
|
12931
|
-
dir
|
|
12932
|
-
dir.mtime = Date.now();
|
|
13111
|
+
place(dir, name2, node);
|
|
12933
13112
|
},
|
|
12934
13113
|
async readlink(path) {
|
|
12935
13114
|
const node = find(path);
|
|
@@ -13829,7 +14008,7 @@ var Launcher = class {
|
|
|
13829
14008
|
serveFilesystem(
|
|
13830
14009
|
port2,
|
|
13831
14010
|
tmpfs(),
|
|
13832
|
-
{ symlinks: true, chmod: true, linkTimes: true, ranges: true, attrTtl: 0 },
|
|
14011
|
+
{ symlinks: true, links: true, chmod: true, linkTimes: true, ranges: true, attrTtl: 0 },
|
|
13833
14012
|
{ owned: true }
|
|
13834
14013
|
);
|
|
13835
14014
|
return {
|