OSDN Git Service

Get rid of the last caller to get_disk_name()
[android-x86/external-efivar.git] / src / linux.c
1 /*
2  * libefiboot - library for the manipulation of EFI boot variables
3  * Copyright 2012-2015 Red Hat, Inc.
4  * Copyright (C) 2001 Dell Computer Corporation <Matt_Domsch@dell.com>
5  *
6  * This library is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; either version 2 of the License, or (at your
9  * option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this library.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 #include <dirent.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <inttypes.h>
23 #include <limits.h>
24 #include <linux/nvme.h>
25 #include <scsi/scsi.h>
26 #include <stdio.h>
27 #include <sys/ioctl.h>
28 #include <sys/types.h>
29 #include <sys/param.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32
33 #include <efivar.h>
34 #include <efiboot.h>
35
36 #include "dp.h"
37 #include "linux.h"
38 #include "util.h"
39
40 #ifndef SCSI_IOCTL_GET_IDLUN
41 #define SCSI_IOCTL_GET_IDLUN 0x5382
42 #endif
43
44 int
45 __attribute__((__visibility__ ("hidden")))
46 eb_nvme_ns_id(int fd, uint32_t *ns_id)
47 {
48         uint64_t ret = ioctl(fd, NVME_IOCTL_ID, NULL);
49         if ((int)ret < 0)
50                 return ret;
51         *ns_id = (uint32_t)ret;
52         return 0;
53 }
54
55 typedef struct scsi_idlun_s {
56         uint32_t dev_id;
57         uint32_t host_unique_id;
58 } scsi_idlun;
59
60 int
61 __attribute__((__visibility__ ("hidden")))
62 eb_scsi_idlun(int fd, uint8_t *host, uint8_t *channel, uint8_t *id,
63                    uint8_t *lun)
64 {
65         int rc;
66         scsi_idlun idlun = {0, 0};
67
68         if (fd < 0 || !host || !channel || !id || !lun) {
69                 errno = EINVAL;
70                 return -1;
71         }
72
73         rc = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun);
74         if (rc < 0)
75                 return rc;
76
77         *host = (idlun.dev_id >> 24) & 0xff;
78         *channel = (idlun.dev_id >> 16) & 0xff;
79         *lun = (idlun.dev_id >> 8) & 0xff;
80         *id = idlun.dev_id & 0xff;
81         return 0;
82 }
83
84 /*
85  * Look up dynamic major device node numbers.
86  */
87 static int
88 get_dynamic_major(char *name, int block)
89 {
90         static int cached;
91         static char cached_name[1024] = "";
92         static int cached_block;
93
94         FILE *f;
95         char line[256];
96         int seen_block = 0;
97         char namenl[strlen(name)+2];
98
99         if (cached != 0 && block==cached_block &&
100             !strncmp(cached_name, name, 1023)) {
101                 return cached;
102         }
103         strcpy(namenl, name);
104         strcat(namenl, "\n");
105
106         cached = -1;
107         f = fopen("/proc/devices", "r");
108         if (f == NULL)
109                 return cached;
110
111         while (fgets(line, sizeof line, f) != NULL) {
112                 size_t len = strlen(line);
113                 int major, scanned = 0;
114
115                 if (!strcmp(line, "Block devices:\n"))
116                         seen_block = 1;
117                 if (len == 0 || line[len - 1] != '\n') {
118                         break;
119                 }
120                 if (seen_block == block &&
121                     sscanf(line, "%d %n", &major, &scanned) == 1 &&
122                     strcmp(line + scanned, namenl) == 0) {
123                         cached = major;
124                         strncpy(cached_name, name, 1023);
125                         cached_block = block;
126                         break;
127                 }
128         }
129         fclose(f);
130         return cached;
131 }
132
133 int
134 __attribute__((__visibility__ ("hidden")))
135 eb_ide_pci(int fd, const struct disk_info *info, uint8_t *bus, uint8_t *device,
136            uint8_t *function)
137 {
138         return -1;
139 }
140
141 #ifndef SCSI_IOCTL_GET_PCI
142 #define SCSI_IOCTL_GET_PCI 0x5387
143 #endif
144
145 /* see scsi_ioctl_get_pci() in linux/drivers/scsi/scsi_ioctl.c */
146 #define SLOT_NAME_SIZE ((size_t)21)
147
148 /* TODO: move this to get it from sysfs? */
149 int
150 __attribute__((__visibility__ ("hidden")))
151 eb_scsi_pci(int fd, const struct disk_info *info, uint8_t *bus,
152             uint8_t *device, uint8_t *function)
153 {
154         char buf[SLOT_NAME_SIZE] = "";
155         int rc;
156         unsigned int b=0,d=0,f=0;
157
158         /*
159          * Maybe if we're on an old enough kernel,
160          * SCSI_IOCTL_GET_PCI gives b:d.f ...
161          */
162         rc = ioctl(fd, SCSI_IOCTL_GET_PCI, buf);
163         if (rc < 0)
164                 return rc;
165
166         rc = sscanf(buf, "%x:%x:%x", &b, &d, &f);
167         if (rc != 3) {
168                 errno = EINVAL;
169                 return -1;
170         }
171
172         *bus = b & 0xff;
173         *device = d & 0xff;
174         *function = f & 0xff;
175         return 0;
176 }
177
178 int
179 __attribute__((__visibility__ ("hidden")))
180 set_disk_and_part_name(struct disk_info *info)
181 {
182         char *linkbuf;
183         ssize_t rc;
184
185         rc = sysfs_readlink(&linkbuf, "/sys/dev/block/%"PRIu64":%hhd",
186                       info->major, info->minor);
187         if (rc < 0)
188                 return -1;
189
190         char *ultimate;
191         ultimate = strrchr(linkbuf, '/');
192         if (!ultimate) {
193                 errno = EINVAL;
194                 return -1;
195         }
196         *ultimate = '\0';
197         ultimate++;
198
199         char *penultimate;
200         penultimate = strrchr(linkbuf, '/');
201         if (!penultimate) {
202                 errno = EINVAL;
203                 return -1;
204         }
205         penultimate++;
206
207         if (!strcmp(penultimate, "block")) {
208                 if (!info->disk_name) {
209                         info->disk_name = strdup(ultimate);
210                         if (!info->disk_name)
211                                 return -1;
212                 }
213                 if (!info->part_name) {
214                         rc = asprintf(&info->part_name, "%s%d", info->disk_name,
215                                       info->part);
216                         if (rc < 0)
217                                 return -1;
218                 }
219         } else {
220                 if (!info->disk_name) {
221                         info->disk_name = strdup(penultimate);
222                         if (!info->disk_name)
223                                 return -1;
224                 }
225                 if (!info->part_name) {
226                         info->part_name = strdup(ultimate);
227                         if (!info->part_name)
228                                 return -1;
229                 }
230         }
231
232         return 0;
233 }
234
235 static int
236 sysfs_test_sata(const char *buf, ssize_t size)
237 {
238         if (!strncmp(buf, "ata", MIN(size,3)))
239                 return 1;
240         return 0;
241 }
242
243 static int
244 sysfs_test_sas(const char *buf, ssize_t size, struct disk_info *info)
245 {
246         int rc;
247         char *path;
248         struct stat statbuf = { 0, };
249
250         int host;
251         int sz;
252
253         errno = 0;
254         rc = sscanf(buf, "host%d/%n", &host, &sz);
255         if (rc < 1)
256                 return (errno == 0) ? 0 : -1;
257
258         rc = asprintfa(&path, "/sys/class/scsi_host/host%d/host_sas_address",
259                         host);
260         if (rc < 0)
261                 return -1;
262
263         rc = stat(path, &statbuf);
264         if (rc >= 0)
265                 return 1;
266         return 0;
267 }
268
269 static ssize_t
270 sysfs_sata_get_port_info(uint32_t print_id, struct disk_info *info)
271 {
272         DIR *d;
273         struct dirent *de;
274         int saved_errno;
275         uint8_t *buf = NULL;
276         int rc;
277
278         d = opendir("/sys/class/ata_device/");
279         if (!d)
280                 return -1;
281
282         while ((de = readdir(d)) != NULL) {
283                 uint32_t found_print_id;
284                 uint32_t found_pmp;
285                 uint32_t found_devno = 0;
286
287                 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
288                         continue;
289
290                 int rc;
291                 rc = sscanf(de->d_name, "dev%d.%d.%d", &found_print_id,
292                             &found_pmp, &found_devno);
293                 if (rc == 2) {
294                         found_devno = found_pmp;
295                         found_pmp=0;
296                 } else if (rc != 3) {
297                         saved_errno = errno;
298                         closedir(d);
299                         errno = saved_errno;
300                         return -1;
301                 }
302                 if (found_print_id == print_id) {
303                         info->sata_info.ata_devno = found_devno;
304                         info->sata_info.ata_pmp = found_pmp;
305                         break;
306                 }
307         }
308         closedir(d);
309
310         rc = read_sysfs_file(&buf, "/sys/class/ata_port/ata%d/port_no",
311                              print_id);
312         if (rc <= 0)
313                 return -1;
314
315         rc = sscanf((char *)buf, "%d", &info->sata_info.ata_port);
316         if (rc != 1)
317                 return -1;
318
319         info->sata_info.ata_port -= 1;
320         return 0;
321 }
322
323 static ssize_t
324 sysfs_parse_sata(uint8_t *buf, ssize_t size, ssize_t *off,
325                  const char *pbuf, ssize_t psize, ssize_t *poff,
326                  struct disk_info *info)
327 {
328         int psz = 0;
329         int rc;
330
331         *poff = 0;
332         *off = 0;
333
334         uint32_t print_id;
335
336         uint32_t scsi_bus;
337         uint32_t scsi_device;
338         uint32_t scsi_target;
339         uint32_t scsi_lun;
340
341         /* find the ata info:
342          * ata1/host0/target0:0:0/
343          *    ^dev  ^host   x y z
344          */
345         rc = sscanf(pbuf, "ata%d/host%d/target%d:%d:%d/%n",
346                     &print_id, &scsi_bus, &scsi_device, &scsi_target, &scsi_lun,
347                     &psz);
348         if (rc != 5)
349                 return -1;
350         *poff += psz;
351
352         /* find the emulated scsi bits (and ignore them)
353          * 0:0:0:0/
354          */
355         uint32_t dummy0, dummy1, dummy2;
356         uint64_t dummy3;
357         rc = sscanf(pbuf+*poff, "%d:%d:%d:%"PRIu64"/%n", &dummy0, &dummy1,
358                     &dummy2, &dummy3, &psz);
359         if (rc != 4)
360                 return -1;
361         *poff += psz;
362
363         /* what's left is:
364          * block/sda/sda4
365          */
366         char *disk_name = NULL;
367         char *part_name = NULL;
368         int psz1 = 0;
369         rc = sscanf(pbuf+*poff, "block/%m[^/]%n/%m[^/]%n", &disk_name, &psz,
370                     &part_name, &psz1);
371         if (rc == 1) {
372                 rc = asprintf(&part_name, "%s%d", disk_name, info->part);
373                 if (rc < 0) {
374                         free(disk_name);
375                         errno = EINVAL;
376                         return -1;
377                 }
378                 *poff += psz;
379         } else if (rc != 2) {
380                 errno = EINVAL;
381                 return -1;
382         } else {
383                 *poff += psz1;
384         }
385
386         info->sata_info.scsi_bus = scsi_bus;
387         info->sata_info.scsi_device = scsi_device;
388         info->sata_info.scsi_target = scsi_target;
389         info->sata_info.scsi_lun = scsi_lun;
390
391         rc = sysfs_sata_get_port_info(print_id, info);
392         if (rc < 0) {
393                 free(disk_name);
394                 free(part_name);
395                 return -1;
396         }
397
398         if (pbuf[*poff] != '\0') {
399                 free(disk_name);
400                 free(part_name);
401                 errno = EINVAL;
402                 return -1;
403         }
404
405         info->disk_name = disk_name;
406         info->part_name = part_name;
407         info->interface_type = sata;
408
409         *off = efidp_make_sata(buf, size, info->sata_info.ata_port,
410                                info->sata_info.ata_pmp,
411                                info->sata_info.ata_devno);
412         return *off;
413 }
414
415 static ssize_t
416 sysfs_parse_sas(uint8_t *buf, ssize_t size, ssize_t *off,
417                 const char *pbuf, ssize_t psize, ssize_t *poff,
418                 struct disk_info *info)
419 {
420         int rc;
421         int psz = 0;
422         char *filebuf = NULL;
423         uint64_t sas_address;
424
425         *poff = 0;
426         *off = 0;
427
428         /* buf is:
429          * host4/port-4:0/end_device-4:0/target4:0:0/4:0:0:0/block/sdc/sdc1
430          */
431         uint32_t tosser0, tosser1, tosser2;
432
433         /* ignore a bunch of stuff
434          *    host4/port-4:0
435          * or host4/port-4:0:0
436          */
437         rc = sscanf(pbuf+*poff, "host%d/port-%d:%d%n", &tosser0, &tosser1,
438                     &tosser2, &psz);
439         if (rc != 3)
440                 return -1;
441         *poff += psz;
442
443         psz = 0;
444         rc = sscanf(pbuf+*poff, ":%d%n", &tosser0, &psz);
445         if (rc != 0 && rc != 1)
446                 return -1;
447         *poff += psz;
448
449         /* next:
450          *    /end_device-4:0
451          * or /end_device-4:0:0
452          * awesomely these are the exact same fields that go into port-blah,
453          * but we don't care for now about any of them anyway.
454          */
455         rc = sscanf(pbuf+*poff, "/end_device-%d:%d%n", &tosser0, &tosser1,
456                     &psz);
457         if (rc != 2)
458                 return -1;
459         *poff += psz;
460
461         psz = 0;
462         rc = sscanf(pbuf+*poff, ":%d%n", &tosser0, &psz);
463         if (rc != 0 && rc != 1)
464                 return -1;
465         *poff += psz;
466
467         /* now:
468          * /target4:0:0/
469          */
470         uint64_t tosser3;
471         rc = sscanf(pbuf+*poff, "/target%d:%d:%"PRIu64"/%n", &tosser0, &tosser1,
472                     &tosser3, &psz);
473         if (rc != 3)
474                 return -1;
475         *poff += psz;
476
477         /* now:
478          * %d:%d:%d:%llu/
479          */
480         rc = sscanf(pbuf+*poff, "%d:%d:%d:%"PRIu64"/%n",
481                     &info->sas_info.scsi_bus,
482                     &info->sas_info.scsi_device,
483                     &info->sas_info.scsi_target,
484                     &info->sas_info.scsi_lun, &psz);
485         if (rc != 4)
486                 return -1;
487         *poff += psz;
488
489         /* what's left is:
490          * block/sdc/sdc1
491          */
492         char *disk_name = NULL;
493         char *part_name = NULL;
494         rc = sscanf(pbuf+*poff, "block/%m[^/]/%m[^/]%n", &disk_name, &part_name,
495                     &psz);
496         if (rc != 2)
497                 return -1;
498         *poff += psz;
499
500         if (pbuf[*poff] != '\0') {
501                 free(disk_name);
502                 free(part_name);
503                 errno = EINVAL;
504                 return -1;
505         }
506
507         /*
508          * we also need to get the actual sas_address from someplace...
509          */
510         rc = read_sysfs_file(&filebuf,
511                              "/sys/class/block/%s/device/sas_address",
512                              disk_name);
513         if (rc < 0)
514                 return -1;
515
516         rc = sscanf(filebuf, "%"PRIx64, &sas_address);
517         if (rc != 1)
518                 return -1;
519
520         info->sas_info.sas_address = sas_address;
521         info->disk_name = disk_name;
522         info->part_name = part_name;
523         info->interface_type = sas;
524
525         *off = efidp_make_sas(buf, size, sas_address);
526         return *off;
527 }
528
529 int
530 __attribute__((__visibility__ ("hidden")))
531 make_blockdev_path(uint8_t *buf, ssize_t size, int fd, struct disk_info *info)
532 {
533         char *linkbuf = NULL;
534         int loff = 0;
535         int lsz = 0;
536         int rc;
537         ssize_t off=0, sz=0;
538
539         rc = sysfs_readlink(&linkbuf, "/sys/dev/block/%"PRIu64":%u",
540                             info->major, info->minor);
541         if (rc < 0)
542                 return -1;
543
544         /*
545          * find the pci root domain and port; they basically look like:
546          * ../../devices/pci0000:00/
547          *                  ^d   ^p
548          */
549         uint16_t root_domain;
550         uint8_t root_bus;
551         rc = sscanf(linkbuf+loff, "../../devices/pci%hx:%hhx/%n",
552                     &root_domain, &root_bus, &lsz);
553         if (rc != 2)
554                 return -1;
555         info->pci_root.root_pci_domain = root_domain;
556         info->pci_root.root_pci_bus = root_bus;
557         loff += lsz;
558
559         char *fbuf = NULL;
560         rc = read_sysfs_file(&fbuf, "/sys/devices/pci%04x:%02x/firmware_node/hid",
561                              root_domain, root_bus);
562         if (rc < 0)
563                 return -1;
564
565         uint16_t acpi_hid;
566         rc = sscanf((char *)fbuf, "PNP%hx", &acpi_hid);
567         if (rc != 1)
568                 return -1;
569         info->pci_root.root_pci_acpi_hid = EFIDP_EFI_PNP_ID(acpi_hid);
570
571         errno = 0;
572         fbuf = NULL;
573         rc = read_sysfs_file(&fbuf, "/sys/devices/pci%4x:%02x/firmware_node/uid",
574                              root_domain, root_bus);
575         uint64_t acpi_uid_int = 0;
576         int use_uid_str = 0;
577         if (rc <= 0 && errno != ENOENT)
578                 return -1;
579         if (rc > 0) {
580                 rc = sscanf((char *)fbuf, "%"PRIu64"\n", &acpi_uid_int);
581                 if (rc != 1) {
582                         /* kernel uses "%s\n" to print it, so there
583                          * should always be some value and a newline... */
584                         int l = strlen((char *)buf);
585                         if (l >= 1) {
586                                 use_uid_str=1;
587                                 fbuf[l-1] = '\0';
588                         }
589                 }
590         }
591         errno = 0;
592         info->pci_root.root_pci_acpi_uid = acpi_uid_int;
593
594         if (use_uid_str) {
595                 sz = efidp_make_acpi_hid_ex(buf+off, size?size-off:0,
596                                             info->pci_root.root_pci_acpi_hid,
597                                             0, 0, "", (char *)fbuf, "");
598         } else {
599                 sz = efidp_make_acpi_hid(buf+off, size?size-off:0,
600                                          info->pci_root.root_pci_acpi_hid,
601                                          info->pci_root.root_pci_acpi_uid);
602         }
603         if (sz < 0)
604                 return -1;
605         off += sz;
606
607         /* find the pci domain/bus/device/function:
608          * 0000:00:01.0/0000:01:00.0/
609          *              ^d   ^b ^d ^f (of the last one in the series)
610          */
611         int found=0;
612         while (1) {
613                 uint16_t domain;
614                 uint8_t bus, device, function;
615                 rc = sscanf(linkbuf+loff, "%hx:%hhx:%hhx.%hhx/%n",
616                             &domain, &bus, &device, &function, &lsz);
617                 if (rc != 4)
618                         break;
619                 info->pci_dev.pci_domain = domain;
620                 info->pci_dev.pci_bus = bus;
621                 info->pci_dev.pci_device = device;
622                 info->pci_dev.pci_function = function;
623                 found=1;
624                 loff += lsz;
625
626                 sz = efidp_make_pci(buf+off, size?size-off:0, device, function);
627                 if (sz < 0)
628                         return -1;
629                 off += sz;
630         }
631         if (!found)
632                 return -1;
633
634         found = 0;
635
636         if (info->interface_type == interface_type_unknown ||
637             info->interface_type == ata ||
638             info->interface_type == atapi ||
639             info->interface_type == usb ||
640             info->interface_type == i1394 ||
641             info->interface_type == fibre ||
642             info->interface_type == i2o ||
643             info->interface_type == md) {
644                 errno = ENOSYS;
645                 return -1;
646         }
647
648         if (info->interface_type == virtblk) {
649                 found = 1;
650         }
651
652         if (info->interface_type == nvme) {
653                 uint32_t ns_id=0;
654                 int rc = eb_nvme_ns_id(fd, &ns_id);
655                 if (rc < 0)
656                         return -1;
657
658                 sz = efidp_make_nvme(buf+off, size?size-off:0,
659                                      ns_id, NULL);
660                 if (sz < 0)
661                         return -1;
662                 off += sz;
663                 found = 1;
664         }
665
666         /* /dev/sda as SATA looks like:
667          * /sys/dev/block/8:0 -> ../../devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda
668          */
669         rc = sysfs_test_sata(linkbuf+loff, PATH_MAX-off);
670         if (rc < 0)
671                 return -1;
672         if (!found && rc > 0) {
673                 ssize_t linksz=0;
674                 rc = sysfs_parse_sata(buf+off, size?size-off:0, &sz,
675                                        linkbuf+loff, PATH_MAX-off, &linksz,
676                                        info);
677                 if (rc < 0)
678                         return -1;
679                 loff += linksz;
680                 off += sz;
681                 found = 1;
682         }
683
684         /* /dev/sdc as SAS looks like:
685          * /sys/dev/block/8:32 -> ../../devices/pci0000:00/0000:00:01.0/0000:01:00.0/host4/port-4:0/end_device-4:0/target4:0:0/4:0:0:0/block/sdc
686          */
687         rc = sysfs_test_sas(linkbuf+loff, PATH_MAX-off, info);
688         if (rc < 0)
689                 return -1;
690         if (!found && rc > 0) {
691                 ssize_t linksz=0;
692                 rc = sysfs_parse_sas(buf+off, size?size-off:0, &sz,
693                                       linkbuf+loff, PATH_MAX-off, &linksz,
694                                       info);
695                 if (rc < 0)
696                         return -1;
697                 loff += linksz;
698                 off += sz;
699                 found = 1;
700         }
701
702         if (!found && info->interface_type == scsi) {
703                 char *linkbuf;
704
705                 rc = sysfs_readlink(&linkbuf, "/sys/class/block/%s/device",
706                               info->disk_name);
707                 if (rc < 0)
708                         return 0;
709
710                 rc = sscanf(linkbuf, "../../../%d:%d:%d:%"PRIu64,
711                             &info->scsi_info.scsi_bus,
712                             &info->scsi_info.scsi_device,
713                             &info->scsi_info.scsi_target,
714                             &info->scsi_info.scsi_lun);
715                 if (rc != 4)
716                         return -1;
717
718                 sz = efidp_make_scsi(buf+off, size?size-off:0,
719                                      info->scsi_info.scsi_target,
720                                      info->scsi_info.scsi_lun);
721                 if (sz < 0)
722                         return -1;
723                 off += sz;
724                 found = 1;
725         }
726
727         if (!found) {
728                 errno = ENOENT;
729                 return -1;
730         }
731
732         return off;
733 }
734
735 int
736 __attribute__((__visibility__ ("hidden")))
737 eb_disk_info_from_fd(int fd, struct disk_info *info)
738 {
739         struct stat buf;
740         int rc;
741
742         memset(info, 0, sizeof *info);
743
744         info->pci_root.root_pci_domain = 0xffff;
745         info->pci_root.root_pci_bus = 0xff;
746
747         memset(&buf, 0, sizeof(struct stat));
748         rc = fstat(fd, &buf);
749         if (rc == -1) {
750                 perror("stat");
751                 return 1;
752         }
753         if (S_ISBLK(buf.st_mode)) {
754                 info->major = buf.st_rdev >> 8;
755                 info->minor = buf.st_rdev & 0xFF;
756         } else if (S_ISREG(buf.st_mode)) {
757                 info->major = buf.st_dev >> 8;
758                 info->minor = buf.st_dev & 0xFF;
759         } else {
760                 printf("Cannot stat non-block or non-regular file\n");
761                 return 1;
762         }
763
764         /* IDE disks can have up to 64 partitions, or 6 bits worth,
765          * and have one bit for the disk number.
766          * This leaves an extra bit at the top.
767          */
768         if (info->major == 3) {
769                 info->disknum = (info->minor >> 6) & 1;
770                 info->controllernum = (info->major - 3 + 0) + info->disknum;
771                 info->interface_type = ata;
772                 info->part    = info->minor & 0x3F;
773                 return 0;
774         } else if (info->major == 22) {
775                 info->disknum = (info->minor >> 6) & 1;
776                 info->controllernum = (info->major - 22 + 2) + info->disknum;
777                 info->interface_type = ata;
778                 info->part    = info->minor & 0x3F;
779                 return 0;
780         } else if (info->major >= 33 && info->major <= 34) {
781                 info->disknum = (info->minor >> 6) & 1;
782                 info->controllernum = (info->major - 33 + 4) + info->disknum;
783                 info->interface_type = ata;
784                 info->part    = info->minor & 0x3F;
785                 return 0;
786         } else if (info->major >= 56 && info->major <= 57) {
787                 info->disknum = (info->minor >> 6) & 1;
788                 info->controllernum = (info->major - 56 + 8) + info->disknum;
789                 info->interface_type = ata;
790                 info->part    = info->minor & 0x3F;
791                 return 0;
792         } else if (info->major >= 88 && info->major <= 91) {
793                 info->disknum = (info->minor >> 6) & 1;
794                 info->controllernum = (info->major - 88 + 12) + info->disknum;
795                 info->interface_type = ata;
796                 info->part    = info->minor & 0x3F;
797                 return 0;
798         }
799
800         /* I2O disks can have up to 16 partitions, or 4 bits worth. */
801         if (info->major >= 80 && info->major <= 87) {
802                 info->interface_type = i2o;
803                 info->disknum = 16*(info->major-80) + (info->minor >> 4);
804                 info->part    = (info->minor & 0xF);
805                 return 0;
806         }
807
808         /* SCSI disks can have up to 16 partitions, or 4 bits worth
809          * and have one bit for the disk number.
810          */
811         if (info->major == 8) {
812                 info->interface_type = scsi;
813                 info->disknum = (info->minor >> 4);
814                 info->part    = (info->minor & 0xF);
815                 return 0;
816         } else  if (info->major >= 65 && info->major <= 71) {
817                 info->interface_type = scsi;
818                 info->disknum = 16*(info->major-64) + (info->minor >> 4);
819                 info->part    = (info->minor & 0xF);
820                 return 0;
821         } else  if (info->major >= 128 && info->major <= 135) {
822                 info->interface_type = scsi;
823                 info->disknum = 16*(info->major-128) + (info->minor >> 4);
824                 info->part    = (info->minor & 0xF);
825                 return 0;
826         }
827
828         int64_t major = get_dynamic_major("nvme", 1);
829         if (major >= 0 && (uint64_t)major == info->major) {
830                 info->interface_type = nvme;
831                 return 0;
832         }
833
834         major = get_dynamic_major("virtblk", 1);
835         if (major >= 0 && (uint64_t)major == info->major) {
836                 info->interface_type = virtblk;
837                 info->disknum = info->minor >> 4;
838                 info->part = info->minor & 0xF;
839                 return 0;
840         }
841
842         errno = ENOSYS;
843         return -1;
844 }