UNPKG

12.3 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.File = exports.Link = exports.Node = exports.SEP = void 0;
4const process_1 = require("./process");
5const buffer_1 = require("./internal/buffer");
6const constants_1 = require("./constants");
7const events_1 = require("events");
8const Stats_1 = require("./Stats");
9const { S_IFMT, S_IFDIR, S_IFREG, S_IFLNK, O_APPEND } = constants_1.constants;
10const getuid = () => { var _a, _b; return (_b = (_a = process_1.default.getuid) === null || _a === void 0 ? void 0 : _a.call(process_1.default)) !== null && _b !== void 0 ? _b : 0; };
11const getgid = () => { var _a, _b; return (_b = (_a = process_1.default.getgid) === null || _a === void 0 ? void 0 : _a.call(process_1.default)) !== null && _b !== void 0 ? _b : 0; };
12exports.SEP = '/';
13/**
14 * Node in a file system (like i-node, v-node).
15 */
16class Node extends events_1.EventEmitter {
17 constructor(ino, perm = 0o666) {
18 super();
19 // User ID and group ID.
20 this._uid = getuid();
21 this._gid = getgid();
22 this._atime = new Date();
23 this._mtime = new Date();
24 this._ctime = new Date();
25 this._perm = 0o666; // Permissions `chmod`, `fchmod`
26 this.mode = S_IFREG; // S_IFDIR, S_IFREG, etc.. (file by default?)
27 // Number of hard links pointing at this Node.
28 this._nlink = 1;
29 this._perm = perm;
30 this.mode |= perm;
31 this.ino = ino;
32 }
33 set ctime(ctime) {
34 this._ctime = ctime;
35 }
36 get ctime() {
37 return this._ctime;
38 }
39 set uid(uid) {
40 this._uid = uid;
41 this.ctime = new Date();
42 }
43 get uid() {
44 return this._uid;
45 }
46 set gid(gid) {
47 this._gid = gid;
48 this.ctime = new Date();
49 }
50 get gid() {
51 return this._gid;
52 }
53 set atime(atime) {
54 this._atime = atime;
55 this.ctime = new Date();
56 }
57 get atime() {
58 return this._atime;
59 }
60 set mtime(mtime) {
61 this._mtime = mtime;
62 this.ctime = new Date();
63 }
64 get mtime() {
65 return this._mtime;
66 }
67 set perm(perm) {
68 this._perm = perm;
69 this.ctime = new Date();
70 }
71 get perm() {
72 return this._perm;
73 }
74 set nlink(nlink) {
75 this._nlink = nlink;
76 this.ctime = new Date();
77 }
78 get nlink() {
79 return this._nlink;
80 }
81 getString(encoding = 'utf8') {
82 this.atime = new Date();
83 return this.getBuffer().toString(encoding);
84 }
85 setString(str) {
86 // this.setBuffer(bufferFrom(str, 'utf8'));
87 this.buf = (0, buffer_1.bufferFrom)(str, 'utf8');
88 this.touch();
89 }
90 getBuffer() {
91 this.atime = new Date();
92 if (!this.buf)
93 this.setBuffer((0, buffer_1.bufferAllocUnsafe)(0));
94 return (0, buffer_1.bufferFrom)(this.buf); // Return a copy.
95 }
96 setBuffer(buf) {
97 this.buf = (0, buffer_1.bufferFrom)(buf); // Creates a copy of data.
98 this.touch();
99 }
100 getSize() {
101 return this.buf ? this.buf.length : 0;
102 }
103 setModeProperty(property) {
104 this.mode = (this.mode & ~S_IFMT) | property;
105 }
106 setIsFile() {
107 this.setModeProperty(S_IFREG);
108 }
109 setIsDirectory() {
110 this.setModeProperty(S_IFDIR);
111 }
112 setIsSymlink() {
113 this.setModeProperty(S_IFLNK);
114 }
115 isFile() {
116 return (this.mode & S_IFMT) === S_IFREG;
117 }
118 isDirectory() {
119 return (this.mode & S_IFMT) === S_IFDIR;
120 }
121 isSymlink() {
122 // return !!this.symlink;
123 return (this.mode & S_IFMT) === S_IFLNK;
124 }
125 makeSymlink(steps) {
126 this.symlink = steps;
127 this.setIsSymlink();
128 }
129 write(buf, off = 0, len = buf.length, pos = 0) {
130 if (!this.buf)
131 this.buf = (0, buffer_1.bufferAllocUnsafe)(0);
132 if (pos + len > this.buf.length) {
133 const newBuf = (0, buffer_1.bufferAllocUnsafe)(pos + len);
134 this.buf.copy(newBuf, 0, 0, this.buf.length);
135 this.buf = newBuf;
136 }
137 buf.copy(this.buf, pos, off, off + len);
138 this.touch();
139 return len;
140 }
141 // Returns the number of bytes read.
142 read(buf, off = 0, len = buf.byteLength, pos = 0) {
143 this.atime = new Date();
144 if (!this.buf)
145 this.buf = (0, buffer_1.bufferAllocUnsafe)(0);
146 let actualLen = len;
147 if (actualLen > buf.byteLength) {
148 actualLen = buf.byteLength;
149 }
150 if (actualLen + pos > this.buf.length) {
151 actualLen = this.buf.length - pos;
152 }
153 const buf2 = buf instanceof Buffer ? buf : Buffer.from(buf.buffer);
154 this.buf.copy(buf2, off, pos, pos + actualLen);
155 return actualLen;
156 }
157 truncate(len = 0) {
158 if (!len)
159 this.buf = (0, buffer_1.bufferAllocUnsafe)(0);
160 else {
161 if (!this.buf)
162 this.buf = (0, buffer_1.bufferAllocUnsafe)(0);
163 if (len <= this.buf.length) {
164 this.buf = this.buf.slice(0, len);
165 }
166 else {
167 const buf = (0, buffer_1.bufferAllocUnsafe)(len);
168 this.buf.copy(buf);
169 buf.fill(0, this.buf.length);
170 this.buf = buf;
171 }
172 }
173 this.touch();
174 }
175 chmod(perm) {
176 this.perm = perm;
177 this.mode = (this.mode & ~0o777) | perm;
178 this.touch();
179 }
180 chown(uid, gid) {
181 this.uid = uid;
182 this.gid = gid;
183 this.touch();
184 }
185 touch() {
186 this.mtime = new Date();
187 this.emit('change', this);
188 }
189 canRead(uid = getuid(), gid = getgid()) {
190 if (this.perm & 4 /* S.IROTH */) {
191 return true;
192 }
193 if (gid === this.gid) {
194 if (this.perm & 32 /* S.IRGRP */) {
195 return true;
196 }
197 }
198 if (uid === this.uid) {
199 if (this.perm & 256 /* S.IRUSR */) {
200 return true;
201 }
202 }
203 return false;
204 }
205 canWrite(uid = getuid(), gid = getgid()) {
206 if (this.perm & 2 /* S.IWOTH */) {
207 return true;
208 }
209 if (gid === this.gid) {
210 if (this.perm & 16 /* S.IWGRP */) {
211 return true;
212 }
213 }
214 if (uid === this.uid) {
215 if (this.perm & 128 /* S.IWUSR */) {
216 return true;
217 }
218 }
219 return false;
220 }
221 del() {
222 this.emit('delete', this);
223 }
224 toJSON() {
225 return {
226 ino: this.ino,
227 uid: this.uid,
228 gid: this.gid,
229 atime: this.atime.getTime(),
230 mtime: this.mtime.getTime(),
231 ctime: this.ctime.getTime(),
232 perm: this.perm,
233 mode: this.mode,
234 nlink: this.nlink,
235 symlink: this.symlink,
236 data: this.getString(),
237 };
238 }
239}
240exports.Node = Node;
241/**
242 * Represents a hard link that points to an i-node `node`.
243 */
244class Link extends events_1.EventEmitter {
245 get steps() {
246 return this._steps;
247 }
248 // Recursively sync children steps, e.g. in case of dir rename
249 set steps(val) {
250 this._steps = val;
251 for (const [child, link] of this.children.entries()) {
252 if (child === '.' || child === '..') {
253 continue;
254 }
255 link === null || link === void 0 ? void 0 : link.syncSteps();
256 }
257 }
258 constructor(vol, parent, name) {
259 super();
260 this.children = new Map();
261 // Path to this node as Array: ['usr', 'bin', 'node'].
262 this._steps = [];
263 // "i-node" number of the node.
264 this.ino = 0;
265 // Number of children.
266 this.length = 0;
267 this.vol = vol;
268 this.parent = parent;
269 this.name = name;
270 this.syncSteps();
271 }
272 setNode(node) {
273 this.node = node;
274 this.ino = node.ino;
275 }
276 getNode() {
277 return this.node;
278 }
279 createChild(name, node = this.vol.createNode()) {
280 const link = new Link(this.vol, this, name);
281 link.setNode(node);
282 if (node.isDirectory()) {
283 link.children.set('.', link);
284 link.getNode().nlink++;
285 }
286 this.setChild(name, link);
287 return link;
288 }
289 setChild(name, link = new Link(this.vol, this, name)) {
290 this.children.set(name, link);
291 link.parent = this;
292 this.length++;
293 const node = link.getNode();
294 if (node.isDirectory()) {
295 link.children.set('..', this);
296 this.getNode().nlink++;
297 }
298 this.getNode().mtime = new Date();
299 this.emit('child:add', link, this);
300 return link;
301 }
302 deleteChild(link) {
303 const node = link.getNode();
304 if (node.isDirectory()) {
305 link.children.delete('..');
306 this.getNode().nlink--;
307 }
308 this.children.delete(link.getName());
309 this.length--;
310 this.getNode().mtime = new Date();
311 this.emit('child:delete', link, this);
312 }
313 getChild(name) {
314 this.getNode().mtime = new Date();
315 return this.children.get(name);
316 }
317 getPath() {
318 return this.steps.join(exports.SEP);
319 }
320 getName() {
321 return this.steps[this.steps.length - 1];
322 }
323 // del() {
324 // const parent = this.parent;
325 // if(parent) {
326 // parent.deleteChild(link);
327 // }
328 // this.parent = null;
329 // this.vol = null;
330 // }
331 /**
332 * Walk the tree path and return the `Link` at that location, if any.
333 * @param steps {string[]} Desired location.
334 * @param stop {number} Max steps to go into.
335 * @param i {number} Current step in the `steps` array.
336 *
337 * @return {Link|null}
338 */
339 walk(steps, stop = steps.length, i = 0) {
340 if (i >= steps.length)
341 return this;
342 if (i >= stop)
343 return this;
344 const step = steps[i];
345 const link = this.getChild(step);
346 if (!link)
347 return null;
348 return link.walk(steps, stop, i + 1);
349 }
350 toJSON() {
351 return {
352 steps: this.steps,
353 ino: this.ino,
354 children: Array.from(this.children.keys()),
355 };
356 }
357 syncSteps() {
358 this.steps = this.parent ? this.parent.steps.concat([this.name]) : [this.name];
359 }
360}
361exports.Link = Link;
362/**
363 * Represents an open file (file descriptor) that points to a `Link` (Hard-link) and a `Node`.
364 */
365class File {
366 /**
367 * Open a Link-Node pair. `node` is provided separately as that might be a different node
368 * rather the one `link` points to, because it might be a symlink.
369 * @param link
370 * @param node
371 * @param flags
372 * @param fd
373 */
374 constructor(link, node, flags, fd) {
375 this.link = link;
376 this.node = node;
377 this.flags = flags;
378 this.fd = fd;
379 this.position = 0;
380 if (this.flags & O_APPEND)
381 this.position = this.getSize();
382 }
383 getString(encoding = 'utf8') {
384 return this.node.getString();
385 }
386 setString(str) {
387 this.node.setString(str);
388 }
389 getBuffer() {
390 return this.node.getBuffer();
391 }
392 setBuffer(buf) {
393 this.node.setBuffer(buf);
394 }
395 getSize() {
396 return this.node.getSize();
397 }
398 truncate(len) {
399 this.node.truncate(len);
400 }
401 seekTo(position) {
402 this.position = position;
403 }
404 stats() {
405 return Stats_1.default.build(this.node);
406 }
407 write(buf, offset = 0, length = buf.length, position) {
408 if (typeof position !== 'number')
409 position = this.position;
410 const bytes = this.node.write(buf, offset, length, position);
411 this.position = position + bytes;
412 return bytes;
413 }
414 read(buf, offset = 0, length = buf.byteLength, position) {
415 if (typeof position !== 'number')
416 position = this.position;
417 const bytes = this.node.read(buf, offset, length, position);
418 this.position = position + bytes;
419 return bytes;
420 }
421 chmod(perm) {
422 this.node.chmod(perm);
423 }
424 chown(uid, gid) {
425 this.node.chown(uid, gid);
426 }
427}
428exports.File = File;
429//# sourceMappingURL=node.js.map
\No newline at end of file