Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | 65x 65x 65x 65x 3067x 3067x 3067x 3067x 3067x 6133x 3130x 3130x 22x 14x 8x 3067x 3067x 3067x 3067x 3067x 3067x 61589x 2180x 2180x 2180x 903x 903x 2180x 2180x 4003x 4003x 4003x 4003x 4003x 2675x 2675x 4003x 4003x 4003x 100x 100x 34x 34x 100x 100x 100x 100x 15255x 15255x 2213x 452x 612x 3075x | import { constants, PATH } from '../constants';
import { FanOut } from 'thingies/lib/fanout';
import type { Node } from './Node';
import type { Superblock } from './Superblock';
export type LinkEventChildAdd = [type: 'child:add', link: Link, parent: Link];
export type LinkEventChildDelete = [type: 'child:del', link: Link, parent: Link];
export type LinkEvent = LinkEventChildAdd | LinkEventChildDelete;
const { S_IFREG } = constants;
/**
* Represents a hard link that points to an i-node `node`.
*/
export class Link {
public readonly changes = new FanOut<LinkEvent>();
vol: Superblock;
parent: Link | undefined;
children = new Map<string, Link | undefined>();
// Path to this node as Array: ['usr', 'bin', 'node'].
private _steps: string[] = [];
// "i-node" of this hard link.
node: Node;
// "i-node" number of the node.
ino: number = 0;
// Number of children.
length: number = 0;
name: string;
get steps() {
return this._steps;
}
// Recursively sync children steps, e.g. in case of dir rename
set steps(val) {
this._steps = val;
for (const [child, link] of this.children.entries()) {
if (child === '.' || child === '..') {
continue;
}
link?.syncSteps();
}
}
constructor(vol: Superblock, parent: Link | undefined, name: string) {
this.vol = vol;
this.parent = parent;
this.name = name;
this.syncSteps();
}
setNode(node: Node) {
this.node = node;
this.ino = node.ino;
}
getNode(): Node {
return this.node;
}
createChild(name: string, node: Node = this.vol.createNode(S_IFREG | 0o666)): Link {
const link = new Link(this.vol, this, name);
link.setNode(node);
if (node.isDirectory()) {
link.children.set('.', link);
link.getNode().nlink++;
}
this.setChild(name, link);
return link;
}
setChild(name: string, link: Link = new Link(this.vol, this, name)): Link {
this.children.set(name, link);
link.parent = this;
this.length++;
const node = link.getNode();
if (node.isDirectory()) {
link.children.set('..', this);
this.getNode().nlink++;
}
this.getNode().mtime = new Date();
this.changes.emit(['child:add', link, this]);
return link;
}
deleteChild(link: Link) {
const node = link.getNode();
if (node.isDirectory()) {
link.children.delete('..');
this.getNode().nlink--;
}
this.children.delete(link.getName());
this.length--;
this.getNode().mtime = new Date();
this.changes.emit(['child:del', link, this]);
}
getChild(name: string): Link | undefined {
this.getNode().atime = new Date();
return this.children.get(name);
}
getPath(): string {
return this.steps.join(PATH.SEP);
}
getParentPath(): string {
return this.steps.slice(0, -1).join(PATH.SEP);
}
getName(): string {
return this.steps[this.steps.length - 1];
}
toJSON() {
return {
steps: this.steps,
ino: this.ino,
children: Array.from(this.children.keys()),
};
}
syncSteps() {
this.steps = this.parent ? this.parent.steps.concat([this.name]) : [this.name];
}
}
|