@zenfs/core 0.9.7 → 0.10.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/dist/backends/AsyncStore.js +29 -29
- package/dist/backends/Fetch.d.ts +84 -0
- package/dist/backends/Fetch.js +171 -0
- package/dist/backends/Index.js +19 -19
- package/dist/backends/Locked.d.ts +11 -11
- package/dist/backends/Locked.js +50 -49
- package/dist/backends/Overlay.js +21 -21
- package/dist/backends/SyncStore.js +27 -27
- package/dist/backends/backend.js +4 -4
- package/dist/backends/port/fs.d.ts +124 -0
- package/dist/backends/port/fs.js +241 -0
- package/dist/backends/port/rpc.d.ts +60 -0
- package/dist/backends/port/rpc.js +71 -0
- package/dist/backends/port/store.d.ts +30 -0
- package/dist/backends/port/store.js +142 -0
- package/dist/browser.min.js +4 -4
- package/dist/browser.min.js.map +4 -4
- package/dist/config.d.ts +8 -10
- package/dist/config.js +11 -11
- package/dist/emulation/async.js +6 -6
- package/dist/emulation/dir.js +2 -2
- package/dist/emulation/index.d.ts +1 -1
- package/dist/emulation/index.js +1 -1
- package/dist/emulation/path.d.ts +3 -2
- package/dist/emulation/path.js +19 -45
- package/dist/emulation/promises.d.ts +7 -12
- package/dist/emulation/promises.js +144 -146
- package/dist/emulation/shared.d.ts +5 -10
- package/dist/emulation/shared.js +8 -8
- package/dist/emulation/streams.js +3 -3
- package/dist/emulation/sync.js +25 -25
- package/dist/{ApiError.d.ts → error.d.ts} +13 -14
- package/dist/error.js +292 -0
- package/dist/file.d.ts +2 -0
- package/dist/file.js +10 -4
- package/dist/filesystem.js +15 -15
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/mutex.js +2 -1
- package/dist/utils.d.ts +8 -7
- package/dist/utils.js +11 -12
- package/package.json +3 -3
- package/readme.md +17 -9
- package/src/backends/AsyncStore.ts +29 -29
- package/src/backends/Fetch.ts +230 -0
- package/src/backends/Index.ts +19 -19
- package/src/backends/Locked.ts +50 -49
- package/src/backends/Overlay.ts +23 -23
- package/src/backends/SyncStore.ts +27 -27
- package/src/backends/backend.ts +6 -6
- package/src/backends/port/fs.ts +308 -0
- package/src/backends/port/readme.md +59 -0
- package/src/backends/port/rpc.ts +144 -0
- package/src/backends/port/store.ts +187 -0
- package/src/config.ts +20 -24
- package/src/emulation/async.ts +6 -6
- package/src/emulation/dir.ts +2 -2
- package/src/emulation/index.ts +1 -1
- package/src/emulation/path.ts +25 -49
- package/src/emulation/promises.ts +150 -159
- package/src/emulation/shared.ts +12 -14
- package/src/emulation/streams.ts +3 -3
- package/src/emulation/sync.ts +28 -28
- package/src/{ApiError.ts → error.ts} +89 -89
- package/src/file.ts +12 -4
- package/src/filesystem.ts +15 -15
- package/src/index.ts +4 -1
- package/src/mutex.ts +3 -1
- package/src/utils.ts +16 -18
- package/tsconfig.json +2 -2
- package/dist/ApiError.js +0 -292
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Buffer } from 'buffer';
|
|
2
|
-
import {
|
|
2
|
+
import { ErrnoError, Errno } from '../error.js';
|
|
3
3
|
import { ActionType, isAppendable, isReadable, isWriteable, parseFlag, pathExistsAction, pathNotExistsAction } from '../file.js';
|
|
4
4
|
import { BigIntStats, FileType } from '../stats.js';
|
|
5
5
|
import { normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
@@ -10,18 +10,10 @@ import { cred, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from '.
|
|
|
10
10
|
import { ReadStream, WriteStream } from './streams.js';
|
|
11
11
|
export * as constants from './constants.js';
|
|
12
12
|
export class FileHandle {
|
|
13
|
-
constructor(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
fd) {
|
|
18
|
-
this.fd = fd;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* @internal
|
|
22
|
-
*/
|
|
23
|
-
get file() {
|
|
24
|
-
return fd2file(this.fd);
|
|
13
|
+
constructor(fdOrFile) {
|
|
14
|
+
const isFile = typeof fdOrFile != 'number';
|
|
15
|
+
this.fd = isFile ? file2fd(fdOrFile) : fdOrFile;
|
|
16
|
+
this.file = isFile ? fdOrFile : fd2file(fdOrFile);
|
|
25
17
|
}
|
|
26
18
|
/**
|
|
27
19
|
* Asynchronous fchown(2) - Change ownership of a file.
|
|
@@ -36,7 +28,7 @@ export class FileHandle {
|
|
|
36
28
|
chmod(mode) {
|
|
37
29
|
const numMode = normalizeMode(mode, -1);
|
|
38
30
|
if (numMode < 0) {
|
|
39
|
-
throw new
|
|
31
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid mode.');
|
|
40
32
|
}
|
|
41
33
|
return this.file.chmod(numMode);
|
|
42
34
|
}
|
|
@@ -59,7 +51,7 @@ export class FileHandle {
|
|
|
59
51
|
truncate(len) {
|
|
60
52
|
len || (len = 0);
|
|
61
53
|
if (len < 0) {
|
|
62
|
-
throw new
|
|
54
|
+
throw new ErrnoError(Errno.EINVAL);
|
|
63
55
|
}
|
|
64
56
|
return this.file.truncate(len);
|
|
65
57
|
}
|
|
@@ -85,10 +77,10 @@ export class FileHandle {
|
|
|
85
77
|
const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
|
|
86
78
|
const flag = parseFlag(options.flag);
|
|
87
79
|
if (!isAppendable(flag)) {
|
|
88
|
-
throw new
|
|
80
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending.');
|
|
89
81
|
}
|
|
90
82
|
if (typeof data != 'string' && !options.encoding) {
|
|
91
|
-
throw new
|
|
83
|
+
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
92
84
|
}
|
|
93
85
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : data;
|
|
94
86
|
await this.file.write(encodedData, 0, encodedData.length, null);
|
|
@@ -107,11 +99,11 @@ export class FileHandle {
|
|
|
107
99
|
}
|
|
108
100
|
return this.file.read(buffer, offset, length, position);
|
|
109
101
|
}
|
|
110
|
-
async readFile(_options
|
|
102
|
+
async readFile(_options) {
|
|
111
103
|
const options = normalizeOptions(_options, null, 'r', 0o444);
|
|
112
104
|
const flag = parseFlag(options.flag);
|
|
113
105
|
if (!isReadable(flag)) {
|
|
114
|
-
throw new
|
|
106
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for reading.');
|
|
115
107
|
}
|
|
116
108
|
const { size } = await this.stat();
|
|
117
109
|
const data = new Uint8Array(size);
|
|
@@ -145,7 +137,7 @@ export class FileHandle {
|
|
|
145
137
|
enqueue(result.buffer.slice(0, result.bytesRead));
|
|
146
138
|
position += result.bytesRead;
|
|
147
139
|
if (++i >= maxChunks) {
|
|
148
|
-
throw new
|
|
140
|
+
throw new ErrnoError(Errno.EFBIG, 'Too many iterations on readable stream', this.file.path, 'FileHandle.readableWebStream');
|
|
149
141
|
}
|
|
150
142
|
bytesRead = result.bytesRead;
|
|
151
143
|
}
|
|
@@ -157,7 +149,7 @@ export class FileHandle {
|
|
|
157
149
|
return new globalThis.ReadableStream({ start, type: options.type });
|
|
158
150
|
}
|
|
159
151
|
readLines(options) {
|
|
160
|
-
throw
|
|
152
|
+
throw ErrnoError.With('ENOSYS', this.file.path, 'FileHandle.readLines');
|
|
161
153
|
}
|
|
162
154
|
[Symbol.asyncDispose]() {
|
|
163
155
|
return this.close();
|
|
@@ -202,10 +194,10 @@ export class FileHandle {
|
|
|
202
194
|
const options = normalizeOptions(_options, 'utf8', 'w', 0o644);
|
|
203
195
|
const flag = parseFlag(options.flag);
|
|
204
196
|
if (!isWriteable(flag)) {
|
|
205
|
-
throw new
|
|
197
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for writing.');
|
|
206
198
|
}
|
|
207
199
|
if (typeof data != 'string' && !options.encoding) {
|
|
208
|
-
throw new
|
|
200
|
+
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
209
201
|
}
|
|
210
202
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : data;
|
|
211
203
|
await this.file.write(encodedData, 0, encodedData.length, 0);
|
|
@@ -292,31 +284,6 @@ export class FileHandle {
|
|
|
292
284
|
return stream;
|
|
293
285
|
}
|
|
294
286
|
}
|
|
295
|
-
/**
|
|
296
|
-
* Utility for FS ops. It handles
|
|
297
|
-
* - path normalization (for the first parameter to the FS op)
|
|
298
|
-
* - path translation for errors
|
|
299
|
-
* - FS/mount point resolution
|
|
300
|
-
*
|
|
301
|
-
* It can't be used for functions which may operate on multiple mounted FSs or paths (e.g. `rename`)
|
|
302
|
-
* @param name the function name
|
|
303
|
-
* @param resolveSymlinks whether to resolve symlinks
|
|
304
|
-
* @param args the rest of the parameters are passed to the FS function. Note that the first parameter is required to be a path
|
|
305
|
-
* @returns
|
|
306
|
-
*/
|
|
307
|
-
async function doOp(...[name, resolveSymlinks, rawPath, ...args]) {
|
|
308
|
-
rawPath = normalizePath(rawPath);
|
|
309
|
-
const _path = resolveSymlinks && (await exists(rawPath)) ? await realpath(rawPath) : rawPath;
|
|
310
|
-
const { fs, path } = resolveMount(_path);
|
|
311
|
-
try {
|
|
312
|
-
// @ts-expect-error 2556 (since ...args is not correctly picked up as being a tuple)
|
|
313
|
-
return fs[name](path, ...args);
|
|
314
|
-
}
|
|
315
|
-
catch (e) {
|
|
316
|
-
throw fixError(e, { [path]: rawPath });
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
// fs.promises
|
|
320
287
|
/**
|
|
321
288
|
* Renames a file
|
|
322
289
|
* @param oldPath
|
|
@@ -342,28 +309,42 @@ export async function rename(oldPath, newPath) {
|
|
|
342
309
|
rename;
|
|
343
310
|
/**
|
|
344
311
|
* Test whether or not the given path exists by checking with the file system.
|
|
345
|
-
* @param
|
|
312
|
+
* @param path
|
|
346
313
|
*/
|
|
347
|
-
export async function exists(
|
|
314
|
+
export async function exists(path) {
|
|
348
315
|
try {
|
|
349
|
-
const { fs, path } = resolveMount(await realpath(
|
|
350
|
-
return await fs.exists(
|
|
316
|
+
const { fs, path: resolved } = resolveMount(await realpath(path));
|
|
317
|
+
return await fs.exists(resolved, cred);
|
|
351
318
|
}
|
|
352
319
|
catch (e) {
|
|
353
|
-
if (e.
|
|
320
|
+
if (e instanceof ErrnoError && e.code == 'ENOENT') {
|
|
354
321
|
return false;
|
|
355
322
|
}
|
|
356
323
|
throw e;
|
|
357
324
|
}
|
|
358
325
|
}
|
|
359
326
|
export async function stat(path, options) {
|
|
360
|
-
|
|
361
|
-
|
|
327
|
+
path = normalizePath(path);
|
|
328
|
+
const { fs, path: resolved } = resolveMount((await exists(path)) ? await realpath(path) : path);
|
|
329
|
+
try {
|
|
330
|
+
const stats = await fs.stat(resolved, cred);
|
|
331
|
+
return options?.bigint ? new BigIntStats(stats) : stats;
|
|
332
|
+
}
|
|
333
|
+
catch (e) {
|
|
334
|
+
throw fixError(e, { [resolved]: path });
|
|
335
|
+
}
|
|
362
336
|
}
|
|
363
337
|
stat;
|
|
364
338
|
export async function lstat(path, options) {
|
|
365
|
-
|
|
366
|
-
|
|
339
|
+
path = normalizePath(path);
|
|
340
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
341
|
+
try {
|
|
342
|
+
const stats = await fs.stat(resolved, cred);
|
|
343
|
+
return options?.bigint ? new BigIntStats(stats) : stats;
|
|
344
|
+
}
|
|
345
|
+
catch (e) {
|
|
346
|
+
throw fixError(e, { [resolved]: path });
|
|
347
|
+
}
|
|
367
348
|
}
|
|
368
349
|
lstat;
|
|
369
350
|
// FILE-ONLY METHODS
|
|
@@ -387,19 +368,29 @@ truncate;
|
|
|
387
368
|
* @param path
|
|
388
369
|
*/
|
|
389
370
|
export async function unlink(path) {
|
|
390
|
-
|
|
371
|
+
path = normalizePath(path);
|
|
372
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
373
|
+
try {
|
|
374
|
+
await fs.unlink(resolved, cred);
|
|
375
|
+
}
|
|
376
|
+
catch (e) {
|
|
377
|
+
throw fixError(e, { [resolved]: path });
|
|
378
|
+
}
|
|
391
379
|
}
|
|
392
380
|
unlink;
|
|
393
381
|
/**
|
|
394
382
|
* Opens a file. This helper handles the complexity of file flags.
|
|
395
383
|
* @internal
|
|
396
384
|
*/
|
|
397
|
-
async function _open(
|
|
398
|
-
|
|
385
|
+
async function _open(path, _flag, _mode = 0o644, resolveSymlinks) {
|
|
386
|
+
path = normalizePath(path);
|
|
387
|
+
const mode = normalizeMode(_mode, 0o644), flag = parseFlag(_flag);
|
|
388
|
+
path = resolveSymlinks && (await exists(path)) ? await realpath(path) : path;
|
|
389
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
399
390
|
try {
|
|
400
391
|
switch (pathExistsAction(flag)) {
|
|
401
392
|
case ActionType.THROW:
|
|
402
|
-
throw
|
|
393
|
+
throw ErrnoError.With('EEXIST', path, '_open');
|
|
403
394
|
case ActionType.TRUNCATE:
|
|
404
395
|
/*
|
|
405
396
|
In a previous implementation, we deleted the file and
|
|
@@ -407,33 +398,30 @@ async function _open(_path, _flag, _mode = 0o644, resolveSymlinks) {
|
|
|
407
398
|
asynchronous request was trying to read the file, as the file
|
|
408
399
|
would not exist for a small period of time.
|
|
409
400
|
*/
|
|
410
|
-
const file = await
|
|
411
|
-
if (!file) {
|
|
412
|
-
throw new ApiError(ErrorCode.EIO, 'Impossible code path reached');
|
|
413
|
-
}
|
|
401
|
+
const file = await fs.openFile(resolved, flag, cred);
|
|
414
402
|
await file.truncate(0);
|
|
415
403
|
await file.sync();
|
|
416
|
-
return file;
|
|
404
|
+
return new FileHandle(file);
|
|
417
405
|
case ActionType.NOP:
|
|
418
406
|
// Must await so thrown errors are caught by the catch below
|
|
419
|
-
return await
|
|
407
|
+
return new FileHandle(await fs.openFile(resolved, flag, cred));
|
|
420
408
|
default:
|
|
421
|
-
throw new
|
|
409
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid file flag');
|
|
422
410
|
}
|
|
423
411
|
}
|
|
424
412
|
catch (e) {
|
|
425
413
|
switch (pathNotExistsAction(flag)) {
|
|
426
414
|
case ActionType.CREATE:
|
|
427
415
|
// Ensure parent exists.
|
|
428
|
-
const parentStats = await
|
|
416
|
+
const parentStats = await fs.stat(dirname(resolved), cred);
|
|
429
417
|
if (parentStats && !parentStats.isDirectory()) {
|
|
430
|
-
throw
|
|
418
|
+
throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
|
|
431
419
|
}
|
|
432
|
-
return await
|
|
420
|
+
return new FileHandle(await fs.createFile(resolved, flag, mode, cred));
|
|
433
421
|
case ActionType.THROW:
|
|
434
|
-
throw
|
|
422
|
+
throw ErrnoError.With('ENOENT', path, '_open');
|
|
435
423
|
default:
|
|
436
|
-
throw new
|
|
424
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid file flag');
|
|
437
425
|
}
|
|
438
426
|
}
|
|
439
427
|
}
|
|
@@ -444,36 +432,18 @@ async function _open(_path, _flag, _mode = 0o644, resolveSymlinks) {
|
|
|
444
432
|
* @param mode Mode to use to open the file. Can be ignored if the filesystem doesn't support permissions.
|
|
445
433
|
*/
|
|
446
434
|
export async function open(path, flag = 'r', mode = 0o644) {
|
|
447
|
-
|
|
448
|
-
return new FileHandle(file2fd(file));
|
|
435
|
+
return await _open(path, flag, mode, true);
|
|
449
436
|
}
|
|
450
437
|
open;
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
async function _readFile(fname, flag, resolveSymlinks) {
|
|
455
|
-
const file = await _open(normalizePath(fname), flag, 0o644, resolveSymlinks);
|
|
438
|
+
export async function readFile(path, _options) {
|
|
439
|
+
const options = normalizeOptions(_options, null, 'r', 0o644);
|
|
440
|
+
const handle = typeof path == 'object' && 'fd' in path ? path : await open(path, options.flag, options.mode);
|
|
456
441
|
try {
|
|
457
|
-
|
|
458
|
-
const data = new Uint8Array(stat.size);
|
|
459
|
-
await file.read(data, 0, stat.size, 0);
|
|
460
|
-
await file.close();
|
|
461
|
-
return data;
|
|
462
|
-
}
|
|
463
|
-
catch (e) {
|
|
464
|
-
await file.close();
|
|
465
|
-
throw e;
|
|
442
|
+
return await handle.readFile(options);
|
|
466
443
|
}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
const options = normalizeOptions(_options, null, 'r', 0);
|
|
470
|
-
const flag = parseFlag(options.flag);
|
|
471
|
-
if (!isReadable(flag)) {
|
|
472
|
-
throw new ApiError(ErrorCode.EINVAL, 'Flag passed must allow for reading.');
|
|
444
|
+
finally {
|
|
445
|
+
await handle.close();
|
|
473
446
|
}
|
|
474
|
-
path = path instanceof FileHandle ? path.file.path : path.toString();
|
|
475
|
-
const data = Buffer.from(await _readFile(path, options.flag, true));
|
|
476
|
-
return options.encoding ? data.toString(options.encoding) : data;
|
|
477
447
|
}
|
|
478
448
|
readFile;
|
|
479
449
|
/**
|
|
@@ -493,7 +463,7 @@ export async function writeFile(path, data, _options) {
|
|
|
493
463
|
try {
|
|
494
464
|
const _data = typeof data == 'string' ? data : data;
|
|
495
465
|
if (typeof _data != 'string' && !(_data instanceof Uint8Array)) {
|
|
496
|
-
throw new
|
|
466
|
+
throw new ErrnoError(Errno.EINVAL, 'Iterables and streams not supported', handle.file.path, 'writeFile');
|
|
497
467
|
}
|
|
498
468
|
await handle.writeFile(_data, options);
|
|
499
469
|
}
|
|
@@ -502,19 +472,6 @@ export async function writeFile(path, data, _options) {
|
|
|
502
472
|
}
|
|
503
473
|
}
|
|
504
474
|
writeFile;
|
|
505
|
-
/**
|
|
506
|
-
* Asynchronously append data to a file, creating the file if
|
|
507
|
-
* it not yet exists.
|
|
508
|
-
*/
|
|
509
|
-
async function _appendFile(path, data, flag, mode, resolveSymlinks) {
|
|
510
|
-
const file = await _open(path, flag, mode, resolveSymlinks);
|
|
511
|
-
try {
|
|
512
|
-
await file.write(data, 0, data.length, null);
|
|
513
|
-
}
|
|
514
|
-
finally {
|
|
515
|
-
await file.close();
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
475
|
/**
|
|
519
476
|
* Asynchronously append data to a file, creating the file if it not yet
|
|
520
477
|
* exists.
|
|
@@ -529,13 +486,19 @@ export async function appendFile(path, data, _options) {
|
|
|
529
486
|
const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
|
|
530
487
|
const flag = parseFlag(options.flag);
|
|
531
488
|
if (!isAppendable(flag)) {
|
|
532
|
-
throw new
|
|
489
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending.');
|
|
533
490
|
}
|
|
534
491
|
if (typeof data != 'string' && !options.encoding) {
|
|
535
|
-
throw new
|
|
492
|
+
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
536
493
|
}
|
|
537
494
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
538
|
-
|
|
495
|
+
const handle = typeof path == 'object' && 'fd' in path ? path : await open(path, options.flag, options.mode);
|
|
496
|
+
try {
|
|
497
|
+
await handle.appendFile(encodedData, options);
|
|
498
|
+
}
|
|
499
|
+
finally {
|
|
500
|
+
await handle.close();
|
|
501
|
+
}
|
|
539
502
|
}
|
|
540
503
|
appendFile;
|
|
541
504
|
// DIRECTORY-ONLY METHODS
|
|
@@ -544,19 +507,41 @@ appendFile;
|
|
|
544
507
|
* @param path
|
|
545
508
|
*/
|
|
546
509
|
export async function rmdir(path) {
|
|
547
|
-
|
|
510
|
+
path = normalizePath(path);
|
|
511
|
+
path = (await exists(path)) ? await realpath(path) : path;
|
|
512
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
513
|
+
try {
|
|
514
|
+
await fs.rmdir(resolved, cred);
|
|
515
|
+
}
|
|
516
|
+
catch (e) {
|
|
517
|
+
throw fixError(e, { [resolved]: path });
|
|
518
|
+
}
|
|
548
519
|
}
|
|
549
520
|
rmdir;
|
|
550
521
|
export async function mkdir(path, options) {
|
|
551
|
-
|
|
552
|
-
|
|
522
|
+
path = normalizePath(path);
|
|
523
|
+
path = (await exists(path)) ? await realpath(path) : path;
|
|
524
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
525
|
+
try {
|
|
526
|
+
await fs.mkdir(resolved, normalizeMode(typeof options == 'object' ? options?.mode : options, 0o777), cred);
|
|
527
|
+
}
|
|
528
|
+
catch (e) {
|
|
529
|
+
throw fixError(e, { [resolved]: path });
|
|
530
|
+
}
|
|
553
531
|
}
|
|
554
532
|
mkdir;
|
|
555
533
|
export async function readdir(path, options) {
|
|
556
534
|
path = normalizePath(path);
|
|
557
|
-
|
|
558
|
-
const
|
|
559
|
-
|
|
535
|
+
path = (await exists(path)) ? await realpath(path) : path;
|
|
536
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
537
|
+
let entries;
|
|
538
|
+
try {
|
|
539
|
+
entries = await fs.readdir(resolved, cred);
|
|
540
|
+
}
|
|
541
|
+
catch (e) {
|
|
542
|
+
throw fixError(e, { [resolved]: path });
|
|
543
|
+
}
|
|
544
|
+
for (const point of mounts.keys()) {
|
|
560
545
|
if (point.startsWith(path)) {
|
|
561
546
|
const entry = point.slice(path.length);
|
|
562
547
|
if (entry.includes('/') || entry.length == 0) {
|
|
@@ -580,8 +565,15 @@ readdir;
|
|
|
580
565
|
* @param newpath
|
|
581
566
|
*/
|
|
582
567
|
export async function link(existing, newpath) {
|
|
568
|
+
existing = normalizePath(existing);
|
|
583
569
|
newpath = normalizePath(newpath);
|
|
584
|
-
|
|
570
|
+
const { fs, path: resolved } = resolveMount(newpath);
|
|
571
|
+
try {
|
|
572
|
+
return await fs.link(existing, newpath, cred);
|
|
573
|
+
}
|
|
574
|
+
catch (e) {
|
|
575
|
+
throw fixError(e, { [resolved]: newpath });
|
|
576
|
+
}
|
|
585
577
|
}
|
|
586
578
|
link;
|
|
587
579
|
/**
|
|
@@ -592,20 +584,26 @@ link;
|
|
|
592
584
|
*/
|
|
593
585
|
export async function symlink(target, path, type = 'file') {
|
|
594
586
|
if (!['file', 'dir', 'junction'].includes(type)) {
|
|
595
|
-
throw new
|
|
587
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid symlink type: ' + type);
|
|
596
588
|
}
|
|
597
589
|
if (await exists(path)) {
|
|
598
|
-
throw
|
|
590
|
+
throw ErrnoError.With('EEXIST', path.toString(), 'symlink');
|
|
599
591
|
}
|
|
600
592
|
await writeFile(path, target.toString());
|
|
601
|
-
const
|
|
602
|
-
await file._setType(FileType.SYMLINK);
|
|
593
|
+
const handle = await _open(path, 'r+', 0o644, false);
|
|
594
|
+
await handle.file._setType(FileType.SYMLINK);
|
|
603
595
|
}
|
|
604
596
|
symlink;
|
|
605
597
|
export async function readlink(path, options) {
|
|
606
|
-
const
|
|
607
|
-
|
|
608
|
-
|
|
598
|
+
const handle = await _open(normalizePath(path), 'r', 0o644, false);
|
|
599
|
+
try {
|
|
600
|
+
const value = await handle.readFile();
|
|
601
|
+
const encoding = typeof options == 'object' ? options?.encoding : options;
|
|
602
|
+
return encoding == 'buffer' ? value : value.toString(encoding);
|
|
603
|
+
}
|
|
604
|
+
finally {
|
|
605
|
+
await handle.close();
|
|
606
|
+
}
|
|
609
607
|
}
|
|
610
608
|
readlink;
|
|
611
609
|
// PROPERTY OPERATIONS
|
|
@@ -632,12 +630,12 @@ chown;
|
|
|
632
630
|
* @param gid
|
|
633
631
|
*/
|
|
634
632
|
export async function lchown(path, uid, gid) {
|
|
635
|
-
const
|
|
633
|
+
const handle = await _open(path, 'r+', 0o644, false);
|
|
636
634
|
try {
|
|
637
|
-
await
|
|
635
|
+
await handle.chown(uid, gid);
|
|
638
636
|
}
|
|
639
637
|
finally {
|
|
640
|
-
await
|
|
638
|
+
await handle.close();
|
|
641
639
|
}
|
|
642
640
|
}
|
|
643
641
|
lchown;
|
|
@@ -662,12 +660,12 @@ chmod;
|
|
|
662
660
|
* @param mode
|
|
663
661
|
*/
|
|
664
662
|
export async function lchmod(path, mode) {
|
|
665
|
-
const
|
|
663
|
+
const handle = await _open(path, 'r+', 0o644, false);
|
|
666
664
|
try {
|
|
667
|
-
await
|
|
665
|
+
await handle.chmod(mode);
|
|
668
666
|
}
|
|
669
667
|
finally {
|
|
670
|
-
await
|
|
668
|
+
await handle.close();
|
|
671
669
|
}
|
|
672
670
|
}
|
|
673
671
|
lchmod;
|
|
@@ -694,12 +692,12 @@ utimes;
|
|
|
694
692
|
* @param mtime
|
|
695
693
|
*/
|
|
696
694
|
export async function lutimes(path, atime, mtime) {
|
|
697
|
-
const
|
|
695
|
+
const handle = await _open(path, 'r+', 0o644, false);
|
|
698
696
|
try {
|
|
699
|
-
await
|
|
697
|
+
await handle.utimes(new Date(atime), new Date(mtime));
|
|
700
698
|
}
|
|
701
699
|
finally {
|
|
702
|
-
await
|
|
700
|
+
await handle.close();
|
|
703
701
|
}
|
|
704
702
|
}
|
|
705
703
|
lutimes;
|
|
@@ -721,7 +719,7 @@ export async function realpath(path, options) {
|
|
|
721
719
|
}
|
|
722
720
|
realpath;
|
|
723
721
|
export function watch(filename, options = {}) {
|
|
724
|
-
throw
|
|
722
|
+
throw ErrnoError.With('ENOSYS', filename.toString(), 'watch');
|
|
725
723
|
}
|
|
726
724
|
watch;
|
|
727
725
|
/**
|
|
@@ -732,7 +730,7 @@ watch;
|
|
|
732
730
|
export async function access(path, mode = constants.F_OK) {
|
|
733
731
|
const stats = await stat(path);
|
|
734
732
|
if (!stats.hasAccess(mode, cred)) {
|
|
735
|
-
throw new
|
|
733
|
+
throw new ErrnoError(Errno.EACCES);
|
|
736
734
|
}
|
|
737
735
|
}
|
|
738
736
|
access;
|
|
@@ -761,7 +759,7 @@ export async function rm(path, options) {
|
|
|
761
759
|
case constants.S_IFIFO:
|
|
762
760
|
case constants.S_IFSOCK:
|
|
763
761
|
default:
|
|
764
|
-
throw new
|
|
762
|
+
throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
|
|
765
763
|
}
|
|
766
764
|
}
|
|
767
765
|
rm;
|
|
@@ -784,7 +782,7 @@ export async function copyFile(src, dest, mode) {
|
|
|
784
782
|
src = normalizePath(src);
|
|
785
783
|
dest = normalizePath(dest);
|
|
786
784
|
if (mode && mode & constants.COPYFILE_EXCL && (await exists(dest))) {
|
|
787
|
-
throw new
|
|
785
|
+
throw new ErrnoError(Errno.EEXIST, 'Destination file already exists.', dest, 'copyFile');
|
|
788
786
|
}
|
|
789
787
|
await writeFile(dest, await readFile(src));
|
|
790
788
|
}
|
|
@@ -817,12 +815,12 @@ export async function cp(source, destination, opts) {
|
|
|
817
815
|
destination = normalizePath(destination);
|
|
818
816
|
const srcStats = await lstat(source); // Use lstat to follow symlinks if not dereferencing
|
|
819
817
|
if (opts?.errorOnExist && (await exists(destination))) {
|
|
820
|
-
throw new
|
|
818
|
+
throw new ErrnoError(Errno.EEXIST, 'Destination file or directory already exists.', destination, 'cp');
|
|
821
819
|
}
|
|
822
820
|
switch (srcStats.mode & constants.S_IFMT) {
|
|
823
821
|
case constants.S_IFDIR:
|
|
824
822
|
if (!opts?.recursive) {
|
|
825
|
-
throw new
|
|
823
|
+
throw new ErrnoError(Errno.EISDIR, source + ' is a directory (not copied)', source, 'cp');
|
|
826
824
|
}
|
|
827
825
|
await mkdir(destination, { recursive: true }); // Ensure the destination directory exists
|
|
828
826
|
for (const dirent of await readdir(source, { withFileTypes: true })) {
|
|
@@ -841,7 +839,7 @@ export async function cp(source, destination, opts) {
|
|
|
841
839
|
case constants.S_IFIFO:
|
|
842
840
|
case constants.S_IFSOCK:
|
|
843
841
|
default:
|
|
844
|
-
throw new
|
|
842
|
+
throw new ErrnoError(Errno.EPERM, 'File type not supported', source, 'rm');
|
|
845
843
|
}
|
|
846
844
|
// Optionally preserve timestamps
|
|
847
845
|
if (opts?.preserveTimestamps) {
|
|
@@ -850,5 +848,5 @@ export async function cp(source, destination, opts) {
|
|
|
850
848
|
}
|
|
851
849
|
cp;
|
|
852
850
|
export async function statfs(path, opts) {
|
|
853
|
-
throw
|
|
851
|
+
throw ErrnoError.With('ENOSYS', path.toString(), 'statfs');
|
|
854
852
|
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { Cred } from '../cred.js';
|
|
2
2
|
import type { File } from '../file.js';
|
|
3
3
|
import { FileSystem } from '../filesystem.js';
|
|
4
|
+
import { type AbsolutePath } from './path.js';
|
|
4
5
|
export declare let cred: Cred;
|
|
5
6
|
export declare function setCred(val: Cred): void;
|
|
6
7
|
export declare const fdMap: Map<number, File>;
|
|
7
8
|
export declare function file2fd(file: File): number;
|
|
8
9
|
export declare function fd2file(fd: number): File;
|
|
9
|
-
export
|
|
10
|
-
[point: string]: FileSystem;
|
|
11
|
-
}
|
|
10
|
+
export type MountObject = Record<AbsolutePath, FileSystem>;
|
|
12
11
|
/**
|
|
13
12
|
* The map of mount points
|
|
14
13
|
* @internal
|
|
@@ -33,10 +32,6 @@ export declare function resolveMount(path: string): {
|
|
|
33
32
|
/**
|
|
34
33
|
* Reverse maps the paths in text from the mounted FileSystem to the global path
|
|
35
34
|
*/
|
|
36
|
-
export declare function fixPaths(text: string, paths:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
export declare function fixError<E extends Error>(e: E, paths: {
|
|
40
|
-
[from: string]: string;
|
|
41
|
-
}): E;
|
|
42
|
-
export declare function mountMapping(mountMapping: MountMapping): void;
|
|
35
|
+
export declare function fixPaths(text: string, paths: Record<string, string>): string;
|
|
36
|
+
export declare function fixError<E extends Error>(e: E, paths: Record<string, string>): E;
|
|
37
|
+
export declare function mountObject(mounts: MountObject): void;
|
package/dist/emulation/shared.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Utilities and shared data
|
|
2
|
-
import {
|
|
2
|
+
import { ErrnoError, Errno } from '../error.js';
|
|
3
3
|
import { InMemory } from '../backends/InMemory.js';
|
|
4
4
|
import { rootCred } from '../cred.js';
|
|
5
5
|
import { normalizePath } from '../utils.js';
|
|
@@ -19,7 +19,7 @@ export function file2fd(file) {
|
|
|
19
19
|
}
|
|
20
20
|
export function fd2file(fd) {
|
|
21
21
|
if (!fdMap.has(fd)) {
|
|
22
|
-
throw new
|
|
22
|
+
throw new ErrnoError(Errno.EBADF);
|
|
23
23
|
}
|
|
24
24
|
return fdMap.get(fd);
|
|
25
25
|
}
|
|
@@ -41,7 +41,7 @@ export function mount(mountPoint, fs) {
|
|
|
41
41
|
}
|
|
42
42
|
mountPoint = resolve(mountPoint);
|
|
43
43
|
if (mounts.has(mountPoint)) {
|
|
44
|
-
throw new
|
|
44
|
+
throw new ErrnoError(Errno.EINVAL, 'Mount point ' + mountPoint + ' is already in use.');
|
|
45
45
|
}
|
|
46
46
|
mounts.set(mountPoint, fs);
|
|
47
47
|
}
|
|
@@ -54,7 +54,7 @@ export function umount(mountPoint) {
|
|
|
54
54
|
}
|
|
55
55
|
mountPoint = resolve(mountPoint);
|
|
56
56
|
if (!mounts.has(mountPoint)) {
|
|
57
|
-
throw new
|
|
57
|
+
throw new ErrnoError(Errno.EINVAL, 'Mount point ' + mountPoint + ' is already unmounted.');
|
|
58
58
|
}
|
|
59
59
|
mounts.delete(mountPoint);
|
|
60
60
|
}
|
|
@@ -74,7 +74,7 @@ export function resolveMount(path) {
|
|
|
74
74
|
return { fs, path, mountPoint };
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
|
-
throw new
|
|
77
|
+
throw new ErrnoError(Errno.EIO, 'ZenFS not initialized with a file system');
|
|
78
78
|
}
|
|
79
79
|
/**
|
|
80
80
|
* Reverse maps the paths in text from the mounted FileSystem to the global path
|
|
@@ -92,11 +92,11 @@ export function fixError(e, paths) {
|
|
|
92
92
|
e.message = fixPaths(e.message, paths);
|
|
93
93
|
return e;
|
|
94
94
|
}
|
|
95
|
-
export function
|
|
96
|
-
if ('/' in
|
|
95
|
+
export function mountObject(mounts) {
|
|
96
|
+
if ('/' in mounts) {
|
|
97
97
|
umount('/');
|
|
98
98
|
}
|
|
99
|
-
for (const [point, fs] of Object.entries(
|
|
99
|
+
for (const [point, fs] of Object.entries(mounts)) {
|
|
100
100
|
mount(point, fs);
|
|
101
101
|
}
|
|
102
102
|
}
|