@zenfs/core 2.7.0 → 2.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -108
- package/dist/config.d.ts +4 -0
- package/dist/config.js +2 -0
- package/dist/context.js +4 -4
- package/dist/internal/contexts.js +2 -2
- package/dist/internal/devices.d.ts +18 -1
- package/dist/internal/devices.js +14 -0
- package/dist/internal/inode.js +3 -1
- package/dist/node/async.js +3 -3
- package/dist/node/promises.js +16 -16
- package/dist/node/sync.js +16 -16
- package/dist/vfs/async.js +8 -8
- package/dist/vfs/file.d.ts +11 -1
- package/dist/vfs/file.js +26 -4
- package/dist/vfs/ioctl.js +2 -2
- package/dist/vfs/shared.js +1 -1
- package/dist/vfs/sync.js +8 -8
- package/dist/vfs/watchers.js +1 -1
- package/dist/vfs/xattr.js +8 -8
- package/package.json +1 -1
- package/tests/common/context.test.ts +11 -1
- package/tests/common/utf8stream.test.ts +1 -1
- package/tests/fs/permissions.test.ts +1 -1
package/README.md
CHANGED
|
@@ -43,6 +43,9 @@ Your financial support would go a long way toward improving ZenFS and its commun
|
|
|
43
43
|
|
|
44
44
|
## Usage
|
|
45
45
|
|
|
46
|
+
> [!IMPORTANT]
|
|
47
|
+
> **[Check out the ZenFS docs!](https://zenfs.dev/start/usage/)**
|
|
48
|
+
|
|
46
49
|
```js
|
|
47
50
|
import { fs } from '@zenfs/core'; // You can also use the default export
|
|
48
51
|
|
|
@@ -52,15 +55,9 @@ const contents = fs.readFileSync('/test.txt', 'utf-8');
|
|
|
52
55
|
console.log(contents);
|
|
53
56
|
```
|
|
54
57
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
You can configure ZenFS to use a different backend and mount multiple backends. It is strongly recommended to do so using the `configure` function.
|
|
60
|
-
|
|
61
|
-
You can use multiple backends by passing an object to `configure` which maps paths to file systems.
|
|
62
|
-
|
|
63
|
-
The following example mounts a zip file to `/zip`, in-memory storage to `/tmp`, and IndexedDB to `/home`. Note that `/` has the default in-memory backend.
|
|
58
|
+
A single `InMemory` backend is created by default, mounted on `/`. To use different backends, and to
|
|
59
|
+
mount more than one, configure ZenFS with `configure`. This mounts a zip file to `/mnt/zip`,
|
|
60
|
+
in-memory storage to `/tmp`, and IndexedDB to `/home`:
|
|
64
61
|
|
|
65
62
|
```js
|
|
66
63
|
import { configure, InMemory } from '@zenfs/core';
|
|
@@ -78,110 +75,16 @@ await configure({
|
|
|
78
75
|
});
|
|
79
76
|
```
|
|
80
77
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
> [!TIP]
|
|
84
|
-
> When configuring a mount point, you can pass in
|
|
85
|
-
>
|
|
86
|
-
> 1. A `Backend` object, if the backend has no required options
|
|
87
|
-
> 2. An object that has the options accepted by the backend and a `backend` property which is a `Backend` object
|
|
88
|
-
> 3. A `FileSystem` instance
|
|
89
|
-
|
|
90
|
-
Here is an example that mounts the `WebStorage` backend from `@zenfs/dom` on `/`:
|
|
91
|
-
|
|
92
|
-
```js
|
|
93
|
-
import { configureSingle, fs } from '@zenfs/core';
|
|
94
|
-
import { WebStorage } from '@zenfs/dom';
|
|
95
|
-
|
|
96
|
-
await configureSingle({ backend: WebStorage });
|
|
97
|
-
|
|
98
|
-
if (!fs.existsSync('/test.txt')) {
|
|
99
|
-
fs.writeFileSync('/test.txt', 'This will persist across reloads!');
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const contents = fs.readFileSync('/test.txt', 'utf-8');
|
|
103
|
-
console.log(contents);
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
#### FS Promises
|
|
107
|
-
|
|
108
|
-
The FS promises API is exposed as `promises`.
|
|
109
|
-
|
|
110
|
-
```js
|
|
111
|
-
import { configureSingle } from '@zenfs/core';
|
|
112
|
-
import { exists, writeFile } from '@zenfs/core/promises';
|
|
113
|
-
import { IndexedDB } from '@zenfs/dom';
|
|
114
|
-
|
|
115
|
-
await configureSingle({ backend: IndexedDB });
|
|
116
|
-
|
|
117
|
-
const exists = await exists('/myfile.txt');
|
|
118
|
-
if (!exists) {
|
|
119
|
-
await writeFile('/myfile.txt', 'Lots of persistent data');
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
> [!NOTE]
|
|
124
|
-
> You can import the promises API using:
|
|
125
|
-
>
|
|
126
|
-
> 1. Exports from `@zenfs/core/promises`
|
|
127
|
-
> 2. The `promises` export from `@zenfs/core`
|
|
128
|
-
> 3. `fs.promises` on the exported `fs` from `@zenfs/core`.
|
|
78
|
+
The `fs/promises` API is available from `@zenfs/core/promises`, as the `promises` export, or as
|
|
79
|
+
`fs.promises`.
|
|
129
80
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
If you would like to create backends without configure (e.g. to do something dynamic at runtime), you may do so by importing the backend and calling `resolveMountConfig` with it.
|
|
133
|
-
|
|
134
|
-
You can then mount and unmount the backend instance by using `mount` and `umount`.
|
|
135
|
-
|
|
136
|
-
```js
|
|
137
|
-
import { configure, resolveMountConfig, InMemory } from '@zenfs/core';
|
|
138
|
-
import { IndexedDB } from '@zenfs/dom';
|
|
139
|
-
import { Zip } from '@zenfs/archives';
|
|
140
|
-
|
|
141
|
-
await configure({
|
|
142
|
-
mounts: {
|
|
143
|
-
'/tmp': InMemory,
|
|
144
|
-
'/home': IndexedDB,
|
|
145
|
-
},
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
fs.mkdirSync('/mnt/zip', { recursive: true });
|
|
149
|
-
|
|
150
|
-
const res = await fetch('mydata.zip');
|
|
151
|
-
const zipfs = await resolveMountConfig({ backend: Zip, data: await res.arrayBuffer() });
|
|
152
|
-
fs.mount('/mnt/zip', zipfs);
|
|
153
|
-
|
|
154
|
-
// do stuff with the mounted zip
|
|
155
|
-
|
|
156
|
-
fs.umount('/mnt/zip'); // finished using the zip
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
> [!CAUTION]
|
|
160
|
-
> Instances of backends follow the _internal_ API. You should never use a backend's methods unless you are extending a backend.
|
|
161
|
-
|
|
162
|
-
### Devices and device files
|
|
163
|
-
|
|
164
|
-
ZenFS includes support for device files. These are designed to follow Linux's device file behavior, for consistency and ease of use. Check out the [Devices and Device Drivers](https://zenfs.dev/core/documents/Devices_and_Device_Drivers) documentation for more information.
|
|
165
|
-
|
|
166
|
-
## `node:*` emulation
|
|
167
|
-
|
|
168
|
-
ZenFS also includes emulation of some other `node:` modules for various reasons, importable from `@zenfs/core/<name>`:
|
|
169
|
-
|
|
170
|
-
- `node:path`
|
|
171
|
-
- `node:readline`
|
|
172
|
-
|
|
173
|
-
For example:
|
|
174
|
-
|
|
175
|
-
```ts
|
|
176
|
-
import * as path from '@zenfs/core/path';
|
|
177
|
-
```
|
|
81
|
+
For the full usage guide, see **[the documentation](https://zenfs.dev/start/usage/)**.
|
|
82
|
+
This includes mounting at runtime, contexts and permissions, devices, and the `node:*`module emulation.
|
|
178
83
|
|
|
179
84
|
## Bundling
|
|
180
85
|
|
|
181
86
|
ZenFS exports a drop-in for Node's `fs` module, so you can use it for your bundler of preference using the default export.
|
|
182
|
-
|
|
183
|
-
> [!IMPORTANT]
|
|
184
|
-
> See [COPYING.md](./COPYING.md)
|
|
87
|
+
See [COPYING.md](./COPYING.md) for more info.
|
|
185
88
|
|
|
186
89
|
## Sponsors
|
|
187
90
|
|
package/dist/config.d.ts
CHANGED
|
@@ -56,6 +56,8 @@ export interface Configuration<T extends ConfigMounts> extends SharedConfig {
|
|
|
56
56
|
/**
|
|
57
57
|
* Whether to automatically add normal Linux devices
|
|
58
58
|
* @default false
|
|
59
|
+
* @deprecated Use `@zenfs/linux`!
|
|
60
|
+
* @todo [breaking] remove
|
|
59
61
|
*/
|
|
60
62
|
addDevices: boolean;
|
|
61
63
|
/**
|
|
@@ -97,6 +99,8 @@ export declare function configureSingle<T extends Backend>(configuration: MountC
|
|
|
97
99
|
export declare function configureSingleSync<T extends Backend>(configuration: MountConfiguration<T>): void;
|
|
98
100
|
/**
|
|
99
101
|
* @category Backends and Configuration
|
|
102
|
+
* @deprecated Use `@zenfs/linux`!
|
|
103
|
+
* @todo [breaking] remove
|
|
100
104
|
*/
|
|
101
105
|
export declare function addDevice(driver: DeviceDriver, options?: object): Device;
|
|
102
106
|
/**
|
package/dist/config.js
CHANGED
|
@@ -189,6 +189,8 @@ function mountWithMkdirSync(path, fs) {
|
|
|
189
189
|
}
|
|
190
190
|
/**
|
|
191
191
|
* @category Backends and Configuration
|
|
192
|
+
* @deprecated Use `@zenfs/linux`!
|
|
193
|
+
* @todo [breaking] remove
|
|
192
194
|
*/
|
|
193
195
|
export function addDevice(driver, options) {
|
|
194
196
|
const devfs = defaultContext.mounts.get('/dev');
|
package/dist/context.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
2
2
|
import { UV } from 'kerium';
|
|
3
3
|
import { bindFunctions } from 'utilium';
|
|
4
|
-
import { contextOf, createChildContext } from './internal/contexts.js';
|
|
4
|
+
import { contextOf, createChildContext, defaultContext } from './internal/contexts.js';
|
|
5
5
|
import * as fs from './node/index.js';
|
|
6
6
|
import * as path from './path.js';
|
|
7
7
|
import * as xattr from './vfs/xattr.js';
|
|
@@ -17,9 +17,9 @@ export const boundContexts = new Map();
|
|
|
17
17
|
* @category Contexts
|
|
18
18
|
*/
|
|
19
19
|
export function bindContext(init = {}) {
|
|
20
|
-
const $ = contextOf(this);
|
|
21
|
-
if (!fs.statSync.call(
|
|
22
|
-
throw UV('ENOTDIR', { syscall: 'chroot', path:
|
|
20
|
+
const $ = contextOf(this), parent = $.parent || defaultContext;
|
|
21
|
+
if (!fs.statSync.call(parent, parent.root).isDirectory())
|
|
22
|
+
throw UV('ENOTDIR', { syscall: 'chroot', path: parent.root });
|
|
23
23
|
const ctx = createChildContext($, init);
|
|
24
24
|
const bound = Object.assign(ctx, {
|
|
25
25
|
fs: {
|
|
@@ -46,7 +46,7 @@ let _nextId = 2;
|
|
|
46
46
|
*/
|
|
47
47
|
export function createChildContext(parent, init = {}) {
|
|
48
48
|
assertContext(parent);
|
|
49
|
-
const { root = parent.root, pwd = parent.pwd, credentials = structuredClone(parent.credentials), mounts
|
|
49
|
+
const { root = parent.root, pwd = parent.pwd, credentials = structuredClone(parent.credentials), mounts } = init;
|
|
50
50
|
const ctx = {
|
|
51
51
|
[kIsContext]: true,
|
|
52
52
|
id: _nextId++,
|
|
@@ -56,7 +56,7 @@ export function createChildContext(parent, init = {}) {
|
|
|
56
56
|
descriptors: new Map(),
|
|
57
57
|
parent: parent,
|
|
58
58
|
children: [],
|
|
59
|
-
mounts: new Map(Object.entries(mounts)),
|
|
59
|
+
mounts: mounts ? new Map(Object.entries(mounts)) : parent.mounts,
|
|
60
60
|
};
|
|
61
61
|
Object.defineProperties(ctx, {
|
|
62
62
|
id: { configurable: false, writable: false },
|
|
@@ -4,10 +4,11 @@ import type { CreationOptions } from './filesystem.js';
|
|
|
4
4
|
import { Inode, type InodeLike } from './inode.js';
|
|
5
5
|
/**
|
|
6
6
|
* A device
|
|
7
|
-
* @todo Maybe add some other device information, like a UUID?
|
|
8
7
|
* @category Internals
|
|
9
8
|
* @privateRemarks
|
|
10
9
|
* UUIDs were considered, however they don't make sense without an easy mechanism for persistence
|
|
10
|
+
* @deprecated Use `@zenfs/linux`!
|
|
11
|
+
* @todo [breaking] remove
|
|
11
12
|
*/
|
|
12
13
|
export interface Device<TData = any> {
|
|
13
14
|
/**
|
|
@@ -34,6 +35,8 @@ export interface Device<TData = any> {
|
|
|
34
35
|
}
|
|
35
36
|
/**
|
|
36
37
|
* @category Internals
|
|
38
|
+
* @deprecated Use `@zenfs/linux`!
|
|
39
|
+
* @todo [breaking] remove
|
|
37
40
|
*/
|
|
38
41
|
export interface DeviceInit<TData = any> {
|
|
39
42
|
data?: TData;
|
|
@@ -45,6 +48,8 @@ export interface DeviceInit<TData = any> {
|
|
|
45
48
|
/**
|
|
46
49
|
* A device driver
|
|
47
50
|
* @category Internals
|
|
51
|
+
* @deprecated Use `@zenfs/linux`!
|
|
52
|
+
* @todo [breaking] remove
|
|
48
53
|
*/
|
|
49
54
|
export interface DeviceDriver<TData = any> {
|
|
50
55
|
/**
|
|
@@ -88,6 +93,8 @@ export interface DeviceDriver<TData = any> {
|
|
|
88
93
|
/**
|
|
89
94
|
* A temporary file system that manages and interfaces with devices
|
|
90
95
|
* @category Internals
|
|
96
|
+
* @deprecated Use `@zenfs/linux`!
|
|
97
|
+
* @todo [breaking] remove
|
|
91
98
|
*/
|
|
92
99
|
export declare class DeviceFS extends StoreFS<InMemoryStore> {
|
|
93
100
|
protected readonly devices: Map<string, Device<any>>;
|
|
@@ -132,6 +139,8 @@ export declare class DeviceFS extends StoreFS<InMemoryStore> {
|
|
|
132
139
|
* - Writes discard data, advancing the file position.
|
|
133
140
|
* @category Internals
|
|
134
141
|
* @internal
|
|
142
|
+
* @deprecated Use `@zenfs/linux`!
|
|
143
|
+
* @todo [breaking] remove
|
|
135
144
|
*/
|
|
136
145
|
export declare const nullDevice: DeviceDriver;
|
|
137
146
|
/**
|
|
@@ -144,6 +153,8 @@ export declare const nullDevice: DeviceDriver;
|
|
|
144
153
|
* - Provides basic file metadata, treating it as a character device.
|
|
145
154
|
* @category Internals
|
|
146
155
|
* @internal
|
|
156
|
+
* @deprecated Use `@zenfs/linux`!
|
|
157
|
+
* @todo [breaking] remove
|
|
147
158
|
*/
|
|
148
159
|
export declare const zeroDevice: DeviceDriver;
|
|
149
160
|
/**
|
|
@@ -152,6 +163,8 @@ export declare const zeroDevice: DeviceDriver;
|
|
|
152
163
|
* - Writes always fail with ENOSPC (no space left on device).
|
|
153
164
|
* @category Internals
|
|
154
165
|
* @internal
|
|
166
|
+
* @deprecated Use `@zenfs/linux`!
|
|
167
|
+
* @todo [breaking] remove
|
|
155
168
|
*/
|
|
156
169
|
export declare const fullDevice: DeviceDriver;
|
|
157
170
|
/**
|
|
@@ -160,12 +173,16 @@ export declare const fullDevice: DeviceDriver;
|
|
|
160
173
|
* - Writes discard data, advancing the file position.
|
|
161
174
|
* @category Internals
|
|
162
175
|
* @internal
|
|
176
|
+
* @deprecated Use `@zenfs/linux`!
|
|
177
|
+
* @todo [breaking] remove
|
|
163
178
|
*/
|
|
164
179
|
export declare const randomDevice: DeviceDriver;
|
|
165
180
|
/**
|
|
166
181
|
* Shortcuts for importing.
|
|
167
182
|
* @category Internals
|
|
168
183
|
* @internal
|
|
184
|
+
* @deprecated Use `@zenfs/linux`!
|
|
185
|
+
* @todo [breaking] remove
|
|
169
186
|
*/
|
|
170
187
|
export declare const devices: {
|
|
171
188
|
null: DeviceDriver<any>;
|
package/dist/internal/devices.js
CHANGED
|
@@ -13,6 +13,8 @@ import { Inode } from './inode.js';
|
|
|
13
13
|
/**
|
|
14
14
|
* A temporary file system that manages and interfaces with devices
|
|
15
15
|
* @category Internals
|
|
16
|
+
* @deprecated Use `@zenfs/linux`!
|
|
17
|
+
* @todo [breaking] remove
|
|
16
18
|
*/
|
|
17
19
|
export class DeviceFS extends StoreFS {
|
|
18
20
|
devices = new Map();
|
|
@@ -231,6 +233,8 @@ const emptyBuffer = new Uint8Array();
|
|
|
231
233
|
* - Writes discard data, advancing the file position.
|
|
232
234
|
* @category Internals
|
|
233
235
|
* @internal
|
|
236
|
+
* @deprecated Use `@zenfs/linux`!
|
|
237
|
+
* @todo [breaking] remove
|
|
234
238
|
*/
|
|
235
239
|
export const nullDevice = {
|
|
236
240
|
name: 'null',
|
|
@@ -255,6 +259,8 @@ export const nullDevice = {
|
|
|
255
259
|
* - Provides basic file metadata, treating it as a character device.
|
|
256
260
|
* @category Internals
|
|
257
261
|
* @internal
|
|
262
|
+
* @deprecated Use `@zenfs/linux`!
|
|
263
|
+
* @todo [breaking] remove
|
|
258
264
|
*/
|
|
259
265
|
export const zeroDevice = {
|
|
260
266
|
name: 'zero',
|
|
@@ -275,6 +281,8 @@ export const zeroDevice = {
|
|
|
275
281
|
* - Writes always fail with ENOSPC (no space left on device).
|
|
276
282
|
* @category Internals
|
|
277
283
|
* @internal
|
|
284
|
+
* @deprecated Use `@zenfs/linux`!
|
|
285
|
+
* @todo [breaking] remove
|
|
278
286
|
*/
|
|
279
287
|
export const fullDevice = {
|
|
280
288
|
name: 'full',
|
|
@@ -295,6 +303,8 @@ export const fullDevice = {
|
|
|
295
303
|
* - Writes discard data, advancing the file position.
|
|
296
304
|
* @category Internals
|
|
297
305
|
* @internal
|
|
306
|
+
* @deprecated Use `@zenfs/linux`!
|
|
307
|
+
* @todo [breaking] remove
|
|
298
308
|
*/
|
|
299
309
|
export const randomDevice = {
|
|
300
310
|
name: 'random',
|
|
@@ -315,6 +325,8 @@ export const randomDevice = {
|
|
|
315
325
|
* Simulates the `/dev/console` device.
|
|
316
326
|
* @category Internals
|
|
317
327
|
* @experimental @internal
|
|
328
|
+
* @deprecated Use `@zenfs/linux`!
|
|
329
|
+
* @todo [breaking] remove
|
|
318
330
|
*/
|
|
319
331
|
const consoleDevice = {
|
|
320
332
|
name: 'console',
|
|
@@ -334,6 +346,8 @@ const consoleDevice = {
|
|
|
334
346
|
* Shortcuts for importing.
|
|
335
347
|
* @category Internals
|
|
336
348
|
* @internal
|
|
349
|
+
* @deprecated Use `@zenfs/linux`!
|
|
350
|
+
* @todo [breaking] remove
|
|
337
351
|
*/
|
|
338
352
|
export const devices = {
|
|
339
353
|
null: nullDevice,
|
package/dist/internal/inode.js
CHANGED
|
@@ -642,8 +642,10 @@ export function isFIFO(metadata) {
|
|
|
642
642
|
*/
|
|
643
643
|
export function hasAccess($, inode, access) {
|
|
644
644
|
const { credentials } = contextOf($);
|
|
645
|
-
if (isSymbolicLink(inode)
|
|
645
|
+
if (isSymbolicLink(inode))
|
|
646
646
|
return true;
|
|
647
|
+
if (credentials.euid === 0 || credentials.egid === 0)
|
|
648
|
+
return !(access & c.X_OK) || isDirectory(inode) || !!(inode.mode & (c.S_IXUSR | c.S_IXGRP | c.S_IXOTH));
|
|
647
649
|
let perm = 0;
|
|
648
650
|
if (credentials.uid === inode.uid) {
|
|
649
651
|
if (inode.mode & c.S_IRUSR)
|
package/dist/node/async.js
CHANGED
|
@@ -328,7 +328,7 @@ export function access(path, cbMode, cb = nop) {
|
|
|
328
328
|
access;
|
|
329
329
|
const statWatchers = new Map();
|
|
330
330
|
export function watchFile(path, options, listener) {
|
|
331
|
-
const normalizedPath = normalizePath(path);
|
|
331
|
+
const normalizedPath = normalizePath.call(this, path);
|
|
332
332
|
const opts = typeof options != 'function' ? options : {};
|
|
333
333
|
if (typeof options == 'function') {
|
|
334
334
|
listener = options;
|
|
@@ -365,7 +365,7 @@ watchFile;
|
|
|
365
365
|
* @param listener Optional listener to remove.
|
|
366
366
|
*/
|
|
367
367
|
export function unwatchFile(path, listener = nop) {
|
|
368
|
-
const normalizedPath = normalizePath(path);
|
|
368
|
+
const normalizedPath = normalizePath.call(this, path);
|
|
369
369
|
const entry = statWatchers.get(normalizedPath);
|
|
370
370
|
if (entry) {
|
|
371
371
|
if (listener && listener !== nop) {
|
|
@@ -384,7 +384,7 @@ export function unwatchFile(path, listener = nop) {
|
|
|
384
384
|
}
|
|
385
385
|
unwatchFile;
|
|
386
386
|
export function watch(path, options, listener) {
|
|
387
|
-
const watcher = new FSWatcher(this, normalizePath(path), typeof options == 'object' ? options : {});
|
|
387
|
+
const watcher = new FSWatcher(this, normalizePath.call(this, path), typeof options == 'object' ? options : {});
|
|
388
388
|
listener = typeof options == 'function' ? options : listener;
|
|
389
389
|
watcher.on('change', listener || nop);
|
|
390
390
|
return watcher;
|
package/dist/node/promises.js
CHANGED
|
@@ -529,7 +529,7 @@ rename;
|
|
|
529
529
|
* Test whether or not `path` exists by checking with the file system.
|
|
530
530
|
*/
|
|
531
531
|
export async function exists(path) {
|
|
532
|
-
path = normalizePath(path);
|
|
532
|
+
path = normalizePath.call(this, path);
|
|
533
533
|
try {
|
|
534
534
|
const { fs, path: resolved } = await _async.resolve(this, path);
|
|
535
535
|
return await fs.exists(resolved);
|
|
@@ -579,7 +579,7 @@ truncate;
|
|
|
579
579
|
export async function unlink(path) {
|
|
580
580
|
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
581
581
|
try {
|
|
582
|
-
path = normalizePath(path);
|
|
582
|
+
path = normalizePath.call(this, path);
|
|
583
583
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
584
584
|
const $ex = { syscall: 'unlink', path };
|
|
585
585
|
const stats = await fs.stat(resolved).catch(rethrow($ex));
|
|
@@ -691,7 +691,7 @@ appendFile;
|
|
|
691
691
|
export async function rmdir(path) {
|
|
692
692
|
const env_6 = { stack: [], error: void 0, hasError: false };
|
|
693
693
|
try {
|
|
694
|
-
path = normalizePath(path);
|
|
694
|
+
path = normalizePath.call(this, path);
|
|
695
695
|
const { fs, path: resolved } = await _async.resolve(this, path);
|
|
696
696
|
const $ex = { syscall: 'rmdir', path };
|
|
697
697
|
const stats = await fs.stat(resolved).catch(rethrow($ex));
|
|
@@ -722,7 +722,7 @@ export async function mkdir(path, options) {
|
|
|
722
722
|
}
|
|
723
723
|
mkdir;
|
|
724
724
|
export async function readdir(path, options) {
|
|
725
|
-
path = normalizePath(path);
|
|
725
|
+
path = normalizePath.call(this, path);
|
|
726
726
|
const opt = typeof options === 'object' && options != null ? options : { encoding: options, withFileTypes: false, recursive: false };
|
|
727
727
|
const rawEntries = await _async.readdir.call(this, path, opt);
|
|
728
728
|
const values = [];
|
|
@@ -755,11 +755,11 @@ export async function symlink(dest, path, type = 'file') {
|
|
|
755
755
|
try {
|
|
756
756
|
if (!['file', 'dir', 'junction'].includes(type))
|
|
757
757
|
throw new TypeError('Invalid symlink type: ' + type);
|
|
758
|
-
path = normalizePath(path);
|
|
758
|
+
path = normalizePath.call(this, path);
|
|
759
759
|
if (await exists.call(this, path))
|
|
760
760
|
throw UV('EEXIST', 'symlink', path);
|
|
761
761
|
const handle = __addDisposableResource(env_7, await _async.open(this, path, { flag: 'w+', mode: 0o644, preserveSymlinks: true }), true);
|
|
762
|
-
const encoded = encodeUTF8(normalizePath(dest, true));
|
|
762
|
+
const encoded = encodeUTF8(normalizePath.call(this, dest, true));
|
|
763
763
|
await handle.write(encoded, 0, encoded.length, 0);
|
|
764
764
|
await handle.chmod(constants.S_IFLNK);
|
|
765
765
|
}
|
|
@@ -775,7 +775,7 @@ export async function symlink(dest, path, type = 'file') {
|
|
|
775
775
|
}
|
|
776
776
|
symlink;
|
|
777
777
|
export async function readlink(path, options) {
|
|
778
|
-
path = normalizePath(path);
|
|
778
|
+
path = normalizePath.call(this, path);
|
|
779
779
|
const buf = Buffer.from(await _async.readlink.call(this, path), 'utf-8');
|
|
780
780
|
const encoding = typeof options == 'object' ? options?.encoding : options;
|
|
781
781
|
// always defaults to utf-8 to avoid wrangler (cloudflare) worker "unknown encoding" exception
|
|
@@ -824,7 +824,7 @@ lchown;
|
|
|
824
824
|
export async function chmod(path, mode) {
|
|
825
825
|
const env_10 = { stack: [], error: void 0, hasError: false };
|
|
826
826
|
try {
|
|
827
|
-
const handle = __addDisposableResource(env_10, await open.call(this, path, 'r+').catch(rethrow({ syscall: 'chmod', path: normalizePath(path) })), true);
|
|
827
|
+
const handle = __addDisposableResource(env_10, await open.call(this, path, 'r+').catch(rethrow({ syscall: 'chmod', path: normalizePath.call(this, path) })), true);
|
|
828
828
|
await handle.chmod(mode);
|
|
829
829
|
}
|
|
830
830
|
catch (e_10) {
|
|
@@ -911,7 +911,7 @@ export async function lutimes(path, atime, mtime) {
|
|
|
911
911
|
lutimes;
|
|
912
912
|
export async function realpath(path, options) {
|
|
913
913
|
const encoding = typeof options == 'string' ? options : (options?.encoding ?? 'utf8');
|
|
914
|
-
path = normalizePath(path);
|
|
914
|
+
path = normalizePath.call(this, path);
|
|
915
915
|
const { fullPath } = await _async.resolve(this, path);
|
|
916
916
|
if (encoding == 'utf8' || encoding == 'utf-8')
|
|
917
917
|
return fullPath;
|
|
@@ -976,7 +976,7 @@ access;
|
|
|
976
976
|
* @param path The path to the file or directory to remove.
|
|
977
977
|
*/
|
|
978
978
|
export async function rm(path, options) {
|
|
979
|
-
path = normalizePath(path);
|
|
979
|
+
path = normalizePath.call(this, path);
|
|
980
980
|
const stats = await lstat.call(this, path).catch((error) => {
|
|
981
981
|
if (error.code == 'ENOENT' && options?.force)
|
|
982
982
|
return undefined;
|
|
@@ -1034,8 +1034,8 @@ export async function mkdtempDisposable(prefix, options) {
|
|
|
1034
1034
|
* * `fs.constants.COPYFILE_EXCL`: If the destination file already exists, the operation fails.
|
|
1035
1035
|
*/
|
|
1036
1036
|
export async function copyFile(src, dest, mode) {
|
|
1037
|
-
src = normalizePath(src);
|
|
1038
|
-
dest = normalizePath(dest);
|
|
1037
|
+
src = normalizePath.call(this, src);
|
|
1038
|
+
dest = normalizePath.call(this, dest);
|
|
1039
1039
|
if (mode && mode & constants.COPYFILE_EXCL && (await exists.call(this, dest)))
|
|
1040
1040
|
throw UV('EEXIST', 'copyFile', dest);
|
|
1041
1041
|
await writeFile.call(this, dest, await readFile.call(this, src));
|
|
@@ -1050,7 +1050,7 @@ copyFile;
|
|
|
1050
1050
|
* @todo Use options
|
|
1051
1051
|
*/
|
|
1052
1052
|
export function opendir(path, options) {
|
|
1053
|
-
path = normalizePath(path);
|
|
1053
|
+
path = normalizePath.call(this, path);
|
|
1054
1054
|
return Promise.resolve(new Dir(path, this));
|
|
1055
1055
|
}
|
|
1056
1056
|
opendir;
|
|
@@ -1067,8 +1067,8 @@ opendir;
|
|
|
1067
1067
|
* * `recursive`: If `true`, copies directories recursively.
|
|
1068
1068
|
*/
|
|
1069
1069
|
export async function cp(source, destination, opts) {
|
|
1070
|
-
source = normalizePath(source);
|
|
1071
|
-
destination = normalizePath(destination);
|
|
1070
|
+
source = normalizePath.call(this, source);
|
|
1071
|
+
destination = normalizePath.call(this, destination);
|
|
1072
1072
|
const srcStats = await lstat.call(this, source); // Use lstat to follow symlinks if not dereferencing
|
|
1073
1073
|
if (opts?.errorOnExist && (await exists.call(this, destination)))
|
|
1074
1074
|
throw UV('EEXIST', 'cp', destination);
|
|
@@ -1116,7 +1116,7 @@ export async function cp(source, destination, opts) {
|
|
|
1116
1116
|
}
|
|
1117
1117
|
cp;
|
|
1118
1118
|
export async function statfs(path, opts) {
|
|
1119
|
-
path = normalizePath(path);
|
|
1119
|
+
path = normalizePath.call(this, path);
|
|
1120
1120
|
const { fs } = resolveMount(path, this);
|
|
1121
1121
|
return Promise.resolve(_statfs(fs, opts?.bigint));
|
|
1122
1122
|
}
|
package/dist/node/sync.js
CHANGED
|
@@ -76,7 +76,7 @@ renameSync;
|
|
|
76
76
|
* Test whether or not `path` exists by checking with the file system.
|
|
77
77
|
*/
|
|
78
78
|
export function existsSync(path) {
|
|
79
|
-
path = normalizePath(path);
|
|
79
|
+
path = normalizePath.call(this, path);
|
|
80
80
|
try {
|
|
81
81
|
const { fs, path: resolvedPath } = _sync.resolve(this, path);
|
|
82
82
|
return fs.existsSync(resolvedPath);
|
|
@@ -133,7 +133,7 @@ export function truncateSync(path, len = 0) {
|
|
|
133
133
|
}
|
|
134
134
|
truncateSync;
|
|
135
135
|
export function unlinkSync(path) {
|
|
136
|
-
path = normalizePath(path);
|
|
136
|
+
path = normalizePath.call(this, path);
|
|
137
137
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
138
138
|
try {
|
|
139
139
|
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
@@ -250,7 +250,7 @@ export function appendFileSync(filename, data, _options = {}) {
|
|
|
250
250
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
251
251
|
const file = typeof filename == 'number'
|
|
252
252
|
? fromFD(this, filename)
|
|
253
|
-
: _sync.open(this, normalizePath(filename), {
|
|
253
|
+
: _sync.open(this, normalizePath.call(this, filename), {
|
|
254
254
|
flag,
|
|
255
255
|
mode: options.mode,
|
|
256
256
|
});
|
|
@@ -354,7 +354,7 @@ futimesSync;
|
|
|
354
354
|
export function rmdirSync(path) {
|
|
355
355
|
const env_5 = { stack: [], error: void 0, hasError: false };
|
|
356
356
|
try {
|
|
357
|
-
path = normalizePath(path);
|
|
357
|
+
path = normalizePath.call(this, path);
|
|
358
358
|
const { fs, path: resolved } = _sync.resolve(this, path);
|
|
359
359
|
const stats = wrap(fs, 'statSync', { path, syscall: 'rmdir' })(resolved);
|
|
360
360
|
if (!isDirectory(stats))
|
|
@@ -383,7 +383,7 @@ export function mkdirSync(path, options) {
|
|
|
383
383
|
mkdirSync;
|
|
384
384
|
export function readdirSync(path, options) {
|
|
385
385
|
options = typeof options === 'object' ? options : { encoding: options };
|
|
386
|
-
path = normalizePath(path);
|
|
386
|
+
path = normalizePath.call(this, path);
|
|
387
387
|
const entries = [];
|
|
388
388
|
const rawEntries = _sync.readdir.call(this, path, options ?? undefined);
|
|
389
389
|
for (const raw of rawEntries) {
|
|
@@ -415,9 +415,9 @@ export function symlinkSync(target, path, type = 'file') {
|
|
|
415
415
|
try {
|
|
416
416
|
if (!['file', 'dir', 'junction'].includes(type))
|
|
417
417
|
throw new TypeError('Invalid symlink type: ' + type);
|
|
418
|
-
path = normalizePath(path);
|
|
418
|
+
path = normalizePath.call(this, path);
|
|
419
419
|
const file = __addDisposableResource(env_6, _sync.open(this, path, { flag: 'wx', mode: 0o644 }), false);
|
|
420
|
-
file.writeSync(encodeUTF8(normalizePath(target, true)));
|
|
420
|
+
file.writeSync(encodeUTF8(normalizePath.call(this, target, true)));
|
|
421
421
|
file.chmodSync(constants.S_IFLNK);
|
|
422
422
|
}
|
|
423
423
|
catch (e_6) {
|
|
@@ -466,7 +466,7 @@ export function chmodSync(path, mode) {
|
|
|
466
466
|
fd = openSync.call(this, path, 'r+');
|
|
467
467
|
}
|
|
468
468
|
catch (e) {
|
|
469
|
-
throw setUVMessage(Object.assign(e, { syscall: 'chmod', path: normalizePath(path) }));
|
|
469
|
+
throw setUVMessage(Object.assign(e, { syscall: 'chmod', path: normalizePath.call(this, path) }));
|
|
470
470
|
}
|
|
471
471
|
fchmodSync.call(this, fd, mode);
|
|
472
472
|
closeSync.call(this, fd);
|
|
@@ -498,7 +498,7 @@ export function lutimesSync(path, atime, mtime) {
|
|
|
498
498
|
lutimesSync;
|
|
499
499
|
export function realpathSync(path, options) {
|
|
500
500
|
const encoding = typeof options == 'string' ? options : (options?.encoding ?? 'utf8');
|
|
501
|
-
path = normalizePath(path, true);
|
|
501
|
+
path = normalizePath.call(this, path, true);
|
|
502
502
|
const { fullPath } = _sync.resolve(this, path);
|
|
503
503
|
if (encoding == 'utf8' || encoding == 'utf-8')
|
|
504
504
|
return fullPath;
|
|
@@ -521,7 +521,7 @@ accessSync;
|
|
|
521
521
|
* @param path The path to the file or directory to remove.
|
|
522
522
|
*/
|
|
523
523
|
export function rmSync(path, options) {
|
|
524
|
-
path = normalizePath(path);
|
|
524
|
+
path = normalizePath.call(this, path);
|
|
525
525
|
let stats;
|
|
526
526
|
try {
|
|
527
527
|
stats = lstatSync.bind(this)(path);
|
|
@@ -580,8 +580,8 @@ export function mkdtempDisposableSync(prefix, options) {
|
|
|
580
580
|
* - `fs.constants.COPYFILE_EXCL`: If the destination file already exists, the operation fails.
|
|
581
581
|
*/
|
|
582
582
|
export function copyFileSync(source, destination, flags) {
|
|
583
|
-
source = normalizePath(source);
|
|
584
|
-
destination = normalizePath(destination);
|
|
583
|
+
source = normalizePath.call(this, source);
|
|
584
|
+
destination = normalizePath.call(this, destination);
|
|
585
585
|
if (flags && flags & constants.COPYFILE_EXCL && existsSync.call(this, destination))
|
|
586
586
|
throw UV('EEXIST', 'copyFile', destination);
|
|
587
587
|
writeFileSync.call(this, destination, readFileSync.call(this, source));
|
|
@@ -628,7 +628,7 @@ writevSync;
|
|
|
628
628
|
* @todo Handle options
|
|
629
629
|
*/
|
|
630
630
|
export function opendirSync(path, options) {
|
|
631
|
-
path = normalizePath(path);
|
|
631
|
+
path = normalizePath.call(this, path);
|
|
632
632
|
return new Dir(path, this);
|
|
633
633
|
}
|
|
634
634
|
opendirSync;
|
|
@@ -645,8 +645,8 @@ opendirSync;
|
|
|
645
645
|
* - `recursive`: If `true`, copies directories recursively.
|
|
646
646
|
*/
|
|
647
647
|
export function cpSync(source, destination, opts) {
|
|
648
|
-
source = normalizePath(source);
|
|
649
|
-
destination = normalizePath(destination);
|
|
648
|
+
source = normalizePath.call(this, source);
|
|
649
|
+
destination = normalizePath.call(this, destination);
|
|
650
650
|
const srcStats = lstatSync.call(this, source); // Use lstat to follow symlinks if not dereferencing
|
|
651
651
|
if (opts?.errorOnExist && existsSync.call(this, destination))
|
|
652
652
|
throw UV('EEXIST', 'cp', destination);
|
|
@@ -688,7 +688,7 @@ export function cpSync(source, destination, opts) {
|
|
|
688
688
|
}
|
|
689
689
|
cpSync;
|
|
690
690
|
export function statfsSync(path, options) {
|
|
691
|
-
path = normalizePath(path);
|
|
691
|
+
path = normalizePath.call(this, path);
|
|
692
692
|
const { fs } = resolveMount(path, this);
|
|
693
693
|
return _statfs(fs, options?.bigint);
|
|
694
694
|
}
|
package/dist/vfs/async.js
CHANGED
|
@@ -114,7 +114,7 @@ export async function resolve($, path, preserveSymlinks, extra) {
|
|
|
114
114
|
* @internal
|
|
115
115
|
*/
|
|
116
116
|
export async function open($, path, opt) {
|
|
117
|
-
path = normalizePath(path);
|
|
117
|
+
path = normalizePath.call($, path);
|
|
118
118
|
const mode = normalizeMode(opt.mode, 0o644), flag = flags.parse(opt.flag);
|
|
119
119
|
const $ex = { syscall: 'open', path };
|
|
120
120
|
// eslint-disable-next-line prefer-const
|
|
@@ -169,7 +169,7 @@ export async function open($, path, opt) {
|
|
|
169
169
|
return handle;
|
|
170
170
|
}
|
|
171
171
|
export async function readlink(path) {
|
|
172
|
-
path = normalizePath(path);
|
|
172
|
+
path = normalizePath.call(this, path);
|
|
173
173
|
const $ex = { syscall: 'readlink', path };
|
|
174
174
|
const { fs, stats, path: resolved } = await resolve(this, path, true, $ex);
|
|
175
175
|
if (!stats)
|
|
@@ -184,7 +184,7 @@ export async function readlink(path) {
|
|
|
184
184
|
return decodeUTF8(data);
|
|
185
185
|
}
|
|
186
186
|
export async function mkdir(path, options = {}) {
|
|
187
|
-
const original = normalizePath(path);
|
|
187
|
+
const original = normalizePath.call(this, path);
|
|
188
188
|
const { euid: uid, egid: gid } = contextOf(this).credentials;
|
|
189
189
|
const { mode = 0o777, recursive } = options;
|
|
190
190
|
const $ex = { syscall: 'mkdir', path: original };
|
|
@@ -238,7 +238,7 @@ export async function mkdir(path, options = {}) {
|
|
|
238
238
|
return firstCreated;
|
|
239
239
|
}
|
|
240
240
|
export async function readdir(path, options = {}) {
|
|
241
|
-
path = normalizePath(path);
|
|
241
|
+
path = normalizePath.call(this, path);
|
|
242
242
|
// Node reports `readdir` failures as `scandir`
|
|
243
243
|
const $ex = { syscall: 'scandir', path };
|
|
244
244
|
const { fs, path: resolved, stats } = await resolve(this, path, false, $ex);
|
|
@@ -290,8 +290,8 @@ export async function readdir(path, options = {}) {
|
|
|
290
290
|
export async function rename(oldPath, newPath) {
|
|
291
291
|
const env_4 = { stack: [], error: void 0, hasError: false };
|
|
292
292
|
try {
|
|
293
|
-
oldPath = normalizePath(oldPath);
|
|
294
|
-
newPath = normalizePath(newPath);
|
|
293
|
+
oldPath = normalizePath.call(this, oldPath);
|
|
294
|
+
newPath = normalizePath.call(this, newPath);
|
|
295
295
|
const $ex = { syscall: 'rename', path: oldPath, dest: newPath };
|
|
296
296
|
const src = await resolve(this, oldPath, true, $ex);
|
|
297
297
|
const dst = resolveMount(newPath, this, $ex);
|
|
@@ -344,8 +344,8 @@ export async function rename(oldPath, newPath) {
|
|
|
344
344
|
export async function link(target, link) {
|
|
345
345
|
const env_5 = { stack: [], error: void 0, hasError: false };
|
|
346
346
|
try {
|
|
347
|
-
target = normalizePath(target);
|
|
348
|
-
link = normalizePath(link);
|
|
347
|
+
target = normalizePath.call(this, target);
|
|
348
|
+
link = normalizePath.call(this, link);
|
|
349
349
|
const $ex = { syscall: 'link', path: target, dest: link };
|
|
350
350
|
const { fs, path: resolved } = resolveMount(target, this, $ex);
|
|
351
351
|
const dst = resolveMount(link, this, $ex);
|
package/dist/vfs/file.d.ts
CHANGED
|
@@ -45,6 +45,10 @@ export declare class Handle {
|
|
|
45
45
|
* Whether the file is open or closed
|
|
46
46
|
*/
|
|
47
47
|
protected closed: boolean;
|
|
48
|
+
/** How many descriptors refer to this open file */
|
|
49
|
+
protected refs: number;
|
|
50
|
+
/** Take another reference to the open file */
|
|
51
|
+
ref(): this;
|
|
48
52
|
get isClosed(): boolean;
|
|
49
53
|
/**
|
|
50
54
|
* Creates a file handle for the vnode.
|
|
@@ -141,11 +145,17 @@ export declare class Handle {
|
|
|
141
145
|
streamWrite(options: StreamOptions): WritableStream;
|
|
142
146
|
}
|
|
143
147
|
/**
|
|
148
|
+
* Give an open file a descriptor
|
|
144
149
|
* @internal @hidden
|
|
145
150
|
*/
|
|
146
|
-
export declare function toFD(file: Handle): number;
|
|
151
|
+
export declare function toFD(file: Handle, $?: V_Context): number;
|
|
147
152
|
/**
|
|
148
153
|
* @internal @hidden
|
|
149
154
|
*/
|
|
150
155
|
export declare function fromFD($: V_Context, fd: number): Handle;
|
|
151
156
|
export declare function deleteFD($: V_Context, fd: number): boolean;
|
|
157
|
+
/**
|
|
158
|
+
* Give an open file another descriptor
|
|
159
|
+
* @internal @hidden
|
|
160
|
+
*/
|
|
161
|
+
export declare function dupFD($: V_Context, fd: number): number;
|
package/dist/vfs/file.js
CHANGED
|
@@ -152,6 +152,15 @@ let Handle = (() => {
|
|
|
152
152
|
* Whether the file is open or closed
|
|
153
153
|
*/
|
|
154
154
|
closed = false;
|
|
155
|
+
/** How many descriptors refer to this open file */
|
|
156
|
+
refs = 1;
|
|
157
|
+
/** Take another reference to the open file */
|
|
158
|
+
ref() {
|
|
159
|
+
if (this.closed)
|
|
160
|
+
throw UV('EBADF', 'dup', this.path);
|
|
161
|
+
this.refs++;
|
|
162
|
+
return this;
|
|
163
|
+
}
|
|
155
164
|
get isClosed() {
|
|
156
165
|
return this.closed;
|
|
157
166
|
}
|
|
@@ -199,6 +208,8 @@ let Handle = (() => {
|
|
|
199
208
|
if (this.closed)
|
|
200
209
|
throw UV('EBADF', 'close', this.path);
|
|
201
210
|
this.syncSync();
|
|
211
|
+
if (--this.refs > 0)
|
|
212
|
+
return;
|
|
202
213
|
this.disposeSync(true);
|
|
203
214
|
}
|
|
204
215
|
/**
|
|
@@ -406,6 +417,8 @@ let Handle = (() => {
|
|
|
406
417
|
if (this.closed)
|
|
407
418
|
throw UV('EBADF', 'close', this.path);
|
|
408
419
|
await this.sync();
|
|
420
|
+
if (--this.refs > 0)
|
|
421
|
+
return;
|
|
409
422
|
this.dispose(true);
|
|
410
423
|
}
|
|
411
424
|
/**
|
|
@@ -671,12 +684,14 @@ let Handle = (() => {
|
|
|
671
684
|
export { Handle };
|
|
672
685
|
// descriptors
|
|
673
686
|
/**
|
|
687
|
+
* Give an open file a descriptor
|
|
674
688
|
* @internal @hidden
|
|
675
689
|
*/
|
|
676
|
-
export function toFD(file) {
|
|
677
|
-
const map = contextOf(
|
|
678
|
-
|
|
679
|
-
|
|
690
|
+
export function toFD(file, $ = file.context) {
|
|
691
|
+
const map = contextOf($).descriptors;
|
|
692
|
+
let fd = 0;
|
|
693
|
+
while (map.has(fd))
|
|
694
|
+
fd++;
|
|
680
695
|
map.set(fd, file);
|
|
681
696
|
return fd;
|
|
682
697
|
}
|
|
@@ -694,3 +709,10 @@ export function fromFD($, fd) {
|
|
|
694
709
|
export function deleteFD($, fd) {
|
|
695
710
|
return contextOf($).descriptors.delete(fd);
|
|
696
711
|
}
|
|
712
|
+
/**
|
|
713
|
+
* Give an open file another descriptor
|
|
714
|
+
* @internal @hidden
|
|
715
|
+
*/
|
|
716
|
+
export function dupFD($, fd) {
|
|
717
|
+
return toFD(fromFD($, fd).ref(), $);
|
|
718
|
+
}
|
package/dist/vfs/ioctl.js
CHANGED
|
@@ -26,7 +26,7 @@ export async function ioctl(path, command, ...args) {
|
|
|
26
26
|
vnode.refs++;
|
|
27
27
|
}
|
|
28
28
|
else {
|
|
29
|
-
path = normalizePath(path);
|
|
29
|
+
path = normalizePath.call(this, path);
|
|
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 });
|
|
@@ -64,7 +64,7 @@ export function ioctlSync(path, command, ...args) {
|
|
|
64
64
|
vnode.refs++;
|
|
65
65
|
}
|
|
66
66
|
else {
|
|
67
|
-
path = normalizePath(path);
|
|
67
|
+
path = normalizePath.call(this, path);
|
|
68
68
|
const mnt = resolveSync(this, path, false, { syscall: 'ioctl', path });
|
|
69
69
|
if (!mnt.stats)
|
|
70
70
|
throw UV('ENOENT', { syscall: 'ioctl', path });
|
package/dist/vfs/shared.js
CHANGED
|
@@ -128,7 +128,7 @@ export function chroot(path) {
|
|
|
128
128
|
throw UV('EBUSY', 'chroot', handle.path);
|
|
129
129
|
handle.path = handle.path.slice($.root.length);
|
|
130
130
|
}
|
|
131
|
-
if (newRoot.length
|
|
131
|
+
if (newRoot.length < $.root.length)
|
|
132
132
|
throw withErrno('EPERM', 'Can not chroot() outside of current root');
|
|
133
133
|
$.root = newRoot;
|
|
134
134
|
}
|
package/dist/vfs/sync.js
CHANGED
|
@@ -114,7 +114,7 @@ export function resolve($, path, preserveSymlinks, extra) {
|
|
|
114
114
|
* @internal
|
|
115
115
|
*/
|
|
116
116
|
export function open($, path, opt) {
|
|
117
|
-
path = normalizePath(path);
|
|
117
|
+
path = normalizePath.call($, path);
|
|
118
118
|
const mode = normalizeMode(opt.mode, 0o644), flag = flags.parse(opt.flag);
|
|
119
119
|
path = opt.preserveSymlinks ? path : resolve($, path).fullPath;
|
|
120
120
|
const { fs, path: resolved } = resolveMount(path, $);
|
|
@@ -175,7 +175,7 @@ export function open($, path, opt) {
|
|
|
175
175
|
return file;
|
|
176
176
|
}
|
|
177
177
|
export function readlink(path) {
|
|
178
|
-
path = normalizePath(path);
|
|
178
|
+
path = normalizePath.call(this, path);
|
|
179
179
|
const { fs, stats, path: resolved } = resolve(this, path, true, { syscall: 'readlink' });
|
|
180
180
|
if (!stats)
|
|
181
181
|
throw UV('ENOENT', 'readlink', path);
|
|
@@ -189,7 +189,7 @@ export function readlink(path) {
|
|
|
189
189
|
return decodeUTF8(data);
|
|
190
190
|
}
|
|
191
191
|
export function mkdir(path, options = {}) {
|
|
192
|
-
const original = normalizePath(path);
|
|
192
|
+
const original = normalizePath.call(this, path);
|
|
193
193
|
const $ex = { syscall: 'mkdir', path: original };
|
|
194
194
|
const { fullPath: realParent } = resolve(this, dirname(original), false, $ex);
|
|
195
195
|
const { fs, path: target } = resolveMount(join(realParent, basename(original)), this, $ex);
|
|
@@ -246,7 +246,7 @@ export function mkdir(path, options = {}) {
|
|
|
246
246
|
return firstCreated;
|
|
247
247
|
}
|
|
248
248
|
export function readdir(path, options = {}) {
|
|
249
|
-
path = normalizePath(path);
|
|
249
|
+
path = normalizePath.call(this, path);
|
|
250
250
|
const { fs, path: resolved } = resolve(this, path);
|
|
251
251
|
// Node reports `readdir` failures as `scandir`
|
|
252
252
|
const stats = cacheOf(fs).get(resolved)?.inode ?? wrap(fs, 'statSync', { path, syscall: 'scandir' })(resolved);
|
|
@@ -300,8 +300,8 @@ export function readdir(path, options = {}) {
|
|
|
300
300
|
export function rename(oldPath, newPath) {
|
|
301
301
|
const env_4 = { stack: [], error: void 0, hasError: false };
|
|
302
302
|
try {
|
|
303
|
-
oldPath = normalizePath(oldPath);
|
|
304
|
-
newPath = normalizePath(newPath);
|
|
303
|
+
oldPath = normalizePath.call(this, oldPath);
|
|
304
|
+
newPath = normalizePath.call(this, newPath);
|
|
305
305
|
const $ex = { syscall: 'rename', path: oldPath, dest: newPath };
|
|
306
306
|
const src = resolve(this, oldPath, true, $ex);
|
|
307
307
|
const dst = resolveMount(newPath, this, $ex);
|
|
@@ -357,8 +357,8 @@ export function rename(oldPath, newPath) {
|
|
|
357
357
|
export function link(target, link) {
|
|
358
358
|
const env_5 = { stack: [], error: void 0, hasError: false };
|
|
359
359
|
try {
|
|
360
|
-
target = normalizePath(target);
|
|
361
|
-
link = normalizePath(link);
|
|
360
|
+
target = normalizePath.call(this, target);
|
|
361
|
+
link = normalizePath.call(this, link);
|
|
362
362
|
const $ex = { syscall: 'link', path: target, dest: link };
|
|
363
363
|
const { fs, path: resolved } = resolveMount(target, this, $ex);
|
|
364
364
|
const dst = resolveMount(link, this, $ex);
|
package/dist/vfs/watchers.js
CHANGED
|
@@ -151,7 +151,7 @@ export function emitChange(context, eventType, filename) {
|
|
|
151
151
|
const $ = contextOf(context);
|
|
152
152
|
if ($)
|
|
153
153
|
filename = join($.root ?? '/', filename);
|
|
154
|
-
filename = normalizePath(filename);
|
|
154
|
+
filename = normalizePath.call(context, filename);
|
|
155
155
|
// Notify watchers, including ones on parent directories if they are watching recursively.
|
|
156
156
|
for (let path = filename;; path = dirname(path)) {
|
|
157
157
|
const watchersForPath = watchers.get(path);
|
package/dist/vfs/xattr.js
CHANGED
|
@@ -17,7 +17,7 @@ function checkName($, name, path, syscall) {
|
|
|
17
17
|
throw UV('ENOTSUP', syscall, path);
|
|
18
18
|
}
|
|
19
19
|
export async function get(path, name, opt = {}) {
|
|
20
|
-
path = normalizePath(path);
|
|
20
|
+
path = normalizePath.call(this, path);
|
|
21
21
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
22
22
|
checkName(this, name, path, 'xattr.get');
|
|
23
23
|
const inode = await fs.stat(resolved).catch(rethrow('xattr.get', path));
|
|
@@ -31,7 +31,7 @@ export async function get(path, name, opt = {}) {
|
|
|
31
31
|
return opt.encoding == 'buffer' || !opt.encoding ? buffer : buffer.toString(opt.encoding);
|
|
32
32
|
}
|
|
33
33
|
export function getSync(path, name, opt = {}) {
|
|
34
|
-
path = normalizePath(path);
|
|
34
|
+
path = normalizePath.call(this, path);
|
|
35
35
|
checkName(this, name, path, 'xattr.get');
|
|
36
36
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
37
37
|
let inode;
|
|
@@ -59,7 +59,7 @@ export function getSync(path, name, opt = {}) {
|
|
|
59
59
|
* @param opt Options for the operation
|
|
60
60
|
*/
|
|
61
61
|
export async function set(path, name, value, opt = {}) {
|
|
62
|
-
path = normalizePath(path);
|
|
62
|
+
path = normalizePath.call(this, path);
|
|
63
63
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
64
64
|
checkName(this, name, path, 'xattr.set');
|
|
65
65
|
const inode = await fs.stat(resolved).catch(rethrow('xattr.set', path));
|
|
@@ -83,7 +83,7 @@ export async function set(path, name, value, opt = {}) {
|
|
|
83
83
|
* @param opt Options for the operation
|
|
84
84
|
*/
|
|
85
85
|
export function setSync(path, name, value, opt = {}) {
|
|
86
|
-
path = normalizePath(path);
|
|
86
|
+
path = normalizePath.call(this, path);
|
|
87
87
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
88
88
|
checkName(this, name, path, 'xattr.set');
|
|
89
89
|
let inode;
|
|
@@ -116,7 +116,7 @@ export function setSync(path, name, value, opt = {}) {
|
|
|
116
116
|
* @param name Name of the attribute to remove
|
|
117
117
|
*/
|
|
118
118
|
export async function remove(path, name) {
|
|
119
|
-
path = normalizePath(path);
|
|
119
|
+
path = normalizePath.call(this, path);
|
|
120
120
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
121
121
|
checkName(this, name, path, 'xattr.remove');
|
|
122
122
|
const inode = await fs.stat(resolved).catch(rethrow('xattr.remove', path));
|
|
@@ -136,7 +136,7 @@ export async function remove(path, name) {
|
|
|
136
136
|
* @param name Name of the attribute to remove
|
|
137
137
|
*/
|
|
138
138
|
export function removeSync(path, name) {
|
|
139
|
-
path = normalizePath(path);
|
|
139
|
+
path = normalizePath.call(this, path);
|
|
140
140
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
141
141
|
checkName(this, name, path, 'xattr.remove');
|
|
142
142
|
let inode;
|
|
@@ -167,7 +167,7 @@ export function removeSync(path, name) {
|
|
|
167
167
|
* @returns Array of attribute names
|
|
168
168
|
*/
|
|
169
169
|
export async function list(path) {
|
|
170
|
-
path = normalizePath(path);
|
|
170
|
+
path = normalizePath.call(this, path);
|
|
171
171
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
172
172
|
const inode = await fs.stat(resolved).catch(rethrow('xattr.list', path));
|
|
173
173
|
if (!inode.attributes)
|
|
@@ -181,7 +181,7 @@ export async function list(path) {
|
|
|
181
181
|
* @returns Array of attribute names
|
|
182
182
|
*/
|
|
183
183
|
export function listSync(path) {
|
|
184
|
-
path = normalizePath(path);
|
|
184
|
+
path = normalizePath.call(this, path);
|
|
185
185
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
186
186
|
let inode;
|
|
187
187
|
try {
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
2
|
-
import { bindContext, configure, fs, InMemory } from '@zenfs/core';
|
|
2
|
+
import { bindContext, configure, fs, InMemory, type BoundContext } from '@zenfs/core';
|
|
3
3
|
import assert from 'node:assert/strict';
|
|
4
4
|
import { suite, test } from 'node:test';
|
|
5
5
|
import { canary } from 'utilium';
|
|
@@ -124,4 +124,14 @@ suite('Context', () => {
|
|
|
124
124
|
assert.equal(ctx.readFileSync('/copy.txt', 'utf-8'), 'not in real root!');
|
|
125
125
|
assert(fs.readdirSync('/ctx').includes('copy.txt'));
|
|
126
126
|
});
|
|
127
|
+
|
|
128
|
+
test('bindContext should resolve root relative to the parent context', () => {
|
|
129
|
+
fs.mkdirSync('/secure');
|
|
130
|
+
const ctx = bindContext({ root: '/secure', credentials: { uid: 333, gid: 333 } });
|
|
131
|
+
|
|
132
|
+
const plain = bindContext({ credentials: { uid: 1, gid: 1 } });
|
|
133
|
+
console.log('child of / context: ok, root =', plain.root);
|
|
134
|
+
|
|
135
|
+
assert.doesNotThrow(() => ctx.bind({ credentials: { uid: 500, gid: 500 } }));
|
|
136
|
+
});
|
|
127
137
|
});
|
|
@@ -54,7 +54,7 @@ suite('Permissions', config('permissions'), () => {
|
|
|
54
54
|
assert.equal(error.code, 'EACCES');
|
|
55
55
|
});
|
|
56
56
|
if (!stats) return;
|
|
57
|
-
assert(hasAccess(defaultContext, stats, X_OK));
|
|
57
|
+
if (stats.isDirectory()) assert(hasAccess(defaultContext, stats, X_OK));
|
|
58
58
|
|
|
59
59
|
function checkError(access: number) {
|
|
60
60
|
return function (error: Exception) {
|