@poe-platform/safe-fs 0.1.682 → 0.1.684
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.
|
@@ -37,11 +37,12 @@ export class PythonFileSystem {
|
|
|
37
37
|
return Promise.reject(new FsError("EBADF"));
|
|
38
38
|
let request;
|
|
39
39
|
try {
|
|
40
|
-
this.#scope.signal.throwIfAborted();
|
|
41
40
|
request = parsePythonFsRequest(value, this.#transfer);
|
|
41
|
+
if (request.op !== "close")
|
|
42
|
+
this.#scope.signal.throwIfAborted();
|
|
42
43
|
}
|
|
43
44
|
catch (error) {
|
|
44
|
-
return Promise.reject(error);
|
|
45
|
+
return Promise.reject(this.#scope.signal.aborted ? this.#scope.signal.reason : error);
|
|
45
46
|
}
|
|
46
47
|
const operation = this.#execute(request);
|
|
47
48
|
this.#pending.add(operation);
|
|
@@ -67,6 +68,13 @@ export class PythonFileSystem {
|
|
|
67
68
|
throw new FsError("EFBIG", { message: "Python filesystem transfer exceeds configured limit" });
|
|
68
69
|
}
|
|
69
70
|
async #execute(request) {
|
|
71
|
+
if (request.op === "close") {
|
|
72
|
+
const [id] = request.args;
|
|
73
|
+
const handle = this.#handle(id);
|
|
74
|
+
this.#handles.delete(id);
|
|
75
|
+
await handle.close();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
70
78
|
const signal = this.#scope.signal;
|
|
71
79
|
signal.throwIfAborted();
|
|
72
80
|
const options = { signal };
|
|
@@ -109,13 +117,6 @@ export class PythonFileSystem {
|
|
|
109
117
|
this.#acquiring--;
|
|
110
118
|
}
|
|
111
119
|
}
|
|
112
|
-
case "close": {
|
|
113
|
-
const [id] = request.args;
|
|
114
|
-
const handle = this.#handle(id);
|
|
115
|
-
this.#handles.delete(id);
|
|
116
|
-
await handle.close();
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
120
|
case "descriptorCapabilities": return { ...this.#handle(request.args[0]).capabilities };
|
|
120
121
|
case "fstat": return this.#handle(request.args[0]).stat(options);
|
|
121
122
|
case "position": {
|
|
@@ -5,3 +5,5 @@ export { mountPythonFileSystem } from "./emscripten.js";
|
|
|
5
5
|
export type { PythonMountOptions } from "./emscripten.js";
|
|
6
6
|
export { PythonStatTranslator } from "./stat.js";
|
|
7
7
|
export type { PythonWireStat } from "./stat.js";
|
|
8
|
+
export { createPythonNativeSyscalls } from './native.js';
|
|
9
|
+
export type { PythonNativeSyscallOptions } from './native.js';
|
|
@@ -2,3 +2,4 @@ export { PythonFileSystem } from "./filesystem.js";
|
|
|
2
2
|
export { translatePythonOpenFlags } from "./flags.js";
|
|
3
3
|
export { mountPythonFileSystem } from "./emscripten.js";
|
|
4
4
|
export { PythonStatTranslator } from "./stat.js";
|
|
5
|
+
export { createPythonNativeSyscalls } from './native.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface PythonNativeSyscallOptions {
|
|
2
|
+
readonly runtime: any;
|
|
3
|
+
readonly dispatch: (request: {
|
|
4
|
+
op: string;
|
|
5
|
+
args: unknown[];
|
|
6
|
+
}) => Promise<unknown>;
|
|
7
|
+
readonly signal: AbortSignal;
|
|
8
|
+
readonly cwd: string;
|
|
9
|
+
readonly runtimeMount: string;
|
|
10
|
+
readonly maxTransferBytes: number;
|
|
11
|
+
readonly original?: (name: string, args: readonly (number | bigint)[]) => number;
|
|
12
|
+
}
|
|
13
|
+
export declare function createPythonNativeSyscalls(options: PythonNativeSyscallOptions): {
|
|
14
|
+
invoke(name: string, args: readonly (number | bigint)[]): Promise<number>;
|
|
15
|
+
metadata(path: string | number, follow: boolean): Promise<Record<string, any>>;
|
|
16
|
+
close(): Promise<void>;
|
|
17
|
+
};
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import { translatePythonOpenFlags } from './flags.js';
|
|
2
|
+
import { writePythonStat } from './emscripten.js';
|
|
3
|
+
const errno = {
|
|
4
|
+
EACCES: 2, EAGAIN: 6, EBADF: 8, EBUSY: 10, ECANCELED: 11, EEXIST: 20, EFBIG: 22,
|
|
5
|
+
EINTR: 27, EINVAL: 28, EIO: 29, EISDIR: 31, ELOOP: 32, EMFILE: 33,
|
|
6
|
+
ENAMETOOLONG: 37, ENFILE: 41, ENOENT: 44, ENOMEM: 48, ENOSPC: 51,
|
|
7
|
+
ENOSYS: 52, ENOTSUP: 138, EOPNOTSUPP: 138, ENOTDIR: 54, ENOTEMPTY: 55, ENOTTY: 59,
|
|
8
|
+
EOVERFLOW: 61, EPERM: 63, EPIPE: 64, ERANGE: 68, EROFS: 69, ESPIPE: 70,
|
|
9
|
+
ETIMEDOUT: 73, EXDEV: 75,
|
|
10
|
+
};
|
|
11
|
+
export function createPythonNativeSyscalls(options) {
|
|
12
|
+
const { runtime, signal, maxTransferBytes, runtimeMount } = options;
|
|
13
|
+
if (!Number.isSafeInteger(maxTransferBytes) || maxTransferBytes < 1)
|
|
14
|
+
throw new RangeError('Invalid native transfer limit');
|
|
15
|
+
const descriptors = new Map();
|
|
16
|
+
const closedStdio = new Set();
|
|
17
|
+
let cwd = options.cwd;
|
|
18
|
+
let closing;
|
|
19
|
+
let pending;
|
|
20
|
+
const fail = (code) => { throw { code }; };
|
|
21
|
+
const heap = () => runtime.HEAPU8;
|
|
22
|
+
const view = () => new DataView(heap().buffer, heap().byteOffset, heap().byteLength);
|
|
23
|
+
const range = (pointer, length) => {
|
|
24
|
+
if (!Number.isSafeInteger(pointer) || !Number.isSafeInteger(length) || pointer < 0 || length < 0 || pointer + length > heap().byteLength)
|
|
25
|
+
fail('EINVAL');
|
|
26
|
+
};
|
|
27
|
+
const text = (pointer) => {
|
|
28
|
+
range(pointer, 1);
|
|
29
|
+
const end = heap().indexOf(0, pointer);
|
|
30
|
+
if (end < 0)
|
|
31
|
+
fail('EINVAL');
|
|
32
|
+
try {
|
|
33
|
+
return new TextDecoder('utf-8', { fatal: true }).decode(heap().subarray(pointer, end));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return fail('EINVAL');
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const integer = (value) => {
|
|
40
|
+
const number = Number(value);
|
|
41
|
+
if (!Number.isSafeInteger(number))
|
|
42
|
+
fail('EOVERFLOW');
|
|
43
|
+
return number;
|
|
44
|
+
};
|
|
45
|
+
const descriptor = (fd) => descriptors.get(fd) ?? fail('EBADF');
|
|
46
|
+
const pathAt = (fd, pointer, allowEmpty = false) => {
|
|
47
|
+
const path = text(pointer);
|
|
48
|
+
if (!path && !allowEmpty)
|
|
49
|
+
fail('ENOENT');
|
|
50
|
+
if (path.startsWith('/'))
|
|
51
|
+
return path;
|
|
52
|
+
const base = fd === -100 ? cwd : descriptor(fd).path;
|
|
53
|
+
if (fd !== -100 && path)
|
|
54
|
+
fail('ENOTDIR');
|
|
55
|
+
return path ? base + '/' + path : base;
|
|
56
|
+
};
|
|
57
|
+
const privatePath = (path) => {
|
|
58
|
+
const parts = [];
|
|
59
|
+
for (const part of path.split('/')) {
|
|
60
|
+
if (part === '..')
|
|
61
|
+
parts.pop();
|
|
62
|
+
else if (part && part !== '.')
|
|
63
|
+
parts.push(part);
|
|
64
|
+
}
|
|
65
|
+
const normalized = '/' + parts.join('/');
|
|
66
|
+
const privateNamespace = normalized === runtimeMount || normalized.startsWith(runtimeMount + '/');
|
|
67
|
+
if ((path === runtimeMount || path.startsWith(runtimeMount + '/')) && !privateNamespace)
|
|
68
|
+
fail('EACCES');
|
|
69
|
+
return privateNamespace;
|
|
70
|
+
};
|
|
71
|
+
const request = async (op, ...args) => {
|
|
72
|
+
signal.throwIfAborted();
|
|
73
|
+
const result = await options.dispatch({ op, args });
|
|
74
|
+
signal.throwIfAborted();
|
|
75
|
+
return result;
|
|
76
|
+
};
|
|
77
|
+
const original = (name, args) => options.original?.(name, args) ?? fail('ENOSYS');
|
|
78
|
+
const privateDescriptor = (fd) => {
|
|
79
|
+
const stream = runtime.FS.getStream?.(fd);
|
|
80
|
+
return !descriptors.has(fd) && typeof stream?.path === 'string' && privatePath(stream.path);
|
|
81
|
+
};
|
|
82
|
+
const allocate = (entry) => {
|
|
83
|
+
const refuse = () => fail('ENOTSUP');
|
|
84
|
+
const stream = runtime.FS.createStream({ path: entry.path, flags: entry.flags,
|
|
85
|
+
seekable: entry.seekable, position: entry.position,
|
|
86
|
+
node: { mode: 0o100000 }, stream_ops: { read: refuse, write: refuse, close: refuse } });
|
|
87
|
+
descriptors.set(stream.fd, entry);
|
|
88
|
+
return stream.fd;
|
|
89
|
+
};
|
|
90
|
+
const closeDescriptor = async (fd) => {
|
|
91
|
+
const entry = descriptor(fd);
|
|
92
|
+
descriptors.delete(fd);
|
|
93
|
+
runtime.FS.closeStream(fd);
|
|
94
|
+
if (--entry.references === 0)
|
|
95
|
+
await options.dispatch({ op: 'close', args: [entry.handle] });
|
|
96
|
+
};
|
|
97
|
+
const project = (value) => {
|
|
98
|
+
const type = value.type === 'directory' ? 0o40000 : value.type === 'symlink' ? 0o120000 : value.type === 'character' ? 0o20000 : 0o100000;
|
|
99
|
+
return { ...value, mode: value.mode | type, rdev: value.type === 'character' ? undefined : 0,
|
|
100
|
+
blksize: value.ioBlockSize ?? value.preferredIoBlockSize,
|
|
101
|
+
blocks: value.allocatedBytes === undefined ? undefined : Math.ceil(value.allocatedBytes / 512) };
|
|
102
|
+
};
|
|
103
|
+
const stat = (pointer, value) => {
|
|
104
|
+
try {
|
|
105
|
+
return writePythonStat(heap(), pointer, project(value));
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return fail('EOVERFLOW');
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
const transfer = async (name, args) => {
|
|
112
|
+
const [fd, vectors, count] = args.map(integer);
|
|
113
|
+
const positioned = name === 'fd_pread' || name === 'fd_pwrite';
|
|
114
|
+
const output = integer(args[positioned ? 4 : 3]);
|
|
115
|
+
const writing = name === 'fd_write' || name === 'fd_pwrite';
|
|
116
|
+
let position = positioned ? integer(args[3]) : null;
|
|
117
|
+
range(vectors, count * 8);
|
|
118
|
+
range(output, 4);
|
|
119
|
+
if (position !== null && position < 0)
|
|
120
|
+
fail('EINVAL');
|
|
121
|
+
const entry = fd > 2 ? descriptor(fd) : undefined;
|
|
122
|
+
if (closedStdio.has(fd))
|
|
123
|
+
fail('EBADF');
|
|
124
|
+
if (!entry && (fd < 0 || positioned || (writing ? fd === 0 : fd !== 0)))
|
|
125
|
+
fail('EBADF');
|
|
126
|
+
if (entry && (entry.flags & 3) === (writing ? 0 : 1))
|
|
127
|
+
fail('EBADF');
|
|
128
|
+
if (entry && !positioned && (writing ? entry.capabilities.positionedWrite && !(entry.flags & 1024) : entry.capabilities.positionedRead))
|
|
129
|
+
position = entry.position;
|
|
130
|
+
let total = 0;
|
|
131
|
+
for (let index = 0; index < count; index++) {
|
|
132
|
+
const pointer = view().getUint32(vectors + index * 8, true);
|
|
133
|
+
const length = view().getUint32(vectors + index * 8 + 4, true);
|
|
134
|
+
range(pointer, length);
|
|
135
|
+
let offset = 0;
|
|
136
|
+
while (offset < length) {
|
|
137
|
+
const size = Math.min(length - offset, maxTransferBytes);
|
|
138
|
+
let transferred;
|
|
139
|
+
try {
|
|
140
|
+
if (writing) {
|
|
141
|
+
const bytes = Uint8Array.from(heap().subarray(pointer + offset, pointer + offset + size));
|
|
142
|
+
transferred = entry ? await request('write', entry.handle, bytes, position)
|
|
143
|
+
: (await request(fd === 1 ? 'stdout' : 'stderr', Array.from(bytes)), size);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
const bytes = entry ? await request('read', entry.handle, size, position) : await request('stdin', size);
|
|
147
|
+
if (!(bytes instanceof Uint8Array) && !Array.isArray(bytes) || bytes.length > size)
|
|
148
|
+
fail('EIO');
|
|
149
|
+
heap().set(bytes, pointer + offset);
|
|
150
|
+
transferred = bytes.length;
|
|
151
|
+
}
|
|
152
|
+
if (!Number.isSafeInteger(transferred) || transferred < 0 || transferred > size)
|
|
153
|
+
fail('EIO');
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
if (!total)
|
|
157
|
+
throw error;
|
|
158
|
+
view().setUint32(output, total, true);
|
|
159
|
+
return 0;
|
|
160
|
+
}
|
|
161
|
+
total += transferred;
|
|
162
|
+
offset += transferred;
|
|
163
|
+
if (position !== null)
|
|
164
|
+
position += transferred;
|
|
165
|
+
if (entry && !positioned)
|
|
166
|
+
entry.position = writing && entry.flags & 1024
|
|
167
|
+
? await request('position', entry.handle) : entry.position + transferred;
|
|
168
|
+
if (transferred < size || !writing) {
|
|
169
|
+
view().setUint32(output, total, true);
|
|
170
|
+
return 0;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
view().setUint32(output, total, true);
|
|
175
|
+
return 0;
|
|
176
|
+
};
|
|
177
|
+
const execute = async (name, args) => {
|
|
178
|
+
signal.throwIfAborted();
|
|
179
|
+
const first = integer(args[0] ?? 0);
|
|
180
|
+
const second = integer(args[1] ?? 0);
|
|
181
|
+
const third = integer(args[2] ?? 0);
|
|
182
|
+
const fourth = integer(args[3] ?? 0);
|
|
183
|
+
if (name.startsWith('fd_') && privateDescriptor(first)) {
|
|
184
|
+
if (name === 'fd_write' || name === 'fd_pwrite')
|
|
185
|
+
fail('EROFS');
|
|
186
|
+
return original(name, args);
|
|
187
|
+
}
|
|
188
|
+
switch (name) {
|
|
189
|
+
case '__syscall_openat': {
|
|
190
|
+
const path = pathAt(first, second);
|
|
191
|
+
if (privatePath(path)) {
|
|
192
|
+
if ((third & 3) !== 0 || (third & (64 | 512)))
|
|
193
|
+
fail('EROFS');
|
|
194
|
+
return original(name, args);
|
|
195
|
+
}
|
|
196
|
+
if (fourth)
|
|
197
|
+
range(fourth, 4);
|
|
198
|
+
const openOptions = translatePythonOpenFlags(third, fourth ? view().getUint32(fourth, true) : 0o666);
|
|
199
|
+
const handle = await options.dispatch({ op: 'open', args: [path, openOptions] });
|
|
200
|
+
try {
|
|
201
|
+
signal.throwIfAborted();
|
|
202
|
+
const capabilities = await request('descriptorCapabilities', handle);
|
|
203
|
+
if ((third & 1024) && !capabilities.position)
|
|
204
|
+
fail('ENOTSUP');
|
|
205
|
+
const access = third & 3;
|
|
206
|
+
const seekable = (access === 1 || capabilities.positionedRead === true)
|
|
207
|
+
&& (access === 0 || ((third & 1024) ? capabilities.position === true : capabilities.positionedWrite === true));
|
|
208
|
+
return allocate({ handle, path, flags: third, capabilities, seekable, position: 0, references: 1 });
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
await options.dispatch({ op: 'close', args: [handle] }).catch(() => { });
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
case 'fd_read':
|
|
216
|
+
case 'fd_pread':
|
|
217
|
+
case 'fd_write':
|
|
218
|
+
case 'fd_pwrite': return transfer(name, args);
|
|
219
|
+
case 'fd_close':
|
|
220
|
+
if (first <= 2) {
|
|
221
|
+
if (first < 0 || closedStdio.has(first))
|
|
222
|
+
fail('EBADF');
|
|
223
|
+
const status = options.original?.(name, args) ?? 0;
|
|
224
|
+
if (status === 0)
|
|
225
|
+
closedStdio.add(first);
|
|
226
|
+
return status;
|
|
227
|
+
}
|
|
228
|
+
await closeDescriptor(first);
|
|
229
|
+
return 0;
|
|
230
|
+
case 'fd_seek': {
|
|
231
|
+
if (first < 0 || closedStdio.has(first))
|
|
232
|
+
fail('EBADF');
|
|
233
|
+
if (first <= 2)
|
|
234
|
+
fail('ESPIPE');
|
|
235
|
+
const entry = descriptor(first);
|
|
236
|
+
if (!entry.seekable)
|
|
237
|
+
fail('ESPIPE');
|
|
238
|
+
if (third < 0 || third > 2)
|
|
239
|
+
fail('EINVAL');
|
|
240
|
+
const position = second + (third === 0 ? 0 : third === 1 ? entry.position : (await request('fstat', entry.handle)).size);
|
|
241
|
+
if (!Number.isSafeInteger(position) || position < 0)
|
|
242
|
+
fail('EINVAL');
|
|
243
|
+
range(fourth, 8);
|
|
244
|
+
entry.position = position;
|
|
245
|
+
view().setBigInt64(fourth, BigInt(position), true);
|
|
246
|
+
return 0;
|
|
247
|
+
}
|
|
248
|
+
case 'fd_sync':
|
|
249
|
+
case '__syscall_fdatasync':
|
|
250
|
+
await request('sync', descriptor(first).handle, name === '__syscall_fdatasync');
|
|
251
|
+
return 0;
|
|
252
|
+
case '__syscall_fstat64':
|
|
253
|
+
if (first < 0 || closedStdio.has(first))
|
|
254
|
+
fail('EBADF');
|
|
255
|
+
if (first <= 2 || privateDescriptor(first))
|
|
256
|
+
return original(name, args);
|
|
257
|
+
return stat(second, await request('fstat', descriptor(first).handle));
|
|
258
|
+
case '__syscall_stat64':
|
|
259
|
+
case '__syscall_lstat64': {
|
|
260
|
+
const path = pathAt(-100, first);
|
|
261
|
+
if (privatePath(path))
|
|
262
|
+
return original(name, args);
|
|
263
|
+
return stat(second, await request(name === '__syscall_stat64' ? 'stat' : 'lstat', path));
|
|
264
|
+
}
|
|
265
|
+
case '__syscall_newfstatat': {
|
|
266
|
+
if (fourth & ~(256 | 4096))
|
|
267
|
+
fail('EINVAL');
|
|
268
|
+
const path = pathAt(first, second, Boolean(fourth & 4096));
|
|
269
|
+
if (privatePath(path))
|
|
270
|
+
return original(name, args);
|
|
271
|
+
return stat(third, await request(fourth & 256 ? 'lstat' : 'stat', path));
|
|
272
|
+
}
|
|
273
|
+
case '__syscall_getcwd': {
|
|
274
|
+
const bytes = new TextEncoder().encode(cwd + '\0');
|
|
275
|
+
if (!second)
|
|
276
|
+
fail('EINVAL');
|
|
277
|
+
if (bytes.length > second)
|
|
278
|
+
fail('ERANGE');
|
|
279
|
+
range(first, bytes.length);
|
|
280
|
+
heap().set(bytes, first);
|
|
281
|
+
return bytes.length;
|
|
282
|
+
}
|
|
283
|
+
case '__syscall_chdir': {
|
|
284
|
+
const path = pathAt(-100, first);
|
|
285
|
+
if (privatePath(path))
|
|
286
|
+
fail('ENOTSUP');
|
|
287
|
+
const resolved = await request('realpath', path);
|
|
288
|
+
if ((await request('stat', resolved)).type !== 'directory')
|
|
289
|
+
fail('ENOTDIR');
|
|
290
|
+
cwd = resolved;
|
|
291
|
+
runtime.FS.currentPath = cwd;
|
|
292
|
+
return 0;
|
|
293
|
+
}
|
|
294
|
+
case '__syscall_ioctl':
|
|
295
|
+
if (first < 0 || closedStdio.has(first))
|
|
296
|
+
fail('EBADF');
|
|
297
|
+
if (first > 2 && !privateDescriptor(first))
|
|
298
|
+
descriptor(first);
|
|
299
|
+
return fail('ENOTTY');
|
|
300
|
+
case '__syscall_fcntl64': {
|
|
301
|
+
if (first < 0 || closedStdio.has(first))
|
|
302
|
+
fail('EBADF');
|
|
303
|
+
if (first <= 2 || privateDescriptor(first))
|
|
304
|
+
return original(name, args);
|
|
305
|
+
const entry = descriptor(first);
|
|
306
|
+
if (second === 1 || second === 2)
|
|
307
|
+
return 0;
|
|
308
|
+
if (second === 3)
|
|
309
|
+
return entry.flags;
|
|
310
|
+
return fail('ENOTSUP');
|
|
311
|
+
}
|
|
312
|
+
case '__syscall_dup': {
|
|
313
|
+
const entry = descriptor(first);
|
|
314
|
+
const fd = allocate(entry);
|
|
315
|
+
entry.references++;
|
|
316
|
+
return fd;
|
|
317
|
+
}
|
|
318
|
+
case '__syscall_ftruncate64':
|
|
319
|
+
await request('ftruncate', descriptor(first).handle, second);
|
|
320
|
+
return 0;
|
|
321
|
+
case '__syscall_getdents64':
|
|
322
|
+
if (privateDescriptor(first))
|
|
323
|
+
return original(name, args);
|
|
324
|
+
descriptor(first);
|
|
325
|
+
return fail('ENOTSUP');
|
|
326
|
+
case '__syscall_faccessat': {
|
|
327
|
+
if (fourth !== 0)
|
|
328
|
+
fail('ENOTSUP');
|
|
329
|
+
const path = pathAt(first, second);
|
|
330
|
+
if (privatePath(path))
|
|
331
|
+
return third & 2 ? fail('EROFS') : original(name, args);
|
|
332
|
+
await request('access', path, third);
|
|
333
|
+
return 0;
|
|
334
|
+
}
|
|
335
|
+
case '__syscall_mkdirat':
|
|
336
|
+
case '__syscall_unlinkat': {
|
|
337
|
+
const path = pathAt(first, second);
|
|
338
|
+
if (privatePath(path))
|
|
339
|
+
fail('EROFS');
|
|
340
|
+
if (name === '__syscall_mkdirat')
|
|
341
|
+
await request('mkdir', path, { mode: third & 0o7777 });
|
|
342
|
+
else {
|
|
343
|
+
if (third !== 0 && third !== 512)
|
|
344
|
+
fail('EINVAL');
|
|
345
|
+
await request(third === 512 ? 'rmdir' : 'rm', path);
|
|
346
|
+
}
|
|
347
|
+
return 0;
|
|
348
|
+
}
|
|
349
|
+
case '__syscall_rmdir':
|
|
350
|
+
case '__syscall_chmod':
|
|
351
|
+
case '__syscall_truncate64': {
|
|
352
|
+
const path = pathAt(-100, first);
|
|
353
|
+
if (privatePath(path))
|
|
354
|
+
fail('EROFS');
|
|
355
|
+
if (name === '__syscall_rmdir')
|
|
356
|
+
await request('rmdir', path);
|
|
357
|
+
else
|
|
358
|
+
await request(name === '__syscall_chmod' ? 'chmod' : 'truncate', path, second);
|
|
359
|
+
return 0;
|
|
360
|
+
}
|
|
361
|
+
case '__syscall_renameat': {
|
|
362
|
+
const source = pathAt(first, second);
|
|
363
|
+
const destination = pathAt(third, fourth);
|
|
364
|
+
if (privatePath(source) || privatePath(destination))
|
|
365
|
+
fail('EROFS');
|
|
366
|
+
await request('rename', source, destination);
|
|
367
|
+
return 0;
|
|
368
|
+
}
|
|
369
|
+
case '__syscall_symlinkat': {
|
|
370
|
+
const destination = pathAt(second, third);
|
|
371
|
+
if (privatePath(destination))
|
|
372
|
+
fail('EROFS');
|
|
373
|
+
await request('symlink', text(first), destination);
|
|
374
|
+
return 0;
|
|
375
|
+
}
|
|
376
|
+
case '__syscall_readlinkat': {
|
|
377
|
+
const path = pathAt(first, second);
|
|
378
|
+
if (privatePath(path))
|
|
379
|
+
return original(name, args);
|
|
380
|
+
if (fourth < 1)
|
|
381
|
+
fail('EINVAL');
|
|
382
|
+
range(third, fourth);
|
|
383
|
+
const bytes = new TextEncoder().encode(await request('readlink', path)).subarray(0, fourth);
|
|
384
|
+
heap().set(bytes, third);
|
|
385
|
+
return bytes.length;
|
|
386
|
+
}
|
|
387
|
+
default: return fail('ENOSYS');
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
return {
|
|
391
|
+
async metadata(path, follow) {
|
|
392
|
+
if (closing || pending)
|
|
393
|
+
fail('EBUSY');
|
|
394
|
+
const operation = (async () => {
|
|
395
|
+
signal.throwIfAborted();
|
|
396
|
+
if (path === '')
|
|
397
|
+
fail('ENOENT');
|
|
398
|
+
if (typeof path === 'number' && (path < 0 || closedStdio.has(path)))
|
|
399
|
+
fail('EBADF');
|
|
400
|
+
if (typeof path === 'number' && (path <= 2 || privateDescriptor(path)) || typeof path === 'string' && privatePath(path.startsWith('/') ? path : cwd + '/' + path)) {
|
|
401
|
+
const value = typeof path === 'number' ? runtime.FS.fstat(path) : follow ? runtime.FS.stat(path) : runtime.FS.lstat(path);
|
|
402
|
+
return { ...value, atimeMs: value.atime.getTime(), mtimeMs: value.mtime.getTime(), ctimeMs: value.ctime.getTime() };
|
|
403
|
+
}
|
|
404
|
+
const value = typeof path === 'number' ? await request('fstat', descriptor(path).handle)
|
|
405
|
+
: await request(follow ? 'stat' : 'lstat', path.startsWith('/') ? path : cwd + '/' + path);
|
|
406
|
+
return project(value);
|
|
407
|
+
})();
|
|
408
|
+
pending = operation;
|
|
409
|
+
try {
|
|
410
|
+
return await operation;
|
|
411
|
+
}
|
|
412
|
+
finally {
|
|
413
|
+
pending = undefined;
|
|
414
|
+
}
|
|
415
|
+
},
|
|
416
|
+
invoke(name, args) {
|
|
417
|
+
if (closing || pending)
|
|
418
|
+
return Promise.resolve((name.startsWith('fd_') ? 1 : -1) * errno.EBUSY);
|
|
419
|
+
const operation = execute(name, args).catch(error => {
|
|
420
|
+
const code = signal.aborted ? errno.ECANCELED : errno[error?.code ?? ''] ?? errno.EIO;
|
|
421
|
+
return (name.startsWith('fd_') ? 1 : -1) * code;
|
|
422
|
+
});
|
|
423
|
+
pending = operation;
|
|
424
|
+
void operation.then(() => { pending = undefined; });
|
|
425
|
+
return operation;
|
|
426
|
+
},
|
|
427
|
+
close() {
|
|
428
|
+
closing ??= (async () => {
|
|
429
|
+
await pending?.catch(() => { });
|
|
430
|
+
const failures = [];
|
|
431
|
+
for (const fd of [...descriptors.keys()]) {
|
|
432
|
+
try {
|
|
433
|
+
await closeDescriptor(fd);
|
|
434
|
+
}
|
|
435
|
+
catch (error) {
|
|
436
|
+
failures.push(error);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (failures.length)
|
|
440
|
+
throw new AggregateError(failures, 'Native Python descriptor cleanup failed');
|
|
441
|
+
})();
|
|
442
|
+
return closing;
|
|
443
|
+
},
|
|
444
|
+
};
|
|
445
|
+
}
|