| /* |
| * Copyright (c) 2006-2015 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. |
| */ |
| |
| /* |
| * 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 <string.h> |
| #include <ctype.h> |
| #include <getopt.h> |
| #include <errno.h> |
| |
| #include "sg_lib.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.15 (win32) 20140827"; |
| |
| #define MAX_SCSI_ELEMS 1024 |
| #define MAX_ADAPTER_NUM 64 |
| #define MAX_PHYSICALDRIVE_NUM 512 |
| #define MAX_CDROM_NUM 512 |
| #define MAX_TAPE_NUM 512 |
| #define MAX_HOLE_COUNT 8 |
| |
| // IOCTL_STORAGE_QUERY_PROPERTY |
| |
| #define FILE_DEVICE_MASS_STORAGE 0x0000002d |
| #define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE |
| #define FILE_ANY_ACCESS 0 |
| |
| // #define METHOD_BUFFERED 0 |
| |
| #define IOCTL_STORAGE_QUERY_PROPERTY \ |
| CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) |
| |
| |
| #ifndef _DEVIOCTL_ |
| typedef enum _STORAGE_BUS_TYPE { |
| BusTypeUnknown = 0x00, |
| BusTypeScsi = 0x01, |
| BusTypeAtapi = 0x02, |
| BusTypeAta = 0x03, |
| BusType1394 = 0x04, |
| BusTypeSsa = 0x05, |
| BusTypeFibre = 0x06, |
| BusTypeUsb = 0x07, |
| BusTypeRAID = 0x08, |
| BusTypeiScsi = 0x09, |
| BusTypeSas = 0x0A, |
| BusTypeSata = 0x0B, |
| BusTypeSd = 0x0C, |
| BusTypeMmc = 0x0D, |
| BusTypeVirtual = 0xE, |
| BusTypeFileBackedVirtual = 0xF, |
| BusTypeMax, |
| BusTypeMaxReserved = 0x7F |
| } STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE; |
| |
| typedef struct _STORAGE_DEVICE_DESCRIPTOR { |
| ULONG Version; |
| ULONG Size; |
| UCHAR DeviceType; |
| UCHAR DeviceTypeModifier; |
| BOOLEAN RemovableMedia; |
| BOOLEAN CommandQueueing; |
| ULONG VendorIdOffset; /* 0 if not available */ |
| ULONG ProductIdOffset; /* 0 if not available */ |
| ULONG ProductRevisionOffset;/* 0 if not available */ |
| ULONG SerialNumberOffset; /* -1 if not available ?? */ |
| STORAGE_BUS_TYPE BusType; |
| ULONG RawPropertiesLength; |
| UCHAR RawDeviceProperties[1]; |
| } STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR; |
| #endif |
| |
| typedef struct _STORAGE_DEVICE_UNIQUE_IDENTIFIER { |
| ULONG Version; |
| ULONG Size; |
| ULONG StorageDeviceIdOffset; |
| ULONG StorageDeviceOffset; |
| ULONG DriveLayoutSignatureOffset; |
| } STORAGE_DEVICE_UNIQUE_IDENTIFIER, *PSTORAGE_DEVICE_UNIQUE_IDENTIFIER; |
| |
| // Use CompareStorageDuids(PSTORAGE_DEVICE_UNIQUE_IDENTIFIER duid1, duid2) |
| // to test for equality |
| |
| #ifndef _DEVIOCTL_ |
| typedef enum _STORAGE_QUERY_TYPE { |
| PropertyStandardQuery = 0, |
| PropertyExistsQuery, |
| PropertyMaskQuery, |
| PropertyQueryMaxDefined |
| } STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE; |
| |
| typedef enum _STORAGE_PROPERTY_ID { |
| StorageDeviceProperty = 0, |
| StorageAdapterProperty, |
| StorageDeviceIdProperty, |
| StorageDeviceUniqueIdProperty, |
| StorageDeviceWriteCacheProperty, |
| StorageMiniportProperty, |
| StorageAccessAlignmentProperty |
| } STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID; |
| |
| typedef struct _STORAGE_PROPERTY_QUERY { |
| STORAGE_PROPERTY_ID PropertyId; |
| STORAGE_QUERY_TYPE QueryType; |
| UCHAR AdditionalParameters[1]; |
| } STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY; |
| #endif |
| |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| |
| 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]; |
| int qp_descriptor_valid; |
| int qp_uid_valid; |
| union STORAGE_DEVICE_DESCRIPTOR_DATA qp_descriptor; |
| union STORAGE_DEVICE_UID_DATA qp_uid; |
| }; |
| |
| |
| static struct storage_elem * storage_arr; |
| static int next_unused_elem = 0; |
| static int verbose = 0; |
| |
| static struct option long_options[] = { |
| {"bus", 0, 0, 'b'}, |
| {"help", 0, 0, 'h'}, |
| {"letter", 1, 0, 'l'}, |
| {"verbose", 0, 0, 'v'}, |
| {"scsi", 0, 0, 's'}, |
| {"version", 0, 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 show SCSI adapter (tuple) scan\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) |
| { |
| LPVOID lpMsgBuf; |
| int k, num, ch; |
| |
| if (max_b_len < 2) { |
| if (1 == max_b_len) |
| b[0] = '\0'; |
| return b; |
| } |
| memset(b, 0, max_b_len); |
| FormatMessage( |
| FORMAT_MESSAGE_ALLOCATE_BUFFER | |
| FORMAT_MESSAGE_FROM_SYSTEM, |
| NULL, err, |
| MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
| (LPTSTR) &lpMsgBuf, 0, NULL ); |
| num = lstrlen((LPCTSTR)lpMsgBuf); |
| if (num < 1) |
| return b; |
| num = (num < max_b_len) ? num : (max_b_len - 1); |
| for (k = 0; k < num; ++k) { |
| ch = *((LPCTSTR)lpMsgBuf + k); |
| if ((ch >= 0x0) && (ch < 0x7f)) |
| b[k] = ch & 0x7f; |
| else |
| b[k] = '?'; |
| } |
| return b; |
| } |
| |
| 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"; |
| case BusTypeMax: |
| return "Max "; |
| 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 occured. */ |
| 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; |
| BYTE bus; |
| BOOL success; |
| char adapter_name[64]; |
| char inqDataBuff[8192]; |
| PSCSI_ADAPTER_BUS_INFO ai; |
| char b[256]; |
| |
| 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, inqDataBuff, |
| sizeof(inqDataBuff), &dummy, NULL); |
| if (success) { |
| PSCSI_BUS_DATA pbd; |
| PSCSI_INQUIRY_DATA pid; |
| int num_lus, off; |
| |
| ai = (PSCSI_ADAPTER_BUS_INFO)inqDataBuff; |
| 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 > ((int)sizeof(inqDataBuff) - |
| (int)sizeof(SCSI_INQUIRY_DATA)))) |
| break; |
| pid = (PSCSI_INQUIRY_DATA)(inqDataBuff + 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] % 0x3f, |
| ((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)); |
| } |
| 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_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 = 1; |
| 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 = 1; |
| 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 = 1; |
| 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 = 1; |
| 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_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 = 1; |
| 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 = 1; |
| 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 = 1; |
| 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 = 1; |
| 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, int 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) |
| dStrHexErr(sp->qp_descriptor.raw, 144, 0); |
| } else |
| printf("\n"); |
| if ((verbose > 3) && sp->qp_uid_valid) { |
| printf(" UID valid, in hex:\n"); |
| dStrHexErr(sp->qp_uid.raw, sizeof(sp->qp_uid.raw), 1); |
| } |
| } |
| } |
| |
| if (scsi_scan) { |
| if (scsi_scan < 2) |
| printf("\n"); |
| enum_scsi_adapters(); |
| } |
| return 0; |
| } |
| |
| |
| int |
| main(int argc, char * argv[]) |
| { |
| int c, ret; |
| int vol_letter = 0; |
| int show_bt = 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; |
| 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 = calloc(sizeof(struct storage_elem) * MAX_SCSI_ELEMS, 1); |
| if (storage_arr) { |
| ret = sg_do_wscan(vol_letter, show_bt, scsi_scan); |
| free(storage_arr); |
| } else { |
| pr2serr("Failed to allocate storage_arr on heap\n"); |
| ret = SG_LIB_SYNTAX_ERROR; |
| } |
| return ret; |
| } |