@zenfs/core 0.1.0 → 0.2.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/ApiError.d.ts +51 -14
- package/dist/ApiError.js +60 -34
- package/dist/FileIndex.d.ts +32 -35
- package/dist/FileIndex.js +93 -109
- package/dist/backends/AsyncMirror.d.ts +42 -43
- package/dist/backends/AsyncMirror.js +146 -133
- package/dist/backends/AsyncStore.d.ts +29 -28
- package/dist/backends/AsyncStore.js +139 -189
- package/dist/backends/InMemory.d.ts +16 -13
- package/dist/backends/InMemory.js +29 -14
- package/dist/backends/Locked.d.ts +8 -28
- package/dist/backends/Locked.js +44 -148
- package/dist/backends/OverlayFS.d.ts +26 -34
- package/dist/backends/OverlayFS.js +208 -371
- package/dist/backends/SyncStore.d.ts +54 -72
- package/dist/backends/SyncStore.js +159 -161
- package/dist/backends/backend.d.ts +45 -29
- package/dist/backends/backend.js +83 -13
- package/dist/backends/index.d.ts +6 -7
- package/dist/backends/index.js +5 -6
- package/dist/browser.min.js +5 -7
- package/dist/browser.min.js.map +4 -4
- package/dist/emulation/callbacks.d.ts +36 -67
- package/dist/emulation/callbacks.js +90 -46
- package/dist/emulation/constants.js +1 -1
- package/dist/emulation/promises.d.ts +228 -129
- package/dist/emulation/promises.js +414 -172
- package/dist/emulation/shared.d.ts +10 -10
- package/dist/emulation/shared.js +18 -20
- package/dist/emulation/sync.d.ts +25 -25
- package/dist/emulation/sync.js +187 -73
- package/dist/file.d.ts +166 -170
- package/dist/file.js +199 -218
- package/dist/filesystem.d.ts +68 -241
- package/dist/filesystem.js +59 -383
- package/dist/index.d.ts +7 -44
- package/dist/index.js +13 -52
- package/dist/inode.d.ts +37 -28
- package/dist/inode.js +123 -65
- package/dist/stats.d.ts +21 -19
- package/dist/stats.js +35 -56
- package/dist/utils.d.ts +26 -9
- package/dist/utils.js +73 -102
- package/package.json +4 -3
|
@@ -1,12 +1,154 @@
|
|
|
1
1
|
import { ApiError, ErrorCode } from '../ApiError.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { FileFlag } from '../file.js';
|
|
2
|
+
export * as constants from './constants.js';
|
|
3
|
+
import { ActionType, FileFlag } from '../file.js';
|
|
5
4
|
import { normalizePath, normalizeMode, getFdForFile, normalizeOptions, fd2file, fdMap, normalizeTime, cred, nop, resolveFS, fixError, mounts } from './shared.js';
|
|
6
|
-
import { BigIntStats } from '../stats.js';
|
|
5
|
+
import { BigIntStats, FileType } from '../stats.js';
|
|
7
6
|
import { decode, encode } from '../utils.js';
|
|
8
7
|
import { Dirent } from './dir.js';
|
|
9
|
-
import { join } from './path.js';
|
|
8
|
+
import { dirname, join } from './path.js';
|
|
9
|
+
export class FileHandle {
|
|
10
|
+
constructor(
|
|
11
|
+
/**
|
|
12
|
+
* Gets the file descriptor for this file handle.
|
|
13
|
+
*/
|
|
14
|
+
fd) {
|
|
15
|
+
this.fd = fd;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Asynchronous fchown(2) - Change ownership of a file.
|
|
19
|
+
*/
|
|
20
|
+
chown(uid, gid) {
|
|
21
|
+
return fd2file(this.fd).chown(uid, gid);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Asynchronous fchmod(2) - Change permissions of a file.
|
|
25
|
+
* @param mode A file mode. If a string is passed, it is parsed as an octal integer.
|
|
26
|
+
*/
|
|
27
|
+
chmod(mode) {
|
|
28
|
+
const numMode = normalizeMode(mode, -1);
|
|
29
|
+
if (numMode < 0) {
|
|
30
|
+
throw new ApiError(ErrorCode.EINVAL, 'Invalid mode.');
|
|
31
|
+
}
|
|
32
|
+
return fd2file(this.fd).chmod(numMode);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Asynchronous fdatasync(2) - synchronize a file's in-core state with storage device.
|
|
36
|
+
*/
|
|
37
|
+
datasync() {
|
|
38
|
+
return fd2file(this.fd).datasync();
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Asynchronous fsync(2) - synchronize a file's in-core state with the underlying storage device.
|
|
42
|
+
*/
|
|
43
|
+
sync() {
|
|
44
|
+
return fd2file(this.fd).sync();
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Asynchronous ftruncate(2) - Truncate a file to a specified length.
|
|
48
|
+
* @param len If not specified, defaults to `0`.
|
|
49
|
+
*/
|
|
50
|
+
truncate(len) {
|
|
51
|
+
if (len < 0) {
|
|
52
|
+
throw new ApiError(ErrorCode.EINVAL);
|
|
53
|
+
}
|
|
54
|
+
return fd2file(this.fd).truncate(len);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Asynchronously change file timestamps of the file.
|
|
58
|
+
* @param atime The last access time. If a string is provided, it will be coerced to number.
|
|
59
|
+
* @param mtime The last modified time. If a string is provided, it will be coerced to number.
|
|
60
|
+
*/
|
|
61
|
+
utimes(atime, mtime) {
|
|
62
|
+
return fd2file(this.fd).utimes(normalizeTime(atime), normalizeTime(mtime));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Asynchronously append data to a file, creating the file if it does not exist. The underlying file will _not_ be closed automatically.
|
|
66
|
+
* The `FileHandle` must have been opened for appending.
|
|
67
|
+
* @param data The data to write. If something other than a `Buffer` or `Uint8Array` is provided, the value is coerced to a string.
|
|
68
|
+
* @param options Either the encoding for the file, or an object optionally specifying the encoding, file mode, and flag.
|
|
69
|
+
* If `encoding` is not supplied, the default of `'utf8'` is used.
|
|
70
|
+
* If `mode` is not supplied, the default of `0o666` is used.
|
|
71
|
+
* If `mode` is a string, it is parsed as an octal integer.
|
|
72
|
+
* If `flag` is not supplied, the default of `'a'` is used.
|
|
73
|
+
*/
|
|
74
|
+
appendFile(data, options) {
|
|
75
|
+
return appendFile(fd2file(this.fd).path, data, options);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Asynchronously reads data from the file.
|
|
79
|
+
* The `FileHandle` must have been opened for reading.
|
|
80
|
+
* @param buffer The buffer that the data will be written to.
|
|
81
|
+
* @param offset The offset in the buffer at which to start writing.
|
|
82
|
+
* @param length The number of bytes to read.
|
|
83
|
+
* @param position The offset from the beginning of the file from which data should be read. If `null`, data will be read from the current position.
|
|
84
|
+
*/
|
|
85
|
+
read(buffer, offset, length, position) {
|
|
86
|
+
if (isNaN(+position)) {
|
|
87
|
+
position = fd2file(this.fd).position;
|
|
88
|
+
}
|
|
89
|
+
return fd2file(this.fd).read(buffer, offset, length, position);
|
|
90
|
+
}
|
|
91
|
+
readFile(options) {
|
|
92
|
+
return readFile(fd2file(this.fd).path, options);
|
|
93
|
+
}
|
|
94
|
+
stat(opts) {
|
|
95
|
+
return stat(fd2file(this.fd).path, opts);
|
|
96
|
+
}
|
|
97
|
+
async write(data, posOrOff, lenOrEnc, position) {
|
|
98
|
+
let buffer, offset = 0, length;
|
|
99
|
+
if (typeof data === 'string') {
|
|
100
|
+
// Signature 1: (fd, string, [position?, [encoding?]])
|
|
101
|
+
position = typeof posOrOff === 'number' ? posOrOff : null;
|
|
102
|
+
const encoding = (typeof lenOrEnc === 'string' ? lenOrEnc : 'utf8');
|
|
103
|
+
offset = 0;
|
|
104
|
+
buffer = encode(data, encoding);
|
|
105
|
+
length = buffer.length;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
// Signature 2: (fd, buffer, offset, length, position?)
|
|
109
|
+
buffer = data;
|
|
110
|
+
offset = posOrOff;
|
|
111
|
+
length = lenOrEnc;
|
|
112
|
+
position = typeof position === 'number' ? position : null;
|
|
113
|
+
}
|
|
114
|
+
position ?? (position = fd2file(this.fd).position);
|
|
115
|
+
const bytesWritten = await fd2file(this.fd).write(buffer, offset, length, position);
|
|
116
|
+
return { buffer, bytesWritten };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Asynchronously writes data to a file, replacing the file if it already exists. The underlying file will _not_ be closed automatically.
|
|
120
|
+
* The `FileHandle` must have been opened for writing.
|
|
121
|
+
* It is unsafe to call `writeFile()` multiple times on the same file without waiting for the `Promise` to be resolved (or rejected).
|
|
122
|
+
* @param data The data to write. If something other than a `Buffer` or `Uint8Array` is provided, the value is coerced to a string.
|
|
123
|
+
* @param options Either the encoding for the file, or an object optionally specifying the encoding, file mode, and flag.
|
|
124
|
+
* If `encoding` is not supplied, the default of `'utf8'` is used.
|
|
125
|
+
* If `mode` is not supplied, the default of `0o666` is used.
|
|
126
|
+
* If `mode` is a string, it is parsed as an octal integer.
|
|
127
|
+
* If `flag` is not supplied, the default of `'w'` is used.
|
|
128
|
+
*/
|
|
129
|
+
writeFile(data, options) {
|
|
130
|
+
return writeFile(fd2file(this.fd).path, data, options);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* See `fs.writev` promisified version.
|
|
134
|
+
*/
|
|
135
|
+
writev(buffers, position) {
|
|
136
|
+
throw new ApiError(ErrorCode.ENOTSUP);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* See `fs.readv` promisified version.
|
|
140
|
+
*/
|
|
141
|
+
readv(buffers, position) {
|
|
142
|
+
throw new ApiError(ErrorCode.ENOTSUP);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Asynchronous close(2) - close a `FileHandle`.
|
|
146
|
+
*/
|
|
147
|
+
async close() {
|
|
148
|
+
await fd2file(this.fd).close();
|
|
149
|
+
fdMap.delete(this.fd);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
10
152
|
/**
|
|
11
153
|
* Utility for FS ops. It handles
|
|
12
154
|
* - path normalization (for the first parameter to the FS op)
|
|
@@ -19,15 +161,15 @@ import { join } from './path.js';
|
|
|
19
161
|
* @param args the rest of the parameters are passed to the FS function. Note that the first parameter is required to be a path
|
|
20
162
|
* @returns
|
|
21
163
|
*/
|
|
22
|
-
async function doOp(...[name, resolveSymlinks,
|
|
23
|
-
|
|
24
|
-
const { fs, path
|
|
164
|
+
async function doOp(...[name, resolveSymlinks, rawPath, ...args]) {
|
|
165
|
+
rawPath = normalizePath(rawPath);
|
|
166
|
+
const { fs, path } = resolveFS(resolveSymlinks && (await exists(rawPath)) ? await realpath(rawPath) : rawPath);
|
|
25
167
|
try {
|
|
26
168
|
// @ts-expect-error 2556 (since ...args is not correctly picked up as being a tuple)
|
|
27
|
-
return fs[name](
|
|
169
|
+
return fs[name](path, ...args);
|
|
28
170
|
}
|
|
29
171
|
catch (e) {
|
|
30
|
-
throw fixError(e, { [
|
|
172
|
+
throw fixError(e, { [path]: rawPath });
|
|
31
173
|
}
|
|
32
174
|
}
|
|
33
175
|
// fs.promises
|
|
@@ -39,30 +181,26 @@ async function doOp(...[name, resolveSymlinks, path, ...args]) {
|
|
|
39
181
|
export async function rename(oldPath, newPath) {
|
|
40
182
|
oldPath = normalizePath(oldPath);
|
|
41
183
|
newPath = normalizePath(newPath);
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
const paths = { [_old.path]: oldPath, [_new.path]: newPath };
|
|
184
|
+
const { path: old } = resolveFS(oldPath);
|
|
185
|
+
const { fs, path } = resolveFS(newPath);
|
|
45
186
|
try {
|
|
46
|
-
if (_old === _new) {
|
|
47
|
-
return _old.fs.rename(_old.path, _new.path, cred);
|
|
48
|
-
}
|
|
49
187
|
const data = await readFile(oldPath);
|
|
50
188
|
await writeFile(newPath, data);
|
|
51
189
|
await unlink(oldPath);
|
|
52
190
|
}
|
|
53
191
|
catch (e) {
|
|
54
|
-
throw fixError(e,
|
|
192
|
+
throw fixError(e, { [old]: oldPath, [path]: newPath });
|
|
55
193
|
}
|
|
56
194
|
}
|
|
195
|
+
rename;
|
|
57
196
|
/**
|
|
58
197
|
* Test whether or not the given path exists by checking with the file system.
|
|
59
|
-
* @param
|
|
198
|
+
* @param _path
|
|
60
199
|
*/
|
|
61
|
-
export async function exists(
|
|
62
|
-
path = normalizePath(path);
|
|
200
|
+
export async function exists(_path) {
|
|
63
201
|
try {
|
|
64
|
-
const { fs, path
|
|
65
|
-
return fs.exists(
|
|
202
|
+
const { fs, path } = resolveFS(_path);
|
|
203
|
+
return fs.exists(path, cred);
|
|
66
204
|
}
|
|
67
205
|
catch (e) {
|
|
68
206
|
if (e.errno == ErrorCode.ENOENT) {
|
|
@@ -75,10 +213,12 @@ export async function stat(path, options) {
|
|
|
75
213
|
const stats = await doOp('stat', true, path, cred);
|
|
76
214
|
return options?.bigint ? BigIntStats.clone(stats) : stats;
|
|
77
215
|
}
|
|
216
|
+
stat;
|
|
78
217
|
export async function lstat(path, options) {
|
|
79
218
|
const stats = await doOp('stat', false, path, cred);
|
|
80
219
|
return options?.bigint ? BigIntStats.clone(stats) : stats;
|
|
81
220
|
}
|
|
221
|
+
lstat;
|
|
82
222
|
// FILE-ONLY METHODS
|
|
83
223
|
/**
|
|
84
224
|
* `truncate`.
|
|
@@ -86,11 +226,15 @@ export async function lstat(path, options) {
|
|
|
86
226
|
* @param len
|
|
87
227
|
*/
|
|
88
228
|
export async function truncate(path, len = 0) {
|
|
89
|
-
|
|
90
|
-
|
|
229
|
+
const handle = await open(path, 'r+');
|
|
230
|
+
try {
|
|
231
|
+
await handle.truncate(len);
|
|
232
|
+
}
|
|
233
|
+
finally {
|
|
234
|
+
await handle.close();
|
|
91
235
|
}
|
|
92
|
-
return doOp('truncate', true, path, len, cred);
|
|
93
236
|
}
|
|
237
|
+
truncate;
|
|
94
238
|
/**
|
|
95
239
|
* `unlink`.
|
|
96
240
|
* @param path
|
|
@@ -98,121 +242,181 @@ export async function truncate(path, len = 0) {
|
|
|
98
242
|
export async function unlink(path) {
|
|
99
243
|
return doOp('unlink', false, path, cred);
|
|
100
244
|
}
|
|
245
|
+
unlink;
|
|
246
|
+
/**
|
|
247
|
+
* Opens a file. This helper handles the complexity of file flags.
|
|
248
|
+
* @internal
|
|
249
|
+
*/
|
|
250
|
+
async function _open(_path, _flag, _mode = 0o644, resolveSymlinks) {
|
|
251
|
+
const path = normalizePath(_path), mode = normalizeMode(_mode, 0o644), flag = FileFlag.FromString(_flag);
|
|
252
|
+
try {
|
|
253
|
+
switch (flag.pathExistsAction()) {
|
|
254
|
+
case ActionType.THROW:
|
|
255
|
+
throw ApiError.EEXIST(path);
|
|
256
|
+
case ActionType.TRUNCATE:
|
|
257
|
+
/*
|
|
258
|
+
In a previous implementation, we deleted the file and
|
|
259
|
+
re-created it. However, this created a race condition if another
|
|
260
|
+
asynchronous request was trying to read the file, as the file
|
|
261
|
+
would not exist for a small period of time.
|
|
262
|
+
*/
|
|
263
|
+
const file = await doOp('openFile', resolveSymlinks, path, flag, cred);
|
|
264
|
+
if (!file) {
|
|
265
|
+
throw new ApiError(ErrorCode.EIO, 'Impossible code path reached');
|
|
266
|
+
}
|
|
267
|
+
await file.truncate(0);
|
|
268
|
+
await file.sync();
|
|
269
|
+
return file;
|
|
270
|
+
case ActionType.NOP:
|
|
271
|
+
// Must await so thrown errors are caught by the catch below
|
|
272
|
+
return await doOp('openFile', resolveSymlinks, path, flag, cred);
|
|
273
|
+
default:
|
|
274
|
+
throw new ApiError(ErrorCode.EINVAL, 'Invalid file flag');
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch (e) {
|
|
278
|
+
switch (flag.pathNotExistsAction()) {
|
|
279
|
+
case ActionType.CREATE:
|
|
280
|
+
// Ensure parent exists.
|
|
281
|
+
const parentStats = await doOp('stat', resolveSymlinks, dirname(path), cred);
|
|
282
|
+
if (parentStats && !parentStats.isDirectory()) {
|
|
283
|
+
throw ApiError.ENOTDIR(dirname(path));
|
|
284
|
+
}
|
|
285
|
+
return await doOp('createFile', resolveSymlinks, path, flag, mode, cred);
|
|
286
|
+
case ActionType.THROW:
|
|
287
|
+
throw ApiError.ENOENT(path);
|
|
288
|
+
default:
|
|
289
|
+
throw new ApiError(ErrorCode.EINVAL, 'Invalid file flag');
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
101
293
|
/**
|
|
102
|
-
* file open.
|
|
294
|
+
* Asynchronous file open.
|
|
103
295
|
* @see http://www.manpagez.com/man/2/open/
|
|
104
|
-
* @param
|
|
105
|
-
* @param
|
|
106
|
-
* @param mode defaults to `0644`
|
|
296
|
+
* @param flags Handles the complexity of the various file modes. See its API for more details.
|
|
297
|
+
* @param mode Mode to use to open the file. Can be ignored if the filesystem doesn't support permissions.
|
|
107
298
|
*/
|
|
108
299
|
export async function open(path, flag, mode = 0o644) {
|
|
109
|
-
const file = await
|
|
110
|
-
return getFdForFile(file);
|
|
300
|
+
const file = await _open(path, flag, mode, true);
|
|
301
|
+
return new FileHandle(getFdForFile(file));
|
|
111
302
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
case 'utf8':
|
|
121
|
-
case 'utf-8':
|
|
122
|
-
return decode(data);
|
|
123
|
-
default:
|
|
124
|
-
return data;
|
|
125
|
-
}
|
|
303
|
+
open;
|
|
304
|
+
/**
|
|
305
|
+
* Opens a file without resolving symlinks
|
|
306
|
+
* @internal
|
|
307
|
+
*/
|
|
308
|
+
export async function lopen(path, flag, mode = 0o644) {
|
|
309
|
+
const file = await _open(path, flag, mode, false);
|
|
310
|
+
return new FileHandle(getFdForFile(file));
|
|
126
311
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
312
|
+
/**
|
|
313
|
+
* Asynchronously reads the entire contents of a file.
|
|
314
|
+
*/
|
|
315
|
+
async function _readFile(fname, flag, resolveSymlinks) {
|
|
316
|
+
const file = await _open(normalizePath(fname), flag, 0o644, resolveSymlinks);
|
|
317
|
+
try {
|
|
318
|
+
const stat = await file.stat();
|
|
319
|
+
const data = new Uint8Array(stat.size);
|
|
320
|
+
await file.read(data, 0, stat.size, 0);
|
|
321
|
+
await file.close();
|
|
322
|
+
return data;
|
|
132
323
|
}
|
|
133
|
-
|
|
134
|
-
|
|
324
|
+
finally {
|
|
325
|
+
await file.close();
|
|
135
326
|
}
|
|
136
|
-
const encodedData = typeof data == 'string' ? encode(data) : data;
|
|
137
|
-
return doOp('writeFile', true, filename, encodedData, flag, options.mode, cred);
|
|
138
327
|
}
|
|
139
|
-
export async function
|
|
140
|
-
const options = normalizeOptions(
|
|
141
|
-
const flag = FileFlag.
|
|
142
|
-
if (!flag.
|
|
143
|
-
throw new ApiError(ErrorCode.EINVAL, 'Flag passed
|
|
144
|
-
}
|
|
145
|
-
if (typeof data != 'string' && !options.encoding) {
|
|
146
|
-
throw new ApiError(ErrorCode.EINVAL, 'Encoding not specified');
|
|
328
|
+
export async function readFile(filename, _options) {
|
|
329
|
+
const options = normalizeOptions(_options, null, 'r', null);
|
|
330
|
+
const flag = FileFlag.FromString(options.flag);
|
|
331
|
+
if (!flag.isReadable()) {
|
|
332
|
+
throw new ApiError(ErrorCode.EINVAL, 'Flag passed must allow for reading.');
|
|
147
333
|
}
|
|
148
|
-
const
|
|
149
|
-
return
|
|
150
|
-
}
|
|
151
|
-
export async function fstat(fd, options) {
|
|
152
|
-
const stats = await fd2file(fd).stat();
|
|
153
|
-
return options?.bigint ? BigIntStats.clone(stats) : stats;
|
|
334
|
+
const data = await _readFile(filename, options.flag, true);
|
|
335
|
+
return options.encoding ? decode(data, options.encoding) : data;
|
|
154
336
|
}
|
|
337
|
+
readFile;
|
|
155
338
|
/**
|
|
156
|
-
*
|
|
157
|
-
*
|
|
339
|
+
* Asynchronously writes data to a file, replacing the file
|
|
340
|
+
* if it already exists.
|
|
341
|
+
*
|
|
342
|
+
* The encoding option is ignored if data is a buffer.
|
|
158
343
|
*/
|
|
159
|
-
|
|
160
|
-
await
|
|
161
|
-
|
|
162
|
-
|
|
344
|
+
async function _writeFile(fname, data, flag, mode, resolveSymlinks) {
|
|
345
|
+
const file = await _open(fname, flag, mode, resolveSymlinks);
|
|
346
|
+
try {
|
|
347
|
+
await file.write(data, 0, data.length, 0);
|
|
348
|
+
}
|
|
349
|
+
finally {
|
|
350
|
+
await file.close();
|
|
351
|
+
}
|
|
163
352
|
}
|
|
164
353
|
/**
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
354
|
+
* Synchronously writes data to a file, replacing the file if it already exists.
|
|
355
|
+
*
|
|
356
|
+
* The encoding option is ignored if data is a buffer.
|
|
357
|
+
* @param filename
|
|
358
|
+
* @param data
|
|
359
|
+
* @param _options
|
|
360
|
+
* @option options encoding Defaults to `'utf8'`.
|
|
361
|
+
* @option options mode Defaults to `0644`.
|
|
362
|
+
* @option options flag Defaults to `'w'`.
|
|
168
363
|
*/
|
|
169
|
-
export async function
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
364
|
+
export async function writeFile(filename, data, _options) {
|
|
365
|
+
const options = normalizeOptions(_options, 'utf8', 'w', 0o644);
|
|
366
|
+
const flag = FileFlag.FromString(options.flag);
|
|
367
|
+
if (!flag.isWriteable()) {
|
|
368
|
+
throw new ApiError(ErrorCode.EINVAL, 'Flag passed must allow for writing.');
|
|
369
|
+
}
|
|
370
|
+
if (typeof data != 'string' && !options.encoding) {
|
|
371
|
+
throw new ApiError(ErrorCode.EINVAL, 'Encoding not specified');
|
|
173
372
|
}
|
|
174
|
-
|
|
373
|
+
const encodedData = typeof data == 'string' ? encode(data, options.encoding) : data;
|
|
374
|
+
await _writeFile(filename, encodedData, options.flag, options.mode, true);
|
|
175
375
|
}
|
|
376
|
+
writeFile;
|
|
176
377
|
/**
|
|
177
|
-
*
|
|
178
|
-
*
|
|
378
|
+
* Asynchronously append data to a file, creating the file if
|
|
379
|
+
* it not yet exists.
|
|
179
380
|
*/
|
|
180
|
-
|
|
181
|
-
|
|
381
|
+
async function _appendFile(fname, data, flag, mode, resolveSymlinks) {
|
|
382
|
+
const file = await _open(fname, flag, mode, resolveSymlinks);
|
|
383
|
+
try {
|
|
384
|
+
await file.write(data, 0, data.length, null);
|
|
385
|
+
}
|
|
386
|
+
finally {
|
|
387
|
+
await file.close();
|
|
388
|
+
}
|
|
182
389
|
}
|
|
183
390
|
/**
|
|
184
|
-
*
|
|
185
|
-
*
|
|
391
|
+
* Asynchronously append data to a file, creating the file if it not yet
|
|
392
|
+
* exists.
|
|
393
|
+
* @param filename
|
|
394
|
+
* @param data
|
|
395
|
+
* @param options
|
|
396
|
+
* @option options encoding Defaults to `'utf8'`.
|
|
397
|
+
* @option options mode Defaults to `0644`.
|
|
398
|
+
* @option options flag Defaults to `'a'`.
|
|
186
399
|
*/
|
|
187
|
-
export async function
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
buffer = arg2;
|
|
203
|
-
offset = arg3;
|
|
204
|
-
length = arg4;
|
|
205
|
-
position = typeof arg5 === 'number' ? arg5 : null;
|
|
206
|
-
}
|
|
207
|
-
const file = fd2file(fd);
|
|
208
|
-
if (position === undefined || position === null) {
|
|
209
|
-
position = file.getPos();
|
|
210
|
-
}
|
|
211
|
-
return file.write(buffer, offset, length, position);
|
|
400
|
+
export async function appendFile(filename, data, _options) {
|
|
401
|
+
const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
|
|
402
|
+
const flag = FileFlag.FromString(options.flag);
|
|
403
|
+
if (!flag.isAppendable()) {
|
|
404
|
+
throw new ApiError(ErrorCode.EINVAL, 'Flag passed to appendFile must allow for appending.');
|
|
405
|
+
}
|
|
406
|
+
if (typeof data != 'string' && !options.encoding) {
|
|
407
|
+
throw new ApiError(ErrorCode.EINVAL, 'Encoding not specified');
|
|
408
|
+
}
|
|
409
|
+
const encodedData = typeof data == 'string' ? encode(data) : data;
|
|
410
|
+
await _appendFile(filename, encodedData, options.flag, options.mode, true);
|
|
411
|
+
}
|
|
412
|
+
appendFile;
|
|
413
|
+
export function write(handle, data, posOrOff, lenOrEnc, position) {
|
|
414
|
+
return handle.write(data, posOrOff, lenOrEnc, position);
|
|
212
415
|
}
|
|
416
|
+
write;
|
|
213
417
|
/**
|
|
214
418
|
* Read data from the file specified by `fd`.
|
|
215
|
-
* @param
|
|
419
|
+
* @param handle
|
|
216
420
|
* @param buffer The buffer that the data will be
|
|
217
421
|
* written to.
|
|
218
422
|
* @param offset The offset within the buffer where writing will
|
|
@@ -222,41 +426,40 @@ export async function write(fd, arg2, arg3, arg4, arg5) {
|
|
|
222
426
|
* in the file. If position is null, data will be read from the current file
|
|
223
427
|
* position.
|
|
224
428
|
*/
|
|
225
|
-
export
|
|
226
|
-
|
|
227
|
-
if (isNaN(+position)) {
|
|
228
|
-
position = file.getPos();
|
|
229
|
-
}
|
|
230
|
-
return file.read(buffer, offset, length, position);
|
|
429
|
+
export function read(handle, buffer, offset, length, position) {
|
|
430
|
+
return handle.read(buffer, offset, length, position);
|
|
231
431
|
}
|
|
432
|
+
read;
|
|
232
433
|
/**
|
|
233
434
|
* `fchown`.
|
|
234
|
-
* @param
|
|
435
|
+
* @param handle
|
|
235
436
|
* @param uid
|
|
236
437
|
* @param gid
|
|
237
438
|
*/
|
|
238
|
-
export
|
|
239
|
-
return
|
|
439
|
+
export function fchown(handle, uid, gid) {
|
|
440
|
+
return handle.chown(uid, gid);
|
|
240
441
|
}
|
|
442
|
+
fchown;
|
|
241
443
|
/**
|
|
242
444
|
* `fchmod`.
|
|
243
|
-
* @param
|
|
445
|
+
* @param handle
|
|
244
446
|
* @param mode
|
|
245
447
|
*/
|
|
246
|
-
export
|
|
247
|
-
|
|
248
|
-
return fd2file(fd).chmod(numMode);
|
|
448
|
+
export function fchmod(handle, mode) {
|
|
449
|
+
return handle.chmod(mode);
|
|
249
450
|
}
|
|
451
|
+
fchmod;
|
|
250
452
|
/**
|
|
251
453
|
* Change the file timestamps of a file referenced by the supplied file
|
|
252
454
|
* descriptor.
|
|
253
|
-
* @param
|
|
455
|
+
* @param handle
|
|
254
456
|
* @param atime
|
|
255
457
|
* @param mtime
|
|
256
458
|
*/
|
|
257
|
-
export
|
|
258
|
-
return
|
|
459
|
+
export function futimes(handle, atime, mtime) {
|
|
460
|
+
return handle.utimes(atime, mtime);
|
|
259
461
|
}
|
|
462
|
+
futimes;
|
|
260
463
|
// DIRECTORY-ONLY METHODS
|
|
261
464
|
/**
|
|
262
465
|
* `rmdir`.
|
|
@@ -265,14 +468,11 @@ export async function futimes(fd, atime, mtime) {
|
|
|
265
468
|
export async function rmdir(path) {
|
|
266
469
|
return doOp('rmdir', true, path, cred);
|
|
267
470
|
}
|
|
268
|
-
|
|
269
|
-
* `mkdir`.
|
|
270
|
-
* @param path
|
|
271
|
-
* @param mode defaults to `0777`
|
|
272
|
-
*/
|
|
471
|
+
rmdir;
|
|
273
472
|
export async function mkdir(path, mode) {
|
|
274
|
-
|
|
473
|
+
await doOp('mkdir', true, path, normalizeMode(typeof mode == 'object' ? mode?.mode : mode, 0o777), cred);
|
|
275
474
|
}
|
|
475
|
+
mkdir;
|
|
276
476
|
export async function readdir(path, options) {
|
|
277
477
|
path = normalizePath(path);
|
|
278
478
|
const entries = await doOp('readdir', true, path, cred);
|
|
@@ -289,37 +489,49 @@ export async function readdir(path, options) {
|
|
|
289
489
|
}
|
|
290
490
|
const values = [];
|
|
291
491
|
for (const entry of entries) {
|
|
292
|
-
values.push(options?.withFileTypes ? new Dirent(entry, await stat(join(path, entry))) : entry);
|
|
492
|
+
values.push(typeof options == 'object' && options?.withFileTypes ? new Dirent(entry, await stat(join(path, entry))) : entry);
|
|
293
493
|
}
|
|
294
494
|
return values;
|
|
295
495
|
}
|
|
496
|
+
readdir;
|
|
296
497
|
// SYMLINK METHODS
|
|
297
498
|
/**
|
|
298
499
|
* `link`.
|
|
299
|
-
* @param
|
|
300
|
-
* @param
|
|
500
|
+
* @param existing
|
|
501
|
+
* @param newpath
|
|
301
502
|
*/
|
|
302
|
-
export async function link(
|
|
303
|
-
|
|
304
|
-
return doOp('link', false,
|
|
503
|
+
export async function link(existing, newpath) {
|
|
504
|
+
newpath = normalizePath(newpath);
|
|
505
|
+
return doOp('link', false, existing, newpath, cred);
|
|
305
506
|
}
|
|
507
|
+
link;
|
|
306
508
|
/**
|
|
307
509
|
* `symlink`.
|
|
308
|
-
* @param
|
|
309
|
-
* @param
|
|
510
|
+
* @param target target path
|
|
511
|
+
* @param path link path
|
|
310
512
|
* @param type can be either `'dir'` or `'file'` (default is `'file'`)
|
|
311
513
|
*/
|
|
312
|
-
export async function symlink(
|
|
514
|
+
export async function symlink(target, path, type = 'file') {
|
|
313
515
|
if (!['file', 'dir', 'junction'].includes(type)) {
|
|
314
|
-
throw new ApiError(ErrorCode.EINVAL, 'Invalid type: ' + type);
|
|
516
|
+
throw new ApiError(ErrorCode.EINVAL, 'Invalid symlink type: ' + type);
|
|
517
|
+
}
|
|
518
|
+
if (await exists(path)) {
|
|
519
|
+
throw ApiError.EEXIST(path);
|
|
315
520
|
}
|
|
316
|
-
|
|
317
|
-
|
|
521
|
+
await writeFile(path, target);
|
|
522
|
+
const file = await _open(path, 'r+', 0o644, false);
|
|
523
|
+
await file._setType(FileType.SYMLINK);
|
|
318
524
|
}
|
|
525
|
+
symlink;
|
|
319
526
|
export async function readlink(path, options) {
|
|
320
|
-
const value = await
|
|
321
|
-
|
|
527
|
+
const value = await _readFile(path, 'r', false);
|
|
528
|
+
const encoding = typeof options == 'object' ? options.encoding : options;
|
|
529
|
+
if (encoding == 'buffer') {
|
|
530
|
+
return value;
|
|
531
|
+
}
|
|
532
|
+
return decode(value, encoding);
|
|
322
533
|
}
|
|
534
|
+
readlink;
|
|
323
535
|
// PROPERTY OPERATIONS
|
|
324
536
|
/**
|
|
325
537
|
* `chown`.
|
|
@@ -328,8 +540,15 @@ export async function readlink(path, options) {
|
|
|
328
540
|
* @param gid
|
|
329
541
|
*/
|
|
330
542
|
export async function chown(path, uid, gid) {
|
|
331
|
-
|
|
543
|
+
const handle = await open(path, 'r+');
|
|
544
|
+
try {
|
|
545
|
+
await handle.chown(uid, gid);
|
|
546
|
+
}
|
|
547
|
+
finally {
|
|
548
|
+
await handle.close();
|
|
549
|
+
}
|
|
332
550
|
}
|
|
551
|
+
chown;
|
|
333
552
|
/**
|
|
334
553
|
* `lchown`.
|
|
335
554
|
* @param path
|
|
@@ -337,32 +556,45 @@ export async function chown(path, uid, gid) {
|
|
|
337
556
|
* @param gid
|
|
338
557
|
*/
|
|
339
558
|
export async function lchown(path, uid, gid) {
|
|
340
|
-
|
|
559
|
+
const handle = await lopen(path, 'r+');
|
|
560
|
+
try {
|
|
561
|
+
await handle.chown(uid, gid);
|
|
562
|
+
}
|
|
563
|
+
finally {
|
|
564
|
+
await handle.close();
|
|
565
|
+
}
|
|
341
566
|
}
|
|
567
|
+
lchown;
|
|
342
568
|
/**
|
|
343
569
|
* `chmod`.
|
|
344
570
|
* @param path
|
|
345
571
|
* @param mode
|
|
346
572
|
*/
|
|
347
573
|
export async function chmod(path, mode) {
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
574
|
+
const handle = await open(path, 'r+');
|
|
575
|
+
try {
|
|
576
|
+
await handle.chmod(mode);
|
|
577
|
+
}
|
|
578
|
+
finally {
|
|
579
|
+
await handle.close();
|
|
351
580
|
}
|
|
352
|
-
return doOp('chmod', true, path, numMode, cred);
|
|
353
581
|
}
|
|
582
|
+
chmod;
|
|
354
583
|
/**
|
|
355
584
|
* `lchmod`.
|
|
356
585
|
* @param path
|
|
357
586
|
* @param mode
|
|
358
587
|
*/
|
|
359
588
|
export async function lchmod(path, mode) {
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
589
|
+
const handle = await lopen(path, 'r+');
|
|
590
|
+
try {
|
|
591
|
+
await handle.chmod(mode);
|
|
592
|
+
}
|
|
593
|
+
finally {
|
|
594
|
+
await handle.close();
|
|
363
595
|
}
|
|
364
|
-
return doOp('chmod', false, normalizePath(path), numMode, cred);
|
|
365
596
|
}
|
|
597
|
+
lchmod;
|
|
366
598
|
/**
|
|
367
599
|
* Change file timestamps of the file referenced by the supplied path.
|
|
368
600
|
* @param path
|
|
@@ -370,8 +602,15 @@ export async function lchmod(path, mode) {
|
|
|
370
602
|
* @param mtime
|
|
371
603
|
*/
|
|
372
604
|
export async function utimes(path, atime, mtime) {
|
|
373
|
-
|
|
605
|
+
const handle = await open(path, 'r+');
|
|
606
|
+
try {
|
|
607
|
+
await handle.utimes(atime, mtime);
|
|
608
|
+
}
|
|
609
|
+
finally {
|
|
610
|
+
await handle.close();
|
|
611
|
+
}
|
|
374
612
|
}
|
|
613
|
+
utimes;
|
|
375
614
|
/**
|
|
376
615
|
* Change file timestamps of the file referenced by the supplied path.
|
|
377
616
|
* @param path
|
|
@@ -379,16 +618,15 @@ export async function utimes(path, atime, mtime) {
|
|
|
379
618
|
* @param mtime
|
|
380
619
|
*/
|
|
381
620
|
export async function lutimes(path, atime, mtime) {
|
|
382
|
-
|
|
621
|
+
const handle = await lopen(path, 'r+');
|
|
622
|
+
try {
|
|
623
|
+
await handle.utimes(atime, mtime);
|
|
624
|
+
}
|
|
625
|
+
finally {
|
|
626
|
+
await handle.close();
|
|
627
|
+
}
|
|
383
628
|
}
|
|
384
|
-
|
|
385
|
-
* `realpath`.
|
|
386
|
-
* @param path
|
|
387
|
-
* @param options
|
|
388
|
-
* @return resolved path
|
|
389
|
-
*
|
|
390
|
-
* Note: This *Can not* use doOp since doOp depends on it
|
|
391
|
-
*/
|
|
629
|
+
lutimes;
|
|
392
630
|
export async function realpath(path, options) {
|
|
393
631
|
path = normalizePath(path);
|
|
394
632
|
const { fs, path: resolvedPath, mountPoint } = resolveFS(path);
|
|
@@ -397,13 +635,14 @@ export async function realpath(path, options) {
|
|
|
397
635
|
if (!stats.isSymbolicLink()) {
|
|
398
636
|
return path;
|
|
399
637
|
}
|
|
400
|
-
const dst = mountPoint + normalizePath(await
|
|
638
|
+
const dst = mountPoint + normalizePath(decode(await _readFile(resolvedPath, 'r+', false)));
|
|
401
639
|
return realpath(dst);
|
|
402
640
|
}
|
|
403
641
|
catch (e) {
|
|
404
642
|
throw fixError(e, { [resolvedPath]: path });
|
|
405
643
|
}
|
|
406
644
|
}
|
|
645
|
+
realpath;
|
|
407
646
|
export async function watchFile(filename, arg2, listener = nop) {
|
|
408
647
|
throw new ApiError(ErrorCode.ENOTSUP);
|
|
409
648
|
}
|
|
@@ -419,7 +658,10 @@ export async function watch(filename, arg2, listener = nop) {
|
|
|
419
658
|
* @param mode
|
|
420
659
|
*/
|
|
421
660
|
export async function access(path, mode = 0o600) {
|
|
422
|
-
|
|
661
|
+
const stats = await stat(path);
|
|
662
|
+
if (!stats.hasAccess(mode, cred)) {
|
|
663
|
+
throw new ApiError(ErrorCode.EACCES);
|
|
664
|
+
}
|
|
423
665
|
}
|
|
424
666
|
export async function createReadStream(path, options) {
|
|
425
667
|
throw new ApiError(ErrorCode.ENOTSUP);
|