OSDN Git Service

libefiboot: make everything use "struct device" and kill "struct disk_info"
authorPeter Jones <pjones@redhat.com>
Wed, 6 Jun 2018 21:18:31 +0000 (17:18 -0400)
committerPeter Jones <pmjones@gmail.com>
Fri, 8 Jun 2018 19:11:37 +0000 (15:11 -0400)
Signed-off-by: Peter Jones <pjones@redhat.com>
src/creator.c
src/disk.c
src/disk.h
src/linux.c
src/linux.h

index 147826e..93f185f 100644 (file)
@@ -139,12 +139,12 @@ err:
 }
 
 static int
-open_disk(struct disk_info *info, int flags)
+open_disk(struct device *dev, int flags)
 {
        char *diskpath = NULL;
        int rc;
 
-       rc = asprintfa(&diskpath, "/dev/%s", info->disk_name);
+       rc = asprintfa(&diskpath, "/dev/%s", dev->disk_name);
        if (rc < 0) {
                efi_error("could not allocate buffer");
                return -1;
@@ -173,39 +173,72 @@ efi_va_generate_file_device_path_from_esp(uint8_t *buf, ssize_t size,
                                       const char *relpath,
                                       uint32_t options, va_list ap)
 {
-       int rc;
        ssize_t ret = -1, off = 0, sz;
-       struct disk_info info = { 0, };
+       struct device *dev = NULL;
        int fd = -1;
        int saved_errno;
 
+       debug(DEBUG, "partition:%d", partition);
+
+       if (buf && size)
+               memset(buf, '\0', size);
+
        fd = open(devpath, O_RDONLY);
        if (fd < 0) {
                efi_error("could not open device for ESP");
                goto err;
        }
 
-       rc = eb_disk_info_from_fd(fd, &info);
-       if (rc < 0 && errno != ENOSYS) {
+       dev = device_get(fd, partition);
+       if (dev == NULL) {
                efi_error("could not get ESP disk info");
                goto err;
        }
 
-       if (info.interface_type == nd_pmem) {
+       if (partition < 0) {
+               int disk_fd;
+
+               debug(DEBUG, "partition: %d", partition);
+               disk_fd = open_disk(dev,
+                                   (options & EFIBOOT_OPTIONS_WRITE_SIGNATURE)
+                                    ? O_RDWR : O_RDONLY);
+               if (disk_fd < 0) {
+                       efi_error("could not open disk");
+                       goto err;
+               }
+
+               if (is_partitioned(disk_fd))
+                       partition = 1;
+               else
+                       partition = 0;
+               debug(DEBUG, "is_partitioned(): partition -> %d", partition);
+
+               close(disk_fd);
+       }
+
+       set_part(dev, partition);
+
+       if (partition == 0) {
                options |= EFIBOOT_ABBREV_NONE;
                options &= ~(EFIBOOT_ABBREV_HD|
                             EFIBOOT_ABBREV_FILE|
                             EFIBOOT_ABBREV_EDD10);
        }
 
-       if (partition > 0)
-               info.part = partition;
+       if (options & EFIBOOT_ABBREV_NONE)
+               debug(DEBUG, "EFIBOOT_ABBREV_NONE");
+       if (options & EFIBOOT_ABBREV_HD)
+               debug(DEBUG, "EFIBOOT_ABBREV_HD");
+       if (options & EFIBOOT_ABBREV_FILE)
+               debug(DEBUG, "EFIBOOT_ABBREV_FILE");
+       if (options & EFIBOOT_ABBREV_EDD10)
+               debug(DEBUG, "EFIBOOT_ABBREV_EDD10");
 
        if (options & EFIBOOT_ABBREV_EDD10) {
                va_list aq;
                va_copy(aq, ap);
 
-               info.edd10_devicenum = va_arg(aq, uint32_t);
+               dev->edd10_devicenum = va_arg(aq, uint32_t);
 
                va_end(aq);
        }
@@ -213,7 +246,7 @@ efi_va_generate_file_device_path_from_esp(uint8_t *buf, ssize_t size,
        if ((options & EFIBOOT_ABBREV_EDD10)
                        && (!(options & EFIBOOT_ABBREV_FILE)
                            && !(options & EFIBOOT_ABBREV_HD))) {
-               sz = efidp_make_edd10(buf, size, info.edd10_devicenum);
+               sz = efidp_make_edd10(buf, size, dev->edd10_devicenum);
                if (sz < 0) {
                        efi_error("could not make EDD 1.0 device path");
                        goto err;
@@ -221,18 +254,13 @@ efi_va_generate_file_device_path_from_esp(uint8_t *buf, ssize_t size,
                off = sz;
        } else if (!(options & EFIBOOT_ABBREV_FILE)
                   && !(options & EFIBOOT_ABBREV_HD)) {
-               rc = set_disk_and_part_name(&info);
-               if (rc < 0) {
-                       efi_error("could not set disk and partition name");
-                       goto err;
-               }
 
                /*
                 * We're probably on a modern kernel, so just parse the
                 * symlink from /sys/dev/block/$major:$minor and get it
                 * from there.
                 */
-               sz = make_blockdev_path(buf, size, &info);
+               sz = make_blockdev_path(buf, size, dev);
                if (sz < 0) {
                        efi_error("could not create device path");
                        goto err;
@@ -240,19 +268,12 @@ efi_va_generate_file_device_path_from_esp(uint8_t *buf, ssize_t size,
                off += sz;
        }
 
-       if ((!(options & EFIBOOT_ABBREV_FILE) && info.part_name) ||
-           ((options & EFIBOOT_ABBREV_HD) && ! info.part_name)) {
+       if ((!(options & EFIBOOT_ABBREV_FILE) && dev->part_name) ||
+           ((options & EFIBOOT_ABBREV_HD) && ! dev->part_name)) {
                int disk_fd;
                int saved_errno;
-               int rc;
-
-               rc = set_disk_and_part_name(&info);
-               if (rc < 0) {
-                       efi_error("could not set disk and partition name");
-                       goto err;
-               }
 
-               disk_fd = open_disk(&info,
+               disk_fd = open_disk(dev,
                                    (options & EFIBOOT_OPTIONS_WRITE_SIGNATURE)
                                     ? O_RDWR : O_RDONLY);
                if (disk_fd < 0) {
@@ -260,7 +281,7 @@ efi_va_generate_file_device_path_from_esp(uint8_t *buf, ssize_t size,
                        goto err;
                }
 
-               sz = make_hd_dn(buf, size, off, disk_fd, info.part, options);
+               sz = make_hd_dn(buf, size, off, disk_fd, dev->part, options);
                saved_errno = errno;
                close(disk_fd);
                errno = saved_errno;
@@ -289,19 +310,12 @@ efi_va_generate_file_device_path_from_esp(uint8_t *buf, ssize_t size,
        ret = off;
 err:
        saved_errno = errno;
-       if (info.disk_name) {
-               free(info.disk_name);
-               info.disk_name = NULL;
-       }
-
-       if (info.part_name) {
-               free(info.part_name);
-               info.part_name = NULL;
-       }
-
+       if (dev)
+               device_free(dev);
        if (fd >= 0)
                close(fd);
        errno = saved_errno;
+       debug(DEBUG, "= %zd", ret);
        return ret;
 }
 
@@ -352,12 +366,6 @@ efi_generate_file_device_path(uint8_t *buf, ssize_t size,
                goto err;
        }
 
-       rc = get_partition_number(child_devpath);
-       if (rc < 0) {
-               efi_error("could not get partition number for device");
-               goto err;
-       }
-
        va_start(ap, options);
 
        if (!strcmp(parent_devpath, "/dev/block"))
index 61f0362..deac512 100644 (file)
@@ -25,6 +25,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -239,8 +240,24 @@ get_partition_info(int fd, uint32_t options,
        return rc;
 }
 
+bool HIDDEN
+is_partitioned(int fd)
+{
+       int rc;
+       uint32_t options = 0;
+       uint32_t part = 1;
+       uint64_t start = 0, size = 0;
+       uint8_t signature = 0, mbr_type = 0, signature_type = 0;
+
+       rc = get_partition_info(fd, options, part, &start, &size,
+                               &signature, &mbr_type, &signature_type);
+       if (rc < 0)
+               return false;
+       return true;
+}
+
 ssize_t HIDDEN
-_make_hd_dn(uint8_t *buf, ssize_t size, int fd, uint32_t partition,
+_make_hd_dn(uint8_t *buf, ssize_t size, int fd, int32_t partition,
            uint32_t options)
 {
        uint64_t part_start=0, part_size = 0;
@@ -249,8 +266,10 @@ _make_hd_dn(uint8_t *buf, ssize_t size, int fd, uint32_t partition,
 
        errno = 0;
 
-       rc = get_partition_info(fd, options,
-                               partition > 0 ? partition : 1, &part_start,
+       if (partition <= 0)
+               return 0;
+
+       rc = get_partition_info(fd, options, partition, &part_start,
                                &part_size, signature, &format,
                                &signature_type);
        if (rc < 0) {
@@ -258,8 +277,8 @@ _make_hd_dn(uint8_t *buf, ssize_t size, int fd, uint32_t partition,
                return rc;
        }
 
-       rc = efidp_make_hd(buf, size, partition>0?partition:1, part_start,
-                          part_size, signature, format, signature_type);
+       rc = efidp_make_hd(buf, size, partition, part_start, part_size,
+                          signature, format, signature_type);
        if (rc < 0)
                efi_error("could not make HD DP node");
        return rc;
index 3635a23..c040cc9 100644 (file)
 #ifndef _EFIBOOT_DISK_H
 #define _EFIBOOT_DISK_H
 
+extern bool HIDDEN is_partitioned(int fd);
+
 extern HIDDEN ssize_t _make_hd_dn(uint8_t *buf, ssize_t size, int fd,
-                                  uint32_t partition, uint32_t options);
+                                  int32_t partition, uint32_t options);
 #define make_hd_dn(buf, size, off, fd, partition, option) \
        _make_hd_dn(((buf)+(off)), ((size)?((size)-(off)):0), (fd),\
                    (partition), (options))
index 2d3108e..80f2292 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/sockios.h>
 #include <net/if.h>
 #include <scsi/scsi.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include "efiboot.h"
 
 int HIDDEN
-set_disk_and_part_name(struct disk_info *info)
-{
-       char *linkbuf;
-       ssize_t rc;
-
-       rc = sysfs_readlink(&linkbuf, "dev/block/%"PRIu64":%"PRIu32,
-                     info->major, info->minor);
-       if (rc < 0 || !linkbuf)
-               return -1;
-
-       char *ultimate;
-       ultimate = strrchr(linkbuf, '/');
-       if (!ultimate) {
-               errno = EINVAL;
-               return -1;
-       }
-       *ultimate = '\0';
-       ultimate++;
-
-       char *penultimate;
-       penultimate = strrchr(linkbuf, '/');
-       if (!penultimate) {
-               errno = EINVAL;
-               return -1;
-       }
-       penultimate++;
-
-       /*
-        * If there's a better way to figure this out, it'd be good, because
-        * I don't want to have to change this for every new disk type...
-        */
-        if (!strcmp(penultimate, "block")) {
-               if (!info->disk_name) {
-                       info->disk_name = strdup(ultimate);
-                       if (!info->disk_name)
-                               return -1;
-               }
-               if (!info->part_name) {
-                       rc = asprintf(&info->part_name, "%s%d", info->disk_name,
-                                     info->part);
-                       if (rc < 0)
-                               return -1;
-               }
-       } else {
-               if (!info->disk_name) {
-                       info->disk_name = strdup(penultimate);
-                       if (!info->disk_name)
-                               return -1;
-               }
-               if (!info->part_name) {
-                       info->part_name = strdup(ultimate);
-                       if (!info->part_name)
-                               return -1;
-               }
-       }
-
-       return 0;
-}
-
-int HIDDEN
-get_partition_number(const char *devpath)
-{
-       struct stat statbuf = { 0, };
-       int rc;
-       unsigned int maj, min;
-       char *linkbuf;
-       uint8_t *partbuf = NULL; /* XXX this is wrong and the code below will be wrong */
-       int ret = -1;
-
-       rc = stat(devpath, &statbuf);
-       if (rc < 0) {
-               efi_error("couldn't stat %s\n", devpath);
-               return -1;
-       }
-
-       if (!S_ISBLK(statbuf.st_mode)) {
-               efi_error("%s is not a block device\n", devpath);
-               errno = EINVAL;
-               return -1;
-       }
-
-       maj = major(statbuf.st_rdev);
-       min = minor(statbuf.st_rdev);
-
-       rc = sysfs_readlink(&linkbuf, "dev/block/%u:%u", maj, min);
-       if (rc < 0 || !linkbuf) {
-               efi_error("couldn't get partition number for %u:%u", maj, min);
-               return -1;
-       }
-
-       rc = read_sysfs_file(&partbuf, "dev/block/%s/partition", linkbuf);
-       if (rc < 0) {
-               efi_error("couldn't get partition number for %s", linkbuf);
-               /* This isn't strictly an error for e.g. nvdimm pmem devices */
-               return 0;
-       }
-
-       rc = sscanf((char *)partbuf, "%d\n", &ret);
-       if (rc != 1) {
-               efi_error("couldn't get partition number for %s", partbuf);
-               return -1;
-       }
-       return ret;
-}
-
-int HIDDEN
 find_parent_devpath(const char * const child, char **parent)
 {
        int ret;
@@ -187,291 +82,459 @@ find_parent_devpath(const char * const child, char **parent)
        return 0;
 }
 
-static ssize_t
-make_pci_path(uint8_t *buf, ssize_t size, char *pathstr, ssize_t *pathoff)
+int HIDDEN
+set_part_name(struct device *dev, const char * const fmt, ...)
 {
-       ssize_t off=0, sz=0;
-       ssize_t poff = 0;
-       int psz;
-       int rc;
+        ssize_t rc;
+        va_list ap;
+        int error;
+
+        if (dev->part <= 0)
+                return 0;
+
+        va_start(ap, fmt);
+        rc = vasprintf(&dev->part_name, fmt, ap);
+        error = errno;
+        va_end(ap);
+        errno = error;
+        if (rc < 0)
+                efi_error("could not allocate memory");
+
+        return rc;
+}
 
-       if (pathstr == NULL || pathoff == NULL || pathstr[0] == '\0') {
-               errno = EINVAL;
-               return -1;
-       }
-
-       *pathoff = 0;
-
-       uint16_t root_domain;
-       uint8_t root_bus;
-       uint32_t acpi_hid = 0;
-       uint64_t acpi_uid_int = 0;
-       /*
-        * find the pci root domain and port; they basically look like:
-        * pci0000:00/
-        *    ^d   ^p
-        */
-       rc = sscanf(pathstr+poff, "pci%hx:%hhx/%n", &root_domain,
-                   &root_bus, &psz);
-       if (rc != 2)
-               return -1;
-       poff += psz;
+int HIDDEN
+reset_part_name(struct device *dev)
+{
+        char *part = NULL;
+        int rc;
+
+        if (dev->part_name) {
+                free(dev->part_name);
+                dev->part_name = NULL;
+        }
+
+        if (dev->part < 1)
+                return 0;
+
+        if (dev->probes[dev->n_probes]->make_part_name) {
+                part = dev->probes[dev->n_probes]->make_part_name(dev);
+                dev->part_name = part;
+                rc = 0;
+        } else {
+                rc = asprintf(&dev->part_name, "%s%d",
+                              dev->disk_name, dev->part);
+                if (rc < 0)
+                        efi_error("could not allocate memory");
+        }
+        return rc;
+}
 
-       uint8_t *fbuf = NULL;
-       rc = read_sysfs_file(&fbuf,
-                            "devices/pci%04hx:%02hhx/firmware_node/hid",
-                            root_domain, root_bus);
-       if (rc < 0 || fbuf == NULL)
-               return -1;
+int HIDDEN
+set_part(struct device *dev, int value)
+{
+        int rc;
 
-       uint16_t tmp16 = 0;
-       rc = sscanf((char *)fbuf, "PNP%hx", &tmp16);
-       if (rc != 1)
-               return -1;
-       acpi_hid = EFIDP_EFI_PNP_ID(tmp16);
-
-       /* Apparently basically nothing can look up a PcieRoot() node,
-        * because they just check _CID.  So since _CID for the root pretty
-        * much always has to be PNP0A03 anyway, just use that no matter
-        * what.
-        */
-       if (acpi_hid == EFIDP_ACPI_PCIE_ROOT_HID)
-               acpi_hid = EFIDP_ACPI_PCI_ROOT_HID;
-
-       errno = 0;
-       fbuf = NULL;
-       int use_uid_str = 0;
-       rc = read_sysfs_file(&fbuf,
-                            "devices/pci%04hx:%02hhx/firmware_node/uid",
-                            root_domain, root_bus);
-       if ((rc <= 0 && errno != ENOENT) || fbuf == NULL)
-               return -1;
-       if (rc > 0) {
-               rc = sscanf((char *)fbuf, "%"PRIu64"\n", &acpi_uid_int);
-               if (rc != 1) {
-                       /* kernel uses "%s\n" to print it, so there
-                        * should always be some value and a newline... */
-                       int l = strlen((char *)buf);
-                       if (l >= 1) {
-                               use_uid_str=1;
-                               fbuf[l-1] = '\0';
-                       }
-               }
-       }
-       errno = 0;
-
-       if (use_uid_str) {
-               sz = efidp_make_acpi_hid_ex(buf+off, size?size-off:0,
-                                           acpi_hid, 0, 0, "", (char *)fbuf,
-                                           "");
-       } else {
-               sz = efidp_make_acpi_hid(buf+off, size?size-off:0,
-                                        acpi_hid, acpi_uid_int);
-       }
-       if (sz < 0)
-               return -1;
-       off += sz;
-
-       /* find the pci domain/bus/device/function:
-        * 0000:00:01.0/0000:01:00.0/
-        *              ^d   ^b ^d ^f (of the last one in the series)
-        */
-       int found=0;
-       while (1) {
-               uint16_t domain;
-               uint8_t bus, device, function;
-               rc = sscanf(pathstr+poff, "%hx:%hhx:%hhx.%hhx/%n",
-                           &domain, &bus, &device, &function, &psz);
-               if (rc != 4)
-                       break;
-               found=1;
-               poff += psz;
-
-               sz = efidp_make_pci(buf+off, size?size-off:0, device, function);
-               if (sz < 0)
-                       return -1;
-               off += sz;
-       }
-       if (!found)
-               return -1;
+        if (dev->part == value)
+                return 0;
 
-       *pathoff = poff;
-       return off;
+        dev->part = value;
+        rc = reset_part_name(dev);
+        if (rc < 0)
+                efi_error("reset_part_name() failed");
+
+        return rc;
 }
 
 int HIDDEN
-make_blockdev_path(uint8_t *buf, ssize_t size, struct disk_info *info)
+set_disk_name(struct device *dev, const char * const fmt, ...)
 {
-       char *linkbuf = NULL;
-       char *driverbuf = NULL;
-       ssize_t off=0, sz=0, loff=0;
-       int lsz = 0;
-       int rc;
-       int found = 0;
-
-       rc = sysfs_readlink(&linkbuf, "dev/block/%"PRIu64":%u",
-                           info->major, info->minor);
-       if (rc < 0 || !linkbuf) {
-               efi_error("couldn't read link for /sys/dev/block/%"PRIu64":%u",
-                         info->major, info->minor);
-               return -1;
-       }
-
-       if (!found) {
-               rc = sscanf(linkbuf+loff, "../../devices/%n", &lsz);
-               if (rc != 0) {
-                       efi_error("scanf(\"%s\", %s, &lz) failed",
-                                 linkbuf+loff, "../../devices/%n");
-                       return -1;
-               }
-               loff += lsz;
-
-               ssize_t tmplsz=0;
-               sz = make_pci_path(buf, size, linkbuf+loff, &tmplsz);
-               if (sz < 0)
-                       return -1;
-               loff += tmplsz;
-               off += sz;
-
-               char *tmppath;
-               tmppath = strdupa(linkbuf);
-               if (!tmppath)
-                       return -1;
-               tmppath[loff] = '\0';
-               rc = sysfs_readlink(&driverbuf, "dev/block/%s/driver",
-                                   tmppath);
-               if (rc < 0 || !driverbuf)
-                       return -1;
-
-               char *driver = strrchr(driverbuf, '/');
-               if (!driver || !*driver)
-                       return -1;
-               driver+=1;
-
-       }
-
-       if (!found) {
-               errno = ENOENT;
-               return -1;
-       }
-
-       return off;
+        ssize_t rc;
+        va_list ap;
+        int error;
+
+        va_start(ap, fmt);
+        rc = vasprintf(&dev->disk_name, fmt, ap);
+        error = errno;
+        va_end(ap);
+        errno = error;
+        if (rc < 0)
+                efi_error("could not allocate memory");
+
+        return rc;
 }
 
 int HIDDEN
-eb_disk_info_from_fd(int fd, struct disk_info *info)
+set_disk_and_part_name(struct device *dev)
 {
-       struct stat buf;
-       int rc;
-
-       memset(info, 0, sizeof *info);
-
-       info->pci_root.pci_root_domain = 0xffff;
-       info->pci_root.pci_root_bus = 0xff;
-
-       memset(&buf, 0, sizeof(struct stat));
-       rc = fstat(fd, &buf);
-       if (rc == -1) {
-               efi_error("fstat() failed: %m");
-               return 1;
-       }
-       if (S_ISBLK(buf.st_mode)) {
-               info->major = major(buf.st_rdev);
-               info->minor = minor(buf.st_rdev);
-       } else if (S_ISREG(buf.st_mode)) {
-               info->major = major(buf.st_dev);
-               info->minor = minor(buf.st_dev);
-       } else {
-               efi_error("Cannot stat non-block or non-regular file");
-               return 1;
-       }
-
-       errno = ENOSYS;
-       return -1;
+        /*
+         * results are like such:
+         * maj:min -> ../../devices/pci$PCI_STUFF/$BLOCKDEV_STUFF/block/$DISK/$PART
+         */
+
+        char *ultimate = pathseg(dev->link, -1);
+        char *penultimate = pathseg(dev->link, -2);
+        char *approximate = pathseg(dev->link, -3);
+        char *proximate = pathseg(dev->link, -4);
+
+        errno = 0;
+        debug(DEBUG, "dev->disk_name:%p dev->part_name:%p", dev->disk_name, dev->part_name);
+        debug(DEBUG, "dev->part:%d", dev->part);
+        debug(DEBUG, "ultimate:%s", ultimate);
+        debug(DEBUG, "penultimate:%s", penultimate);
+        debug(DEBUG, "approximate:%s", approximate);
+        debug(DEBUG, "proximate:%s", proximate);
+
+        if (!strcmp(proximate, "nvme") || !strcmp(approximate, "block")) {
+                /*
+                 * 259:1 -> ../../devices/pci0000:00/0000:00:1d.0/0000:05:00.0/nvme/nvme0/nvme0n1/nvme0n1p1
+                 * 8:1 -> ../../devices/pci0000:00/0000:00:17.0/ata2/host1/target1:0:0/1:0:0:0/block/sda/sda1
+                 * 8:33 -> ../../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/sdc1
+                 * 252:1 -> ../../devices/pci0000:00/0000:00:07.0/virtio2/block/vda/vda1
+                 * 259:3 -> ../../devices/LNXSYSTM:00/LNXSYBUS:00/ACPI0012:00/ndbus0/region11/btt11.0/block/pmem11s/pmem11s1
+                 */
+                set_disk_name(dev, "%s", penultimate);
+                set_part_name(dev, "%s", ultimate);
+                debug(DEBUG, "disk:%s part:%s", penultimate, ultimate);
+        } else if (!strcmp(approximate, "nvme")) {
+                /*
+                 * 259:0 -> ../../devices/pci0000:00/0000:00:1d.0/0000:05:00.0/nvme/nvme0/nvme0n1
+                 */
+                set_disk_name(dev, "%s", ultimate);
+                set_part_name(dev, "%sp%d", ultimate, dev->part);
+                debug(DEBUG, "disk:%s part:%sp%d", ultimate, ultimate, dev->part);
+        } else if (!strcmp(penultimate, "block")) {
+                /*
+                 * 253:0 -> ../../devices/virtual/block/dm-0 (... I guess)
+                 * 8:0 -> ../../devices/pci0000:00/0000:00:17.0/ata2/host1/target1:0:0/1:0:0:0/block/sda
+                 * 11:0 -> ../../devices/pci0000:00/0000:00:11.5/ata3/host2/target2:0:0/2:0:0:0/block/sr0
+                 * 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
+                 * 252:0 -> ../../devices/pci0000:00/0000:00:07.0/virtio2/block/vda
+                 * 259:0 -> ../../devices/LNXSYSTM:00/LNXSYBUS:00/ACPI0012:00/ndbus0/region9/btt9.0/block/pmem9s
+                 * 259:1 -> ../../devices/LNXSYSTM:00/LNXSYBUS:00/ACPI0012:00/ndbus0/region11/btt11.0/block/pmem11s
+                 */
+                set_disk_name(dev, "%s", ultimate);
+                set_part_name(dev, "%s%d", ultimate, dev->part);
+                debug(DEBUG, "disk:%s part:%s%d", ultimate, ultimate, dev->part);
+        } else if (!strcmp(approximate, "mtd")) {
+                /*
+                 * 31:0 -> ../../devices/platform/1e000000.palmbus/1e000b00.spi/spi_master/spi32766/spi32766.0/mtd/mtd0/mtdblock0
+                 */
+                set_disk_name(dev, "%s", ultimate);
+                debug(DEBUG, "disk:%s", ultimate);
+        }
+
+        return 0;
 }
 
-static ssize_t
-make_net_pci_path(uint8_t *buf, ssize_t size, const char * const ifname)
+static struct dev_probe *dev_probes[] = {
+        /*
+         * pmem needs to be before PCI, so if it provides root it'll
+         * be found first.
+         */
+        &pmem_parser,
+        &pci_parser,
+        &virtblk_parser,
+        &sas_parser,
+        &sata_parser,
+        &nvme_parser,
+        &ata_parser,
+        &scsi_parser,
+        &i2o_parser,
+        NULL
+};
+
+static inline bool
+supports_iface(struct dev_probe *probe, enum interface_type iftype)
 {
-       char *linkbuf = NULL;
-       ssize_t off=0, sz=0, loff=0;
-       int lsz = 0;
-       int rc;
-
-       rc = sysfs_readlink(&linkbuf, "class/net/%s", ifname);
-       if (rc < 0 || !linkbuf)
-               return -1;
+        for (unsigned int i = 0; probe->iftypes[i] != unknown; i++)
+                if (probe->iftypes[i] == iftype)
+                        return true;
+        return false;
+}
 
-       /*
-        * the sysfs path basically looks like:
-        * ../../devices/$PCI_STUFF/net/$IFACE
-        */
-       rc = sscanf(linkbuf, "../../devices/%n", &lsz);
-       if (rc != 0)
-               return -1;
-       loff += lsz;
+void HIDDEN
+device_free(struct device *dev)
+{
+        if (!dev)
+                return;
+        if (dev->link)
+                free(dev->link);
+
+        if (dev->device)
+                free(dev->device);
+
+        if (dev->driver)
+                free(dev->driver);
+
+        if (dev->probes)
+                free(dev->probes);
+
+        if (dev->interface_type == network) {
+                if (dev->ifname)
+                        free(dev->ifname);
+        } else {
+                if (dev->disk_name)
+                        free(dev->disk_name);
+                if (dev->part_name)
+                        free(dev->part_name);
+        }
+
+        if (dev->pci_dev)
+                free(dev->pci_dev);
+
+        memset(dev, 0, sizeof(*dev));
+        free(dev);
+}
 
-       ssize_t tmplsz = 0;
-       sz = make_pci_path(buf, size, linkbuf+loff, &tmplsz);
-       if (sz < 0)
-               return -1;
-       off += sz;
-       /*
-        * clang-analyzer complains about this because they don't believe in
-        * writing code to avoid introducing bugs later.
-        */
-       //loff += tmplsz;
-
-       return off;
+struct device HIDDEN
+*device_get(int fd, int partition)
+{
+        struct device *dev;
+        char *linkbuf = NULL, *tmpbuf = NULL;
+        unsigned int i, n = 0;
+        int rc;
+
+        size_t nmemb = (sizeof(dev_probes)
+                        / sizeof(dev_probes[0])) + 1;
+
+        dev = calloc(1, sizeof(*dev));
+        if (!dev) {
+                efi_error("could not allocate %zd bytes", sizeof(*dev));
+                return NULL;
+        }
+
+        dev->part = partition;
+        debug(DEBUG, "partition:%d dev->part:%d", partition, dev->part);
+        dev->probes = calloc(nmemb, sizeof(struct dev_probe *));
+        if (!dev->probes) {
+                efi_error("could not allocate %zd bytes",
+                          nmemb * sizeof(struct dev_probe *));
+                goto err;
+        }
+
+        rc = fstat(fd, &dev->stat);
+        if (rc < 0) {
+                efi_error("fstat(%d) failed", fd);
+                goto err;
+        }
+
+        dev->pci_root.pci_root_domain = 0xffff;
+        dev->pci_root.pci_root_bus = 0xff;
+
+        if (S_ISBLK(dev->stat.st_mode)) {
+                dev->major = major(dev->stat.st_rdev);
+                dev->minor = minor(dev->stat.st_rdev);
+        } else if (S_ISREG(dev->stat.st_mode)) {
+                dev->major = major(dev->stat.st_dev);
+                dev->minor = minor(dev->stat.st_dev);
+        } else {
+                efi_error("device is not a block device or regular file");
+                goto err;
+        }
+
+        rc = sysfs_readlink(&linkbuf, "dev/block/%"PRIu64":%"PRIu32,
+                            dev->major, dev->minor);
+        if (rc < 0 || !linkbuf) {
+                efi_error("readlink of /sys/dev/block/%"PRIu64":%"PRIu32" failed",
+                          dev->major, dev->minor);
+                goto err;
+        }
+
+        dev->link = strdup(linkbuf);
+        if (!dev->link) {
+                efi_error("strdup(\"%s\") failed", linkbuf);
+                goto err;
+        }
+        debug(DEBUG, "dev->link: %s\n", dev->link);
+
+        if (dev->part == -1) {
+                rc = read_sysfs_file(&tmpbuf, "dev/block/%s/partition", dev->link);
+                if (rc < 0) {
+                        efi_error("device has no /partition node; not a partition");
+                } else {
+                        rc = sscanf((char *)tmpbuf, "%d\n", &dev->part);
+                        if (rc != 1)
+                                efi_error("couldn't parse partition number for %s", tmpbuf);
+                }
+        }
+
+        rc = set_disk_and_part_name(dev);
+        if (rc < 0) {
+                efi_error("could not set disk and partition names");
+                goto err;
+        }
+        debug(DEBUG, "dev->disk_name: %s", dev->disk_name);
+        debug(DEBUG, "dev->part_name: %s", dev->part_name);
+
+        rc = sysfs_readlink(&tmpbuf, "block/%s/device", dev->disk_name);
+        if (rc < 0 || !tmpbuf) {
+                efi_error("readlink of /sys/block/%s/device failed",
+                          dev->disk_name);
+                goto err;
+        }
+
+        dev->device = strdup(tmpbuf);
+        if (!dev->device) {
+                efi_error("strdup(\"%s\") failed", tmpbuf);
+                goto err;
+        }
+
+        rc = sysfs_readlink(&tmpbuf, "block/%s/device/driver", dev->disk_name);
+        if (rc < 0 || !tmpbuf) {
+                efi_error("readlink of /sys/block/%s/device/driver failed",
+                          dev->disk_name);
+                goto err;
+        }
+
+        linkbuf = pathseg(tmpbuf, -1);
+        if (!linkbuf) {
+                efi_error("could not get segment -1 of \"%s\"", tmpbuf);
+                goto err;
+        }
+
+        dev->driver = strdup(linkbuf);
+        if (!dev->driver) {
+                efi_error("strdup(\"%s\") failed", linkbuf);
+                goto err;
+        }
+
+        const char *current = dev->link;
+        bool needs_root = true;
+
+        debug(DEBUG, "searching for device nodes in %s", dev->link);
+        for (i = 0; dev_probes[i] && dev_probes[i]->parse; i++) {
+                struct dev_probe *probe = dev_probes[i];
+                ssize_t pos;
+
+                if (!needs_root && (probe->flags & DEV_PROVIDES_ROOT)) {
+                        debug(DEBUG, "not testing %s because flags is 0x%x", probe->name, probe->flags);
+                        continue;
+                }
+
+                debug(DEBUG, "trying %s", probe->name);
+                pos = probe->parse(dev, current);
+                if (pos < 0) {
+                        efi_error("parsing %s failed", probe->name);
+                        goto err;
+                } else if (pos == 0) {
+                        continue;
+                }
+                debug(DEBUG, "%s matched %s", probe->name, current);
+
+                if (probe->flags & DEV_PROVIDES_HD)
+                        needs_root = false;
+                dev->probes[n++] = dev_probes[i];
+                current += pos;
+                debug(DEBUG, "current:%s", current);
+
+                if (!*current || !strncmp(current, "block/", 6))
+                        break;
+        }
+
+        if (dev->interface_type == unknown) {
+                efi_error("unknown storage interface");
+                errno = ENOSYS;
+                goto err;
+        }
+
+        return dev;
+err:
+        device_free(dev);
+        return NULL;
 }
 
-ssize_t HIDDEN
-make_mac_path(uint8_t *buf, ssize_t size, const char * const ifname)
+int HIDDEN
+make_blockdev_path(uint8_t *buf, ssize_t size, struct device *dev)
 {
-       struct ifreq ifr;
-       struct ethtool_drvinfo drvinfo = { 0, };
-       int fd, rc;
-       ssize_t ret = -1, sz, off=0;
-       char busname[PATH_MAX+1] = "";
-
-       memset(&ifr, 0, sizeof (ifr));
-       strncpy(ifr.ifr_name, ifname, IF_NAMESIZE);
-       ifr.ifr_name[IF_NAMESIZE-1] = '\0';
-       drvinfo.cmd = ETHTOOL_GDRVINFO;
-       ifr.ifr_data = (caddr_t)&drvinfo;
-
-       fd = socket(AF_INET, SOCK_DGRAM, 0);
-       if (fd < 0)
-               return -1;
+        ssize_t off = 0;
 
-       rc = ioctl(fd, SIOCETHTOOL, &ifr);
-       if (rc < 0)
-               goto err;
+        debug(DEBUG, "entry buf:%p size:%zd", buf, size);
 
-       strncpy(busname, drvinfo.bus_info, PATH_MAX);
+        for (unsigned int i = 0; dev->probes[i] &&
+                                 dev->probes[i]->parse; i++) {
+                struct dev_probe *probe = dev->probes[i];
+                ssize_t sz;
 
-       rc = ioctl(fd, SIOCGIFHWADDR, &ifr);
-       if (rc < 0)
-               goto err;
+                if (!probe->create)
+                        continue;
 
-       sz = make_net_pci_path(buf, size, ifname);
-       if (sz < 0)
-               goto err;
-       off += sz;
+                sz = probe->create(dev, buf + off, size ? size - off : 0, 0);
+                if (sz < 0) {
+                        efi_error("could not create %s device path",
+                                  probe->name);
+                        return sz;
+                }
+                off += sz;
+        }
 
-       sz = efidp_make_mac_addr(buf+off, size?size-off:0,
-                                ifr.ifr_ifru.ifru_hwaddr.sa_family,
-                                (uint8_t *)ifr.ifr_ifru.ifru_hwaddr.sa_data,
-                                sizeof(ifr.ifr_ifru.ifru_hwaddr.sa_data));
-       if (sz < 0)
-               goto err;
+        debug(DEBUG, "= %zd", off);
+
+        return off;
+}
 
-       off += sz;
-       ret = off;
+ssize_t HIDDEN
+make_mac_path(uint8_t *buf, ssize_t size, const char * const ifname)
+{
+        struct ifreq ifr;
+        struct ethtool_drvinfo drvinfo = { 0, };
+        int fd, rc;
+        ssize_t ret = -1, sz, off = 0;
+        char busname[PATH_MAX+1] = "";
+        struct device dev;
+
+        memset(&dev, 0, sizeof (dev));
+        dev.interface_type = network;
+        dev.ifname = strdup(ifname);
+        if (!dev.ifname)
+                return -1;
+
+        /*
+         * find the device link, which looks like:
+         * ../../devices/$PCI_STUFF/net/$IFACE
+         */
+        rc = sysfs_readlink(&dev.link, "class/net/%s", ifname);
+        if (rc < 0 || !dev.link)
+                return -1;
+
+        memset(&ifr, 0, sizeof (ifr));
+        strncpy(ifr.ifr_name, ifname, IF_NAMESIZE);
+        ifr.ifr_name[IF_NAMESIZE-1] = '\0';
+        drvinfo.cmd = ETHTOOL_GDRVINFO;
+        ifr.ifr_data = (caddr_t)&drvinfo;
+
+        fd = socket(AF_INET, SOCK_DGRAM, 0);
+        if (fd < 0)
+                return -1;
+
+        rc = ioctl(fd, SIOCETHTOOL, &ifr);
+        if (rc < 0)
+                goto err;
+
+        strncpy(busname, drvinfo.bus_info, PATH_MAX);
+
+        rc = ioctl(fd, SIOCGIFHWADDR, &ifr);
+        if (rc < 0)
+                goto err;
+
+        sz = pci_parser.create(&dev, buf, size, off);
+        if (sz < 0)
+                goto err;
+        off += sz;
+
+        sz = efidp_make_mac_addr(buf+off, size?size-off:0,
+                                 ifr.ifr_ifru.ifru_hwaddr.sa_family,
+                                 (uint8_t *)ifr.ifr_ifru.ifru_hwaddr.sa_data,
+                                 sizeof(ifr.ifr_ifru.ifru_hwaddr.sa_data));
+        if (sz < 0)
+                goto err;
+
+        off += sz;
+        ret = off;
 err:
-       if (fd >= 0)
-               close(fd);
-       return ret;
+        if (fd >= 0)
+                close(fd);
+        return ret;
 }
 
 /************************************************************
index 0577f62..99ac438 100644 (file)
@@ -72,34 +72,8 @@ struct nvme_info {
         uint8_t eui[8];
 };
 
-struct disk_info {
-       int interface_type;
-       unsigned int controllernum;
-       unsigned int disknum;
-       unsigned char part;
-       uint64_t major;
-       uint32_t minor;
-       uint32_t edd10_devicenum;
-
-       struct pci_root_info pci_root;
-       struct pci_dev_info pci_dev;
-
-       union {
-               struct scsi_info scsi_info;
-               struct sas_info sas_info;
-               struct sata_info sata_info;
-               struct nvme_info nvme_info;
-               efi_guid_t nvdimm_label;
-       };
-
-       char *disk_name;
-       char *part_name;
-};
-
 enum interface_type {
-        unknown = 0,
-        bus_type_unknown = 0, /* XXX remove later */
-        interface_type_unknown = 0, /* XXX remove later */
+        unknown,
         isa, pci, network,
         ata, atapi, scsi, sata, sas,
         usb, i1394, fibre, i2o,
@@ -148,15 +122,18 @@ struct device {
         };
 };
 
-extern int HIDDEN eb_disk_info_from_fd(int fd, struct disk_info *info);
-extern int HIDDEN set_disk_and_part_name(struct disk_info *info);
+extern struct device HIDDEN *device_get(int fd, int partition);
+extern void HIDDEN device_free(struct device *dev);
+extern int HIDDEN set_disk_and_part_name(struct device *dev);
+extern int HIDDEN set_part(struct device *dev, int value);
+extern int HIDDEN set_part_name(struct device *dev, const char * const fmt, ...);
+extern int HIDDEN set_disk_name(struct device *dev, const char * const fmt, ...);
+
 extern int HIDDEN make_blockdev_path(uint8_t *buf, ssize_t size,
-                                     struct disk_info *info);
+                                     struct device *dev);
 
 extern int HIDDEN eb_nvme_ns_id(int fd, uint32_t *ns_id);
 
-extern int HIDDEN get_partition_number(const char *devpath);
-
 int HIDDEN get_sector_size(int filedes);
 
 extern int HIDDEN find_parent_devpath(const char * const child,
@@ -256,8 +233,6 @@ extern ssize_t parse_scsi_link(const char *current, uint32_t *host,
                                       uint32_t *bus, uint32_t *device,
                                       uint32_t *target, uint64_t *lun);
 
-#define set_part(x, y) /* XXX remove later */
-
 /* device support implementations */
 extern struct dev_probe pmem_parser;
 extern struct dev_probe pci_parser;
@@ -269,5 +244,4 @@ extern struct dev_probe i2o_parser;
 extern struct dev_probe scsi_parser;
 extern struct dev_probe ata_parser;
 
-
 #endif /* _EFIBOOT_LINUX_H */