@zenfs/core 0.12.0 → 0.12.2

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.
Files changed (42) hide show
  1. package/dist/backends/backend.d.ts +15 -5
  2. package/dist/backends/index/fs.d.ts +1 -0
  3. package/dist/backends/index/index.d.ts +5 -0
  4. package/dist/backends/index/index.js +1 -0
  5. package/dist/backends/overlay.js +1 -1
  6. package/dist/backends/port/fs.d.ts +3 -1
  7. package/dist/browser.min.js +4 -4
  8. package/dist/browser.min.js.map +4 -4
  9. package/dist/config.d.ts +33 -11
  10. package/dist/config.js +16 -7
  11. package/dist/emulation/async.d.ts +5 -4
  12. package/dist/emulation/async.js +3 -2
  13. package/dist/emulation/index.d.ts +1 -1
  14. package/dist/emulation/index.js +1 -1
  15. package/dist/emulation/path.d.ts +4 -1
  16. package/dist/emulation/promises.d.ts +4 -4
  17. package/dist/emulation/promises.js +27 -27
  18. package/dist/emulation/shared.d.ts +6 -0
  19. package/dist/emulation/shared.js +19 -1
  20. package/dist/emulation/sync.d.ts +4 -4
  21. package/dist/emulation/sync.js +76 -38
  22. package/dist/file.d.ts +5 -10
  23. package/dist/file.js +38 -55
  24. package/dist/filesystem.d.ts +45 -3
  25. package/dist/filesystem.js +37 -8
  26. package/dist/stats.d.ts +16 -16
  27. package/dist/stats.js +42 -49
  28. package/package.json +2 -2
  29. package/src/backends/backend.ts +15 -6
  30. package/src/backends/index/index.ts +5 -0
  31. package/src/backends/overlay.ts +1 -1
  32. package/src/config.ts +62 -21
  33. package/src/emulation/async.ts +19 -18
  34. package/src/emulation/index.ts +1 -1
  35. package/src/emulation/path.ts +4 -1
  36. package/src/emulation/promises.ts +33 -31
  37. package/src/emulation/shared.ts +22 -1
  38. package/src/emulation/sync.ts +83 -54
  39. package/src/error.ts +1 -1
  40. package/src/file.ts +39 -57
  41. package/src/filesystem.ts +133 -51
  42. package/src/stats.ts +48 -60
@@ -1,31 +1,14 @@
1
1
  import { Buffer } from 'buffer';
2
2
  import type * as fs from 'node:fs';
3
- import { ErrnoError, Errno } from '../error.js';
3
+ import { Errno, ErrnoError } from '../error.js';
4
4
  import { ActionType, File, isAppendable, isReadable, isWriteable, parseFlag, pathExistsAction, pathNotExistsAction } from '../file.js';
5
- import { FileContents, FileSystem } from '../filesystem.js';
6
- import { BigIntStats, FileType, type BigIntStatsFs, type Stats, type StatsFs } from '../stats.js';
5
+ import { FileContents } from '../filesystem.js';
6
+ import { BigIntStats, FileType, type Stats } from '../stats.js';
7
7
  import { normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
8
8
  import { COPYFILE_EXCL, F_OK, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK } from './constants.js';
9
9
  import { Dir, Dirent } from './dir.js';
10
10
  import { dirname, join, parse } from './path.js';
11
- import { cred, fd2file, fdMap, fixError, file2fd, mounts, resolveMount } from './shared.js';
12
-
13
- type FileSystemMethod = {
14
- [K in keyof FileSystem]: FileSystem[K] extends (...args: any[]) => unknown
15
- ? (name: K, resolveSymlinks: boolean, ...args: Parameters<FileSystem[K]>) => ReturnType<FileSystem[K]>
16
- : never;
17
- }[keyof FileSystem]; // https://stackoverflow.com/a/76335220/17637456
18
-
19
- function wrap<M extends FileSystemMethod, RT extends ReturnType<M>>(...[name, resolveSymlinks, path, ...args]: Parameters<M>): RT {
20
- path = normalizePath(path!);
21
- const { fs, path: resolvedPath } = resolveMount(resolveSymlinks && existsSync(path) ? realpathSync(path) : path);
22
- try {
23
- // @ts-expect-error 2556 (since ...args is not correctly picked up as being a tuple)
24
- return fs[name](resolvedPath, ...args) as RT;
25
- } catch (e) {
26
- throw fixError(e as Error, { [resolvedPath]: path });
27
- }
28
- }
11
+ import { _statfs, cred, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
29
12
 
30
13
  /**
31
14
  * Synchronous rename.
@@ -78,8 +61,14 @@ existsSync satisfies typeof fs.existsSync;
78
61
  export function statSync(path: fs.PathLike, options?: { bigint?: boolean }): Stats;
79
62
  export function statSync(path: fs.PathLike, options: { bigint: true }): BigIntStats;
80
63
  export function statSync(path: fs.PathLike, options?: fs.StatOptions): Stats | BigIntStats {
81
- const stats: Stats = wrap('statSync', true, path.toString(), cred);
82
- return options?.bigint ? new BigIntStats(stats) : stats;
64
+ path = normalizePath(path);
65
+ const { fs, path: resolved } = resolveMount(existsSync(path) ? realpathSync(path) : path);
66
+ try {
67
+ const stats = fs.statSync(resolved, cred);
68
+ return options?.bigint ? new BigIntStats(stats) : stats;
69
+ } catch (e) {
70
+ throw fixError(e as Error, { [resolved]: path });
71
+ }
83
72
  }
84
73
  statSync satisfies typeof fs.statSync;
85
74
 
@@ -92,8 +81,14 @@ statSync satisfies typeof fs.statSync;
92
81
  export function lstatSync(path: fs.PathLike, options?: { bigint?: boolean }): Stats;
93
82
  export function lstatSync(path: fs.PathLike, options: { bigint: true }): BigIntStats;
94
83
  export function lstatSync(path: fs.PathLike, options?: fs.StatOptions): Stats | BigIntStats {
95
- const stats: Stats = wrap('statSync', false, path.toString(), cred);
96
- return options?.bigint ? new BigIntStats(stats) : stats;
84
+ path = normalizePath(path);
85
+ const { fs, path: resolved } = resolveMount(path);
86
+ try {
87
+ const stats = fs.statSync(resolved, cred);
88
+ return options?.bigint ? new BigIntStats(stats) : stats;
89
+ } catch (e) {
90
+ throw fixError(e as Error, { [resolved]: path });
91
+ }
97
92
  }
98
93
  lstatSync satisfies typeof fs.lstatSync;
99
94
 
@@ -117,34 +112,42 @@ truncateSync satisfies typeof fs.truncateSync;
117
112
  * @param path
118
113
  */
119
114
  export function unlinkSync(path: fs.PathLike): void {
120
- return wrap('unlinkSync', false, path.toString(), cred);
115
+ path = normalizePath(path);
116
+ const { fs, path: resolved } = resolveMount(path);
117
+ try {
118
+ return fs.unlinkSync(resolved, cred);
119
+ } catch (e) {
120
+ throw fixError(e as Error, { [resolved]: path });
121
+ }
121
122
  }
122
123
  unlinkSync satisfies typeof fs.unlinkSync;
123
124
 
124
- function _openSync(_path: fs.PathLike, _flag: fs.OpenMode, _mode?: fs.Mode | null, resolveSymlinks: boolean = true): File {
125
- const path = normalizePath(_path),
126
- mode = normalizeMode(_mode, 0o644),
125
+ function _openSync(path: fs.PathLike, _flag: fs.OpenMode, _mode?: fs.Mode | null, resolveSymlinks: boolean = true): File {
126
+ path = normalizePath(path);
127
+ const mode = normalizeMode(_mode, 0o644),
127
128
  flag = parseFlag(_flag);
128
- // Check if the path exists, and is a file.
129
- let stats: Stats;
130
- try {
131
- stats = wrap('statSync', resolveSymlinks, path, cred);
132
- } catch (e) {
133
- // File does not exist.
129
+
130
+ path = resolveSymlinks && existsSync(path) ? realpathSync(path) : path;
131
+ const { fs, path: resolved } = resolveMount(path);
132
+
133
+ if (!fs.existsSync(resolved, cred)) {
134
134
  switch (pathNotExistsAction(flag)) {
135
135
  case ActionType.CREATE:
136
136
  // Ensure parent exists.
137
- const parentStats: Stats = wrap('statSync', resolveSymlinks, dirname(path), cred);
137
+ const parentStats: Stats = fs.statSync(dirname(resolved), cred);
138
138
  if (!parentStats.isDirectory()) {
139
139
  throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
140
140
  }
141
- return wrap('createFileSync', resolveSymlinks, path, flag, mode, cred);
141
+ return fs.createFileSync(resolved, flag, mode, cred);
142
142
  case ActionType.THROW:
143
143
  throw ErrnoError.With('ENOENT', path, '_open');
144
144
  default:
145
145
  throw new ErrnoError(Errno.EINVAL, 'Invalid FileFlag object.');
146
146
  }
147
147
  }
148
+
149
+ const stats: Stats = fs.statSync(resolved, cred);
150
+
148
151
  if (!stats.hasAccess(mode, cred)) {
149
152
  throw ErrnoError.With('EACCES', path, '_open');
150
153
  }
@@ -155,16 +158,16 @@ function _openSync(_path: fs.PathLike, _flag: fs.OpenMode, _mode?: fs.Mode | nul
155
158
  throw ErrnoError.With('EEXIST', path, '_open');
156
159
  case ActionType.TRUNCATE:
157
160
  // Delete file.
158
- wrap('unlinkSync', resolveSymlinks, path, cred);
161
+ fs.unlinkSync(resolved, cred);
159
162
  /*
160
163
  Create file. Use the same mode as the old file.
161
164
  Node itself modifies the ctime when this occurs, so this action
162
165
  will preserve that behavior if the underlying file system
163
166
  supports those properties.
164
167
  */
165
- return wrap('createFileSync', resolveSymlinks, path, flag, stats.mode, cred);
168
+ return fs.createFileSync(resolved, flag, stats.mode, cred);
166
169
  case ActionType.NOP:
167
- return wrap('openFileSync', resolveSymlinks, path, flag, cred);
170
+ return fs.openFileSync(resolved, flag, cred);
168
171
  default:
169
172
  throw new ErrnoError(Errno.EINVAL, 'Invalid FileFlag object.');
170
173
  }
@@ -372,7 +375,7 @@ export function writeSync(fd: number, data: FileContents, posOrOff?: number | nu
372
375
  if (typeof data === 'string') {
373
376
  // Signature 1: (fd, string, [position?, [encoding?]])
374
377
  position = typeof posOrOff === 'number' ? posOrOff : null;
375
- const encoding = <BufferEncoding>(typeof lenOrEnc === 'string' ? lenOrEnc : 'utf8');
378
+ const encoding = typeof lenOrEnc === 'string' ? lenOrEnc : ('utf8' as BufferEncoding);
376
379
  offset = 0;
377
380
  buffer = Buffer.from(data, encoding);
378
381
  length = buffer.byteLength;
@@ -380,7 +383,7 @@ export function writeSync(fd: number, data: FileContents, posOrOff?: number | nu
380
383
  // Signature 2: (fd, buffer, offset, length, position?)
381
384
  buffer = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
382
385
  offset = posOrOff!;
383
- length = <number>lenOrEnc;
386
+ length = lenOrEnc as number;
384
387
  position = typeof pos === 'number' ? pos : null;
385
388
  }
386
389
 
@@ -463,7 +466,13 @@ futimesSync satisfies typeof fs.futimesSync;
463
466
  * @param path
464
467
  */
465
468
  export function rmdirSync(path: fs.PathLike): void {
466
- return wrap('rmdirSync', true, path.toString(), cred);
469
+ path = normalizePath(path);
470
+ const { fs, path: resolved } = resolveMount(existsSync(path) ? realpathSync(path) : path);
471
+ try {
472
+ fs.rmdirSync(resolved, cred);
473
+ } catch (e) {
474
+ throw fixError(e as Error, { [resolved]: path });
475
+ }
467
476
  }
468
477
  rmdirSync satisfies typeof fs.rmdirSync;
469
478
 
@@ -477,9 +486,15 @@ export function mkdirSync(path: fs.PathLike, options: fs.MakeDirectoryOptions &
477
486
  export function mkdirSync(path: fs.PathLike, options?: fs.Mode | (fs.MakeDirectoryOptions & { recursive?: false }) | null): void;
478
487
  export function mkdirSync(path: fs.PathLike, options?: fs.Mode | fs.MakeDirectoryOptions | null): string | undefined;
479
488
  export function mkdirSync(path: fs.PathLike, options?: fs.Mode | fs.MakeDirectoryOptions | null): string | undefined | void {
480
- const mode: fs.Mode | undefined = typeof options == 'number' || typeof options == 'string' ? options : options?.mode;
489
+ const mode: fs.Mode = normalizeMode(typeof options == 'number' || typeof options == 'string' ? options : options?.mode, 0o777);
481
490
  const recursive = typeof options == 'object' && options?.recursive;
482
- wrap('mkdirSync', true, path.toString(), normalizeMode(mode, 0o777), cred);
491
+ path = normalizePath(path);
492
+ const { fs, path: resolved } = resolveMount(existsSync(path) ? realpathSync(path) : path);
493
+ try {
494
+ return fs.mkdirSync(resolved, mode, cred);
495
+ } catch (e) {
496
+ throw fixError(e as Error, { [resolved]: path });
497
+ }
483
498
  }
484
499
  mkdirSync satisfies typeof fs.mkdirSync;
485
500
 
@@ -496,7 +511,13 @@ export function readdirSync(
496
511
  options?: { recursive?: boolean; encoding?: BufferEncoding | 'buffer' | null; withFileTypes?: boolean } | BufferEncoding | 'buffer' | null
497
512
  ): string[] | Dirent[] | Buffer[] {
498
513
  path = normalizePath(path);
499
- const entries: string[] = wrap('readdirSync', true, path, cred);
514
+ const { fs, path: resolved } = resolveMount(existsSync(path) ? realpathSync(path) : path);
515
+ let entries: string[];
516
+ try {
517
+ entries = fs.readdirSync(resolved, cred);
518
+ } catch (e) {
519
+ throw fixError(e as Error, { [resolved]: path });
520
+ }
500
521
  for (const mount of mounts.keys()) {
501
522
  if (!mount.startsWith(path)) {
502
523
  continue;
@@ -508,7 +529,7 @@ export function readdirSync(
508
529
  }
509
530
  entries.push(entry);
510
531
  }
511
- return <string[] | Dirent[] | Buffer[]>entries.map((entry: string) => {
532
+ return entries.map((entry: string) => {
512
533
  if (typeof options == 'object' && options?.withFileTypes) {
513
534
  return new Dirent(entry, statSync(join(path.toString(), entry)));
514
535
  }
@@ -518,7 +539,7 @@ export function readdirSync(
518
539
  }
519
540
 
520
541
  return entry;
521
- });
542
+ }) as string[] | Dirent[] | Buffer[];
522
543
  }
523
544
  readdirSync satisfies typeof fs.readdirSync;
524
545
 
@@ -530,8 +551,14 @@ readdirSync satisfies typeof fs.readdirSync;
530
551
  * @param newpath
531
552
  */
532
553
  export function linkSync(existing: fs.PathLike, newpath: fs.PathLike): void {
554
+ existing = normalizePath(existing);
533
555
  newpath = normalizePath(newpath);
534
- return wrap('linkSync', false, existing.toString(), newpath.toString(), cred);
556
+ const { fs, path: resolved } = resolveMount(existing);
557
+ try {
558
+ return fs.linkSync(resolved, newpath, cred);
559
+ } catch (e) {
560
+ throw fixError(e as Error, { [resolved]: existing });
561
+ }
535
562
  }
536
563
  linkSync satisfies typeof fs.linkSync;
537
564
 
@@ -872,9 +899,11 @@ cpSync satisfies typeof fs.cpSync;
872
899
  * @param path A path to an existing file or directory on the file system to be queried.
873
900
  * @param callback
874
901
  */
875
- export function statfsSync(path: fs.PathLike, options?: fs.StatFsOptions & { bigint?: false }): StatsFs;
876
- export function statfsSync(path: fs.PathLike, options: fs.StatFsOptions & { bigint: true }): BigIntStatsFs;
877
- export function statfsSync(path: fs.PathLike, options?: fs.StatFsOptions): StatsFs | BigIntStatsFs;
878
- export function statfsSync(path: fs.PathLike, options?: fs.StatFsOptions): StatsFs | BigIntStatsFs {
879
- throw ErrnoError.With('ENOSYS', path.toString(), 'statfs');
902
+ export function statfsSync(path: fs.PathLike, options?: fs.StatFsOptions & { bigint?: false }): fs.StatsFs;
903
+ export function statfsSync(path: fs.PathLike, options: fs.StatFsOptions & { bigint: true }): fs.BigIntStatsFs;
904
+ export function statfsSync(path: fs.PathLike, options?: fs.StatFsOptions): fs.StatsFs | fs.BigIntStatsFs;
905
+ export function statfsSync(path: fs.PathLike, options?: fs.StatFsOptions): fs.StatsFs | fs.BigIntStatsFs {
906
+ path = normalizePath(path);
907
+ const { fs } = resolveMount(path);
908
+ return _statfs(fs, options?.bigint);
880
909
  }
package/src/error.ts CHANGED
@@ -279,7 +279,7 @@ export class ErrnoError extends Error implements NodeJS.ErrnoException {
279
279
  public syscall: string = ''
280
280
  ) {
281
281
  super(message);
282
- this.code = <keyof typeof Errno>Errno[errno];
282
+ this.code = Errno[errno] as keyof typeof Errno;
283
283
  this.message = `${this.code}: ${message}${this.path ? `, '${this.path}'` : ''}`;
284
284
  }
285
285
 
package/src/file.ts CHANGED
@@ -359,7 +359,7 @@ export abstract class File {
359
359
  */
360
360
  export class PreloadFile<FS extends FileSystem> extends File {
361
361
  protected _position: number = 0;
362
- protected _dirty: boolean = false;
362
+ protected dirty: boolean = false;
363
363
  /**
364
364
  * Creates a file with the given path and, optionally, the given contents. Note
365
365
  * that, if contents is specified, it will be mutated by the file!
@@ -383,7 +383,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
383
383
  public readonly path: string,
384
384
  public readonly flag: string,
385
385
  public readonly stats: Stats,
386
- protected _buffer: Uint8Array = new Uint8Array(new ArrayBuffer(0, { maxByteLength: size_max }))
386
+ protected _buffer: Uint8Array = new Uint8Array(new ArrayBuffer(0, fs.metadata().noResizableBuffers ? {} : { maxByteLength: size_max }))
387
387
  ) {
388
388
  super();
389
389
 
@@ -400,7 +400,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
400
400
  throw new Error(`Size mismatch: buffer length ${_buffer.byteLength}, stats size ${this.stats.size}`);
401
401
  }
402
402
 
403
- this._dirty = true;
403
+ this.dirty = true;
404
404
  }
405
405
 
406
406
  /**
@@ -435,19 +435,19 @@ export class PreloadFile<FS extends FileSystem> extends File {
435
435
  }
436
436
 
437
437
  public async sync(): Promise<void> {
438
- if (!this.isDirty()) {
438
+ if (!this.dirty) {
439
439
  return;
440
440
  }
441
441
  await this.fs.sync(this.path, this._buffer, this.stats);
442
- this._dirty = false;
442
+ this.dirty = false;
443
443
  }
444
444
 
445
445
  public syncSync(): void {
446
- if (!this.isDirty()) {
446
+ if (!this.dirty) {
447
447
  return;
448
448
  }
449
449
  this.fs.syncSync(this.path, this._buffer, this.stats);
450
- this._dirty = false;
450
+ this.dirty = false;
451
451
  }
452
452
 
453
453
  public async close(): Promise<void> {
@@ -474,40 +474,33 @@ export class PreloadFile<FS extends FileSystem> extends File {
474
474
 
475
475
  /**
476
476
  * Asynchronous truncate.
477
- * @param len
477
+ * @param length
478
478
  */
479
- public async truncate(len: number): Promise<void> {
480
- this.truncateSync(len);
481
- if (isSynchronous(this.flag)) {
482
- return this.sync();
483
- }
479
+ public async truncate(length: number): Promise<void> {
480
+ this.truncateSync(length);
481
+ return this.sync();
484
482
  }
485
483
 
486
484
  /**
487
485
  * Synchronous truncate.
488
- * @param len
486
+ * @param length
489
487
  */
490
- public truncateSync(len: number): void {
491
- this._dirty = true;
488
+ public truncateSync(length: number): void {
489
+ this.dirty = true;
492
490
  if (!isWriteable(this.flag)) {
493
491
  throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
494
492
  }
495
493
  this.stats.mtimeMs = Date.now();
496
- if (len > this._buffer.length) {
497
- const buf = new Uint8Array(len - this._buffer.length);
498
- // Write will set stats.size for us.
499
- this.writeSync(buf, 0, buf.length, this._buffer.length);
500
- if (isSynchronous(this.flag)) {
501
- this.syncSync();
502
- }
494
+ if (length > this._buffer.length) {
495
+ const data = new Uint8Array(length - this._buffer.length);
496
+ // Write will set stats.size and handle syncing.
497
+ this.writeSync(data, 0, data.length, this._buffer.length);
503
498
  return;
504
499
  }
505
- this.stats.size = len;
506
- // Truncate buffer to 'len'.
507
- this._buffer = this._buffer.subarray(0, len);
508
- if (isSynchronous(this.flag)) {
509
- this.syncSync();
510
- }
500
+ this.stats.size = length;
501
+ // Truncate.
502
+ this._buffer = this._buffer.slice(0, length);
503
+ this.syncSync();
511
504
  }
512
505
 
513
506
  /**
@@ -542,20 +535,21 @@ export class PreloadFile<FS extends FileSystem> extends File {
542
535
  * @returns bytes written
543
536
  */
544
537
  public writeSync(buffer: Uint8Array, offset: number = 0, length: number = this.stats.size, position: number = 0): number {
545
- this._dirty = true;
538
+ this.dirty = true;
546
539
  position ??= this.position;
547
540
  if (!isWriteable(this.flag)) {
548
541
  throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
549
542
  }
550
- const endFp = position + length;
551
- if (endFp > this.stats.size) {
552
- this.stats.size = endFp;
553
- if (endFp > this._buffer.byteLength) {
554
- if (this._buffer.buffer.resizable && this._buffer.buffer.maxByteLength! <= endFp) {
555
- this._buffer.buffer.resize(endFp);
543
+ const end = position + length;
544
+
545
+ if (end > this.stats.size) {
546
+ this.stats.size = end;
547
+ if (end > this._buffer.byteLength) {
548
+ if (this._buffer.buffer.resizable && this._buffer.buffer.maxByteLength! <= end) {
549
+ this._buffer.buffer.resize(end);
556
550
  } else {
557
551
  // Extend the buffer!
558
- const newBuffer = new Uint8Array(new ArrayBuffer(endFp, { maxByteLength: size_max }));
552
+ const newBuffer = new Uint8Array(new ArrayBuffer(end, this.fs.metadata().noResizableBuffers ? {} : { maxByteLength: size_max }));
559
553
  newBuffer.set(this._buffer);
560
554
  this._buffer = newBuffer;
561
555
  }
@@ -565,11 +559,8 @@ export class PreloadFile<FS extends FileSystem> extends File {
565
559
  this._buffer.set(slice, position);
566
560
  const bytesWritten = slice.byteLength;
567
561
  this.stats.mtimeMs = Date.now();
568
- if (isSynchronous(this.flag)) {
569
- this.syncSync();
570
- return bytesWritten;
571
- }
572
562
  this.position = position + bytesWritten;
563
+ this.syncSync();
573
564
  return bytesWritten;
574
565
  }
575
566
 
@@ -608,6 +599,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
608
599
  if (!isReadable(this.flag)) {
609
600
  throw new ErrnoError(Errno.EPERM, 'File not opened with a readable mode.');
610
601
  }
602
+ this.dirty = true;
611
603
  position ??= this.position;
612
604
  let end = position + length;
613
605
  if (end > this.stats.size) {
@@ -616,6 +608,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
616
608
  this.stats.atimeMs = Date.now();
617
609
  this._position = end;
618
610
  const bytesRead = end - position;
611
+ this.syncSync();
619
612
  if (bytesRead == 0) {
620
613
  // No copy/read. Return immediatly for better performance
621
614
  return bytesRead;
@@ -637,7 +630,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
637
630
  * @param mode
638
631
  */
639
632
  public chmodSync(mode: number): void {
640
- this._dirty = true;
633
+ this.dirty = true;
641
634
  this.stats.chmod(mode);
642
635
  this.syncSync();
643
636
  }
@@ -657,7 +650,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
657
650
  * @param gid
658
651
  */
659
652
  public chownSync(uid: number, gid: number): void {
660
- this._dirty = true;
653
+ this.dirty = true;
661
654
  this.stats.chown(uid, gid);
662
655
  this.syncSync();
663
656
  }
@@ -667,31 +660,20 @@ export class PreloadFile<FS extends FileSystem> extends File {
667
660
  }
668
661
 
669
662
  public utimesSync(atime: Date, mtime: Date): void {
670
- this._dirty = true;
663
+ this.dirty = true;
671
664
  this.stats.atime = atime;
672
665
  this.stats.mtime = mtime;
673
666
  this.syncSync();
674
667
  }
675
668
 
676
- protected isDirty(): boolean {
677
- return this._dirty;
678
- }
679
-
680
- /**
681
- * Resets the dirty bit. Should only be called after a sync has completed successfully.
682
- */
683
- protected resetDirty() {
684
- this._dirty = false;
685
- }
686
-
687
669
  public _setType(type: FileType): Promise<void> {
688
- this._dirty = true;
670
+ this.dirty = true;
689
671
  this.stats.mode = (this.stats.mode & ~S_IFMT) | type;
690
672
  return this.sync();
691
673
  }
692
674
 
693
675
  public _setTypeSync(type: FileType): void {
694
- this._dirty = true;
676
+ this.dirty = true;
695
677
  this.stats.mode = (this.stats.mode & ~S_IFMT) | type;
696
678
  this.syncSync();
697
679
  }