OSDN Git Service

dp-*: fix some wrong size_t vs ssize_t usage.
[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
7  * modify it under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of the
9  * License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see
18  * <http://www.gnu.org/licenses/>.
19  *
20  */
21 #include <dirent.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <inttypes.h>
25 #include <limits.h>
26 #include <linux/ethtool.h>
27 #include <linux/version.h>
28 #include <linux/sockios.h>
29 #include <net/if.h>
30 #include <scsi/scsi.h>
31 #include <stdio.h>
32 #include <sys/ioctl.h>
33 #include <sys/socket.h>
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/stat.h>
37 #include <unistd.h>
38
39 #ifndef major
40 #include <sys/sysmacros.h>
41 #endif
42
43 #include <efivar.h>
44 #include <efiboot.h>
45
46 #include "dp.h"
47 #include "linux.h"
48 #include "util.h"
49
50 int
51 __attribute__((__visibility__ ("hidden")))
52 set_disk_and_part_name(struct disk_info *info)
53 {
54         char *linkbuf;
55         ssize_t rc;
56
57         rc = sysfs_readlink(&linkbuf, "/sys/dev/block/%"PRIu64":%hhu",
58                       info->major, info->minor);
59         if (rc < 0)
60                 return -1;
61
62         char *ultimate;
63         ultimate = strrchr(linkbuf, '/');
64         if (!ultimate) {
65                 errno = EINVAL;
66                 return -1;
67         }
68         *ultimate = '\0';
69         ultimate++;
70
71         char *penultimate;
72         penultimate = strrchr(linkbuf, '/');
73         if (!penultimate) {
74                 errno = EINVAL;
75                 return -1;
76         }
77         penultimate++;
78
79         /*
80          * If there's a better way to figure this out, it'd be good, because
81          * I don't want to have to change this for every new disk type...
82          */
83         if (!strcmp(penultimate, "block")) {
84                 if (!info->disk_name) {
85                         info->disk_name = strdup(ultimate);
86                         if (!info->disk_name)
87                                 return -1;
88                 }
89                 if (!info->part_name) {
90                         rc = asprintf(&info->part_name, "%s%d", info->disk_name,
91                                       info->part);
92                         if (rc < 0)
93                                 return -1;
94                 }
95         } else if (!strncmp(penultimate, "nvme", 4)) {
96                 if (!info->disk_name) {
97                         info->disk_name = strdup(ultimate);
98                         if (!info->disk_name)
99                                 return -1;
100                 }
101                 if (!info->part_name) {
102                         rc = asprintf(&info->part_name, "%sp%d",
103                                       info->disk_name, info->part);
104                         if (rc < 0)
105                                 return -1;
106                 }
107         } else {
108                 if (!info->disk_name) {
109                         info->disk_name = strdup(penultimate);
110                         if (!info->disk_name)
111                                 return -1;
112                 }
113                 if (!info->part_name) {
114                         info->part_name = strdup(ultimate);
115                         if (!info->part_name)
116                                 return -1;
117                 }
118         }
119
120         return 0;
121 }
122
123 int
124 __attribute__((__visibility__ ("hidden")))
125 get_partition_number(const char *devpath)
126 {
127         struct stat statbuf = { 0, };
128         int rc;
129         unsigned int maj, min;
130         char *linkbuf;
131         uint8_t *partbuf;
132         int ret = -1;
133
134         rc = stat(devpath, &statbuf);
135         if (rc < 0)
136                 return -1;
137
138         if (!S_ISBLK(statbuf.st_mode)) {
139                 errno = EINVAL;
140                 return -1;
141         }
142
143         maj = major(statbuf.st_rdev);
144         min = minor(statbuf.st_rdev);
145
146         rc = sysfs_readlink(&linkbuf, "/sys/dev/block/%u:%u", maj, min);
147         if (rc < 0)
148                 return -1;
149
150         rc = read_sysfs_file(&partbuf, "/sys/dev/block/%s/partition", linkbuf);
151         if (rc < 0)
152                 return -1;
153
154         rc = sscanf((char *)partbuf, "%d\n", &ret);
155         if (rc != 1)
156                 return -1;
157         return ret;
158 }
159
160 int
161 __attribute__((__visibility__ ("hidden")))
162 find_parent_devpath(const char * const child, char **parent)
163 {
164         int ret;
165         char *node;
166         char *linkbuf;
167
168         /* strip leading /dev/ */
169         node = strrchr(child, '/');
170         if (!node)
171                 return -1;
172         node++;
173
174         /* look up full path symlink */
175         ret = sysfs_readlink(&linkbuf, "/sys/class/block/%s", node);
176         if (ret < 0)
177                 return ret;
178
179         /* strip child */
180         node = strrchr(linkbuf, '/');
181         if (!node)
182                 return -1;
183         *node = '\0';
184
185         /* read parent */
186         node = strrchr(linkbuf, '/');
187         if (!node)
188                 return -1;
189         *node = '\0';
190         node++;
191
192         /* write out new path */
193         ret = asprintf(parent, "/dev/%s", node);
194         if (ret < 0)
195                 return ret;
196
197         return 0;
198 }
199
200 static int
201 sysfs_test_nvme(const char *buf, ssize_t size)
202 {
203         if (!strncmp(buf, "nvme/", MIN(size, 5)))
204                 return 1;
205         return 0;
206 }
207
208 static int
209 sysfs_test_sata(const char *buf, ssize_t size)
210 {
211         if (!strncmp(buf, "ata", MIN(size, 3)))
212                 return 1;
213         return 0;
214 }
215
216 static int
217 sysfs_test_sas(const char *buf, ssize_t size)
218 {
219         int rc;
220         char *path;
221         struct stat statbuf = { 0, };
222         char *newbuf;
223
224         int host;
225         int sz;
226
227         newbuf = strndupa(buf, size+1);
228         if (!newbuf)
229                 return -1;
230         newbuf[size] = '\0';
231
232         errno = 0;
233         rc = sscanf(newbuf, "host%d/%n", &host, &sz);
234         if (rc < 1)
235                 return (errno == 0) ? 0 : -1;
236
237         rc = asprintfa(&path, "/sys/class/scsi_host/host%d/host_sas_address",
238                         host);
239         if (rc < 0)
240                 return -1;
241
242         rc = stat(path, &statbuf);
243         if (rc >= 0)
244                 return 1;
245         return 0;
246 }
247
248 static ssize_t
249 sysfs_sata_get_port_info(uint32_t print_id, struct disk_info *info)
250 {
251         DIR *d;
252         struct dirent *de;
253         uint8_t *buf = NULL;
254         int rc;
255
256         d = opendir("/sys/class/ata_device/");
257         if (!d)
258                 return -1;
259
260         while ((de = readdir(d)) != NULL) {
261                 uint32_t found_print_id;
262                 uint32_t found_pmp;
263                 uint32_t found_devno = 0;
264
265                 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
266                         continue;
267
268                 int rc;
269                 rc = sscanf(de->d_name, "dev%d.%d.%d", &found_print_id,
270                             &found_pmp, &found_devno);
271                 if (rc < 2 || rc > 3) {
272                         closedir(d);
273                         errno = EINVAL;
274                         return -1;
275                 } else if (found_print_id != print_id) {
276                         continue;
277                 } else if (rc == 3) {
278                         /*
279                          * the kernel doesn't't ever tell us the SATA PMPN
280                          * sentinal value, it'll give us devM.N instead of
281                          * devM.N.O in that case instead.
282                          */
283                         if (found_pmp > 0x7fff) {
284                                 closedir(d);
285                                 errno = EINVAL;
286                                 return -1;
287                         }
288                         info->sata_info.ata_devno = 0;
289                         info->sata_info.ata_pmp = found_pmp;
290                         break;
291                 } else if (rc == 2) {
292                         info->sata_info.ata_devno = 0;
293                         info->sata_info.ata_pmp = 0xffff;
294                         break;
295                 }
296         }
297         closedir(d);
298
299         rc = read_sysfs_file(&buf, "/sys/class/ata_port/ata%d/port_no",
300                              print_id);
301         if (rc <= 0)
302                 return -1;
303
304         rc = sscanf((char *)buf, "%d", &info->sata_info.ata_port);
305         if (rc != 1)
306                 return -1;
307
308         /*
309          * ata_port numbers are 1-indexed from libata in the kernel, but
310          * they're 0-indexed in the spec.  For maximal confusion.
311          */
312         if (info->sata_info.ata_port == 0) {
313                 errno = EINVAL;
314                 return -1;
315         } else {
316                 info->sata_info.ata_port -= 1;
317         }
318
319         return 0;
320 }
321
322 static ssize_t
323 sysfs_parse_nvme(uint8_t *buf, ssize_t size, ssize_t *off,
324                 const char *pbuf, ssize_t psize, ssize_t *poff,
325                 struct disk_info *info)
326 {
327         int rc;
328         int psz = 0;
329         uint8_t *filebuf = NULL;
330
331         *poff = 0;
332         *off = 0;
333
334         char *newpbuf;
335
336         newpbuf = strndupa(pbuf, psize+1);
337         if (!newpbuf)
338                 return -1;
339         newpbuf[psize] = '\0';
340
341         int32_t tosser0;
342         int32_t ctrl_id;
343         int32_t ns_id;
344
345         /* buf is:
346          * nvme/nvme0/nvme0n1
347          */
348         rc = sscanf(newpbuf, "nvme/nvme%d/nvme%dn%d%n", &tosser0,
349                     &ctrl_id, &ns_id, &psz);
350         if (rc != 3)
351                 return -1;
352         *poff += psz;
353
354         info->nvme_info.ctrl_id = ctrl_id;
355         info->nvme_info.ns_id = ns_id;
356         info->nvme_info.has_eui = 0;
357         info->interface_type = nvme;
358
359         /*
360          * now fish the eui out of sysfs is there is one...
361          */
362         rc = read_sysfs_file(&filebuf,
363                              "/sys/class/block/nvme%dn%d/device/eui",
364                              ctrl_id, ns_id);
365         if (rc >= 0) {
366                 uint8_t eui[8];
367                 if (rc < 23) {
368                         errno = EINVAL;
369                         return -1;
370                 }
371                 rc = sscanf((char *)filebuf,
372                             "%02hhx-%02hhx-%02hhx-%02hhx-"
373                             "%02hhx-%02hhx-%02hhx-%02hhx",
374                             &eui[0], &eui[1], &eui[2], &eui[3],
375                             &eui[4], &eui[5], &eui[6], &eui[7]);
376                 if (rc < 8) {
377                         errno = EINVAL;
378                         return -1;
379                 }
380                 info->nvme_info.has_eui = 1;
381                 memcpy(info->nvme_info.eui, eui, sizeof(eui));
382         }
383
384         *off = efidp_make_nvme(buf, size,
385                                info->nvme_info.ns_id,
386                                info->nvme_info.has_eui ? info->nvme_info.eui
387                                                        : NULL);
388         return *off;
389 }
390
391
392 static ssize_t
393 sysfs_parse_sata(uint8_t *buf, ssize_t size, ssize_t *off,
394                  const char *pbuf, ssize_t psize, ssize_t *poff,
395                  struct disk_info *info)
396 {
397         int psz = 0;
398         int rc;
399
400         *poff = 0;
401         *off = 0;
402
403         uint32_t print_id;
404
405         uint32_t scsi_bus;
406         uint32_t scsi_device;
407         uint32_t scsi_target;
408         uint32_t scsi_lun;
409
410         char *newpbuf;
411
412         newpbuf = strndupa(pbuf, psize+1);
413         if (!newpbuf)
414                 return -1;
415         newpbuf[psize] = '\0';
416
417         /* find the ata info:
418          * ata1/host0/target0:0:0/
419          *    ^dev  ^host   x y z
420          */
421         rc = sscanf(newpbuf, "ata%d/host%d/target%d:%d:%d/%n",
422                     &print_id, &scsi_bus, &scsi_device, &scsi_target, &scsi_lun,
423                     &psz);
424         if (rc != 5)
425                 return -1;
426         *poff += psz;
427
428         /* find the emulated scsi bits (and ignore them)
429          * 0:0:0:0/
430          */
431         uint32_t dummy0, dummy1, dummy2;
432         uint64_t dummy3;
433         rc = sscanf(newpbuf+*poff, "%d:%d:%d:%"PRIu64"/%n", &dummy0, &dummy1,
434                     &dummy2, &dummy3, &psz);
435         if (rc != 4)
436                 return -1;
437         *poff += psz;
438
439         /* what's left is:
440          * block/sda/sda4
441          */
442         char *disk_name = NULL;
443         char *part_name = NULL;
444         int psz1 = 0;
445         rc = sscanf(newpbuf+*poff, "block/%m[^/]%n/%m[^/]%n", &disk_name, &psz,
446                     &part_name, &psz1);
447         if (rc == 1) {
448                 rc = asprintf(&part_name, "%s%d", disk_name, info->part);
449                 if (rc < 0) {
450                         free(disk_name);
451                         errno = EINVAL;
452                         return -1;
453                 }
454                 *poff += psz;
455         } else if (rc != 2) {
456                 errno = EINVAL;
457                 return -1;
458         } else {
459                 *poff += psz1;
460         }
461
462         info->sata_info.scsi_bus = scsi_bus;
463         info->sata_info.scsi_device = scsi_device;
464         info->sata_info.scsi_target = scsi_target;
465         info->sata_info.scsi_lun = scsi_lun;
466
467         rc = sysfs_sata_get_port_info(print_id, info);
468         if (rc < 0) {
469                 free(disk_name);
470                 free(part_name);
471                 return -1;
472         }
473
474         /* check the original of this; it's guaranteed in our copy */
475         if (pbuf[*poff] != '\0') {
476                 free(disk_name);
477                 free(part_name);
478                 errno = EINVAL;
479                 return -1;
480         }
481
482         info->disk_name = disk_name;
483         info->part_name = part_name;
484         if (info->interface_type == interface_type_unknown)
485                 info->interface_type = sata;
486
487         if (info->interface_type == ata) {
488                 *off = efidp_make_atapi(buf, size, info->sata_info.ata_port,
489                                         info->sata_info.ata_pmp,
490                                         info->sata_info.ata_devno);
491         } else {
492                 *off = efidp_make_sata(buf, size, info->sata_info.ata_port,
493                                        info->sata_info.ata_pmp,
494                                        info->sata_info.ata_devno);
495         }
496         return *off;
497 }
498
499 static ssize_t
500 sysfs_parse_sas(uint8_t *buf, ssize_t size, ssize_t *off,
501                 const char *pbuf, ssize_t psize, ssize_t *poff,
502                 struct disk_info *info)
503 {
504         int rc;
505         int psz = 0;
506         uint8_t *filebuf = NULL;
507         uint64_t sas_address;
508
509         char *newpbuf;
510
511         newpbuf = strndupa(pbuf, psize+1);
512         if (!newpbuf)
513                 return -1;
514         newpbuf[psize] = '\0';
515
516         *poff = 0;
517         *off = 0;
518
519         /* buf is:
520          * host4/port-4:0/end_device-4:0/target4:0:0/4:0:0:0/block/sdc/sdc1
521          */
522         uint32_t tosser0, tosser1, tosser2;
523
524         /* ignore a bunch of stuff
525          *    host4/port-4:0
526          * or host4/port-4:0:0
527          */
528         rc = sscanf(newpbuf+*poff, "host%d/port-%d:%d%n", &tosser0, &tosser1,
529                     &tosser2, &psz);
530         if (rc != 3)
531                 return -1;
532         *poff += psz;
533
534         psz = 0;
535         rc = sscanf(pbuf+*poff, ":%d%n", &tosser0, &psz);
536         if (rc != 0 && rc != 1)
537                 return -1;
538         *poff += psz;
539
540         /* next:
541          *    /end_device-4:0
542          * or /end_device-4:0:0
543          * awesomely these are the exact same fields that go into port-blah,
544          * but we don't care for now about any of them anyway.
545          */
546         rc = sscanf(newpbuf+*poff, "/end_device-%d:%d%n", &tosser0, &tosser1,
547                     &psz);
548         if (rc != 2)
549                 return -1;
550         *poff += psz;
551
552         psz = 0;
553         rc = sscanf(newpbuf+*poff, ":%d%n", &tosser0, &psz);
554         if (rc != 0 && rc != 1)
555                 return -1;
556         *poff += psz;
557
558         /* now:
559          * /target4:0:0/
560          */
561         uint64_t tosser3;
562         rc = sscanf(newpbuf+*poff, "/target%d:%d:%"PRIu64"/%n", &tosser0,
563                     &tosser1, &tosser3, &psz);
564         if (rc != 3)
565                 return -1;
566         *poff += psz;
567
568         /* now:
569          * %d:%d:%d:%llu/
570          */
571         rc = sscanf(newpbuf+*poff, "%d:%d:%d:%"PRIu64"/%n",
572                     &info->sas_info.scsi_bus,
573                     &info->sas_info.scsi_device,
574                     &info->sas_info.scsi_target,
575                     &info->sas_info.scsi_lun, &psz);
576         if (rc != 4)
577                 return -1;
578         *poff += psz;
579
580         /* what's left is:
581          * block/sdc/sdc1
582          */
583         char *disk_name = NULL;
584         char *part_name = NULL;
585         rc = sscanf(newpbuf+*poff, "block/%m[^/]/%m[^/]%n", &disk_name,
586                     &part_name, &psz);
587         if (rc != 2)
588                 return -1;
589         *poff += psz;
590
591         /* check the original of this; it's guaranteed in our copy */
592         if (pbuf[*poff] != '\0') {
593                 free(disk_name);
594                 free(part_name);
595                 errno = EINVAL;
596                 return -1;
597         }
598
599         /*
600          * we also need to get the actual sas_address from someplace...
601          */
602         rc = read_sysfs_file(&filebuf,
603                              "/sys/class/block/%s/device/sas_address",
604                              disk_name);
605         if (rc < 0)
606                 return -1;
607
608         rc = sscanf((char *)filebuf, "%"PRIx64, &sas_address);
609         if (rc != 1)
610                 return -1;
611
612         info->sas_info.sas_address = sas_address;
613         info->disk_name = disk_name;
614         info->part_name = part_name;
615         info->interface_type = sas;
616
617         *off = efidp_make_sas(buf, size, sas_address);
618         return *off;
619 }
620
621 static ssize_t
622 make_pci_path(uint8_t *buf, ssize_t size, char *pathstr, ssize_t *pathoff)
623 {
624         ssize_t off=0, sz=0;
625         ssize_t poff = 0;
626         int psz;
627         int rc;
628
629         if (pathstr == NULL || pathoff == NULL || pathstr[0] == '\0') {
630                 errno = EINVAL;
631                 return -1;
632         }
633
634         *pathoff = 0;
635
636         uint16_t root_domain;
637         uint8_t root_bus;
638         uint32_t acpi_hid = 0;
639         uint64_t acpi_uid_int = 0;
640         /*
641          * find the pci root domain and port; they basically look like:
642          * pci0000:00/
643          *    ^d   ^p
644          */
645         rc = sscanf(pathstr+poff, "pci%hx:%hhx/%n", &root_domain,
646                     &root_bus, &psz);
647         if (rc != 2)
648                 return -1;
649         poff += psz;
650
651         uint8_t *fbuf = NULL;
652         rc = read_sysfs_file(&fbuf,
653                              "/sys/devices/pci%04x:%02x/firmware_node/hid",
654                              root_domain, root_bus);
655         if (rc < 0)
656                 return -1;
657
658         uint16_t tmp16 = 0;
659         rc = sscanf((char *)fbuf, "PNP%hx", &tmp16);
660         if (rc != 1)
661                 return -1;
662         acpi_hid = EFIDP_EFI_PNP_ID(tmp16);
663
664         /* Apparently basically nothing can look up a PcieRoot() node,
665          * because they just check _CID.  So since _CID for the root pretty
666          * much always has to be PNP0A03 anyway, just use that no matter
667          * what.
668          */
669         if (acpi_hid == EFIDP_ACPI_PCIE_ROOT_HID)
670                 acpi_hid = EFIDP_ACPI_PCI_ROOT_HID;
671
672         errno = 0;
673         fbuf = NULL;
674         int use_uid_str = 0;
675         rc = read_sysfs_file(&fbuf,
676                              "/sys/devices/pci%4x:%02x/firmware_node/uid",
677                              root_domain, root_bus);
678         if (rc <= 0 && errno != ENOENT)
679                 return -1;
680         if (rc > 0) {
681                 rc = sscanf((char *)fbuf, "%"PRIu64"\n", &acpi_uid_int);
682                 if (rc != 1) {
683                         /* kernel uses "%s\n" to print it, so there
684                          * should always be some value and a newline... */
685                         int l = strlen((char *)buf);
686                         if (l >= 1) {
687                                 use_uid_str=1;
688                                 fbuf[l-1] = '\0';
689                         }
690                 }
691         }
692         errno = 0;
693
694         if (use_uid_str) {
695                 sz = efidp_make_acpi_hid_ex(buf+off, size?size-off:0,
696                                             acpi_hid, 0, 0, "", (char *)fbuf,
697                                             "");
698         } else {
699                 sz = efidp_make_acpi_hid(buf+off, size?size-off:0,
700                                          acpi_hid, acpi_uid_int);
701         }
702         if (sz < 0)
703                 return -1;
704         off += sz;
705
706         /* find the pci domain/bus/device/function:
707          * 0000:00:01.0/0000:01:00.0/
708          *              ^d   ^b ^d ^f (of the last one in the series)
709          */
710         int found=0;
711         while (1) {
712                 uint16_t domain;
713                 uint8_t bus, device, function;
714                 rc = sscanf(pathstr+poff, "%hx:%hhx:%hhx.%hhx/%n",
715                             &domain, &bus, &device, &function, &psz);
716                 if (rc != 4)
717                         break;
718                 found=1;
719                 poff += psz;
720
721                 sz = efidp_make_pci(buf+off, size?size-off:0, device, function);
722                 if (sz < 0)
723                         return -1;
724                 off += sz;
725         }
726         if (!found)
727                 return -1;
728
729         *pathoff = poff;
730         return off;
731 }
732
733 int
734 __attribute__((__visibility__ ("hidden")))
735 make_blockdev_path(uint8_t *buf, ssize_t size, struct disk_info *info)
736 {
737         char *linkbuf = NULL;
738         char *driverbuf = NULL;
739         ssize_t off=0, sz=0, loff=0;
740         int lsz = 0;
741         int rc;
742         int found = 0;
743
744         rc = sysfs_readlink(&linkbuf, "/sys/dev/block/%"PRIu64":%u",
745                             info->major, info->minor);
746         if (rc < 0)
747                 return -1;
748
749         /*
750          * the sysfs path basically looks like:
751          * ../../devices/pci$PCI_STUFF/$BLOCKDEV_STUFF/block/$DISK/$PART
752          */
753         rc = sscanf(linkbuf+loff, "../../devices/%n", &lsz);
754         if (rc != 0)
755                 return -1;
756         loff += lsz;
757
758         ssize_t tmplsz=0;
759         sz = make_pci_path(buf, size, linkbuf+loff, &tmplsz);
760         if (sz < 0)
761                 return -1;
762         loff += tmplsz;
763         off += sz;
764
765         char *tmppath = strdupa(linkbuf);
766         if (!tmppath)
767                 return -1;
768         tmppath[loff] = '\0';
769         rc = sysfs_readlink(&driverbuf, "/sys/dev/block/%s/driver", tmppath);
770         if (rc < 0)
771                 return -1;
772
773         char *driver = strrchr(driverbuf, '/');
774         if (!driver || !*driver)
775                 return -1;
776         driver+=1;
777
778         if (!strncmp(driver, "pata_", 5) || !(strcmp(driver, "ata_piix")))
779                 info->interface_type = ata;
780
781         if (info->interface_type == interface_type_unknown ||
782             info->interface_type == atapi ||
783             info->interface_type == usb ||
784             info->interface_type == i1394 ||
785             info->interface_type == fibre ||
786             info->interface_type == i2o ||
787             info->interface_type == md) {
788                 uint32_t tosser;
789                 int tmpoff;
790
791                 rc = sscanf(linkbuf+loff, "virtio%x/%n", &tosser, &tmpoff);
792                 if (rc < 0) {
793                         return -1;
794                 } else if (rc == 1) {
795                         info->interface_type = virtblk;
796                         loff += tmpoff;
797                         found = 1;
798                 }
799         }
800
801         /* /dev/nvme0n1 looks like:
802          * /sys/dev/block/259:0 -> ../../devices/pci0000:00/0000:00:1d.0/0000:05:00.0/nvme/nvme0/nvme0n1
803          */
804         if (!found) {
805                 rc = sysfs_test_nvme(linkbuf+loff, PATH_MAX-off);
806                 if (rc < 0)
807                         return -1;
808                 else if (rc > 0) {
809                         ssize_t linksz;
810                         rc = sysfs_parse_nvme(buf+off, size?size-off:0, &sz,
811                                               linkbuf+loff, PATH_MAX-off,
812                                               &linksz, info);
813                         if (rc < 0)
814                                 return -1;
815                         loff += linksz;
816                         off += sz;
817                         found = 1;
818                 }
819         }
820
821         /* /dev/sda as SATA looks like:
822          * /sys/dev/block/8:0 -> ../../devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda
823          */
824         if (!found) {
825                 rc = sysfs_test_sata(linkbuf+loff, PATH_MAX-off);
826                 if (rc < 0)
827                         return -1;
828                 if (!found && rc > 0) {
829                         ssize_t linksz=0;
830                         rc = sysfs_parse_sata(buf+off, size?size-off:0, &sz,
831                                               linkbuf+loff, PATH_MAX-off,
832                                               &linksz, info);
833                         if (rc < 0)
834                                 return -1;
835                         loff += linksz;
836                         off += sz;
837                         found = 1;
838                 }
839         }
840
841         /* /dev/sdc as SAS looks like:
842          * /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
843          */
844         if (!found) {
845                 rc = sysfs_test_sas(linkbuf+loff, PATH_MAX-off);
846                 if (rc < 0)
847                         return -1;
848                 else if (rc > 0) {
849                         ssize_t linksz=0;
850                         rc = sysfs_parse_sas(buf+off, size?size-off:0, &sz,
851                                              linkbuf+loff, PATH_MAX-off,
852                                              &linksz, info);
853                         if (rc < 0)
854                                 return -1;
855                         loff += linksz;
856                         off += sz;
857                         found = 1;
858                 }
859         }
860
861         if (!found && info->interface_type == scsi) {
862                 char *linkbuf;
863
864                 rc = sysfs_readlink(&linkbuf, "/sys/class/block/%s/device",
865                               info->disk_name);
866                 if (rc < 0)
867                         return 0;
868
869                 rc = sscanf(linkbuf, "../../../%d:%d:%d:%"PRIu64,
870                             &info->scsi_info.scsi_bus,
871                             &info->scsi_info.scsi_device,
872                             &info->scsi_info.scsi_target,
873                             &info->scsi_info.scsi_lun);
874                 if (rc != 4)
875                         return -1;
876
877                 sz = efidp_make_scsi(buf+off, size?size-off:0,
878                                      info->scsi_info.scsi_target,
879                                      info->scsi_info.scsi_lun);
880                 if (sz < 0)
881                         return -1;
882                 off += sz;
883                 found = 1;
884         }
885
886         if (!found) {
887                 errno = ENOENT;
888                 return -1;
889         }
890
891         return off;
892 }
893
894 int
895 __attribute__((__visibility__ ("hidden")))
896 eb_disk_info_from_fd(int fd, struct disk_info *info)
897 {
898         struct stat buf;
899         int rc;
900
901         memset(info, 0, sizeof *info);
902
903         info->pci_root.root_pci_domain = 0xffff;
904         info->pci_root.root_pci_bus = 0xff;
905
906         memset(&buf, 0, sizeof(struct stat));
907         rc = fstat(fd, &buf);
908         if (rc == -1) {
909                 perror("stat");
910                 return 1;
911         }
912         if (S_ISBLK(buf.st_mode)) {
913                 info->major = buf.st_rdev >> 8;
914                 info->minor = buf.st_rdev & 0xFF;
915         } else if (S_ISREG(buf.st_mode)) {
916                 info->major = buf.st_dev >> 8;
917                 info->minor = buf.st_dev & 0xFF;
918         } else {
919                 printf("Cannot stat non-block or non-regular file\n");
920                 return 1;
921         }
922
923         /* IDE disks can have up to 64 partitions, or 6 bits worth,
924          * and have one bit for the disk number.
925          * This leaves an extra bit at the top.
926          */
927         if (info->major == 3) {
928                 info->disknum = (info->minor >> 6) & 1;
929                 info->controllernum = (info->major - 3 + 0) + info->disknum;
930                 info->interface_type = ata;
931                 info->part    = info->minor & 0x3F;
932                 return 0;
933         } else if (info->major == 22) {
934                 info->disknum = (info->minor >> 6) & 1;
935                 info->controllernum = (info->major - 22 + 2) + info->disknum;
936                 info->interface_type = ata;
937                 info->part    = info->minor & 0x3F;
938                 return 0;
939         } else if (info->major >= 33 && info->major <= 34) {
940                 info->disknum = (info->minor >> 6) & 1;
941                 info->controllernum = (info->major - 33 + 4) + info->disknum;
942                 info->interface_type = ata;
943                 info->part    = info->minor & 0x3F;
944                 return 0;
945         } else if (info->major >= 56 && info->major <= 57) {
946                 info->disknum = (info->minor >> 6) & 1;
947                 info->controllernum = (info->major - 56 + 8) + info->disknum;
948                 info->interface_type = ata;
949                 info->part    = info->minor & 0x3F;
950                 return 0;
951         } else if (info->major >= 88 && info->major <= 91) {
952                 info->disknum = (info->minor >> 6) & 1;
953                 info->controllernum = (info->major - 88 + 12) + info->disknum;
954                 info->interface_type = ata;
955                 info->part    = info->minor & 0x3F;
956                 return 0;
957         }
958
959         /* I2O disks can have up to 16 partitions, or 4 bits worth. */
960         if (info->major >= 80 && info->major <= 87) {
961                 info->interface_type = i2o;
962                 info->disknum = 16*(info->major-80) + (info->minor >> 4);
963                 info->part    = (info->minor & 0xF);
964                 return 0;
965         }
966
967         /* SCSI disks can have up to 16 partitions, or 4 bits worth
968          * and have one bit for the disk number.
969          */
970         if (info->major == 8) {
971                 info->interface_type = scsi;
972                 info->disknum = (info->minor >> 4);
973                 info->part    = (info->minor & 0xF);
974                 return 0;
975         } else  if (info->major >= 65 && info->major <= 71) {
976                 info->interface_type = scsi;
977                 info->disknum = 16*(info->major-64) + (info->minor >> 4);
978                 info->part    = (info->minor & 0xF);
979                 return 0;
980         } else  if (info->major >= 128 && info->major <= 135) {
981                 info->interface_type = scsi;
982                 info->disknum = 16*(info->major-128) + (info->minor >> 4);
983                 info->part    = (info->minor & 0xF);
984                 return 0;
985         }
986
987         errno = ENOSYS;
988         return -1;
989 }
990
991 static ssize_t
992 make_net_pci_path(uint8_t *buf, ssize_t size, const char * const ifname)
993 {
994         char *linkbuf = NULL;
995         ssize_t off=0, sz=0, loff=0;
996         int lsz = 0;
997         int rc;
998
999         rc = sysfs_readlink(&linkbuf, "/sys/class/net/%s", ifname);
1000         if (rc < 0)
1001                 return -1;
1002
1003         /*
1004          * the sysfs path basically looks like:
1005          * ../../devices/$PCI_STUFF/net/$IFACE
1006          */
1007         rc = sscanf(linkbuf+loff, "../../devices/%n", &lsz);
1008         if (rc != 0)
1009                 return -1;
1010         loff += lsz;
1011
1012         ssize_t tmplsz = 0;
1013         sz = make_pci_path(buf, size, linkbuf+loff, &tmplsz);
1014         if (sz < 0)
1015                 return -1;
1016         off += sz;
1017         loff += tmplsz;
1018
1019         return off;
1020 }
1021
1022 ssize_t
1023 __attribute__((__visibility__ ("hidden")))
1024 make_mac_path(uint8_t *buf, ssize_t size, const char * const ifname)
1025 {
1026         struct ifreq ifr;
1027         struct ethtool_drvinfo drvinfo = { 0, };
1028         int fd, rc;
1029         ssize_t ret = -1, sz, off=0;
1030         char busname[PATH_MAX+1] = "";
1031
1032         memset(&ifr, 0, sizeof (ifr));
1033         strncpy(ifr.ifr_name, ifname, IF_NAMESIZE);
1034         drvinfo.cmd = ETHTOOL_GDRVINFO;
1035         ifr.ifr_data = (caddr_t)&drvinfo;
1036
1037         fd = socket(AF_INET, SOCK_DGRAM, 0);
1038         if (fd < 0)
1039                 return -1;
1040
1041         rc = ioctl(fd, SIOCETHTOOL, &ifr);
1042         if (rc < 0)
1043                 goto err;
1044
1045         strncpy(busname, drvinfo.bus_info, PATH_MAX);
1046
1047         rc = ioctl(fd, SIOCGIFHWADDR, &ifr);
1048         if (rc < 0)
1049                 goto err;
1050
1051         sz = make_net_pci_path(buf, size, ifname);
1052         if (sz < 0)
1053                 goto err;
1054         off += sz;
1055
1056         sz = efidp_make_mac_addr(buf+off, size?size-off:0,
1057                                  ifr.ifr_ifru.ifru_hwaddr.sa_family,
1058                                  (uint8_t *)ifr.ifr_ifru.ifru_hwaddr.sa_data,
1059                                  sizeof(ifr.ifr_ifru.ifru_hwaddr.sa_data));
1060         if (sz < 0)
1061                 return -1;
1062         off += sz;
1063         ret = off;
1064 err:
1065         if (fd >= 0)
1066                 close(fd);
1067         return ret;
1068 }