OSDN Git Service

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