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