@zenfs/core 2.4.3 → 2.4.4
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/context.d.ts +1 -1
- package/dist/context.js +10 -19
- package/dist/internal/contexts.d.ts +24 -4
- package/dist/internal/contexts.js +40 -0
- package/dist/internal/inode.js +3 -3
- package/dist/node/sync.js +1 -1
- package/dist/path.js +3 -3
- package/dist/utils.d.ts +3 -1
- package/dist/utils.js +2 -1
- package/dist/vfs/acl.js +3 -3
- package/dist/vfs/async.js +4 -3
- package/dist/vfs/file.js +4 -4
- package/dist/vfs/shared.js +5 -4
- package/dist/vfs/sync.js +4 -3
- package/dist/vfs/watchers.d.ts +1 -1
- package/dist/vfs/watchers.js +8 -5
- package/package.json +1 -1
- package/tests/common/context.test.ts +2 -4
- package/tests/fs/links.test.ts +1 -2
- package/tests/fs/read.test.ts +1 -2
package/dist/context.d.ts
CHANGED
|
@@ -11,4 +11,4 @@ export declare const boundContexts: Map<number, BoundContext>;
|
|
|
11
11
|
* Note that the default credentials of a bound context are copied from the global credentials.
|
|
12
12
|
* @category Contexts
|
|
13
13
|
*/
|
|
14
|
-
export declare function bindContext(this:
|
|
14
|
+
export declare function bindContext(this: V_Context, init?: ContextInit): BoundContext;
|
package/dist/context.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
2
|
+
import { UV } from 'kerium';
|
|
2
3
|
import { bindFunctions } from 'utilium';
|
|
3
|
-
import {
|
|
4
|
-
import { createCredentials } from './internal/credentials.js';
|
|
5
|
-
import * as path from './path.js';
|
|
4
|
+
import { contextOf, createChildContext } from './internal/contexts.js';
|
|
6
5
|
import * as fs from './node/index.js';
|
|
6
|
+
import * as path from './path.js';
|
|
7
7
|
import * as xattr from './vfs/xattr.js';
|
|
8
|
-
// 0 is reserved for the global/default context
|
|
9
|
-
let _nextId = 1;
|
|
10
8
|
/**
|
|
11
9
|
* A map of all contexts.
|
|
12
10
|
* @internal
|
|
@@ -18,19 +16,12 @@ export const boundContexts = new Map();
|
|
|
18
16
|
* Note that the default credentials of a bound context are copied from the global credentials.
|
|
19
17
|
* @category Contexts
|
|
20
18
|
*/
|
|
21
|
-
export function bindContext(
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
credentials: createCredentials(credentials),
|
|
28
|
-
descriptors: new Map(),
|
|
29
|
-
parent,
|
|
30
|
-
children: [],
|
|
31
|
-
};
|
|
32
|
-
const bound = {
|
|
33
|
-
...ctx,
|
|
19
|
+
export function bindContext(init = {}) {
|
|
20
|
+
const $ = contextOf(this);
|
|
21
|
+
if (!fs.statSync.call(this, $.root).isDirectory())
|
|
22
|
+
throw UV('ENOTDIR', { syscall: 'chroot', path: $.root });
|
|
23
|
+
const ctx = createChildContext($, init);
|
|
24
|
+
const bound = Object.assign(ctx, {
|
|
34
25
|
fs: {
|
|
35
26
|
...bindFunctions(fs, ctx),
|
|
36
27
|
promises: bindFunctions(fs.promises, ctx),
|
|
@@ -42,7 +33,7 @@ export function bindContext({ root = this?.root || '/', pwd = this?.pwd || '/',
|
|
|
42
33
|
ctx.children.push(child);
|
|
43
34
|
return child;
|
|
44
35
|
},
|
|
45
|
-
};
|
|
36
|
+
});
|
|
46
37
|
boundContexts.set(ctx.id, bound);
|
|
47
38
|
return bound;
|
|
48
39
|
}
|
|
@@ -4,11 +4,18 @@ import type * as path from '../path.js';
|
|
|
4
4
|
import type { Handle } from '../vfs/file.js';
|
|
5
5
|
import type * as xattr from '../vfs/xattr.js';
|
|
6
6
|
import type { Credentials, CredentialsInit } from './credentials.js';
|
|
7
|
+
/**
|
|
8
|
+
* Symbol used for context branding
|
|
9
|
+
* @internal @hidden
|
|
10
|
+
*/
|
|
11
|
+
declare const kIsContext: unique symbol;
|
|
7
12
|
/**
|
|
8
13
|
* A context used for FS operations
|
|
9
14
|
* @category Contexts
|
|
10
15
|
*/
|
|
11
16
|
export interface FSContext {
|
|
17
|
+
/** The unique ID of the context */
|
|
18
|
+
readonly [kIsContext]: boolean;
|
|
12
19
|
/** The unique ID of the context */
|
|
13
20
|
readonly id: number;
|
|
14
21
|
/**
|
|
@@ -22,16 +29,16 @@ export interface FSContext {
|
|
|
22
29
|
/** The credentials of the context, used for access checks */
|
|
23
30
|
readonly credentials: Credentials;
|
|
24
31
|
/** A map of open file descriptors to their handles */
|
|
25
|
-
descriptors: Map<number, Handle>;
|
|
32
|
+
readonly descriptors: Map<number, Handle>;
|
|
26
33
|
/** The parent context, if any. */
|
|
27
|
-
parent:
|
|
34
|
+
readonly parent: FSContext | null;
|
|
28
35
|
/** The child contexts */
|
|
29
|
-
children: FSContext[];
|
|
36
|
+
readonly children: FSContext[];
|
|
30
37
|
}
|
|
31
38
|
/**
|
|
32
39
|
* maybe an FS context
|
|
33
40
|
*/
|
|
34
|
-
export type V_Context =
|
|
41
|
+
export type V_Context = unknown;
|
|
35
42
|
/**
|
|
36
43
|
* Allows you to restrict operations to a specific root path and set of credentials.
|
|
37
44
|
* @category Contexts
|
|
@@ -62,3 +69,16 @@ export interface ContextInit {
|
|
|
62
69
|
* @category Contexts
|
|
63
70
|
*/
|
|
64
71
|
export declare const defaultContext: FSContext;
|
|
72
|
+
export declare function contextOf($: unknown): FSContext;
|
|
73
|
+
/**
|
|
74
|
+
* Create a blank FS Context
|
|
75
|
+
* @internal
|
|
76
|
+
* @category Contexts
|
|
77
|
+
* @todo Make sure parent root can't be escaped
|
|
78
|
+
*
|
|
79
|
+
* This exists so that `kIsContext` is not exported and to make sure the context is "secure".
|
|
80
|
+
*/
|
|
81
|
+
export declare function createChildContext(parent: FSContext, init?: ContextInit): FSContext & {
|
|
82
|
+
parent: FSContext;
|
|
83
|
+
};
|
|
84
|
+
export {};
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { createCredentials } from './credentials.js';
|
|
2
|
+
/**
|
|
3
|
+
* Symbol used for context branding
|
|
4
|
+
* @internal @hidden
|
|
5
|
+
*/
|
|
6
|
+
const kIsContext = Symbol('ZenFSContext');
|
|
2
7
|
/**
|
|
3
8
|
* The default/global context.
|
|
4
9
|
* @internal @hidden
|
|
5
10
|
* @category Contexts
|
|
6
11
|
*/
|
|
7
12
|
export const defaultContext = {
|
|
13
|
+
[kIsContext]: true,
|
|
8
14
|
id: 0,
|
|
9
15
|
root: '/',
|
|
10
16
|
pwd: '/',
|
|
@@ -13,3 +19,37 @@ export const defaultContext = {
|
|
|
13
19
|
parent: null,
|
|
14
20
|
children: [],
|
|
15
21
|
};
|
|
22
|
+
export function contextOf($) {
|
|
23
|
+
return typeof $ === 'object' && $ !== null && kIsContext in $ ? $ : defaultContext;
|
|
24
|
+
}
|
|
25
|
+
// 0 is reserved for the global/default context
|
|
26
|
+
let _nextId = 1;
|
|
27
|
+
/**
|
|
28
|
+
* Create a blank FS Context
|
|
29
|
+
* @internal
|
|
30
|
+
* @category Contexts
|
|
31
|
+
* @todo Make sure parent root can't be escaped
|
|
32
|
+
*
|
|
33
|
+
* This exists so that `kIsContext` is not exported and to make sure the context is "secure".
|
|
34
|
+
*/
|
|
35
|
+
export function createChildContext(parent, init = {}) {
|
|
36
|
+
const { root = parent.root, pwd = parent.pwd, credentials = structuredClone(parent.credentials) } = init;
|
|
37
|
+
const ctx = {
|
|
38
|
+
[kIsContext]: true,
|
|
39
|
+
id: _nextId++,
|
|
40
|
+
root,
|
|
41
|
+
pwd,
|
|
42
|
+
credentials: createCredentials(credentials),
|
|
43
|
+
descriptors: new Map(),
|
|
44
|
+
parent: parent,
|
|
45
|
+
children: [],
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperties(ctx, {
|
|
48
|
+
id: { configurable: false, writable: false },
|
|
49
|
+
credentials: { configurable: false, writable: false },
|
|
50
|
+
descriptors: { configurable: false, writable: false },
|
|
51
|
+
parent: { configurable: false, writable: false },
|
|
52
|
+
children: { configurable: false, writable: false },
|
|
53
|
+
});
|
|
54
|
+
return ctx;
|
|
55
|
+
}
|
package/dist/internal/inode.js
CHANGED
|
@@ -39,9 +39,9 @@ import { sizeof } from 'memium';
|
|
|
39
39
|
import { $from, field, struct, types as t } from 'memium/decorators';
|
|
40
40
|
import { decodeUTF8, encodeUTF8, pick } from 'utilium';
|
|
41
41
|
import { BufferView } from 'utilium/buffer.js';
|
|
42
|
-
import { Stats } from '../node/stats.js';
|
|
43
42
|
import * as c from '../constants.js';
|
|
44
|
-
import {
|
|
43
|
+
import { Stats } from '../node/stats.js';
|
|
44
|
+
import { contextOf } from './contexts.js';
|
|
45
45
|
/**
|
|
46
46
|
* Root inode
|
|
47
47
|
* @hidden
|
|
@@ -620,7 +620,7 @@ export function isFIFO(metadata) {
|
|
|
620
620
|
* @internal
|
|
621
621
|
*/
|
|
622
622
|
export function hasAccess($, inode, access) {
|
|
623
|
-
const credentials =
|
|
623
|
+
const { credentials } = contextOf($);
|
|
624
624
|
if (isSymbolicLink(inode) || credentials.euid === 0 || credentials.egid === 0)
|
|
625
625
|
return true;
|
|
626
626
|
let perm = 0;
|
package/dist/node/sync.js
CHANGED
|
@@ -467,7 +467,7 @@ export function lutimesSync(path, atime, mtime) {
|
|
|
467
467
|
lutimesSync;
|
|
468
468
|
export function realpathSync(path, options) {
|
|
469
469
|
const encoding = typeof options == 'string' ? options : (options?.encoding ?? 'utf8');
|
|
470
|
-
path = normalizePath(path);
|
|
470
|
+
path = normalizePath(path, true);
|
|
471
471
|
const { fullPath } = _sync.resolve(this, path);
|
|
472
472
|
if (encoding == 'utf8' || encoding == 'utf-8')
|
|
473
473
|
return fullPath;
|
package/dist/path.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
2
|
-
import {
|
|
2
|
+
import { contextOf } from './internal/contexts.js';
|
|
3
3
|
import { globToRegex } from './utils.js';
|
|
4
4
|
export const sep = '/';
|
|
5
5
|
function validateObject(str, name) {
|
|
@@ -81,7 +81,7 @@ export function formatExt(ext) {
|
|
|
81
81
|
}
|
|
82
82
|
export function resolve(...parts) {
|
|
83
83
|
let resolved = '';
|
|
84
|
-
for (const part of [...parts.reverse(), this
|
|
84
|
+
for (const part of [...parts.reverse(), contextOf(this).pwd]) {
|
|
85
85
|
if (!part?.length)
|
|
86
86
|
continue;
|
|
87
87
|
resolved = `${part}/${resolved}`;
|
|
@@ -334,7 +334,7 @@ export function format(pathObject) {
|
|
|
334
334
|
return dir === pathObject.root ? `${dir}${base}` : `${dir}/${base}`;
|
|
335
335
|
}
|
|
336
336
|
export function parse(path) {
|
|
337
|
-
const isAbsolute = path
|
|
337
|
+
const isAbsolute = path[0] === '/';
|
|
338
338
|
const ret = { root: isAbsolute ? '/' : '', dir: '', base: '', ext: '', name: '' };
|
|
339
339
|
if (path.length === 0)
|
|
340
340
|
return ret;
|
package/dist/utils.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type Exception } from 'kerium';
|
|
|
2
2
|
import type * as fs from 'node:fs';
|
|
3
3
|
import type { Worker as NodeWorker } from 'node:worker_threads';
|
|
4
4
|
import { type OptionalTuple } from 'utilium';
|
|
5
|
+
import type { V_Context } from './internal/contexts.js';
|
|
5
6
|
declare global {
|
|
6
7
|
function atob(data: string): string;
|
|
7
8
|
function btoa(data: string): string;
|
|
@@ -31,8 +32,9 @@ export declare function normalizeTime(time: fs.TimeLike): number;
|
|
|
31
32
|
/**
|
|
32
33
|
* Normalizes a path
|
|
33
34
|
* @internal
|
|
35
|
+
* @todo clean this up and make it so `path.resolve` is only called when an explicit context is passed (i.e. `normalizePath(..., $)` to use `path.resolve`)
|
|
34
36
|
*/
|
|
35
|
-
export declare function normalizePath(p: fs.PathLike, noResolve?: boolean): string;
|
|
37
|
+
export declare function normalizePath(this: V_Context, p: fs.PathLike, noResolve?: boolean): string;
|
|
36
38
|
/**
|
|
37
39
|
* Normalizes options
|
|
38
40
|
* @param options options to normalize
|
package/dist/utils.js
CHANGED
|
@@ -51,6 +51,7 @@ export function normalizeTime(time) {
|
|
|
51
51
|
/**
|
|
52
52
|
* Normalizes a path
|
|
53
53
|
* @internal
|
|
54
|
+
* @todo clean this up and make it so `path.resolve` is only called when an explicit context is passed (i.e. `normalizePath(..., $)` to use `path.resolve`)
|
|
54
55
|
*/
|
|
55
56
|
export function normalizePath(p, noResolve = false) {
|
|
56
57
|
if (p instanceof URL) {
|
|
@@ -67,7 +68,7 @@ export function normalizePath(p, noResolve = false) {
|
|
|
67
68
|
throw withErrno('EINVAL', 'Path can not be empty');
|
|
68
69
|
p = p.replaceAll(/[/\\]+/g, '/');
|
|
69
70
|
// Note: PWD is not resolved here, it is resolved later.
|
|
70
|
-
return noResolve ? p : resolve(p);
|
|
71
|
+
return noResolve ? p : resolve.call(this, p);
|
|
71
72
|
}
|
|
72
73
|
/**
|
|
73
74
|
* Normalizes options
|
package/dist/vfs/acl.js
CHANGED
|
@@ -44,9 +44,9 @@ import { err } from 'kerium/log';
|
|
|
44
44
|
import { packed, sizeof } from 'memium';
|
|
45
45
|
import { $from, struct, types as t } from 'memium/decorators';
|
|
46
46
|
import { BufferView } from 'utilium/buffer.js';
|
|
47
|
-
import { defaultContext } from '../internal/contexts.js';
|
|
48
|
-
import { Attributes } from '../internal/inode.js';
|
|
49
47
|
import { R_OK, S_IRWXG, S_IRWXO, S_IRWXU, W_OK, X_OK } from '../constants.js';
|
|
48
|
+
import { contextOf } from '../internal/contexts.js';
|
|
49
|
+
import { Attributes } from '../internal/inode.js';
|
|
50
50
|
import * as xattr from './xattr.js';
|
|
51
51
|
const version = 2;
|
|
52
52
|
export var Type;
|
|
@@ -206,7 +206,7 @@ export function check($, inode, access) {
|
|
|
206
206
|
if (!shouldCheck)
|
|
207
207
|
return true;
|
|
208
208
|
inode.attributes ??= new Attributes();
|
|
209
|
-
const { euid, egid } =
|
|
209
|
+
const { euid, egid } = contextOf($).credentials;
|
|
210
210
|
const data = inode.attributes.get('system.posix_acl_access');
|
|
211
211
|
if (!data)
|
|
212
212
|
return true;
|
package/dist/vfs/async.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { setUVMessage, UV } from 'kerium';
|
|
2
2
|
import { decodeUTF8 } from 'utilium';
|
|
3
3
|
import * as constants from '../constants.js';
|
|
4
|
-
import {
|
|
4
|
+
import { contextOf } from '../internal/contexts.js';
|
|
5
5
|
import { hasAccess, isDirectory, isSymbolicLink } from '../internal/inode.js';
|
|
6
6
|
import { basename, dirname, join, parse, resolve as resolvePath } from '../path.js';
|
|
7
7
|
import { normalizeMode, normalizePath } from '../utils.js';
|
|
@@ -17,6 +17,7 @@ import { emitChange } from './watchers.js';
|
|
|
17
17
|
* @internal @hidden
|
|
18
18
|
*/
|
|
19
19
|
export async function resolve($, path, preserveSymlinks, extra) {
|
|
20
|
+
path = resolvePath.call($, path);
|
|
20
21
|
if (preserveSymlinks) {
|
|
21
22
|
const resolved = resolveMount(path, $, extra);
|
|
22
23
|
const stats = await resolved.fs.stat(resolved.path).catch(() => undefined);
|
|
@@ -74,7 +75,7 @@ export async function open($, path, opt) {
|
|
|
74
75
|
throw UV('ENOTDIR', 'open', dirname(path));
|
|
75
76
|
if (!opt.allowDirectory && mode & constants.S_IFDIR)
|
|
76
77
|
throw UV('EISDIR', 'open', path);
|
|
77
|
-
const { euid: uid, egid: gid } =
|
|
78
|
+
const { euid: uid, egid: gid } = contextOf($).credentials;
|
|
78
79
|
const inode = await fs.createFile(resolved, {
|
|
79
80
|
mode,
|
|
80
81
|
uid: parentStats.mode & constants.S_ISUID ? parentStats.uid : uid,
|
|
@@ -110,7 +111,7 @@ export async function readlink(path) {
|
|
|
110
111
|
}
|
|
111
112
|
export async function mkdir(path, options = {}) {
|
|
112
113
|
path = normalizePath(path);
|
|
113
|
-
const { euid: uid, egid: gid } = this
|
|
114
|
+
const { euid: uid, egid: gid } = contextOf(this).credentials;
|
|
114
115
|
const { mode = 0o777, recursive } = options;
|
|
115
116
|
const { fs, path: resolved } = resolveMount(path, this, { syscall: 'mkdir' });
|
|
116
117
|
const __create = async (path, resolved, parent) => {
|
package/dist/vfs/file.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
2
2
|
import { UV, withErrno } from 'kerium';
|
|
3
3
|
import * as c from '../constants.js';
|
|
4
|
-
import {
|
|
4
|
+
import { contextOf } from '../internal/contexts.js';
|
|
5
5
|
import { _chown, InodeFlags, isBlockDevice, isCharacterDevice } from '../internal/inode.js';
|
|
6
6
|
import '../polyfills.js';
|
|
7
7
|
/**
|
|
@@ -371,7 +371,7 @@ export class Handle {
|
|
|
371
371
|
* @internal @hidden
|
|
372
372
|
*/
|
|
373
373
|
export function toFD(file) {
|
|
374
|
-
const map = file.context
|
|
374
|
+
const map = contextOf(file.context).descriptors;
|
|
375
375
|
const fd = Math.max(map.size ? Math.max(...map.keys()) + 1 : 0, 4);
|
|
376
376
|
map.set(fd, file);
|
|
377
377
|
return fd;
|
|
@@ -380,12 +380,12 @@ export function toFD(file) {
|
|
|
380
380
|
* @internal @hidden
|
|
381
381
|
*/
|
|
382
382
|
export function fromFD($, fd) {
|
|
383
|
-
const map =
|
|
383
|
+
const map = contextOf($).descriptors;
|
|
384
384
|
const value = map.get(fd);
|
|
385
385
|
if (!value)
|
|
386
386
|
throw withErrno('EBADF');
|
|
387
387
|
return value;
|
|
388
388
|
}
|
|
389
389
|
export function deleteFD($, fd) {
|
|
390
|
-
return (
|
|
390
|
+
return contextOf($).descriptors.delete(fd);
|
|
391
391
|
}
|
package/dist/vfs/shared.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Errno, Exception, UV, withErrno } from 'kerium';
|
|
|
4
4
|
import { alert, debug, err, info, notice, warn } from 'kerium/log';
|
|
5
5
|
import { InMemory } from '../backends/memory.js';
|
|
6
6
|
import { size_max } from '../constants.js';
|
|
7
|
-
import {
|
|
7
|
+
import { contextOf } from '../internal/contexts.js';
|
|
8
8
|
import { credentialsAllowRoot } from '../internal/credentials.js';
|
|
9
9
|
import { withExceptionContext } from '../internal/error.js';
|
|
10
10
|
import { join, resolve } from '../path.js';
|
|
@@ -53,9 +53,10 @@ export function umount(mountPoint) {
|
|
|
53
53
|
* @internal @hidden
|
|
54
54
|
*/
|
|
55
55
|
export function resolveMount(path, ctx, extra) {
|
|
56
|
-
const root = ctx
|
|
56
|
+
const { root } = contextOf(ctx);
|
|
57
57
|
const _exceptionContext = { path, ...extra };
|
|
58
|
-
path = normalizePath(join(root, path));
|
|
58
|
+
path = normalizePath(join(root, path), true);
|
|
59
|
+
path = resolve.call(ctx, path);
|
|
59
60
|
const sortedMounts = [...mounts].sort((a, b) => (a[0].length > b[0].length ? -1 : 1)); // descending order of the string length
|
|
60
61
|
for (const [mountPoint, fs] of sortedMounts) {
|
|
61
62
|
// We know path is normalized, so it would be a substring of the mount point.
|
|
@@ -94,7 +95,7 @@ export function _statfs(fs, bigint) {
|
|
|
94
95
|
* @category Backends and Configuration
|
|
95
96
|
*/
|
|
96
97
|
export function chroot(path) {
|
|
97
|
-
const $ = this
|
|
98
|
+
const $ = contextOf(this);
|
|
98
99
|
if (!credentialsAllowRoot($.credentials))
|
|
99
100
|
throw withErrno('EPERM', 'Can not chroot() as non-root user');
|
|
100
101
|
$.root ??= '/';
|
package/dist/vfs/sync.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { setUVMessage, UV } from 'kerium';
|
|
2
2
|
import { decodeUTF8 } from 'utilium';
|
|
3
3
|
import * as constants from '../constants.js';
|
|
4
|
-
import {
|
|
4
|
+
import { contextOf } from '../internal/contexts.js';
|
|
5
5
|
import { hasAccess, isDirectory, isSymbolicLink } from '../internal/inode.js';
|
|
6
6
|
import { basename, dirname, join, parse, resolve as resolvePath } from '../path.js';
|
|
7
7
|
import { normalizeMode, normalizePath } from '../utils.js';
|
|
@@ -18,6 +18,7 @@ import { emitChange } from './watchers.js';
|
|
|
18
18
|
* @internal @hidden
|
|
19
19
|
*/
|
|
20
20
|
export function resolve($, path, preserveSymlinks, extra) {
|
|
21
|
+
path = resolvePath.call($, path);
|
|
21
22
|
/* Try to resolve it directly. If this works,
|
|
22
23
|
that means we don't need to perform any resolution for parent directories. */
|
|
23
24
|
try {
|
|
@@ -87,7 +88,7 @@ export function open(path, opt) {
|
|
|
87
88
|
if (checkAccess && !hasAccess(this, parentStats, constants.W_OK)) {
|
|
88
89
|
throw UV('EACCES', 'open', path);
|
|
89
90
|
}
|
|
90
|
-
const { euid: uid, egid: gid } = this
|
|
91
|
+
const { euid: uid, egid: gid } = contextOf(this).credentials;
|
|
91
92
|
const inode = fs.createFileSync(resolved, {
|
|
92
93
|
mode,
|
|
93
94
|
uid: parentStats.mode & constants.S_ISUID ? parentStats.uid : uid,
|
|
@@ -124,7 +125,7 @@ export function readlink(path) {
|
|
|
124
125
|
export function mkdir(path, options = {}) {
|
|
125
126
|
path = normalizePath(path);
|
|
126
127
|
const { fs, path: resolved } = resolve(this, path);
|
|
127
|
-
const { euid: uid, egid: gid } = this
|
|
128
|
+
const { euid: uid, egid: gid } = contextOf(this).credentials;
|
|
128
129
|
const { mode = 0o777, recursive } = options;
|
|
129
130
|
const __create = (path, resolved, parent) => {
|
|
130
131
|
if (checkAccess && !hasAccess(this, parent, constants.W_OK))
|
package/dist/vfs/watchers.d.ts
CHANGED
|
@@ -75,5 +75,5 @@ export declare function removeWatcher(path: string, watcher: FSWatcher): void;
|
|
|
75
75
|
/**
|
|
76
76
|
* @internal @hidden
|
|
77
77
|
*/
|
|
78
|
-
export declare function emitChange(
|
|
78
|
+
export declare function emitChange(context: V_Context, eventType: fs.WatchEventType, filename: string): void;
|
|
79
79
|
export {};
|
package/dist/vfs/watchers.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { EventEmitter } from 'eventemitter3';
|
|
2
2
|
import { UV } from 'kerium';
|
|
3
|
-
import {
|
|
4
|
-
import { normalizePath } from '../utils.js';
|
|
3
|
+
import { contextOf } from '../internal/contexts.js';
|
|
5
4
|
import { isStatsEqual } from '../node/stats.js';
|
|
6
5
|
import { statSync } from '../node/sync.js';
|
|
6
|
+
import { basename, dirname, join, relative } from '../path.js';
|
|
7
|
+
import { normalizePath } from '../utils.js';
|
|
7
8
|
/**
|
|
8
9
|
* Base class for file system watchers.
|
|
9
10
|
* Provides event handling capabilities for watching file system changes.
|
|
@@ -61,9 +62,10 @@ export class FSWatcher extends Watcher {
|
|
|
61
62
|
options;
|
|
62
63
|
realpath;
|
|
63
64
|
constructor(context, path, options) {
|
|
64
|
-
|
|
65
|
+
const $ = contextOf(context);
|
|
66
|
+
super($, path);
|
|
65
67
|
this.options = options;
|
|
66
|
-
this.realpath =
|
|
68
|
+
this.realpath = join($.root, path);
|
|
67
69
|
addWatcher(this.realpath, this);
|
|
68
70
|
}
|
|
69
71
|
close() {
|
|
@@ -145,7 +147,8 @@ export function removeWatcher(path, watcher) {
|
|
|
145
147
|
/**
|
|
146
148
|
* @internal @hidden
|
|
147
149
|
*/
|
|
148
|
-
export function emitChange(
|
|
150
|
+
export function emitChange(context, eventType, filename) {
|
|
151
|
+
const $ = contextOf(context);
|
|
149
152
|
if ($)
|
|
150
153
|
filename = join($.root ?? '/', filename);
|
|
151
154
|
filename = normalizePath(filename);
|
package/package.json
CHANGED
|
@@ -60,8 +60,7 @@ suite('Context', () => {
|
|
|
60
60
|
await promise;
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
test('Path resolution of / with context root and mount point being the same', async () => {
|
|
64
|
-
// @zenfs/core#226
|
|
63
|
+
test('Path resolution of / with context root and mount point being the same #226', async () => {
|
|
65
64
|
await configure({
|
|
66
65
|
mounts: { '/bananas': InMemory },
|
|
67
66
|
});
|
|
@@ -73,8 +72,7 @@ suite('Context', () => {
|
|
|
73
72
|
assert.deepEqual(bananas.fs.readdirSync('/'), ['yellow']);
|
|
74
73
|
});
|
|
75
74
|
|
|
76
|
-
test('Different working directory',
|
|
77
|
-
// @zenfs/core#263
|
|
75
|
+
test('Different working directory #263', () => {
|
|
78
76
|
ctx.mkdirSync('/test');
|
|
79
77
|
context.pwd = '/test';
|
|
80
78
|
|
package/tests/fs/links.test.ts
CHANGED
|
@@ -19,8 +19,7 @@ suite('Links', () => {
|
|
|
19
19
|
assert(stats.isSymbolicLink());
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
test('lstat file inside symlinked directory', async () => {
|
|
23
|
-
// @zenfs/core#241
|
|
22
|
+
test('lstat file inside symlinked directory #241', async () => {
|
|
24
23
|
await fs.promises.mkdir('/a');
|
|
25
24
|
await fs.promises.writeFile('/a/hello.txt', 'hello world');
|
|
26
25
|
await fs.promises.symlink('/a', '/b');
|
package/tests/fs/read.test.ts
CHANGED
|
@@ -70,8 +70,7 @@ suite('read', () => {
|
|
|
70
70
|
assert.equal(bytesRead, expected.length);
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
test('read using callback API', async () => {
|
|
74
|
-
// @zenfs/core#239
|
|
73
|
+
test('read using callback API #239', async () => {
|
|
75
74
|
const path = '/text.txt';
|
|
76
75
|
|
|
77
76
|
fs.writeFileSync(path, 'hello world');
|