xref: /aosp_15_r20/external/sg3_utils/src/sg_scan_win32.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /*
2  * Copyright (c) 2006-2022 Douglas Gilbert.
3  * All rights reserved.
4  * Use of this source code is governed by a BSD-style
5  * license that can be found in the BSD_LICENSE file.
6  *
7  * SPDX-License-Identifier: BSD-2-Clause
8  */
9 
10 /*
11  * This utility shows the relationship between various device names and
12  * volumes in Windows OSes (Windows 2000, 2003, XP and Vista). There is
13  * an optional scsi adapter scan.
14  */
15 
16 #ifdef HAVE_CONFIG_H
17 #include "config.h"
18 #endif
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stddef.h>
23 #include <stdarg.h>
24 #include <stdbool.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <getopt.h>
28 #include <errno.h>
29 
30 #include "sg_lib.h"
31 #include "sg_pt.h"
32 #include "sg_pr2serr.h"
33 
34 #ifdef _WIN32_WINNT
35  #if _WIN32_WINNT < 0x0602
36  #undef _WIN32_WINNT
37  #define _WIN32_WINNT 0x0602
38  #endif
39 #else
40 #define _WIN32_WINNT 0x0602
41 /* claim its W8 */
42 #endif
43 
44 #include "sg_pt_win32.h"
45 
46 static const char * version_str = "1.23 (win32) 20220127";
47 
48 #define MAX_SCSI_ELEMS 4096
49 #define MAX_ADAPTER_NUM 256
50 #define MAX_PHYSICALDRIVE_NUM 2048
51 #define MAX_CDROM_NUM 512
52 #define MAX_TAPE_NUM 512
53 #define MAX_HOLE_COUNT 16
54 #define MAX_GET_INQUIRY_DATA_SZ (32 * 1024)
55 
56 
57 union STORAGE_DEVICE_DESCRIPTOR_DATA {
58     STORAGE_DEVICE_DESCRIPTOR desc;
59     char raw[256];
60 };
61 
62 union STORAGE_DEVICE_UID_DATA {
63     STORAGE_DEVICE_UNIQUE_IDENTIFIER desc;
64     char raw[1060];
65 };
66 
67 struct storage_elem {
68     char    name[32];
69     char    volume_letters[32];
70     bool qp_descriptor_valid;
71     bool qp_uid_valid;
72     union STORAGE_DEVICE_DESCRIPTOR_DATA qp_descriptor;
73     union STORAGE_DEVICE_UID_DATA qp_uid;
74 };
75 
76 
77 static struct storage_elem * storage_arr;
78 static uint8_t * free_storage_arr;
79 static int next_unused_elem = 0;
80 static int verbose = 0;
81 
82 static struct option long_options[] = {
83         {"bus", no_argument, 0, 'b'},
84         {"help", no_argument, 0, 'h'},
85         {"letter", required_argument, 0, 'l'},
86         {"verbose", no_argument, 0, 'v'},
87         {"scsi", no_argument, 0, 's'},
88         {"version", no_argument, 0, 'V'},
89         {0, 0, 0, 0},
90 };
91 
92 
93 static void
usage()94 usage()
95 {
96     pr2serr("Usage: sg_scan  [--bus] [--help] [--letter=VL] [--scsi] "
97             "[--verbose] [--version]\n");
98     pr2serr("       --bus|-b        output bus type\n"
99             "       --help|-h       output this usage message then exit\n"
100             "       --letter=VL|-l VL    volume letter (e.g. 'F' for F:) "
101             "to match\n"
102             "       --scsi|-s       used once: show SCSI adapters (tuple) "
103             "scan after\n"
104             "                       device scan; default: show no "
105             "adapters;\n"
106             "                       used twice: show only adapters\n"
107             "       --verbose|-v    increase verbosity\n"
108             "       --version|-V    print version string and exit\n\n"
109             "Scan for storage and related device names\n");
110 }
111 
112 static char *
get_err_str(DWORD err,int max_b_len,char * b)113 get_err_str(DWORD err, int max_b_len, char * b)
114 {
115     char * cp;
116     struct sg_pt_base * tmp_p = construct_scsi_pt_obj();
117 
118     if ((NULL == b) || (max_b_len < 2)) {
119         if (b && (max_b_len > 0))
120             b[0] = '\0';
121         return b;
122     }
123     if (NULL == tmp_p) {
124         snprintf(b, max_b_len, "%s: construct_scsi_pt_obj() failed\n",
125                  __func__);
126 
127         return b;
128     }
129     set_scsi_pt_transport_err(tmp_p, (int)err);
130     cp = get_scsi_pt_transport_err_str(tmp_p, max_b_len, b);
131     destruct_scsi_pt_obj(tmp_p);
132     return cp;
133 }
134 
135 static const char *
get_bus_type(int bt)136 get_bus_type(int bt)
137 {
138     switch (bt)
139     {
140     case BusTypeUnknown:
141         return "Unkno";
142     case BusTypeScsi:
143         return "Scsi ";
144     case BusTypeAtapi:
145         return "Atapi";
146     case BusTypeAta:
147         return "Ata  ";
148     case BusType1394:
149         return "1394 ";
150     case BusTypeSsa:
151         return "Ssa  ";
152     case BusTypeFibre:
153         return "Fibre";
154     case BusTypeUsb:
155         return "Usb  ";
156     case BusTypeRAID:
157         return "RAID ";
158     case BusTypeiScsi:
159         return "iScsi";
160     case BusTypeSas:
161         return "Sas  ";
162     case BusTypeSata:
163         return "Sata ";
164     case BusTypeSd:
165         return "Sd   ";
166     case BusTypeMmc:
167         return "Mmc  ";
168     case BusTypeVirtual:
169         return "Virt ";
170     case BusTypeFileBackedVirtual:
171         return "FBVir";
172 #ifdef BusTypeSpaces
173     case BusTypeSpaces:
174 #else
175     case 0x10:
176 #endif
177         return "Spaces";
178 #ifdef BusTypeNvme
179     case BusTypeNvme:
180 #else
181     case 0x11:
182 #endif
183         return "NVMe ";
184 #ifdef BusTypeSCM
185     case BusTypeSCM:
186 #else
187     case 0x12:
188 #endif
189         return "SCM  ";
190 #ifdef BusTypeUfs
191     case BusTypeUfs:
192 #else
193     case 0x13:
194 #endif
195         return "Ufs ";
196     case 0x14:
197         return "Max ";
198     case 0x7f:
199         return "Max Reserved";
200     default:
201         return "_unkn";
202     }
203 }
204 
205 static int
query_dev_property(HANDLE hdevice,union STORAGE_DEVICE_DESCRIPTOR_DATA * data)206 query_dev_property(HANDLE hdevice,
207                    union STORAGE_DEVICE_DESCRIPTOR_DATA * data)
208 {
209     DWORD num_out, err;
210     char b[256];
211     STORAGE_PROPERTY_QUERY query = {StorageDeviceProperty,
212                                     PropertyStandardQuery, {0} };
213 
214     memset(data, 0, sizeof(*data));
215     if (! DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY,
216                           &query, sizeof(query), data, sizeof(*data),
217                           &num_out, NULL)) {
218         if (verbose > 2) {
219             err = GetLastError();
220             pr2serr("  IOCTL_STORAGE_QUERY_PROPERTY(Devprop) failed, "
221                     "Error=%u %s\n", (unsigned int)err,
222                     get_err_str(err, sizeof(b), b));
223         }
224         return -ENOSYS;
225     }
226 
227     if (verbose > 3)
228         pr2serr("  IOCTL_STORAGE_QUERY_PROPERTY(DevProp) num_out=%u\n",
229                 (unsigned int)num_out);
230     return 0;
231 }
232 
233 static int
query_dev_uid(HANDLE hdevice,union STORAGE_DEVICE_UID_DATA * data)234 query_dev_uid(HANDLE hdevice, union STORAGE_DEVICE_UID_DATA * data)
235 {
236     DWORD num_out, err;
237     char b[256];
238     STORAGE_PROPERTY_QUERY query = {StorageDeviceUniqueIdProperty,
239                                     PropertyStandardQuery, {0} };
240 
241     memset(data, 0, sizeof(*data));
242     num_out = 0;
243     query.QueryType = PropertyExistsQuery;
244     if (! DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY,
245                           &query, sizeof(query), NULL, 0, &num_out, NULL)) {
246         if (verbose > 2) {
247             err = GetLastError();
248             pr2serr("  IOCTL_STORAGE_QUERY_PROPERTY(DevUid(exists)) failed, "
249                     "Error=%u %s\n", (unsigned int)err,
250                     get_err_str(err, sizeof(b), b));
251         }
252         if (verbose > 3)
253             pr2serr("      num_out=%u\n", (unsigned int)num_out);
254         /* interpret any error to mean this property doesn't exist */
255         return 0;
256     }
257 
258     query.QueryType = PropertyStandardQuery;
259     if (! DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY,
260                           &query, sizeof(query), data, sizeof(*data),
261                           &num_out, NULL)) {
262         if (verbose > 2) {
263             err = GetLastError();
264             pr2serr("  IOCTL_STORAGE_QUERY_PROPERTY(DevUid) failed, Error=%u "
265                     "%s\n", (unsigned int)err,
266                     get_err_str(err, sizeof(b), b));
267         }
268         return -ENOSYS;
269     }
270     if (verbose > 3)
271         pr2serr("  IOCTL_STORAGE_QUERY_PROPERTY(DevUid) num_out=%u\n",
272                 (unsigned int)num_out);
273     return 0;
274 }
275 
276 /* Updates storage_arr based on sep. Returns 1 if update occurred, 0 if
277  * no update occurred. */
278 static int
check_devices(const struct storage_elem * sep)279 check_devices(const struct storage_elem * sep)
280 {
281     int k, j;
282     struct storage_elem * sarr = storage_arr;
283 
284     for (k = 0; k < next_unused_elem; ++k, ++sarr) {
285         if ('\0' == sarr->name[0])
286             continue;
287         if (sep->qp_uid_valid && sarr->qp_uid_valid) {
288             if (0 == memcmp(&sep->qp_uid, &sarr->qp_uid,
289                             sizeof(sep->qp_uid))) {
290                 for (j = 0; j < (int)sizeof(sep->volume_letters); ++j) {
291                     if ('\0' == sarr->volume_letters[j]) {
292                         sarr->volume_letters[j] = sep->name[0];
293                         break;
294                     }
295                 }
296                 return 1;
297             }
298         } else if (sep->qp_descriptor_valid && sarr->qp_descriptor_valid) {
299             if (0 == memcmp(&sep->qp_descriptor, &sarr->qp_descriptor,
300                             sizeof(sep->qp_descriptor))) {
301                 for (j = 0; j < (int)sizeof(sep->volume_letters); ++j) {
302                     if ('\0' == sarr->volume_letters[j]) {
303                         sarr->volume_letters[j] = sep->name[0];
304                         break;
305                     }
306                 }
307                 return 1;
308             }
309         }
310     }
311     return 0;
312 }
313 
314 static int
enum_scsi_adapters(void)315 enum_scsi_adapters(void)
316 {
317     int k, j;
318     int hole_count = 0;
319     HANDLE fh;
320     ULONG dummy;
321     DWORD err = 0;
322     BYTE bus;
323     BOOL success;
324     char adapter_name[64];
325     char * inq_dbp;
326     uint8_t * free_inq_dbp = NULL;
327     PSCSI_ADAPTER_BUS_INFO  ai;
328     char b[256];
329 
330     inq_dbp = (char *)sg_memalign(MAX_GET_INQUIRY_DATA_SZ, 0, &free_inq_dbp,
331                                   false);
332     if (NULL == inq_dbp) {
333         pr2serr("%s: unable to allocate %d bytes on heap\n", __func__,
334                 MAX_GET_INQUIRY_DATA_SZ);
335         return sg_convert_errno(ENOMEM);
336     }
337 
338     for (k = 0; k < MAX_ADAPTER_NUM; ++k) {
339         snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\SCSI%d:", k);
340         fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE,
341                         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
342                         OPEN_EXISTING, 0, NULL);
343         if (fh != INVALID_HANDLE_VALUE) {
344             hole_count = 0;
345             success = DeviceIoControl(fh, IOCTL_SCSI_GET_INQUIRY_DATA, NULL,
346                                       0, inq_dbp, MAX_GET_INQUIRY_DATA_SZ,
347                                       &dummy, NULL);
348             if (success) {
349                 PSCSI_BUS_DATA pbd;
350                 PSCSI_INQUIRY_DATA pid;
351                 int num_lus, off;
352 
353                 ai = (PSCSI_ADAPTER_BUS_INFO)inq_dbp;
354                 for (bus = 0; bus < ai->NumberOfBusses; bus++) {
355                     pbd = ai->BusData + bus;
356                     num_lus = pbd->NumberOfLogicalUnits;
357                     off = pbd->InquiryDataOffset;
358                     for (j = 0; j < num_lus; ++j) {
359                         if ((off < (int)sizeof(SCSI_ADAPTER_BUS_INFO)) ||
360                             (off > (MAX_GET_INQUIRY_DATA_SZ -
361                                     (int)sizeof(SCSI_INQUIRY_DATA))))
362                             break;
363                         pid = (PSCSI_INQUIRY_DATA)(inq_dbp + off);
364                         snprintf(b, sizeof(b) - 1, "SCSI%d:%d,%d,%d ", k,
365                                  pid->PathId, pid->TargetId, pid->Lun);
366                         printf("%-15s", b);
367                         snprintf(b, sizeof(b) - 1, "claimed=%d pdt=%xh %s ",
368                                  pid->DeviceClaimed,
369                                  pid->InquiryData[0] % PDT_MASK,
370                                  ((0 == pid->InquiryData[4]) ? "dubious" :
371                                                                ""));
372                         printf("%-26s", b);
373                         printf("%.8s  %.16s  %.4s\n", pid->InquiryData + 8,
374                                pid->InquiryData + 16, pid->InquiryData + 32);
375                         off = pid->NextInquiryDataOffset;
376                     }
377                 }
378             } else {
379                 err = GetLastError();
380                 pr2serr("%s: IOCTL_SCSI_GET_INQUIRY_DATA failed err=%u\n\t%s",
381                         adapter_name, (unsigned int)err,
382                         get_err_str(err, sizeof(b), b));
383                 err = SG_LIB_WINDOWS_ERR;
384             }
385             CloseHandle(fh);
386         } else {
387             err = GetLastError();
388             if (ERROR_SHARING_VIOLATION == err)
389                 pr2serr("%s: in use by other process (sharing violation "
390                         "[34])\n", adapter_name);
391             else if (verbose > 3)
392                 pr2serr("%s: CreateFile failed err=%u\n\t%s", adapter_name,
393                         (unsigned int)err, get_err_str(err, sizeof(b), b));
394             if (++hole_count >= MAX_HOLE_COUNT)
395                 break;
396             /* hope problem is local to this adapter so continue to next */
397         }
398     }
399     if (free_inq_dbp)
400         free(free_inq_dbp);
401     return 0;
402 }
403 
404 static int
enum_volumes(char letter)405 enum_volumes(char letter)
406 {
407     int k;
408     HANDLE fh;
409     char adapter_name[64];
410     struct storage_elem tmp_se;
411 
412     if (verbose > 2)
413         pr2serr("%s: enter\n", __FUNCTION__ );
414     for (k = 0; k < 24; ++k) {
415         memset(&tmp_se, 0, sizeof(tmp_se));
416         snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\%c:", 'C' + k);
417         tmp_se.name[0] = 'C' + k;
418         fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE,
419                         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
420                         OPEN_EXISTING, 0, NULL);
421         if (fh != INVALID_HANDLE_VALUE) {
422             if (query_dev_property(fh, &tmp_se.qp_descriptor) < 0)
423                 pr2serr("%s: query_dev_property failed\n", __FUNCTION__ );
424             else
425                 tmp_se.qp_descriptor_valid = true;
426             if (query_dev_uid(fh, &tmp_se.qp_uid) < 0) {
427                 if (verbose > 2)
428                     pr2serr("%s: query_dev_uid failed\n", __FUNCTION__ );
429             } else
430                 tmp_se.qp_uid_valid = true;
431             if (('\0' == letter) || (letter == tmp_se.name[0]))
432                 check_devices(&tmp_se);
433             CloseHandle(fh);
434         }
435     }
436     return 0;
437 }
438 
439 static int
enum_pds(void)440 enum_pds(void)
441 {
442     int k;
443     int hole_count = 0;
444     HANDLE fh;
445     DWORD err;
446     char adapter_name[64];
447     char b[256];
448     struct storage_elem tmp_se;
449 
450     if (verbose > 2)
451         pr2serr("%s: enter\n", __FUNCTION__ );
452     for (k = 0; k < MAX_PHYSICALDRIVE_NUM; ++k) {
453         memset(&tmp_se, 0, sizeof(tmp_se));
454         snprintf(adapter_name, sizeof (adapter_name),
455                  "\\\\.\\PhysicalDrive%d", k);
456         snprintf(tmp_se.name, sizeof(tmp_se.name), "PD%d", k);
457         fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE,
458                         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
459                         OPEN_EXISTING, 0, NULL);
460         if (fh != INVALID_HANDLE_VALUE) {
461             if (query_dev_property(fh, &tmp_se.qp_descriptor) < 0)
462                 pr2serr("%s: query_dev_property failed\n", __FUNCTION__ );
463             else
464                 tmp_se.qp_descriptor_valid = true;
465             if (query_dev_uid(fh, &tmp_se.qp_uid) < 0) {
466                 if (verbose > 2)
467                     pr2serr("%s: query_dev_uid failed\n", __FUNCTION__ );
468             } else
469                 tmp_se.qp_uid_valid = true;
470             hole_count = 0;
471             memcpy(&storage_arr[next_unused_elem++], &tmp_se, sizeof(tmp_se));
472             CloseHandle(fh);
473         } else {
474             err = GetLastError();
475             if ((0 == k) && (ERROR_ACCESS_DENIED == err))
476                 pr2serr("Access denied on %s, may need Administrator\n",
477                         adapter_name);
478             if (ERROR_SHARING_VIOLATION == err)
479                 pr2serr("%s: in use by other process (sharing violation "
480                         "[34])\n", adapter_name);
481             else if (verbose > 3)
482                 pr2serr("%s: CreateFile failed err=%u\n\t%s", adapter_name,
483                         (unsigned int)err, get_err_str(err, sizeof(b), b));
484             if (++hole_count >= MAX_HOLE_COUNT)
485                 break;
486         }
487     }
488     return 0;
489 }
490 
491 static int
enum_cdroms(void)492 enum_cdroms(void)
493 {
494     int k;
495     int hole_count = 0;
496     HANDLE fh;
497     DWORD err;
498     char adapter_name[64];
499     char b[256];
500     struct storage_elem tmp_se;
501 
502     if (verbose > 2)
503         pr2serr("%s: enter\n", __FUNCTION__ );
504     for (k = 0; k < MAX_CDROM_NUM; ++k) {
505         memset(&tmp_se, 0, sizeof(tmp_se));
506         snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\CDROM%d", k);
507         snprintf(tmp_se.name, sizeof(tmp_se.name), "CDROM%d", k);
508         fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE,
509                         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
510                         OPEN_EXISTING, 0, NULL);
511         if (fh != INVALID_HANDLE_VALUE) {
512             if (query_dev_property(fh, &tmp_se.qp_descriptor) < 0)
513                 pr2serr("%s: query_dev_property failed\n", __FUNCTION__ );
514             else
515                 tmp_se.qp_descriptor_valid = true;
516             if (query_dev_uid(fh, &tmp_se.qp_uid) < 0) {
517                 if (verbose > 2)
518                     pr2serr("%s: query_dev_uid failed\n", __FUNCTION__ );
519             } else
520                 tmp_se.qp_uid_valid = true;
521             hole_count = 0;
522             memcpy(&storage_arr[next_unused_elem++], &tmp_se, sizeof(tmp_se));
523             CloseHandle(fh);
524         } else {
525             err = GetLastError();
526             if (ERROR_SHARING_VIOLATION == err)
527                 pr2serr("%s: in use by other process (sharing violation "
528                         "[34])\n", adapter_name);
529             else if (verbose > 3)
530                 pr2serr("%s: CreateFile failed err=%u\n\t%s", adapter_name,
531                         (unsigned int)err, get_err_str(err, sizeof(b), b));
532             if (++hole_count >= MAX_HOLE_COUNT)
533                 break;
534         }
535     }
536     return 0;
537 }
538 
539 static int
enum_tapes(void)540 enum_tapes(void)
541 {
542     int k;
543     int hole_count = 0;
544     HANDLE fh;
545     DWORD err;
546     char adapter_name[64];
547     char b[256];
548     struct storage_elem tmp_se;
549 
550     if (verbose > 2)
551         pr2serr("%s: enter\n", __FUNCTION__ );
552     for (k = 0; k < MAX_TAPE_NUM; ++k) {
553         memset(&tmp_se, 0, sizeof(tmp_se));
554         snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\TAPE%d", k);
555         snprintf(tmp_se.name, sizeof(tmp_se.name), "TAPE%d", k);
556         fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE,
557                         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
558                         OPEN_EXISTING, 0, NULL);
559         if (fh != INVALID_HANDLE_VALUE) {
560             if (query_dev_property(fh, &tmp_se.qp_descriptor) < 0)
561                 pr2serr("%s: query_dev_property failed\n", __FUNCTION__ );
562             else
563                 tmp_se.qp_descriptor_valid = true;
564             if (query_dev_uid(fh, &tmp_se.qp_uid) < 0) {
565                 if (verbose > 2)
566                     pr2serr("%s: query_dev_uid failed\n", __FUNCTION__ );
567             } else
568                 tmp_se.qp_uid_valid = true;
569             hole_count = 0;
570             memcpy(&storage_arr[next_unused_elem++], &tmp_se, sizeof(tmp_se));
571             CloseHandle(fh);
572         } else {
573             err = GetLastError();
574             if (ERROR_SHARING_VIOLATION == err)
575                 pr2serr("%s: in use by other process (sharing violation "
576                         "[34])\n", adapter_name);
577             else if (verbose > 3)
578                 pr2serr("%s: CreateFile failed err=%u\n\t%s", adapter_name,
579                         (unsigned int)err, get_err_str(err, sizeof(b), b));
580             if (++hole_count >= MAX_HOLE_COUNT)
581                 break;
582         }
583     }
584     return 0;
585 }
586 
587 static int
sg_do_wscan(char letter,bool show_bt,int scsi_scan)588 sg_do_wscan(char letter, bool show_bt, int scsi_scan)
589 {
590     int k, j, n;
591     struct storage_elem * sp;
592 
593     if (scsi_scan < 2) {
594         k = enum_pds();
595         if (k)
596             return k;
597         k = enum_cdroms();
598         if (k)
599             return k;
600         k = enum_tapes();
601         if (k)
602             return k;
603         k = enum_volumes(letter);
604         if (k)
605             return k;
606 
607         for (k = 0; k < next_unused_elem; ++k) {
608             sp = storage_arr + k;
609             if ('\0' == sp->name[0])
610                 continue;
611             printf("%-7s ", sp->name);
612             n = strlen(sp->volume_letters);
613             if (0 == n)
614                 printf("        ");
615             else if (1 == n)
616                 printf("[%s]     ", sp->volume_letters);
617             else if (2 == n)
618                 printf("[%s]    ", sp->volume_letters);
619             else if (3 == n)
620                 printf("[%s]   ", sp->volume_letters);
621             else if (4 == n)
622                 printf("[%s]  ", sp->volume_letters);
623             else
624                 printf("[%4s+] ", sp->volume_letters);
625             if (sp->qp_descriptor_valid) {
626                 if (show_bt)
627                     printf("<%s>  ",
628                            get_bus_type(sp->qp_descriptor.desc.BusType));
629                 j = sp->qp_descriptor.desc.VendorIdOffset;
630                 if (j > 0)
631                     printf("%s  ", sp->qp_descriptor.raw + j);
632                 j = sp->qp_descriptor.desc.ProductIdOffset;
633                 if (j > 0)
634                     printf("%s  ", sp->qp_descriptor.raw + j);
635                 j = sp->qp_descriptor.desc.ProductRevisionOffset;
636                 if (j > 0)
637                     printf("%s  ", sp->qp_descriptor.raw + j);
638                 j = sp->qp_descriptor.desc.SerialNumberOffset;
639                 if (j > 0)
640                     printf("%s", sp->qp_descriptor.raw + j);
641                 printf("\n");
642                 if (verbose > 2)
643                     hex2stderr((const uint8_t *)sp->qp_descriptor.raw, 144, 0);
644             } else
645                 printf("\n");
646             if ((verbose > 3) && sp->qp_uid_valid) {
647                 printf("  UID valid, in hex:\n");
648                 hex2stderr((const uint8_t *)sp->qp_uid.raw,
649                            sizeof(sp->qp_uid.raw), 0);
650             }
651         }
652     }
653 
654     if (scsi_scan) {
655         if (scsi_scan < 2)
656             printf("\n");
657         enum_scsi_adapters();
658     }
659     return 0;
660 }
661 
662 
663 int
main(int argc,char * argv[])664 main(int argc, char * argv[])
665 {
666     bool show_bt = false;
667     int c, ret;
668     int vol_letter = 0;
669     int scsi_scan = 0;
670 
671     while (1) {
672         int option_index = 0;
673 
674         c = getopt_long(argc, argv, "bhHl:svV", long_options,
675                         &option_index);
676         if (c == -1)
677             break;
678 
679         switch (c) {
680         case 'b':
681             show_bt = true;
682             break;
683         case 'h':
684         case '?':
685             usage();
686             return 0;
687         case 'l':
688             vol_letter = toupper(optarg[0]);
689             if ((vol_letter < 'C') || (vol_letter > 'Z')) {
690                 pr2serr("'--letter=' expects a letter in the 'C' to 'Z' "
691                         "range\n");
692                 usage();
693                 return SG_LIB_SYNTAX_ERROR;
694             }
695             break;
696         case 's':
697             ++scsi_scan;
698             break;
699         case 'v':
700             ++verbose;
701             break;
702         case 'V':
703             pr2serr("version: %s\n", version_str);
704             return 0;
705         default:
706             pr2serr("unrecognised option code 0x%x ??\n", c);
707             usage();
708             return SG_LIB_SYNTAX_ERROR;
709         }
710     }
711     if (optind < argc) {
712         if (optind < argc) {
713             for (; optind < argc; ++optind)
714                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
715             usage();
716             return SG_LIB_SYNTAX_ERROR;
717         }
718     }
719 
720     storage_arr = (struct storage_elem *)
721                   sg_memalign(sizeof(struct storage_elem) * MAX_SCSI_ELEMS, 0,
722                               &free_storage_arr, false);
723     if (storage_arr) {
724         ret = sg_do_wscan(vol_letter, show_bt, scsi_scan);
725         if (free_storage_arr)
726             free(free_storage_arr);
727     } else {
728         pr2serr("Failed to allocate storage_arr (%d bytes) on heap\n",
729                 (int)(sizeof(struct storage_elem) * MAX_SCSI_ELEMS));
730         ret = sg_convert_errno(ENOMEM);
731     }
732     return ret;
733 }
734