]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/partition/repart.c
man: fix incorrectly placed full stop
[thirdparty/systemd.git] / src / partition / repart.c
index 46e82eaf90a360c6d46e21a39c60c2d63c225472..9a9fd6dff3a3cf0e340cb0c6d5311b661c6a51e5 100644 (file)
@@ -33,6 +33,7 @@
 #include "fs-util.h"
 #include "gpt.h"
 #include "id128-util.h"
+#include "json.h"
 #include "list.h"
 #include "locale-util.h"
 #include "main-func.h"
@@ -41,6 +42,7 @@
 #include "pretty-print.h"
 #include "proc-cmdline.h"
 #include "sort-util.h"
+#include "specifier.h"
 #include "stat-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
 #include "terminal-util.h"
 #include "utf8.h"
 
+/* If not configured otherwise use a minimal partition size of 10M */
+#define DEFAULT_MIN_SIZE (10*1024*1024)
+
+/* Hard lower limit for new partition sizes */
+#define HARD_MIN_SIZE 4096
+
 /* Note: When growing and placing new partitions we always align to 4K sector size. It's how newer hard disks
  * are designed, and if everything is aligned to that performance is best. And for older hard disks with 512B
  * sector size devices were generally assumed to have an even number of sectors, hence at the worst we'll
@@ -72,6 +80,8 @@ static sd_id128_t arg_seed = SD_ID128_NULL;
 static bool arg_randomize = false;
 static int arg_pretty = -1;
 static uint64_t arg_size = UINT64_MAX;
+static bool arg_json = false;
+static JsonFormatFlags arg_json_format_flags = 0;
 
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_definitions, freep);
@@ -107,6 +117,10 @@ struct Partition {
         FreeArea *padding_area;
         FreeArea *allocated_to_area;
 
+        char *copy_blocks_path;
+        int copy_blocks_fd;
+        uint64_t copy_blocks_size;
+
         LIST_FIELDS(Partition, partitions);
 };
 
@@ -167,6 +181,8 @@ static Partition *partition_new(void) {
                 .padding_max = UINT64_MAX,
                 .partno = UINT64_MAX,
                 .offset = UINT64_MAX,
+                .copy_blocks_fd = -1,
+                .copy_blocks_size = UINT64_MAX,
         };
 
         return p;
@@ -185,6 +201,9 @@ static Partition* partition_free(Partition *p) {
         if (p->new_partition)
                 fdisk_unref_partition(p->new_partition);
 
+        free(p->copy_blocks_path);
+        safe_close(p->copy_blocks_fd);
+
         return mfree(p);
 }
 
@@ -321,7 +340,9 @@ static uint64_t partition_min_size(const Partition *p) {
 
         /* Calculate the disk space we really need at minimum for this partition. If the partition already
          * exists the current size is what we really need. If it doesn't exist yet refuse to allocate less
-         * than 4K. */
+         * than 4K.
+         *
+         * DEFAULT_MIN_SIZE is the default SizeMin= we configure if nothing else is specified. */
 
         if (PARTITION_IS_FOREIGN(p)) {
                 /* Don't allow changing size of partitions not managed by us */
@@ -329,11 +350,12 @@ static uint64_t partition_min_size(const Partition *p) {
                 return p->current_size;
         }
 
-        sz = p->current_size != UINT64_MAX ? p->current_size : 4096;
-        if (p->size_min != UINT64_MAX)
-                return MAX(p->size_min, sz);
+        sz = p->current_size != UINT64_MAX ? p->current_size : HARD_MIN_SIZE;
+
+        if (p->copy_blocks_size != UINT64_MAX)
+                sz = MAX(p->copy_blocks_size, sz);
 
-        return sz;
+        return MAX(p->size_min != UINT64_MAX ? p->size_min : DEFAULT_MIN_SIZE, sz);
 }
 
 static uint64_t partition_max_size(const Partition *p) {
@@ -857,33 +879,53 @@ static int config_parse_label(
                 void *data,
                 void *userdata) {
 
+        static const Specifier specifier_table[] = {
+                { 'm', specifier_machine_id,      NULL },
+                { 'b', specifier_boot_id,         NULL },
+                { 'H', specifier_host_name,       NULL },
+                { 'l', specifier_short_host_name, NULL },
+                { 'v', specifier_kernel_release,  NULL },
+                { 'a', specifier_architecture,    NULL },
+                { 'o', specifier_os_id,           NULL },
+                { 'w', specifier_os_version_id,   NULL },
+                { 'B', specifier_os_build_id,     NULL },
+                { 'W', specifier_os_variant_id,   NULL },
+                {}
+        };
+
         _cleanup_free_ char16_t *recoded = NULL;
+        _cleanup_free_ char *resolved = NULL;
         char **label = data;
         int r;
 
         assert(rvalue);
         assert(label);
 
-        if (!utf8_is_valid(rvalue)) {
+        r = specifier_printf(rvalue, specifier_table, NULL, &resolved);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to expand specifiers in Label=, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        if (!utf8_is_valid(resolved)) {
                 log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Partition label not valid UTF-8, ignoring: %s", rvalue);
                 return 0;
         }
 
-        recoded = utf8_to_utf16(rvalue, strlen(rvalue));
+        recoded = utf8_to_utf16(resolved, strlen(resolved));
         if (!recoded)
                 return log_oom();
 
         if (char16_strlen(recoded) > 36) {
                 log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "Partition label too long for GPT table, ignoring: %s", rvalue);
+                           "Partition label too long for GPT table, ignoring: \"%s\" (from \"%s\")",
+                           resolved, rvalue);
                 return 0;
         }
 
-        r = free_and_strdup(label, rvalue);
-        if (r < 0)
-                return log_oom();
-
+        free_and_replace(*label, resolved);
         return 0;
 }
 
@@ -913,7 +955,7 @@ static int config_parse_weight(
         }
 
         if (v > 1000U*1000U) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Weight needs to be in range 0…10000000, ignoring: %" PRIu32, v);
                 return 0;
         }
@@ -942,7 +984,7 @@ static int config_parse_size4096(
 
         r = parse_size(rvalue, 1024, &parsed);
         if (r < 0)
-                return log_syntax(unit, LOG_WARNING, filename, line, r,
+                return log_syntax(unit, LOG_ERR, filename, line, r,
                                   "Failed to parse size value: %s", rvalue);
 
         if (ltype > 0)
@@ -961,21 +1003,28 @@ static int config_parse_size4096(
 static int partition_read_definition(Partition *p, const char *path) {
 
         ConfigTableItem table[] = {
-                { "Partition", "Type",            config_parse_type,     0,  &p->type_uuid      },
-                { "Partition", "Label",           config_parse_label,    0,  &p->new_label      },
-                { "Partition", "Priority",        config_parse_int32,    0,  &p->priority       },
-                { "Partition", "Weight",          config_parse_weight,   0,  &p->weight         },
-                { "Partition", "PaddingWeight",   config_parse_weight,   0,  &p->padding_weight },
-                { "Partition", "SizeMinBytes",    config_parse_size4096, 1,  &p->size_min       },
-                { "Partition", "SizeMaxBytes",    config_parse_size4096, -1, &p->size_max       },
-                { "Partition", "PaddingMinBytes", config_parse_size4096, 1,  &p->padding_min    },
-                { "Partition", "PaddingMaxBytes", config_parse_size4096, -1, &p->padding_max    },
-                { "Partition", "FactoryReset",    config_parse_bool,     0,  &p->factory_reset  },
+                { "Partition", "Type",            config_parse_type,     0,  &p->type_uuid        },
+                { "Partition", "Label",           config_parse_label,    0,  &p->new_label        },
+                { "Partition", "UUID",            config_parse_id128,    0,  &p->new_uuid         },
+                { "Partition", "Priority",        config_parse_int32,    0,  &p->priority         },
+                { "Partition", "Weight",          config_parse_weight,   0,  &p->weight           },
+                { "Partition", "PaddingWeight",   config_parse_weight,   0,  &p->padding_weight   },
+                { "Partition", "SizeMinBytes",    config_parse_size4096, 1,  &p->size_min         },
+                { "Partition", "SizeMaxBytes",    config_parse_size4096, -1, &p->size_max         },
+                { "Partition", "PaddingMinBytes", config_parse_size4096, 1,  &p->padding_min      },
+                { "Partition", "PaddingMaxBytes", config_parse_size4096, -1, &p->padding_max      },
+                { "Partition", "FactoryReset",    config_parse_bool,     0,  &p->factory_reset    },
+                { "Partition", "CopyBlocks",      config_parse_path,     0,  &p->copy_blocks_path },
                 {}
         };
         int r;
 
-        r = config_parse(NULL, path, NULL, "Partition\0", config_item_table_lookup, table, CONFIG_PARSE_WARN, p);
+        r = config_parse(NULL, path, NULL,
+                         "Partition\0",
+                         config_item_table_lookup, table,
+                         CONFIG_PARSE_WARN,
+                         p,
+                         NULL);
         if (r < 0)
                 return r;
 
@@ -1574,12 +1623,23 @@ static int context_dump_partitions(Context *context, const char *node) {
         Partition *p;
         int r;
 
-        t = table_new("type", "label", "uuid", "file", "node", "offset", "raw size", "size", "raw padding", "padding");
+        if (!arg_json && context->n_partitions == 0) {
+                log_info("Empty partition table.");
+                return 0;
+        }
+
+        t = table_new("type", "label", "uuid", "file", "node", "offset", "old size", "raw size", "size", "old padding", "raw padding", "padding", "activity");
         if (!t)
                 return log_oom();
 
-        if (!DEBUG_LOGGING)
-                (void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4, (size_t) 7, (size_t) 9, (size_t) -1);
+        if (!DEBUG_LOGGING) {
+                if (arg_json)
+                        (void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
+                                                    (size_t) 5, (size_t) 6, (size_t) 7, (size_t) 9, (size_t) 10, (size_t) 12, (size_t) -1);
+                else
+                        (void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
+                                                    (size_t) 8, (size_t) 11, (size_t) -1);
+        }
 
         (void) table_set_align_percent(t, table_get_cell(t, 0, 4), 100);
         (void) table_set_align_percent(t, table_get_cell(t, 0, 5), 100);
@@ -1587,11 +1647,16 @@ static int context_dump_partitions(Context *context, const char *node) {
         LIST_FOREACH(partitions, p, context->partitions) {
                 _cleanup_free_ char *size_change = NULL, *padding_change = NULL, *partname = NULL;
                 char uuid_buffer[ID128_UUID_STRING_MAX];
-                const char *label;
+                const char *label, *activity = NULL;
 
                 if (p->dropped)
                         continue;
 
+                if (p->current_size == UINT64_MAX)
+                        activity = "create";
+                else if (p->current_size != p->new_size)
+                        activity = "resize";
+
                 label = partition_label(p);
                 partname = p->partno != UINT64_MAX ? fdisk_partname(node, p->partno+1) : NULL;
 
@@ -1614,17 +1679,20 @@ static int context_dump_partitions(Context *context, const char *node) {
                                 TABLE_STRING, label ?: "-", TABLE_SET_COLOR, label ? NULL : ansi_grey(),
                                 TABLE_UUID, sd_id128_is_null(p->new_uuid) ? p->current_uuid : p->new_uuid,
                                 TABLE_STRING, p->definition_path ? basename(p->definition_path) : "-", TABLE_SET_COLOR, p->definition_path ? NULL : ansi_grey(),
-                                TABLE_STRING, partname ?: "no", TABLE_SET_COLOR, partname ? NULL : ansi_highlight(),
+                                TABLE_STRING, partname ?: "-", TABLE_SET_COLOR, partname ? NULL : ansi_highlight(),
                                 TABLE_UINT64, p->offset,
+                                TABLE_UINT64, p->current_size == UINT64_MAX ? 0 : p->current_size,
                                 TABLE_UINT64, p->new_size,
                                 TABLE_STRING, size_change, TABLE_SET_COLOR, !p->partitions_next && sum_size > 0 ? ansi_underline() : NULL,
+                                TABLE_UINT64, p->current_padding == UINT64_MAX ? 0 : p->current_padding,
                                 TABLE_UINT64, p->new_padding,
-                                TABLE_STRING, padding_change, TABLE_SET_COLOR, !p->partitions_next && sum_padding > 0 ? ansi_underline() : NULL);
+                                TABLE_STRING, padding_change, TABLE_SET_COLOR, !p->partitions_next && sum_padding > 0 ? ansi_underline() : NULL,
+                                TABLE_STRING, activity ?: "unknown");
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add row to table: %m");
+                        return table_log_add_error(r);
         }
 
-        if (sum_padding > 0 || sum_size > 0) {
+        if (!arg_json && (sum_padding > 0 || sum_size > 0)) {
                 char s[FORMAT_BYTES_MAX];
                 const char *a, *b;
 
@@ -1640,14 +1708,20 @@ static int context_dump_partitions(Context *context, const char *node) {
                                 TABLE_EMPTY,
                                 TABLE_EMPTY,
                                 TABLE_EMPTY,
+                                TABLE_EMPTY,
                                 TABLE_STRING, a,
                                 TABLE_EMPTY,
-                                TABLE_STRING, b);
+                                TABLE_EMPTY,
+                                TABLE_STRING, b,
+                                TABLE_EMPTY);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add row to table: %m");
+                        return table_log_add_error(r);
         }
 
-        r = table_print(t, stdout);
+        if (arg_json)
+                r = table_print_json(t, stdout, arg_json_format_flags);
+        else
+                r = table_print(t, stdout);
         if (r < 0)
                 return log_error_errno(r, "Failed to dump table: %m");
 
@@ -2095,6 +2169,48 @@ static int context_wipe_and_discard(Context *context, bool from_scratch) {
         return 0;
 }
 
+static int context_copy_blocks(Context *context) {
+        Partition *p;
+        int fd = -1, r;
+
+        assert(context);
+
+        /* Copy in file systems on the block level */
+
+        LIST_FOREACH(partitions, p, context->partitions) {
+                char buf[FORMAT_BYTES_MAX];
+
+                if (p->copy_blocks_fd < 0)
+                        continue;
+
+                if (p->dropped)
+                        continue;
+
+                if (PARTITION_EXISTS(p)) /* Never copy over existing partitions */
+                        continue;
+
+                assert(p->new_size != UINT64_MAX);
+                assert(p->copy_blocks_size != UINT64_MAX);
+                assert(p->new_size >= p->copy_blocks_size);
+
+                if (fd < 0)
+                        assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
+
+                if (lseek(fd, p->offset, SEEK_SET) == (off_t) -1)
+                        return log_error_errno(errno, "Failed to seek to partition offset: %m");
+
+                log_info("Copying in '%s' (%s) on block level into partition %" PRIu64 ".", p->copy_blocks_path, format_bytes(buf, sizeof(buf), p->copy_blocks_size), p->partno);
+
+                r = copy_bytes_full(p->copy_blocks_fd, fd, p->copy_blocks_size, 0, NULL, NULL, NULL, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to copy in data from '%s': %m", p->copy_blocks_path);
+
+                log_info("Copying in of '%s' on block level completed.", p->copy_blocks_path);
+        }
+
+        return 0;
+}
+
 static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *ret) {
         struct {
                 sd_id128_t type_uuid;
@@ -2232,13 +2348,12 @@ static int context_acquire_partition_uuids_and_labels(Context *context) {
         assert(context);
 
         LIST_FOREACH(partitions, p, context->partitions) {
-                assert(sd_id128_is_null(p->new_uuid));
-
                 /* Never touch foreign partitions */
                 if (PARTITION_IS_FOREIGN(p)) {
                         p->new_uuid = p->current_uuid;
 
                         if (p->current_label) {
+                                free(p->new_label);
                                 p->new_label = strdup(p->current_label);
                                 if (!p->new_label)
                                         return log_oom();
@@ -2249,20 +2364,21 @@ static int context_acquire_partition_uuids_and_labels(Context *context) {
 
                 if (!sd_id128_is_null(p->current_uuid))
                         p->new_uuid = p->current_uuid; /* Never change initialized UUIDs */
-                else {
+                else if (sd_id128_is_null(p->new_uuid)) {
+                        /* Not explicitly set by user! */
                         r = partition_acquire_uuid(context, p, &p->new_uuid);
                         if (r < 0)
                                 return r;
                 }
 
-                if (p->new_label) /* Explicitly set by user? */
-                        continue;
-
                 if (!isempty(p->current_label)) {
+                        free(p->new_label);
                         p->new_label = strdup(p->current_label); /* never change initialized labels */
                         if (!p->new_label)
                                 return log_oom();
-                } else {
+                } else if (!p->new_label) {
+                        /* Not explicitly set by user! */
+
                         r = partition_acquire_label(context, p, &p->new_label);
                         if (r < 0)
                                 return r;
@@ -2311,16 +2427,14 @@ static int context_write_partition_table(
         assert(context);
 
         if (arg_pretty > 0 ||
-            (arg_pretty < 0 && isatty(STDOUT_FILENO) > 0)) {
+            (arg_pretty < 0 && isatty(STDOUT_FILENO) > 0) || arg_json) {
 
-                if (context->n_partitions == 0)
-                        puts("Empty partition table.");
-                else
-                        (void) context_dump_partitions(context, node);
+                (void) context_dump_partitions(context, node);
 
                 putc('\n', stdout);
 
-                (void) context_dump_partition_bar(context, node);
+                if (!arg_json)
+                        (void) context_dump_partition_bar(context, node);
                 putc('\n', stdout);
                 fflush(stdout);
         }
@@ -2357,6 +2471,10 @@ static int context_write_partition_table(
         if (r < 0)
                 return r;
 
+        r = context_copy_blocks(context);
+        if (r < 0)
+                return r;
+
         LIST_FOREACH(partitions, p, context->partitions) {
                 if (p->dropped)
                         continue;
@@ -2602,6 +2720,87 @@ static int context_can_factory_reset(Context *context) {
         return false;
 }
 
+static int context_open_copy_block_paths(Context *context) {
+        Partition *p;
+        int r;
+
+        assert(context);
+
+        LIST_FOREACH(partitions, p, context->partitions) {
+                _cleanup_close_ int source_fd = -1;
+                uint64_t size;
+                struct stat st;
+
+                assert(p->copy_blocks_fd < 0);
+                assert(p->copy_blocks_size == UINT64_MAX);
+
+                if (PARTITION_EXISTS(p)) /* Never copy over partitions that already exist! */
+                        continue;
+
+                if (!p->copy_blocks_path)
+                        continue;
+
+                source_fd = open(p->copy_blocks_path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                if (source_fd < 0)
+                        return log_error_errno(errno, "Failed to open block copy file '%s': %m", p->copy_blocks_path);
+
+                if (fstat(source_fd, &st) < 0)
+                        return log_error_errno(errno, "Failed to stat block copy file '%s': %m", p->copy_blocks_path);
+
+                if (S_ISDIR(st.st_mode)) {
+                        _cleanup_free_ char *bdev = NULL;
+
+                        /* If the file is a directory, automatically find the backing block device */
+
+                        if (major(st.st_dev) != 0)
+                                r = device_path_make_major_minor(S_IFBLK, st.st_dev, &bdev);
+                        else {
+                                dev_t devt;
+
+                                /* Special support for btrfs */
+
+                                r = btrfs_get_block_device_fd(source_fd, &devt);
+                                if (r < 0)
+                                        return log_error_errno(r, "Unable to determine backing block device of '%s': %m", p->copy_blocks_path);
+
+                                r = device_path_make_major_minor(S_IFBLK, devt, &bdev);
+                        }
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to determine block device path for block device backing '%s': %m", p->copy_blocks_path);
+
+                        safe_close(source_fd);
+
+                        source_fd = open(bdev, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                        if (source_fd < 0)
+                                return log_error_errno(errno, "Failed to open block device '%s': %m", bdev);
+
+                        if (fstat(source_fd, &st) < 0)
+                                return log_error_errno(errno, "Failed to stat block device '%s': %m", bdev);
+
+                        if (!S_ISBLK(st.st_mode))
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Block device '%s' is not actually a block device, refusing.", bdev);
+                }
+
+                if (S_ISREG(st.st_mode))
+                        size = st.st_size;
+                else if (S_ISBLK(st.st_mode)) {
+                        if (ioctl(source_fd, BLKGETSIZE64, &size) != 0)
+                                return log_error_errno(errno, "Failed to determine size of block device to copy from: %m");
+                } else
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified path to copy blocks from '%s' is not a regular file, block device or directory, refusing: %m", p->copy_blocks_path);
+
+                if (size <= 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has zero size, refusing.", p->copy_blocks_path);
+                if (size % 512 != 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has size that is not multiple of 512, refusing.", p->copy_blocks_path);
+
+                p->copy_blocks_fd = TAKE_FD(source_fd);
+                p->copy_blocks_size = size;
+        }
+
+        return 0;
+}
+
 static int help(void) {
         _cleanup_free_ char *link = NULL;
         int r;
@@ -2618,7 +2817,7 @@ static int help(void) {
                "     --empty=MODE         One of refuse, allow, require, force, create; controls\n"
                "                          how to handle empty disks lacking partition tables\n"
                "     --discard=BOOL       Whether to discard backing blocks for new partitions\n"
-               "     --pretty=BOOL        Whether to show pretty summary before executing operation\n"
+               "     --pretty=BOOL        Whether to show pretty summary before doing changes\n"
                "     --factory-reset=BOOL Whether to remove data partitions before recreating\n"
                "                          them\n"
                "     --can-factory-reset  Test whether factory reset is defined\n"
@@ -2626,6 +2825,8 @@ static int help(void) {
                "     --definitions=DIR    Find partitions in specified directory\n"
                "     --seed=UUID          128bit seed UUID to derive all UUIDs from\n"
                "     --size=BYTES         Grow loopback file to specified size\n"
+               "     --json=pretty|short|off\n"
+               "                          Generate json output\n"
                "\nSee the %s for details.\n"
                , program_invocation_short_name
                , ansi_highlight(), ansi_normal()
@@ -2649,6 +2850,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_PRETTY,
                 ARG_DEFINITIONS,
                 ARG_SIZE,
+                ARG_JSON,
         };
 
         static const struct option options[] = {
@@ -2664,6 +2866,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "pretty",            required_argument, NULL, ARG_PRETTY            },
                 { "definitions",       required_argument, NULL, ARG_DEFINITIONS       },
                 { "size",              required_argument, NULL, ARG_SIZE              },
+                { "json",              required_argument, NULL, ARG_JSON              },
                 {}
         };
 
@@ -2787,6 +2990,26 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_size = rounded;
                         break;
                 }
+                case ARG_JSON:
+                        if (streq(optarg, "pretty")) {
+                                arg_json = true;
+                                arg_json_format_flags = JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR_AUTO;
+                        } else if (streq(optarg, "short")) {
+                                arg_json = true;
+                                arg_json_format_flags = JSON_FORMAT_NEWLINE;
+                        } else if (streq(optarg, "off")) {
+                                arg_json = false;
+                                arg_json_format_flags = 0;
+                        } else if (streq(optarg, "help")) {
+                                puts("pretty\n"
+                                     "short\n"
+                                     "off");
+                                return 0;
+                        } else
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown argument to --json=: %s", optarg);
+
+                        break;
+
 
                 case '?':
                         return -EINVAL;
@@ -3182,6 +3405,11 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return r;
 
+        /* Open all files to copy blocks from now, since we want to take their size into consideration */
+        r = context_open_copy_block_paths(context);
+        if (r < 0)
+                return r;
+
         /* First try to fit new partitions in, dropping by priority until it fits */
         for (;;) {
                 if (context_allocate_partitions(context))