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