From: Stephen Warren swarren@nvidia.com
This will allow other types of UUID to be stored here, aside from true UUIDs. This also simplifies code that uses this field, since it's usually constructed from a, used as a, or compared to other, strings.
Note: A simplistic approach here would be to set uuid_str[36]=0 whenever a /PARTNROFF option was found to be present. However, this modifies the input string, and causes subsequent calls to devt_from_partuuid() not to see the /PARTNROFF option, which causes different results. In order to avoid misleading future maintainers, this parameter is marked const.
Signed-off-by: Stephen Warren swarren@nvidia.com --- block/genhd.c | 8 +------- block/partitions/efi.c | 7 +------ include/linux/genhd.h | 8 ++++++-- init/do_mounts.c | 28 +++++++++++++++++----------- 4 files changed, 25 insertions(+), 26 deletions(-)
diff --git a/block/genhd.c b/block/genhd.c index 6cace66..b281f3a 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -743,7 +743,6 @@ void __init printk_all_partitions(void) struct hd_struct *part; char name_buf[BDEVNAME_SIZE]; char devt_buf[BDEVT_SIZE]; - char uuid_buf[PARTITION_META_INFO_UUIDLTH * 2 + 5];
/* * Don't show empty devices or things that have been @@ -762,16 +761,11 @@ void __init printk_all_partitions(void) while ((part = disk_part_iter_next(&piter))) { bool is_part0 = part == &disk->part0;
- uuid_buf[0] = '\0'; - if (part->info) - snprintf(uuid_buf, sizeof(uuid_buf), "%pU", - part->info->uuid); - printk("%s%s %10llu %s %s", is_part0 ? "" : " ", bdevt_str(part_devt(part), devt_buf), (unsigned long long)part_nr_sects_read(part) >> 1 , disk_name(disk, part->partno, name_buf), - uuid_buf); + part->info ? part->info->uuid : ""); if (is_part0) { if (disk->driverfs_dev != NULL && disk->driverfs_dev->driver != NULL) diff --git a/block/partitions/efi.c b/block/partitions/efi.c index 6296b40..b62fb88 100644 --- a/block/partitions/efi.c +++ b/block/partitions/efi.c @@ -620,7 +620,6 @@ int efi_partition(struct parsed_partitions *state) gpt_entry *ptes = NULL; u32 i; unsigned ssz = bdev_logical_block_size(state->bdev) / 512; - u8 unparsed_guid[37];
if (!find_valid_gpt(state, &gpt, &ptes) || !gpt || !ptes) { kfree(gpt); @@ -649,11 +648,7 @@ int efi_partition(struct parsed_partitions *state) state->parts[i + 1].flags = ADDPART_FLAG_RAID;
info = &state->parts[i + 1].info; - /* Instead of doing a manual swap to big endian, reuse the - * common ASCII hex format as the interim. - */ - efi_guid_unparse(&ptes[i].unique_partition_guid, unparsed_guid); - part_pack_uuid(unparsed_guid, info->uuid); + efi_guid_unparse(&ptes[i].unique_partition_guid, info->uuid);
/* Naively convert UTF16-LE to 7 bits. */ label_max = min(sizeof(info->volname) - 1, diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 4f440b3..79b8bba 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -88,10 +88,14 @@ struct disk_stats { };
#define PARTITION_META_INFO_VOLNAMELTH 64 -#define PARTITION_META_INFO_UUIDLTH 16 +/* + * Enough for the string representation of any kind of UUID plus NULL. + * EFI UUID is 36 characters. MSDOS UUID is 11 characters. + */ +#define PARTITION_META_INFO_UUIDLTH 37
struct partition_meta_info { - u8 uuid[PARTITION_META_INFO_UUIDLTH]; /* always big endian */ + char uuid[PARTITION_META_INFO_UUIDLTH]; u8 volname[PARTITION_META_INFO_VOLNAMELTH]; };
diff --git a/init/do_mounts.c b/init/do_mounts.c index d3f0aee..4dba733 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -69,23 +69,28 @@ __setup("ro", readonly); __setup("rw", readwrite);
#ifdef CONFIG_BLOCK +struct uuidcmp { + const char *uuid; + int len; +}; + /** * match_dev_by_uuid - callback for finding a partition using its uuid * @dev: device passed in by the caller - * @data: opaque pointer to a 36 byte char array with a UUID + * @data: opaque pointer to the desired struct uuidcmp to match * * Returns 1 if the device matches, and 0 otherwise. */ static int match_dev_by_uuid(struct device *dev, void *data) { - u8 *uuid = data; + struct uuidcmp *cmp = data; struct hd_struct *part = dev_to_part(dev);
if (!part->info) goto no_match;
- if (memcmp(uuid, part->info->uuid, sizeof(part->info->uuid))) - goto no_match; + if (strncasecmp(cmp->uuid, part->info->uuid, cmp->len)) + goto no_match;
return 1; no_match: @@ -95,7 +100,7 @@ no_match:
/** * devt_from_partuuid - looks up the dev_t of a partition by its UUID - * @uuid: min 36 byte char array containing a hex ascii UUID + * @uuid: char array containing ascii UUID * * The function will return the first partition which contains a matching * UUID value in its partition_meta_info struct. This does not search @@ -106,11 +111,11 @@ no_match: * * Returns the matching dev_t on success or 0 on failure. */ -static dev_t devt_from_partuuid(char *uuid_str) +static dev_t devt_from_partuuid(const char *uuid_str) { dev_t res = 0; + struct uuidcmp cmp; struct device *dev = NULL; - u8 uuid[16]; struct gendisk *disk; struct hd_struct *part; int offset = 0; @@ -118,6 +123,9 @@ static dev_t devt_from_partuuid(char *uuid_str) if (strlen(uuid_str) < 36) goto done;
+ cmp.uuid = uuid_str; + cmp.len = 36; + /* Check for optional partition number offset attributes. */ if (uuid_str[36]) { char c = 0; @@ -134,10 +142,8 @@ static dev_t devt_from_partuuid(char *uuid_str) } }
- /* Pack the requested UUID in the expected format. */ - part_pack_uuid(uuid_str, uuid); - - dev = class_find_device(&block_class, NULL, uuid, &match_dev_by_uuid); + dev = class_find_device(&block_class, NULL, &cmp, + &match_dev_by_uuid); if (!dev) goto done;
From: Stephen Warren swarren@nvidia.com
Reduce the minimum length for a root=PARTUUID= parameter to be considered valid from 36 to 1. EFI/GPT partition UUIDs are always exactly 36 characters long, hence the previous limit. However, the next patch will support DOS/MBR UUIDs too, which have a different, shorter, format. Instead of validating any particular length, just ensure that at least some non-empty value was given by the user.
Also, consider a missing UUID value to be a parsing error, in the same vein as if /PARTNROFF exists and can't be parsed. As such, make both error cases print a message and disable rootwait. Convert to pr_err while we're at it.
Signed-off-by: Stephen Warren swarren@nvidia.com --- init/do_mounts.c | 35 ++++++++++++++++++++++------------- 1 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/init/do_mounts.c b/init/do_mounts.c index 4dba733..fcbe825 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -119,27 +119,29 @@ static dev_t devt_from_partuuid(const char *uuid_str) struct gendisk *disk; struct hd_struct *part; int offset = 0; - - if (strlen(uuid_str) < 36) - goto done; + bool clear_root_wait = false; + char *slash;
cmp.uuid = uuid_str; - cmp.len = 36;
+ slash = strchr(uuid_str, '/'); /* Check for optional partition number offset attributes. */ - if (uuid_str[36]) { + if (slash) { char c = 0; /* Explicitly fail on poor PARTUUID syntax. */ - if (sscanf(&uuid_str[36], - "/PARTNROFF=%d%c", &offset, &c) != 1) { - printk(KERN_ERR "VFS: PARTUUID= is invalid.\n" - "Expected PARTUUID=<valid-uuid-id>[/PARTNROFF=%%d]\n"); - if (root_wait) - printk(KERN_ERR - "Disabling rootwait; root= is invalid.\n"); - root_wait = 0; + if (sscanf(slash + 1, + "PARTNROFF=%d%c", &offset, &c) != 1) { + clear_root_wait = true; goto done; } + cmp.len = slash - uuid_str; + } else { + cmp.len = strlen(uuid_str); + } + + if (!cmp.len) { + clear_root_wait = true; + goto done; }
dev = class_find_device(&block_class, NULL, &cmp, @@ -164,6 +166,13 @@ static dev_t devt_from_partuuid(const char *uuid_str) no_offset: put_device(dev); done: + if (clear_root_wait) { + pr_err("VFS: PARTUUID= is invalid.\n" + "Expected PARTUUID=<valid-uuid-id>[/PARTNROFF=%%d]\n"); + if (root_wait) + pr_err("Disabling rootwait; root= is invalid.\n"); + root_wait = 0; + } return res; } #endif
From: Stephen Warren swarren@nvidia.com
The MSDOS/MBR partition table includes a 32-bit unique ID, often referred to as the NT disk signature. When combined with a partition number within the table, this can form a unique ID similar in concept to EFI/GPT's partition UUID. Constructing and recording this value in struct partition_meta_info allows MSDOS partitions to be referred to on the kernel command-line using the following syntax:
root=PARTUUID=0002dd75-01
Signed-off-by: Stephen Warren swarren@nvidia.com --- block/partitions/msdos.c | 21 +++++++++++++++++++-- init/do_mounts.c | 4 ++++ 2 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/block/partitions/msdos.c b/block/partitions/msdos.c index 5f79a66..8752a5d 100644 --- a/block/partitions/msdos.c +++ b/block/partitions/msdos.c @@ -94,6 +94,17 @@ static int aix_magic_present(struct parsed_partitions *state, unsigned char *p) return ret; }
+static void set_info(struct parsed_partitions *state, int slot, + u32 disksig) +{ + struct partition_meta_info *info = &state->parts[slot].info; + + snprintf(info->uuid, sizeof(info->uuid), "%08x-%02x", disksig, + slot); + info->volname[0] = 0; + state->parts[slot].has_info = true; +} + /* * Create devices for each logical partition in an extended partition. * The logical partitions form a linked list, with each entry being @@ -106,7 +117,8 @@ static int aix_magic_present(struct parsed_partitions *state, unsigned char *p) */
static void parse_extended(struct parsed_partitions *state, - sector_t first_sector, sector_t first_size) + sector_t first_sector, sector_t first_size, + u32 disksig) { struct partition *p; Sector sect; @@ -166,6 +178,7 @@ static void parse_extended(struct parsed_partitions *state, }
put_partition(state, state->next, next, size); + set_info(state, state->next, disksig); if (SYS_IND(p) == LINUX_RAID_PARTITION) state->parts[state->next].flags = ADDPART_FLAG_RAID; loopct = 0; @@ -437,6 +450,7 @@ int msdos_partition(struct parsed_partitions *state) struct partition *p; struct fat_boot_sector *fb; int slot; + u32 disksig;
data = read_part_sector(state, 0, §); if (!data) @@ -491,6 +505,8 @@ int msdos_partition(struct parsed_partitions *state) #endif p = (struct partition *) (data + 0x1be);
+ disksig = le32_to_cpup((__le32 *)(data + 0x1b8)); + /* * Look for partitions in two passes: * First find the primary and DOS-type extended partitions. @@ -515,11 +531,12 @@ int msdos_partition(struct parsed_partitions *state) put_partition(state, slot, start, n);
strlcat(state->pp_buf, " <", PAGE_SIZE); - parse_extended(state, start, size); + parse_extended(state, start, size, disksig); strlcat(state->pp_buf, " >", PAGE_SIZE); continue; } put_partition(state, slot, start, size); + set_info(state, slot, disksig); if (SYS_IND(p) == LINUX_RAID_PARTITION) state->parts[slot].flags = ADDPART_FLAG_RAID; if (SYS_IND(p) == DM6_PARTITION) diff --git a/init/do_mounts.c b/init/do_mounts.c index fcbe825..b6aa632 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -189,6 +189,10 @@ done: * used when disk name of partitioned disk ends on a digit. * 6) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the * unique id of a partition if the partition table provides it. + * The UUID may be either an EFI/GPT UUID, or refer to an MSDOS + * partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero- + * filled hex representation of the 32-bit "NT disk signature", and PP + * is a zero-filled hex representation of the 1-based partition number. * 7) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation to * a partition with a known unique id. *