From 152bd148e9e8fce7226de7dd6553eff98832052a Mon Sep 17 00:00:00 2001 From: "Brian C. Lane" Date: Wed, 25 Aug 2021 15:36:37 -0700 Subject: [PATCH] - parted: Add --json cmdline switch to output JSON (aschnell) - parted: Allow empty string for partition name (aschnell) - libparted: Check devpath before passing to strlen (bcl) --- ...eck-devpath-before-passing-to-strlen.patch | 26 + ...llow-empty-string-for-partition-name.patch | 125 +++ ...d-json-cmdline-switch-to-output-JSON.patch | 917 ++++++++++++++++++ parted.spec | 11 +- 4 files changed, 1077 insertions(+), 2 deletions(-) create mode 100644 0032-libparted-Check-devpath-before-passing-to-strlen.patch create mode 100644 0033-parted-Allow-empty-string-for-partition-name.patch create mode 100644 0034-parted-Add-json-cmdline-switch-to-output-JSON.patch diff --git a/0032-libparted-Check-devpath-before-passing-to-strlen.patch b/0032-libparted-Check-devpath-before-passing-to-strlen.patch new file mode 100644 index 0000000..c48f653 --- /dev/null +++ b/0032-libparted-Check-devpath-before-passing-to-strlen.patch @@ -0,0 +1,26 @@ +From 74636ce7f34081e92c5680f34588217978306488 Mon Sep 17 00:00:00 2001 +From: "Brian C. Lane" +Date: Thu, 19 Aug 2021 13:56:31 -0700 +Subject: [PATCH 32/34] libparted: Check devpath before passing to strlen + +--- + libparted/arch/linux.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/libparted/arch/linux.c b/libparted/arch/linux.c +index 758d36a..430d02e 100644 +--- a/libparted/arch/linux.c ++++ b/libparted/arch/linux.c +@@ -2453,6 +2453,9 @@ _device_get_part_path (PedDevice const *dev, int num) + #else + devpath = dev->path; + #endif ++ if (!devpath) ++ return NULL; ++ + path_len = strlen (devpath); + /* Check for devfs-style /disc => /partN transformation + unconditionally; the system might be using udev with devfs rules, +-- +2.31.1 + diff --git a/0033-parted-Allow-empty-string-for-partition-name.patch b/0033-parted-Allow-empty-string-for-partition-name.patch new file mode 100644 index 0000000..23b605b --- /dev/null +++ b/0033-parted-Allow-empty-string-for-partition-name.patch @@ -0,0 +1,125 @@ +From 72faef8673b5b1d00059608f0729e93cb21a7664 Mon Sep 17 00:00:00 2001 +From: Arvin Schnell +Date: Fri, 13 Aug 2021 13:02:27 +0000 +Subject: [PATCH 33/34] parted: Allow empty string for partition name + +This makes it possible to pass an empty string in script mode e.g. to +set no partition name (on GPT): + +parted -s ./disk.img mklabel gpt mkpart '""' ext2 1 100M + +Includes a new test for this feature. + +Signed-off-by: Brian C. Lane +--- + parted/ui.c | 9 +++++++-- + tests/Makefile.am | 1 + + tests/t0290-gpt-name.sh | 41 +++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 49 insertions(+), 2 deletions(-) + create mode 100755 tests/t0290-gpt-name.sh + +diff --git a/parted/ui.c b/parted/ui.c +index b5948d3..25ad4f1 100644 +--- a/parted/ui.c ++++ b/parted/ui.c +@@ -16,7 +16,6 @@ + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +-#include + + #include + +@@ -727,6 +726,7 @@ void + command_line_push_line (const char* line, int multi_word) + { + int quoted = 0; ++ int quotes_empty = 0; + char quote_char = 0; + char this_word [256]; + int i; +@@ -754,6 +754,9 @@ command_line_push_line (const char* line, int multi_word) + + if (quoted && *line == quote_char) { + quoted = 0; ++ /* allow empty partition name in script mode */ ++ if (!i) ++ quotes_empty = 1; + continue; + } + +@@ -761,9 +764,11 @@ command_line_push_line (const char* line, int multi_word) + if (quoted && line[0] == '\\' && line[1]) + line++; + ++ quotes_empty = 0; + this_word [i++] = *line; + } +- if (i || !multi_word) { ++ if (i || !multi_word || quotes_empty) { ++ quotes_empty = 0; + this_word [i] = 0; + command_line_push_word (this_word); + } +diff --git a/tests/Makefile.am b/tests/Makefile.am +index 3473e6b..9c4a79d 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -33,6 +33,7 @@ TESTS = \ + t0281-gpt-grow.sh \ + t0282-gpt-move-backup.sh \ + t0283-overlap-partitions.sh \ ++ t0290-gpt-name.sh \ + t0300-dos-on-gpt.sh \ + t0301-overwrite-gpt-pmbr.sh \ + t0350-mac-PT-increases-sector-size.sh \ +diff --git a/tests/t0290-gpt-name.sh b/tests/t0290-gpt-name.sh +new file mode 100755 +index 0000000..26041b6 +--- /dev/null ++++ b/tests/t0290-gpt-name.sh +@@ -0,0 +1,41 @@ ++#!/bin/sh ++ ++# Test setting empty GPT partition name in non-interactive mode ++ ++# Copyright (C) 2021 SUSE LLC ++ ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 3 of the License, or ++# (at your option) any later version. ++ ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++ ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++. "${srcdir=.}/init.sh"; path_prepend_ ../parted ++ ++dev=loop-file ++ ++truncate --size 50MiB "$dev" || fail=1 ++ ++# create partition with empty name ++parted --script "$dev" mklabel gpt mkpart '""' ext4 1MiB 49MiB > out 2>&1 || fail=1 ++parted --script --machine "$dev" unit MiB print > out 2>&1 || fail=1 ++grep 'MiB:::;' out || fail=1 ++ ++# set a non-empty name ++parted --script "$dev" name 1 "test" > out 2>&1 || fail=1 ++parted --script --machine "$dev" unit MiB print > out 2>&1 || fail=1 ++grep 'MiB::test:;' out || fail=1 ++ ++# set empty name ++parted --script "$dev" name 1 "''" > out 2>&1 || fail=1 ++parted --script --machine "$dev" unit MiB print > out 2>&1 || fail=1 ++grep 'MiB:::;' out || fail=1 ++ ++Exit $fail +-- +2.31.1 + diff --git a/0034-parted-Add-json-cmdline-switch-to-output-JSON.patch b/0034-parted-Add-json-cmdline-switch-to-output-JSON.patch new file mode 100644 index 0000000..c9611e3 --- /dev/null +++ b/0034-parted-Add-json-cmdline-switch-to-output-JSON.patch @@ -0,0 +1,917 @@ +From 36d16c57d4ff6cf9903bfe65671c7f90879e004f Mon Sep 17 00:00:00 2001 +From: Arvin Schnell +Date: Tue, 17 Aug 2021 11:05:38 +0000 +Subject: [PATCH 34/34] parted: Add --json cmdline switch to output JSON + +This outputs the disk's details as a JSON object. eg. a disk image with +a single partition from 1M to 100M: + +{ + "disk": { + "path": "/root/disk1.img", + "size": "2097152s", + "model": "", + "transport": "file", + "logical-sector-size": 512, + "physical-sector-size": 512, + "label": "gpt", + "max-partitions": 128, + "partitions": [ + { + "number": 0, + "start": "34s", + "end": "2047s", + "size": "2014s", + "type": "free" + },{ + "number": 1, + "start": "2048s", + "end": "200703s", + "size": "198656s", + "type": "primary", + "name": "root" + },{ + "number": 0, + "start": "200704s", + "end": "2097118s", + "size": "1896415s", + "type": "free" + } + ] + } +} + +Signed-off-by: Brian C. Lane +--- + doc/C/parted.8 | 3 + + doc/parted.texi | 8 ++ + parted/Makefile.am | 2 + + parted/jsonwrt.c | 225 ++++++++++++++++++++++++++++++++++++++ + parted/jsonwrt.h | 46 ++++++++ + parted/parted.c | 170 ++++++++++++++++++++++++++-- + tests/Makefile.am | 2 + + tests/t0800-json-gpt.sh | 94 ++++++++++++++++ + tests/t0801-json-msdos.sh | 86 +++++++++++++++ + 9 files changed, 624 insertions(+), 12 deletions(-) + create mode 100644 parted/jsonwrt.c + create mode 100644 parted/jsonwrt.h + create mode 100755 tests/t0800-json-gpt.sh + create mode 100755 tests/t0801-json-msdos.sh + +diff --git a/doc/C/parted.8 b/doc/C/parted.8 +index d8e556e..46b30ad 100644 +--- a/doc/C/parted.8 ++++ b/doc/C/parted.8 +@@ -24,6 +24,9 @@ lists partition layout on all block devices + .B -m, --machine + displays machine parseable output + .TP ++.B -m, --json ++displays JSON output ++.TP + .B -s, --script + never prompts for user intervention + .TP +diff --git a/doc/parted.texi b/doc/parted.texi +index 0da68e9..57ceb55 100644 +--- a/doc/parted.texi ++++ b/doc/parted.texi +@@ -402,6 +402,14 @@ Options: + @itemx --help + display a help message + ++@item -m ++@itemx --machine ++display output in machine parseable format ++ ++@item -j ++@itemx --json ++display output in JSON format ++ + @item -s + @itemx --script + never prompt the user +diff --git a/parted/Makefile.am b/parted/Makefile.am +index e7bba2e..e5f3288 100644 +--- a/parted/Makefile.am ++++ b/parted/Makefile.am +@@ -13,6 +13,8 @@ parted_SOURCES = command.c \ + strlist.h \ + ui.c \ + ui.h \ ++ jsonwrt.c \ ++ jsonwrt.h \ + table.c \ + table.h + +diff --git a/parted/jsonwrt.c b/parted/jsonwrt.c +new file mode 100644 +index 0000000..1560224 +--- /dev/null ++++ b/parted/jsonwrt.c +@@ -0,0 +1,225 @@ ++/* ++ * JSON output formatting functions. ++ * ++ * No copyright is claimed. This code is in the public domain; do with ++ * it what you wish. ++ * ++ * Written by Karel Zak ++ */ ++ ++#include "config.h" ++#include ++#include ++#include ++#include ++#include ++#include "jsonwrt.h" ++ ++/* ++ * Requirements enumerated via testing (V8, Firefox, IE11): ++ * ++ * var charsToEscape = []; ++ * for (var i = 0; i < 65535; i += 1) { ++ * try { ++ * JSON.parse('{"sample": "' + String.fromCodePoint(i) + '"}'); ++ * } catch (e) { ++ * charsToEscape.push(i); ++ * } ++ * } ++ */ ++static void fputs_quoted_case_json(const char *data, FILE *out, int dir) ++{ ++ const char *p; ++ ++ fputc('"', out); ++ for (p = data; p && *p; p++) { ++ ++ const unsigned int c = (unsigned int) *p; ++ ++ /* From http://www.json.org ++ * ++ * The double-quote and backslashes would break out a string or ++ * init an escape sequence if not escaped. ++ * ++ * Note that single-quotes and forward slashes, while they're ++ * in the JSON spec, don't break double-quoted strings. ++ */ ++ if (c == '"' || c == '\\') { ++ fputc('\\', out); ++ fputc(c, out); ++ continue; ++ } ++ ++ /* All non-control characters OK; do the case swap as required. */ ++ if (c >= 0x20) { ++ /* ++ * Don't use locale sensitive ctype.h functions for regular ++ * ASCII chars, because for example with Turkish locale ++ * (aka LANG=tr_TR.UTF-8) toupper('I') returns 'I'. ++ */ ++ if (c <= 127) ++ fputc(dir == 1 ? c_toupper(c) : ++ dir == -1 ? c_tolower(c) : *p, out); ++ else ++ fputc(dir == 1 ? toupper(c) : ++ dir == -1 ? tolower(c) : *p, out); ++ continue; ++ } ++ ++ /* In addition, all chars under ' ' break Node's/V8/Chrome's, and ++ * Firefox's JSON.parse function ++ */ ++ switch (c) { ++ /* Handle short-hand cases to reduce output size. C ++ * has most of the same stuff here, so if there's an ++ * "Escape for C" function somewhere in the STL, we ++ * should probably be using it. ++ */ ++ case '\b': ++ fputs("\\b", out); ++ break; ++ case '\t': ++ fputs("\\t", out); ++ break; ++ case '\n': ++ fputs("\\n", out); ++ break; ++ case '\f': ++ fputs("\\f", out); ++ break; ++ case '\r': ++ fputs("\\r", out); ++ break; ++ default: ++ /* Other assorted control characters */ ++ fprintf(out, "\\u00%02x", c); ++ break; ++ } ++ } ++ fputc('"', out); ++} ++ ++#define fputs_quoted_json(_d, _o) fputs_quoted_case_json(_d, _o, 0) ++#define fputs_quoted_json_upper(_d, _o) fputs_quoted_case_json(_d, _o, 1) ++#define fputs_quoted_json_lower(_d, _o) fputs_quoted_case_json(_d, _o, -1) ++ ++void ul_jsonwrt_init(struct ul_jsonwrt *fmt, FILE *out, int indent) ++{ ++ fmt->out = out; ++ fmt->indent = indent; ++ fmt->after_close = 0; ++} ++ ++void ul_jsonwrt_indent(struct ul_jsonwrt *fmt) ++{ ++ int i; ++ ++ for (i = 0; i < fmt->indent; i++) ++ fputs(" ", fmt->out); ++} ++ ++void ul_jsonwrt_open(struct ul_jsonwrt *fmt, const char *name, int type) ++{ ++ if (name) { ++ if (fmt->after_close) ++ fputs(",\n", fmt->out); ++ ul_jsonwrt_indent(fmt); ++ fputs_quoted_json_lower(name, fmt->out); ++ } else { ++ if (fmt->after_close) ++ fputs(",", fmt->out); ++ else ++ ul_jsonwrt_indent(fmt); ++ } ++ ++ switch (type) { ++ case UL_JSON_OBJECT: ++ fputs(name ? ": {\n" : "{\n", fmt->out); ++ fmt->indent++; ++ break; ++ case UL_JSON_ARRAY: ++ fputs(name ? ": [\n" : "[\n", fmt->out); ++ fmt->indent++; ++ break; ++ case UL_JSON_VALUE: ++ fputs(name ? ": " : " ", fmt->out); ++ break; ++ } ++ fmt->after_close = 0; ++} ++ ++void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type) ++{ ++ if (fmt->indent == 1) { ++ fputs("\n}\n", fmt->out); ++ fmt->indent--; ++ fmt->after_close = 1; ++ return; ++ } ++ assert(fmt->indent > 0); ++ ++ switch (type) { ++ case UL_JSON_OBJECT: ++ fmt->indent--; ++ fputc('\n', fmt->out); ++ ul_jsonwrt_indent(fmt); ++ fputs("}", fmt->out); ++ break; ++ case UL_JSON_ARRAY: ++ fmt->indent--; ++ fputc('\n', fmt->out); ++ ul_jsonwrt_indent(fmt); ++ fputs("]", fmt->out); ++ break; ++ case UL_JSON_VALUE: ++ break; ++ } ++ ++ fmt->after_close = 1; ++} ++ ++void ul_jsonwrt_value_raw(struct ul_jsonwrt *fmt, ++ const char *name, const char *data) ++{ ++ ul_jsonwrt_value_open(fmt, name); ++ if (data && *data) ++ fputs(data, fmt->out); ++ else ++ fputs("null", fmt->out); ++ ul_jsonwrt_value_close(fmt); ++} ++ ++void ul_jsonwrt_value_s(struct ul_jsonwrt *fmt, ++ const char *name, const char *data) ++{ ++ ul_jsonwrt_value_open(fmt, name); ++ if (data) ++ fputs_quoted_json(data, fmt->out); ++ else ++ fputs("null", fmt->out); ++ ul_jsonwrt_value_close(fmt); ++} ++ ++void ul_jsonwrt_value_u64(struct ul_jsonwrt *fmt, ++ const char *name, uint64_t data) ++{ ++ ul_jsonwrt_value_open(fmt, name); ++ fprintf(fmt->out, "%"PRIu64, data); ++ ul_jsonwrt_value_close(fmt); ++} ++ ++void ul_jsonwrt_value_boolean(struct ul_jsonwrt *fmt, ++ const char *name, int data) ++{ ++ ul_jsonwrt_value_open(fmt, name); ++ fputs(data ? "true" : "false", fmt->out); ++ ul_jsonwrt_value_close(fmt); ++} ++ ++void ul_jsonwrt_value_null(struct ul_jsonwrt *fmt, ++ const char *name) ++{ ++ ul_jsonwrt_value_open(fmt, name); ++ fputs("null", fmt->out); ++ ul_jsonwrt_value_close(fmt); ++} +diff --git a/parted/jsonwrt.h b/parted/jsonwrt.h +new file mode 100644 +index 0000000..4587b60 +--- /dev/null ++++ b/parted/jsonwrt.h +@@ -0,0 +1,46 @@ ++#ifndef UTIL_LINUX_JSONWRT_H ++#define UTIL_LINUX_JSONWRT_H ++ ++enum { ++ UL_JSON_OBJECT, ++ UL_JSON_ARRAY, ++ UL_JSON_VALUE ++}; ++ ++struct ul_jsonwrt { ++ FILE *out; ++ int indent; ++ ++ unsigned int after_close :1; ++}; ++ ++void ul_jsonwrt_init(struct ul_jsonwrt *fmt, FILE *out, int indent); ++void ul_jsonwrt_indent(struct ul_jsonwrt *fmt); ++void ul_jsonwrt_open(struct ul_jsonwrt *fmt, const char *name, int type); ++void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type); ++ ++#define ul_jsonwrt_root_open(_f) ul_jsonwrt_open(_f, NULL, UL_JSON_OBJECT) ++#define ul_jsonwrt_root_close(_f) ul_jsonwrt_close(_f, UL_JSON_OBJECT) ++ ++#define ul_jsonwrt_array_open(_f, _n) ul_jsonwrt_open(_f, _n, UL_JSON_ARRAY) ++#define ul_jsonwrt_array_close(_f) ul_jsonwrt_close(_f, UL_JSON_ARRAY) ++ ++#define ul_jsonwrt_object_open(_f, _n) ul_jsonwrt_open(_f, _n, UL_JSON_OBJECT) ++#define ul_jsonwrt_object_close(_f) ul_jsonwrt_close(_f, UL_JSON_OBJECT) ++ ++#define ul_jsonwrt_value_open(_f, _n) ul_jsonwrt_open(_f, _n, UL_JSON_VALUE) ++#define ul_jsonwrt_value_close(_f) ul_jsonwrt_close(_f, UL_JSON_VALUE) ++ ++ ++void ul_jsonwrt_value_raw(struct ul_jsonwrt *fmt, ++ const char *name, const char *data); ++void ul_jsonwrt_value_s(struct ul_jsonwrt *fmt, ++ const char *name, const char *data); ++void ul_jsonwrt_value_u64(struct ul_jsonwrt *fmt, ++ const char *name, uint64_t data); ++void ul_jsonwrt_value_boolean(struct ul_jsonwrt *fmt, ++ const char *name, int data); ++void ul_jsonwrt_value_null(struct ul_jsonwrt *fmt, ++ const char *name); ++ ++#endif /* UTIL_LINUX_JSONWRT_H */ +diff --git a/parted/parted.c b/parted/parted.c +index 65b5ab2..975700c 100644 +--- a/parted/parted.c ++++ b/parted/parted.c +@@ -57,6 +57,7 @@ + #include "c-ctype.h" + #include "c-strcase.h" + #include "xalloc.h" ++#include "jsonwrt.h" + + #ifdef ENABLE_MTRACE + #include +@@ -79,6 +80,14 @@ enum + PRETEND_INPUT_TTY = CHAR_MAX + 1, + }; + ++/* Output modes */ ++enum ++{ ++ HUMAN, ++ MACHINE, ++ JSON ++}; ++ + enum + { + ALIGNMENT_NONE = 2, +@@ -115,6 +124,7 @@ static struct option const options[] = { + {"help", 0, NULL, 'h'}, + {"list", 0, NULL, 'l'}, + {"machine", 0, NULL, 'm'}, ++ {"json", 0, NULL, 'j'}, + {"script", 0, NULL, 's'}, + {"fix", 0, NULL, 'f'}, + {"version", 0, NULL, 'v'}, +@@ -127,6 +137,7 @@ static const char *const options_help [][2] = { + {"help", N_("displays this help message")}, + {"list", N_("lists partition layout on all block devices")}, + {"machine", N_("displays machine parseable output")}, ++ {"json", N_("displays JSON output")}, + {"script", N_("never prompts for user intervention")}, + {"fix", N_("in script mode, fix instead of abort when asked")}, + {"version", N_("displays the version")}, +@@ -137,7 +148,7 @@ static const char *const options_help [][2] = { + int opt_script_mode = 0; + int opt_fix_mode = 0; + int pretend_input_tty = 0; +-int opt_machine_mode = 0; ++int opt_output_mode = HUMAN; + int disk_is_modified = 0; + int is_toggle_mode = 0; + int alignment = ALIGNMENT_OPTIMAL; +@@ -184,6 +195,8 @@ static Command* commands [256] = {NULL}; + static PedTimer* g_timer; + static TimerContext timer_context; + ++static struct ul_jsonwrt json; ++ + static int _print_list (); + static void _done (PedDevice* dev, PedDisk *diskp); + static bool partition_align_check (PedDisk const *disk, +@@ -931,6 +944,32 @@ partition_print_flags (PedPartition const *part) + return res; + } + ++static void ++partition_print_flags_json (PedPartition const *part) ++{ ++ if (!part) ++ return; ++ ++ int did_open_array = 0; ++ ++ PedPartitionFlag flag; ++ for (flag = ped_partition_flag_next (0); flag; ++ flag = ped_partition_flag_next (flag)) ++ { ++ if (ped_partition_get_flag (part, flag)) ++ { ++ if (!did_open_array) ++ ul_jsonwrt_array_open (&json, "flags"); ++ did_open_array = 1; ++ ++ ul_jsonwrt_value_s (&json, NULL, ped_partition_flag_get_name (flag)); ++ } ++ } ++ ++ if (did_open_array) ++ ul_jsonwrt_array_close (&json); ++} ++ + static int + partition_print (PedPartition* part) + { +@@ -964,6 +1003,32 @@ disk_print_flags (PedDisk const *disk) + return res; + } + ++static void ++disk_print_flags_json (PedDisk const *disk) ++{ ++ if (!disk) ++ return; ++ ++ int did_open_array = 0; ++ ++ PedDiskFlag flag; ++ for (flag = ped_disk_flag_next (0); flag; ++ flag = ped_disk_flag_next (flag)) ++ { ++ if (ped_disk_get_flag (disk, flag)) ++ { ++ if (!did_open_array) ++ ul_jsonwrt_array_open (&json, "flags"); ++ did_open_array = 1; ++ ++ ul_jsonwrt_value_s (&json, NULL, ped_disk_flag_get_name (flag)); ++ } ++ } ++ ++ if (did_open_array) ++ ul_jsonwrt_array_close (&json); ++} ++ + static void + _print_disk_geometry (const PedDevice *dev) + { +@@ -973,9 +1038,14 @@ _print_disk_geometry (const PedDevice *dev) + chs->heads * chs->sectors, + PED_UNIT_KILOBYTE); + +- if (opt_machine_mode) { ++ if (opt_output_mode == MACHINE) { + printf ("%d:%d:%d:%s;\n", + chs->cylinders, chs->heads, chs->sectors, cyl_size); ++ } else if (opt_output_mode == JSON) { ++ char* tmp = ped_malloc (128); ++ snprintf (tmp, 128, "%d,%d,%d %s", chs->cylinders, chs->heads, chs->sectors, cyl_size); ++ ul_jsonwrt_value_s (&json, "geometry", tmp); ++ free (tmp); + } else { + printf (_("BIOS cylinder,head,sector geometry: %d,%d,%d. " + "Each cylinder is %s.\n"), +@@ -1030,7 +1100,7 @@ _print_disk_info (const PedDevice *dev, const PedDisk *diskp) + const char* pt_name = diskp ? diskp->type->name : "unknown"; + char *disk_flags = disk_print_flags (diskp); + +- if (opt_machine_mode) { ++ if (opt_output_mode == MACHINE) { + char *escaped_path = _escape_machine_string (dev->path); + char *escaped_model = _escape_machine_string (dev->model); + +@@ -1049,6 +1119,20 @@ _print_disk_info (const PedDevice *dev, const PedDisk *diskp) + pt_name, escaped_model, disk_flags); + free (escaped_path); + free (escaped_model); ++ } else if (opt_output_mode == JSON) { ++ ul_jsonwrt_value_s (&json, "path", dev->path); ++ ul_jsonwrt_value_s (&json, "size", end); ++ ul_jsonwrt_value_s (&json, "model", dev->model); ++ ul_jsonwrt_value_s (&json, "transport", transport[dev->type]); ++ ul_jsonwrt_value_u64 (&json, "logical-sector-size", dev->sector_size); ++ ul_jsonwrt_value_u64 (&json, "physical-sector-size", dev->phys_sector_size); ++ ul_jsonwrt_value_s (&json, "label", pt_name); ++ if (diskp) { ++ if (diskp->type->ops->get_max_primary_partition_count) ++ ul_jsonwrt_value_u64 (&json, "max-partitions", ++ diskp->type->ops->get_max_primary_partition_count(diskp)); ++ disk_print_flags_json (diskp); ++ } + } else { + printf (_("Model: %s (%s)\n"), + dev->model, transport[dev->type]); +@@ -1064,7 +1148,7 @@ _print_disk_info (const PedDevice *dev, const PedDisk *diskp) + || ped_unit_get_default () == PED_UNIT_CYLINDER) + _print_disk_geometry (dev); + +- if (!opt_machine_mode) { ++ if (opt_output_mode == HUMAN) { + printf (_("Partition Table: %s\n"), pt_name); + printf (_("Disk Flags: %s\n"), disk_flags); + } +@@ -1170,10 +1254,16 @@ do_print (PedDevice** dev, PedDisk** diskp) + return status; + } + ++ if (opt_output_mode == JSON) { ++ ul_jsonwrt_init (&json, stdout, 0); ++ ul_jsonwrt_root_open (&json); ++ ul_jsonwrt_object_open (&json, "disk"); ++ } ++ + _print_disk_info (*dev, *diskp); + if (!*diskp) + goto nopt; +- if (!opt_machine_mode) ++ if (opt_output_mode == HUMAN) + putchar ('\n'); + + has_extended = ped_disk_type_check_feature ((*diskp)->type, +@@ -1182,7 +1272,7 @@ do_print (PedDevice** dev, PedDisk** diskp) + PED_DISK_TYPE_PARTITION_NAME); + + PedPartition* part; +- if (!opt_machine_mode) { ++ if (opt_output_mode == HUMAN) { + StrList *row1; + + if (ped_unit_get_default() == PED_UNIT_CHS) { +@@ -1283,6 +1373,57 @@ do_print (PedDevice** dev, PedDisk** diskp) + table_destroy (table); + str_list_destroy (row1); + ++ } else if (opt_output_mode == JSON) { ++ ++ ul_jsonwrt_array_open (&json, "partitions"); ++ ++ for (part = ped_disk_next_partition (*diskp, NULL); part; ++ part = ped_disk_next_partition (*diskp, part)) { ++ ++ if ((!has_free_arg && !ped_partition_is_active(part)) || ++ part->type & PED_PARTITION_METADATA) ++ continue; ++ ++ ul_jsonwrt_object_open (&json, NULL); ++ ++ ul_jsonwrt_value_u64 (&json, "number", part->num >= 0 ? part->num : 0); ++ ++ tmp = ped_unit_format (*dev, part->geom.start); ++ ul_jsonwrt_value_s (&json, "start", tmp); ++ free (tmp); ++ ++ tmp = ped_unit_format_byte (*dev, (part->geom.end + 1) * (*dev)->sector_size - 1); ++ ul_jsonwrt_value_s (&json, "end", tmp); ++ free (tmp); ++ ++ if (ped_unit_get_default() != PED_UNIT_CHS) { ++ tmp = ped_unit_format (*dev, part->geom.length); ++ ul_jsonwrt_value_s (&json, "size", tmp); ++ free (tmp); ++ } ++ ++ name = ped_partition_type_get_name (part->type); ++ ul_jsonwrt_value_s (&json, "type", name); ++ ++ if (!(part->type & PED_PARTITION_FREESPACE)) { ++ ++ if (has_name) { ++ name = ped_partition_get_name (part); ++ if (strcmp (name, "") != 0) ++ ul_jsonwrt_value_s (&json, "name", ped_partition_get_name (part)); ++ } ++ ++ if (part->fs_type) ++ ul_jsonwrt_value_s (&json, "filesystem", part->fs_type->name); ++ ++ partition_print_flags_json (part); ++ } ++ ++ ul_jsonwrt_object_close (&json); ++ } ++ ++ ul_jsonwrt_array_close (&json); ++ + } else { + + for (part = ped_disk_next_partition (*diskp, NULL); part; +@@ -1337,9 +1478,13 @@ do_print (PedDevice** dev, PedDisk** diskp) + } + } + +- return ok; +- + nopt: ++ ++ if (opt_output_mode == JSON) { ++ ul_jsonwrt_object_close (&json); ++ ul_jsonwrt_root_close (&json); ++ } ++ + return ok; + } + +@@ -2232,7 +2377,7 @@ int opt, help = 0, list = 0, version = 0, wrong = 0; + + while (1) + { +- opt = getopt_long (*argc_ptr, *argv_ptr, "hlmsfva:", ++ opt = getopt_long (*argc_ptr, *argv_ptr, "hlmjsfva:", + options, NULL); + if (opt == -1) + break; +@@ -2240,7 +2385,8 @@ while (1) + switch (opt) { + case 'h': help = 1; break; + case 'l': list = 1; break; +- case 'm': opt_machine_mode = 1; break; ++ case 'm': opt_output_mode = MACHINE; break; ++ case 'j': opt_output_mode = JSON; break; + case 's': opt_script_mode = 1; break; + case 'f': opt_fix_mode = 1; break; + case 'v': version = 1; break; +@@ -2289,7 +2435,7 @@ _choose_device (int* argc_ptr, char*** argv_ptr) + { + PedDevice* dev; + +-/* specified on comand line? */ ++/* specified on command line? */ + if (*argc_ptr) { + dev = ped_device_get ((*argv_ptr) [0]); + if (!dev) +@@ -2377,7 +2523,7 @@ _done (PedDevice* dev, PedDisk* diskp) + "rebooting. Read section 4 of the Parted User " + "documentation for more information.")); + } +- if (!opt_script_mode && !opt_machine_mode && disk_is_modified) { ++ if (!opt_script_mode && opt_output_mode == HUMAN && disk_is_modified) { + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, PED_EXCEPTION_OK, + _("You may need to update /etc/fstab.\n")); +diff --git a/tests/Makefile.am b/tests/Makefile.am +index 9c4a79d..f9340aa 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -40,6 +40,8 @@ TESTS = \ + t0400-loop-clobber-infloop.sh \ + t0500-dup-clobber.sh \ + t0501-duplicate.sh \ ++ t0800-json-gpt.sh \ ++ t0801-json-msdos.sh \ + t1100-busy-label.sh \ + t1101-busy-partition.sh \ + t1102-loop-label.sh \ +diff --git a/tests/t0800-json-gpt.sh b/tests/t0800-json-gpt.sh +new file mode 100755 +index 0000000..8dd1862 +--- /dev/null ++++ b/tests/t0800-json-gpt.sh +@@ -0,0 +1,94 @@ ++#!/bin/sh ++ ++# Test JSON output with GPT label ++ ++# Copyright (C) 2021 SUSE LLC ++ ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 3 of the License, or ++# (at your option) any later version. ++ ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++ ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++. "${srcdir=.}/init.sh"; path_prepend_ ../parted ++require_512_byte_sector_size_ ++ ++dev=loop-file ++ ++# create device ++truncate --size 50MiB "$dev" || fail=1 ++ ++# create gpt label and some partitions ++parted --script "$dev" mklabel gpt > out 2>&1 || fail=1 ++parted --script "$dev" disk_set pmbr_boot on > out 2>&1 || fail=1 ++parted --script "$dev" mkpart "test1" ext4 10% 20% > out 2>&1 || fail=1 ++parted --script "$dev" mkpart "test2" xfs 20% 60% > out 2>&1 || fail=1 ++parted --script "$dev" set 2 raid on > out 2>&1 || fail=1 ++ ++# print with json format ++parted --script --json "$dev" unit s print free > out 2>&1 || fail=1 ++ ++cat < exp || fail=1 ++{ ++ "disk": { ++ "path": "loop-file", ++ "size": "102400s", ++ "model": "", ++ "transport": "file", ++ "logical-sector-size": 512, ++ "physical-sector-size": 512, ++ "label": "gpt", ++ "max-partitions": 128, ++ "flags": [ ++ "pmbr_boot" ++ ], ++ "partitions": [ ++ { ++ "number": 0, ++ "start": "34s", ++ "end": "10239s", ++ "size": "10206s", ++ "type": "free" ++ },{ ++ "number": 1, ++ "start": "10240s", ++ "end": "20479s", ++ "size": "10240s", ++ "type": "primary", ++ "name": "test1" ++ },{ ++ "number": 2, ++ "start": "20480s", ++ "end": "61439s", ++ "size": "40960s", ++ "type": "primary", ++ "name": "test2", ++ "flags": [ ++ "raid" ++ ] ++ },{ ++ "number": 0, ++ "start": "61440s", ++ "end": "102366s", ++ "size": "40927s", ++ "type": "free" ++ } ++ ] ++ } ++} ++EOF ++ ++# remove full path of device from actual output ++mv out o2 && sed "s,\"/.*/$dev\",\"$dev\"," o2 > out || fail=1 ++ ++# check for expected output ++compare exp out || fail=1 ++ ++Exit $fail +diff --git a/tests/t0801-json-msdos.sh b/tests/t0801-json-msdos.sh +new file mode 100755 +index 0000000..a14a5af +--- /dev/null ++++ b/tests/t0801-json-msdos.sh +@@ -0,0 +1,86 @@ ++#!/bin/sh ++ ++# Test JSON output with MS-DOS label ++ ++# Copyright (C) 2021 SUSE LLC ++ ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 3 of the License, or ++# (at your option) any later version. ++ ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++ ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++. "${srcdir=.}/init.sh"; path_prepend_ ../parted ++require_512_byte_sector_size_ ++ ++dev=loop-file ++ ++# create device ++truncate --size 50MiB "$dev" || fail=1 ++ ++# create msdos label and some partitions ++parted --script "$dev" mklabel msdos > out 2>&1 || fail=1 ++parted --script "$dev" mkpart primary ext4 10% 20% > out 2>&1 || fail=1 ++parted --script "$dev" mkpart extended 20% 60% > out 2>&1 || fail=1 ++parted --script "$dev" mkpart logical ext4 20% 40% > out 2>&1 || fail=1 ++parted --script "$dev" set 5 lvm on > out 2>&1 || fail=1 ++ ++# print with json format ++parted --script --json "$dev" unit MiB print > out 2>&1 || fail=1 ++ ++cat < exp || fail=1 ++{ ++ "disk": { ++ "path": "loop-file", ++ "size": "50.0MiB", ++ "model": "", ++ "transport": "file", ++ "logical-sector-size": 512, ++ "physical-sector-size": 512, ++ "label": "msdos", ++ "max-partitions": 4, ++ "partitions": [ ++ { ++ "number": 1, ++ "start": "5.00MiB", ++ "end": "10.0MiB", ++ "size": "5.00MiB", ++ "type": "primary" ++ },{ ++ "number": 2, ++ "start": "10.0MiB", ++ "end": "30.0MiB", ++ "size": "20.0MiB", ++ "type": "extended", ++ "flags": [ ++ "lba" ++ ] ++ },{ ++ "number": 5, ++ "start": "10.0MiB", ++ "end": "20.0MiB", ++ "size": "10.0MiB", ++ "type": "logical", ++ "flags": [ ++ "lvm" ++ ] ++ } ++ ] ++ } ++} ++EOF ++ ++# remove full path of device from actual output ++mv out o2 && sed "s,\"/.*/$dev\",\"$dev\"," o2 > out || fail=1 ++ ++# check for expected output ++compare exp out || fail=1 ++ ++Exit $fail +-- +2.31.1 + diff --git a/parted.spec b/parted.spec index 3813d29..0cf7d08 100644 --- a/parted.spec +++ b/parted.spec @@ -1,7 +1,7 @@ Summary: The GNU disk partition manipulation program Name: parted Version: 3.4 -Release: 7%{?dist} +Release: 8%{?dist} License: GPLv3+ URL: http://www.gnu.org/software/parted @@ -41,8 +41,10 @@ Patch0027: 0027-tests-add-aarch64-and-mips64-as-a-valid-64-bit-machi.patch Patch0028: 0028-tests-add-a-helper-to-check-the-kernel-knows-about-a.patch Patch0029: 0029-tests-check-for-vfat-kernel-support-and-tools.patch Patch0030: 0030-parted-Escape-colons-and-backslashes-in-machine-outp.patch -# Fixes rhbz#1980697 but not yet upstream Patch0031: 0031-libparted-Tell-libdevmapper-to-retry-remove-when-BUS.patch +Patch0032: 0032-libparted-Check-devpath-before-passing-to-strlen.patch +Patch0033: 0033-parted-Allow-empty-string-for-partition-name.patch +Patch0034: 0034-parted-Add-json-cmdline-switch-to-output-JSON.patch BuildRequires: gcc BuildRequires: e2fsprogs-devel @@ -146,6 +148,11 @@ make check %changelog +* Wed Aug 25 2021 Brian C. Lane - 3.4-8 +- parted: Add --json cmdline switch to output JSON (aschnell) +- parted: Allow empty string for partition name (aschnell) +- libparted: Check devpath before passing to strlen (bcl) + * Tue Aug 10 2021 Brian C. Lane - 3.4-7 - libparted: Tell libdevmapper to retry remove when BUSY (bcl) Resolves: rhbz#1980697