}
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;
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);
}
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;
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;
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) {
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;
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;
}
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"))
#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;
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;
}
/************************************************************