summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2024-02-19 09:31:37 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2024-02-20 11:46:30 +0100
commit914f24316014b04ce3d6dbbb99cb7c659f4bf066 (patch)
tree482b3924849dc9b26faaf01dc23538dc26238117
parent5843759d80f728596e50c0db3b45b438596a007b (diff)
downloadbarebox-914f24316014.tar.gz
barebox-914f24316014.tar.xz
partitions: implement partition manipulation support
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. Link: https://lore.barebox.org/20240219083140.2713047-10-s.hauer@pengutronix.de Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
-rw-r--r--common/Kconfig3
-rw-r--r--common/partitions.c156
-rw-r--r--include/partitions.h22
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__ */