@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
package/dist/file.js
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
import { ApiError, ErrorCode } from './ApiError.js';
|
|
2
2
|
import { Stats } from './stats.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_EXCL, O_TRUNC, O_APPEND, O_SYNC, S_IFMT } from './emulation/constants.js';
|
|
4
|
+
import { size_max } from './inode.js';
|
|
5
5
|
export var ActionType;
|
|
6
6
|
(function (ActionType) {
|
|
7
7
|
// Indicates that the code should not do anything.
|
|
8
8
|
ActionType[ActionType["NOP"] = 0] = "NOP";
|
|
9
9
|
// Indicates that the code should throw an exception.
|
|
10
|
-
ActionType[ActionType["
|
|
10
|
+
ActionType[ActionType["THROW"] = 1] = "THROW";
|
|
11
11
|
// Indicates that the code should truncate the file, but only if it is a file.
|
|
12
|
-
ActionType[ActionType["
|
|
12
|
+
ActionType[ActionType["TRUNCATE"] = 2] = "TRUNCATE";
|
|
13
13
|
// Indicates that the code should create the file.
|
|
14
|
-
ActionType[ActionType["
|
|
14
|
+
ActionType[ActionType["CREATE"] = 3] = "CREATE";
|
|
15
15
|
})(ActionType = ActionType || (ActionType = {}));
|
|
16
16
|
/**
|
|
17
17
|
* Represents one of the following file flags. A convenience object.
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
19
|
+
* - r: Open file for reading. An exception occurs if the file does not exist.
|
|
20
|
+
* - r+: Open file for reading and writing. An exception occurs if the file does not exist.
|
|
21
|
+
* - rs: Open file for reading in synchronous mode. Instructs the filesystem to not cache writes.
|
|
22
|
+
* - rs+: Open file for reading and writing, and opens the file in synchronous mode.
|
|
23
|
+
* - w: Open file for writing. The file is created (if it does not exist) or truncated (if it exists).
|
|
24
|
+
* - wx: Like 'w' but opens the file in exclusive mode.
|
|
25
|
+
* - w+: Open file for reading and writing. The file is created (if it does not exist) or truncated (if it exists).
|
|
26
|
+
* - wx+: Like 'w+' but opens the file in exclusive mode.
|
|
27
|
+
* - a: Open file for appending. The file is created if it does not exist.
|
|
28
|
+
* - ax: Like 'a' but opens the file in exclusive mode.
|
|
29
|
+
* - a+: Open file for reading and appending. The file is created if it does not exist.
|
|
30
|
+
* - ax+: Like 'a+' but opens the file in exclusive mode.
|
|
31
31
|
*
|
|
32
32
|
* Exclusive mode ensures that the file path is newly created.
|
|
33
33
|
*/
|
|
@@ -38,7 +38,7 @@ export class FileFlag {
|
|
|
38
38
|
* @return The FileFlag object representing the flag
|
|
39
39
|
* @throw when the flag string is invalid
|
|
40
40
|
*/
|
|
41
|
-
static
|
|
41
|
+
static FromString(flag) {
|
|
42
42
|
// Check cache first.
|
|
43
43
|
if (!FileFlag.flagCache.has(flag)) {
|
|
44
44
|
FileFlag.flagCache.set(flag, new FileFlag(flag));
|
|
@@ -52,7 +52,7 @@ export class FileFlag {
|
|
|
52
52
|
*/
|
|
53
53
|
constructor(flag) {
|
|
54
54
|
if (typeof flag === 'number') {
|
|
55
|
-
flag = FileFlag.
|
|
55
|
+
flag = FileFlag.NumberToString(flag);
|
|
56
56
|
}
|
|
57
57
|
if (FileFlag.validFlagStrs.indexOf(flag) < 0) {
|
|
58
58
|
throw new ApiError(ErrorCode.EINVAL, 'Invalid flag string: ' + flag);
|
|
@@ -64,7 +64,7 @@ export class FileFlag {
|
|
|
64
64
|
* @return The string representing the flag
|
|
65
65
|
* @throw when the flag number is invalid
|
|
66
66
|
*/
|
|
67
|
-
static
|
|
67
|
+
static NumberToString(flag) {
|
|
68
68
|
// based on https://github.com/nodejs/node/blob/abbdc3efaa455e6c907ebef5409ac8b0f222f969/lib/internal/fs/utils.js#L619
|
|
69
69
|
switch (flag) {
|
|
70
70
|
case O_RDONLY:
|
|
@@ -98,14 +98,14 @@ export class FileFlag {
|
|
|
98
98
|
/**
|
|
99
99
|
* Get the underlying flag string for this flag.
|
|
100
100
|
*/
|
|
101
|
-
|
|
101
|
+
toString() {
|
|
102
102
|
return this.flagStr;
|
|
103
103
|
}
|
|
104
104
|
/**
|
|
105
105
|
* Get the equivalent mode (0b0xxx: read, write, execute)
|
|
106
106
|
* Note: Execute will always be 0
|
|
107
107
|
*/
|
|
108
|
-
|
|
108
|
+
get mode() {
|
|
109
109
|
let mode = 0;
|
|
110
110
|
mode <<= 1;
|
|
111
111
|
mode += +this.isReadable();
|
|
@@ -156,14 +156,12 @@ export class FileFlag {
|
|
|
156
156
|
*/
|
|
157
157
|
pathExistsAction() {
|
|
158
158
|
if (this.isExclusive()) {
|
|
159
|
-
return ActionType.
|
|
159
|
+
return ActionType.THROW;
|
|
160
160
|
}
|
|
161
|
-
|
|
162
|
-
return ActionType.
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
return ActionType.NOP;
|
|
161
|
+
if (this.isTruncating()) {
|
|
162
|
+
return ActionType.TRUNCATE;
|
|
166
163
|
}
|
|
164
|
+
return ActionType.NOP;
|
|
167
165
|
}
|
|
168
166
|
/**
|
|
169
167
|
* Returns one of the static fields on this object that indicates the
|
|
@@ -171,10 +169,10 @@ export class FileFlag {
|
|
|
171
169
|
*/
|
|
172
170
|
pathNotExistsAction() {
|
|
173
171
|
if ((this.isWriteable() || this.isAppendable()) && this.flagStr !== 'r+') {
|
|
174
|
-
return ActionType.
|
|
172
|
+
return ActionType.CREATE;
|
|
175
173
|
}
|
|
176
174
|
else {
|
|
177
|
-
return ActionType.
|
|
175
|
+
return ActionType.THROW;
|
|
178
176
|
}
|
|
179
177
|
}
|
|
180
178
|
}
|
|
@@ -182,41 +180,23 @@ export class FileFlag {
|
|
|
182
180
|
FileFlag.flagCache = new Map();
|
|
183
181
|
// Array of valid mode strings.
|
|
184
182
|
FileFlag.validFlagStrs = ['r', 'r+', 'rs', 'rs+', 'w', 'wx', 'w+', 'wx+', 'a', 'ax', 'a+', 'ax+'];
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
syncSync() {
|
|
194
|
-
throw new ApiError(ErrorCode.ENOTSUP);
|
|
195
|
-
}
|
|
196
|
-
async datasync() {
|
|
183
|
+
export class File {
|
|
184
|
+
/**
|
|
185
|
+
* Asynchronous `datasync`.
|
|
186
|
+
*
|
|
187
|
+
* Default implementation maps to `sync`.
|
|
188
|
+
*/
|
|
189
|
+
datasync() {
|
|
197
190
|
return this.sync();
|
|
198
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Synchronous `datasync`.
|
|
194
|
+
*
|
|
195
|
+
* Default implementation maps to `syncSync`.
|
|
196
|
+
*/
|
|
199
197
|
datasyncSync() {
|
|
200
198
|
return this.syncSync();
|
|
201
199
|
}
|
|
202
|
-
async chown(uid, gid) {
|
|
203
|
-
throw new ApiError(ErrorCode.ENOTSUP);
|
|
204
|
-
}
|
|
205
|
-
chownSync(uid, gid) {
|
|
206
|
-
throw new ApiError(ErrorCode.ENOTSUP);
|
|
207
|
-
}
|
|
208
|
-
async chmod(mode) {
|
|
209
|
-
throw new ApiError(ErrorCode.ENOTSUP);
|
|
210
|
-
}
|
|
211
|
-
chmodSync(mode) {
|
|
212
|
-
throw new ApiError(ErrorCode.ENOTSUP);
|
|
213
|
-
}
|
|
214
|
-
async utimes(atime, mtime) {
|
|
215
|
-
throw new ApiError(ErrorCode.ENOTSUP);
|
|
216
|
-
}
|
|
217
|
-
utimesSync(atime, mtime) {
|
|
218
|
-
throw new ApiError(ErrorCode.ENOTSUP);
|
|
219
|
-
}
|
|
220
200
|
}
|
|
221
201
|
/**
|
|
222
202
|
* An implementation of the File interface that operates on a file that is
|
|
@@ -227,59 +207,54 @@ export class BaseFile {
|
|
|
227
207
|
* extend this class and implement those two methods.
|
|
228
208
|
* @todo 'close' lever that disables functionality once closed.
|
|
229
209
|
*/
|
|
230
|
-
export class PreloadFile extends
|
|
210
|
+
export class PreloadFile extends File {
|
|
231
211
|
/**
|
|
232
212
|
* Creates a file with the given path and, optionally, the given contents. Note
|
|
233
213
|
* that, if contents is specified, it will be mutated by the file!
|
|
234
|
-
* @param _fs The file system that created the file.
|
|
235
|
-
* @param _path
|
|
236
214
|
* @param _mode The mode that the file was opened using.
|
|
237
215
|
* Dictates permissions and where the file pointer starts.
|
|
238
|
-
* @param
|
|
216
|
+
* @param stats The stats object for the given file.
|
|
239
217
|
* PreloadFile will mutate this object. Note that this object must contain
|
|
240
218
|
* the appropriate mode that the file was opened as.
|
|
241
|
-
* @param
|
|
219
|
+
* @param buffer A buffer containing the entire
|
|
242
220
|
* contents of the file. PreloadFile will mutate this buffer. If not
|
|
243
221
|
* specified, we assume it is a new file.
|
|
244
222
|
*/
|
|
245
|
-
constructor(
|
|
246
|
-
super();
|
|
247
|
-
this._pos = 0;
|
|
248
|
-
this._dirty = false;
|
|
249
|
-
this._fs = _fs;
|
|
250
|
-
this._path = _path;
|
|
251
|
-
this._flag = _flag;
|
|
252
|
-
this._stat = _stat;
|
|
253
|
-
this._buffer = contents ? contents : new Uint8Array(0);
|
|
254
|
-
// Note: This invariant is *not* maintained once the file starts getting
|
|
255
|
-
// modified.
|
|
256
|
-
// Note: Only actually matters if file is readable, as writeable modes may
|
|
257
|
-
// truncate/append to file.
|
|
258
|
-
if (this._stat.size !== this._buffer.length && this._flag.isReadable()) {
|
|
259
|
-
throw new Error(`Invalid buffer: Uint8Array is ${this._buffer.length} long, yet Stats object specifies that file is ${this._stat.size} long.`);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
223
|
+
constructor(
|
|
262
224
|
/**
|
|
263
|
-
*
|
|
225
|
+
* The file system that created the file.
|
|
264
226
|
*/
|
|
265
|
-
|
|
266
|
-
return this._buffer;
|
|
267
|
-
}
|
|
227
|
+
fs,
|
|
268
228
|
/**
|
|
269
|
-
*
|
|
229
|
+
* Path to the file
|
|
270
230
|
*/
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
231
|
+
path, flag, stats, _buffer = new Uint8Array(new ArrayBuffer(0, { maxByteLength: size_max }))) {
|
|
232
|
+
super();
|
|
233
|
+
this.fs = fs;
|
|
234
|
+
this.path = path;
|
|
235
|
+
this.flag = flag;
|
|
236
|
+
this.stats = stats;
|
|
237
|
+
this._buffer = _buffer;
|
|
238
|
+
this._position = 0;
|
|
239
|
+
this._dirty = false;
|
|
240
|
+
/*
|
|
241
|
+
Note:
|
|
242
|
+
This invariant is *not* maintained once the file starts getting modified.
|
|
243
|
+
It only actually matters if file is readable, as writeable modes may truncate/append to file.
|
|
244
|
+
*/
|
|
245
|
+
if (this.stats.size == _buffer.byteLength) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (this.flag.isReadable()) {
|
|
249
|
+
throw new Error(`Size mismatch: buffer length ${_buffer.byteLength}, stats size ${this.stats.size}`);
|
|
250
|
+
}
|
|
251
|
+
this._dirty = true;
|
|
276
252
|
}
|
|
277
253
|
/**
|
|
278
|
-
* Get the
|
|
279
|
-
* @return [String] The path to the file.
|
|
254
|
+
* Get the underlying buffer for this file. Mutating not recommended and will mess up dirty tracking.
|
|
280
255
|
*/
|
|
281
|
-
|
|
282
|
-
return this.
|
|
256
|
+
get buffer() {
|
|
257
|
+
return this._buffer;
|
|
283
258
|
}
|
|
284
259
|
/**
|
|
285
260
|
* Get the current file position.
|
|
@@ -288,103 +263,66 @@ export class PreloadFile extends BaseFile {
|
|
|
288
263
|
* > On Linux, positional writes don't work when the file is opened in append
|
|
289
264
|
* mode. The kernel ignores the position argument and always appends the data
|
|
290
265
|
* to the end of the file.
|
|
291
|
-
* @return
|
|
266
|
+
* @return The current file position.
|
|
292
267
|
*/
|
|
293
|
-
|
|
294
|
-
if (this.
|
|
295
|
-
return this.
|
|
268
|
+
get position() {
|
|
269
|
+
if (this.flag.isAppendable()) {
|
|
270
|
+
return this.stats.size;
|
|
296
271
|
}
|
|
297
|
-
return this.
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* Advance the current file position by the indicated number of positions.
|
|
301
|
-
* @param [Number] delta
|
|
302
|
-
*/
|
|
303
|
-
advancePos(delta) {
|
|
304
|
-
return (this._pos += delta);
|
|
272
|
+
return this._position;
|
|
305
273
|
}
|
|
306
274
|
/**
|
|
307
275
|
* Set the file position.
|
|
308
|
-
* @param
|
|
309
|
-
*/
|
|
310
|
-
setPos(newPos) {
|
|
311
|
-
return (this._pos = newPos);
|
|
312
|
-
}
|
|
313
|
-
/**
|
|
314
|
-
* **Core**: Asynchronous sync. Must be implemented by subclasses of this
|
|
315
|
-
* class.
|
|
316
|
-
* @param [Function(ZenFS.ApiError)] cb
|
|
276
|
+
* @param newPos new position
|
|
317
277
|
*/
|
|
318
|
-
|
|
319
|
-
this.
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* **Core**: Synchronous sync.
|
|
323
|
-
*/
|
|
324
|
-
syncSync() {
|
|
325
|
-
throw new ApiError(ErrorCode.ENOTSUP);
|
|
326
|
-
}
|
|
327
|
-
/**
|
|
328
|
-
* **Core**: Asynchronous close. Must be implemented by subclasses of this
|
|
329
|
-
* class.
|
|
330
|
-
* @param [Function(ZenFS.ApiError)] cb
|
|
331
|
-
*/
|
|
332
|
-
async close() {
|
|
333
|
-
this.closeSync();
|
|
334
|
-
}
|
|
335
|
-
/**
|
|
336
|
-
* **Core**: Synchronous close.
|
|
337
|
-
*/
|
|
338
|
-
closeSync() {
|
|
339
|
-
throw new ApiError(ErrorCode.ENOTSUP);
|
|
278
|
+
set position(newPos) {
|
|
279
|
+
this._position = newPos;
|
|
340
280
|
}
|
|
341
281
|
/**
|
|
342
282
|
* Asynchronous `stat`.
|
|
343
|
-
* @param [Function(ZenFS.ApiError, ZenFS.node.fs.Stats)] cb
|
|
344
283
|
*/
|
|
345
284
|
async stat() {
|
|
346
|
-
return Stats.clone(this.
|
|
285
|
+
return Stats.clone(this.stats);
|
|
347
286
|
}
|
|
348
287
|
/**
|
|
349
288
|
* Synchronous `stat`.
|
|
350
289
|
*/
|
|
351
290
|
statSync() {
|
|
352
|
-
return Stats.clone(this.
|
|
291
|
+
return Stats.clone(this.stats);
|
|
353
292
|
}
|
|
354
293
|
/**
|
|
355
294
|
* Asynchronous truncate.
|
|
356
|
-
* @param
|
|
357
|
-
* @param [Function(ZenFS.ApiError)] cb
|
|
295
|
+
* @param len
|
|
358
296
|
*/
|
|
359
297
|
truncate(len) {
|
|
360
298
|
this.truncateSync(len);
|
|
361
|
-
if (this.
|
|
299
|
+
if (this.flag.isSynchronous() && !this.fs.metadata.synchronous) {
|
|
362
300
|
return this.sync();
|
|
363
301
|
}
|
|
364
302
|
}
|
|
365
303
|
/**
|
|
366
304
|
* Synchronous truncate.
|
|
367
|
-
* @param
|
|
305
|
+
* @param len
|
|
368
306
|
*/
|
|
369
307
|
truncateSync(len) {
|
|
370
308
|
this._dirty = true;
|
|
371
|
-
if (!this.
|
|
309
|
+
if (!this.flag.isWriteable()) {
|
|
372
310
|
throw new ApiError(ErrorCode.EPERM, 'File not opened with a writeable mode.');
|
|
373
311
|
}
|
|
374
|
-
this.
|
|
312
|
+
this.stats.mtimeMs = Date.now();
|
|
375
313
|
if (len > this._buffer.length) {
|
|
376
314
|
const buf = new Uint8Array(len - this._buffer.length);
|
|
377
|
-
// Write will set
|
|
315
|
+
// Write will set stats.size for us.
|
|
378
316
|
this.writeSync(buf, 0, buf.length, this._buffer.length);
|
|
379
|
-
if (this.
|
|
317
|
+
if (this.flag.isSynchronous() && this.fs.metadata.synchronous) {
|
|
380
318
|
this.syncSync();
|
|
381
319
|
}
|
|
382
320
|
return;
|
|
383
321
|
}
|
|
384
|
-
this.
|
|
322
|
+
this.stats.size = len;
|
|
385
323
|
// Truncate buffer to 'len'.
|
|
386
324
|
this._buffer = this._buffer.subarray(0, len);
|
|
387
|
-
if (this.
|
|
325
|
+
if (this.flag.isSynchronous() && this.fs.metadata.synchronous) {
|
|
388
326
|
this.syncSync();
|
|
389
327
|
}
|
|
390
328
|
}
|
|
@@ -392,142 +330,155 @@ export class PreloadFile extends BaseFile {
|
|
|
392
330
|
* Write buffer to the file.
|
|
393
331
|
* Note that it is unsafe to use fs.write multiple times on the same file
|
|
394
332
|
* without waiting for the callback.
|
|
395
|
-
* @param
|
|
333
|
+
* @param buffer Uint8Array containing the data to write to
|
|
396
334
|
* the file.
|
|
397
|
-
* @param
|
|
398
|
-
* @param
|
|
399
|
-
* @param
|
|
335
|
+
* @param offset Offset in the buffer to start reading data from.
|
|
336
|
+
* @param length The amount of bytes to write to the file.
|
|
337
|
+
* @param position Offset from the beginning of the file where this
|
|
400
338
|
* data should be written. If position is null, the data will be written at
|
|
401
339
|
* the current position.
|
|
402
|
-
* @param [Function(ZenFS.ApiError, Number, ZenFS.node.Uint8Array)]
|
|
403
|
-
* cb The number specifies the number of bytes written into the file.
|
|
404
340
|
*/
|
|
405
|
-
async write(buffer, offset, length, position) {
|
|
341
|
+
async write(buffer, offset = 0, length = this.stats.size, position = 0) {
|
|
406
342
|
return this.writeSync(buffer, offset, length, position);
|
|
407
343
|
}
|
|
408
344
|
/**
|
|
409
345
|
* Write buffer to the file.
|
|
410
346
|
* Note that it is unsafe to use fs.writeSync multiple times on the same file
|
|
411
347
|
* without waiting for the callback.
|
|
412
|
-
* @param
|
|
348
|
+
* @param buffer Uint8Array containing the data to write to
|
|
413
349
|
* the file.
|
|
414
|
-
* @param
|
|
415
|
-
* @param
|
|
416
|
-
* @param
|
|
350
|
+
* @param offset Offset in the buffer to start reading data from.
|
|
351
|
+
* @param length The amount of bytes to write to the file.
|
|
352
|
+
* @param position Offset from the beginning of the file where this
|
|
417
353
|
* data should be written. If position is null, the data will be written at
|
|
418
354
|
* the current position.
|
|
419
|
-
* @
|
|
355
|
+
* @returns bytes written
|
|
420
356
|
*/
|
|
421
|
-
writeSync(buffer, offset, length, position) {
|
|
357
|
+
writeSync(buffer, offset = 0, length = this.stats.size, position = 0) {
|
|
422
358
|
this._dirty = true;
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}
|
|
426
|
-
if (!this._flag.isWriteable()) {
|
|
359
|
+
position ?? (position = this.position);
|
|
360
|
+
if (!this.flag.isWriteable()) {
|
|
427
361
|
throw new ApiError(ErrorCode.EPERM, 'File not opened with a writeable mode.');
|
|
428
362
|
}
|
|
429
363
|
const endFp = position + length;
|
|
430
|
-
if (endFp > this.
|
|
431
|
-
this.
|
|
432
|
-
if (endFp > this._buffer.
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
364
|
+
if (endFp > this.stats.size) {
|
|
365
|
+
this.stats.size = endFp;
|
|
366
|
+
if (endFp > this._buffer.byteLength) {
|
|
367
|
+
if (this._buffer.buffer.resizable && this._buffer.buffer.maxByteLength <= endFp) {
|
|
368
|
+
this._buffer.buffer.resize(endFp);
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
// Extend the buffer!
|
|
372
|
+
const newBuffer = new Uint8Array(new ArrayBuffer(endFp, { maxByteLength: size_max }));
|
|
373
|
+
newBuffer.set(this._buffer);
|
|
374
|
+
this._buffer = newBuffer;
|
|
375
|
+
}
|
|
437
376
|
}
|
|
438
377
|
}
|
|
439
378
|
this._buffer.set(buffer.slice(offset, offset + length), position);
|
|
440
|
-
const len = this._buffer.
|
|
441
|
-
this.
|
|
442
|
-
if (this.
|
|
379
|
+
const len = this._buffer.byteOffset;
|
|
380
|
+
this.stats.mtimeMs = Date.now();
|
|
381
|
+
if (this.flag.isSynchronous()) {
|
|
443
382
|
this.syncSync();
|
|
444
383
|
return len;
|
|
445
384
|
}
|
|
446
|
-
this.
|
|
385
|
+
this.position = position + len;
|
|
447
386
|
return len;
|
|
448
387
|
}
|
|
449
388
|
/**
|
|
450
389
|
* Read data from the file.
|
|
451
|
-
* @param
|
|
390
|
+
* @param buffer The buffer that the data will be
|
|
452
391
|
* written to.
|
|
453
|
-
* @param
|
|
392
|
+
* @param offset The offset within the buffer where writing will
|
|
454
393
|
* start.
|
|
455
|
-
* @param
|
|
456
|
-
* @param
|
|
394
|
+
* @param length An integer specifying the number of bytes to read.
|
|
395
|
+
* @param position An integer specifying where to begin reading from
|
|
457
396
|
* in the file. If position is null, data will be read from the current file
|
|
458
397
|
* position.
|
|
459
|
-
* @param [Function(ZenFS.ApiError, Number, ZenFS.node.Uint8Array)] cb The
|
|
460
|
-
* number is the number of bytes read
|
|
461
398
|
*/
|
|
462
|
-
async read(buffer, offset, length, position) {
|
|
399
|
+
async read(buffer, offset = 0, length = this.stats.size, position = 0) {
|
|
463
400
|
return { bytesRead: this.readSync(buffer, offset, length, position), buffer };
|
|
464
401
|
}
|
|
465
402
|
/**
|
|
466
403
|
* Read data from the file.
|
|
467
|
-
* @param
|
|
404
|
+
* @param buffer The buffer that the data will be
|
|
468
405
|
* written to.
|
|
469
|
-
* @param
|
|
470
|
-
*
|
|
471
|
-
* @param
|
|
472
|
-
* @param [Number] position An integer specifying where to begin reading from
|
|
406
|
+
* @param offset The offset within the buffer where writing will start.
|
|
407
|
+
* @param length An integer specifying the number of bytes to read.
|
|
408
|
+
* @param position An integer specifying where to begin reading from
|
|
473
409
|
* in the file. If position is null, data will be read from the current file
|
|
474
410
|
* position.
|
|
475
|
-
* @
|
|
411
|
+
* @returns number of bytes written
|
|
476
412
|
*/
|
|
477
|
-
readSync(buffer, offset, length, position) {
|
|
478
|
-
if (!this.
|
|
413
|
+
readSync(buffer, offset = 0, length = this.stats.size, position = 0) {
|
|
414
|
+
if (!this.flag.isReadable()) {
|
|
479
415
|
throw new ApiError(ErrorCode.EPERM, 'File not opened with a readable mode.');
|
|
480
416
|
}
|
|
481
|
-
|
|
482
|
-
|
|
417
|
+
position ?? (position = this.position);
|
|
418
|
+
let end = position + length;
|
|
419
|
+
if (end > this.stats.size) {
|
|
420
|
+
end = position + Math.max(this.stats.size - position, 0);
|
|
483
421
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
422
|
+
this.stats.atimeMs = Date.now();
|
|
423
|
+
this._position = end;
|
|
424
|
+
const bytesRead = end - position;
|
|
425
|
+
if (bytesRead == 0) {
|
|
426
|
+
// No copy/read. Return immediatly for better performance
|
|
427
|
+
return bytesRead;
|
|
487
428
|
}
|
|
488
|
-
this._buffer.
|
|
489
|
-
|
|
490
|
-
this._pos = position + length;
|
|
491
|
-
return this._buffer.length;
|
|
429
|
+
buffer.set(this._buffer.slice(position, end), offset);
|
|
430
|
+
return bytesRead;
|
|
492
431
|
}
|
|
493
432
|
/**
|
|
494
433
|
* Asynchronous `fchmod`.
|
|
495
|
-
* @param
|
|
434
|
+
* @param mode the mode
|
|
496
435
|
*/
|
|
497
436
|
async chmod(mode) {
|
|
498
437
|
this.chmodSync(mode);
|
|
499
438
|
}
|
|
500
439
|
/**
|
|
501
440
|
* Synchronous `fchmod`.
|
|
502
|
-
* @param
|
|
441
|
+
* @param mode
|
|
503
442
|
*/
|
|
504
443
|
chmodSync(mode) {
|
|
505
|
-
if (!this.
|
|
444
|
+
if (!this.fs.metadata.supportsProperties) {
|
|
506
445
|
throw new ApiError(ErrorCode.ENOTSUP);
|
|
507
446
|
}
|
|
508
447
|
this._dirty = true;
|
|
509
|
-
this.
|
|
448
|
+
this.stats.chmod(mode);
|
|
510
449
|
this.syncSync();
|
|
511
450
|
}
|
|
512
451
|
/**
|
|
513
452
|
* Asynchronous `fchown`.
|
|
514
|
-
* @param
|
|
515
|
-
* @param
|
|
453
|
+
* @param uid
|
|
454
|
+
* @param gid
|
|
516
455
|
*/
|
|
517
456
|
async chown(uid, gid) {
|
|
518
457
|
this.chownSync(uid, gid);
|
|
519
458
|
}
|
|
520
459
|
/**
|
|
521
460
|
* Synchronous `fchown`.
|
|
522
|
-
* @param
|
|
523
|
-
* @param
|
|
461
|
+
* @param uid
|
|
462
|
+
* @param gid
|
|
524
463
|
*/
|
|
525
464
|
chownSync(uid, gid) {
|
|
526
|
-
if (!this.
|
|
465
|
+
if (!this.fs.metadata.supportsProperties) {
|
|
466
|
+
throw new ApiError(ErrorCode.ENOTSUP);
|
|
467
|
+
}
|
|
468
|
+
this._dirty = true;
|
|
469
|
+
this.stats.chown(uid, gid);
|
|
470
|
+
this.syncSync();
|
|
471
|
+
}
|
|
472
|
+
async utimes(atime, mtime) {
|
|
473
|
+
this.utimesSync(atime, mtime);
|
|
474
|
+
}
|
|
475
|
+
utimesSync(atime, mtime) {
|
|
476
|
+
if (!this.fs.metadata.supportsProperties) {
|
|
527
477
|
throw new ApiError(ErrorCode.ENOTSUP);
|
|
528
478
|
}
|
|
529
479
|
this._dirty = true;
|
|
530
|
-
this.
|
|
480
|
+
this.stats.atime = atime;
|
|
481
|
+
this.stats.mtime = mtime;
|
|
531
482
|
this.syncSync();
|
|
532
483
|
}
|
|
533
484
|
isDirty() {
|
|
@@ -539,10 +490,42 @@ export class PreloadFile extends BaseFile {
|
|
|
539
490
|
resetDirty() {
|
|
540
491
|
this._dirty = false;
|
|
541
492
|
}
|
|
493
|
+
_setType(type) {
|
|
494
|
+
this._dirty = true;
|
|
495
|
+
this.stats.mode = (this.stats.mode & ~S_IFMT) | type;
|
|
496
|
+
return this.sync();
|
|
497
|
+
}
|
|
498
|
+
_setTypeSync(type) {
|
|
499
|
+
this._dirty = true;
|
|
500
|
+
this.stats.mode = (this.stats.mode & ~S_IFMT) | type;
|
|
501
|
+
this.syncSync();
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* For synchronous file systems
|
|
506
|
+
*/
|
|
507
|
+
export class SyncFile extends PreloadFile {
|
|
508
|
+
constructor(_fs, _path, _flag, _stat, contents) {
|
|
509
|
+
super(_fs, _path, _flag, _stat, contents);
|
|
510
|
+
}
|
|
511
|
+
async sync() {
|
|
512
|
+
this.syncSync();
|
|
513
|
+
}
|
|
514
|
+
syncSync() {
|
|
515
|
+
if (this.isDirty()) {
|
|
516
|
+
this.fs.syncSync(this.path, this._buffer, this.stats);
|
|
517
|
+
this.resetDirty();
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
async close() {
|
|
521
|
+
this.closeSync();
|
|
522
|
+
}
|
|
523
|
+
closeSync() {
|
|
524
|
+
this.syncSync();
|
|
525
|
+
}
|
|
542
526
|
}
|
|
543
527
|
/**
|
|
544
|
-
*
|
|
545
|
-
* Doesn't sync to anything, so it works nicely for memory-only files.
|
|
528
|
+
* For the filesystems which do not sync to anything..
|
|
546
529
|
*/
|
|
547
530
|
export class NoSyncFile extends PreloadFile {
|
|
548
531
|
constructor(_fs, _path, _flag, _stat, contents) {
|
|
@@ -550,7 +533,6 @@ export class NoSyncFile extends PreloadFile {
|
|
|
550
533
|
}
|
|
551
534
|
/**
|
|
552
535
|
* Asynchronous sync. Doesn't do anything, simply calls the cb.
|
|
553
|
-
* @param [Function(ZenFS.ApiError)] cb
|
|
554
536
|
*/
|
|
555
537
|
async sync() {
|
|
556
538
|
return;
|
|
@@ -563,7 +545,6 @@ export class NoSyncFile extends PreloadFile {
|
|
|
563
545
|
}
|
|
564
546
|
/**
|
|
565
547
|
* Asynchronous close. Doesn't do anything, simply calls the cb.
|
|
566
|
-
* @param [Function(ZenFS.ApiError)] cb
|
|
567
548
|
*/
|
|
568
549
|
async close() {
|
|
569
550
|
return;
|