diff options
Diffstat (limited to 'common/partitions.c')
-rw-r--r-- | common/partitions.c | 235 |
1 files changed, 199 insertions, 36 deletions
diff --git a/common/partitions.c b/common/partitions.c index d80878e065..17c2f1eb28 100644 --- a/common/partitions.c +++ b/common/partitions.c @@ -15,8 +15,7 @@ #include <disks.h> #include <filetype.h> #include <linux/err.h> - -#include "partitions/parser.h" +#include <partitions.h> static LIST_HEAD(partition_parser_list); @@ -27,30 +26,33 @@ static LIST_HEAD(partition_parser_list); * @param no Partition number * @return 0 on success */ -static int register_one_partition(struct block_device *blk, - struct partition *part, int no) +static int register_one_partition(struct block_device *blk, struct partition *part) { char *partition_name; int ret; - uint64_t start = part->first_sec * SECTOR_SIZE; - uint64_t size = part->size * SECTOR_SIZE; struct cdev *cdev; + struct devfs_partition partinfo = { + .offset = part->first_sec * SECTOR_SIZE, + .size = part->size * SECTOR_SIZE, + }; - partition_name = basprintf("%s.%d", blk->cdev.name, no); + partition_name = basprintf("%s.%d", blk->cdev.name, part->num); if (!partition_name) return -ENOMEM; + + partinfo.name = partition_name; + dev_dbg(blk->dev, "Registering partition %s on drive %s (partuuid=%s)\n", partition_name, blk->cdev.name, part->partuuid); - cdev = devfs_add_partition(blk->cdev.name, - start, size, 0, partition_name); + cdev = cdevfs_add_partition(&blk->cdev, &partinfo); if (IS_ERR(cdev)) { ret = PTR_ERR(cdev); goto out; } - cdev->flags |= DEVFS_PARTITION_FROM_TABLE; - - cdev->dos_partition_type = part->dos_partition_type; + cdev->flags |= DEVFS_PARTITION_FROM_TABLE | part->flags; + cdev->typeflags |= part->typeflags; + cdev->typeuuid = part->typeuuid; strcpy(cdev->partuuid, part->partuuid); free(partition_name); @@ -100,42 +102,157 @@ static struct partition_parser *partition_parser_get_by_filetype(uint8_t *buf) return NULL; } -/** - * Try to collect partition information on the given block device - * @param blk Block device to examine - * @return 0 most of the time, negative value else - * - * It is not a failure if no partition information is found - */ -int parse_partition_table(struct block_device *blk) +struct partition_desc *partition_table_new(struct block_device *blk, const char *type) { struct partition_desc *pdesc; - int i; - int rc = 0; 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; - pdesc = xzalloc(sizeof(*pdesc)); buf = malloc(2 * SECTOR_SIZE); - 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; + 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 on_error; + goto err; + + pdesc = parser->parse(buf, blk); + if (!pdesc) + goto err; + + pdesc->parser = parser; +err: + free(buf); + + return pdesc; +} - parser->parse(buf, blk, 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; +} - if (!pdesc->used_entries) - goto on_error; +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 + * @return 0 most of the time, negative value else + * + * It is not a failure if no partition information is found + */ +int parse_partition_table(struct block_device *blk) +{ + int i; + int rc = 0; + struct partition *part; + struct partition_desc *pdesc; + + pdesc = partition_table_read(blk); + if (!pdesc) + return 0; /* at least one partition description found */ - for (i = 0; i < pdesc->used_entries; i++) { - rc = register_one_partition(blk, &pdesc->parts[i], i); + list_for_each_entry(part, &pdesc->partitions, list) { + rc = register_one_partition(blk, part); if (rc != 0) dev_err(blk->dev, "Failed to register partition %d on %s (%d)\n", @@ -144,15 +261,61 @@ int parse_partition_table(struct block_device *blk) rc = 0; } -on_error: - free(buf); - free(pdesc); + partition_table_free(pdesc); + return rc; } +int reparse_partition_table(struct block_device *blk) +{ + struct cdev *cdev = &blk->cdev; + struct cdev *c, *tmp; + + list_for_each_entry(c, &cdev->partitions, partition_entry) { + if (c->open) { + pr_warn("%s is busy, will continue to use old partition table\n", c->name); + return -EBUSY; + } + } + + list_for_each_entry_safe(c, tmp, &cdev->partitions, partition_entry) { + if (c->flags & DEVFS_PARTITION_FROM_TABLE) + cdevfs_del_partition(c); + } + + return parse_partition_table(blk); +} + int partition_parser_register(struct partition_parser *p) { list_add_tail(&p->list, &partition_parser_list); return 0; } + +/** + * cdev_unallocated_space - return unallocated space + * cdev: The cdev + * + * This function returns the space that is not allocated by any partition + * at the start of a device. + * + * Return: The unallocated space at the start of the device in bytes + */ +loff_t cdev_unallocated_space(struct cdev *cdev) +{ + struct cdev *partcdev; + loff_t start; + + if (!cdev) + return 0; + + start = cdev->size; + + list_for_each_entry(partcdev, &cdev->partitions, partition_entry) { + if (partcdev->offset < start) + start = partcdev->offset; + } + + return start; +} |