[PATCH 09/12] partitions: implement partition manipulation support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This adds a function API to manipulate partition tables. Basically
partition_table_new() and partition_table_read() are factored out from
existing code. Apart from that we will have partition_create() to
create a new partition and partition_remove() to remove a partition.
These functions do basic sanity checks like partition overlap checking,
access beyond device cheks and call into not yet existing efi or msdos
specific function hooks.

Signed-off-by: Sascha Hauer <s.hauer@xxxxxxxxxxxxxx>
---
 common/Kconfig       |   3 +
 common/partitions.c  | 156 ++++++++++++++++++++++++++++++++++++-------
 include/partitions.h |  22 ++++++
 3 files changed, 157 insertions(+), 24 deletions(-)

diff --git a/common/Kconfig b/common/Kconfig
index ddca1e913b..d16c8696da 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -841,6 +841,9 @@ config PARTITION
 	bool
 	prompt "Enable Partitions"
 
+config PARTITION_MANIPULATION
+	bool
+
 source "common/partitions/Kconfig"
 
 config ENV_HANDLING
diff --git a/common/partitions.c b/common/partitions.c
index abbe31ed11..7bcd0973b8 100644
--- a/common/partitions.c
+++ b/common/partitions.c
@@ -102,6 +102,133 @@ static struct partition_parser *partition_parser_get_by_filetype(uint8_t *buf)
 	return NULL;
 }
 
+struct partition_desc *partition_table_new(struct block_device *blk, const char *type)
+{
+	struct partition_desc *pdesc;
+	struct partition_parser *parser;
+
+	list_for_each_entry(parser, &partition_parser_list, list) {
+		if (!strcmp(parser->name, type))
+			goto found;
+	}
+
+	pr_err("Cannot find partition parser \"%s\"\n", type);
+
+	return ERR_PTR(-ENOSYS);
+
+found:
+	pdesc = parser->create(blk);
+	if (IS_ERR(pdesc))
+		return ERR_CAST(pdesc);
+
+	pdesc->parser = parser;
+
+	return pdesc;
+}
+
+struct partition_desc *partition_table_read(struct block_device *blk)
+{
+	struct partition_parser *parser;
+	struct partition_desc *pdesc = NULL;
+	uint8_t *buf;
+	int ret;
+
+	buf = malloc(2 * SECTOR_SIZE);
+
+	ret = block_read(blk, buf, 0, 2);
+	if (ret != 0) {
+		dev_err(blk->dev, "Cannot read MBR/partition table: %pe\n", ERR_PTR(ret));
+		goto err;
+	}
+
+	parser = partition_parser_get_by_filetype(buf);
+	if (!parser)
+		goto err;
+
+	pdesc = parser->parse(buf, blk);
+	pdesc->parser = parser;
+err:
+	free(buf);
+
+	return pdesc;
+}
+
+int partition_table_write(struct partition_desc *pdesc)
+{
+	if (!pdesc->parser->write)
+		return -ENOSYS;
+
+	return pdesc->parser->write(pdesc);
+}
+
+static bool overlap(uint64_t s1, uint64_t e1, uint64_t s2, uint64_t e2)
+{
+	if (e1 < s2)
+		return false;
+	if (s1 > e2)
+		return false;
+	return true;
+}
+
+int partition_create(struct partition_desc *pdesc, const char *name,
+		     const char *fs_type, uint64_t lba_start, uint64_t lba_end)
+{
+	struct partition *part;
+
+	if (!pdesc->parser->mkpart)
+		return -ENOSYS;
+
+	if (lba_end < lba_start) {
+		pr_err("lba_end < lba_start: %llu < %llu\n", lba_end, lba_start);
+		return -EINVAL;
+	}
+
+	if (lba_end >= pdesc->blk->num_blocks) {
+		pr_err("lba_end exceeds device: %llu >= %llu\n", lba_end, pdesc->blk->num_blocks);
+		return -EINVAL;
+	}
+
+	list_for_each_entry(part, &pdesc->partitions, list) {
+		if (overlap(part->first_sec,
+				part->first_sec + part->size - 1,
+				lba_start, lba_end)) {
+			pr_err("new partition %llu-%llu overlaps with partition %s (%llu-%llu)\n",
+			       lba_start, lba_end, part->name, part->first_sec,
+				part->first_sec + part->size - 1);
+			return -EINVAL;
+		}
+	}
+
+	return pdesc->parser->mkpart(pdesc, name, fs_type, lba_start, lba_end);
+}
+
+int partition_remove(struct partition_desc *pdesc, int num)
+{
+	struct partition *part;
+
+	if (!pdesc->parser->rmpart)
+		return -ENOSYS;
+
+	list_for_each_entry(part, &pdesc->partitions, list) {
+		if (part->num == num)
+			return pdesc->parser->rmpart(pdesc, part);
+	}
+
+	pr_err("Partition %d doesn't exist\n", num);
+	return -ENOENT;
+}
+
+void partition_table_free(struct partition_desc *pdesc)
+{
+	pdesc->parser->partition_free(pdesc);
+}
+
+void partition_desc_init(struct partition_desc *pd, struct block_device *blk)
+{
+	pd->blk = blk;
+	INIT_LIST_HEAD(&pd->partitions);
+}
+
 /**
  * Try to collect partition information on the given block device
  * @param blk Block device to examine
@@ -111,31 +238,14 @@ static struct partition_parser *partition_parser_get_by_filetype(uint8_t *buf)
  */
 int parse_partition_table(struct block_device *blk)
 {
-	struct partition_desc *pdesc = NULL;
 	int i;
 	int rc = 0;
-	struct partition_parser *parser;
 	struct partition *part;
-	uint8_t *buf;
-
-	buf = malloc(2 * SECTOR_SIZE);
+	struct partition_desc *pdesc;
 
-	rc = block_read(blk, buf, 0, 2);
-	if (rc != 0) {
-		dev_err(blk->dev, "Cannot read MBR/partition table: %pe\n", ERR_PTR(rc));
-		goto on_error;
-	}
-
-	parser = partition_parser_get_by_filetype(buf);
-	if (!parser)
-		goto on_error;
-
-	pdesc = parser->parse(buf, blk);
+	pdesc = partition_table_read(blk);
 	if (!pdesc)
-		goto on_error;
-
-	if (list_empty(&pdesc->partitions))
-		goto on_error;
+		return 0;
 
 	/* at least one partition description found */
 	list_for_each_entry(part, &pdesc->partitions, list) {
@@ -148,10 +258,8 @@ int parse_partition_table(struct block_device *blk)
 			rc = 0;
 	}
 
-on_error:
-	free(buf);
-	if (pdesc)
-		parser->partition_free(pdesc);
+	partition_table_free(pdesc);
+
 	return rc;
 }
 
diff --git a/include/partitions.h b/include/partitions.h
index ab593109e6..b5379db92a 100644
--- a/include/partitions.h
+++ b/include/partitions.h
@@ -28,18 +28,40 @@ struct partition {
 	int num;
 };
 
+struct partition_parser;
+
 struct partition_desc {
 	struct list_head partitions;
+	struct partition_parser *parser;
+	struct block_device *blk;
 };
 
 struct partition_parser {
 	struct partition_desc *(*parse)(void *buf, struct block_device *blk);
 	void (*partition_free)(struct partition_desc *pd);
+	struct partition_desc *(*create)(struct block_device *blk);
+	int (*mkpart)(struct partition_desc *pd, const char *name, const char *fs_type,
+		      uint64_t start, uint64_t end);
+	int (*rmpart)(struct partition_desc *pd, struct partition *part);
+	int (*write)(struct partition_desc *pd);
+	int (*rename)(struct partition *part, const char *name);
+	int (*setguid)(struct partition *part, guid_t *guid);
 	enum filetype type;
 
 	struct list_head list;
+
+	const char *name;
 };
 
+void partition_desc_init(struct partition_desc *pd, struct block_device *blk);
 int partition_parser_register(struct partition_parser *p);
+struct partition_desc *partition_table_read(struct block_device *blk);
+struct partition_desc *partition_table_new(struct block_device *blk, const char *type);
+int partition_table_write(struct partition_desc *pdesc);
+int partition_create(struct partition_desc *pdesc, const char *name,
+		     const char *fs_type, uint64_t lba_start, uint64_t lba_end);
+int partition_remove(struct partition_desc *pdesc, int num);
+void partition_table_free(struct partition_desc *pdesc);
+
 
 #endif /* __PARTITIONS_PARSER_H__ */
-- 
2.39.2





[Index of Archives]     [Linux Embedded]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux