OSDN Git Service

Deal with devices that don't have a ->device link in sysfs
[android-x86/external-efivar.git] / src / linux.c
index 1e7db4e..6d405af 100644 (file)
@@ -117,7 +117,9 @@ reset_part_name(struct device *dev)
         if (dev->part < 1)
                 return 0;
 
-        if (dev->probes[dev->n_probes]->make_part_name) {
+        if (dev->n_probes > 0 &&
+            dev->probes[dev->n_probes-1] &&
+            dev->probes[dev->n_probes-1]->make_part_name) {
                 part = dev->probes[dev->n_probes]->make_part_name(dev);
                 dev->part_name = part;
                 rc = 0;
@@ -178,12 +180,12 @@ set_disk_and_part_name(struct device *dev)
         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 ? : "");
+        debug("dev->disk_name:%p dev->part_name:%p", dev->disk_name, dev->part_name);
+        debug("dev->part:%d", dev->part);
+        debug("ultimate:\"%s\"", ultimate ? : "");
+        debug("penultimate:\"%s\"", penultimate ? : "");
+        debug("approximate:\"%s\"", approximate ? : "");
+        debug("proximate:\"%s\"", proximate ? : "");
 
         if (ultimate && penultimate &&
             ((proximate && !strcmp(proximate, "nvme")) ||
@@ -197,14 +199,14 @@ set_disk_and_part_name(struct device *dev)
                  */
                 set_disk_name(dev, "%s", penultimate);
                 set_part_name(dev, "%s", ultimate);
-                debug(DEBUG, "disk:%s part:%s", penultimate, ultimate);
+                debug("disk:%s part:%s", penultimate, ultimate);
         } else if (ultimate && approximate && !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);
+                debug("disk:%s part:%sp%d", ultimate, ultimate, dev->part);
         } else if (ultimate && penultimate && !strcmp(penultimate, "block")) {
                 /*
                  * 253:0 -> ../../devices/virtual/block/dm-0 (... I guess)
@@ -217,13 +219,13 @@ set_disk_and_part_name(struct device *dev)
                  */
                 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);
+                debug("disk:%s part:%s%d", ultimate, ultimate, dev->part);
         } else if (ultimate && approximate && !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);
+                debug("disk:%s", ultimate);
         }
 
         return 0;
@@ -246,6 +248,7 @@ static struct dev_probe *dev_probes[] = {
         &ata_parser,
         &scsi_parser,
         &i2o_parser,
+        &emmc_parser,
         NULL
 };
 
@@ -308,7 +311,8 @@ struct device HIDDEN
 {
         struct device *dev;
         char *linkbuf = NULL, *tmpbuf = NULL;
-        unsigned int i, n = 0;
+        int i = 0;
+        unsigned int n = 0;
         int rc;
 
         size_t nmemb = (sizeof(dev_probes)
@@ -321,7 +325,7 @@ struct device HIDDEN
         }
 
         dev->part = partition;
-        debug(DEBUG, "partition:%d dev->part:%d", partition, dev->part);
+        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",
@@ -362,7 +366,7 @@ struct device HIDDEN
                 efi_error("strdup(\"%s\") failed", linkbuf);
                 goto err;
         }
-        debug(DEBUG, "dev->link: %s", dev->link);
+        debug("dev->link: %s", dev->link);
 
         if (dev->part == -1) {
                 rc = read_sysfs_file(&tmpbuf, "dev/block/%s/partition", dev->link);
@@ -380,48 +384,55 @@ struct device HIDDEN
                 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);
+        debug("dev->disk_name: %s", dev->disk_name);
+        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",
+                debug("readlink of /sys/block/%s/device failed",
                           dev->disk_name);
-                goto err;
+
+                dev->device = strdup("");
+        } else {
+                dev->device = strdup(tmpbuf);
         }
 
-        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) {
-                if (errno == ENOENT) {
-                        /*
-                         * nvme, for example, will have nvme0n1/device point
-                         * at nvme0, and we need to look for device/driver
-                         * there.
-                         */
-                        rc = sysfs_readlink(&tmpbuf,
-                                            "block/%s/device/device/driver",
-                                            dev->disk_name);
-                }
+        if (dev->device[0] != 0) {
+                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);
+                        if (errno == ENOENT) {
+                                /*
+                                 * nvme, for example, will have nvme0n1/device point
+                                 * at nvme0, and we need to look for device/driver
+                                 * there.
+                                 */
+                                rc = sysfs_readlink(&tmpbuf,
+                                                    "block/%s/device/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;
                 }
-        }
 
-        linkbuf = pathseg(tmpbuf, -1);
-        if (!linkbuf) {
-                efi_error("could not get segment -1 of \"%s\"", tmpbuf);
-                goto err;
+                dev->driver = strdup(linkbuf);
+        } else {
+                dev->driver = strdup("");
         }
 
-        dev->driver = strdup(linkbuf);
         if (!dev->driver) {
                 efi_error("strdup(\"%s\") failed", linkbuf);
                 goto err;
@@ -429,41 +440,79 @@ struct device HIDDEN
 
         const char *current = dev->link;
         bool needs_root = true;
+        int last_successful_probe = -1;
 
-        debug(DEBUG, "searching for device nodes in %s", dev->link);
-        for (i = 0; dev_probes[i] && dev_probes[i]->parse; i++) {
+        debug("searching for device nodes in %s", dev->link);
+        for (i = 0;
+             dev_probes[i] && dev_probes[i]->parse && *current;
+             i++) {
                 struct dev_probe *probe = dev_probes[i];
-                ssize_t pos;
+                int pos;
 
-                if (!needs_root && (probe->flags & DEV_PROVIDES_ROOT)) {
-                        debug(DEBUG, "not testing %s because flags is 0x%x", probe->name, probe->flags);
+                if (!needs_root &&
+                    (probe->flags & DEV_PROVIDES_ROOT)) {
+                        debug("not testing %s because flags is 0x%x",
+                              probe->name, probe->flags);
                         continue;
                 }
 
-                debug(DEBUG, "trying %s", probe->name);
+                debug("trying %s", probe->name);
                 pos = probe->parse(dev, current, dev->link);
                 if (pos < 0) {
                         efi_error("parsing %s failed", probe->name);
                         goto err;
-                } else if (pos == 0) {
+                } else if (pos > 0) {
+                        debug("%s matched %s", probe->name, current);
+                        dev->flags |= probe->flags;
+
+                        if (probe->flags & DEV_PROVIDES_HD ||
+                            probe->flags & DEV_PROVIDES_ROOT ||
+                            probe->flags & DEV_ABBREV_ONLY)
+                                needs_root = false;
+
+                        dev->probes[n++] = dev_probes[i];
+                        current += pos;
+                        debug("current:%s", current);
+                        last_successful_probe = i;
+
+                        if (!*current || !strncmp(current, "block/", 6))
+                                break;
+
                         continue;
                 }
-                debug(DEBUG, "%s matched %s", probe->name, current);
-                dev->flags |= probe->flags;
-
-                if (probe->flags & DEV_PROVIDES_HD ||
-                    probe->flags & DEV_PROVIDES_ROOT ||
-                    probe->flags & DEV_ABBREV_ONLY)
-                        needs_root = false;
-                dev->probes[n++] = dev_probes[i];
-                current += pos;
-                debug(DEBUG, "current:%s", current);
-
-                if (!*current || !strncmp(current, "block/", 6))
-                        break;
+
+                debug("dev_probes[i+1]: %p dev->interface_type: %d\n",
+                      dev_probes[i+1], dev->interface_type);
+                if (dev_probes[i+1] == NULL && dev->interface_type == unknown) {
+                        pos = 0;
+                        rc = sscanf(current, "%*[^/]/%n", &pos);
+                        if (rc < 0) {
+slash_err:
+                                efi_error("Cannot parse device link segment \"%s\"", current);
+                                goto err;
+                        }
+
+                        while (current[pos] == '/')
+                                pos += 1;
+
+                        if (!current[pos])
+                                goto slash_err;
+
+                        debug("Cannot parse device link segment \"%s\"", current);
+                        debug("Skipping to \"%s\"", current + pos);
+                        debug("This means we can only create abbreviated paths");
+                        dev->flags |= DEV_ABBREV_ONLY;
+                        i = last_successful_probe;
+                        current += pos;
+
+                        if (!*current || !strncmp(current, "block/", 6))
+                                break;
+                }
         }
 
-        if (dev->interface_type == unknown) {
+        if (dev->interface_type == unknown &&
+            !(dev->flags & DEV_ABBREV_ONLY) &&
+            !strcmp(current, "block/")) {
                 efi_error("unknown storage interface");
                 errno = ENOSYS;
                 goto err;
@@ -480,7 +529,7 @@ make_blockdev_path(uint8_t *buf, ssize_t size, struct device *dev)
 {
         ssize_t off = 0;
 
-        debug(DEBUG, "entry buf:%p size:%zd", buf, size);
+        debug("entry buf:%p size:%zd", buf, size);
 
         for (unsigned int i = 0; dev->probes[i] &&
                                  dev->probes[i]->parse; i++) {
@@ -499,7 +548,7 @@ make_blockdev_path(uint8_t *buf, ssize_t size, struct device *dev)
                 off += sz;
         }
 
-        debug(DEBUG, "= %zd", off);
+        debug("= %zd", off);
 
         return off;
 }