]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/partition/repart.c
mkosi: update arch commit reference
[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 16#include "sd-id128.h"
309a747f 17#include "sd-json.h"
e594a3b1
LP
18
19#include "alloc-util.h"
20#include "blkid-util.h"
3a37a13a 21#include "blockdev-list.h"
e594a3b1
LP
22#include "blockdev-util.h"
23#include "btrfs-util.h"
d6b4d1c7 24#include "build.h"
f461a28d 25#include "chase.h"
b9c0b6c0 26#include "chattr-util.h"
e594a3b1
LP
27#include "conf-files.h"
28#include "conf-parser.h"
28db6fbf 29#include "constants.h"
1e2f3230 30#include "cryptsetup-util.h"
ca822829 31#include "device-util.h"
7176f06c 32#include "devnum-util.h"
5c08da58 33#include "dirent-util.h"
e594a3b1
LP
34#include "efivars.h"
35#include "errno-util.h"
36#include "fd-util.h"
4dc07c3a 37#include "fdisk-util.h"
b9df3536 38#include "fileio.h"
e594a3b1
LP
39#include "format-table.h"
40#include "format-util.h"
41#include "fs-util.h"
d8e32c47 42#include "glyph-util.h"
e594a3b1 43#include "gpt.h"
889914ef 44#include "hexdecoct.h"
ade99252 45#include "hmac.h"
e594a3b1 46#include "id128-util.h"
baa6a42d 47#include "initrd-util.h"
b456191d 48#include "io-util.h"
309a747f 49#include "json-util.h"
e594a3b1 50#include "list.h"
562881c1 51#include "logarithm.h"
53171c04 52#include "loop-util.h"
e594a3b1 53#include "main-func.h"
8a794850 54#include "mkdir.h"
53171c04 55#include "mkfs-util.h"
8a794850 56#include "mount-util.h"
5c08da58 57#include "mountpoint-util.h"
c0fad2d9 58#include "nulstr-util.h"
b456191d 59#include "openssl-util.h"
614b022c 60#include "parse-argument.h"
c3eaba2d 61#include "parse-helpers.h"
e594a3b1
LP
62#include "pretty-print.h"
63#include "proc-cmdline.h"
8a794850 64#include "process-util.h"
b9df3536 65#include "random-util.h"
170c9823 66#include "resize-fs.h"
95bfd3cd 67#include "rm-rf.h"
e594a3b1 68#include "sort-util.h"
e031166e 69#include "specifier.h"
e594a3b1 70#include "stdio-util.h"
889914ef 71#include "string-table.h"
e594a3b1
LP
72#include "string-util.h"
73#include "strv.h"
bf819d3a 74#include "sync-util.h"
e594a3b1 75#include "terminal-util.h"
e0e1f4f7
LP
76#include "tmpfile-util.h"
77#include "tpm2-pcr.h"
889914ef 78#include "tpm2-util.h"
8a794850 79#include "user-util.h"
e594a3b1
LP
80#include "utf8.h"
81
fb08381c 82/* If not configured otherwise use a minimal partition size of 10M */
b262cbe8 83#define DEFAULT_MIN_SIZE (10ULL*1024ULL*1024ULL)
fb08381c
LP
84
85/* Hard lower limit for new partition sizes */
b262cbe8 86#define HARD_MIN_SIZE 4096ULL
fb08381c 87
b456191d 88/* We know up front we're never going to put more than this in a verity sig partition. */
b262cbe8 89#define VERITY_SIG_SIZE (HARD_MIN_SIZE*4ULL)
b456191d 90
69e3234d 91/* libfdisk takes off slightly more than 1M of the disk size when creating a GPT disk label */
b262cbe8 92#define GPT_METADATA_SIZE (1044ULL*1024ULL)
170c9823
LP
93
94/* LUKS2 takes off 16M of the partition size with its metadata by default */
b262cbe8 95#define LUKS2_METADATA_SIZE (16ULL*1024ULL*1024ULL)
170c9823 96
48a09a8f
DDM
97/* To do LUKS2 offline encryption, we need to keep some extra free space at the end of the partition. */
98#define LUKS2_METADATA_KEEP_FREE (LUKS2_METADATA_SIZE*2ULL)
99
98e0456e
DDM
100/* LUKS2 volume key size. */
101#define VOLUME_KEY_SIZE (512ULL/8ULL)
102
93f125a6
DDM
103/* Use 4K as the default filesystem sector size because as long as the partitions are aligned to 4K, the
104 * filesystems will then also be compatible with sector sizes 512, 1024 and 2048. */
105#define DEFAULT_FILESYSTEM_SECTOR_SIZE 4096ULL
106
e57b7020
DDM
107#define APIVFS_TMP_DIRS_NULSTR "proc\0sys\0dev\0tmp\0run\0var/tmp\0"
108
e594a3b1
LP
109/* Note: When growing and placing new partitions we always align to 4K sector size. It's how newer hard disks
110 * are designed, and if everything is aligned to that performance is best. And for older hard disks with 512B
111 * sector size devices were generally assumed to have an even number of sectors, hence at the worst we'll
112 * waste 3K per partition, which is probably fine. */
113
634b8471 114typedef enum EmptyMode {
243dd1e9 115 EMPTY_UNSET, /* no choice has been made yet */
e594a3b1
LP
116 EMPTY_REFUSE, /* refuse empty disks, never create a partition table */
117 EMPTY_ALLOW, /* allow empty disks, create partition table if necessary */
118 EMPTY_REQUIRE, /* require an empty disk, create a partition table */
119 EMPTY_FORCE, /* make disk empty, erase everything, create a partition table always */
a26f4a49 120 EMPTY_CREATE, /* create disk as loopback file, create a partition table always */
634b8471
LP
121 _EMPTY_MODE_MAX,
122 _EMPTY_MODE_INVALID = -EINVAL,
123} EmptyMode;
e594a3b1 124
53538e33 125typedef enum FilterPartitionType {
81d1098b
DDM
126 FILTER_PARTITIONS_NONE,
127 FILTER_PARTITIONS_EXCLUDE,
128 FILTER_PARTITIONS_INCLUDE,
129 _FILTER_PARTITIONS_MAX,
130 _FILTER_PARTITIONS_INVALID = -EINVAL,
131} FilterPartitionsType;
132
634b8471 133static EmptyMode arg_empty = EMPTY_UNSET;
e594a3b1 134static bool arg_dry_run = true;
ecb4c5a6 135static char *arg_node = NULL;
e594a3b1 136static char *arg_root = NULL;
252d6267 137static char *arg_image = NULL;
224c853f 138static char **arg_definitions = NULL;
e594a3b1
LP
139static bool arg_discard = true;
140static bool arg_can_factory_reset = false;
141static int arg_factory_reset = -1;
142static sd_id128_t arg_seed = SD_ID128_NULL;
143static bool arg_randomize = false;
144static int arg_pretty = -1;
a26f4a49 145static uint64_t arg_size = UINT64_MAX;
170c9823 146static bool arg_size_auto = false;
309a747f 147static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
896e678b
LP
148static PagerFlags arg_pager_flags = 0;
149static bool arg_legend = true;
b9df3536
LP
150static void *arg_key = NULL;
151static size_t arg_key_size = 0;
b456191d 152static EVP_PKEY *arg_private_key = NULL;
a73144bb
LB
153static KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE;
154static char *arg_private_key_source = NULL;
b456191d 155static X509 *arg_certificate = NULL;
889914ef 156static char *arg_tpm2_device = NULL;
78fdf0f6
LP
157static uint32_t arg_tpm2_seal_key_handle = 0;
158static char *arg_tpm2_device_key = NULL;
9e437994
DS
159static Tpm2PCRValue *arg_tpm2_hash_pcr_values = NULL;
160static size_t arg_tpm2_n_hash_pcr_values = 0;
02ef97cd 161static char *arg_tpm2_public_key = NULL;
9e437994 162static uint32_t arg_tpm2_public_key_pcr_mask = 0;
404aea78 163static char *arg_tpm2_pcrlock = NULL;
4cee8333 164static bool arg_split = false;
9786dfe6 165static GptPartitionType *arg_filter_partitions = NULL;
d989dd76 166static size_t arg_n_filter_partitions = 0;
81d1098b 167static FilterPartitionsType arg_filter_partitions_type = FILTER_PARTITIONS_NONE;
9786dfe6 168static GptPartitionType *arg_defer_partitions = NULL;
8275334b 169static size_t arg_n_defer_partitions = 0;
e1878ef7 170static uint64_t arg_sector_size = 0;
84be0c71 171static ImagePolicy *arg_image_policy = NULL;
9786dfe6 172static Architecture arg_architecture = _ARCHITECTURE_INVALID;
fc10b158 173static int arg_offline = -1;
e1536d1f 174static char **arg_copy_from = NULL;
607343a1 175static char *arg_copy_source = NULL;
a121b331 176static char *arg_make_ddi = NULL;
1a0541d4
DDM
177static char *arg_generate_fstab = NULL;
178static char *arg_generate_crypttab = NULL;
e594a3b1 179
ecb4c5a6 180STATIC_DESTRUCTOR_REGISTER(arg_node, freep);
e594a3b1 181STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
252d6267 182STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
224c853f 183STATIC_DESTRUCTOR_REGISTER(arg_definitions, strv_freep);
b9df3536 184STATIC_DESTRUCTOR_REGISTER(arg_key, erase_and_freep);
b456191d 185STATIC_DESTRUCTOR_REGISTER(arg_private_key, EVP_PKEY_freep);
a73144bb 186STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
b456191d 187STATIC_DESTRUCTOR_REGISTER(arg_certificate, X509_freep);
889914ef 188STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
78fdf0f6 189STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device_key, freep);
9e437994 190STATIC_DESTRUCTOR_REGISTER(arg_tpm2_hash_pcr_values, freep);
02ef97cd 191STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
404aea78 192STATIC_DESTRUCTOR_REGISTER(arg_tpm2_pcrlock, freep);
81d1098b 193STATIC_DESTRUCTOR_REGISTER(arg_filter_partitions, freep);
a81f5ffd 194STATIC_DESTRUCTOR_REGISTER(arg_defer_partitions, freep);
84be0c71 195STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
e1536d1f 196STATIC_DESTRUCTOR_REGISTER(arg_copy_from, strv_freep);
607343a1 197STATIC_DESTRUCTOR_REGISTER(arg_copy_source, freep);
a121b331 198STATIC_DESTRUCTOR_REGISTER(arg_make_ddi, freep);
1a0541d4
DDM
199STATIC_DESTRUCTOR_REGISTER(arg_generate_fstab, freep);
200STATIC_DESTRUCTOR_REGISTER(arg_generate_crypttab, freep);
e594a3b1 201
e594a3b1 202typedef struct FreeArea FreeArea;
e594a3b1 203
889914ef
LP
204typedef enum EncryptMode {
205 ENCRYPT_OFF,
206 ENCRYPT_KEY_FILE,
207 ENCRYPT_TPM2,
208 ENCRYPT_KEY_FILE_TPM2,
209 _ENCRYPT_MODE_MAX,
2d93c20e 210 _ENCRYPT_MODE_INVALID = -EINVAL,
889914ef
LP
211} EncryptMode;
212
b5b7879a
DDM
213typedef enum VerityMode {
214 VERITY_OFF,
215 VERITY_DATA,
216 VERITY_HASH,
b456191d 217 VERITY_SIG,
b5b7879a
DDM
218 _VERITY_MODE_MAX,
219 _VERITY_MODE_INVALID = -EINVAL,
220} VerityMode;
221
5c33b686
DDM
222typedef enum MinimizeMode {
223 MINIMIZE_OFF,
224 MINIMIZE_BEST,
225 MINIMIZE_GUESS,
226 _MINIMIZE_MODE_MAX,
227 _MINIMIZE_MODE_INVALID = -EINVAL,
228} MinimizeMode;
229
1a0541d4
DDM
230typedef struct PartitionMountPoint {
231 char *where;
232 char *options;
233} PartitionMountPoint;
234
235static void partition_mountpoint_free_many(PartitionMountPoint *f, size_t n) {
236 assert(f || n == 0);
237
238 FOREACH_ARRAY(i, f, n) {
239 free(i->where);
240 free(i->options);
241 }
242
243 free(f);
244}
245
246typedef struct PartitionEncryptedVolume {
247 char *name;
248 char *keyfile;
249 char *options;
250} PartitionEncryptedVolume;
251
252static PartitionEncryptedVolume* partition_encrypted_volume_free(PartitionEncryptedVolume *c) {
253 if (!c)
254 return NULL;
255
256 free(c->name);
257 free(c->keyfile);
258 free(c->options);
259
260 return mfree(c);
261}
262
562881c1
DDM
263typedef enum SubvolumeFlags {
264 SUBVOLUME_RO = 1 << 0,
265 _SUBVOLUME_FLAGS_MASK = SUBVOLUME_RO,
266 _SUBVOLUME_FLAGS_INVALID = -EINVAL,
267 _SUBVOLUME_FLAGS_ERRNO_MAX = -ERRNO_MAX, /* Ensure the whole errno range fits into this enum */
268} SubvolumeFlags;
269
270static SubvolumeFlags subvolume_flags_from_string_one(const char *s) {
271 /* This is a bitmask (i.e. not dense), hence we don't use the "string-table.h" stuff here. */
272
273 assert(s);
274
275 if (streq(s, "ro"))
276 return SUBVOLUME_RO;
277
278 return _SUBVOLUME_FLAGS_INVALID;
279}
280
281static SubvolumeFlags subvolume_flags_from_string(const char *s) {
282 SubvolumeFlags flags = 0;
283 int r;
284
285 assert(s);
286
287 for (;;) {
288 _cleanup_free_ char *f = NULL;
289 SubvolumeFlags ff;
290
291 r = extract_first_word(&s, &f, ",", EXTRACT_DONT_COALESCE_SEPARATORS);
292 if (r < 0)
293 return r;
294 if (r == 0)
295 break;
296
297 ff = subvolume_flags_from_string_one(f);
298 if (ff < 0)
299 return -EBADRQC; /* recognizable error */
300
301 flags |= ff;
302 }
303
304 return flags;
305}
306
562881c1
DDM
307typedef struct Subvolume {
308 char *path;
309 SubvolumeFlags flags;
310} Subvolume;
311
312static Subvolume* subvolume_free(Subvolume *s) {
313 if (!s)
314 return NULL;
315
316 free(s->path);
317 return mfree(s);
318}
319
320DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(subvolume_hash_ops, char, path_hash_func, path_compare, Subvolume, subvolume_free);
321
448cfb7f 322typedef struct Partition {
e594a3b1 323 char *definition_path;
39fc0174 324 char **drop_in_files;
e594a3b1 325
22e932f4 326 GptPartitionType type;
e594a3b1 327 sd_id128_t current_uuid, new_uuid;
11749b61 328 bool new_uuid_is_set;
e594a3b1 329 char *current_label, *new_label;
81e04781
VH
330 sd_id128_t fs_uuid, luks_uuid, verity_uuid;
331 uint8_t verity_salt[SHA256_DIGEST_SIZE];
e594a3b1
LP
332
333 bool dropped;
334 bool factory_reset;
335 int32_t priority;
336
337 uint32_t weight, padding_weight;
338
339 uint64_t current_size, new_size;
340 uint64_t size_min, size_max;
341
342 uint64_t current_padding, new_padding;
343 uint64_t padding_min, padding_max;
344
345 uint64_t partno;
346 uint64_t offset;
347
348 struct fdisk_partition *current_partition;
349 struct fdisk_partition *new_partition;
350 FreeArea *padding_area;
351 FreeArea *allocated_to_area;
352
757bc2e4 353 char *copy_blocks_path;
7d07030e 354 bool copy_blocks_path_is_our_file;
5c08da58 355 bool copy_blocks_auto;
585c5c75 356 const char *copy_blocks_root;
757bc2e4 357 int copy_blocks_fd;
1e46985a 358 uint64_t copy_blocks_offset;
757bc2e4 359 uint64_t copy_blocks_size;
add090ea 360 uint64_t copy_blocks_done;
757bc2e4 361
53171c04 362 char *format;
8a794850 363 char **copy_files;
600bf76c
DDM
364 char **exclude_files_source;
365 char **exclude_files_target;
d83d8048 366 char **make_directories;
c64ddefd 367 char **make_symlinks;
562881c1 368 OrderedHashmap *subvolumes;
3799fa80 369 char *default_subvolume;
889914ef 370 EncryptMode encrypt;
b5b7879a
DDM
371 VerityMode verity;
372 char *verity_match_key;
5c33b686 373 MinimizeMode minimize;
c380047b
MC
374 uint64_t verity_data_block_size;
375 uint64_t verity_hash_block_size;
27cacec9
DDM
376 char *compression;
377 char *compression_level;
53171c04 378
e73309c5 379 uint64_t gpt_flags;
ff0771bf 380 int no_auto;
e73309c5 381 int read_only;
1c41c1dc 382 int growfs;
e73309c5 383
6b4b40f4 384 struct iovec roothash;
b5b7879a 385
4cee8333 386 char *split_name_format;
62108348 387 char *split_path;
4cee8333 388
1a0541d4
DDM
389 PartitionMountPoint *mountpoints;
390 size_t n_mountpoints;
391
392 PartitionEncryptedVolume *encrypted_volume;
393
2cb9c68c
AV
394 char *supplement_for_name;
395 struct Partition *supplement_for, *supplement_target_for;
396 struct Partition *suppressing;
397
448cfb7f 398 struct Partition *siblings[_VERITY_MODE_MAX];
b5b7879a 399
448cfb7f
DDM
400 LIST_FIELDS(struct Partition, partitions);
401} Partition;
e594a3b1
LP
402
403#define PARTITION_IS_FOREIGN(p) (!(p)->definition_path)
404#define PARTITION_EXISTS(p) (!!(p)->current_partition)
2cb9c68c 405#define PARTITION_SUPPRESSED(p) ((p)->supplement_for && (p)->supplement_for->suppressing == (p))
e594a3b1
LP
406
407struct FreeArea {
408 Partition *after;
409 uint64_t size;
410 uint64_t allocated;
411};
412
448cfb7f 413typedef struct Context {
e594a3b1
LP
414 LIST_HEAD(Partition, partitions);
415 size_t n_partitions;
416
417 FreeArea **free_areas;
319a4f4b 418 size_t n_free_areas;
e594a3b1
LP
419
420 uint64_t start, end, total;
421
422 struct fdisk_context *fdisk_context;
93f125a6 423 uint64_t sector_size, grain_size, fs_sector_size;
e594a3b1
LP
424
425 sd_id128_t seed;
cc751c75
DDM
426
427 char *node;
db1d4e6b 428 bool node_is_our_file;
cc751c75
DDM
429 int backing_fd;
430
431 bool from_scratch;
448cfb7f 432} Context;
e594a3b1 433
634b8471
LP
434static const char *empty_mode_table[_EMPTY_MODE_MAX] = {
435 [EMPTY_UNSET] = "unset",
436 [EMPTY_REFUSE] = "refuse",
437 [EMPTY_ALLOW] = "allow",
438 [EMPTY_REQUIRE] = "require",
439 [EMPTY_FORCE] = "force",
440 [EMPTY_CREATE] = "create",
441};
442
889914ef
LP
443static const char *encrypt_mode_table[_ENCRYPT_MODE_MAX] = {
444 [ENCRYPT_OFF] = "off",
445 [ENCRYPT_KEY_FILE] = "key-file",
446 [ENCRYPT_TPM2] = "tpm2",
447 [ENCRYPT_KEY_FILE_TPM2] = "key-file+tpm2",
448};
449
b5b7879a
DDM
450static const char *verity_mode_table[_VERITY_MODE_MAX] = {
451 [VERITY_OFF] = "off",
452 [VERITY_DATA] = "data",
453 [VERITY_HASH] = "hash",
b456191d 454 [VERITY_SIG] = "signature",
b5b7879a
DDM
455};
456
5c33b686
DDM
457static const char *minimize_mode_table[_MINIMIZE_MODE_MAX] = {
458 [MINIMIZE_OFF] = "off",
459 [MINIMIZE_BEST] = "best",
460 [MINIMIZE_GUESS] = "guess",
461};
462
634b8471 463DEFINE_PRIVATE_STRING_TABLE_LOOKUP(empty_mode, EmptyMode);
2709d029 464DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(encrypt_mode, EncryptMode, ENCRYPT_KEY_FILE);
86bebe38 465DEFINE_PRIVATE_STRING_TABLE_LOOKUP(verity_mode, VerityMode);
5c33b686 466DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(minimize_mode, MinimizeMode, MINIMIZE_BEST);
889914ef 467
e594a3b1
LP
468static uint64_t round_down_size(uint64_t v, uint64_t p) {
469 return (v / p) * p;
470}
471
472static uint64_t round_up_size(uint64_t v, uint64_t p) {
473
474 v = DIV_ROUND_UP(v, p);
475
476 if (v > UINT64_MAX / p)
477 return UINT64_MAX; /* overflow */
478
479 return v * p;
480}
481
482static Partition *partition_new(void) {
483 Partition *p;
484
485 p = new(Partition, 1);
486 if (!p)
487 return NULL;
488
489 *p = (Partition) {
490 .weight = 1000,
491 .padding_weight = 0,
492 .current_size = UINT64_MAX,
493 .new_size = UINT64_MAX,
494 .size_min = UINT64_MAX,
495 .size_max = UINT64_MAX,
496 .current_padding = UINT64_MAX,
497 .new_padding = UINT64_MAX,
498 .padding_min = UINT64_MAX,
499 .padding_max = UINT64_MAX,
500 .partno = UINT64_MAX,
501 .offset = UINT64_MAX,
254d1313 502 .copy_blocks_fd = -EBADF,
1e46985a 503 .copy_blocks_offset = UINT64_MAX,
757bc2e4 504 .copy_blocks_size = UINT64_MAX,
ff0771bf 505 .no_auto = -1,
e73309c5 506 .read_only = -1,
1c41c1dc 507 .growfs = -1,
c380047b
MC
508 .verity_data_block_size = UINT64_MAX,
509 .verity_hash_block_size = UINT64_MAX,
e594a3b1
LP
510 };
511
512 return p;
513}
514
2cb9c68c
AV
515static void partition_unlink_supplement(Partition *p) {
516 assert(p);
517
518 assert(!p->supplement_for || !p->supplement_target_for); /* Can't be both */
519
520 if (p->supplement_target_for) {
521 assert(p->supplement_target_for->supplement_for == p);
522
523 p->supplement_target_for->supplement_for = NULL;
524 }
525
526 if (p->supplement_for) {
527 assert(p->supplement_for->supplement_target_for == p);
528 assert(!p->supplement_for->suppressing || p->supplement_for->suppressing == p);
529
530 p->supplement_for->supplement_target_for = p->supplement_for->suppressing = NULL;
531 }
532
533 p->supplement_for_name = mfree(p->supplement_for_name);
534 p->supplement_target_for = p->supplement_for = p->suppressing = NULL;
535}
536
e594a3b1
LP
537static Partition* partition_free(Partition *p) {
538 if (!p)
539 return NULL;
540
541 free(p->current_label);
542 free(p->new_label);
543 free(p->definition_path);
39fc0174 544 strv_free(p->drop_in_files);
e594a3b1
LP
545
546 if (p->current_partition)
547 fdisk_unref_partition(p->current_partition);
548 if (p->new_partition)
549 fdisk_unref_partition(p->new_partition);
550
7d07030e
DDM
551 if (p->copy_blocks_path_is_our_file)
552 unlink_and_free(p->copy_blocks_path);
553 else
554 free(p->copy_blocks_path);
757bc2e4
LP
555 safe_close(p->copy_blocks_fd);
556
53171c04 557 free(p->format);
8a794850 558 strv_free(p->copy_files);
600bf76c
DDM
559 strv_free(p->exclude_files_source);
560 strv_free(p->exclude_files_target);
d83d8048 561 strv_free(p->make_directories);
c64ddefd 562 strv_free(p->make_symlinks);
562881c1 563 ordered_hashmap_free(p->subvolumes);
9f482c5a 564 free(p->default_subvolume);
b5b7879a 565 free(p->verity_match_key);
27cacec9
DDM
566 free(p->compression);
567 free(p->compression_level);
b5b7879a 568
6b4b40f4 569 iovec_done(&p->roothash);
53171c04 570
4cee8333 571 free(p->split_name_format);
a2d7c42e 572 unlink_and_free(p->split_path);
4cee8333 573
1a0541d4
DDM
574 partition_mountpoint_free_many(p->mountpoints, p->n_mountpoints);
575 p->mountpoints = NULL;
576 p->n_mountpoints = 0;
577
578 partition_encrypted_volume_free(p->encrypted_volume);
579
2cb9c68c
AV
580 partition_unlink_supplement(p);
581
e594a3b1
LP
582 return mfree(p);
583}
584
9ccceb9d
YW
585static void partition_foreignize(Partition *p) {
586 assert(p);
587 assert(PARTITION_EXISTS(p));
588
589 /* Reset several parameters set through definition file to make the partition foreign. */
590
9ccceb9d
YW
591 p->definition_path = mfree(p->definition_path);
592 p->drop_in_files = strv_free(p->drop_in_files);
593
594 p->copy_blocks_path = mfree(p->copy_blocks_path);
595 p->copy_blocks_fd = safe_close(p->copy_blocks_fd);
585c5c75 596 p->copy_blocks_root = NULL;
9ccceb9d
YW
597
598 p->format = mfree(p->format);
599 p->copy_files = strv_free(p->copy_files);
600bf76c
DDM
600 p->exclude_files_source = strv_free(p->exclude_files_source);
601 p->exclude_files_target = strv_free(p->exclude_files_target);
9ccceb9d 602 p->make_directories = strv_free(p->make_directories);
c64ddefd 603 p->make_symlinks = strv_free(p->make_symlinks);
562881c1 604 p->subvolumes = ordered_hashmap_free(p->subvolumes);
9f482c5a 605 p->default_subvolume = mfree(p->default_subvolume);
9ccceb9d 606 p->verity_match_key = mfree(p->verity_match_key);
27cacec9
DDM
607 p->compression = mfree(p->compression);
608 p->compression_level = mfree(p->compression_level);
9ccceb9d 609
9ccceb9d
YW
610 p->priority = 0;
611 p->weight = 1000;
612 p->padding_weight = 0;
613 p->size_min = UINT64_MAX;
614 p->size_max = UINT64_MAX;
615 p->padding_min = UINT64_MAX;
616 p->padding_max = UINT64_MAX;
617 p->no_auto = -1;
618 p->read_only = -1;
619 p->growfs = -1;
620 p->verity = VERITY_OFF;
1a0541d4
DDM
621
622 partition_mountpoint_free_many(p->mountpoints, p->n_mountpoints);
623 p->mountpoints = NULL;
624 p->n_mountpoints = 0;
625
626 p->encrypted_volume = partition_encrypted_volume_free(p->encrypted_volume);
2cb9c68c
AV
627
628 partition_unlink_supplement(p);
9ccceb9d
YW
629}
630
c74d50ff 631static bool partition_type_exclude(const GptPartitionType *type) {
81d1098b
DDM
632 if (arg_filter_partitions_type == FILTER_PARTITIONS_NONE)
633 return false;
634
d989dd76 635 for (size_t i = 0; i < arg_n_filter_partitions; i++)
c74d50ff 636 if (sd_id128_equal(type->uuid, arg_filter_partitions[i].uuid))
81d1098b
DDM
637 return arg_filter_partitions_type == FILTER_PARTITIONS_EXCLUDE;
638
639 return arg_filter_partitions_type == FILTER_PARTITIONS_INCLUDE;
640}
641
c74d50ff 642static bool partition_type_defer(const GptPartitionType *type) {
8275334b 643 for (size_t i = 0; i < arg_n_defer_partitions; i++)
c74d50ff 644 if (sd_id128_equal(type->uuid, arg_defer_partitions[i].uuid))
7d505753
DDM
645 return true;
646
647 return false;
648}
649
e594a3b1
LP
650static Partition* partition_unlink_and_free(Context *context, Partition *p) {
651 if (!p)
652 return NULL;
653
654 LIST_REMOVE(partitions, context->partitions, p);
655
656 assert(context->n_partitions > 0);
657 context->n_partitions--;
658
659 return partition_free(p);
660}
661
662DEFINE_TRIVIAL_CLEANUP_FUNC(Partition*, partition_free);
663
52031044 664static Context* context_new(sd_id128_t seed) {
e594a3b1
LP
665 Context *context;
666
667 context = new(Context, 1);
668 if (!context)
669 return NULL;
670
671 *context = (Context) {
672 .start = UINT64_MAX,
673 .end = UINT64_MAX,
674 .total = UINT64_MAX,
675 .seed = seed,
676 };
677
678 return context;
679}
680
681static void context_free_free_areas(Context *context) {
682 assert(context);
683
684 for (size_t i = 0; i < context->n_free_areas; i++)
685 free(context->free_areas[i]);
686
687 context->free_areas = mfree(context->free_areas);
688 context->n_free_areas = 0;
e594a3b1
LP
689}
690
52031044 691static Context* context_free(Context *context) {
e594a3b1
LP
692 if (!context)
693 return NULL;
694
695 while (context->partitions)
696 partition_unlink_and_free(context, context->partitions);
697 assert(context->n_partitions == 0);
698
699 context_free_free_areas(context);
700
701 if (context->fdisk_context)
702 fdisk_unref_context(context->fdisk_context);
703
cc751c75 704 safe_close(context->backing_fd);
db1d4e6b
DDM
705 if (context->node_is_our_file)
706 unlink_and_free(context->node);
707 else
708 free(context->node);
cc751c75 709
e594a3b1
LP
710 return mfree(context);
711}
712
713DEFINE_TRIVIAL_CLEANUP_FUNC(Context*, context_free);
714
715static int context_add_free_area(
716 Context *context,
717 uint64_t size,
718 Partition *after) {
719
720 FreeArea *a;
721
722 assert(context);
723 assert(!after || !after->padding_area);
724
319a4f4b 725 if (!GREEDY_REALLOC(context->free_areas, context->n_free_areas + 1))
e594a3b1
LP
726 return -ENOMEM;
727
728 a = new(FreeArea, 1);
729 if (!a)
730 return -ENOMEM;
731
732 *a = (FreeArea) {
733 .size = size,
734 .after = after,
735 };
736
737 context->free_areas[context->n_free_areas++] = a;
738
739 if (after)
740 after->padding_area = a;
741
742 return 0;
743}
744
9ccceb9d
YW
745static void partition_drop_or_foreignize(Partition *p) {
746 if (!p || p->dropped || PARTITION_IS_FOREIGN(p))
747 return;
748
749 if (PARTITION_EXISTS(p)) {
750 log_info("Can't grow existing partition %s of priority %" PRIi32 ", ignoring.",
751 strna(p->current_label ?: p->new_label), p->priority);
752
753 /* Handle the partition as foreign. Do not set dropped flag. */
754 partition_foreignize(p);
755 } else {
756 log_info("Can't fit partition %s of priority %" PRIi32 ", dropping.",
757 p->definition_path, p->priority);
758
759 p->dropped = true;
760 p->allocated_to_area = NULL;
2cb9c68c
AV
761
762 /* If a supplement partition is dropped, we don't want to merge in its settings. */
763 if (PARTITION_SUPPRESSED(p))
764 p->supplement_for->suppressing = NULL;
9ccceb9d
YW
765 }
766}
767
768static bool context_drop_or_foreignize_one_priority(Context *context) {
e594a3b1 769 int32_t priority = 0;
e594a3b1
LP
770
771 LIST_FOREACH(partitions, p, context->partitions) {
772 if (p->dropped)
773 continue;
e594a3b1 774
9ccceb9d 775 priority = MAX(priority, p->priority);
e594a3b1
LP
776 }
777
778 /* Refuse to drop partitions with 0 or negative priorities or partitions of priorities that have at
779 * least one existing priority */
9ccceb9d 780 if (priority <= 0)
e594a3b1
LP
781 return false;
782
783 LIST_FOREACH(partitions, p, context->partitions) {
784 if (p->priority < priority)
785 continue;
786
9ccceb9d 787 partition_drop_or_foreignize(p);
b5b7879a
DDM
788
789 /* We ensure that all verity sibling partitions have the same priority, so it's safe
790 * to drop all siblings here as well. */
791
9ccceb9d
YW
792 for (VerityMode mode = VERITY_OFF + 1; mode < _VERITY_MODE_MAX; mode++)
793 partition_drop_or_foreignize(p->siblings[mode]);
e594a3b1
LP
794 }
795
796 return true;
797}
798
a80701e6 799static uint64_t partition_min_size(const Context *context, const Partition *p) {
2cb9c68c 800 uint64_t sz, override_min;
e594a3b1 801
994b3031
LP
802 assert(context);
803 assert(p);
804
e594a3b1
LP
805 /* Calculate the disk space we really need at minimum for this partition. If the partition already
806 * exists the current size is what we really need. If it doesn't exist yet refuse to allocate less
fb08381c
LP
807 * than 4K.
808 *
809 * DEFAULT_MIN_SIZE is the default SizeMin= we configure if nothing else is specified. */
e594a3b1
LP
810
811 if (PARTITION_IS_FOREIGN(p)) {
812 /* Don't allow changing size of partitions not managed by us */
813 assert(p->current_size != UINT64_MAX);
814 return p->current_size;
815 }
816
e11745d0 817 if (IN_SET(p->type.designator, PARTITION_ROOT_VERITY_SIG, PARTITION_USR_VERITY_SIG))
b456191d
DDM
818 return VERITY_SIG_SIZE;
819
fb08381c 820 sz = p->current_size != UINT64_MAX ? p->current_size : HARD_MIN_SIZE;
757bc2e4 821
170c9823
LP
822 if (!PARTITION_EXISTS(p)) {
823 uint64_t d = 0;
824
889914ef 825 if (p->encrypt != ENCRYPT_OFF)
48a09a8f 826 d += round_up_size(LUKS2_METADATA_KEEP_FREE, context->grain_size);
170c9823
LP
827
828 if (p->copy_blocks_size != UINT64_MAX)
994b3031 829 d += round_up_size(p->copy_blocks_size, context->grain_size);
889914ef 830 else if (p->format || p->encrypt != ENCRYPT_OFF) {
170c9823
LP
831 uint64_t f;
832
833 /* If we shall synthesize a file system, take minimal fs size into account (assumed to be 4K if not known) */
994b3031
LP
834 f = p->format ? round_up_size(minimal_size_by_fs_name(p->format), context->grain_size) : UINT64_MAX;
835 d += f == UINT64_MAX ? context->grain_size : f;
170c9823
LP
836 }
837
838 if (d > sz)
839 sz = d;
840 }
757bc2e4 841
2cb9c68c
AV
842 override_min = p->suppressing ? MAX(p->size_min, p->suppressing->size_min) : p->size_min;
843
844 return MAX(round_up_size(override_min != UINT64_MAX ? override_min : DEFAULT_MIN_SIZE, context->grain_size), sz);
e594a3b1
LP
845}
846
994b3031 847static uint64_t partition_max_size(const Context *context, const Partition *p) {
2cb9c68c 848 uint64_t sm, override_max;
994b3031 849
e594a3b1
LP
850 /* Calculate how large the partition may become at max. This is generally the configured maximum
851 * size, except when it already exists and is larger than that. In that case it's the existing size,
852 * since we never want to shrink partitions. */
853
994b3031
LP
854 assert(context);
855 assert(p);
856
e594a3b1
LP
857 if (PARTITION_IS_FOREIGN(p)) {
858 /* Don't allow changing size of partitions not managed by us */
859 assert(p->current_size != UINT64_MAX);
860 return p->current_size;
861 }
862
b456191d
DDM
863 if (p->verity == VERITY_SIG)
864 return VERITY_SIG_SIZE;
865
2cb9c68c
AV
866 override_max = p->suppressing ? MIN(p->size_max, p->suppressing->size_max) : p->size_max;
867 if (override_max == UINT64_MAX)
822d9b9a
YW
868 return UINT64_MAX;
869
2cb9c68c 870 sm = round_down_size(override_max, context->grain_size);
994b3031 871
e594a3b1 872 if (p->current_size != UINT64_MAX)
b0fbf90b 873 sm = MAX(p->current_size, sm);
e594a3b1 874
b0fbf90b 875 return MAX(partition_min_size(context, p), sm);
e594a3b1
LP
876}
877
a801bb01 878static uint64_t partition_min_padding(const Partition *p) {
2cb9c68c
AV
879 uint64_t override_min;
880
a801bb01 881 assert(p);
2cb9c68c
AV
882
883 override_min = p->suppressing ? MAX(p->padding_min, p->suppressing->padding_min) : p->padding_min;
884 return override_min != UINT64_MAX ? override_min : 0;
a801bb01
YW
885}
886
887static uint64_t partition_max_padding(const Partition *p) {
888 assert(p);
2cb9c68c 889 return p->suppressing ? MIN(p->padding_max, p->suppressing->padding_max) : p->padding_max;
a801bb01
YW
890}
891
994b3031 892static uint64_t partition_min_size_with_padding(Context *context, const Partition *p) {
e594a3b1
LP
893 uint64_t sz;
894
895 /* Calculate the disk space we need for this partition plus any free space coming after it. This
896 * takes user configured padding into account as well as any additional whitespace needed to align
897 * the next partition to 4K again. */
898
994b3031
LP
899 assert(context);
900 assert(p);
901
a801bb01 902 sz = partition_min_size(context, p) + partition_min_padding(p);
e594a3b1
LP
903
904 if (PARTITION_EXISTS(p)) {
905 /* If the partition wasn't aligned, add extra space so that any we might add will be aligned */
906 assert(p->offset != UINT64_MAX);
994b3031 907 return round_up_size(p->offset + sz, context->grain_size) - p->offset;
e594a3b1
LP
908 }
909
910 /* If this is a new partition we'll place it aligned, hence we just need to round up the required size here */
994b3031 911 return round_up_size(sz, context->grain_size);
e594a3b1
LP
912}
913
914static uint64_t free_area_available(const FreeArea *a) {
915 assert(a);
916
917 /* Determines how much of this free area is not allocated yet */
918
919 assert(a->size >= a->allocated);
920 return a->size - a->allocated;
921}
922
58b06ac1 923static uint64_t free_area_current_end(Context *context, const FreeArea *a) {
994b3031
LP
924 assert(context);
925 assert(a);
926
58b06ac1
YW
927 if (!a->after)
928 return free_area_available(a);
e594a3b1 929
58b06ac1
YW
930 assert(a->after->offset != UINT64_MAX);
931 assert(a->after->current_size != UINT64_MAX);
e594a3b1 932
58b06ac1
YW
933 /* Calculate where the free area ends, based on the offset of the partition preceding it. */
934 return round_up_size(a->after->offset + a->after->current_size, context->grain_size) + free_area_available(a);
935}
e594a3b1 936
58b06ac1
YW
937static uint64_t free_area_min_end(Context *context, const FreeArea *a) {
938 assert(context);
939 assert(a);
e594a3b1 940
58b06ac1
YW
941 if (!a->after)
942 return 0;
e594a3b1 943
58b06ac1
YW
944 assert(a->after->offset != UINT64_MAX);
945 assert(a->after->current_size != UINT64_MAX);
1052a114 946
58b06ac1
YW
947 /* Calculate where the partition would end when we give it as much as it needs. */
948 return round_up_size(a->after->offset + partition_min_size_with_padding(context, a->after), context->grain_size);
949}
950
951static uint64_t free_area_available_for_new_partitions(Context *context, const FreeArea *a) {
952 assert(context);
953 assert(a);
954
955 /* Similar to free_area_available(), but takes into account that the required size and padding of the
956 * preceding partition is honoured. */
e594a3b1 957
58b06ac1 958 return LESS_BY(free_area_current_end(context, a), free_area_min_end(context, a));
e594a3b1
LP
959}
960
994b3031
LP
961static int free_area_compare(FreeArea *const *a, FreeArea *const*b, Context *context) {
962 assert(context);
963
964 return CMP(free_area_available_for_new_partitions(context, *a),
965 free_area_available_for_new_partitions(context, *b));
e594a3b1
LP
966}
967
994b3031
LP
968static uint64_t charge_size(Context *context, uint64_t total, uint64_t amount) {
969 assert(context);
e594a3b1 970 /* Subtract the specified amount from total, rounding up to multiple of 4K if there's room */
184cf99a 971 assert(amount <= total);
994b3031 972 return LESS_BY(total, round_up_size(amount, context->grain_size));
e594a3b1
LP
973}
974
975static uint64_t charge_weight(uint64_t total, uint64_t amount) {
976 assert(amount <= total);
977 return total - amount;
978}
979
14a4c4ed 980static bool context_allocate_partitions(Context *context, uint64_t *ret_largest_free_area) {
e594a3b1
LP
981 assert(context);
982
f39cf264
YW
983 /* This may be called multiple times. Reset previous assignments. */
984 for (size_t i = 0; i < context->n_free_areas; i++)
985 context->free_areas[i]->allocated = 0;
986
14a4c4ed 987 /* Sort free areas by size, putting smallest first */
994b3031 988 typesafe_qsort_r(context->free_areas, context->n_free_areas, free_area_compare, context);
e594a3b1 989
14a4c4ed
LP
990 /* In any case return size of the largest free area (i.e. not the size of all free areas
991 * combined!) */
992 if (ret_largest_free_area)
993 *ret_largest_free_area =
994 context->n_free_areas == 0 ? 0 :
994b3031 995 free_area_available_for_new_partitions(context, context->free_areas[context->n_free_areas-1]);
14a4c4ed 996
cdbcc339
YW
997 /* Check that each existing partition can fit its area. */
998 for (size_t i = 0; i < context->n_free_areas; i++)
999 if (free_area_current_end(context, context->free_areas[i]) <
1000 free_area_min_end(context, context->free_areas[i]))
1001 return false;
1002
14a4c4ed 1003 /* A simple first-fit algorithm. We return true if we can fit the partitions in, otherwise false. */
e594a3b1
LP
1004 LIST_FOREACH(partitions, p, context->partitions) {
1005 bool fits = false;
1006 uint64_t required;
1007 FreeArea *a = NULL;
1008
2cb9c68c 1009 if (p->dropped || PARTITION_IS_FOREIGN(p) || PARTITION_SUPPRESSED(p))
e594a3b1
LP
1010 continue;
1011
e594a3b1 1012 /* How much do we need to fit? */
994b3031
LP
1013 required = partition_min_size_with_padding(context, p);
1014 assert(required % context->grain_size == 0);
e594a3b1 1015
78e90592
AV
1016 /* For existing partitions, we should verify that they'll actually fit */
1017 if (PARTITION_EXISTS(p)) {
1018 if (p->current_size + p->current_padding < required)
1019 return false; /* 😢 We won't be able to grow to the required min size! */
1020
1021 continue;
1022 }
1023
1024 /* For new partitions, see if there's a free area big enough */
e594a3b1
LP
1025 for (size_t i = 0; i < context->n_free_areas; i++) {
1026 a = context->free_areas[i];
1027
994b3031 1028 if (free_area_available_for_new_partitions(context, a) >= required) {
e594a3b1
LP
1029 fits = true;
1030 break;
1031 }
1032 }
1033
1034 if (!fits)
1035 return false; /* 😢 Oh no! We can't fit this partition into any free area! */
1036
1037 /* Assign the partition to this free area */
1038 p->allocated_to_area = a;
1039
1040 /* Budget the minimal partition size */
1041 a->allocated += required;
1042 }
1043
1044 return true;
1045}
1046
2cb9c68c
AV
1047static bool context_unmerge_and_allocate_partitions(Context *context) {
1048 assert(context);
1049
1050 /* This should only be called after plain context_allocate_partitions fails. This algorithm will
1051 * try, in the order that minimizes the number of created supplement partitions, all combinations of
1052 * un-suppressing supplement partitions until it finds one that works. */
1053
1054 /* First, let's try to un-suppress just one supplement partition and see if that gets us anywhere */
1055 LIST_FOREACH(partitions, p, context->partitions) {
1056 Partition *unsuppressed;
1057
1058 if (!p->suppressing)
1059 continue;
1060
1061 unsuppressed = TAKE_PTR(p->suppressing);
1062
1063 if (context_allocate_partitions(context, NULL))
1064 return true;
1065
1066 p->suppressing = unsuppressed;
1067 }
1068
1069 /* Looks like not. So we have to un-suppress at least two partitions. We can do this recursively */
1070 LIST_FOREACH(partitions, p, context->partitions) {
1071 Partition *unsuppressed;
1072
1073 if (!p->suppressing)
1074 continue;
1075
1076 unsuppressed = TAKE_PTR(p->suppressing);
1077
1078 if (context_unmerge_and_allocate_partitions(context))
1079 return true;
1080
1081 p->suppressing = unsuppressed;
1082 }
1083
1084 /* No combination of un-suppressed supplements made it possible to fit the partitions */
1085 return false;
1086}
1087
1088static uint32_t partition_weight(const Partition *p) {
1089 assert(p);
1090 return p->suppressing ? p->suppressing->weight : p->weight;
1091}
1092
1093static uint32_t partition_padding_weight(const Partition *p) {
1094 assert(p);
1095 return p->suppressing ? p->suppressing->padding_weight : p->padding_weight;
1096}
1097
e594a3b1
LP
1098static int context_sum_weights(Context *context, FreeArea *a, uint64_t *ret) {
1099 uint64_t weight_sum = 0;
e594a3b1
LP
1100
1101 assert(context);
1102 assert(a);
1103 assert(ret);
1104
1105 /* Determine the sum of the weights of all partitions placed in or before the specified free area */
1106
1107 LIST_FOREACH(partitions, p, context->partitions) {
1108 if (p->padding_area != a && p->allocated_to_area != a)
1109 continue;
1110
2cb9c68c 1111 if (!INC_SAFE(&weight_sum, partition_weight(p)))
e594a3b1 1112 goto overflow_sum;
e594a3b1 1113
2cb9c68c 1114 if (!INC_SAFE(&weight_sum, partition_padding_weight(p)))
e594a3b1 1115 goto overflow_sum;
e594a3b1
LP
1116 }
1117
1118 *ret = weight_sum;
1119 return 0;
1120
1121overflow_sum:
da890466 1122 return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Combined weight of partition exceeds unsigned 64-bit range, refusing.");
e594a3b1
LP
1123}
1124
0245e15a 1125static uint64_t scale_by_weight(uint64_t value, uint64_t weight, uint64_t weight_sum) {
e594a3b1 1126 assert(weight_sum >= weight);
e594a3b1 1127
0245e15a
YW
1128 for (;;) {
1129 if (weight == 0)
1130 return 0;
1131 if (weight == weight_sum)
1132 return value;
1133 if (value <= UINT64_MAX / weight)
1134 return value * weight / weight_sum;
1135
1136 /* Rescale weight and weight_sum to make not the calculation overflow. To satisfy the
1137 * following conditions, 'weight_sum' is rounded up but 'weight' is rounded down:
1138 * - the sum of scale_by_weight() for all weights must not be larger than the input value,
1139 * - scale_by_weight() must not be larger than the ideal value (i.e. calculated with uint128_t). */
1140 weight_sum = DIV_ROUND_UP(weight_sum, 2);
1141 weight /= 2;
e594a3b1 1142 }
e594a3b1
LP
1143}
1144
1145typedef enum GrowPartitionPhase {
bf99aed6
YW
1146 /* The zeroth phase: do not touch foreign partitions (i.e. those we don't manage). */
1147 PHASE_FOREIGN,
1148
e594a3b1
LP
1149 /* The first phase: we charge partitions which need more (according to constraints) than their weight-based share. */
1150 PHASE_OVERCHARGE,
1151
1152 /* The second phase: we charge partitions which need less (according to constraints) than their weight-based share. */
1153 PHASE_UNDERCHARGE,
1154
1155 /* The third phase: we distribute what remains among the remaining partitions, according to the weights */
1156 PHASE_DISTRIBUTE,
ae0613c6
LP
1157
1158 _GROW_PARTITION_PHASE_MAX,
e594a3b1
LP
1159} GrowPartitionPhase;
1160
0245e15a 1161static bool context_grow_partitions_phase(
e594a3b1
LP
1162 Context *context,
1163 FreeArea *a,
1164 GrowPartitionPhase phase,
1165 uint64_t *span,
1166 uint64_t *weight_sum) {
1167
2a503ad2
YW
1168 bool try_again = false;
1169
e594a3b1
LP
1170 assert(context);
1171 assert(a);
0245e15a
YW
1172 assert(span);
1173 assert(weight_sum);
e594a3b1
LP
1174
1175 /* Now let's look at the intended weights and adjust them taking the minimum space assignments into
1176 * account. i.e. if a partition has a small weight but a high minimum space value set it should not
1177 * get any additional room from the left-overs. Similar, if two partitions have the same weight they
1178 * should get the same space if possible, even if one has a smaller minimum size than the other. */
1179 LIST_FOREACH(partitions, p, context->partitions) {
e594a3b1 1180 /* Look only at partitions associated with this free area, i.e. immediately
162392b7 1181 * preceding it, or allocated into it */
e594a3b1
LP
1182 if (p->allocated_to_area != a && p->padding_area != a)
1183 continue;
1184
1185 if (p->new_size == UINT64_MAX) {
e594a3b1 1186 uint64_t share, rsz, xsz;
2cb9c68c 1187 uint32_t weight;
2a503ad2 1188 bool charge = false;
e594a3b1 1189
2cb9c68c
AV
1190 weight = partition_weight(p);
1191
e594a3b1
LP
1192 /* Calculate how much this space this partition needs if everyone would get
1193 * the weight based share */
2cb9c68c 1194 share = scale_by_weight(*span, weight, *weight_sum);
e594a3b1 1195
994b3031
LP
1196 rsz = partition_min_size(context, p);
1197 xsz = partition_max_size(context, p);
e594a3b1 1198
bf99aed6
YW
1199 if (phase == PHASE_FOREIGN && PARTITION_IS_FOREIGN(p)) {
1200 /* Never change of foreign partitions (i.e. those we don't manage) */
1201
1202 p->new_size = p->current_size;
1203 charge = true;
1204
1205 } else if (phase == PHASE_OVERCHARGE && rsz > share) {
e594a3b1
LP
1206 /* This partition needs more than its calculated share. Let's assign
1207 * it that, and take this partition out of all calculations and start
1208 * again. */
1209
1210 p->new_size = rsz;
1211 charge = try_again = true;
1212
822d9b9a 1213 } else if (phase == PHASE_UNDERCHARGE && xsz < share) {
e594a3b1
LP
1214 /* This partition accepts less than its calculated
1215 * share. Let's assign it that, and take this partition out
1216 * of all calculations and start again. */
1217
1218 p->new_size = xsz;
1219 charge = try_again = true;
1220
1221 } else if (phase == PHASE_DISTRIBUTE) {
1222 /* This partition can accept its calculated share. Let's
1223 * assign it. There's no need to restart things here since
1224 * assigning this shouldn't impact the shares of the other
1225 * partitions. */
1226
d7c46b5e
YW
1227 assert(share >= rsz);
1228 p->new_size = CLAMP(round_down_size(share, context->grain_size), rsz, xsz);
e594a3b1
LP
1229 charge = true;
1230 }
1231
1232 if (charge) {
994b3031 1233 *span = charge_size(context, *span, p->new_size);
2cb9c68c 1234 *weight_sum = charge_weight(*weight_sum, weight);
e594a3b1 1235 }
e594a3b1
LP
1236 }
1237
1238 if (p->new_padding == UINT64_MAX) {
a801bb01 1239 uint64_t share, rsz, xsz;
2cb9c68c 1240 uint32_t padding_weight;
2a503ad2 1241 bool charge = false;
e594a3b1 1242
2cb9c68c
AV
1243 padding_weight = partition_padding_weight(p);
1244
1245 share = scale_by_weight(*span, padding_weight, *weight_sum);
e594a3b1 1246
a801bb01
YW
1247 rsz = partition_min_padding(p);
1248 xsz = partition_max_padding(p);
1249
1250 if (phase == PHASE_OVERCHARGE && rsz > share) {
1251 p->new_padding = rsz;
e594a3b1 1252 charge = try_again = true;
a801bb01
YW
1253 } else if (phase == PHASE_UNDERCHARGE && xsz < share) {
1254 p->new_padding = xsz;
e594a3b1
LP
1255 charge = try_again = true;
1256 } else if (phase == PHASE_DISTRIBUTE) {
d7c46b5e
YW
1257 assert(share >= rsz);
1258 p->new_padding = CLAMP(round_down_size(share, context->grain_size), rsz, xsz);
e594a3b1
LP
1259 charge = true;
1260 }
1261
1262 if (charge) {
994b3031 1263 *span = charge_size(context, *span, p->new_padding);
2cb9c68c 1264 *weight_sum = charge_weight(*weight_sum, padding_weight);
e594a3b1 1265 }
e594a3b1
LP
1266 }
1267 }
1268
2a503ad2 1269 return !try_again;
e594a3b1
LP
1270}
1271
19903a43
YW
1272static void context_grow_partition_one(Context *context, FreeArea *a, Partition *p, uint64_t *span) {
1273 uint64_t m;
1274
1275 assert(context);
1276 assert(a);
1277 assert(p);
1278 assert(span);
1279
1280 if (*span == 0)
1281 return;
1282
1283 if (p->allocated_to_area != a)
1284 return;
1285
1286 if (PARTITION_IS_FOREIGN(p))
1287 return;
1288
1289 assert(p->new_size != UINT64_MAX);
1290
1291 /* Calculate new size and align. */
1292 m = round_down_size(p->new_size + *span, context->grain_size);
1293 /* But ensure this doesn't shrink the size. */
1294 m = MAX(m, p->new_size);
1295 /* And ensure this doesn't exceed the maximum size. */
1296 m = MIN(m, partition_max_size(context, p));
1297
1298 assert(m >= p->new_size);
1299
1300 *span = charge_size(context, *span, m - p->new_size);
1301 p->new_size = m;
1302}
1303
e594a3b1
LP
1304static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) {
1305 uint64_t weight_sum = 0, span;
1306 int r;
1307
1308 assert(context);
1309 assert(a);
1310
1311 r = context_sum_weights(context, a, &weight_sum);
1312 if (r < 0)
1313 return r;
1314
1315 /* Let's calculate the total area covered by this free area and the partition before it */
1316 span = a->size;
1317 if (a->after) {
1318 assert(a->after->offset != UINT64_MAX);
1319 assert(a->after->current_size != UINT64_MAX);
1320
994b3031 1321 span += round_up_size(a->after->offset + a->after->current_size, context->grain_size) - a->after->offset;
e594a3b1
LP
1322 }
1323
0245e15a
YW
1324 for (GrowPartitionPhase phase = 0; phase < _GROW_PARTITION_PHASE_MAX;)
1325 if (context_grow_partitions_phase(context, a, phase, &span, &weight_sum))
1326 phase++; /* go to the next phase */
e594a3b1 1327
162392b7 1328 /* We still have space left over? Donate to preceding partition if we have one */
19903a43
YW
1329 if (span > 0 && a->after)
1330 context_grow_partition_one(context, a, a->after, &span);
e594a3b1 1331
162392b7 1332 /* What? Even still some space left (maybe because there was no preceding partition, or it had a
e594a3b1 1333 * size limit), then let's donate it to whoever wants it. */
03677889 1334 if (span > 0)
e594a3b1 1335 LIST_FOREACH(partitions, p, context->partitions) {
19903a43 1336 context_grow_partition_one(context, a, p, &span);
e594a3b1
LP
1337 if (span == 0)
1338 break;
1339 }
e594a3b1 1340
162392b7 1341 /* Yuck, still no one? Then make it padding */
e594a3b1
LP
1342 if (span > 0 && a->after) {
1343 assert(a->after->new_padding != UINT64_MAX);
1344 a->after->new_padding += span;
1345 }
1346
1347 return 0;
1348}
1349
1350static int context_grow_partitions(Context *context) {
e594a3b1
LP
1351 int r;
1352
1353 assert(context);
1354
1355 for (size_t i = 0; i < context->n_free_areas; i++) {
1356 r = context_grow_partitions_on_free_area(context, context->free_areas[i]);
1357 if (r < 0)
1358 return r;
1359 }
1360
1361 /* All existing partitions that have no free space after them can't change size */
1362 LIST_FOREACH(partitions, p, context->partitions) {
1363 if (p->dropped)
1364 continue;
1365
1366 if (!PARTITION_EXISTS(p) || p->padding_area) {
1367 /* The algorithm above must have initialized this already */
1368 assert(p->new_size != UINT64_MAX);
1369 continue;
1370 }
1371
1372 assert(p->new_size == UINT64_MAX);
1373 p->new_size = p->current_size;
1374
1375 assert(p->new_padding == UINT64_MAX);
1376 p->new_padding = p->current_padding;
1377 }
1378
1379 return 0;
1380}
1381
00428745 1382static uint64_t find_first_unused_partno(Context *context) {
e594a3b1 1383 uint64_t partno = 0;
e594a3b1
LP
1384
1385 assert(context);
1386
5f59807d
DDM
1387 for (partno = 0;; partno++) {
1388 bool found = false;
1389 LIST_FOREACH(partitions, p, context->partitions)
1390 if (p->partno != UINT64_MAX && p->partno == partno)
1391 found = true;
1392 if (!found)
1393 break;
e594a3b1
LP
1394 }
1395
00428745
DDM
1396 return partno;
1397}
1398
1399static void context_place_partitions(Context *context) {
1400
1401 assert(context);
1402
e594a3b1
LP
1403 for (size_t i = 0; i < context->n_free_areas; i++) {
1404 FreeArea *a = context->free_areas[i];
2ea7eb00
FS
1405 _unused_ uint64_t left;
1406 uint64_t start;
e594a3b1
LP
1407
1408 if (a->after) {
1409 assert(a->after->offset != UINT64_MAX);
1410 assert(a->after->new_size != UINT64_MAX);
1411 assert(a->after->new_padding != UINT64_MAX);
1412
1413 start = a->after->offset + a->after->new_size + a->after->new_padding;
1414 } else
1415 start = context->start;
1416
994b3031 1417 start = round_up_size(start, context->grain_size);
e594a3b1
LP
1418 left = a->size;
1419
1420 LIST_FOREACH(partitions, p, context->partitions) {
1421 if (p->allocated_to_area != a)
1422 continue;
1423
1424 p->offset = start;
00428745 1425 p->partno = find_first_unused_partno(context);
e594a3b1
LP
1426
1427 assert(left >= p->new_size);
1428 start += p->new_size;
1429 left -= p->new_size;
1430
1431 assert(left >= p->new_padding);
1432 start += p->new_padding;
1433 left -= p->new_padding;
1434 }
1435 }
1436}
1437
e594a3b1
LP
1438static int config_parse_type(
1439 const char *unit,
1440 const char *filename,
1441 unsigned line,
1442 const char *section,
1443 unsigned section_line,
1444 const char *lvalue,
1445 int ltype,
1446 const char *rvalue,
1447 void *data,
1448 void *userdata) {
1449
22e932f4 1450 GptPartitionType *type = ASSERT_PTR(data);
e594a3b1
LP
1451 int r;
1452
1453 assert(rvalue);
e594a3b1 1454
22e932f4 1455 r = gpt_partition_type_from_string(rvalue, type);
e594a3b1
LP
1456 if (r < 0)
1457 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse partition type: %s", rvalue);
1458
9786dfe6
DDM
1459 if (arg_architecture >= 0)
1460 *type = gpt_partition_type_override_architecture(*type, arg_architecture);
1461
e594a3b1
LP
1462 return 0;
1463}
1464
1465static int config_parse_label(
1466 const char *unit,
1467 const char *filename,
1468 unsigned line,
1469 const char *section,
1470 unsigned section_line,
1471 const char *lvalue,
1472 int ltype,
1473 const char *rvalue,
1474 void *data,
1475 void *userdata) {
1476
e031166e 1477 _cleanup_free_ char *resolved = NULL;
99534007 1478 char **label = ASSERT_PTR(data);
e594a3b1
LP
1479 int r;
1480
1481 assert(rvalue);
e594a3b1 1482
be9ce018
LP
1483 /* Nota bene: the empty label is a totally valid one. Let's hence not follow our usual rule of
1484 * assigning the empty string to reset to default here, but really accept it as label to set. */
1485
de61a04b 1486 r = specifier_printf(rvalue, GPT_LABEL_MAX, system_and_tmp_specifier_table, arg_root, NULL, &resolved);
e031166e 1487 if (r < 0) {
e459258f 1488 log_syntax(unit, LOG_WARNING, filename, line, r,
e031166e
LP
1489 "Failed to expand specifiers in Label=, ignoring: %s", rvalue);
1490 return 0;
1491 }
1492
1493 if (!utf8_is_valid(resolved)) {
e594a3b1
LP
1494 log_syntax(unit, LOG_WARNING, filename, line, 0,
1495 "Partition label not valid UTF-8, ignoring: %s", rvalue);
1496 return 0;
1497 }
1498
22a0a36e
LP
1499 r = gpt_partition_label_valid(resolved);
1500 if (r < 0) {
1501 log_syntax(unit, LOG_WARNING, filename, line, r,
1502 "Failed to check if string is valid as GPT partition label, ignoring: \"%s\" (from \"%s\")",
1503 resolved, rvalue);
1504 return 0;
1505 }
1506 if (!r) {
e594a3b1 1507 log_syntax(unit, LOG_WARNING, filename, line, 0,
46072ae3
ZJS
1508 "Partition label too long for GPT table, ignoring: \"%s\" (from \"%s\")",
1509 resolved, rvalue);
e594a3b1
LP
1510 return 0;
1511 }
1512
e031166e 1513 free_and_replace(*label, resolved);
e594a3b1
LP
1514 return 0;
1515}
1516
1517static int config_parse_weight(
1518 const char *unit,
1519 const char *filename,
1520 unsigned line,
1521 const char *section,
1522 unsigned section_line,
1523 const char *lvalue,
1524 int ltype,
1525 const char *rvalue,
1526 void *data,
1527 void *userdata) {
1528
f126038f 1529 uint32_t *w = ASSERT_PTR(data), v;
e594a3b1
LP
1530 int r;
1531
1532 assert(rvalue);
e594a3b1
LP
1533
1534 r = safe_atou32(rvalue, &v);
1535 if (r < 0) {
1536 log_syntax(unit, LOG_WARNING, filename, line, r,
1537 "Failed to parse weight value, ignoring: %s", rvalue);
1538 return 0;
1539 }
1540
1541 if (v > 1000U*1000U) {
c8f3d767 1542 log_syntax(unit, LOG_WARNING, filename, line, 0,
e594a3b1
LP
1543 "Weight needs to be in range 0…10000000, ignoring: %" PRIu32, v);
1544 return 0;
1545 }
1546
f126038f 1547 *w = v;
e594a3b1
LP
1548 return 0;
1549}
1550
1551static int config_parse_size4096(
1552 const char *unit,
1553 const char *filename,
1554 unsigned line,
1555 const char *section,
1556 unsigned section_line,
1557 const char *lvalue,
1558 int ltype,
1559 const char *rvalue,
1560 void *data,
1561 void *userdata) {
1562
1563 uint64_t *sz = data, parsed;
1564 int r;
1565
1566 assert(rvalue);
1567 assert(data);
1568
1569 r = parse_size(rvalue, 1024, &parsed);
1570 if (r < 0)
c8f3d767 1571 return log_syntax(unit, LOG_ERR, filename, line, r,
e594a3b1
LP
1572 "Failed to parse size value: %s", rvalue);
1573
1574 if (ltype > 0)
1575 *sz = round_up_size(parsed, 4096);
1576 else if (ltype < 0)
1577 *sz = round_down_size(parsed, 4096);
1578 else
1579 *sz = parsed;
1580
1581 if (*sz != parsed)
e2341b6b
DT
1582 log_syntax(unit, LOG_NOTICE, filename, line, r, "Rounded %s= size %" PRIu64 " %s %" PRIu64 ", a multiple of 4096.",
1583 lvalue, parsed, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), *sz);
e594a3b1
LP
1584
1585 return 0;
1586}
1587
c380047b
MC
1588static int config_parse_block_size(
1589 const char *unit,
1590 const char *filename,
1591 unsigned line,
1592 const char *section,
1593 unsigned section_line,
1594 const char *lvalue,
1595 int ltype,
1596 const char *rvalue,
1597 void *data,
1598 void *userdata) {
1599
1600 uint64_t *blksz = ASSERT_PTR(data), parsed;
1601 int r;
1602
1603 assert(rvalue);
1604
1605 r = parse_size(rvalue, 1024, &parsed);
1606 if (r < 0)
1607 return log_syntax(unit, LOG_ERR, filename, line, r,
1608 "Failed to parse size value: %s", rvalue);
1609
1610 if (parsed < 512 || parsed > 4096)
1611 return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
1612 "Value not between 512 and 4096: %s", rvalue);
1613
1614 if (!ISPOWEROF2(parsed))
1615 return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
1616 "Value not a power of 2: %s", rvalue);
1617
1618 *blksz = parsed;
1619 return 0;
1620}
1621
53171c04
LP
1622static int config_parse_fstype(
1623 const char *unit,
1624 const char *filename,
1625 unsigned line,
1626 const char *section,
1627 unsigned section_line,
1628 const char *lvalue,
1629 int ltype,
1630 const char *rvalue,
1631 void *data,
1632 void *userdata) {
1633
99534007 1634 char **fstype = ASSERT_PTR(data);
e59049d7 1635 const char *e;
53171c04
LP
1636
1637 assert(rvalue);
53171c04 1638
e59049d7
LP
1639 /* Let's provide an easy way to override the chosen fstype for file system partitions */
1640 e = secure_getenv("SYSTEMD_REPART_OVERRIDE_FSTYPE");
1641 if (e && !streq(rvalue, e)) {
1642 log_syntax(unit, LOG_NOTICE, filename, line, 0,
1643 "Overriding defined file system type '%s' with '%s'.", rvalue, e);
1644 rvalue = e;
1645 }
1646
53171c04
LP
1647 if (!filename_is_valid(rvalue))
1648 return log_syntax(unit, LOG_ERR, filename, line, 0,
1649 "File system type is not valid, refusing: %s", rvalue);
1650
1651 return free_and_strdup_warn(fstype, rvalue);
1652}
1653
8a794850
LP
1654static int config_parse_copy_files(
1655 const char *unit,
1656 const char *filename,
1657 unsigned line,
1658 const char *section,
1659 unsigned section_line,
1660 const char *lvalue,
1661 int ltype,
1662 const char *rvalue,
1663 void *data,
1664 void *userdata) {
1665
1666 _cleanup_free_ char *source = NULL, *buffer = NULL, *resolved_source = NULL, *resolved_target = NULL;
1667 const char *p = rvalue, *target;
de98e6a7 1668 char ***copy_files = ASSERT_PTR(data);
8a794850
LP
1669 int r;
1670
1671 assert(rvalue);
8a794850
LP
1672
1673 r = extract_first_word(&p, &source, ":", EXTRACT_CUNESCAPE|EXTRACT_DONT_COALESCE_SEPARATORS);
1674 if (r < 0)
1675 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract source path: %s", rvalue);
1676 if (r == 0) {
1677 log_syntax(unit, LOG_WARNING, filename, line, 0, "No argument specified: %s", rvalue);
1678 return 0;
1679 }
1680
1681 r = extract_first_word(&p, &buffer, ":", EXTRACT_CUNESCAPE|EXTRACT_DONT_COALESCE_SEPARATORS);
1682 if (r < 0)
1683 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract target path: %s", rvalue);
1684 if (r == 0)
1685 target = source; /* No target, then it's the same as the source */
1686 else
1687 target = buffer;
1688
1689 if (!isempty(p))
1690 return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Too many arguments: %s", rvalue);
1691
de61a04b 1692 r = specifier_printf(source, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_source);
8a794850
LP
1693 if (r < 0) {
1694 log_syntax(unit, LOG_WARNING, filename, line, r,
1695 "Failed to expand specifiers in CopyFiles= source, ignoring: %s", rvalue);
1696 return 0;
1697 }
1698
0ade2213
LP
1699 r = path_simplify_and_warn(resolved_source, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
1700 if (r < 0)
8a794850 1701 return 0;
8a794850 1702
de61a04b 1703 r = specifier_printf(target, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_target);
8a794850
LP
1704 if (r < 0) {
1705 log_syntax(unit, LOG_WARNING, filename, line, r,
1706 "Failed to expand specifiers in CopyFiles= target, ignoring: %s", resolved_target);
1707 return 0;
1708 }
1709
0ade2213
LP
1710 r = path_simplify_and_warn(resolved_target, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
1711 if (r < 0)
8a794850 1712 return 0;
8a794850 1713
de98e6a7 1714 r = strv_consume_pair(copy_files, TAKE_PTR(resolved_source), TAKE_PTR(resolved_target));
8a794850
LP
1715 if (r < 0)
1716 return log_oom();
1717
1718 return 0;
1719}
1720
a9af8276
DDM
1721static int config_parse_exclude_files(
1722 const char *unit,
1723 const char *filename,
1724 unsigned line,
1725 const char *section,
1726 unsigned section_line,
1727 const char *lvalue,
1728 int ltype,
1729 const char *rvalue,
1730 void *data,
1731 void *userdata) {
81af8f99 1732
a9af8276 1733 char ***exclude_files = ASSERT_PTR(data);
81af8f99 1734 const char *p = ASSERT_PTR(rvalue);
a9af8276
DDM
1735 int r;
1736
1737 if (isempty(rvalue)) {
1738 *exclude_files = strv_free(*exclude_files);
1739 return 0;
1740 }
1741
81af8f99
DDM
1742 for (;;) {
1743 _cleanup_free_ char *word = NULL, *resolved = NULL;
a9af8276 1744
81af8f99
DDM
1745 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
1746 if (r == -ENOMEM)
1747 return log_oom();
1748 if (r < 0) {
1749 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", p);
1750 return 0;
1751 }
1752 if (r == 0)
1753 return 0;
a9af8276 1754
81af8f99
DDM
1755 r = specifier_printf(word, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved);
1756 if (r < 0) {
1757 log_syntax(unit, LOG_WARNING, filename, line, r,
1758 "Failed to expand specifiers in %s path, ignoring: %s", lvalue, word);
1759 return 0;
1760 }
1761
1762 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE|PATH_KEEP_TRAILING_SLASH, unit, filename, line, lvalue);
1763 if (r < 0)
1764 return 0;
1765
1766 if (strv_consume(exclude_files, TAKE_PTR(resolved)) < 0)
1767 return log_oom();
1768 }
8a794850
LP
1769
1770 return 0;
1771}
1772
5c08da58
LP
1773static int config_parse_copy_blocks(
1774 const char *unit,
1775 const char *filename,
1776 unsigned line,
1777 const char *section,
1778 unsigned section_line,
1779 const char *lvalue,
1780 int ltype,
1781 const char *rvalue,
1782 void *data,
1783 void *userdata) {
1784
1785 _cleanup_free_ char *d = NULL;
99534007 1786 Partition *partition = ASSERT_PTR(data);
5c08da58
LP
1787 int r;
1788
1789 assert(rvalue);
5c08da58
LP
1790
1791 if (isempty(rvalue)) {
1792 partition->copy_blocks_path = mfree(partition->copy_blocks_path);
1793 partition->copy_blocks_auto = false;
1794 return 0;
1795 }
1796
1797 if (streq(rvalue, "auto")) {
1798 partition->copy_blocks_path = mfree(partition->copy_blocks_path);
1799 partition->copy_blocks_auto = true;
585c5c75 1800 partition->copy_blocks_root = arg_root;
5c08da58
LP
1801 return 0;
1802 }
1803
de61a04b 1804 r = specifier_printf(rvalue, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &d);
5c08da58
LP
1805 if (r < 0) {
1806 log_syntax(unit, LOG_WARNING, filename, line, r,
1807 "Failed to expand specifiers in CopyBlocks= source path, ignoring: %s", rvalue);
1808 return 0;
1809 }
1810
1811 r = path_simplify_and_warn(d, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
1812 if (r < 0)
1813 return 0;
1814
1815 free_and_replace(partition->copy_blocks_path, d);
1816 partition->copy_blocks_auto = false;
585c5c75 1817 partition->copy_blocks_root = arg_root;
5c08da58
LP
1818 return 0;
1819}
1820
d83d8048
LP
1821static int config_parse_make_dirs(
1822 const char *unit,
1823 const char *filename,
1824 unsigned line,
1825 const char *section,
1826 unsigned section_line,
1827 const char *lvalue,
1828 int ltype,
1829 const char *rvalue,
1830 void *data,
1831 void *userdata) {
1832
440f805c 1833 char ***sv = ASSERT_PTR(data);
99534007 1834 const char *p = ASSERT_PTR(rvalue);
d83d8048
LP
1835 int r;
1836
d83d8048
LP
1837 for (;;) {
1838 _cleanup_free_ char *word = NULL, *d = NULL;
1839
1840 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
1841 if (r == -ENOMEM)
1842 return log_oom();
1843 if (r < 0) {
1844 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
1845 return 0;
1846 }
1847 if (r == 0)
1848 return 0;
1849
de61a04b 1850 r = specifier_printf(word, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &d);
d83d8048
LP
1851 if (r < 0) {
1852 log_syntax(unit, LOG_WARNING, filename, line, r,
1853 "Failed to expand specifiers in MakeDirectories= parameter, ignoring: %s", word);
1854 continue;
1855 }
1856
1857 r = path_simplify_and_warn(d, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
1858 if (r < 0)
1859 continue;
1860
f75641b7
DDM
1861 if (strv_contains(*sv, d))
1862 continue;
1863
440f805c 1864 r = strv_consume(sv, TAKE_PTR(d));
d83d8048
LP
1865 if (r < 0)
1866 return log_oom();
1867 }
1868}
1869
c64ddefd
DDM
1870static int config_parse_make_symlinks(
1871 const char *unit,
1872 const char *filename,
1873 unsigned line,
1874 const char *section,
1875 unsigned section_line,
1876 const char *lvalue,
1877 int ltype,
1878 const char *rvalue,
1879 void *data,
1880 void *userdata) {
1881
1882 char ***sv = ASSERT_PTR(data);
1883 const char *p = ASSERT_PTR(rvalue);
1884 int r;
1885
1886 for (;;) {
1887 _cleanup_free_ char *word = NULL, *path = NULL, *target = NULL, *d = NULL;
1888
1889 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
1890 if (r == -ENOMEM)
1891 return log_oom();
1892 if (r < 0) {
1893 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
1894 return 0;
1895 }
1896 if (r == 0)
1897 return 0;
1898
1899 const char *q = word;
1900 r = extract_many_words(&q, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS, &path, &target);
1901 if (r < 0) {
1902 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", q);
1903 continue;
1904 }
1905 if (r != 2) {
1906 log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL),
1907 "Missing source or target in %s, ignoring", rvalue);
1908 continue;
1909 }
1910
1911 r = specifier_printf(path, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, /*userdata=*/ NULL, &d);
1912 if (r < 0) {
1913 log_syntax(unit, LOG_WARNING, filename, line, r,
1914 "Failed to expand specifiers in Subvolumes= parameter, ignoring: %s", path);
1915 continue;
1916 }
1917
1918 r = path_simplify_and_warn(d, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
1919 if (r < 0)
1920 continue;
1921
1922 r = strv_consume_pair(sv, TAKE_PTR(d), TAKE_PTR(target));
1923 if (r < 0)
1924 return log_error_errno(r, "Failed to add symlink to list: %m");
1925 }
1926}
1927
562881c1
DDM
1928static int config_parse_subvolumes(
1929 const char *unit,
1930 const char *filename,
1931 unsigned line,
1932 const char *section,
1933 unsigned section_line,
1934 const char *lvalue,
1935 int ltype,
1936 const char *rvalue,
1937 void *data,
1938 void *userdata) {
1939
1940 OrderedHashmap **subvolumes = ASSERT_PTR(data);
1941 const char *p = ASSERT_PTR(rvalue);
1942 int r;
1943
1944 for (;;) {
1945 _cleanup_free_ char *word = NULL, *path = NULL, *f = NULL, *d = NULL;
1946 Subvolume *s = NULL;
1947
1948 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
1949 if (r == -ENOMEM)
1950 return log_oom();
1951 if (r < 0) {
1952 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
1953 return 0;
1954 }
1955 if (r == 0)
1956 return 0;
1957
bc48bd83
DDM
1958 const char *q = word;
1959 r = extract_many_words(&q, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS, &path, &f);
562881c1 1960 if (r < 0) {
bc48bd83 1961 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", q);
562881c1
DDM
1962 continue;
1963 }
1964
e2b0f237 1965 r = specifier_printf(path, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, /*userdata=*/ NULL, &d);
562881c1
DDM
1966 if (r < 0) {
1967 log_syntax(unit, LOG_WARNING, filename, line, r,
1968 "Failed to expand specifiers in Subvolumes= parameter, ignoring: %s", path);
1969 continue;
1970 }
1971
1972 r = path_simplify_and_warn(d, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
1973 if (r < 0)
1974 continue;
1975
1976 s = ordered_hashmap_get(*subvolumes, d);
1977 if (!s) {
1978 s = new(Subvolume, 1);
1979 if (!s)
1980 return log_oom();
1981
1982 *s = (Subvolume) {
1983 .path = TAKE_PTR(d),
1984 };
1985
1986 r = ordered_hashmap_ensure_put(subvolumes, &subvolume_hash_ops, s->path, s);
1987 if (r < 0) {
1988 subvolume_free(s);
1989 return r;
1990 }
1991 }
1992
1993 if (f) {
1994 SubvolumeFlags flags = subvolume_flags_from_string(f);
1995 if (flags == -EBADRQC) {
1996 log_syntax(unit, LOG_WARNING, filename, line, r, "Unknown subvolume flag in subvolume, ignoring: %s", f);
1997 continue;
1998 }
1999 if (flags < 0) {
2000 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse subvolume flags, ignoring: %s", f);
2001 continue;
2002 }
2003
2004 s->flags = flags;
2005 }
2006 }
2007}
2008
3799fa80
DDM
2009static int config_parse_default_subvolume(
2010 const char *unit,
2011 const char *filename,
2012 unsigned line,
2013 const char *section,
2014 unsigned section_line,
2015 const char *lvalue,
2016 int ltype,
2017 const char *rvalue,
2018 void *data,
2019 void *userdata) {
2020
2021 char **subvol = ASSERT_PTR(data);
2022 _cleanup_free_ char *p = NULL;
2023 int r;
2024
2025 if (isempty(rvalue)) {
2026 *subvol = mfree(*subvol);
2027 return 0;
2028 }
2029
2030 r = specifier_printf(rvalue, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &p);
2031 if (r < 0) {
2032 log_syntax(unit, LOG_WARNING, filename, line, r,
2033 "Failed to expand specifiers in DefaultSubvolume= parameter, ignoring: %s", rvalue);
2034 return 0;
2035 }
2036
2037 r = path_simplify_and_warn(p, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
2038 if (r < 0)
2039 return 0;
2040
2041 return free_and_replace(*subvol, p);
2042}
2043
42efe5be 2044static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_encrypt, encrypt_mode, EncryptMode, ENCRYPT_OFF);
889914ef 2045
e73309c5
LP
2046static int config_parse_gpt_flags(
2047 const char *unit,
2048 const char *filename,
2049 unsigned line,
2050 const char *section,
2051 unsigned section_line,
2052 const char *lvalue,
2053 int ltype,
2054 const char *rvalue,
2055 void *data,
2056 void *userdata) {
2057
99534007 2058 uint64_t *gpt_flags = ASSERT_PTR(data);
e73309c5
LP
2059 int r;
2060
2061 assert(rvalue);
e73309c5
LP
2062
2063 r = safe_atou64(rvalue, gpt_flags);
2064 if (r < 0) {
2065 log_syntax(unit, LOG_WARNING, filename, line, r,
2066 "Failed to parse Flags= value, ignoring: %s", rvalue);
2067 return 0;
2068 }
2069
2070 return 0;
2071}
2072
11749b61
DDM
2073static int config_parse_uuid(
2074 const char *unit,
2075 const char *filename,
2076 unsigned line,
2077 const char *section,
2078 unsigned section_line,
2079 const char *lvalue,
2080 int ltype,
2081 const char *rvalue,
2082 void *data,
2083 void *userdata) {
2084
2085 Partition *partition = ASSERT_PTR(data);
2086 int r;
2087
2088 if (isempty(rvalue)) {
2089 partition->new_uuid = SD_ID128_NULL;
2090 partition->new_uuid_is_set = false;
2091 return 0;
2092 }
2093
2094 if (streq(rvalue, "null")) {
2095 partition->new_uuid = SD_ID128_NULL;
2096 partition->new_uuid_is_set = true;
2097 return 0;
2098 }
2099
2100 r = sd_id128_from_string(rvalue, &partition->new_uuid);
2101 if (r < 0) {
da890466 2102 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse 128-bit ID/UUID, ignoring: %s", rvalue);
11749b61
DDM
2103 return 0;
2104 }
2105
2106 partition->new_uuid_is_set = true;
2107
2108 return 0;
2109}
2110
1a0541d4
DDM
2111static int config_parse_mountpoint(
2112 const char *unit,
2113 const char *filename,
2114 unsigned line,
2115 const char *section,
2116 unsigned section_line,
2117 const char *lvalue,
2118 int ltype,
2119 const char *rvalue,
2120 void *data,
2121 void *userdata) {
2122
2123 _cleanup_free_ char *where = NULL, *options = NULL;
2124 Partition *p = ASSERT_PTR(data);
2125 int r;
2126
2127 if (isempty(rvalue)) {
2128 partition_mountpoint_free_many(p->mountpoints, p->n_mountpoints);
2129 return 0;
2130 }
2131
2132 const char *q = rvalue;
2133 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_UNQUOTE,
4f495126 2134 &where, &options);
1a0541d4
DDM
2135 if (r == -ENOMEM)
2136 return log_oom();
2137 if (r < 0) {
2138 log_syntax(unit, LOG_WARNING, filename, line, r,
2139 "Invalid syntax in %s=, ignoring: %s", lvalue, rvalue);
2140 return 0;
2141 }
2142 if (r < 1) {
4e494e6a 2143 log_syntax(unit, LOG_WARNING, filename, line, 0,
1a0541d4
DDM
2144 "Too few arguments in %s=, ignoring: %s", lvalue, rvalue);
2145 return 0;
2146 }
2147 if (!isempty(q)) {
4e494e6a 2148 log_syntax(unit, LOG_WARNING, filename, line, 0,
1a0541d4
DDM
2149 "Too many arguments in %s=, ignoring: %s", lvalue, rvalue);
2150 return 0;
2151 }
2152
2153 r = path_simplify_and_warn(where, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
2154 if (r < 0)
2155 return 0;
2156
2157 if (!GREEDY_REALLOC(p->mountpoints, p->n_mountpoints + 1))
2158 return log_oom();
2159
2160 p->mountpoints[p->n_mountpoints++] = (PartitionMountPoint) {
2161 .where = TAKE_PTR(where),
2162 .options = TAKE_PTR(options),
2163 };
2164
2165 return 0;
2166}
2167
2168static int config_parse_encrypted_volume(
2169 const char *unit,
2170 const char *filename,
2171 unsigned line,
2172 const char *section,
2173 unsigned section_line,
2174 const char *lvalue,
2175 int ltype,
2176 const char *rvalue,
2177 void *data,
2178 void *userdata) {
2179
2180 _cleanup_free_ char *volume = NULL, *keyfile = NULL, *options = NULL;
2181 Partition *p = ASSERT_PTR(data);
2182 int r;
2183
2184 if (isempty(rvalue)) {
2185 p->encrypted_volume = mfree(p->encrypted_volume);
2186 return 0;
2187 }
2188
2189 const char *q = rvalue;
2190 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_UNQUOTE,
4f495126 2191 &volume, &keyfile, &options);
1a0541d4
DDM
2192 if (r == -ENOMEM)
2193 return log_oom();
2194 if (r < 0) {
2195 log_syntax(unit, LOG_WARNING, filename, line, r,
2196 "Invalid syntax in %s=, ignoring: %s", lvalue, rvalue);
2197 return 0;
2198 }
2199 if (r < 1) {
4e494e6a 2200 log_syntax(unit, LOG_WARNING, filename, line, 0,
1a0541d4
DDM
2201 "Too few arguments in %s=, ignoring: %s", lvalue, rvalue);
2202 return 0;
2203 }
2204 if (!isempty(q)) {
4e494e6a 2205 log_syntax(unit, LOG_WARNING, filename, line, 0,
1a0541d4
DDM
2206 "Too many arguments in %s=, ignoring: %s", lvalue, rvalue);
2207 return 0;
2208 }
2209
2210 if (!filename_is_valid(volume)) {
4e494e6a 2211 log_syntax(unit, LOG_WARNING, filename, line, 0,
1a0541d4
DDM
2212 "Volume name %s is not valid, ignoring", volume);
2213 return 0;
2214 }
2215
2216 partition_encrypted_volume_free(p->encrypted_volume);
2217
2218 p->encrypted_volume = new(PartitionEncryptedVolume, 1);
2219 if (!p->encrypted_volume)
2220 return log_oom();
2221
2222 *p->encrypted_volume = (PartitionEncryptedVolume) {
2223 .name = TAKE_PTR(volume),
2224 .keyfile = TAKE_PTR(keyfile),
2225 .options = TAKE_PTR(options),
2226 };
2227
2228 return 0;
2229}
2230
42efe5be
YW
2231static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_verity, verity_mode, VerityMode, VERITY_OFF);
2232static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_minimize, minimize_mode, MinimizeMode, MINIMIZE_OFF);
b5b7879a 2233
90a25577
DDM
2234static int partition_finalize_fstype(Partition *p, const char *path) {
2235 _cleanup_free_ char *e = NULL, *upper = NULL;
2236
2237 assert(p);
2238 assert(path);
2239
2240 if (!gpt_partition_type_has_filesystem(p->type))
2241 return 0;
2242
2243 upper = strdup(partition_designator_to_string(p->type.designator));
2244 if (!upper)
2245 return log_oom();
2246
2247 e = strjoin("SYSTEMD_REPART_OVERRIDE_FSTYPE_", string_replace_char(ascii_strupper(upper), '-', '_'));
2248 if (!e)
2249 return log_oom();
2250
2251 const char *v = secure_getenv(e);
2252 if (!v || streq(p->format, v))
2253 return 0;
2254
2255 log_syntax(NULL, LOG_NOTICE, path, 1, 0,
2256 "Overriding defined file system type '%s' for '%s' partition with '%s'.",
2257 p->format, partition_designator_to_string(p->type.designator), v);
2258
2259 return free_and_strdup_warn(&p->format, v);
2260}
2261
22324523
DDM
2262static bool partition_needs_populate(const Partition *p) {
2263 assert(p);
2cb9c68c
AV
2264 assert(!p->supplement_for || !p->suppressing); /* Avoid infinite recursion */
2265 return !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories) || !strv_isempty(p->make_symlinks) ||
2266 (p->suppressing && partition_needs_populate(p->suppressing));
22324523
DDM
2267}
2268
39fc0174 2269static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
e594a3b1
LP
2270
2271 ConfigTableItem table[] = {
27cacec9
DDM
2272 { "Partition", "Type", config_parse_type, 0, &p->type },
2273 { "Partition", "Label", config_parse_label, 0, &p->new_label },
2274 { "Partition", "UUID", config_parse_uuid, 0, p },
2275 { "Partition", "Priority", config_parse_int32, 0, &p->priority },
2276 { "Partition", "Weight", config_parse_weight, 0, &p->weight },
2277 { "Partition", "PaddingWeight", config_parse_weight, 0, &p->padding_weight },
2278 { "Partition", "SizeMinBytes", config_parse_size4096, -1, &p->size_min },
2279 { "Partition", "SizeMaxBytes", config_parse_size4096, 1, &p->size_max },
2280 { "Partition", "PaddingMinBytes", config_parse_size4096, -1, &p->padding_min },
2281 { "Partition", "PaddingMaxBytes", config_parse_size4096, 1, &p->padding_max },
2282 { "Partition", "FactoryReset", config_parse_bool, 0, &p->factory_reset },
2283 { "Partition", "CopyBlocks", config_parse_copy_blocks, 0, p },
2284 { "Partition", "Format", config_parse_fstype, 0, &p->format },
2285 { "Partition", "CopyFiles", config_parse_copy_files, 0, &p->copy_files },
2286 { "Partition", "ExcludeFiles", config_parse_exclude_files, 0, &p->exclude_files_source },
2287 { "Partition", "ExcludeFilesTarget", config_parse_exclude_files, 0, &p->exclude_files_target },
2288 { "Partition", "MakeDirectories", config_parse_make_dirs, 0, &p->make_directories },
c64ddefd 2289 { "Partition", "MakeSymlinks", config_parse_make_symlinks, 0, &p->make_symlinks },
27cacec9
DDM
2290 { "Partition", "Encrypt", config_parse_encrypt, 0, &p->encrypt },
2291 { "Partition", "Verity", config_parse_verity, 0, &p->verity },
2292 { "Partition", "VerityMatchKey", config_parse_string, 0, &p->verity_match_key },
2293 { "Partition", "Flags", config_parse_gpt_flags, 0, &p->gpt_flags },
2294 { "Partition", "ReadOnly", config_parse_tristate, 0, &p->read_only },
2295 { "Partition", "NoAuto", config_parse_tristate, 0, &p->no_auto },
2296 { "Partition", "GrowFileSystem", config_parse_tristate, 0, &p->growfs },
2297 { "Partition", "SplitName", config_parse_string, 0, &p->split_name_format },
2298 { "Partition", "Minimize", config_parse_minimize, 0, &p->minimize },
2299 { "Partition", "Subvolumes", config_parse_subvolumes, 0, &p->subvolumes },
2300 { "Partition", "DefaultSubvolume", config_parse_default_subvolume, 0, &p->default_subvolume },
2301 { "Partition", "VerityDataBlockSizeBytes", config_parse_block_size, 0, &p->verity_data_block_size },
2302 { "Partition", "VerityHashBlockSizeBytes", config_parse_block_size, 0, &p->verity_hash_block_size },
2303 { "Partition", "MountPoint", config_parse_mountpoint, 0, p },
2304 { "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p },
2305 { "Partition", "Compression", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression },
2306 { "Partition", "CompressionLevel", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression_level },
2cb9c68c 2307 { "Partition", "SupplementFor", config_parse_string, 0, &p->supplement_for_name },
e594a3b1
LP
2308 {}
2309 };
39fc0174
RP
2310 _cleanup_free_ char *filename = NULL;
2311 const char* dropin_dirname;
562881c1 2312 int r;
e594a3b1 2313
39fc0174
RP
2314 r = path_extract_filename(path, &filename);
2315 if (r < 0)
bef69ae8 2316 return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);
39fc0174
RP
2317
2318 dropin_dirname = strjoina(filename, ".d");
2319
2320 r = config_parse_many(
2321 STRV_MAKE_CONST(path),
2322 conf_file_dirs,
2323 dropin_dirname,
34f2fd50 2324 arg_definitions ? NULL : arg_root,
39fc0174
RP
2325 "Partition\0",
2326 config_item_table_lookup, table,
2327 CONFIG_PARSE_WARN,
2328 p,
2329 NULL,
2330 &p->drop_in_files);
e594a3b1
LP
2331 if (r < 0)
2332 return r;
2333
c74d50ff 2334 if (partition_type_exclude(&p->type))
7d505753
DDM
2335 return 0;
2336
e594a3b1
LP
2337 if (p->size_min != UINT64_MAX && p->size_max != UINT64_MAX && p->size_min > p->size_max)
2338 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
2339 "SizeMinBytes= larger than SizeMaxBytes=, refusing.");
2340
2341 if (p->padding_min != UINT64_MAX && p->padding_max != UINT64_MAX && p->padding_min > p->padding_max)
2342 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
2343 "PaddingMinBytes= larger than PaddingMaxBytes=, refusing.");
2344
22e932f4 2345 if (sd_id128_is_null(p->type.uuid))
e594a3b1
LP
2346 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
2347 "Type= not defined, refusing.");
2348
22324523 2349 if ((p->copy_blocks_path || p->copy_blocks_auto) && (p->format || partition_needs_populate(p)))
86320e62 2350 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
c64ddefd 2351 "Format=/CopyFiles=/MakeDirectories=/MakeSymlinks= and CopyBlocks= cannot be combined, refusing.");
86320e62 2352
22324523 2353 if (partition_needs_populate(p) && streq_ptr(p->format, "swap"))
8a794850 2354 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
c64ddefd 2355 "Format=swap and CopyFiles=/MakeDirectories=/MakeSymlinks= cannot be combined, refusing.");
8a794850 2356
973d6be4
DDM
2357 if (!p->format) {
2358 const char *format = NULL;
2359
942eba93
DDM
2360 if (p->type.designator == PARTITION_SWAP)
2361 format = "swap";
2362 else if (partition_needs_populate(p) || (p->encrypt != ENCRYPT_OFF && !(p->copy_blocks_path || p->copy_blocks_auto)))
973d6be4
DDM
2363 /* Pick "vfat" as file system for esp and xbootldr partitions, otherwise default to "ext4". */
2364 format = IN_SET(p->type.designator, PARTITION_ESP, PARTITION_XBOOTLDR) ? "vfat" : "ext4";
973d6be4
DDM
2365
2366 if (format) {
2367 p->format = strdup(format);
2368 if (!p->format)
2369 return log_oom();
2370 }
8a794850
LP
2371 }
2372
5eef7047 2373 if (p->minimize != MINIMIZE_OFF && !p->format && p->verity != VERITY_HASH)
c4a87b76 2374 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
4e494e6a 2375 "Minimize= can only be enabled if Format= or Verity=hash are set.");
c4a87b76 2376
5eef7047 2377 if (p->minimize == MINIMIZE_BEST && (p->format && !fstype_is_ro(p->format)) && p->verity != VERITY_HASH)
5c33b686 2378 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
4e494e6a 2379 "Minimize=best can only be used with read-only filesystems or Verity=hash.");
5c33b686 2380
22324523 2381 if (partition_needs_populate(p) && !mkfs_supports_root_option(p->format) && geteuid() != 0)
6d6cefad 2382 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EPERM),
c64ddefd 2383 "Need to be root to populate %s filesystems with CopyFiles=/MakeDirectories=/MakeSymlinks=.",
6d6cefad
DDM
2384 p->format);
2385
22324523 2386 if (p->format && fstype_is_ro(p->format) && !partition_needs_populate(p))
0eb23798 2387 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
4e494e6a 2388 "Cannot format %s filesystem without source files, refusing.", p->format);
0eb23798 2389
b5b7879a
DDM
2390 if (p->verity != VERITY_OFF || p->encrypt != ENCRYPT_OFF) {
2391 r = dlopen_cryptsetup();
2392 if (r < 0)
2393 return log_syntax(NULL, LOG_ERR, path, 1, r,
2394 "libcryptsetup not found, Verity=/Encrypt= are not supported: %m");
2395 }
2396
2397 if (p->verity != VERITY_OFF && !p->verity_match_key)
2398 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
4e494e6a 2399 "VerityMatchKey= must be set if Verity=%s.", verity_mode_to_string(p->verity));
b5b7879a
DDM
2400
2401 if (p->verity == VERITY_OFF && p->verity_match_key)
2402 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
4e494e6a 2403 "VerityMatchKey= can only be set if Verity= is not \"%s\".",
b5b7879a
DDM
2404 verity_mode_to_string(p->verity));
2405
22324523 2406 if (IN_SET(p->verity, VERITY_HASH, VERITY_SIG) && (p->copy_blocks_path || p->copy_blocks_auto || p->format || partition_needs_populate(p)))
b5b7879a 2407 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
c64ddefd 2408 "CopyBlocks=/CopyFiles=/Format=/MakeDirectories=/MakeSymlinks= cannot be used with Verity=%s.",
b5b7879a
DDM
2409 verity_mode_to_string(p->verity));
2410
2411 if (p->verity != VERITY_OFF && p->encrypt != ENCRYPT_OFF)
2412 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
4e494e6a 2413 "Encrypting verity hash/data partitions is not supported.");
b5b7879a 2414
b456191d
DDM
2415 if (p->verity == VERITY_SIG && !arg_private_key)
2416 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
4e494e6a 2417 "Verity signature partition requested but no private key provided (--private-key=).");
b456191d
DDM
2418
2419 if (p->verity == VERITY_SIG && !arg_certificate)
2420 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
4e494e6a 2421 "Verity signature partition requested but no PEM certificate provided (--certificate=).");
b456191d
DDM
2422
2423 if (p->verity == VERITY_SIG && (p->size_min != UINT64_MAX || p->size_max != UINT64_MAX))
2424 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
4e494e6a 2425 "SizeMinBytes=/SizeMaxBytes= cannot be used with Verity=%s.",
b456191d
DDM
2426 verity_mode_to_string(p->verity));
2427
4d9ccdc9
DDM
2428 if (!ordered_hashmap_isempty(p->subvolumes) && arg_offline > 0)
2429 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EOPNOTSUPP),
2430 "Subvolumes= cannot be used with --offline=yes.");
2431
2432 if (p->default_subvolume && arg_offline > 0)
2433 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EOPNOTSUPP),
2434 "DefaultSubvolume= cannot be used with --offline=yes.");
2435
562881c1 2436 if (p->default_subvolume && !ordered_hashmap_contains(p->subvolumes, p->default_subvolume))
3799fa80 2437 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
4e494e6a 2438 "DefaultSubvolume= must be one of the paths in Subvolumes=.");
3799fa80 2439
2cb9c68c
AV
2440 if (p->supplement_for_name) {
2441 if (!filename_part_is_valid(p->supplement_for_name))
2442 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
2443 "SupplementFor= is an invalid filename: %s",
2444 p->supplement_for_name);
2445
2446 if (p->copy_blocks_path || p->copy_blocks_auto || p->encrypt != ENCRYPT_OFF ||
2447 p->verity != VERITY_OFF)
2448 return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
2449 "SupplementFor= cannot be combined with CopyBlocks=/Encrypt=/Verity=");
2450 }
2451
e73309c5 2452 /* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */
2208d492
DDM
2453 if ((IN_SET(p->type.designator,
2454 PARTITION_ROOT_VERITY,
ade85cd5 2455 PARTITION_USR_VERITY) || p->verity == VERITY_DATA) && p->read_only < 0)
e73309c5
LP
2456 p->read_only = true;
2457
1c41c1dc 2458 /* Default to "growfs" on, unless read-only */
22e932f4 2459 if (gpt_partition_type_knows_growfs(p->type) &&
1c41c1dc
LP
2460 p->read_only <= 0)
2461 p->growfs = true;
2462
4cee8333
DDM
2463 if (!p->split_name_format) {
2464 char *s = strdup("%t");
2465 if (!s)
2466 return log_oom();
2467
2468 p->split_name_format = s;
2469 } else if (streq(p->split_name_format, "-"))
2470 p->split_name_format = mfree(p->split_name_format);
2471
90a25577
DDM
2472 r = partition_finalize_fstype(p, path);
2473 if (r < 0)
2474 return r;
2475
7d505753 2476 return 1;
e594a3b1
LP
2477}
2478
b5b7879a
DDM
2479static int find_verity_sibling(Context *context, Partition *p, VerityMode mode, Partition **ret) {
2480 Partition *s = NULL;
2481
2482 assert(p);
2483 assert(p->verity != VERITY_OFF);
2484 assert(p->verity_match_key);
2485 assert(mode != VERITY_OFF);
2486 assert(p->verity != mode);
2487 assert(ret);
2488
2489 /* Try to find the matching sibling partition of the given type for a verity partition. For a data
af3d3873
YW
2490 * partition, this is the corresponding hash partition with the same verity name (and vice versa for
2491 * the hash partition). */
b5b7879a
DDM
2492
2493 LIST_FOREACH(partitions, q, context->partitions) {
2494 if (p == q)
2495 continue;
2496
2497 if (q->verity != mode)
2498 continue;
2499
2500 assert(q->verity_match_key);
2501
2502 if (!streq(p->verity_match_key, q->verity_match_key))
2503 continue;
2504
2505 if (s)
2506 return -ENOTUNIQ;
2507
2508 s = q;
2509 }
2510
2511 if (!s)
2512 return -ENXIO;
2513
2514 *ret = s;
2515
2516 return 0;
2517}
2518
3cdcf4e2 2519static int context_open_and_lock_backing_fd(const char *node, int operation, int *backing_fd) {
1e46985a
DDM
2520 _cleanup_close_ int fd = -EBADF;
2521
2522 assert(node);
2523 assert(backing_fd);
2524
2525 if (*backing_fd >= 0)
2526 return 0;
2527
2528 fd = open(node, O_RDONLY|O_CLOEXEC);
2529 if (fd < 0)
2530 return log_error_errno(errno, "Failed to open device '%s': %m", node);
2531
2532 /* Tell udev not to interfere while we are processing the device */
3cdcf4e2 2533 if (flock(fd, operation) < 0)
1e46985a
DDM
2534 return log_error_errno(errno, "Failed to lock device '%s': %m", node);
2535
2536 log_debug("Device %s opened and locked.", node);
2537 *backing_fd = TAKE_FD(fd);
2538 return 1;
2539}
2540
2541static int determine_current_padding(
2542 struct fdisk_context *c,
2543 struct fdisk_table *t,
2544 struct fdisk_partition *p,
2545 uint64_t secsz,
2546 uint64_t grainsz,
2547 uint64_t *ret) {
2548
2549 size_t n_partitions;
2550 uint64_t offset, next = UINT64_MAX;
2551
2552 assert(c);
2553 assert(t);
2554 assert(p);
2555 assert(ret);
2556
2557 if (!fdisk_partition_has_end(p))
4e494e6a 2558 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition has no end.");
1e46985a
DDM
2559
2560 offset = fdisk_partition_get_end(p);
2561 assert(offset < UINT64_MAX);
2562 offset++; /* The end is one sector before the next partition or padding. */
2563 assert(offset < UINT64_MAX / secsz);
2564 offset *= secsz;
2565
2566 n_partitions = fdisk_table_get_nents(t);
2567 for (size_t i = 0; i < n_partitions; i++) {
2568 struct fdisk_partition *q;
2569 uint64_t start;
2570
2571 q = fdisk_table_get_partition(t, i);
2572 if (!q)
4e494e6a 2573 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read partition metadata.");
1e46985a
DDM
2574
2575 if (fdisk_partition_is_used(q) <= 0)
2576 continue;
2577
2578 if (!fdisk_partition_has_start(q))
2579 continue;
2580
2581 start = fdisk_partition_get_start(q);
2582 assert(start < UINT64_MAX / secsz);
2583 start *= secsz;
2584
2585 if (start >= offset && (next == UINT64_MAX || next > start))
2586 next = start;
2587 }
2588
2589 if (next == UINT64_MAX) {
2590 /* No later partition? In that case check the end of the usable area */
2591 next = fdisk_get_last_lba(c);
2592 assert(next < UINT64_MAX);
2593 next++; /* The last LBA is one sector before the end */
2594
2595 assert(next < UINT64_MAX / secsz);
2596 next *= secsz;
2597
2598 if (offset > next)
2599 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition end beyond disk end.");
2600 }
2601
2602 assert(next >= offset);
2603 offset = round_up_size(offset, grainsz);
2604 next = round_down_size(next, grainsz);
2605
2606 *ret = LESS_BY(next, offset); /* Saturated subtraction, rounding might have fucked things up */
2607 return 0;
2608}
2609
e1536d1f 2610static int context_copy_from_one(Context *context, const char *src) {
1e46985a
DDM
2611 _cleanup_close_ int fd = -EBADF;
2612 _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
2613 _cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL;
2614 Partition *last = NULL;
2615 unsigned long secsz, grainsz;
2616 size_t n_partitions;
2617 int r;
2618
e1536d1f 2619 assert(src);
1e46985a 2620
e1536d1f 2621 r = context_open_and_lock_backing_fd(src, LOCK_SH, &fd);
1e46985a
DDM
2622 if (r < 0)
2623 return r;
2624
2625 r = fd_verify_regular(fd);
2626 if (r < 0)
e1536d1f 2627 return log_error_errno(r, "%s is not a file: %m", src);
1e46985a 2628
fd9fe57a 2629 r = fdisk_new_context_at(fd, /* path = */ NULL, /* read_only = */ true, /* sector_size = */ UINT32_MAX, &c);
1e46985a
DDM
2630 if (r < 0)
2631 return log_error_errno(r, "Failed to create fdisk context: %m");
2632
2633 secsz = fdisk_get_sector_size(c);
2634 grainsz = fdisk_get_grain_size(c);
2635
2636 /* Insist on a power of two, and that it's a multiple of 512, i.e. the traditional sector size. */
2637 if (secsz < 512 || !ISPOWEROF2(secsz))
2638 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Sector size %lu is not a power of two larger than 512? Refusing.", secsz);
2639
2640 if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT))
e1536d1f 2641 return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON), "Cannot copy from disk %s with no GPT disk label.", src);
1e46985a
DDM
2642
2643 r = fdisk_get_partitions(c, &t);
2644 if (r < 0)
2645 return log_error_errno(r, "Failed to acquire partition table: %m");
2646
2647 n_partitions = fdisk_table_get_nents(t);
2648 for (size_t i = 0; i < n_partitions; i++) {
2649 _cleanup_(partition_freep) Partition *np = NULL;
2650 _cleanup_free_ char *label_copy = NULL;
2651 struct fdisk_partition *p;
2652 const char *label;
2653 uint64_t sz, start, padding;
2654 sd_id128_t ptid, id;
2655 GptPartitionType type;
2656
2657 p = fdisk_table_get_partition(t, i);
2658 if (!p)
4e494e6a 2659 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read partition metadata.");
1e46985a
DDM
2660
2661 if (fdisk_partition_is_used(p) <= 0)
2662 continue;
2663
2664 if (fdisk_partition_has_start(p) <= 0 ||
2665 fdisk_partition_has_size(p) <= 0 ||
2666 fdisk_partition_has_partno(p) <= 0)
2667 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found a partition without a position, size or number.");
2668
2669 r = fdisk_partition_get_type_as_id128(p, &ptid);
2670 if (r < 0)
2671 return log_error_errno(r, "Failed to query partition type UUID: %m");
2672
2673 type = gpt_partition_type_from_uuid(ptid);
2674
2675 r = fdisk_partition_get_uuid_as_id128(p, &id);
2676 if (r < 0)
2677 return log_error_errno(r, "Failed to query partition UUID: %m");
2678
2679 label = fdisk_partition_get_name(p);
2680 if (!isempty(label)) {
2681 label_copy = strdup(label);
2682 if (!label_copy)
2683 return log_oom();
2684 }
2685
2686 sz = fdisk_partition_get_size(p);
2687 assert(sz <= UINT64_MAX/secsz);
2688 sz *= secsz;
2689
2690 start = fdisk_partition_get_start(p);
2691 assert(start <= UINT64_MAX/secsz);
2692 start *= secsz;
2693
c74d50ff 2694 if (partition_type_exclude(&type))
1e46985a
DDM
2695 continue;
2696
2697 np = partition_new();
2698 if (!np)
2699 return log_oom();
2700
2701 np->type = type;
2702 np->new_uuid = id;
2703 np->new_uuid_is_set = true;
2704 np->size_min = np->size_max = sz;
2705 np->new_label = TAKE_PTR(label_copy);
2706
e1536d1f 2707 np->definition_path = strdup(src);
1e46985a
DDM
2708 if (!np->definition_path)
2709 return log_oom();
2710
6e4d47b5
DDM
2711 np->split_name_format = strdup("%t");
2712 if (!np->split_name_format)
2713 return log_oom();
2714
1e46985a
DDM
2715 r = determine_current_padding(c, t, p, secsz, grainsz, &padding);
2716 if (r < 0)
2717 return r;
2718
2719 np->padding_min = np->padding_max = padding;
2720
e1536d1f 2721 np->copy_blocks_path = strdup(src);
1e46985a
DDM
2722 if (!np->copy_blocks_path)
2723 return log_oom();
2724
2725 np->copy_blocks_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
2726 if (np->copy_blocks_fd < 0)
e1536d1f 2727 return log_error_errno(r, "Failed to duplicate file descriptor of %s: %m", src);
1e46985a
DDM
2728
2729 np->copy_blocks_offset = start;
2730 np->copy_blocks_size = sz;
2731
2732 r = fdisk_partition_get_attrs_as_uint64(p, &np->gpt_flags);
2733 if (r < 0)
2734 return log_error_errno(r, "Failed to get partition flags: %m");
2735
2736 LIST_INSERT_AFTER(partitions, context->partitions, last, np);
2737 last = TAKE_PTR(np);
2738 context->n_partitions++;
2739 }
2740
2741 return 0;
2742}
2743
e1536d1f
DDM
2744static int context_copy_from(Context *context) {
2745 int r;
2746
2747 assert(context);
2748
2749 STRV_FOREACH(src, arg_copy_from) {
2750 r = context_copy_from_one(context, *src);
2751 if (r < 0)
2752 return r;
2753 }
2754
2755 return 0;
2756}
2757
2cb9c68c
AV
2758static bool check_cross_def_ranges_valid(uint64_t a_min, uint64_t a_max, uint64_t b_min, uint64_t b_max) {
2759 if (a_min == UINT64_MAX && b_min == UINT64_MAX)
2760 return true;
2761
2762 if (a_max == UINT64_MAX && b_max == UINT64_MAX)
2763 return true;
2764
2765 return MAX(a_min != UINT64_MAX ? a_min : 0, b_min != UINT64_MAX ? b_min : 0) <= MIN(a_max, b_max);
2766}
2767
2768static int supplement_find_target(const Context *context, const Partition *supplement, Partition **ret) {
2769 int r;
2770
2771 assert(context);
2772 assert(supplement);
2773 assert(ret);
2774
2775 LIST_FOREACH(partitions, p, context->partitions) {
2776 _cleanup_free_ char *filename = NULL;
2777
2778 if (p == supplement)
2779 continue;
2780
2781 r = path_extract_filename(p->definition_path, &filename);
2782 if (r < 0)
2783 return log_error_errno(r,
2784 "Failed to extract filename from path '%s': %m",
2785 p->definition_path);
2786
2787 *ASSERT_PTR(endswith(filename, ".conf")) = 0; /* Remove the file extension */
2788
2789 if (!streq(supplement->supplement_for_name, filename))
2790 continue;
2791
2792 if (p->supplement_for_name)
2793 return log_syntax(NULL, LOG_ERR, supplement->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
2794 "SupplementFor= target is itself configured as a supplement.");
2795
2796 if (p->suppressing)
2797 return log_syntax(NULL, LOG_ERR, supplement->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
2798 "SupplementFor= target already has a supplement defined: %s",
2799 p->suppressing->definition_path);
2800
2801 *ret = p;
2802 return 0;
2803 }
2804
2805 return log_syntax(NULL, LOG_ERR, supplement->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
2806 "Couldn't find target partition for SupplementFor=%s",
2807 supplement->supplement_for_name);
2808}
2809
4b047310 2810static int context_read_definitions(Context *context) {
e594a3b1 2811 _cleanup_strv_free_ char **files = NULL;
1e46985a 2812 Partition *last = LIST_FIND_TAIL(partitions, context->partitions);
39fc0174 2813 const char *const *dirs;
9b05a371 2814 int r;
e594a3b1
LP
2815
2816 assert(context);
2817
4b047310 2818 dirs = (const char* const*) (arg_definitions ?: CONF_PATHS_STRV("repart.d"));
39fc0174 2819
4b047310 2820 r = conf_files_list_strv(&files, ".conf", arg_definitions ? NULL : arg_root, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, dirs);
e594a3b1
LP
2821 if (r < 0)
2822 return log_error_errno(r, "Failed to enumerate *.conf files: %m");
2823
2824 STRV_FOREACH(f, files) {
2825 _cleanup_(partition_freep) Partition *p = NULL;
2826
2827 p = partition_new();
2828 if (!p)
2829 return log_oom();
2830
2831 p->definition_path = strdup(*f);
2832 if (!p->definition_path)
2833 return log_oom();
2834
39fc0174 2835 r = partition_read_definition(p, *f, dirs);
e594a3b1
LP
2836 if (r < 0)
2837 return r;
7d505753
DDM
2838 if (r == 0)
2839 continue;
e594a3b1
LP
2840
2841 LIST_INSERT_AFTER(partitions, context->partitions, last, p);
2842 last = TAKE_PTR(p);
2843 context->n_partitions++;
2844 }
2845
b5b7879a
DDM
2846 /* Check that each configured verity hash/data partition has a matching verity data/hash partition. */
2847
2848 LIST_FOREACH(partitions, p, context->partitions) {
2849 if (p->verity == VERITY_OFF)
2850 continue;
2851
2852 for (VerityMode mode = VERITY_OFF + 1; mode < _VERITY_MODE_MAX; mode++) {
b456191d 2853 Partition *q = NULL;
b5b7879a
DDM
2854
2855 if (p->verity == mode)
2856 continue;
2857
2858 if (p->siblings[mode])
2859 continue;
2860
2861 r = find_verity_sibling(context, p, mode, &q);
8e52ed02
DDM
2862 if (r == -ENXIO) {
2863 if (mode != VERITY_SIG)
2864 return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
4e494e6a 2865 "Missing verity %s partition for verity %s partition with VerityMatchKey=%s.",
8e52ed02
DDM
2866 verity_mode_to_string(mode), verity_mode_to_string(p->verity), p->verity_match_key);
2867 } else if (r == -ENOTUNIQ)
b5b7879a 2868 return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
4e494e6a 2869 "Multiple verity %s partitions found for verity %s partition with VerityMatchKey=%s.",
b5b7879a 2870 verity_mode_to_string(mode), verity_mode_to_string(p->verity), p->verity_match_key);
8e52ed02
DDM
2871 else if (r < 0)
2872 return log_syntax(NULL, LOG_ERR, p->definition_path, 1, r,
4e494e6a 2873 "Failed to find verity %s partition for verity %s partition with VerityMatchKey=%s.",
8e52ed02 2874 verity_mode_to_string(mode), verity_mode_to_string(p->verity), p->verity_match_key);
b5b7879a 2875
b456191d
DDM
2876 if (q) {
2877 if (q->priority != p->priority)
2878 return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
4e494e6a 2879 "Priority mismatch (%i != %i) for verity sibling partitions with VerityMatchKey=%s.",
9f08d7f7 2880 p->priority, q->priority, p->verity_match_key);
b5b7879a 2881
b456191d
DDM
2882 p->siblings[mode] = q;
2883 }
b5b7879a
DDM
2884 }
2885 }
2886
5eef7047
DDM
2887 LIST_FOREACH(partitions, p, context->partitions) {
2888 Partition *dp;
2889
2890 if (p->verity != VERITY_HASH)
2891 continue;
2892
2893 if (p->minimize == MINIMIZE_OFF)
2894 continue;
2895
2896 assert_se(dp = p->siblings[VERITY_DATA]);
2897
5a77fbb8 2898 if (dp->minimize == MINIMIZE_OFF && !(dp->copy_blocks_path || dp->copy_blocks_auto))
5eef7047 2899 return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
4e494e6a 2900 "Minimize= set for verity hash partition but data partition does not set CopyBlocks= or Minimize=.");
2cb9c68c 2901 }
5eef7047 2902
2cb9c68c
AV
2903 LIST_FOREACH(partitions, p, context->partitions) {
2904 Partition *tgt = NULL;
2905
2906 if (!p->supplement_for_name)
2907 continue;
2908
2909 r = supplement_find_target(context, p, &tgt);
2910 if (r < 0)
2911 return r;
2912
2913 if (tgt->copy_blocks_path || tgt->copy_blocks_auto || tgt->encrypt != ENCRYPT_OFF ||
2914 tgt->verity != VERITY_OFF)
2915 return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
2916 "SupplementFor= target uses CopyBlocks=/Encrypt=/Verity=");
2917
2918 if (!check_cross_def_ranges_valid(p->size_min, p->size_max, tgt->size_min, tgt->size_max))
2919 return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
2920 "SizeMinBytes= larger than SizeMaxBytes= when merged with SupplementFor= target.");
2921
2922 if (!check_cross_def_ranges_valid(p->padding_min, p->padding_max, tgt->padding_min, tgt->padding_max))
2923 return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
2924 "PaddingMinBytes= larger than PaddingMaxBytes= when merged with SupplementFor= target.");
2925
2926 p->supplement_for = tgt;
2927 tgt->suppressing = tgt->supplement_target_for = p;
5eef7047
DDM
2928 }
2929
e594a3b1
LP
2930 return 0;
2931}
2932
e594a3b1
LP
2933static int fdisk_ask_cb(struct fdisk_context *c, struct fdisk_ask *ask, void *data) {
2934 _cleanup_free_ char *ids = NULL;
2935 int r;
2936
2937 if (fdisk_ask_get_type(ask) != FDISK_ASKTYPE_STRING)
2938 return -EINVAL;
2939
b7416360 2940 ids = new(char, SD_ID128_UUID_STRING_MAX);
e594a3b1
LP
2941 if (!ids)
2942 return -ENOMEM;
2943
b7416360 2944 r = fdisk_ask_string_set_result(ask, sd_id128_to_uuid_string(*(sd_id128_t*) data, ids));
e594a3b1
LP
2945 if (r < 0)
2946 return r;
2947
2948 TAKE_PTR(ids);
2949 return 0;
2950}
2951
2952static int fdisk_set_disklabel_id_by_uuid(struct fdisk_context *c, sd_id128_t id) {
2953 int r;
2954
2955 r = fdisk_set_ask(c, fdisk_ask_cb, &id);
2956 if (r < 0)
2957 return r;
2958
2959 r = fdisk_set_disklabel_id(c);
2960 if (r < 0)
2961 return r;
2962
2963 return fdisk_set_ask(c, NULL, NULL);
2964}
2965
53171c04 2966static int derive_uuid(sd_id128_t base, const char *token, sd_id128_t *ret) {
e594a3b1 2967 union {
ade99252 2968 uint8_t md[SHA256_DIGEST_SIZE];
e594a3b1
LP
2969 sd_id128_t id;
2970 } result;
2971
53171c04 2972 assert(token);
e594a3b1
LP
2973 assert(ret);
2974
53171c04
LP
2975 /* Derive a new UUID from the specified UUID in a stable and reasonably safe way. Specifically, we
2976 * calculate the HMAC-SHA256 of the specified token string, keyed by the supplied base (typically the
2977 * machine ID). We use the machine ID as key (and not as cleartext!) of the HMAC operation since it's
2978 * the machine ID we don't want to leak. */
e594a3b1 2979
ade99252 2980 hmac_sha256(base.bytes, sizeof(base.bytes), token, strlen(token), result.md);
e594a3b1
LP
2981
2982 /* Take the first half, mark it as v4 UUID */
2983 assert_cc(sizeof(result.md) == sizeof(result.id) * 2);
2984 *ret = id128_make_v4_uuid(result.id);
2985 return 0;
2986}
2987
81e04781
VH
2988static void derive_salt(sd_id128_t base, const char *token, uint8_t ret[static SHA256_DIGEST_SIZE]) {
2989 assert(token);
2990
2991 hmac_sha256(base.bytes, sizeof(base.bytes), token, strlen(token), ret);
2992}
2993
cc751c75 2994static int context_load_partition_table(Context *context) {
e594a3b1
LP
2995 _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
2996 _cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL;
2997 uint64_t left_boundary = UINT64_MAX, first_lba, last_lba, nsectors;
2998 _cleanup_free_ char *disk_uuid_string = NULL;
2999 bool from_scratch = false;
3000 sd_id128_t disk_uuid;
3001 size_t n_partitions;
994b3031 3002 unsigned long secsz;
93f125a6 3003 uint64_t grainsz, fs_secsz = DEFAULT_FILESYSTEM_SECTOR_SIZE;
e594a3b1
LP
3004 int r;
3005
3006 assert(context);
170c9823
LP
3007 assert(!context->fdisk_context);
3008 assert(!context->free_areas);
3009 assert(context->start == UINT64_MAX);
3010 assert(context->end == UINT64_MAX);
3011 assert(context->total == UINT64_MAX);
e594a3b1 3012
e1878ef7
DDM
3013 c = fdisk_new_context();
3014 if (!c)
3015 return log_oom();
3016
93f125a6 3017 if (arg_sector_size > 0) {
93f125a6 3018 fs_secsz = arg_sector_size;
5f13fb0a 3019 r = fdisk_save_user_sector_size(c, /* phy= */ 0, arg_sector_size);
93f125a6 3020 } else {
6c54e1d6 3021 uint32_t ssz;
93f125a6 3022 struct stat st;
6c54e1d6 3023
a575f214
LP
3024 r = context_open_and_lock_backing_fd(
3025 context->node,
3026 arg_dry_run ? LOCK_SH : LOCK_EX,
3027 &context->backing_fd);
37734dc6
YW
3028 if (r < 0)
3029 return r;
6c54e1d6 3030
93f125a6 3031 if (fstat(context->backing_fd, &st) < 0)
9f08d7f7 3032 return log_error_errno(errno, "Failed to stat %s: %m", context->node);
93f125a6 3033
a575f214 3034 if (IN_SET(arg_empty, EMPTY_REQUIRE, EMPTY_FORCE, EMPTY_CREATE) && S_ISREG(st.st_mode))
e53fcb09 3035 /* Don't probe sector size from partition table if we are supposed to start from an empty disk */
d3436114 3036 ssz = 512;
a575f214
LP
3037 else {
3038 /* Auto-detect sector size if not specified. */
3039 r = probe_sector_size_prefer_ioctl(context->backing_fd, &ssz);
3040 if (r < 0)
3041 return log_error_errno(r, "Failed to probe sector size of '%s': %m", context->node);
3042
3043 /* If we found the sector size and we're operating on a block device, use it as the file
3044 * system sector size as well, as we know its the sector size of the actual block device and
3045 * not just the offset at which we found the GPT header. */
0e445aaa
DDM
3046 if (r > 0 && S_ISBLK(st.st_mode)) {
3047 log_debug("Probed sector size of %s is %" PRIu32 " bytes.", context->node, ssz);
a575f214 3048 fs_secsz = ssz;
0e445aaa 3049 }
a575f214 3050 }
93f125a6 3051
6c54e1d6 3052 r = fdisk_save_user_sector_size(c, /* phy= */ 0, ssz);
e1878ef7 3053 }
6c54e1d6
LP
3054 if (r < 0)
3055 return log_error_errno(r, "Failed to set sector size: %m");
e1878ef7 3056
a26f4a49
LP
3057 /* libfdisk doesn't have an API to operate on arbitrary fds, hence reopen the fd going via the
3058 * /proc/self/fd/ magic path if we have an existing fd. Open the original file otherwise. */
e1878ef7
DDM
3059 r = fdisk_assign_device(
3060 c,
3061 context->backing_fd >= 0 ? FORMAT_PROC_FD_PATH(context->backing_fd) : context->node,
3062 arg_dry_run);
170c9823
LP
3063 if (r == -EINVAL && arg_size_auto) {
3064 struct stat st;
3065
3066 /* libfdisk returns EINVAL if opening a file of size zero. Let's check for that, and accept
3067 * it if automatic sizing is requested. */
3068
cc751c75
DDM
3069 if (context->backing_fd < 0)
3070 r = stat(context->node, &st);
170c9823 3071 else
cc751c75 3072 r = fstat(context->backing_fd, &st);
170c9823 3073 if (r < 0)
cc751c75 3074 return log_error_errno(errno, "Failed to stat block device '%s': %m", context->node);
170c9823 3075
994b3031 3076 if (S_ISREG(st.st_mode) && st.st_size == 0) {
264668a8 3077 /* Use the fallback values if we have no better idea */
93f125a6
DDM
3078 context->sector_size = fdisk_get_sector_size(c);
3079 context->fs_sector_size = fs_secsz;
994b3031 3080 context->grain_size = 4096;
170c9823 3081 return /* from_scratch = */ true;
994b3031 3082 }
170c9823
LP
3083
3084 r = -EINVAL;
3085 }
e594a3b1 3086 if (r < 0)
cc751c75 3087 return log_error_errno(r, "Failed to open device '%s': %m", context->node);
a26f4a49 3088
cc751c75 3089 if (context->backing_fd < 0) {
a26f4a49 3090 /* If we have no fd referencing the device yet, make a copy of the fd now, so that we have one */
3cdcf4e2
DDM
3091 r = context_open_and_lock_backing_fd(FORMAT_PROC_FD_PATH(fdisk_get_devfd(c)),
3092 arg_dry_run ? LOCK_SH : LOCK_EX,
3093 &context->backing_fd);
37734dc6
YW
3094 if (r < 0)
3095 return r;
25baae50 3096 }
e594a3b1 3097
994b3031
LP
3098 /* The offsets/sizes libfdisk returns to us will be in multiple of the sector size of the
3099 * device. This is typically 512, and sometimes 4096. Let's query libfdisk once for it, and then use
3100 * it for all our needs. Note that the values we use ourselves always are in bytes though, thus mean
3101 * the same thing universally. Also note that regardless what kind of sector size is in use we'll
3102 * place partitions at multiples of 4K. */
3103 secsz = fdisk_get_sector_size(c);
3104
3105 /* Insist on a power of two, and that it's a multiple of 512, i.e. the traditional sector size. */
983ce0b5
LP
3106 if (secsz < 512 || !ISPOWEROF2(secsz))
3107 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Sector size %lu is not a power of two larger than 512? Refusing.", secsz);
994b3031
LP
3108
3109 /* Use at least 4K, and ensure it's a multiple of the sector size, regardless if that is smaller or
3110 * larger */
3111 grainsz = secsz < 4096 ? 4096 : secsz;
3112
0e445aaa 3113 log_debug("Sector size of device is %lu bytes. Using filesystem sector size of %" PRIu64 " and grain size of %" PRIu64 ".", secsz, fs_secsz, grainsz);
994b3031 3114
e594a3b1
LP
3115 switch (arg_empty) {
3116
3117 case EMPTY_REFUSE:
3118 /* Refuse empty disks, insist on an existing GPT partition table */
3119 if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT))
cc751c75 3120 return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has no GPT disk label, not repartitioning.", context->node);
e594a3b1
LP
3121
3122 break;
3123
3124 case EMPTY_REQUIRE:
3125 /* Require an empty disk, refuse any existing partition table */
3126 r = fdisk_has_label(c);
3127 if (r < 0)
cc751c75 3128 return log_error_errno(r, "Failed to determine whether disk %s has a disk label: %m", context->node);
e594a3b1 3129 if (r > 0)
cc751c75 3130 return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s already has a disk label, refusing.", context->node);
e594a3b1
LP
3131
3132 from_scratch = true;
3133 break;
3134
3135 case EMPTY_ALLOW:
3136 /* Allow both an empty disk and an existing partition table, but only GPT */
3137 r = fdisk_has_label(c);
3138 if (r < 0)
cc751c75 3139 return log_error_errno(r, "Failed to determine whether disk %s has a disk label: %m", context->node);
e594a3b1
LP
3140 if (r > 0) {
3141 if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT))
cc751c75 3142 return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has non-GPT disk label, not repartitioning.", context->node);
e594a3b1
LP
3143 } else
3144 from_scratch = true;
3145
3146 break;
3147
3148 case EMPTY_FORCE:
a26f4a49 3149 case EMPTY_CREATE:
e594a3b1
LP
3150 /* Always reinitiaize the disk, don't consider what there was on the disk before */
3151 from_scratch = true;
3152 break;
243dd1e9 3153
634b8471 3154 default:
243dd1e9 3155 assert_not_reached();
e594a3b1
LP
3156 }
3157
3158 if (from_scratch) {
e594a3b1
LP
3159 r = fdisk_create_disklabel(c, "gpt");
3160 if (r < 0)
3161 return log_error_errno(r, "Failed to create GPT disk label: %m");
3162
53171c04 3163 r = derive_uuid(context->seed, "disk-uuid", &disk_uuid);
e594a3b1
LP
3164 if (r < 0)
3165 return log_error_errno(r, "Failed to acquire disk GPT uuid: %m");
3166
3167 r = fdisk_set_disklabel_id_by_uuid(c, disk_uuid);
3168 if (r < 0)
3169 return log_error_errno(r, "Failed to set GPT disk label: %m");
3170
3171 goto add_initial_free_area;
3172 }
3173
3174 r = fdisk_get_disklabel_id(c, &disk_uuid_string);
3175 if (r < 0)
3176 return log_error_errno(r, "Failed to get current GPT disk label UUID: %m");
3177
aea3f594
ZJS
3178 r = id128_from_string_nonzero(disk_uuid_string, &disk_uuid);
3179 if (r == -ENXIO) {
53171c04 3180 r = derive_uuid(context->seed, "disk-uuid", &disk_uuid);
e594a3b1
LP
3181 if (r < 0)
3182 return log_error_errno(r, "Failed to acquire disk GPT uuid: %m");
3183
3184 r = fdisk_set_disklabel_id(c);
3185 if (r < 0)
3186 return log_error_errno(r, "Failed to set GPT disk label: %m");
aea3f594
ZJS
3187 } else if (r < 0)
3188 return log_error_errno(r, "Failed to parse current GPT disk label UUID: %m");
e594a3b1
LP
3189
3190 r = fdisk_get_partitions(c, &t);
3191 if (r < 0)
3192 return log_error_errno(r, "Failed to acquire partition table: %m");
3193
3194 n_partitions = fdisk_table_get_nents(t);
695cfd53 3195 for (size_t i = 0; i < n_partitions; i++) {
e594a3b1 3196 _cleanup_free_ char *label_copy = NULL;
03677889 3197 Partition *last = NULL;
e594a3b1 3198 struct fdisk_partition *p;
63b96eb9 3199 const char *label;
e594a3b1
LP
3200 uint64_t sz, start;
3201 bool found = false;
3202 sd_id128_t ptid, id;
3203 size_t partno;
3204
3205 p = fdisk_table_get_partition(t, i);
3206 if (!p)
4e494e6a 3207 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read partition metadata.");
e594a3b1
LP
3208
3209 if (fdisk_partition_is_used(p) <= 0)
3210 continue;
3211
3212 if (fdisk_partition_has_start(p) <= 0 ||
3213 fdisk_partition_has_size(p) <= 0 ||
3214 fdisk_partition_has_partno(p) <= 0)
3215 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found a partition without a position, size or number.");
3216
63b96eb9 3217 r = fdisk_partition_get_type_as_id128(p, &ptid);
e594a3b1 3218 if (r < 0)
63b96eb9 3219 return log_error_errno(r, "Failed to query partition type UUID: %m");
e594a3b1 3220
02e32aa6 3221 r = fdisk_partition_get_uuid_as_id128(p, &id);
e594a3b1 3222 if (r < 0)
02e32aa6 3223 return log_error_errno(r, "Failed to query partition UUID: %m");
e594a3b1
LP
3224
3225 label = fdisk_partition_get_name(p);
3226 if (!isempty(label)) {
3227 label_copy = strdup(label);
3228 if (!label_copy)
3229 return log_oom();
3230 }
3231
3232 sz = fdisk_partition_get_size(p);
ac33e147 3233 assert(sz <= UINT64_MAX/secsz);
994b3031 3234 sz *= secsz;
e594a3b1
LP
3235
3236 start = fdisk_partition_get_start(p);
ac33e147 3237 assert(start <= UINT64_MAX/secsz);
994b3031 3238 start *= secsz;
e594a3b1
LP
3239
3240 partno = fdisk_partition_get_partno(p);
3241
3242 if (left_boundary == UINT64_MAX || left_boundary > start)
3243 left_boundary = start;
3244
3245 /* Assign this existing partition to the first partition of the right type that doesn't have
3246 * an existing one assigned yet. */
3247 LIST_FOREACH(partitions, pp, context->partitions) {
3248 last = pp;
3249
22e932f4 3250 if (!sd_id128_equal(pp->type.uuid, ptid))
e594a3b1
LP
3251 continue;
3252
3253 if (!pp->current_partition) {
3254 pp->current_uuid = id;
3255 pp->current_size = sz;
3256 pp->offset = start;
3257 pp->partno = partno;
3258 pp->current_label = TAKE_PTR(label_copy);
3259
3260 pp->current_partition = p;
3261 fdisk_ref_partition(p);
3262
994b3031 3263 r = determine_current_padding(c, t, p, secsz, grainsz, &pp->current_padding);
e594a3b1
LP
3264 if (r < 0)
3265 return r;
3266
3267 if (pp->current_padding > 0) {
3268 r = context_add_free_area(context, pp->current_padding, pp);
3269 if (r < 0)
3270 return r;
3271 }
3272
3273 found = true;
3274 break;
3275 }
3276 }
3277
3278 /* If we have no matching definition, create a new one. */
3279 if (!found) {
3280 _cleanup_(partition_freep) Partition *np = NULL;
3281
3282 np = partition_new();
3283 if (!np)
3284 return log_oom();
3285
3286 np->current_uuid = id;
22e932f4 3287 np->type = gpt_partition_type_from_uuid(ptid);
e594a3b1
LP
3288 np->current_size = sz;
3289 np->offset = start;
3290 np->partno = partno;
3291 np->current_label = TAKE_PTR(label_copy);
3292
3293 np->current_partition = p;
3294 fdisk_ref_partition(p);
3295
994b3031 3296 r = determine_current_padding(c, t, p, secsz, grainsz, &np->current_padding);
e594a3b1
LP
3297 if (r < 0)
3298 return r;
3299
3300 if (np->current_padding > 0) {
3301 r = context_add_free_area(context, np->current_padding, np);
3302 if (r < 0)
3303 return r;
3304 }
3305
3306 LIST_INSERT_AFTER(partitions, context->partitions, last, TAKE_PTR(np));
3307 context->n_partitions++;
3308 }
3309 }
3310
2cb9c68c
AV
3311 LIST_FOREACH(partitions, p, context->partitions)
3312 if (PARTITION_SUPPRESSED(p) && PARTITION_EXISTS(p))
3313 p->supplement_for->suppressing = NULL;
3314
e594a3b1
LP
3315add_initial_free_area:
3316 nsectors = fdisk_get_nsectors(c);
994b3031
LP
3317 assert(nsectors <= UINT64_MAX/secsz);
3318 nsectors *= secsz;
e594a3b1
LP
3319
3320 first_lba = fdisk_get_first_lba(c);
994b3031
LP
3321 assert(first_lba <= UINT64_MAX/secsz);
3322 first_lba *= secsz;
e594a3b1
LP
3323
3324 last_lba = fdisk_get_last_lba(c);
3325 assert(last_lba < UINT64_MAX);
3326 last_lba++;
994b3031
LP
3327 assert(last_lba <= UINT64_MAX/secsz);
3328 last_lba *= secsz;
e594a3b1
LP
3329
3330 assert(last_lba >= first_lba);
3331
3332 if (left_boundary == UINT64_MAX) {
3333 /* No partitions at all? Then the whole disk is up for grabs. */
3334
994b3031
LP
3335 first_lba = round_up_size(first_lba, grainsz);
3336 last_lba = round_down_size(last_lba, grainsz);
e594a3b1
LP
3337
3338 if (last_lba > first_lba) {
3339 r = context_add_free_area(context, last_lba - first_lba, NULL);
3340 if (r < 0)
3341 return r;
3342 }
3343 } else {
3344 /* Add space left of first partition */
3345 assert(left_boundary >= first_lba);
3346
994b3031
LP
3347 first_lba = round_up_size(first_lba, grainsz);
3348 left_boundary = round_down_size(left_boundary, grainsz);
3349 last_lba = round_down_size(last_lba, grainsz);
e594a3b1
LP
3350
3351 if (left_boundary > first_lba) {
3352 r = context_add_free_area(context, left_boundary - first_lba, NULL);
3353 if (r < 0)
3354 return r;
3355 }
3356 }
3357
3358 context->start = first_lba;
3359 context->end = last_lba;
3360 context->total = nsectors;
994b3031 3361 context->sector_size = secsz;
93f125a6 3362 context->fs_sector_size = fs_secsz;
994b3031 3363 context->grain_size = grainsz;
e594a3b1
LP
3364 context->fdisk_context = TAKE_PTR(c);
3365
3366 return from_scratch;
3367}
3368
3369static void context_unload_partition_table(Context *context) {
e594a3b1
LP
3370 assert(context);
3371
80a226b2 3372 LIST_FOREACH(partitions, p, context->partitions) {
e594a3b1
LP
3373
3374 /* Entirely remove partitions that have no configuration */
3375 if (PARTITION_IS_FOREIGN(p)) {
3376 partition_unlink_and_free(context, p);
3377 continue;
3378 }
3379
3380 /* Otherwise drop all data we read off the block device and everything we might have
3381 * calculated based on it */
3382
3383 p->dropped = false;
3384 p->current_size = UINT64_MAX;
3385 p->new_size = UINT64_MAX;
3386 p->current_padding = UINT64_MAX;
3387 p->new_padding = UINT64_MAX;
3388 p->partno = UINT64_MAX;
3389 p->offset = UINT64_MAX;
3390
3391 if (p->current_partition) {
3392 fdisk_unref_partition(p->current_partition);
3393 p->current_partition = NULL;
3394 }
3395
3396 if (p->new_partition) {
3397 fdisk_unref_partition(p->new_partition);
3398 p->new_partition = NULL;
3399 }
3400
3401 p->padding_area = NULL;
3402 p->allocated_to_area = NULL;
3403
15d43e30
LP
3404 p->current_uuid = SD_ID128_NULL;
3405 p->current_label = mfree(p->current_label);
2cb9c68c
AV
3406
3407 /* A supplement partition is only ever un-suppressed if the existing partition table prevented
3408 * us from suppressing it. So when unloading the partition table, we must re-suppress. */
3409 if (p->supplement_for)
3410 p->supplement_for->suppressing = p;
e594a3b1
LP
3411 }
3412
3413 context->start = UINT64_MAX;
3414 context->end = UINT64_MAX;
3415 context->total = UINT64_MAX;
3416
3417 if (context->fdisk_context) {
3418 fdisk_unref_context(context->fdisk_context);
3419 context->fdisk_context = NULL;
3420 }
3421
3422 context_free_free_areas(context);
3423}
3424
3425static int format_size_change(uint64_t from, uint64_t to, char **ret) {
2b59bf51 3426 char *t;
e594a3b1
LP
3427
3428 if (from != UINT64_MAX) {
3429 if (from == to || to == UINT64_MAX)
2b59bf51 3430 t = strdup(FORMAT_BYTES(from));
e594a3b1 3431 else
fc03e80c 3432 t = strjoin(FORMAT_BYTES(from), " ", special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), " ", FORMAT_BYTES(to));
e594a3b1 3433 } else if (to != UINT64_MAX)
fc03e80c 3434 t = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), " ", FORMAT_BYTES(to));
e594a3b1
LP
3435 else {
3436 *ret = NULL;
3437 return 0;
3438 }
3439
2b59bf51 3440 if (!t)
e594a3b1
LP
3441 return log_oom();
3442
2b59bf51 3443 *ret = t;
e594a3b1
LP
3444 return 1;
3445}
3446
3447static const char *partition_label(const Partition *p) {
3448 assert(p);
3449
3450 if (p->new_label)
3451 return p->new_label;
3452
3453 if (p->current_label)
3454 return p->current_label;
3455
22e932f4 3456 return gpt_partition_type_uuid_to_string(p->type.uuid);
e594a3b1
LP
3457}
3458
cc751c75 3459static int context_dump_partitions(Context *context) {
e594a3b1
LP
3460 _cleanup_(table_unrefp) Table *t = NULL;
3461 uint64_t sum_padding = 0, sum_size = 0;
e594a3b1 3462 int r;
1ffa5cfb 3463 const size_t roothash_col = 14, dropin_files_col = 15, split_path_col = 16;
bf030f55 3464 bool has_roothash = false, has_dropin_files = false, has_split_path = false;
e594a3b1 3465
309a747f 3466 if ((arg_json_format_flags & SD_JSON_FORMAT_OFF) && context->n_partitions == 0) {
a015fbe7
TH
3467 log_info("Empty partition table.");
3468 return 0;
3469 }
3470
bf030f55
DDM
3471 t = table_new("type",
3472 "label",
3473 "uuid",
1ffa5cfb 3474 "partno",
bf030f55
DDM
3475 "file",
3476 "node",
3477 "offset",
3478 "old size",
3479 "raw size",
3480 "size",
3481 "old padding",
3482 "raw padding",
3483 "padding",
3484 "activity",
3485 "roothash",
3486 "drop-in files",
3487 "split path");
e594a3b1
LP
3488 if (!t)
3489 return log_oom();
3490
c82dfaf7
AV
3491 /* Starting in v257, these fields would be automatically formatted with underscores. This would have
3492 * been a breaking change, so to avoid that let's hard-code their original names. */
3493 table_set_json_field_name(t, 15, "drop-in_files");
3494
a015fbe7 3495 if (!DEBUG_LOGGING) {
309a747f 3496 if (arg_json_format_flags & SD_JSON_FORMAT_OFF)
a015fbe7 3497 (void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
1ffa5cfb 3498 (size_t) 8, (size_t) 9, (size_t) 12, roothash_col, dropin_files_col,
bf030f55 3499 split_path_col);
a015fbe7
TH
3500 else
3501 (void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
1ffa5cfb
DDM
3502 (size_t) 5, (size_t) 6, (size_t) 7, (size_t) 8, (size_t) 10,
3503 (size_t) 11, (size_t) 13, roothash_col, dropin_files_col,
bf030f55 3504 split_path_col);
a015fbe7 3505 }
e594a3b1 3506
e594a3b1 3507 (void) table_set_align_percent(t, table_get_cell(t, 0, 5), 100);
9c07c9ec
LP
3508 (void) table_set_align_percent(t, table_get_cell(t, 0, 6), 100);
3509 (void) table_set_align_percent(t, table_get_cell(t, 0, 7), 100);
3510 (void) table_set_align_percent(t, table_get_cell(t, 0, 8), 100);
3511 (void) table_set_align_percent(t, table_get_cell(t, 0, 9), 100);
3512 (void) table_set_align_percent(t, table_get_cell(t, 0, 10), 100);
3513 (void) table_set_align_percent(t, table_get_cell(t, 0, 11), 100);
e594a3b1
LP
3514
3515 LIST_FOREACH(partitions, p, context->partitions) {
b5b7879a 3516 _cleanup_free_ char *size_change = NULL, *padding_change = NULL, *partname = NULL, *rh = NULL;
b7416360 3517 char uuid_buffer[SD_ID128_UUID_STRING_MAX];
a015fbe7 3518 const char *label, *activity = NULL;
e594a3b1
LP
3519
3520 if (p->dropped)
3521 continue;
3522
a015fbe7
TH
3523 if (p->current_size == UINT64_MAX)
3524 activity = "create";
3525 else if (p->current_size != p->new_size)
3526 activity = "resize";
3527
e594a3b1 3528 label = partition_label(p);
cc751c75 3529 partname = p->partno != UINT64_MAX ? fdisk_partname(context->node, p->partno+1) : NULL;
e594a3b1
LP
3530
3531 r = format_size_change(p->current_size, p->new_size, &size_change);
3532 if (r < 0)
3533 return r;
3534
3535 r = format_size_change(p->current_padding, p->new_padding, &padding_change);
3536 if (r < 0)
3537 return r;
3538
3539 if (p->new_size != UINT64_MAX)
3540 sum_size += p->new_size;
3541 if (p->new_padding != UINT64_MAX)
3542 sum_padding += p->new_padding;
3543
2ecc7a5b
DDM
3544 if (p->verity != VERITY_OFF) {
3545 Partition *hp = p->verity == VERITY_HASH ? p : p->siblings[VERITY_HASH];
3546
6b4b40f4 3547 rh = iovec_is_set(&hp->roothash) ? hexmem(hp->roothash.iov_base, hp->roothash.iov_len) : strdup("TBD");
b5b7879a
DDM
3548 if (!rh)
3549 return log_oom();
3550 }
3551
e594a3b1
LP
3552 r = table_add_many(
3553 t,
22e932f4 3554 TABLE_STRING, gpt_partition_type_uuid_to_string_harder(p->type.uuid, uuid_buffer),
be9ce018 3555 TABLE_STRING, empty_to_null(label) ?: "-", TABLE_SET_COLOR, empty_to_null(label) ? NULL : ansi_grey(),
11749b61 3556 TABLE_UUID, p->new_uuid_is_set ? p->new_uuid : p->current_uuid,
1ffa5cfb 3557 TABLE_UINT64, p->partno,
d0242ac9 3558 TABLE_PATH_BASENAME, p->definition_path, TABLE_SET_COLOR, p->definition_path ? NULL : ansi_grey(),
a015fbe7 3559 TABLE_STRING, partname ?: "-", TABLE_SET_COLOR, partname ? NULL : ansi_highlight(),
e594a3b1 3560 TABLE_UINT64, p->offset,
a015fbe7 3561 TABLE_UINT64, p->current_size == UINT64_MAX ? 0 : p->current_size,
e594a3b1
LP
3562 TABLE_UINT64, p->new_size,
3563 TABLE_STRING, size_change, TABLE_SET_COLOR, !p->partitions_next && sum_size > 0 ? ansi_underline() : NULL,
a015fbe7 3564 TABLE_UINT64, p->current_padding == UINT64_MAX ? 0 : p->current_padding,
e594a3b1 3565 TABLE_UINT64, p->new_padding,
a015fbe7 3566 TABLE_STRING, padding_change, TABLE_SET_COLOR, !p->partitions_next && sum_padding > 0 ? ansi_underline() : NULL,
39fc0174 3567 TABLE_STRING, activity ?: "unchanged",
b5b7879a 3568 TABLE_STRING, rh,
bf030f55
DDM
3569 TABLE_STRV, p->drop_in_files,
3570 TABLE_STRING, empty_to_null(p->split_path) ?: "-");
e594a3b1 3571 if (r < 0)
f987a261 3572 return table_log_add_error(r);
39fc0174 3573
b5b7879a 3574 has_roothash = has_roothash || !isempty(rh);
3ab44dbd 3575 has_dropin_files = has_dropin_files || !strv_isempty(p->drop_in_files);
bf030f55 3576 has_split_path = has_split_path || !isempty(p->split_path);
e594a3b1
LP
3577 }
3578
309a747f 3579 if ((arg_json_format_flags & SD_JSON_FORMAT_OFF) && (sum_padding > 0 || sum_size > 0)) {
e594a3b1
LP
3580 const char *a, *b;
3581
2b59bf51
ZJS
3582 a = strjoina(special_glyph(SPECIAL_GLYPH_SIGMA), " = ", FORMAT_BYTES(sum_size));
3583 b = strjoina(special_glyph(SPECIAL_GLYPH_SIGMA), " = ", FORMAT_BYTES(sum_padding));
e594a3b1
LP
3584
3585 r = table_add_many(
3586 t,
3587 TABLE_EMPTY,
3588 TABLE_EMPTY,
3589 TABLE_EMPTY,
3590 TABLE_EMPTY,
3591 TABLE_EMPTY,
3592 TABLE_EMPTY,
3593 TABLE_EMPTY,
1ffa5cfb 3594 TABLE_EMPTY,
a015fbe7 3595 TABLE_EMPTY,
e594a3b1
LP
3596 TABLE_STRING, a,
3597 TABLE_EMPTY,
a015fbe7
TH
3598 TABLE_EMPTY,
3599 TABLE_STRING, b,
39fc0174 3600 TABLE_EMPTY,
b5b7879a 3601 TABLE_EMPTY,
bf030f55 3602 TABLE_EMPTY,
a015fbe7 3603 TABLE_EMPTY);
e594a3b1 3604 if (r < 0)
f987a261 3605 return table_log_add_error(r);
e594a3b1
LP
3606 }
3607
b5b7879a
DDM
3608 if (!has_roothash) {
3609 r = table_hide_column_from_display(t, roothash_col);
3610 if (r < 0)
3611 return log_error_errno(r, "Failed to set columns to display: %m");
3612 }
3613
3ab44dbd 3614 if (!has_dropin_files) {
39fc0174
RP
3615 r = table_hide_column_from_display(t, dropin_files_col);
3616 if (r < 0)
3617 return log_error_errno(r, "Failed to set columns to display: %m");
3618 }
3619
bf030f55
DDM
3620 if (!has_split_path) {
3621 r = table_hide_column_from_display(t, split_path_col);
3622 if (r < 0)
3623 return log_error_errno(r, "Failed to set columns to display: %m");
3624 }
3625
896e678b 3626 return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
e594a3b1
LP
3627}
3628
f569dc6a 3629static int context_bar_char_process_partition(
e594a3b1
LP
3630 Context *context,
3631 Partition *bar[],
3632 size_t n,
3633 Partition *p,
f569dc6a
FS
3634 size_t **start_array,
3635 size_t *n_start_array) {
e594a3b1
LP
3636
3637 uint64_t from, to, total;
3638 size_t x, y;
3639
3640 assert(context);
3641 assert(bar);
3642 assert(n > 0);
3643 assert(p);
f569dc6a
FS
3644 assert(start_array);
3645 assert(n_start_array);
e594a3b1
LP
3646
3647 if (p->dropped)
f569dc6a 3648 return 0;
e594a3b1
LP
3649
3650 assert(p->offset != UINT64_MAX);
3651 assert(p->new_size != UINT64_MAX);
3652
3653 from = p->offset;
3654 to = from + p->new_size;
3655
d8daed09
TY
3656 assert(context->total > 0);
3657 total = context->total;
e594a3b1 3658
d8daed09
TY
3659 assert(from <= total);
3660 x = from * n / total;
e594a3b1 3661
d8daed09
TY
3662 assert(to <= total);
3663 y = to * n / total;
e594a3b1
LP
3664
3665 assert(x <= y);
3666 assert(y <= n);
3667
3668 for (size_t i = x; i < y; i++)
3669 bar[i] = p;
3670
f569dc6a
FS
3671 if (!GREEDY_REALLOC_APPEND(*start_array, *n_start_array, &x, 1))
3672 return log_oom();
3673
3674 return 1;
e594a3b1
LP
3675}
3676
3677static int partition_hint(const Partition *p, const char *node, char **ret) {
3678 _cleanup_free_ char *buf = NULL;
e594a3b1
LP
3679 const char *label;
3680 sd_id128_t id;
3681
3682 /* Tries really hard to find a suitable description for this partition */
3683
7b2ffb59
LP
3684 if (p->definition_path)
3685 return path_extract_filename(p->definition_path, ret);
e594a3b1
LP
3686
3687 label = partition_label(p);
3688 if (!isempty(label)) {
3689 buf = strdup(label);
3690 goto done;
3691 }
3692
3693 if (p->partno != UINT64_MAX) {
3694 buf = fdisk_partname(node, p->partno+1);
3695 goto done;
3696 }
3697
11749b61 3698 if (p->new_uuid_is_set)
e594a3b1
LP
3699 id = p->new_uuid;
3700 else if (!sd_id128_is_null(p->current_uuid))
3701 id = p->current_uuid;
3702 else
22e932f4 3703 id = p->type.uuid;
e594a3b1 3704
b7416360 3705 buf = strdup(SD_ID128_TO_UUID_STRING(id));
e594a3b1
LP
3706
3707done:
3708 if (!buf)
3709 return -ENOMEM;
3710
3711 *ret = TAKE_PTR(buf);
3712 return 0;
3713}
3714
cc751c75 3715static int context_dump_partition_bar(Context *context) {
e594a3b1
LP
3716 _cleanup_free_ Partition **bar = NULL;
3717 _cleanup_free_ size_t *start_array = NULL;
f569dc6a 3718 size_t n_start_array = 0;
03677889 3719 Partition *last = NULL;
e594a3b1
LP
3720 bool z = false;
3721 size_t c, j = 0;
f569dc6a 3722 int r;
e594a3b1 3723
f391597c 3724 assert_se((c = columns()) >= 2);
e594a3b1
LP
3725 c -= 2; /* We do not use the leftmost and rightmost character cell */
3726
3727 bar = new0(Partition*, c);
3728 if (!bar)
3729 return log_oom();
3730
f569dc6a
FS
3731 LIST_FOREACH(partitions, p, context->partitions) {
3732 r = context_bar_char_process_partition(context, bar, c, p, &start_array, &n_start_array);
3733 if (r < 0)
3734 return r;
3735 }
e594a3b1
LP
3736
3737 putc(' ', stdout);
3738
3739 for (size_t i = 0; i < c; i++) {
3740 if (bar[i]) {
3741 if (last != bar[i])
3742 z = !z;
3743
3744 fputs(z ? ansi_green() : ansi_yellow(), stdout);
3745 fputs(special_glyph(SPECIAL_GLYPH_DARK_SHADE), stdout);
3746 } else {
3747 fputs(ansi_normal(), stdout);
3748 fputs(special_glyph(SPECIAL_GLYPH_LIGHT_SHADE), stdout);
3749 }
3750
3751 last = bar[i];
3752 }
3753
3754 fputs(ansi_normal(), stdout);
3755 putc('\n', stdout);
3756
f569dc6a 3757 for (size_t i = 0; i < n_start_array; i++) {
e594a3b1
LP
3758 _cleanup_free_ char **line = NULL;
3759
3760 line = new0(char*, c);
3761 if (!line)
3762 return log_oom();
3763
3764 j = 0;
3765 LIST_FOREACH(partitions, p, context->partitions) {
3766 _cleanup_free_ char *d = NULL;
f569dc6a
FS
3767
3768 if (p->dropped)
3769 continue;
3770
e594a3b1
LP
3771 j++;
3772
f569dc6a 3773 if (i < n_start_array - j) {
e594a3b1
LP
3774
3775 if (line[start_array[j-1]]) {
3776 const char *e;
3777
3778 /* Upgrade final corner to the right with a branch to the right */
3779 e = startswith(line[start_array[j-1]], special_glyph(SPECIAL_GLYPH_TREE_RIGHT));
3780 if (e) {
3781 d = strjoin(special_glyph(SPECIAL_GLYPH_TREE_BRANCH), e);
3782 if (!d)
3783 return log_oom();
3784 }
3785 }
3786
3787 if (!d) {
3788 d = strdup(special_glyph(SPECIAL_GLYPH_TREE_VERTICAL));
3789 if (!d)
3790 return log_oom();
3791 }
3792
f569dc6a 3793 } else if (i == n_start_array - j) {
e594a3b1
LP
3794 _cleanup_free_ char *hint = NULL;
3795
cc751c75 3796 (void) partition_hint(p, context->node, &hint);
e594a3b1
LP
3797
3798 if (streq_ptr(line[start_array[j-1]], special_glyph(SPECIAL_GLYPH_TREE_VERTICAL)))
3799 d = strjoin(special_glyph(SPECIAL_GLYPH_TREE_BRANCH), " ", strna(hint));
3800 else
3801 d = strjoin(special_glyph(SPECIAL_GLYPH_TREE_RIGHT), " ", strna(hint));
3802
3803 if (!d)
3804 return log_oom();
3805 }
3806
3807 if (d)
3808 free_and_replace(line[start_array[j-1]], d);
3809 }
3810
3811 putc(' ', stdout);
3812
3813 j = 0;
3814 while (j < c) {
3815 if (line[j]) {
3816 fputs(line[j], stdout);
3817 j += utf8_console_width(line[j]);
3818 } else {
3819 putc(' ', stdout);
3820 j++;
3821 }
3822 }
3823
3824 putc('\n', stdout);
3825
3826 for (j = 0; j < c; j++)
3827 free(line[j]);
3828 }
3829
3830 return 0;
3831}
3832
b5b7879a
DDM
3833static bool context_has_roothash(Context *context) {
3834 LIST_FOREACH(partitions, p, context->partitions)
6b4b40f4 3835 if (iovec_is_set(&p->roothash))
b5b7879a
DDM
3836 return true;
3837
3838 return false;
3839}
3840
cc751c75 3841static int context_dump(Context *context, bool late) {
a26d463d
DDM
3842 int r;
3843
3844 assert(context);
a26d463d 3845
309a747f 3846 if (arg_pretty == 0 && FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
a26d463d
DDM
3847 return 0;
3848
b5b7879a
DDM
3849 /* If we're outputting JSON, only dump after doing all operations so we can include the roothashes
3850 * in the output. */
309a747f 3851 if (!late && !FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
b5b7879a
DDM
3852 return 0;
3853
3854 /* If we're not outputting JSON, only dump again after doing all operations if there are any
3855 * roothashes that we need to communicate to the user. */
309a747f 3856 if (late && FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF) && !context_has_roothash(context))
b5b7879a
DDM
3857 return 0;
3858
cc751c75 3859 r = context_dump_partitions(context);
a26d463d
DDM
3860 if (r < 0)
3861 return r;
3862
a954b427
ZJS
3863 /* Only write the partition bar once, even if we're writing the partition table twice to communicate
3864 * roothashes. */
309a747f 3865 if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF) && !late) {
a26d463d
DDM
3866 putc('\n', stdout);
3867
cc751c75 3868 r = context_dump_partition_bar(context);
a26d463d
DDM
3869 if (r < 0)
3870 return r;
3871
3872 putc('\n', stdout);
3873 }
3874
3875 fflush(stdout);
3876
3877 return 0;
3878}
3879
3880
e594a3b1 3881static bool context_changed(const Context *context) {
03677889 3882 assert(context);
e594a3b1
LP
3883
3884 LIST_FOREACH(partitions, p, context->partitions) {
3885 if (p->dropped)
3886 continue;
3887
3888 if (p->allocated_to_area)
3889 return true;
3890
3891 if (p->new_size != p->current_size)
3892 return true;
3893 }
3894
3895 return false;
3896}
3897
81873a6b 3898static int context_wipe_range(Context *context, uint64_t offset, uint64_t size) {
e594a3b1
LP
3899 _cleanup_(blkid_free_probep) blkid_probe probe = NULL;
3900 int r;
3901
3902 assert(context);
81873a6b
LP
3903 assert(offset != UINT64_MAX);
3904 assert(size != UINT64_MAX);
e594a3b1
LP
3905
3906 probe = blkid_new_probe();
3907 if (!probe)
3908 return log_oom();
3909
e594a3b1 3910 errno = 0;
81873a6b 3911 r = blkid_probe_set_device(probe, fdisk_get_devfd(context->fdisk_context), offset, size);
e594a3b1 3912 if (r < 0)
81873a6b 3913 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to allocate device probe for wiping.");
e594a3b1
LP
3914
3915 errno = 0;
3916 if (blkid_probe_enable_superblocks(probe, true) < 0 ||
3917 blkid_probe_set_superblocks_flags(probe, BLKID_SUBLKS_MAGIC|BLKID_SUBLKS_BADCSUM) < 0 ||
3918 blkid_probe_enable_partitions(probe, true) < 0 ||
3919 blkid_probe_set_partitions_flags(probe, BLKID_PARTS_MAGIC) < 0)
81873a6b 3920 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to enable superblock and partition probing.");
e594a3b1
LP
3921
3922 for (;;) {
3923 errno = 0;
3924 r = blkid_do_probe(probe);
3925 if (r < 0)
9f08d7f7 3926 return log_error_errno(errno_or_else(EIO), "Failed to probe for file systems.");
e594a3b1
LP
3927 if (r > 0)
3928 break;
3929
3930 errno = 0;
3931 if (blkid_do_wipe(probe, false) < 0)
9f08d7f7 3932 return log_error_errno(errno_or_else(EIO), "Failed to wipe file system signature.");
e594a3b1
LP
3933 }
3934
e594a3b1
LP
3935 return 0;
3936}
3937
81873a6b
LP
3938static int context_wipe_partition(Context *context, Partition *p) {
3939 int r;
3940
3941 assert(context);
3942 assert(p);
3943 assert(!PARTITION_EXISTS(p)); /* Safety check: never wipe existing partitions */
3944
3945 assert(p->offset != UINT64_MAX);
3946 assert(p->new_size != UINT64_MAX);
3947
3948 r = context_wipe_range(context, p->offset, p->new_size);
3949 if (r < 0)
3950 return r;
3951
3952 log_info("Successfully wiped file system signatures from future partition %" PRIu64 ".", p->partno);
3953 return 0;
3954}
3955
3956static int context_discard_range(
3957 Context *context,
3958 uint64_t offset,
3959 uint64_t size) {
3960
e594a3b1
LP
3961 struct stat st;
3962 int fd;
3963
3964 assert(context);
3965 assert(offset != UINT64_MAX);
3966 assert(size != UINT64_MAX);
3967
3968 if (size <= 0)
3969 return 0;
3970
a26f4a49 3971 assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
e594a3b1
LP
3972
3973 if (fstat(fd, &st) < 0)
3974 return -errno;
3975
3976 if (S_ISREG(st.st_mode)) {
3977 if (fallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, offset, size) < 0) {
3978 if (ERRNO_IS_NOT_SUPPORTED(errno))
3979 return -EOPNOTSUPP;
3980
3981 return -errno;
3982 }
3983
3984 return 1;
3985 }
3986
3987 if (S_ISBLK(st.st_mode)) {
3988 uint64_t range[2], end;
3989
994b3031 3990 range[0] = round_up_size(offset, context->sector_size);
e594a3b1 3991
55d38014
LP
3992 if (offset > UINT64_MAX - size)
3993 return -ERANGE;
3994
e594a3b1
LP
3995 end = offset + size;
3996 if (end <= range[0])
3997 return 0;
3998
994b3031 3999 range[1] = round_down_size(end - range[0], context->sector_size);
e594a3b1
LP
4000 if (range[1] <= 0)
4001 return 0;
4002
4003 if (ioctl(fd, BLKDISCARD, range) < 0) {
4004 if (ERRNO_IS_NOT_SUPPORTED(errno))
4005 return -EOPNOTSUPP;
4006
4007 return -errno;
4008 }
4009
4010 return 1;
4011 }
4012
4013 return -EOPNOTSUPP;
4014}
4015
4016static int context_discard_partition(Context *context, Partition *p) {
4017 int r;
4018
4019 assert(context);
4020 assert(p);
4021
4022 assert(p->offset != UINT64_MAX);
4023 assert(p->new_size != UINT64_MAX);
4024 assert(!PARTITION_EXISTS(p)); /* Safety check: never discard existing partitions */
4025
4026 if (!arg_discard)
4027 return 0;
4028
4029 r = context_discard_range(context, p->offset, p->new_size);
4030 if (r == -EOPNOTSUPP) {
5b5109e2 4031 log_info("Storage does not support discard, not discarding data in future partition %" PRIu64 ".", p->partno);
e594a3b1
LP
4032 return 0;
4033 }
22163eb5
LP
4034 if (r == -EBUSY) {
4035 /* Let's handle this gracefully: https://bugzilla.kernel.org/show_bug.cgi?id=211167 */
4036 log_info("Block device is busy, not discarding partition %" PRIu64 " because it probably is mounted.", p->partno);
4037 return 0;
4038 }
e594a3b1
LP
4039 if (r == 0) {
4040 log_info("Partition %" PRIu64 " too short for discard, skipping.", p->partno);
4041 return 0;
4042 }
4043 if (r < 0)
5b5109e2 4044 return log_error_errno(r, "Failed to discard data for future partition %" PRIu64 ".", p->partno);
e594a3b1 4045
5b5109e2 4046 log_info("Successfully discarded data from future partition %" PRIu64 ".", p->partno);
e594a3b1
LP
4047 return 1;
4048}
4049
4050static int context_discard_gap_after(Context *context, Partition *p) {
4051 uint64_t gap, next = UINT64_MAX;
e594a3b1
LP
4052 int r;
4053
4054 assert(context);
4055 assert(!p || (p->offset != UINT64_MAX && p->new_size != UINT64_MAX));
4056
5113436b
AF
4057 if (!arg_discard)
4058 return 0;
4059
e594a3b1
LP
4060 if (p)
4061 gap = p->offset + p->new_size;
4062 else
771805eb
SS
4063 /* The context start gets rounded up to grain_size, however
4064 * existing partitions may be before that so ensure the gap
4065 * starts at the first actually usable lba
4066 */
4067 gap = fdisk_get_first_lba(context->fdisk_context) * context->sector_size;
e594a3b1
LP
4068
4069 LIST_FOREACH(partitions, q, context->partitions) {
4070 if (q->dropped)
4071 continue;
4072
4073 assert(q->offset != UINT64_MAX);
4074 assert(q->new_size != UINT64_MAX);
4075
4076 if (q->offset < gap)
4077 continue;
4078
4079 if (next == UINT64_MAX || q->offset < next)
4080 next = q->offset;
4081 }
4082
4083 if (next == UINT64_MAX) {
771805eb 4084 next = (fdisk_get_last_lba(context->fdisk_context) + 1) * context->sector_size;
e594a3b1
LP
4085 if (gap > next)
4086 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition end beyond disk end.");
4087 }
4088
4089 assert(next >= gap);
4090 r = context_discard_range(context, gap, next - gap);
4091 if (r == -EOPNOTSUPP) {
4092 if (p)
5b5109e2 4093 log_info("Storage does not support discard, not discarding gap after partition %" PRIu64 ".", p->partno);
e594a3b1 4094 else
5b5109e2 4095 log_info("Storage does not support discard, not discarding gap at beginning of disk.");
e594a3b1
LP
4096 return 0;
4097 }
4098 if (r == 0) /* Too short */
4099 return 0;
4100 if (r < 0) {
4101 if (p)
4102 return log_error_errno(r, "Failed to discard gap after partition %" PRIu64 ".", p->partno);
4103 else
4104 return log_error_errno(r, "Failed to discard gap at beginning of disk.");
4105 }
4106
4107 if (p)
4108 log_info("Successfully discarded gap after partition %" PRIu64 ".", p->partno);
4109 else
4110 log_info("Successfully discarded gap at beginning of disk.");
4111
4112 return 0;
4113}
4114
cc751c75 4115static int context_wipe_and_discard(Context *context) {
e594a3b1
LP
4116 int r;
4117
4118 assert(context);
4119
19c58989
LP
4120 if (arg_empty == EMPTY_CREATE) /* If we just created the image, no need to wipe */
4121 return 0;
4122
e594a3b1
LP
4123 /* Wipe and discard the contents of all partitions we are about to create. We skip the discarding if
4124 * we were supposed to start from scratch anyway, as in that case we just discard the whole block
4125 * device in one go early on. */
4126
4127 LIST_FOREACH(partitions, p, context->partitions) {
4128
4129 if (!p->allocated_to_area)
4130 continue;
4131
c74d50ff 4132 if (partition_type_defer(&p->type))
81d1098b
DDM
4133 continue;
4134
e594a3b1
LP
4135 r = context_wipe_partition(context, p);
4136 if (r < 0)
4137 return r;
4138
cc751c75 4139 if (!context->from_scratch) {
f0cb1b95
LP
4140 r = context_discard_partition(context, p);
4141 if (r < 0)
4142 return r;
4143
e594a3b1
LP
4144 r = context_discard_gap_after(context, p);
4145 if (r < 0)
4146 return r;
4147 }
4148 }
4149
cc751c75 4150 if (!context->from_scratch) {
e594a3b1
LP
4151 r = context_discard_gap_after(context, NULL);
4152 if (r < 0)
4153 return r;
4154 }
4155
4156 return 0;
4157}
4158
a09ae915
DDM
4159typedef struct DecryptedPartitionTarget {
4160 int fd;
fd9b68d9 4161 char *dm_name;
a09ae915
DDM
4162 char *volume;
4163 struct crypt_device *device;
4164} DecryptedPartitionTarget;
4165
4166static DecryptedPartitionTarget* decrypted_partition_target_free(DecryptedPartitionTarget *t) {
465261bf 4167#if HAVE_LIBCRYPTSETUP
a09ae915
DDM
4168 int r;
4169
4170 if (!t)
4171 return NULL;
4172
a09ae915
DDM
4173 safe_close(t->fd);
4174
fd9b68d9
DDM
4175 /* udev or so might access out block device in the background while we are done. Let's hence
4176 * force detach the volume. We sync'ed before, hence this should be safe. */
4177 r = sym_crypt_deactivate_by_name(t->device, t->dm_name, CRYPT_DEACTIVATE_FORCE);
4178 if (r < 0)
4179 log_warning_errno(r, "Failed to deactivate LUKS device, ignoring: %m");
a09ae915
DDM
4180
4181 sym_crypt_free(t->device);
fd9b68d9 4182 free(t->dm_name);
a09ae915
DDM
4183 free(t->volume);
4184 free(t);
4185#endif
4186 return NULL;
4187}
4188
a64769d6
DDM
4189typedef struct {
4190 LoopDevice *loop;
4191 int fd;
4192 char *path;
4193 int whole_fd;
a09ae915 4194 DecryptedPartitionTarget *decrypted;
a64769d6
DDM
4195} PartitionTarget;
4196
4197static int partition_target_fd(PartitionTarget *t) {
4198 assert(t);
4199 assert(t->loop || t->fd >= 0 || t->whole_fd >= 0);
a09ae915
DDM
4200
4201 if (t->decrypted)
4202 return t->decrypted->fd;
4203
4204 if (t->loop)
4205 return t->loop->fd;
4206
4207 if (t->fd >= 0)
4208 return t->fd;
4209
4210 return t->whole_fd;
a64769d6
DDM
4211}
4212
4213static const char* partition_target_path(PartitionTarget *t) {
4214 assert(t);
4215 assert(t->loop || t->path);
a09ae915
DDM
4216
4217 if (t->decrypted)
4218 return t->decrypted->volume;
4219
4220 if (t->loop)
4221 return t->loop->node;
4222
4223 return t->path;
a64769d6
DDM
4224}
4225
52031044 4226static PartitionTarget* partition_target_free(PartitionTarget *t) {
a64769d6
DDM
4227 if (!t)
4228 return NULL;
4229
a09ae915 4230 decrypted_partition_target_free(t->decrypted);
a64769d6
DDM
4231 loop_device_unref(t->loop);
4232 safe_close(t->fd);
4233 unlink_and_free(t->path);
4234
4235 return mfree(t);
4236}
4237
4238DEFINE_TRIVIAL_CLEANUP_FUNC(PartitionTarget*, partition_target_free);
4239
b9c0b6c0 4240static int prepare_temporary_file(Context *context, PartitionTarget *t, uint64_t size) {
d3201eb4 4241 _cleanup_(unlink_and_freep) char *temp = NULL;
254d1313 4242 _cleanup_close_ int fd = -EBADF;
d3201eb4 4243 const char *vt;
b9c0b6c0 4244 unsigned attrs = 0;
d3201eb4
DDM
4245 int r;
4246
b9c0b6c0 4247 assert(context);
d3201eb4
DDM
4248 assert(t);
4249
4250 r = var_tmp_dir(&vt);
4251 if (r < 0)
4252 return log_error_errno(r, "Could not determine temporary directory: %m");
4253
4254 temp = path_join(vt, "repart-XXXXXX");
4255 if (!temp)
4256 return log_oom();
4257
4258 fd = mkostemp_safe(temp);
4259 if (fd < 0)
4260 return log_error_errno(fd, "Failed to create temporary file: %m");
4261
b9c0b6c0
DDM
4262 r = read_attr_fd(fdisk_get_devfd(context->fdisk_context), &attrs);
4263 if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r))
4264 return log_error_errno(r, "Failed to read file attributes of %s: %m", arg_node);
4265
4266 if (FLAGS_SET(attrs, FS_NOCOW_FL)) {
4267 r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
4268 if (r < 0 && !ERRNO_IS_NOT_SUPPORTED(r))
4269 return log_error_errno(r, "Failed to disable copy-on-write on %s: %m", temp);
4270 }
4271
d3201eb4
DDM
4272 if (ftruncate(fd, size) < 0)
4273 return log_error_errno(errno, "Failed to truncate temporary file to %s: %m",
9f08d7f7 4274 FORMAT_BYTES(size));
d3201eb4
DDM
4275
4276 t->fd = TAKE_FD(fd);
4277 t->path = TAKE_PTR(temp);
4278
4279 return 0;
4280}
4281
3799fa80
DDM
4282static bool loop_device_error_is_fatal(const Partition *p, int r) {
4283 assert(p);
4d9ccdc9 4284 return arg_offline == 0 || (r != -ENOENT && !ERRNO_IS_PRIVILEGE(r)) || !ordered_hashmap_isempty(p->subvolumes) || p->default_subvolume;
3799fa80
DDM
4285}
4286
a64769d6
DDM
4287static int partition_target_prepare(
4288 Context *context,
4289 Partition *p,
4290 uint64_t size,
4291 bool need_path,
4292 PartitionTarget **ret) {
4293
4294 _cleanup_(partition_target_freep) PartitionTarget *t = NULL;
d3201eb4
DDM
4295 _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
4296 int whole_fd, r;
a64769d6
DDM
4297
4298 assert(context);
4299 assert(p);
4300 assert(ret);
4301
4302 assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
4303
6b1ea55e 4304 t = new(PartitionTarget, 1);
a64769d6
DDM
4305 if (!t)
4306 return log_oom();
6b1ea55e 4307 *t = (PartitionTarget) {
254d1313
ZJS
4308 .fd = -EBADF,
4309 .whole_fd = -EBADF,
6b1ea55e 4310 };
a64769d6 4311
d3201eb4 4312 if (!need_path) {
86cbbc6d 4313 if (lseek(whole_fd, p->offset, SEEK_SET) < 0)
d3201eb4 4314 return log_error_errno(errno, "Failed to seek to partition offset: %m");
a64769d6 4315
d3201eb4
DDM
4316 t->whole_fd = whole_fd;
4317 *ret = TAKE_PTR(t);
4318 return 0;
4319 }
a64769d6 4320
d3201eb4
DDM
4321 /* Loopback block devices are not only useful to turn regular files into block devices, but
4322 * also to cut out sections of block devices into new block devices. */
a64769d6 4323
fc10b158 4324 if (arg_offline <= 0) {
66a8c74c 4325 r = loop_device_make(whole_fd, O_RDWR, p->offset, size, context->sector_size, 0, LOCK_EX, &d);
3799fa80 4326 if (r < 0 && loop_device_error_is_fatal(p, r))
fc10b158
DDM
4327 return log_error_errno(r, "Failed to make loopback device of future partition %" PRIu64 ": %m", p->partno);
4328 if (r >= 0) {
4329 t->loop = TAKE_PTR(d);
4330 *ret = TAKE_PTR(t);
4331 return 0;
4332 }
4333
4334 log_debug_errno(r, "No access to loop devices, falling back to a regular file");
d3201eb4 4335 }
a64769d6 4336
d3201eb4
DDM
4337 /* If we can't allocate a loop device, let's write to a regular file that we copy into the final
4338 * image so we can run in containers and without needing root privileges. On filesystems with
4339 * reflinking support, we can take advantage of this and just reflink the result into the image.
4340 */
a64769d6 4341
b9c0b6c0 4342 r = prepare_temporary_file(context, t, size);
d3201eb4
DDM
4343 if (r < 0)
4344 return r;
a64769d6
DDM
4345
4346 *ret = TAKE_PTR(t);
4347
4348 return 0;
4349}
4350
4351static int partition_target_grow(PartitionTarget *t, uint64_t size) {
4352 int r;
4353
4354 assert(t);
a09ae915 4355 assert(!t->decrypted);
a64769d6
DDM
4356
4357 if (t->loop) {
4358 r = loop_device_refresh_size(t->loop, UINT64_MAX, size);
4359 if (r < 0)
4360 return log_error_errno(r, "Failed to refresh loopback device size: %m");
4361 } else if (t->fd >= 0) {
4362 if (ftruncate(t->fd, size) < 0)
4363 return log_error_errno(errno, "Failed to grow '%s' to %s by truncation: %m",
4364 t->path, FORMAT_BYTES(size));
4365 }
4366
4367 return 0;
4368}
4369
4370static int partition_target_sync(Context *context, Partition *p, PartitionTarget *t) {
4371 int whole_fd, r;
4372
4373 assert(context);
4374 assert(p);
4375 assert(t);
4376
4377 assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
4378
f3b8e81f
DDM
4379 log_info("Syncing future partition %"PRIu64" contents to disk.", p->partno);
4380
a09ae915
DDM
4381 if (t->decrypted && fsync(t->decrypted->fd) < 0)
4382 return log_error_errno(errno, "Failed to sync changes to '%s': %m", t->decrypted->volume);
4383
a64769d6
DDM
4384 if (t->loop) {
4385 r = loop_device_sync(t->loop);
4386 if (r < 0)
4387 return log_error_errno(r, "Failed to sync loopback device: %m");
4388 } else if (t->fd >= 0) {
e21be797
DDM
4389 struct stat st;
4390
86cbbc6d 4391 if (lseek(whole_fd, p->offset, SEEK_SET) < 0)
a64769d6
DDM
4392 return log_error_errno(errno, "Failed to seek to partition offset: %m");
4393
86cbbc6d 4394 if (lseek(t->fd, 0, SEEK_SET) < 0)
6eccec00
DDM
4395 return log_error_errno(errno, "Failed to seek to start of temporary file: %m");
4396
e21be797
DDM
4397 if (fstat(t->fd, &st) < 0)
4398 return log_error_errno(errno, "Failed to stat temporary file: %m");
4399
4400 if (st.st_size > (off_t) p->new_size)
4401 return log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
4e494e6a 4402 "Partition %" PRIu64 "'s contents (%s) don't fit in the partition (%s).",
e21be797
DDM
4403 p->partno, FORMAT_BYTES(st.st_size), FORMAT_BYTES(p->new_size));
4404
a64769d6
DDM
4405 r = copy_bytes(t->fd, whole_fd, UINT64_MAX, COPY_REFLINK|COPY_HOLES|COPY_FSYNC);
4406 if (r < 0)
4407 return log_error_errno(r, "Failed to copy bytes to partition: %m");
4408 } else {
4409 if (fsync(t->whole_fd) < 0)
4410 return log_error_errno(errno, "Failed to sync changes: %m");
4411 }
4412
4413 return 0;
4414}
4415
a09ae915 4416static int partition_encrypt(Context *context, Partition *p, PartitionTarget *target, bool offline) {
b99b2941 4417#if HAVE_LIBCRYPTSETUP && HAVE_CRYPT_SET_DATA_OFFSET && HAVE_CRYPT_REENCRYPT_INIT_BY_PASSPHRASE && (HAVE_CRYPT_REENCRYPT_RUN || HAVE_CRYPT_REENCRYPT)
a09ae915 4418 const char *node = partition_target_path(target);
48a09a8f 4419 struct crypt_params_luks2 luks_params = {
922576e4 4420 .label = strempty(ASSERT_PTR(p)->new_label),
93f125a6 4421 .sector_size = ASSERT_PTR(context)->fs_sector_size,
a09ae915 4422 .data_device = offline ? node : NULL,
48a09a8f
DDM
4423 };
4424 struct crypt_params_reencrypt reencrypt_params = {
4425 .mode = CRYPT_REENCRYPT_ENCRYPT,
4426 .direction = CRYPT_REENCRYPT_BACKWARD,
4427 .resilience = "datashift",
4428 .data_shift = LUKS2_METADATA_SIZE / 512,
4429 .luks2 = &luks_params,
4430 .flags = CRYPT_REENCRYPT_INITIALIZE_ONLY|CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT,
4431 };
0d12936d 4432 _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
1d877271 4433#if HAVE_TPM2
48a09a8f 4434 _cleanup_(erase_and_freep) char *base64_encoded = NULL;
1d877271 4435#endif
48a09a8f 4436 _cleanup_fclose_ FILE *h = NULL;
a09ae915 4437 _cleanup_free_ char *hp = NULL, *vol = NULL, *dm_name = NULL;
48a09a8f
DDM
4438 const char *passphrase = NULL;
4439 size_t passphrase_size = 0;
0b75744d 4440 const char *vt;
b9df3536
LP
4441 int r;
4442
994b3031 4443 assert(context);
b9df3536 4444 assert(p);
889914ef
LP
4445 assert(p->encrypt != ENCRYPT_OFF);
4446
0d12936d
LP
4447 r = dlopen_cryptsetup();
4448 if (r < 0)
4449 return log_error_errno(r, "libcryptsetup not found, cannot encrypt: %m");
4450
b9df3536
LP
4451 log_info("Encrypting future partition %" PRIu64 "...", p->partno);
4452
a09ae915
DDM
4453 if (offline) {
4454 r = var_tmp_dir(&vt);
4455 if (r < 0)
4456 return log_error_errno(r, "Failed to determine temporary files directory: %m");
0b75744d 4457
a09ae915
DDM
4458 r = fopen_temporary_child(vt, &h, &hp);
4459 if (r < 0)
4460 return log_error_errno(r, "Failed to create temporary LUKS header file: %m");
48a09a8f 4461
a09ae915
DDM
4462 /* Weird cryptsetup requirement which requires the header file to be the size of at least one
4463 * sector. */
9f08d7f7
MY
4464 if (ftruncate(fileno(h), luks_params.sector_size) < 0)
4465 return log_error_errno(errno, "Failed to grow temporary LUKS header file: %m");
a09ae915
DDM
4466 } else {
4467 if (asprintf(&dm_name, "luks-repart-%08" PRIx64, random_u64()) < 0)
4468 return log_oom();
4469
4470 vol = path_join("/dev/mapper/", dm_name);
4471 if (!vol)
4472 return log_oom();
4473 }
48a09a8f 4474
a09ae915 4475 r = sym_crypt_init(&cd, offline ? hp : node);
48a09a8f
DDM
4476 if (r < 0)
4477 return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", hp);
b9df3536
LP
4478
4479 cryptsetup_enable_logging(cd);
4480
a09ae915
DDM
4481 if (offline) {
4482 /* Disable kernel keyring usage by libcryptsetup as a workaround for
4483 * https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/273. This makes sure that we can
4484 * do offline encryption even when repart is running in a container. */
4485 r = sym_crypt_volume_key_keyring(cd, false);
4486 if (r < 0)
4487 return log_error_errno(r, "Failed to disable kernel keyring: %m");
48a09a8f 4488
a09ae915
DDM
4489 r = sym_crypt_metadata_locking(cd, false);
4490 if (r < 0)
4491 return log_error_errno(r, "Failed to disable metadata locking: %m");
48a09a8f 4492
a09ae915
DDM
4493 r = sym_crypt_set_data_offset(cd, LUKS2_METADATA_SIZE / 512);
4494 if (r < 0)
4495 return log_error_errno(r, "Failed to set data offset: %m");
4496 }
48a09a8f 4497
9f08d7f7
MY
4498 r = sym_crypt_format(
4499 cd,
4500 CRYPT_LUKS2,
4501 "aes",
4502 "xts-plain64",
4503 SD_ID128_TO_UUID_STRING(p->luks_uuid),
4504 NULL,
4505 VOLUME_KEY_SIZE,
4506 &luks_params);
b9df3536
LP
4507 if (r < 0)
4508 return log_error_errno(r, "Failed to LUKS2 format future partition: %m");
4509
889914ef
LP
4510 if (IN_SET(p->encrypt, ENCRYPT_KEY_FILE, ENCRYPT_KEY_FILE_TPM2)) {
4511 r = sym_crypt_keyslot_add_by_volume_key(
4512 cd,
4513 CRYPT_ANY_SLOT,
98e0456e
DDM
4514 NULL,
4515 VOLUME_KEY_SIZE,
889914ef
LP
4516 strempty(arg_key),
4517 arg_key_size);
4518 if (r < 0)
4519 return log_error_errno(r, "Failed to add LUKS2 key: %m");
48a09a8f
DDM
4520
4521 passphrase = strempty(arg_key);
4522 passphrase_size = arg_key_size;
889914ef
LP
4523 }
4524
4525 if (IN_SET(p->encrypt, ENCRYPT_TPM2, ENCRYPT_KEY_FILE_TPM2)) {
4526#if HAVE_TPM2
8e658767 4527 _cleanup_(iovec_done) struct iovec pubkey = {}, srk = {};
8d042bc4 4528 _cleanup_(iovec_done_erase) struct iovec secret = {};
309a747f 4529 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
5e476b85 4530 ssize_t base64_encoded_size;
889914ef 4531 int keyslot;
59c8afbc 4532 TPM2Flags flags = 0;
889914ef 4533
02ef97cd 4534 if (arg_tpm2_public_key_pcr_mask != 0) {
8d042bc4 4535 r = tpm2_load_pcr_public_key(arg_tpm2_public_key, &pubkey.iov_base, &pubkey.iov_len);
02ef97cd
LP
4536 if (r < 0) {
4537 if (arg_tpm2_public_key || r != -ENOENT)
2e9f6072 4538 return log_error_errno(r, "Failed to read TPM PCR public key: %m");
02ef97cd
LP
4539
4540 log_debug_errno(r, "Failed to read TPM2 PCR public key, proceeding without: %m");
4541 arg_tpm2_public_key_pcr_mask = 0;
4542 }
4543 }
4544
9e437994 4545 TPM2B_PUBLIC public;
8d042bc4
LP
4546 if (iovec_is_set(&pubkey)) {
4547 r = tpm2_tpm2b_public_from_pem(pubkey.iov_base, pubkey.iov_len, &public);
9e437994
DS
4548 if (r < 0)
4549 return log_error_errno(r, "Could not convert public key to TPM2B_PUBLIC: %m");
4550 }
4551
78fdf0f6
LP
4552 _cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy pcrlock_policy = {};
4553 if (arg_tpm2_pcrlock) {
4554 r = tpm2_pcrlock_policy_load(arg_tpm2_pcrlock, &pcrlock_policy);
4555 if (r < 0)
4556 return r;
4557
4558 flags |= TPM2_FLAGS_USE_PCRLOCK;
4559 }
4560
4561 _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
4562 TPM2B_PUBLIC device_key_public = {};
4563 if (arg_tpm2_device_key) {
4564 r = tpm2_load_public_key_file(arg_tpm2_device_key, &device_key_public);
4565 if (r < 0)
4566 return r;
4567
4568 if (!tpm2_pcr_values_has_all_values(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values))
4569 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
4570 "Must provide all PCR values when using TPM2 device key.");
4571 } else {
21a3bc6b 4572 r = tpm2_context_new_or_warn(arg_tpm2_device, &tpm2_context);
78fdf0f6 4573 if (r < 0)
21a3bc6b 4574 return r;
78fdf0f6
LP
4575
4576 if (!tpm2_pcr_values_has_all_values(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values)) {
4577 r = tpm2_pcr_read_missing_values(tpm2_context, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values);
4578 if (r < 0)
4579 return log_error_errno(r, "Could not read pcr values: %m");
4580 }
4581 }
9e437994
DS
4582
4583 uint16_t hash_pcr_bank = 0;
4584 uint32_t hash_pcr_mask = 0;
4585 if (arg_tpm2_n_hash_pcr_values > 0) {
4586 size_t hash_count;
4587 r = tpm2_pcr_values_hash_count(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, &hash_count);
4588 if (r < 0)
4589 return log_error_errno(r, "Could not get hash count: %m");
4590
4591 if (hash_count > 1)
4592 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Multiple PCR banks selected.");
4593
4594 hash_pcr_bank = arg_tpm2_hash_pcr_values[0].hash;
4595 r = tpm2_pcr_values_to_mask(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, hash_pcr_bank, &hash_pcr_mask);
4596 if (r < 0)
4597 return log_error_errno(r, "Could not get hash mask: %m");
4598 }
4599
8e658767
LP
4600 TPM2B_DIGEST policy_hash[2] = {
4601 TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE),
4602 TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE),
4603 };
4604 size_t n_policy_hash = 1;
4605
4606 /* If both PCR public key unlock and pcrlock unlock is selected, then shard the encryption key. */
a4342701
LP
4607 r = tpm2_calculate_sealing_policy(
4608 arg_tpm2_hash_pcr_values,
4609 arg_tpm2_n_hash_pcr_values,
8d042bc4 4610 iovec_is_set(&pubkey) ? &public : NULL,
a4342701 4611 /* use_pin= */ false,
8e658767
LP
4612 arg_tpm2_pcrlock && !iovec_is_set(&pubkey) ? &pcrlock_policy : NULL,
4613 policy_hash + 0);
9e437994 4614 if (r < 0)
8e658767
LP
4615 return log_error_errno(r, "Could not calculate sealing policy digest for shard 0: %m");
4616
4617 if (arg_tpm2_pcrlock && iovec_is_set(&pubkey)) {
4618 r = tpm2_calculate_sealing_policy(
4619 arg_tpm2_hash_pcr_values,
4620 arg_tpm2_n_hash_pcr_values,
4621 /* pubkey= */ NULL, /* Turn this one off for the 2nd shard */
4622 /* use_pin= */ false,
4623 &pcrlock_policy, /* But turn this one on */
4624 policy_hash + 1);
4625 if (r < 0)
4626 return log_error_errno(r, "Could not calculate sealing policy digest for shard 1: %m");
4627
4628 n_policy_hash++;
4629 }
4630
4631 struct iovec *blobs = NULL;
4632 size_t n_blobs = 0;
4633 CLEANUP_ARRAY(blobs, n_blobs, iovec_array_free);
4634
4635 if (arg_tpm2_device_key) {
4636 if (n_policy_hash > 1)
4637 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
4638 "Combined signed PCR policies and pcrlock policies cannot be calculated offline, currently.");
4639
4640 blobs = new0(struct iovec, 1);
4641 if (!blobs)
4642 return log_oom();
4643
4644 n_blobs = 1;
9e437994 4645
78fdf0f6
LP
4646 r = tpm2_calculate_seal(
4647 arg_tpm2_seal_key_handle,
4648 &device_key_public,
4649 /* attributes= */ NULL,
8d042bc4 4650 /* secret= */ NULL,
8e658767 4651 policy_hash + 0,
78fdf0f6 4652 /* pin= */ NULL,
8d042bc4 4653 &secret,
8e658767 4654 blobs + 0,
8d042bc4 4655 &srk);
8e658767 4656 } else
78fdf0f6
LP
4657 r = tpm2_seal(tpm2_context,
4658 arg_tpm2_seal_key_handle,
8e658767
LP
4659 policy_hash,
4660 n_policy_hash,
78fdf0f6 4661 /* pin= */ NULL,
8d042bc4 4662 &secret,
8e658767
LP
4663 &blobs,
4664 &n_blobs,
78fdf0f6 4665 /* ret_primary_alg= */ NULL,
8d042bc4 4666 &srk);
889914ef
LP
4667 if (r < 0)
4668 return log_error_errno(r, "Failed to seal to TPM2: %m");
4669
8d042bc4 4670 base64_encoded_size = base64mem(secret.iov_base, secret.iov_len, &base64_encoded);
5e476b85
LP
4671 if (base64_encoded_size < 0)
4672 return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
889914ef
LP
4673
4674 r = cryptsetup_set_minimal_pbkdf(cd);
4675 if (r < 0)
4676 return log_error_errno(r, "Failed to set minimal PBKDF: %m");
4677
4678 keyslot = sym_crypt_keyslot_add_by_volume_key(
4679 cd,
4680 CRYPT_ANY_SLOT,
78fdf0f6
LP
4681 /* volume_key= */ NULL,
4682 /* volume_key_size= */ VOLUME_KEY_SIZE,
889914ef 4683 base64_encoded,
5e476b85 4684 base64_encoded_size);
889914ef 4685 if (keyslot < 0)
48a09a8f 4686 return log_error_errno(keyslot, "Failed to add new TPM2 key: %m");
889914ef 4687
8e658767
LP
4688 struct iovec policy_hash_as_iovec[2] = {
4689 IOVEC_MAKE(policy_hash[0].buffer, policy_hash[0].size),
4690 IOVEC_MAKE(policy_hash[1].buffer, policy_hash[1].size),
4691 };
4692
f0f4fcae
LP
4693 r = tpm2_make_luks2_json(
4694 keyslot,
9e437994
DS
4695 hash_pcr_mask,
4696 hash_pcr_bank,
8d042bc4 4697 &pubkey,
02ef97cd 4698 arg_tpm2_public_key_pcr_mask,
9e437994 4699 /* primary_alg= */ 0,
8e658767
LP
4700 blobs,
4701 n_blobs,
4702 policy_hash_as_iovec,
4703 n_policy_hash,
8d042bc4
LP
4704 /* salt= */ NULL, /* no salt because tpm2_seal has no pin */
4705 &srk,
d37c312b 4706 &pcrlock_policy.nv_handle,
78fdf0f6 4707 flags,
f0f4fcae 4708 &v);
889914ef
LP
4709 if (r < 0)
4710 return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");
4711
4712 r = cryptsetup_add_token_json(cd, v);
4713 if (r < 0)
4714 return log_error_errno(r, "Failed to add TPM2 JSON token to LUKS2 header: %m");
48a09a8f
DDM
4715
4716 passphrase = base64_encoded;
4717 passphrase_size = strlen(base64_encoded);
889914ef
LP
4718#else
4719 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
4720 "Support for TPM2 enrollment not enabled.");
4721#endif
4722 }
b9df3536 4723
a09ae915
DDM
4724 if (offline) {
4725 r = sym_crypt_reencrypt_init_by_passphrase(
4726 cd,
4727 NULL,
4728 passphrase,
4729 passphrase_size,
4730 CRYPT_ANY_SLOT,
4731 0,
4732 sym_crypt_get_cipher(cd),
4733 sym_crypt_get_cipher_mode(cd),
4734 &reencrypt_params);
4735 if (r < 0)
4736 return log_error_errno(r, "Failed to prepare for reencryption: %m");
b9df3536 4737
a09ae915
DDM
4738 /* crypt_reencrypt_init_by_passphrase() doesn't actually put the LUKS header at the front, we
4739 * have to do that ourselves. */
b9df3536 4740
a09ae915
DDM
4741 sym_crypt_free(cd);
4742 cd = NULL;
b9df3536 4743
a09ae915
DDM
4744 r = sym_crypt_init(&cd, node);
4745 if (r < 0)
4746 return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", node);
b9df3536 4747
a09ae915
DDM
4748 r = sym_crypt_header_restore(cd, CRYPT_LUKS2, hp);
4749 if (r < 0)
4750 return log_error_errno(r, "Failed to place new LUKS header at head of %s: %m", node);
b9df3536 4751
a09ae915 4752 reencrypt_params.flags &= ~CRYPT_REENCRYPT_INITIALIZE_ONLY;
b9df3536 4753
a09ae915
DDM
4754 r = sym_crypt_reencrypt_init_by_passphrase(
4755 cd,
4756 NULL,
4757 passphrase,
4758 passphrase_size,
4759 CRYPT_ANY_SLOT,
4760 0,
4761 NULL,
4762 NULL,
4763 &reencrypt_params);
4764 if (r < 0)
4765 return log_error_errno(r, "Failed to load reencryption context: %m");
b9df3536 4766
b99b2941
DDM
4767#if HAVE_CRYPT_REENCRYPT_RUN
4768 r = sym_crypt_reencrypt_run(cd, NULL, NULL);
4769#else
a09ae915 4770 r = sym_crypt_reencrypt(cd, NULL);
b99b2941 4771#endif
a09ae915
DDM
4772 if (r < 0)
4773 return log_error_errno(r, "Failed to encrypt %s: %m", node);
4774 } else {
4775 _cleanup_free_ DecryptedPartitionTarget *t = NULL;
4776 _cleanup_close_ int dev_fd = -1;
4777
4778 r = sym_crypt_activate_by_volume_key(
4779 cd,
4780 dm_name,
4781 NULL,
4782 VOLUME_KEY_SIZE,
726fc7ae 4783 (arg_discard ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0) | CRYPT_ACTIVATE_PRIVATE);
a09ae915
DDM
4784 if (r < 0)
4785 return log_error_errno(r, "Failed to activate LUKS superblock: %m");
4786
4787 dev_fd = open(vol, O_RDWR|O_CLOEXEC|O_NOCTTY);
4788 if (dev_fd < 0)
4789 return log_error_errno(errno, "Failed to open LUKS volume '%s': %m", vol);
4790
4791 if (flock(dev_fd, LOCK_EX) < 0)
4792 return log_error_errno(errno, "Failed to lock '%s': %m", vol);
4793
4794 t = new(DecryptedPartitionTarget, 1);
4795 if (!t)
4796 return log_oom();
4797
4798 *t = (DecryptedPartitionTarget) {
4799 .fd = TAKE_FD(dev_fd),
fd9b68d9 4800 .dm_name = TAKE_PTR(dm_name),
a09ae915
DDM
4801 .volume = TAKE_PTR(vol),
4802 .device = TAKE_PTR(cd),
4803 };
4804
4805 target->decrypted = TAKE_PTR(t);
4806 }
48a09a8f
DDM
4807
4808 log_info("Successfully encrypted future partition %" PRIu64 ".", p->partno);
b9df3536 4809
3dd8ae5c 4810 return 0;
48a09a8f
DDM
4811#else
4812 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
4e494e6a 4813 "libcryptsetup is not supported or is missing required symbols, cannot encrypt.");
3dd8ae5c 4814#endif
b9df3536
LP
4815}
4816
2b392d86
DDM
4817static int partition_format_verity_hash(
4818 Context *context,
4819 Partition *p,
13bde177 4820 const char *node,
2b392d86
DDM
4821 const char *data_node) {
4822
4823#if HAVE_LIBCRYPTSETUP
4824 Partition *dp;
a64769d6 4825 _cleanup_(partition_target_freep) PartitionTarget *t = NULL;
2b392d86 4826 _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
cf18d96f 4827 _cleanup_free_ char *hint = NULL;
a64769d6 4828 int r;
2b392d86
DDM
4829
4830 assert(context);
4831 assert(p);
deeae10e 4832 assert(p->verity == VERITY_HASH);
2b392d86
DDM
4833 assert(data_node);
4834
4835 if (p->dropped)
4836 return 0;
4837
4838 if (PARTITION_EXISTS(p)) /* Never format existing partitions */
4839 return 0;
4840
a954b427 4841 /* Minimized partitions will use the copy blocks logic so skip those here. */
5eef7047
DDM
4842 if (p->copy_blocks_fd >= 0)
4843 return 0;
4844
2b392d86
DDM
4845 assert_se(dp = p->siblings[VERITY_DATA]);
4846 assert(!dp->dropped);
4847
cf18d96f
DDM
4848 (void) partition_hint(p, node, &hint);
4849
2b392d86
DDM
4850 r = dlopen_cryptsetup();
4851 if (r < 0)
4852 return log_error_errno(r, "libcryptsetup not found, cannot setup verity: %m");
4853
13bde177
DDM
4854 if (!node) {
4855 r = partition_target_prepare(context, p, p->new_size, /*need_path=*/ true, &t);
4856 if (r < 0)
4857 return r;
2b392d86 4858
13bde177
DDM
4859 node = partition_target_path(t);
4860 }
4861
c380047b
MC
4862 if (p->verity_data_block_size == UINT64_MAX)
4863 p->verity_data_block_size = context->fs_sector_size;
4864 if (p->verity_hash_block_size == UINT64_MAX)
4865 p->verity_hash_block_size = context->fs_sector_size;
4866
13bde177 4867 r = sym_crypt_init(&cd, node);
2b392d86 4868 if (r < 0)
cf18d96f 4869 return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", node);
2b392d86 4870
e21be797
DDM
4871 cryptsetup_enable_logging(cd);
4872
2b392d86 4873 r = sym_crypt_format(
81e04781 4874 cd, CRYPT_VERITY, NULL, NULL, SD_ID128_TO_UUID_STRING(p->verity_uuid), NULL, 0,
2b392d86
DDM
4875 &(struct crypt_params_verity){
4876 .data_device = data_node,
4877 .flags = CRYPT_VERITY_CREATE_HASH,
4878 .hash_name = "sha256",
4879 .hash_type = 1,
c380047b
MC
4880 .data_block_size = p->verity_data_block_size,
4881 .hash_block_size = p->verity_hash_block_size,
81e04781
VH
4882 .salt_size = sizeof(p->verity_salt),
4883 .salt = (const char*)p->verity_salt,
2b392d86 4884 });
e21be797
DDM
4885 if (r < 0) {
4886 /* libcryptsetup reports non-descriptive EIO errors for every I/O failure. Luckily, it
4887 * doesn't clobber errno so let's check for ENOSPC so we can report a better error if the
4888 * partition is too small. */
4889 if (r == -EIO && errno == ENOSPC)
4890 return log_error_errno(errno,
cf18d96f
DDM
4891 "Verity hash data does not fit in partition %s with size %s",
4892 strna(hint), FORMAT_BYTES(p->new_size));
e21be797 4893
cf18d96f 4894 return log_error_errno(r, "Failed to setup verity hash data of partition %s: %m", strna(hint));
e21be797 4895 }
2b392d86 4896
13bde177
DDM
4897 if (t) {
4898 r = partition_target_sync(context, p, t);
4899 if (r < 0)
4900 return r;
4901 }
a64769d6 4902
2b392d86
DDM
4903 r = sym_crypt_get_volume_key_size(cd);
4904 if (r < 0)
cf18d96f 4905 return log_error_errno(r, "Failed to determine verity root hash size of partition %s: %m", strna(hint));
2b392d86 4906
6b4b40f4
LP
4907 _cleanup_(iovec_done) struct iovec rh = {
4908 .iov_base = malloc(r),
4909 .iov_len = r,
4910 };
4911 if (!rh.iov_base)
2b392d86
DDM
4912 return log_oom();
4913
6b4b40f4 4914 r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, (char *) rh.iov_base, &rh.iov_len, NULL, 0);
2b392d86 4915 if (r < 0)
cf18d96f 4916 return log_error_errno(r, "Failed to get verity root hash of partition %s: %m", strna(hint));
2b392d86 4917
6b4b40f4 4918 assert(rh.iov_len >= sizeof(sd_id128_t) * 2);
2b392d86
DDM
4919
4920 if (!dp->new_uuid_is_set) {
6b4b40f4 4921 memcpy_safe(dp->new_uuid.bytes, rh.iov_base, sizeof(sd_id128_t));
2b392d86
DDM
4922 dp->new_uuid_is_set = true;
4923 }
4924
4925 if (!p->new_uuid_is_set) {
6b4b40f4 4926 memcpy_safe(p->new_uuid.bytes, (uint8_t*) rh.iov_base + (rh.iov_len - sizeof(sd_id128_t)), sizeof(sd_id128_t));
2b392d86
DDM
4927 p->new_uuid_is_set = true;
4928 }
4929
3c9783c7 4930 p->roothash = TAKE_STRUCT(rh);
2b392d86
DDM
4931
4932 return 0;
4933#else
4e494e6a 4934 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "libcryptsetup is not supported, cannot setup verity hashes.");
2b392d86
DDM
4935#endif
4936}
4937
4ecd39c5 4938static int sign_verity_roothash(
6b4b40f4
LP
4939 const struct iovec *roothash,
4940 struct iovec *ret_signature) {
4ecd39c5
DDM
4941
4942#if HAVE_OPENSSL
4943 _cleanup_(BIO_freep) BIO *rb = NULL;
4944 _cleanup_(PKCS7_freep) PKCS7 *p7 = NULL;
4945 _cleanup_free_ char *hex = NULL;
4946 _cleanup_free_ uint8_t *sig = NULL;
4947 int sigsz;
4948
4949 assert(roothash);
6b4b40f4 4950 assert(iovec_is_set(roothash));
4ecd39c5 4951 assert(ret_signature);
4ecd39c5 4952
6b4b40f4 4953 hex = hexmem(roothash->iov_base, roothash->iov_len);
4ecd39c5
DDM
4954 if (!hex)
4955 return log_oom();
4956
4957 rb = BIO_new_mem_buf(hex, -1);
4958 if (!rb)
4959 return log_oom();
4960
4961 p7 = PKCS7_sign(arg_certificate, arg_private_key, NULL, rb, PKCS7_DETACHED|PKCS7_NOATTR|PKCS7_BINARY);
4962 if (!p7)
4963 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to calculate PKCS7 signature: %s",
4964 ERR_error_string(ERR_get_error(), NULL));
4965
4966 sigsz = i2d_PKCS7(p7, &sig);
4967 if (sigsz < 0)
4968 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert PKCS7 signature to DER: %s",
4969 ERR_error_string(ERR_get_error(), NULL));
4970
c490b6dd 4971 *ret_signature = IOVEC_MAKE(TAKE_PTR(sig), sigsz);
4ecd39c5
DDM
4972
4973 return 0;
4974#else
4e494e6a 4975 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot setup verity signature.");
4ecd39c5
DDM
4976#endif
4977}
4978
4979static int partition_format_verity_sig(Context *context, Partition *p) {
309a747f 4980 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
7dc951ef 4981 _cleanup_(iovec_done) struct iovec sig = {};
cf18d96f 4982 _cleanup_free_ char *text = NULL, *hint = NULL;
4ecd39c5
DDM
4983 Partition *hp;
4984 uint8_t fp[X509_FINGERPRINT_SIZE];
4ecd39c5
DDM
4985 int whole_fd, r;
4986
4987 assert(p->verity == VERITY_SIG);
4988
4989 if (p->dropped)
4990 return 0;
4991
4992 if (PARTITION_EXISTS(p))
4993 return 0;
4994
cf18d96f
DDM
4995 (void) partition_hint(p, context->node, &hint);
4996
4ecd39c5
DDM
4997 assert_se(hp = p->siblings[VERITY_HASH]);
4998 assert(!hp->dropped);
4999
5000 assert(arg_certificate);
5001
5002 assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
5003
6b4b40f4 5004 r = sign_verity_roothash(&hp->roothash, &sig);
4ecd39c5
DDM
5005 if (r < 0)
5006 return r;
5007
5008 r = x509_fingerprint(arg_certificate, fp);
5009 if (r < 0)
5010 return log_error_errno(r, "Unable to calculate X509 certificate fingerprint: %m");
5011
be5bee2a
LP
5012 r = sd_json_buildo(
5013 &v,
5014 SD_JSON_BUILD_PAIR("rootHash", SD_JSON_BUILD_HEX(hp->roothash.iov_base, hp->roothash.iov_len)),
5015 SD_JSON_BUILD_PAIR("certificateFingerprint", SD_JSON_BUILD_HEX(fp, sizeof(fp))),
5016 SD_JSON_BUILD_PAIR("signature", JSON_BUILD_IOVEC_BASE64(&sig)));
4ecd39c5 5017 if (r < 0)
cf18d96f 5018 return log_error_errno(r, "Failed to build verity signature JSON object: %m");
4ecd39c5 5019
309a747f 5020 r = sd_json_variant_format(v, 0, &text);
4ecd39c5 5021 if (r < 0)
cf18d96f 5022 return log_error_errno(r, "Failed to format verity signature JSON object: %m");
4ecd39c5 5023
3510df0a 5024 if (strlen(text)+1 > p->new_size)
4e494e6a 5025 return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "Verity signature too long for partition.");
3510df0a 5026
d28c6ce6 5027 r = strgrowpad0(&text, p->new_size);
4ecd39c5 5028 if (r < 0)
d28c6ce6 5029 return log_error_errno(r, "Failed to pad string to %s", FORMAT_BYTES(p->new_size));
4ecd39c5 5030
86cbbc6d 5031 if (lseek(whole_fd, p->offset, SEEK_SET) < 0)
cf18d96f 5032 return log_error_errno(errno, "Failed to seek to partition %s offset: %m", strna(hint));
4ecd39c5 5033
e22c60a9 5034 r = loop_write(whole_fd, text, p->new_size);
4ecd39c5 5035 if (r < 0)
cf18d96f 5036 return log_error_errno(r, "Failed to write verity signature to partition %s: %m", strna(hint));
4ecd39c5
DDM
5037
5038 if (fsync(whole_fd) < 0)
cf18d96f 5039 return log_error_errno(errno, "Failed to synchronize partition %s: %m", strna(hint));
4ecd39c5
DDM
5040
5041 return 0;
5042}
5043
add090ea
LP
5044static int progress_bytes(uint64_t n_bytes, void *userdata) {
5045 Partition *p = ASSERT_PTR(userdata);
5046 _cleanup_free_ char *s = NULL;
5047
5048 p->copy_blocks_done += n_bytes;
5049
6e199216 5050 if (asprintf(&s, "%s %s %s %s/%s",
add090ea
LP
5051 strna(p->copy_blocks_path),
5052 special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
5053 strna(p->definition_path),
5054 FORMAT_BYTES(p->copy_blocks_done),
5055 FORMAT_BYTES(p->copy_blocks_size)) < 0)
5056 return log_oom();
5057
5058 draw_progress_bar(s,
5059 p->copy_blocks_done >= p->copy_blocks_size ? 100.0 : /* catch division be zero */
5060 100.0 * (double) p->copy_blocks_done / (double) p->copy_blocks_size);
5061 return 0;
5062}
5063
757bc2e4 5064static int context_copy_blocks(Context *context) {
a64769d6 5065 int r;
757bc2e4
LP
5066
5067 assert(context);
5068
5069 /* Copy in file systems on the block level */
5070
5071 LIST_FOREACH(partitions, p, context->partitions) {
a64769d6 5072 _cleanup_(partition_target_freep) PartitionTarget *t = NULL;
757bc2e4
LP
5073
5074 if (p->copy_blocks_fd < 0)
5075 continue;
5076
5077 if (p->dropped)
5078 continue;
5079
5080 if (PARTITION_EXISTS(p)) /* Never copy over existing partitions */
5081 continue;
5082
c74d50ff 5083 if (partition_type_defer(&p->type))
81d1098b
DDM
5084 continue;
5085
757bc2e4 5086 assert(p->new_size != UINT64_MAX);
468d09c3
LP
5087
5088 size_t extra = p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0;
5089
5090 if (p->copy_blocks_size == UINT64_MAX)
5091 p->copy_blocks_size = LESS_BY(p->new_size, extra);
5092
5093 assert(p->new_size >= p->copy_blocks_size + extra);
757bc2e4 5094
b7786df0
LP
5095 usec_t start_timestamp = now(CLOCK_MONOTONIC);
5096
a64769d6
DDM
5097 r = partition_target_prepare(context, p, p->new_size,
5098 /*need_path=*/ p->encrypt != ENCRYPT_OFF || p->siblings[VERITY_HASH],
5099 &t);
5100 if (r < 0)
5101 return r;
757bc2e4 5102
a09ae915
DDM
5103 if (p->encrypt != ENCRYPT_OFF && t->loop) {
5104 r = partition_encrypt(context, p, t, /* offline = */ false);
5105 if (r < 0)
5106 return r;
5107 }
5108
a053f801
LP
5109 if (p->copy_blocks_offset == UINT64_MAX)
5110 log_info("Copying in '%s' (%s) on block level into future partition %" PRIu64 ".",
5111 p->copy_blocks_path, FORMAT_BYTES(p->copy_blocks_size), p->partno);
5112 else {
5113 log_info("Copying in '%s' @ %" PRIu64 " (%s) on block level into future partition %" PRIu64 ".",
5114 p->copy_blocks_path, p->copy_blocks_offset, FORMAT_BYTES(p->copy_blocks_size), p->partno);
5115
5116 if (lseek(p->copy_blocks_fd, p->copy_blocks_offset, SEEK_SET) < 0)
5117 return log_error_errno(errno, "Failed to seek to copy blocks offset in %s: %m", p->copy_blocks_path);
5118 }
1e46985a 5119
add090ea
LP
5120 r = copy_bytes_full(p->copy_blocks_fd, partition_target_fd(t), p->copy_blocks_size, COPY_REFLINK, /* ret_remains= */ NULL, /* ret_remains_size= */ NULL, progress_bytes, p);
5121 clear_progress_bar(/* prefix= */ NULL);
757bc2e4
LP
5122 if (r < 0)
5123 return log_error_errno(r, "Failed to copy in data from '%s': %m", p->copy_blocks_path);
5124
a09ae915
DDM
5125 log_info("Copying in of '%s' on block level completed.", p->copy_blocks_path);
5126
5127 if (p->encrypt != ENCRYPT_OFF && !t->loop) {
5128 r = partition_encrypt(context, p, t, /* offline = */ true);
b9df3536
LP
5129 if (r < 0)
5130 return r;
b9df3536
LP
5131 }
5132
a64769d6
DDM
5133 r = partition_target_sync(context, p, t);
5134 if (r < 0)
5135 return r;
48a09a8f 5136
b7786df0
LP
5137 usec_t time_spent = usec_sub_unsigned(now(CLOCK_MONOTONIC), start_timestamp);
5138 if (time_spent > 250 * USEC_PER_MSEC) /* Show throughput, but not if we spent too little time on it, since it's just noise then */
5139 log_info("Block level copying and synchronization of partition %" PRIu64 " complete in %s (%s/s).",
5140 p->partno, FORMAT_TIMESPAN(time_spent, 0), FORMAT_BYTES((uint64_t) ((double) p->copy_blocks_size / time_spent * USEC_PER_SEC)));
5141 else
5142 log_info("Block level copying and synchronization of partition %" PRIu64 " complete in %s.",
5143 p->partno, FORMAT_TIMESPAN(time_spent, 0));
5144
c74d50ff 5145 if (p->siblings[VERITY_HASH] && !partition_type_defer(&p->siblings[VERITY_HASH]->type)) {
a64769d6 5146 r = partition_format_verity_hash(context, p->siblings[VERITY_HASH],
13bde177 5147 /* node = */ NULL, partition_target_path(t));
2b392d86
DDM
5148 if (r < 0)
5149 return r;
5150 }
4ecd39c5 5151
c74d50ff 5152 if (p->siblings[VERITY_SIG] && !partition_type_defer(&p->siblings[VERITY_SIG]->type)) {
4ecd39c5
DDM
5153 r = partition_format_verity_sig(context, p->siblings[VERITY_SIG]);
5154 if (r < 0)
5155 return r;
5156 }
757bc2e4
LP
5157 }
5158
5159 return 0;
5160}
5161
e57b7020
DDM
5162static int add_exclude_path(const char *path, Hashmap **denylist, DenyType type) {
5163 _cleanup_free_ struct stat *st = NULL;
5164 int r;
5165
5166 assert(path);
5167 assert(denylist);
5168
5169 st = new(struct stat, 1);
5170 if (!st)
5171 return log_oom();
5172
607343a1 5173 r = chase_and_stat(path, arg_copy_source, CHASE_PREFIX_ROOT, NULL, st);
e57b7020
DDM
5174 if (r == -ENOENT)
5175 return 0;
5176 if (r < 0)
607343a1 5177 return log_error_errno(r, "Failed to stat source file '%s/%s': %m", strempty(arg_copy_source), path);
e57b7020
DDM
5178
5179 r = hashmap_ensure_put(denylist, &inode_hash_ops, st, INT_TO_PTR(type));
5180 if (r == -EEXIST)
5181 return 0;
5182 if (r < 0)
5183 return log_oom();
5184 if (r > 0)
5185 TAKE_PTR(st);
5186
5187 return 0;
5188}
5189
2cb9c68c
AV
5190static int shallow_join_strv(char ***ret, char **a, char **b) {
5191 _cleanup_free_ char **joined = NULL;
5192 char **iter;
5193
5194 assert(ret);
5195
5196 joined = new(char*, strv_length(a) + strv_length(b) + 1);
5197 if (!joined)
5198 return log_oom();
5199
5200 iter = joined;
5201
5202 STRV_FOREACH(i, a)
5203 *(iter++) = *i;
5204
5205 STRV_FOREACH(i, b)
5206 if (!strv_contains(joined, *i))
5207 *(iter++) = *i;
5208
5209 *iter = NULL;
5210
5211 *ret = TAKE_PTR(joined);
5212 return 0;
5213}
5214
e57b7020
DDM
5215static int make_copy_files_denylist(
5216 Context *context,
5217 const Partition *p,
5218 const char *source,
5219 const char *target,
5220 Hashmap **ret) {
5221
5222 _cleanup_hashmap_free_ Hashmap *denylist = NULL;
2cb9c68c 5223 _cleanup_free_ char **override_exclude_src = NULL, **override_exclude_tgt = NULL;
e57b7020
DDM
5224 int r;
5225
5226 assert(context);
5227 assert(p);
5228 assert(source);
5229 assert(target);
5230 assert(ret);
5231
5232 /* Always exclude the top level APIVFS and temporary directories since the contents of these
5233 * directories are almost certainly not intended to end up in an image. */
5234
5235 NULSTR_FOREACH(s, APIVFS_TMP_DIRS_NULSTR) {
5236 r = add_exclude_path(s, &denylist, DENY_CONTENTS);
5237 if (r < 0)
5238 return r;
5239 }
5240
5241 /* Add the user configured excludes. */
5242
2cb9c68c
AV
5243 if (p->suppressing) {
5244 r = shallow_join_strv(&override_exclude_src,
5245 p->exclude_files_source,
5246 p->suppressing->exclude_files_source);
5247 if (r < 0)
5248 return r;
5249 r = shallow_join_strv(&override_exclude_tgt,
5250 p->exclude_files_target,
5251 p->suppressing->exclude_files_target);
5252 if (r < 0)
5253 return r;
5254 }
5255
5256 STRV_FOREACH(e, override_exclude_src ?: p->exclude_files_source) {
e57b7020
DDM
5257 r = add_exclude_path(*e, &denylist, endswith(*e, "/") ? DENY_CONTENTS : DENY_INODE);
5258 if (r < 0)
5259 return r;
5260 }
5261
2cb9c68c 5262 STRV_FOREACH(e, override_exclude_tgt ?: p->exclude_files_target) {
600bf76c
DDM
5263 _cleanup_free_ char *path = NULL;
5264
5265 const char *s = path_startswith(*e, target);
5266 if (!s)
5267 continue;
5268
5269 path = path_join(source, s);
5270 if (!path)
5271 return log_oom();
5272
5273 r = add_exclude_path(path, &denylist, endswith(*e, "/") ? DENY_CONTENTS : DENY_INODE);
5274 if (r < 0)
5275 return r;
5276 }
5277
e57b7020
DDM
5278 /* If we're populating a root partition, we don't want any files to end up under the APIVFS mount
5279 * points. While we already exclude <source>/proc, users could still do something such as
5280 * "CopyFiles=/abc:/". Now, if /abc has a proc subdirectory with files in it, those will end up in
5281 * the top level proc directory in the root partition, which we want to avoid. To deal with these
5282 * cases, whenever we're populating a root partition and the target of CopyFiles= is the root
5283 * directory of the root partition, we exclude all directories under the source that are named after
5284 * APIVFS directories or named after mount points of other partitions that are also going to be part
5285 * of the image. */
5286
5287 if (p->type.designator == PARTITION_ROOT && empty_or_root(target)) {
5288 LIST_FOREACH(partitions, q, context->partitions) {
5289 if (q->type.designator == PARTITION_ROOT)
5290 continue;
5291
5292 const char *sources = gpt_partition_type_mountpoint_nulstr(q->type);
5293 if (!sources)
5294 continue;
5295
5296 NULSTR_FOREACH(s, sources) {
5297 _cleanup_free_ char *path = NULL;
5298
5299 /* Exclude only the children of partition mount points so that the nested
5300 * partition mount point itself still ends up in the upper partition. */
5301
5302 path = path_join(source, s);
5303 if (!path)
5304 return -ENOMEM;
5305
5306 r = add_exclude_path(path, &denylist, DENY_CONTENTS);
5307 if (r < 0)
5308 return r;
5309 }
5310 }
5311
5312 NULSTR_FOREACH(s, APIVFS_TMP_DIRS_NULSTR) {
5313 _cleanup_free_ char *path = NULL;
5314
5315 path = path_join(source, s);
5316 if (!path)
5317 return -ENOMEM;
5318
5319 r = add_exclude_path(path, &denylist, DENY_CONTENTS);
5320 if (r < 0)
5321 return r;
5322 }
5323 }
5324
5325 *ret = TAKE_PTR(denylist);
5326 return 0;
5327}
5328
440f805c
DDM
5329static int add_subvolume_path(const char *path, Set **subvolumes) {
5330 _cleanup_free_ struct stat *st = NULL;
5331 int r;
5332
5333 assert(path);
5334 assert(subvolumes);
5335
5336 st = new(struct stat, 1);
5337 if (!st)
5338 return log_oom();
5339
607343a1 5340 r = chase_and_stat(path, arg_copy_source, CHASE_PREFIX_ROOT, NULL, st);
440f805c
DDM
5341 if (r == -ENOENT)
5342 return 0;
5343 if (r < 0)
607343a1 5344 return log_error_errno(r, "Failed to stat source file '%s/%s': %m", strempty(arg_copy_source), path);
440f805c 5345
fc159b2f 5346 r = set_ensure_consume(subvolumes, &inode_hash_ops, TAKE_PTR(st));
440f805c
DDM
5347 if (r < 0)
5348 return log_oom();
440f805c
DDM
5349
5350 return 0;
5351}
5352
562881c1
DDM
5353static int make_subvolumes_strv(const Partition *p, char ***ret) {
5354 _cleanup_strv_free_ char **subvolumes = NULL;
5355 Subvolume *subvolume;
2cb9c68c 5356 int r;
562881c1
DDM
5357
5358 assert(p);
5359 assert(ret);
5360
5361 ORDERED_HASHMAP_FOREACH(subvolume, p->subvolumes)
5362 if (strv_extend(&subvolumes, subvolume->path) < 0)
5363 return log_oom();
5364
2cb9c68c 5365 if (p->suppressing) {
a2c8652a 5366 char **suppressing;
2cb9c68c
AV
5367
5368 r = make_subvolumes_strv(p->suppressing, &suppressing);
5369 if (r < 0)
5370 return r;
5371
a2c8652a 5372 r = strv_extend_strv_consume(&subvolumes, suppressing, /* filter_duplicates= */ true);
2cb9c68c
AV
5373 if (r < 0)
5374 return log_oom();
5375 }
5376
562881c1
DDM
5377 *ret = TAKE_PTR(subvolumes);
5378 return 0;
5379}
5380
440f805c 5381static int make_subvolumes_set(
440f805c
DDM
5382 const Partition *p,
5383 const char *source,
5384 const char *target,
5385 Set **ret) {
562881c1 5386
2cb9c68c 5387 _cleanup_strv_free_ char **paths = NULL;
440f805c
DDM
5388 _cleanup_set_free_ Set *subvolumes = NULL;
5389 int r;
5390
440f805c
DDM
5391 assert(p);
5392 assert(target);
5393 assert(ret);
5394
2cb9c68c
AV
5395 r = make_subvolumes_strv(p, &paths);
5396 if (r < 0)
5397 return r;
5398
5399 STRV_FOREACH(subvolume, paths) {
440f805c
DDM
5400 _cleanup_free_ char *path = NULL;
5401
2cb9c68c 5402 const char *s = path_startswith(*subvolume, target);
440f805c
DDM
5403 if (!s)
5404 continue;
5405
5406 path = path_join(source, s);
5407 if (!path)
5408 return log_oom();
5409
5410 r = add_subvolume_path(path, &subvolumes);
5411 if (r < 0)
5412 return r;
5413 }
5414
5415 *ret = TAKE_PTR(subvolumes);
5416 return 0;
5417}
5418
34c3d574
MS
5419static usec_t epoch_or_infinity(void) {
5420 static usec_t cache;
5421 static bool cached = false;
5422 uint64_t epoch;
5423 int r;
5424
5425 if (cached)
5426 return cache;
5427
5428 r = secure_getenv_uint64("SOURCE_DATE_EPOCH", &epoch);
5429 if (r >= 0) {
5430 if (epoch <= UINT64_MAX / USEC_PER_SEC) { /* Overflow check */
5431 cached = true;
5432 return (cache = epoch * USEC_PER_SEC);
5433 }
5434 r = -ERANGE;
5435 }
5436 if (r != -ENXIO)
5437 log_debug_errno(r, "Failed to parse $SOURCE_DATE_EPOCH, ignoring: %m");
5438
5439 cached = true;
5440 return (cache = USEC_INFINITY);
5441}
5442
e57b7020 5443static int do_copy_files(Context *context, Partition *p, const char *root) {
562881c1 5444 _cleanup_strv_free_ char **subvolumes = NULL;
2cb9c68c 5445 _cleanup_free_ char **override_copy_files = NULL;
8a794850
LP
5446 int r;
5447
5448 assert(p);
92cd7e7c 5449 assert(root);
8a794850 5450
562881c1
DDM
5451 r = make_subvolumes_strv(p, &subvolumes);
5452 if (r < 0)
5453 return r;
5454
2cb9c68c
AV
5455 if (p->suppressing) {
5456 r = shallow_join_strv(&override_copy_files, p->copy_files, p->suppressing->copy_files);
5457 if (r < 0)
5458 return r;
5459 }
5460
3e451460
DDM
5461 /* copy_tree_at() automatically copies the permissions of source directories to target directories if
5462 * it created them. However, the root directory is created by us, so we have to manually take care
5463 * that it is initialized. We use the first source directory targeting "/" as the metadata source for
5464 * the root directory. */
2cb9c68c 5465 STRV_FOREACH_PAIR(source, target, override_copy_files ?: p->copy_files) {
3e451460
DDM
5466 _cleanup_close_ int rfd = -EBADF, sfd = -EBADF;
5467
5468 if (!path_equal(*target, "/"))
5469 continue;
5470
5471 rfd = open(root, O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
5472 if (rfd < 0)
9f08d7f7 5473 return -errno;
3e451460 5474
607343a1 5475 sfd = chase_and_open(*source, arg_copy_source, CHASE_PREFIX_ROOT, O_PATH|O_DIRECTORY|O_CLOEXEC|O_NOCTTY, NULL);
3e451460 5476 if (sfd < 0)
607343a1 5477 return log_error_errno(sfd, "Failed to open source file '%s%s': %m", strempty(arg_copy_source), *source);
3e451460
DDM
5478
5479 (void) copy_xattr(sfd, NULL, rfd, NULL, COPY_ALL_XATTRS);
5480 (void) copy_access(sfd, rfd);
5481 (void) copy_times(sfd, rfd, 0);
5482
5483 break;
5484 }
5485
2cb9c68c 5486 STRV_FOREACH_PAIR(source, target, override_copy_files ?: p->copy_files) {
e57b7020 5487 _cleanup_hashmap_free_ Hashmap *denylist = NULL;
440f805c 5488 _cleanup_set_free_ Set *subvolumes_by_source_inode = NULL;
254d1313 5489 _cleanup_close_ int sfd = -EBADF, pfd = -EBADF, tfd = -EBADF;
34c3d574 5490 usec_t ts = epoch_or_infinity();
8a794850 5491
e57b7020
DDM
5492 r = make_copy_files_denylist(context, p, *source, *target, &denylist);
5493 if (r < 0)
5494 return r;
5495
9023266a 5496 r = make_subvolumes_set(p, *source, *target, &subvolumes_by_source_inode);
440f805c
DDM
5497 if (r < 0)
5498 return r;
5499
607343a1 5500 sfd = chase_and_open(*source, arg_copy_source, CHASE_PREFIX_ROOT, O_CLOEXEC|O_NOCTTY, NULL);
489b0f51
LP
5501 if (sfd == -ENOENT) {
5502 log_notice_errno(sfd, "Failed to open source file '%s%s', skipping: %m", strempty(arg_copy_source), *source);
5503 continue;
5504 }
8a794850 5505 if (sfd < 0)
607343a1 5506 return log_error_errno(sfd, "Failed to open source file '%s%s': %m", strempty(arg_copy_source), *source);
8a794850
LP
5507
5508 r = fd_verify_regular(sfd);
5509 if (r < 0) {
5510 if (r != -EISDIR)
5511 return log_error_errno(r, "Failed to check type of source file '%s': %m", *source);
5512
5513 /* We are looking at a directory */
f461a28d 5514 tfd = chase_and_open(*target, root, CHASE_PREFIX_ROOT, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL);
8a794850 5515 if (tfd < 0) {
f21a3a82
LP
5516 _cleanup_free_ char *dn = NULL, *fn = NULL;
5517
8a794850
LP
5518 if (tfd != -ENOENT)
5519 return log_error_errno(tfd, "Failed to open target directory '%s': %m", *target);
5520
f21a3a82
LP
5521 r = path_extract_filename(*target, &fn);
5522 if (r < 0)
5523 return log_error_errno(r, "Failed to extract filename from '%s': %m", *target);
5524
5525 r = path_extract_directory(*target, &dn);
5526 if (r < 0)
5527 return log_error_errno(r, "Failed to extract directory from '%s': %m", *target);
5528
562881c1 5529 r = mkdir_p_root_full(root, dn, UID_INVALID, GID_INVALID, 0755, ts, subvolumes);
8a794850
LP
5530 if (r < 0)
5531 return log_error_errno(r, "Failed to create parent directory '%s': %m", dn);
5532
f461a28d 5533 pfd = chase_and_open(dn, root, CHASE_PREFIX_ROOT, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL);
8a794850
LP
5534 if (pfd < 0)
5535 return log_error_errno(pfd, "Failed to open parent directory of target: %m");
5536
652d9040
LP
5537 r = copy_tree_at(
5538 sfd, ".",
6020d00d 5539 pfd, fn,
ff1b55ff 5540 UID_INVALID, GID_INVALID,
d850a544 5541 COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE|COPY_RESTORE_DIRECTORY_TIMESTAMPS,
440f805c 5542 denylist, subvolumes_by_source_inode);
8a794850 5543 } else
652d9040
LP
5544 r = copy_tree_at(
5545 sfd, ".",
5546 tfd, ".",
ff1b55ff 5547 UID_INVALID, GID_INVALID,
d850a544 5548 COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN|COPY_TRUNCATE|COPY_RESTORE_DIRECTORY_TIMESTAMPS,
440f805c 5549 denylist, subvolumes_by_source_inode);
8a794850 5550 if (r < 0)
cf2ed23c 5551 return log_error_errno(r, "Failed to copy '%s%s' to '%s%s': %m",
607343a1 5552 strempty(arg_copy_source), *source, strempty(root), *target);
8a794850 5553 } else {
f21a3a82
LP
5554 _cleanup_free_ char *dn = NULL, *fn = NULL;
5555
8a794850
LP
5556 /* We are looking at a regular file */
5557
f21a3a82
LP
5558 r = path_extract_filename(*target, &fn);
5559 if (r == -EADDRNOTAVAIL || r == O_DIRECTORY)
5560 return log_error_errno(SYNTHETIC_ERRNO(EISDIR),
5561 "Target path '%s' refers to a directory, but source path '%s' refers to regular file, can't copy.", *target, *source);
5562 if (r < 0)
5563 return log_error_errno(r, "Failed to extract filename from '%s': %m", *target);
5564
5565 r = path_extract_directory(*target, &dn);
5566 if (r < 0)
5567 return log_error_errno(r, "Failed to extract directory from '%s': %m", *target);
5568
562881c1 5569 r = mkdir_p_root_full(root, dn, UID_INVALID, GID_INVALID, 0755, ts, subvolumes);
8a794850
LP
5570 if (r < 0)
5571 return log_error_errno(r, "Failed to create parent directory: %m");
5572
f461a28d 5573 pfd = chase_and_open(dn, root, CHASE_PREFIX_ROOT, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL);
8a794850 5574 if (pfd < 0)
a0ff9971 5575 return log_error_errno(pfd, "Failed to open parent directory of target: %m");
8a794850 5576
e2819067 5577 tfd = openat(pfd, fn, O_CREAT|O_EXCL|O_WRONLY|O_CLOEXEC, 0700);
8a794850
LP
5578 if (tfd < 0)
5579 return log_error_errno(errno, "Failed to create target file '%s': %m", *target);
5580
ab645e0a 5581 r = copy_bytes(sfd, tfd, UINT64_MAX, COPY_REFLINK|COPY_HOLES|COPY_SIGINT|COPY_TRUNCATE);
8a794850 5582 if (r < 0)
607343a1 5583 return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_copy_source), *target);
8a794850 5584
c17cfe6e 5585 (void) copy_xattr(sfd, NULL, tfd, NULL, COPY_ALL_XATTRS);
8a794850
LP
5586 (void) copy_access(sfd, tfd);
5587 (void) copy_times(sfd, tfd, 0);
34c3d574 5588
8aed8587
MY
5589 if (ts != USEC_INFINITY) {
5590 struct timespec tspec;
5591 timespec_store(&tspec, ts);
34c3d574 5592
c876cbdd 5593 if (futimens(pfd, (const struct timespec[2]) { TIMESPEC_OMIT, tspec }) < 0)
8aed8587
MY
5594 return -errno;
5595 }
8a794850
LP
5596 }
5597 }
5598
5599 return 0;
5600}
5601
49fb6e97 5602static int do_make_directories(Partition *p, const char *root) {
562881c1 5603 _cleanup_strv_free_ char **subvolumes = NULL;
2cb9c68c 5604 _cleanup_free_ char **override_dirs = NULL;
d83d8048
LP
5605 int r;
5606
5607 assert(p);
92cd7e7c 5608 assert(root);
d83d8048 5609
562881c1
DDM
5610 r = make_subvolumes_strv(p, &subvolumes);
5611 if (r < 0)
5612 return r;
5613
2cb9c68c
AV
5614 if (p->suppressing) {
5615 r = shallow_join_strv(&override_dirs, p->make_directories, p->suppressing->make_directories);
5616 if (r < 0)
5617 return r;
5618 }
5619
5620 STRV_FOREACH(d, override_dirs ?: p->make_directories) {
562881c1 5621 r = mkdir_p_root_full(root, *d, UID_INVALID, GID_INVALID, 0755, epoch_or_infinity(), subvolumes);
d83d8048
LP
5622 if (r < 0)
5623 return log_error_errno(r, "Failed to create directory '%s' in file system: %m", *d);
5624 }
5625
5626 return 0;
5627}
5628
c64ddefd
DDM
5629static int do_make_symlinks(Partition *p, const char *root) {
5630 int r;
5631
5632 assert(p);
5633 assert(root);
5634
5635 STRV_FOREACH_PAIR(path, target, p->make_symlinks) {
5636 _cleanup_close_ int parent_fd = -EBADF;
5637 _cleanup_free_ char *f = NULL;
5638
5639 r = chase(*path, root, CHASE_PREFIX_ROOT|CHASE_PARENT|CHASE_EXTRACT_FILENAME, &f, &parent_fd);
5640 if (r < 0)
5641 return log_error_errno(r, "Failed to resolve %s in %s", *path, root);
5642
5643 if (symlinkat(*target, parent_fd, f) < 0)
5644 return log_error_errno(errno, "Failed to create symlink at %s to %s: %m", *path, *target);
5645 }
5646
5647 return 0;
5648}
5649
562881c1
DDM
5650static int make_subvolumes_read_only(Partition *p, const char *root) {
5651 _cleanup_free_ char *path = NULL;
5652 Subvolume *subvolume;
5653 int r;
5654
5655 ORDERED_HASHMAP_FOREACH(subvolume, p->subvolumes) {
5656 if (!FLAGS_SET(subvolume->flags, SUBVOLUME_RO))
5657 continue;
5658
5659 path = path_join(root, subvolume->path);
5660 if (!path)
5661 return log_oom();
5662
5663 r = btrfs_subvol_set_read_only(path, true);
5664 if (r < 0)
5665 return log_error_errno(r, "Failed to make subvolume '%s' read-only: %m", subvolume->path);
5666 }
5667
2cb9c68c
AV
5668 if (p->suppressing) {
5669 r = make_subvolumes_read_only(p->suppressing, root);
5670 if (r < 0)
5671 return r;
5672 }
5673
562881c1
DDM
5674 return 0;
5675}
5676
3799fa80
DDM
5677static int set_default_subvolume(Partition *p, const char *root) {
5678 _cleanup_free_ char *path = NULL;
5679 int r;
5680
5681 assert(p);
5682 assert(root);
5683
5684 if (!p->default_subvolume)
5685 return 0;
5686
5687 path = path_join(root, p->default_subvolume);
5688 if (!path)
5689 return log_oom();
5690
5691 r = btrfs_subvol_make_default(path);
5692 if (r < 0)
5693 return log_error_errno(r, "Failed to make '%s' the default subvolume: %m", p->default_subvolume);
5694
5695 return 0;
5696}
5697
e57b7020 5698static int partition_populate_directory(Context *context, Partition *p, char **ret) {
95bfd3cd 5699 _cleanup_(rm_rf_physical_and_freep) char *root = NULL;
1eb86ddd 5700 const char *vt;
95bfd3cd
DDM
5701 int r;
5702
e59678b2 5703 assert(ret);
95bfd3cd 5704
e0258ac8 5705 log_info("Preparing to populate %s filesystem.", p->format);
b24bfd6e 5706
1eb86ddd
DDM
5707 r = var_tmp_dir(&vt);
5708 if (r < 0)
5709 return log_error_errno(r, "Could not determine temporary directory: %m");
95bfd3cd 5710
1eb86ddd
DDM
5711 r = tempfn_random_child(vt, "repart", &root);
5712 if (r < 0)
5713 return log_error_errno(r, "Failed to generate temporary directory: %m");
5714
5715 r = mkdir(root, 0755);
5716 if (r < 0)
5717 return log_error_errno(errno, "Failed to create temporary directory: %m");
0b34f351 5718
e57b7020 5719 r = do_copy_files(context, p, root);
95bfd3cd
DDM
5720 if (r < 0)
5721 return r;
5722
49fb6e97 5723 r = do_make_directories(p, root);
95bfd3cd
DDM
5724 if (r < 0)
5725 return r;
5726
c64ddefd
DDM
5727 r = do_make_symlinks(p, root);
5728 if (r < 0)
5729 return r;
5730
e0258ac8 5731 log_info("Ready to populate %s filesystem.", p->format);
b24bfd6e 5732
e59678b2 5733 *ret = TAKE_PTR(root);
95bfd3cd
DDM
5734 return 0;
5735}
5736
e57b7020 5737static int partition_populate_filesystem(Context *context, Partition *p, const char *node) {
8a794850
LP
5738 int r;
5739
5740 assert(p);
5741 assert(node);
5742
b24bfd6e 5743 log_info("Populating %s filesystem.", p->format);
8a794850
LP
5744
5745 /* We copy in a child process, since we have to mount the fs for that, and we don't want that fs to
5746 * appear in the host namespace. Hence we fork a child that has its own file system namespace and
5747 * detached mount propagation. */
5748
e9ccae31 5749 r = safe_fork("(sd-copy)", FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, NULL);
8a794850
LP
5750 if (r < 0)
5751 return r;
5752 if (r == 0) {
5753 static const char fs[] = "/run/systemd/mount-root";
5754 /* This is a child process with its own mount namespace and propagation to host turned off */
5755
5756 r = mkdir_p(fs, 0700);
5757 if (r < 0) {
5758 log_error_errno(r, "Failed to create mount point: %m");
5759 _exit(EXIT_FAILURE);
5760 }
5761
511a8cfe 5762 if (mount_nofollow_verbose(LOG_ERR, node, fs, p->format, MS_NOATIME|MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL) < 0)
8a794850
LP
5763 _exit(EXIT_FAILURE);
5764
e57b7020 5765 if (do_copy_files(context, p, fs) < 0)
8a794850
LP
5766 _exit(EXIT_FAILURE);
5767
49fb6e97 5768 if (do_make_directories(p, fs) < 0)
d83d8048
LP
5769 _exit(EXIT_FAILURE);
5770
c64ddefd
DDM
5771 if (do_make_symlinks(p, fs) < 0)
5772 _exit(EXIT_FAILURE);
5773
562881c1
DDM
5774 if (make_subvolumes_read_only(p, fs) < 0)
5775 _exit(EXIT_FAILURE);
5776
3799fa80
DDM
5777 if (set_default_subvolume(p, fs) < 0)
5778 _exit(EXIT_FAILURE);
5779
8a794850
LP
5780 r = syncfs_path(AT_FDCWD, fs);
5781 if (r < 0) {
5782 log_error_errno(r, "Failed to synchronize written files: %m");
5783 _exit(EXIT_FAILURE);
5784 }
5785
5786 _exit(EXIT_SUCCESS);
5787 }
5788
b24bfd6e 5789 log_info("Successfully populated %s filesystem.", p->format);
8a794850
LP
5790 return 0;
5791}
5792
eca3d07d 5793static int finalize_extra_mkfs_options(const Partition *p, const char *root, char ***ret) {
eca3d07d 5794 _cleanup_strv_free_ char **sv = NULL;
562881c1 5795 int r;
eca3d07d
DDM
5796
5797 assert(p);
5798 assert(ret);
5799
5800 r = mkfs_options_from_env("REPART", p->format, &sv);
5801 if (r < 0)
5802 return log_error_errno(r,
5803 "Failed to determine mkfs command line options for '%s': %m",
5804 p->format);
5805
eca3d07d
DDM
5806 *ret = TAKE_PTR(sv);
5807 return 0;
5808}
5809
53171c04 5810static int context_mkfs(Context *context) {
a64769d6 5811 int r;
53171c04
LP
5812
5813 assert(context);
5814
5815 /* Make a file system */
5816
5817 LIST_FOREACH(partitions, p, context->partitions) {
e59678b2 5818 _cleanup_(rm_rf_physical_and_freep) char *root = NULL;
a64769d6 5819 _cleanup_(partition_target_freep) PartitionTarget *t = NULL;
4b8ce14f 5820 _cleanup_strv_free_ char **extra_mkfs_options = NULL;
53171c04
LP
5821
5822 if (p->dropped)
5823 continue;
5824
5825 if (PARTITION_EXISTS(p)) /* Never format existing partitions */
5826 continue;
5827
5828 if (!p->format)
5829 continue;
5830
a954b427 5831 /* Minimized partitions will use the copy blocks logic so skip those here. */
c4a87b76
DDM
5832 if (p->copy_blocks_fd >= 0)
5833 continue;
5834
c74d50ff 5835 if (partition_type_defer(&p->type))
81d1098b
DDM
5836 continue;
5837
53171c04
LP
5838 assert(p->offset != UINT64_MAX);
5839 assert(p->new_size != UINT64_MAX);
48a09a8f 5840 assert(p->new_size >= (p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0));
53171c04 5841
a954b427 5842 /* If we're doing encryption, keep free space at the end which is required
a64769d6
DDM
5843 * for cryptsetup's offline encryption. */
5844 r = partition_target_prepare(context, p,
5845 p->new_size - (p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0),
5846 /*need_path=*/ true,
5847 &t);
53171c04 5848 if (r < 0)
a64769d6 5849 return r;
53171c04 5850
a09ae915
DDM
5851 if (p->encrypt != ENCRYPT_OFF && t->loop) {
5852 r = partition_target_grow(t, p->new_size);
5853 if (r < 0)
5854 return r;
5855
5856 r = partition_encrypt(context, p, t, /* offline = */ false);
5857 if (r < 0)
5858 return log_error_errno(r, "Failed to encrypt device: %m");
5859 }
5860
53171c04
LP
5861 log_info("Formatting future partition %" PRIu64 ".", p->partno);
5862
d3201eb4
DDM
5863 /* If we're not writing to a loop device or if we're populating a read-only filesystem, we
5864 * have to populate using the filesystem's mkfs's --root (or equivalent) option. To do that,
5865 * we need to set up the final directory tree beforehand. */
5866
5867 if (partition_needs_populate(p) && (!t->loop || fstype_is_ro(p->format))) {
5868 if (!mkfs_supports_root_option(p->format))
5869 return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
5870 "Loop device access is required to populate %s filesystems.",
5871 p->format);
95bfd3cd 5872
e57b7020 5873 r = partition_populate_directory(context, p, &root);
143c3c08
DDM
5874 if (r < 0)
5875 return r;
5876 }
95bfd3cd 5877
eca3d07d 5878 r = finalize_extra_mkfs_options(p, root, &extra_mkfs_options);
4b8ce14f 5879 if (r < 0)
eca3d07d 5880 return r;
4b8ce14f 5881
a64769d6 5882 r = make_filesystem(partition_target_path(t), p->format, strempty(p->new_label), root,
93f125a6 5883 p->fs_uuid, arg_discard, /* quiet = */ false,
27cacec9
DDM
5884 context->fs_sector_size, p->compression, p->compression_level,
5885 extra_mkfs_options);
48a09a8f 5886 if (r < 0)
53171c04
LP
5887 return r;
5888
0122c7d0 5889 /* The mkfs binary we invoked might have removed our temporary file when we're not operating
a954b427
ZJS
5890 * on a loop device, so open the file again to make sure our file descriptor points to actual
5891 * new file. */
0122c7d0
DDM
5892
5893 if (t->fd >= 0 && t->path && !t->loop) {
5894 safe_close(t->fd);
5895 t->fd = open(t->path, O_RDWR|O_CLOEXEC);
5896 if (t->fd < 0)
5897 return log_error_errno(errno, "Failed to reopen temporary file: %m");
5898 }
5899
53171c04
LP
5900 log_info("Successfully formatted future partition %" PRIu64 ".", p->partno);
5901
d3201eb4
DDM
5902 /* If we're writing to a loop device, we can now mount the empty filesystem and populate it. */
5903 if (partition_needs_populate(p) && !root) {
5904 assert(t->loop);
5905
a09ae915 5906 r = partition_populate_filesystem(context, p, partition_target_path(t));
48a09a8f 5907 if (r < 0)
143c3c08 5908 return r;
b9df3536
LP
5909 }
5910
a09ae915 5911 if (p->encrypt != ENCRYPT_OFF && !t->loop) {
a64769d6 5912 r = partition_target_grow(t, p->new_size);
b9df3536 5913 if (r < 0)
a64769d6 5914 return r;
b9df3536 5915
a09ae915 5916 r = partition_encrypt(context, p, t, /* offline = */ true);
48a09a8f
DDM
5917 if (r < 0)
5918 return log_error_errno(r, "Failed to encrypt device: %m");
b9df3536 5919 }
8a794850 5920
48a09a8f
DDM
5921 /* Note that we always sync explicitly here, since mkfs.fat doesn't do that on its own, and
5922 * if we don't sync before detaching a block device the in-flight sectors possibly won't hit
5923 * the disk. */
5924
a64769d6 5925 r = partition_target_sync(context, p, t);
53171c04 5926 if (r < 0)
a64769d6 5927 return r;
b5b7879a 5928
c74d50ff 5929 if (p->siblings[VERITY_HASH] && !partition_type_defer(&p->siblings[VERITY_HASH]->type)) {
a64769d6 5930 r = partition_format_verity_hash(context, p->siblings[VERITY_HASH],
13bde177 5931 /* node = */ NULL, partition_target_path(t));
2b392d86
DDM
5932 if (r < 0)
5933 return r;
b5b7879a 5934 }
4ecd39c5 5935
c74d50ff 5936 if (p->siblings[VERITY_SIG] && !partition_type_defer(&p->siblings[VERITY_SIG]->type)) {
4ecd39c5
DDM
5937 r = partition_format_verity_sig(context, p->siblings[VERITY_SIG]);
5938 if (r < 0)
5939 return r;
5940 }
b5b7879a
DDM
5941 }
5942
5943 return 0;
5944}
5945
b456191d
DDM
5946static int parse_x509_certificate(const char *certificate, size_t certificate_size, X509 **ret) {
5947#if HAVE_OPENSSL
5948 _cleanup_(X509_freep) X509 *cert = NULL;
5949 _cleanup_(BIO_freep) BIO *cb = NULL;
5950
5951 assert(certificate);
5952 assert(certificate_size > 0);
5953 assert(ret);
5954
5955 cb = BIO_new_mem_buf(certificate, certificate_size);
5956 if (!cb)
5957 return log_oom();
5958
5959 cert = PEM_read_bio_X509(cb, NULL, NULL, NULL);
5960 if (!cert)
5961 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse X.509 certificate: %s",
5962 ERR_error_string(ERR_get_error(), NULL));
5963
5964 if (ret)
5965 *ret = TAKE_PTR(cert);
5966
5967 return 0;
5968#else
968d232d 5969 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot parse X509 certificate.");
b456191d
DDM
5970#endif
5971}
5972
5973static int parse_private_key(const char *key, size_t key_size, EVP_PKEY **ret) {
5974#if HAVE_OPENSSL
5975 _cleanup_(BIO_freep) BIO *kb = NULL;
5976 _cleanup_(EVP_PKEY_freep) EVP_PKEY *pk = NULL;
5977
5978 assert(key);
5979 assert(key_size > 0);
5980 assert(ret);
5981
5982 kb = BIO_new_mem_buf(key, key_size);
5983 if (!kb)
5984 return log_oom();
5985
5986 pk = PEM_read_bio_PrivateKey(kb, NULL, NULL, NULL);
5987 if (!pk)
5988 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse PEM private key: %s",
5989 ERR_error_string(ERR_get_error(), NULL));
5990
5991 if (ret)
5992 *ret = TAKE_PTR(pk);
5993
5994 return 0;
5995#else
968d232d 5996 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot parse private key.");
b456191d
DDM
5997#endif
5998}
5999
e594a3b1
LP
6000static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *ret) {
6001 struct {
6002 sd_id128_t type_uuid;
6003 uint64_t counter;
695cfd53 6004 } _packed_ plaintext = {};
e594a3b1 6005 union {
ade99252 6006 uint8_t md[SHA256_DIGEST_SIZE];
e594a3b1
LP
6007 sd_id128_t id;
6008 } result;
6009
6010 uint64_t k = 0;
e594a3b1
LP
6011 int r;
6012
6013 assert(context);
6014 assert(p);
6015 assert(ret);
6016
6017 /* Calculate a good UUID for the indicated partition. We want a certain degree of reproducibility,
6018 * hence we won't generate the UUIDs randomly. Instead we use a cryptographic hash (precisely:
6019 * HMAC-SHA256) to derive them from a single seed. The seed is generally the machine ID of the
6020 * installation we are processing, but if random behaviour is desired can be random, too. We use the
6021 * seed value as key for the HMAC (since the machine ID is something we generally don't want to leak)
6022 * and the partition type as plaintext. The partition type is suffixed with a counter (only for the
6023 * second and later partition of the same type) if we have more than one partition of the same
6024 * time. Or in other words:
6025 *
6026 * With:
6027 * SEED := /etc/machine-id
6028 *
6029 * If first partition instance of type TYPE_UUID:
6030 * PARTITION_UUID := HMAC-SHA256(SEED, TYPE_UUID)
6031 *
6032 * For all later partition instances of type TYPE_UUID with INSTANCE being the LE64 encoded instance number:
6033 * PARTITION_UUID := HMAC-SHA256(SEED, TYPE_UUID || INSTANCE)
6034 */
6035
6036 LIST_FOREACH(partitions, q, context->partitions) {
6037 if (p == q)
6038 break;
6039
22e932f4 6040 if (!sd_id128_equal(p->type.uuid, q->type.uuid))
e594a3b1
LP
6041 continue;
6042
6043 k++;
6044 }
6045
22e932f4 6046 plaintext.type_uuid = p->type.uuid;
e594a3b1
LP
6047 plaintext.counter = htole64(k);
6048
ade99252
KK
6049 hmac_sha256(context->seed.bytes, sizeof(context->seed.bytes),
6050 &plaintext,
6051 k == 0 ? sizeof(sd_id128_t) : sizeof(plaintext),
6052 result.md);
e594a3b1
LP
6053
6054 /* Take the first half, mark it as v4 UUID */
6055 assert_cc(sizeof(result.md) == sizeof(result.id) * 2);
6056 result.id = id128_make_v4_uuid(result.id);
6057
6058 /* Ensure this partition UUID is actually unique, and there's no remaining partition from an earlier run? */
6059 LIST_FOREACH(partitions, q, context->partitions) {
6060 if (p == q)
6061 continue;
6062
580f48cc 6063 if (sd_id128_in_set(result.id, q->current_uuid, q->new_uuid)) {
da1af43d 6064 log_warning("Partition UUID calculated from seed for partition %" PRIu64 " already used, reverting to randomized UUID.", p->partno);
e594a3b1
LP
6065
6066 r = sd_id128_randomize(&result.id);
6067 if (r < 0)
6068 return log_error_errno(r, "Failed to generate randomized UUID: %m");
6069
6070 break;
6071 }
6072 }
6073
6074 *ret = result.id;
6075 return 0;
6076}
6077
6078static int partition_acquire_label(Context *context, Partition *p, char **ret) {
6079 _cleanup_free_ char *label = NULL;
6080 const char *prefix;
6081 unsigned k = 1;
6082
6083 assert(context);
6084 assert(p);
6085 assert(ret);
6086
22e932f4 6087 prefix = gpt_partition_type_uuid_to_string(p->type.uuid);
e594a3b1
LP
6088 if (!prefix)
6089 prefix = "linux";
6090
6091 for (;;) {
6092 const char *ll = label ?: prefix;
6093 bool retry = false;
e594a3b1
LP
6094
6095 LIST_FOREACH(partitions, q, context->partitions) {
6096 if (p == q)
6097 break;
6098
6099 if (streq_ptr(ll, q->current_label) ||
6100 streq_ptr(ll, q->new_label)) {
6101 retry = true;
6102 break;
6103 }
6104 }
6105
6106 if (!retry)
6107 break;
6108
6109 label = mfree(label);
e594a3b1
LP
6110 if (asprintf(&label, "%s-%u", prefix, ++k) < 0)
6111 return log_oom();
6112 }
6113
6114 if (!label) {
6115 label = strdup(prefix);
6116 if (!label)
6117 return log_oom();
6118 }
6119
6120 *ret = TAKE_PTR(label);
6121 return 0;
6122}
6123
6124static int context_acquire_partition_uuids_and_labels(Context *context) {
e594a3b1
LP
6125 int r;
6126
6127 assert(context);
6128
6129 LIST_FOREACH(partitions, p, context->partitions) {
15cad3a2
DDM
6130 sd_id128_t uuid;
6131
e594a3b1
LP
6132 /* Never touch foreign partitions */
6133 if (PARTITION_IS_FOREIGN(p)) {
6134 p->new_uuid = p->current_uuid;
6135
6136 if (p->current_label) {
78eee6ce
LP
6137 r = free_and_strdup_warn(&p->new_label, strempty(p->current_label));
6138 if (r < 0)
6139 return r;
e594a3b1
LP
6140 }
6141
6142 continue;
6143 }
6144
6145 if (!sd_id128_is_null(p->current_uuid))
15cad3a2
DDM
6146 p->new_uuid = uuid = p->current_uuid; /* Never change initialized UUIDs */
6147 else if (p->new_uuid_is_set)
6148 uuid = p->new_uuid;
6149 else {
12963533 6150 /* Not explicitly set by user! */
15cad3a2 6151 r = partition_acquire_uuid(context, p, &uuid);
e594a3b1
LP
6152 if (r < 0)
6153 return r;
11749b61 6154
15cad3a2
DDM
6155 /* The final verity hash/data UUIDs can only be determined after formatting the
6156 * verity hash partition. However, we still want to use the generated partition UUID
6157 * to derive other UUIDs to keep things unique and reproducible, so we always
6158 * generate a UUID if none is set, but we only use it as the actual partition UUID if
6159 * verity is not configured. */
6160 if (!IN_SET(p->verity, VERITY_DATA, VERITY_HASH)) {
6161 p->new_uuid = uuid;
6162 p->new_uuid_is_set = true;
6163 }
e594a3b1
LP
6164 }
6165
8bbbdfd7
DDM
6166 /* Calculate the UUID for the file system as HMAC-SHA256 of the string "file-system-uuid",
6167 * keyed off the partition UUID. */
15cad3a2 6168 r = derive_uuid(uuid, "file-system-uuid", &p->fs_uuid);
8bbbdfd7
DDM
6169 if (r < 0)
6170 return r;
6171
15cad3a2
DDM
6172 if (p->encrypt != ENCRYPT_OFF) {
6173 r = derive_uuid(uuid, "luks-uuid", &p->luks_uuid);
6174 if (r < 0)
6175 return r;
6176 }
6177
81e04781
VH
6178 /* Derive the verity salt and verity superblock UUID from the seed to keep them reproducible */
6179 if (p->verity == VERITY_HASH) {
6180 derive_salt(context->seed, "verity-salt", p->verity_salt);
6181
6182 r = derive_uuid(context->seed, "verity-uuid", &p->verity_uuid);
6183 if (r < 0)
6184 return log_error_errno(r, "Failed to acquire verity uuid: %m");
6185 }
6186
e594a3b1 6187 if (!isempty(p->current_label)) {
78eee6ce
LP
6188 /* never change initialized labels */
6189 r = free_and_strdup_warn(&p->new_label, p->current_label);
6190 if (r < 0)
6191 return r;
12963533
TH
6192 } else if (!p->new_label) {
6193 /* Not explicitly set by user! */
6194
e594a3b1
LP
6195 r = partition_acquire_label(context, p, &p->new_label);
6196 if (r < 0)
6197 return r;
6198 }
6199 }
6200
6201 return 0;
6202}
6203
e73309c5
LP
6204static int set_gpt_flags(struct fdisk_partition *q, uint64_t flags) {
6205 _cleanup_free_ char *a = NULL;
6206
6207 for (unsigned i = 0; i < sizeof(flags) * 8; i++) {
6208 uint64_t bit = UINT64_C(1) << i;
6209 char buf[DECIMAL_STR_MAX(unsigned)+1];
6210
6211 if (!FLAGS_SET(flags, bit))
6212 continue;
6213
6214 xsprintf(buf, "%u", i);
6215 if (!strextend_with_separator(&a, ",", buf))
6216 return -ENOMEM;
6217 }
6218
6219 return fdisk_partition_set_attrs(q, a);
6220}
6221
1c41c1dc
LP
6222static uint64_t partition_merge_flags(Partition *p) {
6223 uint64_t f;
6224
6225 assert(p);
6226
6227 f = p->gpt_flags;
6228
ff0771bf 6229 if (p->no_auto >= 0) {
22e932f4 6230 if (gpt_partition_type_knows_no_auto(p->type))
92e72028 6231 SET_FLAG(f, SD_GPT_FLAG_NO_AUTO, p->no_auto);
ff0771bf 6232 else {
b7416360 6233 char buffer[SD_ID128_UUID_STRING_MAX];
ff0771bf
LP
6234 log_warning("Configured NoAuto=%s for partition type '%s' that doesn't support it, ignoring.",
6235 yes_no(p->no_auto),
22e932f4 6236 gpt_partition_type_uuid_to_string_harder(p->type.uuid, buffer));
ff0771bf
LP
6237 }
6238 }
6239
1c41c1dc 6240 if (p->read_only >= 0) {
22e932f4 6241 if (gpt_partition_type_knows_read_only(p->type))
92e72028 6242 SET_FLAG(f, SD_GPT_FLAG_READ_ONLY, p->read_only);
1c41c1dc 6243 else {
b7416360 6244 char buffer[SD_ID128_UUID_STRING_MAX];
1c41c1dc
LP
6245 log_warning("Configured ReadOnly=%s for partition type '%s' that doesn't support it, ignoring.",
6246 yes_no(p->read_only),
22e932f4 6247 gpt_partition_type_uuid_to_string_harder(p->type.uuid, buffer));
1c41c1dc
LP
6248 }
6249 }
6250
6251 if (p->growfs >= 0) {
22e932f4 6252 if (gpt_partition_type_knows_growfs(p->type))
92e72028 6253 SET_FLAG(f, SD_GPT_FLAG_GROWFS, p->growfs);
1c41c1dc 6254 else {
b7416360 6255 char buffer[SD_ID128_UUID_STRING_MAX];
1c41c1dc
LP
6256 log_warning("Configured GrowFileSystem=%s for partition type '%s' that doesn't support it, ignoring.",
6257 yes_no(p->growfs),
22e932f4 6258 gpt_partition_type_uuid_to_string_harder(p->type.uuid, buffer));
1c41c1dc
LP
6259 }
6260 }
6261
6262 return f;
6263}
6264
f28d4f42 6265static int context_mangle_partitions(Context *context) {
f28d4f42 6266 int r;
e594a3b1
LP
6267
6268 assert(context);
6269
e594a3b1
LP
6270 LIST_FOREACH(partitions, p, context->partitions) {
6271 if (p->dropped)
6272 continue;
6273
c74d50ff 6274 if (partition_type_defer(&p->type))
81d1098b
DDM
6275 continue;
6276
e594a3b1
LP
6277 assert(p->new_size != UINT64_MAX);
6278 assert(p->offset != UINT64_MAX);
6279 assert(p->partno != UINT64_MAX);
6280
6281 if (PARTITION_EXISTS(p)) {
6282 bool changed = false;
6283
6284 assert(p->current_partition);
6285
6286 if (p->new_size != p->current_size) {
6287 assert(p->new_size >= p->current_size);
994b3031 6288 assert(p->new_size % context->sector_size == 0);
e594a3b1
LP
6289
6290 r = fdisk_partition_size_explicit(p->current_partition, true);
6291 if (r < 0)
6292 return log_error_errno(r, "Failed to enable explicit sizing: %m");
6293
994b3031 6294 r = fdisk_partition_set_size(p->current_partition, p->new_size / context->sector_size);
e594a3b1
LP
6295 if (r < 0)
6296 return log_error_errno(r, "Failed to grow partition: %m");
6297
6298 log_info("Growing existing partition %" PRIu64 ".", p->partno);
6299 changed = true;
6300 }
6301
6302 if (!sd_id128_equal(p->new_uuid, p->current_uuid)) {
b7416360 6303 r = fdisk_partition_set_uuid(p->current_partition, SD_ID128_TO_UUID_STRING(p->new_uuid));
e594a3b1
LP
6304 if (r < 0)
6305 return log_error_errno(r, "Failed to set partition UUID: %m");
6306
6307 log_info("Initializing UUID of existing partition %" PRIu64 ".", p->partno);
6308 changed = true;
6309 }
6310
6311 if (!streq_ptr(p->new_label, p->current_label)) {
be9ce018 6312 r = fdisk_partition_set_name(p->current_partition, strempty(p->new_label));
e594a3b1
LP
6313 if (r < 0)
6314 return log_error_errno(r, "Failed to set partition label: %m");
6315
6316 log_info("Setting partition label of existing partition %" PRIu64 ".", p->partno);
6317 changed = true;
6318 }
6319
6320 if (changed) {
6321 assert(!PARTITION_IS_FOREIGN(p)); /* never touch foreign partitions */
6322
6323 r = fdisk_set_partition(context->fdisk_context, p->partno, p->current_partition);
6324 if (r < 0)
6325 return log_error_errno(r, "Failed to update partition: %m");
6326 }
6327 } else {
6328 _cleanup_(fdisk_unref_partitionp) struct fdisk_partition *q = NULL;
6329 _cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *t = NULL;
e594a3b1
LP
6330
6331 assert(!p->new_partition);
994b3031
LP
6332 assert(p->offset % context->sector_size == 0);
6333 assert(p->new_size % context->sector_size == 0);
be9ce018 6334 assert(p->new_label);
e594a3b1
LP
6335
6336 t = fdisk_new_parttype();
6337 if (!t)
6338 return log_oom();
6339
22e932f4 6340 r = fdisk_parttype_set_typestr(t, SD_ID128_TO_UUID_STRING(p->type.uuid));
e594a3b1
LP
6341 if (r < 0)
6342 return log_error_errno(r, "Failed to initialize partition type: %m");
6343
6344 q = fdisk_new_partition();
6345 if (!q)
6346 return log_oom();
6347
6348 r = fdisk_partition_set_type(q, t);
6349 if (r < 0)
6350 return log_error_errno(r, "Failed to set partition type: %m");
6351
6352 r = fdisk_partition_size_explicit(q, true);
6353 if (r < 0)
6354 return log_error_errno(r, "Failed to enable explicit sizing: %m");
6355
994b3031 6356 r = fdisk_partition_set_start(q, p->offset / context->sector_size);
e594a3b1
LP
6357 if (r < 0)
6358 return log_error_errno(r, "Failed to position partition: %m");
6359
994b3031 6360 r = fdisk_partition_set_size(q, p->new_size / context->sector_size);
e594a3b1
LP
6361 if (r < 0)
6362 return log_error_errno(r, "Failed to grow partition: %m");
6363
6364 r = fdisk_partition_set_partno(q, p->partno);
6365 if (r < 0)
6366 return log_error_errno(r, "Failed to set partition number: %m");
6367
b7416360 6368 r = fdisk_partition_set_uuid(q, SD_ID128_TO_UUID_STRING(p->new_uuid));
e594a3b1
LP
6369 if (r < 0)
6370 return log_error_errno(r, "Failed to set partition UUID: %m");
6371
be9ce018 6372 r = fdisk_partition_set_name(q, strempty(p->new_label));
e594a3b1
LP
6373 if (r < 0)
6374 return log_error_errno(r, "Failed to set partition label: %m");
6375
ff0771bf 6376 /* Merge the no auto + read only + growfs setting with the literal flags, and set them for the partition */
1c41c1dc 6377 r = set_gpt_flags(q, partition_merge_flags(p));
e73309c5
LP
6378 if (r < 0)
6379 return log_error_errno(r, "Failed to set GPT partition flags: %m");
6380
5b5109e2 6381 log_info("Adding new partition %" PRIu64 " to partition table.", p->partno);
e594a3b1
LP
6382
6383 r = fdisk_add_partition(context->fdisk_context, q, NULL);
6384 if (r < 0)
6385 return log_error_errno(r, "Failed to add partition: %m");
6386
6387 assert(!p->new_partition);
6388 p->new_partition = TAKE_PTR(q);
6389 }
6390 }
6391
f28d4f42
LP
6392 return 0;
6393}
6394
62108348 6395static int split_name_printf(Partition *p, char **ret) {
4cee8333
DDM
6396 assert(p);
6397
6398 const Specifier table[] = {
22e932f4
DDM
6399 { 't', specifier_string, GPT_PARTITION_TYPE_UUID_TO_STRING_HARDER(p->type.uuid) },
6400 { 'T', specifier_id128, &p->type.uuid },
4cee8333
DDM
6401 { 'U', specifier_id128, &p->new_uuid },
6402 { 'n', specifier_uint64, &p->partno },
6403
6404 COMMON_SYSTEM_SPECIFIERS,
6405 {}
6406 };
6407
62108348 6408 return specifier_printf(p->split_name_format, NAME_MAX, table, arg_root, p, ret);
4cee8333
DDM
6409}
6410
62108348
DDM
6411static int split_node(const char *node, char **ret_base, char **ret_ext) {
6412 _cleanup_free_ char *base = NULL, *ext = NULL;
6413 char *e;
6414 int r;
6415
6416 assert(node);
6417 assert(ret_base);
6418 assert(ret_ext);
6419
6420 r = path_extract_filename(node, &base);
6421 if (r == O_DIRECTORY || r == -EADDRNOTAVAIL)
6422 return log_error_errno(r, "Device node %s cannot be a directory", node);
6423 if (r < 0)
6424 return log_error_errno(r, "Failed to extract filename from %s: %m", node);
6425
6426 e = endswith(base, ".raw");
6427 if (e) {
6428 ext = strdup(e);
6429 if (!ext)
6430 return log_oom();
6431
6432 *e = 0;
6433 }
6434
6435 *ret_base = TAKE_PTR(base);
6436 *ret_ext = TAKE_PTR(ext);
6437
6438 return 0;
4cee8333
DDM
6439}
6440
6441static int split_name_resolve(Context *context) {
62108348 6442 _cleanup_free_ char *parent = NULL, *base = NULL, *ext = NULL;
4cee8333
DDM
6443 int r;
6444
62108348 6445 assert(context);
62108348 6446
cc751c75 6447 r = path_extract_directory(context->node, &parent);
62108348 6448 if (r < 0 && r != -EDESTADDRREQ)
cc751c75 6449 return log_error_errno(r, "Failed to extract directory from %s: %m", context->node);
62108348 6450
cc751c75 6451 r = split_node(context->node, &base, &ext);
62108348
DDM
6452 if (r < 0)
6453 return r;
6454
4cee8333 6455 LIST_FOREACH(partitions, p, context->partitions) {
62108348
DDM
6456 _cleanup_free_ char *resolved = NULL;
6457
4cee8333
DDM
6458 if (p->dropped)
6459 continue;
6460
6461 if (!p->split_name_format)
6462 continue;
6463
62108348 6464 r = split_name_printf(p, &resolved);
4cee8333
DDM
6465 if (r < 0)
6466 return log_error_errno(r, "Failed to resolve specifiers in %s: %m", p->split_name_format);
62108348
DDM
6467
6468 if (parent)
6469 p->split_path = strjoin(parent, "/", base, ".", resolved, ext);
6470 else
6471 p->split_path = strjoin(base, ".", resolved, ext);
6472 if (!p->split_path)
6473 return log_oom();
4cee8333
DDM
6474 }
6475
6476 LIST_FOREACH(partitions, p, context->partitions) {
62108348 6477 if (!p->split_path)
4cee8333
DDM
6478 continue;
6479
6480 LIST_FOREACH(partitions, q, context->partitions) {
6481 if (p == q)
6482 continue;
6483
62108348 6484 if (!q->split_path)
4cee8333
DDM
6485 continue;
6486
62108348 6487 if (!streq(p->split_path, q->split_path))
4cee8333
DDM
6488 continue;
6489
6490 return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
4e494e6a 6491 "%s and %s have the same resolved split name \"%s\", refusing.",
62108348 6492 p->definition_path, q->definition_path, p->split_path);
4cee8333
DDM
6493 }
6494 }
6495
6496 return 0;
6497}
6498
4cee8333 6499static int context_split(Context *context) {
b9c0b6c0 6500 unsigned attrs = 0;
254d1313 6501 int fd = -EBADF, r;
4cee8333
DDM
6502
6503 if (!arg_split)
6504 return 0;
6505
6506 assert(context);
4cee8333
DDM
6507
6508 /* We can't do resolution earlier because the partition UUIDs for verity partitions are only filled
6509 * in after they've been generated. */
6510
6511 r = split_name_resolve(context);
6512 if (r < 0)
6513 return r;
6514
4cee8333 6515 LIST_FOREACH(partitions, p, context->partitions) {
254d1313 6516 _cleanup_close_ int fdt = -EBADF;
4cee8333
DDM
6517
6518 if (p->dropped)
6519 continue;
6520
62108348 6521 if (!p->split_path)
4cee8333
DDM
6522 continue;
6523
c74d50ff 6524 if (partition_type_defer(&p->type))
81d1098b
DDM
6525 continue;
6526
b9c0b6c0
DDM
6527 if (fd < 0) {
6528 assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
6529
6530 r = read_attr_fd(fd, &attrs);
6531 if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r))
6532 return log_error_errno(r, "Failed to read file attributes of %s: %m", arg_node);
6533 }
6534
6535 fdt = xopenat_full(
6536 AT_FDCWD,
6537 p->split_path,
6538 O_WRONLY|O_NOCTTY|O_CLOEXEC|O_NOFOLLOW|O_CREAT|O_EXCL,
6539 attrs & FS_NOCOW_FL ? XO_NOCOW : 0,
6540 0666);
4cee8333 6541 if (fdt < 0)
62108348 6542 return log_error_errno(fdt, "Failed to open split partition file %s: %m", p->split_path);
4cee8333 6543
4cee8333
DDM
6544 if (lseek(fd, p->offset, SEEK_SET) < 0)
6545 return log_error_errno(errno, "Failed to seek to partition offset: %m");
6546
ab645e0a 6547 r = copy_bytes(fd, fdt, p->new_size, COPY_REFLINK|COPY_HOLES|COPY_TRUNCATE);
4cee8333 6548 if (r < 0)
62108348 6549 return log_error_errno(r, "Failed to copy to split partition %s: %m", p->split_path);
4cee8333
DDM
6550 }
6551
6552 return 0;
6553}
6554
cc751c75 6555static int context_write_partition_table(Context *context) {
f28d4f42
LP
6556 _cleanup_(fdisk_unref_tablep) struct fdisk_table *original_table = NULL;
6557 int capable, r;
6558
6559 assert(context);
6560
cc751c75 6561 if (!context->from_scratch && !context_changed(context)) {
f28d4f42
LP
6562 log_info("No changes.");
6563 return 0;
6564 }
6565
6566 if (arg_dry_run) {
6567 log_notice("Refusing to repartition, please re-run with --dry-run=no.");
6568 return 0;
6569 }
6570
acdf6bcf 6571 log_info("Applying changes to %s.", context->node);
f28d4f42 6572
19c58989
LP
6573 if (context->from_scratch && arg_empty != EMPTY_CREATE) {
6574 /* Erase everything if we operate from scratch, except if the image was just created anyway, and thus is definitely empty. */
81873a6b
LP
6575 r = context_wipe_range(context, 0, context->total);
6576 if (r < 0)
6577 return r;
6578
6579 log_info("Wiped block device.");
6580
0dce448b
LB
6581 if (arg_discard) {
6582 r = context_discard_range(context, 0, context->total);
6583 if (r == -EOPNOTSUPP)
6584 log_info("Storage does not support discard, not discarding entire block device data.");
6585 else if (r < 0)
6586 return log_error_errno(r, "Failed to discard entire block device: %m");
6587 else if (r > 0)
6588 log_info("Discarded entire block device.");
6589 }
f28d4f42
LP
6590 }
6591
6592 r = fdisk_get_partitions(context->fdisk_context, &original_table);
6593 if (r < 0)
6594 return log_error_errno(r, "Failed to acquire partition table: %m");
6595
6596 /* Wipe fs signatures and discard sectors where the new partitions are going to be placed and in the
6597 * gaps between partitions, just to be sure. */
cc751c75 6598 r = context_wipe_and_discard(context);
f28d4f42
LP
6599 if (r < 0)
6600 return r;
6601
6602 r = context_copy_blocks(context);
6603 if (r < 0)
6604 return r;
6605
6606 r = context_mkfs(context);
6607 if (r < 0)
6608 return r;
6609
6610 r = context_mangle_partitions(context);
6611 if (r < 0)
6612 return r;
6613
e594a3b1
LP
6614 log_info("Writing new partition table.");
6615
6616 r = fdisk_write_disklabel(context->fdisk_context);
6617 if (r < 0)
6618 return log_error_errno(r, "Failed to write partition table: %m");
6619
bff5d2fd 6620 capable = blockdev_partscan_enabled_fd(fdisk_get_devfd(context->fdisk_context));
9a1deb85
LP
6621 if (capable == -ENOTBLK)
6622 log_debug("Not telling kernel to reread partition table, since we are not operating on a block device.");
6623 else if (capable < 0)
911ba624 6624 return log_error_errno(capable, "Failed to check if block device supports partition scanning: %m");
9a1deb85 6625 else if (capable > 0) {
e594a3b1
LP
6626 log_info("Telling kernel to reread partition table.");
6627
cc751c75 6628 if (context->from_scratch)
e594a3b1
LP
6629 r = fdisk_reread_partition_table(context->fdisk_context);
6630 else
6631 r = fdisk_reread_changes(context->fdisk_context, original_table);
6632 if (r < 0)
6633 return log_error_errno(r, "Failed to reread partition table: %m");
6634 } else
6635 log_notice("Not telling kernel to reread partition table, because selected image does not support kernel partition block devices.");
6636
6637 log_info("All done.");
6638
6639 return 0;
6640}
6641
6642static int context_read_seed(Context *context, const char *root) {
6643 int r;
6644
6645 assert(context);
6646
6647 if (!sd_id128_is_null(context->seed))
6648 return 0;
6649
6650 if (!arg_randomize) {
5ee37b70
YW
6651 r = id128_get_machine(root, &context->seed);
6652 if (r >= 0)
6653 return 0;
e594a3b1 6654
5ee37b70
YW
6655 if (!ERRNO_IS_MACHINE_ID_UNSET(r))
6656 return log_error_errno(r, "Failed to parse machine ID of image: %m");
74e795ee 6657
5ee37b70 6658 log_info("No machine ID set, using randomized partition UUIDs.");
e594a3b1
LP
6659 }
6660
6661 r = sd_id128_randomize(&context->seed);
6662 if (r < 0)
6663 return log_error_errno(r, "Failed to generate randomized seed: %m");
6664
6665 return 0;
6666}
6667
cc751c75 6668static int context_factory_reset(Context *context) {
e594a3b1
LP
6669 size_t n = 0;
6670 int r;
6671
6672 assert(context);
6673
6674 if (arg_factory_reset <= 0)
6675 return 0;
6676
cc751c75 6677 if (context->from_scratch) /* Nothing to reset if we start from scratch */
e594a3b1
LP
6678 return 0;
6679
6680 if (arg_dry_run) {
6681 log_notice("Refusing to factory reset, please re-run with --dry-run=no.");
6682 return 0;
6683 }
6684
6685 log_info("Applying factory reset.");
6686
6687 LIST_FOREACH(partitions, p, context->partitions) {
6688
6689 if (!p->factory_reset || !PARTITION_EXISTS(p))
6690 continue;
6691
6692 assert(p->partno != UINT64_MAX);
6693
6694 log_info("Removing partition %" PRIu64 " for factory reset.", p->partno);
6695
6696 r = fdisk_delete_partition(context->fdisk_context, p->partno);
6697 if (r < 0)
6698 return log_error_errno(r, "Failed to remove partition %" PRIu64 ": %m", p->partno);
6699
6700 n++;
6701 }
6702
6703 if (n == 0) {
6704 log_info("Factory reset requested, but no partitions to delete found.");
6705 return 0;
6706 }
6707
6708 r = fdisk_write_disklabel(context->fdisk_context);
6709 if (r < 0)
6710 return log_error_errno(r, "Failed to write disk label: %m");
6711
6712 log_info("Successfully deleted %zu partitions.", n);
6713 return 1;
6714}
6715
6716static int context_can_factory_reset(Context *context) {
e594a3b1
LP
6717 assert(context);
6718
6719 LIST_FOREACH(partitions, p, context->partitions)
6720 if (p->factory_reset && PARTITION_EXISTS(p))
6721 return true;
6722
6723 return false;
6724}
6725
5c08da58
LP
6726static int resolve_copy_blocks_auto_candidate(
6727 dev_t partition_devno,
22e932f4 6728 GptPartitionType partition_type,
5c08da58
LP
6729 dev_t restrict_devno,
6730 sd_id128_t *ret_uuid) {
6731
6732 _cleanup_(blkid_free_probep) blkid_probe b = NULL;
254d1313 6733 _cleanup_close_ int fd = -EBADF;
ca822829
YW
6734 _cleanup_free_ char *p = NULL;
6735 const char *pttype, *t;
5c08da58
LP
6736 sd_id128_t pt_parsed, u;
6737 blkid_partition pp;
6738 dev_t whole_devno;
6739 blkid_partlist pl;
5c08da58
LP
6740 int r;
6741
6742 /* Checks if the specified partition has the specified GPT type UUID, and is located on the specified
6743 * 'restrict_devno' device. The type check is particularly relevant if we have Verity volume which is
6744 * backed by two separate partitions: the data and the hash partitions, and we need to find the right
6745 * one of the two. */
6746
6747 r = block_get_whole_disk(partition_devno, &whole_devno);
6748 if (r < 0)
6749 return log_error_errno(
6750 r,
6751 "Unable to determine containing block device of partition %u:%u: %m",
6752 major(partition_devno), minor(partition_devno));
6753
6754 if (restrict_devno != (dev_t) -1 &&
6755 restrict_devno != whole_devno)
6756 return log_error_errno(
6757 SYNTHETIC_ERRNO(EPERM),
6758 "Partition %u:%u is located outside of block device %u:%u, refusing.",
6759 major(partition_devno), minor(partition_devno),
6760 major(restrict_devno), minor(restrict_devno));
6761
ca822829 6762 fd = r = device_open_from_devnum(S_IFBLK, whole_devno, O_RDONLY|O_CLOEXEC|O_NONBLOCK, &p);
5c08da58 6763 if (r < 0)
ca822829
YW
6764 return log_error_errno(r, "Failed to open block device " DEVNUM_FORMAT_STR ": %m",
6765 DEVNUM_FORMAT_VAL(whole_devno));
5c08da58
LP
6766
6767 b = blkid_new_probe();
6768 if (!b)
6769 return log_oom();
6770
6771 errno = 0;
6772 r = blkid_probe_set_device(b, fd, 0, 0);
6773 if (r != 0)
6774 return log_error_errno(errno_or_else(ENOMEM), "Failed to open block device '%s': %m", p);
6775
6776 (void) blkid_probe_enable_partitions(b, 1);
6777 (void) blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
6778
6779 errno = 0;
6780 r = blkid_do_safeprobe(b);
2e3944b8
LP
6781 if (r == _BLKID_SAFEPROBE_ERROR)
6782 return log_error_errno(errno_or_else(EIO), "Unable to probe for partition table of '%s': %m", p);
6783 if (IN_SET(r, _BLKID_SAFEPROBE_AMBIGUOUS, _BLKID_SAFEPROBE_NOT_FOUND)) {
5c08da58
LP
6784 log_debug("Didn't find partition table on block device '%s'.", p);
6785 return false;
6786 }
2e3944b8
LP
6787
6788 assert(r == _BLKID_SAFEPROBE_FOUND);
5c08da58
LP
6789
6790 (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
6791 if (!streq_ptr(pttype, "gpt")) {
6792 log_debug("Didn't find a GPT partition table on '%s'.", p);
6793 return false;
6794 }
6795
6796 errno = 0;
6797 pl = blkid_probe_get_partitions(b);
6798 if (!pl)
6799 return log_error_errno(errno_or_else(EIO), "Unable read partition table of '%s': %m", p);
5c08da58
LP
6800
6801 pp = blkid_partlist_devno_to_partition(pl, partition_devno);
6802 if (!pp) {
6803 log_debug("Partition %u:%u has no matching partition table entry on '%s'.",
6804 major(partition_devno), minor(partition_devno), p);
6805 return false;
6806 }
6807
6808 t = blkid_partition_get_type_string(pp);
6809 if (isempty(t)) {
6810 log_debug("Partition %u:%u has no type on '%s'.",
6811 major(partition_devno), minor(partition_devno), p);
6812 return false;
6813 }
6814
6815 r = sd_id128_from_string(t, &pt_parsed);
6816 if (r < 0) {
6817 log_debug_errno(r, "Failed to parse partition type \"%s\": %m", t);
6818 return false;
6819 }
6820
22e932f4 6821 if (!sd_id128_equal(pt_parsed, partition_type.uuid)) {
5c08da58
LP
6822 log_debug("Partition %u:%u has non-matching partition type " SD_ID128_FORMAT_STR " (needed: " SD_ID128_FORMAT_STR "), ignoring.",
6823 major(partition_devno), minor(partition_devno),
22e932f4 6824 SD_ID128_FORMAT_VAL(pt_parsed), SD_ID128_FORMAT_VAL(partition_type.uuid));
5c08da58
LP
6825 return false;
6826 }
6827
e3b9a5ff
LP
6828 r = blkid_partition_get_uuid_id128(pp, &u);
6829 if (r == -ENXIO) {
6830 log_debug_errno(r, "Partition " DEVNUM_FORMAT_STR " has no UUID.", DEVNUM_FORMAT_VAL(partition_devno));
5c08da58
LP
6831 return false;
6832 }
5c08da58 6833 if (r < 0) {
e3b9a5ff 6834 log_debug_errno(r, "Failed to read partition UUID of " DEVNUM_FORMAT_STR ": %m", DEVNUM_FORMAT_VAL(partition_devno));
5c08da58
LP
6835 return false;
6836 }
6837
e3b9a5ff
LP
6838 log_debug("Automatically found partition " DEVNUM_FORMAT_STR " of right type " SD_ID128_FORMAT_STR ".",
6839 DEVNUM_FORMAT_VAL(partition_devno),
5c08da58
LP
6840 SD_ID128_FORMAT_VAL(pt_parsed));
6841
6842 if (ret_uuid)
6843 *ret_uuid = u;
6844
6845 return true;
6846}
6847
6848static int find_backing_devno(
6849 const char *path,
6850 const char *root,
6851 dev_t *ret) {
6852
6853 _cleanup_free_ char *resolved = NULL;
6854 int r;
6855
6856 assert(path);
6857
f461a28d 6858 r = chase(path, root, CHASE_PREFIX_ROOT, &resolved, NULL);
5c08da58
LP
6859 if (r < 0)
6860 return r;
6861
b409aacb 6862 r = path_is_mount_point(resolved);
5c08da58
LP
6863 if (r < 0)
6864 return r;
6865 if (r == 0) /* Not a mount point, then it's not a partition of its own, let's not automatically use it. */
6866 return -ENOENT;
6867
6868 r = get_block_device(resolved, ret);
6869 if (r < 0)
6870 return r;
6871 if (r == 0) /* Not backed by physical file system, we can't use this */
6872 return -ENOENT;
6873
6874 return 0;
6875}
6876
6877static int resolve_copy_blocks_auto(
22e932f4 6878 GptPartitionType type,
5c08da58
LP
6879 const char *root,
6880 dev_t restrict_devno,
1a037ba2 6881 dev_t *ret_devno,
5c08da58
LP
6882 sd_id128_t *ret_uuid) {
6883
6884 const char *try1 = NULL, *try2 = NULL;
6885 char p[SYS_BLOCK_PATH_MAX("/slaves")];
5d2a48da 6886 _cleanup_closedir_ DIR *d = NULL;
5c08da58
LP
6887 sd_id128_t found_uuid = SD_ID128_NULL;
6888 dev_t devno, found = 0;
6889 int r;
6890
86320e62
DDM
6891 /* Enforce some security restrictions: CopyBlocks=auto should not be an avenue to get outside of the
6892 * --root=/--image= confinement. Specifically, refuse CopyBlocks= in combination with --root= at all,
6893 * and restrict block device references in the --image= case to loopback block device we set up.
6894 *
6895 * restrict_devno contain the dev_t of the loop back device we operate on in case of --image=, and
6896 * thus declares which device (and its partition subdevices) we shall limit access to. If
6897 * restrict_devno is zero no device probing access shall be allowed at all (used for --root=) and if
6898 * it is (dev_t) -1 then free access shall be allowed (if neither switch is used). */
6899
6900 if (restrict_devno == 0)
6901 return log_error_errno(SYNTHETIC_ERRNO(EPERM),
6902 "Automatic discovery of backing block devices not permitted in --root= mode, refusing.");
6903
5c08da58
LP
6904 /* Handles CopyBlocks=auto, and finds the right source partition to copy from. We look for matching
6905 * partitions in the host, using the appropriate directory as key and ensuring that the partition
6906 * type matches. */
6907
22e932f4 6908 if (type.designator == PARTITION_ROOT)
5c08da58 6909 try1 = "/";
22e932f4 6910 else if (type.designator == PARTITION_USR)
5c08da58 6911 try1 = "/usr/";
22e932f4 6912 else if (type.designator == PARTITION_ROOT_VERITY)
5c08da58 6913 try1 = "/";
22e932f4 6914 else if (type.designator == PARTITION_USR_VERITY)
5c08da58 6915 try1 = "/usr/";
22e932f4 6916 else if (type.designator == PARTITION_ESP) {
5c08da58
LP
6917 try1 = "/efi/";
6918 try2 = "/boot/";
22e932f4 6919 } else if (type.designator == PARTITION_XBOOTLDR)
5c08da58
LP
6920 try1 = "/boot/";
6921 else
6922 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
6923 "Partition type " SD_ID128_FORMAT_STR " not supported from automatic source block device discovery.",
22e932f4 6924 SD_ID128_FORMAT_VAL(type.uuid));
5c08da58
LP
6925
6926 r = find_backing_devno(try1, root, &devno);
6927 if (r == -ENOENT && try2)
6928 r = find_backing_devno(try2, root, &devno);
6929 if (r < 0)
6930 return log_error_errno(r, "Failed to resolve automatic CopyBlocks= path for partition type " SD_ID128_FORMAT_STR ", sorry: %m",
22e932f4 6931 SD_ID128_FORMAT_VAL(type.uuid));
5c08da58
LP
6932
6933 xsprintf_sys_block_path(p, "/slaves", devno);
6934 d = opendir(p);
6935 if (d) {
6936 struct dirent *de;
6937
6938 for (;;) {
6939 _cleanup_free_ char *q = NULL, *t = NULL;
6940 sd_id128_t u;
6941 dev_t sl;
6942
6943 errno = 0;
6944 de = readdir_no_dot(d);
6945 if (!de) {
6946 if (errno != 0)
6947 return log_error_errno(errno, "Failed to read directory '%s': %m", p);
6948
6949 break;
6950 }
6951
6952 if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
6953 continue;
6954
6955 q = path_join(p, de->d_name, "/dev");
6956 if (!q)
6957 return log_oom();
6958
6959 r = read_one_line_file(q, &t);
6960 if (r < 0)
6961 return log_error_errno(r, "Failed to read %s: %m", q);
6962
7176f06c 6963 r = parse_devnum(t, &sl);
5c08da58
LP
6964 if (r < 0) {
6965 log_debug_errno(r, "Failed to parse %s, ignoring: %m", q);
6966 continue;
6967 }
6968 if (major(sl) == 0) {
375ffdba 6969 log_debug("Device backing %s is special, ignoring.", q);
5c08da58
LP
6970 continue;
6971 }
6972
22e932f4 6973 r = resolve_copy_blocks_auto_candidate(sl, type, restrict_devno, &u);
5c08da58
LP
6974 if (r < 0)
6975 return r;
6976 if (r > 0) {
6977 /* We found a matching one! */
6978 if (found != 0)
6979 return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
6980 "Multiple matching partitions found, refusing.");
6981
6982 found = sl;
6983 found_uuid = u;
6984 }
6985 }
6986 } else if (errno != ENOENT)
6987 return log_error_errno(errno, "Failed open %s: %m", p);
6988 else {
22e932f4 6989 r = resolve_copy_blocks_auto_candidate(devno, type, restrict_devno, &found_uuid);
5c08da58
LP
6990 if (r < 0)
6991 return r;
6992 if (r > 0)
6993 found = devno;
6994 }
6995
86320e62
DDM
6996 if (found == 0)
6997 return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
6998 "Unable to automatically discover suitable partition to copy blocks from.");
6999
1a037ba2
YW
7000 if (ret_devno)
7001 *ret_devno = found;
5c08da58
LP
7002
7003 if (ret_uuid)
7004 *ret_uuid = found_uuid;
7005
86320e62 7006 return 0;
5c08da58
LP
7007}
7008
7009static int context_open_copy_block_paths(
7010 Context *context,
5c08da58
LP
7011 dev_t restrict_devno) {
7012
757bc2e4
LP
7013 int r;
7014
7015 assert(context);
7016
7017 LIST_FOREACH(partitions, p, context->partitions) {
254d1313 7018 _cleanup_close_ int source_fd = -EBADF;
5c08da58
LP
7019 _cleanup_free_ char *opened = NULL;
7020 sd_id128_t uuid = SD_ID128_NULL;
757bc2e4
LP
7021 uint64_t size;
7022 struct stat st;
7023
5eef7047
DDM
7024 if (p->copy_blocks_fd >= 0)
7025 continue;
7026
757bc2e4
LP
7027 assert(p->copy_blocks_size == UINT64_MAX);
7028
7029 if (PARTITION_EXISTS(p)) /* Never copy over partitions that already exist! */
7030 continue;
7031
5c08da58 7032 if (p->copy_blocks_path) {
757bc2e4 7033
f461a28d 7034 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
7035 if (source_fd < 0)
7036 return log_error_errno(source_fd, "Failed to open '%s': %m", p->copy_blocks_path);
757bc2e4 7037
5c08da58
LP
7038 if (fstat(source_fd, &st) < 0)
7039 return log_error_errno(errno, "Failed to stat block copy file '%s': %m", opened);
7040
7041 if (!S_ISREG(st.st_mode) && restrict_devno != (dev_t) -1)
7042 return log_error_errno(SYNTHETIC_ERRNO(EPERM),
7043 "Copying from block device node is not permitted in --image=/--root= mode, refusing.");
7044
7045 } else if (p->copy_blocks_auto) {
03f5e501 7046 dev_t devno = 0; /* Fake initialization to appease gcc. */
5c08da58 7047
22e932f4 7048 r = resolve_copy_blocks_auto(p->type, p->copy_blocks_root, restrict_devno, &devno, &uuid);
5c08da58
LP
7049 if (r < 0)
7050 return r;
03f5e501 7051 assert(devno != 0);
5c08da58 7052
ca822829 7053 source_fd = r = device_open_from_devnum(S_IFBLK, devno, O_RDONLY|O_CLOEXEC|O_NONBLOCK, &opened);
1a037ba2 7054 if (r < 0)
ca822829
YW
7055 return log_error_errno(r, "Failed to open automatically determined source block copy device " DEVNUM_FORMAT_STR ": %m",
7056 DEVNUM_FORMAT_VAL(devno));
5c08da58
LP
7057
7058 if (fstat(source_fd, &st) < 0)
7059 return log_error_errno(errno, "Failed to stat block copy file '%s': %m", opened);
1a037ba2 7060 } else
5c08da58 7061 continue;
757bc2e4
LP
7062
7063 if (S_ISDIR(st.st_mode)) {
ca822829
YW
7064 _cleanup_free_ char *bdev = NULL;
7065 dev_t devt;
757bc2e4
LP
7066
7067 /* If the file is a directory, automatically find the backing block device */
7068
7069 if (major(st.st_dev) != 0)
ca822829 7070 devt = st.st_dev;
757bc2e4 7071 else {
757bc2e4 7072 /* Special support for btrfs */
757bc2e4 7073 r = btrfs_get_block_device_fd(source_fd, &devt);
67f0ac8c 7074 if (r == -EUCLEAN)
5c08da58 7075 return btrfs_log_dev_root(LOG_ERR, r, opened);
757bc2e4 7076 if (r < 0)
5c08da58 7077 return log_error_errno(r, "Unable to determine backing block device of '%s': %m", opened);
757bc2e4 7078 }
757bc2e4
LP
7079
7080 safe_close(source_fd);
7081
ca822829
YW
7082 source_fd = r = device_open_from_devnum(S_IFBLK, devt, O_RDONLY|O_CLOEXEC|O_NONBLOCK, &bdev);
7083 if (r < 0)
7084 return log_error_errno(r, "Failed to open block device backing '%s': %m", opened);
757bc2e4
LP
7085
7086 if (fstat(source_fd, &st) < 0)
7087 return log_error_errno(errno, "Failed to stat block device '%s': %m", bdev);
757bc2e4
LP
7088 }
7089
7090 if (S_ISREG(st.st_mode))
7091 size = st.st_size;
7092 else if (S_ISBLK(st.st_mode)) {
01db9c85
LP
7093 r = blockdev_get_device_size(source_fd, &size);
7094 if (r < 0)
7095 return log_error_errno(r, "Failed to determine size of block device to copy from: %m");
468d09c3
LP
7096 } else if (S_ISCHR(st.st_mode))
7097 size = UINT64_MAX;
7098 else
4e494e6a 7099 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified path to copy blocks from '%s' is not a regular file, block device or directory, refusing.", opened);
757bc2e4 7100
468d09c3
LP
7101 if (size != UINT64_MAX) {
7102 if (size <= 0)
7103 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has zero size, refusing.", opened);
7104 if (size % 512 != 0)
7105 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has size that is not multiple of 512, refusing.", opened);
7106 }
757bc2e4
LP
7107
7108 p->copy_blocks_fd = TAKE_FD(source_fd);
7109 p->copy_blocks_size = size;
5c08da58
LP
7110
7111 free_and_replace(p->copy_blocks_path, opened);
7112
7113 /* When copying from an existing partition copy that partitions UUID if none is configured explicitly */
11749b61 7114 if (!p->new_uuid_is_set && !sd_id128_is_null(uuid)) {
5c08da58 7115 p->new_uuid = uuid;
11749b61
DDM
7116 p->new_uuid_is_set = true;
7117 }
757bc2e4
LP
7118 }
7119
7120 return 0;
7121}
7122
c4a87b76
DDM
7123static int fd_apparent_size(int fd, uint64_t *ret) {
7124 off_t initial = 0;
7125 uint64_t size = 0;
7126
7127 assert(fd >= 0);
7128 assert(ret);
7129
7130 initial = lseek(fd, 0, SEEK_CUR);
7131 if (initial < 0)
7132 return log_error_errno(errno, "Failed to get file offset: %m");
7133
7134 for (off_t off = 0;;) {
7135 off_t r;
7136
7137 r = lseek(fd, off, SEEK_DATA);
7138 if (r < 0 && errno == ENXIO)
7139 /* If errno == ENXIO, that means we've reached the final hole of the file and
7140 * that hole isn't followed by more data. */
7141 break;
7142 if (r < 0)
7143 return log_error_errno(errno, "Failed to seek data in file from offset %"PRIi64": %m", off);
7144
7145 off = r; /* Set the offset to the start of the data segment. */
7146
7147 /* After copying a potential hole, find the end of the data segment by looking for
7148 * the next hole. If we get ENXIO, we're at EOF. */
7149 r = lseek(fd, off, SEEK_HOLE);
7150 if (r < 0) {
7151 if (errno == ENXIO)
7152 break;
7153 return log_error_errno(errno, "Failed to seek hole in file from offset %"PRIi64": %m", off);
7154 }
7155
7156 size += r - off;
7157 off = r;
7158 }
7159
7160 if (lseek(fd, initial, SEEK_SET) < 0)
7161 return log_error_errno(errno, "Failed to reset file offset: %m");
7162
7163 *ret = size;
7164
7165 return 0;
7166}
7167
1a0541d4
DDM
7168static bool need_fstab_one(const Partition *p) {
7169 assert(p);
7170
7171 if (p->dropped)
7172 return false;
7173
7174 if (!p->format)
7175 return false;
7176
7177 if (p->n_mountpoints == 0)
7178 return false;
7179
7180 return true;
7181}
7182
7183static bool need_fstab(const Context *context) {
7184 assert(context);
7185
7186 LIST_FOREACH(partitions, p, context->partitions)
7187 if (need_fstab_one(p))
7188 return true;
7189
7190 return false;
7191}
7192
7193static int context_fstab(Context *context) {
7194 _cleanup_(unlink_and_freep) char *t = NULL;
7195 _cleanup_fclose_ FILE *f = NULL;
7196 _cleanup_free_ char *path = NULL;
7197 int r;
7198
7199 assert(context);
7200
7201 if (!arg_generate_fstab)
7202 return false;
7203
7204 if (!need_fstab(context)) {
1651c914 7205 log_notice("MountPoint= is not specified for any eligible partitions, not generating %s",
1a0541d4
DDM
7206 arg_generate_fstab);
7207 return 0;
7208 }
7209
7210 path = path_join(arg_copy_source, arg_generate_fstab);
7211 if (!path)
7212 return log_oom();
7213
7214 r = fopen_tmpfile_linkable(path, O_WRONLY|O_CLOEXEC, &t, &f);
7215 if (r < 0)
7216 return log_error_errno(r, "Failed to open temporary file for %s: %m", path);
7217
7218 fprintf(f, "# Automatically generated by systemd-repart\n\n");
7219
7220 LIST_FOREACH(partitions, p, context->partitions) {
7221 _cleanup_free_ char *what = NULL, *options = NULL;
7222
7223 if (!need_fstab_one(p))
7224 continue;
7225
7226 what = strjoin("UUID=", SD_ID128_TO_UUID_STRING(p->fs_uuid));
7227 if (!what)
7228 return log_oom();
7229
7230 FOREACH_ARRAY(mountpoint, p->mountpoints, p->n_mountpoints) {
7231 r = partition_pick_mount_options(
7232 p->type.designator,
7233 p->format,
7234 /* rw= */ true,
7235 /* discard= */ !IN_SET(p->type.designator, PARTITION_ESP, PARTITION_XBOOTLDR),
7236 &options,
7237 NULL);
7238 if (r < 0)
7239 return r;
7240
7241 if (!strextend_with_separator(&options, ",", mountpoint->options))
7242 return log_oom();
7243
7244 fprintf(f, "%s %s %s %s 0 %i\n",
7245 what,
7246 mountpoint->where,
7247 p->format,
7248 options,
7249 p->type.designator == PARTITION_ROOT ? 1 : 2);
7250 }
7251 }
7252
7253 r = flink_tmpfile(f, t, path, 0);
7254 if (r < 0)
7255 return log_error_errno(r, "Failed to link temporary file to %s: %m", path);
7256
7257 log_info("%s written.", path);
7258
7259 return 0;
7260}
7261
7262static bool need_crypttab_one(const Partition *p) {
7263 assert(p);
7264
7265 if (p->dropped)
7266 return false;
7267
7268 if (p->encrypt == ENCRYPT_OFF)
7269 return false;
7270
7271 if (!p->encrypted_volume)
7272 return false;
7273
7274 return true;
7275}
7276
7277static bool need_crypttab(Context *context) {
7278 assert(context);
7279
7280 LIST_FOREACH(partitions, p, context->partitions)
7281 if (need_crypttab_one(p))
7282 return true;
7283
7284 return false;
7285}
7286
7287static int context_crypttab(Context *context) {
7288 _cleanup_(unlink_and_freep) char *t = NULL;
7289 _cleanup_fclose_ FILE *f = NULL;
7290 _cleanup_free_ char *path = NULL;
7291 int r;
7292
7293 assert(context);
7294
7295 if (!arg_generate_crypttab)
7296 return false;
7297
7298 if (!need_crypttab(context)) {
1651c914 7299 log_notice("EncryptedVolume= is not specified for any eligible partitions, not generating %s",
1a0541d4
DDM
7300 arg_generate_crypttab);
7301 return 0;
7302 }
7303
7304 path = path_join(arg_copy_source, arg_generate_crypttab);
7305 if (!path)
7306 return log_oom();
7307
7308 r = fopen_tmpfile_linkable(path, O_WRONLY|O_CLOEXEC, &t, &f);
7309 if (r < 0)
7310 return log_error_errno(r, "Failed to open temporary file for %s: %m", path);
7311
7312 fprintf(f, "# Automatically generated by systemd-repart\n\n");
7313
7314 LIST_FOREACH(partitions, p, context->partitions) {
7315 _cleanup_free_ char *volume = NULL;
7316
7317 if (!need_crypttab_one(p))
7318 continue;
7319
7320 if (!p->encrypted_volume->name && asprintf(&volume, "luks-%s", SD_ID128_TO_UUID_STRING(p->luks_uuid)) < 0)
7321 return log_oom();
7322
7323 fprintf(f, "%s UUID=%s %s %s\n",
7324 p->encrypted_volume->name ?: volume,
7325 SD_ID128_TO_UUID_STRING(p->luks_uuid),
7326 isempty(p->encrypted_volume->keyfile) ? "-" : p->encrypted_volume->keyfile,
7327 strempty(p->encrypted_volume->options));
7328 }
7329
7330 r = flink_tmpfile(f, t, path, 0);
7331 if (r < 0)
7332 return log_error_errno(r, "Failed to link temporary file to %s: %m", path);
7333
7334 log_info("%s written.", path);
7335
7336 return 0;
7337}
7338
c4a87b76 7339static int context_minimize(Context *context) {
c264ec5f 7340 const char *vt = NULL;
b9c0b6c0 7341 unsigned attrs = 0;
c4a87b76
DDM
7342 int r;
7343
7344 assert(context);
7345
b9c0b6c0
DDM
7346 r = read_attr_fd(context->backing_fd, &attrs);
7347 if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r))
7348 return log_error_errno(r, "Failed to read file attributes of %s: %m", arg_node);
7349
c4a87b76 7350 LIST_FOREACH(partitions, p, context->partitions) {
e59678b2 7351 _cleanup_(rm_rf_physical_and_freep) char *root = NULL;
c4a87b76 7352 _cleanup_(unlink_and_freep) char *temp = NULL;
d3201eb4 7353 _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
4b8ce14f 7354 _cleanup_strv_free_ char **extra_mkfs_options = NULL;
254d1313 7355 _cleanup_close_ int fd = -EBADF;
b24bfd6e 7356 _cleanup_free_ char *hint = NULL;
c4a87b76 7357 sd_id128_t fs_uuid;
6ac299e3 7358 struct stat st;
c4a87b76
DDM
7359 uint64_t fsz;
7360
7361 if (p->dropped)
7362 continue;
7363
7364 if (PARTITION_EXISTS(p)) /* Never format existing partitions */
7365 continue;
7366
7367 if (!p->format)
7368 continue;
7369
dea0dc7b
DDM
7370 if (p->copy_blocks_fd >= 0)
7371 continue;
7372
5c33b686 7373 if (p->minimize == MINIMIZE_OFF)
c4a87b76
DDM
7374 continue;
7375
d3201eb4
DDM
7376 if (!partition_needs_populate(p))
7377 continue;
7378
c4a87b76
DDM
7379 assert(!p->copy_blocks_path);
7380
b24bfd6e
DDM
7381 (void) partition_hint(p, context->node, &hint);
7382
7383 log_info("Pre-populating %s filesystem of partition %s twice to calculate minimal partition size",
7384 p->format, strna(hint));
7385
c264ec5f
ZJS
7386 if (!vt) {
7387 r = var_tmp_dir(&vt);
7388 if (r < 0)
7389 return log_error_errno(r, "Could not determine temporary directory: %m");
7390 }
7391
c4a87b76
DDM
7392 r = tempfn_random_child(vt, "repart", &temp);
7393 if (r < 0)
7394 return log_error_errno(r, "Failed to generate temporary file path: %m");
7395
b9c0b6c0
DDM
7396 fd = xopenat_full(
7397 AT_FDCWD,
7398 temp,
7399 O_CREAT|O_EXCL|O_CLOEXEC|O_RDWR|O_NOCTTY,
7400 attrs & FS_NOCOW_FL ? XO_NOCOW : 0,
7401 0600);
7402 if (fd < 0)
7403 return log_error_errno(errno, "Failed to open temporary file %s: %m", temp);
7404
59e2be46
DDM
7405 if (fstype_is_ro(p->format))
7406 fs_uuid = p->fs_uuid;
7407 else {
c4a87b76 7408 /* This may seem huge but it will be created sparse so it doesn't take up any space
d3201eb4 7409 * on disk until written to. */
c4a87b76
DDM
7410 if (ftruncate(fd, 1024ULL * 1024ULL * 1024ULL * 1024ULL) < 0)
7411 return log_error_errno(errno, "Failed to truncate temporary file to %s: %m",
7412 FORMAT_BYTES(1024ULL * 1024ULL * 1024ULL * 1024ULL));
7413
e30c6e91 7414 if (arg_offline <= 0) {
66a8c74c 7415 r = loop_device_make(fd, O_RDWR, 0, UINT64_MAX, context->sector_size, 0, LOCK_EX, &d);
3799fa80 7416 if (r < 0 && loop_device_error_is_fatal(p, r))
e30c6e91
DDM
7417 return log_error_errno(r, "Failed to make loopback device of %s: %m", temp);
7418 }
d3201eb4 7419
c4a87b76
DDM
7420 /* We're going to populate this filesystem twice so use a random UUID the first time
7421 * to avoid UUID conflicts. */
7422 r = sd_id128_randomize(&fs_uuid);
7423 if (r < 0)
7424 return r;
59e2be46
DDM
7425 }
7426
d3201eb4
DDM
7427 if (!d || fstype_is_ro(p->format)) {
7428 if (!mkfs_supports_root_option(p->format))
7429 return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
4e494e6a 7430 "Loop device access is required to populate %s filesystems.",
d3201eb4
DDM
7431 p->format);
7432
e57b7020 7433 r = partition_populate_directory(context, p, &root);
c4a87b76
DDM
7434 if (r < 0)
7435 return r;
c4a87b76
DDM
7436 }
7437
eca3d07d 7438 r = finalize_extra_mkfs_options(p, root, &extra_mkfs_options);
4b8ce14f 7439 if (r < 0)
eca3d07d 7440 return r;
4b8ce14f 7441
93f125a6
DDM
7442 r = make_filesystem(d ? d->node : temp,
7443 p->format,
7444 strempty(p->new_label),
7445 root,
7446 fs_uuid,
7447 arg_discard, /* quiet = */ false,
7448 context->fs_sector_size,
27cacec9
DDM
7449 p->compression,
7450 p->compression_level,
93f125a6 7451 extra_mkfs_options);
c4a87b76
DDM
7452 if (r < 0)
7453 return r;
7454
7455 /* Read-only filesystems are minimal from the first try because they create and size the
7456 * loopback file for us. */
7457 if (fstype_is_ro(p->format)) {
b9c0b6c0 7458 fd = safe_close(fd);
2843df3e 7459
dea0dc7b
DDM
7460 fd = open(temp, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
7461 if (fd < 0)
7462 return log_error_errno(errno, "Failed to open temporary file %s: %m", temp);
7463
7464 if (fstat(fd, &st) < 0)
b24bfd6e
DDM
7465 return log_error_errno(errno, "Failed to stat temporary file: %m");
7466
7467 log_info("Minimal partition size of %s filesystem of partition %s is %s",
7468 p->format, strna(hint), FORMAT_BYTES(st.st_size));
7469
c4a87b76 7470 p->copy_blocks_path = TAKE_PTR(temp);
7d07030e 7471 p->copy_blocks_path_is_our_file = true;
2843df3e
DDM
7472 p->copy_blocks_fd = TAKE_FD(fd);
7473 p->copy_blocks_size = st.st_size;
c4a87b76
DDM
7474 continue;
7475 }
7476
d3201eb4
DDM
7477 if (!root) {
7478 assert(d);
7479
e57b7020 7480 r = partition_populate_filesystem(context, p, d->node);
59e2be46
DDM
7481 if (r < 0)
7482 return r;
7483 }
c4a87b76
DDM
7484
7485 /* Other filesystems need to be provided with a pre-sized loopback file and will adapt to
7486 * fully occupy it. Because we gave the filesystem a 1T sparse file, we need to shrink the
7487 * filesystem down to a reasonable size again to fit it in the disk image. While there are
7488 * some filesystems that support shrinking, it doesn't always work properly (e.g. shrinking
7489 * btrfs gives us a 2.0G filesystem regardless of what we put in it). Instead, let's populate
7490 * the filesystem again, but this time, instead of providing the filesystem with a 1T sparse
7491 * loopback file, let's size the loopback file based on the actual data used by the
7492 * filesystem in the sparse file after the first attempt. This should be a good guess of the
7493 * minimal amount of space needed in the filesystem to fit all the required data.
7494 */
7495 r = fd_apparent_size(fd, &fsz);
7496 if (r < 0)
7497 return r;
7498
7499 /* Massage the size a bit because just going by actual data used in the sparse file isn't
7500 * fool-proof. */
7bc6c028
DDM
7501 uint64_t heuristic = streq(p->format, "xfs") ? fsz : fsz / 2;
7502 fsz = round_up_size(fsz + heuristic, context->grain_size);
c4a87b76
DDM
7503 if (minimal_size_by_fs_name(p->format) != UINT64_MAX)
7504 fsz = MAX(minimal_size_by_fs_name(p->format), fsz);
7505
b24bfd6e
DDM
7506 log_info("Minimal partition size of %s filesystem of partition %s is %s",
7507 p->format, strna(hint), FORMAT_BYTES(fsz));
7508
d3201eb4
DDM
7509 d = loop_device_unref(d);
7510
c4a87b76 7511 /* Erase the previous filesystem first. */
6c7f4bad 7512 if (ftruncate(fd, 0) < 0)
c4a87b76
DDM
7513 return log_error_errno(errno, "Failed to erase temporary file: %m");
7514
6c7f4bad 7515 if (ftruncate(fd, fsz) < 0)
c4a87b76
DDM
7516 return log_error_errno(errno, "Failed to truncate temporary file to %s: %m", FORMAT_BYTES(fsz));
7517
e30c6e91 7518 if (arg_offline <= 0) {
66a8c74c 7519 r = loop_device_make(fd, O_RDWR, 0, UINT64_MAX, context->sector_size, 0, LOCK_EX, &d);
9fb5d912 7520 if (r < 0 && loop_device_error_is_fatal(p, r))
e30c6e91
DDM
7521 return log_error_errno(r, "Failed to make loopback device of %s: %m", temp);
7522 }
d3201eb4 7523
93f125a6
DDM
7524 r = make_filesystem(d ? d->node : temp,
7525 p->format,
7526 strempty(p->new_label),
7527 root,
7528 p->fs_uuid,
7529 arg_discard,
7530 /* quiet = */ false,
7531 context->fs_sector_size,
27cacec9
DDM
7532 p->compression,
7533 p->compression_level,
93f125a6 7534 extra_mkfs_options);
c4a87b76
DDM
7535 if (r < 0)
7536 return r;
7537
d3201eb4
DDM
7538 if (!root) {
7539 assert(d);
7540
e57b7020 7541 r = partition_populate_filesystem(context, p, d->node);
59e2be46
DDM
7542 if (r < 0)
7543 return r;
7544 }
c4a87b76 7545
6ac299e3
DDM
7546 if (fstat(fd, &st) < 0)
7547 return log_error_errno(errno, "Failed to stat temporary file: %m");
dea0dc7b 7548
c4a87b76 7549 p->copy_blocks_path = TAKE_PTR(temp);
7d07030e 7550 p->copy_blocks_path_is_our_file = true;
2843df3e 7551 p->copy_blocks_fd = TAKE_FD(fd);
6ac299e3 7552 p->copy_blocks_size = st.st_size;
c4a87b76
DDM
7553 }
7554
5eef7047
DDM
7555 /* Now that we've done the data partitions, do the verity hash partitions. We do these in a separate
7556 * step because they might depend on data generated in the previous step. */
7557
7558 LIST_FOREACH(partitions, p, context->partitions) {
7559 _cleanup_(unlink_and_freep) char *temp = NULL;
7560 _cleanup_free_ char *hint = NULL;
2843df3e 7561 _cleanup_close_ int fd = -EBADF;
5eef7047
DDM
7562 struct stat st;
7563 Partition *dp;
7564
7565 if (p->dropped)
7566 continue;
7567
7568 if (PARTITION_EXISTS(p)) /* Never format existing partitions */
7569 continue;
7570
7571 if (p->minimize == MINIMIZE_OFF)
7572 continue;
7573
7574 if (p->verity != VERITY_HASH)
7575 continue;
7576
7577 assert_se(dp = p->siblings[VERITY_DATA]);
7578 assert(!dp->dropped);
7579 assert(dp->copy_blocks_path);
7580
7581 (void) partition_hint(p, context->node, &hint);
7582
7583 log_info("Pre-populating verity hash data of partition %s to calculate minimal partition size",
7584 strna(hint));
7585
c264ec5f
ZJS
7586 if (!vt) {
7587 r = var_tmp_dir(&vt);
7588 if (r < 0)
7589 return log_error_errno(r, "Could not determine temporary directory: %m");
7590 }
7591
5eef7047
DDM
7592 r = tempfn_random_child(vt, "repart", &temp);
7593 if (r < 0)
7594 return log_error_errno(r, "Failed to generate temporary file path: %m");
7595
b9c0b6c0
DDM
7596 fd = xopenat_full(
7597 AT_FDCWD,
7598 temp,
7599 O_RDONLY|O_CLOEXEC|O_CREAT|O_NONBLOCK,
7600 attrs & FS_NOCOW_FL ? XO_NOCOW : 0,
7601 0600);
7602 if (fd < 0)
7603 return log_error_errno(errno, "Failed to open temporary file %s: %m", temp);
5eef7047
DDM
7604
7605 r = partition_format_verity_hash(context, p, temp, dp->copy_blocks_path);
7606 if (r < 0)
7607 return r;
7608
2843df3e 7609 if (fstat(fd, &st) < 0)
9f08d7f7 7610 return log_error_errno(errno, "Failed to stat temporary file: %m");
5eef7047
DDM
7611
7612 log_info("Minimal partition size of verity hash partition %s is %s",
7613 strna(hint), FORMAT_BYTES(st.st_size));
7614
7615 p->copy_blocks_path = TAKE_PTR(temp);
7616 p->copy_blocks_path_is_our_file = true;
2843df3e
DDM
7617 p->copy_blocks_fd = TAKE_FD(fd);
7618 p->copy_blocks_size = st.st_size;
5eef7047
DDM
7619 }
7620
c4a87b76
DDM
7621 return 0;
7622}
7623
9786dfe6 7624static int parse_partition_types(const char *p, GptPartitionType **partitions, size_t *n_partitions) {
81d1098b
DDM
7625 int r;
7626
220780db
DDM
7627 assert(partitions);
7628 assert(n_partitions);
7629
81d1098b
DDM
7630 for (;;) {
7631 _cleanup_free_ char *name = NULL;
7632 GptPartitionType type;
7633
7634 r = extract_first_word(&p, &name, ",", EXTRACT_CUNESCAPE|EXTRACT_DONT_COALESCE_SEPARATORS);
7635 if (r == 0)
7636 break;
7637 if (r < 0)
766f52f2 7638 return log_error_errno(r, "Failed to extract partition type identifier or GUID: %s", p);
81d1098b
DDM
7639
7640 r = gpt_partition_type_from_string(name, &type);
7641 if (r < 0)
766f52f2 7642 return log_error_errno(r, "'%s' is not a valid partition type identifier or GUID", name);
81d1098b 7643
220780db 7644 if (!GREEDY_REALLOC(*partitions, *n_partitions + 1))
81d1098b
DDM
7645 return log_oom();
7646
9786dfe6 7647 (*partitions)[(*n_partitions)++] = type;
81d1098b
DDM
7648 }
7649
7650 return 0;
7651}
7652
e594a3b1
LP
7653static int help(void) {
7654 _cleanup_free_ char *link = NULL;
7655 int r;
7656
6dadf31d 7657 r = terminal_urlify_man("systemd-repart", "8", &link);
e594a3b1
LP
7658 if (r < 0)
7659 return log_oom();
7660
68debef4
LP
7661 printf("%1$s [OPTIONS...] [DEVICE]\n"
7662 "\n%5$sGrow and add partitions to a partition table, and generate disk images (DDIs).%6$s\n\n"
e594a3b1
LP
7663 " -h --help Show this help\n"
7664 " --version Show package version\n"
896e678b
LP
7665 " --no-pager Do not pipe output into a pager\n"
7666 " --no-legend Do not show the headers and footers\n"
68debef4 7667 "\n%3$sOperation:%4$s\n"
e594a3b1 7668 " --dry-run=BOOL Whether to run dry-run operation\n"
a26f4a49
LP
7669 " --empty=MODE One of refuse, allow, require, force, create; controls\n"
7670 " how to handle empty disks lacking partition tables\n"
68debef4 7671 " --offline=BOOL Whether to build the image offline\n"
e594a3b1 7672 " --discard=BOOL Whether to discard backing blocks for new partitions\n"
68debef4
LP
7673 " --sector-size=SIZE Set the logical sector size for the image\n"
7674 " --architecture=ARCH Set the generic architecture for the image\n"
7675 " --size=BYTES Grow loopback file to specified size\n"
7676 " --seed=UUID 128-bit seed UUID to derive all UUIDs from\n"
7677 " --split=BOOL Whether to generate split artifacts\n"
7678 "\n%3$sOutput:%4$s\n"
2d2d0a57 7679 " --pretty=BOOL Whether to show pretty summary before doing changes\n"
68debef4
LP
7680 " --json=pretty|short|off\n"
7681 " Generate JSON output\n"
7682 "\n%3$sFactory Reset:%4$s\n"
e594a3b1
LP
7683 " --factory-reset=BOOL Whether to remove data partitions before recreating\n"
7684 " them\n"
7685 " --can-factory-reset Test whether factory reset is defined\n"
68debef4 7686 "\n%3$sConfiguration & Image Control:%4$s\n"
e594a3b1 7687 " --root=PATH Operate relative to root path\n"
252d6267 7688 " --image=PATH Operate relative to image file\n"
84be0c71
LP
7689 " --image-policy=POLICY\n"
7690 " Specify disk image dissection policy\n"
9d252fbb 7691 " --definitions=DIR Find partition definitions in specified directory\n"
3a37a13a 7692 " --list-devices List candidate block devices to operate on\n"
68debef4 7693 "\n%3$sVerity:%4$s\n"
a73144bb
LB
7694 " --private-key=PATH|URI\n"
7695 " Private key to use when generating verity roothash\n"
7696 " signatures, or an engine or provider specific\n"
4955d2e3 7697 " designation if --private-key-source= is used\n"
a73144bb 7698 " --private-key-source=file|provider:PROVIDER|engine:ENGINE\n"
4955d2e3 7699 " Specify how to use KEY for --private-key=. Allows\n"
1cd53a1f
ZJS
7700 " an OpenSSL engine/provider to be used when generating\n"
7701 " verity roothash signatures\n"
b456191d
DDM
7702 " --certificate=PATH PEM certificate to use when generating verity\n"
7703 " roothash signatures\n"
68debef4
LP
7704 "\n%3$sEncryption:%4$s\n"
7705 " --key-file=PATH Key to use when encrypting partitions\n"
889914ef 7706 " --tpm2-device=PATH Path to TPM2 device node to use\n"
78fdf0f6
LP
7707 " --tpm2-device-key=PATH\n"
7708 " Enroll a TPM2 device using its public key\n"
7709 " --tpm2-seal-key-handle=HANDLE\n"
7710 " Specify handle of key to use for sealing\n"
a1788a69 7711 " --tpm2-pcrs=PCR1+PCR2+PCR3+…\n"
889914ef 7712 " TPM2 PCR indexes to use for TPM2 enrollment\n"
02ef97cd
LP
7713 " --tpm2-public-key=PATH\n"
7714 " Enroll signed TPM2 PCR policy against PEM public key\n"
7715 " --tpm2-public-key-pcrs=PCR1+PCR2+PCR3+…\n"
7716 " Enroll signed TPM2 PCR policy for specified TPM2 PCRs\n"
fadc7d8c
LP
7717 " --tpm2-pcrlock=PATH\n"
7718 " Specify pcrlock policy to lock against\n"
68debef4 7719 "\n%3$sPartition Control:%4$s\n"
81d1098b 7720 " --include-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
7d505753 7721 " Ignore partitions not of the specified types\n"
81d1098b 7722 " --exclude-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
7d505753 7723 " Ignore partitions of the specified types\n"
8275334b 7724 " --defer-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
7d505753
DDM
7725 " Take partitions of the specified types into account\n"
7726 " but don't populate them yet\n"
68debef4 7727 "\n%3$sCopying:%4$s\n"
607343a1 7728 " -s --copy-source=PATH Specify the primary source tree to copy files from\n"
e1536d1f 7729 " --copy-from=IMAGE Copy partitions from the given image(s)\n"
68debef4 7730 "\n%3$sDDI Profile:%4$s\n"
a121b331
LP
7731 " -S --make-ddi=sysext Make a system extension DDI\n"
7732 " -C --make-ddi=confext Make a configuration extension DDI\n"
7733 " -P --make-ddi=portable Make a portable service DDI\n"
68debef4 7734 "\n%3$sAuxiliary Resource Generation:%4$s\n"
1a0541d4
DDM
7735 " --generate-fstab=PATH\n"
7736 " Write fstab configuration to the given path\n"
7737 " --generate-crypttab=PATH\n"
7738 " Write crypttab configuration to the given path\n"
68debef4 7739 "\nSee the %2$s for details.\n",
bc556335 7740 program_invocation_short_name,
68debef4
LP
7741 link,
7742 ansi_underline(),
bc556335 7743 ansi_normal(),
68debef4
LP
7744 ansi_highlight(),
7745 ansi_normal());
e594a3b1
LP
7746
7747 return 0;
7748}
7749
7750static int parse_argv(int argc, char *argv[]) {
a73144bb 7751 _cleanup_free_ char *private_key = NULL;
e594a3b1
LP
7752
7753 enum {
7754 ARG_VERSION = 0x100,
896e678b
LP
7755 ARG_NO_PAGER,
7756 ARG_NO_LEGEND,
e594a3b1
LP
7757 ARG_DRY_RUN,
7758 ARG_EMPTY,
7759 ARG_DISCARD,
7760 ARG_FACTORY_RESET,
7761 ARG_CAN_FACTORY_RESET,
7762 ARG_ROOT,
252d6267 7763 ARG_IMAGE,
06e78680 7764 ARG_IMAGE_POLICY,
e594a3b1
LP
7765 ARG_SEED,
7766 ARG_PRETTY,
7767 ARG_DEFINITIONS,
a26f4a49 7768 ARG_SIZE,
a015fbe7 7769 ARG_JSON,
b9df3536 7770 ARG_KEY_FILE,
b456191d 7771 ARG_PRIVATE_KEY,
a73144bb 7772 ARG_PRIVATE_KEY_SOURCE,
b456191d 7773 ARG_CERTIFICATE,
889914ef 7774 ARG_TPM2_DEVICE,
78fdf0f6
LP
7775 ARG_TPM2_DEVICE_KEY,
7776 ARG_TPM2_SEAL_KEY_HANDLE,
889914ef 7777 ARG_TPM2_PCRS,
02ef97cd
LP
7778 ARG_TPM2_PUBLIC_KEY,
7779 ARG_TPM2_PUBLIC_KEY_PCRS,
404aea78 7780 ARG_TPM2_PCRLOCK,
4cee8333 7781 ARG_SPLIT,
81d1098b
DDM
7782 ARG_INCLUDE_PARTITIONS,
7783 ARG_EXCLUDE_PARTITIONS,
8275334b 7784 ARG_DEFER_PARTITIONS,
e1878ef7 7785 ARG_SECTOR_SIZE,
84be0c71 7786 ARG_SKIP_PARTITIONS,
9786dfe6 7787 ARG_ARCHITECTURE,
fc10b158 7788 ARG_OFFLINE,
1e46985a 7789 ARG_COPY_FROM,
a121b331 7790 ARG_MAKE_DDI,
1a0541d4
DDM
7791 ARG_GENERATE_FSTAB,
7792 ARG_GENERATE_CRYPTTAB,
3a37a13a 7793 ARG_LIST_DEVICES,
e594a3b1
LP
7794 };
7795
7796 static const struct option options[] = {
02ef97cd
LP
7797 { "help", no_argument, NULL, 'h' },
7798 { "version", no_argument, NULL, ARG_VERSION },
7799 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
7800 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
7801 { "dry-run", required_argument, NULL, ARG_DRY_RUN },
7802 { "empty", required_argument, NULL, ARG_EMPTY },
7803 { "discard", required_argument, NULL, ARG_DISCARD },
7804 { "factory-reset", required_argument, NULL, ARG_FACTORY_RESET },
7805 { "can-factory-reset", no_argument, NULL, ARG_CAN_FACTORY_RESET },
7806 { "root", required_argument, NULL, ARG_ROOT },
7807 { "image", required_argument, NULL, ARG_IMAGE },
06e78680 7808 { "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
02ef97cd
LP
7809 { "seed", required_argument, NULL, ARG_SEED },
7810 { "pretty", required_argument, NULL, ARG_PRETTY },
7811 { "definitions", required_argument, NULL, ARG_DEFINITIONS },
7812 { "size", required_argument, NULL, ARG_SIZE },
7813 { "json", required_argument, NULL, ARG_JSON },
7814 { "key-file", required_argument, NULL, ARG_KEY_FILE },
b456191d 7815 { "private-key", required_argument, NULL, ARG_PRIVATE_KEY },
a73144bb 7816 { "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE },
b456191d 7817 { "certificate", required_argument, NULL, ARG_CERTIFICATE },
02ef97cd 7818 { "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
78fdf0f6
LP
7819 { "tpm2-device-key", required_argument, NULL, ARG_TPM2_DEVICE_KEY },
7820 { "tpm2-seal-key-handle", required_argument, NULL, ARG_TPM2_SEAL_KEY_HANDLE },
02ef97cd
LP
7821 { "tpm2-pcrs", required_argument, NULL, ARG_TPM2_PCRS },
7822 { "tpm2-public-key", required_argument, NULL, ARG_TPM2_PUBLIC_KEY },
7823 { "tpm2-public-key-pcrs", required_argument, NULL, ARG_TPM2_PUBLIC_KEY_PCRS },
404aea78 7824 { "tpm2-pcrlock", required_argument, NULL, ARG_TPM2_PCRLOCK },
4cee8333 7825 { "split", required_argument, NULL, ARG_SPLIT },
81d1098b
DDM
7826 { "include-partitions", required_argument, NULL, ARG_INCLUDE_PARTITIONS },
7827 { "exclude-partitions", required_argument, NULL, ARG_EXCLUDE_PARTITIONS },
8275334b 7828 { "defer-partitions", required_argument, NULL, ARG_DEFER_PARTITIONS },
e1878ef7 7829 { "sector-size", required_argument, NULL, ARG_SECTOR_SIZE },
9786dfe6 7830 { "architecture", required_argument, NULL, ARG_ARCHITECTURE },
fc10b158 7831 { "offline", required_argument, NULL, ARG_OFFLINE },
1e46985a 7832 { "copy-from", required_argument, NULL, ARG_COPY_FROM },
607343a1 7833 { "copy-source", required_argument, NULL, 's' },
a121b331 7834 { "make-ddi", required_argument, NULL, ARG_MAKE_DDI },
1a0541d4
DDM
7835 { "generate-fstab", required_argument, NULL, ARG_GENERATE_FSTAB },
7836 { "generate-crypttab", required_argument, NULL, ARG_GENERATE_CRYPTTAB },
3a37a13a 7837 { "list-devices", no_argument, NULL, ARG_LIST_DEVICES },
e594a3b1
LP
7838 {}
7839 };
7840
404aea78 7841 bool auto_hash_pcr_values = true, auto_public_key_pcr_mask = true, auto_pcrlock = true;
ba6bd342 7842 int c, r;
e594a3b1
LP
7843
7844 assert(argc >= 0);
7845 assert(argv);
7846
a121b331 7847 while ((c = getopt_long(argc, argv, "hs:SCP", options, NULL)) >= 0)
e594a3b1
LP
7848
7849 switch (c) {
7850
7851 case 'h':
7852 return help();
7853
7854 case ARG_VERSION:
7855 return version();
7856
896e678b
LP
7857 case ARG_NO_PAGER:
7858 arg_pager_flags |= PAGER_DISABLE;
7859 break;
7860
7861 case ARG_NO_LEGEND:
7862 arg_legend = false;
7863 break;
7864
e594a3b1 7865 case ARG_DRY_RUN:
599c7c54 7866 r = parse_boolean_argument("--dry-run=", optarg, &arg_dry_run);
e594a3b1 7867 if (r < 0)
599c7c54 7868 return r;
e594a3b1
LP
7869 break;
7870
7871 case ARG_EMPTY:
634b8471 7872 if (isempty(optarg)) {
243dd1e9 7873 arg_empty = EMPTY_UNSET;
634b8471
LP
7874 break;
7875 }
7876
7877 arg_empty = empty_mode_from_string(optarg);
7878 if (arg_empty < 0)
7879 return log_error_errno(arg_empty, "Failed to parse --empty= parameter: %s", optarg);
7880
e594a3b1
LP
7881 break;
7882
7883 case ARG_DISCARD:
599c7c54 7884 r = parse_boolean_argument("--discard=", optarg, &arg_discard);
e594a3b1 7885 if (r < 0)
599c7c54 7886 return r;
e594a3b1
LP
7887 break;
7888
7889 case ARG_FACTORY_RESET:
c3470872 7890 r = parse_boolean_argument("--factory-reset=", optarg, NULL);
e594a3b1 7891 if (r < 0)
c3470872 7892 return r;
e594a3b1
LP
7893 arg_factory_reset = r;
7894 break;
7895
7896 case ARG_CAN_FACTORY_RESET:
7897 arg_can_factory_reset = true;
7898 break;
7899
7900 case ARG_ROOT:
252d6267
LP
7901 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_root);
7902 if (r < 0)
7903 return r;
7904 break;
7905
7906 case ARG_IMAGE:
7907 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
e594a3b1
LP
7908 if (r < 0)
7909 return r;
7910 break;
7911
06e78680
YW
7912 case ARG_IMAGE_POLICY:
7913 r = parse_image_policy_argument(optarg, &arg_image_policy);
7914 if (r < 0)
7915 return r;
7916 break;
7917
e594a3b1
LP
7918 case ARG_SEED:
7919 if (isempty(optarg)) {
7920 arg_seed = SD_ID128_NULL;
7921 arg_randomize = false;
7922 } else if (streq(optarg, "random"))
7923 arg_randomize = true;
7924 else {
7925 r = sd_id128_from_string(optarg, &arg_seed);
7926 if (r < 0)
7927 return log_error_errno(r, "Failed to parse seed: %s", optarg);
7928
7929 arg_randomize = false;
7930 }
7931
7932 break;
7933
7934 case ARG_PRETTY:
c3470872 7935 r = parse_boolean_argument("--pretty=", optarg, NULL);
e594a3b1 7936 if (r < 0)
c3470872 7937 return r;
e594a3b1
LP
7938 arg_pretty = r;
7939 break;
7940
224c853f
RP
7941 case ARG_DEFINITIONS: {
7942 _cleanup_free_ char *path = NULL;
7943 r = parse_path_argument(optarg, false, &path);
e594a3b1
LP
7944 if (r < 0)
7945 return r;
224c853f
RP
7946 if (strv_consume(&arg_definitions, TAKE_PTR(path)) < 0)
7947 return log_oom();
e594a3b1 7948 break;
224c853f 7949 }
e594a3b1 7950
a26f4a49
LP
7951 case ARG_SIZE: {
7952 uint64_t parsed, rounded;
7953
170c9823
LP
7954 if (streq(optarg, "auto")) {
7955 arg_size = UINT64_MAX;
7956 arg_size_auto = true;
7957 break;
7958 }
7959
a26f4a49
LP
7960 r = parse_size(optarg, 1024, &parsed);
7961 if (r < 0)
7962 return log_error_errno(r, "Failed to parse --size= parameter: %s", optarg);
7963
7964 rounded = round_up_size(parsed, 4096);
7965 if (rounded == 0)
7966 return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Specified image size too small, refusing.");
7967 if (rounded == UINT64_MAX)
7968 return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Specified image size too large, refusing.");
7969
7970 if (rounded != parsed)
e2341b6b
DT
7971 log_warning("Specified size is not a multiple of 4096, rounding up automatically. (%" PRIu64 " %s %" PRIu64 ")",
7972 parsed, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), rounded);
a26f4a49
LP
7973
7974 arg_size = rounded;
170c9823 7975 arg_size_auto = false;
a26f4a49
LP
7976 break;
7977 }
b9df3536 7978
a015fbe7 7979 case ARG_JSON:
b1e8f46c 7980 r = parse_json_argument(optarg, &arg_json_format_flags);
6a01ea4a
LP
7981 if (r <= 0)
7982 return r;
a015fbe7
TH
7983
7984 break;
7985
b9df3536
LP
7986 case ARG_KEY_FILE: {
7987 _cleanup_(erase_and_freep) char *k = NULL;
7988 size_t n = 0;
7989
8b3c3a49 7990 r = read_full_file_full(
986311c2 7991 AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
8b3c3a49
LP
7992 READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
7993 NULL,
7994 &k, &n);
b9df3536
LP
7995 if (r < 0)
7996 return log_error_errno(r, "Failed to read key file '%s': %m", optarg);
7997
7998 erase_and_free(arg_key);
7999 arg_key = TAKE_PTR(k);
8000 arg_key_size = n;
8001 break;
8002 }
a26f4a49 8003
b456191d 8004 case ARG_PRIVATE_KEY: {
0a826408 8005 r = free_and_strdup_warn(&private_key, optarg);
b456191d 8006 if (r < 0)
0a826408
LB
8007 return r;
8008 break;
8009 }
b456191d 8010
a73144bb
LB
8011 case ARG_PRIVATE_KEY_SOURCE:
8012 r = parse_openssl_key_source_argument(
8013 optarg,
8014 &arg_private_key_source,
8015 &arg_private_key_source_type);
b456191d
DDM
8016 if (r < 0)
8017 return r;
8018 break;
b456191d
DDM
8019
8020 case ARG_CERTIFICATE: {
8021 _cleanup_free_ char *cert = NULL;
8022 size_t n = 0;
8023
8024 r = read_full_file_full(
8025 AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
8026 READ_FULL_FILE_CONNECT_SOCKET,
8027 NULL,
8028 &cert, &n);
8029 if (r < 0)
8030 return log_error_errno(r, "Failed to read certificate file '%s': %m", optarg);
8031
8032 X509_free(arg_certificate);
8033 arg_certificate = NULL;
8034 r = parse_x509_certificate(cert, n, &arg_certificate);
8035 if (r < 0)
8036 return r;
8037 break;
8038 }
8039
889914ef
LP
8040 case ARG_TPM2_DEVICE: {
8041 _cleanup_free_ char *device = NULL;
8042
8043 if (streq(optarg, "list"))
8044 return tpm2_list_devices();
8045
8046 if (!streq(optarg, "auto")) {
8047 device = strdup(optarg);
8048 if (!device)
8049 return log_oom();
8050 }
8051
8052 free(arg_tpm2_device);
8053 arg_tpm2_device = TAKE_PTR(device);
8054 break;
8055 }
8056
78fdf0f6
LP
8057 case ARG_TPM2_DEVICE_KEY:
8058 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_device_key);
8059 if (r < 0)
8060 return r;
8061
8062 break;
8063
8064 case ARG_TPM2_SEAL_KEY_HANDLE:
8065 r = safe_atou32_full(optarg, 16, &arg_tpm2_seal_key_handle);
8066 if (r < 0)
8067 return log_error_errno(r, "Could not parse TPM2 seal key handle index '%s': %m", optarg);
8068
8069 break;
8070
222a951f 8071 case ARG_TPM2_PCRS:
c588cf97 8072 auto_hash_pcr_values = false;
9e437994 8073 r = tpm2_parse_pcr_argument_append(optarg, &arg_tpm2_hash_pcr_values, &arg_tpm2_n_hash_pcr_values);
889914ef
LP
8074 if (r < 0)
8075 return r;
8076
889914ef 8077 break;
889914ef 8078
02ef97cd
LP
8079 case ARG_TPM2_PUBLIC_KEY:
8080 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_public_key);
8081 if (r < 0)
8082 return r;
8083
8084 break;
8085
8086 case ARG_TPM2_PUBLIC_KEY_PCRS:
c588cf97 8087 auto_public_key_pcr_mask = false;
07c04061 8088 r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_public_key_pcr_mask);
02ef97cd
LP
8089 if (r < 0)
8090 return r;
8091
8092 break;
8093
404aea78
LP
8094 case ARG_TPM2_PCRLOCK:
8095 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_pcrlock);
8096 if (r < 0)
8097 return r;
8098
8099 auto_pcrlock = false;
8100 break;
8101
4cee8333
DDM
8102 case ARG_SPLIT:
8103 r = parse_boolean_argument("--split=", optarg, NULL);
8104 if (r < 0)
8105 return r;
8106
8107 arg_split = r;
8108 break;
8109
81d1098b
DDM
8110 case ARG_INCLUDE_PARTITIONS:
8111 if (arg_filter_partitions_type == FILTER_PARTITIONS_EXCLUDE)
8112 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
8113 "Combination of --include-partitions= and --exclude-partitions= is invalid.");
8114
220780db 8115 r = parse_partition_types(optarg, &arg_filter_partitions, &arg_n_filter_partitions);
81d1098b
DDM
8116 if (r < 0)
8117 return r;
8118
8119 arg_filter_partitions_type = FILTER_PARTITIONS_INCLUDE;
8120
8121 break;
8122
8123 case ARG_EXCLUDE_PARTITIONS:
8124 if (arg_filter_partitions_type == FILTER_PARTITIONS_INCLUDE)
8125 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
8126 "Combination of --include-partitions= and --exclude-partitions= is invalid.");
8127
220780db 8128 r = parse_partition_types(optarg, &arg_filter_partitions, &arg_n_filter_partitions);
81d1098b
DDM
8129 if (r < 0)
8130 return r;
8131
8132 arg_filter_partitions_type = FILTER_PARTITIONS_EXCLUDE;
8133
8134 break;
8135
8275334b
DDM
8136 case ARG_DEFER_PARTITIONS:
8137 r = parse_partition_types(optarg, &arg_defer_partitions, &arg_n_defer_partitions);
7d505753
DDM
8138 if (r < 0)
8139 return r;
8140
8141 break;
8142
e1878ef7
DDM
8143 case ARG_SECTOR_SIZE:
8144 r = parse_sector_size(optarg, &arg_sector_size);
8145 if (r < 0)
8146 return r;
8147
8148 break;
8149
9786dfe6
DDM
8150 case ARG_ARCHITECTURE:
8151 r = architecture_from_string(optarg);
8152 if (r < 0)
4e494e6a 8153 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid architecture '%s'.", optarg);
9786dfe6
DDM
8154
8155 arg_architecture = r;
8156 break;
8157
fc10b158
DDM
8158 case ARG_OFFLINE:
8159 if (streq(optarg, "auto"))
8160 arg_offline = -1;
8161 else {
8162 r = parse_boolean_argument("--offline=", optarg, NULL);
8163 if (r < 0)
8164 return r;
8165
8166 arg_offline = r;
8167 }
8168
8169 break;
8170
e1536d1f
DDM
8171 case ARG_COPY_FROM: {
8172 _cleanup_free_ char *p = NULL;
8173
8174 r = parse_path_argument(optarg, /* suppress_root= */ false, &p);
1e46985a
DDM
8175 if (r < 0)
8176 return r;
e1536d1f
DDM
8177
8178 if (strv_consume(&arg_copy_from, TAKE_PTR(p)) < 0)
8179 return log_oom();
8180
1e46985a 8181 break;
e1536d1f 8182 }
1e46985a 8183
607343a1
LP
8184 case 's':
8185 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_copy_source);
8186 if (r < 0)
8187 return r;
8188 break;
8189
a121b331
LP
8190 case ARG_MAKE_DDI:
8191 if (!filename_is_valid(optarg))
8192 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid DDI type: %s", optarg);
8193
8194 r = free_and_strdup_warn(&arg_make_ddi, optarg);
8195 if (r < 0)
8196 return r;
8197 break;
8198
8199 case 'S':
8200 r = free_and_strdup_warn(&arg_make_ddi, "sysext");
8201 if (r < 0)
8202 return r;
8203 break;
8204
8205 case 'C':
8206 r = free_and_strdup_warn(&arg_make_ddi, "confext");
8207 if (r < 0)
8208 return r;
8209 break;
8210
8211 case 'P':
8212 r = free_and_strdup_warn(&arg_make_ddi, "portable");
8213 if (r < 0)
8214 return r;
8215 break;
8216
1a0541d4
DDM
8217 case ARG_GENERATE_FSTAB:
8218 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_generate_fstab);
8219 if (r < 0)
8220 return r;
8221 break;
8222
8223 case ARG_GENERATE_CRYPTTAB:
8224 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_generate_crypttab);
8225 if (r < 0)
8226 return r;
8227 break;
8228
3a37a13a
LP
8229 case ARG_LIST_DEVICES:
8230 r = blockdev_list(BLOCKDEV_LIST_REQUIRE_PARTITION_SCANNING|BLOCKDEV_LIST_SHOW_SYMLINKS|BLOCKDEV_LIST_IGNORE_ZRAM);
8231 if (r < 0)
8232 return r;
8233
8234 return 0;
8235
e594a3b1
LP
8236 case '?':
8237 return -EINVAL;
8238
8239 default:
04499a70 8240 assert_not_reached();
e594a3b1
LP
8241 }
8242
8243 if (argc - optind > 1)
8244 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
37a38788 8245 "Expected at most one argument, the path to the block device or image file.");
e594a3b1 8246
a121b331
LP
8247 if (arg_make_ddi) {
8248 if (arg_definitions)
8249 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Combination of --make-ddi= and --definitions= is not supported.");
8250 if (!IN_SET(arg_empty, EMPTY_UNSET, EMPTY_CREATE))
634b8471 8251 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Combination of --make-ddi= and --empty=%s is not supported.", empty_mode_to_string(arg_empty));
a121b331
LP
8252
8253 /* Imply automatic sizing in DDI mode */
8254 if (arg_size == UINT64_MAX)
8255 arg_size_auto = true;
8256
8257 if (!arg_copy_source)
8258 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No --copy-source= specified, refusing.");
8259
8260 r = dir_is_empty(arg_copy_source, /* ignore_hidden_or_backup= */ false);
8261 if (r < 0)
8262 return log_error_errno(r, "Failed to determine if '%s' is empty: %m", arg_copy_source);
8263 if (r > 0)
8264 return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Source directory '%s' is empty, refusing to create empty image.", arg_copy_source);
8265
8266 if (sd_id128_is_null(arg_seed) && !arg_randomize) {
8267 /* We don't want that /etc/machine-id leaks into any image built this way, hence
8268 * let's randomize the seed if not specified explicitly */
8269 log_notice("No seed value specified, randomizing generated UUIDs, resulting image will not be reproducible.");
8270 arg_randomize = true;
8271 }
8272
8273 arg_empty = EMPTY_CREATE;
8274 }
8275
243dd1e9
LP
8276 if (arg_empty == EMPTY_UNSET) /* default to refuse mode, if not otherwise specified */
8277 arg_empty = EMPTY_REFUSE;
8278
a26f4a49 8279 if (arg_factory_reset > 0 && IN_SET(arg_empty, EMPTY_FORCE, EMPTY_REQUIRE, EMPTY_CREATE))
e594a3b1 8280 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
a26f4a49 8281 "Combination of --factory-reset=yes and --empty=force/--empty=require/--empty=create is invalid.");
e594a3b1
LP
8282
8283 if (arg_can_factory_reset)
a26f4a49
LP
8284 arg_dry_run = true; /* When --can-factory-reset is specified we don't make changes, hence
8285 * non-dry-run mode makes no sense. Thus, imply dry run mode so that we
8286 * open things strictly read-only. */
ba6bd342
LP
8287 else if (arg_empty == EMPTY_CREATE)
8288 arg_dry_run = false; /* Imply --dry-run=no if we create the loopback file anew. After all we
8289 * cannot really break anyone's partition tables that way. */
a26f4a49 8290
6c05395e
LP
8291 /* Disable pager once we are not just reviewing, but doing things. */
8292 if (!arg_dry_run)
8293 arg_pager_flags |= PAGER_DISABLE;
8294
248f0186 8295 if (arg_empty == EMPTY_CREATE && arg_size == UINT64_MAX && !arg_size_auto)
a26f4a49
LP
8296 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
8297 "If --empty=create is specified, --size= must be specified, too.");
e594a3b1 8298
252d6267
LP
8299 if (arg_image && arg_root)
8300 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
8301 else if (!arg_image && !arg_root && in_initrd()) {
8f47e32a
LP
8302
8303 /* By default operate on /sysusr/ or /sysroot/ when invoked in the initrd. We prefer the
8304 * former, if it is mounted, so that we have deterministic behaviour on systems where /usr/
8305 * is vendor-supplied but the root fs formatted on first boot. */
b409aacb 8306 r = path_is_mount_point("/sysusr/usr");
8f47e32a
LP
8307 if (r <= 0) {
8308 if (r < 0 && r != -ENOENT)
8309 log_debug_errno(r, "Unable to determine whether /sysusr/usr is a mount point, assuming it is not: %m");
8310
8311 arg_root = strdup("/sysroot");
8312 } else
8313 arg_root = strdup("/sysusr");
252d6267
LP
8314 if (!arg_root)
8315 return log_oom();
8316 }
8317
ecb4c5a6
AAF
8318 if (argc > optind) {
8319 arg_node = strdup(argv[optind]);
8320 if (!arg_node)
8321 return log_oom();
8322 }
a26f4a49 8323
252d6267 8324 if (IN_SET(arg_empty, EMPTY_FORCE, EMPTY_REQUIRE, EMPTY_CREATE) && !arg_node && !arg_image)
a26f4a49 8325 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
37a38788 8326 "A path to a device node or image file must be specified when --make-ddi=, --empty=force, --empty=require or --empty=create are used.");
a26f4a49 8327
4cee8333
DDM
8328 if (arg_split && !arg_node)
8329 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
37a38788 8330 "A path to an image file must be specified when --split is used.");
4cee8333 8331
404aea78
LP
8332 if (auto_pcrlock) {
8333 assert(!arg_tpm2_pcrlock);
8334
8335 r = tpm2_pcrlock_search_file(NULL, NULL, &arg_tpm2_pcrlock);
8336 if (r < 0) {
8337 if (r != -ENOENT)
8338 log_warning_errno(r, "Search for pcrlock.json failed, assuming it does not exist: %m");
8339 } else
dc7e34b3 8340 log_debug("Automatically using pcrlock policy '%s'.", arg_tpm2_pcrlock);
404aea78
LP
8341 }
8342
2e9f6072 8343 if (auto_public_key_pcr_mask) {
c588cf97 8344 assert(arg_tpm2_public_key_pcr_mask == 0);
2099cd62 8345 arg_tpm2_public_key_pcr_mask = INDEX_TO_MASK(uint32_t, TPM2_PCR_KERNEL_BOOT);
c588cf97 8346 }
9e437994 8347
404aea78 8348 if (auto_hash_pcr_values && !arg_tpm2_pcrlock) { /* Only lock to PCR 7 if no pcr policy is specified. */
c588cf97
LP
8349 assert(arg_tpm2_n_hash_pcr_values == 0);
8350
8351 if (!GREEDY_REALLOC_APPEND(
8352 arg_tpm2_hash_pcr_values,
8353 arg_tpm2_n_hash_pcr_values,
8354 &TPM2_PCR_VALUE_MAKE(TPM2_PCR_INDEX_DEFAULT, /* hash= */ 0, /* value= */ {}),
8355 1))
8356 return log_oom();
8357 }
889914ef 8358
300b7e76 8359 if (arg_pretty < 0 && isatty_safe(STDOUT_FILENO))
a26d463d
DDM
8360 arg_pretty = true;
8361
9786dfe6
DDM
8362 if (arg_architecture >= 0) {
8363 FOREACH_ARRAY(p, arg_filter_partitions, arg_n_filter_partitions)
8364 *p = gpt_partition_type_override_architecture(*p, arg_architecture);
8365
8366 FOREACH_ARRAY(p, arg_defer_partitions, arg_n_defer_partitions)
8367 *p = gpt_partition_type_override_architecture(*p, arg_architecture);
8368 }
8369
a73144bb 8370 if (private_key && arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) {
0a826408
LB
8371 _cleanup_(erase_and_freep) char *k = NULL;
8372 size_t n = 0;
8373
8374 r = read_full_file_full(
8375 AT_FDCWD, private_key, UINT64_MAX, SIZE_MAX,
8376 READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
8377 NULL,
8378 &k, &n);
8379 if (r < 0)
8380 return log_error_errno(r, "Failed to read key file '%s': %m", private_key);
8381
8382 r = parse_private_key(k, n, &arg_private_key);
8383 if (r < 0)
8384 return r;
a73144bb
LB
8385 } else if (private_key &&
8386 IN_SET(arg_private_key_source_type, OPENSSL_KEY_SOURCE_ENGINE, OPENSSL_KEY_SOURCE_PROVIDER)) {
0a826408
LB
8387 /* This must happen after parse_x509_certificate() is called above, otherwise
8388 * signing later will get stuck as the parsed private key won't have the
8389 * certificate, so this block cannot be inline in ARG_PRIVATE_KEY. */
a73144bb
LB
8390 r = openssl_load_key_from_token(
8391 arg_private_key_source_type,
8392 arg_private_key_source,
8393 private_key,
8394 &arg_private_key);
0a826408
LB
8395 if (r < 0)
8396 return log_error_errno(
8397 r,
a73144bb
LB
8398 "Failed to load key '%s' from OpenSSL private key source %s: %m",
8399 private_key,
8400 arg_private_key_source);
0a826408
LB
8401 }
8402
e594a3b1
LP
8403 return 1;
8404}
8405
8406static int parse_proc_cmdline_factory_reset(void) {
8407 bool b;
8408 int r;
8409
8410 if (arg_factory_reset >= 0) /* Never override what is specified on the process command line */
8411 return 0;
8412
8413 if (!in_initrd()) /* Never honour kernel command line factory reset request outside of the initrd */
8414 return 0;
8415
3787934b 8416 r = proc_cmdline_get_bool("systemd.factory_reset", /* flags = */ 0, &b);
e594a3b1
LP
8417 if (r < 0)
8418 return log_error_errno(r, "Failed to parse systemd.factory_reset kernel command line argument: %m");
8419 if (r > 0) {
8420 arg_factory_reset = b;
8421
8422 if (b)
8423 log_notice("Honouring factory reset requested via kernel command line.");
8424 }
8425
8426 return 0;
8427}
8428
8429static int parse_efi_variable_factory_reset(void) {
8430 _cleanup_free_ char *value = NULL;
8431 int r;
8432
8433 if (arg_factory_reset >= 0) /* Never override what is specified on the process command line */
8434 return 0;
8435
8436 if (!in_initrd()) /* Never honour EFI variable factory reset request outside of the initrd */
8437 return 0;
8438
e6f055cb 8439 r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE(FactoryReset), &value);
6ce691c3
DL
8440 if (r < 0) {
8441 if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
8442 return 0;
e594a3b1 8443 return log_error_errno(r, "Failed to read EFI variable FactoryReset: %m");
6ce691c3 8444 }
e594a3b1
LP
8445
8446 r = parse_boolean(value);
8447 if (r < 0)
8448 return log_error_errno(r, "Failed to parse EFI variable FactoryReset: %m");
8449
8450 arg_factory_reset = r;
8451 if (r)
111a3aae 8452 log_notice("Factory reset requested via EFI variable FactoryReset.");
e594a3b1
LP
8453
8454 return 0;
8455}
8456
8457static int remove_efi_variable_factory_reset(void) {
8458 int r;
8459
e6f055cb 8460 r = efi_set_variable(EFI_SYSTEMD_VARIABLE(FactoryReset), NULL, 0);
6ce691c3
DL
8461 if (r < 0) {
8462 if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
8463 return 0;
e594a3b1 8464 return log_error_errno(r, "Failed to remove EFI variable FactoryReset: %m");
6ce691c3 8465 }
e594a3b1
LP
8466
8467 log_info("Successfully unset EFI variable FactoryReset.");
8468 return 0;
8469}
8470
252d6267
LP
8471static int acquire_root_devno(
8472 const char *p,
8473 const char *root,
8474 int mode,
8475 char **ret,
8476 int *ret_fd) {
8477
37734dc6 8478 _cleanup_free_ char *found_path = NULL, *node = NULL;
252d6267 8479 dev_t devno, fd_devno = MODE_INVALID;
254d1313 8480 _cleanup_close_ int fd = -EBADF;
e594a3b1 8481 struct stat st;
e594a3b1
LP
8482 int r;
8483
a26f4a49
LP
8484 assert(p);
8485 assert(ret);
8486 assert(ret_fd);
8487
f461a28d 8488 fd = chase_and_open(p, root, CHASE_PREFIX_ROOT, mode, &found_path);
e594a3b1 8489 if (fd < 0)
252d6267 8490 return fd;
e594a3b1
LP
8491
8492 if (fstat(fd, &st) < 0)
8493 return -errno;
8494
8495 if (S_ISREG(st.st_mode)) {
252d6267 8496 *ret = TAKE_PTR(found_path);
a26f4a49 8497 *ret_fd = TAKE_FD(fd);
e594a3b1
LP
8498 return 0;
8499 }
8500
252d6267
LP
8501 if (S_ISBLK(st.st_mode)) {
8502 /* Refuse referencing explicit block devices if a root dir is specified, after all we should
5c08da58 8503 * not be able to leave the image the root path constrains us to. */
252d6267
LP
8504 if (root)
8505 return -EPERM;
8506
a26f4a49 8507 fd_devno = devno = st.st_rdev;
252d6267 8508 } else if (S_ISDIR(st.st_mode)) {
e594a3b1
LP
8509
8510 devno = st.st_dev;
a26f4a49 8511 if (major(devno) == 0) {
e594a3b1
LP
8512 r = btrfs_get_block_device_fd(fd, &devno);
8513 if (r == -ENOTTY) /* not btrfs */
8514 return -ENODEV;
8515 if (r < 0)
8516 return r;
8517 }
e594a3b1
LP
8518 } else
8519 return -ENOTBLK;
8520
8521 /* From dm-crypt to backing partition */
8522 r = block_get_originating(devno, &devno);
8e5f3cec
LP
8523 if (r == -ENOENT)
8524 log_debug_errno(r, "Device '%s' has no dm-crypt/dm-verity device, no need to look for underlying block device.", p);
8525 else if (r < 0)
e594a3b1
LP
8526 log_debug_errno(r, "Failed to find underlying block device for '%s', ignoring: %m", p);
8527
8528 /* From partition to whole disk containing it */
8529 r = block_get_whole_disk(devno, &devno);
8530 if (r < 0)
162392b7 8531 log_debug_errno(r, "Failed to find whole disk block device for '%s', ignoring: %m", p);
e594a3b1 8532
37734dc6 8533 r = devname_from_devnum(S_IFBLK, devno, &node);
a26f4a49
LP
8534 if (r < 0)
8535 return log_debug_errno(r, "Failed to determine canonical path for '%s': %m", p);
8536
6bbae9f8 8537 /* Only if we still look at the same block device we can reuse the fd. Otherwise return an
a26f4a49 8538 * invalidated fd. */
37734dc6
YW
8539 if (fd_devno != MODE_INVALID && fd_devno == devno) {
8540 /* Tell udev not to interfere while we are processing the device */
8541 if (flock(fd, arg_dry_run ? LOCK_SH : LOCK_EX) < 0)
8542 return log_error_errno(errno, "Failed to lock device '%s': %m", node);
8543
8544 *ret_fd = TAKE_FD(fd);
8545 } else
8546 *ret_fd = -EBADF;
8547
8548 *ret = TAKE_PTR(node);
a26f4a49 8549 return 0;
e594a3b1
LP
8550}
8551
cc751c75 8552static int find_root(Context *context) {
54632d2e 8553 _cleanup_free_ char *device = NULL;
5980d463 8554 int r;
e594a3b1 8555
cc751c75 8556 assert(context);
a26f4a49 8557
e594a3b1 8558 if (arg_node) {
a26f4a49 8559 if (arg_empty == EMPTY_CREATE) {
254d1313 8560 _cleanup_close_ int fd = -EBADF;
a26f4a49
LP
8561 _cleanup_free_ char *s = NULL;
8562
8563 s = strdup(arg_node);
8564 if (!s)
8565 return log_oom();
8566
4dee4e00 8567 fd = xopenat_full(AT_FDCWD, arg_node, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOFOLLOW, XO_NOCOW, 0666);
a26f4a49
LP
8568 if (fd < 0)
8569 return log_error_errno(errno, "Failed to create '%s': %m", arg_node);
8570
cc751c75 8571 context->node = TAKE_PTR(s);
db1d4e6b 8572 context->node_is_our_file = true;
cc751c75 8573 context->backing_fd = TAKE_FD(fd);
a26f4a49
LP
8574 return 0;
8575 }
8576
252d6267
LP
8577 /* Note that we don't specify a root argument here: if the user explicitly configured a node
8578 * we'll take it relative to the host, not the image */
cc751c75 8579 r = acquire_root_devno(arg_node, NULL, O_RDONLY|O_CLOEXEC, &context->node, &context->backing_fd);
67f0ac8c
LP
8580 if (r == -EUCLEAN)
8581 return btrfs_log_dev_root(LOG_ERR, r, arg_node);
e594a3b1 8582 if (r < 0)
aa2a74ad 8583 return log_error_errno(r, "Failed to open file or determine backing device of %s: %m", arg_node);
e594a3b1
LP
8584
8585 return 0;
8586 }
8587
a26f4a49
LP
8588 assert(IN_SET(arg_empty, EMPTY_REFUSE, EMPTY_ALLOW));
8589
54632d2e
KK
8590 /* If the root mount has been replaced by some form of volatile file system (overlayfs), the
8591 * original root block device node is symlinked in /run/systemd/volatile-root. Let's read that
8592 * here. */
8593 r = readlink_malloc("/run/systemd/volatile-root", &device);
8594 if (r == -ENOENT) { /* volatile-root not found */
8595 /* Let's search for the root device. We look for two cases here: first in /, and then in /usr. The
8596 * latter we check for cases where / is a tmpfs and only /usr is an actual persistent block device
8597 * (think: volatile setups) */
e594a3b1 8598
54632d2e 8599 FOREACH_STRING(p, "/", "/usr") {
e594a3b1 8600
cc751c75
DDM
8601 r = acquire_root_devno(p, arg_root, O_RDONLY|O_DIRECTORY|O_CLOEXEC, &context->node,
8602 &context->backing_fd);
54632d2e
KK
8603 if (r < 0) {
8604 if (r == -EUCLEAN)
8605 return btrfs_log_dev_root(LOG_ERR, r, p);
8606 if (r != -ENODEV)
0b6891ab 8607 return log_error_errno(r, "Failed to determine backing device of %s%s: %m", strempty(arg_root), p);
54632d2e
KK
8608 } else
8609 return 0;
8610 }
8611 } else if (r < 0)
8612 return log_error_errno(r, "Failed to read symlink /run/systemd/volatile-root: %m");
8613 else {
cc751c75 8614 r = acquire_root_devno(device, NULL, O_RDONLY|O_CLOEXEC, &context->node, &context->backing_fd);
54632d2e
KK
8615 if (r == -EUCLEAN)
8616 return btrfs_log_dev_root(LOG_ERR, r, device);
8617 if (r < 0)
8618 return log_error_errno(r, "Failed to open file or determine backing device of %s: %m", device);
8619
8620 return 0;
e594a3b1
LP
8621 }
8622
8623 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "Failed to discover root block device.");
8624}
8625
81dde3d8 8626static int resize_pt(int fd, uint64_t sector_size) {
f9b3afae
LP
8627 _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
8628 int r;
8629
8630 /* After resizing the backing file we need to resize the partition table itself too, so that it takes
8631 * possession of the enlarged backing file. For this it suffices to open the device with libfdisk and
8632 * immediately write it again, with no changes. */
8633
fd9fe57a 8634 r = fdisk_new_context_at(fd, /* path= */ NULL, /* read_only= */ false, sector_size, &c);
f9b3afae 8635 if (r < 0)
ddb6eeaf 8636 return log_error_errno(r, "Failed to open device '%s': %m", FORMAT_PROC_FD_PATH(fd));
f9b3afae
LP
8637
8638 r = fdisk_has_label(c);
8639 if (r < 0)
ddb6eeaf 8640 return log_error_errno(r, "Failed to determine whether disk '%s' has a disk label: %m", FORMAT_PROC_FD_PATH(fd));
f9b3afae
LP
8641 if (r == 0) {
8642 log_debug("Not resizing partition table, as there currently is none.");
8643 return 0;
8644 }
8645
8646 r = fdisk_write_disklabel(c);
8647 if (r < 0)
8648 return log_error_errno(r, "Failed to write resized partition table: %m");
8649
8650 log_info("Resized partition table.");
8651 return 1;
8652}
8653
252d6267
LP
8654static int resize_backing_fd(
8655 const char *node, /* The primary way we access the disk image to operate on */
8656 int *fd, /* An O_RDONLY fd referring to that inode */
8657 const char *backing_file, /* If the above refers to a loopback device, the backing regular file for that, which we can grow */
81dde3d8
LP
8658 LoopDevice *loop_device,
8659 uint64_t sector_size) {
252d6267 8660
254d1313 8661 _cleanup_close_ int writable_fd = -EBADF;
252d6267 8662 uint64_t current_size;
a26f4a49
LP
8663 struct stat st;
8664 int r;
8665
8666 assert(node);
8667 assert(fd);
8668
8669 if (arg_size == UINT64_MAX) /* Nothing to do */
8670 return 0;
8671
8672 if (*fd < 0) {
8673 /* Open the file if we haven't opened it yet. Note that we open it read-only here, just to
8674 * keep a reference to the file we can pass around. */
8675 *fd = open(node, O_RDONLY|O_CLOEXEC);
8676 if (*fd < 0)
8677 return log_error_errno(errno, "Failed to open '%s' in order to adjust size: %m", node);
8678 }
8679
8680 if (fstat(*fd, &st) < 0)
8681 return log_error_errno(errno, "Failed to stat '%s': %m", node);
8682
252d6267
LP
8683 if (S_ISBLK(st.st_mode)) {
8684 if (!backing_file)
8685 return log_error_errno(SYNTHETIC_ERRNO(EBADF), "Cannot resize block device '%s'.", node);
8686
8687 assert(loop_device);
a26f4a49 8688
01db9c85
LP
8689 r = blockdev_get_device_size(*fd, &current_size);
8690 if (r < 0)
8691 return log_error_errno(r, "Failed to determine size of block device %s: %m", node);
252d6267
LP
8692 } else {
8693 r = stat_verify_regular(&st);
8694 if (r < 0)
8695 return log_error_errno(r, "Specified path '%s' is not a regular file or loopback block device, cannot resize: %m", node);
8696
8697 assert(!backing_file);
8698 assert(!loop_device);
8699 current_size = st.st_size;
8700 }
8701
252d6267 8702 if (current_size >= arg_size) {
2b59bf51
ZJS
8703 log_info("File '%s' already is of requested size or larger, not growing. (%s >= %s)",
8704 node, FORMAT_BYTES(current_size), FORMAT_BYTES(arg_size));
a26f4a49
LP
8705 return 0;
8706 }
8707
252d6267
LP
8708 if (S_ISBLK(st.st_mode)) {
8709 assert(backing_file);
8710
8711 /* This is a loopback device. We can't really grow those directly, but we can grow the
8712 * backing file, hence let's do that. */
8713
8714 writable_fd = open(backing_file, O_WRONLY|O_CLOEXEC|O_NONBLOCK);
8715 if (writable_fd < 0)
8716 return log_error_errno(errno, "Failed to open backing file '%s': %m", backing_file);
8717
8718 if (fstat(writable_fd, &st) < 0)
8719 return log_error_errno(errno, "Failed to stat() backing file '%s': %m", backing_file);
8720
8721 r = stat_verify_regular(&st);
8722 if (r < 0)
8723 return log_error_errno(r, "Backing file '%s' of block device is not a regular file: %m", backing_file);
8724
8725 if ((uint64_t) st.st_size != current_size)
8726 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2b59bf51
ZJS
8727 "Size of backing file '%s' of loopback block device '%s' don't match, refusing.",
8728 node, backing_file);
252d6267
LP
8729 } else {
8730 assert(S_ISREG(st.st_mode));
8731 assert(!backing_file);
a26f4a49 8732
252d6267
LP
8733 /* The file descriptor is read-only. In order to grow the file we need to have a writable fd. We
8734 * reopen the file for that temporarily. We keep the writable fd only open for this operation though,
8735 * as fdisk can't accept it anyway. */
8736
8737 writable_fd = fd_reopen(*fd, O_WRONLY|O_CLOEXEC);
8738 if (writable_fd < 0)
8739 return log_error_errno(writable_fd, "Failed to reopen backing file '%s' writable: %m", node);
8740 }
a26f4a49
LP
8741
8742 if (!arg_discard) {
8743 if (fallocate(writable_fd, 0, 0, arg_size) < 0) {
8744 if (!ERRNO_IS_NOT_SUPPORTED(errno))
8745 return log_error_errno(errno, "Failed to grow '%s' from %s to %s by allocation: %m",
2b59bf51 8746 node, FORMAT_BYTES(current_size), FORMAT_BYTES(arg_size));
a26f4a49
LP
8747
8748 /* Fallback to truncation, if fallocate() is not supported. */
8749 log_debug("Backing file system does not support fallocate(), falling back to ftruncate().");
8750 } else {
252d6267 8751 if (current_size == 0) /* Likely regular file just created by us */
2b59bf51 8752 log_info("Allocated %s for '%s'.", FORMAT_BYTES(arg_size), node);
a26f4a49 8753 else
2b59bf51
ZJS
8754 log_info("File '%s' grown from %s to %s by allocation.",
8755 node, FORMAT_BYTES(current_size), FORMAT_BYTES(arg_size));
a26f4a49 8756
252d6267 8757 goto done;
a26f4a49
LP
8758 }
8759 }
8760
8761 if (ftruncate(writable_fd, arg_size) < 0)
8762 return log_error_errno(errno, "Failed to grow '%s' from %s to %s by truncation: %m",
2b59bf51 8763 node, FORMAT_BYTES(current_size), FORMAT_BYTES(arg_size));
a26f4a49 8764
252d6267 8765 if (current_size == 0) /* Likely regular file just created by us */
2b59bf51 8766 log_info("Sized '%s' to %s.", node, FORMAT_BYTES(arg_size));
252d6267 8767 else
2b59bf51
ZJS
8768 log_info("File '%s' grown from %s to %s by truncation.",
8769 node, FORMAT_BYTES(current_size), FORMAT_BYTES(arg_size));
252d6267
LP
8770
8771done:
81dde3d8 8772 r = resize_pt(writable_fd, sector_size);
f9b3afae
LP
8773 if (r < 0)
8774 return r;
8775
252d6267
LP
8776 if (loop_device) {
8777 r = loop_device_refresh_size(loop_device, UINT64_MAX, arg_size);
8778 if (r < 0)
8779 return log_error_errno(r, "Failed to update loop device size: %m");
8780 }
a26f4a49
LP
8781
8782 return 1;
8783}
8784
170c9823 8785static int determine_auto_size(Context *c) {
994b3031 8786 uint64_t sum;
170c9823 8787
ac33e147 8788 assert(c);
170c9823 8789
994b3031
LP
8790 sum = round_up_size(GPT_METADATA_SIZE, 4096);
8791
170c9823
LP
8792 LIST_FOREACH(partitions, p, c->partitions) {
8793 uint64_t m;
8794
2cb9c68c 8795 if (p->dropped || PARTITION_SUPPRESSED(p))
170c9823
LP
8796 continue;
8797
994b3031 8798 m = partition_min_size_with_padding(c, p);
170c9823
LP
8799 if (m > UINT64_MAX - sum)
8800 return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Image would grow too large, refusing.");
8801
8802 sum += m;
8803 }
8804
2b59bf51
ZJS
8805 if (c->total != UINT64_MAX)
8806 /* Image already allocated? Then show its size. */
8807 log_info("Automatically determined minimal disk image size as %s, current image size is %s.",
8808 FORMAT_BYTES(sum), FORMAT_BYTES(c->total));
8809 else
8810 /* If the image is being created right now, then it has no previous size, suppress any comment about it hence. */
8811 log_info("Automatically determined minimal disk image size as %s.",
8812 FORMAT_BYTES(sum));
170c9823
LP
8813
8814 arg_size = sum;
8815 return 0;
8816}
8817
e594a3b1 8818static int run(int argc, char *argv[]) {
252d6267 8819 _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
a4b3e942 8820 _cleanup_(umount_and_freep) char *mounted_dir = NULL;
e594a3b1 8821 _cleanup_(context_freep) Context* context = NULL;
cc751c75 8822 bool node_is_our_loop = false;
e594a3b1
LP
8823 int r;
8824
aa976d87 8825 log_setup();
e594a3b1 8826
e594a3b1
LP
8827 r = parse_argv(argc, argv);
8828 if (r <= 0)
8829 return r;
8830
8831 r = parse_proc_cmdline_factory_reset();
8832 if (r < 0)
8833 return r;
8834
8835 r = parse_efi_variable_factory_reset();
8836 if (r < 0)
8837 return r;
8838
30f19400
LP
8839#if HAVE_LIBCRYPTSETUP
8840 cryptsetup_enable_logging(NULL);
8841#endif
8842
252d6267
LP
8843 if (arg_image) {
8844 assert(!arg_root);
8845
8846 /* Mount this strictly read-only: we shall modify the partition table, not the file
8847 * systems */
8848 r = mount_image_privately_interactively(
8849 arg_image,
84be0c71 8850 arg_image_policy,
252d6267
LP
8851 DISSECT_IMAGE_MOUNT_READ_ONLY |
8852 (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) */
8853 DISSECT_IMAGE_GPT_ONLY |
8854 DISSECT_IMAGE_RELAX_VAR_CHECK |
8855 DISSECT_IMAGE_USR_NO_ROOT |
f4a63ce2
LP
8856 DISSECT_IMAGE_REQUIRE_ROOT |
8857 DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
252d6267 8858 &mounted_dir,
a133d2c3 8859 /* ret_dir_fd= */ NULL,
e330f97a 8860 &loop_device);
252d6267
LP
8861 if (r < 0)
8862 return r;
8863
8864 arg_root = strdup(mounted_dir);
8865 if (!arg_root)
8866 return log_oom();
8867
8868 if (!arg_node) {
8869 arg_node = strdup(loop_device->node);
8870 if (!arg_node)
8871 return log_oom();
8872
3d62af7d 8873 /* Remember that the device we are about to manipulate is actually the one we
252d6267
LP
8874 * allocated here, and thus to increase its backing file we know what to do */
8875 node_is_our_loop = true;
8876 }
8877 }
8878
607343a1
LP
8879 if (!arg_copy_source && arg_root) {
8880 /* If no explicit copy source is specified, then use --root=/--image= */
8881 arg_copy_source = strdup(arg_root);
8882 if (!arg_copy_source)
8883 return log_oom();
8884 }
8885
e594a3b1
LP
8886 context = context_new(arg_seed);
8887 if (!context)
8888 return log_oom();
8889
b8a8000a
YW
8890 r = context_read_seed(context, arg_root);
8891 if (r < 0)
8892 return r;
8893
1e46985a
DDM
8894 r = context_copy_from(context);
8895 if (r < 0)
8896 return r;
8897
a121b331
LP
8898 if (arg_make_ddi) {
8899 _cleanup_free_ char *d = NULL, *dp = NULL;
8900 assert(!arg_definitions);
8901
8902 d = strjoin(arg_make_ddi, ".repart.d/");
8903 if (!d)
8904 return log_oom();
8905
76d75d8b 8906 r = search_and_access(d, F_OK, NULL, CONF_PATHS_STRV("systemd/repart/definitions"), &dp);
a121b331 8907 if (r < 0)
9f08d7f7 8908 return log_error_errno(r, "DDI type '%s' is not defined: %m", arg_make_ddi);
a121b331
LP
8909
8910 if (strv_consume(&arg_definitions, TAKE_PTR(dp)) < 0)
8911 return log_oom();
8912 } else
8913 strv_uniq(arg_definitions);
224c853f 8914
4b047310 8915 r = context_read_definitions(context);
e594a3b1
LP
8916 if (r < 0)
8917 return r;
8918
cc751c75 8919 r = find_root(context);
021b0ff4
DDM
8920 if (r == -ENODEV)
8921 return 76; /* Special return value which means "Root block device not found, so not doing
8922 * anything". This isn't really an error when called at boot. */
0ae5ffe0
YW
8923 if (r < 0)
8924 return r;
8925
a26f4a49 8926 if (arg_size != UINT64_MAX) {
252d6267 8927 r = resize_backing_fd(
cc751c75
DDM
8928 context->node,
8929 &context->backing_fd,
252d6267 8930 node_is_our_loop ? arg_image : NULL,
81dde3d8
LP
8931 node_is_our_loop ? loop_device : NULL,
8932 context->sector_size);
a26f4a49
LP
8933 if (r < 0)
8934 return r;
8935 }
8936
cc751c75 8937 r = context_load_partition_table(context);
e594a3b1
LP
8938 if (r == -EHWPOISON)
8939 return 77; /* Special return value which means "Not GPT, so not doing anything". This isn't
8940 * really an error when called at boot. */
8941 if (r < 0)
8942 return r;
cc751c75 8943 context->from_scratch = r > 0; /* Starting from scratch */
e594a3b1
LP
8944
8945 if (arg_can_factory_reset) {
8946 r = context_can_factory_reset(context);
8947 if (r < 0)
8948 return r;
8949 if (r == 0)
8950 return EXIT_FAILURE;
8951
8952 return 0;
8953 }
8954
cc751c75 8955 r = context_factory_reset(context);
e594a3b1
LP
8956 if (r < 0)
8957 return r;
8958 if (r > 0) {
8959 /* We actually did a factory reset! */
8960 r = remove_efi_variable_factory_reset();
8961 if (r < 0)
8962 return r;
8963
8964 /* Reload the reduced partition table */
8965 context_unload_partition_table(context);
cc751c75 8966 r = context_load_partition_table(context);
e594a3b1
LP
8967 if (r < 0)
8968 return r;
8969 }
8970
8bbbdfd7
DDM
8971 /* Make sure each partition has a unique UUID and unique label */
8972 r = context_acquire_partition_uuids_and_labels(context);
8973 if (r < 0)
8974 return r;
8975
757bc2e4 8976 /* Open all files to copy blocks from now, since we want to take their size into consideration */
5c08da58
LP
8977 r = context_open_copy_block_paths(
8978 context,
7802194a 8979 loop_device ? loop_device->devno : /* if --image= is specified, only allow partitions on the loopback device */
5c08da58
LP
8980 arg_root && !arg_image ? 0 : /* if --root= is specified, don't accept any block device */
8981 (dev_t) -1); /* if neither is specified, make no restrictions */
757bc2e4
LP
8982 if (r < 0)
8983 return r;
8984
1a0541d4
DDM
8985 r = context_fstab(context);
8986 if (r < 0)
8987 return r;
8988
8989 r = context_crypttab(context);
8990 if (r < 0)
8991 return r;
8992
5eef7047
DDM
8993 r = context_minimize(context);
8994 if (r < 0)
8995 return r;
8996
170c9823
LP
8997 if (arg_size_auto) {
8998 r = determine_auto_size(context);
8999 if (r < 0)
9000 return r;
9001
9002 /* Flush out everything again, and let's grow the file first, then start fresh */
9003 context_unload_partition_table(context);
9004
ac33e147 9005 assert(arg_size != UINT64_MAX);
252d6267 9006 r = resize_backing_fd(
cc751c75
DDM
9007 context->node,
9008 &context->backing_fd,
252d6267 9009 node_is_our_loop ? arg_image : NULL,
81dde3d8
LP
9010 node_is_our_loop ? loop_device : NULL,
9011 context->sector_size);
170c9823
LP
9012 if (r < 0)
9013 return r;
9014
cc751c75 9015 r = context_load_partition_table(context);
170c9823
LP
9016 if (r < 0)
9017 return r;
9018 }
9019
e594a3b1
LP
9020 /* First try to fit new partitions in, dropping by priority until it fits */
9021 for (;;) {
14a4c4ed
LP
9022 uint64_t largest_free_area;
9023
9024 if (context_allocate_partitions(context, &largest_free_area))
e594a3b1
LP
9025 break; /* Success! */
9026
2cb9c68c
AV
9027 if (context_unmerge_and_allocate_partitions(context))
9028 break; /* We had to un-suppress a supplement or few, but still success! */
9029
9030 if (context_drop_or_foreignize_one_priority(context))
9031 continue; /* Still no luck. Let's drop a priority and try again. */
9032
9033 /* No more priorities left to drop. This configuration just doesn't fit on this disk... */
9034 r = log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
9035 "Can't fit requested partitions into available free space (%s), refusing.",
9036 FORMAT_BYTES(largest_free_area));
9037 determine_auto_size(context);
9038 return r;
9039 }
9040
9041 LIST_FOREACH(partitions, p, context->partitions) {
9042 if (!p->supplement_for)
9043 continue;
9044
9045 if (PARTITION_SUPPRESSED(p)) {
9046 assert(!p->allocated_to_area);
9047 p->dropped = true;
9048
9049 log_debug("Partition %s can be merged into %s, suppressing supplement.",
9050 p->definition_path, p->supplement_for->definition_path);
9051 } else if (PARTITION_EXISTS(p))
9052 log_info("Partition %s already exists on disk, using supplement verbatim.",
9053 p->definition_path);
9054 else
9055 log_info("Couldn't allocate partitions with %s merged into %s, using supplement verbatim.",
9056 p->definition_path, p->supplement_for->definition_path);
e594a3b1
LP
9057 }
9058
9059 /* Now assign free space according to the weight logic */
9060 r = context_grow_partitions(context);
9061 if (r < 0)
9062 return r;
9063
0b7f574f 9064 /* Now calculate where each new partition gets placed */
e594a3b1
LP
9065 context_place_partitions(context);
9066
cc751c75 9067 (void) context_dump(context, /*late=*/ false);
a26d463d 9068
cc751c75 9069 r = context_write_partition_table(context);
e594a3b1
LP
9070 if (r < 0)
9071 return r;
9072
4cee8333
DDM
9073 r = context_split(context);
9074 if (r < 0)
9075 return r;
9076
cc751c75 9077 (void) context_dump(context, /*late=*/ true);
b5b7879a 9078
db1d4e6b
DDM
9079 context->node = mfree(context->node);
9080
a2d7c42e
DDM
9081 LIST_FOREACH(partitions, p, context->partitions)
9082 p->split_path = mfree(p->split_path);
b5b7879a 9083
e594a3b1
LP
9084 return 0;
9085}
9086
9087DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);