ohmu
·
2026-10-15
1#include "physicaldisk.h"
2#include "common/debug.h"
3#include "common/strutil.h"
4#include "sys/scsi/generic/inquiry.h"
5
6#include <libdevinfo.h>
7#include <sys/stat.h>
8
9struct FFWalkTreeBundle {
10 FFPhysicalDiskOptions* options;
11 FFlist* disks;
12};
13
14static int walkDevTree(di_node_t node, di_minor_t minor, struct FFWalkTreeBundle* bundle) {
15 FFPhysicalDiskOptions* options = bundle->options;
16 FFlist* result = bundle->disks;
17
18 if (di_minor_spectype(minor) != S_IFCHR || !ffStrEquals(di_minor_name(minor), "a,raw")) {
19 return DI_WALK_CONTINUE;
20 }
21
22 char* productId;
23 char* vendorId;
24 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "inquiry-product-id", &productId) > 0 && di_prop_lookup_strings(DDI_DEV_T_ANY, node, "inquiry-vendor-id", &vendorId) > 0) {
25 FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateF("%s %s", vendorId, productId);
26 if (options->namePrefix.length && !ffStrbufStartsWithIgnCase(&name, &options->namePrefix)) {
27 return DI_WALK_CONTINUE;
28 }
29
30 int* value;
31
32 FFPhysicalDiskType type = FF_PHYSICALDISK_TYPE_NONE;
33 uint64_t size = 0;
34 int64_t* nblocks;
35 if (di_prop_lookup_int64(DDI_DEV_T_ANY, node, "device-nblocks", &nblocks) > 0) {
36 if (*nblocks == 0) {
37 if (options->hideType & FF_PHYSICALDISK_TYPE_UNUSED) {
38 return DI_WALK_CONTINUE;
39 }
40
41 type |= FF_PHYSICALDISK_TYPE_UNUSED;
42 } else if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "device-blksize", &value) > 0) {
43 size = (uint64_t) ((uint64_t) *nblocks * (uint64_t) *value);
44 }
45 }
46
47 FFPhysicalDiskResult* device = FF_LIST_ADD(FFPhysicalDiskResult, *result);
48 ffStrbufInitMove(&device->name, &name);
49 ffStrbufInitF(&device->devPath, "/devices%s", di_devfs_path(node));
50 ffStrbufInit(&device->serial);
51 ffStrbufInit(&device->revision);
52 ffStrbufInit(&device->interconnect);
53 device->temperature = FF_PHYSICALDISK_TEMP_UNSET;
54 device->type = type;
55 device->size = size;
56
57 char* buf;
58 bool usb = false;
59 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "inquiry-serial-no", &buf) > 0) {
60 ffStrbufSetS(&device->serial, buf);
61 ffStrbufTrimSpace(&device->serial);
62 } else {
63 di_node_t parent = di_parent_node(node);
64 if (parent != DI_NODE_NIL && di_prop_lookup_strings(DDI_DEV_T_ANY, parent, "usb-serialno", &buf) > 0) {
65 ffStrbufSetS(&device->serial, buf);
66 usb = true;
67 }
68 }
69 if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "inquiry-revision-id", &buf) > 0) {
70 ffStrbufSetS(&device->revision, buf);
71 ffStrbufTrimRightSpace(&device->revision);
72 }
73
74 if (usb) {
75 ffStrbufSetStatic(&device->interconnect, "USB");
76 } else if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "class", &buf) > 0) {
77 ffStrbufSetS(&device->interconnect, buf);
78 } else {
79 di_node_t parent = di_parent_node(node);
80 if (parent != DI_NODE_NIL && di_prop_lookup_strings(DDI_DEV_T_ANY, parent, "model", &buf) > 0) {
81 ffStrbufSetS(&device->interconnect, buf);
82 }
83 }
84
85 device->type |= di_prop_find(DDI_DEV_T_ANY, node, "removable-media") ? FF_PHYSICALDISK_TYPE_REMOVABLE : FF_PHYSICALDISK_TYPE_FIXED;
86
87 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "device-solid-state", &value) > 0) {
88 device->type |= *value ? FF_PHYSICALDISK_TYPE_SSD : FF_PHYSICALDISK_TYPE_HDD;
89 }
90 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type", &value) > 0) {
91 device->type |= *value == DTYPE_DIRECT ? FF_PHYSICALDISK_TYPE_READWRITE : *value == DTYPE_RODIRECT ? FF_PHYSICALDISK_TYPE_READONLY
92 : 0;
93 }
94 }
95
96 return DI_WALK_CONTINUE;
97}
98
99const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options) {
100 di_node_t rootNode = di_init("/", DINFOCPYALL);
101 if (rootNode == DI_NODE_NIL) {
102 FF_DEBUG("di_init() failed");
103 return "di_init() failed";
104 }
105 di_walk_minor(rootNode, DDI_NT_BLOCK, DI_WALK_CLDFIRST, &(struct FFWalkTreeBundle) { options, result }, (void*) walkDevTree);
106 di_fini(rootNode);
107
108 return nullptr;
109}