@ai-ecoverse/slicc-kernel 1.57.1 → 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 +3 -2
- 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 +286 -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 +207 -28
- package/dist/node-process-worker.js.map +3 -3
- package/dist/node.js +286 -111
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +207 -28
- 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
|
}
|
|
@@ -9286,6 +9441,7 @@ var VfsNode = class {
|
|
|
9286
9441
|
fs;
|
|
9287
9442
|
path;
|
|
9288
9443
|
orphaned;
|
|
9444
|
+
id;
|
|
9289
9445
|
constructor(fs, path, contents2, orphaned = false, dirty = false, pin) {
|
|
9290
9446
|
this.fs = fs;
|
|
9291
9447
|
this.pin = pin;
|
|
@@ -9455,6 +9611,7 @@ var VfsNode = class {
|
|
|
9455
9611
|
var VfsNodes = class {
|
|
9456
9612
|
byPath = /* @__PURE__ */ new Map();
|
|
9457
9613
|
kept = /* @__PURE__ */ new Map();
|
|
9614
|
+
byId = /* @__PURE__ */ new Map();
|
|
9458
9615
|
keepsOnDisk = false;
|
|
9459
9616
|
fs;
|
|
9460
9617
|
entries = /* @__PURE__ */ new Map();
|
|
@@ -9474,7 +9631,7 @@ var VfsNodes = class {
|
|
|
9474
9631
|
return run2;
|
|
9475
9632
|
}
|
|
9476
9633
|
async entryKey(path) {
|
|
9477
|
-
const parent =
|
|
9634
|
+
const parent = parentOf5(path);
|
|
9478
9635
|
const dir = await this.real(parent, true, () => realDir(this.fs, parent));
|
|
9479
9636
|
return joinName(dir, path.slice(path.lastIndexOf("/") + 1));
|
|
9480
9637
|
}
|
|
@@ -9499,21 +9656,66 @@ var VfsNodes = class {
|
|
|
9499
9656
|
await this.fs.writeFile(path, new Uint8Array(0), owner);
|
|
9500
9657
|
});
|
|
9501
9658
|
}
|
|
9502
|
-
|
|
9503
|
-
|
|
9504
|
-
if (!
|
|
9505
|
-
|
|
9506
|
-
|
|
9507
|
-
|
|
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));
|
|
9508
9711
|
node.opens++;
|
|
9509
9712
|
if (writable) node.writers++;
|
|
9510
9713
|
return node;
|
|
9511
9714
|
}
|
|
9512
|
-
adopt(path, contents2, dirty, writable = false) {
|
|
9513
|
-
let node = this.
|
|
9715
|
+
adopt(path, contents2, dirty, writable = false, id2) {
|
|
9716
|
+
let node = this.named(path, id2);
|
|
9514
9717
|
if (!node) {
|
|
9515
|
-
node = new VfsNode(this.fs, path, contents2, false, dirty);
|
|
9516
|
-
this.byPath.set(path, node);
|
|
9718
|
+
node = this.create(path, id2, new VfsNode(this.fs, path, contents2, false, dirty));
|
|
9517
9719
|
} else if (dirty) {
|
|
9518
9720
|
node.replace(contents2);
|
|
9519
9721
|
}
|
|
@@ -9537,7 +9739,7 @@ var VfsNodes = class {
|
|
|
9537
9739
|
if (writable && node.writers === 0) this.onClosed?.(node.path);
|
|
9538
9740
|
return;
|
|
9539
9741
|
}
|
|
9540
|
-
|
|
9742
|
+
this.forget(node);
|
|
9541
9743
|
this.kept.get(node)?.release();
|
|
9542
9744
|
this.kept.delete(node);
|
|
9543
9745
|
this.onClosed?.(node.path);
|
|
@@ -9553,7 +9755,7 @@ var VfsNodes = class {
|
|
|
9553
9755
|
}
|
|
9554
9756
|
keep(path, file) {
|
|
9555
9757
|
const node = this.byPath.get(path);
|
|
9556
|
-
if (!node) {
|
|
9758
|
+
if (!node || this.dropName(path, node)) {
|
|
9557
9759
|
file.release();
|
|
9558
9760
|
return;
|
|
9559
9761
|
}
|
|
@@ -9582,33 +9784,32 @@ var VfsNodes = class {
|
|
|
9582
9784
|
}
|
|
9583
9785
|
}
|
|
9584
9786
|
revoke(prefix) {
|
|
9585
|
-
for (const [path, node] of this.byPath) {
|
|
9787
|
+
for (const [path, node] of [...this.byPath]) {
|
|
9586
9788
|
if (!within5(path, prefix)) continue;
|
|
9587
9789
|
node.revoke();
|
|
9588
|
-
this.
|
|
9790
|
+
this.forget(node);
|
|
9589
9791
|
}
|
|
9590
9792
|
}
|
|
9591
9793
|
unlinked(path) {
|
|
9592
9794
|
for (const [held, node] of [...this.byPath]) {
|
|
9593
|
-
if (!within5(held, path)) continue;
|
|
9594
|
-
|
|
9595
|
-
this.byPath.delete(held);
|
|
9795
|
+
if (!within5(held, path) || this.byPath.get(held) !== node) continue;
|
|
9796
|
+
if (!this.dropName(held, node)) this.orphan(node);
|
|
9596
9797
|
}
|
|
9597
9798
|
}
|
|
9598
9799
|
renamed(from, to) {
|
|
9599
9800
|
if (from === to) return;
|
|
9600
9801
|
const moved2 = [];
|
|
9601
|
-
for (const [p, node] of this.byPath) {
|
|
9602
|
-
if (within5(p, from)) moved2.push(node);
|
|
9603
|
-
else if (within5(p, to)) {
|
|
9604
|
-
node
|
|
9605
|
-
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);
|
|
9606
9806
|
}
|
|
9607
9807
|
}
|
|
9608
|
-
for (const
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
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);
|
|
9612
9813
|
}
|
|
9613
9814
|
}
|
|
9614
9815
|
};
|
|
@@ -9622,65 +9823,11 @@ async function present(fs, path, follow2 = false) {
|
|
|
9622
9823
|
throw err;
|
|
9623
9824
|
}
|
|
9624
9825
|
}
|
|
9625
|
-
var MAX_LINKS5 = 40;
|
|
9626
|
-
function parentOf6(path) {
|
|
9627
|
-
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
9628
|
-
}
|
|
9629
|
-
async function mustBeDir(fs, path) {
|
|
9630
|
-
if (!fs.lstat) return;
|
|
9631
|
-
const st = await fs.lstat(path);
|
|
9632
|
-
if (st.isDirectory !== true) throw fsError("ENOTDIR", path);
|
|
9633
|
-
}
|
|
9634
|
-
async function linkOf(fs, path) {
|
|
9635
|
-
return fs.readlink?.(path).catch(() => void 0);
|
|
9636
|
-
}
|
|
9637
|
-
async function realDir(fs, dir, budget = { hops: 0 }) {
|
|
9638
|
-
const queue = dir.split("/").filter(Boolean);
|
|
9639
|
-
let at = "/";
|
|
9640
|
-
while (queue.length > 0) {
|
|
9641
|
-
const part = queue.shift();
|
|
9642
|
-
if (part === ".") continue;
|
|
9643
|
-
if (part === "..") {
|
|
9644
|
-
at = parentOf6(at);
|
|
9645
|
-
continue;
|
|
9646
|
-
}
|
|
9647
|
-
const next2 = at === "/" ? `/${part}` : `${at}/${part}`;
|
|
9648
|
-
const link2 = await linkOf(fs, next2);
|
|
9649
|
-
if (link2 === void 0) {
|
|
9650
|
-
if (queue.find((p) => p !== ".") === "..") await mustBeDir(fs, next2);
|
|
9651
|
-
at = next2;
|
|
9652
|
-
continue;
|
|
9653
|
-
}
|
|
9654
|
-
if (budget.hops >= MAX_LINKS5) throw fsError("ELOOP", dir);
|
|
9655
|
-
budget.hops++;
|
|
9656
|
-
queue.unshift(...link2.split("/").filter(Boolean));
|
|
9657
|
-
if (link2.startsWith("/")) at = "/";
|
|
9658
|
-
}
|
|
9659
|
-
return at;
|
|
9660
|
-
}
|
|
9661
9826
|
function errCode(err) {
|
|
9662
9827
|
return err?.code;
|
|
9663
9828
|
}
|
|
9664
|
-
function joinName(dir, name2) {
|
|
9665
|
-
return dir === "/" ? `/${name2}` : `${dir}/${name2}`;
|
|
9666
|
-
}
|
|
9667
|
-
async function realTarget(fs, path) {
|
|
9668
|
-
const budget = { hops: 0 };
|
|
9669
|
-
let dir = await realDir(fs, parentOf6(path), budget);
|
|
9670
|
-
let name2 = path.slice(path.lastIndexOf("/") + 1);
|
|
9671
|
-
for (; ; ) {
|
|
9672
|
-
const link2 = await linkOf(fs, joinName(dir, name2));
|
|
9673
|
-
if (link2 === void 0) break;
|
|
9674
|
-
if (budget.hops >= MAX_LINKS5) throw fsError("ELOOP", path);
|
|
9675
|
-
budget.hops++;
|
|
9676
|
-
const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
|
|
9677
|
-
dir = await realDir(fs, parentOf6(target), budget);
|
|
9678
|
-
name2 = target.slice(target.lastIndexOf("/") + 1);
|
|
9679
|
-
}
|
|
9680
|
-
return joinName(dir, name2);
|
|
9681
|
-
}
|
|
9682
9829
|
async function targetDir(fs, path) {
|
|
9683
|
-
return
|
|
9830
|
+
return parentOf5(await realTarget(fs, path).catch(() => path));
|
|
9684
9831
|
}
|
|
9685
9832
|
async function refuseReadonly(fs, path, flags, opts = {}) {
|
|
9686
9833
|
const writes = (flags & O_ACCMODE) !== 0 || opts.truncate === true;
|
|
@@ -9716,6 +9863,15 @@ function keepingOpen(fs, nodes) {
|
|
|
9716
9863
|
writeFile: (path, content, owner) => nodes.onEntry(fs.resolvePath("/", path), () => fs.writeFile(path, content, owner)),
|
|
9717
9864
|
createExclusive: async (path, owner) => nodes.createExclusive(await nodes.entryKey(fs.resolvePath("/", path)), owner),
|
|
9718
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
|
+
},
|
|
9719
9875
|
mkdir: (path, options) => nodes.onEntry(fs.resolvePath("/", path), () => fs.mkdir(path, options))
|
|
9720
9876
|
};
|
|
9721
9877
|
}
|
|
@@ -9723,7 +9879,7 @@ function vfsFile(fs, opts, nodes) {
|
|
|
9723
9879
|
const access = opts.flags & O_ACCMODE;
|
|
9724
9880
|
const readable = access !== O_WRONLY;
|
|
9725
9881
|
const writable = access === O_WRONLY || access === O_RDWR;
|
|
9726
|
-
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);
|
|
9727
9883
|
let openError;
|
|
9728
9884
|
const atOpen = (op) => {
|
|
9729
9885
|
void node.serial(op).catch((err) => {
|
|
@@ -10158,12 +10314,12 @@ var WasmProcess = class {
|
|
|
10158
10314
|
} else if (req.create) {
|
|
10159
10315
|
await nodes.onEntry(path, async () => {
|
|
10160
10316
|
const fresh = this.options.owners && !await present(this.options.fs, path, true);
|
|
10161
|
-
this.fds.installAt(fd, this.openVfsFile(open));
|
|
10317
|
+
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
10162
10318
|
await this.fds.get(fd).file.stat?.();
|
|
10163
10319
|
if (fresh) await this.options.fs.chown?.(path, { ...this.newOwner(), kind: "new" });
|
|
10164
10320
|
});
|
|
10165
10321
|
} else {
|
|
10166
|
-
this.fds.installAt(fd, this.openVfsFile(open));
|
|
10322
|
+
this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
|
|
10167
10323
|
}
|
|
10168
10324
|
if (req.existing && !await present(this.options.fs, path, true)) {
|
|
10169
10325
|
throw new KernelError("ENOENT");
|
|
@@ -10194,7 +10350,7 @@ var WasmProcess = class {
|
|
|
10194
10350
|
);
|
|
10195
10351
|
return memoryFile(tree.read(path), path, req.position);
|
|
10196
10352
|
}
|
|
10197
|
-
openVfsFile(req) {
|
|
10353
|
+
openVfsFile(req, id2) {
|
|
10198
10354
|
return vfsFile(
|
|
10199
10355
|
this.options.fs,
|
|
10200
10356
|
{
|
|
@@ -10205,7 +10361,8 @@ var WasmProcess = class {
|
|
|
10205
10361
|
...req.orphan ? { orphan: true } : {},
|
|
10206
10362
|
...req.truncate ? { truncate: true } : {},
|
|
10207
10363
|
...req.create ? { create: true } : {},
|
|
10208
|
-
...req.pin ? { pin: req.pin } : {}
|
|
10364
|
+
...req.pin ? { pin: req.pin } : {},
|
|
10365
|
+
...id2 !== void 0 && !req.orphan ? { id: id2 } : {}
|
|
10209
10366
|
},
|
|
10210
10367
|
this.nodes
|
|
10211
10368
|
);
|
|
@@ -10353,6 +10510,7 @@ var WasmProcess = class {
|
|
|
10353
10510
|
}
|
|
10354
10511
|
if (req.path === void 0) throw new KernelError("EINVAL");
|
|
10355
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);
|
|
10356
10514
|
if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
|
|
10357
10515
|
const file = vfsFile(
|
|
10358
10516
|
this.options.fs,
|
|
@@ -10363,7 +10521,8 @@ var WasmProcess = class {
|
|
|
10363
10521
|
...req.contents !== void 0 ? { contents: req.contents } : {},
|
|
10364
10522
|
...req.orphan ? { orphan: true } : {},
|
|
10365
10523
|
...req.dirty ? { dirty: true } : {},
|
|
10366
|
-
...req.pin ? { pin: req.pin } : {}
|
|
10524
|
+
...req.pin ? { pin: req.pin } : {},
|
|
10525
|
+
...id2 !== void 0 ? { id: id2 } : {}
|
|
10367
10526
|
},
|
|
10368
10527
|
this.nodes
|
|
10369
10528
|
);
|
|
@@ -11435,6 +11594,9 @@ async function dispatch(h, req) {
|
|
|
11435
11594
|
return h.unlink(req.path);
|
|
11436
11595
|
case "rename":
|
|
11437
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);
|
|
11438
11600
|
case "symlink":
|
|
11439
11601
|
if (!h.symlink) throw fsError2("EPERM", "this file system has no symlinks");
|
|
11440
11602
|
return h.symlink(req.target, req.path);
|
|
@@ -12620,8 +12782,8 @@ var ChunkedBytes = class {
|
|
|
12620
12782
|
}
|
|
12621
12783
|
return out;
|
|
12622
12784
|
}
|
|
12623
|
-
write(offset, bytes,
|
|
12624
|
-
const whole =
|
|
12785
|
+
write(offset, bytes, owned2 = false) {
|
|
12786
|
+
const whole = owned2 && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
|
|
12625
12787
|
for (let done4 = 0; done4 < bytes.length; ) {
|
|
12626
12788
|
const at = offset + done4;
|
|
12627
12789
|
const index = Math.floor(at / CHUNK3);
|
|
@@ -12663,12 +12825,17 @@ function tmpfs() {
|
|
|
12663
12825
|
mtime: Date.now(),
|
|
12664
12826
|
data: new ChunkedBytes(),
|
|
12665
12827
|
target: "",
|
|
12666
|
-
children: /* @__PURE__ */ new Map()
|
|
12828
|
+
children: /* @__PURE__ */ new Map(),
|
|
12829
|
+
links: 1
|
|
12667
12830
|
});
|
|
12668
12831
|
const root = make("directory", 16877);
|
|
12669
12832
|
const handles = /* @__PURE__ */ new Map();
|
|
12670
12833
|
let nextFh = 0;
|
|
12671
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
|
+
};
|
|
12672
12839
|
function find(path) {
|
|
12673
12840
|
let node = root;
|
|
12674
12841
|
for (const name2 of parts(path)) {
|
|
@@ -12692,7 +12859,8 @@ function tmpfs() {
|
|
|
12692
12859
|
size: node.kind === "file" ? node.data.size : node.kind === "symlink" ? node.target.length : 4096,
|
|
12693
12860
|
mtime: node.mtime,
|
|
12694
12861
|
mode: node.mode,
|
|
12695
|
-
ino: node.ino
|
|
12862
|
+
ino: node.ino,
|
|
12863
|
+
...node.kind === "directory" ? {} : { nlink: node.links }
|
|
12696
12864
|
});
|
|
12697
12865
|
const handle = (fh) => {
|
|
12698
12866
|
const found = handles.get(fh);
|
|
@@ -12707,8 +12875,7 @@ function tmpfs() {
|
|
|
12707
12875
|
if (existing) return existing;
|
|
12708
12876
|
if (!flags.create) throw fsError2("ENOENT", path);
|
|
12709
12877
|
const node = make("file", 33188);
|
|
12710
|
-
dir
|
|
12711
|
-
dir.mtime = Date.now();
|
|
12878
|
+
place(dir, name2, node);
|
|
12712
12879
|
return node;
|
|
12713
12880
|
}
|
|
12714
12881
|
return {
|
|
@@ -12747,8 +12914,7 @@ function tmpfs() {
|
|
|
12747
12914
|
async mkdir(path) {
|
|
12748
12915
|
const { dir, name: name2 } = parent(path);
|
|
12749
12916
|
if (dir.children.has(name2)) throw fsError2("EEXIST", path);
|
|
12750
|
-
dir
|
|
12751
|
-
dir.mtime = Date.now();
|
|
12917
|
+
place(dir, name2, make("directory", 16877));
|
|
12752
12918
|
},
|
|
12753
12919
|
async rmdir(path) {
|
|
12754
12920
|
const { dir, name: name2 } = parent(path);
|
|
@@ -12765,6 +12931,7 @@ function tmpfs() {
|
|
|
12765
12931
|
if (!node) throw fsError2("ENOENT", path);
|
|
12766
12932
|
if (node.kind === "directory") throw fsError2("EISDIR", path);
|
|
12767
12933
|
dir.children.delete(name2);
|
|
12934
|
+
node.links--;
|
|
12768
12935
|
dir.mtime = Date.now();
|
|
12769
12936
|
},
|
|
12770
12937
|
async rename(from, to) {
|
|
@@ -12781,17 +12948,25 @@ function tmpfs() {
|
|
|
12781
12948
|
throw fsError2(node.kind === "directory" ? "ENOTEMPTY" : "EISDIR", to);
|
|
12782
12949
|
}
|
|
12783
12950
|
source.dir.children.delete(source.name);
|
|
12951
|
+
if (replaced) replaced.links--;
|
|
12784
12952
|
target.dir.children.set(target.name, node);
|
|
12785
12953
|
source.dir.mtime = Date.now();
|
|
12786
12954
|
target.dir.mtime = source.dir.mtime;
|
|
12787
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
|
+
},
|
|
12788
12964
|
async symlink(target, path) {
|
|
12789
12965
|
const { dir, name: name2 } = parent(path);
|
|
12790
12966
|
if (dir.children.has(name2)) throw fsError2("EEXIST", path);
|
|
12791
12967
|
const node = make("symlink", 41471);
|
|
12792
12968
|
node.target = target;
|
|
12793
|
-
dir
|
|
12794
|
-
dir.mtime = Date.now();
|
|
12969
|
+
place(dir, name2, node);
|
|
12795
12970
|
},
|
|
12796
12971
|
async readlink(path) {
|
|
12797
12972
|
const node = find(path);
|
|
@@ -13691,7 +13866,7 @@ var Launcher = class {
|
|
|
13691
13866
|
serveFilesystem(
|
|
13692
13867
|
port2,
|
|
13693
13868
|
tmpfs(),
|
|
13694
|
-
{ symlinks: true, chmod: true, linkTimes: true, ranges: true, attrTtl: 0 },
|
|
13869
|
+
{ symlinks: true, links: true, chmod: true, linkTimes: true, ranges: true, attrTtl: 0 },
|
|
13695
13870
|
{ owned: true }
|
|
13696
13871
|
);
|
|
13697
13872
|
return {
|