@zenfs/core 0.0.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 (55) hide show
  1. package/README.md +293 -0
  2. package/dist/ApiError.d.ts +86 -0
  3. package/dist/ApiError.js +135 -0
  4. package/dist/backends/AsyncMirror.d.ts +102 -0
  5. package/dist/backends/AsyncMirror.js +252 -0
  6. package/dist/backends/AsyncStore.d.ts +166 -0
  7. package/dist/backends/AsyncStore.js +620 -0
  8. package/dist/backends/FolderAdapter.d.ts +52 -0
  9. package/dist/backends/FolderAdapter.js +184 -0
  10. package/dist/backends/InMemory.d.ts +25 -0
  11. package/dist/backends/InMemory.js +46 -0
  12. package/dist/backends/Locked.d.ts +64 -0
  13. package/dist/backends/Locked.js +302 -0
  14. package/dist/backends/OverlayFS.d.ts +120 -0
  15. package/dist/backends/OverlayFS.js +749 -0
  16. package/dist/backends/SyncStore.d.ts +223 -0
  17. package/dist/backends/SyncStore.js +479 -0
  18. package/dist/backends/backend.d.ts +73 -0
  19. package/dist/backends/backend.js +14 -0
  20. package/dist/backends/index.d.ts +11 -0
  21. package/dist/backends/index.js +15 -0
  22. package/dist/browser.min.js +12 -0
  23. package/dist/browser.min.js.map +7 -0
  24. package/dist/cred.d.ts +14 -0
  25. package/dist/cred.js +15 -0
  26. package/dist/emulation/callbacks.d.ts +382 -0
  27. package/dist/emulation/callbacks.js +422 -0
  28. package/dist/emulation/constants.d.ts +101 -0
  29. package/dist/emulation/constants.js +110 -0
  30. package/dist/emulation/fs.d.ts +7 -0
  31. package/dist/emulation/fs.js +5 -0
  32. package/dist/emulation/index.d.ts +5 -0
  33. package/dist/emulation/index.js +7 -0
  34. package/dist/emulation/promises.d.ts +309 -0
  35. package/dist/emulation/promises.js +521 -0
  36. package/dist/emulation/shared.d.ts +62 -0
  37. package/dist/emulation/shared.js +192 -0
  38. package/dist/emulation/sync.d.ts +278 -0
  39. package/dist/emulation/sync.js +392 -0
  40. package/dist/file.d.ts +449 -0
  41. package/dist/file.js +576 -0
  42. package/dist/filesystem.d.ts +367 -0
  43. package/dist/filesystem.js +542 -0
  44. package/dist/index.d.ts +78 -0
  45. package/dist/index.js +113 -0
  46. package/dist/inode.d.ts +51 -0
  47. package/dist/inode.js +112 -0
  48. package/dist/mutex.d.ts +12 -0
  49. package/dist/mutex.js +48 -0
  50. package/dist/stats.d.ts +98 -0
  51. package/dist/stats.js +226 -0
  52. package/dist/utils.d.ts +52 -0
  53. package/dist/utils.js +261 -0
  54. package/license.md +122 -0
  55. package/package.json +61 -0
package/dist/index.js ADDED
@@ -0,0 +1,113 @@
1
+ /**
2
+ * ZenFS's main module. This is exposed in the browser via the ZenFS global.
3
+ */
4
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
5
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
6
+ return new (P || (P = Promise))(function (resolve, reject) {
7
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
8
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
9
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
10
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
11
+ });
12
+ };
13
+ import fs from './emulation/fs';
14
+ import { FileSystem } from './filesystem';
15
+ import { backends } from './backends';
16
+ import { ErrorCode, ApiError } from './ApiError';
17
+ import { Cred } from './cred';
18
+ import * as process from 'process';
19
+ import { setCred } from './emulation/shared';
20
+ if (process && process['initializeTTYs']) {
21
+ process['initializeTTYs']();
22
+ }
23
+ /**
24
+ * Initializes ZenFS with the given file systems.
25
+ */
26
+ export function initialize(mounts, uid = 0, gid = 0) {
27
+ setCred(new Cred(uid, gid, uid, gid, uid, gid));
28
+ return fs.initialize(mounts);
29
+ }
30
+ function _configure(config) {
31
+ return __awaiter(this, void 0, void 0, function* () {
32
+ if ('fs' in config || config instanceof FileSystem) {
33
+ // single FS
34
+ config = { '/': config };
35
+ }
36
+ for (let [point, value] of Object.entries(config)) {
37
+ if (typeof value == 'number') {
38
+ //should never happen
39
+ continue;
40
+ }
41
+ point = point.toString(); // so linting stops complaining that point should be declared with const, which can't be done since value is assigned to
42
+ if (value instanceof FileSystem) {
43
+ continue;
44
+ }
45
+ if (typeof value == 'string') {
46
+ value = { fs: value };
47
+ }
48
+ config[point] = yield getFileSystem(value);
49
+ }
50
+ return initialize(config);
51
+ });
52
+ }
53
+ export function configure(config, cb) {
54
+ // Promise version
55
+ if (typeof cb != 'function') {
56
+ return _configure(config);
57
+ }
58
+ // Callback version
59
+ _configure(config)
60
+ .then(() => cb())
61
+ .catch(err => cb(err));
62
+ return;
63
+ }
64
+ function _getFileSystem({ fs: fsName, options = {} }) {
65
+ return __awaiter(this, void 0, void 0, function* () {
66
+ if (!fsName) {
67
+ throw new ApiError(ErrorCode.EPERM, 'Missing "fs" property on configuration object.');
68
+ }
69
+ if (typeof options !== 'object' || options === null) {
70
+ throw new ApiError(ErrorCode.EINVAL, 'Invalid "options" property on configuration object.');
71
+ }
72
+ const props = Object.keys(options).filter(k => k != 'fs');
73
+ for (const prop of props) {
74
+ const opt = options[prop];
75
+ if (opt === null || typeof opt !== 'object' || !('fs' in opt)) {
76
+ continue;
77
+ }
78
+ const fs = yield _getFileSystem(opt);
79
+ options[prop] = fs;
80
+ }
81
+ const fsc = backends[fsName];
82
+ if (!fsc) {
83
+ throw new ApiError(ErrorCode.EPERM, `File system ${fsName} is not available in ZenFS.`);
84
+ }
85
+ else {
86
+ return fsc.Create(options);
87
+ }
88
+ });
89
+ }
90
+ export function getFileSystem(config, cb) {
91
+ // Promise version
92
+ if (typeof cb != 'function') {
93
+ return _getFileSystem(config);
94
+ }
95
+ // Callback version
96
+ _getFileSystem(config)
97
+ .then(fs => cb(null, fs))
98
+ .catch(err => cb(err));
99
+ return;
100
+ }
101
+ export * from './backends';
102
+ export * from './backends/AsyncStore';
103
+ export * from './backends/SyncStore';
104
+ export * from './ApiError';
105
+ export * from './cred';
106
+ export * from './file';
107
+ export * from './filesystem';
108
+ export * from './inode';
109
+ export * from './mutex';
110
+ export * from './stats';
111
+ export * from './utils';
112
+ export { fs };
113
+ export default fs;
@@ -0,0 +1,51 @@
1
+ /// <reference types="node" />
2
+ import { Stats } from './stats';
3
+ /**
4
+ * Generic inode definition that can easily be serialized.
5
+ */
6
+ export default class Inode {
7
+ id: string;
8
+ size: number;
9
+ mode: number;
10
+ atime: number;
11
+ mtime: number;
12
+ ctime: number;
13
+ uid: number;
14
+ gid: number;
15
+ /**
16
+ * Converts the buffer into an Inode.
17
+ */
18
+ static fromBuffer(buffer: Buffer): Inode;
19
+ constructor(id: string, size: number, mode: number, atime: number, mtime: number, ctime: number, uid: number, gid: number);
20
+ /**
21
+ * Handy function that converts the Inode to a Node Stats object.
22
+ */
23
+ toStats(): Stats;
24
+ /**
25
+ * Get the size of this Inode, in bytes.
26
+ */
27
+ getSize(): number;
28
+ /**
29
+ * Writes the inode into the start of the buffer.
30
+ */
31
+ toBuffer(buff?: Buffer): Buffer;
32
+ /**
33
+ * Updates the Inode using information from the stats object. Used by file
34
+ * systems at sync time, e.g.:
35
+ * - Program opens file and gets a File object.
36
+ * - Program mutates file. File object is responsible for maintaining
37
+ * metadata changes locally -- typically in a Stats object.
38
+ * - Program closes file. File object's metadata changes are synced with the
39
+ * file system.
40
+ * @return True if any changes have occurred.
41
+ */
42
+ update(stats: Stats): boolean;
43
+ /**
44
+ * @return [Boolean] True if this item is a file.
45
+ */
46
+ isFile(): boolean;
47
+ /**
48
+ * @return [Boolean] True if this item is a directory.
49
+ */
50
+ isDirectory(): boolean;
51
+ }
package/dist/inode.js ADDED
@@ -0,0 +1,112 @@
1
+ import { Stats, FileType } from './stats';
2
+ import { Buffer } from 'buffer';
3
+ /**
4
+ * Generic inode definition that can easily be serialized.
5
+ */
6
+ export default class Inode {
7
+ /**
8
+ * Converts the buffer into an Inode.
9
+ */
10
+ static fromBuffer(buffer) {
11
+ if (buffer === undefined) {
12
+ throw new Error('NO');
13
+ }
14
+ return new Inode(buffer.toString('ascii', 38), buffer.readUInt32LE(0), buffer.readUInt16LE(4), buffer.readDoubleLE(6), buffer.readDoubleLE(14), buffer.readDoubleLE(22), buffer.readUInt32LE(30), buffer.readUInt32LE(34));
15
+ }
16
+ constructor(id, size, mode, atime, mtime, ctime, uid, gid) {
17
+ this.id = id;
18
+ this.size = size;
19
+ this.mode = mode;
20
+ this.atime = atime;
21
+ this.mtime = mtime;
22
+ this.ctime = ctime;
23
+ this.uid = uid;
24
+ this.gid = gid;
25
+ }
26
+ /**
27
+ * Handy function that converts the Inode to a Node Stats object.
28
+ */
29
+ toStats() {
30
+ return new Stats((this.mode & 0xf000) === FileType.DIRECTORY ? FileType.DIRECTORY : FileType.FILE, this.size, this.mode, this.atime, this.mtime, this.ctime, this.uid, this.gid);
31
+ }
32
+ /**
33
+ * Get the size of this Inode, in bytes.
34
+ */
35
+ getSize() {
36
+ // ASSUMPTION: ID is ASCII (1 byte per char).
37
+ return 38 + this.id.length;
38
+ }
39
+ /**
40
+ * Writes the inode into the start of the buffer.
41
+ */
42
+ toBuffer(buff = Buffer.alloc(this.getSize())) {
43
+ buff.writeUInt32LE(this.size, 0);
44
+ buff.writeUInt16LE(this.mode, 4);
45
+ buff.writeDoubleLE(this.atime, 6);
46
+ buff.writeDoubleLE(this.mtime, 14);
47
+ buff.writeDoubleLE(this.ctime, 22);
48
+ buff.writeUInt32LE(this.uid, 30);
49
+ buff.writeUInt32LE(this.gid, 34);
50
+ buff.write(this.id, 38, this.id.length, 'ascii');
51
+ return buff;
52
+ }
53
+ /**
54
+ * Updates the Inode using information from the stats object. Used by file
55
+ * systems at sync time, e.g.:
56
+ * - Program opens file and gets a File object.
57
+ * - Program mutates file. File object is responsible for maintaining
58
+ * metadata changes locally -- typically in a Stats object.
59
+ * - Program closes file. File object's metadata changes are synced with the
60
+ * file system.
61
+ * @return True if any changes have occurred.
62
+ */
63
+ update(stats) {
64
+ let hasChanged = false;
65
+ if (this.size !== stats.size) {
66
+ this.size = stats.size;
67
+ hasChanged = true;
68
+ }
69
+ if (this.mode !== stats.mode) {
70
+ this.mode = stats.mode;
71
+ hasChanged = true;
72
+ }
73
+ const atimeMs = stats.atime.getTime();
74
+ if (this.atime !== atimeMs) {
75
+ this.atime = atimeMs;
76
+ hasChanged = true;
77
+ }
78
+ const mtimeMs = stats.mtime.getTime();
79
+ if (this.mtime !== mtimeMs) {
80
+ this.mtime = mtimeMs;
81
+ hasChanged = true;
82
+ }
83
+ const ctimeMs = stats.ctime.getTime();
84
+ if (this.ctime !== ctimeMs) {
85
+ this.ctime = ctimeMs;
86
+ hasChanged = true;
87
+ }
88
+ if (this.uid !== stats.uid) {
89
+ this.uid = stats.uid;
90
+ hasChanged = true;
91
+ }
92
+ if (this.uid !== stats.uid) {
93
+ this.uid = stats.uid;
94
+ hasChanged = true;
95
+ }
96
+ return hasChanged;
97
+ }
98
+ // XXX: Copied from Stats. Should reconcile these two into something more
99
+ // compact.
100
+ /**
101
+ * @return [Boolean] True if this item is a file.
102
+ */
103
+ isFile() {
104
+ return (this.mode & 0xf000) === FileType.FILE;
105
+ }
106
+ /**
107
+ * @return [Boolean] True if this item is a directory.
108
+ */
109
+ isDirectory() {
110
+ return (this.mode & 0xf000) === FileType.DIRECTORY;
111
+ }
112
+ }
@@ -0,0 +1,12 @@
1
+ export type MutexCallback = () => void;
2
+ /**
3
+ * Non-recursive mutex
4
+ * @internal
5
+ */
6
+ export default class Mutex {
7
+ private _locks;
8
+ lock(path: string): Promise<void>;
9
+ unlock(path: string): void;
10
+ tryLock(path: string): boolean;
11
+ isLocked(path: string): boolean;
12
+ }
package/dist/mutex.js ADDED
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Non-recursive mutex
3
+ * @internal
4
+ */
5
+ export default class Mutex {
6
+ constructor() {
7
+ this._locks = new Map();
8
+ }
9
+ lock(path) {
10
+ return new Promise(resolve => {
11
+ if (this._locks.has(path)) {
12
+ this._locks.get(path).push(resolve);
13
+ }
14
+ else {
15
+ this._locks.set(path, []);
16
+ }
17
+ });
18
+ }
19
+ unlock(path) {
20
+ if (!this._locks.has(path)) {
21
+ throw new Error('unlock of a non-locked mutex');
22
+ }
23
+ const next = this._locks.get(path).shift();
24
+ /*
25
+ don't unlock - we want to queue up next for the
26
+ end of the current task execution, but we don't
27
+ want it to be called inline with whatever the
28
+ current stack is. This way we still get the nice
29
+ behavior that an unlock immediately followed by a
30
+ lock won't cause starvation.
31
+ */
32
+ if (next) {
33
+ setTimeout(next, 0);
34
+ return;
35
+ }
36
+ this._locks.delete(path);
37
+ }
38
+ tryLock(path) {
39
+ if (this._locks.has(path)) {
40
+ return false;
41
+ }
42
+ this._locks.set(path, []);
43
+ return true;
44
+ }
45
+ isLocked(path) {
46
+ return this._locks.has(path);
47
+ }
48
+ }
@@ -0,0 +1,98 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import type { StatsBase } from 'fs';
4
+ import { Cred } from './cred';
5
+ /**
6
+ * Indicates the type of the given file. Applied to 'mode'.
7
+ */
8
+ export declare enum FileType {
9
+ FILE,
10
+ DIRECTORY,
11
+ SYMLINK
12
+ }
13
+ /**
14
+ * Implementation of Node's `Stats`.
15
+ *
16
+ * Attribute descriptions are from `man 2 stat'
17
+ * @see http://nodejs.org/api/fs.html#fs_class_fs_stats
18
+ * @see http://man7.org/linux/man-pages/man2/stat.2.html
19
+ */
20
+ export declare class Stats implements StatsBase<number> {
21
+ static fromBuffer(buffer: Buffer): Stats;
22
+ /**
23
+ * Clones the stats object.
24
+ */
25
+ static clone(s: Stats): Stats;
26
+ blocks: number;
27
+ mode: number;
28
+ dev: number;
29
+ ino: number;
30
+ rdev: number;
31
+ nlink: number;
32
+ blksize: number;
33
+ uid: number;
34
+ gid: number;
35
+ fileData: Buffer | null;
36
+ atimeMs: number;
37
+ mtimeMs: number;
38
+ ctimeMs: number;
39
+ birthtimeMs: number;
40
+ size: number;
41
+ get atime(): Date;
42
+ get mtime(): Date;
43
+ get ctime(): Date;
44
+ get birthtime(): Date;
45
+ /**
46
+ * Provides information about a particular entry in the file system.
47
+ * @param itemType Type of the item (FILE, DIRECTORY, SYMLINK, or SOCKET)
48
+ * @param size Size of the item in bytes. For directories/symlinks,
49
+ * this is normally the size of the struct that represents the item.
50
+ * @param mode Unix-style file mode (e.g. 0o644)
51
+ * @param atimeMs time of last access, in milliseconds since epoch
52
+ * @param mtimeMs time of last modification, in milliseconds since epoch
53
+ * @param ctimeMs time of last time file status was changed, in milliseconds since epoch
54
+ * @param uid the id of the user that owns the file
55
+ * @param gid the id of the group that owns the file
56
+ * @param birthtimeMs time of file creation, in milliseconds since epoch
57
+ */
58
+ constructor(itemType: FileType, size: number, mode?: number, atimeMs?: number, mtimeMs?: number, ctimeMs?: number, uid?: number, gid?: number, birthtimeMs?: number);
59
+ toBuffer(): Buffer;
60
+ /**
61
+ * @return [Boolean] True if this item is a file.
62
+ */
63
+ isFile(): boolean;
64
+ /**
65
+ * @return [Boolean] True if this item is a directory.
66
+ */
67
+ isDirectory(): boolean;
68
+ /**
69
+ * @return [Boolean] True if this item is a symbolic link (only valid through lstat)
70
+ */
71
+ isSymbolicLink(): boolean;
72
+ /**
73
+ * Checks if a given user/group has access to this item
74
+ * @param mode The request access as 4 bits (unused, read, write, execute)
75
+ * @param uid The requesting UID
76
+ * @param gid The requesting GID
77
+ * @returns [Boolean] True if the request has access, false if the request does not
78
+ */
79
+ hasAccess(mode: number, cred: Cred): boolean;
80
+ /**
81
+ * Convert the current stats object into a cred object
82
+ */
83
+ getCred(uid?: number, gid?: number): Cred;
84
+ /**
85
+ * Change the mode of the file. We use this helper function to prevent messing
86
+ * up the type of the file, which is encoded in mode.
87
+ */
88
+ chmod(mode: number): void;
89
+ /**
90
+ * Change the owner user/group of the file.
91
+ * This function makes sure it is a valid UID/GID (that is, a 32 unsigned int)
92
+ */
93
+ chown(uid: number, gid: number): void;
94
+ isSocket(): boolean;
95
+ isBlockDevice(): boolean;
96
+ isCharacterDevice(): boolean;
97
+ isFIFO(): boolean;
98
+ }
package/dist/stats.js ADDED
@@ -0,0 +1,226 @@
1
+ import { Cred } from './cred';
2
+ import { Buffer } from 'buffer';
3
+ import { S_IFDIR, S_IFLNK, S_IFMT, S_IFREG } from './emulation/constants';
4
+ /**
5
+ * Indicates the type of the given file. Applied to 'mode'.
6
+ */
7
+ export var FileType;
8
+ (function (FileType) {
9
+ FileType[FileType["FILE"] = S_IFREG] = "FILE";
10
+ FileType[FileType["DIRECTORY"] = S_IFDIR] = "DIRECTORY";
11
+ FileType[FileType["SYMLINK"] = S_IFLNK] = "SYMLINK";
12
+ })(FileType || (FileType = {}));
13
+ /**
14
+ * Implementation of Node's `Stats`.
15
+ *
16
+ * Attribute descriptions are from `man 2 stat'
17
+ * @see http://nodejs.org/api/fs.html#fs_class_fs_stats
18
+ * @see http://man7.org/linux/man-pages/man2/stat.2.html
19
+ */
20
+ export class Stats {
21
+ static fromBuffer(buffer) {
22
+ const size = buffer.readUInt32LE(0), mode = buffer.readUInt32LE(4), atime = buffer.readDoubleLE(8), mtime = buffer.readDoubleLE(16), ctime = buffer.readDoubleLE(24), uid = buffer.readUInt32LE(32), gid = buffer.readUInt32LE(36);
23
+ return new Stats(mode & S_IFMT, size, mode & ~S_IFMT, atime, mtime, ctime, uid, gid);
24
+ }
25
+ /**
26
+ * Clones the stats object.
27
+ */
28
+ static clone(s) {
29
+ return new Stats(s.mode & S_IFMT, s.size, s.mode & ~S_IFMT, s.atimeMs, s.mtimeMs, s.ctimeMs, s.uid, s.gid, s.birthtimeMs);
30
+ }
31
+ get atime() {
32
+ return new Date(this.atimeMs);
33
+ }
34
+ get mtime() {
35
+ return new Date(this.mtimeMs);
36
+ }
37
+ get ctime() {
38
+ return new Date(this.ctimeMs);
39
+ }
40
+ get birthtime() {
41
+ return new Date(this.birthtimeMs);
42
+ }
43
+ /**
44
+ * Provides information about a particular entry in the file system.
45
+ * @param itemType Type of the item (FILE, DIRECTORY, SYMLINK, or SOCKET)
46
+ * @param size Size of the item in bytes. For directories/symlinks,
47
+ * this is normally the size of the struct that represents the item.
48
+ * @param mode Unix-style file mode (e.g. 0o644)
49
+ * @param atimeMs time of last access, in milliseconds since epoch
50
+ * @param mtimeMs time of last modification, in milliseconds since epoch
51
+ * @param ctimeMs time of last time file status was changed, in milliseconds since epoch
52
+ * @param uid the id of the user that owns the file
53
+ * @param gid the id of the group that owns the file
54
+ * @param birthtimeMs time of file creation, in milliseconds since epoch
55
+ */
56
+ constructor(itemType, size, mode, atimeMs, mtimeMs, ctimeMs, uid, gid, birthtimeMs) {
57
+ // ID of device containing file
58
+ this.dev = 0;
59
+ // inode number
60
+ this.ino = 0;
61
+ // device ID (if special file)
62
+ this.rdev = 0;
63
+ // number of hard links
64
+ this.nlink = 1;
65
+ // blocksize for file system I/O
66
+ this.blksize = 4096;
67
+ // user ID of owner
68
+ this.uid = 0;
69
+ // group ID of owner
70
+ this.gid = 0;
71
+ // Some file systems stash data on stats objects.
72
+ this.fileData = null;
73
+ this.size = size;
74
+ let currentTime = 0;
75
+ if (typeof atimeMs !== 'number') {
76
+ currentTime = Date.now();
77
+ atimeMs = currentTime;
78
+ }
79
+ if (typeof mtimeMs !== 'number') {
80
+ if (!currentTime) {
81
+ currentTime = Date.now();
82
+ }
83
+ mtimeMs = currentTime;
84
+ }
85
+ if (typeof ctimeMs !== 'number') {
86
+ if (!currentTime) {
87
+ currentTime = Date.now();
88
+ }
89
+ ctimeMs = currentTime;
90
+ }
91
+ if (typeof birthtimeMs !== 'number') {
92
+ if (!currentTime) {
93
+ currentTime = Date.now();
94
+ }
95
+ birthtimeMs = currentTime;
96
+ }
97
+ if (typeof uid !== 'number') {
98
+ uid = 0;
99
+ }
100
+ if (typeof gid !== 'number') {
101
+ gid = 0;
102
+ }
103
+ this.atimeMs = atimeMs;
104
+ this.ctimeMs = ctimeMs;
105
+ this.mtimeMs = mtimeMs;
106
+ this.birthtimeMs = birthtimeMs;
107
+ if (!mode) {
108
+ switch (itemType) {
109
+ case FileType.FILE:
110
+ this.mode = 0o644;
111
+ break;
112
+ case FileType.DIRECTORY:
113
+ default:
114
+ this.mode = 0o777;
115
+ }
116
+ }
117
+ else {
118
+ this.mode = mode;
119
+ }
120
+ // number of 512B blocks allocated
121
+ this.blocks = Math.ceil(size / 512);
122
+ // Check if mode also includes top-most bits, which indicate the file's
123
+ // type.
124
+ if ((this.mode & S_IFMT) == 0) {
125
+ this.mode |= itemType;
126
+ }
127
+ }
128
+ toBuffer() {
129
+ const buffer = Buffer.alloc(32);
130
+ buffer.writeUInt32LE(this.size, 0);
131
+ buffer.writeUInt32LE(this.mode, 4);
132
+ buffer.writeDoubleLE(this.atime.getTime(), 8);
133
+ buffer.writeDoubleLE(this.mtime.getTime(), 16);
134
+ buffer.writeDoubleLE(this.ctime.getTime(), 24);
135
+ buffer.writeUInt32LE(this.uid, 32);
136
+ buffer.writeUInt32LE(this.gid, 36);
137
+ return buffer;
138
+ }
139
+ /**
140
+ * @return [Boolean] True if this item is a file.
141
+ */
142
+ isFile() {
143
+ return (this.mode & S_IFMT) === S_IFREG;
144
+ }
145
+ /**
146
+ * @return [Boolean] True if this item is a directory.
147
+ */
148
+ isDirectory() {
149
+ return (this.mode & S_IFMT) === S_IFDIR;
150
+ }
151
+ /**
152
+ * @return [Boolean] True if this item is a symbolic link (only valid through lstat)
153
+ */
154
+ isSymbolicLink() {
155
+ return (this.mode & S_IFMT) === S_IFLNK;
156
+ }
157
+ /**
158
+ * Checks if a given user/group has access to this item
159
+ * @param mode The request access as 4 bits (unused, read, write, execute)
160
+ * @param uid The requesting UID
161
+ * @param gid The requesting GID
162
+ * @returns [Boolean] True if the request has access, false if the request does not
163
+ */
164
+ hasAccess(mode, cred) {
165
+ if (cred.euid === 0 || cred.egid === 0) {
166
+ //Running as root
167
+ return true;
168
+ }
169
+ const perms = this.mode & ~S_IFMT;
170
+ let uMode = 0xf, gMode = 0xf, wMode = 0xf;
171
+ if (cred.euid == this.uid) {
172
+ const uPerms = (0xf00 & perms) >> 8;
173
+ uMode = (mode ^ uPerms) & mode;
174
+ }
175
+ if (cred.egid == this.gid) {
176
+ const gPerms = (0xf0 & perms) >> 4;
177
+ gMode = (mode ^ gPerms) & mode;
178
+ }
179
+ const wPerms = 0xf & perms;
180
+ wMode = (mode ^ wPerms) & mode;
181
+ /*
182
+ Result = 0b0xxx (read, write, execute)
183
+ If any bits are set that means the request does not have that permission.
184
+ */
185
+ const result = uMode & gMode & wMode;
186
+ return !result;
187
+ }
188
+ /**
189
+ * Convert the current stats object into a cred object
190
+ */
191
+ getCred(uid = this.uid, gid = this.gid) {
192
+ return new Cred(uid, gid, this.uid, this.gid, uid, gid);
193
+ }
194
+ /**
195
+ * Change the mode of the file. We use this helper function to prevent messing
196
+ * up the type of the file, which is encoded in mode.
197
+ */
198
+ chmod(mode) {
199
+ this.mode = (this.mode & S_IFMT) | mode;
200
+ }
201
+ /**
202
+ * Change the owner user/group of the file.
203
+ * This function makes sure it is a valid UID/GID (that is, a 32 unsigned int)
204
+ */
205
+ chown(uid, gid) {
206
+ if (!isNaN(+uid) && 0 <= +uid && +uid < Math.pow(2, 32)) {
207
+ this.uid = uid;
208
+ }
209
+ if (!isNaN(+gid) && 0 <= +gid && +gid < Math.pow(2, 32)) {
210
+ this.gid = gid;
211
+ }
212
+ }
213
+ // We don't support the following types of files.
214
+ isSocket() {
215
+ return false;
216
+ }
217
+ isBlockDevice() {
218
+ return false;
219
+ }
220
+ isCharacterDevice() {
221
+ return false;
222
+ }
223
+ isFIFO() {
224
+ return false;
225
+ }
226
+ }