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