@@ -33,6 +33,10 @@ config HAVE_LIBCURL
bool
option env="HAVE_LIBCURL"
+config HAVE_LIBFDISK
+ bool
+ option env="HAVE_LIBFDISK"
+
config HAVE_LIBGPIOD
bool
option env="HAVE_LIBGPIOD"
@@ -14,6 +14,10 @@ ifeq ($(HAVE_LIBCURL),)
export HAVE_LIBCURL = y
endif
+ifeq ($(HAVE_LIBFDISK),)
+export HAVE_LIBFDISK = y
+endif
+
ifeq ($(HAVE_LIBGPIOD),)
export HAVE_LIBGPIOD = y
endif
@@ -166,6 +166,10 @@ ifeq ($(CONFIG_ZSTD),y)
LDLIBS += zstd
endif
+ifeq ($(CONFIG_DISKPART),y)
+LDLIBS += fdisk
+endif
+
ifeq ($(CONFIG_RDIFFHANDLER),y)
LDLIBS += rsync
endif
@@ -23,6 +23,8 @@ Supplied handlers
In mainline there are the handlers for the most common cases. They include:
- flash devices in raw mode (both NOR and NAND)
- UBI volumes
+ - UBI volumus partitioner
+ - disk partitioner
- raw devices, such as a SD Card partition
- bootloader (U-Boot, GRUB, EFI Boot Guard) environment
- Lua scripts
@@ -684,3 +686,60 @@ Properties ``size`` and ``offset`` are optional, all the other properties are ma
| offset | string | Offset (in bytes) to the start of the partition. |
| | | If not set, default value 0 will be used. |
+-------------+----------+----------------------------------------------------+
+
+Disk partitioner
+----------------
+
+This handler creates or modifies partitions using the library libfdisk. Handler must be put into
+the `partitions` section of sw-description. Setup for each partition is put into the `properties` field
+of sw-description.
+
+.. table:: Properties for diskpart handler
+
+ +-------------+----------+----------------------------------------------------+
+ | Name | Type | Description |
+ +=============+==========+====================================================+
+ | labeltype | string | "gpt" or "dos" |
+ +-------------+----------+----------------------------------------------------+
+ | partition-X | array | Array of values belonging to the partition number X|
+ +-------------+----------+----------------------------------------------------+
+
+For each partition, an array of couples key=value must be given. The following keys are
+supported:
+
+.. table:: Setup for a disk partition
+
+ +-------------+----------+----------------------------------------------------+
+ | Name | Type | Description |
+ +=============+==========+====================================================+
+ | size | string | Size of partition. K, M and G can be used for |
+ | | | Kilobytes, Megabytes and Gigabytes. |
+ +-------------+----------+----------------------------------------------------+
+ | start | integer | First sector for the partition |
+ +-------------+----------+----------------------------------------------------+
+ | name | string | Name of the partition |
+ +-------------+----------+----------------------------------------------------+
+ | type | string | Type of partition, it has two different meanings. |
+ | | | It is the hex code for DOS (MBR) partition table |
+ | | | or it is the string identifier in case of GPT. |
+ +-------------+----------+----------------------------------------------------+
+
+
+
+Example:
+
+::
+
+ properties: {
+ labeltype = "gpt";
+ partition-1 = [ "size=64M", "start=2048",
+ "name=bigrootfs", "type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B"];
+ partition-2 = ["size=256M", "start=133120",
+ "name=ldata", "type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"];
+ partition-3 = ["size=512M", "start=657408",
+ "name=log", "type=0FC63DAF-8483-4772-8E79-3D69D8477DE4"];
+ partition-4 = ["size=4G", "start=1705984",
+ "name=system", "type=0FC63DAF-8483-4772-8E79-3D69D8477DE4"];
+ partition-5 = ["size=512M", "start=10094592",
+ "name=part5", "type=0FC63DAF-8483-4772-8E79-3D69D8477DE4"];
+ }
@@ -92,6 +92,16 @@ config CFIHAMMING1
You do not need this if you do not have an OMAP SoC.
+config DISKPART
+ bool "diskpart"
+ depends on HAVE_LIBFDISK
+ default n
+ help
+ Handler to partition a disk, eMMC or SD
+
+comment "diskpart support needs libfdisk"
+ depends on !HAVE_LIBFDISK
+
config RAW
bool "raw"
default n
@@ -11,6 +11,7 @@ obj-y += dummy_handler.o
obj-$(CONFIG_ARCHIVE) += archive_handler.o
obj-$(CONFIG_BOOTLOADERHANDLER) += boot_handler.o
obj-$(CONFIG_CFI) += flash_handler.o
+obj-$(CONFIG_DISKPART) += diskpart_handler.o
obj-$(CONFIG_CFIHAMMING1)+= flash_hamming1_handler.o
obj-$(CONFIG_LUASCRIPTHANDLER) += lua_scripthandler.o
obj-$(CONFIG_RAW) += raw_handler.o
new file mode 100644
@@ -0,0 +1,297 @@
+/*
+ * (C) Copyright 2019
+ * Stefano Babic, DENX Software Engineering, sbabic@denx.de.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <libfdisk/libfdisk.h>
+#include "swupdate.h"
+#include "handler.h"
+#include "util.h"
+
+void diskpart_handler(void);
+
+/**
+ * Keys for the properties field in sw-description
+ */
+enum partfield {
+ PART_SIZE = 0,
+ PART_START,
+ PART_TYPE,
+ PART_NAME
+};
+
+const char *fields[] = {
+ [PART_SIZE] = "size",
+ [PART_START] = "start",
+ [PART_TYPE] = "type",
+ [PART_NAME] = "name"
+};
+
+struct partition_data {
+ size_t partno;
+ uint64_t size;
+ size_t start;
+ char type[SWUPDATE_GENERAL_STRING_SIZE];
+ char name[SWUPDATE_GENERAL_STRING_SIZE];
+ LIST_ENTRY(partition_data) next;
+};
+LIST_HEAD(listparts, partition_data);
+
+/*
+ * Internal handler data
+ */
+struct hnd_priv {
+ enum fdisk_labeltype labeltype;
+ struct listparts listparts; /* list of partitions */
+};
+
+/**
+ * diskpart_set_partition - set values in a fdisk_partition
+ * @cxt: libfdisk context
+ * @pa: pointer to fdisk_partition to be changed
+ * @part: structure with values to be set, read from sw-description
+ *
+ * return 0 if ok
+ */
+static int diskpart_set_partition(struct fdisk_context *cxt,
+ struct fdisk_partition *pa,
+ struct partition_data *part)
+{
+ unsigned long sector_size = fdisk_get_sector_size(cxt);
+ struct fdisk_label *lb;
+ struct fdisk_parttype *parttype = NULL;
+ int ret;
+
+ lb = fdisk_get_label(cxt, NULL);
+
+ if (!sector_size)
+ sector_size = 1;
+ ret = fdisk_partition_set_partno(pa, part->partno) ||
+ fdisk_partition_set_size(pa, part->size / sector_size) ||
+ fdisk_partition_set_name(pa, part->name) ||
+ fdisk_partition_set_start(pa, part->start);
+
+ /*
+ * GPT uses strings instead of hex code for partition type
+ */
+ if (fdisk_is_label(cxt, GPT)) {
+ parttype = fdisk_label_get_parttype_from_string(lb, part->type);
+ } else if (fdisk_is_label(cxt, DOS)) {
+ parttype = fdisk_label_get_parttype_from_code(lb, ustrtoull(part->type, 16));
+ } else
+ WARN("Partition type set just for GPT or DOS");
+
+ if (parttype)
+ ret |= fdisk_partition_set_type(pa, parttype);
+ return ret;
+}
+
+static int diskpart(struct img_type *img,
+ void __attribute__ ((__unused__)) *data)
+{
+ char *lbtype = dict_get_value(&img->properties, "labeltype");
+ struct dict_list *parts;
+ struct dict_list_elem *elem;
+ struct fdisk_context *cxt;
+ struct partition_data *part;
+ int ret = 0;
+ int i;
+ struct hnd_priv priv;
+
+ if (lbtype && strcmp(lbtype, "gpt") && strcmp(lbtype, "dos")) {
+ ERROR("Just GPT or DOS partition table are supported");
+ return -EINVAL;
+ }
+ LIST_INIT(&priv.listparts);
+ if (!strlen(img->device)) {
+ ERROR("Partition handler without setting the device");
+ return -EINVAL;
+ }
+
+ cxt = fdisk_new_context();
+ if (!cxt) {
+ ERROR("Failed to allocate libfdisk context");
+ return -ENOMEM;
+ }
+
+ ret = fdisk_assign_device(cxt, img->device, 0);
+ if (ret == -EACCES) {
+ ERROR("no access to %s", img->device);
+ goto handler_release;
+ }
+
+ struct dict_entry *entry;
+ LIST_FOREACH(entry, &img->properties, next) {
+ parts = &entry->list;
+ if (!parts)
+ continue;
+
+ if (strncmp(dict_entry_get_key(entry),
+ "partition-", strlen("partition-")))
+ continue;
+
+ part = (struct partition_data *)calloc(1, sizeof(struct partition_data));
+ if (!part) {
+ ERROR("FAULT: no memory");
+ ret = -ENOMEM;
+ goto handler_exit;
+ }
+ elem = LIST_FIRST(parts);
+
+ part->partno = strtoul(entry->key + strlen("partition-"), NULL, 10);
+ while (elem) {
+ char *equal = index(elem->value, '=');
+ if (equal) {
+ for (i = 0; i < ARRAY_SIZE(fields); i++) {
+ if (!((equal - elem->value) == strlen(fields[i]) &&
+ !strncmp(elem->value, fields[i], strlen(fields[i]))))
+ continue;
+ equal++;
+ switch (i) {
+ case PART_SIZE:
+ part->size = ustrtoull(equal, 10);
+ break;
+ case PART_START:
+ part->start = ustrtoull(equal, 10);
+ break;
+ case PART_TYPE:
+ strncpy(part->type, equal, sizeof(part->type));
+ break;
+ case PART_NAME:
+ strncpy(part->name, equal, sizeof(part->name));
+ break;
+ }
+ }
+ }
+ elem = LIST_NEXT(elem, next);
+ }
+
+ TRACE("partition-%lu:%s size %lu start %lu type %s",
+ part->partno,
+ part->name,
+ part->size,
+ part->start,
+ part->type);
+
+ /*
+ * Partitions in sw-description start from 1,
+ * libfdisk first partition is 0
+ */
+ if (part->partno > 0)
+ part->partno--;
+
+ /*
+ * Insert the partition in the list sorted by partno
+ */
+ struct partition_data *p = LIST_FIRST(&priv.listparts);
+ if (!p)
+ LIST_INSERT_HEAD(&priv.listparts, part, next);
+ else {
+ while (LIST_NEXT(p, next) &&
+ LIST_NEXT(p, next)->partno < part->partno)
+ p = LIST_NEXT(p, next);
+ LIST_INSERT_BEFORE(p, part, next);
+ }
+ }
+ /*
+ * Check partition table
+ */
+ if (!fdisk_has_label(cxt)) {
+ WARN("%s does not contain a recognized partition table",
+ img->device);
+ fdisk_create_disklabel(cxt, lbtype);
+ } else if (lbtype) {
+ if (!strcmp(lbtype, "gpt"))
+ priv.labeltype = FDISK_DISKLABEL_GPT;
+ else
+ priv.labeltype = FDISK_DISKLABEL_DOS;
+
+ if (!fdisk_is_labeltype(cxt, priv.labeltype)) {
+ WARN("Partition table of different type, setting to %s, all data lost !",
+ lbtype);
+ fdisk_create_disklabel(cxt, lbtype);
+ }
+ }
+
+ i = 0;
+
+ if (priv.labeltype == FDISK_DISKLABEL_DOS) {
+ fdisk_delete_all_partitions(cxt);
+ }
+
+ LIST_FOREACH(part, &priv.listparts, next) {
+ struct fdisk_partition *pa = NULL;
+ size_t partno;
+
+ /*
+ * Allow to have not consecutives partitions
+ */
+ if (part->partno > i) {
+ while (i < part->partno) {
+ TRACE("DELETE PARTITION %d", i);
+ fdisk_delete_partition(cxt, i);
+ i++;
+ }
+ }
+
+ if (fdisk_get_partition(cxt, part->partno, &pa)) {
+ struct fdisk_partition *newpa;
+ newpa = fdisk_new_partition();
+ ret = diskpart_set_partition(cxt, newpa, part);
+ if (ret) {
+ WARN("I cannot set all partition's parameters");
+ }
+ if (fdisk_add_partition(cxt, newpa, &partno) < 0) {
+ ERROR("I cannot add partition %lu(%s)", part->partno, part->name);
+ }
+ fdisk_unref_partition(newpa);
+ } else {
+ ret = diskpart_set_partition(cxt, pa, part);
+ if (ret) {
+ WARN("I cannot set all partition's parameters");
+ }
+ if (fdisk_set_partition(cxt, part->partno, pa) < 0) {
+ ERROR("I cannot modify partition %lu(%s)", part->partno, part->name);
+ }
+ fdisk_unref_partition(pa);
+ }
+ i++;
+ }
+
+ /*
+ * Everything done, write into disk
+ */
+ ret = fdisk_write_disklabel(cxt);
+
+handler_exit:
+ if (fdisk_deassign_device(cxt, 0))
+ WARN("Error deassign device %s", img->device);
+
+handler_release:
+ LIST_FOREACH(part, &priv.listparts, next) {
+ LIST_REMOVE(part, next);
+ free(part);
+ }
+
+ fdisk_unref_context(cxt);
+
+ return ret;
+}
+
+__attribute__((constructor))
+void diskpart_handler(void)
+{
+ register_handler("diskpart", diskpart,
+ PARTITION_HANDLER | NO_DATA_HANDLER, NULL);
+}
Add handler to change partition of a block device. Signed-off-by: Stefano Babic <sbabic@denx.de> --- Kconfig | 4 + Makefile.deps | 4 + Makefile.flags | 4 + doc/source/handlers.rst | 59 +++++++ handlers/Config.in | 10 ++ handlers/Makefile | 1 + handlers/diskpart_handler.c | 297 ++++++++++++++++++++++++++++++++++++ 7 files changed, 379 insertions(+) create mode 100644 handlers/diskpart_handler.c