mharj-diskinfo 0.0.2 → 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.
- package/README.md +6 -5
- package/dist/index.cjs +228 -0
- package/dist/index.d.cts +48 -0
- package/dist/index.d.mts +48 -0
- package/dist/index.mjs +203 -0
- package/package.json +55 -36
- package/diskinfo.js +0 -179
package/README.md
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
# diskinfo
|
|
2
2
|
|
|
3
3
|
read disk partition and filesystem information with NodeJS
|
|
4
|
+
Requires: NodeJS 10.4 as using Bigint
|
|
4
5
|
|
|
5
6
|
```bash
|
|
6
7
|
npm install mharj-diskinfo --save
|
|
7
8
|
```
|
|
8
9
|
|
|
9
10
|
```javascript
|
|
10
|
-
const fs = require(
|
|
11
|
-
const scan = require(
|
|
12
|
-
const device =
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const scan = require('mharj-diskinfo').scan;
|
|
13
|
+
const device = '\\\\.\\PHYSICALDRIVE0';
|
|
13
14
|
//const device = '/dev/sda';
|
|
14
|
-
const fd = fs.openSync(device,
|
|
15
|
-
let data = scan(fd);
|
|
15
|
+
const fd = fs.openSync(device, 'rs+');
|
|
16
|
+
let data = await scan(fd);
|
|
16
17
|
console.log(data);
|
|
17
18
|
```
|
|
18
19
|
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
23
|
+
//#endregion
|
|
24
|
+
let node_fs = require("node:fs");
|
|
25
|
+
node_fs = __toESM(node_fs);
|
|
26
|
+
//#region src/Magic.ts
|
|
27
|
+
var Magic = class {
|
|
28
|
+
constructor(fd) {
|
|
29
|
+
this.fd = fd;
|
|
30
|
+
}
|
|
31
|
+
haveExt(offset) {
|
|
32
|
+
const data = Buffer.allocUnsafe(2048);
|
|
33
|
+
node_fs.default.readSync(this.fd, data, 0, data.length, 512 * offset);
|
|
34
|
+
return data.readInt16BE(1080) === 21487;
|
|
35
|
+
}
|
|
36
|
+
haveNtfs(offset) {
|
|
37
|
+
const data = Buffer.allocUnsafe(512);
|
|
38
|
+
node_fs.default.readSync(this.fd, data, 0, data.length, 512 * offset);
|
|
39
|
+
return data.readInt32BE(3) === 1314145875;
|
|
40
|
+
}
|
|
41
|
+
haveLvm2(offset) {
|
|
42
|
+
const data = Buffer.allocUnsafe(1024);
|
|
43
|
+
node_fs.default.readSync(this.fd, data, 0, data.length, 512 * offset);
|
|
44
|
+
return data.readInt32BE(536) === 1280724274;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/gptPart.ts
|
|
49
|
+
const EFI_PART = Buffer.from([
|
|
50
|
+
69,
|
|
51
|
+
70,
|
|
52
|
+
73,
|
|
53
|
+
32,
|
|
54
|
+
80,
|
|
55
|
+
65,
|
|
56
|
+
82,
|
|
57
|
+
84
|
|
58
|
+
]);
|
|
59
|
+
const gptPartTypes = Object.freeze({
|
|
60
|
+
EMPTY: "00000000-0000-0000-0000-000000000000",
|
|
61
|
+
MBR: "024dee41-33e7-11d3-9d69-0008c781f39f",
|
|
62
|
+
EFI: "c12a7328-f81f-11d2-ba4b-00a0c93ec93b",
|
|
63
|
+
LINUX: "0fc63daf-8483-4772-8e79-3d69d8477de4",
|
|
64
|
+
LINUX_SWAP: "0657fd6d-a4ab-43c4-84e5-0933c84b4f4f",
|
|
65
|
+
LINUX_LVM: "e6d6d379-f507-44c2-a23c-238f2a3df928",
|
|
66
|
+
LINUX_RAID: "a19d880f-05fc-4d3b-a006-743f0f84911e",
|
|
67
|
+
MSR: "e3c9e316-0b5c-4db8-817d-f92df00215ae",
|
|
68
|
+
BASIC_DATA: "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7",
|
|
69
|
+
getName: function(val) {
|
|
70
|
+
return Object.entries(this).reduce((acc, [k, v]) => {
|
|
71
|
+
if (typeof v === "string" && v === val) return k;
|
|
72
|
+
return acc;
|
|
73
|
+
}, "Unknown");
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
function readUuidBytes(buf, pos) {
|
|
77
|
+
return Buffer.from([
|
|
78
|
+
buf[pos + 3],
|
|
79
|
+
buf[pos + 2],
|
|
80
|
+
buf[pos + 1],
|
|
81
|
+
buf[pos + 0],
|
|
82
|
+
buf[pos + 5],
|
|
83
|
+
buf[pos + 4],
|
|
84
|
+
buf[pos + 7],
|
|
85
|
+
buf[pos + 6],
|
|
86
|
+
buf[pos + 8],
|
|
87
|
+
buf[pos + 9],
|
|
88
|
+
buf[pos + 10],
|
|
89
|
+
buf[pos + 11],
|
|
90
|
+
buf[pos + 12],
|
|
91
|
+
buf[pos + 13],
|
|
92
|
+
buf[pos + 14],
|
|
93
|
+
buf[pos + 15]
|
|
94
|
+
]);
|
|
95
|
+
}
|
|
96
|
+
function readUuidString(buf, pos) {
|
|
97
|
+
const uuid = readUuidBytes(buf, pos).toString("hex");
|
|
98
|
+
return `${uuid.slice(0, 8)}-${uuid.slice(8, 12)}-${uuid.slice(12, 16)}-${uuid.slice(16, 20)}-${uuid.slice(20, 32)}`;
|
|
99
|
+
}
|
|
100
|
+
function parseGPTable(buf) {
|
|
101
|
+
const typeId = readUuidString(buf, 0);
|
|
102
|
+
const uuid = readUuidString(buf, 16);
|
|
103
|
+
const startLBA = buf.readBigUInt64LE(32);
|
|
104
|
+
const endLBA = buf.readBigUInt64LE(40);
|
|
105
|
+
return {
|
|
106
|
+
typeId,
|
|
107
|
+
type: gptPartTypes.getName(typeId),
|
|
108
|
+
uuid,
|
|
109
|
+
active: uuid !== gptPartTypes.EMPTY,
|
|
110
|
+
startLBA,
|
|
111
|
+
endLBA,
|
|
112
|
+
partitionSize: endLBA - startLBA + 1n,
|
|
113
|
+
attributes: buf.readBigUInt64BE(48),
|
|
114
|
+
label: buf.subarray(56, 128).toString("utf16le").split("\0", 1)[0]
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function parseGPT(buf) {
|
|
118
|
+
if (buf.indexOf(EFI_PART) !== 0) throw Error("not GTP entry");
|
|
119
|
+
return {
|
|
120
|
+
revision: `${buf[8]}.${buf[9]}.${buf[10]}.${buf[11]}`,
|
|
121
|
+
headerSize: buf.readUInt32LE(12),
|
|
122
|
+
headerCRC32: buf.readUInt32LE(16),
|
|
123
|
+
currentLBA: buf.readBigUInt64LE(24),
|
|
124
|
+
backupLBA: buf.readBigUInt64LE(32),
|
|
125
|
+
firstUsableLBA: buf.readBigUInt64LE(40),
|
|
126
|
+
lastUsableLBA: buf.readBigUInt64LE(48),
|
|
127
|
+
uuid: readUuidString(buf, 56),
|
|
128
|
+
tableLBA: buf.readBigUInt64LE(72),
|
|
129
|
+
partitions: buf.readUInt32LE(80),
|
|
130
|
+
partitionSize: buf.readUInt32LE(84),
|
|
131
|
+
partitionCRC32: buf.readUInt32LE(88)
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
//#endregion
|
|
135
|
+
//#region src/mbrPart.ts
|
|
136
|
+
const partTypes = {
|
|
137
|
+
EMPTY: 0,
|
|
138
|
+
EXTENDED: 5,
|
|
139
|
+
NTFS: 7,
|
|
140
|
+
LINUX_SWAP: 130,
|
|
141
|
+
LINUX: 131,
|
|
142
|
+
LINUX_EXTENDED: 133,
|
|
143
|
+
LINUX_LVM: 142,
|
|
144
|
+
GPT: 238,
|
|
145
|
+
EFI: 239,
|
|
146
|
+
LINUX_RAID: 253,
|
|
147
|
+
getName: function(val) {
|
|
148
|
+
return Object.entries(this).reduce((acc, [k, v]) => {
|
|
149
|
+
if (typeof v === "number" && v === val) return k;
|
|
150
|
+
return acc;
|
|
151
|
+
}, "Unknown");
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
function parseMBR(mbr) {
|
|
155
|
+
if (mbr.length < 512 || mbr[510] !== 85 || mbr[511] !== 170) throw Error("no MBR signature or buffer is less than 512 bytes");
|
|
156
|
+
const ret = {
|
|
157
|
+
copyProtected: !!(mbr[444] === 90 && mbr[444] === 90),
|
|
158
|
+
uuid: Buffer.from([
|
|
159
|
+
mbr[443],
|
|
160
|
+
mbr[442],
|
|
161
|
+
mbr[441],
|
|
162
|
+
mbr[440]
|
|
163
|
+
]).toString("hex"),
|
|
164
|
+
partitions: [],
|
|
165
|
+
type: "MBR"
|
|
166
|
+
};
|
|
167
|
+
for (let i = 446; i <= 508; i += 16) ret.partitions.push(parseMBRPartition(mbr.slice(i, i + 16)));
|
|
168
|
+
return ret;
|
|
169
|
+
}
|
|
170
|
+
function parseMBRPartition(part) {
|
|
171
|
+
const startLBA = part.readUInt32LE(8);
|
|
172
|
+
const partitionSize = part.readUInt32LE(12);
|
|
173
|
+
return {
|
|
174
|
+
active: part.readUInt8(0) === 128,
|
|
175
|
+
type: part.readUInt8(4),
|
|
176
|
+
startLBA,
|
|
177
|
+
partitionSize,
|
|
178
|
+
endLBA: startLBA + partitionSize
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function isMbrPartition(part) {
|
|
182
|
+
return typeof part.type === "number";
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/util.ts
|
|
186
|
+
function readFile(fd, offset, length, position) {
|
|
187
|
+
return new Promise((resolve, reject) => {
|
|
188
|
+
const buffer = Buffer.allocUnsafe(length);
|
|
189
|
+
node_fs.default.read(fd, buffer, offset, buffer.length, position, (err) => {
|
|
190
|
+
if (err) reject(err);
|
|
191
|
+
else resolve(buffer);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
//#endregion
|
|
196
|
+
//#region src/scan.ts
|
|
197
|
+
async function scan(fd) {
|
|
198
|
+
const rootMbr = parseMBR(await readFile(fd, 0, 512, 0));
|
|
199
|
+
rootMbr.partitions.forEach(async function(p) {
|
|
200
|
+
if (p.type === partTypes.EXTENDED) {
|
|
201
|
+
if (!isMbrPartition(p)) throw TypeError("we did get GPT partition as extended");
|
|
202
|
+
parseMBR(await readFile(fd, 0, 512, 512)).partitions.forEach(function(extpart) {
|
|
203
|
+
if (!isMbrPartition(extpart)) throw TypeError("we did get GPT partition as extended");
|
|
204
|
+
if (extpart.type !== partTypes.EMPTY) {
|
|
205
|
+
extpart.startLBA = extpart.startLBA + p.startLBA;
|
|
206
|
+
rootMbr.partitions.push(extpart);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
if (p.type === partTypes.GPT) {
|
|
211
|
+
rootMbr.type = "GPT";
|
|
212
|
+
const gpt = parseGPT(await readFile(fd, 0, 512, 512));
|
|
213
|
+
rootMbr.uuid = gpt.uuid;
|
|
214
|
+
const gBuff = Buffer.allocUnsafe(gpt.partitions * gpt.partitionSize);
|
|
215
|
+
node_fs.default.readSync(fd, gBuff, 0, gBuff.length, Number(gpt.tableLBA) * 512);
|
|
216
|
+
const partitions = [];
|
|
217
|
+
for (let i = 0; i < gpt.partitions * gpt.partitionSize; i += gpt.partitionSize) {
|
|
218
|
+
const table = parseGPTable(gBuff.slice(i, i + gpt.partitionSize));
|
|
219
|
+
if (table.typeId !== gptPartTypes.EMPTY) partitions.push(table);
|
|
220
|
+
}
|
|
221
|
+
rootMbr.partitions = partitions;
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
return rootMbr;
|
|
225
|
+
}
|
|
226
|
+
//#endregion
|
|
227
|
+
exports.Magic = Magic;
|
|
228
|
+
exports.scan = scan;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
//#region src/Magic.d.ts
|
|
2
|
+
declare class Magic {
|
|
3
|
+
private fd;
|
|
4
|
+
constructor(fd: number);
|
|
5
|
+
haveExt(offset: number): boolean;
|
|
6
|
+
haveNtfs(offset: number): boolean;
|
|
7
|
+
haveLvm2(offset: number): boolean;
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/types.d.ts
|
|
11
|
+
type MBRPartition = {
|
|
12
|
+
active: boolean;
|
|
13
|
+
type: number;
|
|
14
|
+
startLBA: number;
|
|
15
|
+
partitionSize: number;
|
|
16
|
+
endLBA: number;
|
|
17
|
+
};
|
|
18
|
+
type GPTPartition = {
|
|
19
|
+
typeId: string;
|
|
20
|
+
type: string;
|
|
21
|
+
uuid: string;
|
|
22
|
+
active: boolean;
|
|
23
|
+
startLBA: bigint;
|
|
24
|
+
endLBA: bigint;
|
|
25
|
+
partitionSize: bigint;
|
|
26
|
+
attributes: bigint;
|
|
27
|
+
label: string;
|
|
28
|
+
};
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/mbrPart.d.ts
|
|
31
|
+
type GptData = {
|
|
32
|
+
copyProtected: boolean;
|
|
33
|
+
uuid: string;
|
|
34
|
+
type: 'GPT';
|
|
35
|
+
partitions: GPTPartition[];
|
|
36
|
+
};
|
|
37
|
+
type MbrData = {
|
|
38
|
+
copyProtected: boolean;
|
|
39
|
+
uuid: string;
|
|
40
|
+
type: 'MBR';
|
|
41
|
+
partitions: MBRPartition[];
|
|
42
|
+
};
|
|
43
|
+
type IMbrData = MbrData | GptData;
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/scan.d.ts
|
|
46
|
+
declare function scan(fd: number): Promise<IMbrData>;
|
|
47
|
+
//#endregion
|
|
48
|
+
export { Magic, scan };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
//#region src/Magic.d.ts
|
|
2
|
+
declare class Magic {
|
|
3
|
+
private fd;
|
|
4
|
+
constructor(fd: number);
|
|
5
|
+
haveExt(offset: number): boolean;
|
|
6
|
+
haveNtfs(offset: number): boolean;
|
|
7
|
+
haveLvm2(offset: number): boolean;
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/types.d.ts
|
|
11
|
+
type MBRPartition = {
|
|
12
|
+
active: boolean;
|
|
13
|
+
type: number;
|
|
14
|
+
startLBA: number;
|
|
15
|
+
partitionSize: number;
|
|
16
|
+
endLBA: number;
|
|
17
|
+
};
|
|
18
|
+
type GPTPartition = {
|
|
19
|
+
typeId: string;
|
|
20
|
+
type: string;
|
|
21
|
+
uuid: string;
|
|
22
|
+
active: boolean;
|
|
23
|
+
startLBA: bigint;
|
|
24
|
+
endLBA: bigint;
|
|
25
|
+
partitionSize: bigint;
|
|
26
|
+
attributes: bigint;
|
|
27
|
+
label: string;
|
|
28
|
+
};
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/mbrPart.d.ts
|
|
31
|
+
type GptData = {
|
|
32
|
+
copyProtected: boolean;
|
|
33
|
+
uuid: string;
|
|
34
|
+
type: 'GPT';
|
|
35
|
+
partitions: GPTPartition[];
|
|
36
|
+
};
|
|
37
|
+
type MbrData = {
|
|
38
|
+
copyProtected: boolean;
|
|
39
|
+
uuid: string;
|
|
40
|
+
type: 'MBR';
|
|
41
|
+
partitions: MBRPartition[];
|
|
42
|
+
};
|
|
43
|
+
type IMbrData = MbrData | GptData;
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/scan.d.ts
|
|
46
|
+
declare function scan(fd: number): Promise<IMbrData>;
|
|
47
|
+
//#endregion
|
|
48
|
+
export { Magic, scan };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
//#region src/Magic.ts
|
|
3
|
+
var Magic = class {
|
|
4
|
+
constructor(fd) {
|
|
5
|
+
this.fd = fd;
|
|
6
|
+
}
|
|
7
|
+
haveExt(offset) {
|
|
8
|
+
const data = Buffer.allocUnsafe(2048);
|
|
9
|
+
fs.readSync(this.fd, data, 0, data.length, 512 * offset);
|
|
10
|
+
return data.readInt16BE(1080) === 21487;
|
|
11
|
+
}
|
|
12
|
+
haveNtfs(offset) {
|
|
13
|
+
const data = Buffer.allocUnsafe(512);
|
|
14
|
+
fs.readSync(this.fd, data, 0, data.length, 512 * offset);
|
|
15
|
+
return data.readInt32BE(3) === 1314145875;
|
|
16
|
+
}
|
|
17
|
+
haveLvm2(offset) {
|
|
18
|
+
const data = Buffer.allocUnsafe(1024);
|
|
19
|
+
fs.readSync(this.fd, data, 0, data.length, 512 * offset);
|
|
20
|
+
return data.readInt32BE(536) === 1280724274;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/gptPart.ts
|
|
25
|
+
const EFI_PART = Buffer.from([
|
|
26
|
+
69,
|
|
27
|
+
70,
|
|
28
|
+
73,
|
|
29
|
+
32,
|
|
30
|
+
80,
|
|
31
|
+
65,
|
|
32
|
+
82,
|
|
33
|
+
84
|
|
34
|
+
]);
|
|
35
|
+
const gptPartTypes = Object.freeze({
|
|
36
|
+
EMPTY: "00000000-0000-0000-0000-000000000000",
|
|
37
|
+
MBR: "024dee41-33e7-11d3-9d69-0008c781f39f",
|
|
38
|
+
EFI: "c12a7328-f81f-11d2-ba4b-00a0c93ec93b",
|
|
39
|
+
LINUX: "0fc63daf-8483-4772-8e79-3d69d8477de4",
|
|
40
|
+
LINUX_SWAP: "0657fd6d-a4ab-43c4-84e5-0933c84b4f4f",
|
|
41
|
+
LINUX_LVM: "e6d6d379-f507-44c2-a23c-238f2a3df928",
|
|
42
|
+
LINUX_RAID: "a19d880f-05fc-4d3b-a006-743f0f84911e",
|
|
43
|
+
MSR: "e3c9e316-0b5c-4db8-817d-f92df00215ae",
|
|
44
|
+
BASIC_DATA: "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7",
|
|
45
|
+
getName: function(val) {
|
|
46
|
+
return Object.entries(this).reduce((acc, [k, v]) => {
|
|
47
|
+
if (typeof v === "string" && v === val) return k;
|
|
48
|
+
return acc;
|
|
49
|
+
}, "Unknown");
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
function readUuidBytes(buf, pos) {
|
|
53
|
+
return Buffer.from([
|
|
54
|
+
buf[pos + 3],
|
|
55
|
+
buf[pos + 2],
|
|
56
|
+
buf[pos + 1],
|
|
57
|
+
buf[pos + 0],
|
|
58
|
+
buf[pos + 5],
|
|
59
|
+
buf[pos + 4],
|
|
60
|
+
buf[pos + 7],
|
|
61
|
+
buf[pos + 6],
|
|
62
|
+
buf[pos + 8],
|
|
63
|
+
buf[pos + 9],
|
|
64
|
+
buf[pos + 10],
|
|
65
|
+
buf[pos + 11],
|
|
66
|
+
buf[pos + 12],
|
|
67
|
+
buf[pos + 13],
|
|
68
|
+
buf[pos + 14],
|
|
69
|
+
buf[pos + 15]
|
|
70
|
+
]);
|
|
71
|
+
}
|
|
72
|
+
function readUuidString(buf, pos) {
|
|
73
|
+
const uuid = readUuidBytes(buf, pos).toString("hex");
|
|
74
|
+
return `${uuid.slice(0, 8)}-${uuid.slice(8, 12)}-${uuid.slice(12, 16)}-${uuid.slice(16, 20)}-${uuid.slice(20, 32)}`;
|
|
75
|
+
}
|
|
76
|
+
function parseGPTable(buf) {
|
|
77
|
+
const typeId = readUuidString(buf, 0);
|
|
78
|
+
const uuid = readUuidString(buf, 16);
|
|
79
|
+
const startLBA = buf.readBigUInt64LE(32);
|
|
80
|
+
const endLBA = buf.readBigUInt64LE(40);
|
|
81
|
+
return {
|
|
82
|
+
typeId,
|
|
83
|
+
type: gptPartTypes.getName(typeId),
|
|
84
|
+
uuid,
|
|
85
|
+
active: uuid !== gptPartTypes.EMPTY,
|
|
86
|
+
startLBA,
|
|
87
|
+
endLBA,
|
|
88
|
+
partitionSize: endLBA - startLBA + 1n,
|
|
89
|
+
attributes: buf.readBigUInt64BE(48),
|
|
90
|
+
label: buf.subarray(56, 128).toString("utf16le").split("\0", 1)[0]
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function parseGPT(buf) {
|
|
94
|
+
if (buf.indexOf(EFI_PART) !== 0) throw Error("not GTP entry");
|
|
95
|
+
return {
|
|
96
|
+
revision: `${buf[8]}.${buf[9]}.${buf[10]}.${buf[11]}`,
|
|
97
|
+
headerSize: buf.readUInt32LE(12),
|
|
98
|
+
headerCRC32: buf.readUInt32LE(16),
|
|
99
|
+
currentLBA: buf.readBigUInt64LE(24),
|
|
100
|
+
backupLBA: buf.readBigUInt64LE(32),
|
|
101
|
+
firstUsableLBA: buf.readBigUInt64LE(40),
|
|
102
|
+
lastUsableLBA: buf.readBigUInt64LE(48),
|
|
103
|
+
uuid: readUuidString(buf, 56),
|
|
104
|
+
tableLBA: buf.readBigUInt64LE(72),
|
|
105
|
+
partitions: buf.readUInt32LE(80),
|
|
106
|
+
partitionSize: buf.readUInt32LE(84),
|
|
107
|
+
partitionCRC32: buf.readUInt32LE(88)
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
//#region src/mbrPart.ts
|
|
112
|
+
const partTypes = {
|
|
113
|
+
EMPTY: 0,
|
|
114
|
+
EXTENDED: 5,
|
|
115
|
+
NTFS: 7,
|
|
116
|
+
LINUX_SWAP: 130,
|
|
117
|
+
LINUX: 131,
|
|
118
|
+
LINUX_EXTENDED: 133,
|
|
119
|
+
LINUX_LVM: 142,
|
|
120
|
+
GPT: 238,
|
|
121
|
+
EFI: 239,
|
|
122
|
+
LINUX_RAID: 253,
|
|
123
|
+
getName: function(val) {
|
|
124
|
+
return Object.entries(this).reduce((acc, [k, v]) => {
|
|
125
|
+
if (typeof v === "number" && v === val) return k;
|
|
126
|
+
return acc;
|
|
127
|
+
}, "Unknown");
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
function parseMBR(mbr) {
|
|
131
|
+
if (mbr.length < 512 || mbr[510] !== 85 || mbr[511] !== 170) throw Error("no MBR signature or buffer is less than 512 bytes");
|
|
132
|
+
const ret = {
|
|
133
|
+
copyProtected: !!(mbr[444] === 90 && mbr[444] === 90),
|
|
134
|
+
uuid: Buffer.from([
|
|
135
|
+
mbr[443],
|
|
136
|
+
mbr[442],
|
|
137
|
+
mbr[441],
|
|
138
|
+
mbr[440]
|
|
139
|
+
]).toString("hex"),
|
|
140
|
+
partitions: [],
|
|
141
|
+
type: "MBR"
|
|
142
|
+
};
|
|
143
|
+
for (let i = 446; i <= 508; i += 16) ret.partitions.push(parseMBRPartition(mbr.slice(i, i + 16)));
|
|
144
|
+
return ret;
|
|
145
|
+
}
|
|
146
|
+
function parseMBRPartition(part) {
|
|
147
|
+
const startLBA = part.readUInt32LE(8);
|
|
148
|
+
const partitionSize = part.readUInt32LE(12);
|
|
149
|
+
return {
|
|
150
|
+
active: part.readUInt8(0) === 128,
|
|
151
|
+
type: part.readUInt8(4),
|
|
152
|
+
startLBA,
|
|
153
|
+
partitionSize,
|
|
154
|
+
endLBA: startLBA + partitionSize
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function isMbrPartition(part) {
|
|
158
|
+
return typeof part.type === "number";
|
|
159
|
+
}
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region src/util.ts
|
|
162
|
+
function readFile(fd, offset, length, position) {
|
|
163
|
+
return new Promise((resolve, reject) => {
|
|
164
|
+
const buffer = Buffer.allocUnsafe(length);
|
|
165
|
+
fs.read(fd, buffer, offset, buffer.length, position, (err) => {
|
|
166
|
+
if (err) reject(err);
|
|
167
|
+
else resolve(buffer);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region src/scan.ts
|
|
173
|
+
async function scan(fd) {
|
|
174
|
+
const rootMbr = parseMBR(await readFile(fd, 0, 512, 0));
|
|
175
|
+
rootMbr.partitions.forEach(async function(p) {
|
|
176
|
+
if (p.type === partTypes.EXTENDED) {
|
|
177
|
+
if (!isMbrPartition(p)) throw TypeError("we did get GPT partition as extended");
|
|
178
|
+
parseMBR(await readFile(fd, 0, 512, 512)).partitions.forEach(function(extpart) {
|
|
179
|
+
if (!isMbrPartition(extpart)) throw TypeError("we did get GPT partition as extended");
|
|
180
|
+
if (extpart.type !== partTypes.EMPTY) {
|
|
181
|
+
extpart.startLBA = extpart.startLBA + p.startLBA;
|
|
182
|
+
rootMbr.partitions.push(extpart);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
if (p.type === partTypes.GPT) {
|
|
187
|
+
rootMbr.type = "GPT";
|
|
188
|
+
const gpt = parseGPT(await readFile(fd, 0, 512, 512));
|
|
189
|
+
rootMbr.uuid = gpt.uuid;
|
|
190
|
+
const gBuff = Buffer.allocUnsafe(gpt.partitions * gpt.partitionSize);
|
|
191
|
+
fs.readSync(fd, gBuff, 0, gBuff.length, Number(gpt.tableLBA) * 512);
|
|
192
|
+
const partitions = [];
|
|
193
|
+
for (let i = 0; i < gpt.partitions * gpt.partitionSize; i += gpt.partitionSize) {
|
|
194
|
+
const table = parseGPTable(gBuff.slice(i, i + gpt.partitionSize));
|
|
195
|
+
if (table.typeId !== gptPartTypes.EMPTY) partitions.push(table);
|
|
196
|
+
}
|
|
197
|
+
rootMbr.partitions = partitions;
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
return rootMbr;
|
|
201
|
+
}
|
|
202
|
+
//#endregion
|
|
203
|
+
export { Magic, scan };
|
package/package.json
CHANGED
|
@@ -1,38 +1,57 @@
|
|
|
1
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
|
-
|
|
2
|
+
"name": "mharj-diskinfo",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Node.js library to parse MBR and GPT partition information from disk images or devices.",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.mts",
|
|
8
|
+
"exports": {
|
|
9
|
+
"import": {
|
|
10
|
+
"types": "./dist/index.d.mts",
|
|
11
|
+
"default": "./dist/index.mjs"
|
|
12
|
+
},
|
|
13
|
+
"require": {
|
|
14
|
+
"types": "./dist/index.d.cts",
|
|
15
|
+
"default": "./dist/index.cjs"
|
|
16
|
+
},
|
|
17
|
+
"default": "./dist/index.mjs"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"prepare": "lefthook install",
|
|
21
|
+
"build": "tsdown src/index.ts --format cjs,esm --dts --clean",
|
|
22
|
+
"prepublishOnly": "npm run build",
|
|
23
|
+
"test": "vitest test --typecheck --run --no-isolate --coverage",
|
|
24
|
+
"coverage": "vitest test --run --no-isolate --reporter=dot --coverage --coverage.reporter=lcov",
|
|
25
|
+
"lint": "biome check",
|
|
26
|
+
"validate": "tsc --noEmit --project tsconfig.test.json"
|
|
27
|
+
},
|
|
28
|
+
"repository": "github:mharj/diskinfo",
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=10.4.0"
|
|
34
|
+
},
|
|
35
|
+
"author": "mharj",
|
|
36
|
+
"license": "LGPL",
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/mharj/diskinfo/issues"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/mharj/diskinfo#readme",
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@biomejs/biome": "^2.4.15",
|
|
43
|
+
"@tsconfig/node16": "^16.1.8",
|
|
44
|
+
"@types/buffer-crc32": "^0.2.0",
|
|
45
|
+
"@types/node": "^22.19.1",
|
|
46
|
+
"@vitest/coverage-v8": "^4.1.6",
|
|
47
|
+
"buffer-crc32": "^0.2.13",
|
|
48
|
+
"c8": "^11.0.0",
|
|
49
|
+
"lefthook": "^2.1.6",
|
|
50
|
+
"tsdown": "^0.22.0",
|
|
51
|
+
"tslib": "^2.8.1",
|
|
52
|
+
"typescript": "^6.0.3",
|
|
53
|
+
"vite": "^8.0.12",
|
|
54
|
+
"vitest": "^4.1.6"
|
|
55
|
+
},
|
|
56
|
+
"packageManager": "pnpm@10.33.4+sha512.1c67b3b359b2d408119ba1ed289f34b8fc3c6873412bec6fd264fbdc82489e510fcbecb9ce9d22dae7f3b76269d8441046014bdca53b9979cd7a561ad631b800"
|
|
38
57
|
}
|
package/diskinfo.js
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const iconv = require('iconv-lite');
|
|
3
|
-
const uuidParse = require('uuid-parse');
|
|
4
|
-
const EFI_PART = Buffer.from([0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54]);
|
|
5
|
-
|
|
6
|
-
const gptPartTypes = Object.freeze({
|
|
7
|
-
EMPTY: '00000000-0000-0000-0000-000000000000',
|
|
8
|
-
MBR: '024dee41-33e7-11d3-9d69-0008c781f39f',
|
|
9
|
-
EFI: 'c12a7328-f81f-11d2-ba4b-00a0c93ec93b',
|
|
10
|
-
LINUX: '0fc63daf-8483-4772-8e79-3d69d8477de4',
|
|
11
|
-
LINUX_SWAP: '0657fd6d-a4ab-43c4-84e5-0933c84b4f4f',
|
|
12
|
-
LINUX_LVM: 'e6d6d379-f507-44c2-a23c-238f2a3df928',
|
|
13
|
-
LINUX_RAID: 'a19d880f-05fc-4d3b-a006-743f0f84911e',
|
|
14
|
-
MSR: 'e3c9e316-0b5c-4db8-817d-f92df00215ae',
|
|
15
|
-
BASIC_DATA: 'ebd0a0a2-b9e5-4433-87c0-68b6b72699c7',
|
|
16
|
-
getName: function(val) { // print names for values
|
|
17
|
-
for (let k in gptPartTypes) {
|
|
18
|
-
if (gptPartTypes[k] == val) {
|
|
19
|
-
return k;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
return 'Unknown';
|
|
23
|
-
},
|
|
24
|
-
});
|
|
25
|
-
module.exports.gptPartTypes = gptPartTypes;
|
|
26
|
-
|
|
27
|
-
const partTypes = {
|
|
28
|
-
EMPTY: 0x00,
|
|
29
|
-
EXTENDED: 0x05,
|
|
30
|
-
NTFS: 0x07,
|
|
31
|
-
LINUX_SWAP: 0x82,
|
|
32
|
-
LINUX: 0x83,
|
|
33
|
-
LINUX_EXTENDED: 0x85,
|
|
34
|
-
LINUX_LVM: 0x8e,
|
|
35
|
-
GPT: 0xee,
|
|
36
|
-
EFI: 0xef,
|
|
37
|
-
LINUX_RAID: 0xfd,
|
|
38
|
-
getName: function(val) { // print names for values
|
|
39
|
-
for (let k in partTypes) {
|
|
40
|
-
if (partTypes[k] == val) {
|
|
41
|
-
return k+' ('+val.toString(16)+')';
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return '('+val.toString(16)+')';
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
module.exports.partTypes = partTypes;
|
|
48
|
-
|
|
49
|
-
function parseMBRPartition(part) {
|
|
50
|
-
let out = {};
|
|
51
|
-
out.active = (part.readUInt8(0)==0x80?true:false);
|
|
52
|
-
// out.startCHS = Buffer.from([part[1],part[2],part[3]]); // obsolete
|
|
53
|
-
out.type = part.readUInt8(4);
|
|
54
|
-
// out.endCHS = Buffer.from([part[5],part[6],part[7]]); // obsolete
|
|
55
|
-
out.startLBA = part.readUInt32LE(8);
|
|
56
|
-
out.partitionSize = part.readUInt32LE(12);
|
|
57
|
-
out.endLBA = out.startLBA+out.partitionSize;
|
|
58
|
-
return out;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function parseMBR(mbr) {
|
|
62
|
-
if ( mbr.length < 512 || mbr[0x1fe] != 85 || mbr[0x1ff] != 170 ) { // MBR signature
|
|
63
|
-
throw Error('no MBR signature or buffer is less than 512 bytes');
|
|
64
|
-
}
|
|
65
|
-
let ret = {partitions: []};
|
|
66
|
-
if ( mbr[0x1bc] == 90 && mbr[0x1bc] == 90 ) { // 0x5A5A = copy protected (UEFI)
|
|
67
|
-
ret.copyProtected = true;
|
|
68
|
-
}
|
|
69
|
-
ret.uuid = Buffer.from([mbr[0x1bb], mbr[0x1ba], mbr[0x1b9], mbr[0x1b8]]).toString('hex'); // DiskID: 1B8 (hex) through 1BE (hex) (looks like reverse)
|
|
70
|
-
for (let i = 446; i <= 508; i += 16) { // MBR table blocks
|
|
71
|
-
ret.partitions.push( parseMBRPartition( mbr.slice(i, i+16) ) );
|
|
72
|
-
}
|
|
73
|
-
return ret;
|
|
74
|
-
}
|
|
75
|
-
module.exports.parseMBR = parseMBR;
|
|
76
|
-
|
|
77
|
-
function readUuidBytes(buf, pos) {
|
|
78
|
-
return [
|
|
79
|
-
buf[(pos+3)], buf[(pos+2)], buf[(pos+1)], buf[(pos+0)],
|
|
80
|
-
buf[(pos+5)], buf[(pos+4)], buf[(pos+7)], buf[(pos+6)],
|
|
81
|
-
buf[(pos+8)], buf[(pos+9)], buf[(pos+10)], buf[(pos+11)],
|
|
82
|
-
buf[(pos+12)], buf[(pos+13)], buf[(pos+14)], buf[(pos+15)],
|
|
83
|
-
];
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function parseGPT(buf) { // https://en.wikipedia.org/wiki/GUID_Partition_Table
|
|
87
|
-
let out = {};
|
|
88
|
-
if ( buf.indexOf(EFI_PART) != 0 ) {
|
|
89
|
-
throw Error('not GTP entry');
|
|
90
|
-
}
|
|
91
|
-
out.revision = buf[8]+'.'+buf[9]+'.'+buf[10]+'.'+buf[11];
|
|
92
|
-
out.headerSize = buf.readUInt32LE(12);
|
|
93
|
-
out.headerCRC32 = buf.readUInt32LE(16);
|
|
94
|
-
// buf.readUInt32LE(20); // reserved; must be zero
|
|
95
|
-
out.currentLBA = buf.readBigUInt64LE(24); // Warning: 64bit uint, JS uses this as 53bit
|
|
96
|
-
out.backupLBA = buf.readBigUInt64LE(32); // Warning: 64bit uint, JS uses this as 53bit
|
|
97
|
-
out.firstUsableLBA = buf.readBigUInt64LE(40); // Warning: 64bit uint, JS uses this as 53bit
|
|
98
|
-
out.lastUsableLBA = buf.readBigUInt64LE(48); // Warning: 64bit uint, JS uses this as 53bit
|
|
99
|
-
out.uuid = uuidParse.unparse(readUuidBytes(buf, 56));
|
|
100
|
-
out.tableLBA = buf.readBigUInt64LE(72); // Warning: 64bit uint, JS uses this as 53bit
|
|
101
|
-
out.partitions = buf.readUInt32LE(80);
|
|
102
|
-
out.partitionSize = buf.readUInt32LE(84);
|
|
103
|
-
out.partitionCRC32 = buf.readUInt32LE(88);
|
|
104
|
-
return out;
|
|
105
|
-
}
|
|
106
|
-
module.exports.parseGPT = parseGPT;
|
|
107
|
-
|
|
108
|
-
function parseGPTable(buf) {
|
|
109
|
-
let out = {};
|
|
110
|
-
out.typeId = uuidParse.unparse(readUuidBytes(buf, 0));
|
|
111
|
-
out.type = gptPartTypes.getName(out.typeId);
|
|
112
|
-
out.uuid = uuidParse.unparse(readUuidBytes(buf, 16));
|
|
113
|
-
out.active = (out.uuid==gptPartTypes.EMPTY?false:true);
|
|
114
|
-
out.startLBA = buf.readBigUInt64LE(32); // Warning: 64bit uint, JS uses this as 53bit
|
|
115
|
-
out.endLBA = buf.readBigUInt64LE(40); // Warning: 64bit uint, JS uses this as 53bit
|
|
116
|
-
out.partitionSize = out.endLBA-out.startLBA + 1n; // +1?
|
|
117
|
-
out.attributes = buf.readBigUInt64BE(48); // Warning: 64bit uint, JS uses this as 53bit
|
|
118
|
-
out.label = iconv.decode(buf.slice(56, 128), 'utf16le').split('\u0000')[0]; // bit hack in here
|
|
119
|
-
return out;
|
|
120
|
-
}
|
|
121
|
-
module.exports.parseGPTable = parseGPTable;
|
|
122
|
-
|
|
123
|
-
const buffer = Buffer.allocUnsafe(512);
|
|
124
|
-
function scan(fd) {
|
|
125
|
-
if ( fs.readSync(fd, buffer, 0, buffer.length, 0) != 512 ) {
|
|
126
|
-
throw Error('read only partial data from MBR');
|
|
127
|
-
}
|
|
128
|
-
let rootMbr = parseMBR(buffer);
|
|
129
|
-
rootMbr.type = 'MBR';
|
|
130
|
-
rootMbr.partitions.forEach(function(p) {
|
|
131
|
-
if ( p.type == partTypes.EXTENDED ) { // Extended partition reading
|
|
132
|
-
fs.readSync(fd, buffer, 0, buffer.length, (p.startSector*512));
|
|
133
|
-
let extparts = parseMBR(buffer);
|
|
134
|
-
extparts.partitions.forEach(function(extpart) {
|
|
135
|
-
if ( extpart.type != partTypes.EMPTY ) {
|
|
136
|
-
extpart.startLBA = extpart.startLBA + p.startLBA;
|
|
137
|
-
rootMbr.partitions.push(extpart);
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
if ( p.type == partTypes.GPT ) { // GPT partition table reading
|
|
142
|
-
fs.readSync(fd, buffer, 0, buffer.length, 512);
|
|
143
|
-
let gpt = parseGPT(buffer);
|
|
144
|
-
rootMbr.uuid = gpt.uuid;
|
|
145
|
-
let gBuff = Buffer.allocUnsafe( (gpt.partitions*gpt.partitionSize) );
|
|
146
|
-
fs.readSync(fd, gBuff, 0, gBuff.length, (Number(gpt.tableLBA)*512));
|
|
147
|
-
let partitions = [];
|
|
148
|
-
for (let i=0; i < (gpt.partitions*gpt.partitionSize); i+=gpt.partitionSize ) {
|
|
149
|
-
let table = parseGPTable(gBuff.slice(i, i+gpt.partitionSize));
|
|
150
|
-
if ( table.typeId != gptPartTypes.EMPTY ) {
|
|
151
|
-
partitions.push(table);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
rootMbr.partitions = partitions;
|
|
155
|
-
rootMbr.type = 'GPT';
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
return rootMbr;
|
|
159
|
-
}
|
|
160
|
-
module.exports.scan = scan;
|
|
161
|
-
|
|
162
|
-
function magic(fd) {
|
|
163
|
-
this.haveExt = function(offset) {
|
|
164
|
-
let data = Buffer.allocUnsafe(2048);
|
|
165
|
-
fs.readSync(fd, data, 0, data.length, (512*offset) );
|
|
166
|
-
return (data.readInt16BE(1080) == 0x53ef);
|
|
167
|
-
};
|
|
168
|
-
this.haveNtfs = function(offset) {
|
|
169
|
-
let data = Buffer.allocUnsafe(512);
|
|
170
|
-
fs.readSync(fd, data, 0, data.length, (512*offset) );
|
|
171
|
-
return (data.readInt32BE(3) == 0x4e544653);
|
|
172
|
-
};
|
|
173
|
-
this.haveLvm2 = function(offset) {
|
|
174
|
-
let data = Buffer.allocUnsafe(1024);
|
|
175
|
-
fs.readSync(fd, data, 0, data.length, (512*offset) );
|
|
176
|
-
return (data.readInt32BE(536) == 0x4c564d32);
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
module.exports.magic = magic;
|