OSDN Git Service

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