@depup/systeminformation 5.31.4-depup.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.
@@ -0,0 +1,1745 @@
1
+ 'use strict';
2
+ // @ts-check
3
+ // ==================================================================================
4
+ // filesystem.js
5
+ // ----------------------------------------------------------------------------------
6
+ // Description: System Information - library
7
+ // for Node.js
8
+ // Copyright: (c) 2014 - 2026
9
+ // Author: Sebastian Hildebrandt
10
+ // ----------------------------------------------------------------------------------
11
+ // License: MIT
12
+ // ==================================================================================
13
+ // 8. File System
14
+ // ----------------------------------------------------------------------------------
15
+
16
+ const util = require('./util');
17
+ const fs = require('fs');
18
+ const os = require('os');
19
+
20
+ const exec = require('child_process').exec;
21
+ const execSync = require('child_process').execSync;
22
+ const execPromiseSave = util.promisifySave(require('child_process').exec);
23
+
24
+ const _platform = process.platform;
25
+
26
+ const _linux = _platform === 'linux' || _platform === 'android';
27
+ const _darwin = _platform === 'darwin';
28
+ const _windows = _platform === 'win32';
29
+ const _freebsd = _platform === 'freebsd';
30
+ const _openbsd = _platform === 'openbsd';
31
+ const _netbsd = _platform === 'netbsd';
32
+ const _sunos = _platform === 'sunos';
33
+
34
+ const _fs_speed = {};
35
+ const _disk_io = {};
36
+
37
+ // --------------------------
38
+ // FS - mounted file systems
39
+
40
+ function fsSize(drive, callback) {
41
+ if (util.isFunction(drive)) {
42
+ callback = drive;
43
+ drive = '';
44
+ }
45
+
46
+ let macOsDisks = [];
47
+ let osMounts = [];
48
+
49
+ function getmacOsFsType(fs) {
50
+ if (!fs.startsWith('/')) {
51
+ return 'NFS';
52
+ }
53
+ const parts = fs.split('/');
54
+ const fsShort = parts[parts.length - 1];
55
+ const macOsDisksSingle = macOsDisks.filter((item) => item.indexOf(fsShort) >= 0);
56
+ if (macOsDisksSingle.length === 1 && macOsDisksSingle[0].indexOf('APFS') >= 0) {
57
+ return 'APFS';
58
+ }
59
+ return 'HFS';
60
+ }
61
+
62
+ function isLinuxTmpFs(fs) {
63
+ const linuxTmpFileSystems = ['rootfs', 'unionfs', 'squashfs', 'cramfs', 'initrd', 'initramfs', 'devtmpfs', 'tmpfs', 'udev', 'devfs', 'specfs', 'type', 'appimaged'];
64
+ let result = false;
65
+ linuxTmpFileSystems.forEach((linuxFs) => {
66
+ if (fs.toLowerCase().indexOf(linuxFs) >= 0) {
67
+ result = true;
68
+ }
69
+ });
70
+ return result;
71
+ }
72
+
73
+ function filterLines(stdout) {
74
+ const lines = stdout.toString().split('\n');
75
+ lines.shift();
76
+ if (stdout.toString().toLowerCase().indexOf('filesystem')) {
77
+ let removeLines = 0;
78
+ for (let i = 0; i < lines.length; i++) {
79
+ if (lines[i] && lines[i].toLowerCase().startsWith('filesystem')) {
80
+ removeLines = i;
81
+ }
82
+ }
83
+ for (let i = 0; i < removeLines; i++) {
84
+ lines.shift();
85
+ }
86
+ }
87
+ return lines;
88
+ }
89
+
90
+ function parseDf(lines) {
91
+ const data = [];
92
+ lines.forEach((line) => {
93
+ if (line !== '') {
94
+ line = line.replace(/ +/g, ' ').split(' ');
95
+ if (line && (line[0].startsWith('/') || (line[6] && line[6] === '/') || line[0].indexOf('/') > 0 || line[0].indexOf(':') === 1 || (!_darwin && !isLinuxTmpFs(line[1])))) {
96
+ const fs = line[0];
97
+ const fsType = _linux || _freebsd || _openbsd || _netbsd ? line[1] : getmacOsFsType(line[0]);
98
+ const size = parseInt(_linux || _freebsd || _openbsd || _netbsd ? line[2] : line[1], 10) * 1024;
99
+ const used = parseInt(_linux || _freebsd || _openbsd || _netbsd ? line[3] : line[2], 10) * 1024;
100
+ const available = parseInt(_linux || _freebsd || _openbsd || _netbsd ? line[4] : line[3], 10) * 1024;
101
+ const use = parseFloat((100.0 * (used / (used + available))).toFixed(2));
102
+ const rw = osMounts && Object.keys(osMounts).length > 0 ? osMounts[fs] || false : null;
103
+ line.splice(0, _linux || _freebsd || _openbsd || _netbsd ? 6 : 5);
104
+ const mount = line.join(' ');
105
+ if (!data.find((el) => el.fs === fs && el.type === fsType && el.mount === mount)) {
106
+ data.push({
107
+ fs,
108
+ type: fsType,
109
+ size,
110
+ used,
111
+ available,
112
+ use,
113
+ mount,
114
+ rw
115
+ });
116
+ }
117
+ }
118
+ }
119
+ });
120
+ return data;
121
+ }
122
+
123
+ return new Promise((resolve) => {
124
+ process.nextTick(() => {
125
+ let data = [];
126
+ if (_linux || _freebsd || _openbsd || _netbsd || _darwin) {
127
+ let cmd = '';
128
+ macOsDisks = [];
129
+ osMounts = {};
130
+ if (_darwin) {
131
+ cmd = 'df -kP';
132
+ try {
133
+ macOsDisks = execSync('diskutil list')
134
+ .toString()
135
+ .split('\n')
136
+ .filter((line) => {
137
+ return !line.startsWith('/') && line.indexOf(':') > 0;
138
+ });
139
+ execSync('mount')
140
+ .toString()
141
+ .split('\n')
142
+ .filter((line) => {
143
+ return line.startsWith('/');
144
+ })
145
+ .forEach((line) => {
146
+ osMounts[line.split(' ')[0]] = line.toLowerCase().indexOf('read-only') === -1;
147
+ });
148
+ } catch {
149
+ util.noop();
150
+ }
151
+ }
152
+ if (_linux) {
153
+ try {
154
+ cmd = 'export LC_ALL=C; df -kPTx squashfs; unset LC_ALL';
155
+ execSync('cat /proc/mounts 2>/dev/null', util.execOptsLinux)
156
+ .toString()
157
+ .split('\n')
158
+ .filter((line) => {
159
+ return line.startsWith('/');
160
+ })
161
+ .forEach((line) => {
162
+ osMounts[line.split(' ')[0]] = osMounts[line.split(' ')[0]] || false;
163
+ if (line.toLowerCase().indexOf('/snap/') === -1) {
164
+ osMounts[line.split(' ')[0]] = line.toLowerCase().indexOf('rw,') >= 0 || line.toLowerCase().indexOf(' rw ') >= 0;
165
+ }
166
+ });
167
+ } catch {
168
+ util.noop();
169
+ }
170
+ }
171
+ if (_freebsd || _openbsd || _netbsd) {
172
+ try {
173
+ cmd = 'df -kPT';
174
+ execSync('mount')
175
+ .toString()
176
+ .split('\n')
177
+ .forEach((line) => {
178
+ osMounts[line.split(' ')[0]] = line.toLowerCase().indexOf('read-only') === -1;
179
+ });
180
+ } catch {
181
+ util.noop();
182
+ }
183
+ }
184
+ exec(cmd, { maxBuffer: 1024 * 1024 }, (error, stdout) => {
185
+ const lines = filterLines(stdout);
186
+ data = parseDf(lines);
187
+ if (drive) {
188
+ data = data.filter((item) => {
189
+ return item.fs.toLowerCase().indexOf(drive.toLowerCase()) >= 0 || item.mount.toLowerCase().indexOf(drive.toLowerCase()) >= 0;
190
+ });
191
+ }
192
+ if ((!error || data.length) && stdout.toString().trim() !== '') {
193
+ if (callback) {
194
+ callback(data);
195
+ }
196
+ resolve(data);
197
+ } else {
198
+ exec('df -kPT 2>/dev/null', { maxBuffer: 1024 * 1024 }, (error, stdout) => {
199
+ // fixed issue alpine fallback
200
+ const lines = filterLines(stdout);
201
+ data = parseDf(lines);
202
+ if (callback) {
203
+ callback(data);
204
+ }
205
+ resolve(data);
206
+ });
207
+ }
208
+ });
209
+ }
210
+ if (_sunos) {
211
+ if (callback) {
212
+ callback(data);
213
+ }
214
+ resolve(data);
215
+ }
216
+ if (_windows) {
217
+ try {
218
+ const driveSanitized = drive ? util.sanitizeShellString(drive, true) : '';
219
+ const cmd = `Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size ${driveSanitized ? '| where -property Caption -eq ' + driveSanitized : ''} | fl`;
220
+ util.powerShell(cmd).then((stdout, error) => {
221
+ if (!error) {
222
+ const devices = stdout.toString().split(/\n\s*\n/);
223
+ devices.forEach((device) => {
224
+ const lines = device.split('\r\n');
225
+ const size = util.toInt(util.getValue(lines, 'size', ':'));
226
+ const free = util.toInt(util.getValue(lines, 'freespace', ':'));
227
+ const caption = util.getValue(lines, 'caption', ':');
228
+ const rwValue = util.getValue(lines, 'access', ':');
229
+ const rw = rwValue ? util.toInt(rwValue) !== 1 : null;
230
+ if (size) {
231
+ data.push({
232
+ fs: caption,
233
+ type: util.getValue(lines, 'filesystem', ':'),
234
+ size,
235
+ used: size - free,
236
+ available: free,
237
+ use: parseFloat(((100.0 * (size - free)) / size).toFixed(2)),
238
+ mount: caption,
239
+ rw
240
+ });
241
+ }
242
+ });
243
+ }
244
+ if (callback) {
245
+ callback(data);
246
+ }
247
+ resolve(data);
248
+ });
249
+ } catch {
250
+ if (callback) {
251
+ callback(data);
252
+ }
253
+ resolve(data);
254
+ }
255
+ }
256
+ });
257
+ });
258
+ }
259
+
260
+ exports.fsSize = fsSize;
261
+
262
+ // --------------------------
263
+ // FS - open files count
264
+
265
+ function fsOpenFiles(callback) {
266
+ return new Promise((resolve) => {
267
+ process.nextTick(() => {
268
+ const result = {
269
+ max: null,
270
+ allocated: null,
271
+ available: null
272
+ };
273
+ if (_freebsd || _openbsd || _netbsd || _darwin) {
274
+ const cmd = 'sysctl -i kern.maxfiles kern.num_files kern.open_files';
275
+ exec(cmd, { maxBuffer: 1024 * 1024 }, (error, stdout) => {
276
+ if (!error) {
277
+ const lines = stdout.toString().split('\n');
278
+ result.max = parseInt(util.getValue(lines, 'kern.maxfiles', ':'), 10);
279
+ result.allocated = parseInt(util.getValue(lines, 'kern.num_files', ':'), 10) || parseInt(util.getValue(lines, 'kern.open_files', ':'), 10);
280
+ result.available = result.max - result.allocated;
281
+ }
282
+ if (callback) {
283
+ callback(result);
284
+ }
285
+ resolve(result);
286
+ });
287
+ }
288
+ if (_linux) {
289
+ fs.readFile('/proc/sys/fs/file-nr', (error, stdout) => {
290
+ if (!error) {
291
+ const lines = stdout.toString().split('\n');
292
+ if (lines[0]) {
293
+ const parts = lines[0].replace(/\s+/g, ' ').split(' ');
294
+ if (parts.length === 3) {
295
+ result.allocated = parseInt(parts[0], 10);
296
+ result.available = parseInt(parts[1], 10);
297
+ result.max = parseInt(parts[2], 10);
298
+ if (!result.available) {
299
+ result.available = result.max - result.allocated;
300
+ }
301
+ }
302
+ }
303
+ if (callback) {
304
+ callback(result);
305
+ }
306
+ resolve(result);
307
+ } else {
308
+ fs.readFile('/proc/sys/fs/file-max', (error, stdout) => {
309
+ if (!error) {
310
+ const lines = stdout.toString().split('\n');
311
+ if (lines[0]) {
312
+ result.max = parseInt(lines[0], 10);
313
+ }
314
+ }
315
+ if (callback) {
316
+ callback(result);
317
+ }
318
+ resolve(result);
319
+ });
320
+ }
321
+ });
322
+ }
323
+ if (_sunos) {
324
+ if (callback) {
325
+ callback(null);
326
+ }
327
+ resolve(null);
328
+ }
329
+ if (_windows) {
330
+ if (callback) {
331
+ callback(null);
332
+ }
333
+ resolve(null);
334
+ }
335
+ });
336
+ });
337
+ }
338
+
339
+ exports.fsOpenFiles = fsOpenFiles;
340
+
341
+ // --------------------------
342
+ // disks
343
+
344
+ function parseBytes(s) {
345
+ return parseInt(s.substr(s.indexOf(' (') + 2, s.indexOf(' Bytes)') - 10), 10);
346
+ }
347
+
348
+ function parseDevices(lines) {
349
+ const devices = [];
350
+ let i = 0;
351
+ lines.forEach((line) => {
352
+ if (line.length > 0) {
353
+ if (line[0] === '*') {
354
+ i++;
355
+ } else {
356
+ const parts = line.split(':');
357
+ if (parts.length > 1) {
358
+ if (!devices[i]) {
359
+ devices[i] = {
360
+ name: '',
361
+ identifier: '',
362
+ type: 'disk',
363
+ fsType: '',
364
+ mount: '',
365
+ size: 0,
366
+ physical: 'HDD',
367
+ uuid: '',
368
+ label: '',
369
+ model: '',
370
+ serial: '',
371
+ removable: false,
372
+ protocol: '',
373
+ group: '',
374
+ device: ''
375
+ };
376
+ }
377
+ parts[0] = parts[0].trim().toUpperCase().replace(/ +/g, '');
378
+ parts[1] = parts[1].trim();
379
+ if ('DEVICEIDENTIFIER' === parts[0]) {
380
+ devices[i].identifier = parts[1];
381
+ }
382
+ if ('DEVICENODE' === parts[0]) {
383
+ devices[i].name = parts[1];
384
+ }
385
+ if ('VOLUMENAME' === parts[0]) {
386
+ if (parts[1].indexOf('Not applicable') === -1) {
387
+ devices[i].label = parts[1];
388
+ }
389
+ }
390
+ if ('PROTOCOL' === parts[0]) {
391
+ devices[i].protocol = parts[1];
392
+ }
393
+ if ('DISKSIZE' === parts[0]) {
394
+ devices[i].size = parseBytes(parts[1]);
395
+ }
396
+ if ('FILESYSTEMPERSONALITY' === parts[0]) {
397
+ devices[i].fsType = parts[1];
398
+ }
399
+ if ('MOUNTPOINT' === parts[0]) {
400
+ devices[i].mount = parts[1];
401
+ }
402
+ if ('VOLUMEUUID' === parts[0]) {
403
+ devices[i].uuid = parts[1];
404
+ }
405
+ if ('READ-ONLYMEDIA' === parts[0] && parts[1] === 'Yes') {
406
+ devices[i].physical = 'CD/DVD';
407
+ }
408
+ if ('SOLIDSTATE' === parts[0] && parts[1] === 'Yes') {
409
+ devices[i].physical = 'SSD';
410
+ }
411
+ if ('VIRTUAL' === parts[0]) {
412
+ devices[i].type = 'virtual';
413
+ }
414
+ if ('REMOVABLEMEDIA' === parts[0]) {
415
+ devices[i].removable = parts[1] === 'Removable';
416
+ }
417
+ if ('PARTITIONTYPE' === parts[0]) {
418
+ devices[i].type = 'part';
419
+ }
420
+ if ('DEVICE/MEDIANAME' === parts[0]) {
421
+ devices[i].model = parts[1];
422
+ }
423
+ }
424
+ }
425
+ }
426
+ });
427
+ return devices;
428
+ }
429
+
430
+ function parseBlk(lines) {
431
+ let data = [];
432
+
433
+ lines
434
+ .filter((line) => line !== '')
435
+ .forEach((line) => {
436
+ try {
437
+ line = decodeURIComponent(line.replace(/\\x/g, '%'));
438
+ line = line.replace(/\\/g, '\\\\');
439
+ const disk = JSON.parse(line);
440
+ data.push({
441
+ name: util.sanitizeShellString(disk.name),
442
+ type: disk.type,
443
+ fsType: disk.fsType,
444
+ mount: disk.mountpoint,
445
+ size: parseInt(disk.size, 10),
446
+ physical: disk.type === 'disk' ? (disk.rota === '0' ? 'SSD' : 'HDD') : disk.type === 'rom' ? 'CD/DVD' : '',
447
+ uuid: disk.uuid,
448
+ label: disk.label,
449
+ model: (disk.model || '').trim(),
450
+ serial: disk.serial,
451
+ removable: disk.rm === '1',
452
+ protocol: disk.tran,
453
+ group: disk.group || ''
454
+ });
455
+ } catch {
456
+ util.noop();
457
+ }
458
+ });
459
+ data = util.unique(data);
460
+ data = util.sortByKey(data, ['type', 'name']);
461
+ return data;
462
+ }
463
+
464
+ function decodeMdabmData(lines) {
465
+ const raid = util.getValue(lines, 'md_level', '=');
466
+ const label = util.getValue(lines, 'md_name', '='); // <- get label info
467
+ const uuid = util.getValue(lines, 'md_uuid', '='); // <- get uuid info
468
+ const members = [];
469
+ lines.forEach((line) => {
470
+ if (line.toLowerCase().startsWith('md_device_dev') && line.toLowerCase().indexOf('/dev/') > 0) {
471
+ members.push(line.split('/dev/')[1]);
472
+ }
473
+ });
474
+ return {
475
+ raid,
476
+ label,
477
+ uuid,
478
+ members
479
+ };
480
+ }
481
+
482
+ function raidMatchLinux(data) {
483
+ // for all block devices of type "raid%"
484
+ let result = data;
485
+ try {
486
+ data.forEach((element) => {
487
+ if (element.type.startsWith('raid')) {
488
+ const lines = execSync(`mdadm --export --detail /dev/${element.name}`, util.execOptsLinux).toString().split('\n');
489
+ const mdData = decodeMdabmData(lines);
490
+
491
+ element.label = mdData.label; // <- assign label info
492
+ element.uuid = mdData.uuid; // <- assign uuid info
493
+
494
+ if (mdData && mdData.members && mdData.members.length && mdData.raid === element.type) {
495
+ result = result.map((blockdevice) => {
496
+ if (blockdevice.fsType === 'linux_raid_member' && mdData.members.indexOf(blockdevice.name) >= 0) {
497
+ blockdevice.group = element.name;
498
+ }
499
+ return blockdevice;
500
+ });
501
+ }
502
+ }
503
+ });
504
+ } catch {
505
+ util.noop();
506
+ }
507
+ return result;
508
+ }
509
+
510
+ function getDevicesLinux(data) {
511
+ const result = [];
512
+ data.forEach((element) => {
513
+ if (element.type.startsWith('disk')) {
514
+ result.push(element.name);
515
+ }
516
+ });
517
+ return result;
518
+ }
519
+
520
+ function matchDevicesLinux(data) {
521
+ let result = data;
522
+ try {
523
+ const devices = getDevicesLinux(data);
524
+ result = result.map((blockdevice) => {
525
+ if (blockdevice.type.startsWith('part') || blockdevice.type.startsWith('disk')) {
526
+ devices.forEach((element) => {
527
+ if (blockdevice.name.startsWith(element)) {
528
+ blockdevice.device = '/dev/' + element;
529
+ }
530
+ });
531
+ }
532
+ return blockdevice;
533
+ });
534
+ } catch {
535
+ util.noop();
536
+ }
537
+ return result;
538
+ }
539
+
540
+ function getDevicesMac(data) {
541
+ const result = [];
542
+ data.forEach((element) => {
543
+ if (element.type.startsWith('disk')) {
544
+ result.push({ name: element.name, model: element.model, device: element.name });
545
+ }
546
+ if (element.type.startsWith('virtual')) {
547
+ let device = '';
548
+ result.forEach((e) => {
549
+ if (e.model === element.model) {
550
+ device = e.device;
551
+ }
552
+ });
553
+ if (device) {
554
+ result.push({ name: element.name, model: element.model, device });
555
+ }
556
+ }
557
+ });
558
+ return result;
559
+ }
560
+
561
+ function matchDevicesMac(data) {
562
+ let result = data;
563
+ try {
564
+ const devices = getDevicesMac(data);
565
+ result = result.map((blockdevice) => {
566
+ if (blockdevice.type.startsWith('part') || blockdevice.type.startsWith('disk') || blockdevice.type.startsWith('virtual')) {
567
+ devices.forEach((element) => {
568
+ if (blockdevice.name.startsWith(element.name)) {
569
+ blockdevice.device = element.device;
570
+ }
571
+ });
572
+ }
573
+ return blockdevice;
574
+ });
575
+ } catch {
576
+ util.noop();
577
+ }
578
+ return result;
579
+ }
580
+
581
+ function getDevicesWin(diskDrives) {
582
+ const result = [];
583
+ diskDrives.forEach((element) => {
584
+ const lines = element.split('\r\n');
585
+ const device = util.getValue(lines, 'DeviceID', ':');
586
+ let partitions = element.split('@{DeviceID=');
587
+ if (partitions.length > 1) {
588
+ partitions = partitions.slice(1);
589
+ partitions.forEach((partition) => {
590
+ result.push({ name: partition.split(';')[0].toUpperCase(), device });
591
+ });
592
+ }
593
+ });
594
+ return result;
595
+ }
596
+
597
+ function matchDevicesWin(data, diskDrives) {
598
+ const devices = getDevicesWin(diskDrives);
599
+ data.map((element) => {
600
+ const filteresDevices = devices.filter((e) => {
601
+ return e.name === element.name.toUpperCase();
602
+ });
603
+ if (filteresDevices.length > 0) {
604
+ element.device = filteresDevices[0].device;
605
+ }
606
+ return element;
607
+ });
608
+ return data;
609
+ }
610
+
611
+ function blkStdoutToObject(stdout) {
612
+ return stdout
613
+ .toString()
614
+ .replace(/NAME=/g, '{"name":')
615
+ .replace(/FSTYPE=/g, ',"fsType":')
616
+ .replace(/TYPE=/g, ',"type":')
617
+ .replace(/SIZE=/g, ',"size":')
618
+ .replace(/MOUNTPOINT=/g, ',"mountpoint":')
619
+ .replace(/UUID=/g, ',"uuid":')
620
+ .replace(/ROTA=/g, ',"rota":')
621
+ .replace(/RO=/g, ',"ro":')
622
+ .replace(/RM=/g, ',"rm":')
623
+ .replace(/TRAN=/g, ',"tran":')
624
+ .replace(/SERIAL=/g, ',"serial":')
625
+ .replace(/LABEL=/g, ',"label":')
626
+ .replace(/MODEL=/g, ',"model":')
627
+ .replace(/OWNER=/g, ',"owner":')
628
+ .replace(/GROUP=/g, ',"group":')
629
+ .replace(/\n/g, '}\n');
630
+ }
631
+
632
+ function blockDevices(callback) {
633
+ return new Promise((resolve) => {
634
+ process.nextTick(() => {
635
+ let data = [];
636
+ if (_linux) {
637
+ // see https://wiki.ubuntuusers.de/lsblk/
638
+ // exec("lsblk -bo NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,ROTA,RO,TRAN,SERIAL,LABEL,MODEL,OWNER,GROUP,MODE,ALIGNMENT,MIN-IO,OPT-IO,PHY-SEC,LOG-SEC,SCHED,RQ-SIZE,RA,WSAME", function (error, stdout) {
639
+ const procLsblk1 = exec('lsblk -bPo NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,ROTA,RO,RM,TRAN,SERIAL,LABEL,MODEL,OWNER 2>/dev/null', { maxBuffer: 1024 * 1024 }, (error, stdout) => {
640
+ if (!error) {
641
+ const lines = blkStdoutToObject(stdout).split('\n');
642
+ data = parseBlk(lines);
643
+ data = raidMatchLinux(data);
644
+ data = matchDevicesLinux(data);
645
+ if (callback) {
646
+ callback(data);
647
+ }
648
+ resolve(data);
649
+ } else {
650
+ const procLsblk2 = exec('lsblk -bPo NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,ROTA,RO,RM,LABEL,MODEL,OWNER 2>/dev/null', { maxBuffer: 1024 * 1024 }, (error, stdout) => {
651
+ if (!error) {
652
+ const lines = blkStdoutToObject(stdout).split('\n');
653
+ data = parseBlk(lines);
654
+ data = raidMatchLinux(data);
655
+ }
656
+ if (callback) {
657
+ callback(data);
658
+ }
659
+ resolve(data);
660
+ });
661
+ procLsblk2.on('error', () => {
662
+ if (callback) {
663
+ callback(data);
664
+ }
665
+ resolve(data);
666
+ });
667
+ }
668
+ });
669
+ procLsblk1.on('error', () => {
670
+ if (callback) {
671
+ callback(data);
672
+ }
673
+ resolve(data);
674
+ });
675
+ }
676
+ if (_darwin) {
677
+ const procDskutil = exec('diskutil info -all', { maxBuffer: 1024 * 1024 }, (error, stdout) => {
678
+ if (!error) {
679
+ const lines = stdout.toString().split('\n');
680
+ // parse lines into temp array of devices
681
+ data = parseDevices(lines);
682
+ data = matchDevicesMac(data);
683
+ }
684
+ if (callback) {
685
+ callback(data);
686
+ }
687
+ resolve(data);
688
+ });
689
+ procDskutil.on('error', () => {
690
+ if (callback) {
691
+ callback(data);
692
+ }
693
+ resolve(data);
694
+ });
695
+ }
696
+ if (_sunos) {
697
+ if (callback) {
698
+ callback(data);
699
+ }
700
+ resolve(data);
701
+ }
702
+ if (_windows) {
703
+ const drivetypes = ['Unknown', 'NoRoot', 'Removable', 'Local', 'Network', 'CD/DVD', 'RAM'];
704
+ try {
705
+ const workload = [];
706
+ workload.push(util.powerShell('Get-CimInstance -ClassName Win32_LogicalDisk | select Caption,DriveType,Name,FileSystem,Size,VolumeSerialNumber,VolumeName | fl'));
707
+ workload.push(
708
+ util.powerShell(
709
+ "Get-WmiObject -Class Win32_diskdrive | Select-Object -Property PNPDeviceId,DeviceID, Model, Size, @{L='Partitions'; E={$_.GetRelated('Win32_DiskPartition').GetRelated('Win32_LogicalDisk') | Select-Object -Property DeviceID, VolumeName, Size, FreeSpace}} | fl"
710
+ )
711
+ );
712
+ util.promiseAll(workload).then((res) => {
713
+ const logicalDisks = res.results[0].toString().split(/\n\s*\n/);
714
+ const diskDrives = res.results[1].toString().split(/\n\s*\n/);
715
+ logicalDisks.forEach((device) => {
716
+ const lines = device.split('\r\n');
717
+ const drivetype = util.getValue(lines, 'drivetype', ':');
718
+ if (drivetype) {
719
+ data.push({
720
+ name: util.getValue(lines, 'name', ':'),
721
+ identifier: util.getValue(lines, 'caption', ':'),
722
+ type: 'disk',
723
+ fsType: util.getValue(lines, 'filesystem', ':').toLowerCase(),
724
+ mount: util.getValue(lines, 'caption', ':'),
725
+ size: util.getValue(lines, 'size', ':'),
726
+ physical: drivetype >= 0 && drivetype <= 6 ? drivetypes[drivetype] : drivetypes[0],
727
+ uuid: util.getValue(lines, 'volumeserialnumber', ':'),
728
+ label: util.getValue(lines, 'volumename', ':'),
729
+ model: '',
730
+ serial: util.getValue(lines, 'volumeserialnumber', ':'),
731
+ removable: drivetype === '2',
732
+ protocol: '',
733
+ group: '',
734
+ device: ''
735
+ });
736
+ }
737
+ });
738
+ // match devices
739
+ data = matchDevicesWin(data, diskDrives);
740
+ if (callback) {
741
+ callback(data);
742
+ }
743
+ resolve(data);
744
+ });
745
+ } catch {
746
+ if (callback) {
747
+ callback(data);
748
+ }
749
+ resolve(data);
750
+ }
751
+ }
752
+ if (_freebsd || _openbsd || _netbsd) {
753
+ // will follow
754
+ if (callback) {
755
+ callback(null);
756
+ }
757
+ resolve(null);
758
+ }
759
+ });
760
+ });
761
+ }
762
+
763
+ exports.blockDevices = blockDevices;
764
+
765
+ // --------------------------
766
+ // FS - speed
767
+
768
+ function calcFsSpeed(rx, wx) {
769
+ const result = {
770
+ rx: 0,
771
+ wx: 0,
772
+ tx: 0,
773
+ rx_sec: null,
774
+ wx_sec: null,
775
+ tx_sec: null,
776
+ ms: 0
777
+ };
778
+
779
+ if (_fs_speed && _fs_speed.ms) {
780
+ result.rx = rx;
781
+ result.wx = wx;
782
+ result.tx = result.rx + result.wx;
783
+ result.ms = Date.now() - _fs_speed.ms;
784
+ result.rx_sec = (result.rx - _fs_speed.bytes_read) / (result.ms / 1000);
785
+ result.wx_sec = (result.wx - _fs_speed.bytes_write) / (result.ms / 1000);
786
+ result.tx_sec = result.rx_sec + result.wx_sec;
787
+ _fs_speed.rx_sec = result.rx_sec;
788
+ _fs_speed.wx_sec = result.wx_sec;
789
+ _fs_speed.tx_sec = result.tx_sec;
790
+ _fs_speed.bytes_read = result.rx;
791
+ _fs_speed.bytes_write = result.wx;
792
+ _fs_speed.bytes_overall = result.rx + result.wx;
793
+ _fs_speed.ms = Date.now();
794
+ _fs_speed.last_ms = result.ms;
795
+ } else {
796
+ result.rx = rx;
797
+ result.wx = wx;
798
+ result.tx = result.rx + result.wx;
799
+ _fs_speed.rx_sec = null;
800
+ _fs_speed.wx_sec = null;
801
+ _fs_speed.tx_sec = null;
802
+ _fs_speed.bytes_read = result.rx;
803
+ _fs_speed.bytes_write = result.wx;
804
+ _fs_speed.bytes_overall = result.rx + result.wx;
805
+ _fs_speed.ms = Date.now();
806
+ _fs_speed.last_ms = 0;
807
+ }
808
+ return result;
809
+ }
810
+
811
+ function fsStats(callback) {
812
+ return new Promise((resolve) => {
813
+ process.nextTick(() => {
814
+ if (_windows || _freebsd || _openbsd || _netbsd || _sunos) {
815
+ return resolve(null);
816
+ }
817
+
818
+ let result = {
819
+ rx: 0,
820
+ wx: 0,
821
+ tx: 0,
822
+ rx_sec: null,
823
+ wx_sec: null,
824
+ tx_sec: null,
825
+ ms: 0
826
+ };
827
+
828
+ let rx = 0;
829
+ let wx = 0;
830
+ if ((_fs_speed && !_fs_speed.ms) || (_fs_speed && _fs_speed.ms && Date.now() - _fs_speed.ms >= 500)) {
831
+ if (_linux) {
832
+ // exec("df -k | grep /dev/", function(error, stdout) {
833
+ const procLsblk = exec('lsblk -r 2>/dev/null | grep /', { maxBuffer: 1024 * 1024 }, (error, stdout) => {
834
+ if (!error) {
835
+ const lines = stdout.toString().split('\n');
836
+ const fs_filter = [];
837
+ lines.forEach((line) => {
838
+ if (line !== '') {
839
+ line = line.trim().split(' ');
840
+ if (fs_filter.indexOf(line[0]) === -1) {
841
+ fs_filter.push(line[0]);
842
+ }
843
+ }
844
+ });
845
+
846
+ const output = fs_filter.join('|');
847
+ const procCat = exec('cat /proc/diskstats | egrep "' + output + '"', { maxBuffer: 1024 * 1024 }, (error, stdout) => {
848
+ if (!error) {
849
+ const lines = stdout.toString().split('\n');
850
+ lines.forEach((line) => {
851
+ line = line.trim();
852
+ if (line !== '') {
853
+ line = line.replace(/ +/g, ' ').split(' ');
854
+
855
+ rx += parseInt(line[5], 10) * 512;
856
+ wx += parseInt(line[9], 10) * 512;
857
+ }
858
+ });
859
+ result = calcFsSpeed(rx, wx);
860
+ }
861
+ if (callback) {
862
+ callback(result);
863
+ }
864
+ resolve(result);
865
+ });
866
+ procCat.on('error', () => {
867
+ if (callback) {
868
+ callback(result);
869
+ }
870
+ resolve(result);
871
+ });
872
+ } else {
873
+ if (callback) {
874
+ callback(result);
875
+ }
876
+ resolve(result);
877
+ }
878
+ });
879
+ procLsblk.on('error', () => {
880
+ if (callback) {
881
+ callback(result);
882
+ }
883
+ resolve(result);
884
+ });
885
+ }
886
+ if (_darwin) {
887
+ const procIoreg = exec(
888
+ 'ioreg -c IOBlockStorageDriver -k Statistics -r -w0 | sed -n "/IOBlockStorageDriver/,/Statistics/p" | grep "Statistics" | tr -cd "01234567890,\n"',
889
+ { maxBuffer: 1024 * 1024 },
890
+ (error, stdout) => {
891
+ if (!error) {
892
+ const lines = stdout.toString().split('\n');
893
+ lines.forEach((line) => {
894
+ line = line.trim();
895
+ if (line !== '') {
896
+ line = line.split(',');
897
+
898
+ rx += parseInt(line[2], 10);
899
+ wx += parseInt(line[9], 10);
900
+ }
901
+ });
902
+ result = calcFsSpeed(rx, wx);
903
+ }
904
+ if (callback) {
905
+ callback(result);
906
+ }
907
+ resolve(result);
908
+ }
909
+ );
910
+ procIoreg.on('error', () => {
911
+ if (callback) {
912
+ callback(result);
913
+ }
914
+ resolve(result);
915
+ });
916
+ }
917
+ } else {
918
+ result.ms = _fs_speed.last_ms;
919
+ result.rx = _fs_speed.bytes_read;
920
+ result.wx = _fs_speed.bytes_write;
921
+ result.tx = _fs_speed.bytes_read + _fs_speed.bytes_write;
922
+ result.rx_sec = _fs_speed.rx_sec;
923
+ result.wx_sec = _fs_speed.wx_sec;
924
+ result.tx_sec = _fs_speed.tx_sec;
925
+ if (callback) {
926
+ callback(result);
927
+ }
928
+ resolve(result);
929
+ }
930
+ });
931
+ });
932
+ }
933
+
934
+ exports.fsStats = fsStats;
935
+
936
+ function calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime) {
937
+ const result = {
938
+ rIO: 0,
939
+ wIO: 0,
940
+ tIO: 0,
941
+ rIO_sec: null,
942
+ wIO_sec: null,
943
+ tIO_sec: null,
944
+ rWaitTime: 0,
945
+ wWaitTime: 0,
946
+ tWaitTime: 0,
947
+ rWaitPercent: null,
948
+ wWaitPercent: null,
949
+ tWaitPercent: null,
950
+ ms: 0
951
+ };
952
+ if (_disk_io && _disk_io.ms) {
953
+ result.rIO = rIO;
954
+ result.wIO = wIO;
955
+ result.tIO = rIO + wIO;
956
+ result.ms = Date.now() - _disk_io.ms;
957
+ result.rIO_sec = (result.rIO - _disk_io.rIO) / (result.ms / 1000);
958
+ result.wIO_sec = (result.wIO - _disk_io.wIO) / (result.ms / 1000);
959
+ result.tIO_sec = result.rIO_sec + result.wIO_sec;
960
+ result.rWaitTime = rWaitTime;
961
+ result.wWaitTime = wWaitTime;
962
+ result.tWaitTime = tWaitTime;
963
+ result.rWaitPercent = ((result.rWaitTime - _disk_io.rWaitTime) * 100) / result.ms;
964
+ result.wWaitPercent = ((result.wWaitTime - _disk_io.wWaitTime) * 100) / result.ms;
965
+ result.tWaitPercent = ((result.tWaitTime - _disk_io.tWaitTime) * 100) / result.ms;
966
+ _disk_io.rIO = rIO;
967
+ _disk_io.wIO = wIO;
968
+ _disk_io.rIO_sec = result.rIO_sec;
969
+ _disk_io.wIO_sec = result.wIO_sec;
970
+ _disk_io.tIO_sec = result.tIO_sec;
971
+ _disk_io.rWaitTime = rWaitTime;
972
+ _disk_io.wWaitTime = wWaitTime;
973
+ _disk_io.tWaitTime = tWaitTime;
974
+ _disk_io.rWaitPercent = result.rWaitPercent;
975
+ _disk_io.wWaitPercent = result.wWaitPercent;
976
+ _disk_io.tWaitPercent = result.tWaitPercent;
977
+ _disk_io.last_ms = result.ms;
978
+ _disk_io.ms = Date.now();
979
+ } else {
980
+ result.rIO = rIO;
981
+ result.wIO = wIO;
982
+ result.tIO = rIO + wIO;
983
+ result.rWaitTime = rWaitTime;
984
+ result.wWaitTime = wWaitTime;
985
+ result.tWaitTime = tWaitTime;
986
+ _disk_io.rIO = rIO;
987
+ _disk_io.wIO = wIO;
988
+ _disk_io.rIO_sec = null;
989
+ _disk_io.wIO_sec = null;
990
+ _disk_io.tIO_sec = null;
991
+ _disk_io.rWaitTime = rWaitTime;
992
+ _disk_io.wWaitTime = wWaitTime;
993
+ _disk_io.tWaitTime = tWaitTime;
994
+ _disk_io.rWaitPercent = null;
995
+ _disk_io.wWaitPercent = null;
996
+ _disk_io.tWaitPercent = null;
997
+ _disk_io.last_ms = 0;
998
+ _disk_io.ms = Date.now();
999
+ }
1000
+ return result;
1001
+ }
1002
+
1003
+ function disksIO(callback) {
1004
+ return new Promise((resolve) => {
1005
+ process.nextTick(() => {
1006
+ if (_windows) {
1007
+ return resolve(null);
1008
+ }
1009
+ if (_sunos) {
1010
+ return resolve(null);
1011
+ }
1012
+
1013
+ let result = {
1014
+ rIO: 0,
1015
+ wIO: 0,
1016
+ tIO: 0,
1017
+ rIO_sec: null,
1018
+ wIO_sec: null,
1019
+ tIO_sec: null,
1020
+ rWaitTime: 0,
1021
+ wWaitTime: 0,
1022
+ tWaitTime: 0,
1023
+ rWaitPercent: null,
1024
+ wWaitPercent: null,
1025
+ tWaitPercent: null,
1026
+ ms: 0
1027
+ };
1028
+ let rIO = 0;
1029
+ let wIO = 0;
1030
+ let rWaitTime = 0;
1031
+ let wWaitTime = 0;
1032
+ let tWaitTime = 0;
1033
+
1034
+ if ((_disk_io && !_disk_io.ms) || (_disk_io && _disk_io.ms && Date.now() - _disk_io.ms >= 500)) {
1035
+ if (_linux || _freebsd || _openbsd || _netbsd) {
1036
+ // prints Block layer statistics for all mounted volumes
1037
+ // var cmd = "for mount in `lsblk | grep / | sed -r 's/│ └─//' | cut -d ' ' -f 1`; do cat /sys/block/$mount/stat | sed -r 's/ +/;/g' | sed -r 's/^;//'; done";
1038
+ // var cmd = "for mount in `lsblk | grep / | sed 's/[│└─├]//g' | awk '{$1=$1};1' | cut -d ' ' -f 1 | sort -u`; do cat /sys/block/$mount/stat | sed -r 's/ +/;/g' | sed -r 's/^;//'; done";
1039
+ const cmd =
1040
+ 'for mount in `lsblk 2>/dev/null | grep " disk " | sed "s/[│└─├]//g" | awk \'{$1=$1};1\' | cut -d " " -f 1 | sort -u`; do cat /sys/block/$mount/stat | sed -r "s/ +/;/g" | sed -r "s/^;//"; done';
1041
+
1042
+ exec(cmd, { maxBuffer: 1024 * 1024 }, (error, stdout) => {
1043
+ if (!error) {
1044
+ const lines = stdout.split('\n');
1045
+ lines.forEach((line) => {
1046
+ // ignore empty lines
1047
+ if (!line) {
1048
+ return;
1049
+ }
1050
+
1051
+ // sum r/wIO of all disks to compute all disks IO
1052
+ const stats = line.split(';');
1053
+ rIO += parseInt(stats[0], 10);
1054
+ wIO += parseInt(stats[4], 10);
1055
+ rWaitTime += parseInt(stats[3], 10);
1056
+ wWaitTime += parseInt(stats[7], 10);
1057
+ tWaitTime += parseInt(stats[10], 10);
1058
+ });
1059
+ result = calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime);
1060
+
1061
+ if (callback) {
1062
+ callback(result);
1063
+ }
1064
+ resolve(result);
1065
+ } else {
1066
+ if (callback) {
1067
+ callback(result);
1068
+ }
1069
+ resolve(result);
1070
+ }
1071
+ });
1072
+ }
1073
+ if (_darwin) {
1074
+ exec(
1075
+ 'ioreg -c IOBlockStorageDriver -k Statistics -r -w0 | sed -n "/IOBlockStorageDriver/,/Statistics/p" | grep "Statistics" | tr -cd "01234567890,\n"',
1076
+ { maxBuffer: 1024 * 1024 },
1077
+ (error, stdout) => {
1078
+ if (!error) {
1079
+ const lines = stdout.toString().split('\n');
1080
+ lines.forEach((line) => {
1081
+ line = line.trim();
1082
+ if (line !== '') {
1083
+ line = line.split(',');
1084
+
1085
+ rIO += parseInt(line[10], 10);
1086
+ wIO += parseInt(line[0], 10);
1087
+ }
1088
+ });
1089
+ result = calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime);
1090
+ }
1091
+ if (callback) {
1092
+ callback(result);
1093
+ }
1094
+ resolve(result);
1095
+ }
1096
+ );
1097
+ }
1098
+ } else {
1099
+ result.rIO = _disk_io.rIO;
1100
+ result.wIO = _disk_io.wIO;
1101
+ result.tIO = _disk_io.rIO + _disk_io.wIO;
1102
+ result.ms = _disk_io.last_ms;
1103
+ result.rIO_sec = _disk_io.rIO_sec;
1104
+ result.wIO_sec = _disk_io.wIO_sec;
1105
+ result.tIO_sec = _disk_io.tIO_sec;
1106
+ result.rWaitTime = _disk_io.rWaitTime;
1107
+ result.wWaitTime = _disk_io.wWaitTime;
1108
+ result.tWaitTime = _disk_io.tWaitTime;
1109
+ result.rWaitPercent = _disk_io.rWaitPercent;
1110
+ result.wWaitPercent = _disk_io.wWaitPercent;
1111
+ result.tWaitPercent = _disk_io.tWaitPercent;
1112
+ if (callback) {
1113
+ callback(result);
1114
+ }
1115
+ resolve(result);
1116
+ }
1117
+ });
1118
+ });
1119
+ }
1120
+
1121
+ exports.disksIO = disksIO;
1122
+
1123
+ function diskLayout(callback) {
1124
+ function getVendorFromModel(model) {
1125
+ const diskManufacturers = [
1126
+ { pattern: 'WESTERN.*', manufacturer: 'Western Digital' },
1127
+ { pattern: '^WDC.*', manufacturer: 'Western Digital' },
1128
+ { pattern: 'WD.*', manufacturer: 'Western Digital' },
1129
+ { pattern: 'TOSHIBA.*', manufacturer: 'Toshiba' },
1130
+ { pattern: 'HITACHI.*', manufacturer: 'Hitachi' },
1131
+ { pattern: '^IC.*', manufacturer: 'Hitachi' },
1132
+ { pattern: '^HTS.*', manufacturer: 'Hitachi' },
1133
+ { pattern: 'SANDISK.*', manufacturer: 'SanDisk' },
1134
+ { pattern: 'KINGSTON.*', manufacturer: 'Kingston Technology' },
1135
+ { pattern: '^SONY.*', manufacturer: 'Sony' },
1136
+ { pattern: 'TRANSCEND.*', manufacturer: 'Transcend' },
1137
+ { pattern: 'SAMSUNG.*', manufacturer: 'Samsung' },
1138
+ { pattern: '^ST(?!I\\ ).*', manufacturer: 'Seagate' },
1139
+ { pattern: '^STI\\ .*', manufacturer: 'SimpleTech' },
1140
+ { pattern: '^D...-.*', manufacturer: 'IBM' },
1141
+ { pattern: '^IBM.*', manufacturer: 'IBM' },
1142
+ { pattern: '^FUJITSU.*', manufacturer: 'Fujitsu' },
1143
+ { pattern: '^MP.*', manufacturer: 'Fujitsu' },
1144
+ { pattern: '^MK.*', manufacturer: 'Toshiba' },
1145
+ { pattern: 'MAXTO.*', manufacturer: 'Maxtor' },
1146
+ { pattern: 'PIONEER.*', manufacturer: 'Pioneer' },
1147
+ { pattern: 'PHILIPS.*', manufacturer: 'Philips' },
1148
+ { pattern: 'QUANTUM.*', manufacturer: 'Quantum Technology' },
1149
+ { pattern: 'FIREBALL.*', manufacturer: 'Quantum Technology' },
1150
+ { pattern: '^VBOX.*', manufacturer: 'VirtualBox' },
1151
+ { pattern: 'CORSAIR.*', manufacturer: 'Corsair Components' },
1152
+ { pattern: 'CRUCIAL.*', manufacturer: 'Crucial' },
1153
+ { pattern: 'ECM.*', manufacturer: 'ECM' },
1154
+ { pattern: 'INTEL.*', manufacturer: 'INTEL' },
1155
+ { pattern: 'EVO.*', manufacturer: 'Samsung' },
1156
+ { pattern: 'APPLE.*', manufacturer: 'Apple' }
1157
+ ];
1158
+
1159
+ let result = '';
1160
+ if (model) {
1161
+ model = model.toUpperCase();
1162
+ diskManufacturers.forEach((manufacturer) => {
1163
+ const re = RegExp(manufacturer.pattern);
1164
+ if (re.test(model)) {
1165
+ result = manufacturer.manufacturer;
1166
+ }
1167
+ });
1168
+ }
1169
+ return result;
1170
+ }
1171
+
1172
+ return new Promise((resolve) => {
1173
+ process.nextTick(() => {
1174
+ const commitResult = (res) => {
1175
+ for (let i = 0; i < res.length; i++) {
1176
+ delete res[i].BSDName;
1177
+ }
1178
+ if (callback) {
1179
+ callback(res);
1180
+ }
1181
+ resolve(res);
1182
+ };
1183
+
1184
+ const result = [];
1185
+ let cmd = '';
1186
+
1187
+ if (_linux) {
1188
+ let cmdFullSmart = '';
1189
+
1190
+ exec('export LC_ALL=C; lsblk -ablJO 2>/dev/null; unset LC_ALL', { maxBuffer: 1024 * 1024 }, (error, stdout) => {
1191
+ if (!error) {
1192
+ try {
1193
+ const out = stdout.toString().trim();
1194
+ let devices = [];
1195
+ try {
1196
+ const outJSON = JSON.parse(out);
1197
+ if (outJSON && {}.hasOwnProperty.call(outJSON, 'blockdevices')) {
1198
+ devices = outJSON.blockdevices.filter((item) => {
1199
+ return (
1200
+ item.type === 'disk' &&
1201
+ item.size > 0 &&
1202
+ (item.model !== null ||
1203
+ (item.mountpoint === null &&
1204
+ item.label === null &&
1205
+ item.fstype === null &&
1206
+ item.parttype === null &&
1207
+ item.path &&
1208
+ item.path.indexOf('/ram') !== 0 &&
1209
+ item.path.indexOf('/loop') !== 0 &&
1210
+ item['disc-max'] &&
1211
+ item['disc-max'] !== 0))
1212
+ );
1213
+ });
1214
+ }
1215
+ } catch {
1216
+ // fallback to older version of lsblk
1217
+ try {
1218
+ const out2 = execSync(
1219
+ 'export LC_ALL=C; lsblk -bPo NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,ROTA,RO,RM,LABEL,MODEL,OWNER,GROUP 2>/dev/null; unset LC_ALL',
1220
+ util.execOptsLinux
1221
+ ).toString();
1222
+ const lines = blkStdoutToObject(out2).split('\n');
1223
+ const data = parseBlk(lines);
1224
+ devices = data.filter((item) => {
1225
+ return item.type === 'disk' && item.size > 0 && ((item.model !== null && item.model !== '') || (item.mount === '' && item.label === '' && item.fsType === ''));
1226
+ });
1227
+ } catch {
1228
+ util.noop();
1229
+ }
1230
+ }
1231
+ devices.forEach((device) => {
1232
+ let mediumType = '';
1233
+ const BSDName = '/dev/' + device.name;
1234
+ const logical = device.name;
1235
+ try {
1236
+ mediumType = execSync('cat /sys/block/' + logical + '/queue/rotational 2>/dev/null', util.execOptsLinux)
1237
+ .toString()
1238
+ .split('\n')[0];
1239
+ } catch {
1240
+ util.noop();
1241
+ }
1242
+ let interfaceType = device.tran ? device.tran.toUpperCase().trim() : '';
1243
+ if (interfaceType === 'NVME') {
1244
+ mediumType = '2';
1245
+ interfaceType = 'PCIe';
1246
+ }
1247
+ result.push({
1248
+ device: BSDName,
1249
+ type:
1250
+ mediumType === '0'
1251
+ ? 'SSD'
1252
+ : mediumType === '1'
1253
+ ? 'HD'
1254
+ : mediumType === '2'
1255
+ ? 'NVMe'
1256
+ : device.model && device.model.indexOf('SSD') > -1
1257
+ ? 'SSD'
1258
+ : device.model && device.model.indexOf('NVM') > -1
1259
+ ? 'NVMe'
1260
+ : 'HD',
1261
+ name: device.model || '',
1262
+ vendor: getVendorFromModel(device.model) || (device.vendor ? device.vendor.trim() : ''),
1263
+ size: device.size || 0,
1264
+ bytesPerSector: null,
1265
+ totalCylinders: null,
1266
+ totalHeads: null,
1267
+ totalSectors: null,
1268
+ totalTracks: null,
1269
+ tracksPerCylinder: null,
1270
+ sectorsPerTrack: null,
1271
+ firmwareRevision: device.rev ? device.rev.trim() : '',
1272
+ serialNum: device.serial ? device.serial.trim() : '',
1273
+ interfaceType: interfaceType,
1274
+ smartStatus: 'unknown',
1275
+ temperature: null,
1276
+ BSDName: BSDName
1277
+ });
1278
+ cmd += `printf "\n${BSDName}|"; smartctl -H ${BSDName} | grep overall;`;
1279
+ cmdFullSmart += `${cmdFullSmart ? 'printf ",";' : ''}smartctl -a -j ${BSDName};`;
1280
+ });
1281
+ } catch {
1282
+ util.noop();
1283
+ }
1284
+ }
1285
+ // check S.M.A.R.T. status
1286
+ if (cmdFullSmart) {
1287
+ exec(cmdFullSmart, { maxBuffer: 1024 * 1024 }, (error, stdout) => {
1288
+ try {
1289
+ const data = JSON.parse(`[${stdout}]`);
1290
+ data.forEach((disk) => {
1291
+ const diskBSDName = disk.smartctl.argv[disk.smartctl.argv.length - 1];
1292
+
1293
+ for (let i = 0; i < result.length; i++) {
1294
+ if (result[i].BSDName === diskBSDName) {
1295
+ result[i].smartStatus = disk.smart_status.passed ? 'Ok' : disk.smart_status.passed === false ? 'Predicted Failure' : 'unknown';
1296
+ if (disk.temperature && disk.temperature.current) {
1297
+ result[i].temperature = disk.temperature.current;
1298
+ }
1299
+ result[i].smartData = disk;
1300
+ }
1301
+ }
1302
+ });
1303
+ commitResult(result);
1304
+ } catch {
1305
+ if (cmd) {
1306
+ cmd = cmd + 'printf "\n"';
1307
+ exec(cmd, { maxBuffer: 1024 * 1024 }, (error, stdout) => {
1308
+ const lines = stdout.toString().split('\n');
1309
+ lines.forEach((line) => {
1310
+ if (line) {
1311
+ const parts = line.split('|');
1312
+ if (parts.length === 2) {
1313
+ const BSDName = parts[0];
1314
+ parts[1] = parts[1].trim();
1315
+ const parts2 = parts[1].split(':');
1316
+ if (parts2.length === 2) {
1317
+ parts2[1] = parts2[1].trim();
1318
+ const status = parts2[1].toLowerCase();
1319
+ for (let i = 0; i < result.length; i++) {
1320
+ if (result[i].BSDName === BSDName) {
1321
+ result[i].smartStatus = status === 'passed' ? 'Ok' : status === 'failed!' ? 'Predicted Failure' : 'unknown';
1322
+ }
1323
+ }
1324
+ }
1325
+ }
1326
+ }
1327
+ });
1328
+ commitResult(result);
1329
+ });
1330
+ } else {
1331
+ commitResult(result);
1332
+ }
1333
+ }
1334
+ });
1335
+ } else {
1336
+ commitResult(result);
1337
+ }
1338
+ });
1339
+ }
1340
+ if (_freebsd || _openbsd || _netbsd) {
1341
+ if (callback) {
1342
+ callback(result);
1343
+ }
1344
+ resolve(result);
1345
+ }
1346
+ if (_sunos) {
1347
+ if (callback) {
1348
+ callback(result);
1349
+ }
1350
+ resolve(result);
1351
+ }
1352
+ if (_darwin) {
1353
+ let cmdFullSmart = '';
1354
+ exec(`system_profiler SPSerialATADataType SPNVMeDataType ${parseInt(os.release(), 10) > 24 ? 'SPUSBHostDataType' : 'SPUSBDataType'} `, { maxBuffer: 1024 * 1024 }, (error, stdout) => {
1355
+ if (!error) {
1356
+ // split by type:
1357
+ const lines = stdout.toString().split('\n');
1358
+ const linesSATA = [];
1359
+ const linesNVMe = [];
1360
+ const linesUSB = [];
1361
+ let dataType = 'SATA';
1362
+ lines.forEach((line) => {
1363
+ if (line === 'NVMExpress:') {
1364
+ dataType = 'NVMe';
1365
+ } else if (line === 'USB:') {
1366
+ dataType = 'USB';
1367
+ } else if (line === 'SATA/SATA Express:') {
1368
+ dataType = 'SATA';
1369
+ } else if (dataType === 'SATA') {
1370
+ linesSATA.push(line);
1371
+ } else if (dataType === 'NVMe') {
1372
+ linesNVMe.push(line);
1373
+ } else if (dataType === 'USB') {
1374
+ linesUSB.push(line);
1375
+ }
1376
+ });
1377
+ try {
1378
+ // Serial ATA Drives
1379
+ const devices = linesSATA.join('\n').split(' Physical Interconnect: ');
1380
+ devices.shift();
1381
+ devices.forEach((device) => {
1382
+ device = 'InterfaceType: ' + device;
1383
+ const lines = device.split('\n');
1384
+ const mediumType = util.getValue(lines, 'Medium Type', ':', true).trim();
1385
+ const sizeStr = util.getValue(lines, 'capacity', ':', true).trim();
1386
+ const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim();
1387
+ if (sizeStr) {
1388
+ let sizeValue = 0;
1389
+ if (sizeStr.indexOf('(') >= 0) {
1390
+ sizeValue = parseInt(
1391
+ sizeStr
1392
+ .match(/\(([^)]+)\)/)[1]
1393
+ .replace(/\./g, '')
1394
+ .replace(/,/g, '')
1395
+ .replace(/\s/g, ''),
1396
+ 10
1397
+ );
1398
+ }
1399
+ if (!sizeValue) {
1400
+ sizeValue = parseInt(sizeStr, 10);
1401
+ }
1402
+ if (sizeValue) {
1403
+ const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase();
1404
+ result.push({
1405
+ device: BSDName,
1406
+ type: mediumType.startsWith('Solid') ? 'SSD' : 'HD',
1407
+ name: util.getValue(lines, 'Model', ':', true).trim(),
1408
+ vendor: getVendorFromModel(util.getValue(lines, 'Model', ':', true).trim()) || util.getValue(lines, 'Manufacturer', ':', true),
1409
+ size: sizeValue,
1410
+ bytesPerSector: null,
1411
+ totalCylinders: null,
1412
+ totalHeads: null,
1413
+ totalSectors: null,
1414
+ totalTracks: null,
1415
+ tracksPerCylinder: null,
1416
+ sectorsPerTrack: null,
1417
+ firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(),
1418
+ serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(),
1419
+ interfaceType: util.getValue(lines, 'InterfaceType', ':', true).trim(),
1420
+ smartStatus: smartStatusString === 'verified' ? 'OK' : smartStatusString || 'unknown',
1421
+ temperature: null,
1422
+ BSDName: BSDName
1423
+ });
1424
+ cmd = cmd + 'printf "\n' + BSDName + '|"; diskutil info /dev/' + BSDName + ' | grep SMART;';
1425
+ cmdFullSmart += `${cmdFullSmart ? 'printf ",";' : ''}smartctl -a -j ${BSDName};`;
1426
+ }
1427
+ }
1428
+ });
1429
+ } catch {
1430
+ util.noop();
1431
+ }
1432
+
1433
+ // NVME Drives
1434
+ try {
1435
+ const devices = linesNVMe.join('\n').split('\n\n Capacity:');
1436
+ devices.shift();
1437
+ devices.forEach((device) => {
1438
+ device = `!Capacity: ${device}`;
1439
+ const lines = device.split('\n');
1440
+ const linkWidth = util.getValue(lines, 'link width', ':', true).trim();
1441
+ const sizeStr = util.getValue(lines, '!capacity', ':', true).trim();
1442
+ const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim();
1443
+ if (sizeStr) {
1444
+ let sizeValue = 0;
1445
+ if (sizeStr.indexOf('(') >= 0) {
1446
+ sizeValue = parseInt(
1447
+ sizeStr
1448
+ .match(/\(([^)]+)\)/)[1]
1449
+ .replace(/\./g, '')
1450
+ .replace(/,/g, '')
1451
+ .replace(/\s/g, ''),
1452
+ 10
1453
+ );
1454
+ }
1455
+ if (!sizeValue) {
1456
+ sizeValue = parseInt(sizeStr, 10);
1457
+ }
1458
+ if (sizeValue) {
1459
+ const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase();
1460
+ result.push({
1461
+ device: BSDName,
1462
+ type: 'NVMe',
1463
+ name: util.getValue(lines, 'Model', ':', true).trim(),
1464
+ vendor: getVendorFromModel(util.getValue(lines, 'Model', ':', true).trim()),
1465
+ size: sizeValue,
1466
+ bytesPerSector: null,
1467
+ totalCylinders: null,
1468
+ totalHeads: null,
1469
+ totalSectors: null,
1470
+ totalTracks: null,
1471
+ tracksPerCylinder: null,
1472
+ sectorsPerTrack: null,
1473
+ firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(),
1474
+ serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(),
1475
+ interfaceType: ('PCIe ' + linkWidth).trim(),
1476
+ smartStatus: smartStatusString === 'verified' ? 'OK' : smartStatusString || 'unknown',
1477
+ temperature: null,
1478
+ BSDName: BSDName
1479
+ });
1480
+ cmd = `${cmd}printf "\n${BSDName}|"; diskutil info /dev/${BSDName} | grep SMART;`;
1481
+ cmdFullSmart += `${cmdFullSmart ? 'printf ",";' : ''}smartctl -a -j ${BSDName};`;
1482
+ }
1483
+ }
1484
+ });
1485
+ } catch {
1486
+ util.noop();
1487
+ }
1488
+ // USB Drives
1489
+ try {
1490
+ const devices = linesUSB.join('\n').replaceAll('Media:\n ', 'Model:').split('\n\n Product ID:');
1491
+ devices.shift();
1492
+ devices.forEach((device) => {
1493
+ const lines = device.split('\n');
1494
+ const sizeStr = util.getValue(lines, 'Capacity', ':', true).trim();
1495
+ const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim();
1496
+ if (sizeStr) {
1497
+ let sizeValue = 0;
1498
+ if (sizeStr.indexOf('(') >= 0) {
1499
+ sizeValue = parseInt(
1500
+ sizeStr
1501
+ .match(/\(([^)]+)\)/)[1]
1502
+ .replace(/\./g, '')
1503
+ .replace(/,/g, '')
1504
+ .replace(/\s/g, ''),
1505
+ 10
1506
+ );
1507
+ }
1508
+ if (!sizeValue) {
1509
+ sizeValue = parseInt(sizeStr, 10);
1510
+ }
1511
+ if (sizeValue) {
1512
+ const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase();
1513
+ result.push({
1514
+ device: BSDName,
1515
+ type: 'USB',
1516
+ name: util.getValue(lines, 'Model', ':', true).trim().replaceAll(':', ''),
1517
+ vendor: getVendorFromModel(util.getValue(lines, 'Model', ':', true).trim()),
1518
+ size: sizeValue,
1519
+ bytesPerSector: null,
1520
+ totalCylinders: null,
1521
+ totalHeads: null,
1522
+ totalSectors: null,
1523
+ totalTracks: null,
1524
+ tracksPerCylinder: null,
1525
+ sectorsPerTrack: null,
1526
+ firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(),
1527
+ serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(),
1528
+ interfaceType: 'USB',
1529
+ smartStatus: smartStatusString === 'verified' ? 'OK' : smartStatusString || 'unknown',
1530
+ temperature: null,
1531
+ BSDName: BSDName
1532
+ });
1533
+ cmd = cmd + 'printf "\n' + BSDName + '|"; diskutil info /dev/' + BSDName + ' | grep SMART;';
1534
+ cmdFullSmart += `${cmdFullSmart ? 'printf ",";' : ''}smartctl -a -j ${BSDName};`;
1535
+ }
1536
+ }
1537
+ });
1538
+ } catch {
1539
+ util.noop();
1540
+ }
1541
+ // check S.M.A.R.T. status
1542
+ if (cmdFullSmart) {
1543
+ exec(cmdFullSmart, { maxBuffer: 1024 * 1024 }, (error, stdout) => {
1544
+ try {
1545
+ const data = JSON.parse(`[${stdout}]`);
1546
+ data.forEach((disk) => {
1547
+ const diskBSDName = disk.smartctl.argv[disk.smartctl.argv.length - 1];
1548
+
1549
+ for (let i = 0; i < result.length; i++) {
1550
+ if (result[i].BSDName === diskBSDName) {
1551
+ result[i].smartStatus = disk.smart_status.passed ? 'Ok' : disk.smart_status.passed === false ? 'Predicted Failure' : 'unknown';
1552
+ if (disk.temperature && disk.temperature.current) {
1553
+ result[i].temperature = disk.temperature.current;
1554
+ }
1555
+ result[i].smartData = disk;
1556
+ }
1557
+ }
1558
+ });
1559
+ commitResult(result);
1560
+ } catch (e) {
1561
+ if (cmd) {
1562
+ cmd = cmd + 'printf "\n"';
1563
+ exec(cmd, { maxBuffer: 1024 * 1024 }, (error, stdout) => {
1564
+ const lines = stdout.toString().split('\n');
1565
+ lines.forEach((line) => {
1566
+ if (line) {
1567
+ const parts = line.split('|');
1568
+ if (parts.length === 2) {
1569
+ const BSDName = parts[0];
1570
+ parts[1] = parts[1].trim();
1571
+ const parts2 = parts[1].split(':');
1572
+ if (parts2.length === 2) {
1573
+ parts2[1] = parts2[1].trim();
1574
+ const status = parts2[1].toLowerCase();
1575
+ for (let i = 0; i < result.length; i++) {
1576
+ if (result[i].BSDName === BSDName) {
1577
+ result[i].smartStatus = status === 'passed' ? 'Ok' : status === 'failed!' ? 'Predicted Failure' : 'unknown';
1578
+ }
1579
+ }
1580
+ }
1581
+ }
1582
+ }
1583
+ });
1584
+ commitResult(result);
1585
+ });
1586
+ } else {
1587
+ commitResult(result);
1588
+ }
1589
+ }
1590
+ });
1591
+ } else if (cmd) {
1592
+ cmd = cmd + 'printf "\n"';
1593
+ exec(cmd, { maxBuffer: 1024 * 1024 }, (error, stdout) => {
1594
+ const lines = stdout.toString().split('\n');
1595
+ lines.forEach((line) => {
1596
+ if (line) {
1597
+ const parts = line.split('|');
1598
+ if (parts.length === 2) {
1599
+ const BSDName = parts[0];
1600
+ parts[1] = parts[1].trim();
1601
+ const parts2 = parts[1].split(':');
1602
+ if (parts2.length === 2) {
1603
+ parts2[1] = parts2[1].trim();
1604
+ const status = parts2[1].toLowerCase();
1605
+ for (let i = 0; i < result.length; i++) {
1606
+ if (result[i].BSDName === BSDName) {
1607
+ result[i].smartStatus = status === 'not supported' ? 'not supported' : status === 'verified' ? 'Ok' : status === 'failing' ? 'Predicted Failure' : 'unknown';
1608
+ }
1609
+ }
1610
+ }
1611
+ }
1612
+ }
1613
+ });
1614
+ commitResult(result);
1615
+ });
1616
+ } else {
1617
+ commitResult(result);
1618
+ }
1619
+ } else {
1620
+ commitResult(result);
1621
+ }
1622
+ });
1623
+ }
1624
+ if (_windows) {
1625
+ try {
1626
+ const workload = [];
1627
+ workload.push(
1628
+ util.powerShell(
1629
+ 'Get-CimInstance Win32_DiskDrive | select Caption,Size,Status,PNPDeviceId,DeviceId,BytesPerSector,TotalCylinders,TotalHeads,TotalSectors,TotalTracks,TracksPerCylinder,SectorsPerTrack,FirmwareRevision,SerialNumber,InterfaceType | fl'
1630
+ )
1631
+ );
1632
+ workload.push(util.powerShell('Get-PhysicalDisk | select BusType,MediaType,FriendlyName,Model,SerialNumber,Size | fl'));
1633
+ if (util.smartMonToolsInstalled()) {
1634
+ try {
1635
+ const smartDev = JSON.parse(execSync('smartctl --scan -j').toString());
1636
+ if (smartDev && smartDev.devices && smartDev.devices.length > 0) {
1637
+ smartDev.devices.forEach((dev) => {
1638
+ workload.push(execPromiseSave(`smartctl -j -a ${dev.name}`, util.execOptsWin));
1639
+ });
1640
+ }
1641
+ } catch {
1642
+ util.noop();
1643
+ }
1644
+ }
1645
+ util.promiseAll(workload).then((data) => {
1646
+ let devices = data.results[0].toString().split(/\n\s*\n/);
1647
+ devices.forEach((device) => {
1648
+ const lines = device.split('\r\n');
1649
+ const size = util.getValue(lines, 'Size', ':').trim();
1650
+ const status = util.getValue(lines, 'Status', ':').trim().toLowerCase();
1651
+ if (size) {
1652
+ result.push({
1653
+ device: util.getValue(lines, 'DeviceId', ':'), // changed from PNPDeviceId to DeviceID (be be able to match devices)
1654
+ type: device.indexOf('SSD') > -1 ? 'SSD' : 'HD', // just a starting point ... better: MSFT_PhysicalDisk - Media Type ... see below
1655
+ name: util.getValue(lines, 'Caption', ':'),
1656
+ vendor: getVendorFromModel(util.getValue(lines, 'Caption', ':', true).trim()),
1657
+ size: parseInt(size, 10),
1658
+ bytesPerSector: parseInt(util.getValue(lines, 'BytesPerSector', ':'), 10),
1659
+ totalCylinders: parseInt(util.getValue(lines, 'TotalCylinders', ':'), 10),
1660
+ totalHeads: parseInt(util.getValue(lines, 'TotalHeads', ':'), 10),
1661
+ totalSectors: parseInt(util.getValue(lines, 'TotalSectors', ':'), 10),
1662
+ totalTracks: parseInt(util.getValue(lines, 'TotalTracks', ':'), 10),
1663
+ tracksPerCylinder: parseInt(util.getValue(lines, 'TracksPerCylinder', ':'), 10),
1664
+ sectorsPerTrack: parseInt(util.getValue(lines, 'SectorsPerTrack', ':'), 10),
1665
+ firmwareRevision: util.getValue(lines, 'FirmwareRevision', ':').trim(),
1666
+ serialNum: util.getValue(lines, 'SerialNumber', ':').trim(),
1667
+ interfaceType: util.getValue(lines, 'InterfaceType', ':').trim(),
1668
+ smartStatus: status === 'ok' ? 'Ok' : status === 'degraded' ? 'Degraded' : status === 'pred fail' ? 'Predicted Failure' : 'Unknown',
1669
+ temperature: null
1670
+ });
1671
+ }
1672
+ });
1673
+ devices = data.results[1].split(/\n\s*\n/);
1674
+ devices.forEach((device) => {
1675
+ const lines = device.split('\r\n');
1676
+ const serialNum = util.getValue(lines, 'SerialNumber', ':').trim();
1677
+ const name = util.getValue(lines, 'FriendlyName', ':').trim().replace('Msft ', 'Microsoft');
1678
+ const size = util.getValue(lines, 'Size', ':').trim();
1679
+ const model = util.getValue(lines, 'Model', ':').trim();
1680
+ const interfaceType = util.getValue(lines, 'BusType', ':').trim();
1681
+ let mediaType = util.getValue(lines, 'MediaType', ':').trim();
1682
+ if (mediaType === '3' || mediaType === 'HDD') {
1683
+ mediaType = 'HD';
1684
+ }
1685
+ if (mediaType === '4') {
1686
+ mediaType = 'SSD';
1687
+ }
1688
+ if (mediaType === '5') {
1689
+ mediaType = 'SCM';
1690
+ }
1691
+ if (mediaType === 'Unspecified' && (model.toLowerCase().indexOf('virtual') > -1 || model.toLowerCase().indexOf('vbox') > -1)) {
1692
+ mediaType = 'Virtual';
1693
+ }
1694
+ if (size) {
1695
+ let i = util.findObjectByKey(result, 'serialNum', serialNum);
1696
+ if (i === -1 || serialNum === '') {
1697
+ i = util.findObjectByKey(result, 'name', name);
1698
+ }
1699
+ if (i !== -1) {
1700
+ result[i].type = mediaType;
1701
+ result[i].interfaceType = interfaceType;
1702
+ }
1703
+ }
1704
+ });
1705
+ // S.M.A.R.T
1706
+ data.results.shift();
1707
+ data.results.shift();
1708
+ if (data.results.length) {
1709
+ data.results.forEach((smartStr) => {
1710
+ try {
1711
+ const smartData = JSON.parse(smartStr);
1712
+ if (smartData.serial_number) {
1713
+ const serialNum = smartData.serial_number;
1714
+ const i = util.findObjectByKey(result, 'serialNum', serialNum);
1715
+ if (i !== -1) {
1716
+ result[i].smartStatus =
1717
+ smartData.smart_status && smartData.smart_status.passed ? 'Ok' : smartData.smart_status && smartData.smart_status.passed === false ? 'Predicted Failure' : 'unknown';
1718
+ if (smartData.temperature && smartData.temperature.current) {
1719
+ result[i].temperature = smartData.temperature.current;
1720
+ }
1721
+ result[i].smartData = smartData;
1722
+ }
1723
+ }
1724
+ } catch {
1725
+ util.noop();
1726
+ }
1727
+ });
1728
+ }
1729
+ if (callback) {
1730
+ callback(result);
1731
+ }
1732
+ resolve(result);
1733
+ });
1734
+ } catch {
1735
+ if (callback) {
1736
+ callback(result);
1737
+ }
1738
+ resolve(result);
1739
+ }
1740
+ }
1741
+ });
1742
+ });
1743
+ }
1744
+
1745
+ exports.diskLayout = diskLayout;