1 /*
2 * Copyright (c) 2005-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 /* A utility program for the Linux OS SCSI subsystem.
11 *
12 *
13 * This program maps a primary SCSI device node name to the corresponding
14 * SCSI generic device node name (or vice versa). Targets Linux
15 * kernel 2.6, 3 and 4 series. Sysfs device names can also be mapped.
16 */
17
18 /* #define _XOPEN_SOURCE 500 */
19 /* needed to see DT_REG and friends when compiled with: c99 pedantic */
20 #ifndef _GNU_SOURCE
21 #define _GNU_SOURCE 1
22 #endif
23
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <stdbool.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <errno.h>
33 #include <limits.h>
34 #include <getopt.h>
35 #include <dirent.h>
36 #include <libgen.h>
37 #include <sys/ioctl.h>
38 #include <sys/stat.h>
39 #include <sys/sysmacros.h> /* new location for major + minor */
40 #ifndef major
41 #include <sys/types.h>
42 #endif
43 #include <linux/major.h>
44
45 #ifdef HAVE_CONFIG_H
46 #include "config.h"
47 #endif
48 #include "sg_lib.h"
49
50 static const char * version_str = "1.19 20220117";
51
52 #define ME "sg_map26: "
53
54 #define NT_NO_MATCH 0
55 #define NT_SD 1
56 #define NT_SR 2
57 #define NT_HD 3
58 #define NT_ST 4
59 #define NT_OSST 5
60 #define NT_SG 6
61 #define NT_CH 7
62 #define NT_REG 8
63 #define NT_DIR 9
64
65 #define NAME_LEN_MAX 256
66 #define D_NAME_LEN_MAX 520
67
68 #ifndef SCSI_CHANGER_MAJOR
69 #define SCSI_CHANGER_MAJOR 86
70 #endif
71 #ifndef OSST_MAJOR
72 #define OSST_MAJOR 206
73 #endif
74
75 /* scandir() and stat() categories */
76 #define FT_OTHER 0
77 #define FT_REGULAR 1
78 #define FT_BLOCK 2
79 #define FT_CHAR 3
80 #define FT_DIR 4
81
82 /* older major.h headers may not have these */
83 #ifndef SCSI_DISK8_MAJOR
84 #define SCSI_DISK8_MAJOR 128
85 #define SCSI_DISK9_MAJOR 129
86 #define SCSI_DISK10_MAJOR 130
87 #define SCSI_DISK11_MAJOR 131
88 #define SCSI_DISK12_MAJOR 132
89 #define SCSI_DISK13_MAJOR 133
90 #define SCSI_DISK14_MAJOR 134
91 #define SCSI_DISK15_MAJOR 135
92 #endif
93
94 /* st minor decodes from Kai Makisara 20081008 */
95 #define ST_NBR_MODE_BITS 2
96 #define ST_MODE_SHIFT (7 - ST_NBR_MODE_BITS)
97 #define TAPE_NR(minor) ( (((minor) & ~255) >> (ST_NBR_MODE_BITS + 1)) | \
98 ((minor) & ~(UINT_MAX << ST_MODE_SHIFT)) )
99
100 static const char * sys_sg_dir = "/sys/class/scsi_generic/";
101 static const char * sys_sd_dir = "/sys/block/";
102 static const char * sys_sr_dir = "/sys/block/";
103 static const char * sys_hd_dir = "/sys/block/";
104 static const char * sys_st_dir = "/sys/class/scsi_tape/";
105 static const char * sys_sch_dir = "/sys/class/scsi_changer/";
106 static const char * sys_osst_dir = "/sys/class/onstream_tape/";
107 static const char * def_dev_dir = "/dev";
108
109
110 static struct option long_options[] = {
111 {"dev_dir", required_argument, 0, 'd'},
112 {"given_is", required_argument, 0, 'g'},
113 {"help", no_argument, 0, 'h'},
114 {"result", required_argument, 0, 'r'},
115 {"symlink", no_argument, 0, 's'},
116 {"verbose", no_argument, 0, 'v'},
117 {"version", no_argument, 0, 'V'},
118 {0, 0, 0, 0},
119 };
120
121 static const char * nt_names[] = {
122 "No matching",
123 "disk",
124 "cd/dvd",
125 "hd",
126 "tape",
127 "tape (osst)",
128 "generic (sg)",
129 "changer",
130 "regular file",
131 "directory",
132 };
133
134 #if defined(__GNUC__) || defined(__clang__)
135 static int pr2serr(const char * fmt, ...)
136 __attribute__ ((format (printf, 1, 2)));
137 #else
138 static int pr2serr(const char * fmt, ...);
139 #endif
140
141
142 static int
pr2serr(const char * fmt,...)143 pr2serr(const char * fmt, ...)
144 {
145 va_list args;
146 int n;
147
148 va_start(args, fmt);
149 n = vfprintf(stderr, fmt, args);
150 va_end(args);
151 return n;
152 }
153
154 static void
usage()155 usage()
156 {
157 pr2serr("Usage: sg_map26 [--dev_dir=DIR] [--given_is=0...1] [--help] "
158 "[--result=0...3]\n"
159 " [--symlink] [--verbose] [--version] "
160 "DEVICE\n"
161 " where:\n"
162 " --dev_dir=DIR | -d DIR search in DIR for "
163 "resulting special\n"
164 " (def: directory of DEVICE "
165 "or '/dev')\n"
166 " --given_is=0...1 | -g 0...1 variety of given "
167 "DEVICE\n"
168 " 0->block or char special "
169 "(or symlink to)\n"
170 " 1->sysfs device, 'dev' or "
171 "parent\n"
172 " --help | -h print out usage message\n"
173 " --result=0...3 | -r 0...3 variety of file(s) to "
174 "find\n"
175 " 0->mapped block or char "
176 "special(def)\n"
177 " 1->mapped sysfs path\n"
178 " 2->matching block or "
179 "char special\n"
180 " 3->matching sysfs "
181 "path\n"
182 " --symlink | -s symlinks to special included in "
183 "result\n"
184 " --verbose | -v increase verbosity of output\n"
185 " --version | -V print version string and exit\n\n"
186 "Maps SCSI device node to corresponding generic node (and "
187 "vv)\n"
188 );
189 }
190
191
192 /* ssafe_strerror() contributed by Clayton Weaver <cgweav at email dot com>
193 Allows for situation in which strerror() is given a wild value (or the
194 C library is incomplete) and returns NULL. Still not thread safe.
195 */
196
197 static char safe_errbuf[64] = {'u', 'n', 'k', 'n', 'o', 'w', 'n', ' ',
198 'e', 'r', 'r', 'n', 'o', ':', ' ', 0};
199
200 static char *
ssafe_strerror(int errnum)201 ssafe_strerror(int errnum)
202 {
203 size_t len;
204 char * errstr;
205
206 errstr = strerror(errnum);
207 if (NULL == errstr) {
208 len = strlen(safe_errbuf);
209 snprintf(safe_errbuf + len, sizeof(safe_errbuf) - len, "%i",
210 errnum);
211 safe_errbuf[sizeof(safe_errbuf) - 1] = '\0'; /* bombproof */
212 return safe_errbuf;
213 }
214 return errstr;
215 }
216
217 static int
nt_typ_from_filename(const char * filename,int * majj,int * minn)218 nt_typ_from_filename(const char * filename, int * majj, int * minn)
219 {
220 struct stat st;
221 int ma, mi;
222
223 if (stat(filename, &st) < 0)
224 return -errno;
225 ma = major(st.st_rdev);
226 mi = minor(st.st_rdev);
227 if (majj)
228 *majj = ma;
229 if (minn)
230 *minn = mi;
231 if (S_ISCHR(st.st_mode)) {
232 switch(ma) {
233 case OSST_MAJOR:
234 return NT_OSST;
235 case SCSI_GENERIC_MAJOR:
236 return NT_SG;
237 case SCSI_TAPE_MAJOR:
238 return NT_ST;
239 case SCSI_CHANGER_MAJOR:
240 return NT_CH;
241 default:
242 return NT_NO_MATCH;
243 }
244 } else if (S_ISBLK(st.st_mode)) {
245 switch(ma) {
246 case SCSI_DISK0_MAJOR: case SCSI_DISK1_MAJOR:
247 case SCSI_DISK2_MAJOR: case SCSI_DISK3_MAJOR:
248 case SCSI_DISK4_MAJOR: case SCSI_DISK5_MAJOR:
249 case SCSI_DISK6_MAJOR: case SCSI_DISK7_MAJOR:
250 case SCSI_DISK8_MAJOR: case SCSI_DISK9_MAJOR:
251 case SCSI_DISK10_MAJOR: case SCSI_DISK11_MAJOR:
252 case SCSI_DISK12_MAJOR: case SCSI_DISK13_MAJOR:
253 case SCSI_DISK14_MAJOR: case SCSI_DISK15_MAJOR:
254 return NT_SD;
255 case SCSI_CDROM_MAJOR:
256 return NT_SR;
257 case IDE0_MAJOR: case IDE1_MAJOR:
258 case IDE2_MAJOR: case IDE3_MAJOR:
259 case IDE4_MAJOR: case IDE5_MAJOR:
260 case IDE6_MAJOR: case IDE7_MAJOR:
261 case IDE8_MAJOR: case IDE9_MAJOR:
262 return NT_HD;
263 default:
264 return NT_NO_MATCH;
265 }
266 } else if (S_ISREG(st.st_mode))
267 return NT_REG;
268 else if (S_ISDIR(st.st_mode))
269 return NT_DIR;
270 return NT_NO_MATCH;
271 }
272
273 static int
nt_typ_from_major(int ma)274 nt_typ_from_major(int ma)
275 {
276 switch(ma) {
277 case SCSI_DISK0_MAJOR: case SCSI_DISK1_MAJOR:
278 case SCSI_DISK2_MAJOR: case SCSI_DISK3_MAJOR:
279 case SCSI_DISK4_MAJOR: case SCSI_DISK5_MAJOR:
280 case SCSI_DISK6_MAJOR: case SCSI_DISK7_MAJOR:
281 case SCSI_DISK8_MAJOR: case SCSI_DISK9_MAJOR:
282 case SCSI_DISK10_MAJOR: case SCSI_DISK11_MAJOR:
283 case SCSI_DISK12_MAJOR: case SCSI_DISK13_MAJOR:
284 case SCSI_DISK14_MAJOR: case SCSI_DISK15_MAJOR:
285 return NT_SD;
286 case SCSI_CDROM_MAJOR:
287 return NT_SR;
288 case IDE0_MAJOR: case IDE1_MAJOR:
289 case IDE2_MAJOR: case IDE3_MAJOR:
290 case IDE4_MAJOR: case IDE5_MAJOR:
291 case IDE6_MAJOR: case IDE7_MAJOR:
292 case IDE8_MAJOR: case IDE9_MAJOR:
293 return NT_HD;
294 case OSST_MAJOR:
295 return NT_OSST;
296 case SCSI_GENERIC_MAJOR:
297 return NT_SG;
298 case SCSI_TAPE_MAJOR:
299 return NT_ST;
300 case SCSI_CHANGER_MAJOR:
301 return NT_CH;
302 default:
303 return NT_NO_MATCH;
304 }
305 return NT_NO_MATCH;
306 }
307
308
309 struct node_match_item {
310 bool follow_symlink;
311 int file_type;
312 int majj;
313 int minn;
314 char dir_name[D_NAME_LEN_MAX];
315 };
316
317 static struct node_match_item nd_match;
318
319 static int
nd_match_scandir_select(const struct dirent * s)320 nd_match_scandir_select(const struct dirent * s)
321 {
322 bool symlnk = false;
323 struct stat st;
324 char name[D_NAME_LEN_MAX];
325
326 switch (s->d_type) {
327 case DT_BLK:
328 if (FT_BLOCK != nd_match.file_type)
329 return 0;
330 break;
331 case DT_CHR:
332 if (FT_CHAR != nd_match.file_type)
333 return 0;
334 break;
335 case DT_DIR:
336 return (FT_DIR == nd_match.file_type) ? 1 : 0;
337 case DT_REG:
338 return (FT_REGULAR == nd_match.file_type) ? 1 : 0;
339 case DT_LNK: /* follow symlinks */
340 if (! nd_match.follow_symlink)
341 return 0;
342 symlnk = true;
343 break;
344 default:
345 return 0;
346 }
347 if ((! symlnk) && (-1 == nd_match.majj) && (-1 == nd_match.minn))
348 return 1;
349 snprintf(name, sizeof(name), "%.*s/%.*s", NAME_LEN_MAX,
350 nd_match.dir_name, NAME_LEN_MAX, s->d_name);
351 memset(&st, 0, sizeof(st));
352 if (stat(name, &st) < 0)
353 return 0;
354 if (symlnk) {
355 if (S_ISCHR(st.st_mode)) {
356 if (FT_CHAR != nd_match.file_type)
357 return 0;
358 } else if (S_ISBLK(st.st_mode)) {
359 if (FT_BLOCK != nd_match.file_type)
360 return 0;
361 } else
362 return 0;
363 }
364 return (((-1 == nd_match.majj) ||
365 ((unsigned)major(st.st_rdev) == (unsigned)nd_match.majj)) &&
366 ((-1 == nd_match.minn) ||
367 ((unsigned)minor(st.st_rdev) == (unsigned)nd_match.minn)))
368 ? 1 : 0;
369 }
370
371 static int
list_matching_nodes(const char * dir_name,int file_type,int majj,int minn,bool follow_symlink,int verbose)372 list_matching_nodes(const char * dir_name, int file_type, int majj, int minn,
373 bool follow_symlink, int verbose)
374 {
375 struct dirent ** namelist;
376 int num, k;
377
378 strncpy(nd_match.dir_name, dir_name, D_NAME_LEN_MAX - 1);
379 nd_match.file_type = file_type;
380 nd_match.majj = majj;
381 nd_match.minn = minn;
382 nd_match.follow_symlink = follow_symlink;
383 num = scandir(dir_name, &namelist, nd_match_scandir_select, NULL);
384 if (num < 0) {
385 if (verbose)
386 pr2serr("scandir: %s %s\n", dir_name,
387 ssafe_strerror(errno));
388 return -errno;
389 }
390 for (k = 0; k < num; ++k) {
391 printf("%s/%s\n", dir_name, namelist[k]->d_name);
392 free(namelist[k]);
393 }
394 free(namelist);
395 return num;
396 }
397
398 struct sg_item_t {
399 char name[NAME_LEN_MAX + 2];
400 int ft;
401 int nt;
402 int d_type;
403 };
404
405 static struct sg_item_t for_first;
406
407 static int
first_scandir_select(const struct dirent * s)408 first_scandir_select(const struct dirent * s)
409 {
410 if (FT_OTHER != for_first.ft)
411 return 0;
412 if ((DT_LNK != s->d_type) &&
413 ((DT_DIR != s->d_type) || ('.' == s->d_name[0])))
414 return 0;
415 strncpy(for_first.name, s->d_name, NAME_LEN_MAX);
416 for_first.ft = FT_CHAR; /* dummy */
417 for_first.d_type = s->d_type;
418 return 1;
419 }
420
421 /* scan for directory entry that is either a symlink or a directory */
422 static int
scan_for_first(const char * dir_name,int verbose)423 scan_for_first(const char * dir_name, int verbose)
424 {
425 char name[NAME_LEN_MAX];
426 struct dirent ** namelist;
427 int num, k;
428
429 for_first.ft = FT_OTHER;
430 num = scandir(dir_name, &namelist, first_scandir_select, NULL);
431 if (num < 0) {
432 if (verbose > 0) {
433 snprintf(name, NAME_LEN_MAX, "scandir: %s", dir_name);
434 perror(name);
435 }
436 return -1;
437 }
438 for (k = 0; k < num; ++k)
439 free(namelist[k]);
440 free(namelist);
441 return num;
442 }
443
444 static struct sg_item_t from_sg;
445
446 static int
from_sg_scandir_select(const struct dirent * s)447 from_sg_scandir_select(const struct dirent * s)
448 {
449 int len;
450
451 if (FT_OTHER != from_sg.ft)
452 return 0;
453 if ((DT_LNK != s->d_type) &&
454 ((DT_DIR != s->d_type) || ('.' == s->d_name[0])))
455 return 0;
456 from_sg.d_type = s->d_type;
457 if (0 == strncmp("scsi_changer", s->d_name, 12)) {
458 strncpy(from_sg.name, s->d_name, NAME_LEN_MAX);
459 from_sg.ft = FT_CHAR;
460 from_sg.nt = NT_CH;
461 return 1;
462 } else if (0 == strncmp("block", s->d_name, 5)) {
463 strncpy(from_sg.name, s->d_name, NAME_LEN_MAX);
464 from_sg.ft = FT_BLOCK;
465 return 1;
466 } else if (0 == strcmp("tape", s->d_name)) {
467 strcpy(from_sg.name, s->d_name);
468 from_sg.ft = FT_CHAR;
469 from_sg.nt = NT_ST;
470 return 1;
471 } else if (0 == strncmp("scsi_tape:st", s->d_name, 12)) {
472 len = strlen(s->d_name);
473 if (isdigit(s->d_name[len - 1])) {
474 /* want 'st<num>' symlink only */
475 strcpy(from_sg.name, s->d_name);
476 from_sg.ft = FT_CHAR;
477 from_sg.nt = NT_ST;
478 return 1;
479 } else
480 return 0;
481 } else if (0 == strncmp("onstream_tape:os", s->d_name, 16)) {
482 strcpy(from_sg.name, s->d_name);
483 from_sg.ft = FT_CHAR;
484 from_sg.nt = NT_OSST;
485 return 1;
486 } else
487 return 0;
488 }
489
490 static int
from_sg_scan(const char * dir_name,int verbose)491 from_sg_scan(const char * dir_name, int verbose)
492 {
493 struct dirent ** namelist;
494 int num, k;
495
496 from_sg.ft = FT_OTHER;
497 from_sg.nt = NT_NO_MATCH;
498 num = scandir(dir_name, &namelist, from_sg_scandir_select, NULL);
499 if (num < 0) {
500 if (verbose)
501 pr2serr("scandir: %s %s\n", dir_name,
502 ssafe_strerror(errno));
503 return -errno;
504 }
505 if (verbose) {
506 for (k = 0; k < num; ++k)
507 pr2serr(" %s/%s\n", dir_name,
508 namelist[k]->d_name);
509 }
510 for (k = 0; k < num; ++k)
511 free(namelist[k]);
512 free(namelist);
513 return num;
514 }
515
516 static struct sg_item_t to_sg;
517
518 static int
to_sg_scandir_select(const struct dirent * s)519 to_sg_scandir_select(const struct dirent * s)
520 {
521 if (FT_OTHER != to_sg.ft)
522 return 0;
523 if (DT_LNK != s->d_type)
524 return 0;
525 if (0 == strncmp("scsi_generic", s->d_name, 12)) {
526 strncpy(to_sg.name, s->d_name, NAME_LEN_MAX);
527 to_sg.ft = FT_CHAR;
528 to_sg.nt = NT_SG;
529 return 1;
530 } else
531 return 0;
532 }
533
534 static int
to_sg_scan(const char * dir_name)535 to_sg_scan(const char * dir_name)
536 {
537 struct dirent ** namelist;
538 int num, k;
539
540 to_sg.ft = FT_OTHER;
541 to_sg.nt = NT_NO_MATCH;
542 num = scandir(dir_name, &namelist, to_sg_scandir_select, NULL);
543 if (num < 0)
544 return -errno;
545 for (k = 0; k < num; ++k)
546 free(namelist[k]);
547 free(namelist);
548 return num;
549 }
550
551 /* Return 1 if directory, else 0 */
552 static int
if_directory_chdir(const char * dir_name,const char * base_name)553 if_directory_chdir(const char * dir_name, const char * base_name)
554 {
555 char buff[D_NAME_LEN_MAX];
556 struct stat a_stat;
557
558 strcpy(buff, dir_name);
559 strcat(buff, "/");
560 strcat(buff, base_name);
561 if (stat(buff, &a_stat) < 0)
562 return 0;
563 if (S_ISDIR(a_stat.st_mode)) {
564 if (chdir(buff) < 0)
565 return 0;
566 return 1;
567 }
568 return 0;
569 }
570
571 /* Return 1 if directory, else 0 */
572 static int
if_directory_ch2generic(const char * dir_name)573 if_directory_ch2generic(const char * dir_name)
574 {
575 char buff[NAME_LEN_MAX];
576 struct stat a_stat;
577 const char * old_name = "generic";
578
579 strcpy(buff, dir_name);
580 strcat(buff, "/");
581 strcat(buff, old_name);
582 if ((stat(buff, &a_stat) >= 0) && S_ISDIR(a_stat.st_mode)) {
583 if (chdir(buff) < 0)
584 return 0;
585 return 1;
586 }
587 /* No "generic", so now look for "scsi_generic:sg<n>" */
588 if (1 != to_sg_scan(dir_name))
589 return 0;
590 strcpy(buff, dir_name);
591 strcat(buff, "/");
592 strcat(buff, to_sg.name);
593 if (stat(buff, &a_stat) < 0)
594 return 0;
595 if (S_ISDIR(a_stat.st_mode)) {
596 if (chdir(buff) < 0)
597 return 0;
598 return 1;
599 }
600 return 0;
601 }
602
603 /* Return 1 if found, else 0 if problems */
604 static int
get_value(const char * dir_name,const char * base_name,char * value,int max_value_len)605 get_value(const char * dir_name, const char * base_name, char * value,
606 int max_value_len)
607 {
608 char buff[D_NAME_LEN_MAX];
609 FILE * f;
610 int len;
611
612 if ((NULL == dir_name) && (NULL == base_name))
613 return 0;
614 if (dir_name) {
615 strcpy(buff, dir_name);
616 if (base_name && (strlen(base_name) > 0)) {
617 strcat(buff, "/");
618 strcat(buff, base_name);
619 }
620 } else
621 strcpy(buff, base_name);
622 if (NULL == (f = fopen(buff, "r"))) {
623 return 0;
624 }
625 if (NULL == fgets(value, max_value_len, f)) {
626 fclose(f);
627 return 0;
628 }
629 len = strlen(value);
630 if ((len > 0) && (value[len - 1] == '\n'))
631 value[len - 1] = '\0';
632 fclose(f);
633 return 1;
634 }
635
636 static int
map_hd(const char * device_dir,int ma,int mi,int result,bool follow_symlink,int verbose)637 map_hd(const char * device_dir, int ma, int mi, int result,
638 bool follow_symlink, int verbose)
639 {
640 char c, num;
641
642 if (2 == result) {
643 num = list_matching_nodes(device_dir, FT_BLOCK,
644 ma, mi, follow_symlink,
645 verbose);
646 return (num > 0) ? 0 : 1;
647 }
648 switch (ma) {
649 case IDE0_MAJOR: c = 'a'; break;
650 case IDE1_MAJOR: c = 'c'; break;
651 case IDE2_MAJOR: c = 'e'; break;
652 case IDE3_MAJOR: c = 'g'; break;
653 case IDE4_MAJOR: c = 'i'; break;
654 case IDE5_MAJOR: c = 'k'; break;
655 case IDE6_MAJOR: c = 'm'; break;
656 case IDE7_MAJOR: c = 'o'; break;
657 case IDE8_MAJOR: c = 'q'; break;
658 case IDE9_MAJOR: c = 's'; break;
659 default: c = '?'; break;
660 }
661 if (mi > 63)
662 ++c;
663 printf("%shd%c\n", sys_hd_dir, c);
664 return 0;
665 }
666
667 static int
map_sd(const char * device_name,const char * device_dir,int ma,int mi,int result,bool follow_symlink,int verbose)668 map_sd(const char * device_name, const char * device_dir, int ma, int mi,
669 int result, bool follow_symlink, int verbose)
670 {
671 int index, m_mi, m_ma, num;
672 char value[D_NAME_LEN_MAX];
673 char name[D_NAME_LEN_MAX];
674
675 if (2 == result) {
676 num = list_matching_nodes(device_dir, FT_BLOCK, ma, mi,
677 follow_symlink, verbose);
678 return (num > 0) ? 0 : 1;
679 }
680 if (SCSI_DISK0_MAJOR == ma)
681 index = mi / 16;
682 else if (ma >= SCSI_DISK8_MAJOR)
683 index = (mi / 16) + 128 +
684 ((ma - SCSI_DISK8_MAJOR) * 16);
685 else
686 index = (mi / 16) + 16 +
687 ((ma - SCSI_DISK1_MAJOR) * 16);
688 if (index < 26)
689 snprintf(name, sizeof(name), "%ssd%c",
690 sys_sd_dir, 'a' + index % 26);
691 else if (index < (26 + 1) * 26)
692 snprintf(name, sizeof(name), "%ssd%c%c",
693 sys_sd_dir,
694 'a' + index / 26 - 1,'a' + index % 26);
695 else {
696 const unsigned int m1 = (index / 26 - 1) / 26 - 1;
697 const unsigned int m2 = (index / 26 - 1) % 26;
698 const unsigned int m3 = index % 26;
699
700 snprintf(name, sizeof(name), "%ssd%c%c%c",
701 sys_sd_dir, 'a' + m1, 'a' + m2, 'a' + m3);
702 }
703 if (3 == result) {
704 printf("%s\n", name);
705 return 0;
706 }
707 if (! get_value(name, "dev", value, sizeof(value))) {
708 pr2serr("Couldn't find sysfs match for device: %s\n",
709 device_name);
710 return 1;
711 }
712 if (verbose)
713 pr2serr("sysfs sd dev: %s\n", value);
714 if (! if_directory_chdir(name, "device")) {
715 pr2serr("sysfs problem with device: %s\n", device_name);
716 return 1;
717 }
718 if (if_directory_ch2generic(".")) {
719 if (1 == result) {
720 if (NULL == getcwd(value, sizeof(value)))
721 value[0] = '\0';
722 printf("%s\n", value);
723 return 0;
724 }
725 if (! get_value(".", "dev", value, sizeof(value))) {
726 pr2serr("Couldn't find sysfs generic dev\n");
727 return 1;
728 }
729 if (verbose)
730 printf("matching dev: %s\n", value);
731 if (2 != sscanf(value, "%d:%d", &m_ma, &m_mi)) {
732 pr2serr("Couldn't decode mapped dev\n");
733 return 1;
734 }
735 num = list_matching_nodes(device_dir, FT_CHAR, m_ma, m_mi,
736 follow_symlink, verbose);
737 return (num > 0) ? 0 : 1;
738 } else {
739 pr2serr("sd device: %s does not match any SCSI generic "
740 "device\n", device_name);
741 pr2serr(" perhaps sg module is not loaded\n");
742 return 1;
743 }
744 }
745
746 static int
map_sr(const char * device_name,const char * device_dir,int ma,int mi,int result,bool follow_symlink,int verbose)747 map_sr(const char * device_name, const char * device_dir, int ma, int mi,
748 int result, bool follow_symlink, int verbose)
749 {
750 int m_mi, m_ma, num;
751 char value[D_NAME_LEN_MAX];
752 char name[D_NAME_LEN_MAX];
753
754 if (2 == result) {
755 num = list_matching_nodes(device_dir, FT_BLOCK, ma, mi,
756 follow_symlink, verbose);
757 return (num > 0) ? 0 : 1;
758 }
759 snprintf(name, sizeof(name), "%ssr%d", sys_sr_dir, mi);
760 if (3 == result) {
761 printf("%s\n", name);
762 return 0;
763 }
764 if (! get_value(name, "dev", value, sizeof(value))) {
765 pr2serr("Couldn't find sysfs match for device: %s\n",
766 device_name);
767 return 1;
768 }
769 if (verbose)
770 pr2serr("sysfs sr dev: %s\n", value);
771 if (! if_directory_chdir(name, "device")) {
772 pr2serr("sysfs problem with device: %s\n", device_name);
773 return 1;
774 }
775 if (if_directory_ch2generic(".")) {
776 if (1 == result) {
777 if (NULL == getcwd(value, sizeof(value)))
778 value[0] = '\0';
779 printf("%s\n", value);
780 return 0;
781 }
782 if (! get_value(".", "dev", value, sizeof(value))) {
783 pr2serr("Couldn't find sysfs generic dev\n");
784 return 1;
785 }
786 if (verbose)
787 printf("matching dev: %s\n", value);
788 if (2 != sscanf(value, "%d:%d", &m_ma, &m_mi)) {
789 pr2serr("Couldn't decode mapped dev\n");
790 return 1;
791 }
792 num = list_matching_nodes(device_dir, FT_BLOCK, m_ma, m_mi,
793 follow_symlink, verbose);
794 return (num > 0) ? 0 : 1;
795 } else {
796 pr2serr("sr device: %s does not match any SCSI generic "
797 "device\n", device_name);
798 pr2serr(" perhaps sg module is not loaded\n");
799 return 1;
800 }
801 }
802
803 static int
map_st(const char * device_name,const char * device_dir,int ma,int mi,int result,bool follow_symlink,int verbose)804 map_st(const char * device_name, const char * device_dir, int ma, int mi,
805 int result, bool follow_symlink, int verbose)
806 {
807 int m_mi, m_ma, num;
808 char value[D_NAME_LEN_MAX];
809 char name[D_NAME_LEN_MAX];
810
811 if (2 == result) {
812 num = list_matching_nodes(device_dir, FT_CHAR, ma, mi,
813 follow_symlink, verbose);
814 return (num > 0) ? 0 : 1;
815 }
816 snprintf(name, sizeof(name), "%sst%d", sys_st_dir,
817 TAPE_NR(mi));
818 if (3 == result) {
819 printf("%s\n", name);
820 return 0;
821 }
822 if (! get_value(name, "dev", value, sizeof(value))) {
823 pr2serr("Couldn't find sysfs match for device: %s\n",
824 device_name);
825 return 1;
826 }
827 if (verbose)
828 pr2serr("sysfs st dev: %s\n", value);
829 if (! if_directory_chdir(name, "device")) {
830 pr2serr("sysfs problem with device: %s\n", device_name);
831 return 1;
832 }
833 if (if_directory_ch2generic(".")) {
834 if (1 == result) {
835 if (NULL == getcwd(value, sizeof(value)))
836 value[0] = '\0';
837 printf("%s\n", value);
838 return 0;
839 }
840 if (! get_value(".", "dev", value, sizeof(value))) {
841 pr2serr("Couldn't find sysfs generic dev\n");
842 return 1;
843 }
844 if (verbose)
845 printf("matching dev: %s\n", value);
846 if (2 != sscanf(value, "%d:%d", &m_ma, &m_mi)) {
847 pr2serr("Couldn't decode mapped dev\n");
848 return 1;
849 }
850 num = list_matching_nodes(device_dir, FT_CHAR, m_ma, m_mi,
851 follow_symlink, verbose);
852 return (num > 0) ? 0 : 1;
853 } else {
854 pr2serr("st device: %s does not match any SCSI generic "
855 "device\n", device_name);
856 pr2serr(" perhaps sg module is not loaded\n");
857 return 1;
858 }
859 }
860
861 static int
map_osst(const char * device_name,const char * device_dir,int ma,int mi,int result,bool follow_symlink,int verbose)862 map_osst(const char * device_name, const char * device_dir, int ma, int mi,
863 int result, bool follow_symlink, int verbose)
864 {
865 int m_mi, m_ma, num;
866 char value[D_NAME_LEN_MAX];
867 char name[D_NAME_LEN_MAX];
868
869 if (2 == result) {
870 num = list_matching_nodes(device_dir, FT_CHAR, ma, mi,
871 follow_symlink, verbose);
872 return (num > 0) ? 0 : 1;
873 }
874 snprintf(name, sizeof(name), "%sosst%d", sys_osst_dir,
875 TAPE_NR(mi));
876 if (3 == result) {
877 printf("%s\n", name);
878 return 0;
879 }
880 if (! get_value(name, "dev", value, sizeof(value))) {
881 pr2serr("Couldn't find sysfs match for device: %s\n",
882 device_name);
883 return 1;
884 }
885 if (verbose)
886 pr2serr("sysfs osst dev: %s\n", value);
887 if (! if_directory_chdir(name, "device")) {
888 pr2serr("sysfs problem with device: %s\n", device_name);
889 return 1;
890 }
891 if (if_directory_ch2generic(".")) {
892 if (1 == result) {
893 if (NULL == getcwd(value, sizeof(value)))
894 value[0] = '\0';
895 printf("%s\n", value);
896 return 0;
897 }
898 if (! get_value(".", "dev", value, sizeof(value))) {
899 pr2serr("Couldn't find sysfs generic dev\n");
900 return 1;
901 }
902 if (verbose)
903 printf("matching dev: %s\n", value);
904 if (2 != sscanf(value, "%d:%d", &m_ma, &m_mi)) {
905 pr2serr("Couldn't decode mapped dev\n");
906 return 1;
907 }
908 num = list_matching_nodes(device_dir, FT_CHAR, m_ma, m_mi,
909 follow_symlink, verbose);
910 return (num > 0) ? 0 : 1;
911 } else {
912 pr2serr("osst device: %s does not match any SCSI generic "
913 "device\n", device_name);
914 pr2serr(" perhaps sg module is not loaded\n");
915 return 1;
916 }
917 }
918
919 static int
map_ch(const char * device_name,const char * device_dir,int ma,int mi,int result,bool follow_symlink,int verbose)920 map_ch(const char * device_name, const char * device_dir, int ma, int mi,
921 int result, bool follow_symlink, int verbose)
922 {
923 int m_mi, m_ma, num;
924 char value[D_NAME_LEN_MAX];
925 char name[D_NAME_LEN_MAX];
926
927 if (2 == result) {
928 num = list_matching_nodes(device_dir, FT_CHAR, ma, mi,
929 follow_symlink, verbose);
930 return (num > 0) ? 0 : 1;
931 }
932 snprintf(name, sizeof(name), "%ssch%d", sys_sch_dir, mi);
933 if (3 == result) {
934 printf("%s\n", name);
935 return 0;
936 }
937 if (! get_value(name, "dev", value, sizeof(value))) {
938 pr2serr("Couldn't find sysfs match for device: %s\n",
939 device_name);
940 return 1;
941 }
942 if (verbose)
943 pr2serr("sysfs sch dev: %s\n", value);
944 if (! if_directory_chdir(name, "device")) {
945 pr2serr("sysfs problem with device: %s\n", device_name);
946 return 1;
947 }
948 if (if_directory_ch2generic(".")) {
949 if (1 == result) {
950 if (NULL == getcwd(value, sizeof(value)))
951 value[0] = '\0';
952 printf("%s\n", value);
953 return 0;
954 }
955 if (! get_value(".", "dev", value, sizeof(value))) {
956 pr2serr("Couldn't find sysfs generic dev\n");
957 return 1;
958 }
959 if (verbose)
960 printf("matching dev: %s\n", value);
961 if (2 != sscanf(value, "%d:%d", &m_ma, &m_mi)) {
962 pr2serr("Couldn't decode mapped dev\n");
963 return 1;
964 }
965 num = list_matching_nodes(device_dir, FT_CHAR, m_ma, m_mi,
966 follow_symlink, verbose);
967 return (num > 0) ? 0 : 1;
968 } else {
969 pr2serr("sch device: %s does not match any SCSI generic "
970 "device\n", device_name);
971 pr2serr(" perhaps sg module is not loaded\n");
972 return 1;
973 }
974 }
975
976 static int
map_sg(const char * device_name,const char * device_dir,int ma,int mi,int result,bool follow_symlink,int verbose)977 map_sg(const char * device_name, const char * device_dir, int ma, int mi,
978 int result, bool follow_symlink, int verbose)
979 {
980 int m_mi, m_ma, num;
981 char value[D_NAME_LEN_MAX];
982 char name[D_NAME_LEN_MAX];
983
984 if (2 == result) {
985 num = list_matching_nodes(device_dir, FT_CHAR, ma, mi,
986 follow_symlink, verbose);
987 return (num > 0) ? 0 : 1;
988 }
989 snprintf(name, sizeof(name), "%ssg%d", sys_sg_dir, mi);
990 if (3 == result) {
991 printf("%s\n", name);
992 return 0;
993 }
994 if (! get_value(name, "dev", value, sizeof(value))) {
995 pr2serr("Couldn't find sysfs match for device: %s\n",
996 device_name);
997 return 1;
998 }
999 if (verbose)
1000 pr2serr("sysfs sg dev: %s\n", value);
1001 if (! if_directory_chdir(name, "device")) {
1002 pr2serr("sysfs problem with device: %s\n", device_name);
1003 return 1;
1004 }
1005 if ((1 == from_sg_scan(".", verbose)) &&
1006 (if_directory_chdir(".", from_sg.name))) {
1007 if (DT_DIR == from_sg.d_type) {
1008 if ((1 == scan_for_first(".", verbose)) &&
1009 (if_directory_chdir(".", for_first.name))) {
1010 ;
1011 } else {
1012 pr2serr("unexpected scan_for_first error\n");
1013 }
1014 }
1015 if (1 == result) {
1016 if (NULL == getcwd(value, sizeof(value)))
1017 value[0] = '\0';
1018 printf("%s\n", value);
1019 return 0;
1020 }
1021 if (! get_value(".", "dev", value, sizeof(value))) {
1022 pr2serr("Couldn't find sysfs block dev\n");
1023 return 1;
1024 }
1025 if (verbose)
1026 printf("matching dev: %s\n", value);
1027 if (2 != sscanf(value, "%d:%d", &m_ma, &m_mi)) {
1028 pr2serr("Couldn't decode mapped dev\n");
1029 return 1;
1030 }
1031 num = list_matching_nodes(device_dir, from_sg.ft, m_ma, m_mi,
1032 follow_symlink, verbose);
1033 return (num > 0) ? 0 : 1;
1034 } else {
1035 pr2serr("sg device: %s does not match any other SCSI "
1036 "device\n", device_name);
1037 return 1;
1038 }
1039 }
1040
1041
1042 int
main(int argc,char * argv[])1043 main(int argc, char * argv[])
1044 {
1045 bool cont;
1046 int c, num, tt, res;
1047 int given_is = -1;
1048 int result = 0;
1049 int verbose = 0;
1050 int ret = 1;
1051 int ma, mi;
1052 bool do_dev_dir = false;
1053 bool follow_symlink = false;
1054 char device_name[D_NAME_LEN_MAX];
1055 char device_dir[D_NAME_LEN_MAX];
1056 char value[D_NAME_LEN_MAX];
1057
1058 memset(device_name, 0, sizeof(device_name));
1059 memset(device_dir, 0, sizeof(device_dir));
1060 while (1) {
1061 int option_index = 0;
1062
1063 c = getopt_long(argc, argv, "d:hg:r:svV", long_options,
1064 &option_index);
1065 if (c == -1)
1066 break;
1067
1068 switch (c) {
1069 case 'd':
1070 strncpy(device_dir, optarg, sizeof(device_dir) - 1);
1071 do_dev_dir = true;
1072 break;
1073 case 'g':
1074 num = sscanf(optarg, "%d", &res);
1075 if ((1 == num) && ((0 == res) || (1 == res)))
1076 given_is = res;
1077 else {
1078 pr2serr("value for '--given_to=' must be 0 "
1079 "or 1\n");
1080 return SG_LIB_SYNTAX_ERROR;
1081 }
1082 break;
1083 case 'h':
1084 case '?':
1085 usage();
1086 return 0;
1087 case 'r':
1088 num = sscanf(optarg, "%d", &res);
1089 if ((1 == num) && (res >= 0) && (res < 4))
1090 result = res;
1091 else {
1092 pr2serr("value for '--result=' must be "
1093 "0..3\n");
1094 return SG_LIB_SYNTAX_ERROR;
1095 }
1096 break;
1097 case 's':
1098 follow_symlink = true;
1099 break;
1100 case 'v':
1101 ++verbose;
1102 break;
1103 case 'V':
1104 pr2serr(ME "version: %s\n", version_str);
1105 return 0;
1106 default:
1107 pr2serr("unrecognised option code 0x%x ??\n", c);
1108 usage();
1109 return SG_LIB_SYNTAX_ERROR;
1110 }
1111 }
1112 if (optind < argc) {
1113 if ('\0' == device_name[0]) {
1114 strncpy(device_name, argv[optind],
1115 sizeof(device_name) - 1);
1116 device_name[sizeof(device_name) - 1] = '\0';
1117 ++optind;
1118 }
1119 if (optind < argc) {
1120 for (; optind < argc; ++optind)
1121 pr2serr("Unexpected extra argument: %s\n",
1122 argv[optind]);
1123 usage();
1124 return SG_LIB_SYNTAX_ERROR;
1125 }
1126 }
1127
1128 if (0 == device_name[0]) {
1129 pr2serr("missing device name!\n");
1130 usage();
1131 return SG_LIB_SYNTAX_ERROR;
1132 }
1133
1134 ma = 0;
1135 mi = 0;
1136 if (do_dev_dir) {
1137 if (if_directory_chdir(".", device_dir)) {
1138 if (getcwd(device_dir, sizeof(device_dir)))
1139 device_dir[sizeof(device_dir) - 1] = '\0';
1140 else
1141 device_dir[0] = '\0';
1142 if (verbose > 1)
1143 pr2serr("Absolute path to dev_dir: %s\n",
1144 device_dir);
1145 } else {
1146 pr2serr("dev_dir: %s invalid\n", device_dir);
1147 return SG_LIB_FILE_ERROR;
1148 }
1149 } else {
1150 strcpy(device_dir, device_name);
1151 dirname(device_dir);
1152 if (0 == strcmp(device_dir, device_name)) {
1153 if (NULL == getcwd(device_dir, sizeof(device_dir)))
1154 device_dir[0] = '\0';
1155 }
1156 }
1157 ret = nt_typ_from_filename(device_name, &ma, &mi);
1158 if (ret < 0) {
1159 pr2serr("stat failed on %s: %s\n", device_name,
1160 ssafe_strerror(-ret));
1161 return SG_LIB_FILE_ERROR;
1162 }
1163 if (verbose)
1164 pr2serr(" %s: %s device [maj=%d, min=%d]\n", device_name,
1165 nt_names[ret], ma, mi);
1166 res = 0;
1167 switch (ret) {
1168 case NT_SD:
1169 case NT_SR:
1170 case NT_HD:
1171 if (given_is > 0) {
1172 pr2serr("block special but '--given_is=' suggested "
1173 "sysfs device\n");
1174 return SG_LIB_FILE_ERROR;
1175 }
1176 break;
1177 case NT_ST:
1178 case NT_OSST:
1179 case NT_CH:
1180 case NT_SG:
1181 if (given_is > 0) {
1182 pr2serr("character special but '--given_is=' "
1183 "suggested sysfs device\n");
1184 return SG_LIB_FILE_ERROR;
1185 }
1186 break;
1187 case NT_REG:
1188 if (0 == given_is) {
1189 pr2serr("regular file but '--given_is=' suggested "
1190 "block or char special\n");
1191 return SG_LIB_FILE_ERROR;
1192 }
1193 strcpy(device_dir, def_dev_dir);
1194 break;
1195 case NT_DIR:
1196 if (0 == given_is) {
1197 pr2serr("directory but '--given_is=' suggested "
1198 "block or char special\n");
1199 return SG_LIB_FILE_ERROR;
1200 }
1201 strcpy(device_dir, def_dev_dir);
1202 break;
1203 default:
1204 break;
1205 }
1206
1207 tt = NT_NO_MATCH;
1208 do {
1209 cont = false;
1210 switch (ret) {
1211 case NT_NO_MATCH:
1212 res = 1;
1213 break;
1214 case NT_SD:
1215 res = map_sd(device_name, device_dir, ma, mi, result,
1216 follow_symlink, verbose);
1217 break;
1218 case NT_SR:
1219 res = map_sr(device_name, device_dir, ma, mi, result,
1220 follow_symlink, verbose);
1221 break;
1222 case NT_HD:
1223 if (result < 2) {
1224 pr2serr("a hd device does not map to a sg "
1225 "device\n");
1226 return SG_LIB_FILE_ERROR;
1227 }
1228 res = map_hd(device_dir, ma, mi, result,
1229 follow_symlink, verbose);
1230 break;
1231 case NT_ST:
1232 res = map_st(device_name, device_dir, ma, mi, result,
1233 follow_symlink, verbose);
1234 break;
1235 case NT_OSST:
1236 res = map_osst(device_name, device_dir, ma, mi,
1237 result, follow_symlink, verbose);
1238 break;
1239 case NT_CH:
1240 res = map_ch(device_name, device_dir, ma, mi, result,
1241 follow_symlink, verbose);
1242 break;
1243 case NT_SG:
1244 res = map_sg(device_name, device_dir, ma, mi, result,
1245 follow_symlink, verbose);
1246 break;
1247 case NT_REG:
1248 if (! get_value(NULL, device_name, value,
1249 sizeof(value))) {
1250 pr2serr("Couldn't fetch value from: %s\n",
1251 device_name);
1252 return SG_LIB_FILE_ERROR;
1253 }
1254 if (verbose)
1255 pr2serr("value: %s\n", value);
1256 if (2 != sscanf(value, "%d:%d", &ma, &mi)) {
1257 pr2serr("Couldn't decode value\n");
1258 return SG_LIB_FILE_ERROR;
1259 }
1260 tt = nt_typ_from_major(ma);
1261 cont = true;
1262 break;
1263 case NT_DIR:
1264 if (! get_value(device_name, "dev", value,
1265 sizeof(value))) {
1266 pr2serr("Couldn't fetch value from: %s/dev\n",
1267 device_name);
1268 return SG_LIB_FILE_ERROR;
1269 }
1270 if (verbose)
1271 pr2serr("value: %s\n", value);
1272 if (2 != sscanf(value, "%d:%d", &ma, &mi)) {
1273 pr2serr("Couldn't decode value\n");
1274 return SG_LIB_FILE_ERROR;
1275 }
1276 tt = nt_typ_from_major(ma);
1277 cont = true;
1278 break;
1279 default:
1280 break;
1281 }
1282 ret = tt;
1283 } while (cont);
1284 return res;
1285 }
1286