@zenfs/core 2.5.7 → 2.6.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/cow.d.ts +13 -0
- package/dist/backends/cow.js +48 -11
- package/dist/backends/single_buffer.d.ts +2 -2
- package/dist/backends/store/fs.d.ts +6 -6
- package/dist/backends/store/fs.js +14 -10
- package/dist/internal/devices.d.ts +6 -6
- package/dist/internal/filesystem.d.ts +4 -4
- package/dist/internal/index_fs.d.ts +4 -4
- package/dist/internal/index_fs.js +6 -8
- package/dist/internal/inode.js +3 -0
- package/dist/node/async.js +1 -1
- package/dist/node/dir.d.ts +3 -1
- package/dist/node/dir.js +23 -5
- package/dist/node/promises.d.ts +4 -2
- package/dist/node/promises.js +130 -89
- package/dist/node/streams.d.ts +4 -0
- package/dist/node/streams.js +9 -10
- package/dist/node/sync.js +71 -40
- package/dist/utils.d.ts +8 -1
- package/dist/utils.js +26 -7
- package/dist/vfs/async.js +224 -85
- package/dist/vfs/file.d.ts +13 -7
- package/dist/vfs/file.js +661 -357
- package/dist/vfs/shared.d.ts +1 -0
- package/dist/vfs/shared.js +19 -3
- package/dist/vfs/sync.js +214 -85
- package/dist/vfs/vcache.d.ts +66 -0
- package/dist/vfs/vcache.js +249 -0
- package/dist/vfs/vnode.d.ts +66 -0
- package/dist/vfs/vnode.js +200 -0
- package/dist/vfs/watchers.js +4 -2
- package/dist/vfs/xattr.js +2 -2
- package/eslint.shared.js +1 -1
- package/package.json +6 -3
- package/scripts/make-index.js +7 -1
- package/scripts/test.js +28 -21
- package/tests/backend/port.test.ts +3 -3
- package/tests/backend/store.test.ts +49 -0
- package/tests/common/inode.test.ts +8 -0
- package/tests/common.ts +62 -0
- package/tests/fetch/fetch.ts +6 -0
- package/tests/fetch/run.sh +2 -2
- package/tests/fetch/server.js +3 -3
- package/tests/fs/append.test.ts +5 -5
- package/tests/fs/concurrency.test.ts +52 -0
- package/tests/fs/dir.test.ts +13 -15
- package/tests/fs/directory.test.ts +32 -31
- package/tests/fs/errors.test.ts +151 -31
- package/tests/fs/exists.test.ts +4 -4
- package/tests/fs/links.test.ts +4 -11
- package/tests/fs/open.test.ts +4 -4
- package/tests/fs/permissions.test.ts +15 -15
- package/tests/fs/read.test.ts +9 -9
- package/tests/fs/readFile.test.ts +9 -9
- package/tests/fs/rename.test.ts +8 -8
- package/tests/fs/scaling.test.ts +4 -4
- package/tests/fs/stat.test.ts +20 -20
- package/tests/fs/streams.test.ts +9 -6
- package/tests/fs/tempdir.test.ts +33 -10
- package/tests/fs/times.test.ts +11 -11
- package/tests/fs/truncate.test.ts +6 -6
- package/tests/fs/watch.test.ts +107 -53
- package/tests/fs/write.test.ts +10 -10
- package/tests/fs/xattr.test.ts +3 -3
- package/tests/native/run.sh +3 -0
- package/tests/native/setup.ts +230 -0
- package/types/readable-stream.d.ts +2 -2
package/dist/vfs/async.js
CHANGED
|
@@ -1,3 +1,55 @@
|
|
|
1
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
2
|
+
if (value !== null && value !== void 0) {
|
|
3
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
4
|
+
var dispose, inner;
|
|
5
|
+
if (async) {
|
|
6
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
7
|
+
dispose = value[Symbol.asyncDispose];
|
|
8
|
+
}
|
|
9
|
+
if (dispose === void 0) {
|
|
10
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
11
|
+
dispose = value[Symbol.dispose];
|
|
12
|
+
if (async) inner = dispose;
|
|
13
|
+
}
|
|
14
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
15
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
16
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
17
|
+
}
|
|
18
|
+
else if (async) {
|
|
19
|
+
env.stack.push({ async: true });
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
};
|
|
23
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
24
|
+
return function (env) {
|
|
25
|
+
function fail(e) {
|
|
26
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
27
|
+
env.hasError = true;
|
|
28
|
+
}
|
|
29
|
+
var r, s = 0;
|
|
30
|
+
function next() {
|
|
31
|
+
while (r = env.stack.pop()) {
|
|
32
|
+
try {
|
|
33
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
34
|
+
if (r.dispose) {
|
|
35
|
+
var result = r.dispose.call(r.value);
|
|
36
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
37
|
+
}
|
|
38
|
+
else s |= 1;
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
fail(e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
45
|
+
if (env.hasError) throw env.error;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
50
|
+
var e = new Error(message);
|
|
51
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
|
+
});
|
|
1
53
|
import { rethrow, setUVMessage, UV } from 'kerium';
|
|
2
54
|
import { decodeUTF8 } from 'utilium';
|
|
3
55
|
import * as constants from '../constants.js';
|
|
@@ -10,6 +62,7 @@ import { Dirent, ifToDt } from './dir.js';
|
|
|
10
62
|
import { Handle } from './file.js';
|
|
11
63
|
import * as flags from './flags.js';
|
|
12
64
|
import { resolveMount } from './shared.js';
|
|
65
|
+
import { cacheOf, lockPath } from './vcache.js';
|
|
13
66
|
import { emitChange } from './watchers.js';
|
|
14
67
|
/**
|
|
15
68
|
* Resolves the mount and real path for a path.
|
|
@@ -20,15 +73,15 @@ export async function resolve($, path, preserveSymlinks, extra) {
|
|
|
20
73
|
path = resolvePath.call($, path);
|
|
21
74
|
if (preserveSymlinks) {
|
|
22
75
|
const resolved = resolveMount(path, $, extra);
|
|
23
|
-
const stats = await resolved.fs.stat(resolved.path).catch(() => undefined);
|
|
76
|
+
const stats = cacheOf(resolved.fs).get(resolved.path)?.inode ?? (await resolved.fs.stat(resolved.path).catch(() => undefined));
|
|
24
77
|
return { ...resolved, fullPath: path, stats };
|
|
25
78
|
}
|
|
26
79
|
/* Try to resolve it directly. If this works,
|
|
27
80
|
that means we don't need to perform any resolution for parent directories. */
|
|
28
81
|
try {
|
|
29
82
|
const resolved = resolveMount(path, $);
|
|
30
|
-
// Stat it to make sure it exists
|
|
31
|
-
const stats = await resolved.fs.stat(resolved.path);
|
|
83
|
+
// Stat it to make sure it exists. The vnode cache takes precedence since it may have unsynced changes
|
|
84
|
+
const stats = cacheOf(resolved.fs).get(resolved.path)?.inode ?? (await resolved.fs.stat(resolved.path));
|
|
32
85
|
if (!isSymbolicLink(stats)) {
|
|
33
86
|
return { ...resolved, fullPath: path, stats };
|
|
34
87
|
}
|
|
@@ -42,11 +95,12 @@ export async function resolve($, path, preserveSymlinks, extra) {
|
|
|
42
95
|
const realDir = dir == '/' ? '/' : (await resolve($, dir, false, extra)).fullPath;
|
|
43
96
|
const maybePath = join(realDir, base);
|
|
44
97
|
const resolved = resolveMount(maybePath, $);
|
|
45
|
-
const stats =
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
98
|
+
const stats = cacheOf(resolved.fs).get(resolved.path)?.inode
|
|
99
|
+
?? (await resolved.fs.stat(resolved.path).catch((e) => {
|
|
100
|
+
if (e.code == 'ENOENT')
|
|
101
|
+
return;
|
|
102
|
+
throw setUVMessage(Object.assign(e, { syscall: 'stat', path: maybePath, ...extra }));
|
|
103
|
+
}));
|
|
50
104
|
if (!stats)
|
|
51
105
|
return { ...resolved, fullPath: path };
|
|
52
106
|
if (!isSymbolicLink(stats)) {
|
|
@@ -63,33 +117,53 @@ export async function open($, path, opt) {
|
|
|
63
117
|
path = normalizePath(path);
|
|
64
118
|
const mode = normalizeMode(opt.mode, 0o644), flag = flags.parse(opt.flag);
|
|
65
119
|
const $ex = { syscall: 'open', path };
|
|
66
|
-
|
|
120
|
+
// eslint-disable-next-line prefer-const
|
|
121
|
+
let { fs, path: resolved, stats } = await resolve($, path, opt.preserveSymlinks, $ex);
|
|
67
122
|
if (!stats) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
123
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
124
|
+
try {
|
|
125
|
+
if (!(flag & constants.O_CREAT))
|
|
126
|
+
throw UV('ENOENT', $ex);
|
|
127
|
+
// Create the file
|
|
128
|
+
const parentPath = dirname(resolved);
|
|
129
|
+
const parentStats = await fs.stat(parentPath);
|
|
130
|
+
if (checkAccess && !hasAccess($, parentStats, constants.W_OK))
|
|
131
|
+
throw UV('EACCES', 'open', dirname(path));
|
|
132
|
+
if (!isDirectory(parentStats))
|
|
133
|
+
throw UV('ENOTDIR', 'open', dirname(path));
|
|
134
|
+
if (!opt.allowDirectory && mode & constants.S_IFDIR)
|
|
135
|
+
throw UV('EISDIR', 'open', path);
|
|
136
|
+
// Serialize entry creation with other operations on the parent directory
|
|
137
|
+
const _ = __addDisposableResource(env_1, await lockPath(fs, parentPath, 'rw', parentStats), false);
|
|
138
|
+
// Another task may have created the file while we waited for the lock
|
|
139
|
+
stats = cacheOf(fs).get(resolved)?.inode ?? (await fs.stat(resolved).catch(() => undefined));
|
|
140
|
+
if (!stats) {
|
|
141
|
+
const { euid: uid, egid: gid } = contextOf($).credentials;
|
|
142
|
+
const inode = await fs.createFile(resolved, {
|
|
143
|
+
mode,
|
|
144
|
+
uid: parentStats.mode & constants.S_ISUID ? parentStats.uid : uid,
|
|
145
|
+
gid: parentStats.mode & constants.S_ISGID ? parentStats.gid : gid,
|
|
146
|
+
});
|
|
147
|
+
// A new entry in the parent directory, which is a 'rename' event
|
|
148
|
+
emitChange($, 'rename', path);
|
|
149
|
+
return new Handle($, path, resolved, flag, cacheOf(fs).ref(resolved, inode));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch (e_1) {
|
|
153
|
+
env_1.error = e_1;
|
|
154
|
+
env_1.hasError = true;
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
__disposeResources(env_1);
|
|
158
|
+
}
|
|
85
159
|
}
|
|
86
160
|
if (checkAccess && !hasAccess($, stats, flags.toMode(flag)))
|
|
87
161
|
throw UV('EACCES', $ex);
|
|
88
162
|
if (flag & constants.O_EXCL)
|
|
89
163
|
throw UV('EEXIST', $ex);
|
|
90
|
-
const handle = new Handle($, path, fs, resolved, flag, stats);
|
|
91
164
|
if (!opt.allowDirectory && mode & constants.S_IFDIR)
|
|
92
165
|
throw UV('EISDIR', 'open', path);
|
|
166
|
+
const handle = new Handle($, path, resolved, flag, cacheOf(fs).ref(resolved, stats));
|
|
93
167
|
if (flag & constants.O_TRUNC)
|
|
94
168
|
await handle.truncate(0);
|
|
95
169
|
return handle;
|
|
@@ -115,15 +189,28 @@ export async function mkdir(path, options = {}) {
|
|
|
115
189
|
const { mode = 0o777, recursive } = options;
|
|
116
190
|
const { fs, path: resolved } = resolveMount(path, this, { syscall: 'mkdir' });
|
|
117
191
|
const __create = async (path, resolved, parent) => {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
192
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
193
|
+
try {
|
|
194
|
+
if (checkAccess && !hasAccess(this, parent, constants.W_OK))
|
|
195
|
+
throw UV('EACCES', 'mkdir', path);
|
|
196
|
+
const _ = __addDisposableResource(env_2, await lockPath(fs, dirname(resolved), 'rw', parent), false);
|
|
197
|
+
const inode = await fs
|
|
198
|
+
.mkdir(resolved, {
|
|
199
|
+
mode,
|
|
200
|
+
uid: parent.mode & constants.S_ISUID ? parent.uid : uid,
|
|
201
|
+
gid: parent.mode & constants.S_ISGID ? parent.gid : gid,
|
|
202
|
+
})
|
|
203
|
+
.catch(rethrow({ syscall: 'mkdir', path }));
|
|
204
|
+
emitChange(this, 'rename', path);
|
|
205
|
+
return inode;
|
|
206
|
+
}
|
|
207
|
+
catch (e_2) {
|
|
208
|
+
env_2.error = e_2;
|
|
209
|
+
env_2.hasError = true;
|
|
210
|
+
}
|
|
211
|
+
finally {
|
|
212
|
+
__disposeResources(env_2);
|
|
213
|
+
}
|
|
127
214
|
};
|
|
128
215
|
if (!recursive) {
|
|
129
216
|
await __create(path, resolved, await fs.stat(dirname(resolved)));
|
|
@@ -144,7 +231,8 @@ export async function mkdir(path, options = {}) {
|
|
|
144
231
|
}
|
|
145
232
|
export async function readdir(path, options = {}) {
|
|
146
233
|
path = normalizePath(path);
|
|
147
|
-
|
|
234
|
+
// Node reports `readdir` failures as `scandir`
|
|
235
|
+
const $ex = { syscall: 'scandir', path };
|
|
148
236
|
const { fs, path: resolved, stats } = await resolve(this, path, false, $ex);
|
|
149
237
|
if (!stats)
|
|
150
238
|
throw UV('ENOENT', $ex);
|
|
@@ -152,7 +240,21 @@ export async function readdir(path, options = {}) {
|
|
|
152
240
|
throw UV('EACCES', $ex);
|
|
153
241
|
if (!isDirectory(stats))
|
|
154
242
|
throw UV('ENOTDIR', $ex);
|
|
155
|
-
|
|
243
|
+
let entries;
|
|
244
|
+
{
|
|
245
|
+
const env_3 = { stack: [], error: void 0, hasError: false };
|
|
246
|
+
try {
|
|
247
|
+
const _ = __addDisposableResource(env_3, await lockPath(fs, resolved, 'ro', stats), false);
|
|
248
|
+
entries = await fs.readdir(resolved);
|
|
249
|
+
}
|
|
250
|
+
catch (e_3) {
|
|
251
|
+
env_3.error = e_3;
|
|
252
|
+
env_3.hasError = true;
|
|
253
|
+
}
|
|
254
|
+
finally {
|
|
255
|
+
__disposeResources(env_3);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
156
258
|
const values = [];
|
|
157
259
|
const addEntry = async (entry) => {
|
|
158
260
|
const entryStats = await fs.stat(join(resolved, entry)).catch((e) => {
|
|
@@ -178,55 +280,91 @@ export async function readdir(path, options = {}) {
|
|
|
178
280
|
return values;
|
|
179
281
|
}
|
|
180
282
|
export async function rename(oldPath, newPath) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
283
|
+
const env_4 = { stack: [], error: void 0, hasError: false };
|
|
284
|
+
try {
|
|
285
|
+
oldPath = normalizePath(oldPath);
|
|
286
|
+
newPath = normalizePath(newPath);
|
|
287
|
+
const $ex = { syscall: 'rename', path: oldPath, dest: newPath };
|
|
288
|
+
const src = await resolve(this, oldPath, true, $ex);
|
|
289
|
+
const dst = resolveMount(newPath, this, $ex);
|
|
290
|
+
if (src.fs.uuid !== dst.fs.uuid)
|
|
291
|
+
throw UV('EXDEV', $ex);
|
|
292
|
+
// A directory can not be moved inside itself
|
|
293
|
+
if (dst.path.startsWith(src.path + '/'))
|
|
294
|
+
throw UV('EINVAL', $ex);
|
|
295
|
+
if (!src.stats)
|
|
296
|
+
throw UV('ENOENT', $ex);
|
|
297
|
+
const fs = src.fs;
|
|
298
|
+
const oldParent = await fs.stat(dirname(src.path));
|
|
299
|
+
const newParent = await fs.stat(dirname(dst.path));
|
|
300
|
+
const newStats = await fs.stat(dst.path).catch((e) => {
|
|
301
|
+
if (e.code == 'ENOENT')
|
|
302
|
+
return null;
|
|
303
|
+
throw e;
|
|
304
|
+
});
|
|
305
|
+
if (checkAccess && (!hasAccess(this, oldParent, constants.R_OK) || !hasAccess(this, newParent, constants.W_OK)))
|
|
306
|
+
throw UV('EACCES', $ex);
|
|
307
|
+
if (newStats && !isDirectory(src.stats) && isDirectory(newStats))
|
|
308
|
+
throw UV('EISDIR', $ex);
|
|
309
|
+
if (newStats && isDirectory(src.stats) && !isDirectory(newStats))
|
|
310
|
+
throw UV('ENOTDIR', $ex);
|
|
311
|
+
const srcDir = dirname(src.path);
|
|
312
|
+
const dstDir = dirname(dst.path);
|
|
313
|
+
// Lock both parent directories, ordered by inode number to avoid ABBA deadlocks (like Linux's `lock_two_nondirectories`)
|
|
314
|
+
const parents = [
|
|
315
|
+
[srcDir, oldParent],
|
|
316
|
+
[dstDir, newParent],
|
|
317
|
+
];
|
|
318
|
+
if (oldParent.ino > newParent.ino)
|
|
319
|
+
parents.reverse();
|
|
320
|
+
const _first = __addDisposableResource(env_4, await lockPath(fs, parents[0][0], 'rw', parents[0][1]), false);
|
|
321
|
+
const _second = __addDisposableResource(env_4, oldParent.ino == newParent.ino ? null : await lockPath(fs, parents[1][0], 'rw', parents[1][1]), false);
|
|
322
|
+
await src.fs.rename(src.path, dst.path);
|
|
323
|
+
cacheOf(fs).rename(src.path, dst.path);
|
|
324
|
+
// Both names change which entries exist, so both are 'rename' events
|
|
325
|
+
emitChange(this, 'rename', oldPath);
|
|
326
|
+
emitChange(this, 'rename', newPath);
|
|
327
|
+
}
|
|
328
|
+
catch (e_4) {
|
|
329
|
+
env_4.error = e_4;
|
|
330
|
+
env_4.hasError = true;
|
|
331
|
+
}
|
|
332
|
+
finally {
|
|
333
|
+
__disposeResources(env_4);
|
|
334
|
+
}
|
|
209
335
|
}
|
|
210
336
|
export async function link(target, link) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
337
|
+
const env_5 = { stack: [], error: void 0, hasError: false };
|
|
338
|
+
try {
|
|
339
|
+
target = normalizePath(target);
|
|
340
|
+
link = normalizePath(link);
|
|
341
|
+
const $ex = { syscall: 'link', path: target, dest: link };
|
|
342
|
+
const { fs, path: resolved } = resolveMount(target, this, $ex);
|
|
343
|
+
const dst = resolveMount(link, this, $ex);
|
|
344
|
+
if (fs.uuid != dst.fs.uuid)
|
|
345
|
+
throw UV('EXDEV', $ex);
|
|
346
|
+
const stats = await fs.stat(resolved);
|
|
347
|
+
if (checkAccess) {
|
|
348
|
+
if (!hasAccess(this, stats, constants.R_OK))
|
|
349
|
+
throw UV('EACCES', $ex);
|
|
350
|
+
const dirStats = await fs.stat(dirname(resolved));
|
|
351
|
+
if (!hasAccess(this, dirStats, constants.R_OK))
|
|
352
|
+
throw UV('EACCES', $ex);
|
|
353
|
+
const destStats = await fs.stat(dirname(dst.path));
|
|
354
|
+
if (!hasAccess(this, destStats, constants.W_OK))
|
|
355
|
+
throw UV('EACCES', $ex);
|
|
356
|
+
}
|
|
357
|
+
const _ = __addDisposableResource(env_5, await lockPath(fs, dirname(dst.path), 'rw'), false);
|
|
358
|
+
await fs.link(resolved, dst.path);
|
|
359
|
+
cacheOf(fs).link(resolved, dst.path);
|
|
360
|
+
}
|
|
361
|
+
catch (e_5) {
|
|
362
|
+
env_5.error = e_5;
|
|
363
|
+
env_5.hasError = true;
|
|
364
|
+
}
|
|
365
|
+
finally {
|
|
366
|
+
__disposeResources(env_5);
|
|
228
367
|
}
|
|
229
|
-
return await fs.link(resolved, dst.path);
|
|
230
368
|
}
|
|
231
369
|
export async function stat(path, lstat) {
|
|
232
370
|
path = normalizePath.call(this, path);
|
|
@@ -237,7 +375,8 @@ export async function stat(path, lstat) {
|
|
|
237
375
|
else {
|
|
238
376
|
const { base, dir } = parse(path);
|
|
239
377
|
const { fs, path: parent } = await resolve(this, dir, false, extra);
|
|
240
|
-
|
|
378
|
+
const target = base ? join(parent, base) : parent;
|
|
379
|
+
stats = cacheOf(fs).get(target)?.inode ?? (await fs.stat(target).catch(rethrow(extra)));
|
|
241
380
|
}
|
|
242
381
|
if (!stats)
|
|
243
382
|
throw UV('ENOENT', extra);
|
package/dist/vfs/file.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { V_Context } from '../context.js';
|
|
|
2
2
|
import type { FileSystem, StreamOptions } from '../internal/filesystem.js';
|
|
3
3
|
import { type InodeLike } from '../internal/inode.js';
|
|
4
4
|
import '../polyfills.js';
|
|
5
|
+
import type { VNode } from './vnode.js';
|
|
5
6
|
/** @hidden */
|
|
6
7
|
export interface FileReadResult<T extends ArrayBufferView> {
|
|
7
8
|
bytesRead: number;
|
|
@@ -13,11 +14,9 @@ export interface FileReadResult<T extends ArrayBufferView> {
|
|
|
13
14
|
export declare class Handle {
|
|
14
15
|
readonly context: V_Context;
|
|
15
16
|
readonly path: string;
|
|
16
|
-
readonly fs: FileSystem;
|
|
17
17
|
readonly internalPath: string;
|
|
18
18
|
readonly flag: number;
|
|
19
|
-
readonly
|
|
20
|
-
protected _buffer?: Uint8Array;
|
|
19
|
+
readonly vnode: VNode;
|
|
21
20
|
/**
|
|
22
21
|
* Current position
|
|
23
22
|
*/
|
|
@@ -33,20 +32,24 @@ export declare class Handle {
|
|
|
33
32
|
*/
|
|
34
33
|
get position(): number;
|
|
35
34
|
set position(value: number);
|
|
35
|
+
/**
|
|
36
|
+
* The inode for the file. This is shared between all handles for the file via the vnode.
|
|
37
|
+
*/
|
|
38
|
+
get inode(): InodeLike;
|
|
39
|
+
get fs(): FileSystem;
|
|
36
40
|
/**
|
|
37
41
|
* Whether the file has changes which have not been written to the FS
|
|
38
42
|
*/
|
|
39
|
-
protected dirty: boolean;
|
|
43
|
+
protected get dirty(): boolean;
|
|
40
44
|
/**
|
|
41
45
|
* Whether the file is open or closed
|
|
42
46
|
*/
|
|
43
47
|
protected closed: boolean;
|
|
44
48
|
get isClosed(): boolean;
|
|
45
49
|
/**
|
|
46
|
-
* Creates a file
|
|
47
|
-
* Note that, if contents is specified, it will be mutated by the file.
|
|
50
|
+
* Creates a file handle for the vnode.
|
|
48
51
|
*/
|
|
49
|
-
constructor(context: V_Context, path: string,
|
|
52
|
+
constructor(context: V_Context, path: string, internalPath: string, flag: number, vnode: VNode);
|
|
50
53
|
protected get _isSync(): boolean;
|
|
51
54
|
[Symbol.dispose](): void;
|
|
52
55
|
syncSync(): void;
|
|
@@ -127,10 +130,13 @@ export declare class Handle {
|
|
|
127
130
|
utimes(atime: number, mtime: number): Promise<void>;
|
|
128
131
|
/**
|
|
129
132
|
* Create a stream for reading the file.
|
|
133
|
+
* @todo Don't repeat ourselves with the implementation in `FileSystem`
|
|
130
134
|
*/
|
|
131
135
|
streamRead(options: StreamOptions): ReadableStream;
|
|
132
136
|
/**
|
|
133
137
|
* Create a stream for writing the file.
|
|
138
|
+
* Chunks are written to the vnode's cache, then synced when the stream is closed.
|
|
139
|
+
* @todo Don't repeat ourselves with the implementation in `FileSystem`
|
|
134
140
|
*/
|
|
135
141
|
streamWrite(options: StreamOptions): WritableStream;
|
|
136
142
|
}
|