@zenfs/core 2.7.6 → 2.7.7
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/store/fs.js +1 -3
- package/dist/backends/store/store.js +1 -3
- package/dist/config.d.ts +7 -0
- package/dist/config.js +9 -3
- package/dist/internal/error.d.ts +5 -0
- package/dist/internal/error.js +10 -0
- package/dist/internal/inode.d.ts +25 -0
- package/dist/internal/inode.js +75 -10
- package/dist/mixins/async.js +22 -24
- package/dist/node/promises.js +17 -21
- package/dist/node/stats.d.ts +1 -1
- package/dist/node/stats.js +4 -1
- package/dist/node/sync.js +14 -18
- package/dist/polyfills.js +16 -0
- package/dist/vfs/async.js +77 -64
- package/dist/vfs/config.d.ts +10 -1
- package/dist/vfs/config.js +5 -2
- package/dist/vfs/file.js +10 -4
- package/dist/vfs/ioctl.js +2 -2
- package/dist/vfs/shared.d.ts +2 -0
- package/dist/vfs/shared.js +3 -4
- package/dist/vfs/sync.js +77 -60
- package/dist/vfs/vcache.d.ts +14 -14
- package/dist/vfs/vcache.js +65 -57
- package/dist/vfs/watchers.d.ts +1 -0
- package/dist/vfs/watchers.js +2 -4
- package/dist/vfs/xattr.js +18 -17
- package/package.json +2 -3
- package/scripts/make-index.js +1 -1
- package/tests/common/async.test.ts +35 -0
- package/tests/common/config.test.ts +1 -1
- package/tests/common/file_index.test.ts +1 -1
- package/tests/common/mounts.test.ts +12 -0
- package/tests/common/vcache.test.ts +13 -0
- package/tests/fs/concurrency.test.ts +3 -3
- package/tests/fs/links.test.ts +76 -0
- package/tests/fs/permissions.test.ts +232 -0
- package/tests/fs/stat.test.ts +1 -1
- package/tests/fs/streams.test.ts +1 -1
- package/tests/fs/xattr.test.ts +10 -0
package/dist/vfs/async.js
CHANGED
|
@@ -54,15 +54,15 @@ import { rethrow, setUVMessage, UV } from 'kerium';
|
|
|
54
54
|
import { decodeUTF8 } from 'utilium';
|
|
55
55
|
import * as constants from '../constants.js';
|
|
56
56
|
import { contextOf } from '../internal/contexts.js';
|
|
57
|
-
import { hasAccess, isDirectory, isSymbolicLink } from '../internal/inode.js';
|
|
57
|
+
import { assertRemovable, assertSearchable, hasAccess, isDirectory, isSymbolicLink } from '../internal/inode.js';
|
|
58
58
|
import { basename, dirname, join, parse, resolve as resolvePath } from '../path.js';
|
|
59
59
|
import { normalizeMode, normalizePath } from '../utils.js';
|
|
60
|
-
import { checkAccess } from './config.js';
|
|
60
|
+
import { checkAccess, resolveFullWalk } from './config.js';
|
|
61
61
|
import { Dirent, ifToDt } from './dir.js';
|
|
62
62
|
import { Handle } from './file.js';
|
|
63
63
|
import * as flags from './flags.js';
|
|
64
64
|
import { resolveMount } from './shared.js';
|
|
65
|
-
import { cacheOf
|
|
65
|
+
import { cacheOf } from './vcache.js';
|
|
66
66
|
import { emitChange } from './watchers.js';
|
|
67
67
|
/**
|
|
68
68
|
* Resolves the mount and real path for a path.
|
|
@@ -71,38 +71,44 @@ import { emitChange } from './watchers.js';
|
|
|
71
71
|
*/
|
|
72
72
|
export async function resolve($, path, preserveSymlinks, extra) {
|
|
73
73
|
path = resolvePath.call($, path);
|
|
74
|
-
if (preserveSymlinks) {
|
|
75
|
-
const resolved = resolveMount(path, $, extra);
|
|
76
|
-
const stats = cacheOf(resolved.fs).get(resolved.path)?.inode ?? (await resolved.fs.stat(resolved.path).catch(() => undefined));
|
|
77
|
-
return { ...resolved, fullPath: path, stats };
|
|
78
|
-
}
|
|
79
74
|
/* Try to resolve it directly. If this works,
|
|
80
75
|
that means we don't need to perform any resolution for parent directories. */
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
76
|
+
if (!resolveFullWalk)
|
|
77
|
+
try {
|
|
78
|
+
const resolved = resolveMount(path, $);
|
|
79
|
+
const stats = await resolved.cache.stat(resolved.path);
|
|
80
|
+
if (!isSymbolicLink(stats) || preserveSymlinks) {
|
|
81
|
+
return { ...resolved, fullPath: path, stats };
|
|
82
|
+
}
|
|
83
|
+
const target = resolvePath.call($, dirname(path), await readlink.call($, path));
|
|
84
|
+
return await resolve($, target, preserveSymlinks, extra);
|
|
86
85
|
}
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
catch {
|
|
87
|
+
// Go the long way
|
|
88
|
+
}
|
|
89
|
+
const { base, dir } = parse(path);
|
|
90
|
+
const $ex = { syscall: 'stat', path, ...extra };
|
|
91
|
+
let realDir = '/';
|
|
92
|
+
if (dir != '/') {
|
|
93
|
+
const parent = await resolve($, dir, false, extra);
|
|
94
|
+
if (parent.stats && isDirectory(parent.stats))
|
|
95
|
+
assertSearchable($, parent.stats, $ex);
|
|
96
|
+
realDir = parent.fullPath;
|
|
89
97
|
}
|
|
90
|
-
|
|
91
|
-
|
|
98
|
+
else if (base && checkAccess) {
|
|
99
|
+
const root = resolveMount('/', $);
|
|
100
|
+
assertSearchable($, await root.cache.stat(root.path), $ex);
|
|
92
101
|
}
|
|
93
|
-
const { base, dir } = parse(path);
|
|
94
|
-
const realDir = dir == '/' ? '/' : (await resolve($, dir, false, extra)).fullPath;
|
|
95
102
|
const maybePath = join(realDir, base);
|
|
96
103
|
const resolved = resolveMount(maybePath, $);
|
|
97
|
-
const stats =
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}));
|
|
104
|
+
const stats = await resolved.cache.stat(resolved.path).catch((e) => {
|
|
105
|
+
if (e.code == 'ENOENT')
|
|
106
|
+
return;
|
|
107
|
+
throw setUVMessage(Object.assign(e, { syscall: 'stat', path: maybePath, ...extra }));
|
|
108
|
+
});
|
|
103
109
|
if (!stats)
|
|
104
110
|
return { ...resolved, fullPath: maybePath };
|
|
105
|
-
if (!isSymbolicLink(stats)) {
|
|
111
|
+
if (!isSymbolicLink(stats) || preserveSymlinks) {
|
|
106
112
|
return { ...resolved, fullPath: maybePath, stats };
|
|
107
113
|
}
|
|
108
114
|
const target = resolvePath.call($, realDir, await readlink.call($, maybePath));
|
|
@@ -117,7 +123,7 @@ export async function open($, path, opt) {
|
|
|
117
123
|
const mode = normalizeMode(opt.mode, 0o644), flag = flags.parse(opt.flag);
|
|
118
124
|
const $ex = { syscall: 'open', path };
|
|
119
125
|
// eslint-disable-next-line prefer-const
|
|
120
|
-
let { fs, path: resolved, stats } = await resolve($, path, opt.preserveSymlinks, $ex);
|
|
126
|
+
let { fs, cache, path: resolved, stats } = await resolve($, path, opt.preserveSymlinks, $ex);
|
|
121
127
|
if (!stats) {
|
|
122
128
|
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
123
129
|
try {
|
|
@@ -125,16 +131,16 @@ export async function open($, path, opt) {
|
|
|
125
131
|
throw UV('ENOENT', $ex);
|
|
126
132
|
// Create the file
|
|
127
133
|
const parentPath = dirname(resolved);
|
|
128
|
-
const parentStats = await
|
|
129
|
-
if (checkAccess && !hasAccess($, parentStats, constants.W_OK))
|
|
134
|
+
const parentStats = await cache.stat(parentPath, $ex);
|
|
135
|
+
if (checkAccess && !hasAccess($, parentStats, constants.W_OK | constants.X_OK))
|
|
130
136
|
throw UV('EACCES', 'open', dirname(path));
|
|
131
137
|
if (!isDirectory(parentStats))
|
|
132
138
|
throw UV('ENOTDIR', 'open', dirname(path));
|
|
133
139
|
if (!opt.allowDirectory && isDirectory({ mode }))
|
|
134
140
|
throw UV('EISDIR', 'open', path);
|
|
135
141
|
// Serialize entry creation with other operations on the parent directory
|
|
136
|
-
const _ = __addDisposableResource(env_1, await
|
|
137
|
-
stats =
|
|
142
|
+
const _ = __addDisposableResource(env_1, await cache.lock(parentPath, 'rw', parentStats), false);
|
|
143
|
+
stats = await cache.stat(resolved).catch(() => undefined);
|
|
138
144
|
if (!stats) {
|
|
139
145
|
const { euid: uid, egid: gid } = contextOf($).credentials;
|
|
140
146
|
const inode = await fs.createFile(resolved, {
|
|
@@ -143,7 +149,7 @@ export async function open($, path, opt) {
|
|
|
143
149
|
gid: parentStats.mode & constants.S_ISGID ? parentStats.gid : gid,
|
|
144
150
|
});
|
|
145
151
|
emitChange($, 'rename', path);
|
|
146
|
-
return new Handle($, path, resolved, flag,
|
|
152
|
+
return new Handle($, path, resolved, flag, cache.ref(resolved, inode));
|
|
147
153
|
}
|
|
148
154
|
}
|
|
149
155
|
catch (e_1) {
|
|
@@ -160,7 +166,7 @@ export async function open($, path, opt) {
|
|
|
160
166
|
throw UV('EEXIST', $ex);
|
|
161
167
|
if (!opt.allowDirectory && isDirectory({ mode }))
|
|
162
168
|
throw UV('EISDIR', 'open', path);
|
|
163
|
-
const handle = new Handle($, path, resolved, flag,
|
|
169
|
+
const handle = new Handle($, path, resolved, flag, cache.ref(resolved, stats));
|
|
164
170
|
if (flag & constants.O_TRUNC)
|
|
165
171
|
await handle.truncate(0);
|
|
166
172
|
return handle;
|
|
@@ -186,17 +192,17 @@ export async function mkdir(path, options = {}) {
|
|
|
186
192
|
const { mode = 0o777, recursive } = options;
|
|
187
193
|
const $ex = { syscall: 'mkdir', path: original };
|
|
188
194
|
const { fullPath: realParent } = await resolve(this, dirname(original), false, $ex);
|
|
189
|
-
const { fs, path: target } = resolveMount(join(realParent, basename(original)), this, $ex);
|
|
195
|
+
const { fs, cache, path: target } = resolveMount(join(realParent, basename(original)), this, $ex);
|
|
190
196
|
const followed = realParent != dirname(original);
|
|
191
197
|
let firstCreated;
|
|
192
198
|
const __create = async (path, resolved) => {
|
|
193
199
|
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
194
200
|
try {
|
|
195
201
|
const parentPath = dirname(resolved);
|
|
196
|
-
const parent = recursive && parentPath != '/' ? await __create(dirname(path), parentPath) : await
|
|
197
|
-
const _ = __addDisposableResource(env_2, await
|
|
202
|
+
const parent = recursive && parentPath != '/' ? await __create(dirname(path), parentPath) : await cache.stat(parentPath, $ex);
|
|
203
|
+
const _ = __addDisposableResource(env_2, await cache.lock(parentPath, 'rw', parent), false);
|
|
198
204
|
if (recursive) {
|
|
199
|
-
const existing =
|
|
205
|
+
const existing = await cache.stat(resolved).catch(() => null);
|
|
200
206
|
if (existing) {
|
|
201
207
|
const stats = isSymbolicLink(existing) ? (await resolve(this, path, false, $ex)).stats : existing;
|
|
202
208
|
if (!stats)
|
|
@@ -209,7 +215,7 @@ export async function mkdir(path, options = {}) {
|
|
|
209
215
|
if (followed && (await fs.exists(join(parentPath, basename(path)))))
|
|
210
216
|
throw UV('ENOTDIR', 'mkdir', path);
|
|
211
217
|
}
|
|
212
|
-
if (checkAccess && !hasAccess(this, parent, constants.W_OK))
|
|
218
|
+
if (checkAccess && !hasAccess(this, parent, constants.W_OK | constants.X_OK))
|
|
213
219
|
throw UV('EACCES', 'mkdir', path);
|
|
214
220
|
const inode = await fs
|
|
215
221
|
.mkdir(resolved, {
|
|
@@ -237,7 +243,7 @@ export async function mkdir(path, options = {}) {
|
|
|
237
243
|
export async function readdir(path, options = {}) {
|
|
238
244
|
path = normalizePath.call(this, path);
|
|
239
245
|
const $ex = { syscall: 'scandir', path };
|
|
240
|
-
const { fs, path: resolved, stats } = await resolve(this, path, false, $ex);
|
|
246
|
+
const { fs, cache, path: resolved, stats } = await resolve(this, path, false, $ex);
|
|
241
247
|
if (!stats)
|
|
242
248
|
throw UV('ENOENT', $ex);
|
|
243
249
|
if (checkAccess && !hasAccess(this, stats, constants.R_OK))
|
|
@@ -248,7 +254,7 @@ export async function readdir(path, options = {}) {
|
|
|
248
254
|
{
|
|
249
255
|
const env_3 = { stack: [], error: void 0, hasError: false };
|
|
250
256
|
try {
|
|
251
|
-
const _ = __addDisposableResource(env_3, await
|
|
257
|
+
const _ = __addDisposableResource(env_3, await cache.lock(resolved, 'ro', stats), false);
|
|
252
258
|
entries = await fs.readdir(resolved);
|
|
253
259
|
}
|
|
254
260
|
catch (e_3) {
|
|
@@ -261,7 +267,7 @@ export async function readdir(path, options = {}) {
|
|
|
261
267
|
}
|
|
262
268
|
const values = [];
|
|
263
269
|
const addEntry = async (entry) => {
|
|
264
|
-
const entryStats = await
|
|
270
|
+
const entryStats = await cache.stat(join(resolved, entry), $ex).catch((e) => {
|
|
265
271
|
if (e.code == 'ENOENT')
|
|
266
272
|
return;
|
|
267
273
|
throw e;
|
|
@@ -290,7 +296,7 @@ export async function rename(oldPath, newPath) {
|
|
|
290
296
|
newPath = normalizePath.call(this, newPath);
|
|
291
297
|
const $ex = { syscall: 'rename', path: oldPath, dest: newPath };
|
|
292
298
|
const src = await resolve(this, oldPath, true, $ex);
|
|
293
|
-
const dst =
|
|
299
|
+
const dst = await resolve(this, newPath, true, $ex);
|
|
294
300
|
if (src.fs.uuid !== dst.fs.uuid)
|
|
295
301
|
throw UV('EXDEV', $ex);
|
|
296
302
|
// A directory can not be moved inside itself
|
|
@@ -298,22 +304,26 @@ export async function rename(oldPath, newPath) {
|
|
|
298
304
|
throw UV('EINVAL', $ex);
|
|
299
305
|
if (!src.stats)
|
|
300
306
|
throw UV('ENOENT', $ex);
|
|
301
|
-
const
|
|
302
|
-
const
|
|
303
|
-
const
|
|
304
|
-
const
|
|
307
|
+
const srcDir = dirname(src.path);
|
|
308
|
+
const dstDir = dirname(dst.path);
|
|
309
|
+
const oldParent = await src.cache.stat(srcDir, $ex);
|
|
310
|
+
const newParent = await src.cache.stat(dstDir, $ex);
|
|
311
|
+
const newStats = await src.cache.stat(dst.path, $ex).catch((e) => {
|
|
305
312
|
if (e.code == 'ENOENT')
|
|
306
313
|
return null;
|
|
307
314
|
throw e;
|
|
308
315
|
});
|
|
309
|
-
|
|
316
|
+
assertRemovable(this, oldParent, src.stats, $ex);
|
|
317
|
+
if (newStats)
|
|
318
|
+
assertRemovable(this, newParent, newStats, $ex);
|
|
319
|
+
if (checkAccess && !hasAccess(this, newParent, constants.W_OK | constants.X_OK))
|
|
320
|
+
throw UV('EACCES', $ex);
|
|
321
|
+
if (checkAccess && isDirectory(src.stats) && srcDir != dstDir && !hasAccess(this, src.stats, constants.W_OK))
|
|
310
322
|
throw UV('EACCES', $ex);
|
|
311
323
|
if (newStats && !isDirectory(src.stats) && isDirectory(newStats))
|
|
312
324
|
throw UV('EISDIR', $ex);
|
|
313
325
|
if (newStats && isDirectory(src.stats) && !isDirectory(newStats))
|
|
314
326
|
throw UV('ENOTDIR', $ex);
|
|
315
|
-
const srcDir = dirname(src.path);
|
|
316
|
-
const dstDir = dirname(dst.path);
|
|
317
327
|
// Lock both parent directories, ordered by inode number to avoid ABBA deadlocks (like Linux's `lock_two_nondirectories`)
|
|
318
328
|
const parents = [
|
|
319
329
|
[srcDir, oldParent],
|
|
@@ -321,10 +331,10 @@ export async function rename(oldPath, newPath) {
|
|
|
321
331
|
];
|
|
322
332
|
if (oldParent.ino > newParent.ino)
|
|
323
333
|
parents.reverse();
|
|
324
|
-
const _first = __addDisposableResource(env_4, await
|
|
325
|
-
const _second = __addDisposableResource(env_4, oldParent.ino == newParent.ino ? null : await
|
|
334
|
+
const _first = __addDisposableResource(env_4, await src.cache.lock(parents[0][0], 'rw', parents[0][1]), false);
|
|
335
|
+
const _second = __addDisposableResource(env_4, oldParent.ino == newParent.ino ? null : await src.cache.lock(parents[1][0], 'rw', parents[1][1]), false);
|
|
326
336
|
await src.fs.rename(src.path, dst.path);
|
|
327
|
-
|
|
337
|
+
src.cache.rename(src.path, dst.path);
|
|
328
338
|
emitChange(this, 'rename', oldPath);
|
|
329
339
|
emitChange(this, 'rename', newPath);
|
|
330
340
|
}
|
|
@@ -342,24 +352,24 @@ export async function link(target, link) {
|
|
|
342
352
|
target = normalizePath.call(this, target);
|
|
343
353
|
link = normalizePath.call(this, link);
|
|
344
354
|
const $ex = { syscall: 'link', path: target, dest: link };
|
|
345
|
-
const { fs, path: resolved } =
|
|
346
|
-
const dst =
|
|
355
|
+
const { fs, cache, path: resolved } = await resolve(this, target, true, $ex);
|
|
356
|
+
const dst = await resolve(this, link, true, $ex);
|
|
347
357
|
if (fs.uuid != dst.fs.uuid)
|
|
348
358
|
throw UV('EXDEV', $ex);
|
|
349
|
-
const stats = await
|
|
359
|
+
const stats = await cache.stat(resolved, $ex);
|
|
350
360
|
if (checkAccess) {
|
|
351
361
|
if (!hasAccess(this, stats, constants.R_OK))
|
|
352
362
|
throw UV('EACCES', $ex);
|
|
353
|
-
const dirStats = await
|
|
363
|
+
const dirStats = await cache.stat(dirname(resolved), $ex);
|
|
354
364
|
if (!hasAccess(this, dirStats, constants.R_OK))
|
|
355
365
|
throw UV('EACCES', $ex);
|
|
356
|
-
const destStats = await
|
|
366
|
+
const destStats = await cache.stat(dirname(dst.path), $ex);
|
|
357
367
|
if (!hasAccess(this, destStats, constants.W_OK))
|
|
358
368
|
throw UV('EACCES', $ex);
|
|
359
369
|
}
|
|
360
|
-
const _ = __addDisposableResource(env_5, await
|
|
370
|
+
const _ = __addDisposableResource(env_5, await cache.lock(dirname(dst.path), 'rw'), false);
|
|
361
371
|
await fs.link(resolved, dst.path);
|
|
362
|
-
|
|
372
|
+
cache.link(resolved, dst.path);
|
|
363
373
|
}
|
|
364
374
|
catch (e_5) {
|
|
365
375
|
env_5.error = e_5;
|
|
@@ -377,13 +387,16 @@ export async function stat(path, lstat) {
|
|
|
377
387
|
stats = (await resolve(this, path, false, extra)).stats;
|
|
378
388
|
else {
|
|
379
389
|
const { base, dir } = parse(path);
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
390
|
+
const parent = await resolve(this, dir, false, extra);
|
|
391
|
+
if (parent.stats && isDirectory(parent.stats))
|
|
392
|
+
assertSearchable(this, parent.stats, extra);
|
|
393
|
+
const { root, mounts } = contextOf(this);
|
|
394
|
+
const mounted = base && mounts.get(join(root, parent.fullPath, base));
|
|
395
|
+
const fs = mounted || parent.fs;
|
|
396
|
+
const target = mounted ? '/' : join(parent.path, base);
|
|
397
|
+
stats = await cacheOf(fs).stat(target).catch(rethrow(extra));
|
|
383
398
|
}
|
|
384
399
|
if (!stats)
|
|
385
400
|
throw UV('ENOENT', extra);
|
|
386
|
-
if (checkAccess && !hasAccess(this, stats, constants.R_OK))
|
|
387
|
-
throw UV('EACCES', extra);
|
|
388
401
|
return stats;
|
|
389
402
|
}
|
package/dist/vfs/config.d.ts
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
/** Whether to perform access checks */
|
|
2
2
|
export declare let checkAccess: boolean;
|
|
3
|
+
/** If set, path resolution will always do a full walk. More correct but potentially very slow. */
|
|
4
|
+
export declare let resolveFullWalk: boolean;
|
|
3
5
|
/**
|
|
4
6
|
* @internal @hidden
|
|
5
7
|
*/
|
|
6
|
-
export
|
|
8
|
+
export interface _VFSConfig {
|
|
9
|
+
checkAccess: boolean;
|
|
10
|
+
resolveFullWalk: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* @internal @hidden
|
|
14
|
+
*/
|
|
15
|
+
export declare function _setVFSConfig(cfg: _VFSConfig): void;
|
package/dist/vfs/config.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
2
2
|
/** Whether to perform access checks */
|
|
3
3
|
export let checkAccess = true;
|
|
4
|
+
/** If set, path resolution will always do a full walk. More correct but potentially very slow. */
|
|
5
|
+
export let resolveFullWalk = false;
|
|
4
6
|
/**
|
|
5
7
|
* @internal @hidden
|
|
6
8
|
*/
|
|
7
|
-
export function
|
|
8
|
-
checkAccess =
|
|
9
|
+
export function _setVFSConfig(cfg) {
|
|
10
|
+
checkAccess = cfg.checkAccess;
|
|
11
|
+
resolveFullWalk = cfg.resolveFullWalk;
|
|
9
12
|
}
|
package/dist/vfs/file.js
CHANGED
|
@@ -89,7 +89,7 @@ import { UV, withErrno } from 'kerium';
|
|
|
89
89
|
import { memoize } from 'utilium';
|
|
90
90
|
import * as c from '../constants.js';
|
|
91
91
|
import { contextOf } from '../internal/contexts.js';
|
|
92
|
-
import { _chown, InodeFlags, isBlockDevice, isCharacterDevice } from '../internal/inode.js';
|
|
92
|
+
import { _chmod, _chown, assertCanChown, assertOwner, InodeFlags, isBlockDevice, isCharacterDevice } from '../internal/inode.js';
|
|
93
93
|
import '../polyfills.js';
|
|
94
94
|
import { validateFD } from '../utils.js';
|
|
95
95
|
import { cacheOf } from './vcache.js';
|
|
@@ -328,9 +328,10 @@ let Handle = (() => {
|
|
|
328
328
|
try {
|
|
329
329
|
if (this.closed)
|
|
330
330
|
throw UV('EBADF', 'chmod', this.path);
|
|
331
|
+
assertOwner(this.context, this.inode, { syscall: 'chmod', path: this.path });
|
|
331
332
|
const _ = __addDisposableResource(env_5, this.vnode.lockSync('rw'), false);
|
|
332
333
|
this.vnode.metadataDirty = true;
|
|
333
|
-
this.
|
|
334
|
+
_chmod(this.context, this.inode, mode);
|
|
334
335
|
if (this._isSync || mode > c.S_IFMT)
|
|
335
336
|
this.vnode.syncSync();
|
|
336
337
|
}
|
|
@@ -346,7 +347,8 @@ let Handle = (() => {
|
|
|
346
347
|
const env_6 = { stack: [], error: void 0, hasError: false };
|
|
347
348
|
try {
|
|
348
349
|
if (this.closed)
|
|
349
|
-
throw UV('EBADF', '
|
|
350
|
+
throw UV('EBADF', 'chown', this.path);
|
|
351
|
+
assertCanChown(this.context, this.inode, uid, gid, { syscall: 'chown', path: this.path });
|
|
350
352
|
const _ = __addDisposableResource(env_6, this.vnode.lockSync('rw'), false);
|
|
351
353
|
this.vnode.metadataDirty = true;
|
|
352
354
|
_chown(this.inode, uid, gid);
|
|
@@ -369,6 +371,7 @@ let Handle = (() => {
|
|
|
369
371
|
try {
|
|
370
372
|
if (this.closed)
|
|
371
373
|
throw UV('EBADF', 'utimes', this.path);
|
|
374
|
+
assertOwner(this.context, this.inode, { syscall: 'utimes', path: this.path });
|
|
372
375
|
const _ = __addDisposableResource(env_7, this.vnode.lockSync('rw'), false);
|
|
373
376
|
this.vnode.metadataDirty = true;
|
|
374
377
|
this.inode.atimeMs = atime;
|
|
@@ -542,9 +545,10 @@ let Handle = (() => {
|
|
|
542
545
|
try {
|
|
543
546
|
if (this.closed)
|
|
544
547
|
throw UV('EBADF', 'chmod', this.path);
|
|
548
|
+
assertOwner(this.context, this.inode, { syscall: 'chmod', path: this.path });
|
|
545
549
|
const _ = __addDisposableResource(env_12, await this.vnode.lock('rw'), false);
|
|
546
550
|
this.vnode.metadataDirty = true;
|
|
547
|
-
this.
|
|
551
|
+
_chmod(this.context, this.inode, mode);
|
|
548
552
|
if (this._isSync || mode > c.S_IFMT)
|
|
549
553
|
await this.vnode.sync();
|
|
550
554
|
}
|
|
@@ -561,6 +565,7 @@ let Handle = (() => {
|
|
|
561
565
|
try {
|
|
562
566
|
if (this.closed)
|
|
563
567
|
throw UV('EBADF', 'chown', this.path);
|
|
568
|
+
assertCanChown(this.context, this.inode, uid, gid, { syscall: 'chown', path: this.path });
|
|
564
569
|
const _ = __addDisposableResource(env_13, await this.vnode.lock('rw'), false);
|
|
565
570
|
this.vnode.metadataDirty = true;
|
|
566
571
|
_chown(this.inode, uid, gid);
|
|
@@ -583,6 +588,7 @@ let Handle = (() => {
|
|
|
583
588
|
try {
|
|
584
589
|
if (this.closed)
|
|
585
590
|
throw UV('EBADF', 'utimes', this.path);
|
|
591
|
+
assertOwner(this.context, this.inode, { syscall: 'utimes', path: this.path });
|
|
586
592
|
const _ = __addDisposableResource(env_14, await this.vnode.lock('rw'), false);
|
|
587
593
|
this.vnode.metadataDirty = true;
|
|
588
594
|
this.inode.atimeMs = atime;
|
package/dist/vfs/ioctl.js
CHANGED
|
@@ -30,7 +30,7 @@ export async function ioctl(path, command, ...args) {
|
|
|
30
30
|
const mnt = await resolveAsync(this, path, false, { syscall: 'ioctl', path });
|
|
31
31
|
if (!mnt.stats)
|
|
32
32
|
throw UV('ENOENT', { syscall: 'ioctl', path });
|
|
33
|
-
vcache =
|
|
33
|
+
vcache = mnt.cache;
|
|
34
34
|
vnode = vcache.ref(mnt.path, vcache.get(mnt.path)?.inode ?? mnt.stats);
|
|
35
35
|
}
|
|
36
36
|
const context = {
|
|
@@ -68,7 +68,7 @@ export function ioctlSync(path, command, ...args) {
|
|
|
68
68
|
const mnt = resolveSync(this, path, false, { syscall: 'ioctl', path });
|
|
69
69
|
if (!mnt.stats)
|
|
70
70
|
throw UV('ENOENT', { syscall: 'ioctl', path });
|
|
71
|
-
vcache =
|
|
71
|
+
vcache = mnt.cache;
|
|
72
72
|
vnode = vcache.ref(mnt.path, vcache.get(mnt.path)?.inode ?? mnt.stats);
|
|
73
73
|
}
|
|
74
74
|
const context = {
|
package/dist/vfs/shared.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { FileSystem } from '../internal/filesystem.js';
|
|
|
4
4
|
import type { InodeLike } from '../internal/inode.js';
|
|
5
5
|
import { type ExceptionExtra } from 'kerium';
|
|
6
6
|
import { type AbsolutePath } from '../path.js';
|
|
7
|
+
import { VCache } from './vcache.js';
|
|
7
8
|
/**
|
|
8
9
|
* @internal @hidden
|
|
9
10
|
*/
|
|
@@ -33,6 +34,7 @@ export declare function umount(this: V_Context, mountPoint: string): void;
|
|
|
33
34
|
*/
|
|
34
35
|
export interface ResolvedMount {
|
|
35
36
|
fs: FileSystem;
|
|
37
|
+
cache: VCache;
|
|
36
38
|
path: string;
|
|
37
39
|
mountPoint: string;
|
|
38
40
|
root: string;
|
package/dist/vfs/shared.js
CHANGED
|
@@ -9,7 +9,7 @@ import { credentialsAllowRoot } from '../internal/credentials.js';
|
|
|
9
9
|
import { withExceptionContext } from '../internal/error.js';
|
|
10
10
|
import { join, resolve } from '../path.js';
|
|
11
11
|
import { normalizePath } from '../utils.js';
|
|
12
|
-
import { caches, VCache } from './vcache.js';
|
|
12
|
+
import { cacheOf, caches, VCache } from './vcache.js';
|
|
13
13
|
/**
|
|
14
14
|
* The map of mount points
|
|
15
15
|
* @category Backends and Configuration
|
|
@@ -34,8 +34,7 @@ export function mount(mountPoint, fs) {
|
|
|
34
34
|
throw err(withErrno('EINVAL', 'Mount point is already in use: ' + mountPoint));
|
|
35
35
|
fs._mountPoint = mountPoint;
|
|
36
36
|
$.mounts.set(mountPoint, fs);
|
|
37
|
-
|
|
38
|
-
caches.set(fs.uuid, new VCache(fs));
|
|
37
|
+
caches.getOrInsertComputed(fs.uuid, () => new VCache(fs));
|
|
39
38
|
info(`Mounted ${fs.name} on ${mountPoint}`);
|
|
40
39
|
debug(`${fs.name} attributes: ${[...fs.attributes].map(([k, v]) => (v !== undefined && v !== null ? k + '=' + v : k)).join(', ')}`);
|
|
41
40
|
}
|
|
@@ -91,7 +90,7 @@ export function resolveMount(path, ctx, extra) {
|
|
|
91
90
|
path = path.toLowerCase();
|
|
92
91
|
if (case_fold === 'upper')
|
|
93
92
|
path = path.toUpperCase();
|
|
94
|
-
return { fs: withExceptionContext(fs, _exceptionContext), path, mountPoint, root };
|
|
93
|
+
return { fs: withExceptionContext(fs, _exceptionContext), cache: cacheOf(fs), path, mountPoint, root };
|
|
95
94
|
}
|
|
96
95
|
throw alert(new Exception(Errno.EIO, 'No file system for ' + path));
|
|
97
96
|
}
|