[PATCH 12/12] commands: add parted
Ulrich Ölmann
u.oelmann at pengutronix.de
Mon Feb 19 01:38:42 PST 2024
Hi Sascha,
On Mon, Feb 19 2024 at 09:31 +0100, Sascha Hauer <s.hauer at pengutronix.de> wrote:
> This adds a parted command which behaves pretty much like the GNU parted
> program. Unlike other partition manipulation programs parted has a quite
> convenient command line API suitable for scripting. The tool supports
> these commands:
>
> print - print a partition table
> mklabel - create a new partition table
> rm - remove a partition
> mkpart - create a partition
> unit - change input/display units
> refresh - refresh a partition table (barebox specific)
>
> Multiple commands can be given on a single call so that a full partition
> table including partitions can be created with a single command.
> Examples include:
>
> Print a partition table:
>
> $ parted mmc0 print
>
> create a new partition table:
>
> $ parted mmc0 mklabel gpt
>
> create a new partition table and add a partition beginning at offset
> 1MiB ending at offset 128MiB:
>
> $ parted mmc0 mklabel gpt mkpart rootfs ext4 1MiB 128MiB
>
> The same, using KiB as unit and printing the result at the end:
>
> $ parted mmc0 unit KiB mklabel gpt mkpart rootfs ext4 1024 131072 print
>
> The "refresh" command is barebox specific and is useful when for example
> the alternate GPT is missing. This happens when an image is written.
>
> Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
> ---
> commands/Kconfig | 21 +++
> commands/Makefile | 2 +-
> commands/parted.c | 374 ++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 396 insertions(+), 1 deletion(-)
> create mode 100644 commands/parted.c
>
> diff --git a/commands/Kconfig b/commands/Kconfig
> index a6806f198e..819fb80411 100644
> --- a/commands/Kconfig
> +++ b/commands/Kconfig
> @@ -655,6 +655,27 @@ config CMD_MOUNT
> -o OPTIONS set file system OPTIONS
> -v verbose
>
> +config CMD_PARTED
> + tristate
> + depends on PARTITION
> + select PARTITION_MANIPULATION
> + prompt "parted"
> + help
> + parted - edit partition tables
> +
> + Usage: parted <device> [command [options...]...]
> +
> + parted is a partition manipulation program with a behaviour similar to
> + GNU Parted
> +
> + commands:
> + print print partitions
> + mklabel <type> create a new partition table
> + rm <num> remove a partition
> + mkpart <name> <fstype> <start> <end> create a new partition
> + unit <unit> change display/input units
> + refresh refresh a partition table
just a small nitpick that the last line's alignment could be enhanced:
s/refresh refresh a partition table/refresh refresh a partition table/
Best regards
Ulrich
> +
> config CMD_UBI
> tristate
> default y if MTD_UBI
> diff --git a/commands/Makefile b/commands/Makefile
> index 4924755500..b311410276 100644
> --- a/commands/Makefile
> +++ b/commands/Makefile
> @@ -146,5 +146,5 @@ obj-$(CONFIG_CMD_UBSAN) += ubsan.o
> obj-$(CONFIG_CMD_SELFTEST) += selftest.o
> obj-$(CONFIG_CMD_TUTORIAL) += tutorial.o
> obj-$(CONFIG_CMD_STACKSMASH) += stacksmash.o
> -
> +obj-$(CONFIG_CMD_PARTED) += parted.o
> UBSAN_SANITIZE_ubsan.o := y
> diff --git a/commands/parted.c b/commands/parted.c
> new file mode 100644
> index 0000000000..02bb1cff0c
> --- /dev/null
> +++ b/commands/parted.c
> @@ -0,0 +1,374 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +#include <common.h>
> +#include <command.h>
> +#include <block.h>
> +#include <getopt.h>
> +#include <fcntl.h>
> +#include <disks.h>
> +#include <linux/sizes.h>
> +#include <partitions.h>
> +#include <linux/math64.h>
> +
> +static struct partition_desc *gpdesc;
> +static bool table_needs_write;
> +static const char *gunit_str = "KiB";
> +static uint64_t gunit = 1024;
> +
> +struct unit {
> + const char *str;
> + uint64_t size;
> +};
> +
> +static struct unit units[] = {
> + { .str = "B", .size = 1 },
> + { .str = "s", .size = 512 },
> + { .str = "KiB", .size = SZ_1K },
> + { .str = "MiB", .size = SZ_1M },
> + { .str = "GiB", .size = SZ_1G },
> + { .str = "TiB", .size = SZ_1T },
> + { .str = "KB", .size = 1000ULL },
> + { .str = "MB", .size = 1000ULL * 1000 },
> + { .str = "GB", .size = 1000ULL * 1000 * 1000 },
> + { .str = "TB", .size = 1000ULL * 1000 * 1000 * 1000 },
> + { .str = "k", .size = SZ_1K },
> + { .str = "K", .size = SZ_1K },
> + { .str = "M", .size = SZ_1M },
> + { .str = "G", .size = SZ_1G },
> +};
> +
> +static int parted_strtoull(const char *str, uint64_t *val, uint64_t *mult)
> +{
> + char *end;
> + int i;
> +
> + *val = simple_strtoull(str, &end, 0);
> +
> + if (!*end) {
> + *mult = 0;
> + return 0;
> + }
> +
> + for (i = 0; i < ARRAY_SIZE(units); i++) {
> + if (!strcmp(end, units[i].str)) {
> + *mult = units[i].size;
> + return 0;
> + }
> + }
> +
> + printf("Error: Cannot read \"%s\" as number\n", str);
> +
> + return -EINVAL;
> +}
> +
> +static struct partition_desc *pdesc_get(struct block_device *blk)
> +{
> + if (gpdesc)
> + return gpdesc;
> +
> + gpdesc = partition_table_read(blk);
> + if (!gpdesc) {
> + printf("Cannot read partition table\n");
> + return NULL;
> + }
> +
> + return gpdesc;
> +}
> +
> +static int do_unit(struct block_device *blk, int argc, char *argv[])
> +{
> + int i;
> +
> + if (argc < 2) {
> + printf("Error: missing unit\n");
> + return -EINVAL;
> + }
> +
> + for (i = 0; i < ARRAY_SIZE(units); i++) {
> + if (!strcmp(units[i].str, argv[1])) {
> + gunit_str = units[i].str;
> + gunit = units[i].size;
> + return 2;
> + }
> + }
> +
> + printf("invalid unit: %s\n", argv[1]);
> +
> + return -EINVAL;
> +}
> +
> +static int do_print(struct block_device *blk, int argc, char *argv[])
> +{
> + struct partition_desc *pdesc;
> + struct partition *part;
> +
> + pdesc = pdesc_get(blk);
> + if (!pdesc) {
> + printf("Error: Cannot get partition table from %s\n", blk->cdev.name);
> + return -EINVAL;
> + }
> +
> + printf("Disk /dev/%s: %s\n", blk->cdev.name,
> + size_human_readable(blk->num_blocks << SECTOR_SHIFT));
> + printf("Partition Table: %s\n", pdesc->parser->name);
> +
> + printf("Number Start End Size Name\n");
> +
> + list_for_each_entry(part, &pdesc->partitions, list) {
> + uint64_t start = part->first_sec << SECTOR_SHIFT;
> + uint64_t size = part->size << SECTOR_SHIFT;
> + uint64_t end = start + size - SECTOR_SIZE;
> +
> + printf(" %3d %10llu%-3s %10llu%-3s %10llu%-3s %-36s\n",
> + part->num,
> + div64_u64(start, gunit), gunit_str,
> + div64_u64(end, gunit), gunit_str,
> + div64_u64(size, gunit), gunit_str,
> + part->name);
> + }
> +
> + return 1;
> +}
> +
> +static int do_mkpart(struct block_device *blk, int argc, char *argv[])
> +{
> + struct partition_desc *pdesc;
> + uint64_t start, end;
> + const char *name, *fs_type;
> + int ret;
> + uint64_t mult;
> +
> + if (argc < 5) {
> + printf("Error: Missing required arguments\n");
> + return -EINVAL;
> + }
> +
> + name = argv[1];
> + fs_type = argv[2];
> +
> + ret = parted_strtoull(argv[3], &start, &mult);
> + if (ret)
> + return ret;
> +
> + ret = parted_strtoull(argv[4], &end, &mult);
> + if (ret)
> + return ret;
> +
> + if (!mult)
> + mult = gunit;
> +
> + start *= mult;
> + end *= mult;
> +
> + /* If not on sector boundaries move start up and end down */
> + start = ALIGN(start, SECTOR_SIZE);
> + end = ALIGN_DOWN(end, SECTOR_SIZE);
> +
> + /* convert to LBA */
> + start >>= SECTOR_SHIFT;
> + end >>= SECTOR_SHIFT;
> +
> + /*
> + * When unit is >= KB then substract one sector for user convenience.
> + * It allows to start the next partition where the previous ends
> + */
> + if (mult >= 1000)
> + end -= 1;
> +
> + pdesc = pdesc_get(blk);
> + if (!pdesc)
> + return -EINVAL;
> +
> + ret = partition_create(pdesc, name, fs_type, start, end);
> +
> + if (!ret)
> + table_needs_write = true;
> +
> + return ret < 0 ? ret : 5;
> +}
> +
> +static int do_rmpart(struct block_device *blk, int argc, char *argv[])
> +{
> + struct partition_desc *pdesc;
> + unsigned long num;
> + int ret;
> +
> + if (argc < 2) {
> + printf("Error: Expecting a partition number.\n");
> + return -EINVAL;
> + }
> +
> + ret = kstrtoul(argv[1], 0, &num);
> + if (ret)
> + return ret;
> +
> + pdesc = pdesc_get(blk);
> + if (!pdesc)
> + return -EINVAL;
> +
> + ret = partition_remove(pdesc, num);
> + if (ret)
> + return ret;
> +
> + table_needs_write = true;
> +
> + return 2;
> +}
> +
> +static int do_mklabel(struct block_device *blk, int argc, char *argv[])
> +{
> + struct partition_desc *pdesc;
> +
> + if (argc < 2) {
> + printf("Error: Expecting a disk label type.\n");
> + return -EINVAL;
> + }
> +
> + pdesc = partition_table_new(blk, argv[1]);
> + if (IS_ERR(pdesc)) {
> + printf("Error: Cannot create partition table: %pe\n", pdesc);
> + return PTR_ERR(pdesc);
> + }
> +
> + table_needs_write = true;
> +
> + if (gpdesc)
> + partition_table_free(gpdesc);
> + gpdesc = pdesc;
> +
> + return 2;
> +}
> +
> +static int do_refresh(struct block_device *blk, int argc, char *argv[])
> +{
> + struct partition_desc *pdesc;
> +
> + pdesc = pdesc_get(blk);
> + if (!pdesc)
> + return -EINVAL;
> +
> + table_needs_write = true;
> +
> + return 1;
> +}
> +
> +struct parted_command {
> + const char *name;
> + int (*command)(struct block_device *blk, int argc, char *argv[]);
> +};
> +
> +struct parted_command parted_commands[] = {
> + {
> + .name = "mkpart",
> + .command = do_mkpart,
> + }, {
> + .name = "print",
> + .command = do_print,
> + }, {
> + .name = "rm",
> + .command = do_rmpart,
> + }, {
> + .name = "mklabel",
> + .command = do_mklabel,
> + }, {
> + .name = "unit",
> + .command = do_unit,
> + }, {
> + .name = "refresh",
> + .command = do_refresh,
> + },
> +};
> +
> +static int parted_run_command(struct block_device *blk, int argc, char *argv[])
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(parted_commands); i++) {
> + struct parted_command *cmd = &parted_commands[i];
> +
> + if (!strcmp(argv[0], cmd->name))
> + return cmd->command(blk, argc, argv);
> + }
> +
> + printf("No such command: %s\n", argv[0]);
> +
> + return COMMAND_ERROR;
> +}
> +
> +static int do_parted(int argc, char *argv[])
> +{
> + struct cdev *cdev;
> + struct block_device *blk;
> + int ret = 0;
> +
> + table_needs_write = false;
> + gpdesc = NULL;
> +
> + if (argc < 3)
> + return COMMAND_ERROR_USAGE;
> +
> + cdev = cdev_open_by_name(argv[1], O_RDWR);
> + if (!cdev) {
> + printf("Cannot open %s\n", argv[1]);
> + return COMMAND_ERROR;
> + }
> +
> + blk = cdev_get_block_device(cdev);
> + if (!blk) {
> + ret = -EINVAL;
> + goto err;
> + }
> +
> + argc -= 2;
> + argv += 2;
> +
> + while (argc) {
> + debug("---> run command %s\n", argv[0]);
> + ret = parted_run_command(blk, argc, argv);
> + if (ret < 0)
> + break;
> +
> + argc -= ret;
> + argv += ret;
> +
> + ret = 0;
> + }
> +
> + if (!ret && gpdesc && table_needs_write)
> + ret = partition_table_write(gpdesc);
> +
> +err:
> + if (gpdesc)
> + partition_table_free(gpdesc);
> +
> + cdev_close(cdev);
> +
> + return ret;
> +}
> +
> +BAREBOX_CMD_HELP_START(parted)
> +BAREBOX_CMD_HELP_TEXT("parted is a partition manipulation program with a behaviour similar to")
> +BAREBOX_CMD_HELP_TEXT("GNU Parted")
> +BAREBOX_CMD_HELP_TEXT("")
> +BAREBOX_CMD_HELP_TEXT("commands:")
> +BAREBOX_CMD_HELP_OPT ("print", "print partitions")
> +BAREBOX_CMD_HELP_OPT ("mklabel <type>", "create a new partition table")
> +BAREBOX_CMD_HELP_OPT ("rm <num>", "remove a partition")
> +BAREBOX_CMD_HELP_OPT ("mkpart <name> <fstype> <start> <end>", "create a new partition")
> +BAREBOX_CMD_HELP_OPT ("unit <unit>", "change display/input units")
> +BAREBOX_CMD_HELP_OPT ("refresh", "refresh a partition table")
> +BAREBOX_CMD_HELP_TEXT("")
> +BAREBOX_CMD_HELP_TEXT("<unit> can be one of \"s\" (sectors), \"B\" (bytes), \"kB\", \"MB\", \"GB\", \"TB\",")
> +BAREBOX_CMD_HELP_TEXT("\"KiB\", \"MiB\", \"GiB\" or \"TiB\"")
> +BAREBOX_CMD_HELP_TEXT("<type> must be \"gpt\"")
> +BAREBOX_CMD_HELP_TEXT("<fstype> can be one of \"ext2\", \"ext3\", \"ext4\", \"fat16\" or \"fat32\"")
> +BAREBOX_CMD_HELP_TEXT("<name> for MBR partition tables can be one of \"primary\", \"extended\" or")
> +BAREBOX_CMD_HELP_TEXT("\"logical\". For GPT this is a name string.")
> +BAREBOX_CMD_HELP_END
> +
> +BAREBOX_CMD_START(parted)
> + .cmd = do_parted,
> + BAREBOX_CMD_DESC("edit partition tables")
> + BAREBOX_CMD_OPTS("<device> [command [options...]...]")
> + BAREBOX_CMD_GROUP(CMD_GRP_FILE)
> + BAREBOX_CMD_HELP(cmd_parted_help)
> +BAREBOX_CMD_END
--
Pengutronix e.K. | Ulrich Ölmann |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
More information about the barebox
mailing list