@zenfs/core 0.9.2 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backends/Index.d.ts +3 -0
- package/dist/browser.min.js +3 -3
- package/dist/browser.min.js.map +3 -3
- package/dist/filesystem.d.ts +1 -1
- package/dist/filesystem.js +6 -6
- package/package.json +2 -9
- package/src/ApiError.ts +310 -0
- package/src/backends/AsyncStore.ts +635 -0
- package/src/backends/InMemory.ts +56 -0
- package/src/backends/Index.ts +500 -0
- package/src/backends/Locked.ts +181 -0
- package/src/backends/Overlay.ts +591 -0
- package/src/backends/SyncStore.ts +589 -0
- package/src/backends/backend.ts +152 -0
- package/src/config.ts +101 -0
- package/src/cred.ts +21 -0
- package/src/emulation/async.ts +910 -0
- package/src/emulation/constants.ts +176 -0
- package/src/emulation/dir.ts +139 -0
- package/src/emulation/index.ts +8 -0
- package/src/emulation/path.ts +468 -0
- package/src/emulation/promises.ts +1071 -0
- package/src/emulation/shared.ts +128 -0
- package/src/emulation/streams.ts +33 -0
- package/src/emulation/sync.ts +898 -0
- package/src/file.ts +721 -0
- package/src/filesystem.ts +544 -0
- package/src/index.ts +21 -0
- package/src/inode.ts +229 -0
- package/src/mutex.ts +52 -0
- package/src/stats.ts +385 -0
- package/src/utils.ts +287 -0
package/src/inode.ts
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { Stats, type StatsLike } from './stats.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Alias for an ino.
|
|
5
|
+
* This will be helpful if in the future inode numbers/IDs are changed to strings or numbers.
|
|
6
|
+
*/
|
|
7
|
+
export type Ino = bigint;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Max 32-bit integer
|
|
11
|
+
* @hidden
|
|
12
|
+
*/
|
|
13
|
+
export const size_max = 2 ** 32 - 1;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Room inode
|
|
17
|
+
* @hidden
|
|
18
|
+
*/
|
|
19
|
+
export const rootIno: Ino = 0n;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Generates a random 32 bit integer, then converts to a hex string
|
|
23
|
+
*/
|
|
24
|
+
function _random() {
|
|
25
|
+
return Math.round(Math.random() * 2 ** 32).toString(16);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Generate a random ino
|
|
30
|
+
* @internal
|
|
31
|
+
*/
|
|
32
|
+
export function randomIno(): Ino {
|
|
33
|
+
return BigInt('0x' + _random() + _random());
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Offsets for inode members
|
|
38
|
+
*/
|
|
39
|
+
enum Offset {
|
|
40
|
+
ino = 0,
|
|
41
|
+
size = 8, // offsets with a 64-bit size
|
|
42
|
+
mode = 12, // 16
|
|
43
|
+
nlink = 14, // 18
|
|
44
|
+
uid = 18, // 22
|
|
45
|
+
gid = 22, // 26
|
|
46
|
+
atime = 26, // 30
|
|
47
|
+
birthtime = 34, // 38
|
|
48
|
+
mtime = 42, // 46
|
|
49
|
+
ctime = 50, // 54
|
|
50
|
+
end = 58, // 62
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Generic inode definition that can easily be serialized.
|
|
55
|
+
*/
|
|
56
|
+
export class Inode implements StatsLike {
|
|
57
|
+
public readonly buffer: ArrayBufferLike;
|
|
58
|
+
|
|
59
|
+
public get data(): Uint8Array {
|
|
60
|
+
return new Uint8Array(this.buffer);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
protected view: DataView;
|
|
64
|
+
|
|
65
|
+
constructor(buffer?: ArrayBufferLike) {
|
|
66
|
+
const setDefaults = !buffer;
|
|
67
|
+
buffer ??= new ArrayBuffer(Offset.end);
|
|
68
|
+
if (buffer?.byteLength < Offset.end) {
|
|
69
|
+
throw new RangeError(`Can not create an inode from a buffer less than ${Offset.end} bytes`);
|
|
70
|
+
}
|
|
71
|
+
this.view = new DataView(buffer);
|
|
72
|
+
this.buffer = buffer;
|
|
73
|
+
|
|
74
|
+
if (!setDefaults) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// set defaults on a fresh inode
|
|
79
|
+
this.ino = randomIno();
|
|
80
|
+
this.nlink = 1;
|
|
81
|
+
this.size = 4096;
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
this.atimeMs = now;
|
|
84
|
+
this.mtimeMs = now;
|
|
85
|
+
this.ctimeMs = now;
|
|
86
|
+
this.birthtimeMs = now;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public get ino(): Ino {
|
|
90
|
+
return this.view.getBigUint64(Offset.ino, true);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public set ino(value: Ino) {
|
|
94
|
+
this.view.setBigUint64(Offset.ino, value, true);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public get size(): number {
|
|
98
|
+
return this.view.getUint32(Offset.size, true);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public set size(value: number) {
|
|
102
|
+
this.view.setUint32(Offset.size, value, true);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public get mode(): number {
|
|
106
|
+
return this.view.getUint16(Offset.mode, true);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public set mode(value: number) {
|
|
110
|
+
this.view.setUint16(Offset.mode, value, true);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public get nlink(): number {
|
|
114
|
+
return this.view.getUint32(Offset.nlink, true);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public set nlink(value: number) {
|
|
118
|
+
this.view.setUint32(Offset.nlink, value, true);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
public get uid(): number {
|
|
122
|
+
return this.view.getUint32(Offset.uid, true);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public set uid(value: number) {
|
|
126
|
+
this.view.setUint32(Offset.uid, value, true);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public get gid(): number {
|
|
130
|
+
return this.view.getUint32(Offset.gid, true);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public set gid(value: number) {
|
|
134
|
+
this.view.setUint32(Offset.gid, value, true);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public get atimeMs(): number {
|
|
138
|
+
return this.view.getFloat64(Offset.atime, true);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public set atimeMs(value: number) {
|
|
142
|
+
this.view.setFloat64(Offset.atime, value, true);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
public get birthtimeMs(): number {
|
|
146
|
+
return this.view.getFloat64(Offset.birthtime, true);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
public set birthtimeMs(value: number) {
|
|
150
|
+
this.view.setFloat64(Offset.birthtime, value, true);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
public get mtimeMs(): number {
|
|
154
|
+
return this.view.getFloat64(Offset.mtime, true);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
public set mtimeMs(value: number) {
|
|
158
|
+
this.view.setFloat64(Offset.mtime, value, true);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
public get ctimeMs(): number {
|
|
162
|
+
return this.view.getFloat64(Offset.ctime, true);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
public set ctimeMs(value: number) {
|
|
166
|
+
this.view.setFloat64(Offset.ctime, value, true);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Handy function that converts the Inode to a Node Stats object.
|
|
171
|
+
*/
|
|
172
|
+
public toStats(): Stats {
|
|
173
|
+
return new Stats(this);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Updates the Inode using information from the stats object. Used by file
|
|
178
|
+
* systems at sync time, e.g.:
|
|
179
|
+
* - Program opens file and gets a File object.
|
|
180
|
+
* - Program mutates file. File object is responsible for maintaining
|
|
181
|
+
* metadata changes locally -- typically in a Stats object.
|
|
182
|
+
* - Program closes file. File object's metadata changes are synced with the
|
|
183
|
+
* file system.
|
|
184
|
+
* @return True if any changes have occurred.
|
|
185
|
+
*/
|
|
186
|
+
public update(stats: Readonly<Stats>): boolean {
|
|
187
|
+
let hasChanged = false;
|
|
188
|
+
if (this.size !== stats.size) {
|
|
189
|
+
this.size = stats.size;
|
|
190
|
+
hasChanged = true;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (this.mode !== stats.mode) {
|
|
194
|
+
this.mode = stats.mode;
|
|
195
|
+
hasChanged = true;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (this.nlink !== stats.nlink) {
|
|
199
|
+
this.nlink = stats.nlink;
|
|
200
|
+
hasChanged = true;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (this.uid !== stats.uid) {
|
|
204
|
+
this.uid = stats.uid;
|
|
205
|
+
hasChanged = true;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (this.uid !== stats.uid) {
|
|
209
|
+
this.uid = stats.uid;
|
|
210
|
+
hasChanged = true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (this.atimeMs !== stats.atimeMs) {
|
|
214
|
+
this.atimeMs = stats.atimeMs;
|
|
215
|
+
hasChanged = true;
|
|
216
|
+
}
|
|
217
|
+
if (this.mtimeMs !== stats.mtimeMs) {
|
|
218
|
+
this.mtimeMs = stats.mtimeMs;
|
|
219
|
+
hasChanged = true;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (this.ctimeMs !== stats.ctimeMs) {
|
|
223
|
+
this.ctimeMs = stats.ctimeMs;
|
|
224
|
+
hasChanged = true;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return hasChanged;
|
|
228
|
+
}
|
|
229
|
+
}
|
package/src/mutex.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-recursive mutex
|
|
3
|
+
* @internal
|
|
4
|
+
*/
|
|
5
|
+
export class Mutex {
|
|
6
|
+
private _locks: Map<string, (() => void)[]> = new Map();
|
|
7
|
+
|
|
8
|
+
public lock(path: string): Promise<void> {
|
|
9
|
+
return new Promise(resolve => {
|
|
10
|
+
if (this._locks.has(path)) {
|
|
11
|
+
this._locks.get(path).push(resolve);
|
|
12
|
+
} else {
|
|
13
|
+
this._locks.set(path, []);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public unlock(path: string): void {
|
|
19
|
+
if (!this._locks.has(path)) {
|
|
20
|
+
throw new Error('unlock of a non-locked mutex');
|
|
21
|
+
}
|
|
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
|
+
|
|
37
|
+
this._locks.delete(path);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public tryLock(path: string): boolean {
|
|
41
|
+
if (this._locks.has(path)) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this._locks.set(path, []);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public isLocked(path: string): boolean {
|
|
50
|
+
return this._locks.has(path);
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/stats.ts
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import type * as Node from 'fs';
|
|
2
|
+
import { Cred } from './cred.js';
|
|
3
|
+
import { S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, S_IRWXG, S_IRWXO, S_IRWXU } from './emulation/constants.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Indicates the type of the given file. Applied to 'mode'.
|
|
7
|
+
*/
|
|
8
|
+
export enum FileType {
|
|
9
|
+
FILE = S_IFREG,
|
|
10
|
+
DIRECTORY = S_IFDIR,
|
|
11
|
+
SYMLINK = S_IFLNK,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
export interface StatsLike {
|
|
18
|
+
/**
|
|
19
|
+
* Size of the item in bytes.
|
|
20
|
+
* For directories/symlinks, this is normally the size of the struct that represents the item.
|
|
21
|
+
*/
|
|
22
|
+
size: number | bigint;
|
|
23
|
+
/**
|
|
24
|
+
* Unix-style file mode (e.g. 0o644) that includes the item type
|
|
25
|
+
* Type of the item can be FILE, DIRECTORY, SYMLINK, or SOCKET
|
|
26
|
+
*/
|
|
27
|
+
mode: number | bigint;
|
|
28
|
+
/**
|
|
29
|
+
* time of last access, in milliseconds since epoch
|
|
30
|
+
*/
|
|
31
|
+
atimeMs: number | bigint;
|
|
32
|
+
/**
|
|
33
|
+
* time of last modification, in milliseconds since epoch
|
|
34
|
+
*/
|
|
35
|
+
mtimeMs: number | bigint;
|
|
36
|
+
/**
|
|
37
|
+
* time of last time file status was changed, in milliseconds since epoch
|
|
38
|
+
*/
|
|
39
|
+
ctimeMs: number | bigint;
|
|
40
|
+
/**
|
|
41
|
+
* time of file creation, in milliseconds since epoch
|
|
42
|
+
*/
|
|
43
|
+
birthtimeMs: number | bigint;
|
|
44
|
+
/**
|
|
45
|
+
* the id of the user that owns the file
|
|
46
|
+
*/
|
|
47
|
+
uid: number | bigint;
|
|
48
|
+
/**
|
|
49
|
+
* the id of the group that owns the file
|
|
50
|
+
*/
|
|
51
|
+
gid: number | bigint;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Provides information about a particular entry in the file system.
|
|
56
|
+
* Common code used by both Stats and BigIntStats.
|
|
57
|
+
*/
|
|
58
|
+
export abstract class StatsCommon<T extends number | bigint> implements Node.StatsBase<T>, StatsLike {
|
|
59
|
+
protected abstract _isBigint: boolean;
|
|
60
|
+
|
|
61
|
+
protected get _typename(): string {
|
|
62
|
+
return this._isBigint ? 'bigint' : 'number';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
protected get _typename_inverse(): string {
|
|
66
|
+
return this._isBigint ? 'number' : 'bigint';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
protected _convert(arg: number | bigint | string | boolean): T {
|
|
70
|
+
return <T>(this._isBigint ? BigInt(arg) : Number(arg));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public blocks: T;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Unix-style file mode (e.g. 0o644) that includes the type of the item.
|
|
77
|
+
* Type of the item can be FILE, DIRECTORY, SYMLINK, or SOCKET
|
|
78
|
+
*/
|
|
79
|
+
public mode: T;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* ID of device containing file
|
|
83
|
+
*/
|
|
84
|
+
public dev: T = this._convert(0);
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* inode number
|
|
88
|
+
*/
|
|
89
|
+
public ino: T = this._convert(0);
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* device ID (if special file)
|
|
93
|
+
*/
|
|
94
|
+
public rdev: T = this._convert(0);
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* number of hard links
|
|
98
|
+
*/
|
|
99
|
+
public nlink: T = this._convert(1);
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* blocksize for file system I/O
|
|
103
|
+
*/
|
|
104
|
+
public blksize: T = this._convert(4096);
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* user ID of owner
|
|
108
|
+
*/
|
|
109
|
+
public uid: T = this._convert(0);
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* group ID of owner
|
|
113
|
+
*/
|
|
114
|
+
public gid: T = this._convert(0);
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Some file systems stash data on stats objects.
|
|
118
|
+
*/
|
|
119
|
+
public fileData?: Uint8Array = null;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* time of last access, in milliseconds since epoch
|
|
123
|
+
*/
|
|
124
|
+
public atimeMs: T;
|
|
125
|
+
|
|
126
|
+
public get atime(): Date {
|
|
127
|
+
return new Date(Number(this.atimeMs));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public set atime(value: Date) {
|
|
131
|
+
this.atimeMs = this._convert(value.getTime());
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* time of last modification, in milliseconds since epoch
|
|
136
|
+
*/
|
|
137
|
+
public mtimeMs: T;
|
|
138
|
+
|
|
139
|
+
public get mtime(): Date {
|
|
140
|
+
return new Date(Number(this.mtimeMs));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
public set mtime(value: Date) {
|
|
144
|
+
this.mtimeMs = this._convert(value.getTime());
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* time of last time file status was changed, in milliseconds since epoch
|
|
149
|
+
*/
|
|
150
|
+
public ctimeMs: T;
|
|
151
|
+
|
|
152
|
+
public get ctime(): Date {
|
|
153
|
+
return new Date(Number(this.ctimeMs));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
public set ctime(value: Date) {
|
|
157
|
+
this.ctimeMs = this._convert(value.getTime());
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* time of file creation, in milliseconds since epoch
|
|
162
|
+
*/
|
|
163
|
+
public birthtimeMs: T;
|
|
164
|
+
|
|
165
|
+
public get birthtime(): Date {
|
|
166
|
+
return new Date(Number(this.birthtimeMs));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
public set birthtime(value: Date) {
|
|
170
|
+
this.birthtimeMs = this._convert(value.getTime());
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Size of the item in bytes.
|
|
175
|
+
* For directories/symlinks, this is normally the size of the struct that represents the item.
|
|
176
|
+
*/
|
|
177
|
+
public size: T;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Creates a new stats instance from a stats-like object. Can be used to copy stats (note)
|
|
181
|
+
*/
|
|
182
|
+
constructor({ atimeMs, mtimeMs, ctimeMs, birthtimeMs, uid, gid, size, mode }: Partial<StatsLike> = {}) {
|
|
183
|
+
const currentTime = Date.now();
|
|
184
|
+
const resolveT = (val: number | bigint, _default: number) => <T>(typeof val == this._typename ? val : this._convert(typeof val == this._typename_inverse ? val : _default));
|
|
185
|
+
this.atimeMs = resolveT(atimeMs, currentTime);
|
|
186
|
+
this.mtimeMs = resolveT(mtimeMs, currentTime);
|
|
187
|
+
this.ctimeMs = resolveT(ctimeMs, currentTime);
|
|
188
|
+
this.birthtimeMs = resolveT(birthtimeMs, currentTime);
|
|
189
|
+
this.uid = resolveT(uid, 0);
|
|
190
|
+
this.gid = resolveT(gid, 0);
|
|
191
|
+
this.size = this._convert(size);
|
|
192
|
+
const itemType: FileType = Number(mode) & S_IFMT || FileType.FILE;
|
|
193
|
+
|
|
194
|
+
if (mode) {
|
|
195
|
+
this.mode = this._convert(mode);
|
|
196
|
+
} else {
|
|
197
|
+
switch (itemType) {
|
|
198
|
+
case FileType.FILE:
|
|
199
|
+
this.mode = this._convert(0o644);
|
|
200
|
+
break;
|
|
201
|
+
case FileType.DIRECTORY:
|
|
202
|
+
default:
|
|
203
|
+
this.mode = this._convert(0o777);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// number of 512B blocks allocated
|
|
207
|
+
this.blocks = this._convert(Math.ceil(Number(size) / 512));
|
|
208
|
+
// Check if mode also includes top-most bits, which indicate the file's type.
|
|
209
|
+
if ((this.mode & S_IFMT) == 0) {
|
|
210
|
+
this.mode = <T>(this.mode | this._convert(itemType));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* @returns true if this item is a file.
|
|
216
|
+
*/
|
|
217
|
+
public isFile(): boolean {
|
|
218
|
+
return (this.mode & S_IFMT) === S_IFREG;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* @returns True if this item is a directory.
|
|
223
|
+
*/
|
|
224
|
+
public isDirectory(): boolean {
|
|
225
|
+
return (this.mode & S_IFMT) === S_IFDIR;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* @returns true if this item is a symbolic link
|
|
230
|
+
*/
|
|
231
|
+
public isSymbolicLink(): boolean {
|
|
232
|
+
return (this.mode & S_IFMT) === S_IFLNK;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Currently unsupported
|
|
236
|
+
|
|
237
|
+
public isSocket(): boolean {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
public isBlockDevice(): boolean {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
public isCharacterDevice(): boolean {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
public isFIFO(): boolean {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Checks if a given user/group has access to this item
|
|
255
|
+
* @param mode The requested access, combination of W_OK, R_OK, and X_OK
|
|
256
|
+
* @param cred The requesting credentials
|
|
257
|
+
* @returns True if the request has access, false if the request does not
|
|
258
|
+
* @internal
|
|
259
|
+
*/
|
|
260
|
+
public hasAccess(mode: number, cred: Cred): boolean {
|
|
261
|
+
if (cred.euid === 0 || cred.egid === 0) {
|
|
262
|
+
//Running as root
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Mask for
|
|
267
|
+
const adjusted = (cred.uid == this.uid ? S_IRWXU : 0) | (cred.gid == this.gid ? S_IRWXG : 0) | S_IRWXO;
|
|
268
|
+
return (mode & this.mode & adjusted) == mode;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Convert the current stats object into a credentials object
|
|
273
|
+
* @internal
|
|
274
|
+
*/
|
|
275
|
+
public cred(uid: number = Number(this.uid), gid: number = Number(this.gid)): Cred {
|
|
276
|
+
return {
|
|
277
|
+
uid,
|
|
278
|
+
gid,
|
|
279
|
+
suid: Number(this.uid),
|
|
280
|
+
sgid: Number(this.gid),
|
|
281
|
+
euid: uid,
|
|
282
|
+
egid: gid,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Change the mode of the file. We use this helper function to prevent messing
|
|
288
|
+
* up the type of the file, which is encoded in mode.
|
|
289
|
+
* @internal
|
|
290
|
+
*/
|
|
291
|
+
public chmod(mode: number): void {
|
|
292
|
+
this.mode = this._convert((this.mode & S_IFMT) | mode);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Change the owner user/group of the file.
|
|
297
|
+
* This function makes sure it is a valid UID/GID (that is, a 32 unsigned int)
|
|
298
|
+
* @internal
|
|
299
|
+
*/
|
|
300
|
+
public chown(uid: number | bigint, gid: number | bigint): void {
|
|
301
|
+
uid = Number(uid);
|
|
302
|
+
gid = Number(gid);
|
|
303
|
+
if (!isNaN(uid) && 0 <= uid && uid < 2 ** 32) {
|
|
304
|
+
this.uid = this._convert(uid);
|
|
305
|
+
}
|
|
306
|
+
if (!isNaN(gid) && 0 <= gid && gid < 2 ** 32) {
|
|
307
|
+
this.gid = this._convert(gid);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Implementation of Node's `Stats`.
|
|
314
|
+
*
|
|
315
|
+
* Attribute descriptions are from `man 2 stat'
|
|
316
|
+
* @see http://nodejs.org/api/fs.html#fs_class_fs_stats
|
|
317
|
+
* @see http://man7.org/linux/man-pages/man2/stat.2.html
|
|
318
|
+
*/
|
|
319
|
+
export class Stats extends StatsCommon<number> implements Node.Stats, StatsLike {
|
|
320
|
+
protected _isBigint = false;
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Clones the stats object.
|
|
324
|
+
* @deprecated use `new Stats(stats)`
|
|
325
|
+
*/
|
|
326
|
+
public static clone(stats: Stats): Stats {
|
|
327
|
+
return new Stats(stats);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
Stats satisfies typeof Node.Stats;
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Stats with bigint
|
|
334
|
+
* @todo Implement with bigint instead of wrapping Stats
|
|
335
|
+
*/
|
|
336
|
+
export class BigIntStats extends StatsCommon<bigint> implements Node.BigIntStats, StatsLike {
|
|
337
|
+
protected _isBigint = true;
|
|
338
|
+
|
|
339
|
+
public atimeNs: bigint;
|
|
340
|
+
public mtimeNs: bigint;
|
|
341
|
+
public ctimeNs: bigint;
|
|
342
|
+
public birthtimeNs: bigint;
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Clone a stats object.
|
|
346
|
+
* @deprecated use `new BigIntStats(stats)`
|
|
347
|
+
*/
|
|
348
|
+
public static clone(stats: BigIntStats | Stats): BigIntStats {
|
|
349
|
+
return new BigIntStats(stats);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export class StatsFs implements Node.StatsFsBase<number> {
|
|
354
|
+
/** Type of file system. */
|
|
355
|
+
public type: number;
|
|
356
|
+
/** Optimal transfer block size. */
|
|
357
|
+
public bsize: number;
|
|
358
|
+
/** Total data blocks in file system. */
|
|
359
|
+
public blocks: number;
|
|
360
|
+
/** Free blocks in file system. */
|
|
361
|
+
public bfree: number;
|
|
362
|
+
/** Available blocks for unprivileged users */
|
|
363
|
+
public bavail: number;
|
|
364
|
+
/** Total file nodes in file system. */
|
|
365
|
+
public files: number;
|
|
366
|
+
/** Free file nodes in file system. */
|
|
367
|
+
public ffree: number;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export class BigIntStatsFs implements Node.StatsFsBase<bigint> {
|
|
371
|
+
/** Type of file system. */
|
|
372
|
+
public type: bigint;
|
|
373
|
+
/** Optimal transfer block size. */
|
|
374
|
+
public bsize: bigint;
|
|
375
|
+
/** Total data blocks in file system. */
|
|
376
|
+
public blocks: bigint;
|
|
377
|
+
/** Free blocks in file system. */
|
|
378
|
+
public bfree: bigint;
|
|
379
|
+
/** Available blocks for unprivileged users */
|
|
380
|
+
public bavail: bigint;
|
|
381
|
+
/** Total file nodes in file system. */
|
|
382
|
+
public files: bigint;
|
|
383
|
+
/** Free file nodes in file system. */
|
|
384
|
+
public ffree: bigint;
|
|
385
|
+
}
|