@zenfs/core 1.11.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backends/backend.d.ts +19 -15
- package/dist/backends/backend.js +31 -15
- package/dist/backends/cow.d.ts +20 -30
- package/dist/backends/cow.js +52 -142
- package/dist/backends/fetch.d.ts +1 -0
- package/dist/backends/fetch.js +3 -1
- package/dist/backends/index.d.ts +1 -1
- package/dist/backends/index.js +1 -1
- package/dist/backends/memory.d.ts +5 -7
- package/dist/backends/memory.js +2 -3
- package/dist/backends/passthrough.d.ts +19 -22
- package/dist/backends/passthrough.js +85 -160
- package/dist/backends/port.d.ts +207 -0
- package/dist/backends/port.js +297 -0
- package/dist/backends/single_buffer.d.ts +11 -5
- package/dist/backends/single_buffer.js +18 -12
- package/dist/backends/store/fs.d.ts +11 -27
- package/dist/backends/store/fs.js +67 -91
- package/dist/backends/store/store.d.ts +7 -12
- package/dist/config.d.ts +1 -10
- package/dist/config.js +7 -8
- package/dist/context.d.ts +8 -21
- package/dist/context.js +33 -10
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/internal/contexts.d.ts +63 -0
- package/dist/internal/contexts.js +15 -0
- package/dist/internal/credentials.d.ts +2 -11
- package/dist/internal/credentials.js +0 -19
- package/dist/internal/devices.d.ts +18 -80
- package/dist/internal/devices.js +76 -279
- package/dist/internal/file_index.js +3 -3
- package/dist/internal/filesystem.d.ts +31 -89
- package/dist/internal/filesystem.js +21 -20
- package/dist/internal/index.d.ts +0 -1
- package/dist/internal/index.js +0 -1
- package/dist/internal/index_fs.d.ts +12 -30
- package/dist/internal/index_fs.js +23 -55
- package/dist/internal/inode.d.ts +147 -9
- package/dist/internal/inode.js +333 -25
- package/dist/internal/log.d.ts +19 -13
- package/dist/internal/log.js +81 -80
- package/dist/mixins/async.js +26 -90
- package/dist/mixins/mutexed.d.ts +17 -16
- package/dist/mixins/mutexed.js +29 -31
- package/dist/mixins/readonly.d.ts +7 -6
- package/dist/mixins/readonly.js +6 -0
- package/dist/mixins/sync.js +8 -8
- package/dist/{vfs/path.d.ts → path.d.ts} +3 -4
- package/dist/{vfs/path.js → path.js} +6 -9
- package/dist/readline.d.ts +134 -0
- package/dist/readline.js +623 -0
- package/dist/utils.d.ts +4 -35
- package/dist/utils.js +8 -73
- package/dist/vfs/acl.d.ts +42 -0
- package/dist/vfs/acl.js +249 -0
- package/dist/vfs/async.d.ts +7 -21
- package/dist/vfs/async.js +19 -19
- package/dist/vfs/config.d.ts +6 -18
- package/dist/vfs/config.js +8 -18
- package/dist/vfs/dir.d.ts +3 -3
- package/dist/vfs/dir.js +9 -8
- package/dist/vfs/file.d.ts +106 -0
- package/dist/vfs/file.js +235 -0
- package/dist/vfs/flags.d.ts +19 -0
- package/dist/vfs/flags.js +62 -0
- package/dist/vfs/index.d.ts +4 -10
- package/dist/vfs/index.js +4 -13
- package/dist/vfs/ioctl.d.ts +87 -0
- package/dist/vfs/ioctl.js +304 -0
- package/dist/vfs/promises.d.ts +78 -16
- package/dist/vfs/promises.js +273 -122
- package/dist/vfs/shared.d.ts +7 -26
- package/dist/vfs/shared.js +25 -53
- package/dist/{stats.d.ts → vfs/stats.d.ts} +14 -28
- package/dist/{stats.js → vfs/stats.js} +11 -66
- package/dist/vfs/streams.d.ts +1 -0
- package/dist/vfs/streams.js +24 -19
- package/dist/vfs/sync.d.ts +4 -3
- package/dist/vfs/sync.js +143 -128
- package/dist/vfs/watchers.d.ts +2 -2
- package/dist/vfs/watchers.js +6 -6
- package/dist/vfs/xattr.d.ts +116 -0
- package/dist/vfs/xattr.js +218 -0
- package/package.json +3 -3
- package/readme.md +1 -1
- package/tests/backend/config.worker.js +4 -1
- package/tests/backend/fetch.test.ts +3 -0
- package/tests/backend/port.test.ts +21 -35
- package/tests/backend/remote.worker.js +4 -1
- package/tests/backend/single-buffer.test.ts +24 -0
- package/tests/common/context.test.ts +1 -1
- package/tests/common/handle.test.ts +17 -12
- package/tests/common/path.test.ts +1 -1
- package/tests/common/readline.test.ts +104 -0
- package/tests/common.ts +4 -19
- package/tests/fetch/fetch.ts +1 -1
- package/tests/fs/links.test.ts +1 -1
- package/tests/fs/permissions.test.ts +7 -6
- package/tests/fs/readFile.test.ts +3 -3
- package/tests/fs/stat.test.ts +6 -6
- package/tests/fs/streams.test.ts +2 -11
- package/tests/fs/times.test.ts +1 -1
- package/tests/fs/xattr.test.ts +85 -0
- package/tests/logs.js +22 -0
- package/tests/setup/context.ts +1 -1
- package/tests/setup/index.ts +3 -3
- package/tests/setup/port.ts +1 -1
- package/dist/backends/port/fs.d.ts +0 -84
- package/dist/backends/port/fs.js +0 -151
- package/dist/backends/port/rpc.d.ts +0 -77
- package/dist/backends/port/rpc.js +0 -100
- package/dist/backends/store/simple.d.ts +0 -20
- package/dist/backends/store/simple.js +0 -13
- package/dist/internal/file.d.ts +0 -359
- package/dist/internal/file.js +0 -751
package/dist/vfs/promises.js
CHANGED
|
@@ -51,37 +51,74 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
51
51
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
52
|
});
|
|
53
53
|
import { Buffer } from 'buffer';
|
|
54
|
-
import { _throw } from 'utilium';
|
|
55
|
-
import {
|
|
54
|
+
import { _throw, decodeUTF8, pick } from 'utilium';
|
|
55
|
+
import { defaultContext } from '../internal/contexts.js';
|
|
56
56
|
import { Errno, ErrnoError } from '../internal/error.js';
|
|
57
|
-
import {
|
|
57
|
+
import { hasAccess, InodeFlags, isBlockDevice, isCharacterDevice, isDirectory, isSymbolicLink } from '../internal/inode.js';
|
|
58
|
+
import { dirname, join, parse, resolve } from '../path.js';
|
|
58
59
|
import '../polyfills.js';
|
|
59
|
-
import {
|
|
60
|
-
import {
|
|
61
|
-
import {
|
|
60
|
+
import { createInterface } from '../readline.js';
|
|
61
|
+
import { normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
62
|
+
import { checkAccess } from './config.js';
|
|
62
63
|
import * as constants from './constants.js';
|
|
63
64
|
import { Dir, Dirent } from './dir.js';
|
|
64
|
-
import {
|
|
65
|
-
import
|
|
65
|
+
import { deleteFD, fromFD, SyncHandle, toFD } from './file.js';
|
|
66
|
+
import * as flags from './flags.js';
|
|
67
|
+
import { _statfs, fixError, resolveMount } from './shared.js';
|
|
68
|
+
import { _chown, BigIntStats, Stats } from './stats.js';
|
|
66
69
|
import { ReadStream, WriteStream } from './streams.js';
|
|
67
|
-
import {
|
|
70
|
+
import { emitChange, FSWatcher } from './watchers.js';
|
|
68
71
|
export * as constants from './constants.js';
|
|
69
72
|
export class FileHandle {
|
|
70
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Get the current file position.
|
|
75
|
+
*
|
|
76
|
+
* We emulate the following bug mentioned in the Node documentation:
|
|
77
|
+
*
|
|
78
|
+
* On Linux, positional writes don't work when the file is opened in append mode.
|
|
79
|
+
* The kernel ignores the position argument and always appends the data to the end of the file.
|
|
80
|
+
* @returns The current file position.
|
|
81
|
+
*/
|
|
82
|
+
get position() {
|
|
83
|
+
return this.flag & constants.O_APPEND ? this.inode.size : this._position;
|
|
84
|
+
}
|
|
85
|
+
set position(value) {
|
|
86
|
+
this._position = value;
|
|
87
|
+
}
|
|
88
|
+
constructor(context, fd) {
|
|
71
89
|
this.context = context;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
90
|
+
this.fd = fd;
|
|
91
|
+
/**
|
|
92
|
+
* Current position
|
|
93
|
+
*/
|
|
94
|
+
this._position = 0;
|
|
95
|
+
/**
|
|
96
|
+
* Whether the file has changes which have not been written to the FS
|
|
97
|
+
*/
|
|
98
|
+
this.dirty = false;
|
|
99
|
+
/**
|
|
100
|
+
* Whether the file is open or closed
|
|
101
|
+
*/
|
|
102
|
+
this.closed = false;
|
|
103
|
+
const sync = fromFD(context, fd);
|
|
104
|
+
Object.assign(this, pick(sync, 'path', 'fs', 'internalPath', 'flag', 'inode'));
|
|
105
|
+
}
|
|
106
|
+
get _isSync() {
|
|
107
|
+
return !!(this.flag & constants.O_SYNC || this.inode.flags & InodeFlags.Sync);
|
|
75
108
|
}
|
|
76
109
|
_emitChange() {
|
|
77
|
-
|
|
78
|
-
emitChange(this.context, 'change', this.file.path.slice((_c = (_b = (_a = this.context) === null || _a === void 0 ? void 0 : _a.root) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0));
|
|
110
|
+
emitChange(this.context, 'change', this.path);
|
|
79
111
|
}
|
|
80
112
|
/**
|
|
81
113
|
* Asynchronous fchown(2) - Change ownership of a file.
|
|
82
114
|
*/
|
|
83
115
|
async chown(uid, gid) {
|
|
84
|
-
|
|
116
|
+
if (this.closed)
|
|
117
|
+
throw ErrnoError.With('EBADF', this.path, 'chown');
|
|
118
|
+
this.dirty = true;
|
|
119
|
+
_chown(this.inode, uid, gid);
|
|
120
|
+
if (this._isSync)
|
|
121
|
+
await this.sync();
|
|
85
122
|
this._emitChange();
|
|
86
123
|
}
|
|
87
124
|
/**
|
|
@@ -92,30 +129,49 @@ export class FileHandle {
|
|
|
92
129
|
const numMode = normalizeMode(mode, -1);
|
|
93
130
|
if (numMode < 0)
|
|
94
131
|
throw new ErrnoError(Errno.EINVAL, 'Invalid mode');
|
|
95
|
-
|
|
132
|
+
if (this.closed)
|
|
133
|
+
throw ErrnoError.With('EBADF', this.path, 'chmod');
|
|
134
|
+
this.dirty = true;
|
|
135
|
+
this.inode.mode = (this.inode.mode & (numMode > constants.S_IFMT ? ~constants.S_IFMT : constants.S_IFMT)) | numMode;
|
|
136
|
+
if (this._isSync || numMode > constants.S_IFMT)
|
|
137
|
+
await this.sync();
|
|
96
138
|
this._emitChange();
|
|
97
139
|
}
|
|
98
140
|
/**
|
|
99
141
|
* Asynchronous fdatasync(2) - synchronize a file's in-core state with storage device.
|
|
100
142
|
*/
|
|
101
143
|
datasync() {
|
|
102
|
-
return this.
|
|
144
|
+
return this.sync();
|
|
103
145
|
}
|
|
104
146
|
/**
|
|
105
147
|
* Asynchronous fsync(2) - synchronize a file's in-core state with the underlying storage device.
|
|
106
148
|
*/
|
|
107
|
-
sync() {
|
|
108
|
-
|
|
149
|
+
async sync() {
|
|
150
|
+
if (this.closed)
|
|
151
|
+
throw ErrnoError.With('EBADF', this.path, 'sync');
|
|
152
|
+
if (!this.dirty)
|
|
153
|
+
return;
|
|
154
|
+
if (!this.fs.attributes.has('no_write'))
|
|
155
|
+
await this.fs.touch(this.internalPath, this.inode);
|
|
156
|
+
this.dirty = false;
|
|
109
157
|
}
|
|
110
158
|
/**
|
|
111
159
|
* Asynchronous ftruncate(2) - Truncate a file to a specified length.
|
|
112
160
|
* @param length If not specified, defaults to `0`.
|
|
113
161
|
*/
|
|
114
|
-
async truncate(length) {
|
|
115
|
-
|
|
162
|
+
async truncate(length = 0) {
|
|
163
|
+
if (this.closed)
|
|
164
|
+
throw ErrnoError.With('EBADF', this.path, 'truncate');
|
|
116
165
|
if (length < 0)
|
|
117
166
|
throw new ErrnoError(Errno.EINVAL);
|
|
118
|
-
|
|
167
|
+
this.dirty = true;
|
|
168
|
+
if (!(this.flag & constants.O_WRONLY || this.flag & constants.O_RDWR)) {
|
|
169
|
+
throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode', this.path, 'truncate');
|
|
170
|
+
}
|
|
171
|
+
this.inode.mtimeMs = Date.now();
|
|
172
|
+
this.inode.size = length;
|
|
173
|
+
if (this._isSync)
|
|
174
|
+
await this.sync();
|
|
119
175
|
this._emitChange();
|
|
120
176
|
}
|
|
121
177
|
/**
|
|
@@ -124,7 +180,13 @@ export class FileHandle {
|
|
|
124
180
|
* @param mtime The last modified time. If a string is provided, it will be coerced to number.
|
|
125
181
|
*/
|
|
126
182
|
async utimes(atime, mtime) {
|
|
127
|
-
|
|
183
|
+
if (this.closed)
|
|
184
|
+
throw ErrnoError.With('EBADF', this.path, 'utimes');
|
|
185
|
+
this.dirty = true;
|
|
186
|
+
this.inode.atimeMs = normalizeTime(atime);
|
|
187
|
+
this.inode.mtimeMs = normalizeTime(mtime);
|
|
188
|
+
if (this._isSync)
|
|
189
|
+
await this.sync();
|
|
128
190
|
this._emitChange();
|
|
129
191
|
}
|
|
130
192
|
/**
|
|
@@ -138,17 +200,45 @@ export class FileHandle {
|
|
|
138
200
|
*/
|
|
139
201
|
async appendFile(data, _options = {}) {
|
|
140
202
|
const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
|
|
141
|
-
const flag =
|
|
142
|
-
if (!
|
|
203
|
+
const flag = flags.parse(options.flag);
|
|
204
|
+
if (!(flag & constants.O_APPEND)) {
|
|
143
205
|
throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending');
|
|
144
206
|
}
|
|
145
207
|
if (typeof data != 'string' && !options.encoding) {
|
|
146
208
|
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
147
209
|
}
|
|
148
210
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : data;
|
|
149
|
-
await this.
|
|
211
|
+
await this._write(encodedData, 0, encodedData.length);
|
|
150
212
|
this._emitChange();
|
|
151
213
|
}
|
|
214
|
+
/**
|
|
215
|
+
* Read data from the file.
|
|
216
|
+
* @param buffer The buffer that the data will be written to.
|
|
217
|
+
* @param offset The offset within the buffer where writing will start.
|
|
218
|
+
* @param length An integer specifying the number of bytes to read.
|
|
219
|
+
* @param position An integer specifying where to begin reading from in the file.
|
|
220
|
+
* If position is unset, data will be read from the current file position.
|
|
221
|
+
*/
|
|
222
|
+
async _read(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
|
|
223
|
+
if (this.closed)
|
|
224
|
+
throw ErrnoError.With('EBADF', this.path, 'read');
|
|
225
|
+
if (this.flag & constants.O_WRONLY)
|
|
226
|
+
throw new ErrnoError(Errno.EPERM, 'File not opened with a readable mode');
|
|
227
|
+
if (!(this.inode.flags & InodeFlags.NoAtime)) {
|
|
228
|
+
this.dirty = true;
|
|
229
|
+
this.inode.atimeMs = Date.now();
|
|
230
|
+
}
|
|
231
|
+
let end = position + length;
|
|
232
|
+
if (!isCharacterDevice(this.inode) && !isBlockDevice(this.inode) && end > this.inode.size) {
|
|
233
|
+
end = position + Math.max(this.inode.size - position, 0);
|
|
234
|
+
}
|
|
235
|
+
this._position = end;
|
|
236
|
+
const uint8 = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
237
|
+
await this.fs.read(this.internalPath, uint8.subarray(offset, offset + length), position, end);
|
|
238
|
+
if (this._isSync)
|
|
239
|
+
await this.sync();
|
|
240
|
+
return { bytesRead: end - position, buffer };
|
|
241
|
+
}
|
|
152
242
|
async read(buffer, offset, length, position) {
|
|
153
243
|
if (typeof offset == 'object' && offset != null) {
|
|
154
244
|
position = offset.position;
|
|
@@ -161,19 +251,19 @@ export class FileHandle {
|
|
|
161
251
|
offset = buffer.offset;
|
|
162
252
|
buffer = buffer.buffer;
|
|
163
253
|
}
|
|
164
|
-
const pos = Number.isSafeInteger(position) ? position : this.
|
|
165
|
-
buffer || (buffer = new Uint8Array(
|
|
254
|
+
const pos = Number.isSafeInteger(position) ? position : this.position;
|
|
255
|
+
buffer || (buffer = new Uint8Array(this.inode.size));
|
|
166
256
|
offset !== null && offset !== void 0 ? offset : (offset = 0);
|
|
167
|
-
return this.
|
|
257
|
+
return this._read(buffer, offset, length !== null && length !== void 0 ? length : buffer.byteLength - offset, pos);
|
|
168
258
|
}
|
|
169
259
|
async readFile(_options) {
|
|
170
260
|
const options = normalizeOptions(_options, null, 'r', 0o444);
|
|
171
|
-
const flag =
|
|
172
|
-
if (
|
|
173
|
-
throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for reading');
|
|
261
|
+
const flag = flags.parse(options.flag);
|
|
262
|
+
if (flag & constants.O_WRONLY) {
|
|
263
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for reading', this.path, 'readFile');
|
|
174
264
|
}
|
|
175
265
|
const { size } = await this.stat();
|
|
176
|
-
const { buffer: data } = await this.
|
|
266
|
+
const { buffer: data } = await this._read(new Uint8Array(size), 0, size, 0);
|
|
177
267
|
const buffer = Buffer.from(data);
|
|
178
268
|
return options.encoding ? buffer.toString(options.encoding) : buffer;
|
|
179
269
|
}
|
|
@@ -182,23 +272,72 @@ export class FileHandle {
|
|
|
182
272
|
* The handle will not be closed automatically.
|
|
183
273
|
*/
|
|
184
274
|
readableWebStream(options = {}) {
|
|
185
|
-
|
|
275
|
+
if (this.closed)
|
|
276
|
+
throw ErrnoError.With('EBADF', this.path, 'readableWebStream');
|
|
277
|
+
return this.fs.streamRead(this.internalPath, options);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Not part of the Node.js API!
|
|
281
|
+
*
|
|
282
|
+
* Write file data using a `WritableStream`.
|
|
283
|
+
* The handle will not be closed automatically.
|
|
284
|
+
* @internal
|
|
285
|
+
*/
|
|
286
|
+
writableWebStream(options = {}) {
|
|
287
|
+
if (this.closed)
|
|
288
|
+
throw ErrnoError.With('EBADF', this.path, 'writableWebStream');
|
|
289
|
+
if (this.inode.flags & InodeFlags.Immutable)
|
|
290
|
+
throw new ErrnoError(Errno.EPERM, 'File is immutable', this.path, 'writableWebStream');
|
|
291
|
+
return this.fs.streamWrite(this.internalPath, options);
|
|
186
292
|
}
|
|
187
293
|
/**
|
|
188
|
-
*
|
|
294
|
+
* Creates a readline Interface object that allows reading the file line by line
|
|
295
|
+
* @param options Options for creating a read stream
|
|
296
|
+
* @returns A readline interface for reading the file line by line
|
|
189
297
|
*/
|
|
190
298
|
readLines(options) {
|
|
191
|
-
|
|
299
|
+
if (this.closed || this.flag & constants.O_WRONLY)
|
|
300
|
+
throw ErrnoError.With('EBADF', this.path, 'readLines');
|
|
301
|
+
return createInterface({ input: this.createReadStream(options), crlfDelay: Infinity });
|
|
192
302
|
}
|
|
193
303
|
[Symbol.asyncDispose]() {
|
|
194
304
|
return this.close();
|
|
195
305
|
}
|
|
196
306
|
async stat(opts) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
307
|
+
if (this.closed)
|
|
308
|
+
throw ErrnoError.With('EBADF', this.path, 'stat');
|
|
309
|
+
if (checkAccess && !hasAccess(this.context, this.inode, constants.R_OK)) {
|
|
310
|
+
throw ErrnoError.With('EACCES', this.path, 'stat');
|
|
200
311
|
}
|
|
201
|
-
return (opts === null || opts === void 0 ? void 0 : opts.bigint) ? new BigIntStats(
|
|
312
|
+
return (opts === null || opts === void 0 ? void 0 : opts.bigint) ? new BigIntStats(this.inode) : new Stats(this.inode);
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Write buffer to the file.
|
|
316
|
+
* @param buffer Uint8Array containing the data to write to the file.
|
|
317
|
+
* @param offset Offset in the buffer to start reading data from.
|
|
318
|
+
* @param length The amount of bytes to write to the file.
|
|
319
|
+
* @param position Offset from the beginning of the file where this data should be written.
|
|
320
|
+
* If position is null, the data will be written at the current position.
|
|
321
|
+
*/
|
|
322
|
+
async _write(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
|
|
323
|
+
if (this.closed)
|
|
324
|
+
throw ErrnoError.With('EBADF', this.path, 'write');
|
|
325
|
+
if (this.inode.flags & InodeFlags.Immutable)
|
|
326
|
+
throw new ErrnoError(Errno.EPERM, 'File is immutable', this.path, 'write');
|
|
327
|
+
if (!(this.flag & constants.O_WRONLY || this.flag & constants.O_RDWR))
|
|
328
|
+
throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode', this.path, 'write');
|
|
329
|
+
this.dirty = true;
|
|
330
|
+
const end = position + length;
|
|
331
|
+
const slice = buffer.subarray(offset, offset + length);
|
|
332
|
+
if (!isCharacterDevice(this.inode) && !isBlockDevice(this.inode) && end > this.inode.size)
|
|
333
|
+
this.inode.size = end;
|
|
334
|
+
this.inode.mtimeMs = Date.now();
|
|
335
|
+
this.inode.ctimeMs = Date.now();
|
|
336
|
+
this._position = position + slice.byteLength;
|
|
337
|
+
await this.fs.write(this.internalPath, slice, position);
|
|
338
|
+
if (this._isSync)
|
|
339
|
+
await this.sync();
|
|
340
|
+
return slice.byteLength;
|
|
202
341
|
}
|
|
203
342
|
/**
|
|
204
343
|
* Asynchronously writes `string` to the file.
|
|
@@ -225,8 +364,8 @@ export class FileHandle {
|
|
|
225
364
|
length = typeof lenOrEnc == 'number' ? lenOrEnc : buffer.byteLength;
|
|
226
365
|
position = typeof position === 'number' ? position : null;
|
|
227
366
|
}
|
|
228
|
-
position !== null && position !== void 0 ? position : (position = this.
|
|
229
|
-
const bytesWritten = await this.
|
|
367
|
+
position !== null && position !== void 0 ? position : (position = this.position);
|
|
368
|
+
const bytesWritten = await this._write(buffer, offset, length, position);
|
|
230
369
|
this._emitChange();
|
|
231
370
|
return { buffer: data, bytesWritten };
|
|
232
371
|
}
|
|
@@ -242,23 +381,36 @@ export class FileHandle {
|
|
|
242
381
|
*/
|
|
243
382
|
async writeFile(data, _options = {}) {
|
|
244
383
|
const options = normalizeOptions(_options, 'utf8', 'w', 0o644);
|
|
245
|
-
const flag =
|
|
246
|
-
if (!
|
|
247
|
-
throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for writing');
|
|
384
|
+
const flag = flags.parse(options.flag);
|
|
385
|
+
if (!(flag & constants.O_WRONLY || flag & constants.O_RDWR)) {
|
|
386
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for writing', this.path, 'writeFile');
|
|
248
387
|
}
|
|
249
388
|
if (typeof data != 'string' && !options.encoding) {
|
|
250
389
|
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
251
390
|
}
|
|
252
391
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : data;
|
|
253
|
-
await this.
|
|
392
|
+
await this._write(encodedData, 0, encodedData.length, 0);
|
|
254
393
|
this._emitChange();
|
|
255
394
|
}
|
|
256
395
|
/**
|
|
257
396
|
* Asynchronous close(2) - close a `FileHandle`.
|
|
258
397
|
*/
|
|
259
398
|
async close() {
|
|
260
|
-
|
|
261
|
-
|
|
399
|
+
if (this.closed)
|
|
400
|
+
throw ErrnoError.With('EBADF', this.path, 'close');
|
|
401
|
+
await this.sync();
|
|
402
|
+
this.dispose();
|
|
403
|
+
deleteFD(this.context, this.fd);
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Cleans up. This will *not* sync the file data to the FS
|
|
407
|
+
*/
|
|
408
|
+
dispose(force) {
|
|
409
|
+
if (this.closed)
|
|
410
|
+
throw ErrnoError.With('EBADF', this.path, 'dispose');
|
|
411
|
+
if (this.dirty && !force)
|
|
412
|
+
throw ErrnoError.With('EBUSY', this.path, 'dispose');
|
|
413
|
+
this.closed = true;
|
|
262
414
|
}
|
|
263
415
|
/**
|
|
264
416
|
* Asynchronous `writev`. Writes from multiple buffers.
|
|
@@ -268,7 +420,7 @@ export class FileHandle {
|
|
|
268
420
|
*/
|
|
269
421
|
async writev(buffers, position) {
|
|
270
422
|
if (typeof position == 'number')
|
|
271
|
-
this.
|
|
423
|
+
this.position = position;
|
|
272
424
|
let bytesWritten = 0;
|
|
273
425
|
for (const buffer of buffers) {
|
|
274
426
|
bytesWritten += (await this.write(buffer)).bytesWritten;
|
|
@@ -283,7 +435,7 @@ export class FileHandle {
|
|
|
283
435
|
*/
|
|
284
436
|
async readv(buffers, position) {
|
|
285
437
|
if (typeof position == 'number')
|
|
286
|
-
this.
|
|
438
|
+
this.position = position;
|
|
287
439
|
let bytesRead = 0;
|
|
288
440
|
for (const buffer of buffers) {
|
|
289
441
|
bytesRead += (await this.read(buffer)).bytesRead;
|
|
@@ -295,6 +447,8 @@ export class FileHandle {
|
|
|
295
447
|
* @param options Options for the readable stream
|
|
296
448
|
*/
|
|
297
449
|
createReadStream(options = {}) {
|
|
450
|
+
if (this.closed || this.flag & constants.O_WRONLY)
|
|
451
|
+
throw ErrnoError.With('EBADF', this.path, 'createReadStream');
|
|
298
452
|
return new ReadStream(options, this);
|
|
299
453
|
}
|
|
300
454
|
/**
|
|
@@ -302,6 +456,10 @@ export class FileHandle {
|
|
|
302
456
|
* @param options Options for the writeable stream.
|
|
303
457
|
*/
|
|
304
458
|
createWriteStream(options = {}) {
|
|
459
|
+
if (this.closed)
|
|
460
|
+
throw ErrnoError.With('EBADF', this.path, 'createWriteStream');
|
|
461
|
+
if (this.inode.flags & InodeFlags.Immutable)
|
|
462
|
+
throw new ErrnoError(Errno.EPERM, 'File is immutable', this.path, 'createWriteStream');
|
|
305
463
|
return new WriteStream(options, this);
|
|
306
464
|
}
|
|
307
465
|
}
|
|
@@ -310,7 +468,7 @@ export async function rename(oldPath, newPath) {
|
|
|
310
468
|
newPath = normalizePath(newPath);
|
|
311
469
|
const src = resolveMount(oldPath, this);
|
|
312
470
|
const dst = resolveMount(newPath, this);
|
|
313
|
-
if (
|
|
471
|
+
if (checkAccess && !(await stat.call(this, dirname(oldPath))).hasAccess(constants.W_OK, this)) {
|
|
314
472
|
throw ErrnoError.With('EACCES', oldPath, 'rename');
|
|
315
473
|
}
|
|
316
474
|
try {
|
|
@@ -349,10 +507,10 @@ export async function stat(path, options) {
|
|
|
349
507
|
const { fs, path: resolved } = resolveMount(await realpath.call(this, path), this);
|
|
350
508
|
try {
|
|
351
509
|
const stats = await fs.stat(resolved);
|
|
352
|
-
if (
|
|
510
|
+
if (checkAccess && !hasAccess(this, stats, constants.R_OK)) {
|
|
353
511
|
throw ErrnoError.With('EACCES', resolved, 'stat');
|
|
354
512
|
}
|
|
355
|
-
return (options === null || options === void 0 ? void 0 : options.bigint) ? new BigIntStats(stats) : stats;
|
|
513
|
+
return (options === null || options === void 0 ? void 0 : options.bigint) ? new BigIntStats(stats) : new Stats(stats);
|
|
356
514
|
}
|
|
357
515
|
catch (e) {
|
|
358
516
|
throw fixError(e, { [resolved]: path });
|
|
@@ -364,7 +522,10 @@ export async function lstat(path, options) {
|
|
|
364
522
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
365
523
|
try {
|
|
366
524
|
const stats = await fs.stat(resolved);
|
|
367
|
-
|
|
525
|
+
if (checkAccess && !hasAccess(this, stats, constants.R_OK)) {
|
|
526
|
+
throw ErrnoError.With('EACCES', resolved, 'lstat');
|
|
527
|
+
}
|
|
528
|
+
return (options === null || options === void 0 ? void 0 : options.bigint) ? new BigIntStats(stats) : new Stats(stats);
|
|
368
529
|
}
|
|
369
530
|
catch (e) {
|
|
370
531
|
throw fixError(e, { [resolved]: path });
|
|
@@ -393,7 +554,7 @@ export async function unlink(path) {
|
|
|
393
554
|
path = normalizePath(path);
|
|
394
555
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
395
556
|
try {
|
|
396
|
-
if (
|
|
557
|
+
if (checkAccess && !hasAccess(this, await fs.stat(resolved), constants.W_OK)) {
|
|
397
558
|
throw ErrnoError.With('EACCES', resolved, 'unlink');
|
|
398
559
|
}
|
|
399
560
|
await fs.unlink(resolved);
|
|
@@ -404,16 +565,6 @@ export async function unlink(path) {
|
|
|
404
565
|
}
|
|
405
566
|
}
|
|
406
567
|
unlink;
|
|
407
|
-
/**
|
|
408
|
-
* Manually apply setuid/setgid.
|
|
409
|
-
*/
|
|
410
|
-
async function applySetId(file, uid, gid) {
|
|
411
|
-
if (file.fs.attributes.has('setid'))
|
|
412
|
-
return;
|
|
413
|
-
const parent = await file.fs.stat(dirname(file.path));
|
|
414
|
-
await file.chown(parent.mode & constants.S_ISUID ? parent.uid : uid, // manually apply setuid/setgid
|
|
415
|
-
parent.mode & constants.S_ISGID ? parent.gid : gid);
|
|
416
|
-
}
|
|
417
568
|
/**
|
|
418
569
|
* Opens a file. This helper handles the complexity of file flags.
|
|
419
570
|
* @internal
|
|
@@ -421,47 +572,48 @@ async function applySetId(file, uid, gid) {
|
|
|
421
572
|
async function _open($, path, opt) {
|
|
422
573
|
var _a;
|
|
423
574
|
path = normalizePath(path);
|
|
424
|
-
const mode = normalizeMode(opt.mode, 0o644), flag =
|
|
575
|
+
const mode = normalizeMode(opt.mode, 0o644), flag = flags.parse(opt.flag);
|
|
425
576
|
const { fullPath, fs, path: resolved, stats } = await _resolve($, path.toString(), opt.preserveSymlinks);
|
|
426
577
|
if (!stats) {
|
|
427
|
-
if (
|
|
578
|
+
if (!(flag & constants.O_CREAT)) {
|
|
428
579
|
throw ErrnoError.With('ENOENT', fullPath, '_open');
|
|
429
580
|
}
|
|
430
581
|
// Create the file
|
|
431
582
|
const parentStats = await fs.stat(dirname(resolved));
|
|
432
|
-
if (
|
|
583
|
+
if (checkAccess && !hasAccess($, parentStats, constants.W_OK)) {
|
|
433
584
|
throw ErrnoError.With('EACCES', dirname(fullPath), '_open');
|
|
434
585
|
}
|
|
435
|
-
if (!
|
|
586
|
+
if (!isDirectory(parentStats)) {
|
|
436
587
|
throw ErrnoError.With('ENOTDIR', dirname(fullPath), '_open');
|
|
437
588
|
}
|
|
438
|
-
const { euid: uid, egid: gid } = (_a = $ === null || $ === void 0 ? void 0 : $.credentials) !== null && _a !== void 0 ? _a : credentials;
|
|
439
|
-
const
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
589
|
+
const { euid: uid, egid: gid } = (_a = $ === null || $ === void 0 ? void 0 : $.credentials) !== null && _a !== void 0 ? _a : defaultContext.credentials;
|
|
590
|
+
const inode = await fs.createFile(resolved, {
|
|
591
|
+
mode,
|
|
592
|
+
uid: parentStats.mode & constants.S_ISUID ? parentStats.uid : uid,
|
|
593
|
+
gid: parentStats.mode & constants.S_ISGID ? parentStats.gid : gid,
|
|
594
|
+
});
|
|
595
|
+
return new FileHandle($, toFD(new SyncHandle($, path, fs, resolved, flag, inode)));
|
|
596
|
+
}
|
|
597
|
+
if (checkAccess && !hasAccess($, stats, flags.toMode(flag))) {
|
|
444
598
|
throw ErrnoError.With('EACCES', fullPath, '_open');
|
|
445
599
|
}
|
|
446
|
-
if (
|
|
600
|
+
if (flag & constants.O_EXCL)
|
|
447
601
|
throw ErrnoError.With('EEXIST', fullPath, '_open');
|
|
448
|
-
|
|
449
|
-
const handle = new FileHandle(await fs.openFile(resolved, flag), $);
|
|
602
|
+
const handle = new FileHandle($, toFD(new SyncHandle($, path, fs, resolved, flag, stats)));
|
|
450
603
|
/*
|
|
451
604
|
In a previous implementation, we deleted the file and
|
|
452
605
|
re-created it. However, this created a race condition if another
|
|
453
606
|
asynchronous request was trying to read the file, as the file
|
|
454
607
|
would not exist for a small period of time.
|
|
455
608
|
*/
|
|
456
|
-
if (
|
|
609
|
+
if (flag & constants.O_TRUNC)
|
|
457
610
|
await handle.truncate(0);
|
|
458
|
-
}
|
|
459
611
|
return handle;
|
|
460
612
|
}
|
|
461
613
|
/**
|
|
462
614
|
* Asynchronous file open.
|
|
463
|
-
* @see
|
|
464
|
-
* @param flag
|
|
615
|
+
* @see https://nodejs.org/api/fs.html#fspromisesopenpath-flags-mode
|
|
616
|
+
* @param flag {@link https://nodejs.org/api/fs.html#file-system-flags}
|
|
465
617
|
* @param mode Mode to use to open the file. Can be ignored if the filesystem doesn't support permissions.
|
|
466
618
|
*/
|
|
467
619
|
export async function open(path, flag = 'r', mode = 0o644) {
|
|
@@ -471,7 +623,7 @@ open;
|
|
|
471
623
|
export async function readFile(path, _options) {
|
|
472
624
|
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
473
625
|
try {
|
|
474
|
-
const options = normalizeOptions(_options, null, 'r',
|
|
626
|
+
const options = normalizeOptions(_options, null, 'r', 0o444);
|
|
475
627
|
const handle = __addDisposableResource(env_2, typeof path == 'object' && 'fd' in path ? path : await open.call(this, path, options.flag, options.mode), true);
|
|
476
628
|
return await handle.readFile(options);
|
|
477
629
|
}
|
|
@@ -501,7 +653,7 @@ export async function writeFile(path, data, _options) {
|
|
|
501
653
|
const handle = __addDisposableResource(env_3, path instanceof FileHandle ? path : await open.call(this, path.toString(), options.flag, options.mode), true);
|
|
502
654
|
const _data = typeof data == 'string' ? data : data instanceof DataView ? new Uint8Array(data.buffer, data.byteOffset, data.byteLength) : data;
|
|
503
655
|
if (typeof _data != 'string' && !(_data instanceof Uint8Array)) {
|
|
504
|
-
throw new ErrnoError(Errno.EINVAL, 'The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received ' + typeof data, handle.
|
|
656
|
+
throw new ErrnoError(Errno.EINVAL, 'The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received ' + typeof data, handle.path, 'writeFile');
|
|
505
657
|
}
|
|
506
658
|
await handle.writeFile(_data, options);
|
|
507
659
|
}
|
|
@@ -526,8 +678,8 @@ export async function appendFile(path, data, _options) {
|
|
|
526
678
|
const env_4 = { stack: [], error: void 0, hasError: false };
|
|
527
679
|
try {
|
|
528
680
|
const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
|
|
529
|
-
const flag =
|
|
530
|
-
if (!
|
|
681
|
+
const flag = flags.parse(options.flag);
|
|
682
|
+
if (!(flag & constants.O_APPEND)) {
|
|
531
683
|
throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending');
|
|
532
684
|
}
|
|
533
685
|
if (typeof data != 'string' && !options.encoding) {
|
|
@@ -548,21 +700,17 @@ export async function appendFile(path, data, _options) {
|
|
|
548
700
|
}
|
|
549
701
|
}
|
|
550
702
|
appendFile;
|
|
551
|
-
// DIRECTORY-ONLY METHODS
|
|
552
703
|
export async function rmdir(path) {
|
|
553
704
|
path = await realpath.call(this, path);
|
|
554
705
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
555
706
|
try {
|
|
556
707
|
const stats = await fs.stat(resolved);
|
|
557
|
-
if (!stats)
|
|
708
|
+
if (!stats)
|
|
558
709
|
throw ErrnoError.With('ENOENT', path, 'rmdir');
|
|
559
|
-
|
|
560
|
-
if (!stats.isDirectory()) {
|
|
710
|
+
if (!isDirectory(stats))
|
|
561
711
|
throw ErrnoError.With('ENOTDIR', resolved, 'rmdir');
|
|
562
|
-
|
|
563
|
-
if (config.checkAccess && !stats.hasAccess(constants.W_OK, this)) {
|
|
712
|
+
if (checkAccess && !hasAccess(this, stats, constants.W_OK))
|
|
564
713
|
throw ErrnoError.With('EACCES', resolved, 'rmdir');
|
|
565
|
-
}
|
|
566
714
|
await fs.rmdir(resolved);
|
|
567
715
|
emitChange(this, 'rename', path.toString());
|
|
568
716
|
}
|
|
@@ -573,20 +721,26 @@ export async function rmdir(path) {
|
|
|
573
721
|
rmdir;
|
|
574
722
|
export async function mkdir(path, options) {
|
|
575
723
|
var _a, _b;
|
|
576
|
-
const { euid: uid, egid: gid } = (_a = this === null || this === void 0 ? void 0 : this.credentials) !== null && _a !== void 0 ? _a : credentials;
|
|
724
|
+
const { euid: uid, egid: gid } = (_a = this === null || this === void 0 ? void 0 : this.credentials) !== null && _a !== void 0 ? _a : defaultContext.credentials;
|
|
577
725
|
options = typeof options === 'object' ? options : { mode: options };
|
|
578
726
|
const mode = normalizeMode(options === null || options === void 0 ? void 0 : options.mode, 0o777);
|
|
579
727
|
path = await realpath.call(this, path);
|
|
580
728
|
const { fs, path: resolved, root } = resolveMount(path, this);
|
|
581
729
|
const errorPaths = { [resolved]: path };
|
|
730
|
+
const __create = async (path, parent) => {
|
|
731
|
+
if (checkAccess && !hasAccess(this, parent, constants.W_OK))
|
|
732
|
+
throw ErrnoError.With('EACCES', dirname(path), 'mkdir');
|
|
733
|
+
const inode = await fs.mkdir(path, {
|
|
734
|
+
mode,
|
|
735
|
+
uid: parent.mode & constants.S_ISUID ? parent.uid : uid,
|
|
736
|
+
gid: parent.mode & constants.S_ISGID ? parent.gid : gid,
|
|
737
|
+
});
|
|
738
|
+
emitChange(this, 'rename', path);
|
|
739
|
+
return inode;
|
|
740
|
+
};
|
|
582
741
|
try {
|
|
583
742
|
if (!(options === null || options === void 0 ? void 0 : options.recursive)) {
|
|
584
|
-
|
|
585
|
-
throw ErrnoError.With('EACCES', dirname(resolved), 'mkdir');
|
|
586
|
-
}
|
|
587
|
-
await fs.mkdir(resolved, mode, { uid, gid });
|
|
588
|
-
await applySetId(await fs.openFile(resolved, 'r+'), uid, gid);
|
|
589
|
-
emitChange(this, 'rename', path.toString());
|
|
743
|
+
await __create(resolved, await fs.stat(dirname(resolved)));
|
|
590
744
|
return;
|
|
591
745
|
}
|
|
592
746
|
const dirs = [];
|
|
@@ -594,13 +748,11 @@ export async function mkdir(path, options) {
|
|
|
594
748
|
dirs.unshift(dir);
|
|
595
749
|
errorPaths[dir] = origDir;
|
|
596
750
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
await
|
|
602
|
-
await applySetId(await fs.openFile(dir, 'r+'), uid, gid);
|
|
603
|
-
emitChange(this, 'rename', dir);
|
|
751
|
+
if (!dirs.length)
|
|
752
|
+
return;
|
|
753
|
+
const stats = [await fs.stat(dirname(dirs[0]))];
|
|
754
|
+
for (const [i, dir] of dirs.entries()) {
|
|
755
|
+
stats.push(await __create(dir, stats[i]));
|
|
604
756
|
}
|
|
605
757
|
return root.length == 1 ? dirs[0] : (_b = dirs[0]) === null || _b === void 0 ? void 0 : _b.slice(root.length);
|
|
606
758
|
}
|
|
@@ -617,12 +769,10 @@ export async function readdir(path, options) {
|
|
|
617
769
|
if (!stats) {
|
|
618
770
|
throw ErrnoError.With('ENOENT', path, 'readdir');
|
|
619
771
|
}
|
|
620
|
-
if (
|
|
772
|
+
if (checkAccess && !hasAccess(this, stats, constants.R_OK))
|
|
621
773
|
throw ErrnoError.With('EACCES', path, 'readdir');
|
|
622
|
-
|
|
623
|
-
if (!stats.isDirectory()) {
|
|
774
|
+
if (!isDirectory(stats))
|
|
624
775
|
throw ErrnoError.With('ENOTDIR', path, 'readdir');
|
|
625
|
-
}
|
|
626
776
|
const entries = await fs.readdir(resolved).catch((e) => _throw(fixError(e, { [resolved]: path })));
|
|
627
777
|
const values = [];
|
|
628
778
|
const addEntry = async (entry) => {
|
|
@@ -645,7 +795,7 @@ export async function readdir(path, options) {
|
|
|
645
795
|
else {
|
|
646
796
|
values.push(entry);
|
|
647
797
|
}
|
|
648
|
-
if (!(options === null || options === void 0 ? void 0 : options.recursive) || !(entryStats
|
|
798
|
+
if (!(options === null || options === void 0 ? void 0 : options.recursive) || !isDirectory(entryStats))
|
|
649
799
|
return;
|
|
650
800
|
for (const subEntry of await readdir.call(this, join(path, entry), options)) {
|
|
651
801
|
if (subEntry instanceof Dirent) {
|
|
@@ -674,13 +824,14 @@ export async function link(targetPath, linkPath) {
|
|
|
674
824
|
throw ErrnoError.With('EXDEV', linkPath, 'link');
|
|
675
825
|
}
|
|
676
826
|
try {
|
|
677
|
-
if (
|
|
827
|
+
if (checkAccess && !hasAccess(this, await fs.stat(dirname(path)), constants.R_OK)) {
|
|
678
828
|
throw ErrnoError.With('EACCES', dirname(path), 'link');
|
|
679
829
|
}
|
|
680
|
-
|
|
830
|
+
// We need to use the VFS here since the link path may be a mount point
|
|
831
|
+
if (checkAccess && !(await stat.call(this, dirname(linkPath))).hasAccess(constants.W_OK, this)) {
|
|
681
832
|
throw ErrnoError.With('EACCES', dirname(linkPath), 'link');
|
|
682
833
|
}
|
|
683
|
-
if (
|
|
834
|
+
if (checkAccess && !hasAccess(this, await fs.stat(path), constants.R_OK)) {
|
|
684
835
|
throw ErrnoError.With('EACCES', path, 'link');
|
|
685
836
|
}
|
|
686
837
|
return await fs.link(path, link.path);
|
|
@@ -707,7 +858,7 @@ export async function symlink(target, path, type = 'file') {
|
|
|
707
858
|
throw ErrnoError.With('EEXIST', path, 'symlink');
|
|
708
859
|
const handle = __addDisposableResource(env_5, await _open(this, path, { flag: 'w+', mode: 0o644, preserveSymlinks: true }), true);
|
|
709
860
|
await handle.writeFile(normalizePath(target, true));
|
|
710
|
-
await handle.
|
|
861
|
+
await handle.chmod(constants.S_IFLNK);
|
|
711
862
|
}
|
|
712
863
|
catch (e_5) {
|
|
713
864
|
env_5.error = e_5;
|
|
@@ -880,10 +1031,10 @@ async function _resolve($, path, preserveSymlinks) {
|
|
|
880
1031
|
const resolved = resolveMount(path, $);
|
|
881
1032
|
// Stat it to make sure it exists
|
|
882
1033
|
const stats = await resolved.fs.stat(resolved.path);
|
|
883
|
-
if (!
|
|
1034
|
+
if (!isSymbolicLink(stats)) {
|
|
884
1035
|
return { ...resolved, fullPath: path, stats };
|
|
885
1036
|
}
|
|
886
|
-
const target = resolve(dirname(path), (await readlink.call($, path)).toString());
|
|
1037
|
+
const target = resolve.call($, dirname(path), (await readlink.call($, path)).toString());
|
|
887
1038
|
return await _resolve($, target);
|
|
888
1039
|
}
|
|
889
1040
|
catch {
|
|
@@ -895,10 +1046,10 @@ async function _resolve($, path, preserveSymlinks) {
|
|
|
895
1046
|
const resolved = resolveMount(maybePath, $);
|
|
896
1047
|
try {
|
|
897
1048
|
const stats = await resolved.fs.stat(resolved.path);
|
|
898
|
-
if (!
|
|
1049
|
+
if (!isSymbolicLink(stats)) {
|
|
899
1050
|
return { ...resolved, fullPath: maybePath, stats };
|
|
900
1051
|
}
|
|
901
|
-
const target = resolve(realDir, (await readlink.call($, maybePath)).toString());
|
|
1052
|
+
const target = resolve.call($, realDir, (await readlink.call($, maybePath)).toString());
|
|
902
1053
|
return await _resolve($, target);
|
|
903
1054
|
}
|
|
904
1055
|
catch (e) {
|
|
@@ -959,7 +1110,7 @@ export function watch(filename, options = {}) {
|
|
|
959
1110
|
}
|
|
960
1111
|
watch;
|
|
961
1112
|
export async function access(path, mode = constants.F_OK) {
|
|
962
|
-
if (!
|
|
1113
|
+
if (!checkAccess)
|
|
963
1114
|
return;
|
|
964
1115
|
const stats = await stat.call(this, path);
|
|
965
1116
|
if (!stats.hasAccess(mode, this)) {
|