OSDN Git Service

Move SATA parsing to linux-sata.c
authorPeter Jones <pjones@redhat.com>
Wed, 6 Jun 2018 20:45:21 +0000 (16:45 -0400)
committerPeter Jones <pmjones@gmail.com>
Fri, 8 Jun 2018 19:11:37 +0000 (15:11 -0400)
This won't actually *work* yet, because the infrastructure to use it
(replacing "struct disk_info") won't land for a few more patches.

Signed-off-by: Peter Jones <pjones@redhat.com>
src/linux-sata.c [new file with mode: 0644]
src/linux.c
src/linux.h

diff --git a/src/linux-sata.c b/src/linux-sata.c
new file mode 100644 (file)
index 0000000..bd26536
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * libefiboot - library for the manipulation of EFI boot variables
+ * Copyright 2012-2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "fix_coverity.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include "efiboot.h"
+
+/*
+ * Support for SATA (and in some cases other ATA) devices
+ *
+ * /dev/sda as SATA looks like:
+ * /sys/dev/block/8:0 -> ../../devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda
+ *                                                                ^     ^       ^ ^ ^ ^ ^ ^ ^
+ *                                                                |     |       | | | | | | lun again
+ *                                                                |     |       | | | | | target again
+ *                                                                |     |       | | | | device again
+ *                                                                |     |       | | | bus again
+ *                                                                |     |       | | 64-bit lun
+ *                                                                |     |       | 32-bit target
+ *                                                                |     |       32-bit device
+ *                                                                |     32-bit scsi "bus"
+ *                                                                ata print id
+ *
+ * This is not only highly repetitive, but most of it is always 0 anyway.
+ *
+ * /dev/sda1 looks like:
+ * /sys/dev/block/8:1 -> ../../devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda1
+ *
+ * sda/device looks like:
+ * device -> ../../../0:0:0:0
+ *
+ * For all of these we're searching for a match for $PRINT_ID and then getting
+ * the other info - if there's a port multiplier and if so what's the PMPN,
+ * and what's the port number:
+ * /sys/class/ata_device/dev$PRINT_ID.$PMPN.$DEVICE  - values "print id", pmp id, and device number
+ * /sys/class/ata_device/dev$PRINT_ID.$DEVICE - values are "print id" and device number
+ * /sys/class/ata_port/ata$PRINT_ID/port_no - contains off-by-one port number)
+ *
+ */
+
+static ssize_t
+sysfs_sata_get_port_info(uint32_t print_id, struct device *dev)
+{
+        DIR *d;
+        struct dirent *de;
+        uint8_t *buf = NULL;
+        int rc;
+
+        d = sysfs_opendir("class/ata_device/");
+        if (!d) {
+                efi_error("could not open /sys/class/ata_device/");
+                return -1;
+        }
+
+        while ((de = readdir(d)) != NULL) {
+                uint32_t found_print_id;
+                uint32_t found_pmp;
+                uint32_t found_devno = 0;
+
+                if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+                        continue;
+
+                rc = sscanf(de->d_name, "dev%d.%d.%d", &found_print_id,
+                            &found_pmp, &found_devno);
+                if (rc < 2 || rc > 3) {
+                        closedir(d);
+                        errno = EINVAL;
+                        return -1;
+                } else if (found_print_id != print_id) {
+                        continue;
+                } else if (rc == 3) {
+                        /*
+                         * the kernel doesn't ever tell us the SATA PMPN
+                         * sentinal value, it'll give us devM.N instead of
+                         * devM.N.O in that case instead.
+                         */
+                        if (found_pmp > 0x7fff) {
+                                closedir(d);
+                                errno = EINVAL;
+                                return -1;
+                        }
+                        dev->sata_info.ata_devno = 0;
+                        dev->sata_info.ata_pmp = found_pmp;
+                        break;
+                } else if (rc == 2) {
+                        dev->sata_info.ata_devno = 0;
+                        dev->sata_info.ata_pmp = 0xffff;
+                        break;
+                }
+        }
+        closedir(d);
+
+        rc = read_sysfs_file(&buf, "class/ata_port/ata%d/port_no",
+                             print_id);
+        if (rc <= 0 || buf == NULL)
+                return -1;
+
+        rc = sscanf((char *)buf, "%d", &dev->sata_info.ata_port);
+        if (rc != 1)
+                return -1;
+
+        /*
+         * ata_port numbers are 1-indexed from libata in the kernel, but
+         * they're 0-indexed in the spec.  For maximal confusion.
+         */
+        if (dev->sata_info.ata_port == 0) {
+                errno = EINVAL;
+                return -1;
+        } else {
+                dev->sata_info.ata_port -= 1;
+        }
+
+        return 0;
+}
+
+static ssize_t
+parse_sata(struct device *dev, const char *devlink)
+{
+        const char *current = devlink;
+        uint32_t print_id;
+        uint32_t scsi_bus, tosser0;
+        uint32_t scsi_device, tosser1;
+        uint32_t scsi_target, tosser2;
+        uint64_t scsi_lun, tosser3;
+        int pos = 0;
+        int rc;
+        char *spaces;
+
+        pos = strlen(current);
+        spaces = alloca(pos+1);
+        memset(spaces, ' ', pos+1);
+        spaces[pos] = '\0';
+        pos = 0;
+
+        debug(DEBUG, "entry");
+
+        /* find the ata info:
+         * ata1/host0/target0:0:0/0:0:0:0
+         *    ^dev  ^host   x y z
+         */
+        debug(DEBUG, "searching for ata1/");
+        rc = sscanf(current, "ata%"PRIu32"/%n", &print_id, &pos);
+        debug(DEBUG, "current:\"%s\" rc:%d pos:%d\n", current, rc, pos);
+        arrow(DEBUG, spaces, 9, pos, rc, 1);
+        /*
+         * If we don't find this one, it isn't an ata device, so return 0 not
+         * error.  Later errors mean it is an ata device, but we can't parse
+         * it right, so they return -1.
+         */
+        if (rc != 1)
+                return 0;
+        current += pos;
+        pos = 0;
+
+        debug(DEBUG, "searching for host0/");
+        rc = sscanf(current, "host%"PRIu32"/%n", &scsi_bus, &pos);
+        debug(DEBUG, "current:\"%s\" rc:%d pos:%d\n", current, rc, pos);
+        arrow(DEBUG, spaces, 9, pos, rc, 1);
+        if (rc != 1)
+                return -1;
+        current += pos;
+        pos = 0;
+
+        debug(DEBUG, "searching for target0:0:0:0/");
+        rc = sscanf(current, "target%"PRIu32":%"PRIu32":%"PRIu64"/%n",
+                    &scsi_device, &scsi_target, &scsi_lun, &pos);
+        debug(DEBUG, "current:\"%s\" rc:%d pos:%d\n", current, rc, pos);
+        arrow(DEBUG, spaces, 9, pos, rc, 3);
+        if (rc != 3)
+                return -1;
+        current += pos;
+        pos = 0;
+
+        debug(DEBUG, "searching for 0:0:0:0/");
+        rc = sscanf(current, "%"PRIu32":%"PRIu32":%"PRIu32":%"PRIu64"/%n",
+                    &tosser0, &tosser1, &tosser2, &tosser3, &pos);
+        debug(DEBUG, "current:\"%s\" rc:%d pos:%d\n", current, rc, pos);
+        arrow(DEBUG, spaces, 9, pos, rc, 4);
+        if (rc != 4)
+                return -1;
+        current += pos;
+
+        rc = sysfs_sata_get_port_info(print_id, dev);
+        if (rc < 0)
+                return -1;
+
+        dev->sata_info.scsi_bus = scsi_bus;
+        dev->sata_info.scsi_device = scsi_device;
+        dev->sata_info.scsi_target = scsi_target;
+        dev->sata_info.scsi_lun = scsi_lun;
+
+        if (dev->interface_type == unknown)
+                dev->interface_type = sata;
+
+        return current - devlink;
+}
+
+static ssize_t
+dp_create_sata(struct device *dev,
+               uint8_t *buf, ssize_t size, ssize_t off)
+{
+        ssize_t sz = -1;
+
+        debug(DEBUG, "entry buf:%p size:%zd off:%zd", buf, size, off);
+
+        if (dev->interface_type == ata || dev->interface_type == atapi) {
+                sz = efidp_make_atapi(buf + off, size ? size - off : 0,
+                                      dev->sata_info.ata_port,
+                                      dev->sata_info.ata_pmp,
+                                      dev->sata_info.ata_devno);
+                if (sz < 0)
+                        efi_error("efidp_make_atapi() failed");
+        } else if (dev->interface_type == sata) {
+                sz = efidp_make_sata(buf + off, size ? size - off : 0,
+                                     dev->sata_info.ata_port,
+                                     dev->sata_info.ata_pmp,
+                                     dev->sata_info.ata_devno);
+                if (sz < 0)
+                        efi_error("efidp_make_sata() failed");
+        } else {
+                return -EINVAL;
+        }
+
+        return sz;
+}
+
+enum interface_type sata_iftypes[] = { sata, unknown };
+
+struct dev_probe HIDDEN sata_parser = {
+        .name = "sata",
+        .iftypes = sata_iftypes,
+        .flags = DEV_PROVIDES_HD,
+        .parse = parse_sata,
+        .create = dp_create_sata,
+};
index 8aca0eb..16c4680 100644 (file)
@@ -215,14 +215,6 @@ sysfs_test_pmem(const char *buf)
 }
 
 static int
-sysfs_test_sata(const char *buf, ssize_t size)
-{
-       if (!strncmp(buf, "ata", MIN(size, 3)))
-               return 1;
-       return 0;
-}
-
-static int
 sysfs_test_sas(const char *buf, ssize_t size)
 {
        int rc;
@@ -254,82 +246,6 @@ sysfs_test_sas(const char *buf, ssize_t size)
        return 0;
 }
 
-static ssize_t
-sysfs_sata_get_port_info(uint32_t print_id, struct disk_info *info)
-{
-       DIR *d;
-       struct dirent *de;
-       uint8_t *buf = NULL;
-       int rc;
-
-       d = opendir("/sys/class/ata_device/");
-       if (!d) {
-               efi_error("opendir failed on /sys/class/ata_device/");
-               return -1;
-       }
-
-       while ((de = readdir(d)) != NULL) {
-               uint32_t found_print_id;
-               uint32_t found_pmp;
-               uint32_t found_devno = 0;
-
-               if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
-                       continue;
-
-               int rc;
-               rc = sscanf(de->d_name, "dev%d.%d.%d", &found_print_id,
-                           &found_pmp, &found_devno);
-               if (rc < 2 || rc > 3) {
-                       closedir(d);
-                       errno = EINVAL;
-                       return -1;
-               } else if (found_print_id != print_id) {
-                       continue;
-               } else if (rc == 3) {
-                       /*
-                        * the kernel doesn't't ever tell us the SATA PMPN
-                        * sentinal value, it'll give us devM.N instead of
-                        * devM.N.O in that case instead.
-                        */
-                       if (found_pmp > 0x7fff) {
-                               closedir(d);
-                               errno = EINVAL;
-                               return -1;
-                       }
-                       info->sata_info.ata_devno = 0;
-                       info->sata_info.ata_pmp = found_pmp;
-                       break;
-               } else if (rc == 2) {
-                       info->sata_info.ata_devno = 0;
-                       info->sata_info.ata_pmp = 0xffff;
-                       break;
-               }
-       }
-       closedir(d);
-
-       rc = read_sysfs_file(&buf, "class/ata_port/ata%d/port_no",
-                            print_id);
-       if (rc <= 0 || buf == NULL)
-               return -1;
-
-       rc = sscanf((char *)buf, "%d", &info->sata_info.ata_port);
-       if (rc != 1)
-               return -1;
-
-       /*
-        * ata_port numbers are 1-indexed from libata in the kernel, but
-        * they're 0-indexed in the spec.  For maximal confusion.
-        */
-       if (info->sata_info.ata_port == 0) {
-               errno = EINVAL;
-               return -1;
-       } else {
-               info->sata_info.ata_port -= 1;
-       }
-
-       return 0;
-}
-
 /* pmem12s -> ../devices/LNXSYSTM:00/LNXSYBUS:00/ACPI0012:00/ndbus0/region12/btt12.1/block/pmem12s
  * dev: 259:0
  * device -> ../../../btt12.1
@@ -375,113 +291,6 @@ sysfs_parse_pmem(uint8_t *buf,  ssize_t size, ssize_t *off,
 }
 
 static ssize_t
-sysfs_parse_sata(uint8_t *buf, ssize_t size, ssize_t *off,
-                const char *pbuf, ssize_t psize, ssize_t *poff,
-                struct disk_info *info)
-{
-       int psz = 0;
-       int rc;
-
-       *poff = 0;
-       *off = 0;
-
-       uint32_t print_id;
-
-       uint32_t scsi_bus;
-       uint32_t scsi_device;
-       uint32_t scsi_target;
-       uint32_t scsi_lun;
-
-       char *newpbuf;
-
-       newpbuf = strndupa(pbuf, psize+1);
-       if (!newpbuf)
-               return -1;
-       newpbuf[psize] = '\0';
-
-       /* find the ata info:
-        * ata1/host0/target0:0:0/
-        *    ^dev  ^host   x y z
-        */
-       rc = sscanf(newpbuf, "ata%d/host%d/target%d:%d:%d/%n",
-                   &print_id, &scsi_bus, &scsi_device, &scsi_target, &scsi_lun,
-                   &psz);
-       if (rc != 5)
-               return -1;
-       *poff += psz;
-
-       /* find the emulated scsi bits (and ignore them)
-        * 0:0:0:0/
-        */
-       uint32_t dummy0, dummy1, dummy2;
-       uint64_t dummy3;
-       rc = sscanf(newpbuf+*poff, "%d:%d:%d:%"PRIu64"/%n", &dummy0, &dummy1,
-                   &dummy2, &dummy3, &psz);
-       if (rc != 4)
-               return -1;
-       *poff += psz;
-
-       /* what's left is:
-        * block/sda/sda4
-        */
-       char *disk_name = NULL;
-       char *part_name = NULL;
-       int psz1 = 0;
-       rc = sscanf(newpbuf+*poff, "block/%m[^/]%n/%m[^/]%n", &disk_name, &psz,
-                   &part_name, &psz1);
-       if (rc == 1) {
-               rc = asprintf(&part_name, "%s%d", disk_name, info->part);
-               if (rc < 0) {
-                       free(disk_name);
-                       errno = EINVAL;
-                       return -1;
-               }
-               *poff += psz;
-       } else if (rc != 2) {
-               errno = EINVAL;
-               return -1;
-       } else {
-               *poff += psz1;
-       }
-
-       info->sata_info.scsi_bus = scsi_bus;
-       info->sata_info.scsi_device = scsi_device;
-       info->sata_info.scsi_target = scsi_target;
-       info->sata_info.scsi_lun = scsi_lun;
-
-       rc = sysfs_sata_get_port_info(print_id, info);
-       if (rc < 0) {
-               free(disk_name);
-               free(part_name);
-               return -1;
-       }
-
-       /* check the original of this; it's guaranteed in our copy */
-       if (pbuf[*poff] != '\0') {
-               free(disk_name);
-               free(part_name);
-               errno = EINVAL;
-               return -1;
-       }
-
-       info->disk_name = disk_name;
-       info->part_name = part_name;
-       if (info->interface_type == interface_type_unknown)
-               info->interface_type = sata;
-
-       if (info->interface_type == ata) {
-               *off = efidp_make_atapi(buf, size, info->sata_info.ata_port,
-                                       info->sata_info.ata_pmp,
-                                       info->sata_info.ata_devno);
-       } else {
-               *off = efidp_make_sata(buf, size, info->sata_info.ata_port,
-                                      info->sata_info.ata_pmp,
-                                      info->sata_info.ata_devno);
-       }
-       return *off;
-}
-
-static ssize_t
 sysfs_parse_sas(uint8_t *buf, ssize_t size, ssize_t *off,
                const char *pbuf, ssize_t psize, ssize_t *poff,
                struct disk_info *info)
@@ -788,26 +597,6 @@ make_blockdev_path(uint8_t *buf, ssize_t size, struct disk_info *info)
 
        }
 
-       /* /dev/sda as SATA looks like:
-        * /sys/dev/block/8:0 -> ../../devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda
-        */
-       if (!found) {
-               rc = sysfs_test_sata(linkbuf+loff, PATH_MAX-off);
-               if (rc < 0)
-                       return -1;
-               else if (rc > 0) {
-                       ssize_t linksz=0;
-                       rc = sysfs_parse_sata(buf+off, size?size-off:0, &sz,
-                                             linkbuf+loff, PATH_MAX-off,
-                                             &linksz, info);
-                       if (rc < 0)
-                               return -1;
-                       loff += linksz;
-                       off += sz;
-                       found = 1;
-               }
-       }
-
        /* /dev/sdc as SAS looks like:
         * /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
         */
index 0f836c3..17f1586 100644 (file)
@@ -53,16 +53,16 @@ struct sas_info {
 };
 
 struct sata_info {
-       uint32_t scsi_bus;
-       uint32_t scsi_device;
-       uint32_t scsi_target;
-       uint64_t scsi_lun;
+        uint32_t scsi_bus;
+        uint32_t scsi_device;
+        uint32_t scsi_target;
+        uint64_t scsi_lun;
 
-       uint32_t ata_devno;
-       uint32_t ata_port;
-       uint32_t ata_pmp;
+        uint32_t ata_devno;
+        uint32_t ata_port;
+        uint32_t ata_pmp;
 
-       uint32_t ata_print_id;
+        uint32_t ata_print_id;
 };
 
 struct nvme_info {
@@ -259,6 +259,7 @@ extern ssize_t parse_scsi_link(const char *current, uint32_t *host,
 #define set_part(x, y) /* XXX remove later */
 
 /* device support implementations */
+extern struct dev_probe sata_parser;
 extern struct dev_probe nvme_parser;
 extern struct dev_probe virtblk_parser;
 extern struct dev_probe i2o_parser;