@zenfs/core 1.11.4 → 2.1.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 +36 -19
- package/dist/backends/cow.d.ts +20 -30
- package/dist/backends/cow.js +83 -192
- package/dist/backends/fetch.d.ts +1 -0
- package/dist/backends/fetch.js +30 -30
- 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 -23
- package/dist/backends/passthrough.js +98 -288
- package/dist/backends/port.d.ts +220 -0
- package/dist/backends/port.js +328 -0
- package/dist/backends/single_buffer.d.ts +59 -47
- package/dist/backends/single_buffer.js +468 -219
- package/dist/backends/store/fs.d.ts +25 -35
- package/dist/backends/store/fs.js +276 -315
- package/dist/backends/store/store.d.ts +10 -15
- package/dist/backends/store/store.js +11 -10
- package/dist/config.d.ts +3 -12
- package/dist/config.js +17 -19
- 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 +103 -316
- package/dist/internal/error.d.ts +9 -204
- package/dist/internal/error.js +19 -288
- package/dist/internal/file_index.d.ts +1 -1
- package/dist/internal/file_index.js +11 -11
- package/dist/internal/filesystem.d.ts +51 -94
- package/dist/internal/filesystem.js +21 -20
- package/dist/internal/index.d.ts +1 -2
- package/dist/internal/index.js +1 -2
- package/dist/internal/index_fs.d.ts +12 -30
- package/dist/internal/index_fs.js +37 -69
- package/dist/internal/inode.d.ts +140 -24
- package/dist/internal/inode.js +515 -66
- package/dist/mixins/async.js +52 -112
- package/dist/mixins/mutexed.d.ts +19 -18
- package/dist/mixins/mutexed.js +62 -64
- package/dist/mixins/readonly.d.ts +7 -6
- package/dist/mixins/readonly.js +24 -18
- 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/polyfills.js +1 -1
- package/dist/readline.d.ts +134 -0
- package/dist/readline.js +623 -0
- package/dist/utils.d.ts +9 -37
- package/dist/utils.js +17 -85
- package/dist/vfs/acl.d.ts +42 -0
- package/dist/vfs/acl.js +268 -0
- package/dist/vfs/async.d.ts +9 -23
- package/dist/vfs/async.js +25 -27
- 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 +12 -12
- package/dist/vfs/file.d.ts +106 -0
- package/dist/vfs/file.js +244 -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 +88 -0
- package/dist/vfs/ioctl.js +409 -0
- package/dist/vfs/promises.d.ts +81 -19
- package/dist/vfs/promises.js +404 -288
- package/dist/vfs/shared.d.ts +7 -37
- package/dist/vfs/shared.js +29 -85
- 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 +32 -27
- package/dist/vfs/sync.d.ts +3 -3
- package/dist/vfs/sync.js +263 -260
- package/dist/vfs/watchers.d.ts +2 -2
- package/dist/vfs/watchers.js +12 -12
- package/dist/vfs/xattr.d.ts +116 -0
- package/dist/vfs/xattr.js +201 -0
- package/package.json +5 -3
- package/readme.md +1 -1
- package/scripts/test.js +2 -2
- package/tests/assignment.ts +1 -1
- package/tests/backend/config.worker.js +4 -1
- package/tests/backend/fetch.test.ts +3 -0
- package/tests/backend/port.test.ts +19 -33
- package/tests/backend/remote.worker.js +4 -1
- package/tests/backend/single-buffer.test.ts +53 -0
- package/tests/backend/single-buffer.worker.js +30 -0
- package/tests/common/context.test.ts +3 -3
- package/tests/common/handle.test.ts +17 -12
- package/tests/common/mutex.test.ts +9 -9
- 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 +2 -2
- package/tests/fs/append.test.ts +4 -4
- package/tests/fs/directory.test.ts +25 -25
- package/tests/fs/errors.test.ts +15 -19
- package/tests/fs/links.test.ts +4 -3
- package/tests/fs/open.test.ts +4 -21
- package/tests/fs/permissions.test.ts +14 -18
- package/tests/fs/read.test.ts +10 -9
- package/tests/fs/readFile.test.ts +10 -26
- package/tests/fs/rename.test.ts +4 -9
- package/tests/fs/stat.test.ts +8 -8
- package/tests/fs/streams.test.ts +2 -11
- package/tests/fs/times.test.ts +7 -7
- package/tests/fs/truncate.test.ts +8 -36
- package/tests/fs/watch.test.ts +10 -10
- package/tests/fs/write.test.ts +77 -13
- 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 +7 -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/internal/log.d.ts +0 -133
- package/dist/internal/log.js +0 -218
- package/tests/fs/writeFile.test.ts +0 -70
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 {
|
|
55
|
-
import {
|
|
56
|
-
import {
|
|
57
|
-
import {
|
|
54
|
+
import { Exception, rethrow, setUVMessage, UV } from 'kerium';
|
|
55
|
+
import { decodeUTF8, pick } from 'utilium';
|
|
56
|
+
import { defaultContext } from '../internal/contexts.js';
|
|
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 { __assertType, 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, 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 UV('EBADF', 'chown', this.path);
|
|
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
|
/**
|
|
@@ -91,31 +128,55 @@ export class FileHandle {
|
|
|
91
128
|
async chmod(mode) {
|
|
92
129
|
const numMode = normalizeMode(mode, -1);
|
|
93
130
|
if (numMode < 0)
|
|
94
|
-
throw
|
|
95
|
-
|
|
131
|
+
throw UV('EINVAL', 'chmod', this.path);
|
|
132
|
+
if (this.closed)
|
|
133
|
+
throw UV('EBADF', 'chmod', this.path);
|
|
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 UV('EBADF', 'sync', this.path);
|
|
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 UV('EBADF', 'truncate', this.path);
|
|
116
165
|
if (length < 0)
|
|
117
|
-
throw
|
|
118
|
-
|
|
166
|
+
throw UV('EINVAL', 'truncate', this.path);
|
|
167
|
+
if (!(this.flag & constants.O_WRONLY || this.flag & constants.O_RDWR))
|
|
168
|
+
throw UV('EBADF', 'truncate', this.path);
|
|
169
|
+
if (this.fs.attributes.has('readonly'))
|
|
170
|
+
throw UV('EROFS', 'truncate', this.path);
|
|
171
|
+
if (this.inode.flags & InodeFlags.Immutable)
|
|
172
|
+
throw UV('EPERM', 'truncate', this.path);
|
|
173
|
+
this.dirty = true;
|
|
174
|
+
if (!(this.flag & constants.O_WRONLY || this.flag & constants.O_RDWR))
|
|
175
|
+
throw UV('EBADF', 'truncate', this.path);
|
|
176
|
+
this.inode.mtimeMs = Date.now();
|
|
177
|
+
this.inode.size = length;
|
|
178
|
+
if (this._isSync)
|
|
179
|
+
await this.sync();
|
|
119
180
|
this._emitChange();
|
|
120
181
|
}
|
|
121
182
|
/**
|
|
@@ -124,7 +185,13 @@ export class FileHandle {
|
|
|
124
185
|
* @param mtime The last modified time. If a string is provided, it will be coerced to number.
|
|
125
186
|
*/
|
|
126
187
|
async utimes(atime, mtime) {
|
|
127
|
-
|
|
188
|
+
if (this.closed)
|
|
189
|
+
throw UV('EBADF', 'utimes', this.path);
|
|
190
|
+
this.dirty = true;
|
|
191
|
+
this.inode.atimeMs = normalizeTime(atime);
|
|
192
|
+
this.inode.mtimeMs = normalizeTime(mtime);
|
|
193
|
+
if (this._isSync)
|
|
194
|
+
await this.sync();
|
|
128
195
|
this._emitChange();
|
|
129
196
|
}
|
|
130
197
|
/**
|
|
@@ -138,17 +205,41 @@ export class FileHandle {
|
|
|
138
205
|
*/
|
|
139
206
|
async appendFile(data, _options = {}) {
|
|
140
207
|
const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
|
|
141
|
-
const flag =
|
|
142
|
-
if (!
|
|
143
|
-
throw
|
|
144
|
-
}
|
|
145
|
-
if (typeof data != 'string' && !options.encoding) {
|
|
146
|
-
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
147
|
-
}
|
|
208
|
+
const flag = flags.parse(options.flag);
|
|
209
|
+
if (!(flag & constants.O_APPEND))
|
|
210
|
+
throw UV('EBADF', 'write', this.path);
|
|
148
211
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : data;
|
|
149
|
-
await this.
|
|
212
|
+
await this._write(encodedData, 0, encodedData.length);
|
|
150
213
|
this._emitChange();
|
|
151
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Read data from the file.
|
|
217
|
+
* @param buffer The buffer that the data will be written to.
|
|
218
|
+
* @param offset The offset within the buffer where writing will start.
|
|
219
|
+
* @param length An integer specifying the number of bytes to read.
|
|
220
|
+
* @param position An integer specifying where to begin reading from in the file.
|
|
221
|
+
* If position is unset, data will be read from the current file position.
|
|
222
|
+
*/
|
|
223
|
+
async _read(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
|
|
224
|
+
if (this.closed)
|
|
225
|
+
throw UV('EBADF', 'read', this.path);
|
|
226
|
+
if (this.flag & constants.O_WRONLY)
|
|
227
|
+
throw UV('EBADF', 'read', this.path);
|
|
228
|
+
if (!(this.inode.flags & InodeFlags.NoAtime) && !this.fs.attributes.has('no_atime')) {
|
|
229
|
+
this.dirty = true;
|
|
230
|
+
this.inode.atimeMs = Date.now();
|
|
231
|
+
}
|
|
232
|
+
let end = position + length;
|
|
233
|
+
if (!isCharacterDevice(this.inode) && !isBlockDevice(this.inode) && end > this.inode.size) {
|
|
234
|
+
end = position + Math.max(this.inode.size - position, 0);
|
|
235
|
+
}
|
|
236
|
+
this._position = end;
|
|
237
|
+
const uint8 = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
238
|
+
await this.fs.read(this.internalPath, uint8.subarray(offset, offset + length), position, end);
|
|
239
|
+
if (this._isSync)
|
|
240
|
+
await this.sync();
|
|
241
|
+
return { bytesRead: end - position, buffer };
|
|
242
|
+
}
|
|
152
243
|
async read(buffer, offset, length, position) {
|
|
153
244
|
if (typeof offset == 'object' && offset != null) {
|
|
154
245
|
position = offset.position;
|
|
@@ -161,19 +252,19 @@ export class FileHandle {
|
|
|
161
252
|
offset = buffer.offset;
|
|
162
253
|
buffer = buffer.buffer;
|
|
163
254
|
}
|
|
164
|
-
const pos = Number.isSafeInteger(position) ? position : this.
|
|
165
|
-
buffer || (buffer = new Uint8Array(
|
|
255
|
+
const pos = Number.isSafeInteger(position) ? position : this.position;
|
|
256
|
+
buffer || (buffer = new Uint8Array(this.inode.size));
|
|
166
257
|
offset !== null && offset !== void 0 ? offset : (offset = 0);
|
|
167
|
-
return this.
|
|
258
|
+
return this._read(buffer, offset, length !== null && length !== void 0 ? length : buffer.byteLength - offset, pos);
|
|
168
259
|
}
|
|
169
260
|
async readFile(_options) {
|
|
170
261
|
const options = normalizeOptions(_options, null, 'r', 0o444);
|
|
171
|
-
const flag =
|
|
172
|
-
if (
|
|
173
|
-
throw
|
|
174
|
-
}
|
|
262
|
+
const flag = flags.parse(options.flag);
|
|
263
|
+
if (flag & constants.O_WRONLY)
|
|
264
|
+
throw UV('EBADF', 'read', this.path);
|
|
175
265
|
const { size } = await this.stat();
|
|
176
|
-
const
|
|
266
|
+
const data = new Uint8Array(size);
|
|
267
|
+
await this._read(data, 0, size, 0);
|
|
177
268
|
const buffer = Buffer.from(data);
|
|
178
269
|
return options.encoding ? buffer.toString(options.encoding) : buffer;
|
|
179
270
|
}
|
|
@@ -182,23 +273,73 @@ export class FileHandle {
|
|
|
182
273
|
* The handle will not be closed automatically.
|
|
183
274
|
*/
|
|
184
275
|
readableWebStream(options = {}) {
|
|
185
|
-
|
|
276
|
+
if (this.closed)
|
|
277
|
+
throw UV('EBADF', 'readableWebStream', this.path);
|
|
278
|
+
return this.fs.streamRead(this.internalPath, options);
|
|
186
279
|
}
|
|
187
280
|
/**
|
|
188
|
-
*
|
|
281
|
+
* Not part of the Node.js API!
|
|
282
|
+
*
|
|
283
|
+
* Write file data using a `WritableStream`.
|
|
284
|
+
* The handle will not be closed automatically.
|
|
285
|
+
* @internal
|
|
286
|
+
*/
|
|
287
|
+
writableWebStream(options = {}) {
|
|
288
|
+
if (this.closed)
|
|
289
|
+
throw UV('EBADF', 'writableWebStream', this.path);
|
|
290
|
+
if (this.inode.flags & InodeFlags.Immutable)
|
|
291
|
+
throw UV('EPERM', 'writableWebStream', this.path);
|
|
292
|
+
return this.fs.streamWrite(this.internalPath, options);
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Creates a readline Interface object that allows reading the file line by line
|
|
296
|
+
* @param options Options for creating a read stream
|
|
297
|
+
* @returns A readline interface for reading the file line by line
|
|
189
298
|
*/
|
|
190
299
|
readLines(options) {
|
|
191
|
-
|
|
300
|
+
if (this.closed || this.flag & constants.O_WRONLY)
|
|
301
|
+
throw UV('EBADF', 'read', this.path);
|
|
302
|
+
return createInterface({ input: this.createReadStream(options), crlfDelay: Infinity });
|
|
192
303
|
}
|
|
193
304
|
[Symbol.asyncDispose]() {
|
|
194
305
|
return this.close();
|
|
195
306
|
}
|
|
196
307
|
async stat(opts) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return (opts === null || opts === void 0 ? void 0 : opts.bigint) ? new BigIntStats(
|
|
308
|
+
if (this.closed)
|
|
309
|
+
throw UV('EBADF', 'stat', this.path);
|
|
310
|
+
if (checkAccess && !hasAccess(this.context, this.inode, constants.R_OK))
|
|
311
|
+
throw UV('EACCES', 'stat', this.path);
|
|
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 UV('EBADF', 'write', this.path);
|
|
325
|
+
if (this.inode.flags & InodeFlags.Immutable)
|
|
326
|
+
throw UV('EPERM', 'write', this.path);
|
|
327
|
+
if (!(this.flag & constants.O_WRONLY || this.flag & constants.O_RDWR))
|
|
328
|
+
throw UV('EBADF', 'write', this.path);
|
|
329
|
+
if (this.fs.attributes.has('readonly'))
|
|
330
|
+
throw UV('EROFS', 'write', this.path);
|
|
331
|
+
this.dirty = true;
|
|
332
|
+
const end = position + length;
|
|
333
|
+
const slice = buffer.subarray(offset, offset + length);
|
|
334
|
+
if (!isCharacterDevice(this.inode) && !isBlockDevice(this.inode) && end > this.inode.size)
|
|
335
|
+
this.inode.size = end;
|
|
336
|
+
this.inode.mtimeMs = Date.now();
|
|
337
|
+
this.inode.ctimeMs = Date.now();
|
|
338
|
+
this._position = position + slice.byteLength;
|
|
339
|
+
await this.fs.write(this.internalPath, slice, position);
|
|
340
|
+
if (this._isSync)
|
|
341
|
+
await this.sync();
|
|
342
|
+
return slice.byteLength;
|
|
202
343
|
}
|
|
203
344
|
/**
|
|
204
345
|
* Asynchronously writes `string` to the file.
|
|
@@ -225,8 +366,8 @@ export class FileHandle {
|
|
|
225
366
|
length = typeof lenOrEnc == 'number' ? lenOrEnc : buffer.byteLength;
|
|
226
367
|
position = typeof position === 'number' ? position : null;
|
|
227
368
|
}
|
|
228
|
-
position !== null && position !== void 0 ? position : (position = this.
|
|
229
|
-
const bytesWritten = await this.
|
|
369
|
+
position !== null && position !== void 0 ? position : (position = this.position);
|
|
370
|
+
const bytesWritten = await this._write(buffer, offset, length, position);
|
|
230
371
|
this._emitChange();
|
|
231
372
|
return { buffer: data, bytesWritten };
|
|
232
373
|
}
|
|
@@ -242,23 +383,32 @@ export class FileHandle {
|
|
|
242
383
|
*/
|
|
243
384
|
async writeFile(data, _options = {}) {
|
|
244
385
|
const options = normalizeOptions(_options, 'utf8', 'w', 0o644);
|
|
245
|
-
const flag =
|
|
246
|
-
if (!
|
|
247
|
-
throw
|
|
248
|
-
}
|
|
249
|
-
if (typeof data != 'string' && !options.encoding) {
|
|
250
|
-
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
251
|
-
}
|
|
386
|
+
const flag = flags.parse(options.flag);
|
|
387
|
+
if (!(flag & constants.O_WRONLY || flag & constants.O_RDWR))
|
|
388
|
+
throw UV('EBADF', 'writeFile', this.path);
|
|
252
389
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : data;
|
|
253
|
-
await this.
|
|
390
|
+
await this._write(encodedData, 0, encodedData.length, 0);
|
|
254
391
|
this._emitChange();
|
|
255
392
|
}
|
|
256
393
|
/**
|
|
257
394
|
* Asynchronous close(2) - close a `FileHandle`.
|
|
258
395
|
*/
|
|
259
396
|
async close() {
|
|
260
|
-
|
|
261
|
-
|
|
397
|
+
if (this.closed)
|
|
398
|
+
throw UV('EBADF', 'close', this.path);
|
|
399
|
+
await this.sync();
|
|
400
|
+
this.dispose();
|
|
401
|
+
deleteFD(this.context, this.fd);
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Cleans up. This will *not* sync the file data to the FS
|
|
405
|
+
*/
|
|
406
|
+
dispose(force) {
|
|
407
|
+
if (this.closed)
|
|
408
|
+
throw UV('EBADF', 'close', this.path);
|
|
409
|
+
if (this.dirty && !force)
|
|
410
|
+
throw UV('EBUSY', 'close', this.path);
|
|
411
|
+
this.closed = true;
|
|
262
412
|
}
|
|
263
413
|
/**
|
|
264
414
|
* Asynchronous `writev`. Writes from multiple buffers.
|
|
@@ -268,7 +418,7 @@ export class FileHandle {
|
|
|
268
418
|
*/
|
|
269
419
|
async writev(buffers, position) {
|
|
270
420
|
if (typeof position == 'number')
|
|
271
|
-
this.
|
|
421
|
+
this.position = position;
|
|
272
422
|
let bytesWritten = 0;
|
|
273
423
|
for (const buffer of buffers) {
|
|
274
424
|
bytesWritten += (await this.write(buffer)).bytesWritten;
|
|
@@ -283,7 +433,7 @@ export class FileHandle {
|
|
|
283
433
|
*/
|
|
284
434
|
async readv(buffers, position) {
|
|
285
435
|
if (typeof position == 'number')
|
|
286
|
-
this.
|
|
436
|
+
this.position = position;
|
|
287
437
|
let bytesRead = 0;
|
|
288
438
|
for (const buffer of buffers) {
|
|
289
439
|
bytesRead += (await this.read(buffer)).bytesRead;
|
|
@@ -295,6 +445,8 @@ export class FileHandle {
|
|
|
295
445
|
* @param options Options for the readable stream
|
|
296
446
|
*/
|
|
297
447
|
createReadStream(options = {}) {
|
|
448
|
+
if (this.closed || this.flag & constants.O_WRONLY)
|
|
449
|
+
throw UV('EBADF', 'createReadStream', this.path);
|
|
298
450
|
return new ReadStream(options, this);
|
|
299
451
|
}
|
|
300
452
|
/**
|
|
@@ -302,31 +454,44 @@ export class FileHandle {
|
|
|
302
454
|
* @param options Options for the writeable stream.
|
|
303
455
|
*/
|
|
304
456
|
createWriteStream(options = {}) {
|
|
457
|
+
if (this.closed)
|
|
458
|
+
throw UV('EBADF', 'createWriteStream', this.path);
|
|
459
|
+
if (this.inode.flags & InodeFlags.Immutable)
|
|
460
|
+
throw UV('EPERM', 'createWriteStream', this.path);
|
|
461
|
+
if (this.fs.attributes.has('readonly'))
|
|
462
|
+
throw UV('EROFS', 'createWriteStream', this.path);
|
|
305
463
|
return new WriteStream(options, this);
|
|
306
464
|
}
|
|
307
465
|
}
|
|
308
466
|
export async function rename(oldPath, newPath) {
|
|
309
467
|
oldPath = normalizePath(oldPath);
|
|
468
|
+
__assertType(oldPath);
|
|
310
469
|
newPath = normalizePath(newPath);
|
|
470
|
+
__assertType(newPath);
|
|
471
|
+
const $ex = { syscall: 'rename', path: oldPath, dest: newPath };
|
|
311
472
|
const src = resolveMount(oldPath, this);
|
|
312
473
|
const dst = resolveMount(newPath, this);
|
|
313
|
-
if (
|
|
314
|
-
throw
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
throw
|
|
329
|
-
|
|
474
|
+
if (src.fs !== dst.fs)
|
|
475
|
+
throw UV('EXDEV', $ex);
|
|
476
|
+
if (dst.path.startsWith(src.path + '/'))
|
|
477
|
+
throw UV('EBUSY', $ex);
|
|
478
|
+
const parent = (await stat.call(this, dirname(oldPath)).catch(rethrow($ex)));
|
|
479
|
+
const stats = (await stat.call(this, oldPath).catch(rethrow($ex)));
|
|
480
|
+
const newParent = (await stat.call(this, dirname(newPath)).catch(rethrow($ex)));
|
|
481
|
+
const newStats = (await stat.call(this, newPath).catch((e) => {
|
|
482
|
+
if (e.code == 'ENOENT')
|
|
483
|
+
return null;
|
|
484
|
+
throw setUVMessage(Object.assign(e, $ex));
|
|
485
|
+
}));
|
|
486
|
+
if (checkAccess && (!parent.hasAccess(constants.R_OK, this) || !newParent.hasAccess(constants.W_OK, this)))
|
|
487
|
+
throw UV('EACCES', $ex);
|
|
488
|
+
if (newStats && !isDirectory(stats) && isDirectory(newStats))
|
|
489
|
+
throw UV('EISDIR', $ex);
|
|
490
|
+
if (newStats && isDirectory(stats) && !isDirectory(newStats))
|
|
491
|
+
throw UV('ENOTDIR', $ex);
|
|
492
|
+
await src.fs.rename(src.path, dst.path).catch(rethrow($ex));
|
|
493
|
+
emitChange(this, 'rename', oldPath);
|
|
494
|
+
emitChange(this, 'change', newPath);
|
|
330
495
|
}
|
|
331
496
|
rename;
|
|
332
497
|
/**
|
|
@@ -338,7 +503,7 @@ export async function exists(path) {
|
|
|
338
503
|
return await fs.exists(resolved);
|
|
339
504
|
}
|
|
340
505
|
catch (e) {
|
|
341
|
-
if (e instanceof
|
|
506
|
+
if (e instanceof Exception && e.code == 'ENOENT') {
|
|
342
507
|
return false;
|
|
343
508
|
}
|
|
344
509
|
throw e;
|
|
@@ -347,31 +512,23 @@ export async function exists(path) {
|
|
|
347
512
|
export async function stat(path, options) {
|
|
348
513
|
path = normalizePath(path);
|
|
349
514
|
const { fs, path: resolved } = resolveMount(await realpath.call(this, path), this);
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
return (options === null || options === void 0 ? void 0 : options.bigint) ? new BigIntStats(stats) : stats;
|
|
356
|
-
}
|
|
357
|
-
catch (e) {
|
|
358
|
-
throw fixError(e, { [resolved]: path });
|
|
359
|
-
}
|
|
515
|
+
const $ex = { syscall: 'stat', path };
|
|
516
|
+
const stats = await fs.stat(resolved).catch(rethrow($ex));
|
|
517
|
+
if (checkAccess && !hasAccess(this, stats, constants.R_OK))
|
|
518
|
+
throw UV('EACCES', $ex);
|
|
519
|
+
return (options === null || options === void 0 ? void 0 : options.bigint) ? new BigIntStats(stats) : new Stats(stats);
|
|
360
520
|
}
|
|
361
521
|
stat;
|
|
362
522
|
export async function lstat(path, options) {
|
|
363
523
|
path = normalizePath(path);
|
|
364
524
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
throw fixError(e, { [resolved]: path });
|
|
371
|
-
}
|
|
525
|
+
const $ex = { syscall: 'lstat', path };
|
|
526
|
+
const stats = await fs.stat(resolved).catch(rethrow($ex));
|
|
527
|
+
if (checkAccess && !hasAccess(this, stats, constants.R_OK))
|
|
528
|
+
throw UV('EACCES', $ex);
|
|
529
|
+
return (options === null || options === void 0 ? void 0 : options.bigint) ? new BigIntStats(stats) : new Stats(stats);
|
|
372
530
|
}
|
|
373
531
|
lstat;
|
|
374
|
-
// FILE-ONLY METHODS
|
|
375
532
|
export async function truncate(path, len = 0) {
|
|
376
533
|
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
377
534
|
try {
|
|
@@ -392,28 +549,14 @@ truncate;
|
|
|
392
549
|
export async function unlink(path) {
|
|
393
550
|
path = normalizePath(path);
|
|
394
551
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
}
|
|
402
|
-
catch (e) {
|
|
403
|
-
throw fixError(e, { [resolved]: path });
|
|
404
|
-
}
|
|
552
|
+
const $ex = { syscall: 'unlink', path };
|
|
553
|
+
const stats = await fs.stat(resolved).catch(rethrow($ex));
|
|
554
|
+
if (checkAccess && !hasAccess(this, stats, constants.W_OK))
|
|
555
|
+
throw UV('EACCES', $ex);
|
|
556
|
+
await fs.unlink(resolved).catch(rethrow($ex));
|
|
557
|
+
emitChange(this, 'rename', path.toString());
|
|
405
558
|
}
|
|
406
559
|
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
560
|
/**
|
|
418
561
|
* Opens a file. This helper handles the complexity of file flags.
|
|
419
562
|
* @internal
|
|
@@ -421,47 +564,43 @@ async function applySetId(file, uid, gid) {
|
|
|
421
564
|
async function _open($, path, opt) {
|
|
422
565
|
var _a;
|
|
423
566
|
path = normalizePath(path);
|
|
424
|
-
const mode = normalizeMode(opt.mode, 0o644), flag =
|
|
425
|
-
const
|
|
567
|
+
const mode = normalizeMode(opt.mode, 0o644), flag = flags.parse(opt.flag);
|
|
568
|
+
const $ex = { syscall: 'open', path };
|
|
569
|
+
const { fs, path: resolved, stats } = await _resolve($, path.toString(), opt.preserveSymlinks);
|
|
426
570
|
if (!stats) {
|
|
427
|
-
if (
|
|
428
|
-
throw
|
|
429
|
-
}
|
|
571
|
+
if (!(flag & constants.O_CREAT))
|
|
572
|
+
throw UV('ENOENT', $ex);
|
|
430
573
|
// Create the file
|
|
431
574
|
const parentStats = await fs.stat(dirname(resolved));
|
|
432
|
-
if (
|
|
433
|
-
throw
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
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
|
-
|
|
444
|
-
|
|
445
|
-
}
|
|
446
|
-
if (
|
|
447
|
-
throw
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
would not exist for a small period of time.
|
|
455
|
-
*/
|
|
456
|
-
if (isTruncating(flag)) {
|
|
575
|
+
if (checkAccess && !hasAccess($, parentStats, constants.W_OK))
|
|
576
|
+
throw UV('EACCES', 'open', dirname(path));
|
|
577
|
+
if (!isDirectory(parentStats))
|
|
578
|
+
throw UV('ENOTDIR', 'open', dirname(path));
|
|
579
|
+
if (!opt.allowDirectory && mode & constants.S_IFDIR)
|
|
580
|
+
throw UV('EISDIR', 'open', path);
|
|
581
|
+
const { euid: uid, egid: gid } = (_a = $ === null || $ === void 0 ? void 0 : $.credentials) !== null && _a !== void 0 ? _a : defaultContext.credentials;
|
|
582
|
+
const inode = await fs.createFile(resolved, {
|
|
583
|
+
mode,
|
|
584
|
+
uid: parentStats.mode & constants.S_ISUID ? parentStats.uid : uid,
|
|
585
|
+
gid: parentStats.mode & constants.S_ISGID ? parentStats.gid : gid,
|
|
586
|
+
});
|
|
587
|
+
return new FileHandle($, toFD(new SyncHandle($, path, fs, resolved, flag, inode)));
|
|
588
|
+
}
|
|
589
|
+
if (checkAccess && !hasAccess($, stats, flags.toMode(flag)))
|
|
590
|
+
throw UV('EACCES', $ex);
|
|
591
|
+
if (flag & constants.O_EXCL)
|
|
592
|
+
throw UV('EEXIST', $ex);
|
|
593
|
+
const handle = new FileHandle($, toFD(new SyncHandle($, path, fs, resolved, flag, stats)));
|
|
594
|
+
if (!opt.allowDirectory && mode & constants.S_IFDIR)
|
|
595
|
+
throw UV('EISDIR', 'open', path);
|
|
596
|
+
if (flag & constants.O_TRUNC)
|
|
457
597
|
await handle.truncate(0);
|
|
458
|
-
}
|
|
459
598
|
return handle;
|
|
460
599
|
}
|
|
461
600
|
/**
|
|
462
601
|
* Asynchronous file open.
|
|
463
|
-
* @see
|
|
464
|
-
* @param flag
|
|
602
|
+
* @see https://nodejs.org/api/fs.html#fspromisesopenpath-flags-mode
|
|
603
|
+
* @param flag {@link https://nodejs.org/api/fs.html#file-system-flags}
|
|
465
604
|
* @param mode Mode to use to open the file. Can be ignored if the filesystem doesn't support permissions.
|
|
466
605
|
*/
|
|
467
606
|
export async function open(path, flag = 'r', mode = 0o644) {
|
|
@@ -471,7 +610,7 @@ open;
|
|
|
471
610
|
export async function readFile(path, _options) {
|
|
472
611
|
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
473
612
|
try {
|
|
474
|
-
const options = normalizeOptions(_options, null, 'r',
|
|
613
|
+
const options = normalizeOptions(_options, null, 'r', 0o444);
|
|
475
614
|
const handle = __addDisposableResource(env_2, typeof path == 'object' && 'fd' in path ? path : await open.call(this, path, options.flag, options.mode), true);
|
|
476
615
|
return await handle.readFile(options);
|
|
477
616
|
}
|
|
@@ -500,9 +639,8 @@ export async function writeFile(path, data, _options) {
|
|
|
500
639
|
const options = normalizeOptions(_options, 'utf8', 'w+', 0o644);
|
|
501
640
|
const handle = __addDisposableResource(env_3, path instanceof FileHandle ? path : await open.call(this, path.toString(), options.flag, options.mode), true);
|
|
502
641
|
const _data = typeof data == 'string' ? data : data instanceof DataView ? new Uint8Array(data.buffer, data.byteOffset, data.byteLength) : data;
|
|
503
|
-
if (typeof _data != 'string' && !(_data instanceof Uint8Array))
|
|
504
|
-
throw new
|
|
505
|
-
}
|
|
642
|
+
if (typeof _data != 'string' && !(_data instanceof Uint8Array))
|
|
643
|
+
throw new TypeError('The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received ' + typeof data);
|
|
506
644
|
await handle.writeFile(_data, options);
|
|
507
645
|
}
|
|
508
646
|
catch (e_3) {
|
|
@@ -526,13 +664,10 @@ export async function appendFile(path, data, _options) {
|
|
|
526
664
|
const env_4 = { stack: [], error: void 0, hasError: false };
|
|
527
665
|
try {
|
|
528
666
|
const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
|
|
529
|
-
const flag =
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
if (typeof data != 'string' && !options.encoding) {
|
|
534
|
-
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
535
|
-
}
|
|
667
|
+
const flag = flags.parse(options.flag);
|
|
668
|
+
const $ex = { syscall: 'write', path: path instanceof FileHandle ? path.path : path.toString() };
|
|
669
|
+
if (!(flag & constants.O_APPEND))
|
|
670
|
+
throw UV('EBADF', $ex);
|
|
536
671
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
537
672
|
const handle = __addDisposableResource(env_4, typeof path == 'object' && 'fd' in path ? path : await open.call(this, path, options.flag, options.mode), true);
|
|
538
673
|
await handle.appendFile(encodedData, options);
|
|
@@ -548,82 +683,72 @@ export async function appendFile(path, data, _options) {
|
|
|
548
683
|
}
|
|
549
684
|
}
|
|
550
685
|
appendFile;
|
|
551
|
-
// DIRECTORY-ONLY METHODS
|
|
552
686
|
export async function rmdir(path) {
|
|
553
687
|
path = await realpath.call(this, path);
|
|
554
688
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
}
|
|
566
|
-
await fs.rmdir(resolved);
|
|
567
|
-
emitChange(this, 'rename', path.toString());
|
|
568
|
-
}
|
|
569
|
-
catch (e) {
|
|
570
|
-
throw fixError(e, { [resolved]: path });
|
|
571
|
-
}
|
|
689
|
+
const $ex = { syscall: 'rmdir', path };
|
|
690
|
+
const stats = await fs.stat(resolved).catch(rethrow($ex));
|
|
691
|
+
if (!stats)
|
|
692
|
+
throw UV('ENOENT', $ex);
|
|
693
|
+
if (!isDirectory(stats))
|
|
694
|
+
throw UV('ENOTDIR', $ex);
|
|
695
|
+
if (checkAccess && !hasAccess(this, stats, constants.W_OK))
|
|
696
|
+
throw UV('EACCES', $ex);
|
|
697
|
+
await fs.rmdir(resolved).catch(rethrow($ex));
|
|
698
|
+
emitChange(this, 'rename', path.toString());
|
|
572
699
|
}
|
|
573
700
|
rmdir;
|
|
574
701
|
export async function mkdir(path, options) {
|
|
575
|
-
var _a
|
|
576
|
-
const { euid: uid, egid: gid } = (_a = this === null || this === void 0 ? void 0 : this.credentials) !== null && _a !== void 0 ? _a : credentials;
|
|
702
|
+
var _a;
|
|
703
|
+
const { euid: uid, egid: gid } = (_a = this === null || this === void 0 ? void 0 : this.credentials) !== null && _a !== void 0 ? _a : defaultContext.credentials;
|
|
577
704
|
options = typeof options === 'object' ? options : { mode: options };
|
|
578
705
|
const mode = normalizeMode(options === null || options === void 0 ? void 0 : options.mode, 0o777);
|
|
579
706
|
path = await realpath.call(this, path);
|
|
580
|
-
const { fs, path: resolved
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
for (const dir of dirs) {
|
|
598
|
-
if (config.checkAccess && !(await fs.stat(dirname(dir))).hasAccess(constants.W_OK, this)) {
|
|
599
|
-
throw ErrnoError.With('EACCES', dirname(dir), 'mkdir');
|
|
600
|
-
}
|
|
601
|
-
await fs.mkdir(dir, mode, { uid, gid });
|
|
602
|
-
await applySetId(await fs.openFile(dir, 'r+'), uid, gid);
|
|
603
|
-
emitChange(this, 'rename', dir);
|
|
604
|
-
}
|
|
605
|
-
return root.length == 1 ? dirs[0] : (_b = dirs[0]) === null || _b === void 0 ? void 0 : _b.slice(root.length);
|
|
707
|
+
const { fs, path: resolved } = resolveMount(path, this);
|
|
708
|
+
const __create = async (path, resolved, parent) => {
|
|
709
|
+
if (checkAccess && !hasAccess(this, parent, constants.W_OK))
|
|
710
|
+
throw UV('EACCES', 'mkdir', dirname(path));
|
|
711
|
+
const inode = await fs
|
|
712
|
+
.mkdir(resolved, {
|
|
713
|
+
mode,
|
|
714
|
+
uid: parent.mode & constants.S_ISUID ? parent.uid : uid,
|
|
715
|
+
gid: parent.mode & constants.S_ISGID ? parent.gid : gid,
|
|
716
|
+
})
|
|
717
|
+
.catch(rethrow({ syscall: 'mkdir', path }));
|
|
718
|
+
emitChange(this, 'rename', path);
|
|
719
|
+
return inode;
|
|
720
|
+
};
|
|
721
|
+
if (!(options === null || options === void 0 ? void 0 : options.recursive)) {
|
|
722
|
+
await __create(path, resolved, await fs.stat(dirname(resolved)).catch(rethrow({ path: dirname(path) })));
|
|
723
|
+
return;
|
|
606
724
|
}
|
|
607
|
-
|
|
608
|
-
|
|
725
|
+
const dirs = [];
|
|
726
|
+
let origDir = path;
|
|
727
|
+
for (let dir = resolved; !(await fs.exists(dir).catch(rethrow({ syscall: 'exists', path: origDir }))); dir = dirname(dir), origDir = dirname(origDir)) {
|
|
728
|
+
dirs.unshift([origDir, dir]);
|
|
729
|
+
}
|
|
730
|
+
if (!dirs.length)
|
|
731
|
+
return;
|
|
732
|
+
const stats = [await fs.stat(dirname(dirs[0][1])).catch(rethrow({ syscall: 'stat', path: dirname(dirs[0][0]) }))];
|
|
733
|
+
for (const [i, [path, resolved]] of dirs.entries()) {
|
|
734
|
+
stats.push(await __create(path, resolved, stats[i]));
|
|
609
735
|
}
|
|
736
|
+
return dirs[0][0];
|
|
610
737
|
}
|
|
611
738
|
mkdir;
|
|
612
739
|
export async function readdir(path, options) {
|
|
613
740
|
options = typeof options === 'object' ? options : { encoding: options };
|
|
614
741
|
path = await realpath.call(this, path);
|
|
615
742
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
616
|
-
const
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
if (
|
|
621
|
-
throw
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
}
|
|
626
|
-
const entries = await fs.readdir(resolved).catch((e) => _throw(fixError(e, { [resolved]: path })));
|
|
743
|
+
const $ex = { syscall: 'readdir', path };
|
|
744
|
+
const stats = await fs.stat(resolved).catch(rethrow({ syscall: 'stat', path }));
|
|
745
|
+
if (!stats)
|
|
746
|
+
throw UV('ENOENT', $ex);
|
|
747
|
+
if (checkAccess && !hasAccess(this, stats, constants.R_OK))
|
|
748
|
+
throw UV('EACCES', $ex);
|
|
749
|
+
if (!isDirectory(stats))
|
|
750
|
+
throw UV('ENOTDIR', $ex);
|
|
751
|
+
const entries = await fs.readdir(resolved).catch(rethrow($ex));
|
|
627
752
|
const values = [];
|
|
628
753
|
const addEntry = async (entry) => {
|
|
629
754
|
let entryStats;
|
|
@@ -631,7 +756,7 @@ export async function readdir(path, options) {
|
|
|
631
756
|
entryStats = await fs.stat(join(resolved, entry)).catch((e) => {
|
|
632
757
|
if (e.code == 'ENOENT')
|
|
633
758
|
return;
|
|
634
|
-
throw
|
|
759
|
+
throw setUVMessage(Object.assign(e, { syscall: 'stat', path: join(path, entry) }));
|
|
635
760
|
});
|
|
636
761
|
if (!entryStats)
|
|
637
762
|
return;
|
|
@@ -645,7 +770,7 @@ export async function readdir(path, options) {
|
|
|
645
770
|
else {
|
|
646
771
|
values.push(entry);
|
|
647
772
|
}
|
|
648
|
-
if (!(options === null || options === void 0 ? void 0 : options.recursive) || !(entryStats
|
|
773
|
+
if (!(options === null || options === void 0 ? void 0 : options.recursive) || !isDirectory(entryStats))
|
|
649
774
|
return;
|
|
650
775
|
for (const subEntry of await readdir.call(this, join(path, entry), options)) {
|
|
651
776
|
if (subEntry instanceof Dirent) {
|
|
@@ -665,49 +790,42 @@ export async function readdir(path, options) {
|
|
|
665
790
|
return values;
|
|
666
791
|
}
|
|
667
792
|
readdir;
|
|
668
|
-
export async function link(
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
const { fs, path } = resolveMount(
|
|
672
|
-
const
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
}
|
|
686
|
-
return await fs.link(path, link.path);
|
|
687
|
-
}
|
|
688
|
-
catch (e) {
|
|
689
|
-
throw fixError(e, { [link.path]: linkPath, [path]: targetPath });
|
|
690
|
-
}
|
|
793
|
+
export async function link(path, dest) {
|
|
794
|
+
path = normalizePath(path);
|
|
795
|
+
dest = normalizePath(dest);
|
|
796
|
+
const { fs, path: resolved } = resolveMount(path, this);
|
|
797
|
+
const dst = resolveMount(dest, this);
|
|
798
|
+
const $ex = { syscall: 'link', path };
|
|
799
|
+
if (fs != dst.fs)
|
|
800
|
+
throw UV('EXDEV', $ex);
|
|
801
|
+
const stats = await fs.stat(dirname(resolved)).catch(rethrow({ syscall: 'stat', path: dirname(path) }));
|
|
802
|
+
if (checkAccess && !hasAccess(this, stats, constants.R_OK))
|
|
803
|
+
throw UV('EACCES', 'link', dirname(path));
|
|
804
|
+
// We need to use the VFS here since the link path may be a mount point
|
|
805
|
+
if (checkAccess && !(await stat.call(this, dirname(dest))).hasAccess(constants.W_OK, this))
|
|
806
|
+
throw UV('EACCES', 'link', dirname(dest));
|
|
807
|
+
if (checkAccess && !hasAccess(this, await fs.stat(resolved).catch(rethrow($ex)), constants.R_OK))
|
|
808
|
+
throw UV('EACCES', $ex);
|
|
809
|
+
return await fs.link(resolved, dst.path).catch(rethrow($ex));
|
|
691
810
|
}
|
|
692
811
|
link;
|
|
693
812
|
/**
|
|
694
813
|
* `symlink`.
|
|
695
|
-
* @param
|
|
814
|
+
* @param dest target path
|
|
696
815
|
* @param path link path
|
|
697
816
|
* @param type can be either `'dir'` or `'file'` (default is `'file'`)
|
|
698
817
|
*/
|
|
699
|
-
export async function symlink(
|
|
818
|
+
export async function symlink(dest, path, type = 'file') {
|
|
700
819
|
const env_5 = { stack: [], error: void 0, hasError: false };
|
|
701
820
|
try {
|
|
702
|
-
if (!['file', 'dir', 'junction'].includes(type))
|
|
703
|
-
throw new
|
|
704
|
-
}
|
|
821
|
+
if (!['file', 'dir', 'junction'].includes(type))
|
|
822
|
+
throw new TypeError('Invalid symlink type: ' + type);
|
|
705
823
|
path = normalizePath(path);
|
|
706
824
|
if (await exists.call(this, path))
|
|
707
|
-
throw
|
|
825
|
+
throw UV('EEXIST', 'symlink', path);
|
|
708
826
|
const handle = __addDisposableResource(env_5, await _open(this, path, { flag: 'w+', mode: 0o644, preserveSymlinks: true }), true);
|
|
709
|
-
await handle.writeFile(normalizePath(
|
|
710
|
-
await handle.
|
|
827
|
+
await handle.writeFile(normalizePath(dest, true));
|
|
828
|
+
await handle.chmod(constants.S_IFLNK);
|
|
711
829
|
}
|
|
712
830
|
catch (e_5) {
|
|
713
831
|
env_5.error = e_5;
|
|
@@ -723,7 +841,11 @@ symlink;
|
|
|
723
841
|
export async function readlink(path, options) {
|
|
724
842
|
const env_6 = { stack: [], error: void 0, hasError: false };
|
|
725
843
|
try {
|
|
726
|
-
|
|
844
|
+
path = normalizePath(path);
|
|
845
|
+
__assertType(path);
|
|
846
|
+
const handle = __addDisposableResource(env_6, await _open(this, path, { flag: 'r', mode: 0o644, preserveSymlinks: true }), true);
|
|
847
|
+
if (!isSymbolicLink(handle.inode))
|
|
848
|
+
throw UV('EINVAL', 'readlink', path);
|
|
727
849
|
const value = await handle.readFile();
|
|
728
850
|
const encoding = typeof options == 'object' ? options === null || options === void 0 ? void 0 : options.encoding : options;
|
|
729
851
|
// always defaults to utf-8 to avoid wrangler (cloudflare) worker "unknown encoding" exception
|
|
@@ -880,10 +1002,10 @@ async function _resolve($, path, preserveSymlinks) {
|
|
|
880
1002
|
const resolved = resolveMount(path, $);
|
|
881
1003
|
// Stat it to make sure it exists
|
|
882
1004
|
const stats = await resolved.fs.stat(resolved.path);
|
|
883
|
-
if (!
|
|
1005
|
+
if (!isSymbolicLink(stats)) {
|
|
884
1006
|
return { ...resolved, fullPath: path, stats };
|
|
885
1007
|
}
|
|
886
|
-
const target = resolve(dirname(path), (await readlink.call($, path)).toString());
|
|
1008
|
+
const target = resolve.call($, dirname(path), (await readlink.call($, path)).toString());
|
|
887
1009
|
return await _resolve($, target);
|
|
888
1010
|
}
|
|
889
1011
|
catch {
|
|
@@ -893,20 +1015,18 @@ async function _resolve($, path, preserveSymlinks) {
|
|
|
893
1015
|
const realDir = dir == '/' ? '/' : await realpath.call($, dir);
|
|
894
1016
|
const maybePath = join(realDir, base);
|
|
895
1017
|
const resolved = resolveMount(maybePath, $);
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
return
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
if (e.code == 'ENOENT') {
|
|
906
|
-
return { ...resolved, fullPath: path };
|
|
907
|
-
}
|
|
908
|
-
throw fixError(e, { [resolved.path]: maybePath });
|
|
1018
|
+
const stats = await resolved.fs.stat(resolved.path).catch((e) => {
|
|
1019
|
+
if (e.code == 'ENOENT')
|
|
1020
|
+
return;
|
|
1021
|
+
throw setUVMessage(Object.assign(e, { syscall: 'stat', path: maybePath }));
|
|
1022
|
+
});
|
|
1023
|
+
if (!stats)
|
|
1024
|
+
return { ...resolved, fullPath: path };
|
|
1025
|
+
if (!isSymbolicLink(stats)) {
|
|
1026
|
+
return { ...resolved, fullPath: maybePath, stats };
|
|
909
1027
|
}
|
|
1028
|
+
const target = resolve.call($, realDir, (await readlink.call($, maybePath)).toString());
|
|
1029
|
+
return await _resolve($, target);
|
|
910
1030
|
}
|
|
911
1031
|
export async function realpath(path, options) {
|
|
912
1032
|
var _a;
|
|
@@ -959,12 +1079,11 @@ export function watch(filename, options = {}) {
|
|
|
959
1079
|
}
|
|
960
1080
|
watch;
|
|
961
1081
|
export async function access(path, mode = constants.F_OK) {
|
|
962
|
-
if (!
|
|
1082
|
+
if (!checkAccess)
|
|
963
1083
|
return;
|
|
964
1084
|
const stats = await stat.call(this, path);
|
|
965
|
-
if (!stats.hasAccess(mode, this))
|
|
966
|
-
throw
|
|
967
|
-
}
|
|
1085
|
+
if (!stats.hasAccess(mode, this))
|
|
1086
|
+
throw UV('EACCES', 'access', path.toString());
|
|
968
1087
|
}
|
|
969
1088
|
access;
|
|
970
1089
|
/**
|
|
@@ -998,7 +1117,7 @@ export async function rm(path, options) {
|
|
|
998
1117
|
case constants.S_IFIFO:
|
|
999
1118
|
case constants.S_IFSOCK:
|
|
1000
1119
|
default:
|
|
1001
|
-
throw
|
|
1120
|
+
throw UV('ENOSYS', 'rm', path);
|
|
1002
1121
|
}
|
|
1003
1122
|
}
|
|
1004
1123
|
rm;
|
|
@@ -1020,9 +1139,8 @@ mkdtemp;
|
|
|
1020
1139
|
export async function copyFile(src, dest, mode) {
|
|
1021
1140
|
src = normalizePath(src);
|
|
1022
1141
|
dest = normalizePath(dest);
|
|
1023
|
-
if (mode && mode & constants.COPYFILE_EXCL && (await exists.call(this, dest)))
|
|
1024
|
-
throw
|
|
1025
|
-
}
|
|
1142
|
+
if (mode && mode & constants.COPYFILE_EXCL && (await exists.call(this, dest)))
|
|
1143
|
+
throw UV('EEXIST', 'copyFile', dest);
|
|
1026
1144
|
await writeFile.call(this, dest, await readFile.call(this, src));
|
|
1027
1145
|
emitChange(this, 'rename', dest.toString());
|
|
1028
1146
|
}
|
|
@@ -1055,14 +1173,12 @@ export async function cp(source, destination, opts) {
|
|
|
1055
1173
|
source = normalizePath(source);
|
|
1056
1174
|
destination = normalizePath(destination);
|
|
1057
1175
|
const srcStats = await lstat.call(this, source); // Use lstat to follow symlinks if not dereferencing
|
|
1058
|
-
if ((opts === null || opts === void 0 ? void 0 : opts.errorOnExist) && (await exists.call(this, destination)))
|
|
1059
|
-
throw
|
|
1060
|
-
}
|
|
1176
|
+
if ((opts === null || opts === void 0 ? void 0 : opts.errorOnExist) && (await exists.call(this, destination)))
|
|
1177
|
+
throw UV('EEXIST', 'cp', destination);
|
|
1061
1178
|
switch (srcStats.mode & constants.S_IFMT) {
|
|
1062
1179
|
case constants.S_IFDIR: {
|
|
1063
|
-
if (!(opts === null || opts === void 0 ? void 0 : opts.recursive))
|
|
1064
|
-
throw
|
|
1065
|
-
}
|
|
1180
|
+
if (!(opts === null || opts === void 0 ? void 0 : opts.recursive))
|
|
1181
|
+
throw UV('EISDIR', 'cp', source);
|
|
1066
1182
|
const [entries] = await Promise.all([
|
|
1067
1183
|
readdir.call(this, source, { withFileTypes: true }),
|
|
1068
1184
|
mkdir.call(this, destination, { recursive: true }),
|
|
@@ -1086,7 +1202,7 @@ export async function cp(source, destination, opts) {
|
|
|
1086
1202
|
case constants.S_IFIFO:
|
|
1087
1203
|
case constants.S_IFSOCK:
|
|
1088
1204
|
default:
|
|
1089
|
-
throw
|
|
1205
|
+
throw UV('ENOSYS', 'cp', source);
|
|
1090
1206
|
}
|
|
1091
1207
|
// Optionally preserve timestamps
|
|
1092
1208
|
if (opts === null || opts === void 0 ? void 0 : opts.preserveTimestamps) {
|