[U-Boot] [PATCH 09/10] GPT: provide commands to selectively rename partitions

alison at peloton-tech.com alison at peloton-tech.com
Sat Jul 1 22:44:22 UTC 2017


From: Alison Chaiken <alison at peloton-tech.com>

This patch provides support in u-boot for renaming GPT
partitions.  The renaming is accomplished via new 'gpt swap'
and 'gpt rename' commands.

The 'swap' mode returns an error if no matching partition names
are found, or if the number of partitions with one name does not equal
the number with the second name.   The 'rename' variant always
succeeds as long as a partition with the provided number exists.

Rewriting the partition table has the side-effect that all partitions
end up with "msftdata" flag set.  The reason is that partition type
PARTITION_BASIC_DATA_GUID is hard-coded in the gpt_fill_pte()
function.  This does not appear to cause any harm.

Signed-off-by: Alison Chaiken <alison at peloton-tech.com>
---

Changes since v7:
  This version removes casts to unsigned long where the
  underlying data type is lbaint_t.  I tested this version both
  with and without CONFIG_SYS_64BIT_LBA, and the output is fine
  in either case.  This version also removes unneeded casts to
  const and restores the gpt command's error reporting to its
  earlier state if CONFIG_GPT_RENAME is unset.

  In addition, there is an additional check that a malloc() has
  succeeded.

 cmd/Kconfig    |   8 ++
 cmd/gpt.c      | 240 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 doc/README.gpt |  20 ++++-
 3 files changed, 263 insertions(+), 5 deletions(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 07b0e3b..ae61b96 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -595,6 +595,14 @@ config CMD_GPT
 	  Enable the 'gpt' command to ready and write GPT style partition
 	  tables.
 
+config CMD_GPT_RENAME
+	bool "GPT partition renaming commands"
+	depends on CMD_GPT
+	help
+	  Enables the 'gpt' command to interchange names on two GPT
+	  partitions via the 'gpt swap' command or to rename single
+	  partitions via the 'rename' command.
+
 config CMD_ARMFLASH
 	#depends on FLASH_CFI_DRIVER
 	bool "armflash"
diff --git a/cmd/gpt.c b/cmd/gpt.c
index dd5f78b..73bf273 100644
--- a/cmd/gpt.c
+++ b/cmd/gpt.c
@@ -20,6 +20,8 @@
 #include <div64.h>
 #include <memalign.h>
 #include <linux/compat.h>
+#include <linux/sizes.h>
+#include <stdlib.h>
 
 static LIST_HEAD(disk_partitions);
 
@@ -194,16 +196,32 @@ static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum)
 	return newpart;
 }
 
+static void prettyprint_part_size(char *sizestr, lbaint_t partsize,
+				  lbaint_t blksize)
+{
+	unsigned long long partbytes, partmegabytes;
+
+	partbytes = partsize * blksize;
+	partmegabytes = lldiv(partbytes, SZ_1M);
+	snprintf(sizestr, 16, "%lluMiB", partmegabytes);
+}
+
 static void print_gpt_info(void)
 {
 	struct list_head *pos;
 	struct disk_part *curr;
+	char partstartstr[16];
+	char partsizestr[16];
 
 	list_for_each(pos, &disk_partitions) {
 		curr = list_entry(pos, struct disk_part, list);
+		prettyprint_part_size(partstartstr, curr->gpt_part_info.start,
+				      curr->gpt_part_info.blksz);
+		prettyprint_part_size(partsizestr, curr->gpt_part_info.size,
+                                      curr->gpt_part_info.blksz);
+
 		printf("Partition %d:\n", curr->partnum);
-		printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start,
-		       (unsigned)curr->gpt_part_info.size);
+		printf("Start %s, size %s\n", partstartstr, partsizestr);
 		printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz,
 		       curr->gpt_part_info.name);
 		printf("Type %s, bootable %d\n", curr->gpt_part_info.type,
@@ -215,6 +233,73 @@ static void print_gpt_info(void)
 	}
 }
 
+#ifdef CONFIG_CMD_GPT_RENAME
+static int calc_parts_list_len(int numparts)
+{
+	int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk=");
+	/* for the comma */
+	partlistlen++;
+
+	/* per-partition additions; numparts starts at 1, so this should be correct */
+	partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN + 1);
+	/* see part.h for definition of struct disk_partition */
+	partlistlen += numparts * (strlen("start=MiB,") + sizeof(lbaint_t) + 1);
+	partlistlen += numparts * (strlen("size=MiB,") + sizeof(lbaint_t) + 1);
+	partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN + 1);
+	/* for the terminating null */
+	partlistlen++;
+	debug("Length of partitions_list is %d for %d partitions\n", partlistlen,
+	       numparts);
+	return partlistlen;
+}
+
+/*
+ * create the string that upstream 'gpt write' command will accept as an
+ * argument
+ *
+ * From doc/README.gpt, Format of partitions layout:
+ *    "uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
+ *	name=kernel,size=60MiB,uuid=...;"
+ * The fields 'name' and 'size' are mandatory for every partition.
+ * The field 'start' is optional. The fields 'uuid' and 'uuid_disk'
+ * are optional if CONFIG_RANDOM_UUID is enabled.
+ */
+static int create_gpt_partitions_list(int numparts, const char *guid, char *partitions_list)
+{
+	struct list_head *pos;
+	struct disk_part *curr;
+	char partstr[PART_NAME_LEN + 1];
+
+	if (!partitions_list)
+		return -EINVAL;
+
+	strcpy(partitions_list, "uuid_disk=");
+	strncat(partitions_list, guid, UUID_STR_LEN + 1);
+	strcat(partitions_list, ";");
+
+	list_for_each(pos, &disk_partitions) {
+		curr = list_entry(pos, struct disk_part, list);
+		strcat(partitions_list, "name=");
+		strncat(partitions_list, (const char *)curr->gpt_part_info.name, PART_NAME_LEN + 1);
+		strcat(partitions_list, ",start=");
+		prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.start,
+				      (unsigned long) curr->gpt_part_info.blksz);
+		/* one extra byte for NULL */
+		strncat(partitions_list, partstr, PART_NAME_LEN + 1);
+		strcat(partitions_list, ",size=");
+		prettyprint_part_size(partstr, curr->gpt_part_info.size,
+                                      curr->gpt_part_info.blksz);
+		strncat(partitions_list, partstr, PART_NAME_LEN + 1);
+
+		strcat(partitions_list, ",uuid=");
+		strncat(partitions_list, curr->gpt_part_info.uuid,
+			UUID_STR_LEN + 1);
+		strcat(partitions_list, ";");
+	}
+	return 0;
+}
+#endif
+
 /*
  * read partition info into disk_partitions list where
  * it can be printed or modified
@@ -226,8 +311,11 @@ static int get_gpt_info(struct blk_desc *dev_desc)
 	disk_partition_t info;
 	struct disk_part *new_disk_part;
 
-	if (disk_partitions.next == NULL)
-		INIT_LIST_HEAD(&disk_partitions);
+	/*
+	 * Always re-read partition info from device, in case
+	 * it has changed
+	 */
+	INIT_LIST_HEAD(&disk_partitions);
 
 	for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
 		ret = part_get_info(dev_desc, p, &info);
@@ -301,6 +389,8 @@ static int set_gpt_info(struct blk_desc *dev_desc,
 		return -1;
 
 	str = strdup(str_part);
+	if (str == NULL)
+		return -ENOMEM;
 
 	/* extract disk guid */
 	s = str;
@@ -530,6 +620,128 @@ static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr)
 	return ret;
 }
 
+#ifdef CONFIG_CMD_GPT_RENAME
+static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm,
+			       char *name1, char *name2)
+{
+	struct list_head *pos;
+	struct disk_part *curr;
+	disk_partition_t *new_partitions = NULL;
+	char disk_guid[UUID_STR_LEN + 1];
+	char *partitions_list, *str_disk_guid;
+	u8 part_count = 0;
+	int partlistlen, ret, numparts = 0, partnum, i = 1, ctr1 = 0, ctr2 = 0;
+
+	if ((subcomm == NULL) || (name1 == NULL) || (name2 == NULL) ||
+	    (strcmp(subcomm, "swap") && (strcmp(subcomm, "rename"))))
+		return -EINVAL;
+
+	ret = get_disk_guid(dev_desc, disk_guid);
+	if (ret < 0)
+		return ret;
+	numparts = get_gpt_info(dev_desc);
+	if (numparts <=  0)
+		return numparts ? numparts: -ENODEV;
+
+	partlistlen = calc_parts_list_len(numparts);
+	partitions_list = malloc(partlistlen);
+	if (partitions_list == NULL)
+		return -ENOMEM;
+	memset(partitions_list, '\0', partlistlen);
+
+	ret = create_gpt_partitions_list(numparts, disk_guid, partitions_list);
+	if (ret < 0)
+		return ret;
+	/*
+	 * Uncomment the following line to print a string that 'gpt write'
+	 * or 'gpt verify' will accept as input.
+	 */
+	debug("OLD partitions_list is %s with %u chars\n", partitions_list,
+	      (unsigned)strlen(partitions_list));
+
+	ret = set_gpt_info(dev_desc, partitions_list, &str_disk_guid,
+			   &new_partitions, &part_count);
+	if (ret < 0)
+		return ret;
+
+	if (!strcmp(subcomm, "swap")) {
+		if ((strlen(name1) > PART_NAME_LEN) || (strlen(name2) > PART_NAME_LEN)) {
+			printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN);
+			return -EINVAL;
+		}
+		list_for_each(pos, &disk_partitions) {
+			curr = list_entry(pos, struct disk_part, list);
+			if (!strcmp((char *)curr->gpt_part_info.name, name1)) {
+				strcpy((char *)curr->gpt_part_info.name, name2);
+				ctr1++;
+			}
+			else if (!strcmp((char *)curr->gpt_part_info.name, name2)) {
+				strcpy((char *)curr->gpt_part_info.name, name1);
+				ctr2++;
+			}
+		}
+		if ((ctr1 + ctr2 < 2) || (ctr1 != ctr2)) {
+			printf("Cannot swap partition names except in pairs.\n");
+			return -EINVAL;
+		}
+	} else { /* rename */
+		if (strlen(name2) > PART_NAME_LEN) {
+			printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN);
+			return -EINVAL;
+		}
+		partnum = (int)simple_strtol(name1, NULL, 10);
+		if ((partnum < 0) || (partnum > numparts)) {
+			printf("Illegal partition number %s\n", name1);
+			return -EINVAL;
+		}
+		ret = part_get_info(dev_desc, partnum, new_partitions);
+		if (ret < 0)
+			return ret;
+
+		/* U-Boot partition numbering starts at 1 */
+		list_for_each(pos, &disk_partitions) {
+			curr = list_entry(pos, struct disk_part, list);
+			if (i == partnum) {
+				strcpy((char *)curr->gpt_part_info.name, name2);
+				break;
+			}
+			i++;
+		}
+	}
+
+	ret = create_gpt_partitions_list(numparts, disk_guid, partitions_list);
+	if (ret < 0)
+		return ret;
+	debug("NEW partitions_list is %s with %u chars\n", partitions_list,
+	      (unsigned)strlen(partitions_list));
+
+	ret = set_gpt_info(dev_desc, partitions_list, &str_disk_guid,
+			   &new_partitions, &part_count);
+	if (ret < 0)
+		return ret;
+
+	debug("Writing new partition table\n");
+	ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts);
+	if (ret < 0) {
+		printf("Writing new partition table failed\n");
+		return ret;
+	}
+
+	debug("Reading back new partition table\n");
+	numparts = get_gpt_info(dev_desc);
+	if (numparts <=  0)
+		return numparts ? numparts : -ENODEV;
+	printf("new partition table with %d partitions is:\n", numparts);
+	print_gpt_info();
+
+	del_gpt_info();
+	free(partitions_list);
+	free(str_disk_guid);
+	free(new_partitions);
+	return ret;
+}
+#endif
+
 /**
  * do_gpt(): Perform GPT operations
  *
@@ -547,7 +759,11 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 	char *ep;
 	struct blk_desc *blk_dev_desc = NULL;
 
+#ifndef CONFIG_CMD_GPT_RENAME
 	if (argc < 4 || argc > 5)
+#else
+	if (argc < 4 || argc > 6)
+#endif
 		return CMD_RET_USAGE;
 
 	dev = (int)simple_strtoul(argv[3], &ep, 10);
@@ -572,6 +788,11 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 		ret = do_disk_guid(blk_dev_desc, argv[4]);
 	} else if (strcmp(argv[1], "read") == 0) {
 		ret = do_get_gpt_info(blk_dev_desc);
+#ifdef CONFIG_CMD_GPT_RENAME
+	} else if ((strcmp(argv[1], "swap") == 0) ||
+		   (strcmp(argv[1], "rename") == 0)) {
+		ret = do_rename_gpt_parts(blk_dev_desc, argv[1], argv[4], argv[5]);
+#endif
 	} else {
 		return CMD_RET_USAGE;
 	}
@@ -603,4 +824,15 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt,
 	" Example usage:\n"
 	" gpt guid mmc 0\n"
 	" gpt guid mmc 0 varname\n"
+#ifdef CONFIG_CMD_GPT_RENAME
+	"gpt partition renaming commands:\n"
+	"gpt swap <interface> <dev> <name1> <name2>\n"
+	"    - change all partitions named name1 to name2\n"
+	"      and vice-versa\n"
+	"gpt rename <interface> <dev> <part> <name>\n"
+	"    - rename the specified partition\n"
+	" Example usage:\n"
+	" gpt swap mmc 0 foo bar\n"
+	" gpt rename mmc 0 3 foo\n"
+#endif
 );
diff --git a/doc/README.gpt b/doc/README.gpt
index 6c5ab78..d3db8bc 100644
--- a/doc/README.gpt
+++ b/doc/README.gpt
@@ -210,6 +210,24 @@ Following line can be used to assess if GPT verification has succeed:
 U-BOOT> gpt verify mmc 0 $partitions
 U-BOOT> if test $? = 0; then echo "GPT OK"; else echo "GPT ERR"; fi
 
+Renaming GPT partitions from U-Boot:
+====================================
+
+GPT partition names are a mechanism via which userspace and U-Boot can
+communicate about software updates and boot failure.  The 'gpt guid',
+'gpt read', 'gpt rename' and 'gpt swap' commands facilitate
+programmatic renaming of partitions from bootscripts by generating and
+modifying the partitions layout string.  Here is an illustration of
+employing 'swap' to exchange 'primary' and 'backup' partition names:
+
+U-BOOT> gpt swap mmc 0 primary backup
+
+Afterwards, all partitions previously named 'primary' will be named
+'backup', and vice-versa.  Alternatively, single partitions may be
+renamed.  In this example, mmc0's first partition will be renamed
+'primary':
+
+U-BOOT> gpt rename mmc 0 1 primary
 
 The GPT functionality may be tested with the 'sandbox' board by
 creating a disk image as described under 'Block Device Emulation' in
@@ -218,7 +236,7 @@ board/sandbox/README.sandbox:
 =>host bind 0 ./disk.raw
 => gpt read host 0
 [ . . . ]
-=> gpt flip host 0
+=> gpt swap host 0 name othername
 [ . . . ]
 
 Partition type GUID:
-- 
2.1.4



More information about the U-Boot mailing list