@zenfs/core 1.2.9 → 1.3.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/fetch.js +1 -1
- package/dist/backends/memory.d.ts +1 -2
- package/dist/backends/overlay.js +2 -3
- package/dist/backends/port/fs.d.ts +2 -15
- package/dist/backends/store/fs.d.ts +17 -28
- package/dist/backends/store/fs.js +169 -191
- package/dist/backends/store/simple.d.ts +20 -21
- package/dist/backends/store/simple.js +24 -24
- package/dist/backends/store/store.d.ts +22 -23
- package/dist/backends/store/store.js +6 -6
- package/dist/config.d.ts +8 -0
- package/dist/config.js +2 -1
- package/dist/devices.d.ts +2 -3
- package/dist/emulation/cache.d.ts +40 -31
- package/dist/emulation/cache.js +62 -53
- package/dist/emulation/index.d.ts +1 -1
- package/dist/emulation/index.js +1 -1
- package/dist/emulation/promises.js +18 -17
- package/dist/emulation/shared.d.ts +6 -0
- package/dist/emulation/shared.js +13 -1
- package/dist/emulation/sync.d.ts +1 -1
- package/dist/emulation/sync.js +16 -15
- package/dist/file.d.ts +3 -15
- package/dist/file.js +7 -19
- package/dist/inode.d.ts +4 -13
- package/dist/inode.js +22 -29
- package/dist/mixins/async.d.ts +15 -10
- package/dist/mixins/async.js +3 -1
- package/dist/stats.js +30 -5
- package/dist/utils.d.ts +5 -7
- package/dist/utils.js +11 -20
- package/package.json +1 -1
- package/src/backends/fetch.ts +1 -1
- package/src/backends/memory.ts +1 -2
- package/src/backends/overlay.ts +2 -2
- package/src/backends/store/fs.ts +187 -220
- package/src/backends/store/simple.ts +36 -37
- package/src/backends/store/store.ts +25 -26
- package/src/config.ts +11 -1
- package/src/devices.ts +2 -3
- package/src/emulation/cache.ts +68 -60
- package/src/emulation/index.ts +1 -1
- package/src/emulation/promises.ts +20 -19
- package/src/emulation/shared.ts +13 -1
- package/src/emulation/sync.ts +16 -15
- package/src/file.ts +9 -21
- package/src/inode.ts +10 -31
- package/src/mixins/async.ts +27 -24
- package/src/stats.ts +47 -5
- package/src/utils.ts +11 -23
- package/tests/fs/dir.test.ts +21 -31
- package/tests/fs/directory.test.ts +6 -4
- package/tests/fs/links.test.ts +9 -2
- package/tests/fs/permissions.test.ts +2 -2
- package/tests/fs/stat.test.ts +42 -0
- package/tests/fs/times.test.ts +28 -28
- package/tests/setup/cow+fetch.ts +4 -2
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import type { Ino } from '../../inode.js';
|
|
2
1
|
import { SyncTransaction, type Store } from './store.js';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* An interface for simple synchronous stores that don't have special support for transactions and such.
|
|
6
5
|
*/
|
|
7
6
|
export interface SimpleSyncStore extends Store {
|
|
8
|
-
keys(): Iterable<
|
|
9
|
-
get(
|
|
10
|
-
set(
|
|
11
|
-
delete(
|
|
7
|
+
keys(): Iterable<bigint>;
|
|
8
|
+
get(id: bigint): Uint8Array | undefined;
|
|
9
|
+
set(id: bigint, data: Uint8Array): void;
|
|
10
|
+
delete(id: bigint): void;
|
|
12
11
|
}
|
|
13
12
|
|
|
14
13
|
/**
|
|
@@ -18,33 +17,33 @@ export interface SimpleSyncStore extends Store {
|
|
|
18
17
|
export abstract class SimpleAsyncStore implements SimpleSyncStore {
|
|
19
18
|
public abstract name: string;
|
|
20
19
|
|
|
21
|
-
protected cache: Map<
|
|
20
|
+
protected cache: Map<bigint, Uint8Array> = new Map();
|
|
22
21
|
|
|
23
22
|
protected queue: Set<Promise<unknown>> = new Set();
|
|
24
23
|
|
|
25
|
-
protected abstract entries(): Promise<Iterable<[
|
|
24
|
+
protected abstract entries(): Promise<Iterable<[bigint, Uint8Array]>>;
|
|
26
25
|
|
|
27
|
-
public keys(): Iterable<
|
|
26
|
+
public keys(): Iterable<bigint> {
|
|
28
27
|
return this.cache.keys();
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
public get(
|
|
32
|
-
return this.cache.get(
|
|
30
|
+
public get(id: bigint): Uint8Array | undefined {
|
|
31
|
+
return this.cache.get(id);
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
public set(
|
|
36
|
-
this.cache.set(
|
|
37
|
-
this.queue.add(this._set(
|
|
34
|
+
public set(id: bigint, data: Uint8Array): void {
|
|
35
|
+
this.cache.set(id, data);
|
|
36
|
+
this.queue.add(this._set(id, data));
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
protected abstract _set(ino:
|
|
39
|
+
protected abstract _set(ino: bigint, data: Uint8Array): Promise<void>;
|
|
41
40
|
|
|
42
|
-
public delete(
|
|
43
|
-
this.cache.delete(
|
|
44
|
-
this.queue.add(this._delete(
|
|
41
|
+
public delete(id: bigint): void {
|
|
42
|
+
this.cache.delete(id);
|
|
43
|
+
this.queue.add(this._delete(id));
|
|
45
44
|
}
|
|
46
45
|
|
|
47
|
-
protected abstract _delete(ino:
|
|
46
|
+
protected abstract _delete(ino: bigint): Promise<void>;
|
|
48
47
|
|
|
49
48
|
public clearSync(): void {
|
|
50
49
|
this.cache.clear();
|
|
@@ -79,32 +78,32 @@ export class SimpleTransaction extends SyncTransaction<SimpleSyncStore> {
|
|
|
79
78
|
* Stores data in the keys we modify prior to modifying them.
|
|
80
79
|
* Allows us to roll back commits.
|
|
81
80
|
*/
|
|
82
|
-
protected originalData: Map<
|
|
81
|
+
protected originalData: Map<bigint, Uint8Array | void> = new Map();
|
|
83
82
|
/**
|
|
84
83
|
* List of keys modified in this transaction, if any.
|
|
85
84
|
*/
|
|
86
|
-
protected modifiedKeys: Set<
|
|
85
|
+
protected modifiedKeys: Set<bigint> = new Set();
|
|
87
86
|
|
|
88
87
|
protected declare store: SimpleSyncStore;
|
|
89
88
|
|
|
90
|
-
public keysSync(): Iterable<
|
|
89
|
+
public keysSync(): Iterable<bigint> {
|
|
91
90
|
return this.store.keys();
|
|
92
91
|
}
|
|
93
92
|
|
|
94
|
-
public getSync(
|
|
95
|
-
const val = this.store.get(
|
|
96
|
-
this.stashOldValue(
|
|
93
|
+
public getSync(id: bigint): Uint8Array {
|
|
94
|
+
const val = this.store.get(id);
|
|
95
|
+
this.stashOldValue(id, val);
|
|
97
96
|
return val!;
|
|
98
97
|
}
|
|
99
98
|
|
|
100
|
-
public setSync(
|
|
101
|
-
this.markModified(
|
|
102
|
-
return this.store.set(
|
|
99
|
+
public setSync(id: bigint, data: Uint8Array): void {
|
|
100
|
+
this.markModified(id);
|
|
101
|
+
return this.store.set(id, data);
|
|
103
102
|
}
|
|
104
103
|
|
|
105
|
-
public removeSync(
|
|
106
|
-
this.markModified(
|
|
107
|
-
this.store.delete(
|
|
104
|
+
public removeSync(id: bigint): void {
|
|
105
|
+
this.markModified(id);
|
|
106
|
+
this.store.delete(id);
|
|
108
107
|
}
|
|
109
108
|
|
|
110
109
|
public commitSync(): void {
|
|
@@ -135,10 +134,10 @@ export class SimpleTransaction extends SyncTransaction<SimpleSyncStore> {
|
|
|
135
134
|
* prevent needless `get` requests if the program modifies the data later
|
|
136
135
|
* on during the transaction.
|
|
137
136
|
*/
|
|
138
|
-
protected stashOldValue(
|
|
137
|
+
protected stashOldValue(id: bigint, value?: Uint8Array): void {
|
|
139
138
|
// Keep only the earliest value in the transaction.
|
|
140
|
-
if (!this.originalData.has(
|
|
141
|
-
this.originalData.set(
|
|
139
|
+
if (!this.originalData.has(id)) {
|
|
140
|
+
this.originalData.set(id, value);
|
|
142
141
|
}
|
|
143
142
|
}
|
|
144
143
|
|
|
@@ -146,10 +145,10 @@ export class SimpleTransaction extends SyncTransaction<SimpleSyncStore> {
|
|
|
146
145
|
* Marks `ino` as modified, and stashes its value if it has not been
|
|
147
146
|
* stashed already.
|
|
148
147
|
*/
|
|
149
|
-
protected markModified(
|
|
150
|
-
this.modifiedKeys.add(
|
|
151
|
-
if (!this.originalData.has(
|
|
152
|
-
this.originalData.set(
|
|
148
|
+
protected markModified(id: bigint): void {
|
|
149
|
+
this.modifiedKeys.add(id);
|
|
150
|
+
if (!this.originalData.has(id)) {
|
|
151
|
+
this.originalData.set(id, this.store.get(id));
|
|
153
152
|
}
|
|
154
153
|
}
|
|
155
154
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ErrnoError } from '../../error.js';
|
|
2
|
-
import type { Ino } from '../../inode.js';
|
|
3
2
|
import '../../polyfills.js';
|
|
4
3
|
|
|
5
4
|
/**
|
|
@@ -46,52 +45,52 @@ export abstract class Transaction<T extends Store = Store> {
|
|
|
46
45
|
/**
|
|
47
46
|
* Gets all of the keys
|
|
48
47
|
*/
|
|
49
|
-
public abstract keys(): Promise<Iterable<
|
|
48
|
+
public abstract keys(): Promise<Iterable<bigint>>;
|
|
50
49
|
|
|
51
50
|
/**
|
|
52
51
|
* Gets all of the keys
|
|
53
52
|
*/
|
|
54
|
-
public abstract keysSync(): Iterable<
|
|
53
|
+
public abstract keysSync(): Iterable<bigint>;
|
|
55
54
|
|
|
56
55
|
/**
|
|
57
|
-
* Retrieves
|
|
58
|
-
* @param
|
|
56
|
+
* Retrieves data.
|
|
57
|
+
* @param id The key to look under for data.
|
|
59
58
|
*/
|
|
60
|
-
public abstract get(
|
|
59
|
+
public abstract get(id: bigint): Promise<Uint8Array>;
|
|
61
60
|
|
|
62
61
|
/**
|
|
63
|
-
* Retrieves
|
|
62
|
+
* Retrieves data.
|
|
64
63
|
* Throws an error if an error occurs or if the key does not exist.
|
|
65
|
-
* @param
|
|
64
|
+
* @param id The key to look under for data.
|
|
66
65
|
* @return The data stored under the key, or undefined if not present.
|
|
67
66
|
*/
|
|
68
|
-
public abstract getSync(
|
|
67
|
+
public abstract getSync(id: bigint): Uint8Array;
|
|
69
68
|
|
|
70
69
|
/**
|
|
71
|
-
* Adds the data to the store under
|
|
72
|
-
* @param
|
|
70
|
+
* Adds the data to the store under an id. Overwrites any existing data.
|
|
71
|
+
* @param id The key to add the data under.
|
|
73
72
|
* @param data The data to add to the store.
|
|
74
73
|
*/
|
|
75
|
-
public abstract set(
|
|
74
|
+
public abstract set(id: bigint, data: Uint8Array): Promise<void>;
|
|
76
75
|
|
|
77
76
|
/**
|
|
78
|
-
* Adds the data to the store under
|
|
79
|
-
* @param
|
|
77
|
+
* Adds the data to the store under and id.
|
|
78
|
+
* @param id The key to add the data under.
|
|
80
79
|
* @param data The data to add to the store.
|
|
81
80
|
*/
|
|
82
|
-
public abstract setSync(
|
|
81
|
+
public abstract setSync(id: bigint, data: Uint8Array): void;
|
|
83
82
|
|
|
84
83
|
/**
|
|
85
84
|
* Deletes the data at `ino`.
|
|
86
|
-
* @param
|
|
85
|
+
* @param id The key to delete from the store.
|
|
87
86
|
*/
|
|
88
|
-
public abstract remove(
|
|
87
|
+
public abstract remove(id: bigint): Promise<void>;
|
|
89
88
|
|
|
90
89
|
/**
|
|
91
90
|
* Deletes the data at `ino`.
|
|
92
|
-
* @param
|
|
91
|
+
* @param id The key to delete from the store.
|
|
93
92
|
*/
|
|
94
|
-
public abstract removeSync(
|
|
93
|
+
public abstract removeSync(id: bigint): void;
|
|
95
94
|
|
|
96
95
|
/**
|
|
97
96
|
* Commits the transaction.
|
|
@@ -135,19 +134,19 @@ export abstract class Transaction<T extends Store = Store> {
|
|
|
135
134
|
*/
|
|
136
135
|
export abstract class SyncTransaction<T extends Store = Store> extends Transaction<T> {
|
|
137
136
|
/* eslint-disable @typescript-eslint/require-await */
|
|
138
|
-
public async keys(): Promise<Iterable<
|
|
137
|
+
public async keys(): Promise<Iterable<bigint>> {
|
|
139
138
|
return this.keysSync();
|
|
140
139
|
}
|
|
141
|
-
public async get(
|
|
142
|
-
return this.getSync(
|
|
140
|
+
public async get(id: bigint): Promise<Uint8Array> {
|
|
141
|
+
return this.getSync(id);
|
|
143
142
|
}
|
|
144
143
|
|
|
145
|
-
public async set(
|
|
146
|
-
return this.setSync(
|
|
144
|
+
public async set(id: bigint, data: Uint8Array): Promise<void> {
|
|
145
|
+
return this.setSync(id, data);
|
|
147
146
|
}
|
|
148
147
|
|
|
149
|
-
public async remove(
|
|
150
|
-
return this.removeSync(
|
|
148
|
+
public async remove(id: bigint): Promise<void> {
|
|
149
|
+
return this.removeSync(id);
|
|
151
150
|
}
|
|
152
151
|
|
|
153
152
|
public async commit(): Promise<void> {
|
package/src/config.ts
CHANGED
|
@@ -108,6 +108,15 @@ export interface Configuration<T extends ConfigMounts> extends SharedConfig {
|
|
|
108
108
|
*/
|
|
109
109
|
cacheStats: boolean;
|
|
110
110
|
|
|
111
|
+
/**
|
|
112
|
+
* If true, enables caching realpath output
|
|
113
|
+
*
|
|
114
|
+
* This can increase performance.
|
|
115
|
+
* @experimental
|
|
116
|
+
* @default false
|
|
117
|
+
*/
|
|
118
|
+
cachePaths: boolean;
|
|
119
|
+
|
|
111
120
|
/**
|
|
112
121
|
* If true, disables *all* permissions checking.
|
|
113
122
|
*
|
|
@@ -181,7 +190,8 @@ export async function configure<T extends ConfigMounts>(configuration: Partial<C
|
|
|
181
190
|
|
|
182
191
|
Object.assign(credentials, { uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });
|
|
183
192
|
|
|
184
|
-
cache.
|
|
193
|
+
cache.stats.isEnabled = configuration.cacheStats ?? false;
|
|
194
|
+
cache.paths.isEnabled = configuration.cachePaths ?? false;
|
|
185
195
|
config.checkAccess = !configuration.disableAccessChecks;
|
|
186
196
|
config.updateOnRead = !configuration.disableUpdateOnRead;
|
|
187
197
|
config.syncImmediately = !configuration.onlySyncOnClose;
|
package/src/devices.ts
CHANGED
|
@@ -11,7 +11,6 @@ import { File } from './file.js';
|
|
|
11
11
|
import type { StatsLike } from './stats.js';
|
|
12
12
|
import { Stats } from './stats.js';
|
|
13
13
|
import { basename, dirname } from './emulation/path.js';
|
|
14
|
-
import type { Ino } from './inode.js';
|
|
15
14
|
|
|
16
15
|
/**
|
|
17
16
|
* A device
|
|
@@ -28,7 +27,7 @@ export interface Device<TData = any> {
|
|
|
28
27
|
/**
|
|
29
28
|
* Which inode the device is assigned
|
|
30
29
|
*/
|
|
31
|
-
ino:
|
|
30
|
+
ino: bigint;
|
|
32
31
|
|
|
33
32
|
/**
|
|
34
33
|
* Data associated with a device.
|
|
@@ -70,7 +69,7 @@ export interface DeviceDriver<TData = any> {
|
|
|
70
69
|
* @returns `Device.data`
|
|
71
70
|
* @experimental
|
|
72
71
|
*/
|
|
73
|
-
init?(ino:
|
|
72
|
+
init?(ino: bigint): {
|
|
74
73
|
data?: TData;
|
|
75
74
|
minor?: number;
|
|
76
75
|
major?: number;
|
package/src/emulation/cache.ts
CHANGED
|
@@ -3,71 +3,79 @@
|
|
|
3
3
|
import type { Stats } from '../stats.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Used for caching data
|
|
7
|
+
* @internal
|
|
7
8
|
*/
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
export class Cache<T> {
|
|
10
|
+
public isEnabled: boolean = false;
|
|
11
|
+
|
|
12
|
+
protected sync = new Map<string, T>();
|
|
13
|
+
|
|
14
|
+
protected async = new Map<string, Promise<T>>();
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Gets data from the cache, if is exists and the cache is enabled.
|
|
18
|
+
*/
|
|
19
|
+
getSync(path: string): T | undefined {
|
|
20
|
+
if (!this.isEnabled) return;
|
|
21
|
+
|
|
22
|
+
return this.sync.get(path);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Adds data if the cache is enabled
|
|
27
|
+
*/
|
|
28
|
+
setSync(path: string, value: T): void {
|
|
29
|
+
if (!this.isEnabled) return;
|
|
30
|
+
|
|
31
|
+
this.sync.set(path, value);
|
|
32
|
+
this.async.set(path, Promise.resolve(value));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Clears the cache if it is enabled
|
|
37
|
+
*/
|
|
38
|
+
clearSync(): void {
|
|
39
|
+
if (!this.isEnabled) return;
|
|
40
|
+
|
|
41
|
+
this.sync.clear();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Gets data from the cache, if it exists and the cache is enabled.
|
|
46
|
+
*/
|
|
47
|
+
get(path: string): Promise<T> | undefined {
|
|
48
|
+
if (!this.isEnabled) return;
|
|
49
|
+
|
|
50
|
+
return this.async.get(path);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Adds data if the cache is enabled
|
|
55
|
+
*/
|
|
56
|
+
set(path: string, value: Promise<T>): void {
|
|
57
|
+
if (!this.isEnabled) return;
|
|
58
|
+
|
|
59
|
+
this.async.set(path, value);
|
|
60
|
+
void value.then(v => this.sync.set(path, v));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Clears the cache if it is enabled
|
|
65
|
+
*/
|
|
66
|
+
clear(): void {
|
|
67
|
+
if (!this.isEnabled) return;
|
|
68
|
+
|
|
69
|
+
this.async.clear();
|
|
70
|
+
}
|
|
15
71
|
}
|
|
16
72
|
|
|
17
|
-
const statsSync = new Map<string, Stats>();
|
|
18
|
-
|
|
19
73
|
/**
|
|
20
|
-
*
|
|
74
|
+
* Used to cache
|
|
21
75
|
*/
|
|
22
|
-
export
|
|
23
|
-
if (!isEnabled) return;
|
|
24
|
-
|
|
25
|
-
return statsSync.get(path);
|
|
26
|
-
}
|
|
76
|
+
export const stats = new Cache<Stats>();
|
|
27
77
|
|
|
28
78
|
/**
|
|
29
|
-
*
|
|
79
|
+
* Used to cache realpath lookups
|
|
30
80
|
*/
|
|
31
|
-
export
|
|
32
|
-
if (!isEnabled) return;
|
|
33
|
-
|
|
34
|
-
statsSync.set(path, value);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Clears the cache if it is enabled
|
|
39
|
-
*/
|
|
40
|
-
export function clearStatsSync(): void {
|
|
41
|
-
if (!isEnabled) return;
|
|
42
|
-
|
|
43
|
-
statsSync.clear();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const stats = new Map<string, Promise<Stats | undefined>>();
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Gets stats from the cache, if they exist and the cache is enabled.
|
|
50
|
-
*/
|
|
51
|
-
export function getStats(path: string): Promise<Stats | undefined> | undefined {
|
|
52
|
-
if (!isEnabled) return;
|
|
53
|
-
|
|
54
|
-
return stats.get(path);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Adds stats if the cache is enabled
|
|
59
|
-
*/
|
|
60
|
-
export function setStats(path: string, value: Promise<Stats | undefined>): void {
|
|
61
|
-
if (!isEnabled) return;
|
|
62
|
-
|
|
63
|
-
stats.set(path, value);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Clears the cache if it is enabled
|
|
68
|
-
*/
|
|
69
|
-
export function clearStats(): void {
|
|
70
|
-
if (!isEnabled) return;
|
|
71
|
-
|
|
72
|
-
stats.clear();
|
|
73
|
-
}
|
|
81
|
+
export const paths = new Cache<string>();
|
package/src/emulation/index.ts
CHANGED
|
@@ -4,5 +4,5 @@ export * as promises from './promises.js';
|
|
|
4
4
|
export * as constants from './constants.js';
|
|
5
5
|
export * from './streams.js';
|
|
6
6
|
export * from './dir.js';
|
|
7
|
-
export { mountObject, mounts, mount, umount } from './shared.js';
|
|
7
|
+
export { mountObject, mounts, mount, umount, _synced } from './shared.js';
|
|
8
8
|
export { Stats, StatsFs, BigIntStatsFs } from '../stats.js';
|
|
@@ -471,7 +471,7 @@ export async function unlink(path: fs.PathLike): Promise<void> {
|
|
|
471
471
|
path = normalizePath(path);
|
|
472
472
|
const { fs, path: resolved } = resolveMount(path);
|
|
473
473
|
try {
|
|
474
|
-
if (config.checkAccess && !(await (cache.
|
|
474
|
+
if (config.checkAccess && !(await (cache.stats.get(path) || fs.stat(resolved)))!.hasAccess(constants.W_OK)) {
|
|
475
475
|
throw ErrnoError.With('EACCES', resolved, 'unlink');
|
|
476
476
|
}
|
|
477
477
|
await fs.unlink(resolved);
|
|
@@ -623,7 +623,7 @@ export async function rmdir(path: fs.PathLike): Promise<void> {
|
|
|
623
623
|
path = await realpath(path);
|
|
624
624
|
const { fs, path: resolved } = resolveMount(path);
|
|
625
625
|
try {
|
|
626
|
-
const stats = await (cache.
|
|
626
|
+
const stats = await (cache.stats.get(path) || fs.stat(resolved));
|
|
627
627
|
if (!stats) {
|
|
628
628
|
throw ErrnoError.With('ENOENT', path, 'rmdir');
|
|
629
629
|
}
|
|
@@ -718,8 +718,8 @@ export async function readdir(
|
|
|
718
718
|
|
|
719
719
|
const { fs, path: resolved } = resolveMount(path);
|
|
720
720
|
|
|
721
|
-
const _stats = cache.
|
|
722
|
-
cache.
|
|
721
|
+
const _stats = cache.stats.get(path) || fs.stat(resolved).catch(handleError);
|
|
722
|
+
cache.stats.set(path, _stats);
|
|
723
723
|
const stats = await _stats;
|
|
724
724
|
|
|
725
725
|
if (!stats) {
|
|
@@ -740,8 +740,8 @@ export async function readdir(
|
|
|
740
740
|
const addEntry = async (entry: string) => {
|
|
741
741
|
let entryStats: Stats | undefined;
|
|
742
742
|
if (options?.recursive || options?.withFileTypes) {
|
|
743
|
-
const _entryStats = cache.
|
|
744
|
-
cache.
|
|
743
|
+
const _entryStats = cache.stats.get(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
|
|
744
|
+
cache.stats.set(join(path, entry), _entryStats);
|
|
745
745
|
entryStats = await _entryStats;
|
|
746
746
|
}
|
|
747
747
|
if (options?.withFileTypes) {
|
|
@@ -768,7 +768,7 @@ export async function readdir(
|
|
|
768
768
|
};
|
|
769
769
|
await Promise.all(entries.map(addEntry));
|
|
770
770
|
if (!options?._isIndirect) {
|
|
771
|
-
cache.
|
|
771
|
+
cache.stats.clear();
|
|
772
772
|
}
|
|
773
773
|
|
|
774
774
|
return values as string[] | Dirent[];
|
|
@@ -892,16 +892,19 @@ export async function realpath(path: fs.PathLike, options?: fs.EncodingOption |
|
|
|
892
892
|
export async function realpath(path: fs.PathLike, options?: fs.EncodingOption | BufferEncoding | fs.BufferEncodingOption): Promise<string | Buffer> {
|
|
893
893
|
path = normalizePath(path);
|
|
894
894
|
const { base, dir } = parse(path);
|
|
895
|
-
const lpath = join(dir == '/' ? '/' : await realpath(dir), base);
|
|
895
|
+
const lpath = join(dir == '/' ? '/' : await (cache.paths.get(dir) || realpath(dir)), base);
|
|
896
896
|
const { fs, path: resolvedPath, mountPoint } = resolveMount(lpath);
|
|
897
897
|
|
|
898
898
|
try {
|
|
899
|
-
const
|
|
900
|
-
|
|
899
|
+
const _stats = cache.stats.get(lpath) || fs.stat(resolvedPath);
|
|
900
|
+
cache.stats.set(lpath, _stats);
|
|
901
|
+
if (!(await _stats).isSymbolicLink()) {
|
|
901
902
|
return lpath;
|
|
902
903
|
}
|
|
903
904
|
|
|
904
|
-
|
|
905
|
+
const target = mountPoint + (await readlink(lpath));
|
|
906
|
+
|
|
907
|
+
return await (cache.paths.get(target) || realpath(target));
|
|
905
908
|
} catch (e) {
|
|
906
909
|
if ((e as ErrnoError).code == 'ENOENT') {
|
|
907
910
|
return path;
|
|
@@ -965,20 +968,18 @@ access satisfies typeof promises.access;
|
|
|
965
968
|
export async function rm(path: fs.PathLike, options?: fs.RmOptions & InternalOptions) {
|
|
966
969
|
path = normalizePath(path);
|
|
967
970
|
|
|
968
|
-
const
|
|
969
|
-
cache.getStats(path) ||
|
|
971
|
+
const stats = await (cache.stats.get(path) ||
|
|
970
972
|
stat(path).catch((error: ErrnoError) => {
|
|
971
973
|
if (error.code == 'ENOENT' && options?.force) return undefined;
|
|
972
974
|
throw error;
|
|
973
|
-
});
|
|
974
|
-
|
|
975
|
-
cache.setStats(path, _stats);
|
|
976
|
-
const stats = await _stats;
|
|
975
|
+
}));
|
|
977
976
|
|
|
978
977
|
if (!stats) {
|
|
979
978
|
return;
|
|
980
979
|
}
|
|
981
980
|
|
|
981
|
+
cache.stats.setSync(path, stats);
|
|
982
|
+
|
|
982
983
|
switch (stats.mode & constants.S_IFMT) {
|
|
983
984
|
case constants.S_IFDIR:
|
|
984
985
|
if (options?.recursive) {
|
|
@@ -998,12 +999,12 @@ export async function rm(path: fs.PathLike, options?: fs.RmOptions & InternalOpt
|
|
|
998
999
|
case constants.S_IFIFO:
|
|
999
1000
|
case constants.S_IFSOCK:
|
|
1000
1001
|
default:
|
|
1001
|
-
cache.
|
|
1002
|
+
cache.stats.clear();
|
|
1002
1003
|
throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
|
|
1003
1004
|
}
|
|
1004
1005
|
|
|
1005
1006
|
if (!options?._isIndirect) {
|
|
1006
|
-
cache.
|
|
1007
|
+
cache.stats.clear();
|
|
1007
1008
|
}
|
|
1008
1009
|
}
|
|
1009
1010
|
rm satisfies typeof promises.rm;
|
package/src/emulation/shared.ts
CHANGED
|
@@ -83,6 +83,16 @@ export function resolveMount(path: string): { fs: FileSystem; path: string; moun
|
|
|
83
83
|
throw new ErrnoError(Errno.EIO, 'ZenFS not initialized with a file system');
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Wait for all file systems to be ready and synced.
|
|
88
|
+
* May be removed at some point.
|
|
89
|
+
* @experimental @internal
|
|
90
|
+
*/
|
|
91
|
+
export async function _synced(): Promise<void> {
|
|
92
|
+
await Promise.all([...mounts.values()].map(m => m.ready()));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
86
96
|
/**
|
|
87
97
|
* Reverse maps the paths in text from the mounted FileSystem to the global path
|
|
88
98
|
* @hidden
|
|
@@ -102,7 +112,9 @@ export function fixError<E extends ErrnoError>(e: E, paths: Record<string, strin
|
|
|
102
112
|
if (typeof e.stack == 'string') {
|
|
103
113
|
e.stack = fixPaths(e.stack, paths);
|
|
104
114
|
}
|
|
105
|
-
|
|
115
|
+
try {
|
|
116
|
+
e.message = fixPaths(e.message, paths);
|
|
117
|
+
} catch {}
|
|
106
118
|
return e;
|
|
107
119
|
}
|
|
108
120
|
|