summaryrefslogtreecommitdiffstats
path: root/src/detection/physicaldisk/physicaldisk_nbsd.c
diff options
context:
space:
mode:
authorsumuel <samuel@yakubos.org>2026-08-17 20:44:55 +0000
committersumuel <samuel@yakubos.org>2026-08-17 20:44:55 +0000
commit76424950e373d3b04ac3dd13019151bfba3e8423 (patch)
tree4c3cfdbda039e592b9186be3e28f8d7cfd439e8a /src/detection/physicaldisk/physicaldisk_nbsd.c
Add the files
Diffstat (limited to 'src/detection/physicaldisk/physicaldisk_nbsd.c')
-rw-r--r--src/detection/physicaldisk/physicaldisk_nbsd.c324
1 files changed, 324 insertions, 0 deletions
diff --git a/src/detection/physicaldisk/physicaldisk_nbsd.c b/src/detection/physicaldisk/physicaldisk_nbsd.c
new file mode 100644
index 0000000..0733f3b
--- /dev/null
+++ b/src/detection/physicaldisk/physicaldisk_nbsd.c
@@ -0,0 +1,324 @@
+#define DKTYPENAMES
+#include "physicaldisk.h"
+#include "common/io.h"
+#include "common/sysctl.h"
+#include "common/debug.h"
+
+#include <util.h>
+#include <prop/proplib.h>
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+#include <sys/dkio.h>
+#include <sys/scsiio.h>
+#include <dev/scsipi/scsipi_all.h>
+#include <dev/scsipi/scsi_spc.h>
+#include <fcntl.h>
+#include <errno.h>
+
+static inline const char* retstsToStr(uint8_t retsts) {
+ switch (retsts) {
+ case SCCMD_OK:
+ return "OK";
+ case SCCMD_TIMEOUT:
+ return "TIMEOUT";
+ case SCCMD_BUSY:
+ return "BUSY";
+ case SCCMD_SENSE:
+ return "SENSE";
+ case SCCMD_UNKNOWN:
+ return "UNKNOWN";
+ default:
+ return "?";
+ }
+}
+
+#ifndef NDEBUG
+static inline const char* senseKeyToStr(uint8_t key) {
+ switch (key) {
+ case 0x0:
+ return "No Sense";
+ case 0x1:
+ return "Recovered Error";
+ case 0x2:
+ return "Not Ready";
+ case 0x3:
+ return "Medium Error";
+ case 0x4:
+ return "Hardware Error";
+ case 0x5:
+ return "Illegal Request";
+ case 0x6:
+ return "Unit Attention";
+ case 0x7:
+ return "Data Protect";
+ case 0x8:
+ return "Blank Check";
+ case 0x9:
+ return "Vendor Specific";
+ case 0xA:
+ return "Copy Aborted";
+ case 0xB:
+ return "Aborted Command";
+ case 0xC:
+ return "Equal";
+ case 0xD:
+ return "Volume Overflow";
+ case 0xE:
+ return "Miscompare";
+ case 0xF:
+ return "Completed";
+ default:
+ return "Unknown";
+ }
+}
+
+static void logScsiSense(const char* diskName, const char* operation, const scsireq_t* req) {
+ uint8_t responseCode = req->sense[0] & 0x7F;
+ uint8_t senseKey;
+ uint8_t asc;
+ uint8_t ascq;
+
+ if (responseCode == 0x72 || responseCode == 0x73) {
+ senseKey = req->sense[1] & 0x0F;
+ asc = req->sense[2];
+ ascq = req->sense[3];
+ } else {
+ senseKey = req->sense[2] & 0x0F;
+ asc = req->sense[12];
+ ascq = req->sense[13];
+ }
+
+ FF_STRBUF_AUTO_DESTROY rawSense = ffStrbufCreate();
+ for (size_t i = 0; i < req->senselen_used; ++i) {
+ if (i) {
+ ffStrbufAppendC(&rawSense, ' ');
+ }
+ ffStrbufAppendF(&rawSense, "%02X", req->sense[i]);
+ }
+
+ FF_DEBUG(
+ "%s for %s reported SENSE: response=0x%02X, key=0x%X (%s), ASC=0x%02X, ASCQ=0x%02X, raw=[%s]",
+ operation,
+ diskName,
+ responseCode,
+ senseKey,
+ senseKeyToStr(senseKey),
+ asc,
+ ascq,
+ rawSense.length ? rawSense.chars : "empty");
+}
+#else
+ #define logScsiSense(...) ((void) 0)
+#endif
+
+const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options) {
+ FF_STRBUF_AUTO_DESTROY diskNames = ffStrbufCreate();
+ FF_DEBUG("Querying disk names via sysctl hw.disknames");
+ const char* error = ffSysctlGetString("hw.disknames", &diskNames);
+ if (error) {
+ FF_DEBUG("ffSysctlGetString(hw.disknames) failed: %s", error);
+ return error;
+ }
+
+ FF_DEBUG("Disk names: %s", diskNames.chars);
+
+ char* diskName = NULL;
+ size_t len = 0;
+ while (ffStrbufGetdelim(&diskName, &len, ' ', &diskNames)) {
+ FF_DEBUG("Probing disk: %s", diskName);
+
+ if (options->namePrefix.length && !ffStrbufStartsWith(&(FFstrbuf) {
+ .chars = diskName,
+ .length = (uint32_t) len,
+ },
+ &options->namePrefix)) {
+ FF_DEBUG("Skipping %s due to namePrefix filter", diskName);
+ continue;
+ }
+
+ char devPath[256];
+ FF_AUTO_CLOSE_FD int f = opendisk(diskName, O_RDONLY, devPath, ARRAY_SIZE(devPath), 0);
+ if (f < 0) {
+ if (errno == EACCES) {
+ FF_DEBUG("opendisk(%s) failed: permission denied", diskName);
+ return "Permission denied; root required";
+ } else {
+ FF_DEBUG("opendisk(%s) failed: %s", diskName, strerror(errno));
+ continue;
+ }
+ }
+
+ FF_DEBUG("Opened %s as %s", diskName, devPath);
+
+ struct disklabel dl;
+ if (ioctl(f, DIOCGDINFO, &dl) < 0) {
+ FF_DEBUG("ioctl(DIOCGDINFO) failed for %s: %s", diskName, strerror(errno));
+ continue;
+ }
+
+ unsigned long sectorsPerUnit, sectorSize;
+
+ const char* devType = NULL;
+
+ prop_dictionary_t dict = NULL;
+ if (prop_dictionary_recv_ioctl(f, DIOCGDISKINFO, &dict) == 0) {
+ FF_DEBUG("DIOCGDISKINFO succeeded for %s", diskName);
+ prop_dictionary_get_string(dict, "type", &devType);
+
+ prop_dictionary_t geometry;
+ if (prop_dictionary_get_dict(dict, "geometry", &geometry)) {
+ prop_dictionary_get_ulong(geometry, "sectors-per-unit", &sectorsPerUnit);
+ prop_dictionary_get_ulong(geometry, "sector-size", &sectorSize);
+ } else {
+ FF_DEBUG("No geometry in diskinfo for %s, falling back to disklabel", diskName);
+ sectorsPerUnit = dl.d_secperunit;
+ sectorSize = dl.d_secsize;
+ }
+ } else {
+ FF_DEBUG("DIOCGDISKINFO failed for %s, falling back to disklabel", diskName);
+ sectorsPerUnit = dl.d_secperunit;
+ sectorSize = dl.d_secsize;
+ }
+
+ FFPhysicalDiskType type = dl.d_flags & D_REMOVABLE ? FF_PHYSICALDISK_TYPE_REMOVABLE : FF_PHYSICALDISK_TYPE_FIXED;
+
+ switch (dl.d_type) {
+ case DKTYPE_VND:
+ case DKTYPE_LD:
+ case DKTYPE_RAID:
+ case DKTYPE_CGD:
+ case DKTYPE_VINUM:
+ case DKTYPE_DM:
+ case DKTYPE_RUMPD:
+ case DKTYPE_MD: {
+ if (options->hideType & FF_PHYSICALDISK_TYPE_VIRTUAL) {
+ FF_DEBUG("Skipping virtual disk %s due to hideType", diskName);
+ continue;
+ }
+
+ type |= FF_PHYSICALDISK_TYPE_VIRTUAL;
+ break;
+ }
+ }
+
+ uint64_t size = sectorsPerUnit * sectorSize;
+ if (size == 0) {
+ if (options->hideType & FF_PHYSICALDISK_TYPE_UNUSED) {
+ FF_DEBUG("Skipping unused disk %s due to hideType", diskName);
+ continue;
+ }
+
+ type |= FF_PHYSICALDISK_TYPE_UNUSED;
+ }
+
+ FFPhysicalDiskResult* device = FF_LIST_ADD(FFPhysicalDiskResult, *result);
+ ffStrbufInitS(&device->name, devType ?: dl.d_packname);
+ ffStrbufInitS(&device->devPath, devPath);
+ ffStrbufInit(&device->serial);
+ ffStrbufInit(&device->revision);
+ ffStrbufInitS(&device->interconnect, dktypenames[dl.d_type]);
+ device->type = type;
+ device->size = size;
+ device->temperature = FF_PHYSICALDISK_TEMP_UNSET;
+ FF_DEBUG("Added disk entry: name='%s', devPath='%s', interconnect='%s', size=%llu",
+ device->name.chars,
+ device->devPath.chars,
+ device->interconnect.chars,
+ (unsigned long long) device->size);
+
+ if (dict) {
+ prop_object_release(dict);
+ dict = NULL;
+ }
+
+ struct scsipi_inquiry_data inquiry = {};
+ scsireq_t req = {
+ .cmd = { [0] = INQUIRY, [4] = sizeof(inquiry) },
+ .cmdlen = 6,
+ .databuf = (caddr_t) &inquiry,
+ .datalen = sizeof(inquiry),
+ .timeout = 1000,
+ .flags = SCCMD_READ,
+ };
+ if (ioctl(f, SCIOCCOMMAND, &req) == 0) {
+ if (req.retsts == SCCMD_OK) {
+ ffStrbufClear(&device->name);
+ ffStrbufAppendNS(&device->name, (uint32_t) ARRAY_SIZE(inquiry.vendor), inquiry.vendor);
+ ffStrbufTrimRight(&device->name, '\0');
+ ffStrbufTrimRight(&device->name, ' ');
+ ffStrbufTrimRight(&device->name, ',');
+ ffStrbufAppendC(&device->name, ' ');
+ ffStrbufAppendNS(&device->name, (uint32_t) ARRAY_SIZE(inquiry.product), inquiry.product);
+ ffStrbufTrimRight(&device->name, '\0');
+ ffStrbufTrimRight(&device->name, ' ');
+
+ ffStrbufSetNS(&device->revision, (uint32_t) ARRAY_SIZE(inquiry.revision), inquiry.revision);
+ ffStrbufTrimRight(&device->name, '\0');
+ ffStrbufTrimRight(&device->revision, ' ');
+ FF_DEBUG("SCSI inquiry for %s: name='%s', revision='%s'", diskName, device->name.chars, device->revision.chars);
+ } else {
+ FF_DEBUG("SCSI inquiry retsts != SCCMD_OK for %s (%d)", diskName, req.retsts);
+ continue;
+ }
+ } else {
+ FF_DEBUG("ioctl(SCIOCCOMMAND) failed for SCSI inquiry on %s: %s", diskName, strerror(errno));
+ continue;
+ }
+
+#ifdef SINQ_EVPD // Available since NetBSD 11.0
+ struct {
+ struct scsipi_inquiry_evpd_header header;
+ struct scsipi_inquiry_evpd_serial body;
+ } evpd = {};
+ req = (scsireq_t) {
+ .cmd = { [0] = INQUIRY, [1] = SINQ_EVPD, [2] = SINQ_VPD_UNIT_SERIAL, [4] = sizeof(evpd) },
+ .cmdlen = 6,
+ .databuf = (caddr_t) &evpd,
+ .datalen = sizeof(evpd),
+ .timeout = 1000,
+ .flags = SCCMD_READ,
+ };
+ if (ioctl(f, SCIOCCOMMAND, &req) == 0) {
+ if (req.retsts == SCCMD_OK && evpd.header.pagecode == SINQ_VPD_UNIT_SERIAL) {
+ for (uint8_t i = 0; i < evpd.header.length[1]; ++i) {
+ ffStrbufAppendF(&device->serial, "%02X", evpd.body.serial_number[i]);
+ }
+ ffStrbufTrimRight(&device->serial, ' ');
+ FF_DEBUG("SCSI serial for %s: %s", diskName, device->serial.chars);
+ } else if (req.retsts == SCCMD_SENSE) {
+ logScsiSense(diskName, "SCSI serial page", &req);
+ } else {
+ FF_DEBUG("SCSI serial page unavailable for %s (retsts=%s, pagecode=%u)", diskName, retstsToStr(req.retsts), (unsigned) evpd.header.pagecode);
+ }
+ } else {
+ FF_DEBUG("ioctl(SCIOCCOMMAND) failed for SCSI serial on %s: %s", diskName, strerror(errno));
+ }
+#endif
+
+ struct scsi_mode_parameter_header_6 mode = {};
+ req = (scsireq_t) {
+ .cmd = { [0] = SCSI_MODE_SENSE_6, [2] = SMS_PAGE_MASK, [4] = sizeof(mode) },
+ .cmdlen = 6,
+ .databuf = (caddr_t) &mode,
+ .datalen = sizeof(mode),
+ .timeout = 1000,
+ .flags = SCCMD_READ,
+ };
+ if (ioctl(f, SCIOCCOMMAND, &req) == 0) {
+ if (req.retsts == SCCMD_OK && mode.data_length > 0) {
+ device->type |= mode.dev_spec & 0x80 ? FF_PHYSICALDISK_TYPE_READONLY : FF_PHYSICALDISK_TYPE_READWRITE;
+ FF_DEBUG("SCSI mode for %s indicates: %s", diskName, mode.dev_spec & 0x80 ? "readonly" : "readwrite");
+ } else if (req.retsts == SCCMD_SENSE) {
+ logScsiSense(diskName, "SCSI mode sense", &req);
+ } else {
+ FF_DEBUG("SCSI mode sense unavailable for %s (retsts=%s, data_length=%u)", diskName, retstsToStr(req.retsts), (unsigned) mode.data_length);
+ }
+ } else {
+ FF_DEBUG("ioctl(SCIOCCOMMAND) failed for SCSI mode sense on %s: %s", diskName, strerror(errno));
+ }
+
+ FF_DEBUG("Detected disk '%s' (%s), type=%u", device->name.chars, diskName, (unsigned) device->type);
+ }
+
+ return NULL;
+}