From d14389d0f7074e79eb0869baceeef03f4aa7e14f Mon Sep 17 00:00:00 2001 From: Matt Whitlock Date: Tue, 1 Dec 2020 19:09:43 -0500 Subject: [PATCH] linux/DtaDevOS.cpp: improve robustness of drive type auto-detection DtaDevOS::init() previously detected the drive type by checking whether the user-specified device node path starts with "/dev/nvme" or "/dev/s". This is fragile and breaks mysteriously in cases such as when the user specifies a relative path to a device node or when the user specifies a device through a udev-managed symbolic link in /dev/disk/by-id/ et al. This commit reimplements drive type auto-detection using a more robust algorithm that stats the specified device node and treats it as a SATA drive if the device node's major number is one of the SCSI_DISK*_MAJOR numbers or as an NVMe drive if the device is bound to the "nvme" kernel driver. (The latter check requires sysfs to be mounted at /sys.) --- linux/DtaDevOS.cpp | 72 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/linux/DtaDevOS.cpp b/linux/DtaDevOS.cpp index 5261e736..292cf8d3 100644 --- a/linux/DtaDevOS.cpp +++ b/linux/DtaDevOS.cpp @@ -24,12 +24,14 @@ along with sedutil. If not, see . #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -60,23 +62,69 @@ void DtaDevOS::init(const char * devref) memset(&disk_info, 0, sizeof(OPAL_DiskInfo)); dev = devref; - if (!strncmp(devref, "/dev/nvme", 9)) + struct stat st; + if (stat(devref, &st) < 0) { -// DtaDevLinuxNvme *NvmeDrive = new DtaDevLinuxNvme(); - drive = new DtaDevLinuxNvme(); + LOG(E) << "DtaDevOS::init ERROR - failed to stat " << devref; + isOpen = FALSE; + return; } - else if (!strncmp(devref, "/dev/s", 6)) + switch (st.st_mode & S_IFMT) { -// DtaDevLinuxSata *SataDrive = new DtaDevLinuxSata(); - drive = new DtaDevLinuxSata(); + case S_IFBLK: // block device + { + switch (major(st.st_rdev)) + { + case SCSI_DISK0_MAJOR: + case SCSI_DISK1_MAJOR: + case SCSI_DISK2_MAJOR: + case SCSI_DISK3_MAJOR: + case SCSI_DISK4_MAJOR: + case SCSI_DISK5_MAJOR: + case SCSI_DISK6_MAJOR: + case SCSI_DISK7_MAJOR: + case SCSI_DISK8_MAJOR: + case SCSI_DISK9_MAJOR: + case SCSI_DISK10_MAJOR: + case SCSI_DISK11_MAJOR: + case SCSI_DISK12_MAJOR: + case SCSI_DISK13_MAJOR: + case SCSI_DISK14_MAJOR: + case SCSI_DISK15_MAJOR: + { +// DtaDevLinuxSata *SataDrive = new DtaDevLinuxSata(); + drive = new DtaDevLinuxSata(); + goto init; + } + } + break; + } + case S_IFCHR: // character device + { + char path[PATH_MAX]; + snprintf(path, sizeof path, "/sys/dev/char/%u:%u/device/driver", major(st.st_rdev), minor(st.st_rdev)); + ssize_t r; + if ((r = readlink(path, path, sizeof path - 1)) < 0) + { + LOG(E) << "DtaDevOS::init ERROR - failed to readlink " << path; + isOpen = FALSE; + return; + } + path[r] = '\0'; // readlink does not append a null terminator + if (strcmp(basename(path), "nvme") == 0) + { +// DtaDevLinuxNvme *NvmeDrive = new DtaDevLinuxNvme(); + drive = new DtaDevLinuxNvme(); + goto init; + } + break; + } } - else - { - LOG(E) << "DtaDevOS::init ERROR - unknown drive type"; - isOpen = FALSE; - return; - } + LOG(E) << "DtaDevOS::init ERROR - unknown drive type"; + isOpen = FALSE; + return; +init: if (drive->init(devref)) { isOpen = TRUE;