@zenfs/core 0.0.7 → 0.0.9
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 +2 -2
- package/dist/ApiError.js +11 -7
- package/dist/FileIndex.js +2 -2
- package/dist/backends/AsyncMirror.js +2 -2
- package/dist/backends/AsyncStore.d.ts +4 -5
- package/dist/backends/AsyncStore.js +21 -21
- package/dist/backends/FolderAdapter.js +6 -6
- package/dist/backends/InMemory.d.ts +2 -3
- package/dist/backends/OverlayFS.js +7 -7
- package/dist/backends/SyncStore.d.ts +12 -13
- package/dist/backends/SyncStore.js +21 -21
- package/dist/browser.min.js +6 -11
- package/dist/browser.min.js.map +4 -4
- package/dist/emulation/callbacks.d.ts +6 -6
- package/dist/emulation/callbacks.js +3 -2
- package/dist/emulation/path.d.ts +16 -0
- package/dist/emulation/path.js +451 -0
- package/dist/emulation/promises.d.ts +6 -6
- package/dist/emulation/promises.js +2 -1
- package/dist/emulation/shared.js +4 -4
- package/dist/emulation/sync.d.ts +6 -6
- package/dist/emulation/sync.js +2 -1
- package/dist/file.d.ts +23 -24
- package/dist/file.js +18 -20
- package/dist/filesystem.d.ts +3 -3
- package/dist/filesystem.js +10 -10
- package/dist/inode.d.ts +2 -3
- package/dist/inode.js +17 -17
- package/dist/stats.d.ts +3 -4
- package/dist/stats.js +13 -13
- package/dist/utils.d.ts +10 -5
- package/dist/utils.js +3 -9
- package/package.json +1 -5
- package/{README.md → readme.md} +24 -20
package/dist/ApiError.d.ts
CHANGED
|
@@ -44,7 +44,7 @@ export declare class ApiError extends Error implements NodeJS.ErrnoException {
|
|
|
44
44
|
/**
|
|
45
45
|
* Creates an ApiError object from a buffer.
|
|
46
46
|
*/
|
|
47
|
-
static
|
|
47
|
+
static Derserialize(data: ArrayBufferLike | ArrayBufferView, i?: number): ApiError;
|
|
48
48
|
static FileError(code: ErrorCode, p: string): ApiError;
|
|
49
49
|
static EACCES(path: string): ApiError;
|
|
50
50
|
static ENOENT(path: string): ApiError;
|
|
@@ -77,7 +77,7 @@ export declare class ApiError extends Error implements NodeJS.ErrnoException {
|
|
|
77
77
|
/**
|
|
78
78
|
* Writes the API error into a buffer.
|
|
79
79
|
*/
|
|
80
|
-
|
|
80
|
+
serialize(data?: ArrayBufferLike | ArrayBufferView, i?: number): Uint8Array;
|
|
81
81
|
/**
|
|
82
82
|
* The size of the API error in buffer-form in bytes.
|
|
83
83
|
*/
|
package/dist/ApiError.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { decode, encode } from './utils.js';
|
|
2
2
|
/**
|
|
3
3
|
* Standard libc error codes. More will be added to this enum and ErrorStrings as they are
|
|
4
4
|
* needed.
|
|
@@ -56,8 +56,10 @@ export class ApiError extends Error {
|
|
|
56
56
|
/**
|
|
57
57
|
* Creates an ApiError object from a buffer.
|
|
58
58
|
*/
|
|
59
|
-
static
|
|
60
|
-
|
|
59
|
+
static Derserialize(data, i = 0) {
|
|
60
|
+
const view = new DataView('buffer' in data ? data.buffer : data);
|
|
61
|
+
const dataText = decode(view.buffer.slice(i + 4, i + 4 + view.getUint32(i, true)));
|
|
62
|
+
return ApiError.fromJSON(JSON.parse(dataText));
|
|
61
63
|
}
|
|
62
64
|
static FileError(code, p) {
|
|
63
65
|
return new ApiError(code, ErrorStrings[code], p);
|
|
@@ -120,9 +122,11 @@ export class ApiError extends Error {
|
|
|
120
122
|
/**
|
|
121
123
|
* Writes the API error into a buffer.
|
|
122
124
|
*/
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
|
|
125
|
+
serialize(data = new Uint8Array(this.bufferSize()), i = 0) {
|
|
126
|
+
const view = new DataView('buffer' in data ? data.buffer : data), buffer = new Uint8Array(view.buffer);
|
|
127
|
+
const newData = encode(JSON.stringify(this.toJSON()));
|
|
128
|
+
buffer.set(newData);
|
|
129
|
+
view.setUint32(i, newData.byteLength, true);
|
|
126
130
|
return buffer;
|
|
127
131
|
}
|
|
128
132
|
/**
|
|
@@ -130,6 +134,6 @@ export class ApiError extends Error {
|
|
|
130
134
|
*/
|
|
131
135
|
bufferSize() {
|
|
132
136
|
// 4 bytes for string length.
|
|
133
|
-
return 4 +
|
|
137
|
+
return 4 + JSON.stringify(this.toJSON()).length;
|
|
134
138
|
}
|
|
135
139
|
}
|
package/dist/FileIndex.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Stats, FileType } from './stats.js';
|
|
2
|
-
import * as path from 'path';
|
|
2
|
+
import * as path from './emulation/path.js';
|
|
3
3
|
/**
|
|
4
4
|
* A simple class for storing a filesystem index. Assumes that all paths passed
|
|
5
5
|
* to it are *absolute* paths.
|
|
@@ -219,7 +219,7 @@ export class FileIndex {
|
|
|
219
219
|
*/
|
|
220
220
|
_split_path(p) {
|
|
221
221
|
const dirpath = path.dirname(p);
|
|
222
|
-
const itemname = p.
|
|
222
|
+
const itemname = p.slice(dirpath.length + (dirpath === '/' ? 0 : 1));
|
|
223
223
|
return [dirpath, itemname];
|
|
224
224
|
}
|
|
225
225
|
}
|
|
@@ -11,7 +11,7 @@ var _a;
|
|
|
11
11
|
import { SynchronousFileSystem } from '../filesystem.js';
|
|
12
12
|
import { ApiError, ErrorCode } from '../ApiError.js';
|
|
13
13
|
import { FileFlag, PreloadFile } from '../file.js';
|
|
14
|
-
import
|
|
14
|
+
import { join } from '../emulation/path.js';
|
|
15
15
|
import { Cred } from '../cred.js';
|
|
16
16
|
import { CreateBackend } from './backend.js';
|
|
17
17
|
/**
|
|
@@ -185,7 +185,7 @@ export class AsyncMirror extends SynchronousFileSystem {
|
|
|
185
185
|
}
|
|
186
186
|
const files = yield this._async.readdir(p, Cred.Root);
|
|
187
187
|
for (const file of files) {
|
|
188
|
-
yield copyItem(
|
|
188
|
+
yield copyItem(join(p, file));
|
|
189
189
|
}
|
|
190
190
|
}), copyFile = (p, mode) => __awaiter(this, void 0, void 0, function* () {
|
|
191
191
|
const data = yield this._async.readFile(p, null, FileFlag.getFileFlag('r'), Cred.Root);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
2
1
|
import { Cred } from '../cred.js';
|
|
3
2
|
import { PreloadFile, File, FileFlag } from '../file.js';
|
|
4
3
|
import { BaseFileSystem } from '../filesystem.js';
|
|
@@ -33,7 +32,7 @@ export interface AsyncKeyValueROTransaction {
|
|
|
33
32
|
* Retrieves the data at the given key.
|
|
34
33
|
* @param key The key to look under for data.
|
|
35
34
|
*/
|
|
36
|
-
get(key: string): Promise<
|
|
35
|
+
get(key: string): Promise<Uint8Array>;
|
|
37
36
|
}
|
|
38
37
|
/**
|
|
39
38
|
* Represents an asynchronous read-write transaction.
|
|
@@ -47,7 +46,7 @@ export interface AsyncKeyValueRWTransaction extends AsyncKeyValueROTransaction {
|
|
|
47
46
|
* @param overwrite If 'true', overwrite any existing data. If 'false',
|
|
48
47
|
* avoids writing the data if the key exists.
|
|
49
48
|
*/
|
|
50
|
-
put(key: string, data:
|
|
49
|
+
put(key: string, data: Uint8Array, overwrite: boolean): Promise<boolean>;
|
|
51
50
|
/**
|
|
52
51
|
* Deletes the data at the given key.
|
|
53
52
|
* @param key The key to delete from the store.
|
|
@@ -63,7 +62,7 @@ export interface AsyncKeyValueRWTransaction extends AsyncKeyValueROTransaction {
|
|
|
63
62
|
abort(): Promise<void>;
|
|
64
63
|
}
|
|
65
64
|
export declare class AsyncKeyValueFile extends PreloadFile<AsyncKeyValueFileSystem> implements File {
|
|
66
|
-
constructor(_fs: AsyncKeyValueFileSystem, _path: string, _flag: FileFlag, _stat: Stats, contents?:
|
|
65
|
+
constructor(_fs: AsyncKeyValueFileSystem, _path: string, _flag: FileFlag, _stat: Stats, contents?: Uint8Array);
|
|
67
66
|
sync(): Promise<void>;
|
|
68
67
|
close(): Promise<void>;
|
|
69
68
|
}
|
|
@@ -104,7 +103,7 @@ export declare class AsyncKeyValueFileSystem extends BaseFileSystem {
|
|
|
104
103
|
readdir(p: string, cred: Cred): Promise<string[]>;
|
|
105
104
|
chmod(p: string, mode: number, cred: Cred): Promise<void>;
|
|
106
105
|
chown(p: string, new_uid: number, new_gid: number, cred: Cred): Promise<void>;
|
|
107
|
-
_sync(p: string, data:
|
|
106
|
+
_sync(p: string, data: Uint8Array, stats: Stats): Promise<void>;
|
|
108
107
|
/**
|
|
109
108
|
* Checks if the root directory exists. Creates it if it doesn't.
|
|
110
109
|
*/
|
|
@@ -7,14 +7,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import
|
|
10
|
+
import { dirname, basename, join, resolve } from '../emulation/path.js';
|
|
11
11
|
import { ApiError, ErrorCode } from '../ApiError.js';
|
|
12
12
|
import { W_OK, R_OK } from '../emulation/constants.js';
|
|
13
13
|
import { PreloadFile, FileFlag } from '../file.js';
|
|
14
14
|
import { BaseFileSystem } from '../filesystem.js';
|
|
15
15
|
import Inode from '../inode.js';
|
|
16
16
|
import { FileType } from '../stats.js';
|
|
17
|
-
import { ROOT_NODE_ID, randomUUID,
|
|
17
|
+
import { ROOT_NODE_ID, randomUUID, encode } from '../utils.js';
|
|
18
18
|
class LRUNode {
|
|
19
19
|
constructor(key, value) {
|
|
20
20
|
this.key = key;
|
|
@@ -204,7 +204,7 @@ export class AsyncKeyValueFileSystem extends BaseFileSystem {
|
|
|
204
204
|
c.removeAll();
|
|
205
205
|
}
|
|
206
206
|
try {
|
|
207
|
-
const tx = this.store.beginTransaction('readwrite'), oldParent =
|
|
207
|
+
const tx = this.store.beginTransaction('readwrite'), oldParent = dirname(oldPath), oldName = basename(oldPath), newParent = dirname(newPath), newName = basename(newPath),
|
|
208
208
|
// Remove oldPath from parent's directory listing.
|
|
209
209
|
oldDirNode = yield this.findINode(tx, oldParent), oldDirList = yield this.getDirListing(tx, oldParent, oldDirNode);
|
|
210
210
|
if (!oldDirNode.toStats().hasAccess(W_OK, cred)) {
|
|
@@ -255,8 +255,8 @@ export class AsyncKeyValueFileSystem extends BaseFileSystem {
|
|
|
255
255
|
newDirList[newName] = nodeId;
|
|
256
256
|
// Commit the two changed directory listings.
|
|
257
257
|
try {
|
|
258
|
-
yield tx.put(oldDirNode.id,
|
|
259
|
-
yield tx.put(newDirNode.id,
|
|
258
|
+
yield tx.put(oldDirNode.id, encode(JSON.stringify(oldDirList)), true);
|
|
259
|
+
yield tx.put(newDirNode.id, encode(JSON.stringify(newDirList)), true);
|
|
260
260
|
}
|
|
261
261
|
catch (e) {
|
|
262
262
|
yield tx.abort();
|
|
@@ -284,7 +284,7 @@ export class AsyncKeyValueFileSystem extends BaseFileSystem {
|
|
|
284
284
|
}
|
|
285
285
|
createFile(p, flag, mode, cred) {
|
|
286
286
|
return __awaiter(this, void 0, void 0, function* () {
|
|
287
|
-
const tx = this.store.beginTransaction('readwrite'), data =
|
|
287
|
+
const tx = this.store.beginTransaction('readwrite'), data = new Uint8Array(0), newFile = yield this.commitNewFile(tx, p, FileType.FILE, mode, cred, data);
|
|
288
288
|
// Open the file.
|
|
289
289
|
return new AsyncKeyValueFile(this, p, flag, newFile.toStats(), data);
|
|
290
290
|
});
|
|
@@ -318,7 +318,7 @@ export class AsyncKeyValueFileSystem extends BaseFileSystem {
|
|
|
318
318
|
}
|
|
319
319
|
mkdir(p, mode, cred) {
|
|
320
320
|
return __awaiter(this, void 0, void 0, function* () {
|
|
321
|
-
const tx = this.store.beginTransaction('readwrite'), data =
|
|
321
|
+
const tx = this.store.beginTransaction('readwrite'), data = encode('{}');
|
|
322
322
|
yield this.commitNewFile(tx, p, FileType.DIRECTORY, mode, cred, data);
|
|
323
323
|
});
|
|
324
324
|
}
|
|
@@ -350,13 +350,13 @@ export class AsyncKeyValueFileSystem extends BaseFileSystem {
|
|
|
350
350
|
// update is required.
|
|
351
351
|
const tx = this.store.beginTransaction('readwrite'),
|
|
352
352
|
// We use the _findInode helper because we actually need the INode id.
|
|
353
|
-
fileInodeId = yield this._findINode(tx,
|
|
353
|
+
fileInodeId = yield this._findINode(tx, dirname(p), basename(p)), fileInode = yield this.getINode(tx, p, fileInodeId), inodeChanged = fileInode.update(stats);
|
|
354
354
|
try {
|
|
355
355
|
// Sync data.
|
|
356
356
|
yield tx.put(fileInode.id, data, true);
|
|
357
357
|
// Sync metadata.
|
|
358
358
|
if (inodeChanged) {
|
|
359
|
-
yield tx.put(fileInodeId, fileInode.
|
|
359
|
+
yield tx.put(fileInodeId, fileInode.serialize(), true);
|
|
360
360
|
}
|
|
361
361
|
}
|
|
362
362
|
catch (e) {
|
|
@@ -379,8 +379,8 @@ export class AsyncKeyValueFileSystem extends BaseFileSystem {
|
|
|
379
379
|
dirInode = new Inode(randomUUID(), 4096, 511 | FileType.DIRECTORY, currTime, currTime, currTime, 0, 0);
|
|
380
380
|
// If the root doesn't exist, the first random ID shouldn't exist,
|
|
381
381
|
// either.
|
|
382
|
-
yield tx.put(dirInode.id,
|
|
383
|
-
yield tx.put(ROOT_NODE_ID, dirInode.
|
|
382
|
+
yield tx.put(dirInode.id, encode('{}'), false);
|
|
383
|
+
yield tx.put(ROOT_NODE_ID, dirInode.serialize(), false);
|
|
384
384
|
yield tx.commit();
|
|
385
385
|
}
|
|
386
386
|
});
|
|
@@ -393,7 +393,7 @@ export class AsyncKeyValueFileSystem extends BaseFileSystem {
|
|
|
393
393
|
*/
|
|
394
394
|
_findINode(tx, parent, filename, visited = new Set()) {
|
|
395
395
|
return __awaiter(this, void 0, void 0, function* () {
|
|
396
|
-
const currentPath =
|
|
396
|
+
const currentPath = join(parent, filename);
|
|
397
397
|
if (visited.has(currentPath)) {
|
|
398
398
|
throw new ApiError(ErrorCode.EIO, 'Infinite loop detected while finding inode', currentPath);
|
|
399
399
|
}
|
|
@@ -424,7 +424,7 @@ export class AsyncKeyValueFileSystem extends BaseFileSystem {
|
|
|
424
424
|
return id;
|
|
425
425
|
}
|
|
426
426
|
else {
|
|
427
|
-
throw ApiError.ENOENT(
|
|
427
|
+
throw ApiError.ENOENT(resolve(parent, filename));
|
|
428
428
|
}
|
|
429
429
|
}
|
|
430
430
|
}
|
|
@@ -441,7 +441,7 @@ export class AsyncKeyValueFileSystem extends BaseFileSystem {
|
|
|
441
441
|
return id;
|
|
442
442
|
}
|
|
443
443
|
else {
|
|
444
|
-
throw ApiError.ENOENT(
|
|
444
|
+
throw ApiError.ENOENT(resolve(parent, filename));
|
|
445
445
|
}
|
|
446
446
|
}
|
|
447
447
|
});
|
|
@@ -453,7 +453,7 @@ export class AsyncKeyValueFileSystem extends BaseFileSystem {
|
|
|
453
453
|
*/
|
|
454
454
|
findINode(tx, p, visited = new Set()) {
|
|
455
455
|
return __awaiter(this, void 0, void 0, function* () {
|
|
456
|
-
const id = yield this._findINode(tx,
|
|
456
|
+
const id = yield this._findINode(tx, dirname(p), basename(p), visited);
|
|
457
457
|
return this.getINode(tx, p, id);
|
|
458
458
|
});
|
|
459
459
|
}
|
|
@@ -469,7 +469,7 @@ export class AsyncKeyValueFileSystem extends BaseFileSystem {
|
|
|
469
469
|
if (!data) {
|
|
470
470
|
throw ApiError.ENOENT(p);
|
|
471
471
|
}
|
|
472
|
-
return Inode.
|
|
472
|
+
return Inode.Deserialize(data);
|
|
473
473
|
});
|
|
474
474
|
}
|
|
475
475
|
/**
|
|
@@ -532,7 +532,7 @@ export class AsyncKeyValueFileSystem extends BaseFileSystem {
|
|
|
532
532
|
*/
|
|
533
533
|
commitNewFile(tx, p, type, mode, cred, data) {
|
|
534
534
|
return __awaiter(this, void 0, void 0, function* () {
|
|
535
|
-
const parentDir =
|
|
535
|
+
const parentDir = dirname(p), fname = basename(p), parentNode = yield this.findINode(tx, parentDir), dirListing = yield this.getDirListing(tx, parentDir, parentNode), currTime = new Date().getTime();
|
|
536
536
|
//Check that the creater has correct access
|
|
537
537
|
if (!parentNode.toStats().hasAccess(W_OK, cred)) {
|
|
538
538
|
throw ApiError.EACCES(p);
|
|
@@ -553,10 +553,10 @@ export class AsyncKeyValueFileSystem extends BaseFileSystem {
|
|
|
553
553
|
const dataId = yield this.addNewNode(tx, data);
|
|
554
554
|
const fileNode = new Inode(dataId, data.length, mode | type, currTime, currTime, currTime, cred.uid, cred.gid);
|
|
555
555
|
// Commit file node.
|
|
556
|
-
const fileNodeId = yield this.addNewNode(tx, fileNode.
|
|
556
|
+
const fileNodeId = yield this.addNewNode(tx, fileNode.serialize());
|
|
557
557
|
// Update and commit parent directory listing.
|
|
558
558
|
dirListing[fname] = fileNodeId;
|
|
559
|
-
yield tx.put(parentNode.id,
|
|
559
|
+
yield tx.put(parentNode.id, encode(JSON.stringify(dirListing)), true);
|
|
560
560
|
yield tx.commit();
|
|
561
561
|
return fileNode;
|
|
562
562
|
}
|
|
@@ -583,7 +583,7 @@ export class AsyncKeyValueFileSystem extends BaseFileSystem {
|
|
|
583
583
|
if (this._cache) {
|
|
584
584
|
this._cache.remove(p);
|
|
585
585
|
}
|
|
586
|
-
const tx = this.store.beginTransaction('readwrite'), parent =
|
|
586
|
+
const tx = this.store.beginTransaction('readwrite'), parent = dirname(p), parentNode = yield this.findINode(tx, parent), parentListing = yield this.getDirListing(tx, parent, parentNode), fileName = basename(p);
|
|
587
587
|
if (!parentListing[fileName]) {
|
|
588
588
|
throw ApiError.ENOENT(p);
|
|
589
589
|
}
|
|
@@ -607,7 +607,7 @@ export class AsyncKeyValueFileSystem extends BaseFileSystem {
|
|
|
607
607
|
// Delete node.
|
|
608
608
|
yield tx.del(fileNodeId);
|
|
609
609
|
// Update directory listing.
|
|
610
|
-
yield tx.put(parentNode.id,
|
|
610
|
+
yield tx.put(parentNode.id, encode(JSON.stringify(parentListing)), true);
|
|
611
611
|
}
|
|
612
612
|
catch (e) {
|
|
613
613
|
yield tx.abort();
|
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
var _a;
|
|
11
11
|
import { BaseFileSystem } from '../filesystem.js';
|
|
12
|
-
import
|
|
12
|
+
import { relative, join } from '../emulation/path.js';
|
|
13
13
|
import { ApiError } from '../ApiError.js';
|
|
14
14
|
import { Cred } from '../cred.js';
|
|
15
15
|
import { CreateBackend } from './backend.js';
|
|
@@ -80,7 +80,7 @@ function translateError(folder, e) {
|
|
|
80
80
|
const err = e;
|
|
81
81
|
let p = err.path;
|
|
82
82
|
if (p) {
|
|
83
|
-
p = '/' +
|
|
83
|
+
p = '/' + relative(folder, p);
|
|
84
84
|
err.message = err.message.replace(err.path, p);
|
|
85
85
|
err.path = p;
|
|
86
86
|
}
|
|
@@ -112,10 +112,10 @@ function wrapFunction(name, wrapFirst, wrapSecond) {
|
|
|
112
112
|
return function () {
|
|
113
113
|
if (arguments.length > 0) {
|
|
114
114
|
if (wrapFirst) {
|
|
115
|
-
arguments[0] =
|
|
115
|
+
arguments[0] = join(this._folder, arguments[0]);
|
|
116
116
|
}
|
|
117
117
|
if (wrapSecond) {
|
|
118
|
-
arguments[1] =
|
|
118
|
+
arguments[1] = join(this._folder, arguments[1]);
|
|
119
119
|
}
|
|
120
120
|
arguments[arguments.length - 1] = wrapCallback(this._folder, arguments[arguments.length - 1]);
|
|
121
121
|
}
|
|
@@ -127,10 +127,10 @@ function wrapFunction(name, wrapFirst, wrapSecond) {
|
|
|
127
127
|
return function () {
|
|
128
128
|
try {
|
|
129
129
|
if (wrapFirst) {
|
|
130
|
-
arguments[0] =
|
|
130
|
+
arguments[0] = join(this._folder, arguments[0]);
|
|
131
131
|
}
|
|
132
132
|
if (wrapSecond) {
|
|
133
|
-
arguments[1] =
|
|
133
|
+
arguments[1] = join(this._folder, arguments[1]);
|
|
134
134
|
}
|
|
135
135
|
return this._wrapped[name].apply(this._wrapped, arguments);
|
|
136
136
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
2
1
|
import { SyncKeyValueStore, SimpleSyncStore, SyncKeyValueRWTransaction, SyncKeyValueFileSystem } from './SyncStore.js';
|
|
3
2
|
import { type BackendOptions } from './backend.js';
|
|
4
3
|
/**
|
|
@@ -9,8 +8,8 @@ export declare class InMemoryStore implements SyncKeyValueStore, SimpleSyncStore
|
|
|
9
8
|
name(): string;
|
|
10
9
|
clear(): void;
|
|
11
10
|
beginTransaction(type: string): SyncKeyValueRWTransaction;
|
|
12
|
-
get(key: string):
|
|
13
|
-
put(key: string, data:
|
|
11
|
+
get(key: string): Uint8Array;
|
|
12
|
+
put(key: string, data: Uint8Array, overwrite: boolean): boolean;
|
|
14
13
|
del(key: string): void;
|
|
15
14
|
}
|
|
16
15
|
/**
|
|
@@ -13,7 +13,7 @@ import { ApiError, ErrorCode } from '../ApiError.js';
|
|
|
13
13
|
import { FileFlag, ActionType, PreloadFile } from '../file.js';
|
|
14
14
|
import { Stats } from '../stats.js';
|
|
15
15
|
import LockedFS from './Locked.js';
|
|
16
|
-
import
|
|
16
|
+
import { resolve, dirname } from '../emulation/path.js';
|
|
17
17
|
import { Cred } from '../cred.js';
|
|
18
18
|
import { CreateBackend } from './backend.js';
|
|
19
19
|
/**
|
|
@@ -187,7 +187,7 @@ export class UnlockedOverlayFS extends BaseFileSystem {
|
|
|
187
187
|
if (yield this._readable.exists(oldPath, cred)) {
|
|
188
188
|
for (const name of yield this._readable.readdir(oldPath, cred)) {
|
|
189
189
|
// Recursion! Should work for any nested files / folders.
|
|
190
|
-
yield this.rename(
|
|
190
|
+
yield this.rename(resolve(oldPath, name), resolve(newPath, name), cred);
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
}
|
|
@@ -242,7 +242,7 @@ export class UnlockedOverlayFS extends BaseFileSystem {
|
|
|
242
242
|
if (this._readable.existsSync(oldPath, cred)) {
|
|
243
243
|
this._readable.readdirSync(oldPath, cred).forEach(name => {
|
|
244
244
|
// Recursion! Should work for any nested files / folders.
|
|
245
|
-
this.renameSync(
|
|
245
|
+
this.renameSync(resolve(oldPath, name), resolve(newPath, name), cred);
|
|
246
246
|
});
|
|
247
247
|
}
|
|
248
248
|
}
|
|
@@ -622,10 +622,10 @@ export class UnlockedOverlayFS extends BaseFileSystem {
|
|
|
622
622
|
* should they not exist. Use modes from the read-only storage.
|
|
623
623
|
*/
|
|
624
624
|
createParentDirectories(p, cred) {
|
|
625
|
-
let parent =
|
|
625
|
+
let parent = dirname(p), toCreate = [];
|
|
626
626
|
while (!this._writable.existsSync(parent, cred)) {
|
|
627
627
|
toCreate.push(parent);
|
|
628
|
-
parent =
|
|
628
|
+
parent = dirname(parent);
|
|
629
629
|
}
|
|
630
630
|
toCreate = toCreate.reverse();
|
|
631
631
|
for (const p of toCreate) {
|
|
@@ -634,10 +634,10 @@ export class UnlockedOverlayFS extends BaseFileSystem {
|
|
|
634
634
|
}
|
|
635
635
|
createParentDirectoriesAsync(p, cred) {
|
|
636
636
|
return __awaiter(this, void 0, void 0, function* () {
|
|
637
|
-
let parent =
|
|
637
|
+
let parent = dirname(p), toCreate = [];
|
|
638
638
|
while (!(yield this._writable.exists(parent, cred))) {
|
|
639
639
|
toCreate.push(parent);
|
|
640
|
-
parent =
|
|
640
|
+
parent = dirname(parent);
|
|
641
641
|
}
|
|
642
642
|
toCreate = toCreate.reverse();
|
|
643
643
|
for (const p of toCreate) {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
2
1
|
import { Cred } from '../cred.js';
|
|
3
|
-
import {
|
|
2
|
+
import { FileFlag, PreloadFile } from '../file.js';
|
|
4
3
|
import { SynchronousFileSystem } from '../filesystem.js';
|
|
5
4
|
import { Stats } from '../stats.js';
|
|
6
5
|
/**
|
|
@@ -35,7 +34,7 @@ export interface SyncKeyValueROTransaction {
|
|
|
35
34
|
* @param key The key to look under for data.
|
|
36
35
|
* @return The data stored under the key, or undefined if not present.
|
|
37
36
|
*/
|
|
38
|
-
get(key: string):
|
|
37
|
+
get(key: string): Uint8Array | undefined;
|
|
39
38
|
}
|
|
40
39
|
/**
|
|
41
40
|
* A read-write transaction for a synchronous key value store.
|
|
@@ -49,7 +48,7 @@ export interface SyncKeyValueRWTransaction extends SyncKeyValueROTransaction {
|
|
|
49
48
|
* avoids storing the data if the key exists.
|
|
50
49
|
* @return True if storage succeeded, false otherwise.
|
|
51
50
|
*/
|
|
52
|
-
put(key: string, data:
|
|
51
|
+
put(key: string, data: Uint8Array, overwrite: boolean): boolean;
|
|
53
52
|
/**
|
|
54
53
|
* Deletes the data at the given key.
|
|
55
54
|
* @param key The key to delete from the store.
|
|
@@ -69,8 +68,8 @@ export interface SyncKeyValueRWTransaction extends SyncKeyValueROTransaction {
|
|
|
69
68
|
* support for transactions and such.
|
|
70
69
|
*/
|
|
71
70
|
export interface SimpleSyncStore {
|
|
72
|
-
get(key: string):
|
|
73
|
-
put(key: string, data:
|
|
71
|
+
get(key: string): Uint8Array | undefined;
|
|
72
|
+
put(key: string, data: Uint8Array, overwrite: boolean): boolean;
|
|
74
73
|
del(key: string): void;
|
|
75
74
|
}
|
|
76
75
|
/**
|
|
@@ -88,8 +87,8 @@ export declare class SimpleSyncRWTransaction implements SyncKeyValueRWTransactio
|
|
|
88
87
|
*/
|
|
89
88
|
private modifiedKeys;
|
|
90
89
|
constructor(store: SimpleSyncStore);
|
|
91
|
-
get(key: string):
|
|
92
|
-
put(key: string, data:
|
|
90
|
+
get(key: string): Uint8Array | undefined;
|
|
91
|
+
put(key: string, data: Uint8Array, overwrite: boolean): boolean;
|
|
93
92
|
del(key: string): void;
|
|
94
93
|
commit(): void;
|
|
95
94
|
abort(): void;
|
|
@@ -126,8 +125,8 @@ export interface SyncKeyValueFileSystemOptions {
|
|
|
126
125
|
*/
|
|
127
126
|
supportLinks?: boolean;
|
|
128
127
|
}
|
|
129
|
-
export declare class SyncKeyValueFile extends PreloadFile<SyncKeyValueFileSystem>
|
|
130
|
-
constructor(_fs: SyncKeyValueFileSystem, _path: string, _flag: FileFlag, _stat: Stats, contents?:
|
|
128
|
+
export declare class SyncKeyValueFile extends PreloadFile<SyncKeyValueFileSystem> {
|
|
129
|
+
constructor(_fs: SyncKeyValueFileSystem, _path: string, _flag: FileFlag, _stat: Stats, contents?: Uint8Array);
|
|
131
130
|
syncSync(): void;
|
|
132
131
|
closeSync(): void;
|
|
133
132
|
}
|
|
@@ -156,15 +155,15 @@ export declare class SyncKeyValueFileSystem extends SynchronousFileSystem {
|
|
|
156
155
|
accessSync(p: string, mode: number, cred: Cred): void;
|
|
157
156
|
renameSync(oldPath: string, newPath: string, cred: Cred): void;
|
|
158
157
|
statSync(p: string, cred: Cred): Stats;
|
|
159
|
-
createFileSync(p: string, flag: FileFlag, mode: number, cred: Cred):
|
|
160
|
-
openFileSync(p: string, flag: FileFlag, cred: Cred):
|
|
158
|
+
createFileSync(p: string, flag: FileFlag, mode: number, cred: Cred): SyncKeyValueFile;
|
|
159
|
+
openFileSync(p: string, flag: FileFlag, cred: Cred): SyncKeyValueFile;
|
|
161
160
|
unlinkSync(p: string, cred: Cred): void;
|
|
162
161
|
rmdirSync(p: string, cred: Cred): void;
|
|
163
162
|
mkdirSync(p: string, mode: number, cred: Cred): void;
|
|
164
163
|
readdirSync(p: string, cred: Cred): string[];
|
|
165
164
|
chmodSync(p: string, mode: number, cred: Cred): void;
|
|
166
165
|
chownSync(p: string, new_uid: number, new_gid: number, cred: Cred): void;
|
|
167
|
-
_syncSync(p: string, data:
|
|
166
|
+
_syncSync(p: string, data: Uint8Array, stats: Stats): void;
|
|
168
167
|
/**
|
|
169
168
|
* Checks if the root directory exists. Creates it if it doesn't.
|
|
170
169
|
*/
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { dirname, basename, join, resolve, sep } from '../emulation/path.js';
|
|
2
2
|
import { ApiError, ErrorCode } from '../ApiError.js';
|
|
3
3
|
import { W_OK, R_OK } from '../emulation/constants.js';
|
|
4
4
|
import { FileFlag, PreloadFile } from '../file.js';
|
|
5
5
|
import { SynchronousFileSystem } from '../filesystem.js';
|
|
6
6
|
import Inode from '../inode.js';
|
|
7
7
|
import { FileType } from '../stats.js';
|
|
8
|
-
import {
|
|
8
|
+
import { encode, randomUUID, ROOT_NODE_ID } from '../utils.js';
|
|
9
9
|
/**
|
|
10
10
|
* A simple RW transaction for simple synchronous key-value stores.
|
|
11
11
|
*/
|
|
@@ -143,7 +143,7 @@ export class SyncKeyValueFileSystem extends SynchronousFileSystem {
|
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
renameSync(oldPath, newPath, cred) {
|
|
146
|
-
const tx = this.store.beginTransaction('readwrite'), oldParent =
|
|
146
|
+
const tx = this.store.beginTransaction('readwrite'), oldParent = dirname(oldPath), oldName = basename(oldPath), newParent = dirname(newPath), newName = basename(newPath),
|
|
147
147
|
// Remove oldPath from parent's directory listing.
|
|
148
148
|
oldDirNode = this.findINode(tx, oldParent), oldDirList = this.getDirListing(tx, oldParent, oldDirNode);
|
|
149
149
|
if (!oldDirNode.toStats().hasAccess(W_OK, cred)) {
|
|
@@ -194,8 +194,8 @@ export class SyncKeyValueFileSystem extends SynchronousFileSystem {
|
|
|
194
194
|
newDirList[newName] = nodeId;
|
|
195
195
|
// Commit the two changed directory listings.
|
|
196
196
|
try {
|
|
197
|
-
tx.put(oldDirNode.id,
|
|
198
|
-
tx.put(newDirNode.id,
|
|
197
|
+
tx.put(oldDirNode.id, encode(JSON.stringify(oldDirList)), true);
|
|
198
|
+
tx.put(newDirNode.id, encode(JSON.stringify(newDirList)), true);
|
|
199
199
|
}
|
|
200
200
|
catch (e) {
|
|
201
201
|
tx.abort();
|
|
@@ -212,7 +212,7 @@ export class SyncKeyValueFileSystem extends SynchronousFileSystem {
|
|
|
212
212
|
return stats;
|
|
213
213
|
}
|
|
214
214
|
createFileSync(p, flag, mode, cred) {
|
|
215
|
-
const tx = this.store.beginTransaction('readwrite'), data =
|
|
215
|
+
const tx = this.store.beginTransaction('readwrite'), data = new Uint8Array(0), newFile = this.commitNewFile(tx, p, FileType.FILE, mode, cred, data);
|
|
216
216
|
// Open the file.
|
|
217
217
|
return new SyncKeyValueFile(this, p, flag, newFile.toStats(), data);
|
|
218
218
|
}
|
|
@@ -239,7 +239,7 @@ export class SyncKeyValueFileSystem extends SynchronousFileSystem {
|
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
241
|
mkdirSync(p, mode, cred) {
|
|
242
|
-
const tx = this.store.beginTransaction('readwrite'), data =
|
|
242
|
+
const tx = this.store.beginTransaction('readwrite'), data = encode('{}');
|
|
243
243
|
this.commitNewFile(tx, p, FileType.DIRECTORY, mode, cred, data);
|
|
244
244
|
}
|
|
245
245
|
readdirSync(p, cred) {
|
|
@@ -263,13 +263,13 @@ export class SyncKeyValueFileSystem extends SynchronousFileSystem {
|
|
|
263
263
|
// update is required.
|
|
264
264
|
const tx = this.store.beginTransaction('readwrite'),
|
|
265
265
|
// We use the _findInode helper because we actually need the INode id.
|
|
266
|
-
fileInodeId = this._findINode(tx,
|
|
266
|
+
fileInodeId = this._findINode(tx, dirname(p), basename(p)), fileInode = this.getINode(tx, p, fileInodeId), inodeChanged = fileInode.update(stats);
|
|
267
267
|
try {
|
|
268
268
|
// Sync data.
|
|
269
269
|
tx.put(fileInode.id, data, true);
|
|
270
270
|
// Sync metadata.
|
|
271
271
|
if (inodeChanged) {
|
|
272
|
-
tx.put(fileInodeId, fileInode.
|
|
272
|
+
tx.put(fileInodeId, fileInode.serialize(), true);
|
|
273
273
|
}
|
|
274
274
|
}
|
|
275
275
|
catch (e) {
|
|
@@ -290,8 +290,8 @@ export class SyncKeyValueFileSystem extends SynchronousFileSystem {
|
|
|
290
290
|
dirInode = new Inode(randomUUID(), 4096, 511 | FileType.DIRECTORY, currTime, currTime, currTime, 0, 0);
|
|
291
291
|
// If the root doesn't exist, the first random ID shouldn't exist,
|
|
292
292
|
// either.
|
|
293
|
-
tx.put(dirInode.id,
|
|
294
|
-
tx.put(ROOT_NODE_ID, dirInode.
|
|
293
|
+
tx.put(dirInode.id, encode('{}'), false);
|
|
294
|
+
tx.put(ROOT_NODE_ID, dirInode.serialize(), false);
|
|
295
295
|
tx.commit();
|
|
296
296
|
}
|
|
297
297
|
}
|
|
@@ -303,7 +303,7 @@ export class SyncKeyValueFileSystem extends SynchronousFileSystem {
|
|
|
303
303
|
* @return string The ID of the file's inode in the file system.
|
|
304
304
|
*/
|
|
305
305
|
_findINode(tx, parent, filename, visited = new Set()) {
|
|
306
|
-
const currentPath =
|
|
306
|
+
const currentPath = join(parent, filename);
|
|
307
307
|
if (visited.has(currentPath)) {
|
|
308
308
|
throw new ApiError(ErrorCode.EIO, 'Infinite loop detected while finding inode', currentPath);
|
|
309
309
|
}
|
|
@@ -316,7 +316,7 @@ export class SyncKeyValueFileSystem extends SynchronousFileSystem {
|
|
|
316
316
|
return dirList[filename];
|
|
317
317
|
}
|
|
318
318
|
else {
|
|
319
|
-
throw ApiError.ENOENT(
|
|
319
|
+
throw ApiError.ENOENT(resolve(parent, filename));
|
|
320
320
|
}
|
|
321
321
|
};
|
|
322
322
|
if (parent === '/') {
|
|
@@ -330,7 +330,7 @@ export class SyncKeyValueFileSystem extends SynchronousFileSystem {
|
|
|
330
330
|
}
|
|
331
331
|
}
|
|
332
332
|
else {
|
|
333
|
-
return readDirectory(this.getINode(tx, parent +
|
|
333
|
+
return readDirectory(this.getINode(tx, parent + sep + filename, this._findINode(tx, dirname(parent), basename(parent), visited)));
|
|
334
334
|
}
|
|
335
335
|
}
|
|
336
336
|
/**
|
|
@@ -340,7 +340,7 @@ export class SyncKeyValueFileSystem extends SynchronousFileSystem {
|
|
|
340
340
|
* @todo memoize/cache
|
|
341
341
|
*/
|
|
342
342
|
findINode(tx, p) {
|
|
343
|
-
return this.getINode(tx, p, this._findINode(tx,
|
|
343
|
+
return this.getINode(tx, p, this._findINode(tx, dirname(p), basename(p)));
|
|
344
344
|
}
|
|
345
345
|
/**
|
|
346
346
|
* Given the ID of a node, retrieves the corresponding Inode.
|
|
@@ -353,7 +353,7 @@ export class SyncKeyValueFileSystem extends SynchronousFileSystem {
|
|
|
353
353
|
if (inode === undefined) {
|
|
354
354
|
throw ApiError.ENOENT(p);
|
|
355
355
|
}
|
|
356
|
-
return Inode.
|
|
356
|
+
return Inode.Deserialize(inode);
|
|
357
357
|
}
|
|
358
358
|
/**
|
|
359
359
|
* Given the Inode of a directory, retrieves the corresponding directory
|
|
@@ -400,7 +400,7 @@ export class SyncKeyValueFileSystem extends SynchronousFileSystem {
|
|
|
400
400
|
* @return The Inode for the new file.
|
|
401
401
|
*/
|
|
402
402
|
commitNewFile(tx, p, type, mode, cred, data) {
|
|
403
|
-
const parentDir =
|
|
403
|
+
const parentDir = dirname(p), fname = basename(p), parentNode = this.findINode(tx, parentDir), dirListing = this.getDirListing(tx, parentDir, parentNode), currTime = new Date().getTime();
|
|
404
404
|
//Check that the creater has correct access
|
|
405
405
|
if (!parentNode.toStats().hasAccess(0b0100 /* Write */, cred)) {
|
|
406
406
|
throw ApiError.EACCES(p);
|
|
@@ -421,10 +421,10 @@ export class SyncKeyValueFileSystem extends SynchronousFileSystem {
|
|
|
421
421
|
const dataId = this.addNewNode(tx, data);
|
|
422
422
|
fileNode = new Inode(dataId, data.length, mode | type, currTime, currTime, currTime, cred.uid, cred.gid);
|
|
423
423
|
// Commit file node.
|
|
424
|
-
const fileNodeId = this.addNewNode(tx, fileNode.
|
|
424
|
+
const fileNodeId = this.addNewNode(tx, fileNode.serialize());
|
|
425
425
|
// Update and commit parent directory listing.
|
|
426
426
|
dirListing[fname] = fileNodeId;
|
|
427
|
-
tx.put(parentNode.id,
|
|
427
|
+
tx.put(parentNode.id, encode(JSON.stringify(dirListing)), true);
|
|
428
428
|
}
|
|
429
429
|
catch (e) {
|
|
430
430
|
tx.abort();
|
|
@@ -440,7 +440,7 @@ export class SyncKeyValueFileSystem extends SynchronousFileSystem {
|
|
|
440
440
|
* @todo Update mtime.
|
|
441
441
|
*/
|
|
442
442
|
removeEntry(p, isDir, cred) {
|
|
443
|
-
const tx = this.store.beginTransaction('readwrite'), parent =
|
|
443
|
+
const tx = this.store.beginTransaction('readwrite'), parent = dirname(p), parentNode = this.findINode(tx, parent), parentListing = this.getDirListing(tx, parent, parentNode), fileName = basename(p);
|
|
444
444
|
if (!parentListing[fileName]) {
|
|
445
445
|
throw ApiError.ENOENT(p);
|
|
446
446
|
}
|
|
@@ -464,7 +464,7 @@ export class SyncKeyValueFileSystem extends SynchronousFileSystem {
|
|
|
464
464
|
// Delete node.
|
|
465
465
|
tx.del(fileNodeId);
|
|
466
466
|
// Update directory listing.
|
|
467
|
-
tx.put(parentNode.id,
|
|
467
|
+
tx.put(parentNode.id, encode(JSON.stringify(parentListing)), true);
|
|
468
468
|
}
|
|
469
469
|
catch (e) {
|
|
470
470
|
tx.abort();
|