@zenfs/core 0.0.12 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/ApiError.d.ts +52 -15
  2. package/dist/ApiError.js +77 -50
  3. package/dist/FileIndex.d.ts +32 -35
  4. package/dist/FileIndex.js +93 -109
  5. package/dist/backends/AsyncMirror.d.ts +42 -43
  6. package/dist/backends/AsyncMirror.js +154 -147
  7. package/dist/backends/AsyncStore.d.ts +29 -28
  8. package/dist/backends/AsyncStore.js +375 -482
  9. package/dist/backends/FolderAdapter.js +8 -19
  10. package/dist/backends/InMemory.d.ts +16 -13
  11. package/dist/backends/InMemory.js +29 -14
  12. package/dist/backends/Locked.d.ts +8 -28
  13. package/dist/backends/Locked.js +74 -224
  14. package/dist/backends/OverlayFS.d.ts +26 -34
  15. package/dist/backends/OverlayFS.js +303 -511
  16. package/dist/backends/SyncStore.d.ts +54 -72
  17. package/dist/backends/SyncStore.js +159 -161
  18. package/dist/backends/backend.d.ts +45 -29
  19. package/dist/backends/backend.js +83 -13
  20. package/dist/backends/index.d.ts +6 -7
  21. package/dist/backends/index.js +5 -6
  22. package/dist/browser.min.js +21 -6
  23. package/dist/browser.min.js.map +4 -4
  24. package/dist/emulation/callbacks.d.ts +119 -113
  25. package/dist/emulation/callbacks.js +129 -92
  26. package/dist/emulation/constants.js +1 -1
  27. package/dist/emulation/dir.d.ts +55 -0
  28. package/dist/emulation/dir.js +104 -0
  29. package/dist/emulation/fs.d.ts +1 -2
  30. package/dist/emulation/fs.js +0 -1
  31. package/dist/emulation/index.d.ts +3 -0
  32. package/dist/emulation/index.js +3 -0
  33. package/dist/emulation/promises.d.ts +265 -145
  34. package/dist/emulation/promises.js +526 -383
  35. package/dist/emulation/shared.d.ts +20 -6
  36. package/dist/emulation/shared.js +22 -23
  37. package/dist/emulation/streams.d.ts +102 -0
  38. package/dist/emulation/streams.js +55 -0
  39. package/dist/emulation/sync.d.ts +98 -69
  40. package/dist/emulation/sync.js +280 -133
  41. package/dist/file.d.ts +175 -173
  42. package/dist/file.js +257 -273
  43. package/dist/filesystem.d.ts +71 -244
  44. package/dist/filesystem.js +67 -472
  45. package/dist/index.d.ts +7 -44
  46. package/dist/index.js +22 -75
  47. package/dist/inode.d.ts +37 -28
  48. package/dist/inode.js +123 -65
  49. package/dist/stats.d.ts +91 -36
  50. package/dist/stats.js +138 -110
  51. package/dist/utils.d.ts +26 -13
  52. package/dist/utils.js +79 -107
  53. package/package.json +7 -4
  54. package/readme.md +2 -40
@@ -1,39 +1,15 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- var _a;
11
- import { BaseFileSystem } from '../filesystem.js';
1
+ import { FileSystem } from '../filesystem.js';
12
2
  import { ApiError, ErrorCode } from '../ApiError.js';
13
- import { FileFlag, ActionType, PreloadFile } from '../file.js';
3
+ import { FileFlag, PreloadFile } from '../file.js';
14
4
  import { Stats } from '../stats.js';
15
5
  import LockedFS from './Locked.js';
16
- import { resolve, dirname } from '../emulation/path.js';
6
+ import { dirname } from '../emulation/path.js';
17
7
  import { Cred } from '../cred.js';
18
- import { CreateBackend } from './backend.js';
19
8
  import { decode, encode } from '../utils.js';
20
9
  /**
21
10
  * @internal
22
11
  */
23
- const deletionLogPath = '/.deletedFiles.log';
24
- /**
25
- * Given a read-only mode, makes it writable.
26
- * @internal
27
- */
28
- function makeModeWritable(mode) {
29
- return 0o222 | mode;
30
- }
31
- /**
32
- * @internal
33
- */
34
- function getFlag(f) {
35
- return FileFlag.getFileFlag(f);
36
- }
12
+ const deletionLogPath = '/.deleted';
37
13
  /**
38
14
  * Overlays a RO file to make it writable.
39
15
  */
@@ -41,60 +17,65 @@ class OverlayFile extends PreloadFile {
41
17
  constructor(fs, path, flag, stats, data) {
42
18
  super(fs, path, flag, stats, data);
43
19
  }
44
- sync() {
45
- return __awaiter(this, void 0, void 0, function* () {
46
- if (!this.isDirty()) {
47
- return;
48
- }
49
- yield this._fs._syncAsync(this);
50
- this.resetDirty();
51
- });
20
+ async sync() {
21
+ if (!this.isDirty()) {
22
+ return;
23
+ }
24
+ await this.fs.sync(this.path, this.buffer, this.stats);
25
+ this.resetDirty();
52
26
  }
53
27
  syncSync() {
54
28
  if (this.isDirty()) {
55
- this._fs._syncSync(this);
29
+ this.fs.syncSync(this.path, this.buffer, this.stats);
56
30
  this.resetDirty();
57
31
  }
58
32
  }
59
- close() {
60
- return __awaiter(this, void 0, void 0, function* () {
61
- yield this.sync();
62
- });
33
+ async close() {
34
+ await this.sync();
63
35
  }
64
36
  closeSync() {
65
37
  this.syncSync();
66
38
  }
67
39
  }
68
40
  /**
69
- * *INTERNAL, DO NOT USE DIRECTLY!*
41
+ * OverlayFS makes a read-only filesystem writable by storing writes on a second, writable file system.
42
+ * Deletes are persisted via metadata stored on the writable file system.
43
+ *
44
+ * This class contains no locking whatsoever. It is wrapped in a LockedFS to prevent races.
70
45
  *
71
- * Core OverlayFS class that contains no locking whatsoever. We wrap these objects
72
- * in a LockedFS to prevent races.
46
+ * @internal
73
47
  */
74
- export class UnlockedOverlayFS extends BaseFileSystem {
75
- static isAvailable() {
76
- return true;
48
+ export class UnlockedOverlayFS extends FileSystem {
49
+ async ready() {
50
+ await this._readable.ready();
51
+ await this._writable.ready();
52
+ await this._ready;
53
+ return this;
77
54
  }
78
55
  constructor({ writable, readable }) {
79
56
  super();
80
57
  this._isInitialized = false;
81
- this._deletedFiles = {};
58
+ this._deletedFiles = new Set();
82
59
  this._deleteLog = '';
83
60
  // If 'true', we have scheduled a delete log update.
84
61
  this._deleteLogUpdatePending = false;
85
62
  // If 'true', a delete log update is needed after the scheduled delete log
86
63
  // update finishes.
87
64
  this._deleteLogUpdateNeeded = false;
88
- // If there was an error updating the delete log...
89
- this._deleteLogError = null;
90
65
  this._writable = writable;
91
66
  this._readable = readable;
92
67
  if (this._writable.metadata.readonly) {
93
68
  throw new ApiError(ErrorCode.EINVAL, 'Writable file system must be writable.');
94
69
  }
70
+ this._ready = this._initialize();
95
71
  }
96
72
  get metadata() {
97
- return Object.assign(Object.assign({}, super.metadata), { name: OverlayFS.Name, synchronous: this._readable.metadata.synchronous && this._writable.metadata.synchronous, supportsProperties: this._readable.metadata.supportsProperties && this._writable.metadata.supportsProperties });
73
+ return {
74
+ ...super.metadata,
75
+ name: OverlayFS.name,
76
+ synchronous: this._readable.metadata.synchronous && this._writable.metadata.synchronous,
77
+ supportsProperties: this._readable.metadata.supportsProperties && this._writable.metadata.supportsProperties,
78
+ };
98
79
  }
99
80
  getOverlayedFileSystems() {
100
81
  return {
@@ -102,42 +83,38 @@ export class UnlockedOverlayFS extends BaseFileSystem {
102
83
  writable: this._writable,
103
84
  };
104
85
  }
105
- _syncAsync(file) {
106
- return __awaiter(this, void 0, void 0, function* () {
107
- const stats = file.getStats();
108
- yield this.createParentDirectoriesAsync(file.getPath(), stats.getCred(0, 0));
109
- return this._writable.writeFile(file.getPath(), file.getBuffer(), getFlag('w'), stats.mode, stats.getCred(0, 0));
110
- });
86
+ async sync(path, data, stats) {
87
+ const cred = stats.getCred(0, 0);
88
+ await this.createParentDirectories(path, cred);
89
+ await this._writable.sync(path, data, stats);
111
90
  }
112
- _syncSync(file) {
113
- const stats = file.getStats();
114
- this.createParentDirectories(file.getPath(), stats.getCred(0, 0));
115
- this._writable.writeFileSync(file.getPath(), file.getBuffer(), getFlag('w'), stats.mode, stats.getCred(0, 0));
91
+ syncSync(path, data, stats) {
92
+ const cred = stats.getCred(0, 0);
93
+ this.createParentDirectoriesSync(path, cred);
94
+ this._writable.syncSync(path, data, stats);
116
95
  }
117
96
  /**
118
- * **INTERNAL METHOD**
119
- *
120
97
  * Called once to load up metadata stored on the writable file system.
98
+ * @internal
121
99
  */
122
- _initialize() {
123
- return __awaiter(this, void 0, void 0, function* () {
124
- // if we're already initialized, immediately invoke the callback
125
- if (this._isInitialized) {
126
- return;
127
- }
128
- // Read deletion log, process into metadata.
129
- try {
130
- const data = yield this._writable.readFile(deletionLogPath, getFlag('r'), Cred.Root);
131
- this._deleteLog = decode(data);
132
- }
133
- catch (err) {
134
- if (err.errno !== ErrorCode.ENOENT) {
135
- throw err;
136
- }
100
+ async _initialize() {
101
+ if (this._isInitialized) {
102
+ return;
103
+ }
104
+ // Read deletion log, process into metadata.
105
+ try {
106
+ const file = await this._writable.openFile(deletionLogPath, FileFlag.FromString('r'), Cred.Root);
107
+ const { size } = await file.stat();
108
+ const { buffer } = await file.read(new Uint8Array(size));
109
+ this._deleteLog = decode(buffer);
110
+ }
111
+ catch (err) {
112
+ if (err.errno !== ErrorCode.ENOENT) {
113
+ throw err;
137
114
  }
138
- this._isInitialized = true;
139
- this._reparseDeletionLog();
140
- });
115
+ }
116
+ this._isInitialized = true;
117
+ this._reparseDeletionLog();
141
118
  }
142
119
  getDeletionLog() {
143
120
  return this._deleteLog;
@@ -147,133 +124,47 @@ export class UnlockedOverlayFS extends BaseFileSystem {
147
124
  this._reparseDeletionLog();
148
125
  this.updateLog('', cred);
149
126
  }
150
- rename(oldPath, newPath, cred) {
151
- return __awaiter(this, void 0, void 0, function* () {
152
- this.checkInitialized();
153
- this.checkPath(oldPath);
154
- this.checkPath(newPath);
155
- if (oldPath === deletionLogPath || newPath === deletionLogPath) {
156
- throw ApiError.EPERM('Cannot rename deletion log.');
157
- }
158
- // Write newPath using oldPath's contents, delete oldPath.
159
- const oldStats = yield this.stat(oldPath, cred);
160
- if (oldStats.isDirectory()) {
161
- // Optimization: Don't bother moving if old === new.
162
- if (oldPath === newPath) {
163
- return;
164
- }
165
- let mode = 0o777;
166
- if (yield this.exists(newPath, cred)) {
167
- const stats = yield this.stat(newPath, cred);
168
- mode = stats.mode;
169
- if (stats.isDirectory()) {
170
- if ((yield this.readdir(newPath, cred)).length > 0) {
171
- throw ApiError.ENOTEMPTY(newPath);
172
- }
173
- }
174
- else {
175
- throw ApiError.ENOTDIR(newPath);
176
- }
177
- }
178
- // Take care of writable first. Move any files there, or create an empty directory
179
- // if it doesn't exist.
180
- if (yield this._writable.exists(oldPath, cred)) {
181
- yield this._writable.rename(oldPath, newPath, cred);
182
- }
183
- else if (!(yield this._writable.exists(newPath, cred))) {
184
- yield this._writable.mkdir(newPath, mode, cred);
185
- }
186
- // Need to move *every file/folder* currently stored on readable to its new location
187
- // on writable.
188
- if (yield this._readable.exists(oldPath, cred)) {
189
- for (const name of yield this._readable.readdir(oldPath, cred)) {
190
- // Recursion! Should work for any nested files / folders.
191
- yield this.rename(resolve(oldPath, name), resolve(newPath, name), cred);
192
- }
193
- }
194
- }
195
- else {
196
- if ((yield this.exists(newPath, cred)) && (yield this.stat(newPath, cred)).isDirectory()) {
197
- throw ApiError.EISDIR(newPath);
198
- }
199
- yield this.writeFile(newPath, yield this.readFile(oldPath, getFlag('r'), cred), getFlag('w'), oldStats.mode, cred);
200
- }
201
- if (oldPath !== newPath && (yield this.exists(oldPath, cred))) {
202
- yield this.unlink(oldPath, cred);
127
+ async rename(oldPath, newPath, cred) {
128
+ this.checkInitialized();
129
+ this.checkPath(oldPath);
130
+ this.checkPath(newPath);
131
+ try {
132
+ await this._writable.rename(oldPath, newPath, cred);
133
+ }
134
+ catch (e) {
135
+ if (this._deletedFiles.has(oldPath)) {
136
+ throw ApiError.ENOENT(oldPath);
203
137
  }
204
- });
138
+ }
205
139
  }
206
140
  renameSync(oldPath, newPath, cred) {
207
141
  this.checkInitialized();
208
142
  this.checkPath(oldPath);
209
143
  this.checkPath(newPath);
210
- if (oldPath === deletionLogPath || newPath === deletionLogPath) {
211
- throw ApiError.EPERM('Cannot rename deletion log.');
212
- }
213
- // Write newPath using oldPath's contents, delete oldPath.
214
- const oldStats = this.statSync(oldPath, cred);
215
- if (oldStats.isDirectory()) {
216
- // Optimization: Don't bother moving if old === new.
217
- if (oldPath === newPath) {
218
- return;
219
- }
220
- let mode = 0o777;
221
- if (this.existsSync(newPath, cred)) {
222
- const stats = this.statSync(newPath, cred);
223
- mode = stats.mode;
224
- if (stats.isDirectory()) {
225
- if (this.readdirSync(newPath, cred).length > 0) {
226
- throw ApiError.ENOTEMPTY(newPath);
227
- }
228
- }
229
- else {
230
- throw ApiError.ENOTDIR(newPath);
231
- }
232
- }
233
- // Take care of writable first. Move any files there, or create an empty directory
234
- // if it doesn't exist.
235
- if (this._writable.existsSync(oldPath, cred)) {
236
- this._writable.renameSync(oldPath, newPath, cred);
237
- }
238
- else if (!this._writable.existsSync(newPath, cred)) {
239
- this._writable.mkdirSync(newPath, mode, cred);
240
- }
241
- // Need to move *every file/folder* currently stored on readable to its new location
242
- // on writable.
243
- if (this._readable.existsSync(oldPath, cred)) {
244
- this._readable.readdirSync(oldPath, cred).forEach(name => {
245
- // Recursion! Should work for any nested files / folders.
246
- this.renameSync(resolve(oldPath, name), resolve(newPath, name), cred);
247
- });
248
- }
144
+ try {
145
+ this._writable.renameSync(oldPath, newPath, cred);
249
146
  }
250
- else {
251
- if (this.existsSync(newPath, cred) && this.statSync(newPath, cred).isDirectory()) {
252
- throw ApiError.EISDIR(newPath);
147
+ catch (e) {
148
+ if (this._deletedFiles.has(oldPath)) {
149
+ throw ApiError.ENOENT(oldPath);
253
150
  }
254
- this.writeFileSync(newPath, this.readFileSync(oldPath, getFlag('r'), cred), getFlag('w'), oldStats.mode, cred);
255
- }
256
- if (oldPath !== newPath && this.existsSync(oldPath, cred)) {
257
- this.unlinkSync(oldPath, cred);
258
151
  }
259
152
  }
260
- stat(p, cred) {
261
- return __awaiter(this, void 0, void 0, function* () {
262
- this.checkInitialized();
263
- try {
264
- return this._writable.stat(p, cred);
265
- }
266
- catch (e) {
267
- if (this._deletedFiles[p]) {
268
- throw ApiError.ENOENT(p);
269
- }
270
- const oldStat = Stats.clone(yield this._readable.stat(p, cred));
271
- // Make the oldStat's mode writable. Preserve the topmost part of the
272
- // mode, which specifies if it is a file or a directory.
273
- oldStat.mode = makeModeWritable(oldStat.mode);
274
- return oldStat;
153
+ async stat(p, cred) {
154
+ this.checkInitialized();
155
+ try {
156
+ return this._writable.stat(p, cred);
157
+ }
158
+ catch (e) {
159
+ if (this._deletedFiles.has(p)) {
160
+ throw ApiError.ENOENT(p);
275
161
  }
276
- });
162
+ const oldStat = Stats.clone(await this._readable.stat(p, cred));
163
+ // Make the oldStat's mode writable. Preserve the topmost part of the
164
+ // mode, which specifies if it is a file or a directory.
165
+ oldStat.mode |= 0o222;
166
+ return oldStat;
167
+ }
277
168
  }
278
169
  statSync(p, cred) {
279
170
  this.checkInitialized();
@@ -281,219 +172,162 @@ export class UnlockedOverlayFS extends BaseFileSystem {
281
172
  return this._writable.statSync(p, cred);
282
173
  }
283
174
  catch (e) {
284
- if (this._deletedFiles[p]) {
175
+ if (this._deletedFiles.has(p)) {
285
176
  throw ApiError.ENOENT(p);
286
177
  }
287
178
  const oldStat = Stats.clone(this._readable.statSync(p, cred));
288
179
  // Make the oldStat's mode writable. Preserve the topmost part of the
289
180
  // mode, which specifies if it is a file or a directory.
290
- oldStat.mode = makeModeWritable(oldStat.mode);
181
+ oldStat.mode |= 0o222;
291
182
  return oldStat;
292
183
  }
293
184
  }
294
- open(p, flag, mode, cred) {
295
- return __awaiter(this, void 0, void 0, function* () {
296
- this.checkInitialized();
297
- this.checkPath(p);
298
- if (p === deletionLogPath) {
299
- throw ApiError.EPERM('Cannot open deletion log.');
300
- }
301
- if (yield this.exists(p, cred)) {
302
- switch (flag.pathExistsAction()) {
303
- case ActionType.TRUNCATE_FILE:
304
- yield this.createParentDirectoriesAsync(p, cred);
305
- return this._writable.open(p, flag, mode, cred);
306
- case ActionType.NOP:
307
- if (yield this._writable.exists(p, cred)) {
308
- return this._writable.open(p, flag, mode, cred);
309
- }
310
- else {
311
- // Create an OverlayFile.
312
- const buf = yield this._readable.readFile(p, getFlag('r'), cred);
313
- const stats = Stats.clone(yield this._readable.stat(p, cred));
314
- stats.mode = mode;
315
- return new OverlayFile(this, p, flag, stats, buf);
316
- }
317
- default:
318
- throw ApiError.EEXIST(p);
319
- }
320
- }
321
- else {
322
- switch (flag.pathNotExistsAction()) {
323
- case ActionType.CREATE_FILE:
324
- yield this.createParentDirectoriesAsync(p, cred);
325
- return this._writable.open(p, flag, mode, cred);
326
- default:
327
- throw ApiError.ENOENT(p);
328
- }
329
- }
330
- });
185
+ async openFile(path, flag, cred) {
186
+ if (await this._writable.exists(path, cred)) {
187
+ return this._writable.openFile(path, flag, cred);
188
+ }
189
+ // Create an OverlayFile.
190
+ const file = await this._readable.openFile(path, FileFlag.FromString('r'), cred);
191
+ const stats = Stats.clone(await file.stat());
192
+ const { buffer } = await file.read(new Uint8Array(stats.size));
193
+ return new OverlayFile(this, path, flag, stats, buffer);
194
+ }
195
+ openFileSync(path, flag, cred) {
196
+ if (this._writable.existsSync(path, cred)) {
197
+ return this._writable.openFileSync(path, flag, cred);
198
+ }
199
+ // Create an OverlayFile.
200
+ const file = this._readable.openFileSync(path, FileFlag.FromString('r'), cred);
201
+ const stats = Stats.clone(file.statSync());
202
+ const data = new Uint8Array(stats.size);
203
+ file.readSync(data);
204
+ return new OverlayFile(this, path, flag, stats, data);
205
+ }
206
+ async createFile(path, flag, mode, cred) {
207
+ this.checkInitialized();
208
+ await this._writable.createFile(path, flag, mode, cred);
209
+ return this.openFile(path, flag, cred);
210
+ }
211
+ createFileSync(path, flag, mode, cred) {
212
+ this.checkInitialized();
213
+ this._writable.createFileSync(path, flag, mode, cred);
214
+ return this.openFileSync(path, flag, cred);
215
+ }
216
+ async link(srcpath, dstpath, cred) {
217
+ this.checkInitialized();
218
+ await this._writable.link(srcpath, dstpath, cred);
331
219
  }
332
- openSync(p, flag, mode, cred) {
220
+ linkSync(srcpath, dstpath, cred) {
221
+ this.checkInitialized();
222
+ this._writable.linkSync(srcpath, dstpath, cred);
223
+ }
224
+ async unlink(p, cred) {
333
225
  this.checkInitialized();
334
226
  this.checkPath(p);
335
- if (p === deletionLogPath) {
336
- throw ApiError.EPERM('Cannot open deletion log.');
227
+ if (!(await this.exists(p, cred))) {
228
+ throw ApiError.ENOENT(p);
337
229
  }
338
- if (this.existsSync(p, cred)) {
339
- switch (flag.pathExistsAction()) {
340
- case ActionType.TRUNCATE_FILE:
341
- this.createParentDirectories(p, cred);
342
- return this._writable.openSync(p, flag, mode, cred);
343
- case ActionType.NOP:
344
- if (this._writable.existsSync(p, cred)) {
345
- return this._writable.openSync(p, flag, mode, cred);
346
- }
347
- else {
348
- // Create an OverlayFile.
349
- const buf = this._readable.readFileSync(p, getFlag('r'), cred);
350
- const stats = Stats.clone(this._readable.statSync(p, cred));
351
- stats.mode = mode;
352
- return new OverlayFile(this, p, flag, stats, buf);
353
- }
354
- default:
355
- throw ApiError.EEXIST(p);
356
- }
230
+ if (await this._writable.exists(p, cred)) {
231
+ await this._writable.unlink(p, cred);
357
232
  }
358
- else {
359
- switch (flag.pathNotExistsAction()) {
360
- case ActionType.CREATE_FILE:
361
- this.createParentDirectories(p, cred);
362
- return this._writable.openSync(p, flag, mode, cred);
363
- default:
364
- throw ApiError.ENOENT(p);
365
- }
233
+ // if it still exists add to the delete log
234
+ if (await this.exists(p, cred)) {
235
+ this.deletePath(p, cred);
366
236
  }
367
237
  }
368
- unlink(p, cred) {
369
- return __awaiter(this, void 0, void 0, function* () {
370
- this.checkInitialized();
371
- this.checkPath(p);
372
- if (yield this.exists(p, cred)) {
373
- if (yield this._writable.exists(p, cred)) {
374
- yield this._writable.unlink(p, cred);
375
- }
376
- // if it still exists add to the delete log
377
- if (yield this.exists(p, cred)) {
378
- this.deletePath(p, cred);
379
- }
380
- }
381
- else {
382
- throw ApiError.ENOENT(p);
383
- }
384
- });
385
- }
386
238
  unlinkSync(p, cred) {
387
239
  this.checkInitialized();
388
240
  this.checkPath(p);
241
+ if (!this.existsSync(p, cred)) {
242
+ throw ApiError.ENOENT(p);
243
+ }
244
+ if (this._writable.existsSync(p, cred)) {
245
+ this._writable.unlinkSync(p, cred);
246
+ }
247
+ // if it still exists add to the delete log
389
248
  if (this.existsSync(p, cred)) {
390
- if (this._writable.existsSync(p, cred)) {
391
- this._writable.unlinkSync(p, cred);
392
- }
393
- // if it still exists add to the delete log
394
- if (this.existsSync(p, cred)) {
395
- this.deletePath(p, cred);
396
- }
249
+ this.deletePath(p, cred);
397
250
  }
398
- else {
251
+ }
252
+ async rmdir(p, cred) {
253
+ this.checkInitialized();
254
+ if (!(await this.exists(p, cred))) {
399
255
  throw ApiError.ENOENT(p);
400
256
  }
401
- }
402
- rmdir(p, cred) {
403
- return __awaiter(this, void 0, void 0, function* () {
404
- this.checkInitialized();
405
- if (yield this.exists(p, cred)) {
406
- if (yield this._writable.exists(p, cred)) {
407
- yield this._writable.rmdir(p, cred);
408
- }
409
- if (yield this.exists(p, cred)) {
410
- // Check if directory is empty.
411
- if ((yield this.readdir(p, cred)).length > 0) {
412
- throw ApiError.ENOTEMPTY(p);
413
- }
414
- else {
415
- this.deletePath(p, cred);
416
- }
417
- }
257
+ if (await this._writable.exists(p, cred)) {
258
+ await this._writable.rmdir(p, cred);
259
+ }
260
+ if (await this.exists(p, cred)) {
261
+ // Check if directory is empty.
262
+ if ((await this.readdir(p, cred)).length > 0) {
263
+ throw ApiError.ENOTEMPTY(p);
418
264
  }
419
265
  else {
420
- throw ApiError.ENOENT(p);
266
+ this.deletePath(p, cred);
421
267
  }
422
- });
268
+ }
423
269
  }
424
270
  rmdirSync(p, cred) {
425
271
  this.checkInitialized();
426
- if (this.existsSync(p, cred)) {
427
- if (this._writable.existsSync(p, cred)) {
428
- this._writable.rmdirSync(p, cred);
429
- }
430
- if (this.existsSync(p, cred)) {
431
- // Check if directory is empty.
432
- if (this.readdirSync(p, cred).length > 0) {
433
- throw ApiError.ENOTEMPTY(p);
434
- }
435
- else {
436
- this.deletePath(p, cred);
437
- }
438
- }
439
- }
440
- else {
272
+ if (!this.existsSync(p, cred)) {
441
273
  throw ApiError.ENOENT(p);
442
274
  }
443
- }
444
- mkdir(p, mode, cred) {
445
- return __awaiter(this, void 0, void 0, function* () {
446
- this.checkInitialized();
447
- if (yield this.exists(p, cred)) {
448
- throw ApiError.EEXIST(p);
275
+ if (this._writable.existsSync(p, cred)) {
276
+ this._writable.rmdirSync(p, cred);
277
+ }
278
+ if (this.existsSync(p, cred)) {
279
+ // Check if directory is empty.
280
+ if (this.readdirSync(p, cred).length > 0) {
281
+ throw ApiError.ENOTEMPTY(p);
449
282
  }
450
283
  else {
451
- // The below will throw should any of the parent directories fail to exist
452
- // on _writable.
453
- yield this.createParentDirectoriesAsync(p, cred);
454
- yield this._writable.mkdir(p, mode, cred);
284
+ this.deletePath(p, cred);
455
285
  }
456
- });
286
+ }
287
+ }
288
+ async mkdir(p, mode, cred) {
289
+ this.checkInitialized();
290
+ if (await this.exists(p, cred)) {
291
+ throw ApiError.EEXIST(p);
292
+ }
293
+ // The below will throw should any of the parent directories fail to exist on _writable.
294
+ await this.createParentDirectories(p, cred);
295
+ await this._writable.mkdir(p, mode, cred);
457
296
  }
458
297
  mkdirSync(p, mode, cred) {
459
298
  this.checkInitialized();
460
299
  if (this.existsSync(p, cred)) {
461
300
  throw ApiError.EEXIST(p);
462
301
  }
463
- else {
464
- // The below will throw should any of the parent directories fail to exist
465
- // on _writable.
466
- this.createParentDirectories(p, cred);
467
- this._writable.mkdirSync(p, mode, cred);
468
- }
302
+ // The below will throw should any of the parent directories fail to exist on _writable.
303
+ this.createParentDirectoriesSync(p, cred);
304
+ this._writable.mkdirSync(p, mode, cred);
469
305
  }
470
- readdir(p, cred) {
471
- return __awaiter(this, void 0, void 0, function* () {
472
- this.checkInitialized();
473
- const dirStats = yield this.stat(p, cred);
474
- if (!dirStats.isDirectory()) {
475
- throw ApiError.ENOTDIR(p);
476
- }
477
- // Readdir in both, check delete log on RO file system's listing, merge, return.
478
- let contents = [];
479
- try {
480
- contents = contents.concat(yield this._writable.readdir(p, cred));
481
- }
482
- catch (e) {
483
- // NOP.
484
- }
485
- try {
486
- contents = contents.concat((yield this._readable.readdir(p, cred)).filter((fPath) => !this._deletedFiles[`${p}/${fPath}`]));
487
- }
488
- catch (e) {
489
- // NOP.
490
- }
491
- const seenMap = {};
492
- return contents.filter((fileP) => {
493
- const result = !seenMap[fileP];
494
- seenMap[fileP] = true;
495
- return result;
496
- });
306
+ async readdir(p, cred) {
307
+ this.checkInitialized();
308
+ const dirStats = await this.stat(p, cred);
309
+ if (!dirStats.isDirectory()) {
310
+ throw ApiError.ENOTDIR(p);
311
+ }
312
+ // Readdir in both, check delete log on RO file system's listing, merge, return.
313
+ let contents = [];
314
+ try {
315
+ contents.push(...(await this._writable.readdir(p, cred)));
316
+ }
317
+ catch (e) {
318
+ // NOP.
319
+ }
320
+ try {
321
+ contents.push(...(await this._readable.readdir(p, cred)).filter((fPath) => !this._deletedFiles.has(`${p}/${fPath}`)));
322
+ }
323
+ catch (e) {
324
+ // NOP.
325
+ }
326
+ const seenMap = {};
327
+ return contents.filter((fileP) => {
328
+ const result = !seenMap[fileP];
329
+ seenMap[fileP] = true;
330
+ return result;
497
331
  });
498
332
  }
499
333
  readdirSync(p, cred) {
@@ -511,7 +345,7 @@ export class UnlockedOverlayFS extends BaseFileSystem {
511
345
  // NOP.
512
346
  }
513
347
  try {
514
- contents = contents.concat(this._readable.readdirSync(p, cred).filter((fPath) => !this._deletedFiles[`${p}/${fPath}`]));
348
+ contents = contents.concat(this._readable.readdirSync(p, cred).filter((fPath) => !this._deletedFiles.has(`${p}/${fPath}`)));
515
349
  }
516
350
  catch (e) {
517
351
  // NOP.
@@ -523,106 +357,63 @@ export class UnlockedOverlayFS extends BaseFileSystem {
523
357
  return result;
524
358
  });
525
359
  }
526
- exists(p, cred) {
527
- return __awaiter(this, void 0, void 0, function* () {
528
- this.checkInitialized();
529
- return (yield this._writable.exists(p, cred)) || ((yield this._readable.exists(p, cred)) && this._deletedFiles[p] !== true);
530
- });
531
- }
532
- existsSync(p, cred) {
533
- this.checkInitialized();
534
- return this._writable.existsSync(p, cred) || (this._readable.existsSync(p, cred) && this._deletedFiles[p] !== true);
535
- }
536
- chmod(p, mode, cred) {
537
- return __awaiter(this, void 0, void 0, function* () {
538
- this.checkInitialized();
539
- yield this.operateOnWritableAsync(p, cred);
540
- yield this._writable.chmod(p, mode, cred);
541
- });
542
- }
543
- chmodSync(p, mode, cred) {
544
- this.checkInitialized();
545
- this.operateOnWritable(p, cred);
546
- this._writable.chmodSync(p, mode, cred);
547
- }
548
- chown(p, new_uid, new_gid, cred) {
549
- return __awaiter(this, void 0, void 0, function* () {
550
- this.checkInitialized();
551
- yield this.operateOnWritableAsync(p, cred);
552
- yield this._writable.chown(p, new_uid, new_gid, cred);
553
- });
554
- }
555
- chownSync(p, new_uid, new_gid, cred) {
556
- this.checkInitialized();
557
- this.operateOnWritable(p, cred);
558
- this._writable.chownSync(p, new_uid, new_gid, cred);
559
- }
560
- utimes(p, atime, mtime, cred) {
561
- return __awaiter(this, void 0, void 0, function* () {
562
- this.checkInitialized();
563
- yield this.operateOnWritableAsync(p, cred);
564
- yield this._writable.utimes(p, atime, mtime, cred);
565
- });
566
- }
567
- utimesSync(p, atime, mtime, cred) {
568
- this.checkInitialized();
569
- this.operateOnWritable(p, cred);
570
- this._writable.utimesSync(p, atime, mtime, cred);
571
- }
572
360
  deletePath(p, cred) {
573
- this._deletedFiles[p] = true;
361
+ this._deletedFiles.add(p);
574
362
  this.updateLog(`d${p}\n`, cred);
575
363
  }
576
- updateLog(addition, cred) {
364
+ async updateLog(addition, cred) {
577
365
  this._deleteLog += addition;
578
366
  if (this._deleteLogUpdatePending) {
579
367
  this._deleteLogUpdateNeeded = true;
368
+ return;
580
369
  }
581
- else {
582
- this._deleteLogUpdatePending = true;
583
- this._writable
584
- .writeFile(deletionLogPath, encode(this._deleteLog), FileFlag.getFileFlag('w'), 0o644, cred)
585
- .then(() => {
586
- if (this._deleteLogUpdateNeeded) {
587
- this._deleteLogUpdateNeeded = false;
588
- this.updateLog('', cred);
589
- }
590
- })
591
- .catch(e => {
592
- this._deleteLogError = e;
593
- })
594
- .finally(() => {
595
- this._deleteLogUpdatePending = false;
596
- });
370
+ this._deleteLogUpdatePending = true;
371
+ const log = await this._writable.openFile(deletionLogPath, FileFlag.FromString('w'), cred);
372
+ try {
373
+ await log.write(encode(this._deleteLog));
374
+ if (this._deleteLogUpdateNeeded) {
375
+ this._deleteLogUpdateNeeded = false;
376
+ this.updateLog('', cred);
377
+ }
378
+ }
379
+ catch (e) {
380
+ this._deleteLogError = e;
381
+ }
382
+ finally {
383
+ this._deleteLogUpdatePending = false;
597
384
  }
598
385
  }
599
386
  _reparseDeletionLog() {
600
- this._deletedFiles = {};
601
- this._deleteLog.split('\n').forEach((path) => {
387
+ this._deletedFiles.clear();
388
+ for (const entry of this._deleteLog.split('\n')) {
389
+ if (!entry.startsWith('d')) {
390
+ continue;
391
+ }
602
392
  // If the log entry begins w/ 'd', it's a deletion.
603
- this._deletedFiles[path.slice(1)] = path.slice(0, 1) === 'd';
604
- });
393
+ this._deletedFiles.add(entry.slice(1));
394
+ }
605
395
  }
606
396
  checkInitialized() {
607
397
  if (!this._isInitialized) {
608
398
  throw new ApiError(ErrorCode.EPERM, 'OverlayFS is not initialized. Please initialize OverlayFS using its initialize() method before using it.');
609
399
  }
610
- else if (this._deleteLogError !== null) {
611
- const e = this._deleteLogError;
612
- this._deleteLogError = null;
613
- throw e;
400
+ if (!this._deleteLogError) {
401
+ return;
614
402
  }
403
+ const error = this._deleteLogError;
404
+ this._deleteLogError = null;
405
+ throw error;
615
406
  }
616
- checkPath(p) {
617
- if (p === deletionLogPath) {
618
- throw ApiError.EPERM(p);
407
+ checkPath(path) {
408
+ if (path == deletionLogPath) {
409
+ throw ApiError.EPERM(path);
619
410
  }
620
411
  }
621
412
  /**
622
413
  * With the given path, create the needed parent directories on the writable storage
623
414
  * should they not exist. Use modes from the read-only storage.
624
415
  */
625
- createParentDirectories(p, cred) {
416
+ createParentDirectoriesSync(p, cred) {
626
417
  let parent = dirname(p), toCreate = [];
627
418
  while (!this._writable.existsSync(parent, cred)) {
628
419
  toCreate.push(parent);
@@ -633,19 +424,17 @@ export class UnlockedOverlayFS extends BaseFileSystem {
633
424
  this._writable.mkdirSync(p, this.statSync(p, cred).mode, cred);
634
425
  }
635
426
  }
636
- createParentDirectoriesAsync(p, cred) {
637
- return __awaiter(this, void 0, void 0, function* () {
638
- let parent = dirname(p), toCreate = [];
639
- while (!(yield this._writable.exists(parent, cred))) {
640
- toCreate.push(parent);
641
- parent = dirname(parent);
642
- }
643
- toCreate = toCreate.reverse();
644
- for (const p of toCreate) {
645
- const stats = yield this.stat(p, cred);
646
- yield this._writable.mkdir(p, stats.mode, cred);
647
- }
648
- });
427
+ async createParentDirectories(p, cred) {
428
+ let parent = dirname(p), toCreate = [];
429
+ while (!(await this._writable.exists(parent, cred))) {
430
+ toCreate.push(parent);
431
+ parent = dirname(parent);
432
+ }
433
+ toCreate = toCreate.reverse();
434
+ for (const p of toCreate) {
435
+ const stats = await this.stat(p, cred);
436
+ await this._writable.mkdir(p, stats.mode, cred);
437
+ }
649
438
  }
650
439
  /**
651
440
  * Helper function:
@@ -659,42 +448,48 @@ export class UnlockedOverlayFS extends BaseFileSystem {
659
448
  if (!this._writable.existsSync(p, cred)) {
660
449
  // File is on readable storage. Copy to writable storage before
661
450
  // changing its mode.
662
- this.copyToWritable(p, cred);
451
+ this.copyToWritableSync(p, cred);
663
452
  }
664
453
  }
665
- operateOnWritableAsync(p, cred) {
666
- return __awaiter(this, void 0, void 0, function* () {
667
- if (!(yield this.exists(p, cred))) {
668
- throw ApiError.ENOENT(p);
669
- }
670
- if (!(yield this._writable.exists(p, cred))) {
671
- return this.copyToWritableAsync(p, cred);
672
- }
673
- });
454
+ async operateOnWritableAsync(p, cred) {
455
+ if (!(await this.exists(p, cred))) {
456
+ throw ApiError.ENOENT(p);
457
+ }
458
+ if (!(await this._writable.exists(p, cred))) {
459
+ return this.copyToWritable(p, cred);
460
+ }
674
461
  }
675
462
  /**
676
463
  * Copy from readable to writable storage.
677
464
  * PRECONDITION: File does not exist on writable storage.
678
465
  */
679
- copyToWritable(p, cred) {
680
- const pStats = this.statSync(p, cred);
681
- if (pStats.isDirectory()) {
682
- this._writable.mkdirSync(p, pStats.mode, cred);
683
- }
684
- else {
685
- this.writeFileSync(p, this._readable.readFileSync(p, getFlag('r'), cred), getFlag('w'), pStats.mode, cred);
686
- }
687
- }
688
- copyToWritableAsync(p, cred) {
689
- return __awaiter(this, void 0, void 0, function* () {
690
- const pStats = yield this.stat(p, cred);
691
- if (pStats.isDirectory()) {
692
- yield this._writable.mkdir(p, pStats.mode, cred);
693
- }
694
- else {
695
- yield this.writeFile(p, yield this._readable.readFile(p, getFlag('r'), cred), getFlag('w'), pStats.mode, cred);
696
- }
697
- });
466
+ copyToWritableSync(p, cred) {
467
+ const stats = this.statSync(p, cred);
468
+ if (stats.isDirectory()) {
469
+ this._writable.mkdirSync(p, stats.mode, cred);
470
+ return;
471
+ }
472
+ const data = new Uint8Array(stats.size);
473
+ const readable = this._readable.openFileSync(p, FileFlag.FromString('r'), cred);
474
+ readable.readSync(data);
475
+ readable.closeSync();
476
+ const writable = this._writable.openFileSync(p, FileFlag.FromString('w'), cred);
477
+ writable.writeSync(data);
478
+ writable.closeSync();
479
+ }
480
+ async copyToWritable(p, cred) {
481
+ const stats = await this.stat(p, cred);
482
+ if (stats.isDirectory()) {
483
+ await this._writable.mkdir(p, stats.mode, cred);
484
+ return;
485
+ }
486
+ const data = new Uint8Array(stats.size);
487
+ const readable = await this._readable.openFile(p, FileFlag.FromString('r'), cred);
488
+ await readable.read(data);
489
+ await readable.close();
490
+ const writable = await this._writable.openFile(p, FileFlag.FromString('w'), cred);
491
+ await writable.write(data);
492
+ await writable.close();
698
493
  }
699
494
  }
700
495
  /**
@@ -703,15 +498,15 @@ export class UnlockedOverlayFS extends BaseFileSystem {
703
498
  * file system.
704
499
  */
705
500
  export class OverlayFS extends LockedFS {
706
- static isAvailable() {
707
- return UnlockedOverlayFS.isAvailable();
501
+ async ready() {
502
+ await super.ready();
503
+ return this;
708
504
  }
709
505
  /**
710
506
  * @param options The options to initialize the OverlayFS with
711
507
  */
712
508
  constructor(options) {
713
509
  super(new UnlockedOverlayFS(options));
714
- this._ready = this._initialize();
715
510
  }
716
511
  getOverlayedFileSystems() {
717
512
  return super.fs.getOverlayedFileSystems();
@@ -725,26 +520,23 @@ export class OverlayFS extends LockedFS {
725
520
  unwrap() {
726
521
  return super.fs;
727
522
  }
728
- _initialize() {
729
- const _super = Object.create(null, {
730
- fs: { get: () => super.fs }
731
- });
732
- return __awaiter(this, void 0, void 0, function* () {
733
- yield _super.fs._initialize();
734
- return this;
735
- });
736
- }
737
523
  }
738
- _a = OverlayFS;
739
- OverlayFS.Name = 'OverlayFS';
740
- OverlayFS.Create = CreateBackend.bind(_a);
741
- OverlayFS.Options = {
742
- writable: {
743
- type: 'object',
744
- description: 'The file system to write modified files to.',
524
+ export const Overlay = {
525
+ name: 'Overlay',
526
+ options: {
527
+ writable: {
528
+ type: 'object',
529
+ description: 'The file system to write modified files to.',
530
+ },
531
+ readable: {
532
+ type: 'object',
533
+ description: 'The file system that initially populates this file system.',
534
+ },
535
+ },
536
+ isAvailable() {
537
+ return true;
745
538
  },
746
- readable: {
747
- type: 'object',
748
- description: 'The file system that initially populates this file system.',
539
+ create(options) {
540
+ return new OverlayFS(options);
749
541
  },
750
542
  };