@zenfs/core 1.8.8 → 1.9.1

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.
Files changed (116) hide show
  1. package/dist/backends/backend.d.ts +1 -1
  2. package/dist/backends/backend.js +7 -4
  3. package/dist/backends/fetch.d.ts +23 -32
  4. package/dist/backends/fetch.js +94 -134
  5. package/dist/backends/index.d.ts +1 -4
  6. package/dist/backends/index.js +1 -4
  7. package/dist/backends/memory.d.ts +7 -5
  8. package/dist/backends/memory.js +6 -4
  9. package/dist/backends/overlay.d.ts +4 -5
  10. package/dist/backends/overlay.js +16 -20
  11. package/dist/backends/passthrough.d.ts +3 -3
  12. package/dist/backends/passthrough.js +4 -6
  13. package/dist/backends/port/fs.d.ts +4 -5
  14. package/dist/backends/port/fs.js +7 -12
  15. package/dist/backends/port/rpc.d.ts +1 -1
  16. package/dist/backends/port/rpc.js +15 -13
  17. package/dist/backends/store/fs.d.ts +51 -40
  18. package/dist/backends/store/fs.js +347 -241
  19. package/dist/backends/store/map.d.ts +41 -0
  20. package/dist/backends/store/map.js +45 -0
  21. package/dist/backends/store/simple.d.ts +10 -58
  22. package/dist/backends/store/simple.js +8 -115
  23. package/dist/backends/store/store.d.ts +111 -44
  24. package/dist/backends/store/store.js +230 -38
  25. package/dist/config.d.ts +7 -3
  26. package/dist/config.js +17 -14
  27. package/dist/context.d.ts +1 -1
  28. package/dist/context.js +1 -1
  29. package/dist/index.d.ts +1 -5
  30. package/dist/index.js +1 -5
  31. package/dist/{devices.d.ts → internal/devices.d.ts} +4 -4
  32. package/dist/{devices.js → internal/devices.js} +18 -14
  33. package/dist/{file.d.ts → internal/file.d.ts} +3 -2
  34. package/dist/{file.js → internal/file.js} +17 -12
  35. package/dist/{backends/store → internal}/file_index.d.ts +13 -3
  36. package/dist/{backends/store → internal}/file_index.js +28 -5
  37. package/dist/{filesystem.d.ts → internal/filesystem.d.ts} +99 -32
  38. package/dist/internal/filesystem.js +83 -0
  39. package/dist/internal/index.d.ts +9 -0
  40. package/dist/internal/index.js +9 -0
  41. package/dist/internal/index_fs.d.ts +56 -0
  42. package/dist/internal/index_fs.js +188 -0
  43. package/dist/{backends/store → internal}/inode.d.ts +6 -1
  44. package/dist/{backends/store → internal}/inode.js +14 -6
  45. package/dist/internal/log.d.ts +132 -0
  46. package/dist/internal/log.js +177 -0
  47. package/dist/mixins/async.d.ts +2 -2
  48. package/dist/mixins/async.js +19 -16
  49. package/dist/mixins/mutexed.d.ts +9 -3
  50. package/dist/mixins/mutexed.js +22 -3
  51. package/dist/mixins/readonly.d.ts +2 -2
  52. package/dist/mixins/readonly.js +4 -3
  53. package/dist/mixins/shared.d.ts +1 -1
  54. package/dist/mixins/sync.d.ts +2 -2
  55. package/dist/stats.d.ts +2 -3
  56. package/dist/stats.js +7 -5
  57. package/dist/utils.d.ts +2 -15
  58. package/dist/utils.js +10 -47
  59. package/dist/vfs/async.d.ts +2 -2
  60. package/dist/vfs/async.js +3 -3
  61. package/dist/vfs/dir.js +1 -1
  62. package/dist/vfs/promises.d.ts +6 -6
  63. package/dist/vfs/promises.js +54 -49
  64. package/dist/vfs/shared.d.ts +3 -3
  65. package/dist/vfs/shared.js +16 -10
  66. package/dist/vfs/streams.js +1 -1
  67. package/dist/vfs/sync.d.ts +1 -2
  68. package/dist/vfs/sync.js +14 -15
  69. package/dist/vfs/types.d.ts +1 -0
  70. package/dist/vfs/watchers.d.ts +5 -1
  71. package/dist/vfs/watchers.js +16 -19
  72. package/package.json +3 -3
  73. package/readme.md +12 -12
  74. package/scripts/test.js +15 -3
  75. package/tests/backend/fetch.test.ts +49 -0
  76. package/tests/backend/port.test.ts +130 -0
  77. package/tests/common/context.test.ts +9 -4
  78. package/tests/common.ts +21 -3
  79. package/tests/data/image.jpg +0 -0
  80. package/tests/data/utf8.txt +1 -0
  81. package/tests/fetch/config.js +40 -0
  82. package/tests/fetch/fetch.ts +20 -0
  83. package/tests/fetch/run.sh +3 -3
  84. package/tests/fetch/{server.ts → server.js} +15 -11
  85. package/tests/fs/directory.test.ts +1 -1
  86. package/tests/fs/errors.test.ts +1 -1
  87. package/tests/fs/links.test.ts +1 -1
  88. package/tests/fs/open.test.ts +1 -1
  89. package/tests/fs/permissions.test.ts +2 -3
  90. package/tests/fs/rename.test.ts +1 -1
  91. package/tests/fs/stat.test.ts +1 -1
  92. package/tests/fs/times.test.ts +1 -1
  93. package/tests/fs/watch.test.ts +21 -22
  94. package/tests/fs/writeFile.test.ts +8 -7
  95. package/tests/readme.md +3 -3
  96. package/tests/setup/_overlay.ts +7 -0
  97. package/tests/setup/context.ts +2 -2
  98. package/tests/setup/index.ts +3 -3
  99. package/tests/setup/memory.ts +2 -2
  100. package/tests/setup/port.ts +2 -2
  101. package/tests/setup.ts +25 -5
  102. package/tests/tsconfig.json +3 -2
  103. package/dist/backends/store/index_fs.d.ts +0 -34
  104. package/dist/backends/store/index_fs.js +0 -67
  105. package/dist/filesystem.js +0 -52
  106. package/tests/fetch/cow+fetch.ts +0 -13
  107. package/tests/port/channel.test.ts +0 -39
  108. package/tests/port/config.test.ts +0 -30
  109. package/tests/port/remote.test.ts +0 -32
  110. package/tests/port/timeout.test.ts +0 -48
  111. /package/dist/{credentials.d.ts → internal/credentials.d.ts} +0 -0
  112. /package/dist/{credentials.js → internal/credentials.js} +0 -0
  113. /package/dist/{error.d.ts → internal/error.d.ts} +0 -0
  114. /package/dist/{error.js → internal/error.js} +0 -0
  115. /package/tests/{port → backend}/config.worker.js +0 -0
  116. /package/tests/{port → backend}/remote.worker.js +0 -0
@@ -50,16 +50,18 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
50
50
  var e = new Error(message);
51
51
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
52
  });
53
- import { randomInt, serialize } from 'utilium';
54
- import { Errno, ErrnoError } from '../../error.js';
55
- import { LazyFile } from '../../file.js';
56
- import { FileSystem } from '../../filesystem.js';
57
- import { _throw, canary, decodeDirListing, encodeDirListing, encodeUTF8, growBuffer } from '../../utils.js';
53
+ import { _throw, canary, serialize } from 'utilium';
54
+ import { extendBuffer } from 'utilium/buffer.js';
55
+ import { Errno, ErrnoError } from '../../internal/error.js';
56
+ import { LazyFile } from '../../internal/file.js';
57
+ import { Index } from '../../internal/file_index.js';
58
+ import { FileSystem } from '../../internal/filesystem.js';
59
+ import { __inode_sz, Inode, rootIno } from '../../internal/inode.js';
60
+ import { crit, debug, err, log_deprecated, notice, warn } from '../../internal/log.js';
61
+ import { decodeDirListing, encodeDirListing, encodeUTF8 } from '../../utils.js';
58
62
  import { S_IFDIR, S_IFREG, S_ISGID, S_ISUID, size_max } from '../../vfs/constants.js';
59
- import { basename, dirname, join, parse, resolve } from '../../vfs/path.js';
60
- import { Index } from './file_index.js';
61
- import { Inode, rootIno } from './inode.js';
62
- const maxInodeAllocTries = 5;
63
+ import { basename, dirname, join, parse, relative } from '../../vfs/path.js';
64
+ import { WrappedTransaction } from './store.js';
63
65
  /**
64
66
  * A file system which uses a key-value store.
65
67
  *
@@ -70,24 +72,85 @@ const maxInodeAllocTries = 5;
70
72
  * @internal
71
73
  */
72
74
  export class StoreFS extends FileSystem {
75
+ /**
76
+ * Gets the first path associated with an inode
77
+ */
78
+ _path(id) {
79
+ var _a;
80
+ const [path] = (_a = this._paths.get(id)) !== null && _a !== void 0 ? _a : [];
81
+ return path;
82
+ }
83
+ /**
84
+ * Add a inode/path pair
85
+ */
86
+ _add(ino, path) {
87
+ if (!this._paths.has(ino))
88
+ this._paths.set(ino, new Set());
89
+ this._paths.get(ino).add(path);
90
+ this._ids.set(path, ino);
91
+ }
92
+ /**
93
+ * Remove a inode/path pair
94
+ */
95
+ _remove(ino) {
96
+ var _a;
97
+ for (const path of (_a = this._paths.get(ino)) !== null && _a !== void 0 ? _a : []) {
98
+ this._ids.delete(path);
99
+ }
100
+ this._paths.delete(ino);
101
+ }
102
+ /**
103
+ * Move paths in the tables
104
+ */
105
+ _move(from, to) {
106
+ const toMove = [];
107
+ for (const [path, ino] of this._ids) {
108
+ const rel = relative(from, path);
109
+ if (rel.startsWith('..'))
110
+ continue;
111
+ let newKey = join(to, rel);
112
+ if (newKey.endsWith('/'))
113
+ newKey = newKey.slice(0, -1);
114
+ toMove.push({ oldKey: path, newKey, ino });
115
+ }
116
+ for (const { oldKey, newKey, ino } of toMove) {
117
+ this._ids.delete(oldKey);
118
+ this._ids.set(newKey, ino);
119
+ const p = this._paths.get(ino);
120
+ if (!p) {
121
+ warn('Missing paths in table for ino ' + ino);
122
+ continue;
123
+ }
124
+ p.delete(oldKey);
125
+ p.add(newKey);
126
+ }
127
+ }
73
128
  async ready() {
74
- if (this._initialized) {
129
+ if (this._initialized)
75
130
  return;
76
- }
131
+ this.checkRootSync();
77
132
  await this.checkRoot();
133
+ await this._populate();
78
134
  this._initialized = true;
79
135
  }
80
136
  constructor(store) {
81
- super();
137
+ var _a, _b;
138
+ super((_a = store.id) !== null && _a !== void 0 ? _a : 0x6b766673, store.name);
82
139
  this.store = store;
140
+ /**
141
+ * A map of paths to inode IDs
142
+ * @internal @hidden
143
+ */
144
+ this._ids = new Map([['/', 0]]);
145
+ /**
146
+ * A map of inode IDs to paths
147
+ * @internal @hidden
148
+ */
149
+ this._paths = new Map([[0, new Set('/')]]);
83
150
  this._initialized = false;
84
- }
85
- metadata() {
86
- return {
87
- ...super.metadata(),
88
- name: this.store.name,
89
- features: ['setid'],
90
- };
151
+ this.attributes.set('setid');
152
+ store._fs = this;
153
+ debug(this.name + ': supports features: ' + ((_b = this.store.flags) === null || _b === void 0 ? void 0 : _b.join(', ')));
91
154
  }
92
155
  /* node:coverage disable */
93
156
  /**
@@ -95,7 +158,7 @@ export class StoreFS extends FileSystem {
95
158
  * @deprecated
96
159
  */
97
160
  async empty() {
98
- await this.store.clear();
161
+ log_deprecated('StoreFS#empty');
99
162
  // Root always exists.
100
163
  await this.checkRoot();
101
164
  }
@@ -104,7 +167,7 @@ export class StoreFS extends FileSystem {
104
167
  * @deprecated
105
168
  */
106
169
  emptySync() {
107
- this.store.clearSync();
170
+ log_deprecated('StoreFS#emptySync');
108
171
  // Root always exists.
109
172
  this.checkRootSync();
110
173
  }
@@ -116,9 +179,10 @@ export class StoreFS extends FileSystem {
116
179
  async loadIndex(index) {
117
180
  const env_1 = { stack: [], error: void 0, hasError: false };
118
181
  try {
119
- const tx = __addDisposableResource(env_1, this.store.transaction(), true);
182
+ const tx = __addDisposableResource(env_1, this.transaction(), true);
120
183
  const dirs = index.directories();
121
184
  for (const [path, inode] of index) {
185
+ this._add(inode.ino, path);
122
186
  await tx.set(inode.ino, serialize(inode));
123
187
  if (dirs.has(path))
124
188
  await tx.set(inode.data, encodeDirListing(dirs.get(path)));
@@ -142,9 +206,10 @@ export class StoreFS extends FileSystem {
142
206
  loadIndexSync(index) {
143
207
  const env_2 = { stack: [], error: void 0, hasError: false };
144
208
  try {
145
- const tx = __addDisposableResource(env_2, this.store.transaction(), false);
209
+ const tx = __addDisposableResource(env_2, this.transaction(), false);
146
210
  const dirs = index.directories();
147
211
  for (const [path, inode] of index) {
212
+ this._add(inode.ino, path);
148
213
  tx.setSync(inode.ino, serialize(inode));
149
214
  if (dirs.has(path))
150
215
  tx.setSync(inode.data, encodeDirListing(dirs.get(path)));
@@ -160,18 +225,19 @@ export class StoreFS extends FileSystem {
160
225
  }
161
226
  }
162
227
  async createIndex() {
228
+ var _a;
163
229
  const env_3 = { stack: [], error: void 0, hasError: false };
164
230
  try {
165
231
  const index = new Index();
166
- const tx = __addDisposableResource(env_3, this.store.transaction(), true);
232
+ const tx = __addDisposableResource(env_3, this.transaction(), true);
167
233
  const queue = [['/', 0]];
168
- const silence = canary();
234
+ const silence = canary(ErrnoError.With('EDEADLK'));
169
235
  while (queue.length) {
170
236
  const [path, ino] = queue.shift();
171
237
  const inode = new Inode(await tx.get(ino));
172
238
  index.set(path, inode);
173
239
  if (inode.mode & S_IFDIR) {
174
- const dir = decodeDirListing(await tx.get(inode.data));
240
+ const dir = decodeDirListing((_a = (await tx.get(inode.data))) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENODATA', path)));
175
241
  for (const [name, id] of Object.entries(dir)) {
176
242
  queue.push([join(path, name), id]);
177
243
  }
@@ -191,18 +257,19 @@ export class StoreFS extends FileSystem {
191
257
  }
192
258
  }
193
259
  createIndexSync() {
260
+ var _a;
194
261
  const env_4 = { stack: [], error: void 0, hasError: false };
195
262
  try {
196
263
  const index = new Index();
197
- const tx = __addDisposableResource(env_4, this.store.transaction(), false);
264
+ const tx = __addDisposableResource(env_4, this.transaction(), false);
198
265
  const queue = [['/', 0]];
199
- const silence = canary();
266
+ const silence = canary(ErrnoError.With('EDEADLK'));
200
267
  while (queue.length) {
201
268
  const [path, ino] = queue.shift();
202
269
  const inode = new Inode(tx.getSync(ino));
203
270
  index.set(path, inode);
204
271
  if (inode.mode & S_IFDIR) {
205
- const dir = decodeDirListing(tx.getSync(inode.data));
272
+ const dir = decodeDirListing((_a = tx.getSync(inode.data)) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENODATA', path)));
206
273
  for (const [name, id] of Object.entries(dir)) {
207
274
  queue.push([join(path, name), id]);
208
275
  }
@@ -226,36 +293,35 @@ export class StoreFS extends FileSystem {
226
293
  var _a, _b, _c;
227
294
  const env_5 = { stack: [], error: void 0, hasError: false };
228
295
  try {
229
- const tx = __addDisposableResource(env_5, this.store.transaction(), true);
296
+ const tx = __addDisposableResource(env_5, this.transaction(), true);
230
297
  const _old = parse(oldPath), _new = parse(newPath),
231
298
  // Remove oldPath from parent's directory listing.
232
- oldDirNode = await this.findInode(tx, _old.dir, 'rename'), oldDirList = decodeDirListing((_a = (await tx.get(oldDirNode.data))) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENOENT', _old.dir, 'rename')));
233
- if (!oldDirList[_old.base]) {
299
+ oldDirNode = await this.findInode(tx, _old.dir, 'rename'), oldDirList = decodeDirListing((_a = (await tx.get(oldDirNode.data))) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENODATA', _old.dir, 'rename')));
300
+ if (!oldDirList[_old.base])
234
301
  throw ErrnoError.With('ENOENT', oldPath, 'rename');
235
- }
236
302
  const ino = oldDirList[_old.base];
303
+ if (ino != this._ids.get(oldPath))
304
+ err(`Ino mismatch while renaming ${oldPath} to ${newPath}`);
237
305
  delete oldDirList[_old.base];
238
306
  /*
239
307
  Can't move a folder inside itself.
240
308
  This ensures that the check passes only if `oldPath` is a subpath of `_new.dir`.
241
309
  We append '/' to avoid matching folders that are a substring of the bottom-most folder in the path.
242
310
  */
243
- if ((_new.dir + '/').indexOf(oldPath + '/') === 0) {
311
+ if ((_new.dir + '/').startsWith(oldPath + '/'))
244
312
  throw new ErrnoError(Errno.EBUSY, _old.dir);
245
- }
246
313
  // Add newPath to parent's directory listing.
247
314
  const sameParent = _new.dir == _old.dir;
248
315
  // Prevent us from re-grabbing the same directory listing, which still contains `old_path.base.`
249
316
  const newDirNode = sameParent ? oldDirNode : await this.findInode(tx, _new.dir, 'rename');
250
317
  const newDirList = sameParent
251
318
  ? oldDirList
252
- : decodeDirListing((_b = (await tx.get(newDirNode.data))) !== null && _b !== void 0 ? _b : _throw(ErrnoError.With('ENOENT', _new.dir, 'rename')));
319
+ : decodeDirListing((_b = (await tx.get(newDirNode.data))) !== null && _b !== void 0 ? _b : _throw(ErrnoError.With('ENODATA', _new.dir, 'rename')));
253
320
  if (newDirList[_new.base]) {
254
321
  // If it's a file, delete it, if it's a directory, throw a permissions error.
255
322
  const existing = new Inode((_c = (await tx.get(newDirList[_new.base]))) !== null && _c !== void 0 ? _c : _throw(ErrnoError.With('ENOENT', newPath, 'rename')));
256
- if (!existing.toStats().isFile()) {
323
+ if (!existing.toStats().isFile())
257
324
  throw ErrnoError.With('EPERM', newPath, 'rename');
258
- }
259
325
  await tx.remove(existing.data);
260
326
  await tx.remove(newDirList[_new.base]);
261
327
  }
@@ -264,6 +330,7 @@ export class StoreFS extends FileSystem {
264
330
  await tx.set(oldDirNode.data, encodeDirListing(oldDirList));
265
331
  await tx.set(newDirNode.data, encodeDirListing(newDirList));
266
332
  await tx.commit();
333
+ this._move(oldPath, newPath);
267
334
  }
268
335
  catch (e_5) {
269
336
  env_5.error = e_5;
@@ -279,36 +346,35 @@ export class StoreFS extends FileSystem {
279
346
  var _a, _b, _c;
280
347
  const env_6 = { stack: [], error: void 0, hasError: false };
281
348
  try {
282
- const tx = __addDisposableResource(env_6, this.store.transaction(), false);
349
+ const tx = __addDisposableResource(env_6, this.transaction(), false);
283
350
  const _old = parse(oldPath), _new = parse(newPath),
284
351
  // Remove oldPath from parent's directory listing.
285
- oldDirNode = this.findInodeSync(tx, _old.dir, 'rename'), oldDirList = decodeDirListing((_a = tx.getSync(oldDirNode.data)) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENOENT', _old.dir, 'rename')));
286
- if (!oldDirList[_old.base]) {
352
+ oldDirNode = this.findInodeSync(tx, _old.dir, 'rename'), oldDirList = decodeDirListing((_a = tx.getSync(oldDirNode.data)) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENODATA', _old.dir, 'rename')));
353
+ if (!oldDirList[_old.base])
287
354
  throw ErrnoError.With('ENOENT', oldPath, 'rename');
288
- }
289
355
  const ino = oldDirList[_old.base];
356
+ if (ino != this._ids.get(oldPath))
357
+ err(`Ino mismatch while renaming ${oldPath} to ${newPath}`);
290
358
  delete oldDirList[_old.base];
291
359
  /*
292
360
  Can't move a folder inside itself.
293
361
  This ensures that the check passes only if `oldPath` is a subpath of `_new.dir`.
294
362
  We append '/' to avoid matching folders that are a substring of the bottom-most folder in the path.
295
363
  */
296
- if ((_new.dir + '/').indexOf(oldPath + '/') == 0) {
364
+ if ((_new.dir + '/').startsWith(oldPath + '/'))
297
365
  throw new ErrnoError(Errno.EBUSY, _old.dir);
298
- }
299
366
  // Add newPath to parent's directory listing.
300
367
  const sameParent = _new.dir === _old.dir;
301
368
  // Prevent us from re-grabbing the same directory listing, which still contains `old_path.base.`
302
369
  const newDirNode = sameParent ? oldDirNode : this.findInodeSync(tx, _new.dir, 'rename');
303
370
  const newDirList = sameParent
304
371
  ? oldDirList
305
- : decodeDirListing((_b = tx.getSync(newDirNode.data)) !== null && _b !== void 0 ? _b : _throw(ErrnoError.With('ENOENT', _new.dir, 'rename')));
372
+ : decodeDirListing((_b = tx.getSync(newDirNode.data)) !== null && _b !== void 0 ? _b : _throw(ErrnoError.With('ENODATA', _new.dir, 'rename')));
306
373
  if (newDirList[_new.base]) {
307
374
  // If it's a file, delete it, if it's a directory, throw a permissions error.
308
375
  const existing = new Inode((_c = tx.getSync(newDirList[_new.base])) !== null && _c !== void 0 ? _c : _throw(ErrnoError.With('ENOENT', newPath, 'rename')));
309
- if (!existing.toStats().isFile()) {
376
+ if (!existing.toStats().isFile())
310
377
  throw ErrnoError.With('EPERM', newPath, 'rename');
311
- }
312
378
  tx.removeSync(existing.data);
313
379
  tx.removeSync(newDirList[_new.base]);
314
380
  }
@@ -317,6 +383,7 @@ export class StoreFS extends FileSystem {
317
383
  tx.setSync(oldDirNode.data, encodeDirListing(oldDirList));
318
384
  tx.setSync(newDirNode.data, encodeDirListing(newDirList));
319
385
  tx.commitSync();
386
+ this._move(oldPath, newPath);
320
387
  }
321
388
  catch (e_6) {
322
389
  env_6.error = e_6;
@@ -329,7 +396,7 @@ export class StoreFS extends FileSystem {
329
396
  async stat(path) {
330
397
  const env_7 = { stack: [], error: void 0, hasError: false };
331
398
  try {
332
- const tx = __addDisposableResource(env_7, this.store.transaction(), true);
399
+ const tx = __addDisposableResource(env_7, this.transaction(), true);
333
400
  return (await this.findInode(tx, path, 'stat')).toStats();
334
401
  }
335
402
  catch (e_7) {
@@ -345,7 +412,7 @@ export class StoreFS extends FileSystem {
345
412
  statSync(path) {
346
413
  const env_8 = { stack: [], error: void 0, hasError: false };
347
414
  try {
348
- const tx = __addDisposableResource(env_8, this.store.transaction(), false);
415
+ const tx = __addDisposableResource(env_8, this.transaction(), false);
349
416
  return this.findInodeSync(tx, path, 'stat').toStats();
350
417
  }
351
418
  catch (e_8) {
@@ -357,19 +424,18 @@ export class StoreFS extends FileSystem {
357
424
  }
358
425
  }
359
426
  async createFile(path, flag, mode, options) {
360
- const node = await this.commitNew(path, S_IFREG, { mode, ...options }, new Uint8Array(), 'createFile');
427
+ const node = await this.commitNew(path, { mode: mode | S_IFREG, ...options }, new Uint8Array(), 'createFile');
361
428
  return new LazyFile(this, path, flag, node.toStats());
362
429
  }
363
430
  createFileSync(path, flag, mode, options) {
364
- const node = this.commitNewSync(path, S_IFREG, { mode, ...options }, new Uint8Array(), 'createFile');
431
+ const node = this.commitNewSync(path, { mode: mode | S_IFREG, ...options }, new Uint8Array(), 'createFile');
365
432
  return new LazyFile(this, path, flag, node.toStats());
366
433
  }
367
434
  async openFile(path, flag) {
368
435
  const env_9 = { stack: [], error: void 0, hasError: false };
369
436
  try {
370
- const tx = __addDisposableResource(env_9, this.store.transaction(), true);
437
+ const tx = __addDisposableResource(env_9, this.transaction(), true);
371
438
  const node = await this.findInode(tx, path, 'openFile');
372
- //const data = (await tx.get(node.data)) ?? _throw(ErrnoError.With('ENODATA', path, 'openFile'));
373
439
  return new LazyFile(this, path, flag, node.toStats());
374
440
  }
375
441
  catch (e_9) {
@@ -385,9 +451,8 @@ export class StoreFS extends FileSystem {
385
451
  openFileSync(path, flag) {
386
452
  const env_10 = { stack: [], error: void 0, hasError: false };
387
453
  try {
388
- const tx = __addDisposableResource(env_10, this.store.transaction(), false);
454
+ const tx = __addDisposableResource(env_10, this.transaction(), false);
389
455
  const node = this.findInodeSync(tx, path, 'openFile');
390
- //const data = tx.getSync(node.data) ?? _throw(ErrnoError.With('ENODATA', path, 'openFile'));
391
456
  return new LazyFile(this, path, flag, node.toStats());
392
457
  }
393
458
  catch (e_10) {
@@ -399,34 +464,34 @@ export class StoreFS extends FileSystem {
399
464
  }
400
465
  }
401
466
  async unlink(path) {
402
- return this.remove(path, false, 'unlink');
467
+ return this.remove(path, false);
403
468
  }
404
469
  unlinkSync(path) {
405
- this.removeSync(path, false, 'unlink');
470
+ this.removeSync(path, false);
406
471
  }
407
472
  async rmdir(path) {
408
473
  if ((await this.readdir(path)).length) {
409
474
  throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
410
475
  }
411
- await this.remove(path, true, 'rmdir');
476
+ await this.remove(path, true);
412
477
  }
413
478
  rmdirSync(path) {
414
479
  if (this.readdirSync(path).length) {
415
480
  throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
416
481
  }
417
- this.removeSync(path, true, 'rmdir');
482
+ this.removeSync(path, true);
418
483
  }
419
484
  async mkdir(path, mode, options) {
420
- await this.commitNew(path, S_IFDIR, { mode, ...options }, encodeUTF8('{}'), 'mkdir');
485
+ await this.commitNew(path, { mode: mode | S_IFDIR, ...options }, encodeUTF8('{}'), 'mkdir');
421
486
  }
422
487
  mkdirSync(path, mode, options) {
423
- this.commitNewSync(path, S_IFDIR, { mode, ...options }, encodeUTF8('{}'), 'mkdir');
488
+ this.commitNewSync(path, { mode: mode | S_IFDIR, ...options }, encodeUTF8('{}'), 'mkdir');
424
489
  }
425
490
  async readdir(path) {
426
491
  var _a;
427
492
  const env_11 = { stack: [], error: void 0, hasError: false };
428
493
  try {
429
- const tx = __addDisposableResource(env_11, this.store.transaction(), true);
494
+ const tx = __addDisposableResource(env_11, this.transaction(), true);
430
495
  const node = await this.findInode(tx, path, 'readdir');
431
496
  return Object.keys(decodeDirListing((_a = (await tx.get(node.data))) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENOENT', path, 'readdir'))));
432
497
  }
@@ -444,7 +509,7 @@ export class StoreFS extends FileSystem {
444
509
  var _a;
445
510
  const env_12 = { stack: [], error: void 0, hasError: false };
446
511
  try {
447
- const tx = __addDisposableResource(env_12, this.store.transaction(), false);
512
+ const tx = __addDisposableResource(env_12, this.transaction(), false);
448
513
  const node = this.findInodeSync(tx, path, 'readdir');
449
514
  return Object.keys(decodeDirListing((_a = tx.getSync(node.data)) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENOENT', path, 'readdir'))));
450
515
  }
@@ -463,12 +528,14 @@ export class StoreFS extends FileSystem {
463
528
  async sync(path, data, metadata) {
464
529
  const env_13 = { stack: [], error: void 0, hasError: false };
465
530
  try {
466
- const tx = __addDisposableResource(env_13, this.store.transaction(), true);
531
+ const tx = __addDisposableResource(env_13, this.transaction(), true);
467
532
  const inode = await this.findInode(tx, path, 'sync');
468
533
  if (data)
469
534
  await tx.set(inode.data, data);
470
- if (inode.update(metadata))
535
+ if (inode.update(metadata)) {
536
+ this._add(inode.ino, path);
471
537
  await tx.set(inode.ino, serialize(inode));
538
+ }
472
539
  await tx.commit();
473
540
  }
474
541
  catch (e_13) {
@@ -488,12 +555,14 @@ export class StoreFS extends FileSystem {
488
555
  syncSync(path, data, metadata) {
489
556
  const env_14 = { stack: [], error: void 0, hasError: false };
490
557
  try {
491
- const tx = __addDisposableResource(env_14, this.store.transaction(), false);
558
+ const tx = __addDisposableResource(env_14, this.transaction(), false);
492
559
  const inode = this.findInodeSync(tx, path, 'sync');
493
560
  if (data)
494
561
  tx.setSync(inode.data, data);
495
- if (inode.update(metadata))
562
+ if (inode.update(metadata)) {
563
+ this._add(inode.ino, path);
496
564
  tx.setSync(inode.ino, serialize(inode));
565
+ }
497
566
  tx.commitSync();
498
567
  }
499
568
  catch (e_14) {
@@ -508,14 +577,15 @@ export class StoreFS extends FileSystem {
508
577
  var _a;
509
578
  const env_15 = { stack: [], error: void 0, hasError: false };
510
579
  try {
511
- const tx = __addDisposableResource(env_15, this.store.transaction(), true);
580
+ const tx = __addDisposableResource(env_15, this.transaction(), true);
512
581
  const newDir = dirname(link), newDirNode = await this.findInode(tx, newDir, 'link'), listing = decodeDirListing((_a = (await tx.get(newDirNode.data))) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENOENT', newDir, 'link')));
513
582
  const inode = await this.findInode(tx, target, 'link');
514
583
  inode.nlink++;
515
584
  listing[basename(link)] = inode.ino;
516
- tx.setSync(inode.ino, serialize(inode));
517
- tx.setSync(newDirNode.data, encodeDirListing(listing));
518
- tx.commitSync();
585
+ this._add(inode.ino, link);
586
+ await tx.set(inode.ino, serialize(inode));
587
+ await tx.set(newDirNode.data, encodeDirListing(listing));
588
+ await tx.commit();
519
589
  }
520
590
  catch (e_15) {
521
591
  env_15.error = e_15;
@@ -531,11 +601,12 @@ export class StoreFS extends FileSystem {
531
601
  var _a;
532
602
  const env_16 = { stack: [], error: void 0, hasError: false };
533
603
  try {
534
- const tx = __addDisposableResource(env_16, this.store.transaction(), false);
604
+ const tx = __addDisposableResource(env_16, this.transaction(), false);
535
605
  const newDir = dirname(link), newDirNode = this.findInodeSync(tx, newDir, 'link'), listing = decodeDirListing((_a = tx.getSync(newDirNode.data)) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENOENT', newDir, 'link')));
536
606
  const inode = this.findInodeSync(tx, target, 'link');
537
607
  inode.nlink++;
538
608
  listing[basename(link)] = inode.ino;
609
+ this._add(inode.ino, link);
539
610
  tx.setSync(inode.ino, serialize(inode));
540
611
  tx.setSync(newDirNode.data, encodeDirListing(listing));
541
612
  tx.commitSync();
@@ -552,10 +623,15 @@ export class StoreFS extends FileSystem {
552
623
  var _a;
553
624
  const env_17 = { stack: [], error: void 0, hasError: false };
554
625
  try {
555
- const tx = __addDisposableResource(env_17, this.store.transaction(), true);
626
+ const tx = __addDisposableResource(env_17, this.transaction(), true);
556
627
  const inode = await this.findInode(tx, path, 'read');
557
- const data = (_a = (await tx.get(inode.data))) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENODATA', path, 'read'));
558
- buffer.set(data.subarray(offset, end));
628
+ if (inode.size == 0)
629
+ return;
630
+ const data = (_a = (await tx.get(inode.data, offset, end))) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENODATA', path, 'read'));
631
+ const _ = tx.flag('partial') ? data : data.subarray(offset, end);
632
+ if (_.byteLength > buffer.byteLength)
633
+ err(`Trying to place ${_.byteLength} bytes into a ${buffer.byteLength} byte buffer on read`);
634
+ buffer.set(_);
559
635
  }
560
636
  catch (e_17) {
561
637
  env_17.error = e_17;
@@ -571,10 +647,15 @@ export class StoreFS extends FileSystem {
571
647
  var _a;
572
648
  const env_18 = { stack: [], error: void 0, hasError: false };
573
649
  try {
574
- const tx = __addDisposableResource(env_18, this.store.transaction(), false);
650
+ const tx = __addDisposableResource(env_18, this.transaction(), false);
575
651
  const inode = this.findInodeSync(tx, path, 'read');
576
- const data = (_a = tx.getSync(inode.data)) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENODATA', path, 'read'));
577
- buffer.set(data.subarray(offset, end));
652
+ if (inode.size == 0)
653
+ return;
654
+ const data = (_a = tx.getSync(inode.data, offset, end)) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENODATA', path, 'read'));
655
+ const _ = tx.flag('partial') ? data : data.subarray(offset, end);
656
+ if (_.byteLength > buffer.byteLength)
657
+ err(`Trying to place ${_.byteLength} bytes into a ${buffer.byteLength} byte buffer on read`);
658
+ buffer.set(_);
578
659
  }
579
660
  catch (e_18) {
580
661
  env_18.error = e_18;
@@ -585,15 +666,21 @@ export class StoreFS extends FileSystem {
585
666
  }
586
667
  }
587
668
  async write(path, data, offset) {
669
+ var _a;
588
670
  const env_19 = { stack: [], error: void 0, hasError: false };
589
671
  try {
590
- const tx = __addDisposableResource(env_19, this.store.transaction(), true);
672
+ const tx = __addDisposableResource(env_19, this.transaction(), true);
591
673
  const inode = await this.findInode(tx, path, 'write');
592
- const buffer = growBuffer(await tx.get(inode.data), offset + data.byteLength);
593
- buffer.set(data, offset);
594
- inode.update({ mtimeMs: Date.now(), size: buffer.byteLength });
674
+ let buffer = data;
675
+ if (!tx.flag('partial')) {
676
+ buffer = extendBuffer((_a = (await tx.get(inode.data))) !== null && _a !== void 0 ? _a : new Uint8Array(), offset + data.byteLength);
677
+ buffer.set(data, offset);
678
+ offset = 0;
679
+ }
680
+ await tx.set(inode.data, buffer, offset);
681
+ inode.update({ mtimeMs: Date.now(), size: Math.max(inode.size, data.byteLength + offset) });
682
+ this._add(inode.ino, path);
595
683
  await tx.set(inode.ino, serialize(inode));
596
- await tx.set(inode.data, buffer);
597
684
  await tx.commit();
598
685
  }
599
686
  catch (e_19) {
@@ -607,15 +694,21 @@ export class StoreFS extends FileSystem {
607
694
  }
608
695
  }
609
696
  writeSync(path, data, offset) {
697
+ var _a;
610
698
  const env_20 = { stack: [], error: void 0, hasError: false };
611
699
  try {
612
- const tx = __addDisposableResource(env_20, this.store.transaction(), false);
700
+ const tx = __addDisposableResource(env_20, this.transaction(), false);
613
701
  const inode = this.findInodeSync(tx, path, 'write');
614
- const buffer = growBuffer(tx.getSync(inode.data), offset + data.byteLength);
615
- buffer.set(data, offset);
616
- inode.update({ mtimeMs: Date.now(), size: buffer.byteLength });
702
+ let buffer = data;
703
+ if (!tx.flag('partial')) {
704
+ buffer = extendBuffer((_a = tx.getSync(inode.data)) !== null && _a !== void 0 ? _a : new Uint8Array(), offset + data.byteLength);
705
+ buffer.set(data, offset);
706
+ offset = 0;
707
+ }
708
+ tx.setSync(inode.data, buffer, offset);
709
+ inode.update({ mtimeMs: Date.now(), size: Math.max(inode.size, data.byteLength + offset) });
710
+ this._add(inode.ino, path);
617
711
  tx.setSync(inode.ino, serialize(inode));
618
- tx.setSync(inode.data, buffer);
619
712
  tx.commitSync();
620
713
  }
621
714
  catch (e_20) {
@@ -626,21 +719,25 @@ export class StoreFS extends FileSystem {
626
719
  __disposeResources(env_20);
627
720
  }
628
721
  }
722
+ /**
723
+ * Wraps a transaction
724
+ * @internal @hidden
725
+ */
726
+ transaction() {
727
+ return new WrappedTransaction(this.store.transaction(), this);
728
+ }
629
729
  /**
630
730
  * Checks if the root directory exists. Creates it if it doesn't.
631
731
  */
632
732
  async checkRoot() {
633
733
  const env_21 = { stack: [], error: void 0, hasError: false };
634
734
  try {
635
- const tx = __addDisposableResource(env_21, this.store.transaction(), true);
735
+ const tx = __addDisposableResource(env_21, this.transaction(), true);
636
736
  if (await tx.get(rootIno))
637
737
  return;
638
- // Create new inode. o777, owned by root:root
639
- const inode = new Inode();
640
- inode.ino = rootIno;
641
- inode.mode = 0o777 | S_IFDIR;
642
- // If the root doesn't exist, the first random ID shouldn't exist either.
738
+ const inode = new Inode({ ino: rootIno, data: 1, mode: 0o777 | S_IFDIR });
643
739
  await tx.set(inode.data, encodeUTF8('{}'));
740
+ this._add(rootIno, '/');
644
741
  await tx.set(rootIno, serialize(inode));
645
742
  await tx.commit();
646
743
  }
@@ -660,15 +757,12 @@ export class StoreFS extends FileSystem {
660
757
  checkRootSync() {
661
758
  const env_22 = { stack: [], error: void 0, hasError: false };
662
759
  try {
663
- const tx = __addDisposableResource(env_22, this.store.transaction(), false);
760
+ const tx = __addDisposableResource(env_22, this.transaction(), false);
664
761
  if (tx.getSync(rootIno))
665
762
  return;
666
- // Create new inode, mode o777, owned by root:root
667
- const inode = new Inode();
668
- inode.ino = rootIno;
669
- inode.mode = 0o777 | S_IFDIR;
670
- // If the root doesn't exist, the first random ID shouldn't exist either.
763
+ const inode = new Inode({ ino: rootIno, data: 1, mode: 0o777 | S_IFDIR });
671
764
  tx.setSync(inode.data, encodeUTF8('{}'));
765
+ this._add(rootIno, '/');
672
766
  tx.setSync(rootIno, serialize(inode));
673
767
  tx.commitSync();
674
768
  }
@@ -681,64 +775,90 @@ export class StoreFS extends FileSystem {
681
775
  }
682
776
  }
683
777
  /**
684
- * Helper function for findINode.
685
- * @param parent The parent directory of the file we are attempting to find.
686
- * @param filename The filename of the inode we are attempting to find, minus
687
- * the parent.
688
- */
689
- async _findInode(tx, path, syscall, visited = new Set()) {
690
- var _a, _b;
691
- if (visited.has(path)) {
692
- throw new ErrnoError(Errno.EIO, 'Infinite loop detected while finding inode', path);
693
- }
694
- visited.add(path);
695
- if (path == '/') {
696
- return rootIno;
697
- }
698
- const { dir: parent, base: filename } = parse(path);
699
- const inode = parent == '/'
700
- ? new Inode((_a = (await tx.get(rootIno))) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENOENT', parent, syscall)))
701
- : await this.findInode(tx, parent, syscall, visited);
702
- const dirList = decodeDirListing((_b = (await tx.get(inode.data))) !== null && _b !== void 0 ? _b : _throw(ErrnoError.With('ENODATA', parent, syscall)));
703
- if (!(filename in dirList)) {
704
- throw ErrnoError.With('ENOENT', resolve(parent, filename), syscall);
705
- }
706
- return dirList[filename];
707
- }
708
- /**
709
- * Helper function for findINode.
710
- * @param parent The parent directory of the file we are attempting to find.
711
- * @param filename The filename of the inode we are attempting to find, minus
712
- * the parent.
713
- * @return string The ID of the file's inode in the file system.
778
+ * Populates the `_ids` and `_paths` maps with all existing files stored in the underlying `Store`.
714
779
  */
715
- _findInodeSync(tx, path, syscall, visited = new Set()) {
716
- var _a, _b;
717
- if (visited.has(path)) {
718
- throw new ErrnoError(Errno.EIO, 'Infinite loop detected while finding inode', path);
780
+ async _populate() {
781
+ const env_23 = { stack: [], error: void 0, hasError: false };
782
+ try {
783
+ if (this._initialized) {
784
+ warn('Attempted to populate tables after initialization');
785
+ return;
786
+ }
787
+ debug('Populating tables with existing store metadata.');
788
+ const tx = __addDisposableResource(env_23, this.transaction(), true);
789
+ const rootData = await tx.get(rootIno);
790
+ if (!rootData) {
791
+ notice('Store does not have a root inode.');
792
+ const inode = new Inode({ ino: rootIno, data: 1, mode: 0o777 | S_IFDIR });
793
+ await tx.set(inode.data, encodeUTF8('{}'));
794
+ this._add(rootIno, '/');
795
+ await tx.set(rootIno, serialize(inode));
796
+ await tx.commit();
797
+ return;
798
+ }
799
+ if (rootData.length != __inode_sz) {
800
+ crit('Store contains an invalid root inode. Refusing to populate tables.');
801
+ return;
802
+ }
803
+ // Keep track of directories we have already traversed to avoid loops
804
+ const visitedDirectories = new Set();
805
+ let i = 0;
806
+ // Start BFS from root
807
+ const queue = [['/', rootIno]];
808
+ while (queue.length > 0) {
809
+ i++;
810
+ const [path, ino] = queue.shift();
811
+ this._add(ino, path);
812
+ // Get the inode data from the store
813
+ const inodeData = await tx.get(ino);
814
+ if (!inodeData) {
815
+ warn('Store is missing data for inode: ' + ino);
816
+ continue;
817
+ }
818
+ if (inodeData.length != __inode_sz) {
819
+ warn(`Invalid inode size for ino ${ino}: ${inodeData.length}`);
820
+ continue;
821
+ }
822
+ // Parse the raw data into our Inode object
823
+ const inode = new Inode(inodeData);
824
+ // If it is a directory and not yet visited, read its directory listing
825
+ if ((inode.mode & S_IFDIR) != S_IFDIR || visitedDirectories.has(ino)) {
826
+ continue;
827
+ }
828
+ visitedDirectories.add(ino);
829
+ // Grab the directory listing from the store
830
+ const dirData = await tx.get(inode.data);
831
+ if (!dirData) {
832
+ warn('Store is missing directory data: ' + inode.data);
833
+ continue;
834
+ }
835
+ const dirListing = decodeDirListing(dirData);
836
+ for (const [entryName, childIno] of Object.entries(dirListing)) {
837
+ queue.push([join(path, entryName), childIno]);
838
+ }
839
+ }
840
+ debug(`Added ${i} existing inode(s) from store`);
719
841
  }
720
- visited.add(path);
721
- if (path == '/') {
722
- return rootIno;
842
+ catch (e_23) {
843
+ env_23.error = e_23;
844
+ env_23.hasError = true;
723
845
  }
724
- const { dir: parent, base: filename } = parse(path);
725
- const inode = parent == '/'
726
- ? new Inode((_a = tx.getSync(rootIno)) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENOENT', parent, syscall)))
727
- : this.findInodeSync(tx, parent, syscall, visited);
728
- const dir = decodeDirListing((_b = tx.getSync(inode.data)) !== null && _b !== void 0 ? _b : _throw(ErrnoError.With('ENODATA', parent, syscall)));
729
- if (!(filename in dir)) {
730
- throw ErrnoError.With('ENOENT', resolve(parent, filename), syscall);
846
+ finally {
847
+ const result_12 = __disposeResources(env_23);
848
+ if (result_12)
849
+ await result_12;
731
850
  }
732
- return dir[filename];
733
851
  }
734
852
  /**
735
853
  * Finds the Inode of `path`.
736
854
  * @param path The path to look up.
737
855
  * @todo memoize/cache
738
856
  */
739
- async findInode(tx, path, syscall, visited = new Set()) {
857
+ async findInode(tx, path, syscall) {
740
858
  var _a;
741
- const ino = await this._findInode(tx, path, syscall, visited);
859
+ const ino = this._ids.get(path);
860
+ if (ino === undefined)
861
+ throw ErrnoError.With('ENOENT', path, syscall);
742
862
  return new Inode((_a = (await tx.get(ino))) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENOENT', path, syscall)));
743
863
  }
744
864
  /**
@@ -747,76 +867,61 @@ export class StoreFS extends FileSystem {
747
867
  * @return The Inode of the path p.
748
868
  * @todo memoize/cache
749
869
  */
750
- findInodeSync(tx, path, syscall, visited = new Set()) {
870
+ findInodeSync(tx, path, syscall) {
751
871
  var _a;
752
- const ino = this._findInodeSync(tx, path, syscall, visited);
872
+ const ino = this._ids.get(path);
873
+ if (ino === undefined)
874
+ throw ErrnoError.With('ENOENT', path, syscall);
753
875
  return new Inode((_a = tx.getSync(ino)) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENOENT', path, syscall)));
754
876
  }
755
877
  /**
756
- * Adds a new node under a random ID. Retries before giving up in
757
- * the exceedingly unlikely chance that we try to reuse a random id.
758
- */
759
- async allocNew(tx, path, syscall) {
760
- for (let i = 0; i < maxInodeAllocTries; i++) {
761
- const ino = randomInt(0, size_max);
762
- if (await tx.get(ino)) {
763
- continue;
764
- }
765
- return ino;
766
- }
767
- throw new ErrnoError(Errno.ENOSPC, 'No IDs available', path, syscall);
768
- }
769
- /**
770
- * Creates a new node under a random ID. Retries before giving up in
771
- * the exceedingly unlikely chance that we try to reuse a random id.
772
- * @return The ino that the data was stored under.
878
+ * Allocates a new ID and adds the ID/path
773
879
  */
774
- allocNewSync(tx, path, syscall) {
775
- for (let i = 0; i < maxInodeAllocTries; i++) {
776
- const ino = randomInt(0, size_max);
777
- if (tx.getSync(ino)) {
778
- continue;
779
- }
780
- return ino;
781
- }
782
- throw new ErrnoError(Errno.ENOSPC, 'No IDs available', path, syscall);
880
+ allocNew(path, syscall) {
881
+ var _a;
882
+ (_a = this._lastID) !== null && _a !== void 0 ? _a : (this._lastID = Math.max(...this._paths.keys()));
883
+ this._lastID += 2;
884
+ const id = this._lastID;
885
+ if (id > size_max)
886
+ throw err(new ErrnoError(Errno.ENOSPC, 'No IDs available', path, syscall), { fs: this });
887
+ this._add(id, path);
888
+ return id;
783
889
  }
784
890
  /**
785
891
  * Commits a new file (well, a FILE or a DIRECTORY) to the file system with `mode`.
786
892
  * Note: This will commit the transaction.
787
893
  * @param path The path to the new file.
788
- * @param type The type of the new file.
789
- * @param mode The mode to create the new file with.
894
+ * @param options The options to create the new file with.
790
895
  * @param data The data to store at the file's data node.
791
896
  */
792
- async commitNew(path, type, options, data, syscall) {
897
+ async commitNew(path, options, data, syscall) {
793
898
  var _a;
794
- const env_23 = { stack: [], error: void 0, hasError: false };
899
+ const env_24 = { stack: [], error: void 0, hasError: false };
795
900
  try {
796
901
  /*
797
902
  The root always exists.
798
903
  If we don't check this prior to taking steps below,
799
904
  we will create a file with name '' in root if path is '/'.
800
905
  */
801
- if (path == '/') {
906
+ if (path == '/')
802
907
  throw ErrnoError.With('EEXIST', path, syscall);
803
- }
804
- const tx = __addDisposableResource(env_23, this.store.transaction(), true);
908
+ const tx = __addDisposableResource(env_24, this.transaction(), true);
805
909
  const { dir: parentPath, base: fname } = parse(path);
806
910
  const parent = await this.findInode(tx, parentPath, syscall);
807
911
  const listing = decodeDirListing((_a = (await tx.get(parent.data))) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENOENT', parentPath, syscall)));
808
912
  // Check if file already exists.
809
- if (listing[fname]) {
913
+ if (listing[fname])
810
914
  throw ErrnoError.With('EEXIST', path, syscall);
811
- }
915
+ const id = this.allocNew(path, syscall);
812
916
  // Commit data.
813
- const inode = new Inode();
814
- inode.ino = await this.allocNew(tx, path, syscall);
815
- inode.data = await this.allocNew(tx, path, syscall);
816
- inode.mode = options.mode | type;
817
- inode.uid = parent.mode & S_ISUID ? parent.uid : options.uid;
818
- inode.gid = parent.mode & S_ISGID ? parent.gid : options.gid;
819
- inode.size = data.length;
917
+ const inode = new Inode({
918
+ ino: id,
919
+ data: id + 1,
920
+ mode: options.mode,
921
+ size: data.byteLength,
922
+ uid: parent.mode & S_ISUID ? parent.uid : options.uid,
923
+ gid: parent.mode & S_ISGID ? parent.gid : options.gid,
924
+ });
820
925
  await tx.set(inode.ino, serialize(inode));
821
926
  await tx.set(inode.data, data);
822
927
  // Update and commit parent directory listing.
@@ -825,53 +930,52 @@ export class StoreFS extends FileSystem {
825
930
  await tx.commit();
826
931
  return inode;
827
932
  }
828
- catch (e_23) {
829
- env_23.error = e_23;
830
- env_23.hasError = true;
933
+ catch (e_24) {
934
+ env_24.error = e_24;
935
+ env_24.hasError = true;
831
936
  }
832
937
  finally {
833
- const result_12 = __disposeResources(env_23);
834
- if (result_12)
835
- await result_12;
938
+ const result_13 = __disposeResources(env_24);
939
+ if (result_13)
940
+ await result_13;
836
941
  }
837
942
  }
838
943
  /**
839
944
  * Commits a new file (well, a FILE or a DIRECTORY) to the file system with `mode`.
840
945
  * Note: This will commit the transaction.
841
946
  * @param path The path to the new file.
842
- * @param type The type of the new file.
843
- * @param mode The mode to create the new file with.
947
+ * @param options The options to create the new file with.
844
948
  * @param data The data to store at the file's data node.
845
949
  * @return The Inode for the new file.
846
950
  */
847
- commitNewSync(path, type, options, data, syscall) {
951
+ commitNewSync(path, options, data, syscall) {
848
952
  var _a;
849
- const env_24 = { stack: [], error: void 0, hasError: false };
953
+ const env_25 = { stack: [], error: void 0, hasError: false };
850
954
  try {
851
955
  /*
852
956
  The root always exists.
853
957
  If we don't check this prior to taking steps below,
854
958
  we will create a file with name '' in root if path is '/'.
855
959
  */
856
- if (path == '/') {
960
+ if (path == '/')
857
961
  throw ErrnoError.With('EEXIST', path, syscall);
858
- }
859
- const tx = __addDisposableResource(env_24, this.store.transaction(), false);
962
+ const tx = __addDisposableResource(env_25, this.transaction(), false);
860
963
  const { dir: parentPath, base: fname } = parse(path);
861
964
  const parent = this.findInodeSync(tx, parentPath, syscall);
862
965
  const listing = decodeDirListing((_a = tx.getSync(parent.data)) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENOENT', parentPath, syscall)));
863
966
  // Check if file already exists.
864
- if (listing[fname]) {
967
+ if (listing[fname])
865
968
  throw ErrnoError.With('EEXIST', path, syscall);
866
- }
969
+ const id = this.allocNew(path, syscall);
867
970
  // Commit data.
868
- const inode = new Inode();
869
- inode.ino = this.allocNewSync(tx, path, syscall);
870
- inode.data = this.allocNewSync(tx, path, syscall);
871
- inode.size = data.length;
872
- inode.mode = options.mode | type;
873
- inode.uid = parent.mode & S_ISUID ? parent.uid : options.uid;
874
- inode.gid = parent.mode & S_ISGID ? parent.gid : options.gid;
971
+ const inode = new Inode({
972
+ ino: id,
973
+ data: id + 1,
974
+ mode: options.mode,
975
+ size: data.byteLength,
976
+ uid: parent.mode & S_ISUID ? parent.uid : options.uid,
977
+ gid: parent.mode & S_ISGID ? parent.gid : options.gid,
978
+ });
875
979
  // Update and commit parent directory listing.
876
980
  tx.setSync(inode.ino, serialize(inode));
877
981
  tx.setSync(inode.data, data);
@@ -880,12 +984,12 @@ export class StoreFS extends FileSystem {
880
984
  tx.commitSync();
881
985
  return inode;
882
986
  }
883
- catch (e_24) {
884
- env_24.error = e_24;
885
- env_24.hasError = true;
987
+ catch (e_25) {
988
+ env_25.error = e_25;
989
+ env_25.hasError = true;
886
990
  }
887
991
  finally {
888
- __disposeResources(env_24);
992
+ __disposeResources(env_25);
889
993
  }
890
994
  }
891
995
  /**
@@ -894,40 +998,41 @@ export class StoreFS extends FileSystem {
894
998
  * @param isDir Does the path belong to a directory, or a file?
895
999
  * @todo Update mtime.
896
1000
  */
897
- async remove(path, isDir, syscall) {
1001
+ async remove(path, isDir) {
898
1002
  var _a, _b;
899
- const env_25 = { stack: [], error: void 0, hasError: false };
1003
+ const env_26 = { stack: [], error: void 0, hasError: false };
900
1004
  try {
901
- const tx = __addDisposableResource(env_25, this.store.transaction(), true);
1005
+ const syscall = isDir ? 'rmdir' : 'unlink';
1006
+ const tx = __addDisposableResource(env_26, this.transaction(), true);
902
1007
  const { dir: parent, base: fileName } = parse(path), parentNode = await this.findInode(tx, parent, syscall), listing = decodeDirListing((_a = (await tx.get(parentNode.data))) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENOENT', parent, syscall)));
903
1008
  if (!listing[fileName]) {
904
- throw ErrnoError.With('ENOENT', path, 'remove');
1009
+ throw ErrnoError.With('ENOENT', path, syscall);
905
1010
  }
906
1011
  const fileIno = listing[fileName];
907
1012
  // Get file inode.
908
1013
  const fileNode = new Inode((_b = (await tx.get(fileIno))) !== null && _b !== void 0 ? _b : _throw(ErrnoError.With('ENOENT', path, syscall)));
909
1014
  // Remove from directory listing of parent.
910
1015
  delete listing[fileName];
911
- if (!isDir && fileNode.toStats().isDirectory()) {
912
- throw ErrnoError.With('EISDIR', path, 'remove');
913
- }
1016
+ if (!isDir && fileNode.toStats().isDirectory())
1017
+ throw ErrnoError.With('EISDIR', path, syscall);
914
1018
  await tx.set(parentNode.data, encodeDirListing(listing));
915
1019
  if (--fileNode.nlink < 1) {
916
1020
  // remove file
917
1021
  await tx.remove(fileNode.data);
918
1022
  await tx.remove(fileIno);
1023
+ this._remove(fileIno);
919
1024
  }
920
1025
  // Success.
921
1026
  await tx.commit();
922
1027
  }
923
- catch (e_25) {
924
- env_25.error = e_25;
925
- env_25.hasError = true;
1028
+ catch (e_26) {
1029
+ env_26.error = e_26;
1030
+ env_26.hasError = true;
926
1031
  }
927
1032
  finally {
928
- const result_13 = __disposeResources(env_25);
929
- if (result_13)
930
- await result_13;
1033
+ const result_14 = __disposeResources(env_26);
1034
+ if (result_14)
1035
+ await result_14;
931
1036
  }
932
1037
  }
933
1038
  /**
@@ -936,21 +1041,21 @@ export class StoreFS extends FileSystem {
936
1041
  * @param isDir Does the path belong to a directory, or a file?
937
1042
  * @todo Update mtime.
938
1043
  */
939
- removeSync(path, isDir, syscall) {
1044
+ removeSync(path, isDir) {
940
1045
  var _a, _b;
941
- const env_26 = { stack: [], error: void 0, hasError: false };
1046
+ const env_27 = { stack: [], error: void 0, hasError: false };
942
1047
  try {
943
- const tx = __addDisposableResource(env_26, this.store.transaction(), false);
1048
+ const syscall = isDir ? 'rmdir' : 'unlink';
1049
+ const tx = __addDisposableResource(env_27, this.transaction(), false);
944
1050
  const { dir: parent, base: fileName } = parse(path), parentNode = this.findInodeSync(tx, parent, syscall), listing = decodeDirListing((_a = tx.getSync(parentNode.data)) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENOENT', parent, syscall))), fileIno = listing[fileName];
945
- if (!fileIno) {
946
- throw ErrnoError.With('ENOENT', path, 'remove');
947
- }
1051
+ if (!fileIno)
1052
+ throw ErrnoError.With('ENOENT', path, syscall);
948
1053
  // Get file inode.
949
1054
  const fileNode = new Inode((_b = tx.getSync(fileIno)) !== null && _b !== void 0 ? _b : _throw(ErrnoError.With('ENOENT', path, syscall)));
950
1055
  // Remove from directory listing of parent.
951
1056
  delete listing[fileName];
952
1057
  if (!isDir && fileNode.toStats().isDirectory()) {
953
- throw ErrnoError.With('EISDIR', path, 'remove');
1058
+ throw ErrnoError.With('EISDIR', path, syscall);
954
1059
  }
955
1060
  // Update directory listing.
956
1061
  tx.setSync(parentNode.data, encodeDirListing(listing));
@@ -958,16 +1063,17 @@ export class StoreFS extends FileSystem {
958
1063
  // remove file
959
1064
  tx.removeSync(fileNode.data);
960
1065
  tx.removeSync(fileIno);
1066
+ this._remove(fileIno);
961
1067
  }
962
1068
  // Success.
963
1069
  tx.commitSync();
964
1070
  }
965
- catch (e_26) {
966
- env_26.error = e_26;
967
- env_26.hasError = true;
1071
+ catch (e_27) {
1072
+ env_27.error = e_27;
1073
+ env_27.hasError = true;
968
1074
  }
969
1075
  finally {
970
- __disposeResources(env_26);
1076
+ __disposeResources(env_27);
971
1077
  }
972
1078
  }
973
1079
  }