xref: /aosp_15_r20/external/sg3_utils/src/sg_map26.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
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