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