@zenfs/core 1.2.4 → 1.2.6
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/config.d.ts +9 -0
- package/dist/config.js +2 -1
- package/dist/emulation/config.d.ts +9 -1
- package/dist/emulation/config.js +9 -1
- package/dist/emulation/promises.js +54 -43
- package/dist/emulation/sync.js +0 -1
- package/dist/file.js +32 -19
- package/package.json +1 -1
- package/src/config.ts +12 -1
- package/src/emulation/config.ts +11 -1
- package/src/emulation/promises.ts +2 -3
- package/src/emulation/sync.ts +0 -1
- package/src/file.ts +22 -19
- package/tests/fs/chmod.test.ts +12 -16
- package/tests/fs/truncate.test.ts +12 -12
package/dist/config.d.ts
CHANGED
|
@@ -61,6 +61,15 @@ export interface Configuration<T extends ConfigMounts> extends SharedConfig {
|
|
|
61
61
|
* @default false
|
|
62
62
|
*/
|
|
63
63
|
disableUpdateOnRead: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* If true, files will only sync to the file system when closed.
|
|
66
|
+
*
|
|
67
|
+
* This can increase performance.
|
|
68
|
+
* @experimental
|
|
69
|
+
* @overrides `disableUpdateOnRead`
|
|
70
|
+
* @default false
|
|
71
|
+
*/
|
|
72
|
+
onlySyncOnClose: boolean;
|
|
64
73
|
}
|
|
65
74
|
/**
|
|
66
75
|
* Configures ZenFS with single mount point /
|
package/dist/config.js
CHANGED
|
@@ -70,7 +70,8 @@ export async function configure(configuration) {
|
|
|
70
70
|
Object.assign(credentials, { uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });
|
|
71
71
|
cache.setEnabled(configuration.cacheStats ?? false);
|
|
72
72
|
config.checkAccess = !configuration.disableAccessChecks;
|
|
73
|
-
config.
|
|
73
|
+
config.syncOnRead = !configuration.onlySyncOnClose || !configuration.disableUpdateOnRead;
|
|
74
|
+
config.syncOnWrite = !configuration.onlySyncOnClose;
|
|
74
75
|
if (configuration.addDevices) {
|
|
75
76
|
const devfs = new DeviceFS();
|
|
76
77
|
devfs.createDevice('/null', nullDevice);
|
|
@@ -6,5 +6,13 @@ export declare const config: {
|
|
|
6
6
|
/**
|
|
7
7
|
* Whether to sync atime updates immediately when reading from a file
|
|
8
8
|
*/
|
|
9
|
-
|
|
9
|
+
syncOnRead: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Whether to immediately sync when files are written to
|
|
12
|
+
*/
|
|
13
|
+
syncOnWrite: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* If a file's buffer is not large enough to store content when writing and the buffer can't be resized, reuse the buffer passed to write()
|
|
16
|
+
*/
|
|
17
|
+
unsafeBufferReplace: boolean;
|
|
10
18
|
};
|
package/dist/emulation/config.js
CHANGED
|
@@ -6,5 +6,13 @@ export const config = {
|
|
|
6
6
|
/**
|
|
7
7
|
* Whether to sync atime updates immediately when reading from a file
|
|
8
8
|
*/
|
|
9
|
-
|
|
9
|
+
syncOnRead: true,
|
|
10
|
+
/**
|
|
11
|
+
* Whether to immediately sync when files are written to
|
|
12
|
+
*/
|
|
13
|
+
syncOnWrite: true,
|
|
14
|
+
/**
|
|
15
|
+
* If a file's buffer is not large enough to store content when writing and the buffer can't be resized, reuse the buffer passed to write()
|
|
16
|
+
*/
|
|
17
|
+
unsafeBufferReplace: false,
|
|
10
18
|
};
|
|
@@ -485,7 +485,6 @@ async function _open(path, _flag, _mode = 0o644, resolveSymlinks) {
|
|
|
485
485
|
*/
|
|
486
486
|
if (isTruncating(flag)) {
|
|
487
487
|
await handle.truncate(0);
|
|
488
|
-
await handle.sync();
|
|
489
488
|
}
|
|
490
489
|
return handle;
|
|
491
490
|
}
|
|
@@ -738,24 +737,17 @@ link;
|
|
|
738
737
|
* @param type can be either `'dir'` or `'file'` (default is `'file'`)
|
|
739
738
|
*/
|
|
740
739
|
export async function symlink(target, path, type = 'file') {
|
|
741
|
-
if (!['file', 'dir', 'junction'].includes(type)) {
|
|
742
|
-
throw new ErrnoError(Errno.EINVAL, 'Invalid symlink type: ' + type);
|
|
743
|
-
}
|
|
744
|
-
if (await exists(path)) {
|
|
745
|
-
throw ErrnoError.With('EEXIST', path.toString(), 'symlink');
|
|
746
|
-
}
|
|
747
|
-
await writeFile(path, target.toString());
|
|
748
|
-
const handle = await _open(path, 'r+', 0o644, false);
|
|
749
|
-
await handle.file._setType(constants.S_IFLNK);
|
|
750
|
-
}
|
|
751
|
-
symlink;
|
|
752
|
-
export async function readlink(path, options) {
|
|
753
740
|
const env_5 = { stack: [], error: void 0, hasError: false };
|
|
754
741
|
try {
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
742
|
+
if (!['file', 'dir', 'junction'].includes(type)) {
|
|
743
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid symlink type: ' + type);
|
|
744
|
+
}
|
|
745
|
+
if (await exists(path)) {
|
|
746
|
+
throw ErrnoError.With('EEXIST', path.toString(), 'symlink');
|
|
747
|
+
}
|
|
748
|
+
const handle = __addDisposableResource(env_5, await _open(path, 'w+', 0o644, false), true);
|
|
749
|
+
await handle.writeFile(target.toString());
|
|
750
|
+
await handle.file._setType(constants.S_IFLNK);
|
|
759
751
|
}
|
|
760
752
|
catch (e_5) {
|
|
761
753
|
env_5.error = e_5;
|
|
@@ -767,13 +759,14 @@ export async function readlink(path, options) {
|
|
|
767
759
|
await result_5;
|
|
768
760
|
}
|
|
769
761
|
}
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
export async function chown(path, uid, gid) {
|
|
762
|
+
symlink;
|
|
763
|
+
export async function readlink(path, options) {
|
|
773
764
|
const env_6 = { stack: [], error: void 0, hasError: false };
|
|
774
765
|
try {
|
|
775
|
-
const handle = __addDisposableResource(env_6, await
|
|
776
|
-
await handle.
|
|
766
|
+
const handle = __addDisposableResource(env_6, await _open(normalizePath(path), 'r', 0o644, false), true);
|
|
767
|
+
const value = await handle.readFile();
|
|
768
|
+
const encoding = typeof options == 'object' ? options?.encoding : options;
|
|
769
|
+
return encoding == 'buffer' ? value : value.toString(encoding);
|
|
777
770
|
}
|
|
778
771
|
catch (e_6) {
|
|
779
772
|
env_6.error = e_6;
|
|
@@ -785,11 +778,12 @@ export async function chown(path, uid, gid) {
|
|
|
785
778
|
await result_6;
|
|
786
779
|
}
|
|
787
780
|
}
|
|
788
|
-
|
|
789
|
-
|
|
781
|
+
readlink;
|
|
782
|
+
// PROPERTY OPERATIONS
|
|
783
|
+
export async function chown(path, uid, gid) {
|
|
790
784
|
const env_7 = { stack: [], error: void 0, hasError: false };
|
|
791
785
|
try {
|
|
792
|
-
const handle = __addDisposableResource(env_7, await
|
|
786
|
+
const handle = __addDisposableResource(env_7, await open(path, 'r+'), true);
|
|
793
787
|
await handle.chown(uid, gid);
|
|
794
788
|
}
|
|
795
789
|
catch (e_7) {
|
|
@@ -802,12 +796,12 @@ export async function lchown(path, uid, gid) {
|
|
|
802
796
|
await result_7;
|
|
803
797
|
}
|
|
804
798
|
}
|
|
805
|
-
|
|
806
|
-
export async function
|
|
799
|
+
chown;
|
|
800
|
+
export async function lchown(path, uid, gid) {
|
|
807
801
|
const env_8 = { stack: [], error: void 0, hasError: false };
|
|
808
802
|
try {
|
|
809
|
-
const handle = __addDisposableResource(env_8, await
|
|
810
|
-
await handle.
|
|
803
|
+
const handle = __addDisposableResource(env_8, await _open(path, 'r+', 0o644, false), true);
|
|
804
|
+
await handle.chown(uid, gid);
|
|
811
805
|
}
|
|
812
806
|
catch (e_8) {
|
|
813
807
|
env_8.error = e_8;
|
|
@@ -819,11 +813,11 @@ export async function chmod(path, mode) {
|
|
|
819
813
|
await result_8;
|
|
820
814
|
}
|
|
821
815
|
}
|
|
822
|
-
|
|
823
|
-
export async function
|
|
816
|
+
lchown;
|
|
817
|
+
export async function chmod(path, mode) {
|
|
824
818
|
const env_9 = { stack: [], error: void 0, hasError: false };
|
|
825
819
|
try {
|
|
826
|
-
const handle = __addDisposableResource(env_9, await
|
|
820
|
+
const handle = __addDisposableResource(env_9, await open(path, 'r+'), true);
|
|
827
821
|
await handle.chmod(mode);
|
|
828
822
|
}
|
|
829
823
|
catch (e_9) {
|
|
@@ -836,15 +830,12 @@ export async function lchmod(path, mode) {
|
|
|
836
830
|
await result_9;
|
|
837
831
|
}
|
|
838
832
|
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
* Change file timestamps of the file referenced by the supplied path.
|
|
842
|
-
*/
|
|
843
|
-
export async function utimes(path, atime, mtime) {
|
|
833
|
+
chmod;
|
|
834
|
+
export async function lchmod(path, mode) {
|
|
844
835
|
const env_10 = { stack: [], error: void 0, hasError: false };
|
|
845
836
|
try {
|
|
846
|
-
const handle = __addDisposableResource(env_10, await
|
|
847
|
-
await handle.
|
|
837
|
+
const handle = __addDisposableResource(env_10, await _open(path, 'r+', 0o644, false), true);
|
|
838
|
+
await handle.chmod(mode);
|
|
848
839
|
}
|
|
849
840
|
catch (e_10) {
|
|
850
841
|
env_10.error = e_10;
|
|
@@ -856,15 +847,15 @@ export async function utimes(path, atime, mtime) {
|
|
|
856
847
|
await result_10;
|
|
857
848
|
}
|
|
858
849
|
}
|
|
859
|
-
|
|
850
|
+
lchmod;
|
|
860
851
|
/**
|
|
861
852
|
* Change file timestamps of the file referenced by the supplied path.
|
|
862
853
|
*/
|
|
863
|
-
export async function
|
|
854
|
+
export async function utimes(path, atime, mtime) {
|
|
864
855
|
const env_11 = { stack: [], error: void 0, hasError: false };
|
|
865
856
|
try {
|
|
866
|
-
const handle = __addDisposableResource(env_11, await
|
|
867
|
-
await handle.utimes(
|
|
857
|
+
const handle = __addDisposableResource(env_11, await open(path, 'r+'), true);
|
|
858
|
+
await handle.utimes(atime, mtime);
|
|
868
859
|
}
|
|
869
860
|
catch (e_11) {
|
|
870
861
|
env_11.error = e_11;
|
|
@@ -876,6 +867,26 @@ export async function lutimes(path, atime, mtime) {
|
|
|
876
867
|
await result_11;
|
|
877
868
|
}
|
|
878
869
|
}
|
|
870
|
+
utimes;
|
|
871
|
+
/**
|
|
872
|
+
* Change file timestamps of the file referenced by the supplied path.
|
|
873
|
+
*/
|
|
874
|
+
export async function lutimes(path, atime, mtime) {
|
|
875
|
+
const env_12 = { stack: [], error: void 0, hasError: false };
|
|
876
|
+
try {
|
|
877
|
+
const handle = __addDisposableResource(env_12, await _open(path, 'r+', 0o644, false), true);
|
|
878
|
+
await handle.utimes(new Date(atime), new Date(mtime));
|
|
879
|
+
}
|
|
880
|
+
catch (e_12) {
|
|
881
|
+
env_12.error = e_12;
|
|
882
|
+
env_12.hasError = true;
|
|
883
|
+
}
|
|
884
|
+
finally {
|
|
885
|
+
const result_12 = __disposeResources(env_12);
|
|
886
|
+
if (result_12)
|
|
887
|
+
await result_12;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
879
890
|
lutimes;
|
|
880
891
|
export async function realpath(path, options) {
|
|
881
892
|
path = normalizePath(path);
|
package/dist/emulation/sync.js
CHANGED
package/dist/file.js
CHANGED
|
@@ -289,27 +289,33 @@ export class PreloadFile extends File {
|
|
|
289
289
|
}
|
|
290
290
|
async truncate(length) {
|
|
291
291
|
this._truncate(length);
|
|
292
|
-
|
|
292
|
+
if (config.syncOnWrite)
|
|
293
|
+
await this.sync();
|
|
293
294
|
}
|
|
294
295
|
truncateSync(length) {
|
|
295
296
|
this._truncate(length);
|
|
296
|
-
|
|
297
|
+
if (config.syncOnWrite)
|
|
298
|
+
this.syncSync();
|
|
297
299
|
}
|
|
298
300
|
_write(buffer, offset = 0, length = this.stats.size, position = this.position) {
|
|
299
301
|
if (this.closed) {
|
|
300
302
|
throw ErrnoError.With('EBADF', this.path, 'File.write');
|
|
301
303
|
}
|
|
302
|
-
this.dirty = true;
|
|
303
304
|
if (!isWriteable(this.flag)) {
|
|
304
305
|
throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
|
|
305
306
|
}
|
|
307
|
+
this.dirty = true;
|
|
306
308
|
const end = position + length;
|
|
309
|
+
const slice = buffer.slice(offset, offset + length);
|
|
307
310
|
if (end > this.stats.size) {
|
|
308
311
|
this.stats.size = end;
|
|
309
312
|
if (end > this._buffer.byteLength) {
|
|
310
313
|
if (this._buffer.buffer.resizable && this._buffer.buffer.maxByteLength <= end) {
|
|
311
314
|
this._buffer.buffer.resize(end);
|
|
312
315
|
}
|
|
316
|
+
else if (config.unsafeBufferReplace) {
|
|
317
|
+
this._buffer = slice;
|
|
318
|
+
}
|
|
313
319
|
else {
|
|
314
320
|
// Extend the buffer!
|
|
315
321
|
const newBuffer = new Uint8Array(new ArrayBuffer(end, this.fs.metadata().noResizableBuffers ? {} : { maxByteLength: size_max }));
|
|
@@ -318,7 +324,6 @@ export class PreloadFile extends File {
|
|
|
318
324
|
}
|
|
319
325
|
}
|
|
320
326
|
}
|
|
321
|
-
const slice = buffer.slice(offset, offset + length);
|
|
322
327
|
this._buffer.set(slice, position);
|
|
323
328
|
this.stats.mtimeMs = Date.now();
|
|
324
329
|
this.position = position + slice.byteLength;
|
|
@@ -334,7 +339,8 @@ export class PreloadFile extends File {
|
|
|
334
339
|
*/
|
|
335
340
|
async write(buffer, offset, length, position) {
|
|
336
341
|
const bytesWritten = this._write(buffer, offset, length, position);
|
|
337
|
-
|
|
342
|
+
if (config.syncOnWrite)
|
|
343
|
+
await this.sync();
|
|
338
344
|
return bytesWritten;
|
|
339
345
|
}
|
|
340
346
|
/**
|
|
@@ -348,7 +354,8 @@ export class PreloadFile extends File {
|
|
|
348
354
|
*/
|
|
349
355
|
writeSync(buffer, offset = 0, length = this.stats.size, position = this.position) {
|
|
350
356
|
const bytesWritten = this._write(buffer, offset, length, position);
|
|
351
|
-
|
|
357
|
+
if (config.syncOnWrite)
|
|
358
|
+
this.syncSync();
|
|
352
359
|
return bytesWritten;
|
|
353
360
|
}
|
|
354
361
|
_read(buffer, offset = 0, length = this.stats.size, position) {
|
|
@@ -358,10 +365,8 @@ export class PreloadFile extends File {
|
|
|
358
365
|
if (!isReadable(this.flag)) {
|
|
359
366
|
throw new ErrnoError(Errno.EPERM, 'File not opened with a readable mode.');
|
|
360
367
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
this.stats.atimeMs = Date.now();
|
|
364
|
-
}
|
|
368
|
+
this.dirty = true;
|
|
369
|
+
this.stats.atimeMs = Date.now();
|
|
365
370
|
position ?? (position = this.position);
|
|
366
371
|
let end = position + length;
|
|
367
372
|
if (end > this.stats.size) {
|
|
@@ -370,7 +375,7 @@ export class PreloadFile extends File {
|
|
|
370
375
|
this._position = end;
|
|
371
376
|
const bytesRead = end - position;
|
|
372
377
|
if (bytesRead == 0) {
|
|
373
|
-
// No copy/read. Return
|
|
378
|
+
// No copy/read. Return immediately for better performance
|
|
374
379
|
return bytesRead;
|
|
375
380
|
}
|
|
376
381
|
new Uint8Array(buffer.buffer, offset, length).set(this._buffer.slice(position, end));
|
|
@@ -386,7 +391,8 @@ export class PreloadFile extends File {
|
|
|
386
391
|
*/
|
|
387
392
|
async read(buffer, offset, length, position) {
|
|
388
393
|
const bytesRead = this._read(buffer, offset, length, position);
|
|
389
|
-
|
|
394
|
+
if (config.syncOnRead)
|
|
395
|
+
await this.sync();
|
|
390
396
|
return { bytesRead, buffer };
|
|
391
397
|
}
|
|
392
398
|
/**
|
|
@@ -400,7 +406,8 @@ export class PreloadFile extends File {
|
|
|
400
406
|
*/
|
|
401
407
|
readSync(buffer, offset, length, position) {
|
|
402
408
|
const bytesRead = this._read(buffer, offset, length, position);
|
|
403
|
-
|
|
409
|
+
if (config.syncOnRead)
|
|
410
|
+
this.syncSync();
|
|
404
411
|
return bytesRead;
|
|
405
412
|
}
|
|
406
413
|
async chmod(mode) {
|
|
@@ -409,7 +416,8 @@ export class PreloadFile extends File {
|
|
|
409
416
|
}
|
|
410
417
|
this.dirty = true;
|
|
411
418
|
this.stats.chmod(mode);
|
|
412
|
-
|
|
419
|
+
if (config.syncOnWrite)
|
|
420
|
+
await this.sync();
|
|
413
421
|
}
|
|
414
422
|
chmodSync(mode) {
|
|
415
423
|
if (this.closed) {
|
|
@@ -417,7 +425,8 @@ export class PreloadFile extends File {
|
|
|
417
425
|
}
|
|
418
426
|
this.dirty = true;
|
|
419
427
|
this.stats.chmod(mode);
|
|
420
|
-
|
|
428
|
+
if (config.syncOnWrite)
|
|
429
|
+
this.syncSync();
|
|
421
430
|
}
|
|
422
431
|
async chown(uid, gid) {
|
|
423
432
|
if (this.closed) {
|
|
@@ -425,7 +434,8 @@ export class PreloadFile extends File {
|
|
|
425
434
|
}
|
|
426
435
|
this.dirty = true;
|
|
427
436
|
this.stats.chown(uid, gid);
|
|
428
|
-
|
|
437
|
+
if (config.syncOnWrite)
|
|
438
|
+
await this.sync();
|
|
429
439
|
}
|
|
430
440
|
chownSync(uid, gid) {
|
|
431
441
|
if (this.closed) {
|
|
@@ -433,7 +443,8 @@ export class PreloadFile extends File {
|
|
|
433
443
|
}
|
|
434
444
|
this.dirty = true;
|
|
435
445
|
this.stats.chown(uid, gid);
|
|
436
|
-
|
|
446
|
+
if (config.syncOnWrite)
|
|
447
|
+
this.syncSync();
|
|
437
448
|
}
|
|
438
449
|
async utimes(atime, mtime) {
|
|
439
450
|
if (this.closed) {
|
|
@@ -442,7 +453,8 @@ export class PreloadFile extends File {
|
|
|
442
453
|
this.dirty = true;
|
|
443
454
|
this.stats.atime = atime;
|
|
444
455
|
this.stats.mtime = mtime;
|
|
445
|
-
|
|
456
|
+
if (config.syncOnWrite)
|
|
457
|
+
await this.sync();
|
|
446
458
|
}
|
|
447
459
|
utimesSync(atime, mtime) {
|
|
448
460
|
if (this.closed) {
|
|
@@ -451,7 +463,8 @@ export class PreloadFile extends File {
|
|
|
451
463
|
this.dirty = true;
|
|
452
464
|
this.stats.atime = atime;
|
|
453
465
|
this.stats.mtime = mtime;
|
|
454
|
-
|
|
466
|
+
if (config.syncOnWrite)
|
|
467
|
+
this.syncSync();
|
|
455
468
|
}
|
|
456
469
|
async _setType(type) {
|
|
457
470
|
if (this.closed) {
|
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -126,6 +126,16 @@ export interface Configuration<T extends ConfigMounts> extends SharedConfig {
|
|
|
126
126
|
* @default false
|
|
127
127
|
*/
|
|
128
128
|
disableUpdateOnRead: boolean;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* If true, files will only sync to the file system when closed.
|
|
132
|
+
*
|
|
133
|
+
* This can increase performance.
|
|
134
|
+
* @experimental
|
|
135
|
+
* @overrides `disableUpdateOnRead`
|
|
136
|
+
* @default false
|
|
137
|
+
*/
|
|
138
|
+
onlySyncOnClose: boolean;
|
|
129
139
|
}
|
|
130
140
|
|
|
131
141
|
/**
|
|
@@ -153,7 +163,8 @@ export async function configure<T extends ConfigMounts>(configuration: Partial<C
|
|
|
153
163
|
|
|
154
164
|
cache.setEnabled(configuration.cacheStats ?? false);
|
|
155
165
|
config.checkAccess = !configuration.disableAccessChecks;
|
|
156
|
-
config.
|
|
166
|
+
config.syncOnRead = !configuration.onlySyncOnClose || !configuration.disableUpdateOnRead;
|
|
167
|
+
config.syncOnWrite = !configuration.onlySyncOnClose;
|
|
157
168
|
|
|
158
169
|
if (configuration.addDevices) {
|
|
159
170
|
const devfs = new DeviceFS();
|
package/src/emulation/config.ts
CHANGED
|
@@ -7,5 +7,15 @@ export const config = {
|
|
|
7
7
|
/**
|
|
8
8
|
* Whether to sync atime updates immediately when reading from a file
|
|
9
9
|
*/
|
|
10
|
-
|
|
10
|
+
syncOnRead: true,
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Whether to immediately sync when files are written to
|
|
14
|
+
*/
|
|
15
|
+
syncOnWrite: true,
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* If a file's buffer is not large enough to store content when writing and the buffer can't be resized, reuse the buffer passed to write()
|
|
19
|
+
*/
|
|
20
|
+
unsafeBufferReplace: false,
|
|
11
21
|
};
|
|
@@ -529,7 +529,6 @@ async function _open(path: fs.PathLike, _flag: fs.OpenMode, _mode: fs.Mode = 0o6
|
|
|
529
529
|
*/
|
|
530
530
|
if (isTruncating(flag)) {
|
|
531
531
|
await handle.truncate(0);
|
|
532
|
-
await handle.sync();
|
|
533
532
|
}
|
|
534
533
|
|
|
535
534
|
return handle;
|
|
@@ -832,8 +831,8 @@ export async function symlink(target: fs.PathLike, path: fs.PathLike, type: fs.s
|
|
|
832
831
|
throw ErrnoError.With('EEXIST', path.toString(), 'symlink');
|
|
833
832
|
}
|
|
834
833
|
|
|
835
|
-
await
|
|
836
|
-
|
|
834
|
+
await using handle = await _open(path, 'w+', 0o644, false);
|
|
835
|
+
await handle.writeFile(target.toString());
|
|
837
836
|
await handle.file._setType(constants.S_IFLNK);
|
|
838
837
|
}
|
|
839
838
|
symlink satisfies typeof promises.symlink;
|
package/src/emulation/sync.ts
CHANGED
package/src/file.ts
CHANGED
|
@@ -441,29 +441,34 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
441
441
|
|
|
442
442
|
public async truncate(length: number): Promise<void> {
|
|
443
443
|
this._truncate(length);
|
|
444
|
-
await this.sync();
|
|
444
|
+
if (config.syncOnWrite) await this.sync();
|
|
445
445
|
}
|
|
446
446
|
|
|
447
447
|
public truncateSync(length: number): void {
|
|
448
448
|
this._truncate(length);
|
|
449
|
-
this.syncSync();
|
|
449
|
+
if (config.syncOnWrite) this.syncSync();
|
|
450
450
|
}
|
|
451
451
|
|
|
452
452
|
protected _write(buffer: Uint8Array, offset: number = 0, length: number = this.stats.size, position: number = this.position): number {
|
|
453
453
|
if (this.closed) {
|
|
454
454
|
throw ErrnoError.With('EBADF', this.path, 'File.write');
|
|
455
455
|
}
|
|
456
|
-
|
|
456
|
+
|
|
457
457
|
if (!isWriteable(this.flag)) {
|
|
458
458
|
throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
|
|
459
459
|
}
|
|
460
|
+
|
|
461
|
+
this.dirty = true;
|
|
460
462
|
const end = position + length;
|
|
463
|
+
const slice = buffer.slice(offset, offset + length);
|
|
461
464
|
|
|
462
465
|
if (end > this.stats.size) {
|
|
463
466
|
this.stats.size = end;
|
|
464
467
|
if (end > this._buffer.byteLength) {
|
|
465
468
|
if (this._buffer.buffer.resizable && this._buffer.buffer.maxByteLength! <= end) {
|
|
466
469
|
this._buffer.buffer.resize(end);
|
|
470
|
+
} else if (config.unsafeBufferReplace) {
|
|
471
|
+
this._buffer = slice;
|
|
467
472
|
} else {
|
|
468
473
|
// Extend the buffer!
|
|
469
474
|
const newBuffer = new Uint8Array(new ArrayBuffer(end, this.fs.metadata().noResizableBuffers ? {} : { maxByteLength: size_max }));
|
|
@@ -472,7 +477,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
472
477
|
}
|
|
473
478
|
}
|
|
474
479
|
}
|
|
475
|
-
|
|
480
|
+
|
|
476
481
|
this._buffer.set(slice, position);
|
|
477
482
|
this.stats.mtimeMs = Date.now();
|
|
478
483
|
this.position = position + slice.byteLength;
|
|
@@ -489,7 +494,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
489
494
|
*/
|
|
490
495
|
public async write(buffer: Uint8Array, offset?: number, length?: number, position?: number): Promise<number> {
|
|
491
496
|
const bytesWritten = this._write(buffer, offset, length, position);
|
|
492
|
-
await this.sync();
|
|
497
|
+
if (config.syncOnWrite) await this.sync();
|
|
493
498
|
return bytesWritten;
|
|
494
499
|
}
|
|
495
500
|
|
|
@@ -504,7 +509,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
504
509
|
*/
|
|
505
510
|
public writeSync(buffer: Uint8Array, offset: number = 0, length: number = this.stats.size, position: number = this.position): number {
|
|
506
511
|
const bytesWritten = this._write(buffer, offset, length, position);
|
|
507
|
-
this.syncSync();
|
|
512
|
+
if (config.syncOnWrite) this.syncSync();
|
|
508
513
|
return bytesWritten;
|
|
509
514
|
}
|
|
510
515
|
|
|
@@ -515,10 +520,8 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
515
520
|
if (!isReadable(this.flag)) {
|
|
516
521
|
throw new ErrnoError(Errno.EPERM, 'File not opened with a readable mode.');
|
|
517
522
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
this.stats.atimeMs = Date.now();
|
|
521
|
-
}
|
|
523
|
+
this.dirty = true;
|
|
524
|
+
this.stats.atimeMs = Date.now();
|
|
522
525
|
position ??= this.position;
|
|
523
526
|
let end = position + length;
|
|
524
527
|
if (end > this.stats.size) {
|
|
@@ -527,7 +530,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
527
530
|
this._position = end;
|
|
528
531
|
const bytesRead = end - position;
|
|
529
532
|
if (bytesRead == 0) {
|
|
530
|
-
// No copy/read. Return
|
|
533
|
+
// No copy/read. Return immediately for better performance
|
|
531
534
|
return bytesRead;
|
|
532
535
|
}
|
|
533
536
|
new Uint8Array(buffer.buffer, offset, length).set(this._buffer.slice(position, end));
|
|
@@ -544,7 +547,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
544
547
|
*/
|
|
545
548
|
public async read<TBuffer extends ArrayBufferView>(buffer: TBuffer, offset?: number, length?: number, position?: number): Promise<{ bytesRead: number; buffer: TBuffer }> {
|
|
546
549
|
const bytesRead = this._read(buffer, offset, length, position);
|
|
547
|
-
await this.sync();
|
|
550
|
+
if (config.syncOnRead) await this.sync();
|
|
548
551
|
return { bytesRead, buffer };
|
|
549
552
|
}
|
|
550
553
|
|
|
@@ -559,7 +562,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
559
562
|
*/
|
|
560
563
|
public readSync(buffer: ArrayBufferView, offset?: number, length?: number, position?: number): number {
|
|
561
564
|
const bytesRead = this._read(buffer, offset, length, position);
|
|
562
|
-
this.syncSync();
|
|
565
|
+
if (config.syncOnRead) this.syncSync();
|
|
563
566
|
return bytesRead;
|
|
564
567
|
}
|
|
565
568
|
|
|
@@ -569,7 +572,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
569
572
|
}
|
|
570
573
|
this.dirty = true;
|
|
571
574
|
this.stats.chmod(mode);
|
|
572
|
-
await this.sync();
|
|
575
|
+
if (config.syncOnWrite) await this.sync();
|
|
573
576
|
}
|
|
574
577
|
|
|
575
578
|
public chmodSync(mode: number): void {
|
|
@@ -578,7 +581,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
578
581
|
}
|
|
579
582
|
this.dirty = true;
|
|
580
583
|
this.stats.chmod(mode);
|
|
581
|
-
this.syncSync();
|
|
584
|
+
if (config.syncOnWrite) this.syncSync();
|
|
582
585
|
}
|
|
583
586
|
|
|
584
587
|
public async chown(uid: number, gid: number): Promise<void> {
|
|
@@ -587,7 +590,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
587
590
|
}
|
|
588
591
|
this.dirty = true;
|
|
589
592
|
this.stats.chown(uid, gid);
|
|
590
|
-
await this.sync();
|
|
593
|
+
if (config.syncOnWrite) await this.sync();
|
|
591
594
|
}
|
|
592
595
|
|
|
593
596
|
public chownSync(uid: number, gid: number): void {
|
|
@@ -596,7 +599,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
596
599
|
}
|
|
597
600
|
this.dirty = true;
|
|
598
601
|
this.stats.chown(uid, gid);
|
|
599
|
-
this.syncSync();
|
|
602
|
+
if (config.syncOnWrite) this.syncSync();
|
|
600
603
|
}
|
|
601
604
|
|
|
602
605
|
public async utimes(atime: Date, mtime: Date): Promise<void> {
|
|
@@ -606,7 +609,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
606
609
|
this.dirty = true;
|
|
607
610
|
this.stats.atime = atime;
|
|
608
611
|
this.stats.mtime = mtime;
|
|
609
|
-
await this.sync();
|
|
612
|
+
if (config.syncOnWrite) await this.sync();
|
|
610
613
|
}
|
|
611
614
|
|
|
612
615
|
public utimesSync(atime: Date, mtime: Date): void {
|
|
@@ -616,7 +619,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
616
619
|
this.dirty = true;
|
|
617
620
|
this.stats.atime = atime;
|
|
618
621
|
this.stats.mtime = mtime;
|
|
619
|
-
this.syncSync();
|
|
622
|
+
if (config.syncOnWrite) this.syncSync();
|
|
620
623
|
}
|
|
621
624
|
|
|
622
625
|
public async _setType(type: FileType): Promise<void> {
|
package/tests/fs/chmod.test.ts
CHANGED
|
@@ -4,45 +4,41 @@ import { fs } from '../common.js';
|
|
|
4
4
|
|
|
5
5
|
const asyncMode = 0o777;
|
|
6
6
|
const syncMode = 0o644;
|
|
7
|
+
const file = 'a.js';
|
|
7
8
|
|
|
8
9
|
suite('chmod tests', () => {
|
|
9
10
|
test('chmod', async () => {
|
|
10
|
-
|
|
11
|
+
await fs.promises.chmod(file, asyncMode.toString(8));
|
|
11
12
|
|
|
12
|
-
await fs.promises.
|
|
13
|
+
const stats = await fs.promises.stat(file);
|
|
14
|
+
assert.equal(stats.mode & 0o777, asyncMode);
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
assert((
|
|
16
|
-
|
|
17
|
-
fs.chmodSync(file1, syncMode);
|
|
18
|
-
assert((fs.statSync(file1).mode & 0o777) === syncMode);
|
|
16
|
+
fs.chmodSync(file, syncMode);
|
|
17
|
+
assert.equal(fs.statSync(file).mode & 0o777, syncMode);
|
|
19
18
|
});
|
|
20
19
|
|
|
21
20
|
test('fchmod', async () => {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
const handle = await fs.promises.open(file2, 'a', 0o644);
|
|
21
|
+
const handle = await fs.promises.open(file, 'a', 0o644);
|
|
25
22
|
|
|
26
23
|
await handle.chmod(asyncMode);
|
|
27
24
|
const stats = await handle.stat();
|
|
28
25
|
|
|
29
|
-
assert(
|
|
26
|
+
assert.equal(stats.mode & 0o777, asyncMode);
|
|
30
27
|
|
|
31
28
|
fs.fchmodSync(handle.fd, syncMode);
|
|
32
|
-
assert(
|
|
29
|
+
assert.equal(fs.statSync(file).mode & 0o777, syncMode);
|
|
33
30
|
});
|
|
34
31
|
|
|
35
32
|
test('lchmod', async () => {
|
|
36
33
|
const link = 'symbolic-link';
|
|
37
|
-
const target = 'a1.js';
|
|
38
34
|
|
|
39
|
-
await fs.promises.symlink(
|
|
35
|
+
await fs.promises.symlink(file, link);
|
|
40
36
|
await fs.promises.lchmod(link, asyncMode);
|
|
41
37
|
|
|
42
38
|
const stats = await fs.promises.lstat(link);
|
|
43
|
-
assert(
|
|
39
|
+
assert.equal(stats.mode & 0o777, asyncMode);
|
|
44
40
|
|
|
45
41
|
fs.lchmodSync(link, syncMode);
|
|
46
|
-
assert(
|
|
42
|
+
assert.equal(fs.lstatSync(link).mode & 0o777, syncMode);
|
|
47
43
|
});
|
|
48
44
|
});
|
|
@@ -10,22 +10,22 @@ const path: string = 'truncate-file.txt',
|
|
|
10
10
|
suite('Truncate, sync', () => {
|
|
11
11
|
test('initial write', () => {
|
|
12
12
|
fs.writeFileSync(path, data);
|
|
13
|
-
assert(fs.statSync(path).size
|
|
13
|
+
assert.equal(fs.statSync(path).size, size);
|
|
14
14
|
});
|
|
15
15
|
|
|
16
16
|
test('truncate to 1024', () => {
|
|
17
17
|
fs.truncateSync(path, 1024);
|
|
18
|
-
assert(fs.statSync(path).size
|
|
18
|
+
assert.equal(fs.statSync(path).size, 1024);
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
test('truncate to 0', () => {
|
|
22
22
|
fs.truncateSync(path);
|
|
23
|
-
assert(fs.statSync(path).size
|
|
23
|
+
assert.equal(fs.statSync(path).size, 0);
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
test('write', () => {
|
|
27
27
|
fs.writeFileSync(path, data);
|
|
28
|
-
assert(fs.statSync(path).size
|
|
28
|
+
assert.equal(fs.statSync(path).size, size);
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
let fd: number;
|
|
@@ -35,12 +35,12 @@ suite('Truncate, sync', () => {
|
|
|
35
35
|
|
|
36
36
|
test('ftruncate to 1024', () => {
|
|
37
37
|
fs.ftruncateSync(fd, 1024);
|
|
38
|
-
assert(fs.fstatSync(fd).size
|
|
38
|
+
assert.equal(fs.fstatSync(fd).size, 1024);
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
test('ftruncate to 0', () => {
|
|
42
42
|
fs.ftruncateSync(fd);
|
|
43
|
-
assert(fs.fstatSync(fd).size
|
|
43
|
+
assert.equal(fs.fstatSync(fd).size, 0);
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
test('close fd', () => {
|
|
@@ -53,22 +53,22 @@ suite('Truncate, async', () => {
|
|
|
53
53
|
test('initial write', async () => {
|
|
54
54
|
await fs.promises.writeFile(path, data);
|
|
55
55
|
|
|
56
|
-
assert(
|
|
56
|
+
assert.equal(await statSize(path), 1024 * 16);
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
test('truncate to 1024', async () => {
|
|
60
60
|
await fs.promises.truncate(path, 1024);
|
|
61
|
-
assert(
|
|
61
|
+
assert.equal(await statSize(path), 1024);
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
test('truncate to 0', async () => {
|
|
65
65
|
await fs.promises.truncate(path);
|
|
66
|
-
assert(
|
|
66
|
+
assert.equal(await statSize(path), 0);
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
test('write', async () => {
|
|
70
70
|
await fs.promises.writeFile(path, data);
|
|
71
|
-
assert(
|
|
71
|
+
assert.equal(await statSize(path), size);
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
let handle: FileHandle;
|
|
@@ -79,13 +79,13 @@ suite('Truncate, async', () => {
|
|
|
79
79
|
test('handle.truncate to 1024', async () => {
|
|
80
80
|
await handle.truncate(1024);
|
|
81
81
|
await handle.sync();
|
|
82
|
-
assert(
|
|
82
|
+
assert.equal(await statSize(path), 1024);
|
|
83
83
|
});
|
|
84
84
|
|
|
85
85
|
test('handle.truncate to 0', async () => {
|
|
86
86
|
await handle.truncate();
|
|
87
87
|
await handle.sync();
|
|
88
|
-
assert(
|
|
88
|
+
assert.equal(await statSize(path), 0);
|
|
89
89
|
});
|
|
90
90
|
|
|
91
91
|
test('close handle', async () => {
|