]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/partition/repart.c
repart: Inline Partition and Context typedefs
[thirdparty/systemd.git] / src / partition / repart.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
e594a3b1
LP
2
3#if HAVE_VALGRIND_MEMCHECK_H
4#include <valgrind/memcheck.h>
5#endif
6
7#include <fcntl.h>
8#include <getopt.h>
e594a3b1
LP
9#include <linux/fs.h>
10#include <linux/loop.h>
11#include <sys/file.h>
12#include <sys/ioctl.h>
13#include <sys/stat.h>
14
1a037ba2 15#include "sd-device.h"
e594a3b1
LP
16#include "sd-id128.h"
17
18#include "alloc-util.h"
19#include "blkid-util.h"
20#include "blockdev-util.h"
21#include "btrfs-util.h"
d6b4d1c7 22#include "build.h"
f4351959 23#include "chase-symlinks.h"
e594a3b1
LP
24#include "conf-files.h"
25#include "conf-parser.h"
28db6fbf 26#include "constants.h"
1e2f3230 27#include "cryptsetup-util.h"
ca822829 28#include "device-util.h"
7176f06c 29#include "devnum-util.h"
5c08da58 30#include "dirent-util.h"
e594a3b1
LP
31#include "efivars.h"
32#include "errno-util.h"
33#include "fd-util.h"
4dc07c3a 34#include "fdisk-util.h"
b9df3536 35#include "fileio.h"
e594a3b1
LP
36#include "format-table.h"
37#include "format-util.h"
38#include "fs-util.h"
d8e32c47 39#include "glyph-util.h"
e594a3b1 40#include "gpt.h"
889914ef 41#include "hexdecoct.h"
ade99252 42#include "hmac.h"
e594a3b1 43#include "id128-util.h"
baa6a42d 44#include "initrd-util.h"
b456191d 45#include "io-util.h"
a015fbe7 46#include "json.h"
e594a3b1 47#include "list.h"
53171c04 48#include "loop-util.h"
e594a3b1 49#include "main-func.h"
8a794850 50#include "mkdir.h"
53171c04 51#include "mkfs-util.h"
8a794850 52#include "mount-util.h"
5c08da58 53#include "mountpoint-util.h"
c0fad2d9 54#include "nulstr-util.h"
b456191d 55#include "openssl-util.h"
614b022c 56#include "parse-argument.h"
c3eaba2d 57#include "parse-helpers.h"
e594a3b1
LP
58#include "pretty-print.h"
59#include "proc-cmdline.h"
8a794850 60#include "process-util.h"
b9df3536 61#include "random-util.h"
170c9823 62#include "resize-fs.h"
95bfd3cd 63#include "rm-rf.h"
e594a3b1 64#include "sort-util.h"
e031166e 65#include "specifier.h"
e594a3b1 66#include "stdio-util.h"
889914ef 67#include "string-table.h"
e594a3b1
LP
68#include "string-util.h"
69#include "strv.h"
bf819d3a 70#include "sync-util.h"
95bfd3cd 71#include "tmpfile-util.h"
e594a3b1 72#include "terminal-util.h"
02ef97cd 73#include "tpm-pcr.h"
889914ef 74#include "tpm2-util.h"
8a794850 75#include "user-util.h"
e594a3b1
LP
76#include "utf8.h"
77
fb08381c 78/* If not configured otherwise use a minimal partition size of 10M */
b262cbe8 79#define DEFAULT_MIN_SIZE (10ULL*1024ULL*1024ULL)
fb08381c
LP
80
81/* Hard lower limit for new partition sizes */
b262cbe8 82#define HARD_MIN_SIZE 4096ULL
fb08381c 83
b456191d 84/* We know up front we're never going to put more than this in a verity sig partition. */
b262cbe8 85#define VERITY_SIG_SIZE (HARD_MIN_SIZE*4ULL)
b456191d 86
69e3234d 87/* libfdisk takes off slightly more than 1M of the disk size when creating a GPT disk label */
b262cbe8 88#define GPT_METADATA_SIZE (1044ULL*1024ULL)
170c9823
LP
89
90/* LUKS2 takes off 16M of the partition size with its metadata by default */
b262cbe8 91#define LUKS2_METADATA_SIZE (16ULL*1024ULL*1024ULL)
170c9823 92
48a09a8f
DDM
93/* To do LUKS2 offline encryption, we need to keep some extra free space at the end of the partition. */
94#define LUKS2_METADATA_KEEP_FREE (LUKS2_METADATA_SIZE*2ULL)
95
98e0456e
DDM
96/* LUKS2 volume key size. */
97#define VOLUME_KEY_SIZE (512ULL/8ULL)
98
e594a3b1
LP
99/* Note: When growing and placing new partitions we always align to 4K sector size. It's how newer hard disks
100 * are designed, and if everything is aligned to that performance is best. And for older hard disks with 512B
101 * sector size devices were generally assumed to have an even number of sectors, hence at the worst we'll
102 * waste 3K per partition, which is probably fine. */
103
104static enum {
105 EMPTY_REFUSE, /* refuse empty disks, never create a partition table */
106 EMPTY_ALLOW, /* allow empty disks, create partition table if necessary */
107 EMPTY_REQUIRE, /* require an empty disk, create a partition table */
108 EMPTY_FORCE, /* make disk empty, erase everything, create a partition table always */
a26f4a49 109 EMPTY_CREATE, /* create disk as loopback file, create a partition table always */
e594a3b1
LP
110} arg_empty = EMPTY_REFUSE;
111
53538e33 112typedef enum FilterPartitionType {
81d1098b
DDM
113 FILTER_PARTITIONS_NONE,
114 FILTER_PARTITIONS_EXCLUDE,
115 FILTER_PARTITIONS_INCLUDE,
116 _FILTER_PARTITIONS_MAX,
117 _FILTER_PARTITIONS_INVALID = -EINVAL,
118} FilterPartitionsType;
119
e594a3b1
LP
120static bool arg_dry_run = true;
121static const char *arg_node = NULL;
122static char *arg_root = NULL;
252d6267 123static char *arg_image = NULL;
224c853f 124static char **arg_definitions = NULL;
e594a3b1
LP
125static bool arg_discard = true;
126static bool arg_can_factory_reset = false;
127static int arg_factory_reset = -1;
128static sd_id128_t arg_seed = SD_ID128_NULL;
129static bool arg_randomize = false;
130static int arg_pretty = -1;
a26f4a49 131static uint64_t arg_size = UINT64_MAX;
170c9823 132static bool arg_size_auto = false;
6a01ea4a 133static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
896e678b
LP
134static PagerFlags arg_pager_flags = 0;
135static bool arg_legend = true;
b9df3536
LP
136static void *arg_key = NULL;
137static size_t arg_key_size = 0;
b456191d
DDM
138static EVP_PKEY *arg_private_key = NULL;
139static X509 *arg_certificate = NULL;
889914ef
LP
140static char *arg_tpm2_device = NULL;
141static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
02ef97cd
LP
142static char *arg_tpm2_public_key = NULL;
143static uint32_t arg_tpm2_public_key_pcr_mask = UINT32_MAX;
4cee8333 144static bool arg_split = false;
81d1098b 145static sd_id128_t *arg_filter_partitions = NULL;
d989dd76 146static size_t arg_n_filter_partitions = 0;
81d1098b 147static FilterPartitionsType arg_filter_partitions_type = FILTER_PARTITIONS_NONE;
7d505753
DDM
148static sd_id128_t *arg_skip_partitions = NULL;
149static size_t arg_n_skip_partitions = 0;
e594a3b1
LP
150
151STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
252d6267 152STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
224c853f 153STATIC_DESTRUCTOR_REGISTER(arg_definitions, strv_freep);
b9df3536 154STATIC_DESTRUCTOR_REGISTER(arg_key, erase_and_freep);
b456191d
DDM
155STATIC_DESTRUCTOR_REGISTER(arg_private_key, EVP_PKEY_freep);
156STATIC_DESTRUCTOR_REGISTER(arg_certificate, X509_freep);
889914ef 157STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
02ef97cd 158STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
81d1098b 159STATIC_DESTRUCTOR_REGISTER(arg_filter_partitions, freep);
e594a3b1 160
e594a3b1 161typedef struct FreeArea FreeArea;
e594a3b1 162
889914ef
LP
163typedef enum EncryptMode {
164 ENCRYPT_OFF,
165 ENCRYPT_KEY_FILE,
166 ENCRYPT_TPM2,
167 ENCRYPT_KEY_FILE_TPM2,
168 _ENCRYPT_MODE_MAX,
2d93c20e 169 _ENCRYPT_MODE_INVALID = -EINVAL,
889914ef
LP
170} EncryptMode;
171
b5b7879a
DDM
172typedef enum VerityMode {
173 VERITY_OFF,
174 VERITY_DATA,
175 VERITY_HASH,
b456191d 176 VERITY_SIG,
b5b7879a
DDM
177 _VERITY_MODE_MAX,
178 _VERITY_MODE_INVALID = -EINVAL,
179} VerityMode;
180
448cfb7f 181typedef struct Partition {
e594a3b1 182 char *definition_path;
39fc0174 183 char **drop_in_files;
e594a3b1 184
22e932f4 185 GptPartitionType type;
e594a3b1 186 sd_id128_t current_uuid, new_uuid;
11749b61 187 bool new_uuid_is_set;
e594a3b1 188 char *current_label, *new_label;
8bbbdfd7 189 sd_id128_t fs_uuid;
e594a3b1
LP
190
191 bool dropped;
192 bool factory_reset;
193 int32_t priority;
194
195 uint32_t weight, padding_weight;
196
197 uint64_t current_size, new_size;
198 uint64_t size_min, size_max;
199
200 uint64_t current_padding, new_padding;
201 uint64_t padding_min, padding_max;
202
203 uint64_t partno;
204 uint64_t offset;
205
206 struct fdisk_partition *current_partition;
207 struct fdisk_partition *new_partition;
208 FreeArea *padding_area;
209 FreeArea *allocated_to_area;
210
757bc2e4 211 char *copy_blocks_path;
7d07030e 212 bool copy_blocks_path_is_our_file;
5c08da58 213 bool copy_blocks_auto;
585c5c75 214 const char *copy_blocks_root;
757bc2e4
LP
215 int copy_blocks_fd;
216 uint64_t copy_blocks_size;
217
53171c04 218 char *format;
8a794850 219 char **copy_files;
d83d8048 220 char **make_directories;
889914ef 221 EncryptMode encrypt;
b5b7879a
DDM
222 VerityMode verity;
223 char *verity_match_key;
c4a87b76 224 bool minimize;
53171c04 225
e73309c5 226 uint64_t gpt_flags;
ff0771bf 227 int no_auto;
e73309c5 228 int read_only;
1c41c1dc 229 int growfs;
e73309c5 230
b5b7879a
DDM
231 uint8_t *roothash;
232 size_t roothash_size;
233
4cee8333 234 char *split_name_format;
62108348 235 char *split_path;
4cee8333 236
448cfb7f 237 struct Partition *siblings[_VERITY_MODE_MAX];
b5b7879a 238
448cfb7f
DDM
239 LIST_FIELDS(struct Partition, partitions);
240} Partition;
e594a3b1
LP
241
242#define PARTITION_IS_FOREIGN(p) (!(p)->definition_path)
243#define PARTITION_EXISTS(p) (!!(p)->current_partition)
244
245struct FreeArea {
246 Partition *after;
247 uint64_t size;
248 uint64_t allocated;
249};
250
448cfb7f 251typedef struct Context {
e594a3b1
LP
252 LIST_HEAD(Partition, partitions);
253 size_t n_partitions;
254
255 FreeArea **free_areas;
319a4f4b 256 size_t n_free_areas;
e594a3b1
LP
257
258 uint64_t start, end, total;
259
260 struct fdisk_context *fdisk_context;
994b3031
LP
261 uint64_t sector_size;
262 uint64_t grain_size;
e594a3b1
LP
263
264 sd_id128_t seed;
cc751c75
DDM
265
266 char *node;
267 int backing_fd;
268
269 bool from_scratch;
448cfb7f 270} Context;
e594a3b1 271
889914ef
LP
272static const char *encrypt_mode_table[_ENCRYPT_MODE_MAX] = {
273 [ENCRYPT_OFF] = "off",
274 [ENCRYPT_KEY_FILE] = "key-file",
275 [ENCRYPT_TPM2] = "tpm2",
276 [ENCRYPT_KEY_FILE_TPM2] = "key-file+tpm2",
277};
278
b5b7879a
DDM
279static const char *verity_mode_table[_VERITY_MODE_MAX] = {
280 [VERITY_OFF] = "off",
281 [VERITY_DATA] = "data",
282 [VERITY_HASH] = "hash",
b456191d 283 [VERITY_SIG] = "signature",
b5b7879a
DDM
284};
285
2709d029 286DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(encrypt_mode, EncryptMode, ENCRYPT_KEY_FILE);
86bebe38 287DEFINE_PRIVATE_STRING_TABLE_LOOKUP(verity_mode, VerityMode);
889914ef 288
e594a3b1
LP
289static uint64_t round_down_size(uint64_t v, uint64_t p) {
290 return (v / p) * p;
291}
292
293static uint64_t round_up_size(uint64_t v, uint64_t p) {
294
295 v = DIV_ROUND_UP(v, p);
296
297 if (v > UINT64_MAX / p)
298 return UINT64_MAX; /* overflow */
299
300 return v * p;
301}
302
303static Partition *partition_new(void) {
304 Partition *p;
305
306 p = new(Partition, 1);
307 if (!p)
308 return NULL;
309
310 *p = (Partition) {
311 .weight = 1000,
312 .padding_weight = 0,
313 .current_size = UINT64_MAX,
314 .new_size = UINT64_MAX,
315 .size_min = UINT64_MAX,
316 .size_max = UINT64_MAX,
317 .current_padding = UINT64_MAX,
318 .new_padding = UINT64_MAX,
319 .padding_min = UINT64_MAX,
320 .padding_max = UINT64_MAX,
321 .partno = UINT64_MAX,
322 .offset = UINT64_MAX,
757bc2e4
LP
323 .copy_blocks_fd = -1,
324 .copy_blocks_size = UINT64_MAX,
ff0771bf 325 .no_auto = -1,
e73309c5 326 .read_only = -1,
1c41c1dc 327 .growfs = -1,
e594a3b1
LP
328 };
329
330 return p;
331}
332
333static Partition* partition_free(Partition *p) {
334 if (!p)
335 return NULL;
336
337 free(p->current_label);
338 free(p->new_label);
339 free(p->definition_path);
39fc0174 340 strv_free(p->drop_in_files);
e594a3b1
LP
341
342 if (p->current_partition)
343 fdisk_unref_partition(p->current_partition);
344 if (p->new_partition)
345 fdisk_unref_partition(p->new_partition);
346
7d07030e
DDM
347 if (p->copy_blocks_path_is_our_file)
348 unlink_and_free(p->copy_blocks_path);
349 else
350 free(p->copy_blocks_path);
757bc2e4
LP
351 safe_close(p->copy_blocks_fd);
352
53171c04 353 free(p->format);
8a794850 354 strv_free(p->copy_files);
d83d8048 355 strv_free(p->make_directories);
b5b7879a
DDM
356 free(p->verity_match_key);
357
358 free(p->roothash);
53171c04 359
4cee8333 360 free(p->split_name_format);
a2d7c42e 361 unlink_and_free(p->split_path);
4cee8333 362
e594a3b1
LP
363 return mfree(p);
364}
365
9ccceb9d
YW
366static void partition_foreignize(Partition *p) {
367 assert(p);
368 assert(PARTITION_EXISTS(p));
369
370 /* Reset several parameters set through definition file to make the partition foreign. */
371
9ccceb9d
YW
372 p->definition_path = mfree(p->definition_path);
373 p->drop_in_files = strv_free(p->drop_in_files);
374
375 p->copy_blocks_path = mfree(p->copy_blocks_path);
376 p->copy_blocks_fd = safe_close(p->copy_blocks_fd);
585c5c75 377 p->copy_blocks_root = NULL;
9ccceb9d
YW
378
379 p->format = mfree(p->format);
380 p->copy_files = strv_free(p->copy_files);
381 p->make_directories = strv_free(p->make_directories);
382 p->verity_match_key = mfree(p->verity_match_key);
383
9ccceb9d
YW
384 p->priority = 0;
385 p->weight = 1000;
386 p->padding_weight = 0;
387 p->size_min = UINT64_MAX;
388 p->size_max = UINT64_MAX;
389 p->padding_min = UINT64_MAX;
390 p->padding_max = UINT64_MAX;
391 p->no_auto = -1;
392 p->read_only = -1;
393 p->growfs = -1;
394 p->verity = VERITY_OFF;
395}
396
7d505753 397static bool partition_exclude(const Partition *p) {
81d1098b
DDM
398 assert(p);
399
400 if (arg_filter_partitions_type == FILTER_PARTITIONS_NONE)
401 return false;
402
d989dd76 403 for (size_t i = 0; i < arg_n_filter_partitions; i++)
81d1098b
DDM
404 if (sd_id128_equal(p->type.uuid, arg_filter_partitions[i]))
405 return arg_filter_partitions_type == FILTER_PARTITIONS_EXCLUDE;
406
407 return arg_filter_partitions_type == FILTER_PARTITIONS_INCLUDE;
408}
409
7d505753
DDM
410static bool partition_skip(const Partition *p) {
411 assert(p);
412
413 for (size_t i = 0; i < arg_n_skip_partitions; i++)
414 if (sd_id128_equal(p->type.uuid, arg_skip_partitions[i]))
415 return true;
416
417 return false;
418}
419
e594a3b1
LP
420static Partition* partition_unlink_and_free(Context *context, Partition *p) {
421 if (!p)
422 return NULL;
423
424 LIST_REMOVE(partitions, context->partitions, p);
425
426 assert(context->n_partitions > 0);
427 context->n_partitions--;
428
429 return partition_free(p);
430}
431
432DEFINE_TRIVIAL_CLEANUP_FUNC(Partition*, partition_free);
433
434static Context *context_new(sd_id128_t seed) {
435 Context *context;
436
437 context = new(Context, 1);
438 if (!context)
439 return NULL;
440
441 *context = (Context) {
442 .start = UINT64_MAX,
443 .end = UINT64_MAX,
444 .total = UINT64_MAX,
445 .seed = seed,
446 };
447
448 return context;
449}
450
451static void context_free_free_areas(Context *context) {
452 assert(context);
453
454 for (size_t i = 0; i < context->n_free_areas; i++)
455 free(context->free_areas[i]);
456
457 context->free_areas = mfree(context->free_areas);
458 context->n_free_areas = 0;
e594a3b1
LP
459}
460
461static Context *context_free(Context *context) {
462 if (!context)
463 return NULL;
464
465 while (context->partitions)
466 partition_unlink_and_free(context, context->partitions);
467 assert(context->n_partitions == 0);
468
469 context_free_free_areas(context);
470
471 if (context->fdisk_context)
472 fdisk_unref_context(context->fdisk_context);
473
cc751c75
DDM
474 safe_close(context->backing_fd);
475 free(context->node);
476
e594a3b1
LP
477 return mfree(context);
478}
479
480DEFINE_TRIVIAL_CLEANUP_FUNC(Context*, context_free);
481
482static int context_add_free_area(
483 Context *context,
484 uint64_t size,
485 Partition *after) {
486
487 FreeArea *a;
488
489 assert(context);
490 assert(!after || !after->padding_area);
491
319a4f4b 492 if (!GREEDY_REALLOC(context->free_areas, context->n_free_areas + 1))
e594a3b1
LP
493 return -ENOMEM;
494
495 a = new(FreeArea, 1);
496 if (!a)
497 return -ENOMEM;
498
499 *a = (FreeArea) {
500 .size = size,
501 .after = after,
502 };
503
504 context->free_areas[context->n_free_areas++] = a;
505
506 if (after)
507 after->padding_area = a;
508
509 return 0;
510}
511
9ccceb9d
YW
512static void partition_drop_or_foreignize(Partition *p) {
513 if (!p || p->dropped || PARTITION_IS_FOREIGN(p))
514 return;
515
516 if (PARTITION_EXISTS(p)) {
517 log_info("Can't grow existing partition %s of priority %" PRIi32 ", ignoring.",
518 strna(p->current_label ?: p->new_label), p->priority);
519
520 /* Handle the partition as foreign. Do not set dropped flag. */
521 partition_foreignize(p);
522 } else {
523 log_info("Can't fit partition %s of priority %" PRIi32 ", dropping.",
524 p->definition_path, p->priority);
525
526 p->dropped = true;
527 p->allocated_to_area = NULL;
528 }
529}
530
531static bool context_drop_or_foreignize_one_priority(Context *context) {
e594a3b1 532 int32_t priority = 0;
e594a3b1
LP
533
534 LIST_FOREACH(partitions, p, context->partitions) {
535 if (p->dropped)
536 continue;
e594a3b1 537
9ccceb9d 538 priority = MAX(priority, p->priority);
e594a3b1
LP
539 }
540
541 /* Refuse to drop partitions with 0 or negative priorities or partitions of priorities that have at
542 * least one existing priority */
9ccceb9d 543 if (priority <= 0)
e594a3b1
LP
544 return false;
545
546 LIST_FOREACH(partitions, p, context->partitions) {
547 if (p->priority < priority)
548 continue;
549
9ccceb9d 550 partition_drop_or_foreignize(p);
b5b7879a
DDM
551
552 /* We ensure that all verity sibling partitions have the same priority, so it's safe
553 * to drop all siblings here as well. */
554
9ccceb9d
YW
555 for (VerityMode mode = VERITY_OFF + 1; mode < _VERITY_MODE_MAX; mode++)
556 partition_drop_or_foreignize(p->siblings[mode]);
e594a3b1
LP
557 }
558
559 return true;
560}
561
a80701e6 562static uint64_t partition_min_size(const Context *context, const Partition *p) {
e594a3b1
LP
563 uint64_t sz;
564
994b3031
LP
565 assert(context);
566 assert(p);
567
e594a3b1
LP
568 /* Calculate the disk space we really need at minimum for this partition. If the partition already
569 * exists the current size is what we really need. If it doesn't exist yet refuse to allocate less
fb08381c
LP
570 * than 4K.
571 *
572 * DEFAULT_MIN_SIZE is the default SizeMin= we configure if nothing else is specified. */
e594a3b1
LP
573
574 if (PARTITION_IS_FOREIGN(p)) {
575 /* Don't allow changing size of partitions not managed by us */
576 assert(p->current_size != UINT64_MAX);
577 return p->current_size;
578 }
579
b456191d
DDM
580 if (p->verity == VERITY_SIG)
581 return VERITY_SIG_SIZE;
582
fb08381c 583 sz = p->current_size != UINT64_MAX ? p->current_size : HARD_MIN_SIZE;
757bc2e4 584
170c9823
LP
585 if (!PARTITION_EXISTS(p)) {
586 uint64_t d = 0;
587
889914ef 588 if (p->encrypt != ENCRYPT_OFF)
48a09a8f 589 d += round_up_size(LUKS2_METADATA_KEEP_FREE, context->grain_size);
170c9823
LP
590
591 if (p->copy_blocks_size != UINT64_MAX)
994b3031 592 d += round_up_size(p->copy_blocks_size, context->grain_size);
889914ef 593 else if (p->format || p->encrypt != ENCRYPT_OFF) {
170c9823
LP
594 uint64_t f;
595
596 /* If we shall synthesize a file system, take minimal fs size into account (assumed to be 4K if not known) */
994b3031
LP
597 f = p->format ? round_up_size(minimal_size_by_fs_name(p->format), context->grain_size) : UINT64_MAX;
598 d += f == UINT64_MAX ? context->grain_size : f;
170c9823
LP
599 }
600
601 if (d > sz)
602 sz = d;
603 }
757bc2e4 604
994b3031 605 return MAX(round_up_size(p->size_min != UINT64_MAX ? p->size_min : DEFAULT_MIN_SIZE, context->grain_size), sz);
e594a3b1
LP
606}
607
994b3031
LP
608static uint64_t partition_max_size(const Context *context, const Partition *p) {
609 uint64_t sm;
610
e594a3b1
LP
611 /* Calculate how large the partition may become at max. This is generally the configured maximum
612 * size, except when it already exists and is larger than that. In that case it's the existing size,
613 * since we never want to shrink partitions. */
614
994b3031
LP
615 assert(context);
616 assert(p);
617
e594a3b1
LP
618 if (PARTITION_IS_FOREIGN(p)) {
619 /* Don't allow changing size of partitions not managed by us */
620 assert(p->current_size != UINT64_MAX);
621 return p->current_size;
622 }
623
b456191d
DDM
624 if (p->verity == VERITY_SIG)
625 return VERITY_SIG_SIZE;
626
822d9b9a
YW
627 if (p->size_max == UINT64_MAX)
628 return UINT64_MAX;
629
994b3031
LP
630 sm = round_down_size(p->size_max, context->grain_size);
631
e594a3b1 632 if (p->current_size != UINT64_MAX)
b0fbf90b 633 sm = MAX(p->current_size, sm);
e594a3b1 634
b0fbf90b 635 return MAX(partition_min_size(context, p), sm);
e594a3b1
LP
636}
637
a801bb01
YW
638static uint64_t partition_min_padding(const Partition *p) {
639 assert(p);
640 return p->padding_min != UINT64_MAX ? p->padding_min : 0;
641}
642
643static uint64_t partition_max_padding(const Partition *p) {
644 assert(p);
645 return p->padding_max;
646}
647
994b3031 648static uint64_t partition_min_size_with_padding(Context *context, const Partition *p) {
e594a3b1
LP
649 uint64_t sz;
650
651 /* Calculate the disk space we need for this partition plus any free space coming after it. This
652 * takes user configured padding into account as well as any additional whitespace needed to align
653 * the next partition to 4K again. */
654
994b3031
LP
655 assert(context);
656 assert(p);
657
a801bb01 658 sz = partition_min_size(context, p) + partition_min_padding(p);
e594a3b1
LP
659
660 if (PARTITION_EXISTS(p)) {
661 /* If the partition wasn't aligned, add extra space so that any we might add will be aligned */
662 assert(p->offset != UINT64_MAX);
994b3031 663 return round_up_size(p->offset + sz, context->grain_size) - p->offset;
e594a3b1
LP
664 }
665
666 /* If this is a new partition we'll place it aligned, hence we just need to round up the required size here */
994b3031 667 return round_up_size(sz, context->grain_size);
e594a3b1
LP
668}
669
670static uint64_t free_area_available(const FreeArea *a) {
671 assert(a);
672
673 /* Determines how much of this free area is not allocated yet */
674
675 assert(a->size >= a->allocated);
676 return a->size - a->allocated;
677}
678
58b06ac1 679static uint64_t free_area_current_end(Context *context, const FreeArea *a) {
994b3031
LP
680 assert(context);
681 assert(a);
682
58b06ac1
YW
683 if (!a->after)
684 return free_area_available(a);
e594a3b1 685
58b06ac1
YW
686 assert(a->after->offset != UINT64_MAX);
687 assert(a->after->current_size != UINT64_MAX);
e594a3b1 688
58b06ac1
YW
689 /* Calculate where the free area ends, based on the offset of the partition preceding it. */
690 return round_up_size(a->after->offset + a->after->current_size, context->grain_size) + free_area_available(a);
691}
e594a3b1 692
58b06ac1
YW
693static uint64_t free_area_min_end(Context *context, const FreeArea *a) {
694 assert(context);
695 assert(a);
e594a3b1 696
58b06ac1
YW
697 if (!a->after)
698 return 0;
e594a3b1 699
58b06ac1
YW
700 assert(a->after->offset != UINT64_MAX);
701 assert(a->after->current_size != UINT64_MAX);
1052a114 702
58b06ac1
YW
703 /* Calculate where the partition would end when we give it as much as it needs. */
704 return round_up_size(a->after->offset + partition_min_size_with_padding(context, a->after), context->grain_size);
705}
706
707static uint64_t free_area_available_for_new_partitions(Context *context, const FreeArea *a) {
708 assert(context);
709 assert(a);
710
711 /* Similar to free_area_available(), but takes into account that the required size and padding of the
712 * preceding partition is honoured. */
e594a3b1 713
58b06ac1 714 return LESS_BY(free_area_current_end(context, a), free_area_min_end(context, a));
e594a3b1
LP
715}
716
994b3031
LP
717static int free_area_compare(FreeArea *const *a, FreeArea *const*b, Context *context) {
718 assert(context);
719
720 return CMP(free_area_available_for_new_partitions(context, *a),
721 free_area_available_for_new_partitions(context, *b));
e594a3b1
LP
722}
723
994b3031
LP
724static uint64_t charge_size(Context *context, uint64_t total, uint64_t amount) {
725 assert(context);
e594a3b1 726 /* Subtract the specified amount from total, rounding up to multiple of 4K if there's room */
184cf99a 727 assert(amount <= total);
994b3031 728 return LESS_BY(total, round_up_size(amount, context->grain_size));
e594a3b1
LP
729}
730
731static uint64_t charge_weight(uint64_t total, uint64_t amount) {
732 assert(amount <= total);
733 return total - amount;
734}
735
14a4c4ed 736static bool context_allocate_partitions(Context *context, uint64_t *ret_largest_free_area) {
e594a3b1
LP
737 assert(context);
738
f39cf264
YW
739 /* This may be called multiple times. Reset previous assignments. */
740 for (size_t i = 0; i < context->n_free_areas; i++)
741 context->free_areas[i]->allocated = 0;
742
14a4c4ed 743 /* Sort free areas by size, putting smallest first */
994b3031 744 typesafe_qsort_r(context->free_areas, context->n_free_areas, free_area_compare, context);
e594a3b1 745
14a4c4ed
LP
746 /* In any case return size of the largest free area (i.e. not the size of all free areas
747 * combined!) */
748 if (ret_largest_free_area)
749 *ret_largest_free_area =
750 context->n_free_areas == 0 ? 0 :
994b3031 751 free_area_available_for_new_partitions(context, context->free_areas[context->n_free_areas-1]);
14a4c4ed 752
cdbcc339
YW
753 /* Check that each existing partition can fit its area. */
754 for (size_t i = 0; i < context->n_free_areas; i++)
755 if (free_area_current_end(context, context->free_areas[i]) <
756 free_area_min_end(context, context->free_areas[i]))
757 return false;
758
14a4c4ed 759 /* A simple first-fit algorithm. We return true if we can fit the partitions in, otherwise false. */
e594a3b1
LP
760 LIST_FOREACH(partitions, p, context->partitions) {
761 bool fits = false;
762 uint64_t required;
763 FreeArea *a = NULL;
764
765 /* Skip partitions we already dropped or that already exist */
766 if (p->dropped || PARTITION_EXISTS(p))
767 continue;
768
e594a3b1 769 /* How much do we need to fit? */
994b3031
LP
770 required = partition_min_size_with_padding(context, p);
771 assert(required % context->grain_size == 0);
e594a3b1
LP
772
773 for (size_t i = 0; i < context->n_free_areas; i++) {
774 a = context->free_areas[i];
775
994b3031 776 if (free_area_available_for_new_partitions(context, a) >= required) {
e594a3b1
LP
777 fits = true;
778 break;
779 }
780 }
781
782 if (!fits)
783 return false; /* 😢 Oh no! We can't fit this partition into any free area! */
784
785 /* Assign the partition to this free area */
786 p->allocated_to_area = a;
787
788 /* Budget the minimal partition size */
789 a->allocated += required;
790 }
791
792 return true;
793}
794
795static int context_sum_weights(Context *context, FreeArea *a, uint64_t *ret) {
796 uint64_t weight_sum = 0;
e594a3b1
LP
797
798 assert(context);
799 assert(a);
800 assert(ret);
801
802 /* Determine the sum of the weights of all partitions placed in or before the specified free area */
803
804 LIST_FOREACH(partitions, p, context->partitions) {
805 if (p->padding_area != a && p->allocated_to_area != a)
806 continue;
807
808 if (p->weight > UINT64_MAX - weight_sum)
809 goto overflow_sum;
810 weight_sum += p->weight;
811
812 if (p->padding_weight > UINT64_MAX - weight_sum)
813 goto overflow_sum;
814 weight_sum += p->padding_weight;
815 }
816
817 *ret = weight_sum;
818 return 0;
819
820overflow_sum:
821 return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Combined weight of partition exceeds unsigned 64bit range, refusing.");
822}
823
0245e15a 824static uint64_t scale_by_weight(uint64_t value, uint64_t weight, uint64_t weight_sum) {
e594a3b1 825 assert(weight_sum >= weight);
e594a3b1 826
0245e15a
YW
827 for (;;) {
828 if (weight == 0)
829 return 0;
830 if (weight == weight_sum)
831 return value;
832 if (value <= UINT64_MAX / weight)
833 return value * weight / weight_sum;
834
835 /* Rescale weight and weight_sum to make not the calculation overflow. To satisfy the
836 * following conditions, 'weight_sum' is rounded up but 'weight' is rounded down:
837 * - the sum of scale_by_weight() for all weights must not be larger than the input value,
838 * - scale_by_weight() must not be larger than the ideal value (i.e. calculated with uint128_t). */
839 weight_sum = DIV_ROUND_UP(weight_sum, 2);
840 weight /= 2;
e594a3b1 841 }
e594a3b1
LP
842}
843
844typedef enum GrowPartitionPhase {
bf99aed6
YW
845 /* The zeroth phase: do not touch foreign partitions (i.e. those we don't manage). */
846 PHASE_FOREIGN,
847
e594a3b1
LP
848 /* The first phase: we charge partitions which need more (according to constraints) than their weight-based share. */
849 PHASE_OVERCHARGE,
850
851 /* The second phase: we charge partitions which need less (according to constraints) than their weight-based share. */
852 PHASE_UNDERCHARGE,
853
854 /* The third phase: we distribute what remains among the remaining partitions, according to the weights */
855 PHASE_DISTRIBUTE,
ae0613c6
LP
856
857 _GROW_PARTITION_PHASE_MAX,
e594a3b1
LP
858} GrowPartitionPhase;
859
0245e15a 860static bool context_grow_partitions_phase(
e594a3b1
LP
861 Context *context,
862 FreeArea *a,
863 GrowPartitionPhase phase,
864 uint64_t *span,
865 uint64_t *weight_sum) {
866
2a503ad2
YW
867 bool try_again = false;
868
e594a3b1
LP
869 assert(context);
870 assert(a);
0245e15a
YW
871 assert(span);
872 assert(weight_sum);
e594a3b1
LP
873
874 /* Now let's look at the intended weights and adjust them taking the minimum space assignments into
875 * account. i.e. if a partition has a small weight but a high minimum space value set it should not
876 * get any additional room from the left-overs. Similar, if two partitions have the same weight they
877 * should get the same space if possible, even if one has a smaller minimum size than the other. */
878 LIST_FOREACH(partitions, p, context->partitions) {
879
880 /* Look only at partitions associated with this free area, i.e. immediately
162392b7 881 * preceding it, or allocated into it */
e594a3b1
LP
882 if (p->allocated_to_area != a && p->padding_area != a)
883 continue;
884
885 if (p->new_size == UINT64_MAX) {
e594a3b1 886 uint64_t share, rsz, xsz;
2a503ad2 887 bool charge = false;
e594a3b1
LP
888
889 /* Calculate how much this space this partition needs if everyone would get
890 * the weight based share */
0245e15a 891 share = scale_by_weight(*span, p->weight, *weight_sum);
e594a3b1 892
994b3031
LP
893 rsz = partition_min_size(context, p);
894 xsz = partition_max_size(context, p);
e594a3b1 895
bf99aed6
YW
896 if (phase == PHASE_FOREIGN && PARTITION_IS_FOREIGN(p)) {
897 /* Never change of foreign partitions (i.e. those we don't manage) */
898
899 p->new_size = p->current_size;
900 charge = true;
901
902 } else if (phase == PHASE_OVERCHARGE && rsz > share) {
e594a3b1
LP
903 /* This partition needs more than its calculated share. Let's assign
904 * it that, and take this partition out of all calculations and start
905 * again. */
906
907 p->new_size = rsz;
908 charge = try_again = true;
909
822d9b9a 910 } else if (phase == PHASE_UNDERCHARGE && xsz < share) {
e594a3b1
LP
911 /* This partition accepts less than its calculated
912 * share. Let's assign it that, and take this partition out
913 * of all calculations and start again. */
914
915 p->new_size = xsz;
916 charge = try_again = true;
917
918 } else if (phase == PHASE_DISTRIBUTE) {
919 /* This partition can accept its calculated share. Let's
920 * assign it. There's no need to restart things here since
921 * assigning this shouldn't impact the shares of the other
922 * partitions. */
923
d7c46b5e
YW
924 assert(share >= rsz);
925 p->new_size = CLAMP(round_down_size(share, context->grain_size), rsz, xsz);
e594a3b1
LP
926 charge = true;
927 }
928
929 if (charge) {
994b3031 930 *span = charge_size(context, *span, p->new_size);
e594a3b1
LP
931 *weight_sum = charge_weight(*weight_sum, p->weight);
932 }
e594a3b1
LP
933 }
934
935 if (p->new_padding == UINT64_MAX) {
a801bb01 936 uint64_t share, rsz, xsz;
2a503ad2 937 bool charge = false;
e594a3b1 938
0245e15a 939 share = scale_by_weight(*span, p->padding_weight, *weight_sum);
e594a3b1 940
a801bb01
YW
941 rsz = partition_min_padding(p);
942 xsz = partition_max_padding(p);
943
944 if (phase == PHASE_OVERCHARGE && rsz > share) {
945 p->new_padding = rsz;
e594a3b1 946 charge = try_again = true;
a801bb01
YW
947 } else if (phase == PHASE_UNDERCHARGE && xsz < share) {
948 p->new_padding = xsz;
e594a3b1
LP
949 charge = try_again = true;
950 } else if (phase == PHASE_DISTRIBUTE) {
d7c46b5e
YW
951 assert(share >= rsz);
952 p->new_padding = CLAMP(round_down_size(share, context->grain_size), rsz, xsz);
e594a3b1
LP
953 charge = true;
954 }
955
956 if (charge) {
994b3031 957 *span = charge_size(context, *span, p->new_padding);
e594a3b1
LP
958 *weight_sum = charge_weight(*weight_sum, p->padding_weight);
959 }
e594a3b1
LP
960 }
961 }
962
2a503ad2 963 return !try_again;
e594a3b1
LP
964}
965
19903a43
YW
966static void context_grow_partition_one(Context *context, FreeArea *a, Partition *p, uint64_t *span) {
967 uint64_t m;
968
969 assert(context);
970 assert(a);
971 assert(p);
972 assert(span);
973
974 if (*span == 0)
975 return;
976
977 if (p->allocated_to_area != a)
978 return;
979
980 if (PARTITION_IS_FOREIGN(p))
981 return;
982
983 assert(p->new_size != UINT64_MAX);
984
985 /* Calculate new size and align. */
986 m = round_down_size(p->new_size + *span, context->grain_size);
987 /* But ensure this doesn't shrink the size. */
988 m = MAX(m, p->new_size);
989 /* And ensure this doesn't exceed the maximum size. */
990 m = MIN(m, partition_max_size(context, p));
991
992 assert(m >= p->new_size);
993
994 *span = charge_size(context, *span, m - p->new_size);
995 p->new_size = m;
996}
997
e594a3b1
LP
998static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) {
999 uint64_t weight_sum = 0, span;
1000 int r;
1001
1002 assert(context);
1003 assert(a);
1004
1005 r = context_sum_weights(context, a, &weight_sum);
1006 if (r < 0)
1007 return r;
1008
1009 /* Let's calculate the total area covered by this free area and the partition before it */
1010 span = a->size;
1011 if (a->after) {
1012 assert(a->after->offset != UINT64_MAX);
1013 assert(a->after->current_size != UINT64_MAX);
1014
994b3031 1015 span += round_up_size(a->after->offset + a->after->current_size, context->grain_size) - a->after->offset;
e594a3b1
LP
1016 }
1017
0245e15a
YW
1018 for (GrowPartitionPhase phase = 0; phase < _GROW_PARTITION_PHASE_MAX;)
1019 if (context_grow_partitions_phase(context, a, phase, &span, &weight_sum))
1020 phase++; /* go to the next phase */
e594a3b1 1021
162392b7 1022 /* We still have space left over? Donate to preceding partition if we have one */
19903a43
YW
1023 if (span > 0 && a->after)
1024 context_grow_partition_one(context, a, a->after, &span);
e594a3b1 1025
162392b7 1026 /* What? Even still some space left (maybe because there was no preceding partition, or it had a
e594a3b1 1027 * size limit), then let's donate it to whoever wants it. */
03677889 1028 if (span > 0)
e594a3b1 1029 LIST_FOREACH(partitions, p, context->partitions) {
19903a43 1030 context_grow_partition_one(context, a, p, &span);
e594a3b1
LP
1031 if (span == 0)
1032 break;
1033 }
e594a3b1 1034
162392b7 1035 /* Yuck, still no one? Then make it padding */
e594a3b1
LP
1036 if (span > 0 && a->after) {
1037 assert(a->after->new_padding != UINT64_MAX);
1038 a->after->new_padding += span;
1039 }
1040
1041 return 0;
1042}
1043
1044static int context_grow_partitions(Context *context) {
e594a3b1
LP
1045 int r;
1046
1047 assert(context);
1048
1049 for (size_t i = 0; i < context->n_free_areas; i++) {
1050 r = context_grow_partitions_on_free_area(context, context->free_areas[i]);
1051 if (r < 0)
1052 return r;
1053 }
1054
1055 /* All existing partitions that have no free space after them can't change size */
1056 LIST_FOREACH(partitions, p, context->partitions) {
1057 if (p->dropped)
1058 continue;
1059
1060 if (!PARTITION_EXISTS(p) || p->padding_area) {
1061 /* The algorithm above must have initialized this already */
1062 assert(p->new_size != UINT64_MAX);
1063 continue;
1064 }
1065
1066 assert(p->new_size == UINT64_MAX);
1067 p->new_size = p->current_size;
1068
1069 assert(p->new_padding == UINT64_MAX);
1070 p->new_padding = p->current_padding;
1071 }
1072
1073 return 0;
1074}
1075
00428745 1076static uint64_t find_first_unused_partno(Context *context) {
e594a3b1 1077 uint64_t partno = 0;
e594a3b1
LP
1078
1079 assert(context);
1080
5f59807d
DDM
1081 for (partno = 0;; partno++) {
1082 bool found = false;
1083 LIST_FOREACH(partitions, p, context->partitions)
1084 if (p->partno != UINT64_MAX && p->partno == partno)
1085 found = true;
1086 if (!found)
1087 break;
e594a3b1
LP
1088 }
1089
00428745
DDM
1090 return partno;
1091}
1092
1093static void context_place_partitions(Context *context) {
1094
1095 assert(context);
1096
e594a3b1
LP
1097 for (size_t i = 0; i < context->n_free_areas; i++) {
1098 FreeArea *a = context->free_areas[i];
2ea7eb00
FS
1099 _unused_ uint64_t left;
1100 uint64_t start;
e594a3b1
LP
1101
1102 if (a->after) {
1103 assert(a->after->offset != UINT64_MAX);
1104 assert(a->after->new_size != UINT64_MAX);
1105 assert(a->after->new_padding != UINT64_MAX);
1106
1107 start = a->after->offset + a->after->new_size + a->after->new_padding;
1108 } else
1109 start = context->start;
1110
994b3031 1111 start = round_up_size(start, context->grain_size);
e594a3b1
LP
1112 left = a->size;
1113
1114 LIST_FOREACH(partitions, p, context->partitions) {
1115 if (p->allocated_to_area != a)
1116 continue;
1117
1118 p->offset = start;
00428745 1119 p->partno = find_first_unused_partno(context);
e594a3b1
LP
1120
1121 assert(left >= p->new_size);
1122 start += p->new_size;
1123 left -= p->new_size;
1124
1125 assert(left >= p->new_padding);
1126 start += p->new_padding;
1127 left -= p->new_padding;
1128 }
1129 }
1130}
1131
e594a3b1
LP
1132static int config_parse_type(
1133 const char *unit,
1134 const char *filename,
1135 unsigned line,
1136 const char *section,
1137 unsigned section_line,
1138 const char *lvalue,
1139 int ltype,
1140 const char *rvalue,
1141 void *data,
1142 void *userdata) {
1143
22e932f4 1144 GptPartitionType *type = ASSERT_PTR(data);
e594a3b1
LP
1145 int r;
1146
1147 assert(rvalue);
e594a3b1 1148
22e932f4 1149 r = gpt_partition_type_from_string(rvalue, type);
e594a3b1
LP
1150 if (r < 0)
1151 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse partition type: %s", rvalue);
1152
1153 return 0;
1154}
1155
1156static int config_parse_label(
1157 const char *unit,
1158 const char *filename,
1159 unsigned line,
1160 const char *section,
1161 unsigned section_line,
1162 const char *lvalue,
1163 int ltype,
1164 const char *rvalue,
1165 void *data,
1166 void *userdata) {
1167
e031166e 1168 _cleanup_free_ char *resolved = NULL;
99534007 1169 char **label = ASSERT_PTR(data);
e594a3b1
LP
1170 int r;
1171
1172 assert(rvalue);
e594a3b1 1173
be9ce018
LP
1174 /* Nota bene: the empty label is a totally valid one. Let's hence not follow our usual rule of
1175 * assigning the empty string to reset to default here, but really accept it as label to set. */
1176
de61a04b 1177 r = specifier_printf(rvalue, GPT_LABEL_MAX, system_and_tmp_specifier_table, arg_root, NULL, &resolved);
e031166e 1178 if (r < 0) {
e459258f 1179 log_syntax(unit, LOG_WARNING, filename, line, r,
e031166e
LP
1180 "Failed to expand specifiers in Label=, ignoring: %s", rvalue);
1181 return 0;
1182 }
1183
1184 if (!utf8_is_valid(resolved)) {
e594a3b1
LP
1185 log_syntax(unit, LOG_WARNING, filename, line, 0,
1186 "Partition label not valid UTF-8, ignoring: %s", rvalue);
1187 return 0;
1188 }
1189
22a0a36e
LP
1190 r = gpt_partition_label_valid(resolved);
1191 if (r < 0) {
1192 log_syntax(unit, LOG_WARNING, filename, line, r,
1193 "Failed to check if string is valid as GPT partition label, ignoring: \"%s\" (from \"%s\")",
1194 resolved, rvalue);
1195 return 0;
1196 }
1197 if (!r) {
e594a3b1 1198 log_syntax(unit, LOG_WARNING, filename, line, 0,
46072ae3
ZJS
1199 "Partition label too long for GPT table, ignoring: \"%s\" (from \"%s\")",
1200 resolved, rvalue);
e594a3b1
LP
1201 return 0;
1202 }
1203
e031166e 1204 free_and_replace(*label, resolved);
e594a3b1
LP
1205 return 0;
1206}
1207
1208static int config_parse_weight(
1209 const char *unit,
1210 const char *filename,
1211 unsigned line,
1212 const char *section,
1213 unsigned section_line,
1214 const char *lvalue,
1215 int ltype,
1216 const char *rvalue,
1217 void *data,
1218 void *userdata) {
1219
f126038f 1220 uint32_t *w = ASSERT_PTR(data), v;
e594a3b1
LP
1221 int r;
1222
1223 assert(rvalue);
e594a3b1
LP
1224
1225 r = safe_atou32(rvalue, &v);
1226 if (r < 0) {
1227 log_syntax(unit, LOG_WARNING, filename, line, r,
1228 "Failed to parse weight value, ignoring: %s", rvalue);
1229 return 0;
1230 }
1231
1232 if (v > 1000U*1000U) {
c8f3d767 1233 log_syntax(unit, LOG_WARNING, filename, line, 0,
e594a3b1
LP
1234 "Weight needs to be in range 0…10000000, ignoring: %" PRIu32, v);
1235 return 0;
1236 }
1237
f126038f 1238 *w = v;
e594a3b1
LP
1239 return 0;
1240}
1241
1242static int config_parse_size4096(
1243 const char *unit,
1244 const char *filename,
1245 unsigned line,
1246 const char *section,
1247 unsigned section_line,
1248 const char *lvalue,
1249 int ltype,
1250 const char *rvalue,
1251 void *data,
1252 void *userdata) {
1253
1254 uint64_t *sz = data, parsed;
1255 int r;
1256
1257 assert(rvalue);
1258 assert(data);
1259
1260 r = parse_size(rvalue, 1024, &parsed);
1261 if (r < 0)
c8f3d767 1262 return log_syntax(unit, LOG_ERR, filename, line, r,
e594a3b1
LP
1263 "Failed to parse size value: %s", rvalue);
1264
1265 if (ltype > 0)
1266 *sz = round_up_size(parsed, 4096);
1267 else if (ltype < 0)
1268 *sz = round_down_size(parsed, 4096);
1269 else
1270 *sz = parsed;
1271
1272 if (*sz != parsed)
e2341b6b
DT
1273 log_syntax(unit, LOG_NOTICE, filename, line, r, "Rounded %s= size %" PRIu64 " %s %" PRIu64 ", a multiple of 4096.",
1274 lvalue, parsed, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), *sz);
e594a3b1
LP
1275
1276 return 0;
1277}
1278
53171c04
LP
1279static int config_parse_fstype(
1280 const char *unit,
1281 const char *filename,
1282 unsigned line,
1283 const char *section,
1284 unsigned section_line,
1285 const char *lvalue,
1286 int ltype,
1287 const char *rvalue,
1288 void *data,
1289 void *userdata) {
1290
99534007 1291 char **fstype = ASSERT_PTR(data);
53171c04
LP
1292
1293 assert(rvalue);
53171c04
LP
1294
1295 if (!filename_is_valid(rvalue))
1296 return log_syntax(unit, LOG_ERR, filename, line, 0,
1297 "File system type is not valid, refusing: %s", rvalue);
1298
1299 return free_and_strdup_warn(fstype, rvalue);
1300}
1301
8a794850
LP
1302static int config_parse_copy_files(
1303 const char *unit,
1304 const char *filename,
1305 unsigned line,
1306 const char *section,
1307 unsigned section_line,
1308 const char *lvalue,
1309 int ltype,
1310 const char *rvalue,
1311 void *data,
1312 void *userdata) {
1313
1314 _cleanup_free_ char *source = NULL, *buffer = NULL, *resolved_source = NULL, *resolved_target = NULL;
1315 const char *p = rvalue, *target;
99534007 1316 Partition *partition = ASSERT_PTR(data);
8a794850
LP
1317 int r;
1318
1319 assert(rvalue);
8a794850
LP
1320
1321 r = extract_first_word(&p, &source, ":", EXTRACT_CUNESCAPE|EXTRACT_DONT_COALESCE_SEPARATORS);
1322 if (r < 0)
1323 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract source path: %s", rvalue);
1324 if (r == 0) {
1325 log_syntax(unit, LOG_WARNING, filename, line, 0, "No argument specified: %s", rvalue);
1326 return 0;
1327 }
1328
1329 r = extract_first_word(&p, &buffer, ":", EXTRACT_CUNESCAPE|EXTRACT_DONT_COALESCE_SEPARATORS);
1330 if (r < 0)
1331 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract target path: %s", rvalue);
1332 if (r == 0)
1333 target = source; /* No target, then it's the same as the source */
1334 else
1335 target = buffer;
1336
1337 if (!isempty(p))
1338 return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Too many arguments: %s", rvalue);
1339
de61a04b 1340 r = specifier_printf(source, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_source);
8a794850
LP
1341 if (r < 0) {
1342 log_syntax(unit, LOG_WARNING, filename, line, r,
1343 "Failed to expand specifiers in CopyFiles= source, ignoring: %s", rvalue);
1344 return 0;
1345 }
1346
0ade2213
LP
1347 r = path_simplify_and_warn(resolved_source, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
1348 if (r < 0)
8a794850 1349 return 0;
8a794850 1350
de61a04b 1351 r = specifier_printf(target, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_target);
8a794850
LP
1352 if (r < 0) {
1353 log_syntax(unit, LOG_WARNING, filename, line, r,
1354 "Failed to expand specifiers in CopyFiles= target, ignoring: %s", resolved_target);
1355 return 0;
1356 }
1357
0ade2213
LP
1358 r = path_simplify_and_warn(resolved_target, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
1359 if (r < 0)
8a794850 1360 return 0;
8a794850
LP
1361
1362 r = strv_consume_pair(&partition->copy_files, TAKE_PTR(resolved_source), TAKE_PTR(resolved_target));
1363 if (r < 0)
1364 return log_oom();
1365
1366 return 0;
1367}
1368
5c08da58
LP
1369static int config_parse_copy_blocks(
1370 const char *unit,
1371 const char *filename,
1372 unsigned line,
1373 const char *section,
1374 unsigned section_line,
1375 const char *lvalue,
1376 int ltype,
1377 const char *rvalue,
1378 void *data,
1379 void *userdata) {
1380
1381 _cleanup_free_ char *d = NULL;
99534007 1382 Partition *partition = ASSERT_PTR(data);
5c08da58
LP
1383 int r;
1384
1385 assert(rvalue);
5c08da58
LP
1386
1387 if (isempty(rvalue)) {
1388 partition->copy_blocks_path = mfree(partition->copy_blocks_path);
1389 partition->copy_blocks_auto = false;
1390 return 0;
1391 }
1392
1393 if (streq(rvalue, "auto")) {
1394 partition->copy_blocks_path = mfree(partition->copy_blocks_path);
1395 partition->copy_blocks_auto = true;
585c5c75 1396 partition->copy_blocks_root = arg_root;
5c08da58
LP
1397 return 0;
1398 }
1399
de61a04b 1400 r = specifier_printf(rvalue, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &d);
5c08da58
LP
1401 if (r < 0) {
1402 log_syntax(unit, LOG_WARNING, filename, line, r,
1403 "Failed to expand specifiers in CopyBlocks= source path, ignoring: %s", rvalue);
1404 return 0;
1405 }
1406
1407 r = path_simplify_and_warn(d, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
1408 if (r < 0)
1409 return 0;
1410
1411 free_and_replace(partition->copy_blocks_path, d);
1412 partition->copy_blocks_auto = false;
585c5c75 1413 partition->copy_blocks_root = arg_root;
5c08da58
LP
1414 return 0;
1415}
1416
d83d8048
LP
1417static int config_parse_make_dirs(
1418 const char *unit,
1419 const char *filename,
1420 unsigned line,
1421 const char *section,
1422 unsigned section_line,
1423 const char *lvalue,
1424 int ltype,
1425 const char *rvalue,
1426 void *data,
1427 void *userdata) {
1428
99534007
DT
1429 Partition *partition = ASSERT_PTR(data);
1430 const char *p = ASSERT_PTR(rvalue);
d83d8048
LP
1431 int r;
1432
d83d8048
LP
1433 for (;;) {
1434 _cleanup_free_ char *word = NULL, *d = NULL;
1435
1436 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
1437 if (r == -ENOMEM)
1438 return log_oom();
1439 if (r < 0) {
1440 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
1441 return 0;
1442 }
1443 if (r == 0)
1444 return 0;
1445
de61a04b 1446 r = specifier_printf(word, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &d);
d83d8048
LP
1447 if (r < 0) {
1448 log_syntax(unit, LOG_WARNING, filename, line, r,
1449 "Failed to expand specifiers in MakeDirectories= parameter, ignoring: %s", word);
1450 continue;
1451 }
1452
1453 r = path_simplify_and_warn(d, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
1454 if (r < 0)
1455 continue;
1456
1457 r = strv_consume(&partition->make_directories, TAKE_PTR(d));
1458 if (r < 0)
1459 return log_oom();
1460 }
1461}
1462
889914ef
LP
1463static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_encrypt, encrypt_mode, EncryptMode, ENCRYPT_OFF, "Invalid encryption mode");
1464
e73309c5
LP
1465static int config_parse_gpt_flags(
1466 const char *unit,
1467 const char *filename,
1468 unsigned line,
1469 const char *section,
1470 unsigned section_line,
1471 const char *lvalue,
1472 int ltype,
1473 const char *rvalue,
1474 void *data,
1475 void *userdata) {
1476
99534007 1477 uint64_t *gpt_flags = ASSERT_PTR(data);
e73309c5
LP
1478 int r;
1479
1480 assert(rvalue);
e73309c5
LP
1481
1482 r = safe_atou64(rvalue, gpt_flags);
1483 if (r < 0) {
1484 log_syntax(unit, LOG_WARNING, filename, line, r,
1485 "Failed to parse Flags= value, ignoring: %s", rvalue);
1486 return 0;
1487 }
1488
1489 return 0;
1490}
1491
11749b61
DDM
1492static int config_parse_uuid(
1493 const char *unit,
1494 const char *filename,
1495 unsigned line,
1496 const char *section,
1497 unsigned section_line,
1498 const char *lvalue,
1499 int ltype,
1500 const char *rvalue,
1501 void *data,
1502 void *userdata) {
1503
1504 Partition *partition = ASSERT_PTR(data);
1505 int r;
1506
1507 if (isempty(rvalue)) {
1508 partition->new_uuid = SD_ID128_NULL;
1509 partition->new_uuid_is_set = false;
1510 return 0;
1511 }
1512
1513 if (streq(rvalue, "null")) {
1514 partition->new_uuid = SD_ID128_NULL;
1515 partition->new_uuid_is_set = true;
1516 return 0;
1517 }
1518
1519 r = sd_id128_from_string(rvalue, &partition->new_uuid);
1520 if (r < 0) {
1521 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue);
1522 return 0;
1523 }
1524
1525 partition->new_uuid_is_set = true;
1526
1527 return 0;
1528}
1529
b5b7879a
DDM
1530static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_verity, verity_mode, VerityMode, VERITY_OFF, "Invalid verity mode");
1531
39fc0174 1532static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
e594a3b1
LP
1533
1534 ConfigTableItem table[] = {
22e932f4 1535 { "Partition", "Type", config_parse_type, 0, &p->type },
4cee8333
DDM
1536 { "Partition", "Label", config_parse_label, 0, &p->new_label },
1537 { "Partition", "UUID", config_parse_uuid, 0, p },
1538 { "Partition", "Priority", config_parse_int32, 0, &p->priority },
1539 { "Partition", "Weight", config_parse_weight, 0, &p->weight },
1540 { "Partition", "PaddingWeight", config_parse_weight, 0, &p->padding_weight },
1541 { "Partition", "SizeMinBytes", config_parse_size4096, 1, &p->size_min },
1542 { "Partition", "SizeMaxBytes", config_parse_size4096, -1, &p->size_max },
1543 { "Partition", "PaddingMinBytes", config_parse_size4096, 1, &p->padding_min },
1544 { "Partition", "PaddingMaxBytes", config_parse_size4096, -1, &p->padding_max },
1545 { "Partition", "FactoryReset", config_parse_bool, 0, &p->factory_reset },
1546 { "Partition", "CopyBlocks", config_parse_copy_blocks, 0, p },
1547 { "Partition", "Format", config_parse_fstype, 0, &p->format },
1548 { "Partition", "CopyFiles", config_parse_copy_files, 0, p },
1549 { "Partition", "MakeDirectories", config_parse_make_dirs, 0, p },
1550 { "Partition", "Encrypt", config_parse_encrypt, 0, &p->encrypt },
1551 { "Partition", "Verity", config_parse_verity, 0, &p->verity },
1552 { "Partition", "VerityMatchKey", config_parse_string, 0, &p->verity_match_key },
1553 { "Partition", "Flags", config_parse_gpt_flags, 0, &p->gpt_flags },
1554 { "Partition", "ReadOnly", config_parse_tristate, 0, &p->read_only },
1555 { "Partition", "NoAuto", config_parse_tristate, 0, &p->no_auto },
1556 { "Partition", "GrowFileSystem", config_parse_tristate, 0, &p->growfs },
1557 { "Partition", "SplitName", config_parse_string, 0, &p->split_name_format },
c4a87b76 1558 { "Partition", "Minimize", config_parse_bool, 0, &p->minimize },
e594a3b1
LP
1559 {}
1560 };
1561 int r;
39fc0174
RP
1562 _cleanup_free_ char *filename = NULL;
1563 const char* dropin_dirname;
e594a3b1 1564
39fc0174
RP
1565 r = path_extract_filename(path, &filename);
1566 if (r < 0)
bef69ae8 1567 return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);
39fc0174
RP
1568
1569 dropin_dirname = strjoina(filename, ".d");
1570
1571 r = config_parse_many(
1572 STRV_MAKE_CONST(path),
1573 conf_file_dirs,
1574 dropin_dirname,
1575 "Partition\0",
1576 config_item_table_lookup, table,
1577 CONFIG_PARSE_WARN,
1578 p,
1579 NULL,
1580 &p->drop_in_files);
e594a3b1
LP
1581 if (r < 0)
1582 return r;
1583
7d505753
DDM
1584 if (partition_exclude(p))
1585 return 0;
1586
e594a3b1
LP
1587 if (p->size_min != UINT64_MAX && p->size_max != UINT64_MAX && p->size_min > p->size_max)
1588 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
1589 "SizeMinBytes= larger than SizeMaxBytes=, refusing.");
1590
1591 if (p->padding_min != UINT64_MAX && p->padding_max != UINT64_MAX && p->padding_min > p->padding_max)
1592 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
1593 "PaddingMinBytes= larger than PaddingMaxBytes=, refusing.");
1594
22e932f4 1595 if (sd_id128_is_null(p->type.uuid))
e594a3b1
LP
1596 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
1597 "Type= not defined, refusing.");
1598
5c08da58
LP
1599 if ((p->copy_blocks_path || p->copy_blocks_auto) &&
1600 (p->format || !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories)))
53171c04 1601 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
5c08da58 1602 "Format=/CopyFiles=/MakeDirectories= and CopyBlocks= cannot be combined, refusing.");
53171c04 1603
d83d8048 1604 if ((!strv_isempty(p->copy_files) || !strv_isempty(p->make_directories)) && streq_ptr(p->format, "swap"))
8a794850
LP
1605 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
1606 "Format=swap and CopyFiles= cannot be combined, refusing.");
1607
5c08da58 1608 if (!p->format && (!strv_isempty(p->copy_files) || !strv_isempty(p->make_directories) || (p->encrypt != ENCRYPT_OFF && !(p->copy_blocks_path || p->copy_blocks_auto)))) {
b9df3536 1609 /* Pick "ext4" as file system if we are configured to copy files or encrypt the device */
8a794850
LP
1610 p->format = strdup("ext4");
1611 if (!p->format)
1612 return log_oom();
1613 }
1614
c4a87b76
DDM
1615 if (p->minimize && !p->format)
1616 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
1617 "Minimize= can only be enabled if Format= is set");
1618
6d6cefad
DDM
1619 if ((!strv_isempty(p->copy_files) || !strv_isempty(p->make_directories)) && !mkfs_supports_root_option(p->format) && geteuid() != 0)
1620 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EPERM),
1621 "Need to be root to populate %s filesystems with CopyFiles=/MakeDirectories=",
1622 p->format);
1623
0eb23798
DDM
1624 if (p->format && fstype_is_ro(p->format) && strv_isempty(p->copy_files) && strv_isempty(p->make_directories))
1625 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
1626 "Cannot format %s filesystem without source files, refusing", p->format);
1627
b5b7879a
DDM
1628 if (p->verity != VERITY_OFF || p->encrypt != ENCRYPT_OFF) {
1629 r = dlopen_cryptsetup();
1630 if (r < 0)
1631 return log_syntax(NULL, LOG_ERR, path, 1, r,
1632 "libcryptsetup not found, Verity=/Encrypt= are not supported: %m");
1633 }
1634
1635 if (p->verity != VERITY_OFF && !p->verity_match_key)
1636 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
1637 "VerityMatchKey= must be set if Verity=%s", verity_mode_to_string(p->verity));
1638
1639 if (p->verity == VERITY_OFF && p->verity_match_key)
1640 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
1641 "VerityMatchKey= can only be set if Verity= is not \"%s\"",
1642 verity_mode_to_string(p->verity));
1643
b456191d
DDM
1644 if (IN_SET(p->verity, VERITY_HASH, VERITY_SIG) &&
1645 (p->copy_files || p->copy_blocks_path || p->copy_blocks_auto || p->format || p->make_directories))
b5b7879a
DDM
1646 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
1647 "CopyBlocks=/CopyFiles=/Format=/MakeDirectories= cannot be used with Verity=%s",
1648 verity_mode_to_string(p->verity));
1649
1650 if (p->verity != VERITY_OFF && p->encrypt != ENCRYPT_OFF)
1651 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
1652 "Encrypting verity hash/data partitions is not supported");
1653
b456191d
DDM
1654 if (p->verity == VERITY_SIG && !arg_private_key)
1655 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
1656 "Verity signature partition requested but no private key provided (--private-key=)");
1657
1658 if (p->verity == VERITY_SIG && !arg_certificate)
1659 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
ba4a5eff 1660 "Verity signature partition requested but no PEM certificate provided (--certificate=)");
b456191d
DDM
1661
1662 if (p->verity == VERITY_SIG && (p->size_min != UINT64_MAX || p->size_max != UINT64_MAX))
1663 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
1664 "SizeMinBytes=/SizeMaxBytes= cannot be used with Verity=%s",
1665 verity_mode_to_string(p->verity));
1666
e73309c5 1667 /* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */
c1979cd8 1668 if (IN_SET(p->type.designator, PARTITION_ROOT_VERITY, PARTITION_USR_VERITY) && p->read_only < 0)
e73309c5
LP
1669 p->read_only = true;
1670
1c41c1dc 1671 /* Default to "growfs" on, unless read-only */
22e932f4 1672 if (gpt_partition_type_knows_growfs(p->type) &&
1c41c1dc
LP
1673 p->read_only <= 0)
1674 p->growfs = true;
1675
4cee8333
DDM
1676 if (!p->split_name_format) {
1677 char *s = strdup("%t");
1678 if (!s)
1679 return log_oom();
1680
1681 p->split_name_format = s;
1682 } else if (streq(p->split_name_format, "-"))
1683 p->split_name_format = mfree(p->split_name_format);
1684
7d505753 1685 return 1;
e594a3b1
LP
1686}
1687
b5b7879a
DDM
1688static int find_verity_sibling(Context *context, Partition *p, VerityMode mode, Partition **ret) {
1689 Partition *s = NULL;
1690
1691 assert(p);
1692 assert(p->verity != VERITY_OFF);
1693 assert(p->verity_match_key);
1694 assert(mode != VERITY_OFF);
1695 assert(p->verity != mode);
1696 assert(ret);
1697
1698 /* Try to find the matching sibling partition of the given type for a verity partition. For a data
af3d3873
YW
1699 * partition, this is the corresponding hash partition with the same verity name (and vice versa for
1700 * the hash partition). */
b5b7879a
DDM
1701
1702 LIST_FOREACH(partitions, q, context->partitions) {
1703 if (p == q)
1704 continue;
1705
1706 if (q->verity != mode)
1707 continue;
1708
1709 assert(q->verity_match_key);
1710
1711 if (!streq(p->verity_match_key, q->verity_match_key))
1712 continue;
1713
1714 if (s)
1715 return -ENOTUNIQ;
1716
1717 s = q;
1718 }
1719
1720 if (!s)
1721 return -ENXIO;
1722
1723 *ret = s;
1724
1725 return 0;
1726}
1727
e594a3b1
LP
1728static int context_read_definitions(
1729 Context *context,
224c853f 1730 char **directories,
e594a3b1
LP
1731 const char *root) {
1732
1733 _cleanup_strv_free_ char **files = NULL;
1734 Partition *last = NULL;
e594a3b1 1735 int r;
39fc0174 1736 const char *const *dirs;
e594a3b1
LP
1737
1738 assert(context);
1739
224c853f 1740 dirs = (const char* const*) (directories ?: CONF_PATHS_STRV("repart.d"));
39fc0174 1741
224c853f 1742 r = conf_files_list_strv(&files, ".conf", directories ? NULL : root, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, dirs);
e594a3b1
LP
1743 if (r < 0)
1744 return log_error_errno(r, "Failed to enumerate *.conf files: %m");
1745
1746 STRV_FOREACH(f, files) {
1747 _cleanup_(partition_freep) Partition *p = NULL;
1748
1749 p = partition_new();
1750 if (!p)
1751 return log_oom();
1752
1753 p->definition_path = strdup(*f);
1754 if (!p->definition_path)
1755 return log_oom();
1756
39fc0174 1757 r = partition_read_definition(p, *f, dirs);
e594a3b1
LP
1758 if (r < 0)
1759 return r;
7d505753
DDM
1760 if (r == 0)
1761 continue;
e594a3b1
LP
1762
1763 LIST_INSERT_AFTER(partitions, context->partitions, last, p);
1764 last = TAKE_PTR(p);
1765 context->n_partitions++;
1766 }
1767
b5b7879a
DDM
1768 /* Check that each configured verity hash/data partition has a matching verity data/hash partition. */
1769
1770 LIST_FOREACH(partitions, p, context->partitions) {
1771 if (p->verity == VERITY_OFF)
1772 continue;
1773
1774 for (VerityMode mode = VERITY_OFF + 1; mode < _VERITY_MODE_MAX; mode++) {
b456191d 1775 Partition *q = NULL;
b5b7879a
DDM
1776
1777 if (p->verity == mode)
1778 continue;
1779
1780 if (p->siblings[mode])
1781 continue;
1782
1783 r = find_verity_sibling(context, p, mode, &q);
8e52ed02
DDM
1784 if (r == -ENXIO) {
1785 if (mode != VERITY_SIG)
1786 return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
1787 "Missing verity %s partition for verity %s partition with VerityMatchKey=%s",
1788 verity_mode_to_string(mode), verity_mode_to_string(p->verity), p->verity_match_key);
1789 } else if (r == -ENOTUNIQ)
b5b7879a
DDM
1790 return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
1791 "Multiple verity %s partitions found for verity %s partition with VerityMatchKey=%s",
1792 verity_mode_to_string(mode), verity_mode_to_string(p->verity), p->verity_match_key);
8e52ed02
DDM
1793 else if (r < 0)
1794 return log_syntax(NULL, LOG_ERR, p->definition_path, 1, r,
1795 "Failed to find verity %s partition for verity %s partition with VerityMatchKey=%s",
1796 verity_mode_to_string(mode), verity_mode_to_string(p->verity), p->verity_match_key);
b5b7879a 1797
b456191d
DDM
1798 if (q) {
1799 if (q->priority != p->priority)
1800 return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
1801 "Priority mismatch (%i != %i) for verity sibling partitions with VerityMatchKey=%s",
1802 p->priority, q->priority, p->verity_match_key);
b5b7879a 1803
b456191d
DDM
1804 p->siblings[mode] = q;
1805 }
b5b7879a
DDM
1806 }
1807 }
1808
e594a3b1
LP
1809 return 0;
1810}
1811
e594a3b1
LP
1812static int determine_current_padding(
1813 struct fdisk_context *c,
1814 struct fdisk_table *t,
1815 struct fdisk_partition *p,
994b3031
LP
1816 uint64_t secsz,
1817 uint64_t grainsz,
e594a3b1
LP
1818 uint64_t *ret) {
1819
1820 size_t n_partitions;
1821 uint64_t offset, next = UINT64_MAX;
1822
1823 assert(c);
1824 assert(t);
1825 assert(p);
1826
1827 if (!fdisk_partition_has_end(p))
1828 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition has no end!");
1829
1830 offset = fdisk_partition_get_end(p);
994b3031
LP
1831 assert(offset < UINT64_MAX / secsz);
1832 offset *= secsz;
e594a3b1
LP
1833
1834 n_partitions = fdisk_table_get_nents(t);
695cfd53 1835 for (size_t i = 0; i < n_partitions; i++) {
e594a3b1
LP
1836 struct fdisk_partition *q;
1837 uint64_t start;
1838
1839 q = fdisk_table_get_partition(t, i);
1840 if (!q)
1841 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read partition metadata: %m");
1842
1843 if (fdisk_partition_is_used(q) <= 0)
1844 continue;
1845
1846 if (!fdisk_partition_has_start(q))
1847 continue;
1848
1849 start = fdisk_partition_get_start(q);
994b3031
LP
1850 assert(start < UINT64_MAX / secsz);
1851 start *= secsz;
e594a3b1
LP
1852
1853 if (start >= offset && (next == UINT64_MAX || next > start))
1854 next = start;
1855 }
1856
1857 if (next == UINT64_MAX) {
1858 /* No later partition? In that case check the end of the usable area */
1859 next = fdisk_get_last_lba(c);
1860 assert(next < UINT64_MAX);
1861 next++; /* The last LBA is one sector before the end */
1862
994b3031
LP
1863 assert(next < UINT64_MAX / secsz);
1864 next *= secsz;
e594a3b1
LP
1865
1866 if (offset > next)
1867 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition end beyond disk end.");
1868 }
1869
1870 assert(next >= offset);
994b3031
LP
1871 offset = round_up_size(offset, grainsz);
1872 next = round_down_size(next, grainsz);
e594a3b1 1873
a6f44d61 1874 *ret = LESS_BY(next, offset); /* Saturated subtraction, rounding might have fucked things up */
e594a3b1
LP
1875 return 0;
1876}
1877
1878static int fdisk_ask_cb(struct fdisk_context *c, struct fdisk_ask *ask, void *data) {
1879 _cleanup_free_ char *ids = NULL;
1880 int r;
1881
1882 if (fdisk_ask_get_type(ask) != FDISK_ASKTYPE_STRING)
1883 return -EINVAL;
1884
b7416360 1885 ids = new(char, SD_ID128_UUID_STRING_MAX);
e594a3b1
LP
1886 if (!ids)
1887 return -ENOMEM;
1888
b7416360 1889 r = fdisk_ask_string_set_result(ask, sd_id128_to_uuid_string(*(sd_id128_t*) data, ids));
e594a3b1
LP
1890 if (r < 0)
1891 return r;
1892
1893 TAKE_PTR(ids);
1894 return 0;
1895}
1896
1897static int fdisk_set_disklabel_id_by_uuid(struct fdisk_context *c, sd_id128_t id) {
1898 int r;
1899
1900 r = fdisk_set_ask(c, fdisk_ask_cb, &id);
1901 if (r < 0)
1902 return r;
1903
1904 r = fdisk_set_disklabel_id(c);
1905 if (r < 0)
1906 return r;
1907
1908 return fdisk_set_ask(c, NULL, NULL);
1909}
1910
53171c04 1911static int derive_uuid(sd_id128_t base, const char *token, sd_id128_t *ret) {
e594a3b1 1912 union {
ade99252 1913 uint8_t md[SHA256_DIGEST_SIZE];
e594a3b1
LP
1914 sd_id128_t id;
1915 } result;
1916
53171c04 1917 assert(token);
e594a3b1
LP
1918 assert(ret);
1919
53171c04
LP
1920 /* Derive a new UUID from the specified UUID in a stable and reasonably safe way. Specifically, we
1921 * calculate the HMAC-SHA256 of the specified token string, keyed by the supplied base (typically the
1922 * machine ID). We use the machine ID as key (and not as cleartext!) of the HMAC operation since it's
1923 * the machine ID we don't want to leak. */
e594a3b1 1924
ade99252 1925 hmac_sha256(base.bytes, sizeof(base.bytes), token, strlen(token), result.md);
e594a3b1
LP
1926
1927 /* Take the first half, mark it as v4 UUID */
1928 assert_cc(sizeof(result.md) == sizeof(result.id) * 2);
1929 *ret = id128_make_v4_uuid(result.id);
1930 return 0;
1931}
1932
cc751c75 1933static int context_load_partition_table(Context *context) {
e594a3b1
LP
1934 _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
1935 _cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL;
1936 uint64_t left_boundary = UINT64_MAX, first_lba, last_lba, nsectors;
1937 _cleanup_free_ char *disk_uuid_string = NULL;
1938 bool from_scratch = false;
1939 sd_id128_t disk_uuid;
1940 size_t n_partitions;
994b3031
LP
1941 unsigned long secsz;
1942 uint64_t grainsz;
e594a3b1
LP
1943 int r;
1944
1945 assert(context);
170c9823
LP
1946 assert(!context->fdisk_context);
1947 assert(!context->free_areas);
1948 assert(context->start == UINT64_MAX);
1949 assert(context->end == UINT64_MAX);
1950 assert(context->total == UINT64_MAX);
e594a3b1 1951
a26f4a49
LP
1952 /* libfdisk doesn't have an API to operate on arbitrary fds, hence reopen the fd going via the
1953 * /proc/self/fd/ magic path if we have an existing fd. Open the original file otherwise. */
cc751c75 1954 if (context->backing_fd < 0) {
f8cf3d19
LP
1955 c = fdisk_new_context();
1956 if (!c)
1957 return log_oom();
1958
cc751c75 1959 r = fdisk_assign_device(c, context->node, arg_dry_run);
f8cf3d19 1960 } else
cc751c75 1961 r = fdisk_new_context_fd(context->backing_fd, arg_dry_run, &c);
f8cf3d19 1962
170c9823
LP
1963 if (r == -EINVAL && arg_size_auto) {
1964 struct stat st;
1965
1966 /* libfdisk returns EINVAL if opening a file of size zero. Let's check for that, and accept
1967 * it if automatic sizing is requested. */
1968
cc751c75
DDM
1969 if (context->backing_fd < 0)
1970 r = stat(context->node, &st);
170c9823 1971 else
cc751c75 1972 r = fstat(context->backing_fd, &st);
170c9823 1973 if (r < 0)
cc751c75 1974 return log_error_errno(errno, "Failed to stat block device '%s': %m", context->node);
170c9823 1975
994b3031
LP
1976 if (S_ISREG(st.st_mode) && st.st_size == 0) {
1977 /* User the fallback values if we have no better idea */
1978 context->sector_size = 512;
1979 context->grain_size = 4096;
170c9823 1980 return /* from_scratch = */ true;
994b3031 1981 }
170c9823
LP
1982
1983 r = -EINVAL;
1984 }
e594a3b1 1985 if (r < 0)
cc751c75 1986 return log_error_errno(r, "Failed to open device '%s': %m", context->node);
a26f4a49 1987
cc751c75 1988 if (context->backing_fd < 0) {
a26f4a49 1989 /* If we have no fd referencing the device yet, make a copy of the fd now, so that we have one */
cc751c75
DDM
1990 context->backing_fd = fd_reopen(fdisk_get_devfd(c), O_RDONLY|O_CLOEXEC);
1991 if (context->backing_fd < 0)
1992 return log_error_errno(context->backing_fd, "Failed to duplicate fdisk fd: %m");
e594a3b1 1993
25baae50 1994 /* Tell udev not to interfere while we are processing the device */
cc751c75 1995 if (flock(context->backing_fd, arg_dry_run ? LOCK_SH : LOCK_EX) < 0)
25baae50
DDM
1996 return log_error_errno(errno, "Failed to lock block device: %m");
1997 }
e594a3b1 1998
994b3031
LP
1999 /* The offsets/sizes libfdisk returns to us will be in multiple of the sector size of the
2000 * device. This is typically 512, and sometimes 4096. Let's query libfdisk once for it, and then use
2001 * it for all our needs. Note that the values we use ourselves always are in bytes though, thus mean
2002 * the same thing universally. Also note that regardless what kind of sector size is in use we'll
2003 * place partitions at multiples of 4K. */
2004 secsz = fdisk_get_sector_size(c);
2005
2006 /* Insist on a power of two, and that it's a multiple of 512, i.e. the traditional sector size. */
983ce0b5
LP
2007 if (secsz < 512 || !ISPOWEROF2(secsz))
2008 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Sector size %lu is not a power of two larger than 512? Refusing.", secsz);
994b3031
LP
2009
2010 /* Use at least 4K, and ensure it's a multiple of the sector size, regardless if that is smaller or
2011 * larger */
2012 grainsz = secsz < 4096 ? 4096 : secsz;
2013
2014 log_debug("Sector size of device is %lu bytes. Using grain size of %" PRIu64 ".", secsz, grainsz);
2015
e594a3b1
LP
2016 switch (arg_empty) {
2017
2018 case EMPTY_REFUSE:
2019 /* Refuse empty disks, insist on an existing GPT partition table */
2020 if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT))
cc751c75 2021 return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has no GPT disk label, not repartitioning.", context->node);
e594a3b1
LP
2022
2023 break;
2024
2025 case EMPTY_REQUIRE:
2026 /* Require an empty disk, refuse any existing partition table */
2027 r = fdisk_has_label(c);
2028 if (r < 0)
cc751c75 2029 return log_error_errno(r, "Failed to determine whether disk %s has a disk label: %m", context->node);
e594a3b1 2030 if (r > 0)
cc751c75 2031 return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s already has a disk label, refusing.", context->node);
e594a3b1
LP
2032
2033 from_scratch = true;
2034 break;
2035
2036 case EMPTY_ALLOW:
2037 /* Allow both an empty disk and an existing partition table, but only GPT */
2038 r = fdisk_has_label(c);
2039 if (r < 0)
cc751c75 2040 return log_error_errno(r, "Failed to determine whether disk %s has a disk label: %m", context->node);
e594a3b1
LP
2041 if (r > 0) {
2042 if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT))
cc751c75 2043 return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has non-GPT disk label, not repartitioning.", context->node);
e594a3b1
LP
2044 } else
2045 from_scratch = true;
2046
2047 break;
2048
2049 case EMPTY_FORCE:
a26f4a49 2050 case EMPTY_CREATE:
e594a3b1
LP
2051 /* Always reinitiaize the disk, don't consider what there was on the disk before */
2052 from_scratch = true;
2053 break;
2054 }
2055
2056 if (from_scratch) {
e594a3b1
LP
2057 r = fdisk_create_disklabel(c, "gpt");
2058 if (r < 0)
2059 return log_error_errno(r, "Failed to create GPT disk label: %m");
2060
53171c04 2061 r = derive_uuid(context->seed, "disk-uuid", &disk_uuid);
e594a3b1
LP
2062 if (r < 0)
2063 return log_error_errno(r, "Failed to acquire disk GPT uuid: %m");
2064
2065 r = fdisk_set_disklabel_id_by_uuid(c, disk_uuid);
2066 if (r < 0)
2067 return log_error_errno(r, "Failed to set GPT disk label: %m");
2068
2069 goto add_initial_free_area;
2070 }
2071
2072 r = fdisk_get_disklabel_id(c, &disk_uuid_string);
2073 if (r < 0)
2074 return log_error_errno(r, "Failed to get current GPT disk label UUID: %m");
2075
2076 r = sd_id128_from_string(disk_uuid_string, &disk_uuid);
2077 if (r < 0)
2078 return log_error_errno(r, "Failed to parse current GPT disk label UUID: %m");
2079
2080 if (sd_id128_is_null(disk_uuid)) {
53171c04 2081 r = derive_uuid(context->seed, "disk-uuid", &disk_uuid);
e594a3b1
LP
2082 if (r < 0)
2083 return log_error_errno(r, "Failed to acquire disk GPT uuid: %m");
2084
2085 r = fdisk_set_disklabel_id(c);
2086 if (r < 0)
2087 return log_error_errno(r, "Failed to set GPT disk label: %m");
2088 }
2089
2090 r = fdisk_get_partitions(c, &t);
2091 if (r < 0)
2092 return log_error_errno(r, "Failed to acquire partition table: %m");
2093
2094 n_partitions = fdisk_table_get_nents(t);
695cfd53 2095 for (size_t i = 0; i < n_partitions; i++) {
e594a3b1 2096 _cleanup_free_ char *label_copy = NULL;
03677889 2097 Partition *last = NULL;
e594a3b1
LP
2098 struct fdisk_partition *p;
2099 struct fdisk_parttype *pt;
2100 const char *pts, *ids, *label;
2101 uint64_t sz, start;
2102 bool found = false;
2103 sd_id128_t ptid, id;
2104 size_t partno;
2105
2106 p = fdisk_table_get_partition(t, i);
2107 if (!p)
2108 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read partition metadata: %m");
2109
2110 if (fdisk_partition_is_used(p) <= 0)
2111 continue;
2112
2113 if (fdisk_partition_has_start(p) <= 0 ||
2114 fdisk_partition_has_size(p) <= 0 ||
2115 fdisk_partition_has_partno(p) <= 0)
2116 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found a partition without a position, size or number.");
2117
2118 pt = fdisk_partition_get_type(p);
2119 if (!pt)
2120 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire type of partition: %m");
2121
2122 pts = fdisk_parttype_get_string(pt);
2123 if (!pts)
2124 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire type of partition as string: %m");
2125
2126 r = sd_id128_from_string(pts, &ptid);
2127 if (r < 0)
2128 return log_error_errno(r, "Failed to parse partition type UUID %s: %m", pts);
2129
2130 ids = fdisk_partition_get_uuid(p);
2131 if (!ids)
2132 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found a partition without a UUID.");
2133
2134 r = sd_id128_from_string(ids, &id);
2135 if (r < 0)
2136 return log_error_errno(r, "Failed to parse partition UUID %s: %m", ids);
2137
2138 label = fdisk_partition_get_name(p);
2139 if (!isempty(label)) {
2140 label_copy = strdup(label);
2141 if (!label_copy)
2142 return log_oom();
2143 }
2144
2145 sz = fdisk_partition_get_size(p);
ac33e147 2146 assert(sz <= UINT64_MAX/secsz);
994b3031 2147 sz *= secsz;
e594a3b1
LP
2148
2149 start = fdisk_partition_get_start(p);
ac33e147 2150 assert(start <= UINT64_MAX/secsz);
994b3031 2151 start *= secsz;
e594a3b1
LP
2152
2153 partno = fdisk_partition_get_partno(p);
2154
2155 if (left_boundary == UINT64_MAX || left_boundary > start)
2156 left_boundary = start;
2157
2158 /* Assign this existing partition to the first partition of the right type that doesn't have
2159 * an existing one assigned yet. */
2160 LIST_FOREACH(partitions, pp, context->partitions) {
2161 last = pp;
2162
22e932f4 2163 if (!sd_id128_equal(pp->type.uuid, ptid))
e594a3b1
LP
2164 continue;
2165
2166 if (!pp->current_partition) {
2167 pp->current_uuid = id;
2168 pp->current_size = sz;
2169 pp->offset = start;
2170 pp->partno = partno;
2171 pp->current_label = TAKE_PTR(label_copy);
2172
2173 pp->current_partition = p;
2174 fdisk_ref_partition(p);
2175
994b3031 2176 r = determine_current_padding(c, t, p, secsz, grainsz, &pp->current_padding);
e594a3b1
LP
2177 if (r < 0)
2178 return r;
2179
2180 if (pp->current_padding > 0) {
2181 r = context_add_free_area(context, pp->current_padding, pp);
2182 if (r < 0)
2183 return r;
2184 }
2185
2186 found = true;
2187 break;
2188 }
2189 }
2190
2191 /* If we have no matching definition, create a new one. */
2192 if (!found) {
2193 _cleanup_(partition_freep) Partition *np = NULL;
2194
2195 np = partition_new();
2196 if (!np)
2197 return log_oom();
2198
2199 np->current_uuid = id;
22e932f4 2200 np->type = gpt_partition_type_from_uuid(ptid);
e594a3b1
LP
2201 np->current_size = sz;
2202 np->offset = start;
2203 np->partno = partno;
2204 np->current_label = TAKE_PTR(label_copy);
2205
2206 np->current_partition = p;
2207 fdisk_ref_partition(p);
2208
994b3031 2209 r = determine_current_padding(c, t, p, secsz, grainsz, &np->current_padding);
e594a3b1
LP
2210 if (r < 0)
2211 return r;
2212
2213 if (np->current_padding > 0) {
2214 r = context_add_free_area(context, np->current_padding, np);
2215 if (r < 0)
2216 return r;
2217 }
2218
2219 LIST_INSERT_AFTER(partitions, context->partitions, last, TAKE_PTR(np));
2220 context->n_partitions++;
2221 }
2222 }
2223
2224add_initial_free_area:
2225 nsectors = fdisk_get_nsectors(c);
994b3031
LP
2226 assert(nsectors <= UINT64_MAX/secsz);
2227 nsectors *= secsz;
e594a3b1
LP
2228
2229 first_lba = fdisk_get_first_lba(c);
994b3031
LP
2230 assert(first_lba <= UINT64_MAX/secsz);
2231 first_lba *= secsz;
e594a3b1
LP
2232
2233 last_lba = fdisk_get_last_lba(c);
2234 assert(last_lba < UINT64_MAX);
2235 last_lba++;
994b3031
LP
2236 assert(last_lba <= UINT64_MAX/secsz);
2237 last_lba *= secsz;
e594a3b1
LP
2238
2239 assert(last_lba >= first_lba);
2240
2241 if (left_boundary == UINT64_MAX) {
2242 /* No partitions at all? Then the whole disk is up for grabs. */
2243
994b3031
LP
2244 first_lba = round_up_size(first_lba, grainsz);
2245 last_lba = round_down_size(last_lba, grainsz);
e594a3b1
LP
2246
2247 if (last_lba > first_lba) {
2248 r = context_add_free_area(context, last_lba - first_lba, NULL);
2249 if (r < 0)
2250 return r;
2251 }
2252 } else {
2253 /* Add space left of first partition */
2254 assert(left_boundary >= first_lba);
2255
994b3031
LP
2256 first_lba = round_up_size(first_lba, grainsz);
2257 left_boundary = round_down_size(left_boundary, grainsz);
2258 last_lba = round_down_size(last_lba, grainsz);
e594a3b1
LP
2259
2260 if (left_boundary > first_lba) {
2261 r = context_add_free_area(context, left_boundary - first_lba, NULL);
2262 if (r < 0)
2263 return r;
2264 }
2265 }
2266
2267 context->start = first_lba;
2268 context->end = last_lba;
2269 context->total = nsectors;
994b3031
LP
2270 context->sector_size = secsz;
2271 context->grain_size = grainsz;
e594a3b1
LP
2272 context->fdisk_context = TAKE_PTR(c);
2273
2274 return from_scratch;
2275}
2276
2277static void context_unload_partition_table(Context *context) {
e594a3b1
LP
2278 assert(context);
2279
80a226b2 2280 LIST_FOREACH(partitions, p, context->partitions) {
e594a3b1
LP
2281
2282 /* Entirely remove partitions that have no configuration */
2283 if (PARTITION_IS_FOREIGN(p)) {
2284 partition_unlink_and_free(context, p);
2285 continue;
2286 }
2287
2288 /* Otherwise drop all data we read off the block device and everything we might have
2289 * calculated based on it */
2290
2291 p->dropped = false;
2292 p->current_size = UINT64_MAX;
2293 p->new_size = UINT64_MAX;
2294 p->current_padding = UINT64_MAX;
2295 p->new_padding = UINT64_MAX;
2296 p->partno = UINT64_MAX;
2297 p->offset = UINT64_MAX;
2298
2299 if (p->current_partition) {
2300 fdisk_unref_partition(p->current_partition);
2301 p->current_partition = NULL;
2302 }
2303
2304 if (p->new_partition) {
2305 fdisk_unref_partition(p->new_partition);
2306 p->new_partition = NULL;
2307 }
2308
2309 p->padding_area = NULL;
2310 p->allocated_to_area = NULL;
2311
15d43e30
LP
2312 p->current_uuid = SD_ID128_NULL;
2313 p->current_label = mfree(p->current_label);
e594a3b1
LP
2314 }
2315
2316 context->start = UINT64_MAX;
2317 context->end = UINT64_MAX;
2318 context->total = UINT64_MAX;
2319
2320 if (context->fdisk_context) {
2321 fdisk_unref_context(context->fdisk_context);
2322 context->fdisk_context = NULL;
2323 }
2324
2325 context_free_free_areas(context);
2326}
2327
2328static int format_size_change(uint64_t from, uint64_t to, char **ret) {
2b59bf51 2329 char *t;
e594a3b1
LP
2330
2331 if (from != UINT64_MAX) {
2332 if (from == to || to == UINT64_MAX)
2b59bf51 2333 t = strdup(FORMAT_BYTES(from));
e594a3b1 2334 else
fc03e80c 2335 t = strjoin(FORMAT_BYTES(from), " ", special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), " ", FORMAT_BYTES(to));
e594a3b1 2336 } else if (to != UINT64_MAX)
fc03e80c 2337 t = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), " ", FORMAT_BYTES(to));
e594a3b1
LP
2338 else {
2339 *ret = NULL;
2340 return 0;
2341 }
2342
2b59bf51 2343 if (!t)
e594a3b1
LP
2344 return log_oom();
2345
2b59bf51 2346 *ret = t;
e594a3b1
LP
2347 return 1;
2348}
2349
2350static const char *partition_label(const Partition *p) {
2351 assert(p);
2352
2353 if (p->new_label)
2354 return p->new_label;
2355
2356 if (p->current_label)
2357 return p->current_label;
2358
22e932f4 2359 return gpt_partition_type_uuid_to_string(p->type.uuid);
e594a3b1
LP
2360}
2361
cc751c75 2362static int context_dump_partitions(Context *context) {
e594a3b1
LP
2363 _cleanup_(table_unrefp) Table *t = NULL;
2364 uint64_t sum_padding = 0, sum_size = 0;
e594a3b1 2365 int r;
b5b7879a
DDM
2366 const size_t roothash_col = 13, dropin_files_col = 14;
2367 bool has_roothash = false, has_dropin_files = false;
e594a3b1 2368
6a01ea4a 2369 if ((arg_json_format_flags & JSON_FORMAT_OFF) && context->n_partitions == 0) {
a015fbe7
TH
2370 log_info("Empty partition table.");
2371 return 0;
2372 }
2373
b5b7879a 2374 t = table_new("type", "label", "uuid", "file", "node", "offset", "old size", "raw size", "size", "old padding", "raw padding", "padding", "activity", "roothash", "drop-in files");
e594a3b1
LP
2375 if (!t)
2376 return log_oom();
2377
a015fbe7 2378 if (!DEBUG_LOGGING) {
6a01ea4a 2379 if (arg_json_format_flags & JSON_FORMAT_OFF)
a015fbe7 2380 (void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
b5b7879a 2381 (size_t) 8, (size_t) 11, roothash_col, dropin_files_col);
a015fbe7
TH
2382 else
2383 (void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
b5b7879a
DDM
2384 (size_t) 5, (size_t) 6, (size_t) 7, (size_t) 9, (size_t) 10,
2385 (size_t) 12, roothash_col, dropin_files_col);
a015fbe7 2386 }
e594a3b1 2387
e594a3b1 2388 (void) table_set_align_percent(t, table_get_cell(t, 0, 5), 100);
9c07c9ec
LP
2389 (void) table_set_align_percent(t, table_get_cell(t, 0, 6), 100);
2390 (void) table_set_align_percent(t, table_get_cell(t, 0, 7), 100);
2391 (void) table_set_align_percent(t, table_get_cell(t, 0, 8), 100);
2392 (void) table_set_align_percent(t, table_get_cell(t, 0, 9), 100);
2393 (void) table_set_align_percent(t, table_get_cell(t, 0, 10), 100);
2394 (void) table_set_align_percent(t, table_get_cell(t, 0, 11), 100);
e594a3b1
LP
2395
2396 LIST_FOREACH(partitions, p, context->partitions) {
b5b7879a 2397 _cleanup_free_ char *size_change = NULL, *padding_change = NULL, *partname = NULL, *rh = NULL;
b7416360 2398 char uuid_buffer[SD_ID128_UUID_STRING_MAX];
a015fbe7 2399 const char *label, *activity = NULL;
e594a3b1
LP
2400
2401 if (p->dropped)
2402 continue;
2403
a015fbe7
TH
2404 if (p->current_size == UINT64_MAX)
2405 activity = "create";
2406 else if (p->current_size != p->new_size)
2407 activity = "resize";
2408
e594a3b1 2409 label = partition_label(p);
cc751c75 2410 partname = p->partno != UINT64_MAX ? fdisk_partname(context->node, p->partno+1) : NULL;
e594a3b1
LP
2411
2412 r = format_size_change(p->current_size, p->new_size, &size_change);
2413 if (r < 0)
2414 return r;
2415
2416 r = format_size_change(p->current_padding, p->new_padding, &padding_change);
2417 if (r < 0)
2418 return r;
2419
2420 if (p->new_size != UINT64_MAX)
2421 sum_size += p->new_size;
2422 if (p->new_padding != UINT64_MAX)
2423 sum_padding += p->new_padding;
2424
b5b7879a
DDM
2425 if (p->verity == VERITY_HASH) {
2426 rh = p->roothash ? hexmem(p->roothash, p->roothash_size) : strdup("TBD");
2427 if (!rh)
2428 return log_oom();
2429 }
2430
e594a3b1
LP
2431 r = table_add_many(
2432 t,
22e932f4 2433 TABLE_STRING, gpt_partition_type_uuid_to_string_harder(p->type.uuid, uuid_buffer),
be9ce018 2434 TABLE_STRING, empty_to_null(label) ?: "-", TABLE_SET_COLOR, empty_to_null(label) ? NULL : ansi_grey(),
11749b61 2435 TABLE_UUID, p->new_uuid_is_set ? p->new_uuid : p->current_uuid,
e594a3b1 2436 TABLE_STRING, p->definition_path ? basename(p->definition_path) : "-", TABLE_SET_COLOR, p->definition_path ? NULL : ansi_grey(),
a015fbe7 2437 TABLE_STRING, partname ?: "-", TABLE_SET_COLOR, partname ? NULL : ansi_highlight(),
e594a3b1 2438 TABLE_UINT64, p->offset,
a015fbe7 2439 TABLE_UINT64, p->current_size == UINT64_MAX ? 0 : p->current_size,
e594a3b1
LP
2440 TABLE_UINT64, p->new_size,
2441 TABLE_STRING, size_change, TABLE_SET_COLOR, !p->partitions_next && sum_size > 0 ? ansi_underline() : NULL,
a015fbe7 2442 TABLE_UINT64, p->current_padding == UINT64_MAX ? 0 : p->current_padding,
e594a3b1 2443 TABLE_UINT64, p->new_padding,
a015fbe7 2444 TABLE_STRING, padding_change, TABLE_SET_COLOR, !p->partitions_next && sum_padding > 0 ? ansi_underline() : NULL,
39fc0174 2445 TABLE_STRING, activity ?: "unchanged",
b5b7879a 2446 TABLE_STRING, rh,
39fc0174 2447 TABLE_STRV, p->drop_in_files);
e594a3b1 2448 if (r < 0)
f987a261 2449 return table_log_add_error(r);
39fc0174 2450
b5b7879a 2451 has_roothash = has_roothash || !isempty(rh);
3ab44dbd 2452 has_dropin_files = has_dropin_files || !strv_isempty(p->drop_in_files);
e594a3b1
LP
2453 }
2454
6a01ea4a 2455 if ((arg_json_format_flags & JSON_FORMAT_OFF) && (sum_padding > 0 || sum_size > 0)) {
e594a3b1
LP
2456 const char *a, *b;
2457
2b59bf51
ZJS
2458 a = strjoina(special_glyph(SPECIAL_GLYPH_SIGMA), " = ", FORMAT_BYTES(sum_size));
2459 b = strjoina(special_glyph(SPECIAL_GLYPH_SIGMA), " = ", FORMAT_BYTES(sum_padding));
e594a3b1
LP
2460
2461 r = table_add_many(
2462 t,
2463 TABLE_EMPTY,
2464 TABLE_EMPTY,
2465 TABLE_EMPTY,
2466 TABLE_EMPTY,
2467 TABLE_EMPTY,
2468 TABLE_EMPTY,
2469 TABLE_EMPTY,
a015fbe7 2470 TABLE_EMPTY,
e594a3b1
LP
2471 TABLE_STRING, a,
2472 TABLE_EMPTY,
a015fbe7
TH
2473 TABLE_EMPTY,
2474 TABLE_STRING, b,
39fc0174 2475 TABLE_EMPTY,
b5b7879a 2476 TABLE_EMPTY,
a015fbe7 2477 TABLE_EMPTY);
e594a3b1 2478 if (r < 0)
f987a261 2479 return table_log_add_error(r);
e594a3b1
LP
2480 }
2481
b5b7879a
DDM
2482 if (!has_roothash) {
2483 r = table_hide_column_from_display(t, roothash_col);
2484 if (r < 0)
2485 return log_error_errno(r, "Failed to set columns to display: %m");
2486 }
2487
3ab44dbd 2488 if (!has_dropin_files) {
39fc0174
RP
2489 r = table_hide_column_from_display(t, dropin_files_col);
2490 if (r < 0)
2491 return log_error_errno(r, "Failed to set columns to display: %m");
2492 }
2493
896e678b 2494 return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
e594a3b1
LP
2495}
2496
2497static void context_bar_char_process_partition(
2498 Context *context,
2499 Partition *bar[],
2500 size_t n,
2501 Partition *p,
2502 size_t *ret_start) {
2503
2504 uint64_t from, to, total;
2505 size_t x, y;
2506
2507 assert(context);
2508 assert(bar);
2509 assert(n > 0);
2510 assert(p);
2511
2512 if (p->dropped)
2513 return;
2514
2515 assert(p->offset != UINT64_MAX);
2516 assert(p->new_size != UINT64_MAX);
2517
2518 from = p->offset;
2519 to = from + p->new_size;
2520
d8daed09
TY
2521 assert(context->total > 0);
2522 total = context->total;
e594a3b1 2523
d8daed09
TY
2524 assert(from <= total);
2525 x = from * n / total;
e594a3b1 2526
d8daed09
TY
2527 assert(to <= total);
2528 y = to * n / total;
e594a3b1
LP
2529
2530 assert(x <= y);
2531 assert(y <= n);
2532
2533 for (size_t i = x; i < y; i++)
2534 bar[i] = p;
2535
2536 *ret_start = x;
2537}
2538
2539static int partition_hint(const Partition *p, const char *node, char **ret) {
2540 _cleanup_free_ char *buf = NULL;
e594a3b1
LP
2541 const char *label;
2542 sd_id128_t id;
2543
2544 /* Tries really hard to find a suitable description for this partition */
2545
2546 if (p->definition_path) {
2547 buf = strdup(basename(p->definition_path));
2548 goto done;
2549 }
2550
2551 label = partition_label(p);
2552 if (!isempty(label)) {
2553 buf = strdup(label);
2554 goto done;
2555 }
2556
2557 if (p->partno != UINT64_MAX) {
2558 buf = fdisk_partname(node, p->partno+1);
2559 goto done;
2560 }
2561
11749b61 2562 if (p->new_uuid_is_set)
e594a3b1
LP
2563 id = p->new_uuid;
2564 else if (!sd_id128_is_null(p->current_uuid))
2565 id = p->current_uuid;
2566 else
22e932f4 2567 id = p->type.uuid;
e594a3b1 2568
b7416360 2569 buf = strdup(SD_ID128_TO_UUID_STRING(id));
e594a3b1
LP
2570
2571done:
2572 if (!buf)
2573 return -ENOMEM;
2574
2575 *ret = TAKE_PTR(buf);
2576 return 0;
2577}
2578
cc751c75 2579static int context_dump_partition_bar(Context *context) {
e594a3b1
LP
2580 _cleanup_free_ Partition **bar = NULL;
2581 _cleanup_free_ size_t *start_array = NULL;
03677889 2582 Partition *last = NULL;
e594a3b1
LP
2583 bool z = false;
2584 size_t c, j = 0;
2585
f391597c 2586 assert_se((c = columns()) >= 2);
e594a3b1
LP
2587 c -= 2; /* We do not use the leftmost and rightmost character cell */
2588
2589 bar = new0(Partition*, c);
2590 if (!bar)
2591 return log_oom();
2592
2593 start_array = new(size_t, context->n_partitions);
2594 if (!start_array)
2595 return log_oom();
2596
2597 LIST_FOREACH(partitions, p, context->partitions)
2598 context_bar_char_process_partition(context, bar, c, p, start_array + j++);
2599
2600 putc(' ', stdout);
2601
2602 for (size_t i = 0; i < c; i++) {
2603 if (bar[i]) {
2604 if (last != bar[i])
2605 z = !z;
2606
2607 fputs(z ? ansi_green() : ansi_yellow(), stdout);
2608 fputs(special_glyph(SPECIAL_GLYPH_DARK_SHADE), stdout);
2609 } else {
2610 fputs(ansi_normal(), stdout);
2611 fputs(special_glyph(SPECIAL_GLYPH_LIGHT_SHADE), stdout);
2612 }
2613
2614 last = bar[i];
2615 }
2616
2617 fputs(ansi_normal(), stdout);
2618 putc('\n', stdout);
2619
2620 for (size_t i = 0; i < context->n_partitions; i++) {
2621 _cleanup_free_ char **line = NULL;
2622
2623 line = new0(char*, c);
2624 if (!line)
2625 return log_oom();
2626
2627 j = 0;
2628 LIST_FOREACH(partitions, p, context->partitions) {
2629 _cleanup_free_ char *d = NULL;
2630 j++;
2631
2632 if (i < context->n_partitions - j) {
2633
2634 if (line[start_array[j-1]]) {
2635 const char *e;
2636
2637 /* Upgrade final corner to the right with a branch to the right */
2638 e = startswith(line[start_array[j-1]], special_glyph(SPECIAL_GLYPH_TREE_RIGHT));
2639 if (e) {
2640 d = strjoin(special_glyph(SPECIAL_GLYPH_TREE_BRANCH), e);
2641 if (!d)
2642 return log_oom();
2643 }
2644 }
2645
2646 if (!d) {
2647 d = strdup(special_glyph(SPECIAL_GLYPH_TREE_VERTICAL));
2648 if (!d)
2649 return log_oom();
2650 }
2651
2652 } else if (i == context->n_partitions - j) {
2653 _cleanup_free_ char *hint = NULL;
2654
cc751c75 2655 (void) partition_hint(p, context->node, &hint);
e594a3b1
LP
2656
2657 if (streq_ptr(line[start_array[j-1]], special_glyph(SPECIAL_GLYPH_TREE_VERTICAL)))
2658 d = strjoin(special_glyph(SPECIAL_GLYPH_TREE_BRANCH), " ", strna(hint));
2659 else
2660 d = strjoin(special_glyph(SPECIAL_GLYPH_TREE_RIGHT), " ", strna(hint));
2661
2662 if (!d)
2663 return log_oom();
2664 }
2665
2666 if (d)
2667 free_and_replace(line[start_array[j-1]], d);
2668 }
2669
2670 putc(' ', stdout);
2671
2672 j = 0;
2673 while (j < c) {
2674 if (line[j]) {
2675 fputs(line[j], stdout);
2676 j += utf8_console_width(line[j]);
2677 } else {
2678 putc(' ', stdout);
2679 j++;
2680 }
2681 }
2682
2683 putc('\n', stdout);
2684
2685 for (j = 0; j < c; j++)
2686 free(line[j]);
2687 }
2688
2689 return 0;
2690}
2691
b5b7879a
DDM
2692static bool context_has_roothash(Context *context) {
2693 LIST_FOREACH(partitions, p, context->partitions)
2694 if (p->roothash)
2695 return true;
2696
2697 return false;
2698}
2699
cc751c75 2700static int context_dump(Context *context, bool late) {
a26d463d
DDM
2701 int r;
2702
2703 assert(context);
a26d463d
DDM
2704
2705 if (arg_pretty == 0 && FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF))
2706 return 0;
2707
b5b7879a
DDM
2708 /* If we're outputting JSON, only dump after doing all operations so we can include the roothashes
2709 * in the output. */
2710 if (!late && !FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF))
2711 return 0;
2712
2713 /* If we're not outputting JSON, only dump again after doing all operations if there are any
2714 * roothashes that we need to communicate to the user. */
2715 if (late && FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF) && !context_has_roothash(context))
2716 return 0;
2717
cc751c75 2718 r = context_dump_partitions(context);
a26d463d
DDM
2719 if (r < 0)
2720 return r;
2721
b5b7879a
DDM
2722 /* Make sure we only write the partition bar once, even if we're writing the partition table twice to
2723 * communicate roothashes. */
2724 if (FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF) && !late) {
a26d463d
DDM
2725 putc('\n', stdout);
2726
cc751c75 2727 r = context_dump_partition_bar(context);
a26d463d
DDM
2728 if (r < 0)
2729 return r;
2730
2731 putc('\n', stdout);
2732 }
2733
2734 fflush(stdout);
2735
2736 return 0;
2737}
2738
2739
e594a3b1 2740static bool context_changed(const Context *context) {
03677889 2741 assert(context);
e594a3b1
LP
2742
2743 LIST_FOREACH(partitions, p, context->partitions) {
2744 if (p->dropped)
2745 continue;
2746
2747 if (p->allocated_to_area)
2748 return true;
2749
2750 if (p->new_size != p->current_size)
2751 return true;
2752 }
2753
2754 return false;
2755}
2756
81873a6b 2757static int context_wipe_range(Context *context, uint64_t offset, uint64_t size) {
e594a3b1
LP
2758 _cleanup_(blkid_free_probep) blkid_probe probe = NULL;
2759 int r;
2760
2761 assert(context);
81873a6b
LP
2762 assert(offset != UINT64_MAX);
2763 assert(size != UINT64_MAX);
e594a3b1
LP
2764
2765 probe = blkid_new_probe();
2766 if (!probe)
2767 return log_oom();
2768
e594a3b1 2769 errno = 0;
81873a6b 2770 r = blkid_probe_set_device(probe, fdisk_get_devfd(context->fdisk_context), offset, size);
e594a3b1 2771 if (r < 0)
81873a6b 2772 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to allocate device probe for wiping.");
e594a3b1
LP
2773
2774 errno = 0;
2775 if (blkid_probe_enable_superblocks(probe, true) < 0 ||
2776 blkid_probe_set_superblocks_flags(probe, BLKID_SUBLKS_MAGIC|BLKID_SUBLKS_BADCSUM) < 0 ||
2777 blkid_probe_enable_partitions(probe, true) < 0 ||
2778 blkid_probe_set_partitions_flags(probe, BLKID_PARTS_MAGIC) < 0)
81873a6b 2779 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to enable superblock and partition probing.");
e594a3b1
LP
2780
2781 for (;;) {
2782 errno = 0;
2783 r = blkid_do_probe(probe);
2784 if (r < 0)
2785 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe for file systems.");
2786 if (r > 0)
2787 break;
2788
2789 errno = 0;
2790 if (blkid_do_wipe(probe, false) < 0)
2791 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to wipe file system signature.");
2792 }
2793
e594a3b1
LP
2794 return 0;
2795}
2796
81873a6b
LP
2797static int context_wipe_partition(Context *context, Partition *p) {
2798 int r;
2799
2800 assert(context);
2801 assert(p);
2802 assert(!PARTITION_EXISTS(p)); /* Safety check: never wipe existing partitions */
2803
2804 assert(p->offset != UINT64_MAX);
2805 assert(p->new_size != UINT64_MAX);
2806
2807 r = context_wipe_range(context, p->offset, p->new_size);
2808 if (r < 0)
2809 return r;
2810
2811 log_info("Successfully wiped file system signatures from future partition %" PRIu64 ".", p->partno);
2812 return 0;
2813}
2814
2815static int context_discard_range(
2816 Context *context,
2817 uint64_t offset,
2818 uint64_t size) {
2819
e594a3b1
LP
2820 struct stat st;
2821 int fd;
2822
2823 assert(context);
2824 assert(offset != UINT64_MAX);
2825 assert(size != UINT64_MAX);
2826
2827 if (size <= 0)
2828 return 0;
2829
a26f4a49 2830 assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
e594a3b1
LP
2831
2832 if (fstat(fd, &st) < 0)
2833 return -errno;
2834
2835 if (S_ISREG(st.st_mode)) {
2836 if (fallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, offset, size) < 0) {
2837 if (ERRNO_IS_NOT_SUPPORTED(errno))
2838 return -EOPNOTSUPP;
2839
2840 return -errno;
2841 }
2842
2843 return 1;
2844 }
2845
2846 if (S_ISBLK(st.st_mode)) {
2847 uint64_t range[2], end;
2848
994b3031 2849 range[0] = round_up_size(offset, context->sector_size);
e594a3b1 2850
55d38014
LP
2851 if (offset > UINT64_MAX - size)
2852 return -ERANGE;
2853
e594a3b1
LP
2854 end = offset + size;
2855 if (end <= range[0])
2856 return 0;
2857
994b3031 2858 range[1] = round_down_size(end - range[0], context->sector_size);
e594a3b1
LP
2859 if (range[1] <= 0)
2860 return 0;
2861
2862 if (ioctl(fd, BLKDISCARD, range) < 0) {
2863 if (ERRNO_IS_NOT_SUPPORTED(errno))
2864 return -EOPNOTSUPP;
2865
2866 return -errno;
2867 }
2868
2869 return 1;
2870 }
2871
2872 return -EOPNOTSUPP;
2873}
2874
2875static int context_discard_partition(Context *context, Partition *p) {
2876 int r;
2877
2878 assert(context);
2879 assert(p);
2880
2881 assert(p->offset != UINT64_MAX);
2882 assert(p->new_size != UINT64_MAX);
2883 assert(!PARTITION_EXISTS(p)); /* Safety check: never discard existing partitions */
2884
2885 if (!arg_discard)
2886 return 0;
2887
2888 r = context_discard_range(context, p->offset, p->new_size);
2889 if (r == -EOPNOTSUPP) {
5b5109e2 2890 log_info("Storage does not support discard, not discarding data in future partition %" PRIu64 ".", p->partno);
e594a3b1
LP
2891 return 0;
2892 }
22163eb5
LP
2893 if (r == -EBUSY) {
2894 /* Let's handle this gracefully: https://bugzilla.kernel.org/show_bug.cgi?id=211167 */
2895 log_info("Block device is busy, not discarding partition %" PRIu64 " because it probably is mounted.", p->partno);
2896 return 0;
2897 }
e594a3b1
LP
2898 if (r == 0) {
2899 log_info("Partition %" PRIu64 " too short for discard, skipping.", p->partno);
2900 return 0;
2901 }
2902 if (r < 0)
5b5109e2 2903 return log_error_errno(r, "Failed to discard data for future partition %" PRIu64 ".", p->partno);
e594a3b1 2904
5b5109e2 2905 log_info("Successfully discarded data from future partition %" PRIu64 ".", p->partno);
e594a3b1
LP
2906 return 1;
2907}
2908
2909static int context_discard_gap_after(Context *context, Partition *p) {
2910 uint64_t gap, next = UINT64_MAX;
e594a3b1
LP
2911 int r;
2912
2913 assert(context);
2914 assert(!p || (p->offset != UINT64_MAX && p->new_size != UINT64_MAX));
2915
5113436b
AF
2916 if (!arg_discard)
2917 return 0;
2918
e594a3b1
LP
2919 if (p)
2920 gap = p->offset + p->new_size;
2921 else
2922 gap = context->start;
2923
2924 LIST_FOREACH(partitions, q, context->partitions) {
2925 if (q->dropped)
2926 continue;
2927
2928 assert(q->offset != UINT64_MAX);
2929 assert(q->new_size != UINT64_MAX);
2930
2931 if (q->offset < gap)
2932 continue;
2933
2934 if (next == UINT64_MAX || q->offset < next)
2935 next = q->offset;
2936 }
2937
2938 if (next == UINT64_MAX) {
2939 next = context->end;
2940 if (gap > next)
2941 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition end beyond disk end.");
2942 }
2943
2944 assert(next >= gap);
2945 r = context_discard_range(context, gap, next - gap);
2946 if (r == -EOPNOTSUPP) {
2947 if (p)
5b5109e2 2948 log_info("Storage does not support discard, not discarding gap after partition %" PRIu64 ".", p->partno);
e594a3b1 2949 else
5b5109e2 2950 log_info("Storage does not support discard, not discarding gap at beginning of disk.");
e594a3b1
LP
2951 return 0;
2952 }
2953 if (r == 0) /* Too short */
2954 return 0;
2955 if (r < 0) {
2956 if (p)
2957 return log_error_errno(r, "Failed to discard gap after partition %" PRIu64 ".", p->partno);
2958 else
2959 return log_error_errno(r, "Failed to discard gap at beginning of disk.");
2960 }
2961
2962 if (p)
2963 log_info("Successfully discarded gap after partition %" PRIu64 ".", p->partno);
2964 else
2965 log_info("Successfully discarded gap at beginning of disk.");
2966
2967 return 0;
2968}
2969
cc751c75 2970static int context_wipe_and_discard(Context *context) {
e594a3b1
LP
2971 int r;
2972
2973 assert(context);
2974
2975 /* Wipe and discard the contents of all partitions we are about to create. We skip the discarding if
2976 * we were supposed to start from scratch anyway, as in that case we just discard the whole block
2977 * device in one go early on. */
2978
2979 LIST_FOREACH(partitions, p, context->partitions) {
2980
2981 if (!p->allocated_to_area)
2982 continue;
2983
81d1098b
DDM
2984 if (partition_skip(p))
2985 continue;
2986
e594a3b1
LP
2987 r = context_wipe_partition(context, p);
2988 if (r < 0)
2989 return r;
2990
cc751c75 2991 if (!context->from_scratch) {
f0cb1b95
LP
2992 r = context_discard_partition(context, p);
2993 if (r < 0)
2994 return r;
2995
e594a3b1
LP
2996 r = context_discard_gap_after(context, p);
2997 if (r < 0)
2998 return r;
2999 }
3000 }
3001
cc751c75 3002 if (!context->from_scratch) {
e594a3b1
LP
3003 r = context_discard_gap_after(context, NULL);
3004 if (r < 0)
3005 return r;
3006 }
3007
3008 return 0;
3009}
3010
a64769d6
DDM
3011typedef struct {
3012 LoopDevice *loop;
3013 int fd;
3014 char *path;
3015 int whole_fd;
3016} PartitionTarget;
3017
3018static int partition_target_fd(PartitionTarget *t) {
3019 assert(t);
3020 assert(t->loop || t->fd >= 0 || t->whole_fd >= 0);
3021 return t->loop ? t->loop->fd : t->fd >= 0 ? t->fd : t->whole_fd;
3022}
3023
3024static const char* partition_target_path(PartitionTarget *t) {
3025 assert(t);
3026 assert(t->loop || t->path);
3027 return t->loop ? t->loop->node : t->path;
3028}
3029
3030static PartitionTarget *partition_target_free(PartitionTarget *t) {
3031 if (!t)
3032 return NULL;
3033
3034 loop_device_unref(t->loop);
3035 safe_close(t->fd);
3036 unlink_and_free(t->path);
3037
3038 return mfree(t);
3039}
3040
3041DEFINE_TRIVIAL_CLEANUP_FUNC(PartitionTarget*, partition_target_free);
3042
d3201eb4
DDM
3043static int prepare_temporary_file(PartitionTarget *t, uint64_t size) {
3044 _cleanup_(unlink_and_freep) char *temp = NULL;
3045 _cleanup_close_ int fd = -1;
3046 const char *vt;
3047 int r;
3048
3049 assert(t);
3050
3051 r = var_tmp_dir(&vt);
3052 if (r < 0)
3053 return log_error_errno(r, "Could not determine temporary directory: %m");
3054
3055 temp = path_join(vt, "repart-XXXXXX");
3056 if (!temp)
3057 return log_oom();
3058
3059 fd = mkostemp_safe(temp);
3060 if (fd < 0)
3061 return log_error_errno(fd, "Failed to create temporary file: %m");
3062
3063 if (ftruncate(fd, size) < 0)
3064 return log_error_errno(errno, "Failed to truncate temporary file to %s: %m",
3065 FORMAT_BYTES(size));
3066
3067 t->fd = TAKE_FD(fd);
3068 t->path = TAKE_PTR(temp);
3069
3070 return 0;
3071}
3072
a64769d6
DDM
3073static int partition_target_prepare(
3074 Context *context,
3075 Partition *p,
3076 uint64_t size,
3077 bool need_path,
3078 PartitionTarget **ret) {
3079
3080 _cleanup_(partition_target_freep) PartitionTarget *t = NULL;
d3201eb4
DDM
3081 _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
3082 int whole_fd, r;
a64769d6
DDM
3083
3084 assert(context);
3085 assert(p);
3086 assert(ret);
3087
3088 assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
3089
6b1ea55e 3090 t = new(PartitionTarget, 1);
a64769d6
DDM
3091 if (!t)
3092 return log_oom();
6b1ea55e
DDM
3093 *t = (PartitionTarget) {
3094 .fd = -1,
3095 .whole_fd = -1,
3096 };
a64769d6 3097
d3201eb4
DDM
3098 if (!need_path) {
3099 if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1)
3100 return log_error_errno(errno, "Failed to seek to partition offset: %m");
a64769d6 3101
d3201eb4
DDM
3102 t->whole_fd = whole_fd;
3103 *ret = TAKE_PTR(t);
3104 return 0;
3105 }
a64769d6 3106
d3201eb4
DDM
3107 /* Loopback block devices are not only useful to turn regular files into block devices, but
3108 * also to cut out sections of block devices into new block devices. */
a64769d6 3109
d3201eb4
DDM
3110 r = loop_device_make(whole_fd, O_RDWR, p->offset, size, 0, 0, LOCK_EX, &d);
3111 if (r < 0 && r != -ENOENT && !ERRNO_IS_PRIVILEGE(r))
3112 return log_error_errno(r, "Failed to make loopback device of future partition %" PRIu64 ": %m", p->partno);
3113 if (r >= 0) {
6b1ea55e 3114 t->loop = TAKE_PTR(d);
d3201eb4
DDM
3115 *ret = TAKE_PTR(t);
3116 return 0;
3117 }
a64769d6 3118
d3201eb4
DDM
3119 /* If we can't allocate a loop device, let's write to a regular file that we copy into the final
3120 * image so we can run in containers and without needing root privileges. On filesystems with
3121 * reflinking support, we can take advantage of this and just reflink the result into the image.
3122 */
a64769d6 3123
d3201eb4 3124 log_debug_errno(r, "No access to loop devices, falling back to a regular file");
a64769d6 3125
d3201eb4
DDM
3126 r = prepare_temporary_file(t, size);
3127 if (r < 0)
3128 return r;
a64769d6
DDM
3129
3130 *ret = TAKE_PTR(t);
3131
3132 return 0;
3133}
3134
3135static int partition_target_grow(PartitionTarget *t, uint64_t size) {
3136 int r;
3137
3138 assert(t);
3139
3140 if (t->loop) {
3141 r = loop_device_refresh_size(t->loop, UINT64_MAX, size);
3142 if (r < 0)
3143 return log_error_errno(r, "Failed to refresh loopback device size: %m");
3144 } else if (t->fd >= 0) {
3145 if (ftruncate(t->fd, size) < 0)
3146 return log_error_errno(errno, "Failed to grow '%s' to %s by truncation: %m",
3147 t->path, FORMAT_BYTES(size));
3148 }
3149
3150 return 0;
3151}
3152
3153static int partition_target_sync(Context *context, Partition *p, PartitionTarget *t) {
3154 int whole_fd, r;
3155
3156 assert(context);
3157 assert(p);
3158 assert(t);
3159
3160 assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
3161
3162 if (t->loop) {
3163 r = loop_device_sync(t->loop);
3164 if (r < 0)
3165 return log_error_errno(r, "Failed to sync loopback device: %m");
3166 } else if (t->fd >= 0) {
3167 if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1)
3168 return log_error_errno(errno, "Failed to seek to partition offset: %m");
3169
3170 r = copy_bytes(t->fd, whole_fd, UINT64_MAX, COPY_REFLINK|COPY_HOLES|COPY_FSYNC);
3171 if (r < 0)
3172 return log_error_errno(r, "Failed to copy bytes to partition: %m");
3173 } else {
3174 if (fsync(t->whole_fd) < 0)
3175 return log_error_errno(errno, "Failed to sync changes: %m");
3176 }
3177
3178 return 0;
3179}
3180
48a09a8f
DDM
3181static int partition_encrypt(Context *context, Partition *p, const char *node) {
3182#if HAVE_LIBCRYPTSETUP && HAVE_CRYPT_SET_DATA_OFFSET && HAVE_CRYPT_REENCRYPT_INIT_BY_PASSPHRASE && HAVE_CRYPT_REENCRYPT
3183 struct crypt_params_luks2 luks_params = {
922576e4
DDM
3184 .label = strempty(ASSERT_PTR(p)->new_label),
3185 .sector_size = ASSERT_PTR(context)->sector_size,
48a09a8f
DDM
3186 .data_device = node,
3187 };
3188 struct crypt_params_reencrypt reencrypt_params = {
3189 .mode = CRYPT_REENCRYPT_ENCRYPT,
3190 .direction = CRYPT_REENCRYPT_BACKWARD,
3191 .resilience = "datashift",
3192 .data_shift = LUKS2_METADATA_SIZE / 512,
3193 .luks2 = &luks_params,
3194 .flags = CRYPT_REENCRYPT_INITIALIZE_ONLY|CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT,
3195 };
0d12936d 3196 _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
48a09a8f
DDM
3197 _cleanup_(erase_and_freep) char *base64_encoded = NULL;
3198 _cleanup_fclose_ FILE *h = NULL;
3199 _cleanup_free_ char *hp = NULL;
3200 const char *passphrase = NULL;
3201 size_t passphrase_size = 0;
b9df3536 3202 sd_id128_t uuid;
0b75744d 3203 const char *vt;
b9df3536
LP
3204 int r;
3205
994b3031 3206 assert(context);
b9df3536 3207 assert(p);
889914ef
LP
3208 assert(p->encrypt != ENCRYPT_OFF);
3209
0d12936d
LP
3210 r = dlopen_cryptsetup();
3211 if (r < 0)
3212 return log_error_errno(r, "libcryptsetup not found, cannot encrypt: %m");
3213
b9df3536
LP
3214 r = derive_uuid(p->new_uuid, "luks-uuid", &uuid);
3215 if (r < 0)
3216 return r;
3217
3218 log_info("Encrypting future partition %" PRIu64 "...", p->partno);
3219
0b75744d
DDM
3220 r = var_tmp_dir(&vt);
3221 if (r < 0)
3222 return log_error_errno(r, "Failed to determine temporary files directory: %m");
3223
3224 r = fopen_temporary_child(vt, &h, &hp);
48a09a8f
DDM
3225 if (r < 0)
3226 return log_error_errno(r, "Failed to create temporary LUKS header file: %m");
3227
3228 /* Weird cryptsetup requirement which requires the header file to be the size of at least one sector. */
983154f5 3229 r = ftruncate(fileno(h), context->sector_size);
b9df3536 3230 if (r < 0)
48a09a8f
DDM
3231 return log_error_errno(r, "Failed to grow temporary LUKS header file: %m");
3232
3233 r = sym_crypt_init(&cd, hp);
3234 if (r < 0)
3235 return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", hp);
b9df3536
LP
3236
3237 cryptsetup_enable_logging(cd);
3238
48a09a8f
DDM
3239 /* Disable kernel keyring usage by libcryptsetup as a workaround for
3240 * https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/273. This makes sure that we can do
3241 * offline encryption even when repart is running in a container. */
3242 r = sym_crypt_volume_key_keyring(cd, false);
3243 if (r < 0)
3244 return log_error_errno(r, "Failed to disable kernel keyring: %m");
3245
3246 r = sym_crypt_metadata_locking(cd, false);
3247 if (r < 0)
3248 return log_error_errno(r, "Failed to disable metadata locking: %m");
3249
3250 r = sym_crypt_set_data_offset(cd, LUKS2_METADATA_SIZE / 512);
3251 if (r < 0)
3252 return log_error_errno(r, "Failed to set data offset: %m");
3253
0d12936d 3254 r = sym_crypt_format(cd,
b9df3536
LP
3255 CRYPT_LUKS2,
3256 "aes",
3257 "xts-plain64",
b7416360 3258 SD_ID128_TO_UUID_STRING(uuid),
98e0456e
DDM
3259 NULL,
3260 VOLUME_KEY_SIZE,
48a09a8f 3261 &luks_params);
b9df3536
LP
3262 if (r < 0)
3263 return log_error_errno(r, "Failed to LUKS2 format future partition: %m");
3264
889914ef
LP
3265 if (IN_SET(p->encrypt, ENCRYPT_KEY_FILE, ENCRYPT_KEY_FILE_TPM2)) {
3266 r = sym_crypt_keyslot_add_by_volume_key(
3267 cd,
3268 CRYPT_ANY_SLOT,
98e0456e
DDM
3269 NULL,
3270 VOLUME_KEY_SIZE,
889914ef
LP
3271 strempty(arg_key),
3272 arg_key_size);
3273 if (r < 0)
3274 return log_error_errno(r, "Failed to add LUKS2 key: %m");
48a09a8f
DDM
3275
3276 passphrase = strempty(arg_key);
3277 passphrase_size = arg_key_size;
889914ef
LP
3278 }
3279
3280 if (IN_SET(p->encrypt, ENCRYPT_TPM2, ENCRYPT_KEY_FILE_TPM2)) {
3281#if HAVE_TPM2
889914ef
LP
3282 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
3283 _cleanup_(erase_and_freep) void *secret = NULL;
02ef97cd 3284 _cleanup_free_ void *pubkey = NULL;
889914ef 3285 _cleanup_free_ void *blob = NULL, *hash = NULL;
02ef97cd 3286 size_t secret_size, blob_size, hash_size, pubkey_size = 0;
2b92a672 3287 uint16_t pcr_bank, primary_alg;
889914ef
LP
3288 int keyslot;
3289
02ef97cd
LP
3290 if (arg_tpm2_public_key_pcr_mask != 0) {
3291 r = tpm2_load_pcr_public_key(arg_tpm2_public_key, &pubkey, &pubkey_size);
3292 if (r < 0) {
3293 if (arg_tpm2_public_key || r != -ENOENT)
3294 return log_error_errno(r, "Failed read TPM PCR public key: %m");
3295
3296 log_debug_errno(r, "Failed to read TPM2 PCR public key, proceeding without: %m");
3297 arg_tpm2_public_key_pcr_mask = 0;
3298 }
3299 }
3300
d9b5841d
LP
3301 r = tpm2_seal(arg_tpm2_device,
3302 arg_tpm2_pcr_mask,
02ef97cd
LP
3303 pubkey, pubkey_size,
3304 arg_tpm2_public_key_pcr_mask,
d9b5841d
LP
3305 /* pin= */ NULL,
3306 &secret, &secret_size,
3307 &blob, &blob_size,
3308 &hash, &hash_size,
3309 &pcr_bank,
3310 &primary_alg);
889914ef
LP
3311 if (r < 0)
3312 return log_error_errno(r, "Failed to seal to TPM2: %m");
3313
3314 r = base64mem(secret, secret_size, &base64_encoded);
3315 if (r < 0)
3316 return log_error_errno(r, "Failed to base64 encode secret key: %m");
3317
3318 r = cryptsetup_set_minimal_pbkdf(cd);
3319 if (r < 0)
3320 return log_error_errno(r, "Failed to set minimal PBKDF: %m");
3321
3322 keyslot = sym_crypt_keyslot_add_by_volume_key(
3323 cd,
3324 CRYPT_ANY_SLOT,
98e0456e
DDM
3325 NULL,
3326 VOLUME_KEY_SIZE,
889914ef
LP
3327 base64_encoded,
3328 strlen(base64_encoded));
3329 if (keyslot < 0)
48a09a8f 3330 return log_error_errno(keyslot, "Failed to add new TPM2 key: %m");
889914ef 3331
f0f4fcae
LP
3332 r = tpm2_make_luks2_json(
3333 keyslot,
3334 arg_tpm2_pcr_mask,
3335 pcr_bank,
02ef97cd
LP
3336 pubkey, pubkey_size,
3337 arg_tpm2_public_key_pcr_mask,
f0f4fcae
LP
3338 primary_alg,
3339 blob, blob_size,
3340 hash, hash_size,
3341 0,
3342 &v);
889914ef
LP
3343 if (r < 0)
3344 return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");
3345
3346 r = cryptsetup_add_token_json(cd, v);
3347 if (r < 0)
3348 return log_error_errno(r, "Failed to add TPM2 JSON token to LUKS2 header: %m");
48a09a8f
DDM
3349
3350 passphrase = base64_encoded;
3351 passphrase_size = strlen(base64_encoded);
889914ef
LP
3352#else
3353 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
3354 "Support for TPM2 enrollment not enabled.");
3355#endif
3356 }
b9df3536 3357
48a09a8f 3358 r = sym_crypt_reencrypt_init_by_passphrase(
b9df3536 3359 cd,
98e0456e 3360 NULL,
48a09a8f
DDM
3361 passphrase,
3362 passphrase_size,
3363 CRYPT_ANY_SLOT,
3364 0,
3365 sym_crypt_get_cipher(cd),
3366 sym_crypt_get_cipher_mode(cd),
3367 &reencrypt_params);
b9df3536 3368 if (r < 0)
48a09a8f 3369 return log_error_errno(r, "Failed to prepare for reencryption: %m");
b9df3536 3370
48a09a8f
DDM
3371 /* crypt_reencrypt_init_by_passphrase() doesn't actually put the LUKS header at the front, we have
3372 * to do that ourselves. */
b9df3536 3373
48a09a8f
DDM
3374 sym_crypt_free(cd);
3375 cd = NULL;
b9df3536 3376
48a09a8f
DDM
3377 r = sym_crypt_init(&cd, node);
3378 if (r < 0)
3379 return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", node);
b9df3536 3380
48a09a8f
DDM
3381 r = sym_crypt_header_restore(cd, CRYPT_LUKS2, hp);
3382 if (r < 0)
3383 return log_error_errno(r, "Failed to place new LUKS header at head of %s: %m", node);
b9df3536 3384
48a09a8f 3385 reencrypt_params.flags &= ~CRYPT_REENCRYPT_INITIALIZE_ONLY;
b9df3536 3386
48a09a8f
DDM
3387 r = sym_crypt_reencrypt_init_by_passphrase(
3388 cd,
3389 NULL,
3390 passphrase,
3391 passphrase_size,
3392 CRYPT_ANY_SLOT,
3393 0,
3394 NULL,
3395 NULL,
3396 &reencrypt_params);
3397 if (r < 0)
3398 return log_error_errno(r, "Failed to load reencryption context: %m");
b9df3536 3399
48a09a8f 3400 r = sym_crypt_reencrypt(cd, NULL);
b9df3536 3401 if (r < 0)
48a09a8f
DDM
3402 return log_error_errno(r, "Failed to encrypt %s: %m", node);
3403
3404 log_info("Successfully encrypted future partition %" PRIu64 ".", p->partno);
b9df3536 3405
3dd8ae5c 3406 return 0;
48a09a8f
DDM
3407#else
3408 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
3409 "libcryptsetup is not supported or is missing required symbols, cannot encrypt: %m");
3dd8ae5c 3410#endif
b9df3536
LP
3411}
3412
2b392d86
DDM
3413static int partition_format_verity_hash(
3414 Context *context,
3415 Partition *p,
3416 const char *data_node) {
3417
3418#if HAVE_LIBCRYPTSETUP
3419 Partition *dp;
a64769d6 3420 _cleanup_(partition_target_freep) PartitionTarget *t = NULL;
2b392d86
DDM
3421 _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
3422 _cleanup_free_ uint8_t *rh = NULL;
3423 size_t rhs;
a64769d6 3424 int r;
2b392d86
DDM
3425
3426 assert(context);
3427 assert(p);
3428 assert(data_node);
3429
3430 if (p->dropped)
3431 return 0;
3432
3433 if (PARTITION_EXISTS(p)) /* Never format existing partitions */
3434 return 0;
3435
3436 if (p->verity != VERITY_HASH)
3437 return 0;
3438
3439 if (partition_skip(p))
3440 return 0;
3441
3442 assert_se(dp = p->siblings[VERITY_DATA]);
3443 assert(!dp->dropped);
3444
2b392d86
DDM
3445 r = dlopen_cryptsetup();
3446 if (r < 0)
3447 return log_error_errno(r, "libcryptsetup not found, cannot setup verity: %m");
3448
a64769d6 3449 r = partition_target_prepare(context, p, p->new_size, /*need_path=*/ true, &t);
2b392d86 3450 if (r < 0)
a64769d6 3451 return r;
2b392d86 3452
a64769d6 3453 r = sym_crypt_init(&cd, partition_target_path(t));
2b392d86
DDM
3454 if (r < 0)
3455 return log_error_errno(r, "Failed to allocate libcryptsetup context: %m");
3456
3457 r = sym_crypt_format(
3458 cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0,
3459 &(struct crypt_params_verity){
3460 .data_device = data_node,
3461 .flags = CRYPT_VERITY_CREATE_HASH,
3462 .hash_name = "sha256",
3463 .hash_type = 1,
3464 .data_block_size = context->sector_size,
3465 .hash_block_size = context->sector_size,
3466 .salt_size = 32,
3467 });
3468 if (r < 0)
3469 return log_error_errno(r, "Failed to setup verity hash data: %m");
3470
a64769d6
DDM
3471 r = partition_target_sync(context, p, t);
3472 if (r < 0)
3473 return r;
3474
2b392d86
DDM
3475 r = sym_crypt_get_volume_key_size(cd);
3476 if (r < 0)
3477 return log_error_errno(r, "Failed to determine verity root hash size: %m");
3478 rhs = (size_t) r;
3479
3480 rh = malloc(rhs);
3481 if (!rh)
3482 return log_oom();
3483
3484 r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, (char *) rh, &rhs, NULL, 0);
3485 if (r < 0)
3486 return log_error_errno(r, "Failed to get verity root hash: %m");
3487
3488 assert(rhs >= sizeof(sd_id128_t) * 2);
3489
3490 if (!dp->new_uuid_is_set) {
3491 memcpy_safe(dp->new_uuid.bytes, rh, sizeof(sd_id128_t));
3492 dp->new_uuid_is_set = true;
3493 }
3494
3495 if (!p->new_uuid_is_set) {
3496 memcpy_safe(p->new_uuid.bytes, rh + rhs - sizeof(sd_id128_t), sizeof(sd_id128_t));
3497 p->new_uuid_is_set = true;
3498 }
3499
3500 p->roothash = TAKE_PTR(rh);
3501 p->roothash_size = rhs;
3502
3503 return 0;
3504#else
3505 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "libcryptsetup is not supported, cannot setup verity hashes: %m");
3506#endif
3507}
3508
4ecd39c5
DDM
3509static int sign_verity_roothash(
3510 const uint8_t *roothash,
3511 size_t roothash_size,
3512 uint8_t **ret_signature,
3513 size_t *ret_signature_size) {
3514
3515#if HAVE_OPENSSL
3516 _cleanup_(BIO_freep) BIO *rb = NULL;
3517 _cleanup_(PKCS7_freep) PKCS7 *p7 = NULL;
3518 _cleanup_free_ char *hex = NULL;
3519 _cleanup_free_ uint8_t *sig = NULL;
3520 int sigsz;
3521
3522 assert(roothash);
3523 assert(roothash_size > 0);
3524 assert(ret_signature);
3525 assert(ret_signature_size);
3526
3527 hex = hexmem(roothash, roothash_size);
3528 if (!hex)
3529 return log_oom();
3530
3531 rb = BIO_new_mem_buf(hex, -1);
3532 if (!rb)
3533 return log_oom();
3534
3535 p7 = PKCS7_sign(arg_certificate, arg_private_key, NULL, rb, PKCS7_DETACHED|PKCS7_NOATTR|PKCS7_BINARY);
3536 if (!p7)
3537 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to calculate PKCS7 signature: %s",
3538 ERR_error_string(ERR_get_error(), NULL));
3539
3540 sigsz = i2d_PKCS7(p7, &sig);
3541 if (sigsz < 0)
3542 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert PKCS7 signature to DER: %s",
3543 ERR_error_string(ERR_get_error(), NULL));
3544
3545 *ret_signature = TAKE_PTR(sig);
3546 *ret_signature_size = sigsz;
3547
3548 return 0;
3549#else
3550 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot setup verity signature: %m");
3551#endif
3552}
3553
3554static int partition_format_verity_sig(Context *context, Partition *p) {
3555 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
3556 _cleanup_free_ uint8_t *sig = NULL;
3557 _cleanup_free_ char *text = NULL;
3558 Partition *hp;
3559 uint8_t fp[X509_FINGERPRINT_SIZE];
3560 size_t sigsz = 0, padsz; /* avoid false maybe-uninitialized warning */
3561 int whole_fd, r;
3562
3563 assert(p->verity == VERITY_SIG);
3564
3565 if (p->dropped)
3566 return 0;
3567
3568 if (PARTITION_EXISTS(p))
3569 return 0;
3570
3571 if (partition_skip(p))
3572 return 0;
3573
3574 assert_se(hp = p->siblings[VERITY_HASH]);
3575 assert(!hp->dropped);
3576
3577 assert(arg_certificate);
3578
3579 assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
3580
3581 r = sign_verity_roothash(hp->roothash, hp->roothash_size, &sig, &sigsz);
3582 if (r < 0)
3583 return r;
3584
3585 r = x509_fingerprint(arg_certificate, fp);
3586 if (r < 0)
3587 return log_error_errno(r, "Unable to calculate X509 certificate fingerprint: %m");
3588
3589 r = json_build(&v,
3590 JSON_BUILD_OBJECT(
3591 JSON_BUILD_PAIR("rootHash", JSON_BUILD_HEX(hp->roothash, hp->roothash_size)),
3592 JSON_BUILD_PAIR(
3593 "certificateFingerprint",
3594 JSON_BUILD_HEX(fp, sizeof(fp))
3595 ),
3596 JSON_BUILD_PAIR("signature", JSON_BUILD_BASE64(sig, sigsz))
3597 )
3598 );
3599 if (r < 0)
3600 return log_error_errno(r, "Failed to build JSON object: %m");
3601
3602 r = json_variant_format(v, 0, &text);
3603 if (r < 0)
3604 return log_error_errno(r, "Failed to format JSON object: %m");
3605
3606 padsz = round_up_size(strlen(text), 4096);
3607 assert_se(padsz <= p->new_size);
3608
3609 r = strgrowpad0(&text, padsz);
3610 if (r < 0)
3611 return log_error_errno(r, "Failed to pad string to %s", FORMAT_BYTES(padsz));
3612
3613 if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1)
3614 return log_error_errno(errno, "Failed to seek to partition offset: %m");
3615
3616 r = loop_write(whole_fd, text, padsz, /*do_poll=*/ false);
3617 if (r < 0)
3618 return log_error_errno(r, "Failed to write verity signature to partition: %m");
3619
3620 if (fsync(whole_fd) < 0)
3621 return log_error_errno(errno, "Failed to synchronize verity signature JSON: %m");
3622
3623 return 0;
3624}
3625
757bc2e4 3626static int context_copy_blocks(Context *context) {
a64769d6 3627 int r;
757bc2e4
LP
3628
3629 assert(context);
3630
3631 /* Copy in file systems on the block level */
3632
3633 LIST_FOREACH(partitions, p, context->partitions) {
a64769d6 3634 _cleanup_(partition_target_freep) PartitionTarget *t = NULL;
757bc2e4
LP
3635
3636 if (p->copy_blocks_fd < 0)
3637 continue;
3638
3639 if (p->dropped)
3640 continue;
3641
3642 if (PARTITION_EXISTS(p)) /* Never copy over existing partitions */
3643 continue;
3644
81d1098b
DDM
3645 if (partition_skip(p))
3646 continue;
3647
757bc2e4
LP
3648 assert(p->new_size != UINT64_MAX);
3649 assert(p->copy_blocks_size != UINT64_MAX);
48a09a8f 3650 assert(p->new_size >= p->copy_blocks_size + (p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0));
757bc2e4 3651
a64769d6
DDM
3652 r = partition_target_prepare(context, p, p->new_size,
3653 /*need_path=*/ p->encrypt != ENCRYPT_OFF || p->siblings[VERITY_HASH],
3654 &t);
3655 if (r < 0)
3656 return r;
757bc2e4 3657
2b59bf51
ZJS
3658 log_info("Copying in '%s' (%s) on block level into future partition %" PRIu64 ".",
3659 p->copy_blocks_path, FORMAT_BYTES(p->copy_blocks_size), p->partno);
757bc2e4 3660
a64769d6 3661 r = copy_bytes(p->copy_blocks_fd, partition_target_fd(t), p->copy_blocks_size, COPY_REFLINK);
757bc2e4
LP
3662 if (r < 0)
3663 return log_error_errno(r, "Failed to copy in data from '%s': %m", p->copy_blocks_path);
3664
889914ef 3665 if (p->encrypt != ENCRYPT_OFF) {
a64769d6 3666 r = partition_encrypt(context, p, partition_target_path(t));
b9df3536
LP
3667 if (r < 0)
3668 return r;
b9df3536
LP
3669 }
3670
a64769d6
DDM
3671 r = partition_target_sync(context, p, t);
3672 if (r < 0)
3673 return r;
48a09a8f 3674
757bc2e4 3675 log_info("Copying in of '%s' on block level completed.", p->copy_blocks_path);
2b392d86
DDM
3676
3677 if (p->siblings[VERITY_HASH]) {
a64769d6
DDM
3678 r = partition_format_verity_hash(context, p->siblings[VERITY_HASH],
3679 partition_target_path(t));
2b392d86
DDM
3680 if (r < 0)
3681 return r;
3682 }
4ecd39c5
DDM
3683
3684 if (p->siblings[VERITY_SIG]) {
3685 r = partition_format_verity_sig(context, p->siblings[VERITY_SIG]);
3686 if (r < 0)
3687 return r;
3688 }
757bc2e4
LP
3689 }
3690
3691 return 0;
3692}
3693
d2ac7698
DDM
3694static int do_copy_files(
3695 Partition *p,
3696 const char *root,
3697 uid_t override_uid,
3698 gid_t override_gid,
3699 const Set *denylist) {
e59678b2 3700
8a794850
LP
3701 int r;
3702
3703 assert(p);
92cd7e7c 3704 assert(root);
8a794850
LP
3705
3706 STRV_FOREACH_PAIR(source, target, p->copy_files) {
3707 _cleanup_close_ int sfd = -1, pfd = -1, tfd = -1;
8a794850 3708
fd1ca01a 3709 sfd = chase_symlinks_and_open(*source, arg_root, CHASE_PREFIX_ROOT, O_CLOEXEC|O_NOCTTY, NULL);
8a794850
LP
3710 if (sfd < 0)
3711 return log_error_errno(sfd, "Failed to open source file '%s%s': %m", strempty(arg_root), *source);
3712
3713 r = fd_verify_regular(sfd);
3714 if (r < 0) {
3715 if (r != -EISDIR)
3716 return log_error_errno(r, "Failed to check type of source file '%s': %m", *source);
3717
3718 /* We are looking at a directory */
fd1ca01a 3719 tfd = chase_symlinks_and_open(*target, root, CHASE_PREFIX_ROOT, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL);
8a794850 3720 if (tfd < 0) {
f21a3a82
LP
3721 _cleanup_free_ char *dn = NULL, *fn = NULL;
3722
8a794850
LP
3723 if (tfd != -ENOENT)
3724 return log_error_errno(tfd, "Failed to open target directory '%s': %m", *target);
3725
f21a3a82
LP
3726 r = path_extract_filename(*target, &fn);
3727 if (r < 0)
3728 return log_error_errno(r, "Failed to extract filename from '%s': %m", *target);
3729
3730 r = path_extract_directory(*target, &dn);
3731 if (r < 0)
3732 return log_error_errno(r, "Failed to extract directory from '%s': %m", *target);
3733
92cd7e7c 3734 r = mkdir_p_root(root, dn, UID_INVALID, GID_INVALID, 0755);
8a794850
LP
3735 if (r < 0)
3736 return log_error_errno(r, "Failed to create parent directory '%s': %m", dn);
3737
fd1ca01a 3738 pfd = chase_symlinks_and_open(dn, root, CHASE_PREFIX_ROOT, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL);
8a794850
LP
3739 if (pfd < 0)
3740 return log_error_errno(pfd, "Failed to open parent directory of target: %m");
3741
8a050905
DDM
3742 /* Make sure everything is owned by the user running repart so that
3743 * make_filesystem() can map the user running repart to "root" in a user
3744 * namespace to have the files owned by root in the final image. */
3745
652d9040
LP
3746 r = copy_tree_at(
3747 sfd, ".",
6020d00d 3748 pfd, fn,
d2ac7698 3749 override_uid, override_gid,
81427d0f 3750 COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS,
c0fad2d9 3751 denylist);
8a794850 3752 } else
652d9040
LP
3753 r = copy_tree_at(
3754 sfd, ".",
3755 tfd, ".",
d2ac7698 3756 override_uid, override_gid,
81427d0f 3757 COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS,
c0fad2d9 3758 denylist);
8a794850 3759 if (r < 0)
cf2ed23c
DDM
3760 return log_error_errno(r, "Failed to copy '%s%s' to '%s%s': %m",
3761 strempty(arg_root), *source, strempty(root), *target);
8a794850 3762 } else {
f21a3a82
LP
3763 _cleanup_free_ char *dn = NULL, *fn = NULL;
3764
8a794850
LP
3765 /* We are looking at a regular file */
3766
f21a3a82
LP
3767 r = path_extract_filename(*target, &fn);
3768 if (r == -EADDRNOTAVAIL || r == O_DIRECTORY)
3769 return log_error_errno(SYNTHETIC_ERRNO(EISDIR),
3770 "Target path '%s' refers to a directory, but source path '%s' refers to regular file, can't copy.", *target, *source);
3771 if (r < 0)
3772 return log_error_errno(r, "Failed to extract filename from '%s': %m", *target);
3773
3774 r = path_extract_directory(*target, &dn);
3775 if (r < 0)
3776 return log_error_errno(r, "Failed to extract directory from '%s': %m", *target);
3777
92cd7e7c 3778 r = mkdir_p_root(root, dn, UID_INVALID, GID_INVALID, 0755);
8a794850
LP
3779 if (r < 0)
3780 return log_error_errno(r, "Failed to create parent directory: %m");
3781
fd1ca01a 3782 pfd = chase_symlinks_and_open(dn, root, CHASE_PREFIX_ROOT, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL);
8a794850 3783 if (pfd < 0)
a0ff9971 3784 return log_error_errno(pfd, "Failed to open parent directory of target: %m");
8a794850 3785
e2819067 3786 tfd = openat(pfd, fn, O_CREAT|O_EXCL|O_WRONLY|O_CLOEXEC, 0700);
8a794850
LP
3787 if (tfd < 0)
3788 return log_error_errno(errno, "Failed to create target file '%s': %m", *target);
3789
81427d0f 3790 r = copy_bytes(sfd, tfd, UINT64_MAX, COPY_REFLINK|COPY_HOLES|COPY_SIGINT);
8a794850 3791 if (r < 0)
554a2b64 3792 return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target);
8a794850 3793
d2ac7698
DDM
3794 if (fchown(tfd, override_uid, override_gid) < 0)
3795 return log_error_errno(r, "Failed to change ownership of %s", *target);
3796
23e026de 3797 (void) copy_xattr(sfd, tfd, COPY_ALL_XATTRS);
8a794850
LP
3798 (void) copy_access(sfd, tfd);
3799 (void) copy_times(sfd, tfd, 0);
3800 }
3801 }
3802
3803 return 0;
3804}
3805
d2ac7698 3806static int do_make_directories(Partition *p, uid_t override_uid, gid_t override_gid, const char *root) {
d83d8048
LP
3807 int r;
3808
3809 assert(p);
92cd7e7c 3810 assert(root);
d83d8048
LP
3811
3812 STRV_FOREACH(d, p->make_directories) {
3813
d2ac7698 3814 r = mkdir_p_root(root, *d, override_uid, override_gid, 0755);
d83d8048
LP
3815 if (r < 0)
3816 return log_error_errno(r, "Failed to create directory '%s' in file system: %m", *d);
3817 }
3818
3819 return 0;
3820}
3821
d3201eb4
DDM
3822static bool partition_needs_populate(Partition *p) {
3823 assert(p);
3824 return !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories);
3825}
3826
e59678b2 3827static int partition_populate_directory(Partition *p, const Set *denylist, char **ret) {
95bfd3cd 3828 _cleanup_(rm_rf_physical_and_freep) char *root = NULL;
488bb758 3829 _cleanup_close_ int rfd = -1;
95bfd3cd
DDM
3830 int r;
3831
e59678b2 3832 assert(ret);
95bfd3cd 3833
488bb758
DDM
3834 rfd = mkdtemp_open("/var/tmp/repart-XXXXXX", 0, &root);
3835 if (rfd < 0)
3836 return log_error_errno(rfd, "Failed to create temporary directory: %m");
95bfd3cd 3837
488bb758 3838 if (fchmod(rfd, 0755) < 0)
0b34f351
DDM
3839 return log_error_errno(errno, "Failed to change mode of temporary directory: %m");
3840
d2ac7698 3841 r = do_copy_files(p, root, getuid(), getgid(), denylist);
95bfd3cd
DDM
3842 if (r < 0)
3843 return r;
3844
d2ac7698 3845 r = do_make_directories(p, getuid(), getgid(), root);
95bfd3cd
DDM
3846 if (r < 0)
3847 return r;
3848
e59678b2 3849 *ret = TAKE_PTR(root);
95bfd3cd
DDM
3850 return 0;
3851}
3852
c0fad2d9 3853static int partition_populate_filesystem(Partition *p, const char *node, const Set *denylist) {
8a794850
LP
3854 int r;
3855
3856 assert(p);
3857 assert(node);
3858
a7f1f7d8 3859 log_info("Populating %s filesystem with files.", p->format);
8a794850
LP
3860
3861 /* We copy in a child process, since we have to mount the fs for that, and we don't want that fs to
3862 * appear in the host namespace. Hence we fork a child that has its own file system namespace and
3863 * detached mount propagation. */
3864
3865 r = safe_fork("(sd-copy)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, NULL);
3866 if (r < 0)
3867 return r;
3868 if (r == 0) {
3869 static const char fs[] = "/run/systemd/mount-root";
3870 /* This is a child process with its own mount namespace and propagation to host turned off */
3871
3872 r = mkdir_p(fs, 0700);
3873 if (r < 0) {
3874 log_error_errno(r, "Failed to create mount point: %m");
3875 _exit(EXIT_FAILURE);
3876 }
3877
511a8cfe 3878 if (mount_nofollow_verbose(LOG_ERR, node, fs, p->format, MS_NOATIME|MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL) < 0)
8a794850
LP
3879 _exit(EXIT_FAILURE);
3880
d2ac7698 3881 if (do_copy_files(p, fs, 0, 0, denylist) < 0)
8a794850
LP
3882 _exit(EXIT_FAILURE);
3883
d2ac7698 3884 if (do_make_directories(p, 0, 0, fs) < 0)
d83d8048
LP
3885 _exit(EXIT_FAILURE);
3886
8a794850
LP
3887 r = syncfs_path(AT_FDCWD, fs);
3888 if (r < 0) {
3889 log_error_errno(r, "Failed to synchronize written files: %m");
3890 _exit(EXIT_FAILURE);
3891 }
3892
3893 _exit(EXIT_SUCCESS);
3894 }
3895
a7f1f7d8 3896 log_info("Successfully populated %s filesystem with files.", p->format);
8a794850
LP
3897 return 0;
3898}
3899
c0fad2d9
DDM
3900static int make_copy_files_denylist(Context *context, Set **ret) {
3901 _cleanup_set_free_ Set *denylist = NULL;
3902 int r;
3903
3904 assert(context);
3905 assert(ret);
3906
3907 LIST_FOREACH(partitions, p, context->partitions) {
22e932f4 3908 const char *sources = gpt_partition_type_mountpoint_nulstr(p->type);
c0fad2d9
DDM
3909 if (!sources)
3910 continue;
3911
3912 NULSTR_FOREACH(s, sources) {
3913 _cleanup_free_ char *d = NULL;
3914 struct stat st;
3915
3916 r = chase_symlinks_and_stat(s, arg_root, CHASE_PREFIX_ROOT, NULL, &st, NULL);
3917 if (r == -ENOENT)
3918 continue;
3919 if (r < 0)
3920 return log_error_errno(r, "Failed to stat source file '%s%s': %m",
3921 strempty(arg_root), s);
3922
3923 if (set_contains(denylist, &st))
3924 continue;
3925
3926 d = memdup(&st, sizeof(st));
3927 if (!d)
3928 return log_oom();
3929 if (set_ensure_put(&denylist, &inode_hash_ops, d) < 0)
3930 return log_oom();
3931
3932 TAKE_PTR(d);
3933 }
3934 }
3935
3936 *ret = TAKE_PTR(denylist);
3937 return 0;
3938}
3939
53171c04 3940static int context_mkfs(Context *context) {
c0fad2d9 3941 _cleanup_set_free_ Set *denylist = NULL;
a64769d6 3942 int r;
53171c04
LP
3943
3944 assert(context);
3945
3946 /* Make a file system */
3947
c0fad2d9
DDM
3948 r = make_copy_files_denylist(context, &denylist);
3949 if (r < 0)
3950 return r;
3951
53171c04 3952 LIST_FOREACH(partitions, p, context->partitions) {
e59678b2 3953 _cleanup_(rm_rf_physical_and_freep) char *root = NULL;
a64769d6 3954 _cleanup_(partition_target_freep) PartitionTarget *t = NULL;
53171c04
LP
3955
3956 if (p->dropped)
3957 continue;
3958
3959 if (PARTITION_EXISTS(p)) /* Never format existing partitions */
3960 continue;
3961
3962 if (!p->format)
3963 continue;
3964
c4a87b76
DDM
3965 /* Minimized partitions will use the copy blocks logic so let's make sure to skip those here. */
3966 if (p->copy_blocks_fd >= 0)
3967 continue;
3968
81d1098b
DDM
3969 if (partition_skip(p))
3970 continue;
3971
53171c04
LP
3972 assert(p->offset != UINT64_MAX);
3973 assert(p->new_size != UINT64_MAX);
48a09a8f 3974 assert(p->new_size >= (p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0));
53171c04 3975
a64769d6
DDM
3976 /* If we're doing encryption, we make sure we keep free space at the end which is required
3977 * for cryptsetup's offline encryption. */
3978 r = partition_target_prepare(context, p,
3979 p->new_size - (p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0),
3980 /*need_path=*/ true,
3981 &t);
53171c04 3982 if (r < 0)
a64769d6 3983 return r;
53171c04 3984
53171c04
LP
3985 log_info("Formatting future partition %" PRIu64 ".", p->partno);
3986
d3201eb4
DDM
3987 /* If we're not writing to a loop device or if we're populating a read-only filesystem, we
3988 * have to populate using the filesystem's mkfs's --root (or equivalent) option. To do that,
3989 * we need to set up the final directory tree beforehand. */
3990
3991 if (partition_needs_populate(p) && (!t->loop || fstype_is_ro(p->format))) {
3992 if (!mkfs_supports_root_option(p->format))
3993 return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
3994 "Loop device access is required to populate %s filesystems.",
3995 p->format);
95bfd3cd 3996
e59678b2 3997 r = partition_populate_directory(p, denylist, &root);
143c3c08
DDM
3998 if (r < 0)
3999 return r;
4000 }
95bfd3cd 4001
a64769d6
DDM
4002 r = make_filesystem(partition_target_path(t), p->format, strempty(p->new_label), root,
4003 p->fs_uuid, arg_discard);
48a09a8f 4004 if (r < 0)
53171c04
LP
4005 return r;
4006
4007 log_info("Successfully formatted future partition %" PRIu64 ".", p->partno);
4008
d3201eb4
DDM
4009 /* If we're writing to a loop device, we can now mount the empty filesystem and populate it. */
4010 if (partition_needs_populate(p) && !root) {
4011 assert(t->loop);
4012
4013 r = partition_populate_filesystem(p, t->loop->node, denylist);
48a09a8f 4014 if (r < 0)
143c3c08 4015 return r;
b9df3536
LP
4016 }
4017
889914ef 4018 if (p->encrypt != ENCRYPT_OFF) {
a64769d6 4019 r = partition_target_grow(t, p->new_size);
b9df3536 4020 if (r < 0)
a64769d6 4021 return r;
b9df3536 4022
a64769d6 4023 r = partition_encrypt(context, p, partition_target_path(t));
48a09a8f
DDM
4024 if (r < 0)
4025 return log_error_errno(r, "Failed to encrypt device: %m");
b9df3536 4026 }
8a794850 4027
48a09a8f
DDM
4028 /* Note that we always sync explicitly here, since mkfs.fat doesn't do that on its own, and
4029 * if we don't sync before detaching a block device the in-flight sectors possibly won't hit
4030 * the disk. */
4031
a64769d6 4032 r = partition_target_sync(context, p, t);
53171c04 4033 if (r < 0)
a64769d6 4034 return r;
b5b7879a 4035
2b392d86 4036 if (p->siblings[VERITY_HASH]) {
a64769d6
DDM
4037 r = partition_format_verity_hash(context, p->siblings[VERITY_HASH],
4038 partition_target_path(t));
2b392d86
DDM
4039 if (r < 0)
4040 return r;
b5b7879a 4041 }
4ecd39c5
DDM
4042
4043 if (p->siblings[VERITY_SIG]) {
4044 r = partition_format_verity_sig(context, p->siblings[VERITY_SIG]);
4045 if (r < 0)
4046 return r;
4047 }
b5b7879a
DDM
4048 }
4049
4050 return 0;
4051}
4052
b456191d
DDM
4053static int parse_x509_certificate(const char *certificate, size_t certificate_size, X509 **ret) {
4054#if HAVE_OPENSSL
4055 _cleanup_(X509_freep) X509 *cert = NULL;
4056 _cleanup_(BIO_freep) BIO *cb = NULL;
4057
4058 assert(certificate);
4059 assert(certificate_size > 0);
4060 assert(ret);
4061
4062 cb = BIO_new_mem_buf(certificate, certificate_size);
4063 if (!cb)
4064 return log_oom();
4065
4066 cert = PEM_read_bio_X509(cb, NULL, NULL, NULL);
4067 if (!cert)
4068 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse X.509 certificate: %s",
4069 ERR_error_string(ERR_get_error(), NULL));
4070
4071 if (ret)
4072 *ret = TAKE_PTR(cert);
4073
4074 return 0;
4075#else
4076 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot parse X509 certificate.");
4077#endif
4078}
4079
4080static int parse_private_key(const char *key, size_t key_size, EVP_PKEY **ret) {
4081#if HAVE_OPENSSL
4082 _cleanup_(BIO_freep) BIO *kb = NULL;
4083 _cleanup_(EVP_PKEY_freep) EVP_PKEY *pk = NULL;
4084
4085 assert(key);
4086 assert(key_size > 0);
4087 assert(ret);
4088
4089 kb = BIO_new_mem_buf(key, key_size);
4090 if (!kb)
4091 return log_oom();
4092
4093 pk = PEM_read_bio_PrivateKey(kb, NULL, NULL, NULL);
4094 if (!pk)
4095 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse PEM private key: %s",
4096 ERR_error_string(ERR_get_error(), NULL));
4097
4098 if (ret)
4099 *ret = TAKE_PTR(pk);
4100
4101 return 0;
4102#else
4103 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot parse private key.");
4104#endif
4105}
4106
e594a3b1
LP
4107static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *ret) {
4108 struct {
4109 sd_id128_t type_uuid;
4110 uint64_t counter;
695cfd53 4111 } _packed_ plaintext = {};
e594a3b1 4112 union {
ade99252 4113 uint8_t md[SHA256_DIGEST_SIZE];
e594a3b1
LP
4114 sd_id128_t id;
4115 } result;
4116
4117 uint64_t k = 0;
e594a3b1
LP
4118 int r;
4119
4120 assert(context);
4121 assert(p);
4122 assert(ret);
4123
4124 /* Calculate a good UUID for the indicated partition. We want a certain degree of reproducibility,
4125 * hence we won't generate the UUIDs randomly. Instead we use a cryptographic hash (precisely:
4126 * HMAC-SHA256) to derive them from a single seed. The seed is generally the machine ID of the
4127 * installation we are processing, but if random behaviour is desired can be random, too. We use the
4128 * seed value as key for the HMAC (since the machine ID is something we generally don't want to leak)
4129 * and the partition type as plaintext. The partition type is suffixed with a counter (only for the
4130 * second and later partition of the same type) if we have more than one partition of the same
4131 * time. Or in other words:
4132 *
4133 * With:
4134 * SEED := /etc/machine-id
4135 *
4136 * If first partition instance of type TYPE_UUID:
4137 * PARTITION_UUID := HMAC-SHA256(SEED, TYPE_UUID)
4138 *
4139 * For all later partition instances of type TYPE_UUID with INSTANCE being the LE64 encoded instance number:
4140 * PARTITION_UUID := HMAC-SHA256(SEED, TYPE_UUID || INSTANCE)
4141 */
4142
4143 LIST_FOREACH(partitions, q, context->partitions) {
4144 if (p == q)
4145 break;
4146
22e932f4 4147 if (!sd_id128_equal(p->type.uuid, q->type.uuid))
e594a3b1
LP
4148 continue;
4149
4150 k++;
4151 }
4152
22e932f4 4153 plaintext.type_uuid = p->type.uuid;
e594a3b1
LP
4154 plaintext.counter = htole64(k);
4155
ade99252
KK
4156 hmac_sha256(context->seed.bytes, sizeof(context->seed.bytes),
4157 &plaintext,
4158 k == 0 ? sizeof(sd_id128_t) : sizeof(plaintext),
4159 result.md);
e594a3b1
LP
4160
4161 /* Take the first half, mark it as v4 UUID */
4162 assert_cc(sizeof(result.md) == sizeof(result.id) * 2);
4163 result.id = id128_make_v4_uuid(result.id);
4164
4165 /* Ensure this partition UUID is actually unique, and there's no remaining partition from an earlier run? */
4166 LIST_FOREACH(partitions, q, context->partitions) {
4167 if (p == q)
4168 continue;
4169
580f48cc 4170 if (sd_id128_in_set(result.id, q->current_uuid, q->new_uuid)) {
da1af43d 4171 log_warning("Partition UUID calculated from seed for partition %" PRIu64 " already used, reverting to randomized UUID.", p->partno);
e594a3b1
LP
4172
4173 r = sd_id128_randomize(&result.id);
4174 if (r < 0)
4175 return log_error_errno(r, "Failed to generate randomized UUID: %m");
4176
4177 break;
4178 }
4179 }
4180
4181 *ret = result.id;
4182 return 0;
4183}
4184
4185static int partition_acquire_label(Context *context, Partition *p, char **ret) {
4186 _cleanup_free_ char *label = NULL;
4187 const char *prefix;
4188 unsigned k = 1;
4189
4190 assert(context);
4191 assert(p);
4192 assert(ret);
4193
22e932f4 4194 prefix = gpt_partition_type_uuid_to_string(p->type.uuid);
e594a3b1
LP
4195 if (!prefix)
4196 prefix = "linux";
4197
4198 for (;;) {
4199 const char *ll = label ?: prefix;
4200 bool retry = false;
e594a3b1
LP
4201
4202 LIST_FOREACH(partitions, q, context->partitions) {
4203 if (p == q)
4204 break;
4205
4206 if (streq_ptr(ll, q->current_label) ||
4207 streq_ptr(ll, q->new_label)) {
4208 retry = true;
4209 break;
4210 }
4211 }
4212
4213 if (!retry)
4214 break;
4215
4216 label = mfree(label);
e594a3b1
LP
4217 if (asprintf(&label, "%s-%u", prefix, ++k) < 0)
4218 return log_oom();
4219 }
4220
4221 if (!label) {
4222 label = strdup(prefix);
4223 if (!label)
4224 return log_oom();
4225 }
4226
4227 *ret = TAKE_PTR(label);
4228 return 0;
4229}
4230
4231static int context_acquire_partition_uuids_and_labels(Context *context) {
e594a3b1
LP
4232 int r;
4233
4234 assert(context);
4235
4236 LIST_FOREACH(partitions, p, context->partitions) {
e594a3b1
LP
4237 /* Never touch foreign partitions */
4238 if (PARTITION_IS_FOREIGN(p)) {
4239 p->new_uuid = p->current_uuid;
4240
4241 if (p->current_label) {
78eee6ce
LP
4242 r = free_and_strdup_warn(&p->new_label, strempty(p->current_label));
4243 if (r < 0)
4244 return r;
e594a3b1
LP
4245 }
4246
4247 continue;
4248 }
4249
4250 if (!sd_id128_is_null(p->current_uuid))
4251 p->new_uuid = p->current_uuid; /* Never change initialized UUIDs */
b456191d 4252 else if (!p->new_uuid_is_set && !IN_SET(p->verity, VERITY_DATA, VERITY_HASH)) {
12963533 4253 /* Not explicitly set by user! */
e594a3b1
LP
4254 r = partition_acquire_uuid(context, p, &p->new_uuid);
4255 if (r < 0)
4256 return r;
11749b61
DDM
4257
4258 p->new_uuid_is_set = true;
e594a3b1
LP
4259 }
4260
8bbbdfd7
DDM
4261 /* Calculate the UUID for the file system as HMAC-SHA256 of the string "file-system-uuid",
4262 * keyed off the partition UUID. */
4263 r = derive_uuid(p->new_uuid, "file-system-uuid", &p->fs_uuid);
4264 if (r < 0)
4265 return r;
4266
e594a3b1 4267 if (!isempty(p->current_label)) {
78eee6ce
LP
4268 /* never change initialized labels */
4269 r = free_and_strdup_warn(&p->new_label, p->current_label);
4270 if (r < 0)
4271 return r;
12963533
TH
4272 } else if (!p->new_label) {
4273 /* Not explicitly set by user! */
4274
e594a3b1
LP
4275 r = partition_acquire_label(context, p, &p->new_label);
4276 if (r < 0)
4277 return r;
4278 }
4279 }
4280
4281 return 0;
4282}
4283
e73309c5
LP
4284static int set_gpt_flags(struct fdisk_partition *q, uint64_t flags) {
4285 _cleanup_free_ char *a = NULL;
4286
4287 for (unsigned i = 0; i < sizeof(flags) * 8; i++) {
4288 uint64_t bit = UINT64_C(1) << i;
4289 char buf[DECIMAL_STR_MAX(unsigned)+1];
4290
4291 if (!FLAGS_SET(flags, bit))
4292 continue;
4293
4294 xsprintf(buf, "%u", i);
4295 if (!strextend_with_separator(&a, ",", buf))
4296 return -ENOMEM;
4297 }
4298
4299 return fdisk_partition_set_attrs(q, a);
4300}
4301
1c41c1dc
LP
4302static uint64_t partition_merge_flags(Partition *p) {
4303 uint64_t f;
4304
4305 assert(p);
4306
4307 f = p->gpt_flags;
4308
ff0771bf 4309 if (p->no_auto >= 0) {
22e932f4 4310 if (gpt_partition_type_knows_no_auto(p->type))
92e72028 4311 SET_FLAG(f, SD_GPT_FLAG_NO_AUTO, p->no_auto);
ff0771bf 4312 else {
b7416360 4313 char buffer[SD_ID128_UUID_STRING_MAX];
ff0771bf
LP
4314 log_warning("Configured NoAuto=%s for partition type '%s' that doesn't support it, ignoring.",
4315 yes_no(p->no_auto),
22e932f4 4316 gpt_partition_type_uuid_to_string_harder(p->type.uuid, buffer));
ff0771bf
LP
4317 }
4318 }
4319
1c41c1dc 4320 if (p->read_only >= 0) {
22e932f4 4321 if (gpt_partition_type_knows_read_only(p->type))
92e72028 4322 SET_FLAG(f, SD_GPT_FLAG_READ_ONLY, p->read_only);
1c41c1dc 4323 else {
b7416360 4324 char buffer[SD_ID128_UUID_STRING_MAX];
1c41c1dc
LP
4325 log_warning("Configured ReadOnly=%s for partition type '%s' that doesn't support it, ignoring.",
4326 yes_no(p->read_only),
22e932f4 4327 gpt_partition_type_uuid_to_string_harder(p->type.uuid, buffer));
1c41c1dc
LP
4328 }
4329 }
4330
4331 if (p->growfs >= 0) {
22e932f4 4332 if (gpt_partition_type_knows_growfs(p->type))
92e72028 4333 SET_FLAG(f, SD_GPT_FLAG_GROWFS, p->growfs);
1c41c1dc 4334 else {
b7416360 4335 char buffer[SD_ID128_UUID_STRING_MAX];
1c41c1dc
LP
4336 log_warning("Configured GrowFileSystem=%s for partition type '%s' that doesn't support it, ignoring.",
4337 yes_no(p->growfs),
22e932f4 4338 gpt_partition_type_uuid_to_string_harder(p->type.uuid, buffer));
1c41c1dc
LP
4339 }
4340 }
4341
4342 return f;
4343}
4344
f28d4f42 4345static int context_mangle_partitions(Context *context) {
f28d4f42 4346 int r;
e594a3b1
LP
4347
4348 assert(context);
4349
e594a3b1
LP
4350 LIST_FOREACH(partitions, p, context->partitions) {
4351 if (p->dropped)
4352 continue;
4353
81d1098b
DDM
4354 if (partition_skip(p))
4355 continue;
4356
e594a3b1
LP
4357 assert(p->new_size != UINT64_MAX);
4358 assert(p->offset != UINT64_MAX);
4359 assert(p->partno != UINT64_MAX);
4360
4361 if (PARTITION_EXISTS(p)) {
4362 bool changed = false;
4363
4364 assert(p->current_partition);
4365
4366 if (p->new_size != p->current_size) {
4367 assert(p->new_size >= p->current_size);
994b3031 4368 assert(p->new_size % context->sector_size == 0);
e594a3b1
LP
4369
4370 r = fdisk_partition_size_explicit(p->current_partition, true);
4371 if (r < 0)
4372 return log_error_errno(r, "Failed to enable explicit sizing: %m");
4373
994b3031 4374 r = fdisk_partition_set_size(p->current_partition, p->new_size / context->sector_size);
e594a3b1
LP
4375 if (r < 0)
4376 return log_error_errno(r, "Failed to grow partition: %m");
4377
4378 log_info("Growing existing partition %" PRIu64 ".", p->partno);
4379 changed = true;
4380 }
4381
4382 if (!sd_id128_equal(p->new_uuid, p->current_uuid)) {
b7416360 4383 r = fdisk_partition_set_uuid(p->current_partition, SD_ID128_TO_UUID_STRING(p->new_uuid));
e594a3b1
LP
4384 if (r < 0)
4385 return log_error_errno(r, "Failed to set partition UUID: %m");
4386
4387 log_info("Initializing UUID of existing partition %" PRIu64 ".", p->partno);
4388 changed = true;
4389 }
4390
4391 if (!streq_ptr(p->new_label, p->current_label)) {
be9ce018 4392 r = fdisk_partition_set_name(p->current_partition, strempty(p->new_label));
e594a3b1
LP
4393 if (r < 0)
4394 return log_error_errno(r, "Failed to set partition label: %m");
4395
4396 log_info("Setting partition label of existing partition %" PRIu64 ".", p->partno);
4397 changed = true;
4398 }
4399
4400 if (changed) {
4401 assert(!PARTITION_IS_FOREIGN(p)); /* never touch foreign partitions */
4402
4403 r = fdisk_set_partition(context->fdisk_context, p->partno, p->current_partition);
4404 if (r < 0)
4405 return log_error_errno(r, "Failed to update partition: %m");
4406 }
4407 } else {
4408 _cleanup_(fdisk_unref_partitionp) struct fdisk_partition *q = NULL;
4409 _cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *t = NULL;
e594a3b1
LP
4410
4411 assert(!p->new_partition);
994b3031
LP
4412 assert(p->offset % context->sector_size == 0);
4413 assert(p->new_size % context->sector_size == 0);
be9ce018 4414 assert(p->new_label);
e594a3b1
LP
4415
4416 t = fdisk_new_parttype();
4417 if (!t)
4418 return log_oom();
4419
22e932f4 4420 r = fdisk_parttype_set_typestr(t, SD_ID128_TO_UUID_STRING(p->type.uuid));
e594a3b1
LP
4421 if (r < 0)
4422 return log_error_errno(r, "Failed to initialize partition type: %m");
4423
4424 q = fdisk_new_partition();
4425 if (!q)
4426 return log_oom();
4427
4428 r = fdisk_partition_set_type(q, t);
4429 if (r < 0)
4430 return log_error_errno(r, "Failed to set partition type: %m");
4431
4432 r = fdisk_partition_size_explicit(q, true);
4433 if (r < 0)
4434 return log_error_errno(r, "Failed to enable explicit sizing: %m");
4435
994b3031 4436 r = fdisk_partition_set_start(q, p->offset / context->sector_size);
e594a3b1
LP
4437 if (r < 0)
4438 return log_error_errno(r, "Failed to position partition: %m");
4439
994b3031 4440 r = fdisk_partition_set_size(q, p->new_size / context->sector_size);
e594a3b1
LP
4441 if (r < 0)
4442 return log_error_errno(r, "Failed to grow partition: %m");
4443
4444 r = fdisk_partition_set_partno(q, p->partno);
4445 if (r < 0)
4446 return log_error_errno(r, "Failed to set partition number: %m");
4447
b7416360 4448 r = fdisk_partition_set_uuid(q, SD_ID128_TO_UUID_STRING(p->new_uuid));
e594a3b1
LP
4449 if (r < 0)
4450 return log_error_errno(r, "Failed to set partition UUID: %m");
4451
be9ce018 4452 r = fdisk_partition_set_name(q, strempty(p->new_label));
e594a3b1
LP
4453 if (r < 0)
4454 return log_error_errno(r, "Failed to set partition label: %m");
4455
ff0771bf 4456 /* Merge the no auto + read only + growfs setting with the literal flags, and set them for the partition */
1c41c1dc 4457 r = set_gpt_flags(q, partition_merge_flags(p));
e73309c5
LP
4458 if (r < 0)
4459 return log_error_errno(r, "Failed to set GPT partition flags: %m");
4460
5b5109e2 4461 log_info("Adding new partition %" PRIu64 " to partition table.", p->partno);
e594a3b1
LP
4462
4463 r = fdisk_add_partition(context->fdisk_context, q, NULL);
4464 if (r < 0)
4465 return log_error_errno(r, "Failed to add partition: %m");
4466
4467 assert(!p->new_partition);
4468 p->new_partition = TAKE_PTR(q);
4469 }
4470 }
4471
f28d4f42
LP
4472 return 0;
4473}
4474
62108348 4475static int split_name_printf(Partition *p, char **ret) {
4cee8333
DDM
4476 assert(p);
4477
4478 const Specifier table[] = {
22e932f4
DDM
4479 { 't', specifier_string, GPT_PARTITION_TYPE_UUID_TO_STRING_HARDER(p->type.uuid) },
4480 { 'T', specifier_id128, &p->type.uuid },
4cee8333
DDM
4481 { 'U', specifier_id128, &p->new_uuid },
4482 { 'n', specifier_uint64, &p->partno },
4483
4484 COMMON_SYSTEM_SPECIFIERS,
4485 {}
4486 };
4487
62108348 4488 return specifier_printf(p->split_name_format, NAME_MAX, table, arg_root, p, ret);
4cee8333
DDM
4489}
4490
62108348
DDM
4491static int split_node(const char *node, char **ret_base, char **ret_ext) {
4492 _cleanup_free_ char *base = NULL, *ext = NULL;
4493 char *e;
4494 int r;
4495
4496 assert(node);
4497 assert(ret_base);
4498 assert(ret_ext);
4499
4500 r = path_extract_filename(node, &base);
4501 if (r == O_DIRECTORY || r == -EADDRNOTAVAIL)
4502 return log_error_errno(r, "Device node %s cannot be a directory", node);
4503 if (r < 0)
4504 return log_error_errno(r, "Failed to extract filename from %s: %m", node);
4505
4506 e = endswith(base, ".raw");
4507 if (e) {
4508 ext = strdup(e);
4509 if (!ext)
4510 return log_oom();
4511
4512 *e = 0;
4513 }
4514
4515 *ret_base = TAKE_PTR(base);
4516 *ret_ext = TAKE_PTR(ext);
4517
4518 return 0;
4519}
4520
cc751c75 4521static int split_name_resolve(Context *context) {
62108348 4522 _cleanup_free_ char *parent = NULL, *base = NULL, *ext = NULL;
4cee8333
DDM
4523 int r;
4524
62108348 4525 assert(context);
62108348 4526
cc751c75 4527 r = path_extract_directory(context->node, &parent);
62108348 4528 if (r < 0 && r != -EDESTADDRREQ)
cc751c75 4529 return log_error_errno(r, "Failed to extract directory from %s: %m", context->node);
62108348 4530
cc751c75 4531 r = split_node(context->node, &base, &ext);
62108348
DDM
4532 if (r < 0)
4533 return r;
4534
4cee8333 4535 LIST_FOREACH(partitions, p, context->partitions) {
62108348
DDM
4536 _cleanup_free_ char *resolved = NULL;
4537
4cee8333
DDM
4538 if (p->dropped)
4539 continue;
4540
4541 if (!p->split_name_format)
4542 continue;
4543
62108348 4544 r = split_name_printf(p, &resolved);
4cee8333
DDM
4545 if (r < 0)
4546 return log_error_errno(r, "Failed to resolve specifiers in %s: %m", p->split_name_format);
62108348
DDM
4547
4548 if (parent)
4549 p->split_path = strjoin(parent, "/", base, ".", resolved, ext);
4550 else
4551 p->split_path = strjoin(base, ".", resolved, ext);
4552 if (!p->split_path)
4553 return log_oom();
4cee8333
DDM
4554 }
4555
4556 LIST_FOREACH(partitions, p, context->partitions) {
62108348 4557 if (!p->split_path)
4cee8333
DDM
4558 continue;
4559
4560 LIST_FOREACH(partitions, q, context->partitions) {
4561 if (p == q)
4562 continue;
4563
62108348 4564 if (!q->split_path)
4cee8333
DDM
4565 continue;
4566
62108348 4567 if (!streq(p->split_path, q->split_path))
4cee8333
DDM
4568 continue;
4569
4570 return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
4571 "%s and %s have the same resolved split name \"%s\", refusing",
62108348 4572 p->definition_path, q->definition_path, p->split_path);
4cee8333
DDM
4573 }
4574 }
4575
4576 return 0;
4577}
4578
cc751c75 4579static int context_split(Context *context) {
4cee8333
DDM
4580 int fd = -1, r;
4581
4582 if (!arg_split)
4583 return 0;
4584
4585 assert(context);
4cee8333
DDM
4586
4587 /* We can't do resolution earlier because the partition UUIDs for verity partitions are only filled
4588 * in after they've been generated. */
4589
cc751c75 4590 r = split_name_resolve(context);
4cee8333
DDM
4591 if (r < 0)
4592 return r;
4593
4cee8333 4594 LIST_FOREACH(partitions, p, context->partitions) {
4cee8333
DDM
4595 _cleanup_close_ int fdt = -1;
4596
4597 if (p->dropped)
4598 continue;
4599
62108348 4600 if (!p->split_path)
4cee8333
DDM
4601 continue;
4602
81d1098b
DDM
4603 if (partition_skip(p))
4604 continue;
4605
62108348 4606 fdt = open(p->split_path, O_WRONLY|O_NOCTTY|O_CLOEXEC|O_NOFOLLOW|O_CREAT|O_EXCL, 0666);
4cee8333 4607 if (fdt < 0)
62108348 4608 return log_error_errno(fdt, "Failed to open split partition file %s: %m", p->split_path);
4cee8333
DDM
4609
4610 if (fd < 0)
4611 assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
4612
4613 if (lseek(fd, p->offset, SEEK_SET) < 0)
4614 return log_error_errno(errno, "Failed to seek to partition offset: %m");
4615
a673b056 4616 r = copy_bytes(fd, fdt, p->new_size, COPY_REFLINK|COPY_HOLES);
4cee8333 4617 if (r < 0)
62108348 4618 return log_error_errno(r, "Failed to copy to split partition %s: %m", p->split_path);
4cee8333
DDM
4619 }
4620
4621 return 0;
4622}
4623
cc751c75 4624static int context_write_partition_table(Context *context) {
f28d4f42
LP
4625 _cleanup_(fdisk_unref_tablep) struct fdisk_table *original_table = NULL;
4626 int capable, r;
4627
4628 assert(context);
4629
cc751c75 4630 if (!context->from_scratch && !context_changed(context)) {
f28d4f42
LP
4631 log_info("No changes.");
4632 return 0;
4633 }
4634
4635 if (arg_dry_run) {
4636 log_notice("Refusing to repartition, please re-run with --dry-run=no.");
4637 return 0;
4638 }
4639
4640 log_info("Applying changes.");
4641
cc751c75 4642 if (context->from_scratch) {
81873a6b
LP
4643 r = context_wipe_range(context, 0, context->total);
4644 if (r < 0)
4645 return r;
4646
4647 log_info("Wiped block device.");
4648
0dce448b
LB
4649 if (arg_discard) {
4650 r = context_discard_range(context, 0, context->total);
4651 if (r == -EOPNOTSUPP)
4652 log_info("Storage does not support discard, not discarding entire block device data.");
4653 else if (r < 0)
4654 return log_error_errno(r, "Failed to discard entire block device: %m");
4655 else if (r > 0)
4656 log_info("Discarded entire block device.");
4657 }
f28d4f42
LP
4658 }
4659
4660 r = fdisk_get_partitions(context->fdisk_context, &original_table);
4661 if (r < 0)
4662 return log_error_errno(r, "Failed to acquire partition table: %m");
4663
4664 /* Wipe fs signatures and discard sectors where the new partitions are going to be placed and in the
4665 * gaps between partitions, just to be sure. */
cc751c75 4666 r = context_wipe_and_discard(context);
f28d4f42
LP
4667 if (r < 0)
4668 return r;
4669
4670 r = context_copy_blocks(context);
4671 if (r < 0)
4672 return r;
4673
4674 r = context_mkfs(context);
4675 if (r < 0)
4676 return r;
4677
4678 r = context_mangle_partitions(context);
4679 if (r < 0)
4680 return r;
4681
e594a3b1
LP
4682 log_info("Writing new partition table.");
4683
4684 r = fdisk_write_disklabel(context->fdisk_context);
4685 if (r < 0)
4686 return log_error_errno(r, "Failed to write partition table: %m");
4687
911ba624 4688 capable = blockdev_partscan_enabled(fdisk_get_devfd(context->fdisk_context));
9a1deb85
LP
4689 if (capable == -ENOTBLK)
4690 log_debug("Not telling kernel to reread partition table, since we are not operating on a block device.");
4691 else if (capable < 0)
911ba624 4692 return log_error_errno(capable, "Failed to check if block device supports partition scanning: %m");
9a1deb85 4693 else if (capable > 0) {
e594a3b1
LP
4694 log_info("Telling kernel to reread partition table.");
4695
cc751c75 4696 if (context->from_scratch)
e594a3b1
LP
4697 r = fdisk_reread_partition_table(context->fdisk_context);
4698 else
4699 r = fdisk_reread_changes(context->fdisk_context, original_table);
4700 if (r < 0)
4701 return log_error_errno(r, "Failed to reread partition table: %m");
4702 } else
4703 log_notice("Not telling kernel to reread partition table, because selected image does not support kernel partition block devices.");
4704
4705 log_info("All done.");
4706
4707 return 0;
4708}
4709
4710static int context_read_seed(Context *context, const char *root) {
4711 int r;
4712
4713 assert(context);
4714
4715 if (!sd_id128_is_null(context->seed))
4716 return 0;
4717
4718 if (!arg_randomize) {
4719 _cleanup_close_ int fd = -1;
4720
4721 fd = chase_symlinks_and_open("/etc/machine-id", root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC, NULL);
4722 if (fd == -ENOENT)
4723 log_info("No machine ID set, using randomized partition UUIDs.");
4724 else if (fd < 0)
4725 return log_error_errno(fd, "Failed to determine machine ID of image: %m");
4726 else {
448b782c 4727 r = id128_read_fd(fd, ID128_PLAIN_OR_UNINIT, &context->seed);
e594a3b1
LP
4728 if (r == -ENOMEDIUM)
4729 log_info("No machine ID set, using randomized partition UUIDs.");
4730 else if (r < 0)
4731 return log_error_errno(r, "Failed to parse machine ID of image: %m");
4732
4733 return 0;
4734 }
4735 }
4736
4737 r = sd_id128_randomize(&context->seed);
4738 if (r < 0)
4739 return log_error_errno(r, "Failed to generate randomized seed: %m");
4740
4741 return 0;
4742}
4743
cc751c75 4744static int context_factory_reset(Context *context) {
e594a3b1
LP
4745 size_t n = 0;
4746 int r;
4747
4748 assert(context);
4749
4750 if (arg_factory_reset <= 0)
4751 return 0;
4752
cc751c75 4753 if (context->from_scratch) /* Nothing to reset if we start from scratch */
e594a3b1
LP
4754 return 0;
4755
4756 if (arg_dry_run) {
4757 log_notice("Refusing to factory reset, please re-run with --dry-run=no.");
4758 return 0;
4759 }
4760
4761 log_info("Applying factory reset.");
4762
4763 LIST_FOREACH(partitions, p, context->partitions) {
4764
4765 if (!p->factory_reset || !PARTITION_EXISTS(p))
4766 continue;
4767
4768 assert(p->partno != UINT64_MAX);
4769
4770 log_info("Removing partition %" PRIu64 " for factory reset.", p->partno);
4771
4772 r = fdisk_delete_partition(context->fdisk_context, p->partno);
4773 if (r < 0)
4774 return log_error_errno(r, "Failed to remove partition %" PRIu64 ": %m", p->partno);
4775
4776 n++;
4777 }
4778
4779 if (n == 0) {
4780 log_info("Factory reset requested, but no partitions to delete found.");
4781 return 0;
4782 }
4783
4784 r = fdisk_write_disklabel(context->fdisk_context);
4785 if (r < 0)
4786 return log_error_errno(r, "Failed to write disk label: %m");
4787
4788 log_info("Successfully deleted %zu partitions.", n);
4789 return 1;
4790}
4791
4792static int context_can_factory_reset(Context *context) {
e594a3b1
LP
4793 assert(context);
4794
4795 LIST_FOREACH(partitions, p, context->partitions)
4796 if (p->factory_reset && PARTITION_EXISTS(p))
4797 return true;
4798
4799 return false;
4800}
4801
5c08da58
LP
4802static int resolve_copy_blocks_auto_candidate(
4803 dev_t partition_devno,
22e932f4 4804 GptPartitionType partition_type,
5c08da58
LP
4805 dev_t restrict_devno,
4806 sd_id128_t *ret_uuid) {
4807
4808 _cleanup_(blkid_free_probep) blkid_probe b = NULL;
5c08da58 4809 _cleanup_close_ int fd = -1;
ca822829
YW
4810 _cleanup_free_ char *p = NULL;
4811 const char *pttype, *t;
5c08da58
LP
4812 sd_id128_t pt_parsed, u;
4813 blkid_partition pp;
4814 dev_t whole_devno;
4815 blkid_partlist pl;
5c08da58
LP
4816 int r;
4817
4818 /* Checks if the specified partition has the specified GPT type UUID, and is located on the specified
4819 * 'restrict_devno' device. The type check is particularly relevant if we have Verity volume which is
4820 * backed by two separate partitions: the data and the hash partitions, and we need to find the right
4821 * one of the two. */
4822
4823 r = block_get_whole_disk(partition_devno, &whole_devno);
4824 if (r < 0)
4825 return log_error_errno(
4826 r,
4827 "Unable to determine containing block device of partition %u:%u: %m",
4828 major(partition_devno), minor(partition_devno));
4829
4830 if (restrict_devno != (dev_t) -1 &&
4831 restrict_devno != whole_devno)
4832 return log_error_errno(
4833 SYNTHETIC_ERRNO(EPERM),
4834 "Partition %u:%u is located outside of block device %u:%u, refusing.",
4835 major(partition_devno), minor(partition_devno),
4836 major(restrict_devno), minor(restrict_devno));
4837
ca822829 4838 fd = r = device_open_from_devnum(S_IFBLK, whole_devno, O_RDONLY|O_CLOEXEC|O_NONBLOCK, &p);
5c08da58 4839 if (r < 0)
ca822829
YW
4840 return log_error_errno(r, "Failed to open block device " DEVNUM_FORMAT_STR ": %m",
4841 DEVNUM_FORMAT_VAL(whole_devno));
5c08da58
LP
4842
4843 b = blkid_new_probe();
4844 if (!b)
4845 return log_oom();
4846
4847 errno = 0;
4848 r = blkid_probe_set_device(b, fd, 0, 0);
4849 if (r != 0)
4850 return log_error_errno(errno_or_else(ENOMEM), "Failed to open block device '%s': %m", p);
4851
4852 (void) blkid_probe_enable_partitions(b, 1);
4853 (void) blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
4854
4855 errno = 0;
4856 r = blkid_do_safeprobe(b);
4857 if (IN_SET(r, -2, 1)) { /* nothing found or ambiguous result */
4858 log_debug("Didn't find partition table on block device '%s'.", p);
4859 return false;
4860 }
4861 if (r != 0)
4862 return log_error_errno(errno_or_else(EIO), "Unable to probe for partition table of '%s': %m", p);
4863
4864 (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
4865 if (!streq_ptr(pttype, "gpt")) {
4866 log_debug("Didn't find a GPT partition table on '%s'.", p);
4867 return false;
4868 }
4869
4870 errno = 0;
4871 pl = blkid_probe_get_partitions(b);
4872 if (!pl)
4873 return log_error_errno(errno_or_else(EIO), "Unable read partition table of '%s': %m", p);
4874 errno = 0;
4875
4876 pp = blkid_partlist_devno_to_partition(pl, partition_devno);
4877 if (!pp) {
4878 log_debug("Partition %u:%u has no matching partition table entry on '%s'.",
4879 major(partition_devno), minor(partition_devno), p);
4880 return false;
4881 }
4882
4883 t = blkid_partition_get_type_string(pp);
4884 if (isempty(t)) {
4885 log_debug("Partition %u:%u has no type on '%s'.",
4886 major(partition_devno), minor(partition_devno), p);
4887 return false;
4888 }
4889
4890 r = sd_id128_from_string(t, &pt_parsed);
4891 if (r < 0) {
4892 log_debug_errno(r, "Failed to parse partition type \"%s\": %m", t);
4893 return false;
4894 }
4895
22e932f4 4896 if (!sd_id128_equal(pt_parsed, partition_type.uuid)) {
5c08da58
LP
4897 log_debug("Partition %u:%u has non-matching partition type " SD_ID128_FORMAT_STR " (needed: " SD_ID128_FORMAT_STR "), ignoring.",
4898 major(partition_devno), minor(partition_devno),
22e932f4 4899 SD_ID128_FORMAT_VAL(pt_parsed), SD_ID128_FORMAT_VAL(partition_type.uuid));
5c08da58
LP
4900 return false;
4901 }
4902
4903 t = blkid_partition_get_uuid(pp);
4904 if (isempty(t)) {
4905 log_debug("Partition %u:%u has no UUID.",
4906 major(partition_devno), minor(partition_devno));
4907 return false;
4908 }
4909
4910 r = sd_id128_from_string(t, &u);
4911 if (r < 0) {
4912 log_debug_errno(r, "Failed to parse partition UUID \"%s\": %m", t);
4913 return false;
4914 }
4915
4916 log_debug("Automatically found partition %u:%u of right type " SD_ID128_FORMAT_STR ".",
4917 major(partition_devno), minor(partition_devno),
4918 SD_ID128_FORMAT_VAL(pt_parsed));
4919
4920 if (ret_uuid)
4921 *ret_uuid = u;
4922
4923 return true;
4924}
4925
4926static int find_backing_devno(
4927 const char *path,
4928 const char *root,
4929 dev_t *ret) {
4930
4931 _cleanup_free_ char *resolved = NULL;
4932 int r;
4933
4934 assert(path);
4935
4936 r = chase_symlinks(path, root, CHASE_PREFIX_ROOT, &resolved, NULL);
4937 if (r < 0)
4938 return r;
4939
4940 r = path_is_mount_point(resolved, NULL, 0);
4941 if (r < 0)
4942 return r;
4943 if (r == 0) /* Not a mount point, then it's not a partition of its own, let's not automatically use it. */
4944 return -ENOENT;
4945
4946 r = get_block_device(resolved, ret);
4947 if (r < 0)
4948 return r;
4949 if (r == 0) /* Not backed by physical file system, we can't use this */
4950 return -ENOENT;
4951
4952 return 0;
4953}
4954
4955static int resolve_copy_blocks_auto(
22e932f4 4956 GptPartitionType type,
5c08da58
LP
4957 const char *root,
4958 dev_t restrict_devno,
1a037ba2 4959 dev_t *ret_devno,
5c08da58
LP
4960 sd_id128_t *ret_uuid) {
4961
4962 const char *try1 = NULL, *try2 = NULL;
4963 char p[SYS_BLOCK_PATH_MAX("/slaves")];
4964 _cleanup_(closedirp) DIR *d = NULL;
4965 sd_id128_t found_uuid = SD_ID128_NULL;
4966 dev_t devno, found = 0;
4967 int r;
4968
5c08da58
LP
4969 /* Enforce some security restrictions: CopyBlocks=auto should not be an avenue to get outside of the
4970 * --root=/--image= confinement. Specifically, refuse CopyBlocks= in combination with --root= at all,
4971 * and restrict block device references in the --image= case to loopback block device we set up.
4972 *
4973 * restrict_devno contain the dev_t of the loop back device we operate on in case of --image=, and
4974 * thus declares which device (and its partition subdevices) we shall limit access to. If
4975 * restrict_devno is zero no device probing access shall be allowed at all (used for --root=) and if
4976 * it is (dev_t) -1 then free access shall be allowed (if neither switch is used). */
4977
4978 if (restrict_devno == 0)
4979 return log_error_errno(SYNTHETIC_ERRNO(EPERM),
4980 "Automatic discovery of backing block devices not permitted in --root= mode, refusing.");
4981
4982 /* Handles CopyBlocks=auto, and finds the right source partition to copy from. We look for matching
4983 * partitions in the host, using the appropriate directory as key and ensuring that the partition
4984 * type matches. */
4985
22e932f4 4986 if (type.designator == PARTITION_ROOT)
5c08da58 4987 try1 = "/";
22e932f4 4988 else if (type.designator == PARTITION_USR)
5c08da58 4989 try1 = "/usr/";
22e932f4 4990 else if (type.designator == PARTITION_ROOT_VERITY)
5c08da58 4991 try1 = "/";
22e932f4 4992 else if (type.designator == PARTITION_USR_VERITY)
5c08da58 4993 try1 = "/usr/";
22e932f4 4994 else if (type.designator == PARTITION_ESP) {
5c08da58
LP
4995 try1 = "/efi/";
4996 try2 = "/boot/";
22e932f4 4997 } else if (type.designator == PARTITION_XBOOTLDR)
5c08da58
LP
4998 try1 = "/boot/";
4999 else
5000 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
5001 "Partition type " SD_ID128_FORMAT_STR " not supported from automatic source block device discovery.",
22e932f4 5002 SD_ID128_FORMAT_VAL(type.uuid));
5c08da58
LP
5003
5004 r = find_backing_devno(try1, root, &devno);
5005 if (r == -ENOENT && try2)
5006 r = find_backing_devno(try2, root, &devno);
5007 if (r < 0)
5008 return log_error_errno(r, "Failed to resolve automatic CopyBlocks= path for partition type " SD_ID128_FORMAT_STR ", sorry: %m",
22e932f4 5009 SD_ID128_FORMAT_VAL(type.uuid));
5c08da58
LP
5010
5011 xsprintf_sys_block_path(p, "/slaves", devno);
5012 d = opendir(p);
5013 if (d) {
5014 struct dirent *de;
5015
5016 for (;;) {
5017 _cleanup_free_ char *q = NULL, *t = NULL;
5018 sd_id128_t u;
5019 dev_t sl;
5020
5021 errno = 0;
5022 de = readdir_no_dot(d);
5023 if (!de) {
5024 if (errno != 0)
5025 return log_error_errno(errno, "Failed to read directory '%s': %m", p);
5026
5027 break;
5028 }
5029
5030 if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
5031 continue;
5032
5033 q = path_join(p, de->d_name, "/dev");
5034 if (!q)
5035 return log_oom();
5036
5037 r = read_one_line_file(q, &t);
5038 if (r < 0)
5039 return log_error_errno(r, "Failed to read %s: %m", q);
5040
7176f06c 5041 r = parse_devnum(t, &sl);
5c08da58
LP
5042 if (r < 0) {
5043 log_debug_errno(r, "Failed to parse %s, ignoring: %m", q);
5044 continue;
5045 }
5046 if (major(sl) == 0) {
5047 log_debug_errno(r, "Device backing %s is special, ignoring: %m", q);
5048 continue;
5049 }
5050
22e932f4 5051 r = resolve_copy_blocks_auto_candidate(sl, type, restrict_devno, &u);
5c08da58
LP
5052 if (r < 0)
5053 return r;
5054 if (r > 0) {
5055 /* We found a matching one! */
5056 if (found != 0)
5057 return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
5058 "Multiple matching partitions found, refusing.");
5059
5060 found = sl;
5061 found_uuid = u;
5062 }
5063 }
5064 } else if (errno != ENOENT)
5065 return log_error_errno(errno, "Failed open %s: %m", p);
5066 else {
22e932f4 5067 r = resolve_copy_blocks_auto_candidate(devno, type, restrict_devno, &found_uuid);
5c08da58
LP
5068 if (r < 0)
5069 return r;
5070 if (r > 0)
5071 found = devno;
5072 }
5073
5074 if (found == 0)
5075 return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
5076 "Unable to automatically discover suitable partition to copy blocks from.");
5077
1a037ba2
YW
5078 if (ret_devno)
5079 *ret_devno = found;
5c08da58
LP
5080
5081 if (ret_uuid)
5082 *ret_uuid = found_uuid;
5083
5084 return 0;
5085}
5086
5087static int context_open_copy_block_paths(
5088 Context *context,
5c08da58
LP
5089 dev_t restrict_devno) {
5090
757bc2e4
LP
5091 int r;
5092
5093 assert(context);
5094
5095 LIST_FOREACH(partitions, p, context->partitions) {
5096 _cleanup_close_ int source_fd = -1;
5c08da58
LP
5097 _cleanup_free_ char *opened = NULL;
5098 sd_id128_t uuid = SD_ID128_NULL;
757bc2e4
LP
5099 uint64_t size;
5100 struct stat st;
5101
5102 assert(p->copy_blocks_fd < 0);
5103 assert(p->copy_blocks_size == UINT64_MAX);
5104
5105 if (PARTITION_EXISTS(p)) /* Never copy over partitions that already exist! */
5106 continue;
5107
5c08da58 5108 if (p->copy_blocks_path) {
757bc2e4 5109
585c5c75 5110 source_fd = chase_symlinks_and_open(p->copy_blocks_path, p->copy_blocks_root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NONBLOCK, &opened);
5c08da58
LP
5111 if (source_fd < 0)
5112 return log_error_errno(source_fd, "Failed to open '%s': %m", p->copy_blocks_path);
757bc2e4 5113
5c08da58
LP
5114 if (fstat(source_fd, &st) < 0)
5115 return log_error_errno(errno, "Failed to stat block copy file '%s': %m", opened);
5116
5117 if (!S_ISREG(st.st_mode) && restrict_devno != (dev_t) -1)
5118 return log_error_errno(SYNTHETIC_ERRNO(EPERM),
5119 "Copying from block device node is not permitted in --image=/--root= mode, refusing.");
5120
5121 } else if (p->copy_blocks_auto) {
1a037ba2 5122 dev_t devno;
5c08da58 5123
22e932f4 5124 r = resolve_copy_blocks_auto(p->type, p->copy_blocks_root, restrict_devno, &devno, &uuid);
5c08da58
LP
5125 if (r < 0)
5126 return r;
5127
ca822829 5128 source_fd = r = device_open_from_devnum(S_IFBLK, devno, O_RDONLY|O_CLOEXEC|O_NONBLOCK, &opened);
1a037ba2 5129 if (r < 0)
ca822829
YW
5130 return log_error_errno(r, "Failed to open automatically determined source block copy device " DEVNUM_FORMAT_STR ": %m",
5131 DEVNUM_FORMAT_VAL(devno));
5c08da58
LP
5132
5133 if (fstat(source_fd, &st) < 0)
5134 return log_error_errno(errno, "Failed to stat block copy file '%s': %m", opened);
1a037ba2 5135 } else
5c08da58 5136 continue;
757bc2e4
LP
5137
5138 if (S_ISDIR(st.st_mode)) {
ca822829
YW
5139 _cleanup_free_ char *bdev = NULL;
5140 dev_t devt;
757bc2e4
LP
5141
5142 /* If the file is a directory, automatically find the backing block device */
5143
5144 if (major(st.st_dev) != 0)
ca822829 5145 devt = st.st_dev;
757bc2e4 5146 else {
757bc2e4 5147 /* Special support for btrfs */
757bc2e4 5148 r = btrfs_get_block_device_fd(source_fd, &devt);
67f0ac8c 5149 if (r == -EUCLEAN)
5c08da58 5150 return btrfs_log_dev_root(LOG_ERR, r, opened);
757bc2e4 5151 if (r < 0)
5c08da58 5152 return log_error_errno(r, "Unable to determine backing block device of '%s': %m", opened);
757bc2e4 5153 }
757bc2e4
LP
5154
5155 safe_close(source_fd);
5156
ca822829
YW
5157 source_fd = r = device_open_from_devnum(S_IFBLK, devt, O_RDONLY|O_CLOEXEC|O_NONBLOCK, &bdev);
5158 if (r < 0)
5159 return log_error_errno(r, "Failed to open block device backing '%s': %m", opened);
757bc2e4
LP
5160
5161 if (fstat(source_fd, &st) < 0)
5162 return log_error_errno(errno, "Failed to stat block device '%s': %m", bdev);
757bc2e4
LP
5163 }
5164
5165 if (S_ISREG(st.st_mode))
5166 size = st.st_size;
5167 else if (S_ISBLK(st.st_mode)) {
5168 if (ioctl(source_fd, BLKGETSIZE64, &size) != 0)
5169 return log_error_errno(errno, "Failed to determine size of block device to copy from: %m");
5170 } else
5c08da58 5171 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", opened);
757bc2e4
LP
5172
5173 if (size <= 0)
5c08da58 5174 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has zero size, refusing.", opened);
757bc2e4 5175 if (size % 512 != 0)
5c08da58 5176 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has size that is not multiple of 512, refusing.", opened);
757bc2e4
LP
5177
5178 p->copy_blocks_fd = TAKE_FD(source_fd);
5179 p->copy_blocks_size = size;
5c08da58
LP
5180
5181 free_and_replace(p->copy_blocks_path, opened);
5182
5183 /* When copying from an existing partition copy that partitions UUID if none is configured explicitly */
11749b61 5184 if (!p->new_uuid_is_set && !sd_id128_is_null(uuid)) {
5c08da58 5185 p->new_uuid = uuid;
11749b61
DDM
5186 p->new_uuid_is_set = true;
5187 }
757bc2e4
LP
5188 }
5189
5190 return 0;
5191}
5192
c4a87b76
DDM
5193static int fd_apparent_size(int fd, uint64_t *ret) {
5194 off_t initial = 0;
5195 uint64_t size = 0;
5196
5197 assert(fd >= 0);
5198 assert(ret);
5199
5200 initial = lseek(fd, 0, SEEK_CUR);
5201 if (initial < 0)
5202 return log_error_errno(errno, "Failed to get file offset: %m");
5203
5204 for (off_t off = 0;;) {
5205 off_t r;
5206
5207 r = lseek(fd, off, SEEK_DATA);
5208 if (r < 0 && errno == ENXIO)
5209 /* If errno == ENXIO, that means we've reached the final hole of the file and
5210 * that hole isn't followed by more data. */
5211 break;
5212 if (r < 0)
5213 return log_error_errno(errno, "Failed to seek data in file from offset %"PRIi64": %m", off);
5214
5215 off = r; /* Set the offset to the start of the data segment. */
5216
5217 /* After copying a potential hole, find the end of the data segment by looking for
5218 * the next hole. If we get ENXIO, we're at EOF. */
5219 r = lseek(fd, off, SEEK_HOLE);
5220 if (r < 0) {
5221 if (errno == ENXIO)
5222 break;
5223 return log_error_errno(errno, "Failed to seek hole in file from offset %"PRIi64": %m", off);
5224 }
5225
5226 size += r - off;
5227 off = r;
5228 }
5229
5230 if (lseek(fd, initial, SEEK_SET) < 0)
5231 return log_error_errno(errno, "Failed to reset file offset: %m");
5232
5233 *ret = size;
5234
5235 return 0;
5236}
5237
5238static int context_minimize(Context *context) {
5239 _cleanup_set_free_ Set *denylist = NULL;
5240 const char *vt;
5241 int r;
5242
5243 assert(context);
5244
5245 r = make_copy_files_denylist(context, &denylist);
5246 if (r < 0)
5247 return r;
5248
5249 r = var_tmp_dir(&vt);
5250 if (r < 0)
5251 return log_error_errno(r, "Could not determine temporary directory: %m");
5252
5253 LIST_FOREACH(partitions, p, context->partitions) {
e59678b2 5254 _cleanup_(rm_rf_physical_and_freep) char *root = NULL;
c4a87b76 5255 _cleanup_(unlink_and_freep) char *temp = NULL;
d3201eb4 5256 _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
c4a87b76
DDM
5257 _cleanup_close_ int fd = -1;
5258 sd_id128_t fs_uuid;
5259 uint64_t fsz;
5260
5261 if (p->dropped)
5262 continue;
5263
5264 if (PARTITION_EXISTS(p)) /* Never format existing partitions */
5265 continue;
5266
5267 if (!p->format)
5268 continue;
5269
5270 if (!p->minimize)
5271 continue;
5272
d3201eb4
DDM
5273 if (!partition_needs_populate(p))
5274 continue;
5275
c4a87b76
DDM
5276 assert(!p->copy_blocks_path);
5277
5278 r = tempfn_random_child(vt, "repart", &temp);
5279 if (r < 0)
5280 return log_error_errno(r, "Failed to generate temporary file path: %m");
5281
59e2be46
DDM
5282 if (fstype_is_ro(p->format))
5283 fs_uuid = p->fs_uuid;
5284 else {
c4a87b76
DDM
5285 fd = open(temp, O_CREAT|O_EXCL|O_CLOEXEC|O_RDWR|O_NOCTTY, 0600);
5286 if (fd < 0)
5287 return log_error_errno(errno, "Failed to open temporary file %s: %m", temp);
5288
5289 /* This may seem huge but it will be created sparse so it doesn't take up any space
d3201eb4 5290 * on disk until written to. */
c4a87b76
DDM
5291 if (ftruncate(fd, 1024ULL * 1024ULL * 1024ULL * 1024ULL) < 0)
5292 return log_error_errno(errno, "Failed to truncate temporary file to %s: %m",
5293 FORMAT_BYTES(1024ULL * 1024ULL * 1024ULL * 1024ULL));
5294
d3201eb4
DDM
5295 r = loop_device_make(fd, O_RDWR, 0, UINT64_MAX, 0, 0, LOCK_EX, &d);
5296 if (r < 0 && r != -ENOENT && !ERRNO_IS_PRIVILEGE(r))
5297 return log_error_errno(r, "Failed to make loopback device of %s: %m", temp);
5298
c4a87b76
DDM
5299 /* We're going to populate this filesystem twice so use a random UUID the first time
5300 * to avoid UUID conflicts. */
5301 r = sd_id128_randomize(&fs_uuid);
5302 if (r < 0)
5303 return r;
59e2be46
DDM
5304 }
5305
d3201eb4
DDM
5306 if (!d || fstype_is_ro(p->format)) {
5307 if (!mkfs_supports_root_option(p->format))
5308 return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
5309 "Loop device access is required to populate %s filesystems",
5310 p->format);
5311
e59678b2 5312 r = partition_populate_directory(p, denylist, &root);
c4a87b76
DDM
5313 if (r < 0)
5314 return r;
c4a87b76
DDM
5315 }
5316
d3201eb4 5317 r = make_filesystem(d ? d->node : temp, p->format, strempty(p->new_label), root, fs_uuid, arg_discard);
c4a87b76
DDM
5318 if (r < 0)
5319 return r;
5320
5321 /* Read-only filesystems are minimal from the first try because they create and size the
5322 * loopback file for us. */
5323 if (fstype_is_ro(p->format)) {
5324 p->copy_blocks_path = TAKE_PTR(temp);
7d07030e 5325 p->copy_blocks_path_is_our_file = true;
c4a87b76
DDM
5326 continue;
5327 }
5328
d3201eb4
DDM
5329 if (!root) {
5330 assert(d);
5331
5332 r = partition_populate_filesystem(p, d->node, denylist);
59e2be46
DDM
5333 if (r < 0)
5334 return r;
5335 }
c4a87b76
DDM
5336
5337 /* Other filesystems need to be provided with a pre-sized loopback file and will adapt to
5338 * fully occupy it. Because we gave the filesystem a 1T sparse file, we need to shrink the
5339 * filesystem down to a reasonable size again to fit it in the disk image. While there are
5340 * some filesystems that support shrinking, it doesn't always work properly (e.g. shrinking
5341 * btrfs gives us a 2.0G filesystem regardless of what we put in it). Instead, let's populate
5342 * the filesystem again, but this time, instead of providing the filesystem with a 1T sparse
5343 * loopback file, let's size the loopback file based on the actual data used by the
5344 * filesystem in the sparse file after the first attempt. This should be a good guess of the
5345 * minimal amount of space needed in the filesystem to fit all the required data.
5346 */
5347 r = fd_apparent_size(fd, &fsz);
5348 if (r < 0)
5349 return r;
5350
5351 /* Massage the size a bit because just going by actual data used in the sparse file isn't
5352 * fool-proof. */
5353 fsz = round_up_size(fsz + (fsz / 2), context->grain_size);
5354 if (minimal_size_by_fs_name(p->format) != UINT64_MAX)
5355 fsz = MAX(minimal_size_by_fs_name(p->format), fsz);
5356
d3201eb4
DDM
5357 d = loop_device_unref(d);
5358
c4a87b76
DDM
5359 /* Erase the previous filesystem first. */
5360 if (ftruncate(fd, 0))
5361 return log_error_errno(errno, "Failed to erase temporary file: %m");
5362
5363 if (ftruncate(fd, fsz))
5364 return log_error_errno(errno, "Failed to truncate temporary file to %s: %m", FORMAT_BYTES(fsz));
5365
d3201eb4
DDM
5366 r = loop_device_make(fd, O_RDWR, 0, UINT64_MAX, 0, 0, LOCK_EX, &d);
5367 if (r < 0 && r != -ENOENT && !ERRNO_IS_PRIVILEGE(r))
5368 return log_error_errno(r, "Failed to make loopback device of %s: %m", temp);
5369
5370 r = make_filesystem(d ? d->node : temp, p->format, strempty(p->new_label), root, p->fs_uuid, arg_discard);
c4a87b76
DDM
5371 if (r < 0)
5372 return r;
5373
d3201eb4
DDM
5374 if (!root) {
5375 assert(d);
5376
5377 r = partition_populate_filesystem(p, d->node, denylist);
59e2be46
DDM
5378 if (r < 0)
5379 return r;
5380 }
c4a87b76
DDM
5381
5382 p->copy_blocks_path = TAKE_PTR(temp);
7d07030e 5383 p->copy_blocks_path_is_our_file = true;
c4a87b76
DDM
5384 }
5385
5386 return 0;
5387}
5388
220780db 5389static int parse_partition_types(const char *p, sd_id128_t **partitions, size_t *n_partitions) {
81d1098b
DDM
5390 int r;
5391
220780db
DDM
5392 assert(partitions);
5393 assert(n_partitions);
5394
81d1098b
DDM
5395 for (;;) {
5396 _cleanup_free_ char *name = NULL;
5397 GptPartitionType type;
5398
5399 r = extract_first_word(&p, &name, ",", EXTRACT_CUNESCAPE|EXTRACT_DONT_COALESCE_SEPARATORS);
5400 if (r == 0)
5401 break;
5402 if (r < 0)
766f52f2 5403 return log_error_errno(r, "Failed to extract partition type identifier or GUID: %s", p);
81d1098b
DDM
5404
5405 r = gpt_partition_type_from_string(name, &type);
5406 if (r < 0)
766f52f2 5407 return log_error_errno(r, "'%s' is not a valid partition type identifier or GUID", name);
81d1098b 5408
220780db 5409 if (!GREEDY_REALLOC(*partitions, *n_partitions + 1))
81d1098b
DDM
5410 return log_oom();
5411
220780db 5412 (*partitions)[(*n_partitions)++] = type.uuid;
81d1098b
DDM
5413 }
5414
5415 return 0;
5416}
5417
e594a3b1
LP
5418static int help(void) {
5419 _cleanup_free_ char *link = NULL;
5420 int r;
5421
5422 r = terminal_urlify_man("systemd-repart", "1", &link);
5423 if (r < 0)
5424 return log_oom();
5425
5426 printf("%s [OPTIONS...] [DEVICE]\n"
5427 "\n%sGrow and add partitions to partition table.%s\n\n"
5428 " -h --help Show this help\n"
5429 " --version Show package version\n"
896e678b
LP
5430 " --no-pager Do not pipe output into a pager\n"
5431 " --no-legend Do not show the headers and footers\n"
e594a3b1 5432 " --dry-run=BOOL Whether to run dry-run operation\n"
a26f4a49
LP
5433 " --empty=MODE One of refuse, allow, require, force, create; controls\n"
5434 " how to handle empty disks lacking partition tables\n"
e594a3b1 5435 " --discard=BOOL Whether to discard backing blocks for new partitions\n"
2d2d0a57 5436 " --pretty=BOOL Whether to show pretty summary before doing changes\n"
e594a3b1
LP
5437 " --factory-reset=BOOL Whether to remove data partitions before recreating\n"
5438 " them\n"
5439 " --can-factory-reset Test whether factory reset is defined\n"
5440 " --root=PATH Operate relative to root path\n"
252d6267 5441 " --image=PATH Operate relative to image file\n"
9d252fbb 5442 " --definitions=DIR Find partition definitions in specified directory\n"
b9df3536 5443 " --key-file=PATH Key to use when encrypting partitions\n"
b456191d
DDM
5444 " --private-key=PATH Private key to use when generating verity roothash\n"
5445 " signatures\n"
5446 " --certificate=PATH PEM certificate to use when generating verity\n"
5447 " roothash signatures\n"
889914ef 5448 " --tpm2-device=PATH Path to TPM2 device node to use\n"
a1788a69 5449 " --tpm2-pcrs=PCR1+PCR2+PCR3+…\n"
889914ef 5450 " TPM2 PCR indexes to use for TPM2 enrollment\n"
02ef97cd
LP
5451 " --tpm2-public-key=PATH\n"
5452 " Enroll signed TPM2 PCR policy against PEM public key\n"
5453 " --tpm2-public-key-pcrs=PCR1+PCR2+PCR3+…\n"
5454 " Enroll signed TPM2 PCR policy for specified TPM2 PCRs\n"
e594a3b1 5455 " --seed=UUID 128bit seed UUID to derive all UUIDs from\n"
a26f4a49 5456 " --size=BYTES Grow loopback file to specified size\n"
2d2d0a57 5457 " --json=pretty|short|off\n"
de8231b0 5458 " Generate JSON output\n"
4cee8333 5459 " --split=BOOL Whether to generate split artifacts\n"
81d1098b 5460 " --include-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
7d505753 5461 " Ignore partitions not of the specified types\n"
81d1098b 5462 " --exclude-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
7d505753
DDM
5463 " Ignore partitions of the specified types\n"
5464 " --skip-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
5465 " Take partitions of the specified types into account\n"
5466 " but don't populate them yet\n"
bc556335
DDM
5467 "\nSee the %s for details.\n",
5468 program_invocation_short_name,
5469 ansi_highlight(),
5470 ansi_normal(),
5471 link);
e594a3b1
LP
5472
5473 return 0;
5474}
5475
5476static int parse_argv(int argc, char *argv[]) {
5477
5478 enum {
5479 ARG_VERSION = 0x100,
896e678b
LP
5480 ARG_NO_PAGER,
5481 ARG_NO_LEGEND,
e594a3b1
LP
5482 ARG_DRY_RUN,
5483 ARG_EMPTY,
5484 ARG_DISCARD,
5485 ARG_FACTORY_RESET,
5486 ARG_CAN_FACTORY_RESET,
5487 ARG_ROOT,
252d6267 5488 ARG_IMAGE,
e594a3b1
LP
5489 ARG_SEED,
5490 ARG_PRETTY,
5491 ARG_DEFINITIONS,
a26f4a49 5492 ARG_SIZE,
a015fbe7 5493 ARG_JSON,
b9df3536 5494 ARG_KEY_FILE,
b456191d
DDM
5495 ARG_PRIVATE_KEY,
5496 ARG_CERTIFICATE,
889914ef
LP
5497 ARG_TPM2_DEVICE,
5498 ARG_TPM2_PCRS,
02ef97cd
LP
5499 ARG_TPM2_PUBLIC_KEY,
5500 ARG_TPM2_PUBLIC_KEY_PCRS,
4cee8333 5501 ARG_SPLIT,
81d1098b
DDM
5502 ARG_INCLUDE_PARTITIONS,
5503 ARG_EXCLUDE_PARTITIONS,
7d505753 5504 ARG_SKIP_PARTITIONS,
e594a3b1
LP
5505 };
5506
5507 static const struct option options[] = {
02ef97cd
LP
5508 { "help", no_argument, NULL, 'h' },
5509 { "version", no_argument, NULL, ARG_VERSION },
5510 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
5511 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
5512 { "dry-run", required_argument, NULL, ARG_DRY_RUN },
5513 { "empty", required_argument, NULL, ARG_EMPTY },
5514 { "discard", required_argument, NULL, ARG_DISCARD },
5515 { "factory-reset", required_argument, NULL, ARG_FACTORY_RESET },
5516 { "can-factory-reset", no_argument, NULL, ARG_CAN_FACTORY_RESET },
5517 { "root", required_argument, NULL, ARG_ROOT },
5518 { "image", required_argument, NULL, ARG_IMAGE },
5519 { "seed", required_argument, NULL, ARG_SEED },
5520 { "pretty", required_argument, NULL, ARG_PRETTY },
5521 { "definitions", required_argument, NULL, ARG_DEFINITIONS },
5522 { "size", required_argument, NULL, ARG_SIZE },
5523 { "json", required_argument, NULL, ARG_JSON },
5524 { "key-file", required_argument, NULL, ARG_KEY_FILE },
b456191d
DDM
5525 { "private-key", required_argument, NULL, ARG_PRIVATE_KEY },
5526 { "certificate", required_argument, NULL, ARG_CERTIFICATE },
02ef97cd
LP
5527 { "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
5528 { "tpm2-pcrs", required_argument, NULL, ARG_TPM2_PCRS },
5529 { "tpm2-public-key", required_argument, NULL, ARG_TPM2_PUBLIC_KEY },
5530 { "tpm2-public-key-pcrs", required_argument, NULL, ARG_TPM2_PUBLIC_KEY_PCRS },
4cee8333 5531 { "split", required_argument, NULL, ARG_SPLIT },
81d1098b
DDM
5532 { "include-partitions", required_argument, NULL, ARG_INCLUDE_PARTITIONS },
5533 { "exclude-partitions", required_argument, NULL, ARG_EXCLUDE_PARTITIONS },
7d505753 5534 { "skip-partitions", required_argument, NULL, ARG_SKIP_PARTITIONS },
e594a3b1
LP
5535 {}
5536 };
5537
a26f4a49 5538 int c, r, dry_run = -1;
e594a3b1
LP
5539
5540 assert(argc >= 0);
5541 assert(argv);
5542
5543 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
5544
5545 switch (c) {
5546
5547 case 'h':
5548 return help();
5549
5550 case ARG_VERSION:
5551 return version();
5552
896e678b
LP
5553 case ARG_NO_PAGER:
5554 arg_pager_flags |= PAGER_DISABLE;
5555 break;
5556
5557 case ARG_NO_LEGEND:
5558 arg_legend = false;
5559 break;
5560
e594a3b1 5561 case ARG_DRY_RUN:
599c7c54 5562 r = parse_boolean_argument("--dry-run=", optarg, &arg_dry_run);
e594a3b1 5563 if (r < 0)
599c7c54 5564 return r;
e594a3b1
LP
5565 break;
5566
5567 case ARG_EMPTY:
5568 if (isempty(optarg) || streq(optarg, "refuse"))
5569 arg_empty = EMPTY_REFUSE;
5570 else if (streq(optarg, "allow"))
5571 arg_empty = EMPTY_ALLOW;
5572 else if (streq(optarg, "require"))
5573 arg_empty = EMPTY_REQUIRE;
5574 else if (streq(optarg, "force"))
5575 arg_empty = EMPTY_FORCE;
a26f4a49
LP
5576 else if (streq(optarg, "create")) {
5577 arg_empty = EMPTY_CREATE;
5578
5579 if (dry_run < 0)
5580 dry_run = false; /* Imply --dry-run=no if we create the loopback file
5581 * anew. After all we cannot really break anyone's
5582 * partition tables that way. */
5583 } else
e594a3b1
LP
5584 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
5585 "Failed to parse --empty= parameter: %s", optarg);
5586 break;
5587
5588 case ARG_DISCARD:
599c7c54 5589 r = parse_boolean_argument("--discard=", optarg, &arg_discard);
e594a3b1 5590 if (r < 0)
599c7c54 5591 return r;
e594a3b1
LP
5592 break;
5593
5594 case ARG_FACTORY_RESET:
c3470872 5595 r = parse_boolean_argument("--factory-reset=", optarg, NULL);
e594a3b1 5596 if (r < 0)
c3470872 5597 return r;
e594a3b1
LP
5598 arg_factory_reset = r;
5599 break;
5600
5601 case ARG_CAN_FACTORY_RESET:
5602 arg_can_factory_reset = true;
5603 break;
5604
5605 case ARG_ROOT:
252d6267
LP
5606 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_root);
5607 if (r < 0)
5608 return r;
5609 break;
5610
5611 case ARG_IMAGE:
5612 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
e594a3b1
LP
5613 if (r < 0)
5614 return r;
5615 break;
5616
5617 case ARG_SEED:
5618 if (isempty(optarg)) {
5619 arg_seed = SD_ID128_NULL;
5620 arg_randomize = false;
5621 } else if (streq(optarg, "random"))
5622 arg_randomize = true;
5623 else {
5624 r = sd_id128_from_string(optarg, &arg_seed);
5625 if (r < 0)
5626 return log_error_errno(r, "Failed to parse seed: %s", optarg);
5627
5628 arg_randomize = false;
5629 }
5630
5631 break;
5632
5633 case ARG_PRETTY:
c3470872 5634 r = parse_boolean_argument("--pretty=", optarg, NULL);
e594a3b1 5635 if (r < 0)
c3470872 5636 return r;
e594a3b1
LP
5637 arg_pretty = r;
5638 break;
5639
224c853f
RP
5640 case ARG_DEFINITIONS: {
5641 _cleanup_free_ char *path = NULL;
5642 r = parse_path_argument(optarg, false, &path);
e594a3b1
LP
5643 if (r < 0)
5644 return r;
224c853f
RP
5645 if (strv_consume(&arg_definitions, TAKE_PTR(path)) < 0)
5646 return log_oom();
e594a3b1 5647 break;
224c853f 5648 }
e594a3b1 5649
a26f4a49
LP
5650 case ARG_SIZE: {
5651 uint64_t parsed, rounded;
5652
170c9823
LP
5653 if (streq(optarg, "auto")) {
5654 arg_size = UINT64_MAX;
5655 arg_size_auto = true;
5656 break;
5657 }
5658
a26f4a49
LP
5659 r = parse_size(optarg, 1024, &parsed);
5660 if (r < 0)
5661 return log_error_errno(r, "Failed to parse --size= parameter: %s", optarg);
5662
5663 rounded = round_up_size(parsed, 4096);
5664 if (rounded == 0)
5665 return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Specified image size too small, refusing.");
5666 if (rounded == UINT64_MAX)
5667 return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Specified image size too large, refusing.");
5668
5669 if (rounded != parsed)
e2341b6b
DT
5670 log_warning("Specified size is not a multiple of 4096, rounding up automatically. (%" PRIu64 " %s %" PRIu64 ")",
5671 parsed, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), rounded);
a26f4a49
LP
5672
5673 arg_size = rounded;
170c9823 5674 arg_size_auto = false;
a26f4a49
LP
5675 break;
5676 }
b9df3536 5677
a015fbe7 5678 case ARG_JSON:
b1e8f46c 5679 r = parse_json_argument(optarg, &arg_json_format_flags);
6a01ea4a
LP
5680 if (r <= 0)
5681 return r;
a015fbe7
TH
5682
5683 break;
5684
b9df3536
LP
5685 case ARG_KEY_FILE: {
5686 _cleanup_(erase_and_freep) char *k = NULL;
5687 size_t n = 0;
5688
8b3c3a49 5689 r = read_full_file_full(
986311c2 5690 AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
8b3c3a49
LP
5691 READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
5692 NULL,
5693 &k, &n);
b9df3536
LP
5694 if (r < 0)
5695 return log_error_errno(r, "Failed to read key file '%s': %m", optarg);
5696
5697 erase_and_free(arg_key);
5698 arg_key = TAKE_PTR(k);
5699 arg_key_size = n;
5700 break;
5701 }
a26f4a49 5702
b456191d
DDM
5703 case ARG_PRIVATE_KEY: {
5704 _cleanup_(erase_and_freep) char *k = NULL;
5705 size_t n = 0;
5706
5707 r = read_full_file_full(
5708 AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
5709 READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
5710 NULL,
5711 &k, &n);
5712 if (r < 0)
5713 return log_error_errno(r, "Failed to read key file '%s': %m", optarg);
5714
5715 EVP_PKEY_free(arg_private_key);
5716 arg_private_key = NULL;
5717 r = parse_private_key(k, n, &arg_private_key);
5718 if (r < 0)
5719 return r;
5720 break;
5721 }
5722
5723 case ARG_CERTIFICATE: {
5724 _cleanup_free_ char *cert = NULL;
5725 size_t n = 0;
5726
5727 r = read_full_file_full(
5728 AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
5729 READ_FULL_FILE_CONNECT_SOCKET,
5730 NULL,
5731 &cert, &n);
5732 if (r < 0)
5733 return log_error_errno(r, "Failed to read certificate file '%s': %m", optarg);
5734
5735 X509_free(arg_certificate);
5736 arg_certificate = NULL;
5737 r = parse_x509_certificate(cert, n, &arg_certificate);
5738 if (r < 0)
5739 return r;
5740 break;
5741 }
5742
889914ef
LP
5743 case ARG_TPM2_DEVICE: {
5744 _cleanup_free_ char *device = NULL;
5745
5746 if (streq(optarg, "list"))
5747 return tpm2_list_devices();
5748
5749 if (!streq(optarg, "auto")) {
5750 device = strdup(optarg);
5751 if (!device)
5752 return log_oom();
5753 }
5754
5755 free(arg_tpm2_device);
5756 arg_tpm2_device = TAKE_PTR(device);
5757 break;
5758 }
5759
222a951f
LP
5760 case ARG_TPM2_PCRS:
5761 r = tpm2_parse_pcr_argument(optarg, &arg_tpm2_pcr_mask);
889914ef
LP
5762 if (r < 0)
5763 return r;
5764
889914ef 5765 break;
889914ef 5766
02ef97cd
LP
5767 case ARG_TPM2_PUBLIC_KEY:
5768 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_public_key);
5769 if (r < 0)
5770 return r;
5771
5772 break;
5773
5774 case ARG_TPM2_PUBLIC_KEY_PCRS:
5775 r = tpm2_parse_pcr_argument(optarg, &arg_tpm2_public_key_pcr_mask);
5776 if (r < 0)
5777 return r;
5778
5779 break;
5780
4cee8333
DDM
5781 case ARG_SPLIT:
5782 r = parse_boolean_argument("--split=", optarg, NULL);
5783 if (r < 0)
5784 return r;
5785
5786 arg_split = r;
5787 break;
5788
81d1098b
DDM
5789 case ARG_INCLUDE_PARTITIONS:
5790 if (arg_filter_partitions_type == FILTER_PARTITIONS_EXCLUDE)
5791 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
5792 "Combination of --include-partitions= and --exclude-partitions= is invalid.");
5793
220780db 5794 r = parse_partition_types(optarg, &arg_filter_partitions, &arg_n_filter_partitions);
81d1098b
DDM
5795 if (r < 0)
5796 return r;
5797
5798 arg_filter_partitions_type = FILTER_PARTITIONS_INCLUDE;
5799
5800 break;
5801
5802 case ARG_EXCLUDE_PARTITIONS:
5803 if (arg_filter_partitions_type == FILTER_PARTITIONS_INCLUDE)
5804 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
5805 "Combination of --include-partitions= and --exclude-partitions= is invalid.");
5806
220780db 5807 r = parse_partition_types(optarg, &arg_filter_partitions, &arg_n_filter_partitions);
81d1098b
DDM
5808 if (r < 0)
5809 return r;
5810
5811 arg_filter_partitions_type = FILTER_PARTITIONS_EXCLUDE;
5812
5813 break;
5814
7d505753
DDM
5815 case ARG_SKIP_PARTITIONS:
5816 r = parse_partition_types(optarg, &arg_skip_partitions, &arg_n_skip_partitions);
5817 if (r < 0)
5818 return r;
5819
5820 break;
5821
e594a3b1
LP
5822 case '?':
5823 return -EINVAL;
5824
5825 default:
04499a70 5826 assert_not_reached();
e594a3b1
LP
5827 }
5828
5829 if (argc - optind > 1)
5830 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
5831 "Expected at most one argument, the path to the block device.");
5832
a26f4a49 5833 if (arg_factory_reset > 0 && IN_SET(arg_empty, EMPTY_FORCE, EMPTY_REQUIRE, EMPTY_CREATE))
e594a3b1 5834 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
a26f4a49 5835 "Combination of --factory-reset=yes and --empty=force/--empty=require/--empty=create is invalid.");
e594a3b1
LP
5836
5837 if (arg_can_factory_reset)
a26f4a49
LP
5838 arg_dry_run = true; /* When --can-factory-reset is specified we don't make changes, hence
5839 * non-dry-run mode makes no sense. Thus, imply dry run mode so that we
5840 * open things strictly read-only. */
5841 else if (dry_run >= 0)
5842 arg_dry_run = dry_run;
5843
170c9823 5844 if (arg_empty == EMPTY_CREATE && (arg_size == UINT64_MAX && !arg_size_auto))
a26f4a49
LP
5845 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
5846 "If --empty=create is specified, --size= must be specified, too.");
e594a3b1 5847
252d6267
LP
5848 if (arg_image && arg_root)
5849 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
5850 else if (!arg_image && !arg_root && in_initrd()) {
8f47e32a
LP
5851
5852 /* By default operate on /sysusr/ or /sysroot/ when invoked in the initrd. We prefer the
5853 * former, if it is mounted, so that we have deterministic behaviour on systems where /usr/
5854 * is vendor-supplied but the root fs formatted on first boot. */
5855 r = path_is_mount_point("/sysusr/usr", NULL, 0);
5856 if (r <= 0) {
5857 if (r < 0 && r != -ENOENT)
5858 log_debug_errno(r, "Unable to determine whether /sysusr/usr is a mount point, assuming it is not: %m");
5859
5860 arg_root = strdup("/sysroot");
5861 } else
5862 arg_root = strdup("/sysusr");
252d6267
LP
5863 if (!arg_root)
5864 return log_oom();
5865 }
5866
e594a3b1 5867 arg_node = argc > optind ? argv[optind] : NULL;
a26f4a49 5868
252d6267 5869 if (IN_SET(arg_empty, EMPTY_FORCE, EMPTY_REQUIRE, EMPTY_CREATE) && !arg_node && !arg_image)
a26f4a49
LP
5870 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
5871 "A path to a device node or loopback file must be specified when --empty=force, --empty=require or --empty=create are used.");
5872
4cee8333
DDM
5873 if (arg_split && !arg_node)
5874 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
5875 "A path to a loopback file must be specified when --split is used.");
5876
889914ef
LP
5877 if (arg_tpm2_pcr_mask == UINT32_MAX)
5878 arg_tpm2_pcr_mask = TPM2_PCR_MASK_DEFAULT;
02ef97cd
LP
5879 if (arg_tpm2_public_key_pcr_mask == UINT32_MAX)
5880 arg_tpm2_public_key_pcr_mask = UINT32_C(1) << TPM_PCR_INDEX_KERNEL_IMAGE;
889914ef 5881
a26d463d
DDM
5882 if (arg_pretty < 0 && isatty(STDOUT_FILENO))
5883 arg_pretty = true;
5884
e594a3b1
LP
5885 return 1;
5886}
5887
5888static int parse_proc_cmdline_factory_reset(void) {
5889 bool b;
5890 int r;
5891
5892 if (arg_factory_reset >= 0) /* Never override what is specified on the process command line */
5893 return 0;
5894
5895 if (!in_initrd()) /* Never honour kernel command line factory reset request outside of the initrd */
5896 return 0;
5897
5898 r = proc_cmdline_get_bool("systemd.factory_reset", &b);
5899 if (r < 0)
5900 return log_error_errno(r, "Failed to parse systemd.factory_reset kernel command line argument: %m");
5901 if (r > 0) {
5902 arg_factory_reset = b;
5903
5904 if (b)
5905 log_notice("Honouring factory reset requested via kernel command line.");
5906 }
5907
5908 return 0;
5909}
5910
5911static int parse_efi_variable_factory_reset(void) {
5912 _cleanup_free_ char *value = NULL;
5913 int r;
5914
5915 if (arg_factory_reset >= 0) /* Never override what is specified on the process command line */
5916 return 0;
5917
5918 if (!in_initrd()) /* Never honour EFI variable factory reset request outside of the initrd */
5919 return 0;
5920
e6f055cb 5921 r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE(FactoryReset), &value);
e594a3b1
LP
5922 if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
5923 return 0;
5924 if (r < 0)
5925 return log_error_errno(r, "Failed to read EFI variable FactoryReset: %m");
5926
5927 r = parse_boolean(value);
5928 if (r < 0)
5929 return log_error_errno(r, "Failed to parse EFI variable FactoryReset: %m");
5930
5931 arg_factory_reset = r;
5932 if (r)
111a3aae 5933 log_notice("Factory reset requested via EFI variable FactoryReset.");
e594a3b1
LP
5934
5935 return 0;
5936}
5937
5938static int remove_efi_variable_factory_reset(void) {
5939 int r;
5940
e6f055cb 5941 r = efi_set_variable(EFI_SYSTEMD_VARIABLE(FactoryReset), NULL, 0);
e594a3b1
LP
5942 if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
5943 return 0;
5944 if (r < 0)
5945 return log_error_errno(r, "Failed to remove EFI variable FactoryReset: %m");
5946
5947 log_info("Successfully unset EFI variable FactoryReset.");
5948 return 0;
5949}
5950
252d6267
LP
5951static int acquire_root_devno(
5952 const char *p,
5953 const char *root,
5954 int mode,
5955 char **ret,
5956 int *ret_fd) {
5957
5958 _cleanup_free_ char *found_path = NULL;
5959 dev_t devno, fd_devno = MODE_INVALID;
e594a3b1
LP
5960 _cleanup_close_ int fd = -1;
5961 struct stat st;
e594a3b1
LP
5962 int r;
5963
a26f4a49
LP
5964 assert(p);
5965 assert(ret);
5966 assert(ret_fd);
5967
252d6267 5968 fd = chase_symlinks_and_open(p, root, CHASE_PREFIX_ROOT, mode, &found_path);
e594a3b1 5969 if (fd < 0)
252d6267 5970 return fd;
e594a3b1
LP
5971
5972 if (fstat(fd, &st) < 0)
5973 return -errno;
5974
5975 if (S_ISREG(st.st_mode)) {
252d6267 5976 *ret = TAKE_PTR(found_path);
a26f4a49 5977 *ret_fd = TAKE_FD(fd);
e594a3b1
LP
5978 return 0;
5979 }
5980
252d6267
LP
5981 if (S_ISBLK(st.st_mode)) {
5982 /* Refuse referencing explicit block devices if a root dir is specified, after all we should
5c08da58 5983 * not be able to leave the image the root path constrains us to. */
252d6267
LP
5984 if (root)
5985 return -EPERM;
5986
a26f4a49 5987 fd_devno = devno = st.st_rdev;
252d6267 5988 } else if (S_ISDIR(st.st_mode)) {
e594a3b1
LP
5989
5990 devno = st.st_dev;
a26f4a49 5991 if (major(devno) == 0) {
e594a3b1
LP
5992 r = btrfs_get_block_device_fd(fd, &devno);
5993 if (r == -ENOTTY) /* not btrfs */
5994 return -ENODEV;
5995 if (r < 0)
5996 return r;
5997 }
e594a3b1
LP
5998 } else
5999 return -ENOTBLK;
6000
6001 /* From dm-crypt to backing partition */
6002 r = block_get_originating(devno, &devno);
8e5f3cec
LP
6003 if (r == -ENOENT)
6004 log_debug_errno(r, "Device '%s' has no dm-crypt/dm-verity device, no need to look for underlying block device.", p);
6005 else if (r < 0)
e594a3b1
LP
6006 log_debug_errno(r, "Failed to find underlying block device for '%s', ignoring: %m", p);
6007
6008 /* From partition to whole disk containing it */
6009 r = block_get_whole_disk(devno, &devno);
6010 if (r < 0)
162392b7 6011 log_debug_errno(r, "Failed to find whole disk block device for '%s', ignoring: %m", p);
e594a3b1 6012
4fe46c34 6013 r = devname_from_devnum(S_IFBLK, devno, ret);
a26f4a49
LP
6014 if (r < 0)
6015 return log_debug_errno(r, "Failed to determine canonical path for '%s': %m", p);
6016
6bbae9f8 6017 /* Only if we still look at the same block device we can reuse the fd. Otherwise return an
a26f4a49 6018 * invalidated fd. */
f5fbe71d 6019 *ret_fd = fd_devno != MODE_INVALID && fd_devno == devno ? TAKE_FD(fd) : -1;
a26f4a49 6020 return 0;
e594a3b1
LP
6021}
6022
cc751c75 6023static int find_root(Context *context) {
54632d2e 6024 _cleanup_free_ char *device = NULL;
5980d463 6025 int r;
e594a3b1 6026
cc751c75 6027 assert(context);
a26f4a49 6028
e594a3b1 6029 if (arg_node) {
a26f4a49
LP
6030 if (arg_empty == EMPTY_CREATE) {
6031 _cleanup_close_ int fd = -1;
6032 _cleanup_free_ char *s = NULL;
6033
6034 s = strdup(arg_node);
6035 if (!s)
6036 return log_oom();
6037
5332d7c6 6038 fd = open(arg_node, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOFOLLOW, 0666);
a26f4a49
LP
6039 if (fd < 0)
6040 return log_error_errno(errno, "Failed to create '%s': %m", arg_node);
6041
cc751c75
DDM
6042 context->node = TAKE_PTR(s);
6043 context->backing_fd = TAKE_FD(fd);
a26f4a49
LP
6044 return 0;
6045 }
6046
252d6267
LP
6047 /* Note that we don't specify a root argument here: if the user explicitly configured a node
6048 * we'll take it relative to the host, not the image */
cc751c75 6049 r = acquire_root_devno(arg_node, NULL, O_RDONLY|O_CLOEXEC, &context->node, &context->backing_fd);
67f0ac8c
LP
6050 if (r == -EUCLEAN)
6051 return btrfs_log_dev_root(LOG_ERR, r, arg_node);
e594a3b1 6052 if (r < 0)
aa2a74ad 6053 return log_error_errno(r, "Failed to open file or determine backing device of %s: %m", arg_node);
e594a3b1
LP
6054
6055 return 0;
6056 }
6057
a26f4a49
LP
6058 assert(IN_SET(arg_empty, EMPTY_REFUSE, EMPTY_ALLOW));
6059
54632d2e
KK
6060 /* If the root mount has been replaced by some form of volatile file system (overlayfs), the
6061 * original root block device node is symlinked in /run/systemd/volatile-root. Let's read that
6062 * here. */
6063 r = readlink_malloc("/run/systemd/volatile-root", &device);
6064 if (r == -ENOENT) { /* volatile-root not found */
6065 /* Let's search for the root device. We look for two cases here: first in /, and then in /usr. The
6066 * latter we check for cases where / is a tmpfs and only /usr is an actual persistent block device
6067 * (think: volatile setups) */
e594a3b1 6068
54632d2e 6069 FOREACH_STRING(p, "/", "/usr") {
e594a3b1 6070
cc751c75
DDM
6071 r = acquire_root_devno(p, arg_root, O_RDONLY|O_DIRECTORY|O_CLOEXEC, &context->node,
6072 &context->backing_fd);
54632d2e
KK
6073 if (r < 0) {
6074 if (r == -EUCLEAN)
6075 return btrfs_log_dev_root(LOG_ERR, r, p);
6076 if (r != -ENODEV)
6077 return log_error_errno(r, "Failed to determine backing device of %s: %m", p);
6078 } else
6079 return 0;
6080 }
6081 } else if (r < 0)
6082 return log_error_errno(r, "Failed to read symlink /run/systemd/volatile-root: %m");
6083 else {
cc751c75 6084 r = acquire_root_devno(device, NULL, O_RDONLY|O_CLOEXEC, &context->node, &context->backing_fd);
54632d2e
KK
6085 if (r == -EUCLEAN)
6086 return btrfs_log_dev_root(LOG_ERR, r, device);
6087 if (r < 0)
6088 return log_error_errno(r, "Failed to open file or determine backing device of %s: %m", device);
6089
6090 return 0;
e594a3b1
LP
6091 }
6092
6093 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "Failed to discover root block device.");
6094}
6095
f9b3afae 6096static int resize_pt(int fd) {
f9b3afae
LP
6097 _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
6098 int r;
6099
6100 /* After resizing the backing file we need to resize the partition table itself too, so that it takes
6101 * possession of the enlarged backing file. For this it suffices to open the device with libfdisk and
6102 * immediately write it again, with no changes. */
6103
f8cf3d19 6104 r = fdisk_new_context_fd(fd, /* read_only= */ false, &c);
f9b3afae 6105 if (r < 0)
ddb6eeaf 6106 return log_error_errno(r, "Failed to open device '%s': %m", FORMAT_PROC_FD_PATH(fd));
f9b3afae
LP
6107
6108 r = fdisk_has_label(c);
6109 if (r < 0)
ddb6eeaf 6110 return log_error_errno(r, "Failed to determine whether disk '%s' has a disk label: %m", FORMAT_PROC_FD_PATH(fd));
f9b3afae
LP
6111 if (r == 0) {
6112 log_debug("Not resizing partition table, as there currently is none.");
6113 return 0;
6114 }
6115
6116 r = fdisk_write_disklabel(c);
6117 if (r < 0)
6118 return log_error_errno(r, "Failed to write resized partition table: %m");
6119
6120 log_info("Resized partition table.");
6121 return 1;
6122}
6123
252d6267
LP
6124static int resize_backing_fd(
6125 const char *node, /* The primary way we access the disk image to operate on */
6126 int *fd, /* An O_RDONLY fd referring to that inode */
6127 const char *backing_file, /* If the above refers to a loopback device, the backing regular file for that, which we can grow */
6128 LoopDevice *loop_device) {
6129
a26f4a49 6130 _cleanup_close_ int writable_fd = -1;
252d6267 6131 uint64_t current_size;
a26f4a49
LP
6132 struct stat st;
6133 int r;
6134
6135 assert(node);
6136 assert(fd);
6137
6138 if (arg_size == UINT64_MAX) /* Nothing to do */
6139 return 0;
6140
6141 if (*fd < 0) {
6142 /* Open the file if we haven't opened it yet. Note that we open it read-only here, just to
6143 * keep a reference to the file we can pass around. */
6144 *fd = open(node, O_RDONLY|O_CLOEXEC);
6145 if (*fd < 0)
6146 return log_error_errno(errno, "Failed to open '%s' in order to adjust size: %m", node);
6147 }
6148
6149 if (fstat(*fd, &st) < 0)
6150 return log_error_errno(errno, "Failed to stat '%s': %m", node);
6151
252d6267
LP
6152 if (S_ISBLK(st.st_mode)) {
6153 if (!backing_file)
6154 return log_error_errno(SYNTHETIC_ERRNO(EBADF), "Cannot resize block device '%s'.", node);
6155
6156 assert(loop_device);
a26f4a49 6157
252d6267
LP
6158 if (ioctl(*fd, BLKGETSIZE64, &current_size) < 0)
6159 return log_error_errno(errno, "Failed to determine size of block device %s: %m", node);
6160 } else {
6161 r = stat_verify_regular(&st);
6162 if (r < 0)
6163 return log_error_errno(r, "Specified path '%s' is not a regular file or loopback block device, cannot resize: %m", node);
6164
6165 assert(!backing_file);
6166 assert(!loop_device);
6167 current_size = st.st_size;
6168 }
6169
252d6267 6170 if (current_size >= arg_size) {
2b59bf51
ZJS
6171 log_info("File '%s' already is of requested size or larger, not growing. (%s >= %s)",
6172 node, FORMAT_BYTES(current_size), FORMAT_BYTES(arg_size));
a26f4a49
LP
6173 return 0;
6174 }
6175
252d6267
LP
6176 if (S_ISBLK(st.st_mode)) {
6177 assert(backing_file);
6178
6179 /* This is a loopback device. We can't really grow those directly, but we can grow the
6180 * backing file, hence let's do that. */
6181
6182 writable_fd = open(backing_file, O_WRONLY|O_CLOEXEC|O_NONBLOCK);
6183 if (writable_fd < 0)
6184 return log_error_errno(errno, "Failed to open backing file '%s': %m", backing_file);
6185
6186 if (fstat(writable_fd, &st) < 0)
6187 return log_error_errno(errno, "Failed to stat() backing file '%s': %m", backing_file);
6188
6189 r = stat_verify_regular(&st);
6190 if (r < 0)
6191 return log_error_errno(r, "Backing file '%s' of block device is not a regular file: %m", backing_file);
6192
6193 if ((uint64_t) st.st_size != current_size)
6194 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2b59bf51
ZJS
6195 "Size of backing file '%s' of loopback block device '%s' don't match, refusing.",
6196 node, backing_file);
252d6267
LP
6197 } else {
6198 assert(S_ISREG(st.st_mode));
6199 assert(!backing_file);
a26f4a49 6200
252d6267
LP
6201 /* The file descriptor is read-only. In order to grow the file we need to have a writable fd. We
6202 * reopen the file for that temporarily. We keep the writable fd only open for this operation though,
6203 * as fdisk can't accept it anyway. */
6204
6205 writable_fd = fd_reopen(*fd, O_WRONLY|O_CLOEXEC);
6206 if (writable_fd < 0)
6207 return log_error_errno(writable_fd, "Failed to reopen backing file '%s' writable: %m", node);
6208 }
a26f4a49
LP
6209
6210 if (!arg_discard) {
6211 if (fallocate(writable_fd, 0, 0, arg_size) < 0) {
6212 if (!ERRNO_IS_NOT_SUPPORTED(errno))
6213 return log_error_errno(errno, "Failed to grow '%s' from %s to %s by allocation: %m",
2b59bf51 6214 node, FORMAT_BYTES(current_size), FORMAT_BYTES(arg_size));
a26f4a49
LP
6215
6216 /* Fallback to truncation, if fallocate() is not supported. */
6217 log_debug("Backing file system does not support fallocate(), falling back to ftruncate().");
6218 } else {
252d6267 6219 if (current_size == 0) /* Likely regular file just created by us */
2b59bf51 6220 log_info("Allocated %s for '%s'.", FORMAT_BYTES(arg_size), node);
a26f4a49 6221 else
2b59bf51
ZJS
6222 log_info("File '%s' grown from %s to %s by allocation.",
6223 node, FORMAT_BYTES(current_size), FORMAT_BYTES(arg_size));
a26f4a49 6224
252d6267 6225 goto done;
a26f4a49
LP
6226 }
6227 }
6228
6229 if (ftruncate(writable_fd, arg_size) < 0)
6230 return log_error_errno(errno, "Failed to grow '%s' from %s to %s by truncation: %m",
2b59bf51 6231 node, FORMAT_BYTES(current_size), FORMAT_BYTES(arg_size));
a26f4a49 6232
252d6267 6233 if (current_size == 0) /* Likely regular file just created by us */
2b59bf51 6234 log_info("Sized '%s' to %s.", node, FORMAT_BYTES(arg_size));
252d6267 6235 else
2b59bf51
ZJS
6236 log_info("File '%s' grown from %s to %s by truncation.",
6237 node, FORMAT_BYTES(current_size), FORMAT_BYTES(arg_size));
252d6267
LP
6238
6239done:
f9b3afae
LP
6240 r = resize_pt(writable_fd);
6241 if (r < 0)
6242 return r;
6243
252d6267
LP
6244 if (loop_device) {
6245 r = loop_device_refresh_size(loop_device, UINT64_MAX, arg_size);
6246 if (r < 0)
6247 return log_error_errno(r, "Failed to update loop device size: %m");
6248 }
a26f4a49
LP
6249
6250 return 1;
6251}
6252
170c9823 6253static int determine_auto_size(Context *c) {
994b3031 6254 uint64_t sum;
170c9823 6255
ac33e147 6256 assert(c);
170c9823 6257
994b3031
LP
6258 sum = round_up_size(GPT_METADATA_SIZE, 4096);
6259
170c9823
LP
6260 LIST_FOREACH(partitions, p, c->partitions) {
6261 uint64_t m;
6262
6263 if (p->dropped)
6264 continue;
6265
994b3031 6266 m = partition_min_size_with_padding(c, p);
170c9823
LP
6267 if (m > UINT64_MAX - sum)
6268 return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Image would grow too large, refusing.");
6269
6270 sum += m;
6271 }
6272
2b59bf51
ZJS
6273 if (c->total != UINT64_MAX)
6274 /* Image already allocated? Then show its size. */
6275 log_info("Automatically determined minimal disk image size as %s, current image size is %s.",
6276 FORMAT_BYTES(sum), FORMAT_BYTES(c->total));
6277 else
6278 /* If the image is being created right now, then it has no previous size, suppress any comment about it hence. */
6279 log_info("Automatically determined minimal disk image size as %s.",
6280 FORMAT_BYTES(sum));
170c9823
LP
6281
6282 arg_size = sum;
6283 return 0;
6284}
6285
e594a3b1 6286static int run(int argc, char *argv[]) {
252d6267 6287 _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
252d6267 6288 _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
e594a3b1 6289 _cleanup_(context_freep) Context* context = NULL;
cc751c75 6290 bool node_is_our_loop = false;
e594a3b1
LP
6291 int r;
6292
6293 log_show_color(true);
6294 log_parse_environment();
6295 log_open();
6296
e594a3b1
LP
6297 r = parse_argv(argc, argv);
6298 if (r <= 0)
6299 return r;
6300
6301 r = parse_proc_cmdline_factory_reset();
6302 if (r < 0)
6303 return r;
6304
6305 r = parse_efi_variable_factory_reset();
6306 if (r < 0)
6307 return r;
6308
30f19400
LP
6309#if HAVE_LIBCRYPTSETUP
6310 cryptsetup_enable_logging(NULL);
6311#endif
6312
252d6267
LP
6313 if (arg_image) {
6314 assert(!arg_root);
6315
6316 /* Mount this strictly read-only: we shall modify the partition table, not the file
6317 * systems */
6318 r = mount_image_privately_interactively(
6319 arg_image,
6320 DISSECT_IMAGE_MOUNT_READ_ONLY |
6321 (arg_node ? DISSECT_IMAGE_DEVICE_READ_ONLY : 0) | /* If a different node to make changes to is specified let's open the device in read-only mode) */
6322 DISSECT_IMAGE_GPT_ONLY |
6323 DISSECT_IMAGE_RELAX_VAR_CHECK |
6324 DISSECT_IMAGE_USR_NO_ROOT |
6325 DISSECT_IMAGE_REQUIRE_ROOT,
6326 &mounted_dir,
e330f97a 6327 &loop_device);
252d6267
LP
6328 if (r < 0)
6329 return r;
6330
6331 arg_root = strdup(mounted_dir);
6332 if (!arg_root)
6333 return log_oom();
6334
6335 if (!arg_node) {
6336 arg_node = strdup(loop_device->node);
6337 if (!arg_node)
6338 return log_oom();
6339
3d62af7d 6340 /* Remember that the device we are about to manipulate is actually the one we
252d6267
LP
6341 * allocated here, and thus to increase its backing file we know what to do */
6342 node_is_our_loop = true;
6343 }
6344 }
6345
e594a3b1
LP
6346 context = context_new(arg_seed);
6347 if (!context)
6348 return log_oom();
6349
224c853f
RP
6350 strv_uniq(arg_definitions);
6351
e594a3b1
LP
6352 r = context_read_definitions(context, arg_definitions, arg_root);
6353 if (r < 0)
6354 return r;
6355
cc751c75 6356 r = find_root(context);
0ae5ffe0
YW
6357 if (r < 0)
6358 return r;
6359
a26f4a49 6360 if (arg_size != UINT64_MAX) {
252d6267 6361 r = resize_backing_fd(
cc751c75
DDM
6362 context->node,
6363 &context->backing_fd,
252d6267
LP
6364 node_is_our_loop ? arg_image : NULL,
6365 node_is_our_loop ? loop_device : NULL);
a26f4a49
LP
6366 if (r < 0)
6367 return r;
6368 }
6369
cc751c75 6370 r = context_load_partition_table(context);
e594a3b1
LP
6371 if (r == -EHWPOISON)
6372 return 77; /* Special return value which means "Not GPT, so not doing anything". This isn't
6373 * really an error when called at boot. */
6374 if (r < 0)
6375 return r;
cc751c75 6376 context->from_scratch = r > 0; /* Starting from scratch */
e594a3b1
LP
6377
6378 if (arg_can_factory_reset) {
6379 r = context_can_factory_reset(context);
6380 if (r < 0)
6381 return r;
6382 if (r == 0)
6383 return EXIT_FAILURE;
6384
6385 return 0;
6386 }
6387
cc751c75 6388 r = context_factory_reset(context);
e594a3b1
LP
6389 if (r < 0)
6390 return r;
6391 if (r > 0) {
6392 /* We actually did a factory reset! */
6393 r = remove_efi_variable_factory_reset();
6394 if (r < 0)
6395 return r;
6396
6397 /* Reload the reduced partition table */
6398 context_unload_partition_table(context);
cc751c75 6399 r = context_load_partition_table(context);
e594a3b1
LP
6400 if (r < 0)
6401 return r;
6402 }
6403
e594a3b1
LP
6404 r = context_read_seed(context, arg_root);
6405 if (r < 0)
6406 return r;
6407
8bbbdfd7
DDM
6408 /* Make sure each partition has a unique UUID and unique label */
6409 r = context_acquire_partition_uuids_and_labels(context);
6410 if (r < 0)
6411 return r;
6412
c4a87b76
DDM
6413 r = context_minimize(context);
6414 if (r < 0)
6415 return r;
6416
757bc2e4 6417 /* Open all files to copy blocks from now, since we want to take their size into consideration */
5c08da58
LP
6418 r = context_open_copy_block_paths(
6419 context,
7802194a 6420 loop_device ? loop_device->devno : /* if --image= is specified, only allow partitions on the loopback device */
5c08da58
LP
6421 arg_root && !arg_image ? 0 : /* if --root= is specified, don't accept any block device */
6422 (dev_t) -1); /* if neither is specified, make no restrictions */
757bc2e4
LP
6423 if (r < 0)
6424 return r;
6425
170c9823
LP
6426 if (arg_size_auto) {
6427 r = determine_auto_size(context);
6428 if (r < 0)
6429 return r;
6430
6431 /* Flush out everything again, and let's grow the file first, then start fresh */
6432 context_unload_partition_table(context);
6433
ac33e147 6434 assert(arg_size != UINT64_MAX);
252d6267 6435 r = resize_backing_fd(
cc751c75
DDM
6436 context->node,
6437 &context->backing_fd,
252d6267
LP
6438 node_is_our_loop ? arg_image : NULL,
6439 node_is_our_loop ? loop_device : NULL);
170c9823
LP
6440 if (r < 0)
6441 return r;
6442
cc751c75 6443 r = context_load_partition_table(context);
170c9823
LP
6444 if (r < 0)
6445 return r;
6446 }
6447
e594a3b1
LP
6448 /* First try to fit new partitions in, dropping by priority until it fits */
6449 for (;;) {
14a4c4ed
LP
6450 uint64_t largest_free_area;
6451
6452 if (context_allocate_partitions(context, &largest_free_area))
e594a3b1
LP
6453 break; /* Success! */
6454
9ccceb9d 6455 if (!context_drop_or_foreignize_one_priority(context)) {
d17db7b2 6456 r = log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
14a4c4ed 6457 "Can't fit requested partitions into available free space (%s), refusing.",
2b59bf51 6458 FORMAT_BYTES(largest_free_area));
d17db7b2
LP
6459 determine_auto_size(context);
6460 return r;
6461 }
e594a3b1
LP
6462 }
6463
6464 /* Now assign free space according to the weight logic */
6465 r = context_grow_partitions(context);
6466 if (r < 0)
6467 return r;
6468
0b7f574f 6469 /* Now calculate where each new partition gets placed */
e594a3b1
LP
6470 context_place_partitions(context);
6471
cc751c75 6472 (void) context_dump(context, /*late=*/ false);
a26d463d 6473
cc751c75 6474 r = context_write_partition_table(context);
e594a3b1
LP
6475 if (r < 0)
6476 return r;
6477
cc751c75 6478 r = context_split(context);
4cee8333
DDM
6479 if (r < 0)
6480 return r;
6481
cc751c75 6482 (void) context_dump(context, /*late=*/ true);
b5b7879a 6483
a2d7c42e
DDM
6484 LIST_FOREACH(partitions, p, context->partitions)
6485 p->split_path = mfree(p->split_path);
6486
e594a3b1
LP
6487 return 0;
6488}
6489
6490DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);