LCOV - code coverage report
Current view: top level - block - early-lookup.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 107 0.0 %
Date: 2023-08-24 13:40:31 Functions: 0 10 0.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-only
       2             : /*
       3             :  * Code for looking up block devices in the early boot code before mounting the
       4             :  * root file system.
       5             :  */
       6             : #include <linux/blkdev.h>
       7             : #include <linux/ctype.h>
       8             : 
       9             : struct uuidcmp {
      10             :         const char *uuid;
      11             :         int len;
      12             : };
      13             : 
      14             : /**
      15             :  * match_dev_by_uuid - callback for finding a partition using its uuid
      16             :  * @dev:        device passed in by the caller
      17             :  * @data:       opaque pointer to the desired struct uuidcmp to match
      18             :  *
      19             :  * Returns 1 if the device matches, and 0 otherwise.
      20             :  */
      21           0 : static int __init match_dev_by_uuid(struct device *dev, const void *data)
      22             : {
      23           0 :         struct block_device *bdev = dev_to_bdev(dev);
      24           0 :         const struct uuidcmp *cmp = data;
      25             : 
      26           0 :         if (!bdev->bd_meta_info ||
      27           0 :             strncasecmp(cmp->uuid, bdev->bd_meta_info->uuid, cmp->len))
      28             :                 return 0;
      29             :         return 1;
      30             : }
      31             : 
      32             : /**
      33             :  * devt_from_partuuid - looks up the dev_t of a partition by its UUID
      34             :  * @uuid_str:   char array containing ascii UUID
      35             :  * @devt:       dev_t result
      36             :  *
      37             :  * The function will return the first partition which contains a matching
      38             :  * UUID value in its partition_meta_info struct.  This does not search
      39             :  * by filesystem UUIDs.
      40             :  *
      41             :  * If @uuid_str is followed by a "/PARTNROFF=%d", then the number will be
      42             :  * extracted and used as an offset from the partition identified by the UUID.
      43             :  *
      44             :  * Returns 0 on success or a negative error code on failure.
      45             :  */
      46           0 : static int __init devt_from_partuuid(const char *uuid_str, dev_t *devt)
      47             : {
      48             :         struct uuidcmp cmp;
      49           0 :         struct device *dev = NULL;
      50           0 :         int offset = 0;
      51             :         char *slash;
      52             : 
      53           0 :         cmp.uuid = uuid_str;
      54             : 
      55           0 :         slash = strchr(uuid_str, '/');
      56             :         /* Check for optional partition number offset attributes. */
      57           0 :         if (slash) {
      58           0 :                 char c = 0;
      59             : 
      60             :                 /* Explicitly fail on poor PARTUUID syntax. */
      61           0 :                 if (sscanf(slash + 1, "PARTNROFF=%d%c", &offset, &c) != 1)
      62             :                         goto out_invalid;
      63           0 :                 cmp.len = slash - uuid_str;
      64             :         } else {
      65           0 :                 cmp.len = strlen(uuid_str);
      66             :         }
      67             : 
      68           0 :         if (!cmp.len)
      69             :                 goto out_invalid;
      70             : 
      71           0 :         dev = class_find_device(&block_class, NULL, &cmp, &match_dev_by_uuid);
      72           0 :         if (!dev)
      73             :                 return -ENODEV;
      74             : 
      75           0 :         if (offset) {
      76             :                 /*
      77             :                  * Attempt to find the requested partition by adding an offset
      78             :                  * to the partition number found by UUID.
      79             :                  */
      80           0 :                 *devt = part_devt(dev_to_disk(dev),
      81           0 :                                   dev_to_bdev(dev)->bd_partno + offset);
      82             :         } else {
      83           0 :                 *devt = dev->devt;
      84             :         }
      85             : 
      86           0 :         put_device(dev);
      87           0 :         return 0;
      88             : 
      89             : out_invalid:
      90           0 :         pr_err("VFS: PARTUUID= is invalid.\n"
      91             :                "Expected PARTUUID=<valid-uuid-id>[/PARTNROFF=%%d]\n");
      92           0 :         return -EINVAL;
      93             : }
      94             : 
      95             : /**
      96             :  * match_dev_by_label - callback for finding a partition using its label
      97             :  * @dev:        device passed in by the caller
      98             :  * @data:       opaque pointer to the label to match
      99             :  *
     100             :  * Returns 1 if the device matches, and 0 otherwise.
     101             :  */
     102           0 : static int __init match_dev_by_label(struct device *dev, const void *data)
     103             : {
     104           0 :         struct block_device *bdev = dev_to_bdev(dev);
     105           0 :         const char *label = data;
     106             : 
     107           0 :         if (!bdev->bd_meta_info || strcmp(label, bdev->bd_meta_info->volname))
     108             :                 return 0;
     109             :         return 1;
     110             : }
     111             : 
     112           0 : static int __init devt_from_partlabel(const char *label, dev_t *devt)
     113             : {
     114             :         struct device *dev;
     115             : 
     116           0 :         dev = class_find_device(&block_class, NULL, label, &match_dev_by_label);
     117           0 :         if (!dev)
     118             :                 return -ENODEV;
     119           0 :         *devt = dev->devt;
     120           0 :         put_device(dev);
     121           0 :         return 0;
     122             : }
     123             : 
     124           0 : static dev_t __init blk_lookup_devt(const char *name, int partno)
     125             : {
     126           0 :         dev_t devt = MKDEV(0, 0);
     127             :         struct class_dev_iter iter;
     128             :         struct device *dev;
     129             : 
     130           0 :         class_dev_iter_init(&iter, &block_class, NULL, &disk_type);
     131           0 :         while ((dev = class_dev_iter_next(&iter))) {
     132           0 :                 struct gendisk *disk = dev_to_disk(dev);
     133             : 
     134           0 :                 if (strcmp(dev_name(dev), name))
     135           0 :                         continue;
     136             : 
     137           0 :                 if (partno < disk->minors) {
     138             :                         /* We need to return the right devno, even
     139             :                          * if the partition doesn't exist yet.
     140             :                          */
     141           0 :                         devt = MKDEV(MAJOR(dev->devt),
     142             :                                      MINOR(dev->devt) + partno);
     143             :                 } else {
     144           0 :                         devt = part_devt(disk, partno);
     145           0 :                         if (devt)
     146             :                                 break;
     147             :                 }
     148             :         }
     149           0 :         class_dev_iter_exit(&iter);
     150           0 :         return devt;
     151             : }
     152             : 
     153           0 : static int __init devt_from_devname(const char *name, dev_t *devt)
     154             : {
     155             :         int part;
     156             :         char s[32];
     157             :         char *p;
     158             : 
     159           0 :         if (strlen(name) > 31)
     160             :                 return -EINVAL;
     161           0 :         strcpy(s, name);
     162           0 :         for (p = s; *p; p++) {
     163           0 :                 if (*p == '/')
     164           0 :                         *p = '!';
     165             :         }
     166             : 
     167           0 :         *devt = blk_lookup_devt(s, 0);
     168           0 :         if (*devt)
     169             :                 return 0;
     170             : 
     171             :         /*
     172             :          * Try non-existent, but valid partition, which may only exist after
     173             :          * opening the device, like partitioned md devices.
     174             :          */
     175           0 :         while (p > s && isdigit(p[-1]))
     176           0 :                 p--;
     177           0 :         if (p == s || !*p || *p == '0')
     178             :                 return -ENODEV;
     179             : 
     180             :         /* try disk name without <part number> */
     181           0 :         part = simple_strtoul(p, NULL, 10);
     182           0 :         *p = '\0';
     183           0 :         *devt = blk_lookup_devt(s, part);
     184           0 :         if (*devt)
     185             :                 return 0;
     186             : 
     187             :         /* try disk name without p<part number> */
     188           0 :         if (p < s + 2 || !isdigit(p[-2]) || p[-1] != 'p')
     189             :                 return -ENODEV;
     190           0 :         p[-1] = '\0';
     191           0 :         *devt = blk_lookup_devt(s, part);
     192           0 :         if (*devt)
     193             :                 return 0;
     194           0 :         return -ENODEV;
     195             : }
     196             : 
     197           0 : static int __init devt_from_devnum(const char *name, dev_t *devt)
     198             : {
     199             :         unsigned maj, min, offset;
     200             :         char *p, dummy;
     201             : 
     202           0 :         if (sscanf(name, "%u:%u%c", &maj, &min, &dummy) == 2 ||
     203           0 :             sscanf(name, "%u:%u:%u:%c", &maj, &min, &offset, &dummy) == 3) {
     204           0 :                 *devt = MKDEV(maj, min);
     205           0 :                 if (maj != MAJOR(*devt) || min != MINOR(*devt))
     206             :                         return -EINVAL;
     207             :         } else {
     208           0 :                 *devt = new_decode_dev(simple_strtoul(name, &p, 16));
     209           0 :                 if (*p)
     210             :                         return -EINVAL;
     211             :         }
     212             : 
     213             :         return 0;
     214             : }
     215             : 
     216             : /*
     217             :  *      Convert a name into device number.  We accept the following variants:
     218             :  *
     219             :  *      1) <hex_major><hex_minor> device number in hexadecimal represents itself
     220             :  *         no leading 0x, for example b302.
     221             :  *      3) /dev/<disk_name> represents the device number of disk
     222             :  *      4) /dev/<disk_name><decimal> represents the device number
     223             :  *         of partition - device number of disk plus the partition number
     224             :  *      5) /dev/<disk_name>p<decimal> - same as the above, that form is
     225             :  *         used when disk name of partitioned disk ends on a digit.
     226             :  *      6) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
     227             :  *         unique id of a partition if the partition table provides it.
     228             :  *         The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
     229             :  *         partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
     230             :  *         filled hex representation of the 32-bit "NT disk signature", and PP
     231             :  *         is a zero-filled hex representation of the 1-based partition number.
     232             :  *      7) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation to
     233             :  *         a partition with a known unique id.
     234             :  *      8) <major>:<minor> major and minor number of the device separated by
     235             :  *         a colon.
     236             :  *      9) PARTLABEL=<name> with name being the GPT partition label.
     237             :  *         MSDOS partitions do not support labels!
     238             :  *
     239             :  *      If name doesn't have fall into the categories above, we return (0,0).
     240             :  *      block_class is used to check if something is a disk name. If the disk
     241             :  *      name contains slashes, the device name has them replaced with
     242             :  *      bangs.
     243             :  */
     244           0 : int __init early_lookup_bdev(const char *name, dev_t *devt)
     245             : {
     246           0 :         if (strncmp(name, "PARTUUID=", 9) == 0)
     247           0 :                 return devt_from_partuuid(name + 9, devt);
     248           0 :         if (strncmp(name, "PARTLABEL=", 10) == 0)
     249           0 :                 return devt_from_partlabel(name + 10, devt);
     250           0 :         if (strncmp(name, "/dev/", 5) == 0)
     251           0 :                 return devt_from_devname(name + 5, devt);
     252           0 :         return devt_from_devnum(name, devt);
     253             : }
     254             : 
     255           0 : static char __init *bdevt_str(dev_t devt, char *buf)
     256             : {
     257           0 :         if (MAJOR(devt) <= 0xff && MINOR(devt) <= 0xff) {
     258             :                 char tbuf[BDEVT_SIZE];
     259           0 :                 snprintf(tbuf, BDEVT_SIZE, "%02x%02x", MAJOR(devt), MINOR(devt));
     260           0 :                 snprintf(buf, BDEVT_SIZE, "%-9s", tbuf);
     261             :         } else
     262           0 :                 snprintf(buf, BDEVT_SIZE, "%03x:%05x", MAJOR(devt), MINOR(devt));
     263             : 
     264           0 :         return buf;
     265             : }
     266             : 
     267             : /*
     268             :  * print a full list of all partitions - intended for places where the root
     269             :  * filesystem can't be mounted and thus to give the victim some idea of what
     270             :  * went wrong
     271             :  */
     272           0 : void __init printk_all_partitions(void)
     273             : {
     274             :         struct class_dev_iter iter;
     275             :         struct device *dev;
     276             : 
     277           0 :         class_dev_iter_init(&iter, &block_class, NULL, &disk_type);
     278           0 :         while ((dev = class_dev_iter_next(&iter))) {
     279           0 :                 struct gendisk *disk = dev_to_disk(dev);
     280             :                 struct block_device *part;
     281             :                 char devt_buf[BDEVT_SIZE];
     282             :                 unsigned long idx;
     283             : 
     284             :                 /*
     285             :                  * Don't show empty devices or things that have been
     286             :                  * suppressed
     287             :                  */
     288           0 :                 if (get_capacity(disk) == 0 || (disk->flags & GENHD_FL_HIDDEN))
     289           0 :                         continue;
     290             : 
     291             :                 /*
     292             :                  * Note, unlike /proc/partitions, I am showing the numbers in
     293             :                  * hex - the same format as the root= option takes.
     294             :                  */
     295             :                 rcu_read_lock();
     296           0 :                 xa_for_each(&disk->part_tbl, idx, part) {
     297           0 :                         if (!bdev_nr_sectors(part))
     298           0 :                                 continue;
     299           0 :                         printk("%s%s %10llu %pg %s",
     300             :                                bdev_is_partition(part) ? "  " : "",
     301             :                                bdevt_str(part->bd_dev, devt_buf),
     302             :                                bdev_nr_sectors(part) >> 1, part,
     303             :                                part->bd_meta_info ?
     304             :                                         part->bd_meta_info->uuid : "");
     305           0 :                         if (bdev_is_partition(part))
     306           0 :                                 printk("\n");
     307           0 :                         else if (dev->parent && dev->parent->driver)
     308           0 :                                 printk(" driver: %s\n",
     309             :                                         dev->parent->driver->name);
     310             :                         else
     311           0 :                                 printk(" (driver?)\n");
     312             :                 }
     313             :                 rcu_read_unlock();
     314             :         }
     315           0 :         class_dev_iter_exit(&iter);
     316           0 : }

Generated by: LCOV version 1.14