diff options
Diffstat (limited to 'src/sg_scan_win32.c')
-rw-r--r-- | src/sg_scan_win32.c | 733 |
1 files changed, 733 insertions, 0 deletions
diff --git a/src/sg_scan_win32.c b/src/sg_scan_win32.c new file mode 100644 index 00000000..d39212c7 --- /dev/null +++ b/src/sg_scan_win32.c @@ -0,0 +1,733 @@ +/* + * Copyright (c) 2006-2022 Douglas Gilbert. + * All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the BSD_LICENSE file. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +/* + * This utility shows the relationship between various device names and + * volumes in Windows OSes (Windows 2000, 2003, XP and Vista). There is + * an optional scsi adapter scan. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdarg.h> +#include <stdbool.h> +#include <string.h> +#include <ctype.h> +#include <getopt.h> +#include <errno.h> + +#include "sg_lib.h" +#include "sg_pt.h" +#include "sg_pr2serr.h" + +#ifdef _WIN32_WINNT + #if _WIN32_WINNT < 0x0602 + #undef _WIN32_WINNT + #define _WIN32_WINNT 0x0602 + #endif +#else +#define _WIN32_WINNT 0x0602 +/* claim its W8 */ +#endif + +#include "sg_pt_win32.h" + +static const char * version_str = "1.23 (win32) 20220127"; + +#define MAX_SCSI_ELEMS 4096 +#define MAX_ADAPTER_NUM 256 +#define MAX_PHYSICALDRIVE_NUM 2048 +#define MAX_CDROM_NUM 512 +#define MAX_TAPE_NUM 512 +#define MAX_HOLE_COUNT 16 +#define MAX_GET_INQUIRY_DATA_SZ (32 * 1024) + + +union STORAGE_DEVICE_DESCRIPTOR_DATA { + STORAGE_DEVICE_DESCRIPTOR desc; + char raw[256]; +}; + +union STORAGE_DEVICE_UID_DATA { + STORAGE_DEVICE_UNIQUE_IDENTIFIER desc; + char raw[1060]; +}; + +struct storage_elem { + char name[32]; + char volume_letters[32]; + bool qp_descriptor_valid; + bool qp_uid_valid; + union STORAGE_DEVICE_DESCRIPTOR_DATA qp_descriptor; + union STORAGE_DEVICE_UID_DATA qp_uid; +}; + + +static struct storage_elem * storage_arr; +static uint8_t * free_storage_arr; +static int next_unused_elem = 0; +static int verbose = 0; + +static struct option long_options[] = { + {"bus", no_argument, 0, 'b'}, + {"help", no_argument, 0, 'h'}, + {"letter", required_argument, 0, 'l'}, + {"verbose", no_argument, 0, 'v'}, + {"scsi", no_argument, 0, 's'}, + {"version", no_argument, 0, 'V'}, + {0, 0, 0, 0}, +}; + + +static void +usage() +{ + pr2serr("Usage: sg_scan [--bus] [--help] [--letter=VL] [--scsi] " + "[--verbose] [--version]\n"); + pr2serr(" --bus|-b output bus type\n" + " --help|-h output this usage message then exit\n" + " --letter=VL|-l VL volume letter (e.g. 'F' for F:) " + "to match\n" + " --scsi|-s used once: show SCSI adapters (tuple) " + "scan after\n" + " device scan; default: show no " + "adapters;\n" + " used twice: show only adapters\n" + " --verbose|-v increase verbosity\n" + " --version|-V print version string and exit\n\n" + "Scan for storage and related device names\n"); +} + +static char * +get_err_str(DWORD err, int max_b_len, char * b) +{ + char * cp; + struct sg_pt_base * tmp_p = construct_scsi_pt_obj(); + + if ((NULL == b) || (max_b_len < 2)) { + if (b && (max_b_len > 0)) + b[0] = '\0'; + return b; + } + if (NULL == tmp_p) { + snprintf(b, max_b_len, "%s: construct_scsi_pt_obj() failed\n", + __func__); + + return b; + } + set_scsi_pt_transport_err(tmp_p, (int)err); + cp = get_scsi_pt_transport_err_str(tmp_p, max_b_len, b); + destruct_scsi_pt_obj(tmp_p); + return cp; +} + +static const char * +get_bus_type(int bt) +{ + switch (bt) + { + case BusTypeUnknown: + return "Unkno"; + case BusTypeScsi: + return "Scsi "; + case BusTypeAtapi: + return "Atapi"; + case BusTypeAta: + return "Ata "; + case BusType1394: + return "1394 "; + case BusTypeSsa: + return "Ssa "; + case BusTypeFibre: + return "Fibre"; + case BusTypeUsb: + return "Usb "; + case BusTypeRAID: + return "RAID "; + case BusTypeiScsi: + return "iScsi"; + case BusTypeSas: + return "Sas "; + case BusTypeSata: + return "Sata "; + case BusTypeSd: + return "Sd "; + case BusTypeMmc: + return "Mmc "; + case BusTypeVirtual: + return "Virt "; + case BusTypeFileBackedVirtual: + return "FBVir"; +#ifdef BusTypeSpaces + case BusTypeSpaces: +#else + case 0x10: +#endif + return "Spaces"; +#ifdef BusTypeNvme + case BusTypeNvme: +#else + case 0x11: +#endif + return "NVMe "; +#ifdef BusTypeSCM + case BusTypeSCM: +#else + case 0x12: +#endif + return "SCM "; +#ifdef BusTypeUfs + case BusTypeUfs: +#else + case 0x13: +#endif + return "Ufs "; + case 0x14: + return "Max "; + case 0x7f: + return "Max Reserved"; + default: + return "_unkn"; + } +} + +static int +query_dev_property(HANDLE hdevice, + union STORAGE_DEVICE_DESCRIPTOR_DATA * data) +{ + DWORD num_out, err; + char b[256]; + STORAGE_PROPERTY_QUERY query = {StorageDeviceProperty, + PropertyStandardQuery, {0} }; + + memset(data, 0, sizeof(*data)); + if (! DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY, + &query, sizeof(query), data, sizeof(*data), + &num_out, NULL)) { + if (verbose > 2) { + err = GetLastError(); + pr2serr(" IOCTL_STORAGE_QUERY_PROPERTY(Devprop) failed, " + "Error=%u %s\n", (unsigned int)err, + get_err_str(err, sizeof(b), b)); + } + return -ENOSYS; + } + + if (verbose > 3) + pr2serr(" IOCTL_STORAGE_QUERY_PROPERTY(DevProp) num_out=%u\n", + (unsigned int)num_out); + return 0; +} + +static int +query_dev_uid(HANDLE hdevice, union STORAGE_DEVICE_UID_DATA * data) +{ + DWORD num_out, err; + char b[256]; + STORAGE_PROPERTY_QUERY query = {StorageDeviceUniqueIdProperty, + PropertyStandardQuery, {0} }; + + memset(data, 0, sizeof(*data)); + num_out = 0; + query.QueryType = PropertyExistsQuery; + if (! DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY, + &query, sizeof(query), NULL, 0, &num_out, NULL)) { + if (verbose > 2) { + err = GetLastError(); + pr2serr(" IOCTL_STORAGE_QUERY_PROPERTY(DevUid(exists)) failed, " + "Error=%u %s\n", (unsigned int)err, + get_err_str(err, sizeof(b), b)); + } + if (verbose > 3) + pr2serr(" num_out=%u\n", (unsigned int)num_out); + /* interpret any error to mean this property doesn't exist */ + return 0; + } + + query.QueryType = PropertyStandardQuery; + if (! DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY, + &query, sizeof(query), data, sizeof(*data), + &num_out, NULL)) { + if (verbose > 2) { + err = GetLastError(); + pr2serr(" IOCTL_STORAGE_QUERY_PROPERTY(DevUid) failed, Error=%u " + "%s\n", (unsigned int)err, + get_err_str(err, sizeof(b), b)); + } + return -ENOSYS; + } + if (verbose > 3) + pr2serr(" IOCTL_STORAGE_QUERY_PROPERTY(DevUid) num_out=%u\n", + (unsigned int)num_out); + return 0; +} + +/* Updates storage_arr based on sep. Returns 1 if update occurred, 0 if + * no update occurred. */ +static int +check_devices(const struct storage_elem * sep) +{ + int k, j; + struct storage_elem * sarr = storage_arr; + + for (k = 0; k < next_unused_elem; ++k, ++sarr) { + if ('\0' == sarr->name[0]) + continue; + if (sep->qp_uid_valid && sarr->qp_uid_valid) { + if (0 == memcmp(&sep->qp_uid, &sarr->qp_uid, + sizeof(sep->qp_uid))) { + for (j = 0; j < (int)sizeof(sep->volume_letters); ++j) { + if ('\0' == sarr->volume_letters[j]) { + sarr->volume_letters[j] = sep->name[0]; + break; + } + } + return 1; + } + } else if (sep->qp_descriptor_valid && sarr->qp_descriptor_valid) { + if (0 == memcmp(&sep->qp_descriptor, &sarr->qp_descriptor, + sizeof(sep->qp_descriptor))) { + for (j = 0; j < (int)sizeof(sep->volume_letters); ++j) { + if ('\0' == sarr->volume_letters[j]) { + sarr->volume_letters[j] = sep->name[0]; + break; + } + } + return 1; + } + } + } + return 0; +} + +static int +enum_scsi_adapters(void) +{ + int k, j; + int hole_count = 0; + HANDLE fh; + ULONG dummy; + DWORD err = 0; + BYTE bus; + BOOL success; + char adapter_name[64]; + char * inq_dbp; + uint8_t * free_inq_dbp = NULL; + PSCSI_ADAPTER_BUS_INFO ai; + char b[256]; + + inq_dbp = (char *)sg_memalign(MAX_GET_INQUIRY_DATA_SZ, 0, &free_inq_dbp, + false); + if (NULL == inq_dbp) { + pr2serr("%s: unable to allocate %d bytes on heap\n", __func__, + MAX_GET_INQUIRY_DATA_SZ); + return sg_convert_errno(ENOMEM); + } + + for (k = 0; k < MAX_ADAPTER_NUM; ++k) { + snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\SCSI%d:", k); + fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, 0, NULL); + if (fh != INVALID_HANDLE_VALUE) { + hole_count = 0; + success = DeviceIoControl(fh, IOCTL_SCSI_GET_INQUIRY_DATA, NULL, + 0, inq_dbp, MAX_GET_INQUIRY_DATA_SZ, + &dummy, NULL); + if (success) { + PSCSI_BUS_DATA pbd; + PSCSI_INQUIRY_DATA pid; + int num_lus, off; + + ai = (PSCSI_ADAPTER_BUS_INFO)inq_dbp; + for (bus = 0; bus < ai->NumberOfBusses; bus++) { + pbd = ai->BusData + bus; + num_lus = pbd->NumberOfLogicalUnits; + off = pbd->InquiryDataOffset; + for (j = 0; j < num_lus; ++j) { + if ((off < (int)sizeof(SCSI_ADAPTER_BUS_INFO)) || + (off > (MAX_GET_INQUIRY_DATA_SZ - + (int)sizeof(SCSI_INQUIRY_DATA)))) + break; + pid = (PSCSI_INQUIRY_DATA)(inq_dbp + off); + snprintf(b, sizeof(b) - 1, "SCSI%d:%d,%d,%d ", k, + pid->PathId, pid->TargetId, pid->Lun); + printf("%-15s", b); + snprintf(b, sizeof(b) - 1, "claimed=%d pdt=%xh %s ", + pid->DeviceClaimed, + pid->InquiryData[0] % PDT_MASK, + ((0 == pid->InquiryData[4]) ? "dubious" : + "")); + printf("%-26s", b); + printf("%.8s %.16s %.4s\n", pid->InquiryData + 8, + pid->InquiryData + 16, pid->InquiryData + 32); + off = pid->NextInquiryDataOffset; + } + } + } else { + err = GetLastError(); + pr2serr("%s: IOCTL_SCSI_GET_INQUIRY_DATA failed err=%u\n\t%s", + adapter_name, (unsigned int)err, + get_err_str(err, sizeof(b), b)); + err = SG_LIB_WINDOWS_ERR; + } + CloseHandle(fh); + } else { + err = GetLastError(); + if (ERROR_SHARING_VIOLATION == err) + pr2serr("%s: in use by other process (sharing violation " + "[34])\n", adapter_name); + else if (verbose > 3) + pr2serr("%s: CreateFile failed err=%u\n\t%s", adapter_name, + (unsigned int)err, get_err_str(err, sizeof(b), b)); + if (++hole_count >= MAX_HOLE_COUNT) + break; + /* hope problem is local to this adapter so continue to next */ + } + } + if (free_inq_dbp) + free(free_inq_dbp); + return 0; +} + +static int +enum_volumes(char letter) +{ + int k; + HANDLE fh; + char adapter_name[64]; + struct storage_elem tmp_se; + + if (verbose > 2) + pr2serr("%s: enter\n", __FUNCTION__ ); + for (k = 0; k < 24; ++k) { + memset(&tmp_se, 0, sizeof(tmp_se)); + snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\%c:", 'C' + k); + tmp_se.name[0] = 'C' + k; + fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, 0, NULL); + if (fh != INVALID_HANDLE_VALUE) { + if (query_dev_property(fh, &tmp_se.qp_descriptor) < 0) + pr2serr("%s: query_dev_property failed\n", __FUNCTION__ ); + else + tmp_se.qp_descriptor_valid = true; + if (query_dev_uid(fh, &tmp_se.qp_uid) < 0) { + if (verbose > 2) + pr2serr("%s: query_dev_uid failed\n", __FUNCTION__ ); + } else + tmp_se.qp_uid_valid = true; + if (('\0' == letter) || (letter == tmp_se.name[0])) + check_devices(&tmp_se); + CloseHandle(fh); + } + } + return 0; +} + +static int +enum_pds(void) +{ + int k; + int hole_count = 0; + HANDLE fh; + DWORD err; + char adapter_name[64]; + char b[256]; + struct storage_elem tmp_se; + + if (verbose > 2) + pr2serr("%s: enter\n", __FUNCTION__ ); + for (k = 0; k < MAX_PHYSICALDRIVE_NUM; ++k) { + memset(&tmp_se, 0, sizeof(tmp_se)); + snprintf(adapter_name, sizeof (adapter_name), + "\\\\.\\PhysicalDrive%d", k); + snprintf(tmp_se.name, sizeof(tmp_se.name), "PD%d", k); + fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, 0, NULL); + if (fh != INVALID_HANDLE_VALUE) { + if (query_dev_property(fh, &tmp_se.qp_descriptor) < 0) + pr2serr("%s: query_dev_property failed\n", __FUNCTION__ ); + else + tmp_se.qp_descriptor_valid = true; + if (query_dev_uid(fh, &tmp_se.qp_uid) < 0) { + if (verbose > 2) + pr2serr("%s: query_dev_uid failed\n", __FUNCTION__ ); + } else + tmp_se.qp_uid_valid = true; + hole_count = 0; + memcpy(&storage_arr[next_unused_elem++], &tmp_se, sizeof(tmp_se)); + CloseHandle(fh); + } else { + err = GetLastError(); + if ((0 == k) && (ERROR_ACCESS_DENIED == err)) + pr2serr("Access denied on %s, may need Administrator\n", + adapter_name); + if (ERROR_SHARING_VIOLATION == err) + pr2serr("%s: in use by other process (sharing violation " + "[34])\n", adapter_name); + else if (verbose > 3) + pr2serr("%s: CreateFile failed err=%u\n\t%s", adapter_name, + (unsigned int)err, get_err_str(err, sizeof(b), b)); + if (++hole_count >= MAX_HOLE_COUNT) + break; + } + } + return 0; +} + +static int +enum_cdroms(void) +{ + int k; + int hole_count = 0; + HANDLE fh; + DWORD err; + char adapter_name[64]; + char b[256]; + struct storage_elem tmp_se; + + if (verbose > 2) + pr2serr("%s: enter\n", __FUNCTION__ ); + for (k = 0; k < MAX_CDROM_NUM; ++k) { + memset(&tmp_se, 0, sizeof(tmp_se)); + snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\CDROM%d", k); + snprintf(tmp_se.name, sizeof(tmp_se.name), "CDROM%d", k); + fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, 0, NULL); + if (fh != INVALID_HANDLE_VALUE) { + if (query_dev_property(fh, &tmp_se.qp_descriptor) < 0) + pr2serr("%s: query_dev_property failed\n", __FUNCTION__ ); + else + tmp_se.qp_descriptor_valid = true; + if (query_dev_uid(fh, &tmp_se.qp_uid) < 0) { + if (verbose > 2) + pr2serr("%s: query_dev_uid failed\n", __FUNCTION__ ); + } else + tmp_se.qp_uid_valid = true; + hole_count = 0; + memcpy(&storage_arr[next_unused_elem++], &tmp_se, sizeof(tmp_se)); + CloseHandle(fh); + } else { + err = GetLastError(); + if (ERROR_SHARING_VIOLATION == err) + pr2serr("%s: in use by other process (sharing violation " + "[34])\n", adapter_name); + else if (verbose > 3) + pr2serr("%s: CreateFile failed err=%u\n\t%s", adapter_name, + (unsigned int)err, get_err_str(err, sizeof(b), b)); + if (++hole_count >= MAX_HOLE_COUNT) + break; + } + } + return 0; +} + +static int +enum_tapes(void) +{ + int k; + int hole_count = 0; + HANDLE fh; + DWORD err; + char adapter_name[64]; + char b[256]; + struct storage_elem tmp_se; + + if (verbose > 2) + pr2serr("%s: enter\n", __FUNCTION__ ); + for (k = 0; k < MAX_TAPE_NUM; ++k) { + memset(&tmp_se, 0, sizeof(tmp_se)); + snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\TAPE%d", k); + snprintf(tmp_se.name, sizeof(tmp_se.name), "TAPE%d", k); + fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, 0, NULL); + if (fh != INVALID_HANDLE_VALUE) { + if (query_dev_property(fh, &tmp_se.qp_descriptor) < 0) + pr2serr("%s: query_dev_property failed\n", __FUNCTION__ ); + else + tmp_se.qp_descriptor_valid = true; + if (query_dev_uid(fh, &tmp_se.qp_uid) < 0) { + if (verbose > 2) + pr2serr("%s: query_dev_uid failed\n", __FUNCTION__ ); + } else + tmp_se.qp_uid_valid = true; + hole_count = 0; + memcpy(&storage_arr[next_unused_elem++], &tmp_se, sizeof(tmp_se)); + CloseHandle(fh); + } else { + err = GetLastError(); + if (ERROR_SHARING_VIOLATION == err) + pr2serr("%s: in use by other process (sharing violation " + "[34])\n", adapter_name); + else if (verbose > 3) + pr2serr("%s: CreateFile failed err=%u\n\t%s", adapter_name, + (unsigned int)err, get_err_str(err, sizeof(b), b)); + if (++hole_count >= MAX_HOLE_COUNT) + break; + } + } + return 0; +} + +static int +sg_do_wscan(char letter, bool show_bt, int scsi_scan) +{ + int k, j, n; + struct storage_elem * sp; + + if (scsi_scan < 2) { + k = enum_pds(); + if (k) + return k; + k = enum_cdroms(); + if (k) + return k; + k = enum_tapes(); + if (k) + return k; + k = enum_volumes(letter); + if (k) + return k; + + for (k = 0; k < next_unused_elem; ++k) { + sp = storage_arr + k; + if ('\0' == sp->name[0]) + continue; + printf("%-7s ", sp->name); + n = strlen(sp->volume_letters); + if (0 == n) + printf(" "); + else if (1 == n) + printf("[%s] ", sp->volume_letters); + else if (2 == n) + printf("[%s] ", sp->volume_letters); + else if (3 == n) + printf("[%s] ", sp->volume_letters); + else if (4 == n) + printf("[%s] ", sp->volume_letters); + else + printf("[%4s+] ", sp->volume_letters); + if (sp->qp_descriptor_valid) { + if (show_bt) + printf("<%s> ", + get_bus_type(sp->qp_descriptor.desc.BusType)); + j = sp->qp_descriptor.desc.VendorIdOffset; + if (j > 0) + printf("%s ", sp->qp_descriptor.raw + j); + j = sp->qp_descriptor.desc.ProductIdOffset; + if (j > 0) + printf("%s ", sp->qp_descriptor.raw + j); + j = sp->qp_descriptor.desc.ProductRevisionOffset; + if (j > 0) + printf("%s ", sp->qp_descriptor.raw + j); + j = sp->qp_descriptor.desc.SerialNumberOffset; + if (j > 0) + printf("%s", sp->qp_descriptor.raw + j); + printf("\n"); + if (verbose > 2) + hex2stderr((const uint8_t *)sp->qp_descriptor.raw, 144, 0); + } else + printf("\n"); + if ((verbose > 3) && sp->qp_uid_valid) { + printf(" UID valid, in hex:\n"); + hex2stderr((const uint8_t *)sp->qp_uid.raw, + sizeof(sp->qp_uid.raw), 0); + } + } + } + + if (scsi_scan) { + if (scsi_scan < 2) + printf("\n"); + enum_scsi_adapters(); + } + return 0; +} + + +int +main(int argc, char * argv[]) +{ + bool show_bt = false; + int c, ret; + int vol_letter = 0; + int scsi_scan = 0; + + while (1) { + int option_index = 0; + + c = getopt_long(argc, argv, "bhHl:svV", long_options, + &option_index); + if (c == -1) + break; + + switch (c) { + case 'b': + show_bt = true; + break; + case 'h': + case '?': + usage(); + return 0; + case 'l': + vol_letter = toupper(optarg[0]); + if ((vol_letter < 'C') || (vol_letter > 'Z')) { + pr2serr("'--letter=' expects a letter in the 'C' to 'Z' " + "range\n"); + usage(); + return SG_LIB_SYNTAX_ERROR; + } + break; + case 's': + ++scsi_scan; + break; + case 'v': + ++verbose; + break; + case 'V': + pr2serr("version: %s\n", version_str); + return 0; + default: + pr2serr("unrecognised option code 0x%x ??\n", c); + usage(); + return SG_LIB_SYNTAX_ERROR; + } + } + if (optind < argc) { + if (optind < argc) { + for (; optind < argc; ++optind) + pr2serr("Unexpected extra argument: %s\n", argv[optind]); + usage(); + return SG_LIB_SYNTAX_ERROR; + } + } + + storage_arr = (struct storage_elem *) + sg_memalign(sizeof(struct storage_elem) * MAX_SCSI_ELEMS, 0, + &free_storage_arr, false); + if (storage_arr) { + ret = sg_do_wscan(vol_letter, show_bt, scsi_scan); + if (free_storage_arr) + free(free_storage_arr); + } else { + pr2serr("Failed to allocate storage_arr (%d bytes) on heap\n", + (int)(sizeof(struct storage_elem) * MAX_SCSI_ELEMS)); + ret = sg_convert_errno(ENOMEM); + } + return ret; +} |