diff options
Diffstat (limited to 'src/detection/physicaldisk')
| -rw-r--r-- | src/detection/physicaldisk/physicaldisk.h | 17 | ||||
| -rw-r--r-- | src/detection/physicaldisk/physicaldisk_apple.c | 191 | ||||
| -rw-r--r-- | src/detection/physicaldisk/physicaldisk_bsd.c | 127 | ||||
| -rw-r--r-- | src/detection/physicaldisk/physicaldisk_haiku.c | 104 | ||||
| -rw-r--r-- | src/detection/physicaldisk/physicaldisk_linux.c | 202 | ||||
| -rw-r--r-- | src/detection/physicaldisk/physicaldisk_nbsd.c | 324 | ||||
| -rw-r--r-- | src/detection/physicaldisk/physicaldisk_nosupport.c | 5 | ||||
| -rw-r--r-- | src/detection/physicaldisk/physicaldisk_obsd.c | 279 | ||||
| -rw-r--r-- | src/detection/physicaldisk/physicaldisk_sunos.c | 107 | ||||
| -rw-r--r-- | src/detection/physicaldisk/physicaldisk_windows.c | 349 |
10 files changed, 1705 insertions, 0 deletions
diff --git a/src/detection/physicaldisk/physicaldisk.h b/src/detection/physicaldisk/physicaldisk.h new file mode 100644 index 0000000..3481063 --- /dev/null +++ b/src/detection/physicaldisk/physicaldisk.h @@ -0,0 +1,17 @@ +#include "fastfetch.h" +#include "modules/physicaldisk/option.h" + +#define FF_PHYSICALDISK_TEMP_UNSET (-DBL_MAX) + +typedef struct FFPhysicalDiskResult { + FFstrbuf name; + FFstrbuf interconnect; + FFstrbuf serial; + FFstrbuf devPath; + FFstrbuf revision; + FFPhysicalDiskType type; + uint64_t size; + double temperature; +} FFPhysicalDiskResult; + +const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options); diff --git a/src/detection/physicaldisk/physicaldisk_apple.c b/src/detection/physicaldisk/physicaldisk_apple.c new file mode 100644 index 0000000..f4c5cd6 --- /dev/null +++ b/src/detection/physicaldisk/physicaldisk_apple.c @@ -0,0 +1,191 @@ +#include "physicaldisk.h" +#include "common/apple/cf_helpers.h" + +#include <IOKit/IOKitLib.h> +#include <IOKit/IOBSD.h> +#include <IOKit/storage/IOMedia.h> +#include <IOKit/storage/IOBlockStorageDriver.h> +#include <IOKit/storage/IOStorageDeviceCharacteristics.h> +#include <IOKit/storage/IOStorageProtocolCharacteristics.h> +#ifdef MAC_OS_X_VERSION_10_15 + #include <IOKit/storage/nvme/NVMeSMARTLibExternal.h> +#endif + +#ifdef MAC_OS_X_VERSION_10_15 +static inline void wrapIoDestroyPlugInInterface(IOCFPlugInInterface*** pluginInf) { + assert(pluginInf); + if (*pluginInf) { + IODestroyPlugInInterface(*pluginInf); + } +} +#endif + +static const char* detectSsdTemp(io_service_t entryPhysical, double* temp) { +#ifdef MAC_OS_X_VERSION_10_15 + FF_A_CLEANUP(wrapIoDestroyPlugInInterface) IOCFPlugInInterface** pluginInf = NULL; + int32_t score; + if (IOCreatePlugInInterfaceForService(entryPhysical, kIONVMeSMARTUserClientTypeID, kIOCFPlugInInterfaceID, &pluginInf, &score) != kIOReturnSuccess) { + return "IOCreatePlugInInterfaceForService() failed"; + } + + IONVMeSMARTInterface** smartInf = NULL; + if ((*pluginInf)->QueryInterface(pluginInf, CFUUIDGetUUIDBytes(kIONVMeSMARTInterfaceID), (LPVOID) &smartInf) != kIOReturnSuccess) { + return "QueryInterface() failed"; + } + + NVMeSMARTData smartData; + const char* error = NULL; + if ((*smartInf)->SMARTReadData(smartInf, &smartData) == kIOReturnSuccess) { + *temp = smartData.TEMPERATURE - 273; + } else { + error = "SMARTReadData() failed"; + } + + (*pluginInf)->Release(smartInf); + return error; +#else + return "No support for old MacOS version"; +#endif +} + +const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options) { + FF_IOOBJECT_AUTO_RELEASE io_iterator_t iterator = 0; + if (IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching(kIOBlockStorageDriverClass), &iterator) != KERN_SUCCESS) { + return "IOServiceGetMatchingServices() failed"; + } + + io_registry_entry_t registryEntry; + while ((registryEntry = IOIteratorNext(iterator)) != IO_OBJECT_NULL) { + FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryDriver = registryEntry; + + FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryMedia = IO_OBJECT_NULL; + IORegistryEntryGetChildEntry(entryDriver, kIOServicePlane, &entryMedia); + + FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryPhysical = 0; + if (IORegistryEntryGetParentEntry(entryDriver, kIOServicePlane, &entryPhysical) != KERN_SUCCESS) { + continue; + } + + FF_STRBUF_AUTO_DESTROY deviceName = ffStrbufCreate(); + + if (entryMedia != IO_OBJECT_NULL) { + io_name_t name; + if (IORegistryEntryGetName(entryMedia, name) == KERN_SUCCESS) { + ffStrbufSetS(&deviceName, name); + } + } + + if (deviceName.length == 0) { + FF_CFTYPE_AUTO_RELEASE CFDictionaryRef deviceCharacteristics = IORegistryEntryCreateCFProperty(entryPhysical, CFSTR(kIOPropertyDeviceCharacteristicsKey), NULL, kNilOptions); + if (deviceCharacteristics) { + if (ffCfDictGetString(deviceCharacteristics, CFSTR(kIOPropertyVendorNameKey), &deviceName) == NULL && deviceName.length > 0) { + ffStrbufAppendC(&deviceName, ' '); + } + + FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate(); + if (ffCfDictGetString(deviceCharacteristics, CFSTR(kIOPropertyProductNameKey), &name) == NULL) { + ffStrbufAppend(&deviceName, &name); + } + } + } + + if (options->namePrefix.length && ffStrbufStartsWith(&deviceName, &options->namePrefix) != 0) { + continue; + } + + FFPhysicalDiskType type = FF_PHYSICALDISK_TYPE_NONE; + + FF_STRBUF_AUTO_DESTROY interconnect = ffStrbufCreate(); + FF_CFTYPE_AUTO_RELEASE CFDictionaryRef protocolCharacteristics = IORegistryEntryCreateCFProperty(entryPhysical, CFSTR(kIOPropertyProtocolCharacteristicsKey), NULL, kNilOptions); + if (protocolCharacteristics) { + if (ffCfDictGetString(protocolCharacteristics, CFSTR(kIOPropertyPhysicalInterconnectTypeKey), &interconnect) == NULL) { + if (ffStrbufEqualS(&interconnect, kIOPropertyPhysicalInterconnectTypeVirtual)) { + if (options->hideType & FF_PHYSICALDISK_TYPE_VIRTUAL) { + continue; + } + + type |= FF_PHYSICALDISK_TYPE_VIRTUAL; + FF_STRBUF_AUTO_DESTROY location = ffStrbufCreate(); + if (ffCfDictGetString(protocolCharacteristics, CFSTR(kIOPropertyPhysicalInterconnectLocationKey), &location) == NULL) { + ffStrbufAppendS(&interconnect, " - "); + ffStrbufAppend(&interconnect, &location); + } + } + } + } + + uint64_t size = 0; + if (entryMedia != IO_OBJECT_NULL) { + FF_CFTYPE_AUTO_RELEASE CFNumberRef mediaSize = IORegistryEntryCreateCFProperty(entryMedia, CFSTR(kIOMediaSizeKey), NULL, kNilOptions); + if (mediaSize) { + ffCfNumGetInt64(mediaSize, (int64_t*) &size); + } + } + + if (size == 0) { + if (options->hideType & FF_PHYSICALDISK_TYPE_UNUSED) { + continue; + } + + type |= FF_PHYSICALDISK_TYPE_UNUSED; + } + + FFPhysicalDiskResult* device = FF_LIST_ADD(FFPhysicalDiskResult, *result); + ffStrbufInit(&device->serial); + ffStrbufInit(&device->revision); + ffStrbufInitMove(&device->name, &deviceName); + ffStrbufInit(&device->devPath); + ffStrbufInitMove(&device->interconnect, &interconnect); + device->type = type; + device->size = size; + device->temperature = FF_PHYSICALDISK_TEMP_UNSET; + + if (entryMedia != IO_OBJECT_NULL) { + FF_CFTYPE_AUTO_RELEASE CFBooleanRef removable = IORegistryEntryCreateCFProperty(entryMedia, CFSTR(kIOMediaRemovableKey), NULL, kNilOptions); + if (removable) { + device->type |= CFBooleanGetValue(removable) ? FF_PHYSICALDISK_TYPE_REMOVABLE : FF_PHYSICALDISK_TYPE_FIXED; + } + + FF_CFTYPE_AUTO_RELEASE CFBooleanRef writable = IORegistryEntryCreateCFProperty(entryMedia, CFSTR(kIOMediaWritableKey), NULL, kNilOptions); + if (writable) { + device->type |= CFBooleanGetValue(writable) ? FF_PHYSICALDISK_TYPE_READWRITE : FF_PHYSICALDISK_TYPE_READONLY; + } + + FF_CFTYPE_AUTO_RELEASE CFStringRef bsdName = IORegistryEntryCreateCFProperty(entryMedia, CFSTR(kIOBSDNameKey), NULL, kNilOptions); + if (bsdName) { + ffCfStrGetString(bsdName, &device->devPath); + ffStrbufPrependS(&device->devPath, "/dev/"); + } + } + + FF_CFTYPE_AUTO_RELEASE CFDictionaryRef deviceCharacteristics = IORegistryEntryCreateCFProperty(entryPhysical, CFSTR(kIOPropertyDeviceCharacteristicsKey), NULL, kNilOptions); + if (deviceCharacteristics) { + ffCfDictGetString(deviceCharacteristics, CFSTR(kIOPropertyProductSerialNumberKey), &device->serial); + ffStrbufTrimSpace(&device->serial); + ffCfDictGetString(deviceCharacteristics, CFSTR(kIOPropertyProductRevisionLevelKey), &device->revision); + ffStrbufTrimRightSpace(&device->revision); + + if (!(device->type & FF_PHYSICALDISK_TYPE_VIRTUAL)) { + CFStringRef mediumType = (CFStringRef) CFDictionaryGetValue(deviceCharacteristics, CFSTR(kIOPropertyMediumTypeKey)); + if (mediumType) { + if (CFStringCompare(mediumType, CFSTR(kIOPropertyMediumTypeSolidStateKey), 0) == 0) { + device->type |= FF_PHYSICALDISK_TYPE_SSD; + } else if (CFStringCompare(mediumType, CFSTR(kIOPropertyMediumTypeRotationalKey), 0) == 0) { + device->type |= FF_PHYSICALDISK_TYPE_HDD; + } + } + } + } + +#ifdef MAC_OS_X_VERSION_10_15 + if (!(device->type & FF_PHYSICALDISK_TYPE_VIRTUAL) && options->temp) { + FF_CFTYPE_AUTO_RELEASE CFBooleanRef nvmeSMARTCapable = IORegistryEntryCreateCFProperty(entryPhysical, CFSTR(kIOPropertyNVMeSMARTCapableKey), NULL, kNilOptions); + if (nvmeSMARTCapable && CFBooleanGetValue(nvmeSMARTCapable)) { + detectSsdTemp(entryPhysical, &device->temperature); + } + } +#endif + } + + return NULL; +} diff --git a/src/detection/physicaldisk/physicaldisk_bsd.c b/src/detection/physicaldisk/physicaldisk_bsd.c new file mode 100644 index 0000000..5a87768 --- /dev/null +++ b/src/detection/physicaldisk/physicaldisk_bsd.c @@ -0,0 +1,127 @@ +#include "physicaldisk.h" + +#if __has_include(<libgeom.h>) + + #include "common/strutil.h" + + #include <devstat.h> + #include <memory.h> + #include <fcntl.h> + #include <sys/ioctl.h> + #include <sys/disk.h> + #include <libgeom.h> + +const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options) { + struct gmesh geomTree; + if (geom_gettree(&geomTree) < 0) { + return "geom_gettree() failed"; + } + + if (geom_stats_open() < 0) { + return "geom_stats_open() failed"; + } + + void* snap = geom_stats_snapshot_get(); + struct devstat* snapIter; + while ((snapIter = geom_stats_snapshot_next(snap)) != NULL) { + if (snapIter->device_type & DEVSTAT_TYPE_PASS) { + continue; + } + struct gident* geomId = geom_lookupid(&geomTree, snapIter->id); + if (geomId == NULL) { + continue; + } + if (geomId->lg_what != ISPROVIDER) { + continue; + } + struct gprovider* provider = (struct gprovider*) geomId->lg_ptr; + if (provider->lg_geom->lg_rank != 1) { + continue; + } + + FFPhysicalDiskType type = FF_PHYSICALDISK_TYPE_NONE; + if (!ffStrEquals(provider->lg_geom->lg_class->lg_name, "DISK")) { + if (options->hideType & FF_PHYSICALDISK_TYPE_VIRTUAL) { + continue; + } + + type |= FF_PHYSICALDISK_TYPE_VIRTUAL; + } + uint64_t size = (uint64_t) provider->lg_mediasize; + if (size == 0) { + if (options->hideType & FF_PHYSICALDISK_TYPE_UNUSED) { + continue; + } + + type |= FF_PHYSICALDISK_TYPE_UNUSED; + } + + FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateS(provider->lg_name); + FF_STRBUF_AUTO_DESTROY identifier = ffStrbufCreate(); + for (struct gconfig* ptr = provider->lg_config.lh_first; ptr; ptr = ptr->lg_config.le_next) { + if (ffStrEquals(ptr->lg_name, "descr")) { + ffStrbufSetS(&name, ptr->lg_val); + } else if (ffStrEquals(ptr->lg_name, "rotationrate") && !ffStrEquals(ptr->lg_val, "unknown")) { + type |= ffStrEquals(ptr->lg_val, "0") ? FF_PHYSICALDISK_TYPE_SSD : FF_PHYSICALDISK_TYPE_HDD; + } else if (ffStrEquals(ptr->lg_name, "ident")) { + ffStrbufSetS(&identifier, ptr->lg_val); + } else if (ffStrEquals(ptr->lg_name, "access")) { + if (ffStrEquals(ptr->lg_val, "read-only")) { + type |= FF_PHYSICALDISK_TYPE_READONLY; + } else if (ffStrEquals(ptr->lg_val, "read-write")) { + type |= FF_PHYSICALDISK_TYPE_READWRITE; + } + } + } + + if (options->namePrefix.length && !ffStrbufStartsWith(&name, &options->namePrefix)) { + continue; + } + + FFPhysicalDiskResult* device = FF_LIST_ADD(FFPhysicalDiskResult, *result); + ffStrbufInitF(&device->devPath, "/dev/%s", provider->lg_name); + ffStrbufInitMove(&device->serial, &identifier); + ffStrbufTrimSpace(&device->serial); + ffStrbufInit(&device->revision); + ffStrbufInit(&device->interconnect); + ffStrbufInitMove(&device->name, &name); + device->size = size; + device->temperature = FF_PHYSICALDISK_TEMP_UNSET; + + switch (snapIter->device_type & DEVSTAT_TYPE_IF_MASK) { + case DEVSTAT_TYPE_IF_SCSI: + ffStrbufSetStatic(&device->interconnect, "SCSI"); + break; + case DEVSTAT_TYPE_IF_IDE: + ffStrbufSetStatic(&device->interconnect, "IDE"); + break; + case DEVSTAT_TYPE_IF_OTHER: + ffStrbufSetStatic(&device->interconnect, "OTHER"); + break; + + // https://github.com/freebsd/freebsd-src/commit/d282baddb0b029ca8466d23ac51e95c918442535 + case 0x040 /*DEVSTAT_TYPE_IF_NVME*/: + ffStrbufSetStatic(&device->interconnect, "NVMe"); + break; + } + + if (!(device->type & FF_PHYSICALDISK_TYPE_READONLY) && !(device->type & FF_PHYSICALDISK_TYPE_READWRITE)) { + int acr = 1, acw = 1; // Number of partitions mounted for reading or writing + if (sscanf(provider->lg_mode, "r%dw%de%*d", &acr, &acw) == 2 && acr) { + type |= acw ? FF_PHYSICALDISK_TYPE_READWRITE : FF_PHYSICALDISK_TYPE_READONLY; + } + } + + device->type = type; + } + + geom_stats_snapshot_free(snap); + geom_stats_close(); + + return NULL; +} +#else +const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options) { + return "Fastfetch was compiled without libgeom support"; +} +#endif diff --git a/src/detection/physicaldisk/physicaldisk_haiku.c b/src/detection/physicaldisk/physicaldisk_haiku.c new file mode 100644 index 0000000..e44ddce --- /dev/null +++ b/src/detection/physicaldisk/physicaldisk_haiku.c @@ -0,0 +1,104 @@ +#include "physicaldisk.h" + +#include "common/io.h" +#include "common/strutil.h" + +#include <OS.h> +#include <StorageDefs.h> +#include <Drivers.h> +#include <sys/ioctl.h> + +static const char* searchRawDeviceFile(FFstrbuf* path, const char* diskType, FFlist* result, FFPhysicalDiskOptions* options) { + FF_AUTO_CLOSE_DIR DIR* dir = opendir(path->chars); + if (!dir) { + return "detectDiskType: opendir() failed"; + } + uint32_t baseLen = path->length; + + struct dirent* entry; + while ((entry = readdir(dir))) { + if (entry->d_name[0] == '.') { + continue; + } + ffStrbufAppendC(path, '/'); + ffStrbufAppendS(path, entry->d_name); + + struct stat st; + if (stat(path->chars, &st) != 0) { + ffStrbufSubstrBefore(path, baseLen); + continue; + } + + if (S_ISDIR(st.st_mode)) { + searchRawDeviceFile(path, diskType, result, options); + } else if (ffStrEquals(entry->d_name, "raw")) { + FF_AUTO_CLOSE_FD int rawfd = open(path->chars, O_RDONLY | O_CLOEXEC); + if (rawfd < 0) { + continue; + } + + device_geometry geometry; + if (ioctl(rawfd, B_GET_GEOMETRY, &geometry, sizeof(geometry)) < 0) { + continue; + } + + char name[B_OS_NAME_LENGTH]; + if (ioctl(rawfd, B_GET_DEVICE_NAME, name, sizeof(name)) != 0) { + // ioctl reports `not a tty` for NVME drives for some reason + snprintf(name, sizeof(name), "Unknown %s drive", diskType); + } + + if (options->namePrefix.length && strncmp(name, options->namePrefix.chars, options->namePrefix.length) != 0) { + continue; + } + + FFPhysicalDiskType type = FF_PHYSICALDISK_TYPE_NONE; + uint64_t size = (uint64_t) geometry.cylinder_count * geometry.head_count * geometry.sectors_per_track * geometry.bytes_per_sector; + if (size == 0) { + if (options->hideType & FF_PHYSICALDISK_TYPE_UNUSED) { + continue; + } + + type |= FF_PHYSICALDISK_TYPE_UNUSED; + } + + FFPhysicalDiskResult* device = FF_LIST_ADD(FFPhysicalDiskResult, *result); + ffStrbufInitS(&device->name, name); + ffStrbufInitCopy(&device->devPath, path); + ffStrbufInit(&device->serial); + ffStrbufInit(&device->revision); + ffStrbufInitS(&device->interconnect, diskType); + device->temperature = FF_PHYSICALDISK_TEMP_UNSET; + device->type = type | + (geometry.read_only ? FF_PHYSICALDISK_TYPE_READONLY : FF_PHYSICALDISK_TYPE_READWRITE) | + (geometry.removable ? FF_PHYSICALDISK_TYPE_REMOVABLE : FF_PHYSICALDISK_TYPE_FIXED); + device->size = size; + } + + ffStrbufSubstrBefore(path, baseLen); + } + return NULL; +} + +const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options) { + FF_AUTO_CLOSE_DIR DIR* dir = opendir("/dev/disk"); + if (!dir) { + return "opendir(/dev/disk) failed"; + } + + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateA(64); + ffStrbufAppendS(&path, "/dev/disk/"); + uint32_t baseLen = path.length; + + struct dirent* entry; + while ((entry = readdir(dir))) { + if (entry->d_name[0] == '.' || ffStrEquals(entry->d_name, "virtual")) { + continue; + } + ffStrbufAppendS(&path, entry->d_name); + searchRawDeviceFile(&path, entry->d_name, result, options); + ffStrbufSubstrBefore(&path, baseLen); + } + + return NULL; +} diff --git a/src/detection/physicaldisk/physicaldisk_linux.c b/src/detection/physicaldisk/physicaldisk_linux.c new file mode 100644 index 0000000..f8126b9 --- /dev/null +++ b/src/detection/physicaldisk/physicaldisk_linux.c @@ -0,0 +1,202 @@ +#include "physicaldisk.h" +#include "common/io.h" +#include "common/properties.h" +#include "common/strutil.h" + +#include <ctype.h> +#include <limits.h> +#include <unistd.h> +#include <fcntl.h> + +static double detectNvmeTemp(int devfd) { + char pathHwmon[] = "hwmon$/temp1_input"; + + for (char c = '0'; c <= '9'; c++) // hopefully there's only one digit + { + pathHwmon[strlen("hwmon")] = c; + char buffer[64]; + ssize_t size = ffReadFileDataRelative(devfd, pathHwmon, ARRAY_SIZE(buffer), buffer); + if (size > 0) { + buffer[size] = '\0'; + double temp = strtod(buffer, NULL); + return temp > 0 && temp < 10000000 /*VMware*/ ? temp / 1000 : FF_PHYSICALDISK_TEMP_UNSET; + } + } + + return FF_PHYSICALDISK_TEMP_UNSET; +} + +static void parsePhysicalDisk(int dfd, const char* devName, FFPhysicalDiskOptions* options, FFlist* result) { + uint64_t size = 0; + + { + char blkSize[32]; + ssize_t fileSize = ffReadFileDataRelative(dfd, "size", ARRAY_SIZE(blkSize) - 1, blkSize); + if (fileSize > 0) { + blkSize[fileSize] = 0; + size = (uint64_t) strtoul(blkSize, NULL, 10) * 512; + } + } + + FFPhysicalDiskType type = FF_PHYSICALDISK_TYPE_NONE; + if (size == 0) { + if (options->hideType & FF_PHYSICALDISK_TYPE_UNUSED) { + return; + } + + type |= FF_PHYSICALDISK_TYPE_UNUSED; + } + + int devfd = openat(dfd, "device", O_RDONLY | O_CLOEXEC | O_PATH | O_DIRECTORY); + + if (devfd < 0) { + if (options->hideType & FF_PHYSICALDISK_TYPE_VIRTUAL) { + return; + } + + type |= FF_PHYSICALDISK_TYPE_VIRTUAL; + } + + FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate(); + + if (devfd > 0) { + if (ffAppendFileBufferRelative(devfd, "vendor", &name)) { + ffStrbufTrimRightSpace(&name); + if (name.length > 0) { + ffStrbufAppendC(&name, ' '); + } + } + + ffAppendFileBufferRelative(devfd, "model", &name); + ffStrbufTrimRightSpace(&name); + + if (name.length == 0) { + ffStrbufSetS(&name, devName); + } + + if (ffStrStartsWith(devName, "nvme")) { + int devid, nsid; + if (sscanf(devName, "nvme%dn%d", &devid, &nsid) == 2) { + bool multiNs = nsid > 1; + if (!multiNs) { + char pathSysBlock[32]; + snprintf(pathSysBlock, ARRAY_SIZE(pathSysBlock), "/dev/nvme%dn2", devid); + multiNs = access(pathSysBlock, F_OK) == 0; + } + if (multiNs) { + // In Asahi Linux, there are multiple namespaces for the same NVMe drive. + ffStrbufAppendF(&name, " - %d", nsid); + } + } + } + + if (options->namePrefix.length && !ffStrbufStartsWith(&name, &options->namePrefix)) { + return; + } + } else { + ffStrbufSetS(&name, devName); + } + + FFPhysicalDiskResult* device = FF_LIST_ADD(FFPhysicalDiskResult, *result); + ffStrbufInitMove(&device->name, &name); + ffStrbufInitF(&device->devPath, "/dev/%s", devName); + ffStrbufInit(&device->serial); + ffStrbufInit(&device->revision); + ffStrbufInit(&device->interconnect); + device->type = type; + device->size = size; + device->temperature = FF_PHYSICALDISK_TEMP_UNSET; + + bool isVirtio = false; + if (devfd > 0) { + if (ffStrStartsWith(devName, "nvme")) { + ffStrbufSetStatic(&device->interconnect, "NVMe"); + } else if (ffStrStartsWith(devName, "mmcblk")) { + ffStrbufSetStatic(&device->interconnect, "MMC"); + } else { + char pathSysDeviceLink[64]; + snprintf(pathSysDeviceLink, ARRAY_SIZE(pathSysDeviceLink), "/sys/block/%s/device", devName); + char pathSysDeviceReal[PATH_MAX]; + if (realpath(pathSysDeviceLink, pathSysDeviceReal)) { + if (strstr(pathSysDeviceReal, "/usb") != NULL) { + ffStrbufSetStatic(&device->interconnect, "USB"); + } else if (strstr(pathSysDeviceReal, "/ata") != NULL) { + ffStrbufSetStatic(&device->interconnect, "ATA"); + } else if (strstr(pathSysDeviceReal, "/scsi") != NULL) { + ffStrbufSetStatic(&device->interconnect, "SCSI"); + } else if (strstr(pathSysDeviceReal, "/nvme") != NULL) { + ffStrbufSetStatic(&device->interconnect, "NVMe"); + } else if (strstr(pathSysDeviceReal, "/virtio") != NULL) { + ffStrbufSetStatic(&device->interconnect, "VirtIO"); + isVirtio = true; // VirtIO devices are virtual, but we still want to report it + } else { + if (ffAppendFileBufferRelative(devfd, "transport", &device->interconnect)) { + ffStrbufTrimRightSpace(&device->interconnect); + } + } + } + } + } else { + ffStrbufSetStatic(&device->interconnect, "Virtual"); + } + + if (devfd > 0 && !isVirtio) { + char isRotationalChar = '1'; + if (ffReadFileDataRelative(dfd, "queue/rotational", 1, &isRotationalChar) > 0) { + device->type |= isRotationalChar == '1' ? FF_PHYSICALDISK_TYPE_HDD : FF_PHYSICALDISK_TYPE_SSD; + } + + if (ffReadFileBufferRelative(devfd, "serial", &device->serial)) { + ffStrbufTrimSpace(&device->serial); + } + + if (ffReadFileBufferRelative(devfd, "firmware_rev", &device->revision) || + ffReadFileBufferRelative(devfd, "rev", &device->revision)) { + ffStrbufTrimRightSpace(&device->revision); + } + + if (options->temp) { + device->temperature = detectNvmeTemp(devfd); + } + } + + { + char removableChar = '0'; + if (ffReadFileDataRelative(dfd, "removable", 1, &removableChar) > 0) { + device->type |= removableChar == '1' ? FF_PHYSICALDISK_TYPE_REMOVABLE : FF_PHYSICALDISK_TYPE_FIXED; + } + } + + { + char roChar = '0'; + if (ffReadFileDataRelative(dfd, "ro", 1, &roChar) > 0) { + device->type |= roChar == '1' ? FF_PHYSICALDISK_TYPE_READONLY : FF_PHYSICALDISK_TYPE_READWRITE; + } + } +} + +const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options) { + FF_AUTO_CLOSE_DIR DIR* sysBlockDirp = opendir("/sys/block/"); + if (sysBlockDirp == NULL) { + return "opendir(\"/sys/block/\") == NULL"; + } + + struct dirent* sysBlockEntry; + while ((sysBlockEntry = readdir(sysBlockDirp)) != NULL) { + const char* const devName = sysBlockEntry->d_name; + + if (devName[0] == '.') { + continue; + } + + char pathSysBlock[sizeof("/sys/block/") + sizeof(sysBlockEntry->d_name)]; + snprintf(pathSysBlock, ARRAY_SIZE(pathSysBlock), "/sys/block/%s", devName); + + int dfd = openat(dirfd(sysBlockDirp), devName, O_RDONLY | O_CLOEXEC | O_PATH | O_DIRECTORY); + if (dfd > 0) { + parsePhysicalDisk(dfd, devName, options, result); + } + } + + return NULL; +} 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", §orsPerUnit); + prop_dictionary_get_ulong(geometry, "sector-size", §orSize); + } 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; +} diff --git a/src/detection/physicaldisk/physicaldisk_nosupport.c b/src/detection/physicaldisk/physicaldisk_nosupport.c new file mode 100644 index 0000000..26e0ce8 --- /dev/null +++ b/src/detection/physicaldisk/physicaldisk_nosupport.c @@ -0,0 +1,5 @@ +#include "physicaldisk.h" + +const char* ffDetectPhysicalDisk(FF_A_UNUSED FFlist* result, FF_A_UNUSED FFPhysicalDiskOptions* options) { + return "Not supported on this platform"; +} diff --git a/src/detection/physicaldisk/physicaldisk_obsd.c b/src/detection/physicaldisk/physicaldisk_obsd.c new file mode 100644 index 0000000..6e89a99 --- /dev/null +++ b/src/detection/physicaldisk/physicaldisk_obsd.c @@ -0,0 +1,279 @@ +#include "physicaldisk.h" +#include "common/io.h" +#include "common/sysctl.h" +#include "common/debug.h" + +#include <util.h> +#include <sys/ioctl.h> +#include <sys/disklabel.h> +#include <sys/dkio.h> +#include <sys/scsiio.h> +#include <scsi/scsi_all.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(CTL_HW, 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)) { + char* colon = strchr(diskName, ':'); + if (colon) { + *colon = '\0'; + } + + 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 = NULL; + FF_AUTO_CLOSE_FD int f = opendev(diskName, O_RDONLY, OPENDEV_PART, &devPath); + if (f < 0) { + if (errno == EACCES) { + FF_DEBUG("opendev(%s) failed: permission denied", diskName); + return "Permission denied; root required"; + } else { + FF_DEBUG("opendev(%s) failed: %s", diskName, strerror(errno)); + continue; // Unknown error + } + } + + FF_DEBUG("Opened %s as %s", diskName, devPath); + + struct disklabel dl; + if (ioctl(f, DIOCGPDINFO, &dl) < 0) { + FF_DEBUG("ioctl(DIOCGPDINFO) failed for %s: %s", diskName, strerror(errno)); + continue; + } + + FFPhysicalDiskType type = FF_PHYSICALDISK_TYPE_NONE; + if (dl.d_type == DTYPE_VND || dl.d_type == DTYPE_RDROOT) { + if (options->hideType & FF_PHYSICALDISK_TYPE_VIRTUAL) { + FF_DEBUG("Skipping virtual disk %s due to hideType", diskName); + continue; + } + + type |= FF_PHYSICALDISK_TYPE_VIRTUAL; + } + + uint64_t size = DL_GETDSIZE(&dl) * dl.d_secsize; + 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, dl.d_packname); + ffStrbufInitS(&device->devPath, devPath); + ffStrbufInit(&device->serial); + ffStrbufInit(&device->revision); + ffStrbufInitS(&device->interconnect, dl.d_typename); + 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); + + struct scsi_inquiry_data inquiry = {}; + struct scsireq 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, ' '); + 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->revision, '\0'); + ffStrbufTrimRight(&device->revision, ' '); + device->type |= inquiry.dev_qual2 & SID_REMOVABLE ? FF_PHYSICALDISK_TYPE_REMOVABLE : FF_PHYSICALDISK_TYPE_FIXED; + 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); + } + } else { + FF_DEBUG("ioctl(SCIOCCOMMAND) failed for SCSI inquiry on %s: %s", diskName, strerror(errno)); + } + + struct scsi_vpd_serial evpd = {}; + req = (struct scsireq) { + .cmd = { [0] = INQUIRY, [1] = SI_EVPD, [2] = SI_PG_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.hdr.page_code == SI_PG_SERIAL) { + for (uint8_t i = 0; i < evpd.hdr.page_length[1]; ++i) { + ffStrbufAppendF(&device->serial, "%02X", evpd.serial[i]); + } + ffStrbufTrimSpace(&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.hdr.page_code); + } + } else { + FF_DEBUG("ioctl(SCIOCCOMMAND) failed for SCSI serial on %s: %s", diskName, strerror(errno)); + } + + struct scsi_mode_header mode = {}; + req = (scsireq_t) { + .cmd = { [0] = MODE_SENSE, [2] = SMS_PAGE_CODE, [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) { + 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; +} diff --git a/src/detection/physicaldisk/physicaldisk_sunos.c b/src/detection/physicaldisk/physicaldisk_sunos.c new file mode 100644 index 0000000..d929d5a --- /dev/null +++ b/src/detection/physicaldisk/physicaldisk_sunos.c @@ -0,0 +1,107 @@ +#include "physicaldisk.h" +#include "common/strutil.h" +#include "sys/scsi/generic/inquiry.h" + +#include <libdevinfo.h> +#include <sys/stat.h> + +struct FFWalkTreeBundle { + FFPhysicalDiskOptions* options; + FFlist* disks; +}; + +static int walkDevTree(di_node_t node, di_minor_t minor, struct FFWalkTreeBundle* bundle) { + FFPhysicalDiskOptions* options = bundle->options; + FFlist* result = bundle->disks; + + if (di_minor_spectype(minor) != S_IFCHR || !ffStrEquals(di_minor_name(minor), "a,raw")) { + return DI_WALK_CONTINUE; + } + + char* productId; + char* vendorId; + 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) { + FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateF("%s %s", vendorId, productId); + if (options->namePrefix.length && !ffStrbufStartsWithIgnCase(&name, &options->namePrefix)) { + return DI_WALK_CONTINUE; + } + + int* value; + + FFPhysicalDiskType type = FF_PHYSICALDISK_TYPE_NONE; + uint64_t size = 0; + int64_t* nblocks; + if (di_prop_lookup_int64(DDI_DEV_T_ANY, node, "device-nblocks", &nblocks) > 0) { + if (*nblocks == 0) { + if (options->hideType & FF_PHYSICALDISK_TYPE_UNUSED) { + return DI_WALK_CONTINUE; + } + + type |= FF_PHYSICALDISK_TYPE_UNUSED; + } else if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "device-blksize", &value) > 0) { + size = (uint64_t) ((uint64_t) *nblocks * (uint64_t) *value); + } + } + + FFPhysicalDiskResult* device = FF_LIST_ADD(FFPhysicalDiskResult, *result); + ffStrbufInitMove(&device->name, &name); + ffStrbufInitF(&device->devPath, "/devices%s", di_devfs_path(node)); + ffStrbufInit(&device->serial); + ffStrbufInit(&device->revision); + ffStrbufInit(&device->interconnect); + device->temperature = FF_PHYSICALDISK_TEMP_UNSET; + device->type = type; + device->size = size; + + char* buf; + bool usb = false; + if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "inquiry-serial-no", &buf) > 0) { + ffStrbufSetS(&device->serial, buf); + ffStrbufTrimSpace(&device->serial); + } else { + di_node_t parent = di_parent_node(node); + if (parent != DI_NODE_NIL && di_prop_lookup_strings(DDI_DEV_T_ANY, parent, "usb-serialno", &buf) > 0) { + ffStrbufSetS(&device->serial, buf); + usb = true; + } + } + if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "inquiry-revision-id", &buf) > 0) { + ffStrbufSetS(&device->revision, buf); + ffStrbufTrimRightSpace(&device->revision); + } + + if (usb) { + ffStrbufSetStatic(&device->interconnect, "USB"); + } else if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "class", &buf) > 0) { + ffStrbufSetS(&device->interconnect, buf); + } else { + di_node_t parent = di_parent_node(node); + if (parent != DI_NODE_NIL && di_prop_lookup_strings(DDI_DEV_T_ANY, parent, "model", &buf) > 0) { + ffStrbufSetS(&device->interconnect, buf); + } + } + + device->type |= di_prop_find(DDI_DEV_T_ANY, node, "removable-media") ? FF_PHYSICALDISK_TYPE_REMOVABLE : FF_PHYSICALDISK_TYPE_FIXED; + + if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "device-solid-state", &value) > 0) { + device->type |= *value ? FF_PHYSICALDISK_TYPE_SSD : FF_PHYSICALDISK_TYPE_HDD; + } + if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type", &value) > 0) { + device->type |= *value == DTYPE_DIRECT ? FF_PHYSICALDISK_TYPE_READWRITE : *value == DTYPE_RODIRECT ? FF_PHYSICALDISK_TYPE_READONLY + : 0; + } + } + + return DI_WALK_CONTINUE; +} + +const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options) { + di_node_t rootNode = di_init("/", DINFOCPYALL); + if (rootNode == DI_NODE_NIL) { + return "di_init() failed"; + } + di_walk_minor(rootNode, DDI_NT_BLOCK, DI_WALK_CLDFIRST, &(struct FFWalkTreeBundle) { options, result }, (void*) walkDevTree); + di_fini(rootNode); + + return NULL; +} diff --git a/src/detection/physicaldisk/physicaldisk_windows.c b/src/detection/physicaldisk/physicaldisk_windows.c new file mode 100644 index 0000000..5d4f7f7 --- /dev/null +++ b/src/detection/physicaldisk/physicaldisk_windows.c @@ -0,0 +1,349 @@ +#include "physicaldisk.h" +#include "common/io.h" +#include "common/windows/unicode.h" +#include "common/mallocHelper.h" +#include "common/debug.h" + +#include <stdalign.h> +#include <windows.h> +#include <winioctl.h> +#include <cfgmgr32.h> + +static const char* detectPhysicalDisk(const char* physicalType, const wchar_t* szDevice, FFlist* result, FFPhysicalDiskOptions* options) { + FF_AUTO_CLOSE_FD HANDLE hDevice = CreateFileW(szDevice, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hDevice == INVALID_HANDLE_VALUE) { + return "CreateFileW() failed"; + } + + DWORD retSize; + FFPhysicalDiskType type = FF_PHYSICALDISK_TYPE_NONE; + FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate(); + + const char* interconnect = NULL; + uint64_t size = 0; + { + alignas(DISK_GEOMETRY_EX) uint8_t dgeBuffer[4096]; + if (DeviceIoControl( + hDevice, + IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, + NULL, + 0, + dgeBuffer, + sizeof(dgeBuffer), + &retSize, + NULL)) { + const DISK_GEOMETRY_EX* dge = (const DISK_GEOMETRY_EX*) dgeBuffer; + size = (uint64_t) dge->DiskSize.QuadPart; + } else if (DeviceIoControl( + hDevice, + IOCTL_DISK_GET_DRIVE_GEOMETRY, + NULL, + 0, + dgeBuffer, + sizeof(dgeBuffer), + &retSize, + NULL) && + retSize >= sizeof(DISK_GEOMETRY)) { + const DISK_GEOMETRY* dg = (const DISK_GEOMETRY*) dgeBuffer; + size = (uint64_t) dg->BytesPerSector * dg->SectorsPerTrack * dg->TracksPerCylinder * (uint64_t) dg->Cylinders.QuadPart; + switch (dg->MediaType) { + case F3_1Pt44_512: + case F3_2Pt88_512: + case F3_20Pt8_512: + case F3_720_512: + case F3_120M_512: + case F3_640_512: + case F3_1Pt2_512: + case F3_1Pt23_1024: + case F3_128Mb_512: + case F3_230Mb_512: + case F3_200Mb_512: + case F3_240M_512: + case F3_32M_512: + ffStrbufSetStatic(&name, "3.5-inch Floppy Disk"); + break; + case F5_1Pt2_512: + case F5_360_512: + case F5_320_512: + case F5_320_1024: + case F5_180_512: + case F5_160_512: + case F5_640_512: + case F5_720_512: + case F5_1Pt23_1024: + ffStrbufSetStatic(&name, "5.25-inch Floppy Disk"); + break; + case F8_256_128: + ffStrbufSetStatic(&name, "8-inch Floppy Disk"); + break; + default: + return "Unsupported media type"; + } + interconnect = "Floppy Controller"; + type |= FF_PHYSICALDISK_TYPE_HDD | FF_PHYSICALDISK_TYPE_REMOVABLE; + } + } + if (size == 0) { + if (options->hideType & FF_PHYSICALDISK_TYPE_UNUSED) { + return "Skipping unknown disk with size 0"; + } + + type |= FF_PHYSICALDISK_TYPE_UNUSED; + } + + const STORAGE_DEVICE_DESCRIPTOR* sdd = NULL; + alignas(STORAGE_DEVICE_DESCRIPTOR) uint8_t sddBuffer[4096]; + if (!interconnect) { + if (DeviceIoControl( + hDevice, + IOCTL_STORAGE_QUERY_PROPERTY, + &(STORAGE_PROPERTY_QUERY) { + .PropertyId = StorageDeviceProperty, + .QueryType = PropertyStandardQuery, + }, + sizeof(STORAGE_PROPERTY_QUERY), + &sddBuffer, + sizeof(sddBuffer), + &retSize, + NULL) || + retSize == 0) { + sdd = (const STORAGE_DEVICE_DESCRIPTOR*) sddBuffer; + + switch (sdd->BusType) { + case BusTypeScsi: + interconnect = "SCSI"; + break; + case BusTypeAtapi: + interconnect = "ATAPI"; + break; + case BusTypeAta: + interconnect = "ATA"; + break; + case BusType1394: + interconnect = "IEEE 1394"; + break; + case BusTypeSsa: + interconnect = "SSA"; + break; + case BusTypeFibre: + interconnect = "Fibre"; + break; + case BusTypeUsb: + interconnect = "USB"; + break; + case BusTypeRAID: + interconnect = "RAID"; + break; + case BusTypeiScsi: + interconnect = "iSCSI"; + break; + case BusTypeSas: + interconnect = "SAS"; + break; + case BusTypeSata: + interconnect = "SATA"; + break; + case BusTypeSd: + interconnect = "SD"; + break; + case BusTypeMmc: + interconnect = "MMC"; + break; + case BusTypeVirtual: + interconnect = "Virtual"; + type |= FF_PHYSICALDISK_TYPE_VIRTUAL; + break; + case BusTypeFileBackedVirtual: + interconnect = "File Backed Virtual"; + type |= FF_PHYSICALDISK_TYPE_VIRTUAL; + break; + case BusTypeSpaces: + interconnect = "Storage Spaces"; + type |= FF_PHYSICALDISK_TYPE_VIRTUAL; + break; + case BusTypeNvme: + interconnect = "NVMe"; + break; + case BusTypeSCM: + interconnect = "SCM"; + break; + case BusTypeUfs: + interconnect = "UFS"; + break; + case 0x14 /*BusTypeNvmeof*/: + interconnect = "NVMe-oF"; + break; + default: + interconnect = "Unknown"; + break; + } + + if (type & FF_PHYSICALDISK_TYPE_VIRTUAL && options->hideType & FF_PHYSICALDISK_TYPE_VIRTUAL) { + return "Skipping virtual disk"; + } + + if (sdd->VendorIdOffset != 0) { + ffStrbufSetS(&name, (const char*) sdd + sdd->VendorIdOffset); + ffStrbufTrim(&name, ' '); + } + if (sdd->ProductIdOffset != 0) { + if (name.length) { + ffStrbufAppendC(&name, ' '); + } + + ffStrbufAppendS(&name, (const char*) sdd + sdd->ProductIdOffset); + ffStrbufTrimRight(&name, ' '); + } + } + if (!name.length) { + ffStrbufSetStatic(&name, physicalType); + } + } + + if (options->namePrefix.length && !ffStrbufStartsWith(&name, &options->namePrefix)) { + return "Name prefix mismatch"; + } + + FFPhysicalDiskResult* device = FF_LIST_ADD(FFPhysicalDiskResult, *result); + ffStrbufInit(&device->serial); + ffStrbufInit(&device->revision); + ffStrbufInitMove(&device->name, &name); + ffStrbufInit(&device->devPath); + ffStrbufInitStatic(&device->interconnect, interconnect); + device->type = type; + device->size = size; + device->temperature = FF_PHYSICALDISK_TEMP_UNSET; + + ffStrbufSetWS(&device->devPath, szDevice); + + if (sdd) { + if (sdd->SerialNumberOffset != 0) { + ffStrbufSetS(&device->serial, (const char*) sdd + sdd->SerialNumberOffset); + ffStrbufTrimSpace(&device->serial); + } + + if (sdd->ProductRevisionOffset != 0) { + ffStrbufSetS(&device->revision, (const char*) sdd + sdd->ProductRevisionOffset); + ffStrbufTrimRightSpace(&device->revision); + } + + device->type |= sdd->RemovableMedia ? FF_PHYSICALDISK_TYPE_REMOVABLE : FF_PHYSICALDISK_TYPE_FIXED; + } + + { + alignas(GET_MEDIA_TYPES) uint8_t buffer[4096]; + GET_MEDIA_TYPES* gmt = (GET_MEDIA_TYPES*) buffer; + if (DeviceIoControl( + hDevice, + IOCTL_STORAGE_GET_MEDIA_TYPES_EX, + NULL, + 0, + gmt, + sizeof(buffer), + &retSize, + NULL) && + gmt->MediaInfoCount > 0) { + // DiskInfo and RemovableDiskInfo have the same structures. TapeInfo doesn't. + if (gmt->DeviceType != FILE_DEVICE_TAPE) { + __auto_type diskInfo = &gmt->MediaInfo[0].DeviceSpecific.DiskInfo; + if (diskInfo->MediaCharacteristics & MEDIA_READ_ONLY) { + device->type |= FF_PHYSICALDISK_TYPE_READONLY; + } else if (diskInfo->MediaCharacteristics & MEDIA_READ_WRITE) { + device->type |= FF_PHYSICALDISK_TYPE_READWRITE; + } + } else { + __auto_type tapeInfo = &gmt->MediaInfo[0].DeviceSpecific.TapeInfo; + if (tapeInfo->MediaCharacteristics & MEDIA_READ_ONLY) { + device->type |= FF_PHYSICALDISK_TYPE_READONLY; + } else if (tapeInfo->MediaCharacteristics & MEDIA_READ_WRITE) { + device->type |= FF_PHYSICALDISK_TYPE_READWRITE; + } + } + } + } + + if (!(device->type & FF_PHYSICALDISK_TYPE_VIRTUAL) && !(device->type & FF_PHYSICALDISK_TYPE_HDD)) { + DEVICE_SEEK_PENALTY_DESCRIPTOR dspd = {}; + if (DeviceIoControl( + hDevice, + IOCTL_STORAGE_QUERY_PROPERTY, + &(STORAGE_PROPERTY_QUERY) { + .PropertyId = StorageDeviceSeekPenaltyProperty, + .QueryType = PropertyStandardQuery, + }, + sizeof(STORAGE_PROPERTY_QUERY), + &dspd, + sizeof(dspd), + &retSize, + NULL) && + retSize == sizeof(dspd)) { + device->type |= dspd.IncursSeekPenalty ? FF_PHYSICALDISK_TYPE_HDD : FF_PHYSICALDISK_TYPE_SSD; + } + + if (options->temp) { + STORAGE_TEMPERATURE_DATA_DESCRIPTOR stdd = {}; + if (DeviceIoControl( + hDevice, + IOCTL_STORAGE_QUERY_PROPERTY, + &(STORAGE_PROPERTY_QUERY) { + .PropertyId = StorageDeviceTemperatureProperty, + .QueryType = PropertyStandardQuery, + }, + sizeof(STORAGE_PROPERTY_QUERY), + &stdd, + sizeof(stdd), + &retSize, + NULL) && + retSize == sizeof(stdd)) { + device->temperature = stdd.TemperatureInfo[0].Temperature; + } + } + } + + return NULL; +} + +static void detectPhysicalDisksByInterfaceClass(const char* type, const GUID* interfaceClassGuid, FFlist* result, FFPhysicalDiskOptions* options) { + ULONG cchDeviceInterfaces = 0; + if (CM_Get_Device_Interface_List_SizeW( + &cchDeviceInterfaces, + (LPGUID) interfaceClassGuid, + NULL, + CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS || + cchDeviceInterfaces <= 1) { + return; + } + + wchar_t* FF_AUTO_FREE mszDeviceInterfaces = (wchar_t*) malloc(cchDeviceInterfaces * sizeof(wchar_t)); + if (!mszDeviceInterfaces) { + return; + } + + if (CM_Get_Device_Interface_ListW( + (LPGUID) interfaceClassGuid, + NULL, + mszDeviceInterfaces, + cchDeviceInterfaces, + CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS) { + return; + } + + // MULTI_SZ: "str1\0str2\0...\0\0" + for (const wchar_t* p = mszDeviceInterfaces; *p; p += wcslen(p) + 1) { + FF_DEBUG("Probing %s: %ls", type, p); + FF_A_UNUSED const char* error = detectPhysicalDisk(type, p, result, options); + if (error == NULL) { + FF_DEBUG("Detected device \"%s\"", FF_LIST_LAST(FFPhysicalDiskResult, *result)->name.chars); + } else { + FF_DEBUG("Failed to detect device %s: %s", type, error); + } + } +} + +const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options) { + detectPhysicalDisksByInterfaceClass("Floppy", &GUID_DEVINTERFACE_FLOPPY, result, options); + detectPhysicalDisksByInterfaceClass("Disk", &GUID_DEVINTERFACE_DISK, result, options); + detectPhysicalDisksByInterfaceClass("CD-ROM", &GUID_DEVINTERFACE_CDROM, result, options); + detectPhysicalDisksByInterfaceClass("Tape", &GUID_DEVINTERFACE_TAPE, result, options); + return NULL; +} |