summaryrefslogtreecommitdiffstats
path: root/common/partitions.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/partitions.c')
-rw-r--r--common/partitions.c235
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;
+}