@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/node.js
CHANGED
|
@@ -98,6 +98,9 @@ async function lutimesOf(fs, path, atime, mtime) {
|
|
|
98
98
|
if ((await fs.lstat(path)).isSymbolicLink) throw fsError("EOPNOTSUPP", path);
|
|
99
99
|
return fs.utimes(path, atime, mtime);
|
|
100
100
|
}
|
|
101
|
+
function mayLink(fs, path) {
|
|
102
|
+
return fs.links?.(path) ?? true;
|
|
103
|
+
}
|
|
101
104
|
function fsError(code, path) {
|
|
102
105
|
return Object.assign(new Error(`${code}: ${path}`), { code });
|
|
103
106
|
}
|
|
@@ -2922,12 +2925,12 @@ var OpfsFs = class {
|
|
|
2922
2925
|
const base = dir === "/" ? "" : dir;
|
|
2923
2926
|
const metas = await this.meta.get(children2.map(([name2]) => `${base}/${name2}`));
|
|
2924
2927
|
const handed2 = this.owners ? await this.handedTo(dir) : void 0;
|
|
2925
|
-
const
|
|
2928
|
+
const owned2 = (stat, entry) => handed2 ? { ...stat, ...ownOf(entry) ?? handed2 } : stat;
|
|
2926
2929
|
return Promise.all(
|
|
2927
2930
|
children2.map(async ([name2, child], i) => {
|
|
2928
|
-
if (!isHandle(child)) return [name2,
|
|
2931
|
+
if (!isHandle(child)) return [name2, owned2(this.linkStat(child), child)];
|
|
2929
2932
|
const stat = await this.statOf(`${base}/${name2}`, child, metas[i]).catch(() => null);
|
|
2930
|
-
return [name2, stat &&
|
|
2933
|
+
return [name2, stat && owned2(stat, metas[i])];
|
|
2931
2934
|
})
|
|
2932
2935
|
);
|
|
2933
2936
|
}
|
|
@@ -2986,6 +2989,12 @@ var OpfsFs = class {
|
|
|
2986
2989
|
await this.meta.remove([found.path]);
|
|
2987
2990
|
this.touch(parentOf2(found.path));
|
|
2988
2991
|
}
|
|
2992
|
+
links() {
|
|
2993
|
+
return false;
|
|
2994
|
+
}
|
|
2995
|
+
async link(from) {
|
|
2996
|
+
throw fsError("EPERM", `${from}: this file system has no hard links`);
|
|
2997
|
+
}
|
|
2989
2998
|
async rename(from, to) {
|
|
2990
2999
|
if (from === to) return;
|
|
2991
3000
|
const source = await this.locate(from, false);
|
|
@@ -3227,6 +3236,8 @@ var UnlinkedKeeper = class {
|
|
|
3227
3236
|
gate = new PathGate();
|
|
3228
3237
|
queue = Promise.resolve();
|
|
3229
3238
|
count = 0;
|
|
3239
|
+
links = /* @__PURE__ */ new Map();
|
|
3240
|
+
linked = /* @__PURE__ */ new Map();
|
|
3230
3241
|
constructor(fs, holders, rootOf, options = {}) {
|
|
3231
3242
|
const { locks, owner } = options;
|
|
3232
3243
|
this.fs = fs;
|
|
@@ -3246,8 +3257,15 @@ var UnlinkedKeeper = class {
|
|
|
3246
3257
|
wrap(fs = this.fs) {
|
|
3247
3258
|
const keeper = this;
|
|
3248
3259
|
const visible = (names) => names.filter((name2) => name2 !== UNLINKED_DIR);
|
|
3260
|
+
const counted2 = (st) => {
|
|
3261
|
+
const hidden = keeper.links.get(`${st.dev ?? 0}:${st.ino}`) ?? 0;
|
|
3262
|
+
return hidden === 0 || st.nlink === void 0 ? st : { ...st, nlink: Math.max(0, st.nlink - hidden) };
|
|
3263
|
+
};
|
|
3249
3264
|
return {
|
|
3250
3265
|
...fs,
|
|
3266
|
+
links: (path) => mayLink(fs, path),
|
|
3267
|
+
stat: async (path) => counted2(await fs.stat(path)),
|
|
3268
|
+
lstat: async (path) => counted2(await fs.lstat(path)),
|
|
3251
3269
|
async rm(path, options) {
|
|
3252
3270
|
const target = fs.resolvePath("/", path);
|
|
3253
3271
|
const pinned = await keeper.pin(target);
|
|
@@ -3261,7 +3279,7 @@ var UnlinkedKeeper = class {
|
|
|
3261
3279
|
await keeper.settle(pinned, () => fs.rename(from, to));
|
|
3262
3280
|
},
|
|
3263
3281
|
readdir: async (path) => visible(await fs.readdir(path)),
|
|
3264
|
-
readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR)
|
|
3282
|
+
readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR).map(([name2, st]) => [name2, st && counted2(st)])
|
|
3265
3283
|
};
|
|
3266
3284
|
}
|
|
3267
3285
|
async settle(pinned, op) {
|
|
@@ -3311,6 +3329,7 @@ var UnlinkedKeeper = class {
|
|
|
3311
3329
|
for (const holder of held.get(path)) holder.unkeep(path, file);
|
|
3312
3330
|
await this.serial(async () => {
|
|
3313
3331
|
await Promise.allSettled([this.fs.rename(file.hidden, path)]);
|
|
3332
|
+
this.unlinkedName(file.hidden);
|
|
3314
3333
|
await this.tidy(parentOf3(file.hidden));
|
|
3315
3334
|
});
|
|
3316
3335
|
}
|
|
@@ -3333,6 +3352,7 @@ var UnlinkedKeeper = class {
|
|
|
3333
3352
|
try {
|
|
3334
3353
|
await this.fs.mkdir(dir, { recursive: true });
|
|
3335
3354
|
await this.fs.rename(path, hidden);
|
|
3355
|
+
await this.linkedName(hidden);
|
|
3336
3356
|
return hidden;
|
|
3337
3357
|
} catch {
|
|
3338
3358
|
await this.tidy(dir);
|
|
@@ -3349,8 +3369,25 @@ var UnlinkedKeeper = class {
|
|
|
3349
3369
|
return;
|
|
3350
3370
|
}
|
|
3351
3371
|
await this.fs.rm(hidden).catch(() => void 0);
|
|
3372
|
+
this.unlinkedName(hidden);
|
|
3352
3373
|
await this.tidy(parentOf3(hidden));
|
|
3353
3374
|
}
|
|
3375
|
+
async linkedName(hidden) {
|
|
3376
|
+
if (!mayLink(this.fs, hidden)) return;
|
|
3377
|
+
const st = await this.fs.lstat(hidden).catch(() => void 0);
|
|
3378
|
+
if (!st || (st.nlink ?? 1) < 2) return;
|
|
3379
|
+
const id2 = `${st.dev ?? 0}:${st.ino}`;
|
|
3380
|
+
this.linked.set(hidden, id2);
|
|
3381
|
+
this.links.set(id2, (this.links.get(id2) ?? 0) + 1);
|
|
3382
|
+
}
|
|
3383
|
+
unlinkedName(hidden) {
|
|
3384
|
+
const id2 = this.linked.get(hidden);
|
|
3385
|
+
if (id2 === void 0) return;
|
|
3386
|
+
this.linked.delete(hidden);
|
|
3387
|
+
const left = (this.links.get(id2) ?? 1) - 1;
|
|
3388
|
+
if (left > 0) this.links.set(id2, left);
|
|
3389
|
+
else this.links.delete(id2);
|
|
3390
|
+
}
|
|
3354
3391
|
async tidy(dir) {
|
|
3355
3392
|
const left = await this.fs.readdir(dir).catch(() => [UNLINKED_DIR]);
|
|
3356
3393
|
if (left.length === 0) await this.fs.rm(dir, { recursive: true }).catch(() => void 0);
|
|
@@ -4412,6 +4449,8 @@ function withCommandDirs(fs, names, userDir = async () => void 0) {
|
|
|
4412
4449
|
}
|
|
4413
4450
|
},
|
|
4414
4451
|
writeFile: (path, content, owner) => fs.writeFile(path, content, owner),
|
|
4452
|
+
link: (from, to, follow2) => fs.link(from, to, follow2),
|
|
4453
|
+
links: (path) => mayLink(fs, path),
|
|
4415
4454
|
async exists(path) {
|
|
4416
4455
|
return await fs.exists(path) || await linked(path) !== void 0;
|
|
4417
4456
|
},
|
|
@@ -4576,6 +4615,10 @@ var FsWatchers = class {
|
|
|
4576
4615
|
},
|
|
4577
4616
|
rm: after(fs.rm.bind(fs), (path) => [path]),
|
|
4578
4617
|
rename: after(fs.rename.bind(fs), (from, to) => [from, to]),
|
|
4618
|
+
link: after(
|
|
4619
|
+
(from, to, follow2) => fs.link(from, to, follow2),
|
|
4620
|
+
(_from, to) => [to]
|
|
4621
|
+
),
|
|
4579
4622
|
symlink: after(fs.symlink.bind(fs), (_target, path) => [path]),
|
|
4580
4623
|
chmod: after(fs.chmod.bind(fs), (path) => [path]),
|
|
4581
4624
|
...fs.chown ? { chown: after(fs.chown.bind(fs), (path) => [path]) } : {},
|
|
@@ -4789,6 +4832,62 @@ var ChildTable = class {
|
|
|
4789
4832
|
}
|
|
4790
4833
|
};
|
|
4791
4834
|
|
|
4835
|
+
// src/fs/walk.ts
|
|
4836
|
+
var MAX_LINKS4 = 40;
|
|
4837
|
+
function parentOf5(path) {
|
|
4838
|
+
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
4839
|
+
}
|
|
4840
|
+
async function mustBeDir(fs, path) {
|
|
4841
|
+
if (!fs.lstat) return;
|
|
4842
|
+
const st = await fs.lstat(path);
|
|
4843
|
+
if (st.isDirectory !== true) throw fsError("ENOTDIR", path);
|
|
4844
|
+
}
|
|
4845
|
+
async function linkOf(fs, path) {
|
|
4846
|
+
return fs.readlink?.(path).catch(() => void 0);
|
|
4847
|
+
}
|
|
4848
|
+
async function realDir(fs, dir, budget = { hops: 0 }) {
|
|
4849
|
+
const queue = dir.split("/").filter(Boolean);
|
|
4850
|
+
let at = "/";
|
|
4851
|
+
while (queue.length > 0) {
|
|
4852
|
+
const part = queue.shift();
|
|
4853
|
+
if (part === ".") continue;
|
|
4854
|
+
if (part === "..") {
|
|
4855
|
+
at = parentOf5(at);
|
|
4856
|
+
continue;
|
|
4857
|
+
}
|
|
4858
|
+
const next2 = at === "/" ? `/${part}` : `${at}/${part}`;
|
|
4859
|
+
const link2 = await linkOf(fs, next2);
|
|
4860
|
+
if (link2 === void 0) {
|
|
4861
|
+
if (queue.find((p) => p !== ".") === "..") await mustBeDir(fs, next2);
|
|
4862
|
+
at = next2;
|
|
4863
|
+
continue;
|
|
4864
|
+
}
|
|
4865
|
+
if (budget.hops >= MAX_LINKS4) throw fsError("ELOOP", dir);
|
|
4866
|
+
budget.hops++;
|
|
4867
|
+
queue.unshift(...link2.split("/").filter(Boolean));
|
|
4868
|
+
if (link2.startsWith("/")) at = "/";
|
|
4869
|
+
}
|
|
4870
|
+
return at;
|
|
4871
|
+
}
|
|
4872
|
+
function joinName(dir, name2) {
|
|
4873
|
+
return dir === "/" ? `/${name2}` : `${dir}/${name2}`;
|
|
4874
|
+
}
|
|
4875
|
+
async function realTarget(fs, path) {
|
|
4876
|
+
const budget = { hops: 0 };
|
|
4877
|
+
let dir = await realDir(fs, parentOf5(path), budget);
|
|
4878
|
+
let name2 = path.slice(path.lastIndexOf("/") + 1);
|
|
4879
|
+
for (; ; ) {
|
|
4880
|
+
const link2 = await linkOf(fs, joinName(dir, name2));
|
|
4881
|
+
if (link2 === void 0) break;
|
|
4882
|
+
if (budget.hops >= MAX_LINKS4) throw fsError("ELOOP", path);
|
|
4883
|
+
budget.hops++;
|
|
4884
|
+
const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
|
|
4885
|
+
dir = await realDir(fs, parentOf5(target), budget);
|
|
4886
|
+
name2 = target.slice(target.lastIndexOf("/") + 1);
|
|
4887
|
+
}
|
|
4888
|
+
return joinName(dir, name2);
|
|
4889
|
+
}
|
|
4890
|
+
|
|
4792
4891
|
// src/mount/protocol.ts
|
|
4793
4892
|
var DRIVER_PROTOCOL = [1, 1];
|
|
4794
4893
|
function driverVersionError(theirs) {
|
|
@@ -4892,7 +4991,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
4892
4991
|
};
|
|
4893
4992
|
|
|
4894
4993
|
// src/mount/mount-fs.ts
|
|
4895
|
-
var
|
|
4994
|
+
var MAX_LINKS5 = 40;
|
|
4896
4995
|
var STALE_RETRIES = 3;
|
|
4897
4996
|
var SETTLE_ROUNDS = 4;
|
|
4898
4997
|
var DEFAULT_IO = 8 * 1024 * 1024;
|
|
@@ -5056,8 +5155,13 @@ var HeldPaths = class extends Map {
|
|
|
5056
5155
|
return this.revoked.has(path);
|
|
5057
5156
|
}
|
|
5058
5157
|
};
|
|
5059
|
-
var
|
|
5158
|
+
var parentOf6 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
5060
5159
|
var readonly = (mount) => mount.caps.readonly === true || mount.options.ro !== void 0;
|
|
5160
|
+
function inodeless(mount, attr) {
|
|
5161
|
+
if (attr.ino !== void 0) return false;
|
|
5162
|
+
if (mount.caps.links) mount.caps = { ...mount.caps, links: false };
|
|
5163
|
+
return true;
|
|
5164
|
+
}
|
|
5061
5165
|
function statOf(attr, path, mount) {
|
|
5062
5166
|
const mtime = new Date(attr.mtime);
|
|
5063
5167
|
const fallback = attr.kind === "directory" ? 16877 : attr.kind === "symlink" ? 41471 : 33188;
|
|
@@ -5071,10 +5175,12 @@ function statOf(attr, path, mount) {
|
|
|
5071
5175
|
atime: mtime,
|
|
5072
5176
|
ctime: mtime,
|
|
5073
5177
|
ino: attr.ino ?? inodeOf(path),
|
|
5178
|
+
...!inodeless(mount, attr) && attr.nlink !== void 0 ? { nlink: attr.nlink } : {},
|
|
5074
5179
|
dev: mount.dev,
|
|
5075
5180
|
...readonly(mount) ? { readonly: true } : {},
|
|
5076
5181
|
...mount.maxFile > 0 ? { maxFile: mount.maxFile } : {},
|
|
5077
5182
|
...mount.caps.ranges ? { ranged: true } : {},
|
|
5183
|
+
...mount.caps.links ? { linkable: true } : {},
|
|
5078
5184
|
...attr.etag !== void 0 ? { version: attr.etag } : {}
|
|
5079
5185
|
};
|
|
5080
5186
|
}
|
|
@@ -5323,7 +5429,7 @@ var MountTable = class {
|
|
|
5323
5429
|
mount.epoch++;
|
|
5324
5430
|
for (const key of [...mount.attrs.keys()]) if (within3(key, path)) mount.attrs.delete(key);
|
|
5325
5431
|
mount.lists.delete(path);
|
|
5326
|
-
mount.lists.delete(
|
|
5432
|
+
mount.lists.delete(parentOf6(path));
|
|
5327
5433
|
for (const key of [...mount.lists.keys()]) if (within3(key, path)) mount.lists.delete(key);
|
|
5328
5434
|
}
|
|
5329
5435
|
async getattr(mount, path, rel2, settled = false) {
|
|
@@ -5397,9 +5503,9 @@ var MountFs = class {
|
|
|
5397
5503
|
throw err;
|
|
5398
5504
|
}
|
|
5399
5505
|
if (attr.kind !== "symlink") return real;
|
|
5400
|
-
if (hops >=
|
|
5506
|
+
if (hops >= MAX_LINKS5) throw errnoError("ELOOP", path);
|
|
5401
5507
|
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
5402
|
-
return this.follow(this.base.resolvePath(
|
|
5508
|
+
return this.follow(this.base.resolvePath(parentOf6(real), target), hops + 1, create);
|
|
5403
5509
|
}
|
|
5404
5510
|
async open(mount, rel2, write) {
|
|
5405
5511
|
return await mount.conn.call({
|
|
@@ -5729,6 +5835,34 @@ var MountFs = class {
|
|
|
5729
5835
|
this.table.forget(a.mount, normalizePath(to));
|
|
5730
5836
|
}
|
|
5731
5837
|
}
|
|
5838
|
+
links(path) {
|
|
5839
|
+
const found = this.at(path);
|
|
5840
|
+
return found ? found.mount.caps.links === true : mayLink(this.base, path);
|
|
5841
|
+
}
|
|
5842
|
+
async named(path) {
|
|
5843
|
+
const at = normalizePath(path);
|
|
5844
|
+
return joinName(await realDir(this, parentOf6(at)), at.slice(at.lastIndexOf("/") + 1));
|
|
5845
|
+
}
|
|
5846
|
+
async link(source, target, follow2 = false) {
|
|
5847
|
+
const from = follow2 ? await realTarget(this, normalizePath(source)) : await this.named(source);
|
|
5848
|
+
const to = await this.named(target);
|
|
5849
|
+
const a = this.at(from);
|
|
5850
|
+
const b = this.at(to);
|
|
5851
|
+
if (!a && !b) return this.base.link(from, to);
|
|
5852
|
+
if (!a || !b || a.mount !== b.mount) throw errnoError("EXDEV", `${from} -> ${to}`);
|
|
5853
|
+
this.writable(a.mount, normalizePath(to));
|
|
5854
|
+
if (a.mount.caps.links) await this.lstat(from);
|
|
5855
|
+
if (!a.mount.caps.links) {
|
|
5856
|
+
throw errnoError("EPERM", `${from}: this file system has no hard links`);
|
|
5857
|
+
}
|
|
5858
|
+
await this.table.commitUnder(from);
|
|
5859
|
+
try {
|
|
5860
|
+
await a.mount.conn.call({ op: "link", from: a.rel, to: b.rel });
|
|
5861
|
+
} finally {
|
|
5862
|
+
this.table.forget(a.mount, normalizePath(from));
|
|
5863
|
+
this.table.forget(a.mount, normalizePath(to));
|
|
5864
|
+
}
|
|
5865
|
+
}
|
|
5732
5866
|
async symlink(target, path, owner) {
|
|
5733
5867
|
const found = this.at(path);
|
|
5734
5868
|
if (!found) return this.base.symlink(target, path, owner);
|
|
@@ -5828,10 +5962,12 @@ function statJson(s) {
|
|
|
5828
5962
|
atimeMs: s.atime.getTime(),
|
|
5829
5963
|
ctimeMs: s.ctime.getTime(),
|
|
5830
5964
|
ino: s.ino,
|
|
5965
|
+
...s.nlink !== void 0 ? { nlink: s.nlink } : {},
|
|
5831
5966
|
...s.dev !== void 0 ? { dev: s.dev } : {},
|
|
5832
5967
|
...s.readonly ? { readonly: true } : {},
|
|
5833
5968
|
...s.maxFile ? { maxFile: s.maxFile } : {},
|
|
5834
5969
|
...s.ranged ? { ranged: true } : {},
|
|
5970
|
+
...s.linkable ? { linkable: true } : {},
|
|
5835
5971
|
...s.version !== void 0 ? { version: s.version } : {},
|
|
5836
5972
|
...s.uid !== void 0 ? { uid: s.uid } : {},
|
|
5837
5973
|
...s.gid !== void 0 ? { gid: s.gid } : {}
|
|
@@ -5900,6 +6036,9 @@ async function run(entry, path, req) {
|
|
|
5900
6036
|
entry.renamed?.(path, to);
|
|
5901
6037
|
return done;
|
|
5902
6038
|
}
|
|
6039
|
+
case "link":
|
|
6040
|
+
await fs.link(path, fs.resolvePath(cwd, req.arg2 ?? ""), req.mode === 1);
|
|
6041
|
+
return done;
|
|
5903
6042
|
case "unlink":
|
|
5904
6043
|
if ((await fs.lstat(path)).isDirectory) throw syncError("EISDIR", path);
|
|
5905
6044
|
await fs.rm(path);
|
|
@@ -5961,12 +6100,28 @@ async function ranged(fs, path, req) {
|
|
|
5961
6100
|
return done;
|
|
5962
6101
|
}
|
|
5963
6102
|
var UNLINKED_OPS = /* @__PURE__ */ new Set(["read", "pread", "pwrite", "truncate"]);
|
|
5964
|
-
var OWN_OPS = /* @__PURE__ */ new Set(["rm", "unlink", "rmdir", "rename"]);
|
|
6103
|
+
var OWN_OPS = /* @__PURE__ */ new Set(["rm", "unlink", "rmdir", "rename", "link"]);
|
|
6104
|
+
var TWO_PATHS = /* @__PURE__ */ new Set(["rename", "link"]);
|
|
6105
|
+
var UNLINKING_OPS = /* @__PURE__ */ new Set(["rm", "unlink", "rename"]);
|
|
6106
|
+
async function linkedElsewhere(fs, path, from) {
|
|
6107
|
+
if (!mayLink(fs, path)) return false;
|
|
6108
|
+
const st = await fs.lstat(path).catch(() => void 0);
|
|
6109
|
+
if (!st || (st.nlink ?? 1) < 2) return false;
|
|
6110
|
+
const source = from === void 0 ? void 0 : await fs.lstat(from).catch(() => void 0);
|
|
6111
|
+
return !(source && source.ino === st.ino && source.dev === st.dev);
|
|
6112
|
+
}
|
|
5965
6113
|
async function keptOr(entry, path, handle) {
|
|
5966
6114
|
if (handle !== void 0) return entry.keptFor?.(handle)?.hidden ?? path;
|
|
5967
6115
|
const kept = entry.unlinked?.(path);
|
|
5968
6116
|
return kept && !await entry.fs.exists(path) ? kept.hidden : path;
|
|
5969
6117
|
}
|
|
6118
|
+
async function owned(entry, own, req, path, target) {
|
|
6119
|
+
const source = target === path ? void 0 : path;
|
|
6120
|
+
if (UNLINKING_OPS.has(req.op) && await linkedElsewhere(entry.fs, target, source)) {
|
|
6121
|
+
return run(entry, path, req);
|
|
6122
|
+
}
|
|
6123
|
+
return own(target, () => run(entry, path, req));
|
|
6124
|
+
}
|
|
5970
6125
|
async function dispatchSyncFs(req) {
|
|
5971
6126
|
const entry = resolveSyncFsToken(req.token);
|
|
5972
6127
|
if (!entry) return { ok: false, errno: "EACCES", message: "sync-fs: unknown or revoked token" };
|
|
@@ -5990,8 +6145,8 @@ async function dispatchSyncFs(req) {
|
|
|
5990
6145
|
return await (entry.gate ? entry.gate(path, serve) : serve());
|
|
5991
6146
|
}
|
|
5992
6147
|
if (!entry.own || !OWN_OPS.has(req.op)) return await run(entry, path, req);
|
|
5993
|
-
const target = req.op
|
|
5994
|
-
return await entry.own
|
|
6148
|
+
const target = TWO_PATHS.has(req.op) ? fs.resolvePath(cwd, req.arg2 ?? "") : path;
|
|
6149
|
+
return await owned(entry, entry.own, req, path, target);
|
|
5995
6150
|
} catch (err) {
|
|
5996
6151
|
return toErrno(err);
|
|
5997
6152
|
}
|
|
@@ -6593,6 +6748,10 @@ function normalize(path) {
|
|
|
6593
6748
|
}
|
|
6594
6749
|
return `/${out.join("/")}`;
|
|
6595
6750
|
}
|
|
6751
|
+
var RING = 8;
|
|
6752
|
+
var RING_SIZE = 64;
|
|
6753
|
+
var SLOT = 3;
|
|
6754
|
+
var SHARED_IDS_BYTES = (RING + RING_SIZE * SLOT) * 4;
|
|
6596
6755
|
|
|
6597
6756
|
// src/process/wasi/wasi-procfs.ts
|
|
6598
6757
|
var DIR_MODE = 16749;
|
|
@@ -9282,6 +9441,7 @@ var VfsNode = class {
|
|
|
9282
9441
|
fs;
|
|
9283
9442
|
path;
|
|
9284
9443
|
orphaned;
|
|
9444
|
+
id;
|
|
9285
9445
|
constructor(fs, path, contents2, orphaned = false, dirty = false, pin) {
|
|
9286
9446
|
this.fs = fs;
|
|
9287
9447
|
this.pin = pin;
|
|
@@ -9451,6 +9611,7 @@ var VfsNode = class {
|
|
|
9451
9611
|
var VfsNodes = class {
|
|
9452
9612
|
byPath = /* @__PURE__ */ new Map();
|
|
9453
9613
|
kept = /* @__PURE__ */ new Map();
|
|
9614
|
+
byId = /* @__PURE__ */ new Map();
|
|
9454
9615
|
keepsOnDisk = false;
|
|
9455
9616
|
fs;
|
|
9456
9617
|
entries = /* @__PURE__ */ new Map();
|
|
@@ -9470,7 +9631,7 @@ var VfsNodes = class {
|
|
|
9470
9631
|
return run2;
|
|
9471
9632
|
}
|
|
9472
9633
|
async entryKey(path) {
|
|
9473
|
-
const parent =
|
|
9634
|
+
const parent = parentOf5(path);
|
|
9474
9635
|
const dir = await this.real(parent, true, () => realDir(this.fs, parent));
|
|
9475
9636
|
return joinName(dir, path.slice(path.lastIndexOf("/") + 1));
|
|
9476
9637
|
}
|
|
@@ -9495,21 +9656,66 @@ var VfsNodes = class {
|
|
|
9495
9656
|
await this.fs.writeFile(path, new Uint8Array(0), owner);
|
|
9496
9657
|
});
|
|
9497
9658
|
}
|
|
9498
|
-
|
|
9499
|
-
|
|
9500
|
-
if (!
|
|
9501
|
-
|
|
9502
|
-
|
|
9503
|
-
|
|
9659
|
+
async identity(path) {
|
|
9660
|
+
const st = await this.fs.stat?.(path).catch(() => void 0);
|
|
9661
|
+
if (!st || st.ino === void 0 || (st.nlink ?? 1) < 2) return void 0;
|
|
9662
|
+
return `${st.dev ?? 0}:${st.ino}`;
|
|
9663
|
+
}
|
|
9664
|
+
async identify(path) {
|
|
9665
|
+
return this.byPath.has(path) || !mayLink(this.fs, path) ? void 0 : this.identity(path);
|
|
9666
|
+
}
|
|
9667
|
+
async linked(from, to) {
|
|
9668
|
+
const node = this.byPath.get(from);
|
|
9669
|
+
if (!node || node.orphaned) return;
|
|
9670
|
+
const id2 = node.id ?? await this.identity(to);
|
|
9671
|
+
if (id2 === void 0 || node.orphaned) return;
|
|
9672
|
+
this.know(node, id2);
|
|
9673
|
+
if (!this.byPath.has(to)) this.byPath.set(to, node);
|
|
9674
|
+
}
|
|
9675
|
+
know(node, id2) {
|
|
9676
|
+
node.id = id2;
|
|
9677
|
+
if (!this.byId.has(id2)) this.byId.set(id2, node);
|
|
9678
|
+
}
|
|
9679
|
+
namesOf(node) {
|
|
9680
|
+
const out = [];
|
|
9681
|
+
for (const [p, n] of this.byPath) if (n === node) out.push(p);
|
|
9682
|
+
return out;
|
|
9683
|
+
}
|
|
9684
|
+
forget(node) {
|
|
9685
|
+
for (const p of this.namesOf(node)) this.byPath.delete(p);
|
|
9686
|
+
if (node.id !== void 0 && this.byId.get(node.id) === node) this.byId.delete(node.id);
|
|
9687
|
+
}
|
|
9688
|
+
orphan(node) {
|
|
9689
|
+
node.orphaned = true;
|
|
9690
|
+
this.forget(node);
|
|
9691
|
+
}
|
|
9692
|
+
dropName(path, node) {
|
|
9693
|
+
const others = this.namesOf(node).filter((p) => p !== path);
|
|
9694
|
+
if (others.length === 0) return false;
|
|
9695
|
+
this.byPath.delete(path);
|
|
9696
|
+
if (node.path === path) node.renamedTo(others[0]);
|
|
9697
|
+
return true;
|
|
9698
|
+
}
|
|
9699
|
+
named(path, id2) {
|
|
9700
|
+
const node = this.byPath.get(path) ?? (id2 === void 0 ? void 0 : this.byId.get(id2));
|
|
9701
|
+
if (node && !this.byPath.has(path)) this.byPath.set(path, node);
|
|
9702
|
+
return node;
|
|
9703
|
+
}
|
|
9704
|
+
create(path, id2, node) {
|
|
9705
|
+
this.byPath.set(path, node);
|
|
9706
|
+
if (id2 !== void 0) this.know(node, id2);
|
|
9707
|
+
return node;
|
|
9708
|
+
}
|
|
9709
|
+
open(path, pin, writable = false, id2) {
|
|
9710
|
+
const node = this.named(path, id2) ?? this.create(path, id2, new VfsNode(this.fs, path, void 0, false, false, pin));
|
|
9504
9711
|
node.opens++;
|
|
9505
9712
|
if (writable) node.writers++;
|
|
9506
9713
|
return node;
|
|
9507
9714
|
}
|
|
9508
|
-
adopt(path, contents2, dirty, writable = false) {
|
|
9509
|
-
let node = this.
|
|
9715
|
+
adopt(path, contents2, dirty, writable = false, id2) {
|
|
9716
|
+
let node = this.named(path, id2);
|
|
9510
9717
|
if (!node) {
|
|
9511
|
-
node = new VfsNode(this.fs, path, contents2, false, dirty);
|
|
9512
|
-
this.byPath.set(path, node);
|
|
9718
|
+
node = this.create(path, id2, new VfsNode(this.fs, path, contents2, false, dirty));
|
|
9513
9719
|
} else if (dirty) {
|
|
9514
9720
|
node.replace(contents2);
|
|
9515
9721
|
}
|
|
@@ -9533,7 +9739,7 @@ var VfsNodes = class {
|
|
|
9533
9739
|
if (writable && node.writers === 0) this.onClosed?.(node.path);
|
|
9534
9740
|
return;
|
|
9535
9741
|
}
|
|
9536
|
-
|
|
9742
|
+
this.forget(node);
|
|
9537
9743
|
this.kept.get(node)?.release();
|
|
9538
9744
|
this.kept.delete(node);
|
|
9539
9745
|
this.onClosed?.(node.path);
|
|
@@ -9549,7 +9755,7 @@ var VfsNodes = class {
|
|
|
9549
9755
|
}
|
|
9550
9756
|
keep(path, file) {
|
|
9551
9757
|
const node = this.byPath.get(path);
|
|
9552
|
-
if (!node) {
|
|
9758
|
+
if (!node || this.dropName(path, node)) {
|
|
9553
9759
|
file.release();
|
|
9554
9760
|
return;
|
|
9555
9761
|
}
|
|
@@ -9578,33 +9784,32 @@ var VfsNodes = class {
|
|
|
9578
9784
|
}
|
|
9579
9785
|
}
|
|
9580
9786
|
revoke(prefix) {
|
|
9581
|
-
for (const [path, node] of this.byPath) {
|
|
9787
|
+
for (const [path, node] of [...this.byPath]) {
|
|
9582
9788
|
if (!within5(path, prefix)) continue;
|
|
9583
9789
|
node.revoke();
|
|
9584
|
-
this.
|
|
9790
|
+
this.forget(node);
|
|
9585
9791
|
}
|
|
9586
9792
|
}
|
|
9587
9793
|
unlinked(path) {
|
|
9588
9794
|
for (const [held, node] of [...this.byPath]) {
|
|
9589
|
-
if (!within5(held, path)) continue;
|
|
9590
|
-
|
|
9591
|
-
this.byPath.delete(held);
|
|
9795
|
+
if (!within5(held, path) || this.byPath.get(held) !== node) continue;
|
|
9796
|
+
if (!this.dropName(held, node)) this.orphan(node);
|
|
9592
9797
|
}
|
|
9593
9798
|
}
|
|
9594
9799
|
renamed(from, to) {
|
|
9595
9800
|
if (from === to) return;
|
|
9596
9801
|
const moved2 = [];
|
|
9597
|
-
for (const [p, node] of this.byPath) {
|
|
9598
|
-
if (within5(p, from)) moved2.push(node);
|
|
9599
|
-
else if (within5(p, to)) {
|
|
9600
|
-
node
|
|
9601
|
-
this.byPath.delete(p);
|
|
9802
|
+
for (const [p, node] of [...this.byPath]) {
|
|
9803
|
+
if (within5(p, from)) moved2.push([p, node]);
|
|
9804
|
+
else if (within5(p, to) && this.byPath.get(p) === node && !this.dropName(p, node)) {
|
|
9805
|
+
this.orphan(node);
|
|
9602
9806
|
}
|
|
9603
9807
|
}
|
|
9604
|
-
for (const
|
|
9605
|
-
|
|
9606
|
-
|
|
9607
|
-
|
|
9808
|
+
for (const [p] of moved2) this.byPath.delete(p);
|
|
9809
|
+
for (const [p, node] of moved2) {
|
|
9810
|
+
const next2 = to + p.slice(from.length);
|
|
9811
|
+
if (node.path === p) node.renamedTo(next2);
|
|
9812
|
+
this.byPath.set(next2, node);
|
|
9608
9813
|
}
|
|
9609
9814
|
}
|
|
9610
9815
|
};
|
|
@@ -9618,65 +9823,11 @@ async function present(fs, path, follow2 = false) {
|
|
|
9618
9823
|
throw err;
|
|
9619
9824
|
}
|
|
9620
9825
|
}
|
|
9621
|
-
var MAX_LINKS5 = 40;
|
|
9622
|
-
function parentOf6(path) {
|
|
9623
|
-
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
9624
|
-
}
|
|
9625
|
-
async function mustBeDir(fs, path) {
|
|
9626
|
-
if (!fs.lstat) return;
|
|
9627
|
-
const st = await fs.lstat(path);
|
|
9628
|
-
if (st.isDirectory !== true) throw fsError("ENOTDIR", path);
|
|
9629
|
-
}
|
|
9630
|
-
async function linkOf(fs, path) {
|
|
9631
|
-
return fs.readlink?.(path).catch(() => void 0);
|
|
9632
|
-
}
|
|
9633
|
-
async function realDir(fs, dir, budget = { hops: 0 }) {
|
|
9634
|
-
const queue = dir.split("/").filter(Boolean);
|
|
9635
|
-
let at = "/";
|
|
9636
|
-
while (queue.length > 0) {
|
|
9637
|
-
const part = queue.shift();
|
|
9638
|
-
if (part === ".") continue;
|
|
9639
|
-
if (part === "..") {
|
|
9640
|
-
at = parentOf6(at);
|
|
9641
|
-
continue;
|
|
9642
|
-
}
|
|
9643
|
-
const next2 = at === "/" ? `/${part}` : `${at}/${part}`;
|
|
9644
|
-
const link2 = await linkOf(fs, next2);
|
|
9645
|
-
if (link2 === void 0) {
|
|
9646
|
-
if (queue.find((p) => p !== ".") === "..") await mustBeDir(fs, next2);
|
|
9647
|
-
at = next2;
|
|
9648
|
-
continue;
|
|
9649
|
-
}
|
|
9650
|
-
if (budget.hops >= MAX_LINKS5) throw fsError("ELOOP", dir);
|
|
9651
|
-
budget.hops++;
|
|
9652
|
-
queue.unshift(...link2.split("/").filter(Boolean));
|
|
9653
|
-
if (link2.startsWith("/")) at = "/";
|
|
9654
|
-
}
|
|
9655
|
-
return at;
|
|
9656
|
-
}
|
|
9657
9826
|
function errCode(err) {
|
|
9658
9827
|
return err?.code;
|
|
9659
9828
|
}
|
|
9660
|
-
function joinName(dir, name2) {
|
|
9661
|
-
return dir === "/" ? `/${name2}` : `${dir}/${name2}`;
|
|
9662
|
-
}
|
|
9663
|
-
async function realTarget(fs, path) {
|
|
9664
|
-
const budget = { hops: 0 };
|
|
9665
|
-
let dir = await realDir(fs, parentOf6(path), budget);
|
|
9666
|
-
let name2 = path.slice(path.lastIndexOf("/") + 1);
|
|
9667
|
-
for (; ; ) {
|
|
9668
|
-
const link2 = await linkOf(fs, joinName(dir, name2));
|
|
9669
|
-
if (link2 === void 0) break;
|
|
9670
|
-
if (budget.hops >= MAX_LINKS5) throw fsError("ELOOP", path);
|
|
9671
|
-
budget.hops++;
|
|
9672
|
-
const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
|
|
9673
|
-
dir = await realDir(fs, parentOf6(target), budget);
|
|
9674
|
-
name2 = target.slice(target.lastIndexOf("/") + 1);
|
|
9675
|
-
}
|
|
9676
|
-
return joinName(dir, name2);
|
|
9677
|
-
}
|
|
9678
9829
|
async function targetDir(fs, path) {
|
|
9679
|
-
return
|
|
9830
|
+
return parentOf5(await realTarget(fs, path).catch(() => path));
|
|
9680
9831
|
}
|
|
9681
9832
|
async function refuseReadonly(fs, path, flags, opts = {}) {
|
|
9682
9833
|
const writes = (flags & O_ACCMODE) !== 0 || opts.truncate === true;
|
|
@@ -9712,6 +9863,15 @@ function keepingOpen(fs, nodes) {
|
|
|
9712
9863
|
writeFile: (path, content, owner) => nodes.onEntry(fs.resolvePath("/", path), () => fs.writeFile(path, content, owner)),
|
|
9713
9864
|
createExclusive: async (path, owner) => nodes.createExclusive(await nodes.entryKey(fs.resolvePath("/", path)), owner),
|
|
9714
9865
|
symlink: (target, path, owner) => nodes.onEntry(fs.resolvePath("/", path), () => fs.symlink(target, path, owner)),
|
|
9866
|
+
links: (path) => mayLink(fs, path),
|
|
9867
|
+
link: async (from, to, follow2) => {
|
|
9868
|
+
const source = fs.resolvePath("/", from);
|
|
9869
|
+
const at = await (follow2 ? nodes.targetKey(source) : nodes.entryKey(source));
|
|
9870
|
+
await nodes.flush(at);
|
|
9871
|
+
const key = await nodes.entryKey(fs.resolvePath("/", to));
|
|
9872
|
+
await nodes.onEntry(key, () => fs.link(from, to, follow2));
|
|
9873
|
+
await nodes.linked(at, key);
|
|
9874
|
+
},
|
|
9715
9875
|
mkdir: (path, options) => nodes.onEntry(fs.resolvePath("/", path), () => fs.mkdir(path, options))
|
|
9716
9876
|
};
|
|
9717
9877
|
}
|
|
@@ -9719,7 +9879,7 @@ function vfsFile(fs, opts, nodes) {
|
|
|
9719
9879
|
const access = opts.flags & O_ACCMODE;
|
|
9720
9880
|
const readable = access !== O_WRONLY;
|
|
9721
9881
|
const writable = access === O_WRONLY || access === O_RDWR;
|
|
9722
|
-
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);
|
|
9882
|
+
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);
|
|
9723
9883
|
let openError;
|
|
9724
9884
|
const atOpen = (op) => {
|
|
9725
9885
|
void node.serial(op).catch((err) => {
|
|
@@ -10154,12 +10314,12 @@ var WasmProcess = class {
|
|
|
10154
10314
|
} else if (req.create) {
|
|
10155
10315
|
await nodes.onEntry(path, async () => {
|
|
10156
10316
|
const fresh = this.options.owners && !await present(this.options.fs, path, true);
|
|
10157
|
-
this.fds.installAt(fd, this.openVfsFile(open));
|
|
10317
|
+
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
10158
10318
|
await this.fds.get(fd).file.stat?.();
|
|
10159
10319
|
if (fresh) await this.options.fs.chown?.(path, { ...this.newOwner(), kind: "new" });
|
|
10160
10320
|
});
|
|
10161
10321
|
} else {
|
|
10162
|
-
this.fds.installAt(fd, this.openVfsFile(open));
|
|
10322
|
+
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
10163
10323
|
}
|
|
10164
10324
|
if (req.existing && !await present(this.options.fs, path, true)) {
|
|
10165
10325
|
throw new KernelError("ENOENT");
|
|
@@ -10190,7 +10350,7 @@ var WasmProcess = class {
|
|
|
10190
10350
|
);
|
|
10191
10351
|
return memoryFile(tree.read(path), path, req.position);
|
|
10192
10352
|
}
|
|
10193
|
-
openVfsFile(req) {
|
|
10353
|
+
openVfsFile(req, id2) {
|
|
10194
10354
|
return vfsFile(
|
|
10195
10355
|
this.options.fs,
|
|
10196
10356
|
{
|
|
@@ -10201,7 +10361,8 @@ var WasmProcess = class {
|
|
|
10201
10361
|
...req.orphan ? { orphan: true } : {},
|
|
10202
10362
|
...req.truncate ? { truncate: true } : {},
|
|
10203
10363
|
...req.create ? { create: true } : {},
|
|
10204
|
-
...req.pin ? { pin: req.pin } : {}
|
|
10364
|
+
...req.pin ? { pin: req.pin } : {},
|
|
10365
|
+
...id2 !== void 0 && !req.orphan ? { id: id2 } : {}
|
|
10205
10366
|
},
|
|
10206
10367
|
this.nodes
|
|
10207
10368
|
);
|
|
@@ -10349,6 +10510,7 @@ var WasmProcess = class {
|
|
|
10349
10510
|
}
|
|
10350
10511
|
if (req.path === void 0) throw new KernelError("EINVAL");
|
|
10351
10512
|
const path = req.orphan ? req.path : await this.keptOr(await this.nodes.targetKey(req.path), req.handle);
|
|
10513
|
+
const id2 = req.orphan ? void 0 : await this.nodes.identify(path);
|
|
10352
10514
|
if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
|
|
10353
10515
|
const file = vfsFile(
|
|
10354
10516
|
this.options.fs,
|
|
@@ -10359,7 +10521,8 @@ var WasmProcess = class {
|
|
|
10359
10521
|
...req.contents !== void 0 ? { contents: req.contents } : {},
|
|
10360
10522
|
...req.orphan ? { orphan: true } : {},
|
|
10361
10523
|
...req.dirty ? { dirty: true } : {},
|
|
10362
|
-
...req.pin ? { pin: req.pin } : {}
|
|
10524
|
+
...req.pin ? { pin: req.pin } : {},
|
|
10525
|
+
...id2 !== void 0 ? { id: id2 } : {}
|
|
10363
10526
|
},
|
|
10364
10527
|
this.nodes
|
|
10365
10528
|
);
|
|
@@ -11431,6 +11594,9 @@ async function dispatch(h, req) {
|
|
|
11431
11594
|
return h.unlink(req.path);
|
|
11432
11595
|
case "rename":
|
|
11433
11596
|
return h.rename(req.from, req.to);
|
|
11597
|
+
case "link":
|
|
11598
|
+
if (!h.link) throw fsError2("EPERM", "this file system has no hard links");
|
|
11599
|
+
return h.link(req.from, req.to);
|
|
11434
11600
|
case "symlink":
|
|
11435
11601
|
if (!h.symlink) throw fsError2("EPERM", "this file system has no symlinks");
|
|
11436
11602
|
return h.symlink(req.target, req.path);
|
|
@@ -12616,8 +12782,8 @@ var ChunkedBytes = class {
|
|
|
12616
12782
|
}
|
|
12617
12783
|
return out;
|
|
12618
12784
|
}
|
|
12619
|
-
write(offset, bytes,
|
|
12620
|
-
const whole =
|
|
12785
|
+
write(offset, bytes, owned2 = false) {
|
|
12786
|
+
const whole = owned2 && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
|
|
12621
12787
|
for (let done4 = 0; done4 < bytes.length; ) {
|
|
12622
12788
|
const at = offset + done4;
|
|
12623
12789
|
const index = Math.floor(at / CHUNK3);
|
|
@@ -12659,12 +12825,17 @@ function tmpfs() {
|
|
|
12659
12825
|
mtime: Date.now(),
|
|
12660
12826
|
data: new ChunkedBytes(),
|
|
12661
12827
|
target: "",
|
|
12662
|
-
children: /* @__PURE__ */ new Map()
|
|
12828
|
+
children: /* @__PURE__ */ new Map(),
|
|
12829
|
+
links: 1
|
|
12663
12830
|
});
|
|
12664
12831
|
const root = make("directory", 16877);
|
|
12665
12832
|
const handles = /* @__PURE__ */ new Map();
|
|
12666
12833
|
let nextFh = 0;
|
|
12667
12834
|
const parts = (path) => path.split("/").filter(Boolean);
|
|
12835
|
+
const place = (dir, name2, node) => {
|
|
12836
|
+
dir.children.set(name2, node);
|
|
12837
|
+
dir.mtime = Date.now();
|
|
12838
|
+
};
|
|
12668
12839
|
function find(path) {
|
|
12669
12840
|
let node = root;
|
|
12670
12841
|
for (const name2 of parts(path)) {
|
|
@@ -12688,7 +12859,8 @@ function tmpfs() {
|
|
|
12688
12859
|
size: node.kind === "file" ? node.data.size : node.kind === "symlink" ? node.target.length : 4096,
|
|
12689
12860
|
mtime: node.mtime,
|
|
12690
12861
|
mode: node.mode,
|
|
12691
|
-
ino: node.ino
|
|
12862
|
+
ino: node.ino,
|
|
12863
|
+
...node.kind === "directory" ? {} : { nlink: node.links }
|
|
12692
12864
|
});
|
|
12693
12865
|
const handle = (fh) => {
|
|
12694
12866
|
const found = handles.get(fh);
|
|
@@ -12703,8 +12875,7 @@ function tmpfs() {
|
|
|
12703
12875
|
if (existing) return existing;
|
|
12704
12876
|
if (!flags.create) throw fsError2("ENOENT", path);
|
|
12705
12877
|
const node = make("file", 33188);
|
|
12706
|
-
dir
|
|
12707
|
-
dir.mtime = Date.now();
|
|
12878
|
+
place(dir, name2, node);
|
|
12708
12879
|
return node;
|
|
12709
12880
|
}
|
|
12710
12881
|
return {
|
|
@@ -12743,8 +12914,7 @@ function tmpfs() {
|
|
|
12743
12914
|
async mkdir(path) {
|
|
12744
12915
|
const { dir, name: name2 } = parent(path);
|
|
12745
12916
|
if (dir.children.has(name2)) throw fsError2("EEXIST", path);
|
|
12746
|
-
dir
|
|
12747
|
-
dir.mtime = Date.now();
|
|
12917
|
+
place(dir, name2, make("directory", 16877));
|
|
12748
12918
|
},
|
|
12749
12919
|
async rmdir(path) {
|
|
12750
12920
|
const { dir, name: name2 } = parent(path);
|
|
@@ -12761,6 +12931,7 @@ function tmpfs() {
|
|
|
12761
12931
|
if (!node) throw fsError2("ENOENT", path);
|
|
12762
12932
|
if (node.kind === "directory") throw fsError2("EISDIR", path);
|
|
12763
12933
|
dir.children.delete(name2);
|
|
12934
|
+
node.links--;
|
|
12764
12935
|
dir.mtime = Date.now();
|
|
12765
12936
|
},
|
|
12766
12937
|
async rename(from, to) {
|
|
@@ -12777,17 +12948,25 @@ function tmpfs() {
|
|
|
12777
12948
|
throw fsError2(node.kind === "directory" ? "ENOTEMPTY" : "EISDIR", to);
|
|
12778
12949
|
}
|
|
12779
12950
|
source.dir.children.delete(source.name);
|
|
12951
|
+
if (replaced) replaced.links--;
|
|
12780
12952
|
target.dir.children.set(target.name, node);
|
|
12781
12953
|
source.dir.mtime = Date.now();
|
|
12782
12954
|
target.dir.mtime = source.dir.mtime;
|
|
12783
12955
|
},
|
|
12956
|
+
async link(from, to) {
|
|
12957
|
+
const node = find(from);
|
|
12958
|
+
if (node.kind === "directory") throw fsError2("EPERM", `${from} is a directory`);
|
|
12959
|
+
const { dir, name: name2 } = parent(to);
|
|
12960
|
+
if (dir.children.has(name2)) throw fsError2("EEXIST", to);
|
|
12961
|
+
place(dir, name2, node);
|
|
12962
|
+
node.links++;
|
|
12963
|
+
},
|
|
12784
12964
|
async symlink(target, path) {
|
|
12785
12965
|
const { dir, name: name2 } = parent(path);
|
|
12786
12966
|
if (dir.children.has(name2)) throw fsError2("EEXIST", path);
|
|
12787
12967
|
const node = make("symlink", 41471);
|
|
12788
12968
|
node.target = target;
|
|
12789
|
-
dir
|
|
12790
|
-
dir.mtime = Date.now();
|
|
12969
|
+
place(dir, name2, node);
|
|
12791
12970
|
},
|
|
12792
12971
|
async readlink(path) {
|
|
12793
12972
|
const node = find(path);
|
|
@@ -13687,7 +13866,7 @@ var Launcher = class {
|
|
|
13687
13866
|
serveFilesystem(
|
|
13688
13867
|
port2,
|
|
13689
13868
|
tmpfs(),
|
|
13690
|
-
{ symlinks: true, chmod: true, linkTimes: true, ranges: true, attrTtl: 0 },
|
|
13869
|
+
{ symlinks: true, links: true, chmod: true, linkTimes: true, ranges: true, attrTtl: 0 },
|
|
13691
13870
|
{ owned: true }
|
|
13692
13871
|
);
|
|
13693
13872
|
return {
|