@zenfs/core 1.2.9 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backends/fetch.js +1 -1
- package/dist/backends/memory.d.ts +1 -2
- package/dist/backends/overlay.js +2 -3
- package/dist/backends/port/fs.d.ts +2 -15
- package/dist/backends/store/fs.d.ts +17 -28
- package/dist/backends/store/fs.js +169 -191
- package/dist/backends/store/simple.d.ts +20 -21
- package/dist/backends/store/simple.js +24 -24
- package/dist/backends/store/store.d.ts +22 -23
- package/dist/backends/store/store.js +6 -6
- package/dist/config.d.ts +8 -0
- package/dist/config.js +2 -1
- package/dist/devices.d.ts +2 -3
- package/dist/emulation/cache.d.ts +40 -31
- package/dist/emulation/cache.js +62 -53
- package/dist/emulation/index.d.ts +1 -1
- package/dist/emulation/index.js +1 -1
- package/dist/emulation/promises.js +18 -17
- package/dist/emulation/shared.d.ts +6 -0
- package/dist/emulation/shared.js +13 -1
- package/dist/emulation/sync.d.ts +1 -1
- package/dist/emulation/sync.js +16 -15
- package/dist/file.d.ts +3 -15
- package/dist/file.js +7 -19
- package/dist/inode.d.ts +4 -13
- package/dist/inode.js +22 -29
- package/dist/mixins/async.d.ts +15 -10
- package/dist/mixins/async.js +3 -1
- package/dist/stats.js +30 -5
- package/dist/utils.d.ts +5 -7
- package/dist/utils.js +11 -20
- package/package.json +1 -1
- package/src/backends/fetch.ts +1 -1
- package/src/backends/memory.ts +1 -2
- package/src/backends/overlay.ts +2 -2
- package/src/backends/store/fs.ts +187 -220
- package/src/backends/store/simple.ts +36 -37
- package/src/backends/store/store.ts +25 -26
- package/src/config.ts +11 -1
- package/src/devices.ts +2 -3
- package/src/emulation/cache.ts +68 -60
- package/src/emulation/index.ts +1 -1
- package/src/emulation/promises.ts +20 -19
- package/src/emulation/shared.ts +13 -1
- package/src/emulation/sync.ts +16 -15
- package/src/file.ts +9 -21
- package/src/inode.ts +10 -31
- package/src/mixins/async.ts +27 -24
- package/src/stats.ts +47 -5
- package/src/utils.ts +11 -23
- package/tests/fs/dir.test.ts +21 -31
- package/tests/fs/directory.test.ts +6 -4
- package/tests/fs/links.test.ts +9 -2
- package/tests/fs/permissions.test.ts +2 -2
- package/tests/fs/stat.test.ts +42 -0
- package/tests/fs/times.test.ts +28 -28
- package/tests/setup/cow+fetch.ts +4 -2
|
@@ -46,13 +46,14 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
46
46
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
47
47
|
});
|
|
48
48
|
import { credentials } from '../../credentials.js';
|
|
49
|
-
import { S_IFDIR, S_IFREG } from '../../emulation/constants.js';
|
|
50
|
-
import { basename, dirname,
|
|
49
|
+
import { S_IFDIR, S_IFREG, S_ISGID, S_ISUID } from '../../emulation/constants.js';
|
|
50
|
+
import { basename, dirname, parse, resolve } from '../../emulation/path.js';
|
|
51
51
|
import { Errno, ErrnoError } from '../../error.js';
|
|
52
52
|
import { PreloadFile } from '../../file.js';
|
|
53
53
|
import { FileSystem } from '../../filesystem.js';
|
|
54
|
-
import { Inode,
|
|
55
|
-
import { decodeDirListing, encodeUTF8,
|
|
54
|
+
import { Inode, rootIno } from '../../inode.js';
|
|
55
|
+
import { decodeDirListing, encodeDirListing, encodeUTF8, randomBigInt } from '../../utils.js';
|
|
56
|
+
import { serialize } from 'utilium';
|
|
56
57
|
const maxInodeAllocTries = 5;
|
|
57
58
|
/**
|
|
58
59
|
* A file system which uses a key-value store.
|
|
@@ -106,40 +107,40 @@ export class StoreFS extends FileSystem {
|
|
|
106
107
|
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
107
108
|
try {
|
|
108
109
|
const tx = __addDisposableResource(env_1, this.store.transaction(), true);
|
|
109
|
-
const
|
|
110
|
+
const _old = parse(oldPath), _new = parse(newPath),
|
|
110
111
|
// Remove oldPath from parent's directory listing.
|
|
111
|
-
oldDirNode = await this.
|
|
112
|
-
if (!oldDirList[
|
|
112
|
+
oldDirNode = await this.findInode(tx, _old.dir, 'rename'), oldDirList = decodeDirListing(await this.get(tx, oldDirNode.data, _old.dir, 'rename'));
|
|
113
|
+
if (!oldDirList[_old.base]) {
|
|
113
114
|
throw ErrnoError.With('ENOENT', oldPath, 'rename');
|
|
114
115
|
}
|
|
115
|
-
const
|
|
116
|
-
delete oldDirList[
|
|
116
|
+
const ino = oldDirList[_old.base];
|
|
117
|
+
delete oldDirList[_old.base];
|
|
117
118
|
/*
|
|
118
119
|
Can't move a folder inside itself.
|
|
119
|
-
This ensures that the check passes only if `oldPath` is a subpath of `
|
|
120
|
+
This ensures that the check passes only if `oldPath` is a subpath of `_new.dir`.
|
|
120
121
|
We append '/' to avoid matching folders that are a substring of the bottom-most folder in the path.
|
|
121
122
|
*/
|
|
122
|
-
if ((
|
|
123
|
-
throw new ErrnoError(Errno.EBUSY,
|
|
123
|
+
if ((_new.dir + '/').indexOf(oldPath + '/') === 0) {
|
|
124
|
+
throw new ErrnoError(Errno.EBUSY, _old.dir);
|
|
124
125
|
}
|
|
125
126
|
// Add newPath to parent's directory listing.
|
|
126
|
-
const sameParent =
|
|
127
|
-
// Prevent us from re-grabbing the same directory listing, which still contains `
|
|
128
|
-
const newDirNode = sameParent ? oldDirNode : await this.
|
|
129
|
-
const newDirList = sameParent ? oldDirList : await this.
|
|
130
|
-
if (newDirList[
|
|
127
|
+
const sameParent = _new.dir == _old.dir;
|
|
128
|
+
// Prevent us from re-grabbing the same directory listing, which still contains `old_path.base.`
|
|
129
|
+
const newDirNode = sameParent ? oldDirNode : await this.findInode(tx, _new.dir, 'rename');
|
|
130
|
+
const newDirList = sameParent ? oldDirList : decodeDirListing(await this.get(tx, newDirNode.data, _new.dir, 'rename'));
|
|
131
|
+
if (newDirList[_new.base]) {
|
|
131
132
|
// If it's a file, delete it, if it's a directory, throw a permissions error.
|
|
132
|
-
const
|
|
133
|
-
if (!
|
|
133
|
+
const existing = new Inode(await this.get(tx, newDirList[_new.base], newPath, 'rename'));
|
|
134
|
+
if (!existing.toStats().isFile()) {
|
|
134
135
|
throw ErrnoError.With('EPERM', newPath, 'rename');
|
|
135
136
|
}
|
|
136
|
-
await tx.remove(
|
|
137
|
-
await tx.remove(newDirList[
|
|
137
|
+
await tx.remove(existing.data);
|
|
138
|
+
await tx.remove(newDirList[_new.base]);
|
|
138
139
|
}
|
|
139
|
-
newDirList[
|
|
140
|
+
newDirList[_new.base] = ino;
|
|
140
141
|
// Commit the two changed directory listings.
|
|
141
|
-
await tx.set(oldDirNode.
|
|
142
|
-
await tx.set(newDirNode.
|
|
142
|
+
await tx.set(oldDirNode.data, encodeDirListing(oldDirList));
|
|
143
|
+
await tx.set(newDirNode.data, encodeDirListing(newDirList));
|
|
143
144
|
await tx.commit();
|
|
144
145
|
}
|
|
145
146
|
catch (e_1) {
|
|
@@ -156,40 +157,40 @@ export class StoreFS extends FileSystem {
|
|
|
156
157
|
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
157
158
|
try {
|
|
158
159
|
const tx = __addDisposableResource(env_2, this.store.transaction(), false);
|
|
159
|
-
const
|
|
160
|
+
const _old = parse(oldPath), _new = parse(newPath),
|
|
160
161
|
// Remove oldPath from parent's directory listing.
|
|
161
|
-
oldDirNode = this.
|
|
162
|
-
if (!oldDirList[
|
|
162
|
+
oldDirNode = this.findInodeSync(tx, _old.dir, 'rename'), oldDirList = decodeDirListing(this.getSync(tx, oldDirNode.data, _old.dir, 'rename'));
|
|
163
|
+
if (!oldDirList[_old.base]) {
|
|
163
164
|
throw ErrnoError.With('ENOENT', oldPath, 'rename');
|
|
164
165
|
}
|
|
165
|
-
const ino = oldDirList[
|
|
166
|
-
delete oldDirList[
|
|
166
|
+
const ino = oldDirList[_old.base];
|
|
167
|
+
delete oldDirList[_old.base];
|
|
167
168
|
/*
|
|
168
169
|
Can't move a folder inside itself.
|
|
169
|
-
This ensures that the check passes only if `oldPath` is a subpath of `
|
|
170
|
+
This ensures that the check passes only if `oldPath` is a subpath of `_new.dir`.
|
|
170
171
|
We append '/' to avoid matching folders that are a substring of the bottom-most folder in the path.
|
|
171
172
|
*/
|
|
172
|
-
if ((
|
|
173
|
-
throw new ErrnoError(Errno.EBUSY,
|
|
173
|
+
if ((_new.dir + '/').indexOf(oldPath + '/') == 0) {
|
|
174
|
+
throw new ErrnoError(Errno.EBUSY, _old.dir);
|
|
174
175
|
}
|
|
175
176
|
// Add newPath to parent's directory listing.
|
|
176
|
-
const sameParent =
|
|
177
|
-
// Prevent us from re-grabbing the same directory listing, which still contains `
|
|
178
|
-
const newDirNode = sameParent ? oldDirNode : this.
|
|
179
|
-
const newDirList = sameParent ? oldDirList : this.
|
|
180
|
-
if (newDirList[
|
|
177
|
+
const sameParent = _new.dir === _old.dir;
|
|
178
|
+
// Prevent us from re-grabbing the same directory listing, which still contains `old_path.base.`
|
|
179
|
+
const newDirNode = sameParent ? oldDirNode : this.findInodeSync(tx, _new.dir, 'rename');
|
|
180
|
+
const newDirList = sameParent ? oldDirList : decodeDirListing(this.getSync(tx, newDirNode.data, _new.dir, 'rename'));
|
|
181
|
+
if (newDirList[_new.base]) {
|
|
181
182
|
// If it's a file, delete it, if it's a directory, throw a permissions error.
|
|
182
|
-
const
|
|
183
|
-
if (!
|
|
183
|
+
const existing = new Inode(this.getSync(tx, newDirList[_new.base], newPath, 'rename'));
|
|
184
|
+
if (!existing.toStats().isFile()) {
|
|
184
185
|
throw ErrnoError.With('EPERM', newPath, 'rename');
|
|
185
186
|
}
|
|
186
|
-
tx.removeSync(
|
|
187
|
-
tx.removeSync(newDirList[
|
|
187
|
+
tx.removeSync(existing.data);
|
|
188
|
+
tx.removeSync(newDirList[_new.base]);
|
|
188
189
|
}
|
|
189
|
-
newDirList[
|
|
190
|
+
newDirList[_new.base] = ino;
|
|
190
191
|
// Commit the two changed directory listings.
|
|
191
|
-
tx.setSync(oldDirNode.
|
|
192
|
-
tx.setSync(newDirNode.
|
|
192
|
+
tx.setSync(oldDirNode.data, encodeDirListing(oldDirList));
|
|
193
|
+
tx.setSync(newDirNode.data, encodeDirListing(newDirList));
|
|
193
194
|
tx.commitSync();
|
|
194
195
|
}
|
|
195
196
|
catch (e_2) {
|
|
@@ -204,7 +205,7 @@ export class StoreFS extends FileSystem {
|
|
|
204
205
|
const env_3 = { stack: [], error: void 0, hasError: false };
|
|
205
206
|
try {
|
|
206
207
|
const tx = __addDisposableResource(env_3, this.store.transaction(), true);
|
|
207
|
-
return (await this.
|
|
208
|
+
return (await this.findInode(tx, path, 'stat')).toStats();
|
|
208
209
|
}
|
|
209
210
|
catch (e_3) {
|
|
210
211
|
env_3.error = e_3;
|
|
@@ -220,7 +221,7 @@ export class StoreFS extends FileSystem {
|
|
|
220
221
|
const env_4 = { stack: [], error: void 0, hasError: false };
|
|
221
222
|
try {
|
|
222
223
|
const tx = __addDisposableResource(env_4, this.store.transaction(), false);
|
|
223
|
-
return this.
|
|
224
|
+
return this.findInodeSync(tx, path, 'stat').toStats();
|
|
224
225
|
}
|
|
225
226
|
catch (e_4) {
|
|
226
227
|
env_4.error = e_4;
|
|
@@ -231,21 +232,19 @@ export class StoreFS extends FileSystem {
|
|
|
231
232
|
}
|
|
232
233
|
}
|
|
233
234
|
async createFile(path, flag, mode) {
|
|
234
|
-
const node = await this.commitNew(path, S_IFREG, mode, new Uint8Array(
|
|
235
|
-
return new PreloadFile(this, path, flag, node.toStats(), new Uint8Array(
|
|
235
|
+
const node = await this.commitNew(path, S_IFREG, mode, new Uint8Array(), 'createFile');
|
|
236
|
+
return new PreloadFile(this, path, flag, node.toStats(), new Uint8Array());
|
|
236
237
|
}
|
|
237
238
|
createFileSync(path, flag, mode) {
|
|
238
|
-
this.commitNewSync(path, S_IFREG, mode);
|
|
239
|
-
return this
|
|
239
|
+
const node = this.commitNewSync(path, S_IFREG, mode, new Uint8Array(), 'createFile');
|
|
240
|
+
return new PreloadFile(this, path, flag, node.toStats(), new Uint8Array());
|
|
240
241
|
}
|
|
241
242
|
async openFile(path, flag) {
|
|
242
243
|
const env_5 = { stack: [], error: void 0, hasError: false };
|
|
243
244
|
try {
|
|
244
245
|
const tx = __addDisposableResource(env_5, this.store.transaction(), true);
|
|
245
|
-
const node = await this.
|
|
246
|
-
|
|
247
|
-
throw ErrnoError.With('ENOENT', path, 'openFile');
|
|
248
|
-
}
|
|
246
|
+
const node = await this.findInode(tx, path, 'openFile');
|
|
247
|
+
const data = await this.get(tx, node.data, path, 'openFile');
|
|
249
248
|
return new PreloadFile(this, path, flag, node.toStats(), data);
|
|
250
249
|
}
|
|
251
250
|
catch (e_5) {
|
|
@@ -262,10 +261,8 @@ export class StoreFS extends FileSystem {
|
|
|
262
261
|
const env_6 = { stack: [], error: void 0, hasError: false };
|
|
263
262
|
try {
|
|
264
263
|
const tx = __addDisposableResource(env_6, this.store.transaction(), false);
|
|
265
|
-
const node = this.
|
|
266
|
-
|
|
267
|
-
throw ErrnoError.With('ENOENT', path, 'openFile');
|
|
268
|
-
}
|
|
264
|
+
const node = this.findInodeSync(tx, path, 'openFile');
|
|
265
|
+
const data = this.getSync(tx, node.data, path, 'openFile');
|
|
269
266
|
return new PreloadFile(this, path, flag, node.toStats(), data);
|
|
270
267
|
}
|
|
271
268
|
catch (e_6) {
|
|
@@ -277,35 +274,35 @@ export class StoreFS extends FileSystem {
|
|
|
277
274
|
}
|
|
278
275
|
}
|
|
279
276
|
async unlink(path) {
|
|
280
|
-
return this.remove(path, false);
|
|
277
|
+
return this.remove(path, false, 'unlink');
|
|
281
278
|
}
|
|
282
279
|
unlinkSync(path) {
|
|
283
|
-
this.removeSync(path, false);
|
|
280
|
+
this.removeSync(path, false, 'unlink');
|
|
284
281
|
}
|
|
285
282
|
async rmdir(path) {
|
|
286
283
|
if ((await this.readdir(path)).length) {
|
|
287
284
|
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
|
|
288
285
|
}
|
|
289
|
-
await this.remove(path, true);
|
|
286
|
+
await this.remove(path, true, 'rmdir');
|
|
290
287
|
}
|
|
291
288
|
rmdirSync(path) {
|
|
292
289
|
if (this.readdirSync(path).length) {
|
|
293
290
|
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
|
|
294
291
|
}
|
|
295
|
-
this.removeSync(path, true);
|
|
292
|
+
this.removeSync(path, true, 'rmdir');
|
|
296
293
|
}
|
|
297
294
|
async mkdir(path, mode) {
|
|
298
|
-
await this.commitNew(path, S_IFDIR, mode, encodeUTF8('{}'));
|
|
295
|
+
await this.commitNew(path, S_IFDIR, mode, encodeUTF8('{}'), 'mkdir');
|
|
299
296
|
}
|
|
300
297
|
mkdirSync(path, mode) {
|
|
301
|
-
this.commitNewSync(path, S_IFDIR, mode, encodeUTF8('{}'));
|
|
298
|
+
this.commitNewSync(path, S_IFDIR, mode, encodeUTF8('{}'), 'mkdir');
|
|
302
299
|
}
|
|
303
300
|
async readdir(path) {
|
|
304
301
|
const env_7 = { stack: [], error: void 0, hasError: false };
|
|
305
302
|
try {
|
|
306
303
|
const tx = __addDisposableResource(env_7, this.store.transaction(), true);
|
|
307
|
-
const node = await this.
|
|
308
|
-
return Object.keys(await this.
|
|
304
|
+
const node = await this.findInode(tx, path, 'readdir');
|
|
305
|
+
return Object.keys(decodeDirListing(await this.get(tx, node.data, path, 'readdir')));
|
|
309
306
|
}
|
|
310
307
|
catch (e_7) {
|
|
311
308
|
env_7.error = e_7;
|
|
@@ -321,8 +318,8 @@ export class StoreFS extends FileSystem {
|
|
|
321
318
|
const env_8 = { stack: [], error: void 0, hasError: false };
|
|
322
319
|
try {
|
|
323
320
|
const tx = __addDisposableResource(env_8, this.store.transaction(), false);
|
|
324
|
-
const node = this.
|
|
325
|
-
return Object.keys(this.
|
|
321
|
+
const node = this.findInodeSync(tx, path, 'readdir');
|
|
322
|
+
return Object.keys(decodeDirListing(this.getSync(tx, node.data, path, 'readdir')));
|
|
326
323
|
}
|
|
327
324
|
catch (e_8) {
|
|
328
325
|
env_8.error = e_8;
|
|
@@ -341,12 +338,12 @@ export class StoreFS extends FileSystem {
|
|
|
341
338
|
try {
|
|
342
339
|
const tx = __addDisposableResource(env_9, this.store.transaction(), true);
|
|
343
340
|
// We use _findInode because we actually need the INode id.
|
|
344
|
-
const fileInodeId = await this.
|
|
341
|
+
const fileInodeId = await this._findInode(tx, path, 'sync'), fileInode = new Inode(await this.get(tx, fileInodeId, path, 'sync')), inodeChanged = fileInode.update(stats);
|
|
345
342
|
// Sync data.
|
|
346
|
-
await tx.set(fileInode.
|
|
343
|
+
await tx.set(fileInode.data, data);
|
|
347
344
|
// Sync metadata.
|
|
348
345
|
if (inodeChanged) {
|
|
349
|
-
await tx.set(fileInodeId, fileInode
|
|
346
|
+
await tx.set(fileInodeId, serialize(fileInode));
|
|
350
347
|
}
|
|
351
348
|
await tx.commit();
|
|
352
349
|
}
|
|
@@ -369,12 +366,12 @@ export class StoreFS extends FileSystem {
|
|
|
369
366
|
try {
|
|
370
367
|
const tx = __addDisposableResource(env_10, this.store.transaction(), false);
|
|
371
368
|
// We use _findInode because we actually need the INode id.
|
|
372
|
-
const fileInodeId = this.
|
|
369
|
+
const fileInodeId = this._findInodeSync(tx, path, 'sync'), fileInode = new Inode(this.getSync(tx, fileInodeId, path, 'sync')), inodeChanged = fileInode.update(stats);
|
|
373
370
|
// Sync data.
|
|
374
|
-
tx.setSync(fileInode.
|
|
371
|
+
tx.setSync(fileInode.data, data);
|
|
375
372
|
// Sync metadata.
|
|
376
373
|
if (inodeChanged) {
|
|
377
|
-
tx.setSync(fileInodeId, fileInode
|
|
374
|
+
tx.setSync(fileInodeId, serialize(fileInode));
|
|
378
375
|
}
|
|
379
376
|
tx.commitSync();
|
|
380
377
|
}
|
|
@@ -390,13 +387,13 @@ export class StoreFS extends FileSystem {
|
|
|
390
387
|
const env_11 = { stack: [], error: void 0, hasError: false };
|
|
391
388
|
try {
|
|
392
389
|
const tx = __addDisposableResource(env_11, this.store.transaction(), true);
|
|
393
|
-
const newDir = dirname(link), newDirNode = await this.
|
|
394
|
-
const ino = await this.
|
|
395
|
-
const node = await this.
|
|
390
|
+
const newDir = dirname(link), newDirNode = await this.findInode(tx, newDir, 'link'), listing = decodeDirListing(await this.get(tx, newDirNode.data, newDir, 'link'));
|
|
391
|
+
const ino = await this._findInode(tx, target, 'link');
|
|
392
|
+
const node = new Inode(await this.get(tx, ino, target, 'link'));
|
|
396
393
|
node.nlink++;
|
|
397
394
|
listing[basename(link)] = ino;
|
|
398
|
-
tx.setSync(ino, node
|
|
399
|
-
tx.setSync(newDirNode.
|
|
395
|
+
tx.setSync(ino, serialize(node));
|
|
396
|
+
tx.setSync(newDirNode.data, encodeDirListing(listing));
|
|
400
397
|
tx.commitSync();
|
|
401
398
|
}
|
|
402
399
|
catch (e_11) {
|
|
@@ -413,13 +410,13 @@ export class StoreFS extends FileSystem {
|
|
|
413
410
|
const env_12 = { stack: [], error: void 0, hasError: false };
|
|
414
411
|
try {
|
|
415
412
|
const tx = __addDisposableResource(env_12, this.store.transaction(), false);
|
|
416
|
-
const newDir = dirname(link), newDirNode = this.
|
|
417
|
-
const ino = this.
|
|
418
|
-
const node = this.
|
|
413
|
+
const newDir = dirname(link), newDirNode = this.findInodeSync(tx, newDir, 'link'), listing = decodeDirListing(this.getSync(tx, newDirNode.data, newDir, 'link'));
|
|
414
|
+
const ino = this._findInodeSync(tx, target, 'link');
|
|
415
|
+
const node = new Inode(this.getSync(tx, ino, target, 'link'));
|
|
419
416
|
node.nlink++;
|
|
420
417
|
listing[basename(link)] = ino;
|
|
421
|
-
tx.setSync(ino, node
|
|
422
|
-
tx.setSync(newDirNode.
|
|
418
|
+
tx.setSync(ino, serialize(node));
|
|
419
|
+
tx.setSync(newDirNode.data, encodeDirListing(listing));
|
|
423
420
|
tx.commitSync();
|
|
424
421
|
}
|
|
425
422
|
catch (e_12) {
|
|
@@ -442,10 +439,11 @@ export class StoreFS extends FileSystem {
|
|
|
442
439
|
}
|
|
443
440
|
// Create new inode. o777, owned by root:root
|
|
444
441
|
const inode = new Inode();
|
|
442
|
+
inode.ino = rootIno;
|
|
445
443
|
inode.mode = 0o777 | S_IFDIR;
|
|
446
444
|
// If the root doesn't exist, the first random ID shouldn't exist either.
|
|
447
|
-
await tx.set(inode.
|
|
448
|
-
await tx.set(rootIno, inode
|
|
445
|
+
await tx.set(inode.data, encodeUTF8('{}'));
|
|
446
|
+
await tx.set(rootIno, serialize(inode));
|
|
449
447
|
await tx.commit();
|
|
450
448
|
}
|
|
451
449
|
catch (e_13) {
|
|
@@ -470,10 +468,11 @@ export class StoreFS extends FileSystem {
|
|
|
470
468
|
}
|
|
471
469
|
// Create new inode, mode o777, owned by root:root
|
|
472
470
|
const inode = new Inode();
|
|
471
|
+
inode.ino = rootIno;
|
|
473
472
|
inode.mode = 0o777 | S_IFDIR;
|
|
474
473
|
// If the root doesn't exist, the first random ID shouldn't exist either.
|
|
475
|
-
tx.setSync(inode.
|
|
476
|
-
tx.setSync(rootIno, inode
|
|
474
|
+
tx.setSync(inode.data, encodeUTF8('{}'));
|
|
475
|
+
tx.setSync(rootIno, serialize(inode));
|
|
477
476
|
tx.commitSync();
|
|
478
477
|
}
|
|
479
478
|
catch (e_14) {
|
|
@@ -490,19 +489,19 @@ export class StoreFS extends FileSystem {
|
|
|
490
489
|
* @param filename The filename of the inode we are attempting to find, minus
|
|
491
490
|
* the parent.
|
|
492
491
|
*/
|
|
493
|
-
async
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
throw new ErrnoError(Errno.EIO, 'Infinite loop detected while finding inode', currentPath);
|
|
492
|
+
async _findInode(tx, path, syscall, visited = new Set()) {
|
|
493
|
+
if (visited.has(path)) {
|
|
494
|
+
throw new ErrnoError(Errno.EIO, 'Infinite loop detected while finding inode', path);
|
|
497
495
|
}
|
|
498
|
-
visited.add(
|
|
499
|
-
if (
|
|
496
|
+
visited.add(path);
|
|
497
|
+
if (path == '/') {
|
|
500
498
|
return rootIno;
|
|
501
499
|
}
|
|
502
|
-
const
|
|
503
|
-
const
|
|
500
|
+
const { dir: parent, base: filename } = parse(path);
|
|
501
|
+
const inode = parent == '/' ? new Inode(await this.get(tx, rootIno, parent, syscall)) : await this.findInode(tx, parent, syscall, visited);
|
|
502
|
+
const dirList = decodeDirListing(await this.get(tx, inode.data, parent, syscall));
|
|
504
503
|
if (!(filename in dirList)) {
|
|
505
|
-
throw ErrnoError.With('ENOENT', resolve(parent, filename),
|
|
504
|
+
throw ErrnoError.With('ENOENT', resolve(parent, filename), syscall);
|
|
506
505
|
}
|
|
507
506
|
return dirList[filename];
|
|
508
507
|
}
|
|
@@ -513,19 +512,19 @@ export class StoreFS extends FileSystem {
|
|
|
513
512
|
* the parent.
|
|
514
513
|
* @return string The ID of the file's inode in the file system.
|
|
515
514
|
*/
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
throw new ErrnoError(Errno.EIO, 'Infinite loop detected while finding inode', currentPath);
|
|
515
|
+
_findInodeSync(tx, path, syscall, visited = new Set()) {
|
|
516
|
+
if (visited.has(path)) {
|
|
517
|
+
throw new ErrnoError(Errno.EIO, 'Infinite loop detected while finding inode', path);
|
|
520
518
|
}
|
|
521
|
-
visited.add(
|
|
522
|
-
if (
|
|
519
|
+
visited.add(path);
|
|
520
|
+
if (path == '/') {
|
|
523
521
|
return rootIno;
|
|
524
522
|
}
|
|
525
|
-
const
|
|
526
|
-
const
|
|
523
|
+
const { dir: parent, base: filename } = parse(path);
|
|
524
|
+
const inode = parent == '/' ? new Inode(this.getSync(tx, rootIno, parent, syscall)) : this.findInodeSync(tx, parent, syscall, visited);
|
|
525
|
+
const dir = decodeDirListing(this.getSync(tx, inode.data, parent, syscall));
|
|
527
526
|
if (!(filename in dir)) {
|
|
528
|
-
throw ErrnoError.With('ENOENT', resolve(parent, filename),
|
|
527
|
+
throw ErrnoError.With('ENOENT', resolve(parent, filename), syscall);
|
|
529
528
|
}
|
|
530
529
|
return dir[filename];
|
|
531
530
|
}
|
|
@@ -534,9 +533,9 @@ export class StoreFS extends FileSystem {
|
|
|
534
533
|
* @param path The path to look up.
|
|
535
534
|
* @todo memoize/cache
|
|
536
535
|
*/
|
|
537
|
-
async
|
|
538
|
-
const
|
|
539
|
-
return this.
|
|
536
|
+
async findInode(tx, path, syscall, visited = new Set()) {
|
|
537
|
+
const ino = await this._findInode(tx, path, syscall, visited);
|
|
538
|
+
return new Inode(await this.get(tx, ino, path, syscall));
|
|
540
539
|
}
|
|
541
540
|
/**
|
|
542
541
|
* Finds the Inode of `path`.
|
|
@@ -544,92 +543,64 @@ export class StoreFS extends FileSystem {
|
|
|
544
543
|
* @return The Inode of the path p.
|
|
545
544
|
* @todo memoize/cache
|
|
546
545
|
*/
|
|
547
|
-
|
|
548
|
-
const ino = this.
|
|
549
|
-
return this.
|
|
546
|
+
findInodeSync(tx, path, syscall, visited = new Set()) {
|
|
547
|
+
const ino = this._findInodeSync(tx, path, syscall, visited);
|
|
548
|
+
return new Inode(this.getSync(tx, ino, path, syscall));
|
|
550
549
|
}
|
|
551
550
|
/**
|
|
552
|
-
* Given
|
|
551
|
+
* Given an ID, retrieves the corresponding data.
|
|
553
552
|
* @param tx The transaction to use.
|
|
554
553
|
* @param path The corresponding path to the file (used for error messages).
|
|
555
554
|
* @param id The ID to look up.
|
|
556
555
|
*/
|
|
557
|
-
async
|
|
556
|
+
async get(tx, id, path, syscall) {
|
|
558
557
|
const data = await tx.get(id);
|
|
559
558
|
if (!data) {
|
|
560
|
-
throw ErrnoError.With('ENOENT', path,
|
|
559
|
+
throw ErrnoError.With('ENOENT', path, syscall);
|
|
561
560
|
}
|
|
562
|
-
return
|
|
561
|
+
return data;
|
|
563
562
|
}
|
|
564
563
|
/**
|
|
565
|
-
* Given
|
|
564
|
+
* Given an ID, retrieves the corresponding data.
|
|
566
565
|
* @param tx The transaction to use.
|
|
567
566
|
* @param path The corresponding path to the file (used for error messages).
|
|
568
567
|
* @param id The ID to look up.
|
|
569
568
|
*/
|
|
570
|
-
|
|
569
|
+
getSync(tx, id, path, syscall) {
|
|
571
570
|
const data = tx.getSync(id);
|
|
572
571
|
if (!data) {
|
|
573
|
-
throw ErrnoError.With('ENOENT', path,
|
|
574
|
-
}
|
|
575
|
-
const inode = new Inode(data.buffer);
|
|
576
|
-
return inode;
|
|
577
|
-
}
|
|
578
|
-
/**
|
|
579
|
-
* Given the Inode of a directory, retrieves the corresponding directory
|
|
580
|
-
* listing.
|
|
581
|
-
*/
|
|
582
|
-
async getDirListing(tx, inode, path) {
|
|
583
|
-
const data = await tx.get(inode.ino);
|
|
584
|
-
/*
|
|
585
|
-
Occurs when data is undefined,or corresponds to something other than a directory listing.
|
|
586
|
-
The latter should never occur unless the file system is corrupted.
|
|
587
|
-
*/
|
|
588
|
-
if (!data) {
|
|
589
|
-
throw ErrnoError.With('ENOENT', path, 'getDirListing');
|
|
590
|
-
}
|
|
591
|
-
return decodeDirListing(data);
|
|
592
|
-
}
|
|
593
|
-
/**
|
|
594
|
-
* Given the Inode of a directory, retrieves the corresponding directory listing.
|
|
595
|
-
*/
|
|
596
|
-
getDirListingSync(tx, inode, p) {
|
|
597
|
-
const data = tx.getSync(inode.ino);
|
|
598
|
-
if (!data) {
|
|
599
|
-
throw ErrnoError.With('ENOENT', p, 'getDirListing');
|
|
572
|
+
throw ErrnoError.With('ENOENT', path, syscall);
|
|
600
573
|
}
|
|
601
|
-
return
|
|
574
|
+
return data;
|
|
602
575
|
}
|
|
603
576
|
/**
|
|
604
577
|
* Adds a new node under a random ID. Retries before giving up in
|
|
605
|
-
* the exceedingly unlikely chance that we try to reuse a random
|
|
578
|
+
* the exceedingly unlikely chance that we try to reuse a random id.
|
|
606
579
|
*/
|
|
607
|
-
async
|
|
580
|
+
async allocNew(tx, path, syscall) {
|
|
608
581
|
for (let i = 0; i < maxInodeAllocTries; i++) {
|
|
609
|
-
const ino =
|
|
582
|
+
const ino = randomBigInt();
|
|
610
583
|
if (await tx.get(ino)) {
|
|
611
584
|
continue;
|
|
612
585
|
}
|
|
613
|
-
await tx.set(ino, data);
|
|
614
586
|
return ino;
|
|
615
587
|
}
|
|
616
|
-
throw new ErrnoError(Errno.ENOSPC, 'No
|
|
588
|
+
throw new ErrnoError(Errno.ENOSPC, 'No IDs available', path, syscall);
|
|
617
589
|
}
|
|
618
590
|
/**
|
|
619
591
|
* Creates a new node under a random ID. Retries before giving up in
|
|
620
|
-
* the exceedingly unlikely chance that we try to reuse a random
|
|
592
|
+
* the exceedingly unlikely chance that we try to reuse a random id.
|
|
621
593
|
* @return The ino that the data was stored under.
|
|
622
594
|
*/
|
|
623
|
-
|
|
595
|
+
allocNewSync(tx, path, syscall) {
|
|
624
596
|
for (let i = 0; i < maxInodeAllocTries; i++) {
|
|
625
|
-
const ino =
|
|
597
|
+
const ino = randomBigInt();
|
|
626
598
|
if (tx.getSync(ino)) {
|
|
627
599
|
continue;
|
|
628
600
|
}
|
|
629
|
-
tx.setSync(ino, data);
|
|
630
601
|
return ino;
|
|
631
602
|
}
|
|
632
|
-
throw new ErrnoError(Errno.ENOSPC, 'No
|
|
603
|
+
throw new ErrnoError(Errno.ENOSPC, 'No IDs available', path, syscall);
|
|
633
604
|
}
|
|
634
605
|
/**
|
|
635
606
|
* Commits a new file (well, a FILE or a DIRECTORY) to the file system with `mode`.
|
|
@@ -639,7 +610,7 @@ export class StoreFS extends FileSystem {
|
|
|
639
610
|
* @param mode The mode to create the new file with.
|
|
640
611
|
* @param data The data to store at the file's data node.
|
|
641
612
|
*/
|
|
642
|
-
async commitNew(path, type, mode, data) {
|
|
613
|
+
async commitNew(path, type, mode, data, syscall) {
|
|
643
614
|
const env_15 = { stack: [], error: void 0, hasError: false };
|
|
644
615
|
try {
|
|
645
616
|
/*
|
|
@@ -648,26 +619,29 @@ export class StoreFS extends FileSystem {
|
|
|
648
619
|
we will create a file with name '' in root if path is '/'.
|
|
649
620
|
*/
|
|
650
621
|
if (path == '/') {
|
|
651
|
-
throw ErrnoError.With('EEXIST', path,
|
|
622
|
+
throw ErrnoError.With('EEXIST', path, syscall);
|
|
652
623
|
}
|
|
653
624
|
const tx = __addDisposableResource(env_15, this.store.transaction(), true);
|
|
654
|
-
const
|
|
655
|
-
const
|
|
625
|
+
const { dir: parentPath, base: fname } = parse(path);
|
|
626
|
+
const parent = await this.findInode(tx, parentPath, syscall);
|
|
627
|
+
const listing = decodeDirListing(await this.get(tx, parent.data, parentPath, syscall));
|
|
656
628
|
// Check if file already exists.
|
|
657
629
|
if (listing[fname]) {
|
|
658
|
-
|
|
659
|
-
throw ErrnoError.With('EEXIST', path, 'commitNew');
|
|
630
|
+
throw ErrnoError.With('EEXIST', path, syscall);
|
|
660
631
|
}
|
|
661
632
|
// Commit data.
|
|
662
633
|
const inode = new Inode();
|
|
663
|
-
inode.ino = await this.
|
|
634
|
+
inode.ino = await this.allocNew(tx, path, syscall);
|
|
635
|
+
inode.data = await this.allocNew(tx, path, syscall);
|
|
664
636
|
inode.mode = mode | type;
|
|
665
|
-
inode.uid = credentials.uid;
|
|
666
|
-
inode.gid = credentials.gid;
|
|
637
|
+
inode.uid = parent.mode & S_ISUID ? parent.uid : credentials.uid;
|
|
638
|
+
inode.gid = parent.mode & S_ISGID ? parent.gid : credentials.gid;
|
|
667
639
|
inode.size = data.length;
|
|
640
|
+
await tx.set(inode.ino, serialize(inode));
|
|
641
|
+
await tx.set(inode.data, data);
|
|
668
642
|
// Update and commit parent directory listing.
|
|
669
|
-
listing[fname] =
|
|
670
|
-
await tx.set(parent.
|
|
643
|
+
listing[fname] = inode.ino;
|
|
644
|
+
await tx.set(parent.data, encodeDirListing(listing));
|
|
671
645
|
await tx.commit();
|
|
672
646
|
return inode;
|
|
673
647
|
}
|
|
@@ -690,7 +664,7 @@ export class StoreFS extends FileSystem {
|
|
|
690
664
|
* @param data The data to store at the file's data node.
|
|
691
665
|
* @return The Inode for the new file.
|
|
692
666
|
*/
|
|
693
|
-
commitNewSync(path, type, mode, data
|
|
667
|
+
commitNewSync(path, type, mode, data, syscall) {
|
|
694
668
|
const env_16 = { stack: [], error: void 0, hasError: false };
|
|
695
669
|
try {
|
|
696
670
|
/*
|
|
@@ -699,27 +673,31 @@ export class StoreFS extends FileSystem {
|
|
|
699
673
|
we will create a file with name '' in root if path is '/'.
|
|
700
674
|
*/
|
|
701
675
|
if (path == '/') {
|
|
702
|
-
throw ErrnoError.With('EEXIST', path,
|
|
676
|
+
throw ErrnoError.With('EEXIST', path, syscall);
|
|
703
677
|
}
|
|
704
678
|
const tx = __addDisposableResource(env_16, this.store.transaction(), false);
|
|
705
|
-
const
|
|
706
|
-
const
|
|
679
|
+
const { dir: parentPath, base: fname } = parse(path);
|
|
680
|
+
const parent = this.findInodeSync(tx, parentPath, syscall);
|
|
681
|
+
const listing = decodeDirListing(this.getSync(tx, parent.data, parentPath, syscall));
|
|
707
682
|
// Check if file already exists.
|
|
708
683
|
if (listing[fname]) {
|
|
709
|
-
throw ErrnoError.With('EEXIST', path,
|
|
684
|
+
throw ErrnoError.With('EEXIST', path, syscall);
|
|
710
685
|
}
|
|
711
686
|
// Commit data.
|
|
712
|
-
const
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
687
|
+
const inode = new Inode();
|
|
688
|
+
inode.ino = this.allocNewSync(tx, path, syscall);
|
|
689
|
+
inode.data = this.allocNewSync(tx, path, syscall);
|
|
690
|
+
inode.size = data.length;
|
|
691
|
+
inode.mode = mode | type;
|
|
692
|
+
inode.uid = parent.mode & S_ISUID ? parent.uid : credentials.uid;
|
|
693
|
+
inode.gid = parent.mode & S_ISGID ? parent.gid : credentials.gid;
|
|
718
694
|
// Update and commit parent directory listing.
|
|
719
|
-
|
|
720
|
-
tx.setSync(
|
|
695
|
+
tx.setSync(inode.ino, serialize(inode));
|
|
696
|
+
tx.setSync(inode.data, data);
|
|
697
|
+
listing[fname] = inode.ino;
|
|
698
|
+
tx.setSync(parent.data, encodeDirListing(listing));
|
|
721
699
|
tx.commitSync();
|
|
722
|
-
return
|
|
700
|
+
return inode;
|
|
723
701
|
}
|
|
724
702
|
catch (e_16) {
|
|
725
703
|
env_16.error = e_16;
|
|
@@ -735,26 +713,26 @@ export class StoreFS extends FileSystem {
|
|
|
735
713
|
* @param isDir Does the path belong to a directory, or a file?
|
|
736
714
|
* @todo Update mtime.
|
|
737
715
|
*/
|
|
738
|
-
async remove(path, isDir) {
|
|
716
|
+
async remove(path, isDir, syscall) {
|
|
739
717
|
const env_17 = { stack: [], error: void 0, hasError: false };
|
|
740
718
|
try {
|
|
741
719
|
const tx = __addDisposableResource(env_17, this.store.transaction(), true);
|
|
742
|
-
const parent =
|
|
720
|
+
const { dir: parent, base: fileName } = parse(path), parentNode = await this.findInode(tx, parent, syscall), listing = decodeDirListing(await this.get(tx, parentNode.data, parent, syscall));
|
|
743
721
|
if (!listing[fileName]) {
|
|
744
722
|
throw ErrnoError.With('ENOENT', path, 'remove');
|
|
745
723
|
}
|
|
746
724
|
const fileIno = listing[fileName];
|
|
747
725
|
// Get file inode.
|
|
748
|
-
const fileNode = await this.
|
|
726
|
+
const fileNode = new Inode(await this.get(tx, fileIno, path, syscall));
|
|
749
727
|
// Remove from directory listing of parent.
|
|
750
728
|
delete listing[fileName];
|
|
751
729
|
if (!isDir && fileNode.toStats().isDirectory()) {
|
|
752
730
|
throw ErrnoError.With('EISDIR', path, 'remove');
|
|
753
731
|
}
|
|
754
|
-
await tx.set(parentNode.
|
|
732
|
+
await tx.set(parentNode.data, encodeDirListing(listing));
|
|
755
733
|
if (--fileNode.nlink < 1) {
|
|
756
734
|
// remove file
|
|
757
|
-
await tx.remove(fileNode.
|
|
735
|
+
await tx.remove(fileNode.data);
|
|
758
736
|
await tx.remove(fileIno);
|
|
759
737
|
}
|
|
760
738
|
// Success.
|
|
@@ -776,26 +754,26 @@ export class StoreFS extends FileSystem {
|
|
|
776
754
|
* @param isDir Does the path belong to a directory, or a file?
|
|
777
755
|
* @todo Update mtime.
|
|
778
756
|
*/
|
|
779
|
-
removeSync(path, isDir) {
|
|
757
|
+
removeSync(path, isDir, syscall) {
|
|
780
758
|
const env_18 = { stack: [], error: void 0, hasError: false };
|
|
781
759
|
try {
|
|
782
760
|
const tx = __addDisposableResource(env_18, this.store.transaction(), false);
|
|
783
|
-
const parent =
|
|
761
|
+
const { dir: parent, base: fileName } = parse(path), parentNode = this.findInodeSync(tx, parent, syscall), listing = decodeDirListing(this.getSync(tx, parentNode.data, parent, syscall)), fileIno = listing[fileName];
|
|
784
762
|
if (!fileIno) {
|
|
785
763
|
throw ErrnoError.With('ENOENT', path, 'remove');
|
|
786
764
|
}
|
|
787
765
|
// Get file inode.
|
|
788
|
-
const fileNode = this.
|
|
766
|
+
const fileNode = new Inode(this.getSync(tx, fileIno, path, syscall));
|
|
789
767
|
// Remove from directory listing of parent.
|
|
790
768
|
delete listing[fileName];
|
|
791
769
|
if (!isDir && fileNode.toStats().isDirectory()) {
|
|
792
770
|
throw ErrnoError.With('EISDIR', path, 'remove');
|
|
793
771
|
}
|
|
794
772
|
// Update directory listing.
|
|
795
|
-
tx.setSync(parentNode.
|
|
773
|
+
tx.setSync(parentNode.data, encodeDirListing(listing));
|
|
796
774
|
if (--fileNode.nlink < 1) {
|
|
797
775
|
// remove file
|
|
798
|
-
tx.removeSync(fileNode.
|
|
776
|
+
tx.removeSync(fileNode.data);
|
|
799
777
|
tx.removeSync(fileIno);
|
|
800
778
|
}
|
|
801
779
|
// Success.
|