OSDN Git Service

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