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