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