]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/partition/repart.c
tree-wide: use -EBADF for fd initialization
[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
2583 if (p->definition_path) {
2584 buf = strdup(basename(p->definition_path));
2585 goto done;
2586 }
2587
2588 label = partition_label(p);
2589 if (!isempty(label)) {
2590 buf = strdup(label);
2591 goto done;
2592 }
2593
2594 if (p->partno != UINT64_MAX) {
2595 buf = fdisk_partname(node, p->partno+1);
2596 goto done;
2597 }
2598
11749b61 2599 if (p->new_uuid_is_set)
e594a3b1
LP
2600 id = p->new_uuid;
2601 else if (!sd_id128_is_null(p->current_uuid))
2602 id = p->current_uuid;
2603 else
22e932f4 2604 id = p->type.uuid;
e594a3b1 2605
b7416360 2606 buf = strdup(SD_ID128_TO_UUID_STRING(id));
e594a3b1
LP
2607
2608done:
2609 if (!buf)
2610 return -ENOMEM;
2611
2612 *ret = TAKE_PTR(buf);
2613 return 0;
2614}
2615
cc751c75 2616static int context_dump_partition_bar(Context *context) {
e594a3b1
LP
2617 _cleanup_free_ Partition **bar = NULL;
2618 _cleanup_free_ size_t *start_array = NULL;
03677889 2619 Partition *last = NULL;
e594a3b1
LP
2620 bool z = false;
2621 size_t c, j = 0;
2622
f391597c 2623 assert_se((c = columns()) >= 2);
e594a3b1
LP
2624 c -= 2; /* We do not use the leftmost and rightmost character cell */
2625
2626 bar = new0(Partition*, c);
2627 if (!bar)
2628 return log_oom();
2629
2630 start_array = new(size_t, context->n_partitions);
2631 if (!start_array)
2632 return log_oom();
2633
2634 LIST_FOREACH(partitions, p, context->partitions)
2635 context_bar_char_process_partition(context, bar, c, p, start_array + j++);
2636
2637 putc(' ', stdout);
2638
2639 for (size_t i = 0; i < c; i++) {
2640 if (bar[i]) {
2641 if (last != bar[i])
2642 z = !z;
2643
2644 fputs(z ? ansi_green() : ansi_yellow(), stdout);
2645 fputs(special_glyph(SPECIAL_GLYPH_DARK_SHADE), stdout);
2646 } else {
2647 fputs(ansi_normal(), stdout);
2648 fputs(special_glyph(SPECIAL_GLYPH_LIGHT_SHADE), stdout);
2649 }
2650
2651 last = bar[i];
2652 }
2653
2654 fputs(ansi_normal(), stdout);
2655 putc('\n', stdout);
2656
2657 for (size_t i = 0; i < context->n_partitions; i++) {
2658 _cleanup_free_ char **line = NULL;
2659
2660 line = new0(char*, c);
2661 if (!line)
2662 return log_oom();
2663
2664 j = 0;
2665 LIST_FOREACH(partitions, p, context->partitions) {
2666 _cleanup_free_ char *d = NULL;
2667 j++;
2668
2669 if (i < context->n_partitions - j) {
2670
2671 if (line[start_array[j-1]]) {
2672 const char *e;
2673
2674 /* Upgrade final corner to the right with a branch to the right */
2675 e = startswith(line[start_array[j-1]], special_glyph(SPECIAL_GLYPH_TREE_RIGHT));
2676 if (e) {
2677 d = strjoin(special_glyph(SPECIAL_GLYPH_TREE_BRANCH), e);
2678 if (!d)
2679 return log_oom();
2680 }
2681 }
2682
2683 if (!d) {
2684 d = strdup(special_glyph(SPECIAL_GLYPH_TREE_VERTICAL));
2685 if (!d)
2686 return log_oom();
2687 }
2688
2689 } else if (i == context->n_partitions - j) {
2690 _cleanup_free_ char *hint = NULL;
2691
cc751c75 2692 (void) partition_hint(p, context->node, &hint);
e594a3b1
LP
2693
2694 if (streq_ptr(line[start_array[j-1]], special_glyph(SPECIAL_GLYPH_TREE_VERTICAL)))
2695 d = strjoin(special_glyph(SPECIAL_GLYPH_TREE_BRANCH), " ", strna(hint));
2696 else
2697 d = strjoin(special_glyph(SPECIAL_GLYPH_TREE_RIGHT), " ", strna(hint));
2698
2699 if (!d)
2700 return log_oom();
2701 }
2702
2703 if (d)
2704 free_and_replace(line[start_array[j-1]], d);
2705 }
2706
2707 putc(' ', stdout);
2708
2709 j = 0;
2710 while (j < c) {
2711 if (line[j]) {
2712 fputs(line[j], stdout);
2713 j += utf8_console_width(line[j]);
2714 } else {
2715 putc(' ', stdout);
2716 j++;
2717 }
2718 }
2719
2720 putc('\n', stdout);
2721
2722 for (j = 0; j < c; j++)
2723 free(line[j]);
2724 }
2725
2726 return 0;
2727}
2728
b5b7879a
DDM
2729static bool context_has_roothash(Context *context) {
2730 LIST_FOREACH(partitions, p, context->partitions)
2731 if (p->roothash)
2732 return true;
2733
2734 return false;
2735}
2736
cc751c75 2737static int context_dump(Context *context, bool late) {
a26d463d
DDM
2738 int r;
2739
2740 assert(context);
a26d463d
DDM
2741
2742 if (arg_pretty == 0 && FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF))
2743 return 0;
2744
b5b7879a
DDM
2745 /* If we're outputting JSON, only dump after doing all operations so we can include the roothashes
2746 * in the output. */
2747 if (!late && !FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF))
2748 return 0;
2749
2750 /* If we're not outputting JSON, only dump again after doing all operations if there are any
2751 * roothashes that we need to communicate to the user. */
2752 if (late && FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF) && !context_has_roothash(context))
2753 return 0;
2754
cc751c75 2755 r = context_dump_partitions(context);
a26d463d
DDM
2756 if (r < 0)
2757 return r;
2758
b5b7879a
DDM
2759 /* Make sure we only write the partition bar once, even if we're writing the partition table twice to
2760 * communicate roothashes. */
2761 if (FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF) && !late) {
a26d463d
DDM
2762 putc('\n', stdout);
2763
cc751c75 2764 r = context_dump_partition_bar(context);
a26d463d
DDM
2765 if (r < 0)
2766 return r;
2767
2768 putc('\n', stdout);
2769 }
2770
2771 fflush(stdout);
2772
2773 return 0;
2774}
2775
2776
e594a3b1 2777static bool context_changed(const Context *context) {
03677889 2778 assert(context);
e594a3b1
LP
2779
2780 LIST_FOREACH(partitions, p, context->partitions) {
2781 if (p->dropped)
2782 continue;
2783
2784 if (p->allocated_to_area)
2785 return true;
2786
2787 if (p->new_size != p->current_size)
2788 return true;
2789 }
2790
2791 return false;
2792}
2793
81873a6b 2794static int context_wipe_range(Context *context, uint64_t offset, uint64_t size) {
e594a3b1
LP
2795 _cleanup_(blkid_free_probep) blkid_probe probe = NULL;
2796 int r;
2797
2798 assert(context);
81873a6b
LP
2799 assert(offset != UINT64_MAX);
2800 assert(size != UINT64_MAX);
e594a3b1
LP
2801
2802 probe = blkid_new_probe();
2803 if (!probe)
2804 return log_oom();
2805
e594a3b1 2806 errno = 0;
81873a6b 2807 r = blkid_probe_set_device(probe, fdisk_get_devfd(context->fdisk_context), offset, size);
e594a3b1 2808 if (r < 0)
81873a6b 2809 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to allocate device probe for wiping.");
e594a3b1
LP
2810
2811 errno = 0;
2812 if (blkid_probe_enable_superblocks(probe, true) < 0 ||
2813 blkid_probe_set_superblocks_flags(probe, BLKID_SUBLKS_MAGIC|BLKID_SUBLKS_BADCSUM) < 0 ||
2814 blkid_probe_enable_partitions(probe, true) < 0 ||
2815 blkid_probe_set_partitions_flags(probe, BLKID_PARTS_MAGIC) < 0)
81873a6b 2816 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to enable superblock and partition probing.");
e594a3b1
LP
2817
2818 for (;;) {
2819 errno = 0;
2820 r = blkid_do_probe(probe);
2821 if (r < 0)
2822 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe for file systems.");
2823 if (r > 0)
2824 break;
2825
2826 errno = 0;
2827 if (blkid_do_wipe(probe, false) < 0)
2828 return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to wipe file system signature.");
2829 }
2830
e594a3b1
LP
2831 return 0;
2832}
2833
81873a6b
LP
2834static int context_wipe_partition(Context *context, Partition *p) {
2835 int r;
2836
2837 assert(context);
2838 assert(p);
2839 assert(!PARTITION_EXISTS(p)); /* Safety check: never wipe existing partitions */
2840
2841 assert(p->offset != UINT64_MAX);
2842 assert(p->new_size != UINT64_MAX);
2843
2844 r = context_wipe_range(context, p->offset, p->new_size);
2845 if (r < 0)
2846 return r;
2847
2848 log_info("Successfully wiped file system signatures from future partition %" PRIu64 ".", p->partno);
2849 return 0;
2850}
2851
2852static int context_discard_range(
2853 Context *context,
2854 uint64_t offset,
2855 uint64_t size) {
2856
e594a3b1
LP
2857 struct stat st;
2858 int fd;
2859
2860 assert(context);
2861 assert(offset != UINT64_MAX);
2862 assert(size != UINT64_MAX);
2863
2864 if (size <= 0)
2865 return 0;
2866
a26f4a49 2867 assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
e594a3b1
LP
2868
2869 if (fstat(fd, &st) < 0)
2870 return -errno;
2871
2872 if (S_ISREG(st.st_mode)) {
2873 if (fallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, offset, size) < 0) {
2874 if (ERRNO_IS_NOT_SUPPORTED(errno))
2875 return -EOPNOTSUPP;
2876
2877 return -errno;
2878 }
2879
2880 return 1;
2881 }
2882
2883 if (S_ISBLK(st.st_mode)) {
2884 uint64_t range[2], end;
2885
994b3031 2886 range[0] = round_up_size(offset, context->sector_size);
e594a3b1 2887
55d38014
LP
2888 if (offset > UINT64_MAX - size)
2889 return -ERANGE;
2890
e594a3b1
LP
2891 end = offset + size;
2892 if (end <= range[0])
2893 return 0;
2894
994b3031 2895 range[1] = round_down_size(end - range[0], context->sector_size);
e594a3b1
LP
2896 if (range[1] <= 0)
2897 return 0;
2898
2899 if (ioctl(fd, BLKDISCARD, range) < 0) {
2900 if (ERRNO_IS_NOT_SUPPORTED(errno))
2901 return -EOPNOTSUPP;
2902
2903 return -errno;
2904 }
2905
2906 return 1;
2907 }
2908
2909 return -EOPNOTSUPP;
2910}
2911
2912static int context_discard_partition(Context *context, Partition *p) {
2913 int r;
2914
2915 assert(context);
2916 assert(p);
2917
2918 assert(p->offset != UINT64_MAX);
2919 assert(p->new_size != UINT64_MAX);
2920 assert(!PARTITION_EXISTS(p)); /* Safety check: never discard existing partitions */
2921
2922 if (!arg_discard)
2923 return 0;
2924
2925 r = context_discard_range(context, p->offset, p->new_size);
2926 if (r == -EOPNOTSUPP) {
5b5109e2 2927 log_info("Storage does not support discard, not discarding data in future partition %" PRIu64 ".", p->partno);
e594a3b1
LP
2928 return 0;
2929 }
22163eb5
LP
2930 if (r == -EBUSY) {
2931 /* Let's handle this gracefully: https://bugzilla.kernel.org/show_bug.cgi?id=211167 */
2932 log_info("Block device is busy, not discarding partition %" PRIu64 " because it probably is mounted.", p->partno);
2933 return 0;
2934 }
e594a3b1
LP
2935 if (r == 0) {
2936 log_info("Partition %" PRIu64 " too short for discard, skipping.", p->partno);
2937 return 0;
2938 }
2939 if (r < 0)
5b5109e2 2940 return log_error_errno(r, "Failed to discard data for future partition %" PRIu64 ".", p->partno);
e594a3b1 2941
5b5109e2 2942 log_info("Successfully discarded data from future partition %" PRIu64 ".", p->partno);
e594a3b1
LP
2943 return 1;
2944}
2945
2946static int context_discard_gap_after(Context *context, Partition *p) {
2947 uint64_t gap, next = UINT64_MAX;
e594a3b1
LP
2948 int r;
2949
2950 assert(context);
2951 assert(!p || (p->offset != UINT64_MAX && p->new_size != UINT64_MAX));
2952
5113436b
AF
2953 if (!arg_discard)
2954 return 0;
2955
e594a3b1
LP
2956 if (p)
2957 gap = p->offset + p->new_size;
2958 else
2959 gap = context->start;
2960
2961 LIST_FOREACH(partitions, q, context->partitions) {
2962 if (q->dropped)
2963 continue;
2964
2965 assert(q->offset != UINT64_MAX);
2966 assert(q->new_size != UINT64_MAX);
2967
2968 if (q->offset < gap)
2969 continue;
2970
2971 if (next == UINT64_MAX || q->offset < next)
2972 next = q->offset;
2973 }
2974
2975 if (next == UINT64_MAX) {
2976 next = context->end;
2977 if (gap > next)
2978 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition end beyond disk end.");
2979 }
2980
2981 assert(next >= gap);
2982 r = context_discard_range(context, gap, next - gap);
2983 if (r == -EOPNOTSUPP) {
2984 if (p)
5b5109e2 2985 log_info("Storage does not support discard, not discarding gap after partition %" PRIu64 ".", p->partno);
e594a3b1 2986 else
5b5109e2 2987 log_info("Storage does not support discard, not discarding gap at beginning of disk.");
e594a3b1
LP
2988 return 0;
2989 }
2990 if (r == 0) /* Too short */
2991 return 0;
2992 if (r < 0) {
2993 if (p)
2994 return log_error_errno(r, "Failed to discard gap after partition %" PRIu64 ".", p->partno);
2995 else
2996 return log_error_errno(r, "Failed to discard gap at beginning of disk.");
2997 }
2998
2999 if (p)
3000 log_info("Successfully discarded gap after partition %" PRIu64 ".", p->partno);
3001 else
3002 log_info("Successfully discarded gap at beginning of disk.");
3003
3004 return 0;
3005}
3006
cc751c75 3007static int context_wipe_and_discard(Context *context) {
e594a3b1
LP
3008 int r;
3009
3010 assert(context);
3011
3012 /* Wipe and discard the contents of all partitions we are about to create. We skip the discarding if
3013 * we were supposed to start from scratch anyway, as in that case we just discard the whole block
3014 * device in one go early on. */
3015
3016 LIST_FOREACH(partitions, p, context->partitions) {
3017
3018 if (!p->allocated_to_area)
3019 continue;
3020
8275334b 3021 if (partition_defer(p))
81d1098b
DDM
3022 continue;
3023
e594a3b1
LP
3024 r = context_wipe_partition(context, p);
3025 if (r < 0)
3026 return r;
3027
cc751c75 3028 if (!context->from_scratch) {
f0cb1b95
LP
3029 r = context_discard_partition(context, p);
3030 if (r < 0)
3031 return r;
3032
e594a3b1
LP
3033 r = context_discard_gap_after(context, p);
3034 if (r < 0)
3035 return r;
3036 }
3037 }
3038
cc751c75 3039 if (!context->from_scratch) {
e594a3b1
LP
3040 r = context_discard_gap_after(context, NULL);
3041 if (r < 0)
3042 return r;
3043 }
3044
3045 return 0;
3046}
3047
a64769d6
DDM
3048typedef struct {
3049 LoopDevice *loop;
3050 int fd;
3051 char *path;
3052 int whole_fd;
3053} PartitionTarget;
3054
3055static int partition_target_fd(PartitionTarget *t) {
3056 assert(t);
3057 assert(t->loop || t->fd >= 0 || t->whole_fd >= 0);
3058 return t->loop ? t->loop->fd : t->fd >= 0 ? t->fd : t->whole_fd;
3059}
3060
3061static const char* partition_target_path(PartitionTarget *t) {
3062 assert(t);
3063 assert(t->loop || t->path);
3064 return t->loop ? t->loop->node : t->path;
3065}
3066
3067static PartitionTarget *partition_target_free(PartitionTarget *t) {
3068 if (!t)
3069 return NULL;
3070
3071 loop_device_unref(t->loop);
3072 safe_close(t->fd);
3073 unlink_and_free(t->path);
3074
3075 return mfree(t);
3076}
3077
3078DEFINE_TRIVIAL_CLEANUP_FUNC(PartitionTarget*, partition_target_free);
3079
d3201eb4
DDM
3080static int prepare_temporary_file(PartitionTarget *t, uint64_t size) {
3081 _cleanup_(unlink_and_freep) char *temp = NULL;
254d1313 3082 _cleanup_close_ int fd = -EBADF;
d3201eb4
DDM
3083 const char *vt;
3084 int r;
3085
3086 assert(t);
3087
3088 r = var_tmp_dir(&vt);
3089 if (r < 0)
3090 return log_error_errno(r, "Could not determine temporary directory: %m");
3091
3092 temp = path_join(vt, "repart-XXXXXX");
3093 if (!temp)
3094 return log_oom();
3095
3096 fd = mkostemp_safe(temp);
3097 if (fd < 0)
3098 return log_error_errno(fd, "Failed to create temporary file: %m");
3099
3100 if (ftruncate(fd, size) < 0)
3101 return log_error_errno(errno, "Failed to truncate temporary file to %s: %m",
3102 FORMAT_BYTES(size));
3103
3104 t->fd = TAKE_FD(fd);
3105 t->path = TAKE_PTR(temp);
3106
3107 return 0;
3108}
3109
a64769d6
DDM
3110static int partition_target_prepare(
3111 Context *context,
3112 Partition *p,
3113 uint64_t size,
3114 bool need_path,
3115 PartitionTarget **ret) {
3116
3117 _cleanup_(partition_target_freep) PartitionTarget *t = NULL;
d3201eb4
DDM
3118 _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
3119 int whole_fd, r;
a64769d6
DDM
3120
3121 assert(context);
3122 assert(p);
3123 assert(ret);
3124
3125 assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
3126
6b1ea55e 3127 t = new(PartitionTarget, 1);
a64769d6
DDM
3128 if (!t)
3129 return log_oom();
6b1ea55e 3130 *t = (PartitionTarget) {
254d1313
ZJS
3131 .fd = -EBADF,
3132 .whole_fd = -EBADF,
6b1ea55e 3133 };
a64769d6 3134
d3201eb4
DDM
3135 if (!need_path) {
3136 if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1)
3137 return log_error_errno(errno, "Failed to seek to partition offset: %m");
a64769d6 3138
d3201eb4
DDM
3139 t->whole_fd = whole_fd;
3140 *ret = TAKE_PTR(t);
3141 return 0;
3142 }
a64769d6 3143
d3201eb4
DDM
3144 /* Loopback block devices are not only useful to turn regular files into block devices, but
3145 * also to cut out sections of block devices into new block devices. */
a64769d6 3146
d3201eb4
DDM
3147 r = loop_device_make(whole_fd, O_RDWR, p->offset, size, 0, 0, LOCK_EX, &d);
3148 if (r < 0 && r != -ENOENT && !ERRNO_IS_PRIVILEGE(r))
3149 return log_error_errno(r, "Failed to make loopback device of future partition %" PRIu64 ": %m", p->partno);
3150 if (r >= 0) {
6b1ea55e 3151 t->loop = TAKE_PTR(d);
d3201eb4
DDM
3152 *ret = TAKE_PTR(t);
3153 return 0;
3154 }
a64769d6 3155
d3201eb4
DDM
3156 /* If we can't allocate a loop device, let's write to a regular file that we copy into the final
3157 * image so we can run in containers and without needing root privileges. On filesystems with
3158 * reflinking support, we can take advantage of this and just reflink the result into the image.
3159 */
a64769d6 3160
d3201eb4 3161 log_debug_errno(r, "No access to loop devices, falling back to a regular file");
a64769d6 3162
d3201eb4
DDM
3163 r = prepare_temporary_file(t, size);
3164 if (r < 0)
3165 return r;
a64769d6
DDM
3166
3167 *ret = TAKE_PTR(t);
3168
3169 return 0;
3170}
3171
3172static int partition_target_grow(PartitionTarget *t, uint64_t size) {
3173 int r;
3174
3175 assert(t);
3176
3177 if (t->loop) {
3178 r = loop_device_refresh_size(t->loop, UINT64_MAX, size);
3179 if (r < 0)
3180 return log_error_errno(r, "Failed to refresh loopback device size: %m");
3181 } else if (t->fd >= 0) {
3182 if (ftruncate(t->fd, size) < 0)
3183 return log_error_errno(errno, "Failed to grow '%s' to %s by truncation: %m",
3184 t->path, FORMAT_BYTES(size));
3185 }
3186
3187 return 0;
3188}
3189
3190static int partition_target_sync(Context *context, Partition *p, PartitionTarget *t) {
3191 int whole_fd, r;
3192
3193 assert(context);
3194 assert(p);
3195 assert(t);
3196
3197 assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
3198
3199 if (t->loop) {
3200 r = loop_device_sync(t->loop);
3201 if (r < 0)
3202 return log_error_errno(r, "Failed to sync loopback device: %m");
3203 } else if (t->fd >= 0) {
3204 if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1)
3205 return log_error_errno(errno, "Failed to seek to partition offset: %m");
3206
3207 r = copy_bytes(t->fd, whole_fd, UINT64_MAX, COPY_REFLINK|COPY_HOLES|COPY_FSYNC);
3208 if (r < 0)
3209 return log_error_errno(r, "Failed to copy bytes to partition: %m");
3210 } else {
3211 if (fsync(t->whole_fd) < 0)
3212 return log_error_errno(errno, "Failed to sync changes: %m");
3213 }
3214
3215 return 0;
3216}
3217
48a09a8f
DDM
3218static int partition_encrypt(Context *context, Partition *p, const char *node) {
3219#if HAVE_LIBCRYPTSETUP && HAVE_CRYPT_SET_DATA_OFFSET && HAVE_CRYPT_REENCRYPT_INIT_BY_PASSPHRASE && HAVE_CRYPT_REENCRYPT
3220 struct crypt_params_luks2 luks_params = {
922576e4
DDM
3221 .label = strempty(ASSERT_PTR(p)->new_label),
3222 .sector_size = ASSERT_PTR(context)->sector_size,
48a09a8f
DDM
3223 .data_device = node,
3224 };
3225 struct crypt_params_reencrypt reencrypt_params = {
3226 .mode = CRYPT_REENCRYPT_ENCRYPT,
3227 .direction = CRYPT_REENCRYPT_BACKWARD,
3228 .resilience = "datashift",
3229 .data_shift = LUKS2_METADATA_SIZE / 512,
3230 .luks2 = &luks_params,
3231 .flags = CRYPT_REENCRYPT_INITIALIZE_ONLY|CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT,
3232 };
0d12936d 3233 _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
48a09a8f
DDM
3234 _cleanup_(erase_and_freep) char *base64_encoded = NULL;
3235 _cleanup_fclose_ FILE *h = NULL;
3236 _cleanup_free_ char *hp = NULL;
3237 const char *passphrase = NULL;
3238 size_t passphrase_size = 0;
0b75744d 3239 const char *vt;
b9df3536
LP
3240 int r;
3241
994b3031 3242 assert(context);
b9df3536 3243 assert(p);
889914ef
LP
3244 assert(p->encrypt != ENCRYPT_OFF);
3245
0d12936d
LP
3246 r = dlopen_cryptsetup();
3247 if (r < 0)
3248 return log_error_errno(r, "libcryptsetup not found, cannot encrypt: %m");
3249
b9df3536
LP
3250 log_info("Encrypting future partition %" PRIu64 "...", p->partno);
3251
0b75744d
DDM
3252 r = var_tmp_dir(&vt);
3253 if (r < 0)
3254 return log_error_errno(r, "Failed to determine temporary files directory: %m");
3255
3256 r = fopen_temporary_child(vt, &h, &hp);
48a09a8f
DDM
3257 if (r < 0)
3258 return log_error_errno(r, "Failed to create temporary LUKS header file: %m");
3259
3260 /* Weird cryptsetup requirement which requires the header file to be the size of at least one sector. */
983154f5 3261 r = ftruncate(fileno(h), context->sector_size);
b9df3536 3262 if (r < 0)
48a09a8f
DDM
3263 return log_error_errno(r, "Failed to grow temporary LUKS header file: %m");
3264
3265 r = sym_crypt_init(&cd, hp);
3266 if (r < 0)
3267 return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", hp);
b9df3536
LP
3268
3269 cryptsetup_enable_logging(cd);
3270
48a09a8f
DDM
3271 /* Disable kernel keyring usage by libcryptsetup as a workaround for
3272 * https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/273. This makes sure that we can do
3273 * offline encryption even when repart is running in a container. */
3274 r = sym_crypt_volume_key_keyring(cd, false);
3275 if (r < 0)
3276 return log_error_errno(r, "Failed to disable kernel keyring: %m");
3277
3278 r = sym_crypt_metadata_locking(cd, false);
3279 if (r < 0)
3280 return log_error_errno(r, "Failed to disable metadata locking: %m");
3281
3282 r = sym_crypt_set_data_offset(cd, LUKS2_METADATA_SIZE / 512);
3283 if (r < 0)
3284 return log_error_errno(r, "Failed to set data offset: %m");
3285
0d12936d 3286 r = sym_crypt_format(cd,
b9df3536
LP
3287 CRYPT_LUKS2,
3288 "aes",
3289 "xts-plain64",
15cad3a2 3290 SD_ID128_TO_UUID_STRING(p->luks_uuid),
98e0456e
DDM
3291 NULL,
3292 VOLUME_KEY_SIZE,
48a09a8f 3293 &luks_params);
b9df3536
LP
3294 if (r < 0)
3295 return log_error_errno(r, "Failed to LUKS2 format future partition: %m");
3296
889914ef
LP
3297 if (IN_SET(p->encrypt, ENCRYPT_KEY_FILE, ENCRYPT_KEY_FILE_TPM2)) {
3298 r = sym_crypt_keyslot_add_by_volume_key(
3299 cd,
3300 CRYPT_ANY_SLOT,
98e0456e
DDM
3301 NULL,
3302 VOLUME_KEY_SIZE,
889914ef
LP
3303 strempty(arg_key),
3304 arg_key_size);
3305 if (r < 0)
3306 return log_error_errno(r, "Failed to add LUKS2 key: %m");
48a09a8f
DDM
3307
3308 passphrase = strempty(arg_key);
3309 passphrase_size = arg_key_size;
889914ef
LP
3310 }
3311
3312 if (IN_SET(p->encrypt, ENCRYPT_TPM2, ENCRYPT_KEY_FILE_TPM2)) {
3313#if HAVE_TPM2
889914ef
LP
3314 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
3315 _cleanup_(erase_and_freep) void *secret = NULL;
02ef97cd 3316 _cleanup_free_ void *pubkey = NULL;
889914ef 3317 _cleanup_free_ void *blob = NULL, *hash = NULL;
02ef97cd 3318 size_t secret_size, blob_size, hash_size, pubkey_size = 0;
2b92a672 3319 uint16_t pcr_bank, primary_alg;
889914ef
LP
3320 int keyslot;
3321
02ef97cd
LP
3322 if (arg_tpm2_public_key_pcr_mask != 0) {
3323 r = tpm2_load_pcr_public_key(arg_tpm2_public_key, &pubkey, &pubkey_size);
3324 if (r < 0) {
3325 if (arg_tpm2_public_key || r != -ENOENT)
3326 return log_error_errno(r, "Failed read TPM PCR public key: %m");
3327
3328 log_debug_errno(r, "Failed to read TPM2 PCR public key, proceeding without: %m");
3329 arg_tpm2_public_key_pcr_mask = 0;
3330 }
3331 }
3332
d9b5841d
LP
3333 r = tpm2_seal(arg_tpm2_device,
3334 arg_tpm2_pcr_mask,
02ef97cd
LP
3335 pubkey, pubkey_size,
3336 arg_tpm2_public_key_pcr_mask,
d9b5841d
LP
3337 /* pin= */ NULL,
3338 &secret, &secret_size,
3339 &blob, &blob_size,
3340 &hash, &hash_size,
3341 &pcr_bank,
3342 &primary_alg);
889914ef
LP
3343 if (r < 0)
3344 return log_error_errno(r, "Failed to seal to TPM2: %m");
3345
3346 r = base64mem(secret, secret_size, &base64_encoded);
3347 if (r < 0)
3348 return log_error_errno(r, "Failed to base64 encode secret key: %m");
3349
3350 r = cryptsetup_set_minimal_pbkdf(cd);
3351 if (r < 0)
3352 return log_error_errno(r, "Failed to set minimal PBKDF: %m");
3353
3354 keyslot = sym_crypt_keyslot_add_by_volume_key(
3355 cd,
3356 CRYPT_ANY_SLOT,
98e0456e
DDM
3357 NULL,
3358 VOLUME_KEY_SIZE,
889914ef
LP
3359 base64_encoded,
3360 strlen(base64_encoded));
3361 if (keyslot < 0)
48a09a8f 3362 return log_error_errno(keyslot, "Failed to add new TPM2 key: %m");
889914ef 3363
f0f4fcae
LP
3364 r = tpm2_make_luks2_json(
3365 keyslot,
3366 arg_tpm2_pcr_mask,
3367 pcr_bank,
02ef97cd
LP
3368 pubkey, pubkey_size,
3369 arg_tpm2_public_key_pcr_mask,
f0f4fcae
LP
3370 primary_alg,
3371 blob, blob_size,
3372 hash, hash_size,
3373 0,
3374 &v);
889914ef
LP
3375 if (r < 0)
3376 return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");
3377
3378 r = cryptsetup_add_token_json(cd, v);
3379 if (r < 0)
3380 return log_error_errno(r, "Failed to add TPM2 JSON token to LUKS2 header: %m");
48a09a8f
DDM
3381
3382 passphrase = base64_encoded;
3383 passphrase_size = strlen(base64_encoded);
889914ef
LP
3384#else
3385 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
3386 "Support for TPM2 enrollment not enabled.");
3387#endif
3388 }
b9df3536 3389
48a09a8f 3390 r = sym_crypt_reencrypt_init_by_passphrase(
b9df3536 3391 cd,
98e0456e 3392 NULL,
48a09a8f
DDM
3393 passphrase,
3394 passphrase_size,
3395 CRYPT_ANY_SLOT,
3396 0,
3397 sym_crypt_get_cipher(cd),
3398 sym_crypt_get_cipher_mode(cd),
3399 &reencrypt_params);
b9df3536 3400 if (r < 0)
48a09a8f 3401 return log_error_errno(r, "Failed to prepare for reencryption: %m");
b9df3536 3402
48a09a8f
DDM
3403 /* crypt_reencrypt_init_by_passphrase() doesn't actually put the LUKS header at the front, we have
3404 * to do that ourselves. */
b9df3536 3405
48a09a8f
DDM
3406 sym_crypt_free(cd);
3407 cd = NULL;
b9df3536 3408
48a09a8f
DDM
3409 r = sym_crypt_init(&cd, node);
3410 if (r < 0)
3411 return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", node);
b9df3536 3412
48a09a8f
DDM
3413 r = sym_crypt_header_restore(cd, CRYPT_LUKS2, hp);
3414 if (r < 0)
3415 return log_error_errno(r, "Failed to place new LUKS header at head of %s: %m", node);
b9df3536 3416
48a09a8f 3417 reencrypt_params.flags &= ~CRYPT_REENCRYPT_INITIALIZE_ONLY;
b9df3536 3418
48a09a8f
DDM
3419 r = sym_crypt_reencrypt_init_by_passphrase(
3420 cd,
3421 NULL,
3422 passphrase,
3423 passphrase_size,
3424 CRYPT_ANY_SLOT,
3425 0,
3426 NULL,
3427 NULL,
3428 &reencrypt_params);
3429 if (r < 0)
3430 return log_error_errno(r, "Failed to load reencryption context: %m");
b9df3536 3431
48a09a8f 3432 r = sym_crypt_reencrypt(cd, NULL);
b9df3536 3433 if (r < 0)
48a09a8f
DDM
3434 return log_error_errno(r, "Failed to encrypt %s: %m", node);
3435
3436 log_info("Successfully encrypted future partition %" PRIu64 ".", p->partno);
b9df3536 3437
3dd8ae5c 3438 return 0;
48a09a8f
DDM
3439#else
3440 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
3441 "libcryptsetup is not supported or is missing required symbols, cannot encrypt: %m");
3dd8ae5c 3442#endif
b9df3536
LP
3443}
3444
2b392d86
DDM
3445static int partition_format_verity_hash(
3446 Context *context,
3447 Partition *p,
3448 const char *data_node) {
3449
3450#if HAVE_LIBCRYPTSETUP
3451 Partition *dp;
a64769d6 3452 _cleanup_(partition_target_freep) PartitionTarget *t = NULL;
2b392d86
DDM
3453 _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
3454 _cleanup_free_ uint8_t *rh = NULL;
3455 size_t rhs;
a64769d6 3456 int r;
2b392d86
DDM
3457
3458 assert(context);
3459 assert(p);
3460 assert(data_node);
3461
3462 if (p->dropped)
3463 return 0;
3464
3465 if (PARTITION_EXISTS(p)) /* Never format existing partitions */
3466 return 0;
3467
3468 if (p->verity != VERITY_HASH)
3469 return 0;
3470
8275334b 3471 if (partition_defer(p))
2b392d86
DDM
3472 return 0;
3473
3474 assert_se(dp = p->siblings[VERITY_DATA]);
3475 assert(!dp->dropped);
3476
2b392d86
DDM
3477 r = dlopen_cryptsetup();
3478 if (r < 0)
3479 return log_error_errno(r, "libcryptsetup not found, cannot setup verity: %m");
3480
a64769d6 3481 r = partition_target_prepare(context, p, p->new_size, /*need_path=*/ true, &t);
2b392d86 3482 if (r < 0)
a64769d6 3483 return r;
2b392d86 3484
a64769d6 3485 r = sym_crypt_init(&cd, partition_target_path(t));
2b392d86
DDM
3486 if (r < 0)
3487 return log_error_errno(r, "Failed to allocate libcryptsetup context: %m");
3488
3489 r = sym_crypt_format(
3490 cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0,
3491 &(struct crypt_params_verity){
3492 .data_device = data_node,
3493 .flags = CRYPT_VERITY_CREATE_HASH,
3494 .hash_name = "sha256",
3495 .hash_type = 1,
3496 .data_block_size = context->sector_size,
3497 .hash_block_size = context->sector_size,
3498 .salt_size = 32,
3499 });
3500 if (r < 0)
3501 return log_error_errno(r, "Failed to setup verity hash data: %m");
3502
a64769d6
DDM
3503 r = partition_target_sync(context, p, t);
3504 if (r < 0)
3505 return r;
3506
2b392d86
DDM
3507 r = sym_crypt_get_volume_key_size(cd);
3508 if (r < 0)
3509 return log_error_errno(r, "Failed to determine verity root hash size: %m");
3510 rhs = (size_t) r;
3511
3512 rh = malloc(rhs);
3513 if (!rh)
3514 return log_oom();
3515
3516 r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, (char *) rh, &rhs, NULL, 0);
3517 if (r < 0)
3518 return log_error_errno(r, "Failed to get verity root hash: %m");
3519
3520 assert(rhs >= sizeof(sd_id128_t) * 2);
3521
3522 if (!dp->new_uuid_is_set) {
3523 memcpy_safe(dp->new_uuid.bytes, rh, sizeof(sd_id128_t));
3524 dp->new_uuid_is_set = true;
3525 }
3526
3527 if (!p->new_uuid_is_set) {
3528 memcpy_safe(p->new_uuid.bytes, rh + rhs - sizeof(sd_id128_t), sizeof(sd_id128_t));
3529 p->new_uuid_is_set = true;
3530 }
3531
3532 p->roothash = TAKE_PTR(rh);
3533 p->roothash_size = rhs;
3534
3535 return 0;
3536#else
3537 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "libcryptsetup is not supported, cannot setup verity hashes: %m");
3538#endif
3539}
3540
4ecd39c5
DDM
3541static int sign_verity_roothash(
3542 const uint8_t *roothash,
3543 size_t roothash_size,
3544 uint8_t **ret_signature,
3545 size_t *ret_signature_size) {
3546
3547#if HAVE_OPENSSL
3548 _cleanup_(BIO_freep) BIO *rb = NULL;
3549 _cleanup_(PKCS7_freep) PKCS7 *p7 = NULL;
3550 _cleanup_free_ char *hex = NULL;
3551 _cleanup_free_ uint8_t *sig = NULL;
3552 int sigsz;
3553
3554 assert(roothash);
3555 assert(roothash_size > 0);
3556 assert(ret_signature);
3557 assert(ret_signature_size);
3558
3559 hex = hexmem(roothash, roothash_size);
3560 if (!hex)
3561 return log_oom();
3562
3563 rb = BIO_new_mem_buf(hex, -1);
3564 if (!rb)
3565 return log_oom();
3566
3567 p7 = PKCS7_sign(arg_certificate, arg_private_key, NULL, rb, PKCS7_DETACHED|PKCS7_NOATTR|PKCS7_BINARY);
3568 if (!p7)
3569 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to calculate PKCS7 signature: %s",
3570 ERR_error_string(ERR_get_error(), NULL));
3571
3572 sigsz = i2d_PKCS7(p7, &sig);
3573 if (sigsz < 0)
3574 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert PKCS7 signature to DER: %s",
3575 ERR_error_string(ERR_get_error(), NULL));
3576
3577 *ret_signature = TAKE_PTR(sig);
3578 *ret_signature_size = sigsz;
3579
3580 return 0;
3581#else
3582 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot setup verity signature: %m");
3583#endif
3584}
3585
3586static int partition_format_verity_sig(Context *context, Partition *p) {
3587 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
3588 _cleanup_free_ uint8_t *sig = NULL;
3589 _cleanup_free_ char *text = NULL;
3590 Partition *hp;
3591 uint8_t fp[X509_FINGERPRINT_SIZE];
3592 size_t sigsz = 0, padsz; /* avoid false maybe-uninitialized warning */
3593 int whole_fd, r;
3594
3595 assert(p->verity == VERITY_SIG);
3596
3597 if (p->dropped)
3598 return 0;
3599
3600 if (PARTITION_EXISTS(p))
3601 return 0;
3602
8275334b 3603 if (partition_defer(p))
4ecd39c5
DDM
3604 return 0;
3605
3606 assert_se(hp = p->siblings[VERITY_HASH]);
3607 assert(!hp->dropped);
3608
3609 assert(arg_certificate);
3610
3611 assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
3612
3613 r = sign_verity_roothash(hp->roothash, hp->roothash_size, &sig, &sigsz);
3614 if (r < 0)
3615 return r;
3616
3617 r = x509_fingerprint(arg_certificate, fp);
3618 if (r < 0)
3619 return log_error_errno(r, "Unable to calculate X509 certificate fingerprint: %m");
3620
3621 r = json_build(&v,
3622 JSON_BUILD_OBJECT(
3623 JSON_BUILD_PAIR("rootHash", JSON_BUILD_HEX(hp->roothash, hp->roothash_size)),
3624 JSON_BUILD_PAIR(
3625 "certificateFingerprint",
3626 JSON_BUILD_HEX(fp, sizeof(fp))
3627 ),
3628 JSON_BUILD_PAIR("signature", JSON_BUILD_BASE64(sig, sigsz))
3629 )
3630 );
3631 if (r < 0)
3632 return log_error_errno(r, "Failed to build JSON object: %m");
3633
3634 r = json_variant_format(v, 0, &text);
3635 if (r < 0)
3636 return log_error_errno(r, "Failed to format JSON object: %m");
3637
3638 padsz = round_up_size(strlen(text), 4096);
3639 assert_se(padsz <= p->new_size);
3640
3641 r = strgrowpad0(&text, padsz);
3642 if (r < 0)
3643 return log_error_errno(r, "Failed to pad string to %s", FORMAT_BYTES(padsz));
3644
3645 if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1)
3646 return log_error_errno(errno, "Failed to seek to partition offset: %m");
3647
3648 r = loop_write(whole_fd, text, padsz, /*do_poll=*/ false);
3649 if (r < 0)
3650 return log_error_errno(r, "Failed to write verity signature to partition: %m");
3651
3652 if (fsync(whole_fd) < 0)
3653 return log_error_errno(errno, "Failed to synchronize verity signature JSON: %m");
3654
3655 return 0;
3656}
3657
757bc2e4 3658static int context_copy_blocks(Context *context) {
a64769d6 3659 int r;
757bc2e4
LP
3660
3661 assert(context);
3662
3663 /* Copy in file systems on the block level */
3664
3665 LIST_FOREACH(partitions, p, context->partitions) {
a64769d6 3666 _cleanup_(partition_target_freep) PartitionTarget *t = NULL;
757bc2e4
LP
3667
3668 if (p->copy_blocks_fd < 0)
3669 continue;
3670
3671 if (p->dropped)
3672 continue;
3673
3674 if (PARTITION_EXISTS(p)) /* Never copy over existing partitions */
3675 continue;
3676
8275334b 3677 if (partition_defer(p))
81d1098b
DDM
3678 continue;
3679
757bc2e4
LP
3680 assert(p->new_size != UINT64_MAX);
3681 assert(p->copy_blocks_size != UINT64_MAX);
48a09a8f 3682 assert(p->new_size >= p->copy_blocks_size + (p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0));
757bc2e4 3683
a64769d6
DDM
3684 r = partition_target_prepare(context, p, p->new_size,
3685 /*need_path=*/ p->encrypt != ENCRYPT_OFF || p->siblings[VERITY_HASH],
3686 &t);
3687 if (r < 0)
3688 return r;
757bc2e4 3689
2b59bf51
ZJS
3690 log_info("Copying in '%s' (%s) on block level into future partition %" PRIu64 ".",
3691 p->copy_blocks_path, FORMAT_BYTES(p->copy_blocks_size), p->partno);
757bc2e4 3692
a64769d6 3693 r = copy_bytes(p->copy_blocks_fd, partition_target_fd(t), p->copy_blocks_size, COPY_REFLINK);
757bc2e4
LP
3694 if (r < 0)
3695 return log_error_errno(r, "Failed to copy in data from '%s': %m", p->copy_blocks_path);
3696
889914ef 3697 if (p->encrypt != ENCRYPT_OFF) {
a64769d6 3698 r = partition_encrypt(context, p, partition_target_path(t));
b9df3536
LP
3699 if (r < 0)
3700 return r;
b9df3536
LP
3701 }
3702
a64769d6
DDM
3703 r = partition_target_sync(context, p, t);
3704 if (r < 0)
3705 return r;
48a09a8f 3706
757bc2e4 3707 log_info("Copying in of '%s' on block level completed.", p->copy_blocks_path);
2b392d86
DDM
3708
3709 if (p->siblings[VERITY_HASH]) {
a64769d6
DDM
3710 r = partition_format_verity_hash(context, p->siblings[VERITY_HASH],
3711 partition_target_path(t));
2b392d86
DDM
3712 if (r < 0)
3713 return r;
3714 }
4ecd39c5
DDM
3715
3716 if (p->siblings[VERITY_SIG]) {
3717 r = partition_format_verity_sig(context, p->siblings[VERITY_SIG]);
3718 if (r < 0)
3719 return r;
3720 }
757bc2e4
LP
3721 }
3722
3723 return 0;
3724}
3725
d2ac7698
DDM
3726static int do_copy_files(
3727 Partition *p,
3728 const char *root,
3729 uid_t override_uid,
3730 gid_t override_gid,
3731 const Set *denylist) {
e59678b2 3732
8a794850
LP
3733 int r;
3734
3735 assert(p);
92cd7e7c 3736 assert(root);
8a794850
LP
3737
3738 STRV_FOREACH_PAIR(source, target, p->copy_files) {
254d1313 3739 _cleanup_close_ int sfd = -EBADF, pfd = -EBADF, tfd = -EBADF;
8a794850 3740
fd1ca01a 3741 sfd = chase_symlinks_and_open(*source, arg_root, CHASE_PREFIX_ROOT, O_CLOEXEC|O_NOCTTY, NULL);
8a794850
LP
3742 if (sfd < 0)
3743 return log_error_errno(sfd, "Failed to open source file '%s%s': %m", strempty(arg_root), *source);
3744
3745 r = fd_verify_regular(sfd);
3746 if (r < 0) {
3747 if (r != -EISDIR)
3748 return log_error_errno(r, "Failed to check type of source file '%s': %m", *source);
3749
3750 /* We are looking at a directory */
fd1ca01a 3751 tfd = chase_symlinks_and_open(*target, root, CHASE_PREFIX_ROOT, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL);
8a794850 3752 if (tfd < 0) {
f21a3a82
LP
3753 _cleanup_free_ char *dn = NULL, *fn = NULL;
3754
8a794850
LP
3755 if (tfd != -ENOENT)
3756 return log_error_errno(tfd, "Failed to open target directory '%s': %m", *target);
3757
f21a3a82
LP
3758 r = path_extract_filename(*target, &fn);
3759 if (r < 0)
3760 return log_error_errno(r, "Failed to extract filename from '%s': %m", *target);
3761
3762 r = path_extract_directory(*target, &dn);
3763 if (r < 0)
3764 return log_error_errno(r, "Failed to extract directory from '%s': %m", *target);
3765
92cd7e7c 3766 r = mkdir_p_root(root, dn, UID_INVALID, GID_INVALID, 0755);
8a794850
LP
3767 if (r < 0)
3768 return log_error_errno(r, "Failed to create parent directory '%s': %m", dn);
3769
fd1ca01a 3770 pfd = chase_symlinks_and_open(dn, root, CHASE_PREFIX_ROOT, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL);
8a794850
LP
3771 if (pfd < 0)
3772 return log_error_errno(pfd, "Failed to open parent directory of target: %m");
3773
8a050905
DDM
3774 /* Make sure everything is owned by the user running repart so that
3775 * make_filesystem() can map the user running repart to "root" in a user
3776 * namespace to have the files owned by root in the final image. */
3777
652d9040
LP
3778 r = copy_tree_at(
3779 sfd, ".",
6020d00d 3780 pfd, fn,
d2ac7698 3781 override_uid, override_gid,
336ae0e7 3782 COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN,
c0fad2d9 3783 denylist);
8a794850 3784 } else
652d9040
LP
3785 r = copy_tree_at(
3786 sfd, ".",
3787 tfd, ".",
d2ac7698 3788 override_uid, override_gid,
336ae0e7 3789 COPY_REFLINK|COPY_HOLES|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS|COPY_GRACEFUL_WARN,
c0fad2d9 3790 denylist);
8a794850 3791 if (r < 0)
cf2ed23c
DDM
3792 return log_error_errno(r, "Failed to copy '%s%s' to '%s%s': %m",
3793 strempty(arg_root), *source, strempty(root), *target);
8a794850 3794 } else {
f21a3a82
LP
3795 _cleanup_free_ char *dn = NULL, *fn = NULL;
3796
8a794850
LP
3797 /* We are looking at a regular file */
3798
f21a3a82
LP
3799 r = path_extract_filename(*target, &fn);
3800 if (r == -EADDRNOTAVAIL || r == O_DIRECTORY)
3801 return log_error_errno(SYNTHETIC_ERRNO(EISDIR),
3802 "Target path '%s' refers to a directory, but source path '%s' refers to regular file, can't copy.", *target, *source);
3803 if (r < 0)
3804 return log_error_errno(r, "Failed to extract filename from '%s': %m", *target);
3805
3806 r = path_extract_directory(*target, &dn);
3807 if (r < 0)
3808 return log_error_errno(r, "Failed to extract directory from '%s': %m", *target);
3809
92cd7e7c 3810 r = mkdir_p_root(root, dn, UID_INVALID, GID_INVALID, 0755);
8a794850
LP
3811 if (r < 0)
3812 return log_error_errno(r, "Failed to create parent directory: %m");
3813
fd1ca01a 3814 pfd = chase_symlinks_and_open(dn, root, CHASE_PREFIX_ROOT, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL);
8a794850 3815 if (pfd < 0)
a0ff9971 3816 return log_error_errno(pfd, "Failed to open parent directory of target: %m");
8a794850 3817
e2819067 3818 tfd = openat(pfd, fn, O_CREAT|O_EXCL|O_WRONLY|O_CLOEXEC, 0700);
8a794850
LP
3819 if (tfd < 0)
3820 return log_error_errno(errno, "Failed to create target file '%s': %m", *target);
3821
81427d0f 3822 r = copy_bytes(sfd, tfd, UINT64_MAX, COPY_REFLINK|COPY_HOLES|COPY_SIGINT);
8a794850 3823 if (r < 0)
554a2b64 3824 return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target);
8a794850 3825
d2ac7698
DDM
3826 if (fchown(tfd, override_uid, override_gid) < 0)
3827 return log_error_errno(r, "Failed to change ownership of %s", *target);
3828
23e026de 3829 (void) copy_xattr(sfd, tfd, COPY_ALL_XATTRS);
8a794850
LP
3830 (void) copy_access(sfd, tfd);
3831 (void) copy_times(sfd, tfd, 0);
3832 }
3833 }
3834
3835 return 0;
3836}
3837
d2ac7698 3838static int do_make_directories(Partition *p, uid_t override_uid, gid_t override_gid, const char *root) {
d83d8048
LP
3839 int r;
3840
3841 assert(p);
92cd7e7c 3842 assert(root);
d83d8048
LP
3843
3844 STRV_FOREACH(d, p->make_directories) {
3845
d2ac7698 3846 r = mkdir_p_root(root, *d, override_uid, override_gid, 0755);
d83d8048
LP
3847 if (r < 0)
3848 return log_error_errno(r, "Failed to create directory '%s' in file system: %m", *d);
3849 }
3850
3851 return 0;
3852}
3853
d3201eb4
DDM
3854static bool partition_needs_populate(Partition *p) {
3855 assert(p);
3856 return !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories);
3857}
3858
e59678b2 3859static int partition_populate_directory(Partition *p, const Set *denylist, char **ret) {
95bfd3cd 3860 _cleanup_(rm_rf_physical_and_freep) char *root = NULL;
254d1313 3861 _cleanup_close_ int rfd = -EBADF;
95bfd3cd
DDM
3862 int r;
3863
e59678b2 3864 assert(ret);
95bfd3cd 3865
488bb758
DDM
3866 rfd = mkdtemp_open("/var/tmp/repart-XXXXXX", 0, &root);
3867 if (rfd < 0)
3868 return log_error_errno(rfd, "Failed to create temporary directory: %m");
95bfd3cd 3869
488bb758 3870 if (fchmod(rfd, 0755) < 0)
0b34f351
DDM
3871 return log_error_errno(errno, "Failed to change mode of temporary directory: %m");
3872
d2ac7698 3873 r = do_copy_files(p, root, getuid(), getgid(), denylist);
95bfd3cd
DDM
3874 if (r < 0)
3875 return r;
3876
d2ac7698 3877 r = do_make_directories(p, getuid(), getgid(), root);
95bfd3cd
DDM
3878 if (r < 0)
3879 return r;
3880
e59678b2 3881 *ret = TAKE_PTR(root);
95bfd3cd
DDM
3882 return 0;
3883}
3884
c0fad2d9 3885static int partition_populate_filesystem(Partition *p, const char *node, const Set *denylist) {
8a794850
LP
3886 int r;
3887
3888 assert(p);
3889 assert(node);
3890
a7f1f7d8 3891 log_info("Populating %s filesystem with files.", p->format);
8a794850
LP
3892
3893 /* We copy in a child process, since we have to mount the fs for that, and we don't want that fs to
3894 * appear in the host namespace. Hence we fork a child that has its own file system namespace and
3895 * detached mount propagation. */
3896
3897 r = safe_fork("(sd-copy)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, NULL);
3898 if (r < 0)
3899 return r;
3900 if (r == 0) {
3901 static const char fs[] = "/run/systemd/mount-root";
3902 /* This is a child process with its own mount namespace and propagation to host turned off */
3903
3904 r = mkdir_p(fs, 0700);
3905 if (r < 0) {
3906 log_error_errno(r, "Failed to create mount point: %m");
3907 _exit(EXIT_FAILURE);
3908 }
3909
511a8cfe 3910 if (mount_nofollow_verbose(LOG_ERR, node, fs, p->format, MS_NOATIME|MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL) < 0)
8a794850
LP
3911 _exit(EXIT_FAILURE);
3912
d2ac7698 3913 if (do_copy_files(p, fs, 0, 0, denylist) < 0)
8a794850
LP
3914 _exit(EXIT_FAILURE);
3915
d2ac7698 3916 if (do_make_directories(p, 0, 0, fs) < 0)
d83d8048
LP
3917 _exit(EXIT_FAILURE);
3918
8a794850
LP
3919 r = syncfs_path(AT_FDCWD, fs);
3920 if (r < 0) {
3921 log_error_errno(r, "Failed to synchronize written files: %m");
3922 _exit(EXIT_FAILURE);
3923 }
3924
3925 _exit(EXIT_SUCCESS);
3926 }
3927
a7f1f7d8 3928 log_info("Successfully populated %s filesystem with files.", p->format);
8a794850
LP
3929 return 0;
3930}
3931
c0fad2d9
DDM
3932static int make_copy_files_denylist(Context *context, Set **ret) {
3933 _cleanup_set_free_ Set *denylist = NULL;
3934 int r;
3935
3936 assert(context);
3937 assert(ret);
3938
3939 LIST_FOREACH(partitions, p, context->partitions) {
22e932f4 3940 const char *sources = gpt_partition_type_mountpoint_nulstr(p->type);
c0fad2d9
DDM
3941 if (!sources)
3942 continue;
3943
3944 NULSTR_FOREACH(s, sources) {
3945 _cleanup_free_ char *d = NULL;
3946 struct stat st;
3947
3948 r = chase_symlinks_and_stat(s, arg_root, CHASE_PREFIX_ROOT, NULL, &st, NULL);
3949 if (r == -ENOENT)
3950 continue;
3951 if (r < 0)
3952 return log_error_errno(r, "Failed to stat source file '%s%s': %m",
3953 strempty(arg_root), s);
3954
3955 if (set_contains(denylist, &st))
3956 continue;
3957
3958 d = memdup(&st, sizeof(st));
3959 if (!d)
3960 return log_oom();
3961 if (set_ensure_put(&denylist, &inode_hash_ops, d) < 0)
3962 return log_oom();
3963
3964 TAKE_PTR(d);
3965 }
3966 }
3967
3968 *ret = TAKE_PTR(denylist);
3969 return 0;
3970}
3971
53171c04 3972static int context_mkfs(Context *context) {
c0fad2d9 3973 _cleanup_set_free_ Set *denylist = NULL;
a64769d6 3974 int r;
53171c04
LP
3975
3976 assert(context);
3977
3978 /* Make a file system */
3979
c0fad2d9
DDM
3980 r = make_copy_files_denylist(context, &denylist);
3981 if (r < 0)
3982 return r;
3983
53171c04 3984 LIST_FOREACH(partitions, p, context->partitions) {
e59678b2 3985 _cleanup_(rm_rf_physical_and_freep) char *root = NULL;
a64769d6 3986 _cleanup_(partition_target_freep) PartitionTarget *t = NULL;
53171c04
LP
3987
3988 if (p->dropped)
3989 continue;
3990
3991 if (PARTITION_EXISTS(p)) /* Never format existing partitions */
3992 continue;
3993
3994 if (!p->format)
3995 continue;
3996
c4a87b76
DDM
3997 /* Minimized partitions will use the copy blocks logic so let's make sure to skip those here. */
3998 if (p->copy_blocks_fd >= 0)
3999 continue;
4000
8275334b 4001 if (partition_defer(p))
81d1098b
DDM
4002 continue;
4003
53171c04
LP
4004 assert(p->offset != UINT64_MAX);
4005 assert(p->new_size != UINT64_MAX);
48a09a8f 4006 assert(p->new_size >= (p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0));
53171c04 4007
a64769d6
DDM
4008 /* If we're doing encryption, we make sure we keep free space at the end which is required
4009 * for cryptsetup's offline encryption. */
4010 r = partition_target_prepare(context, p,
4011 p->new_size - (p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0),
4012 /*need_path=*/ true,
4013 &t);
53171c04 4014 if (r < 0)
a64769d6 4015 return r;
53171c04 4016
53171c04
LP
4017 log_info("Formatting future partition %" PRIu64 ".", p->partno);
4018
d3201eb4
DDM
4019 /* If we're not writing to a loop device or if we're populating a read-only filesystem, we
4020 * have to populate using the filesystem's mkfs's --root (or equivalent) option. To do that,
4021 * we need to set up the final directory tree beforehand. */
4022
4023 if (partition_needs_populate(p) && (!t->loop || fstype_is_ro(p->format))) {
4024 if (!mkfs_supports_root_option(p->format))
4025 return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
4026 "Loop device access is required to populate %s filesystems.",
4027 p->format);
95bfd3cd 4028
e59678b2 4029 r = partition_populate_directory(p, denylist, &root);
143c3c08
DDM
4030 if (r < 0)
4031 return r;
4032 }
95bfd3cd 4033
a64769d6 4034 r = make_filesystem(partition_target_path(t), p->format, strempty(p->new_label), root,
8f30c00c 4035 p->fs_uuid, arg_discard, NULL);
48a09a8f 4036 if (r < 0)
53171c04
LP
4037 return r;
4038
4039 log_info("Successfully formatted future partition %" PRIu64 ".", p->partno);
4040
d3201eb4
DDM
4041 /* If we're writing to a loop device, we can now mount the empty filesystem and populate it. */
4042 if (partition_needs_populate(p) && !root) {
4043 assert(t->loop);
4044
4045 r = partition_populate_filesystem(p, t->loop->node, denylist);
48a09a8f 4046 if (r < 0)
143c3c08 4047 return r;
b9df3536
LP
4048 }
4049
889914ef 4050 if (p->encrypt != ENCRYPT_OFF) {
a64769d6 4051 r = partition_target_grow(t, p->new_size);
b9df3536 4052 if (r < 0)
a64769d6 4053 return r;
b9df3536 4054
a64769d6 4055 r = partition_encrypt(context, p, partition_target_path(t));
48a09a8f
DDM
4056 if (r < 0)
4057 return log_error_errno(r, "Failed to encrypt device: %m");
b9df3536 4058 }
8a794850 4059
48a09a8f
DDM
4060 /* Note that we always sync explicitly here, since mkfs.fat doesn't do that on its own, and
4061 * if we don't sync before detaching a block device the in-flight sectors possibly won't hit
4062 * the disk. */
4063
a64769d6 4064 r = partition_target_sync(context, p, t);
53171c04 4065 if (r < 0)
a64769d6 4066 return r;
b5b7879a 4067
2b392d86 4068 if (p->siblings[VERITY_HASH]) {
a64769d6
DDM
4069 r = partition_format_verity_hash(context, p->siblings[VERITY_HASH],
4070 partition_target_path(t));
2b392d86
DDM
4071 if (r < 0)
4072 return r;
b5b7879a 4073 }
4ecd39c5
DDM
4074
4075 if (p->siblings[VERITY_SIG]) {
4076 r = partition_format_verity_sig(context, p->siblings[VERITY_SIG]);
4077 if (r < 0)
4078 return r;
4079 }
b5b7879a
DDM
4080 }
4081
4082 return 0;
4083}
4084
b456191d
DDM
4085static int parse_x509_certificate(const char *certificate, size_t certificate_size, X509 **ret) {
4086#if HAVE_OPENSSL
4087 _cleanup_(X509_freep) X509 *cert = NULL;
4088 _cleanup_(BIO_freep) BIO *cb = NULL;
4089
4090 assert(certificate);
4091 assert(certificate_size > 0);
4092 assert(ret);
4093
4094 cb = BIO_new_mem_buf(certificate, certificate_size);
4095 if (!cb)
4096 return log_oom();
4097
4098 cert = PEM_read_bio_X509(cb, NULL, NULL, NULL);
4099 if (!cert)
4100 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse X.509 certificate: %s",
4101 ERR_error_string(ERR_get_error(), NULL));
4102
4103 if (ret)
4104 *ret = TAKE_PTR(cert);
4105
4106 return 0;
4107#else
4108 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot parse X509 certificate.");
4109#endif
4110}
4111
4112static int parse_private_key(const char *key, size_t key_size, EVP_PKEY **ret) {
4113#if HAVE_OPENSSL
4114 _cleanup_(BIO_freep) BIO *kb = NULL;
4115 _cleanup_(EVP_PKEY_freep) EVP_PKEY *pk = NULL;
4116
4117 assert(key);
4118 assert(key_size > 0);
4119 assert(ret);
4120
4121 kb = BIO_new_mem_buf(key, key_size);
4122 if (!kb)
4123 return log_oom();
4124
4125 pk = PEM_read_bio_PrivateKey(kb, NULL, NULL, NULL);
4126 if (!pk)
4127 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse PEM private key: %s",
4128 ERR_error_string(ERR_get_error(), NULL));
4129
4130 if (ret)
4131 *ret = TAKE_PTR(pk);
4132
4133 return 0;
4134#else
4135 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot parse private key.");
4136#endif
4137}
4138
e594a3b1
LP
4139static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *ret) {
4140 struct {
4141 sd_id128_t type_uuid;
4142 uint64_t counter;
695cfd53 4143 } _packed_ plaintext = {};
e594a3b1 4144 union {
ade99252 4145 uint8_t md[SHA256_DIGEST_SIZE];
e594a3b1
LP
4146 sd_id128_t id;
4147 } result;
4148
4149 uint64_t k = 0;
e594a3b1
LP
4150 int r;
4151
4152 assert(context);
4153 assert(p);
4154 assert(ret);
4155
4156 /* Calculate a good UUID for the indicated partition. We want a certain degree of reproducibility,
4157 * hence we won't generate the UUIDs randomly. Instead we use a cryptographic hash (precisely:
4158 * HMAC-SHA256) to derive them from a single seed. The seed is generally the machine ID of the
4159 * installation we are processing, but if random behaviour is desired can be random, too. We use the
4160 * seed value as key for the HMAC (since the machine ID is something we generally don't want to leak)
4161 * and the partition type as plaintext. The partition type is suffixed with a counter (only for the
4162 * second and later partition of the same type) if we have more than one partition of the same
4163 * time. Or in other words:
4164 *
4165 * With:
4166 * SEED := /etc/machine-id
4167 *
4168 * If first partition instance of type TYPE_UUID:
4169 * PARTITION_UUID := HMAC-SHA256(SEED, TYPE_UUID)
4170 *
4171 * For all later partition instances of type TYPE_UUID with INSTANCE being the LE64 encoded instance number:
4172 * PARTITION_UUID := HMAC-SHA256(SEED, TYPE_UUID || INSTANCE)
4173 */
4174
4175 LIST_FOREACH(partitions, q, context->partitions) {
4176 if (p == q)
4177 break;
4178
22e932f4 4179 if (!sd_id128_equal(p->type.uuid, q->type.uuid))
e594a3b1
LP
4180 continue;
4181
4182 k++;
4183 }
4184
22e932f4 4185 plaintext.type_uuid = p->type.uuid;
e594a3b1
LP
4186 plaintext.counter = htole64(k);
4187
ade99252
KK
4188 hmac_sha256(context->seed.bytes, sizeof(context->seed.bytes),
4189 &plaintext,
4190 k == 0 ? sizeof(sd_id128_t) : sizeof(plaintext),
4191 result.md);
e594a3b1
LP
4192
4193 /* Take the first half, mark it as v4 UUID */
4194 assert_cc(sizeof(result.md) == sizeof(result.id) * 2);
4195 result.id = id128_make_v4_uuid(result.id);
4196
4197 /* Ensure this partition UUID is actually unique, and there's no remaining partition from an earlier run? */
4198 LIST_FOREACH(partitions, q, context->partitions) {
4199 if (p == q)
4200 continue;
4201
580f48cc 4202 if (sd_id128_in_set(result.id, q->current_uuid, q->new_uuid)) {
da1af43d 4203 log_warning("Partition UUID calculated from seed for partition %" PRIu64 " already used, reverting to randomized UUID.", p->partno);
e594a3b1
LP
4204
4205 r = sd_id128_randomize(&result.id);
4206 if (r < 0)
4207 return log_error_errno(r, "Failed to generate randomized UUID: %m");
4208
4209 break;
4210 }
4211 }
4212
4213 *ret = result.id;
4214 return 0;
4215}
4216
4217static int partition_acquire_label(Context *context, Partition *p, char **ret) {
4218 _cleanup_free_ char *label = NULL;
4219 const char *prefix;
4220 unsigned k = 1;
4221
4222 assert(context);
4223 assert(p);
4224 assert(ret);
4225
22e932f4 4226 prefix = gpt_partition_type_uuid_to_string(p->type.uuid);
e594a3b1
LP
4227 if (!prefix)
4228 prefix = "linux";
4229
4230 for (;;) {
4231 const char *ll = label ?: prefix;
4232 bool retry = false;
e594a3b1
LP
4233
4234 LIST_FOREACH(partitions, q, context->partitions) {
4235 if (p == q)
4236 break;
4237
4238 if (streq_ptr(ll, q->current_label) ||
4239 streq_ptr(ll, q->new_label)) {
4240 retry = true;
4241 break;
4242 }
4243 }
4244
4245 if (!retry)
4246 break;
4247
4248 label = mfree(label);
e594a3b1
LP
4249 if (asprintf(&label, "%s-%u", prefix, ++k) < 0)
4250 return log_oom();
4251 }
4252
4253 if (!label) {
4254 label = strdup(prefix);
4255 if (!label)
4256 return log_oom();
4257 }
4258
4259 *ret = TAKE_PTR(label);
4260 return 0;
4261}
4262
4263static int context_acquire_partition_uuids_and_labels(Context *context) {
e594a3b1
LP
4264 int r;
4265
4266 assert(context);
4267
4268 LIST_FOREACH(partitions, p, context->partitions) {
15cad3a2
DDM
4269 sd_id128_t uuid;
4270
e594a3b1
LP
4271 /* Never touch foreign partitions */
4272 if (PARTITION_IS_FOREIGN(p)) {
4273 p->new_uuid = p->current_uuid;
4274
4275 if (p->current_label) {
78eee6ce
LP
4276 r = free_and_strdup_warn(&p->new_label, strempty(p->current_label));
4277 if (r < 0)
4278 return r;
e594a3b1
LP
4279 }
4280
4281 continue;
4282 }
4283
4284 if (!sd_id128_is_null(p->current_uuid))
15cad3a2
DDM
4285 p->new_uuid = uuid = p->current_uuid; /* Never change initialized UUIDs */
4286 else if (p->new_uuid_is_set)
4287 uuid = p->new_uuid;
4288 else {
12963533 4289 /* Not explicitly set by user! */
15cad3a2 4290 r = partition_acquire_uuid(context, p, &uuid);
e594a3b1
LP
4291 if (r < 0)
4292 return r;
11749b61 4293
15cad3a2
DDM
4294 /* The final verity hash/data UUIDs can only be determined after formatting the
4295 * verity hash partition. However, we still want to use the generated partition UUID
4296 * to derive other UUIDs to keep things unique and reproducible, so we always
4297 * generate a UUID if none is set, but we only use it as the actual partition UUID if
4298 * verity is not configured. */
4299 if (!IN_SET(p->verity, VERITY_DATA, VERITY_HASH)) {
4300 p->new_uuid = uuid;
4301 p->new_uuid_is_set = true;
4302 }
e594a3b1
LP
4303 }
4304
8bbbdfd7
DDM
4305 /* Calculate the UUID for the file system as HMAC-SHA256 of the string "file-system-uuid",
4306 * keyed off the partition UUID. */
15cad3a2 4307 r = derive_uuid(uuid, "file-system-uuid", &p->fs_uuid);
8bbbdfd7
DDM
4308 if (r < 0)
4309 return r;
4310
15cad3a2
DDM
4311 if (p->encrypt != ENCRYPT_OFF) {
4312 r = derive_uuid(uuid, "luks-uuid", &p->luks_uuid);
4313 if (r < 0)
4314 return r;
4315 }
4316
e594a3b1 4317 if (!isempty(p->current_label)) {
78eee6ce
LP
4318 /* never change initialized labels */
4319 r = free_and_strdup_warn(&p->new_label, p->current_label);
4320 if (r < 0)
4321 return r;
12963533
TH
4322 } else if (!p->new_label) {
4323 /* Not explicitly set by user! */
4324
e594a3b1
LP
4325 r = partition_acquire_label(context, p, &p->new_label);
4326 if (r < 0)
4327 return r;
4328 }
4329 }
4330
4331 return 0;
4332}
4333
e73309c5
LP
4334static int set_gpt_flags(struct fdisk_partition *q, uint64_t flags) {
4335 _cleanup_free_ char *a = NULL;
4336
4337 for (unsigned i = 0; i < sizeof(flags) * 8; i++) {
4338 uint64_t bit = UINT64_C(1) << i;
4339 char buf[DECIMAL_STR_MAX(unsigned)+1];
4340
4341 if (!FLAGS_SET(flags, bit))
4342 continue;
4343
4344 xsprintf(buf, "%u", i);
4345 if (!strextend_with_separator(&a, ",", buf))
4346 return -ENOMEM;
4347 }
4348
4349 return fdisk_partition_set_attrs(q, a);
4350}
4351
1c41c1dc
LP
4352static uint64_t partition_merge_flags(Partition *p) {
4353 uint64_t f;
4354
4355 assert(p);
4356
4357 f = p->gpt_flags;
4358
ff0771bf 4359 if (p->no_auto >= 0) {
22e932f4 4360 if (gpt_partition_type_knows_no_auto(p->type))
92e72028 4361 SET_FLAG(f, SD_GPT_FLAG_NO_AUTO, p->no_auto);
ff0771bf 4362 else {
b7416360 4363 char buffer[SD_ID128_UUID_STRING_MAX];
ff0771bf
LP
4364 log_warning("Configured NoAuto=%s for partition type '%s' that doesn't support it, ignoring.",
4365 yes_no(p->no_auto),
22e932f4 4366 gpt_partition_type_uuid_to_string_harder(p->type.uuid, buffer));
ff0771bf
LP
4367 }
4368 }
4369
1c41c1dc 4370 if (p->read_only >= 0) {
22e932f4 4371 if (gpt_partition_type_knows_read_only(p->type))
92e72028 4372 SET_FLAG(f, SD_GPT_FLAG_READ_ONLY, p->read_only);
1c41c1dc 4373 else {
b7416360 4374 char buffer[SD_ID128_UUID_STRING_MAX];
1c41c1dc
LP
4375 log_warning("Configured ReadOnly=%s for partition type '%s' that doesn't support it, ignoring.",
4376 yes_no(p->read_only),
22e932f4 4377 gpt_partition_type_uuid_to_string_harder(p->type.uuid, buffer));
1c41c1dc
LP
4378 }
4379 }
4380
4381 if (p->growfs >= 0) {
22e932f4 4382 if (gpt_partition_type_knows_growfs(p->type))
92e72028 4383 SET_FLAG(f, SD_GPT_FLAG_GROWFS, p->growfs);
1c41c1dc 4384 else {
b7416360 4385 char buffer[SD_ID128_UUID_STRING_MAX];
1c41c1dc
LP
4386 log_warning("Configured GrowFileSystem=%s for partition type '%s' that doesn't support it, ignoring.",
4387 yes_no(p->growfs),
22e932f4 4388 gpt_partition_type_uuid_to_string_harder(p->type.uuid, buffer));
1c41c1dc
LP
4389 }
4390 }
4391
4392 return f;
4393}
4394
f28d4f42 4395static int context_mangle_partitions(Context *context) {
f28d4f42 4396 int r;
e594a3b1
LP
4397
4398 assert(context);
4399
e594a3b1
LP
4400 LIST_FOREACH(partitions, p, context->partitions) {
4401 if (p->dropped)
4402 continue;
4403
8275334b 4404 if (partition_defer(p))
81d1098b
DDM
4405 continue;
4406
e594a3b1
LP
4407 assert(p->new_size != UINT64_MAX);
4408 assert(p->offset != UINT64_MAX);
4409 assert(p->partno != UINT64_MAX);
4410
4411 if (PARTITION_EXISTS(p)) {
4412 bool changed = false;
4413
4414 assert(p->current_partition);
4415
4416 if (p->new_size != p->current_size) {
4417 assert(p->new_size >= p->current_size);
994b3031 4418 assert(p->new_size % context->sector_size == 0);
e594a3b1
LP
4419
4420 r = fdisk_partition_size_explicit(p->current_partition, true);
4421 if (r < 0)
4422 return log_error_errno(r, "Failed to enable explicit sizing: %m");
4423
994b3031 4424 r = fdisk_partition_set_size(p->current_partition, p->new_size / context->sector_size);
e594a3b1
LP
4425 if (r < 0)
4426 return log_error_errno(r, "Failed to grow partition: %m");
4427
4428 log_info("Growing existing partition %" PRIu64 ".", p->partno);
4429 changed = true;
4430 }
4431
4432 if (!sd_id128_equal(p->new_uuid, p->current_uuid)) {
b7416360 4433 r = fdisk_partition_set_uuid(p->current_partition, SD_ID128_TO_UUID_STRING(p->new_uuid));
e594a3b1
LP
4434 if (r < 0)
4435 return log_error_errno(r, "Failed to set partition UUID: %m");
4436
4437 log_info("Initializing UUID of existing partition %" PRIu64 ".", p->partno);
4438 changed = true;
4439 }
4440
4441 if (!streq_ptr(p->new_label, p->current_label)) {
be9ce018 4442 r = fdisk_partition_set_name(p->current_partition, strempty(p->new_label));
e594a3b1
LP
4443 if (r < 0)
4444 return log_error_errno(r, "Failed to set partition label: %m");
4445
4446 log_info("Setting partition label of existing partition %" PRIu64 ".", p->partno);
4447 changed = true;
4448 }
4449
4450 if (changed) {
4451 assert(!PARTITION_IS_FOREIGN(p)); /* never touch foreign partitions */
4452
4453 r = fdisk_set_partition(context->fdisk_context, p->partno, p->current_partition);
4454 if (r < 0)
4455 return log_error_errno(r, "Failed to update partition: %m");
4456 }
4457 } else {
4458 _cleanup_(fdisk_unref_partitionp) struct fdisk_partition *q = NULL;
4459 _cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *t = NULL;
e594a3b1
LP
4460
4461 assert(!p->new_partition);
994b3031
LP
4462 assert(p->offset % context->sector_size == 0);
4463 assert(p->new_size % context->sector_size == 0);
be9ce018 4464 assert(p->new_label);
e594a3b1
LP
4465
4466 t = fdisk_new_parttype();
4467 if (!t)
4468 return log_oom();
4469
22e932f4 4470 r = fdisk_parttype_set_typestr(t, SD_ID128_TO_UUID_STRING(p->type.uuid));
e594a3b1
LP
4471 if (r < 0)
4472 return log_error_errno(r, "Failed to initialize partition type: %m");
4473
4474 q = fdisk_new_partition();
4475 if (!q)
4476 return log_oom();
4477
4478 r = fdisk_partition_set_type(q, t);
4479 if (r < 0)
4480 return log_error_errno(r, "Failed to set partition type: %m");
4481
4482 r = fdisk_partition_size_explicit(q, true);
4483 if (r < 0)
4484 return log_error_errno(r, "Failed to enable explicit sizing: %m");
4485
994b3031 4486 r = fdisk_partition_set_start(q, p->offset / context->sector_size);
e594a3b1
LP
4487 if (r < 0)
4488 return log_error_errno(r, "Failed to position partition: %m");
4489
994b3031 4490 r = fdisk_partition_set_size(q, p->new_size / context->sector_size);
e594a3b1
LP
4491 if (r < 0)
4492 return log_error_errno(r, "Failed to grow partition: %m");
4493
4494 r = fdisk_partition_set_partno(q, p->partno);
4495 if (r < 0)
4496 return log_error_errno(r, "Failed to set partition number: %m");
4497
b7416360 4498 r = fdisk_partition_set_uuid(q, SD_ID128_TO_UUID_STRING(p->new_uuid));
e594a3b1
LP
4499 if (r < 0)
4500 return log_error_errno(r, "Failed to set partition UUID: %m");
4501
be9ce018 4502 r = fdisk_partition_set_name(q, strempty(p->new_label));
e594a3b1
LP
4503 if (r < 0)
4504 return log_error_errno(r, "Failed to set partition label: %m");
4505
ff0771bf 4506 /* Merge the no auto + read only + growfs setting with the literal flags, and set them for the partition */
1c41c1dc 4507 r = set_gpt_flags(q, partition_merge_flags(p));
e73309c5
LP
4508 if (r < 0)
4509 return log_error_errno(r, "Failed to set GPT partition flags: %m");
4510
5b5109e2 4511 log_info("Adding new partition %" PRIu64 " to partition table.", p->partno);
e594a3b1
LP
4512
4513 r = fdisk_add_partition(context->fdisk_context, q, NULL);
4514 if (r < 0)
4515 return log_error_errno(r, "Failed to add partition: %m");
4516
4517 assert(!p->new_partition);
4518 p->new_partition = TAKE_PTR(q);
4519 }
4520 }
4521
f28d4f42
LP
4522 return 0;
4523}
4524
62108348 4525static int split_name_printf(Partition *p, char **ret) {
4cee8333
DDM
4526 assert(p);
4527
4528 const Specifier table[] = {
22e932f4
DDM
4529 { 't', specifier_string, GPT_PARTITION_TYPE_UUID_TO_STRING_HARDER(p->type.uuid) },
4530 { 'T', specifier_id128, &p->type.uuid },
4cee8333
DDM
4531 { 'U', specifier_id128, &p->new_uuid },
4532 { 'n', specifier_uint64, &p->partno },
4533
4534 COMMON_SYSTEM_SPECIFIERS,
4535 {}
4536 };
4537
62108348 4538 return specifier_printf(p->split_name_format, NAME_MAX, table, arg_root, p, ret);
4cee8333
DDM
4539}
4540
62108348
DDM
4541static int split_node(const char *node, char **ret_base, char **ret_ext) {
4542 _cleanup_free_ char *base = NULL, *ext = NULL;
4543 char *e;
4544 int r;
4545
4546 assert(node);
4547 assert(ret_base);
4548 assert(ret_ext);
4549
4550 r = path_extract_filename(node, &base);
4551 if (r == O_DIRECTORY || r == -EADDRNOTAVAIL)
4552 return log_error_errno(r, "Device node %s cannot be a directory", node);
4553 if (r < 0)
4554 return log_error_errno(r, "Failed to extract filename from %s: %m", node);
4555
4556 e = endswith(base, ".raw");
4557 if (e) {
4558 ext = strdup(e);
4559 if (!ext)
4560 return log_oom();
4561
4562 *e = 0;
4563 }
4564
4565 *ret_base = TAKE_PTR(base);
4566 *ret_ext = TAKE_PTR(ext);
4567
4568 return 0;
4cee8333
DDM
4569}
4570
4571static int split_name_resolve(Context *context) {
62108348 4572 _cleanup_free_ char *parent = NULL, *base = NULL, *ext = NULL;
4cee8333
DDM
4573 int r;
4574
62108348 4575 assert(context);
62108348 4576
cc751c75 4577 r = path_extract_directory(context->node, &parent);
62108348 4578 if (r < 0 && r != -EDESTADDRREQ)
cc751c75 4579 return log_error_errno(r, "Failed to extract directory from %s: %m", context->node);
62108348 4580
cc751c75 4581 r = split_node(context->node, &base, &ext);
62108348
DDM
4582 if (r < 0)
4583 return r;
4584
4cee8333 4585 LIST_FOREACH(partitions, p, context->partitions) {
62108348
DDM
4586 _cleanup_free_ char *resolved = NULL;
4587
4cee8333
DDM
4588 if (p->dropped)
4589 continue;
4590
4591 if (!p->split_name_format)
4592 continue;
4593
62108348 4594 r = split_name_printf(p, &resolved);
4cee8333
DDM
4595 if (r < 0)
4596 return log_error_errno(r, "Failed to resolve specifiers in %s: %m", p->split_name_format);
62108348
DDM
4597
4598 if (parent)
4599 p->split_path = strjoin(parent, "/", base, ".", resolved, ext);
4600 else
4601 p->split_path = strjoin(base, ".", resolved, ext);
4602 if (!p->split_path)
4603 return log_oom();
4cee8333
DDM
4604 }
4605
4606 LIST_FOREACH(partitions, p, context->partitions) {
62108348 4607 if (!p->split_path)
4cee8333
DDM
4608 continue;
4609
4610 LIST_FOREACH(partitions, q, context->partitions) {
4611 if (p == q)
4612 continue;
4613
62108348 4614 if (!q->split_path)
4cee8333
DDM
4615 continue;
4616
62108348 4617 if (!streq(p->split_path, q->split_path))
4cee8333
DDM
4618 continue;
4619
4620 return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
4621 "%s and %s have the same resolved split name \"%s\", refusing",
62108348 4622 p->definition_path, q->definition_path, p->split_path);
4cee8333
DDM
4623 }
4624 }
4625
4626 return 0;
4627}
4628
4cee8333 4629static int context_split(Context *context) {
254d1313 4630 int fd = -EBADF, r;
4cee8333
DDM
4631
4632 if (!arg_split)
4633 return 0;
4634
4635 assert(context);
4cee8333
DDM
4636
4637 /* We can't do resolution earlier because the partition UUIDs for verity partitions are only filled
4638 * in after they've been generated. */
4639
4640 r = split_name_resolve(context);
4641 if (r < 0)
4642 return r;
4643
4cee8333 4644 LIST_FOREACH(partitions, p, context->partitions) {
254d1313 4645 _cleanup_close_ int fdt = -EBADF;
4cee8333
DDM
4646
4647 if (p->dropped)
4648 continue;
4649
62108348 4650 if (!p->split_path)
4cee8333
DDM
4651 continue;
4652
8275334b 4653 if (partition_defer(p))
81d1098b
DDM
4654 continue;
4655
62108348 4656 fdt = open(p->split_path, O_WRONLY|O_NOCTTY|O_CLOEXEC|O_NOFOLLOW|O_CREAT|O_EXCL, 0666);
4cee8333 4657 if (fdt < 0)
62108348 4658 return log_error_errno(fdt, "Failed to open split partition file %s: %m", p->split_path);
4cee8333
DDM
4659
4660 if (fd < 0)
4661 assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
4662
4663 if (lseek(fd, p->offset, SEEK_SET) < 0)
4664 return log_error_errno(errno, "Failed to seek to partition offset: %m");
4665
a673b056 4666 r = copy_bytes(fd, fdt, p->new_size, COPY_REFLINK|COPY_HOLES);
4cee8333 4667 if (r < 0)
62108348 4668 return log_error_errno(r, "Failed to copy to split partition %s: %m", p->split_path);
4cee8333
DDM
4669 }
4670
4671 return 0;
4672}
4673
cc751c75 4674static int context_write_partition_table(Context *context) {
f28d4f42
LP
4675 _cleanup_(fdisk_unref_tablep) struct fdisk_table *original_table = NULL;
4676 int capable, r;
4677
4678 assert(context);
4679
cc751c75 4680 if (!context->from_scratch && !context_changed(context)) {
f28d4f42
LP
4681 log_info("No changes.");
4682 return 0;
4683 }
4684
4685 if (arg_dry_run) {
4686 log_notice("Refusing to repartition, please re-run with --dry-run=no.");
4687 return 0;
4688 }
4689
4690 log_info("Applying changes.");
4691
cc751c75 4692 if (context->from_scratch) {
81873a6b
LP
4693 r = context_wipe_range(context, 0, context->total);
4694 if (r < 0)
4695 return r;
4696
4697 log_info("Wiped block device.");
4698
0dce448b
LB
4699 if (arg_discard) {
4700 r = context_discard_range(context, 0, context->total);
4701 if (r == -EOPNOTSUPP)
4702 log_info("Storage does not support discard, not discarding entire block device data.");
4703 else if (r < 0)
4704 return log_error_errno(r, "Failed to discard entire block device: %m");
4705 else if (r > 0)
4706 log_info("Discarded entire block device.");
4707 }
f28d4f42
LP
4708 }
4709
4710 r = fdisk_get_partitions(context->fdisk_context, &original_table);
4711 if (r < 0)
4712 return log_error_errno(r, "Failed to acquire partition table: %m");
4713
4714 /* Wipe fs signatures and discard sectors where the new partitions are going to be placed and in the
4715 * gaps between partitions, just to be sure. */
cc751c75 4716 r = context_wipe_and_discard(context);
f28d4f42
LP
4717 if (r < 0)
4718 return r;
4719
4720 r = context_copy_blocks(context);
4721 if (r < 0)
4722 return r;
4723
4724 r = context_mkfs(context);
4725 if (r < 0)
4726 return r;
4727
4728 r = context_mangle_partitions(context);
4729 if (r < 0)
4730 return r;
4731
e594a3b1
LP
4732 log_info("Writing new partition table.");
4733
4734 r = fdisk_write_disklabel(context->fdisk_context);
4735 if (r < 0)
4736 return log_error_errno(r, "Failed to write partition table: %m");
4737
911ba624 4738 capable = blockdev_partscan_enabled(fdisk_get_devfd(context->fdisk_context));
9a1deb85
LP
4739 if (capable == -ENOTBLK)
4740 log_debug("Not telling kernel to reread partition table, since we are not operating on a block device.");
4741 else if (capable < 0)
911ba624 4742 return log_error_errno(capable, "Failed to check if block device supports partition scanning: %m");
9a1deb85 4743 else if (capable > 0) {
e594a3b1
LP
4744 log_info("Telling kernel to reread partition table.");
4745
cc751c75 4746 if (context->from_scratch)
e594a3b1
LP
4747 r = fdisk_reread_partition_table(context->fdisk_context);
4748 else
4749 r = fdisk_reread_changes(context->fdisk_context, original_table);
4750 if (r < 0)
4751 return log_error_errno(r, "Failed to reread partition table: %m");
4752 } else
4753 log_notice("Not telling kernel to reread partition table, because selected image does not support kernel partition block devices.");
4754
4755 log_info("All done.");
4756
4757 return 0;
4758}
4759
4760static int context_read_seed(Context *context, const char *root) {
4761 int r;
4762
4763 assert(context);
4764
4765 if (!sd_id128_is_null(context->seed))
4766 return 0;
4767
4768 if (!arg_randomize) {
254d1313 4769 _cleanup_close_ int fd = -EBADF;
e594a3b1
LP
4770
4771 fd = chase_symlinks_and_open("/etc/machine-id", root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC, NULL);
4772 if (fd == -ENOENT)
4773 log_info("No machine ID set, using randomized partition UUIDs.");
4774 else if (fd < 0)
4775 return log_error_errno(fd, "Failed to determine machine ID of image: %m");
4776 else {
057bf780
YW
4777 r = id128_read_fd(fd, ID128_FORMAT_PLAIN, &context->seed);
4778 if (IN_SET(r, -ENOMEDIUM, -ENOPKG))
e594a3b1
LP
4779 log_info("No machine ID set, using randomized partition UUIDs.");
4780 else if (r < 0)
4781 return log_error_errno(r, "Failed to parse machine ID of image: %m");
4782
4783 return 0;
4784 }
4785 }
4786
4787 r = sd_id128_randomize(&context->seed);
4788 if (r < 0)
4789 return log_error_errno(r, "Failed to generate randomized seed: %m");
4790
4791 return 0;
4792}
4793
cc751c75 4794static int context_factory_reset(Context *context) {
e594a3b1
LP
4795 size_t n = 0;
4796 int r;
4797
4798 assert(context);
4799
4800 if (arg_factory_reset <= 0)
4801 return 0;
4802
cc751c75 4803 if (context->from_scratch) /* Nothing to reset if we start from scratch */
e594a3b1
LP
4804 return 0;
4805
4806 if (arg_dry_run) {
4807 log_notice("Refusing to factory reset, please re-run with --dry-run=no.");
4808 return 0;
4809 }
4810
4811 log_info("Applying factory reset.");
4812
4813 LIST_FOREACH(partitions, p, context->partitions) {
4814
4815 if (!p->factory_reset || !PARTITION_EXISTS(p))
4816 continue;
4817
4818 assert(p->partno != UINT64_MAX);
4819
4820 log_info("Removing partition %" PRIu64 " for factory reset.", p->partno);
4821
4822 r = fdisk_delete_partition(context->fdisk_context, p->partno);
4823 if (r < 0)
4824 return log_error_errno(r, "Failed to remove partition %" PRIu64 ": %m", p->partno);
4825
4826 n++;
4827 }
4828
4829 if (n == 0) {
4830 log_info("Factory reset requested, but no partitions to delete found.");
4831 return 0;
4832 }
4833
4834 r = fdisk_write_disklabel(context->fdisk_context);
4835 if (r < 0)
4836 return log_error_errno(r, "Failed to write disk label: %m");
4837
4838 log_info("Successfully deleted %zu partitions.", n);
4839 return 1;
4840}
4841
4842static int context_can_factory_reset(Context *context) {
e594a3b1
LP
4843 assert(context);
4844
4845 LIST_FOREACH(partitions, p, context->partitions)
4846 if (p->factory_reset && PARTITION_EXISTS(p))
4847 return true;
4848
4849 return false;
4850}
4851
5c08da58
LP
4852static int resolve_copy_blocks_auto_candidate(
4853 dev_t partition_devno,
22e932f4 4854 GptPartitionType partition_type,
5c08da58
LP
4855 dev_t restrict_devno,
4856 sd_id128_t *ret_uuid) {
4857
4858 _cleanup_(blkid_free_probep) blkid_probe b = NULL;
254d1313 4859 _cleanup_close_ int fd = -EBADF;
ca822829
YW
4860 _cleanup_free_ char *p = NULL;
4861 const char *pttype, *t;
5c08da58
LP
4862 sd_id128_t pt_parsed, u;
4863 blkid_partition pp;
4864 dev_t whole_devno;
4865 blkid_partlist pl;
5c08da58
LP
4866 int r;
4867
4868 /* Checks if the specified partition has the specified GPT type UUID, and is located on the specified
4869 * 'restrict_devno' device. The type check is particularly relevant if we have Verity volume which is
4870 * backed by two separate partitions: the data and the hash partitions, and we need to find the right
4871 * one of the two. */
4872
4873 r = block_get_whole_disk(partition_devno, &whole_devno);
4874 if (r < 0)
4875 return log_error_errno(
4876 r,
4877 "Unable to determine containing block device of partition %u:%u: %m",
4878 major(partition_devno), minor(partition_devno));
4879
4880 if (restrict_devno != (dev_t) -1 &&
4881 restrict_devno != whole_devno)
4882 return log_error_errno(
4883 SYNTHETIC_ERRNO(EPERM),
4884 "Partition %u:%u is located outside of block device %u:%u, refusing.",
4885 major(partition_devno), minor(partition_devno),
4886 major(restrict_devno), minor(restrict_devno));
4887
ca822829 4888 fd = r = device_open_from_devnum(S_IFBLK, whole_devno, O_RDONLY|O_CLOEXEC|O_NONBLOCK, &p);
5c08da58 4889 if (r < 0)
ca822829
YW
4890 return log_error_errno(r, "Failed to open block device " DEVNUM_FORMAT_STR ": %m",
4891 DEVNUM_FORMAT_VAL(whole_devno));
5c08da58
LP
4892
4893 b = blkid_new_probe();
4894 if (!b)
4895 return log_oom();
4896
4897 errno = 0;
4898 r = blkid_probe_set_device(b, fd, 0, 0);
4899 if (r != 0)
4900 return log_error_errno(errno_or_else(ENOMEM), "Failed to open block device '%s': %m", p);
4901
4902 (void) blkid_probe_enable_partitions(b, 1);
4903 (void) blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
4904
4905 errno = 0;
4906 r = blkid_do_safeprobe(b);
2e3944b8
LP
4907 if (r == _BLKID_SAFEPROBE_ERROR)
4908 return log_error_errno(errno_or_else(EIO), "Unable to probe for partition table of '%s': %m", p);
4909 if (IN_SET(r, _BLKID_SAFEPROBE_AMBIGUOUS, _BLKID_SAFEPROBE_NOT_FOUND)) {
5c08da58
LP
4910 log_debug("Didn't find partition table on block device '%s'.", p);
4911 return false;
4912 }
2e3944b8
LP
4913
4914 assert(r == _BLKID_SAFEPROBE_FOUND);
5c08da58
LP
4915
4916 (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
4917 if (!streq_ptr(pttype, "gpt")) {
4918 log_debug("Didn't find a GPT partition table on '%s'.", p);
4919 return false;
4920 }
4921
4922 errno = 0;
4923 pl = blkid_probe_get_partitions(b);
4924 if (!pl)
4925 return log_error_errno(errno_or_else(EIO), "Unable read partition table of '%s': %m", p);
5c08da58
LP
4926
4927 pp = blkid_partlist_devno_to_partition(pl, partition_devno);
4928 if (!pp) {
4929 log_debug("Partition %u:%u has no matching partition table entry on '%s'.",
4930 major(partition_devno), minor(partition_devno), p);
4931 return false;
4932 }
4933
4934 t = blkid_partition_get_type_string(pp);
4935 if (isempty(t)) {
4936 log_debug("Partition %u:%u has no type on '%s'.",
4937 major(partition_devno), minor(partition_devno), p);
4938 return false;
4939 }
4940
4941 r = sd_id128_from_string(t, &pt_parsed);
4942 if (r < 0) {
4943 log_debug_errno(r, "Failed to parse partition type \"%s\": %m", t);
4944 return false;
4945 }
4946
22e932f4 4947 if (!sd_id128_equal(pt_parsed, partition_type.uuid)) {
5c08da58
LP
4948 log_debug("Partition %u:%u has non-matching partition type " SD_ID128_FORMAT_STR " (needed: " SD_ID128_FORMAT_STR "), ignoring.",
4949 major(partition_devno), minor(partition_devno),
22e932f4 4950 SD_ID128_FORMAT_VAL(pt_parsed), SD_ID128_FORMAT_VAL(partition_type.uuid));
5c08da58
LP
4951 return false;
4952 }
4953
e3b9a5ff
LP
4954 r = blkid_partition_get_uuid_id128(pp, &u);
4955 if (r == -ENXIO) {
4956 log_debug_errno(r, "Partition " DEVNUM_FORMAT_STR " has no UUID.", DEVNUM_FORMAT_VAL(partition_devno));
5c08da58
LP
4957 return false;
4958 }
5c08da58 4959 if (r < 0) {
e3b9a5ff 4960 log_debug_errno(r, "Failed to read partition UUID of " DEVNUM_FORMAT_STR ": %m", DEVNUM_FORMAT_VAL(partition_devno));
5c08da58
LP
4961 return false;
4962 }
4963
e3b9a5ff
LP
4964 log_debug("Automatically found partition " DEVNUM_FORMAT_STR " of right type " SD_ID128_FORMAT_STR ".",
4965 DEVNUM_FORMAT_VAL(partition_devno),
5c08da58
LP
4966 SD_ID128_FORMAT_VAL(pt_parsed));
4967
4968 if (ret_uuid)
4969 *ret_uuid = u;
4970
4971 return true;
4972}
4973
4974static int find_backing_devno(
4975 const char *path,
4976 const char *root,
4977 dev_t *ret) {
4978
4979 _cleanup_free_ char *resolved = NULL;
4980 int r;
4981
4982 assert(path);
4983
4984 r = chase_symlinks(path, root, CHASE_PREFIX_ROOT, &resolved, NULL);
4985 if (r < 0)
4986 return r;
4987
4988 r = path_is_mount_point(resolved, NULL, 0);
4989 if (r < 0)
4990 return r;
4991 if (r == 0) /* Not a mount point, then it's not a partition of its own, let's not automatically use it. */
4992 return -ENOENT;
4993
4994 r = get_block_device(resolved, ret);
4995 if (r < 0)
4996 return r;
4997 if (r == 0) /* Not backed by physical file system, we can't use this */
4998 return -ENOENT;
4999
5000 return 0;
5001}
5002
5003static int resolve_copy_blocks_auto(
22e932f4 5004 GptPartitionType type,
5c08da58
LP
5005 const char *root,
5006 dev_t restrict_devno,
1a037ba2 5007 dev_t *ret_devno,
5c08da58
LP
5008 sd_id128_t *ret_uuid) {
5009
5010 const char *try1 = NULL, *try2 = NULL;
5011 char p[SYS_BLOCK_PATH_MAX("/slaves")];
5012 _cleanup_(closedirp) DIR *d = NULL;
5013 sd_id128_t found_uuid = SD_ID128_NULL;
5014 dev_t devno, found = 0;
5015 int r;
5016
5c08da58
LP
5017 /* Enforce some security restrictions: CopyBlocks=auto should not be an avenue to get outside of the
5018 * --root=/--image= confinement. Specifically, refuse CopyBlocks= in combination with --root= at all,
5019 * and restrict block device references in the --image= case to loopback block device we set up.
5020 *
5021 * restrict_devno contain the dev_t of the loop back device we operate on in case of --image=, and
5022 * thus declares which device (and its partition subdevices) we shall limit access to. If
5023 * restrict_devno is zero no device probing access shall be allowed at all (used for --root=) and if
5024 * it is (dev_t) -1 then free access shall be allowed (if neither switch is used). */
5025
5026 if (restrict_devno == 0)
5027 return log_error_errno(SYNTHETIC_ERRNO(EPERM),
5028 "Automatic discovery of backing block devices not permitted in --root= mode, refusing.");
5029
5030 /* Handles CopyBlocks=auto, and finds the right source partition to copy from. We look for matching
5031 * partitions in the host, using the appropriate directory as key and ensuring that the partition
5032 * type matches. */
5033
22e932f4 5034 if (type.designator == PARTITION_ROOT)
5c08da58 5035 try1 = "/";
22e932f4 5036 else if (type.designator == PARTITION_USR)
5c08da58 5037 try1 = "/usr/";
22e932f4 5038 else if (type.designator == PARTITION_ROOT_VERITY)
5c08da58 5039 try1 = "/";
22e932f4 5040 else if (type.designator == PARTITION_USR_VERITY)
5c08da58 5041 try1 = "/usr/";
22e932f4 5042 else if (type.designator == PARTITION_ESP) {
5c08da58
LP
5043 try1 = "/efi/";
5044 try2 = "/boot/";
22e932f4 5045 } else if (type.designator == PARTITION_XBOOTLDR)
5c08da58
LP
5046 try1 = "/boot/";
5047 else
5048 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
5049 "Partition type " SD_ID128_FORMAT_STR " not supported from automatic source block device discovery.",
22e932f4 5050 SD_ID128_FORMAT_VAL(type.uuid));
5c08da58
LP
5051
5052 r = find_backing_devno(try1, root, &devno);
5053 if (r == -ENOENT && try2)
5054 r = find_backing_devno(try2, root, &devno);
5055 if (r < 0)
5056 return log_error_errno(r, "Failed to resolve automatic CopyBlocks= path for partition type " SD_ID128_FORMAT_STR ", sorry: %m",
22e932f4 5057 SD_ID128_FORMAT_VAL(type.uuid));
5c08da58
LP
5058
5059 xsprintf_sys_block_path(p, "/slaves", devno);
5060 d = opendir(p);
5061 if (d) {
5062 struct dirent *de;
5063
5064 for (;;) {
5065 _cleanup_free_ char *q = NULL, *t = NULL;
5066 sd_id128_t u;
5067 dev_t sl;
5068
5069 errno = 0;
5070 de = readdir_no_dot(d);
5071 if (!de) {
5072 if (errno != 0)
5073 return log_error_errno(errno, "Failed to read directory '%s': %m", p);
5074
5075 break;
5076 }
5077
5078 if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
5079 continue;
5080
5081 q = path_join(p, de->d_name, "/dev");
5082 if (!q)
5083 return log_oom();
5084
5085 r = read_one_line_file(q, &t);
5086 if (r < 0)
5087 return log_error_errno(r, "Failed to read %s: %m", q);
5088
7176f06c 5089 r = parse_devnum(t, &sl);
5c08da58
LP
5090 if (r < 0) {
5091 log_debug_errno(r, "Failed to parse %s, ignoring: %m", q);
5092 continue;
5093 }
5094 if (major(sl) == 0) {
5095 log_debug_errno(r, "Device backing %s is special, ignoring: %m", q);
5096 continue;
5097 }
5098
22e932f4 5099 r = resolve_copy_blocks_auto_candidate(sl, type, restrict_devno, &u);
5c08da58
LP
5100 if (r < 0)
5101 return r;
5102 if (r > 0) {
5103 /* We found a matching one! */
5104 if (found != 0)
5105 return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
5106 "Multiple matching partitions found, refusing.");
5107
5108 found = sl;
5109 found_uuid = u;
5110 }
5111 }
5112 } else if (errno != ENOENT)
5113 return log_error_errno(errno, "Failed open %s: %m", p);
5114 else {
22e932f4 5115 r = resolve_copy_blocks_auto_candidate(devno, type, restrict_devno, &found_uuid);
5c08da58
LP
5116 if (r < 0)
5117 return r;
5118 if (r > 0)
5119 found = devno;
5120 }
5121
5122 if (found == 0)
5123 return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
5124 "Unable to automatically discover suitable partition to copy blocks from.");
5125
1a037ba2
YW
5126 if (ret_devno)
5127 *ret_devno = found;
5c08da58
LP
5128
5129 if (ret_uuid)
5130 *ret_uuid = found_uuid;
5131
5132 return 0;
5133}
5134
5135static int context_open_copy_block_paths(
5136 Context *context,
5c08da58
LP
5137 dev_t restrict_devno) {
5138
757bc2e4
LP
5139 int r;
5140
5141 assert(context);
5142
5143 LIST_FOREACH(partitions, p, context->partitions) {
254d1313 5144 _cleanup_close_ int source_fd = -EBADF;
5c08da58
LP
5145 _cleanup_free_ char *opened = NULL;
5146 sd_id128_t uuid = SD_ID128_NULL;
757bc2e4
LP
5147 uint64_t size;
5148 struct stat st;
5149
5150 assert(p->copy_blocks_fd < 0);
5151 assert(p->copy_blocks_size == UINT64_MAX);
5152
5153 if (PARTITION_EXISTS(p)) /* Never copy over partitions that already exist! */
5154 continue;
5155
5c08da58 5156 if (p->copy_blocks_path) {
757bc2e4 5157
585c5c75 5158 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
5159 if (source_fd < 0)
5160 return log_error_errno(source_fd, "Failed to open '%s': %m", p->copy_blocks_path);
757bc2e4 5161
5c08da58
LP
5162 if (fstat(source_fd, &st) < 0)
5163 return log_error_errno(errno, "Failed to stat block copy file '%s': %m", opened);
5164
5165 if (!S_ISREG(st.st_mode) && restrict_devno != (dev_t) -1)
5166 return log_error_errno(SYNTHETIC_ERRNO(EPERM),
5167 "Copying from block device node is not permitted in --image=/--root= mode, refusing.");
5168
5169 } else if (p->copy_blocks_auto) {
1a037ba2 5170 dev_t devno;
5c08da58 5171
22e932f4 5172 r = resolve_copy_blocks_auto(p->type, p->copy_blocks_root, restrict_devno, &devno, &uuid);
5c08da58
LP
5173 if (r < 0)
5174 return r;
5175
ca822829 5176 source_fd = r = device_open_from_devnum(S_IFBLK, devno, O_RDONLY|O_CLOEXEC|O_NONBLOCK, &opened);
1a037ba2 5177 if (r < 0)
ca822829
YW
5178 return log_error_errno(r, "Failed to open automatically determined source block copy device " DEVNUM_FORMAT_STR ": %m",
5179 DEVNUM_FORMAT_VAL(devno));
5c08da58
LP
5180
5181 if (fstat(source_fd, &st) < 0)
5182 return log_error_errno(errno, "Failed to stat block copy file '%s': %m", opened);
1a037ba2 5183 } else
5c08da58 5184 continue;
757bc2e4
LP
5185
5186 if (S_ISDIR(st.st_mode)) {
ca822829
YW
5187 _cleanup_free_ char *bdev = NULL;
5188 dev_t devt;
757bc2e4
LP
5189
5190 /* If the file is a directory, automatically find the backing block device */
5191
5192 if (major(st.st_dev) != 0)
ca822829 5193 devt = st.st_dev;
757bc2e4 5194 else {
757bc2e4 5195 /* Special support for btrfs */
757bc2e4 5196 r = btrfs_get_block_device_fd(source_fd, &devt);
67f0ac8c 5197 if (r == -EUCLEAN)
5c08da58 5198 return btrfs_log_dev_root(LOG_ERR, r, opened);
757bc2e4 5199 if (r < 0)
5c08da58 5200 return log_error_errno(r, "Unable to determine backing block device of '%s': %m", opened);
757bc2e4 5201 }
757bc2e4
LP
5202
5203 safe_close(source_fd);
5204
ca822829
YW
5205 source_fd = r = device_open_from_devnum(S_IFBLK, devt, O_RDONLY|O_CLOEXEC|O_NONBLOCK, &bdev);
5206 if (r < 0)
5207 return log_error_errno(r, "Failed to open block device backing '%s': %m", opened);
757bc2e4
LP
5208
5209 if (fstat(source_fd, &st) < 0)
5210 return log_error_errno(errno, "Failed to stat block device '%s': %m", bdev);
757bc2e4
LP
5211 }
5212
5213 if (S_ISREG(st.st_mode))
5214 size = st.st_size;
5215 else if (S_ISBLK(st.st_mode)) {
5216 if (ioctl(source_fd, BLKGETSIZE64, &size) != 0)
5217 return log_error_errno(errno, "Failed to determine size of block device to copy from: %m");
5218 } else
5c08da58 5219 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
5220
5221 if (size <= 0)
5c08da58 5222 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has zero size, refusing.", opened);
757bc2e4 5223 if (size % 512 != 0)
5c08da58 5224 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
5225
5226 p->copy_blocks_fd = TAKE_FD(source_fd);
5227 p->copy_blocks_size = size;
5c08da58
LP
5228
5229 free_and_replace(p->copy_blocks_path, opened);
5230
5231 /* When copying from an existing partition copy that partitions UUID if none is configured explicitly */
11749b61 5232 if (!p->new_uuid_is_set && !sd_id128_is_null(uuid)) {
5c08da58 5233 p->new_uuid = uuid;
11749b61
DDM
5234 p->new_uuid_is_set = true;
5235 }
757bc2e4
LP
5236 }
5237
5238 return 0;
5239}
5240
c4a87b76
DDM
5241static int fd_apparent_size(int fd, uint64_t *ret) {
5242 off_t initial = 0;
5243 uint64_t size = 0;
5244
5245 assert(fd >= 0);
5246 assert(ret);
5247
5248 initial = lseek(fd, 0, SEEK_CUR);
5249 if (initial < 0)
5250 return log_error_errno(errno, "Failed to get file offset: %m");
5251
5252 for (off_t off = 0;;) {
5253 off_t r;
5254
5255 r = lseek(fd, off, SEEK_DATA);
5256 if (r < 0 && errno == ENXIO)
5257 /* If errno == ENXIO, that means we've reached the final hole of the file and
5258 * that hole isn't followed by more data. */
5259 break;
5260 if (r < 0)
5261 return log_error_errno(errno, "Failed to seek data in file from offset %"PRIi64": %m", off);
5262
5263 off = r; /* Set the offset to the start of the data segment. */
5264
5265 /* After copying a potential hole, find the end of the data segment by looking for
5266 * the next hole. If we get ENXIO, we're at EOF. */
5267 r = lseek(fd, off, SEEK_HOLE);
5268 if (r < 0) {
5269 if (errno == ENXIO)
5270 break;
5271 return log_error_errno(errno, "Failed to seek hole in file from offset %"PRIi64": %m", off);
5272 }
5273
5274 size += r - off;
5275 off = r;
5276 }
5277
5278 if (lseek(fd, initial, SEEK_SET) < 0)
5279 return log_error_errno(errno, "Failed to reset file offset: %m");
5280
5281 *ret = size;
5282
5283 return 0;
5284}
5285
5286static int context_minimize(Context *context) {
5287 _cleanup_set_free_ Set *denylist = NULL;
5288 const char *vt;
5289 int r;
5290
5291 assert(context);
5292
5293 r = make_copy_files_denylist(context, &denylist);
5294 if (r < 0)
5295 return r;
5296
5297 r = var_tmp_dir(&vt);
5298 if (r < 0)
5299 return log_error_errno(r, "Could not determine temporary directory: %m");
5300
5301 LIST_FOREACH(partitions, p, context->partitions) {
e59678b2 5302 _cleanup_(rm_rf_physical_and_freep) char *root = NULL;
c4a87b76 5303 _cleanup_(unlink_and_freep) char *temp = NULL;
d3201eb4 5304 _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
254d1313 5305 _cleanup_close_ int fd = -EBADF;
c4a87b76
DDM
5306 sd_id128_t fs_uuid;
5307 uint64_t fsz;
5308
5309 if (p->dropped)
5310 continue;
5311
5312 if (PARTITION_EXISTS(p)) /* Never format existing partitions */
5313 continue;
5314
5315 if (!p->format)
5316 continue;
5317
5c33b686 5318 if (p->minimize == MINIMIZE_OFF)
c4a87b76
DDM
5319 continue;
5320
d3201eb4
DDM
5321 if (!partition_needs_populate(p))
5322 continue;
5323
c4a87b76
DDM
5324 assert(!p->copy_blocks_path);
5325
5326 r = tempfn_random_child(vt, "repart", &temp);
5327 if (r < 0)
5328 return log_error_errno(r, "Failed to generate temporary file path: %m");
5329
59e2be46
DDM
5330 if (fstype_is_ro(p->format))
5331 fs_uuid = p->fs_uuid;
5332 else {
c4a87b76
DDM
5333 fd = open(temp, O_CREAT|O_EXCL|O_CLOEXEC|O_RDWR|O_NOCTTY, 0600);
5334 if (fd < 0)
5335 return log_error_errno(errno, "Failed to open temporary file %s: %m", temp);
5336
5337 /* This may seem huge but it will be created sparse so it doesn't take up any space
d3201eb4 5338 * on disk until written to. */
c4a87b76
DDM
5339 if (ftruncate(fd, 1024ULL * 1024ULL * 1024ULL * 1024ULL) < 0)
5340 return log_error_errno(errno, "Failed to truncate temporary file to %s: %m",
5341 FORMAT_BYTES(1024ULL * 1024ULL * 1024ULL * 1024ULL));
5342
d3201eb4
DDM
5343 r = loop_device_make(fd, O_RDWR, 0, UINT64_MAX, 0, 0, LOCK_EX, &d);
5344 if (r < 0 && r != -ENOENT && !ERRNO_IS_PRIVILEGE(r))
5345 return log_error_errno(r, "Failed to make loopback device of %s: %m", temp);
5346
c4a87b76
DDM
5347 /* We're going to populate this filesystem twice so use a random UUID the first time
5348 * to avoid UUID conflicts. */
5349 r = sd_id128_randomize(&fs_uuid);
5350 if (r < 0)
5351 return r;
59e2be46
DDM
5352 }
5353
d3201eb4
DDM
5354 if (!d || fstype_is_ro(p->format)) {
5355 if (!mkfs_supports_root_option(p->format))
5356 return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
5357 "Loop device access is required to populate %s filesystems",
5358 p->format);
5359
e59678b2 5360 r = partition_populate_directory(p, denylist, &root);
c4a87b76
DDM
5361 if (r < 0)
5362 return r;
c4a87b76
DDM
5363 }
5364
8f30c00c 5365 r = make_filesystem(d ? d->node : temp, p->format, strempty(p->new_label), root, fs_uuid, arg_discard, NULL);
c4a87b76
DDM
5366 if (r < 0)
5367 return r;
5368
5369 /* Read-only filesystems are minimal from the first try because they create and size the
5370 * loopback file for us. */
5371 if (fstype_is_ro(p->format)) {
5372 p->copy_blocks_path = TAKE_PTR(temp);
7d07030e 5373 p->copy_blocks_path_is_our_file = true;
c4a87b76
DDM
5374 continue;
5375 }
5376
d3201eb4
DDM
5377 if (!root) {
5378 assert(d);
5379
5380 r = partition_populate_filesystem(p, d->node, denylist);
59e2be46
DDM
5381 if (r < 0)
5382 return r;
5383 }
c4a87b76
DDM
5384
5385 /* Other filesystems need to be provided with a pre-sized loopback file and will adapt to
5386 * fully occupy it. Because we gave the filesystem a 1T sparse file, we need to shrink the
5387 * filesystem down to a reasonable size again to fit it in the disk image. While there are
5388 * some filesystems that support shrinking, it doesn't always work properly (e.g. shrinking
5389 * btrfs gives us a 2.0G filesystem regardless of what we put in it). Instead, let's populate
5390 * the filesystem again, but this time, instead of providing the filesystem with a 1T sparse
5391 * loopback file, let's size the loopback file based on the actual data used by the
5392 * filesystem in the sparse file after the first attempt. This should be a good guess of the
5393 * minimal amount of space needed in the filesystem to fit all the required data.
5394 */
5395 r = fd_apparent_size(fd, &fsz);
5396 if (r < 0)
5397 return r;
5398
5399 /* Massage the size a bit because just going by actual data used in the sparse file isn't
5400 * fool-proof. */
5401 fsz = round_up_size(fsz + (fsz / 2), context->grain_size);
5402 if (minimal_size_by_fs_name(p->format) != UINT64_MAX)
5403 fsz = MAX(minimal_size_by_fs_name(p->format), fsz);
5404
d3201eb4
DDM
5405 d = loop_device_unref(d);
5406
c4a87b76
DDM
5407 /* Erase the previous filesystem first. */
5408 if (ftruncate(fd, 0))
5409 return log_error_errno(errno, "Failed to erase temporary file: %m");
5410
5411 if (ftruncate(fd, fsz))
5412 return log_error_errno(errno, "Failed to truncate temporary file to %s: %m", FORMAT_BYTES(fsz));
5413
d3201eb4
DDM
5414 r = loop_device_make(fd, O_RDWR, 0, UINT64_MAX, 0, 0, LOCK_EX, &d);
5415 if (r < 0 && r != -ENOENT && !ERRNO_IS_PRIVILEGE(r))
5416 return log_error_errno(r, "Failed to make loopback device of %s: %m", temp);
5417
8f30c00c 5418 r = make_filesystem(d ? d->node : temp, p->format, strempty(p->new_label), root, p->fs_uuid, arg_discard, NULL);
c4a87b76
DDM
5419 if (r < 0)
5420 return r;
5421
d3201eb4
DDM
5422 if (!root) {
5423 assert(d);
5424
5425 r = partition_populate_filesystem(p, d->node, denylist);
59e2be46
DDM
5426 if (r < 0)
5427 return r;
5428 }
c4a87b76
DDM
5429
5430 p->copy_blocks_path = TAKE_PTR(temp);
7d07030e 5431 p->copy_blocks_path_is_our_file = true;
c4a87b76
DDM
5432 }
5433
5434 return 0;
5435}
5436
220780db 5437static int parse_partition_types(const char *p, sd_id128_t **partitions, size_t *n_partitions) {
81d1098b
DDM
5438 int r;
5439
220780db
DDM
5440 assert(partitions);
5441 assert(n_partitions);
5442
81d1098b
DDM
5443 for (;;) {
5444 _cleanup_free_ char *name = NULL;
5445 GptPartitionType type;
5446
5447 r = extract_first_word(&p, &name, ",", EXTRACT_CUNESCAPE|EXTRACT_DONT_COALESCE_SEPARATORS);
5448 if (r == 0)
5449 break;
5450 if (r < 0)
766f52f2 5451 return log_error_errno(r, "Failed to extract partition type identifier or GUID: %s", p);
81d1098b
DDM
5452
5453 r = gpt_partition_type_from_string(name, &type);
5454 if (r < 0)
766f52f2 5455 return log_error_errno(r, "'%s' is not a valid partition type identifier or GUID", name);
81d1098b 5456
220780db 5457 if (!GREEDY_REALLOC(*partitions, *n_partitions + 1))
81d1098b
DDM
5458 return log_oom();
5459
220780db 5460 (*partitions)[(*n_partitions)++] = type.uuid;
81d1098b
DDM
5461 }
5462
5463 return 0;
5464}
5465
e594a3b1
LP
5466static int help(void) {
5467 _cleanup_free_ char *link = NULL;
5468 int r;
5469
5470 r = terminal_urlify_man("systemd-repart", "1", &link);
5471 if (r < 0)
5472 return log_oom();
5473
5474 printf("%s [OPTIONS...] [DEVICE]\n"
5475 "\n%sGrow and add partitions to partition table.%s\n\n"
5476 " -h --help Show this help\n"
5477 " --version Show package version\n"
896e678b
LP
5478 " --no-pager Do not pipe output into a pager\n"
5479 " --no-legend Do not show the headers and footers\n"
e594a3b1 5480 " --dry-run=BOOL Whether to run dry-run operation\n"
a26f4a49
LP
5481 " --empty=MODE One of refuse, allow, require, force, create; controls\n"
5482 " how to handle empty disks lacking partition tables\n"
e594a3b1 5483 " --discard=BOOL Whether to discard backing blocks for new partitions\n"
2d2d0a57 5484 " --pretty=BOOL Whether to show pretty summary before doing changes\n"
e594a3b1
LP
5485 " --factory-reset=BOOL Whether to remove data partitions before recreating\n"
5486 " them\n"
5487 " --can-factory-reset Test whether factory reset is defined\n"
5488 " --root=PATH Operate relative to root path\n"
252d6267 5489 " --image=PATH Operate relative to image file\n"
9d252fbb 5490 " --definitions=DIR Find partition definitions in specified directory\n"
b9df3536 5491 " --key-file=PATH Key to use when encrypting partitions\n"
b456191d
DDM
5492 " --private-key=PATH Private key to use when generating verity roothash\n"
5493 " signatures\n"
5494 " --certificate=PATH PEM certificate to use when generating verity\n"
5495 " roothash signatures\n"
889914ef 5496 " --tpm2-device=PATH Path to TPM2 device node to use\n"
a1788a69 5497 " --tpm2-pcrs=PCR1+PCR2+PCR3+…\n"
889914ef 5498 " TPM2 PCR indexes to use for TPM2 enrollment\n"
02ef97cd
LP
5499 " --tpm2-public-key=PATH\n"
5500 " Enroll signed TPM2 PCR policy against PEM public key\n"
5501 " --tpm2-public-key-pcrs=PCR1+PCR2+PCR3+…\n"
5502 " Enroll signed TPM2 PCR policy for specified TPM2 PCRs\n"
e594a3b1 5503 " --seed=UUID 128bit seed UUID to derive all UUIDs from\n"
a26f4a49 5504 " --size=BYTES Grow loopback file to specified size\n"
2d2d0a57 5505 " --json=pretty|short|off\n"
de8231b0 5506 " Generate JSON output\n"
4cee8333 5507 " --split=BOOL Whether to generate split artifacts\n"
81d1098b 5508 " --include-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
7d505753 5509 " Ignore partitions not of the specified types\n"
81d1098b 5510 " --exclude-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
7d505753 5511 " Ignore partitions of the specified types\n"
8275334b 5512 " --defer-partitions=PARTITION1,PARTITION2,PARTITION3,…\n"
7d505753
DDM
5513 " Take partitions of the specified types into account\n"
5514 " but don't populate them yet\n"
bc556335
DDM
5515 "\nSee the %s for details.\n",
5516 program_invocation_short_name,
5517 ansi_highlight(),
5518 ansi_normal(),
5519 link);
e594a3b1
LP
5520
5521 return 0;
5522}
5523
5524static int parse_argv(int argc, char *argv[]) {
5525
5526 enum {
5527 ARG_VERSION = 0x100,
896e678b
LP
5528 ARG_NO_PAGER,
5529 ARG_NO_LEGEND,
e594a3b1
LP
5530 ARG_DRY_RUN,
5531 ARG_EMPTY,
5532 ARG_DISCARD,
5533 ARG_FACTORY_RESET,
5534 ARG_CAN_FACTORY_RESET,
5535 ARG_ROOT,
252d6267 5536 ARG_IMAGE,
e594a3b1
LP
5537 ARG_SEED,
5538 ARG_PRETTY,
5539 ARG_DEFINITIONS,
a26f4a49 5540 ARG_SIZE,
a015fbe7 5541 ARG_JSON,
b9df3536 5542 ARG_KEY_FILE,
b456191d
DDM
5543 ARG_PRIVATE_KEY,
5544 ARG_CERTIFICATE,
889914ef
LP
5545 ARG_TPM2_DEVICE,
5546 ARG_TPM2_PCRS,
02ef97cd
LP
5547 ARG_TPM2_PUBLIC_KEY,
5548 ARG_TPM2_PUBLIC_KEY_PCRS,
4cee8333 5549 ARG_SPLIT,
81d1098b
DDM
5550 ARG_INCLUDE_PARTITIONS,
5551 ARG_EXCLUDE_PARTITIONS,
8275334b 5552 ARG_DEFER_PARTITIONS,
e594a3b1
LP
5553 };
5554
5555 static const struct option options[] = {
02ef97cd
LP
5556 { "help", no_argument, NULL, 'h' },
5557 { "version", no_argument, NULL, ARG_VERSION },
5558 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
5559 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
5560 { "dry-run", required_argument, NULL, ARG_DRY_RUN },
5561 { "empty", required_argument, NULL, ARG_EMPTY },
5562 { "discard", required_argument, NULL, ARG_DISCARD },
5563 { "factory-reset", required_argument, NULL, ARG_FACTORY_RESET },
5564 { "can-factory-reset", no_argument, NULL, ARG_CAN_FACTORY_RESET },
5565 { "root", required_argument, NULL, ARG_ROOT },
5566 { "image", required_argument, NULL, ARG_IMAGE },
5567 { "seed", required_argument, NULL, ARG_SEED },
5568 { "pretty", required_argument, NULL, ARG_PRETTY },
5569 { "definitions", required_argument, NULL, ARG_DEFINITIONS },
5570 { "size", required_argument, NULL, ARG_SIZE },
5571 { "json", required_argument, NULL, ARG_JSON },
5572 { "key-file", required_argument, NULL, ARG_KEY_FILE },
b456191d
DDM
5573 { "private-key", required_argument, NULL, ARG_PRIVATE_KEY },
5574 { "certificate", required_argument, NULL, ARG_CERTIFICATE },
02ef97cd
LP
5575 { "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
5576 { "tpm2-pcrs", required_argument, NULL, ARG_TPM2_PCRS },
5577 { "tpm2-public-key", required_argument, NULL, ARG_TPM2_PUBLIC_KEY },
5578 { "tpm2-public-key-pcrs", required_argument, NULL, ARG_TPM2_PUBLIC_KEY_PCRS },
4cee8333 5579 { "split", required_argument, NULL, ARG_SPLIT },
81d1098b
DDM
5580 { "include-partitions", required_argument, NULL, ARG_INCLUDE_PARTITIONS },
5581 { "exclude-partitions", required_argument, NULL, ARG_EXCLUDE_PARTITIONS },
8275334b 5582 { "defer-partitions", required_argument, NULL, ARG_DEFER_PARTITIONS },
e594a3b1
LP
5583 {}
5584 };
5585
a26f4a49 5586 int c, r, dry_run = -1;
e594a3b1
LP
5587
5588 assert(argc >= 0);
5589 assert(argv);
5590
5591 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
5592
5593 switch (c) {
5594
5595 case 'h':
5596 return help();
5597
5598 case ARG_VERSION:
5599 return version();
5600
896e678b
LP
5601 case ARG_NO_PAGER:
5602 arg_pager_flags |= PAGER_DISABLE;
5603 break;
5604
5605 case ARG_NO_LEGEND:
5606 arg_legend = false;
5607 break;
5608
e594a3b1 5609 case ARG_DRY_RUN:
599c7c54 5610 r = parse_boolean_argument("--dry-run=", optarg, &arg_dry_run);
e594a3b1 5611 if (r < 0)
599c7c54 5612 return r;
e594a3b1
LP
5613 break;
5614
5615 case ARG_EMPTY:
5616 if (isempty(optarg) || streq(optarg, "refuse"))
5617 arg_empty = EMPTY_REFUSE;
5618 else if (streq(optarg, "allow"))
5619 arg_empty = EMPTY_ALLOW;
5620 else if (streq(optarg, "require"))
5621 arg_empty = EMPTY_REQUIRE;
5622 else if (streq(optarg, "force"))
5623 arg_empty = EMPTY_FORCE;
a26f4a49
LP
5624 else if (streq(optarg, "create")) {
5625 arg_empty = EMPTY_CREATE;
5626
5627 if (dry_run < 0)
5628 dry_run = false; /* Imply --dry-run=no if we create the loopback file
5629 * anew. After all we cannot really break anyone's
5630 * partition tables that way. */
5631 } else
e594a3b1
LP
5632 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
5633 "Failed to parse --empty= parameter: %s", optarg);
5634 break;
5635
5636 case ARG_DISCARD:
599c7c54 5637 r = parse_boolean_argument("--discard=", optarg, &arg_discard);
e594a3b1 5638 if (r < 0)
599c7c54 5639 return r;
e594a3b1
LP
5640 break;
5641
5642 case ARG_FACTORY_RESET:
c3470872 5643 r = parse_boolean_argument("--factory-reset=", optarg, NULL);
e594a3b1 5644 if (r < 0)
c3470872 5645 return r;
e594a3b1
LP
5646 arg_factory_reset = r;
5647 break;
5648
5649 case ARG_CAN_FACTORY_RESET:
5650 arg_can_factory_reset = true;
5651 break;
5652
5653 case ARG_ROOT:
252d6267
LP
5654 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_root);
5655 if (r < 0)
5656 return r;
5657 break;
5658
5659 case ARG_IMAGE:
5660 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
e594a3b1
LP
5661 if (r < 0)
5662 return r;
5663 break;
5664
5665 case ARG_SEED:
5666 if (isempty(optarg)) {
5667 arg_seed = SD_ID128_NULL;
5668 arg_randomize = false;
5669 } else if (streq(optarg, "random"))
5670 arg_randomize = true;
5671 else {
5672 r = sd_id128_from_string(optarg, &arg_seed);
5673 if (r < 0)
5674 return log_error_errno(r, "Failed to parse seed: %s", optarg);
5675
5676 arg_randomize = false;
5677 }
5678
5679 break;
5680
5681 case ARG_PRETTY:
c3470872 5682 r = parse_boolean_argument("--pretty=", optarg, NULL);
e594a3b1 5683 if (r < 0)
c3470872 5684 return r;
e594a3b1
LP
5685 arg_pretty = r;
5686 break;
5687
224c853f
RP
5688 case ARG_DEFINITIONS: {
5689 _cleanup_free_ char *path = NULL;
5690 r = parse_path_argument(optarg, false, &path);
e594a3b1
LP
5691 if (r < 0)
5692 return r;
224c853f
RP
5693 if (strv_consume(&arg_definitions, TAKE_PTR(path)) < 0)
5694 return log_oom();
e594a3b1 5695 break;
224c853f 5696 }
e594a3b1 5697
a26f4a49
LP
5698 case ARG_SIZE: {
5699 uint64_t parsed, rounded;
5700
170c9823
LP
5701 if (streq(optarg, "auto")) {
5702 arg_size = UINT64_MAX;
5703 arg_size_auto = true;
5704 break;
5705 }
5706
a26f4a49
LP
5707 r = parse_size(optarg, 1024, &parsed);
5708 if (r < 0)
5709 return log_error_errno(r, "Failed to parse --size= parameter: %s", optarg);
5710
5711 rounded = round_up_size(parsed, 4096);
5712 if (rounded == 0)
5713 return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Specified image size too small, refusing.");
5714 if (rounded == UINT64_MAX)
5715 return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Specified image size too large, refusing.");
5716
5717 if (rounded != parsed)
e2341b6b
DT
5718 log_warning("Specified size is not a multiple of 4096, rounding up automatically. (%" PRIu64 " %s %" PRIu64 ")",
5719 parsed, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), rounded);
a26f4a49
LP
5720
5721 arg_size = rounded;
170c9823 5722 arg_size_auto = false;
a26f4a49
LP
5723 break;
5724 }
b9df3536 5725
a015fbe7 5726 case ARG_JSON:
b1e8f46c 5727 r = parse_json_argument(optarg, &arg_json_format_flags);
6a01ea4a
LP
5728 if (r <= 0)
5729 return r;
a015fbe7
TH
5730
5731 break;
5732
b9df3536
LP
5733 case ARG_KEY_FILE: {
5734 _cleanup_(erase_and_freep) char *k = NULL;
5735 size_t n = 0;
5736
8b3c3a49 5737 r = read_full_file_full(
986311c2 5738 AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
8b3c3a49
LP
5739 READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
5740 NULL,
5741 &k, &n);
b9df3536
LP
5742 if (r < 0)
5743 return log_error_errno(r, "Failed to read key file '%s': %m", optarg);
5744
5745 erase_and_free(arg_key);
5746 arg_key = TAKE_PTR(k);
5747 arg_key_size = n;
5748 break;
5749 }
a26f4a49 5750
b456191d
DDM
5751 case ARG_PRIVATE_KEY: {
5752 _cleanup_(erase_and_freep) char *k = NULL;
5753 size_t n = 0;
5754
5755 r = read_full_file_full(
5756 AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
5757 READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
5758 NULL,
5759 &k, &n);
5760 if (r < 0)
5761 return log_error_errno(r, "Failed to read key file '%s': %m", optarg);
5762
5763 EVP_PKEY_free(arg_private_key);
5764 arg_private_key = NULL;
5765 r = parse_private_key(k, n, &arg_private_key);
5766 if (r < 0)
5767 return r;
5768 break;
5769 }
5770
5771 case ARG_CERTIFICATE: {
5772 _cleanup_free_ char *cert = NULL;
5773 size_t n = 0;
5774
5775 r = read_full_file_full(
5776 AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
5777 READ_FULL_FILE_CONNECT_SOCKET,
5778 NULL,
5779 &cert, &n);
5780 if (r < 0)
5781 return log_error_errno(r, "Failed to read certificate file '%s': %m", optarg);
5782
5783 X509_free(arg_certificate);
5784 arg_certificate = NULL;
5785 r = parse_x509_certificate(cert, n, &arg_certificate);
5786 if (r < 0)
5787 return r;
5788 break;
5789 }
5790
889914ef
LP
5791 case ARG_TPM2_DEVICE: {
5792 _cleanup_free_ char *device = NULL;
5793
5794 if (streq(optarg, "list"))
5795 return tpm2_list_devices();
5796
5797 if (!streq(optarg, "auto")) {
5798 device = strdup(optarg);
5799 if (!device)
5800 return log_oom();
5801 }
5802
5803 free(arg_tpm2_device);
5804 arg_tpm2_device = TAKE_PTR(device);
5805 break;
5806 }
5807
222a951f
LP
5808 case ARG_TPM2_PCRS:
5809 r = tpm2_parse_pcr_argument(optarg, &arg_tpm2_pcr_mask);
889914ef
LP
5810 if (r < 0)
5811 return r;
5812
889914ef 5813 break;
889914ef 5814
02ef97cd
LP
5815 case ARG_TPM2_PUBLIC_KEY:
5816 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_public_key);
5817 if (r < 0)
5818 return r;
5819
5820 break;
5821
5822 case ARG_TPM2_PUBLIC_KEY_PCRS:
5823 r = tpm2_parse_pcr_argument(optarg, &arg_tpm2_public_key_pcr_mask);
5824 if (r < 0)
5825 return r;
5826
5827 break;
5828
4cee8333
DDM
5829 case ARG_SPLIT:
5830 r = parse_boolean_argument("--split=", optarg, NULL);
5831 if (r < 0)
5832 return r;
5833
5834 arg_split = r;
5835 break;
5836
81d1098b
DDM
5837 case ARG_INCLUDE_PARTITIONS:
5838 if (arg_filter_partitions_type == FILTER_PARTITIONS_EXCLUDE)
5839 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
5840 "Combination of --include-partitions= and --exclude-partitions= is invalid.");
5841
220780db 5842 r = parse_partition_types(optarg, &arg_filter_partitions, &arg_n_filter_partitions);
81d1098b
DDM
5843 if (r < 0)
5844 return r;
5845
5846 arg_filter_partitions_type = FILTER_PARTITIONS_INCLUDE;
5847
5848 break;
5849
5850 case ARG_EXCLUDE_PARTITIONS:
5851 if (arg_filter_partitions_type == FILTER_PARTITIONS_INCLUDE)
5852 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
5853 "Combination of --include-partitions= and --exclude-partitions= is invalid.");
5854
220780db 5855 r = parse_partition_types(optarg, &arg_filter_partitions, &arg_n_filter_partitions);
81d1098b
DDM
5856 if (r < 0)
5857 return r;
5858
5859 arg_filter_partitions_type = FILTER_PARTITIONS_EXCLUDE;
5860
5861 break;
5862
8275334b
DDM
5863 case ARG_DEFER_PARTITIONS:
5864 r = parse_partition_types(optarg, &arg_defer_partitions, &arg_n_defer_partitions);
7d505753
DDM
5865 if (r < 0)
5866 return r;
5867
5868 break;
5869
e594a3b1
LP
5870 case '?':
5871 return -EINVAL;
5872
5873 default:
04499a70 5874 assert_not_reached();
e594a3b1
LP
5875 }
5876
5877 if (argc - optind > 1)
5878 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
5879 "Expected at most one argument, the path to the block device.");
5880
a26f4a49 5881 if (arg_factory_reset > 0 && IN_SET(arg_empty, EMPTY_FORCE, EMPTY_REQUIRE, EMPTY_CREATE))
e594a3b1 5882 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
a26f4a49 5883 "Combination of --factory-reset=yes and --empty=force/--empty=require/--empty=create is invalid.");
e594a3b1
LP
5884
5885 if (arg_can_factory_reset)
a26f4a49
LP
5886 arg_dry_run = true; /* When --can-factory-reset is specified we don't make changes, hence
5887 * non-dry-run mode makes no sense. Thus, imply dry run mode so that we
5888 * open things strictly read-only. */
5889 else if (dry_run >= 0)
5890 arg_dry_run = dry_run;
5891
170c9823 5892 if (arg_empty == EMPTY_CREATE && (arg_size == UINT64_MAX && !arg_size_auto))
a26f4a49
LP
5893 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
5894 "If --empty=create is specified, --size= must be specified, too.");
e594a3b1 5895
252d6267
LP
5896 if (arg_image && arg_root)
5897 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
5898 else if (!arg_image && !arg_root && in_initrd()) {
8f47e32a
LP
5899
5900 /* By default operate on /sysusr/ or /sysroot/ when invoked in the initrd. We prefer the
5901 * former, if it is mounted, so that we have deterministic behaviour on systems where /usr/
5902 * is vendor-supplied but the root fs formatted on first boot. */
5903 r = path_is_mount_point("/sysusr/usr", NULL, 0);
5904 if (r <= 0) {
5905 if (r < 0 && r != -ENOENT)
5906 log_debug_errno(r, "Unable to determine whether /sysusr/usr is a mount point, assuming it is not: %m");
5907
5908 arg_root = strdup("/sysroot");
5909 } else
5910 arg_root = strdup("/sysusr");
252d6267
LP
5911 if (!arg_root)
5912 return log_oom();
5913 }
5914
e594a3b1 5915 arg_node = argc > optind ? argv[optind] : NULL;
a26f4a49 5916
252d6267 5917 if (IN_SET(arg_empty, EMPTY_FORCE, EMPTY_REQUIRE, EMPTY_CREATE) && !arg_node && !arg_image)
a26f4a49
LP
5918 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
5919 "A path to a device node or loopback file must be specified when --empty=force, --empty=require or --empty=create are used.");
5920
4cee8333
DDM
5921 if (arg_split && !arg_node)
5922 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
5923 "A path to a loopback file must be specified when --split is used.");
5924
889914ef
LP
5925 if (arg_tpm2_pcr_mask == UINT32_MAX)
5926 arg_tpm2_pcr_mask = TPM2_PCR_MASK_DEFAULT;
02ef97cd
LP
5927 if (arg_tpm2_public_key_pcr_mask == UINT32_MAX)
5928 arg_tpm2_public_key_pcr_mask = UINT32_C(1) << TPM_PCR_INDEX_KERNEL_IMAGE;
889914ef 5929
a26d463d
DDM
5930 if (arg_pretty < 0 && isatty(STDOUT_FILENO))
5931 arg_pretty = true;
5932
e594a3b1
LP
5933 return 1;
5934}
5935
5936static int parse_proc_cmdline_factory_reset(void) {
5937 bool b;
5938 int r;
5939
5940 if (arg_factory_reset >= 0) /* Never override what is specified on the process command line */
5941 return 0;
5942
5943 if (!in_initrd()) /* Never honour kernel command line factory reset request outside of the initrd */
5944 return 0;
5945
5946 r = proc_cmdline_get_bool("systemd.factory_reset", &b);
5947 if (r < 0)
5948 return log_error_errno(r, "Failed to parse systemd.factory_reset kernel command line argument: %m");
5949 if (r > 0) {
5950 arg_factory_reset = b;
5951
5952 if (b)
5953 log_notice("Honouring factory reset requested via kernel command line.");
5954 }
5955
5956 return 0;
5957}
5958
5959static int parse_efi_variable_factory_reset(void) {
5960 _cleanup_free_ char *value = NULL;
5961 int r;
5962
5963 if (arg_factory_reset >= 0) /* Never override what is specified on the process command line */
5964 return 0;
5965
5966 if (!in_initrd()) /* Never honour EFI variable factory reset request outside of the initrd */
5967 return 0;
5968
e6f055cb 5969 r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE(FactoryReset), &value);
e594a3b1
LP
5970 if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
5971 return 0;
5972 if (r < 0)
5973 return log_error_errno(r, "Failed to read EFI variable FactoryReset: %m");
5974
5975 r = parse_boolean(value);
5976 if (r < 0)
5977 return log_error_errno(r, "Failed to parse EFI variable FactoryReset: %m");
5978
5979 arg_factory_reset = r;
5980 if (r)
111a3aae 5981 log_notice("Factory reset requested via EFI variable FactoryReset.");
e594a3b1
LP
5982
5983 return 0;
5984}
5985
5986static int remove_efi_variable_factory_reset(void) {
5987 int r;
5988
e6f055cb 5989 r = efi_set_variable(EFI_SYSTEMD_VARIABLE(FactoryReset), NULL, 0);
e594a3b1
LP
5990 if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
5991 return 0;
5992 if (r < 0)
5993 return log_error_errno(r, "Failed to remove EFI variable FactoryReset: %m");
5994
5995 log_info("Successfully unset EFI variable FactoryReset.");
5996 return 0;
5997}
5998
252d6267
LP
5999static int acquire_root_devno(
6000 const char *p,
6001 const char *root,
6002 int mode,
6003 char **ret,
6004 int *ret_fd) {
6005
6006 _cleanup_free_ char *found_path = NULL;
6007 dev_t devno, fd_devno = MODE_INVALID;
254d1313 6008 _cleanup_close_ int fd = -EBADF;
e594a3b1 6009 struct stat st;
e594a3b1
LP
6010 int r;
6011
a26f4a49
LP
6012 assert(p);
6013 assert(ret);
6014 assert(ret_fd);
6015
252d6267 6016 fd = chase_symlinks_and_open(p, root, CHASE_PREFIX_ROOT, mode, &found_path);
e594a3b1 6017 if (fd < 0)
252d6267 6018 return fd;
e594a3b1
LP
6019
6020 if (fstat(fd, &st) < 0)
6021 return -errno;
6022
6023 if (S_ISREG(st.st_mode)) {
252d6267 6024 *ret = TAKE_PTR(found_path);
a26f4a49 6025 *ret_fd = TAKE_FD(fd);
e594a3b1
LP
6026 return 0;
6027 }
6028
252d6267
LP
6029 if (S_ISBLK(st.st_mode)) {
6030 /* Refuse referencing explicit block devices if a root dir is specified, after all we should
5c08da58 6031 * not be able to leave the image the root path constrains us to. */
252d6267
LP
6032 if (root)
6033 return -EPERM;
6034
a26f4a49 6035 fd_devno = devno = st.st_rdev;
252d6267 6036 } else if (S_ISDIR(st.st_mode)) {
e594a3b1
LP
6037
6038 devno = st.st_dev;
a26f4a49 6039 if (major(devno) == 0) {
e594a3b1
LP
6040 r = btrfs_get_block_device_fd(fd, &devno);
6041 if (r == -ENOTTY) /* not btrfs */
6042 return -ENODEV;
6043 if (r < 0)
6044 return r;
6045 }
e594a3b1
LP
6046 } else
6047 return -ENOTBLK;
6048
6049 /* From dm-crypt to backing partition */
6050 r = block_get_originating(devno, &devno);
8e5f3cec
LP
6051 if (r == -ENOENT)
6052 log_debug_errno(r, "Device '%s' has no dm-crypt/dm-verity device, no need to look for underlying block device.", p);
6053 else if (r < 0)
e594a3b1
LP
6054 log_debug_errno(r, "Failed to find underlying block device for '%s', ignoring: %m", p);
6055
6056 /* From partition to whole disk containing it */
6057 r = block_get_whole_disk(devno, &devno);
6058 if (r < 0)
162392b7 6059 log_debug_errno(r, "Failed to find whole disk block device for '%s', ignoring: %m", p);
e594a3b1 6060
4fe46c34 6061 r = devname_from_devnum(S_IFBLK, devno, ret);
a26f4a49
LP
6062 if (r < 0)
6063 return log_debug_errno(r, "Failed to determine canonical path for '%s': %m", p);
6064
6bbae9f8 6065 /* Only if we still look at the same block device we can reuse the fd. Otherwise return an
a26f4a49 6066 * invalidated fd. */
f5fbe71d 6067 *ret_fd = fd_devno != MODE_INVALID && fd_devno == devno ? TAKE_FD(fd) : -1;
a26f4a49 6068 return 0;
e594a3b1
LP
6069}
6070
cc751c75 6071static int find_root(Context *context) {
54632d2e 6072 _cleanup_free_ char *device = NULL;
5980d463 6073 int r;
e594a3b1 6074
cc751c75 6075 assert(context);
a26f4a49 6076
e594a3b1 6077 if (arg_node) {
a26f4a49 6078 if (arg_empty == EMPTY_CREATE) {
254d1313 6079 _cleanup_close_ int fd = -EBADF;
a26f4a49
LP
6080 _cleanup_free_ char *s = NULL;
6081
6082 s = strdup(arg_node);
6083 if (!s)
6084 return log_oom();
6085
5332d7c6 6086 fd = open(arg_node, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOFOLLOW, 0666);
a26f4a49
LP
6087 if (fd < 0)
6088 return log_error_errno(errno, "Failed to create '%s': %m", arg_node);
6089
cc751c75 6090 context->node = TAKE_PTR(s);
db1d4e6b 6091 context->node_is_our_file = true;
cc751c75 6092 context->backing_fd = TAKE_FD(fd);
a26f4a49
LP
6093 return 0;
6094 }
6095
252d6267
LP
6096 /* Note that we don't specify a root argument here: if the user explicitly configured a node
6097 * we'll take it relative to the host, not the image */
cc751c75 6098 r = acquire_root_devno(arg_node, NULL, O_RDONLY|O_CLOEXEC, &context->node, &context->backing_fd);
67f0ac8c
LP
6099 if (r == -EUCLEAN)
6100 return btrfs_log_dev_root(LOG_ERR, r, arg_node);
e594a3b1 6101 if (r < 0)
aa2a74ad 6102 return log_error_errno(r, "Failed to open file or determine backing device of %s: %m", arg_node);
e594a3b1
LP
6103
6104 return 0;
6105 }
6106
a26f4a49
LP
6107 assert(IN_SET(arg_empty, EMPTY_REFUSE, EMPTY_ALLOW));
6108
54632d2e
KK
6109 /* If the root mount has been replaced by some form of volatile file system (overlayfs), the
6110 * original root block device node is symlinked in /run/systemd/volatile-root. Let's read that
6111 * here. */
6112 r = readlink_malloc("/run/systemd/volatile-root", &device);
6113 if (r == -ENOENT) { /* volatile-root not found */
6114 /* Let's search for the root device. We look for two cases here: first in /, and then in /usr. The
6115 * latter we check for cases where / is a tmpfs and only /usr is an actual persistent block device
6116 * (think: volatile setups) */
e594a3b1 6117
54632d2e 6118 FOREACH_STRING(p, "/", "/usr") {
e594a3b1 6119
cc751c75
DDM
6120 r = acquire_root_devno(p, arg_root, O_RDONLY|O_DIRECTORY|O_CLOEXEC, &context->node,
6121 &context->backing_fd);
54632d2e
KK
6122 if (r < 0) {
6123 if (r == -EUCLEAN)
6124 return btrfs_log_dev_root(LOG_ERR, r, p);
6125 if (r != -ENODEV)
6126 return log_error_errno(r, "Failed to determine backing device of %s: %m", p);
6127 } else
6128 return 0;
6129 }
6130 } else if (r < 0)
6131 return log_error_errno(r, "Failed to read symlink /run/systemd/volatile-root: %m");
6132 else {
cc751c75 6133 r = acquire_root_devno(device, NULL, O_RDONLY|O_CLOEXEC, &context->node, &context->backing_fd);
54632d2e
KK
6134 if (r == -EUCLEAN)
6135 return btrfs_log_dev_root(LOG_ERR, r, device);
6136 if (r < 0)
6137 return log_error_errno(r, "Failed to open file or determine backing device of %s: %m", device);
6138
6139 return 0;
e594a3b1
LP
6140 }
6141
6142 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "Failed to discover root block device.");
6143}
6144
f9b3afae 6145static int resize_pt(int fd) {
f9b3afae
LP
6146 _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
6147 int r;
6148
6149 /* After resizing the backing file we need to resize the partition table itself too, so that it takes
6150 * possession of the enlarged backing file. For this it suffices to open the device with libfdisk and
6151 * immediately write it again, with no changes. */
6152
f8cf3d19 6153 r = fdisk_new_context_fd(fd, /* read_only= */ false, &c);
f9b3afae 6154 if (r < 0)
ddb6eeaf 6155 return log_error_errno(r, "Failed to open device '%s': %m", FORMAT_PROC_FD_PATH(fd));
f9b3afae
LP
6156
6157 r = fdisk_has_label(c);
6158 if (r < 0)
ddb6eeaf 6159 return log_error_errno(r, "Failed to determine whether disk '%s' has a disk label: %m", FORMAT_PROC_FD_PATH(fd));
f9b3afae
LP
6160 if (r == 0) {
6161 log_debug("Not resizing partition table, as there currently is none.");
6162 return 0;
6163 }
6164
6165 r = fdisk_write_disklabel(c);
6166 if (r < 0)
6167 return log_error_errno(r, "Failed to write resized partition table: %m");
6168
6169 log_info("Resized partition table.");
6170 return 1;
6171}
6172
252d6267
LP
6173static int resize_backing_fd(
6174 const char *node, /* The primary way we access the disk image to operate on */
6175 int *fd, /* An O_RDONLY fd referring to that inode */
6176 const char *backing_file, /* If the above refers to a loopback device, the backing regular file for that, which we can grow */
6177 LoopDevice *loop_device) {
6178
254d1313 6179 _cleanup_close_ int writable_fd = -EBADF;
252d6267 6180 uint64_t current_size;
a26f4a49
LP
6181 struct stat st;
6182 int r;
6183
6184 assert(node);
6185 assert(fd);
6186
6187 if (arg_size == UINT64_MAX) /* Nothing to do */
6188 return 0;
6189
6190 if (*fd < 0) {
6191 /* Open the file if we haven't opened it yet. Note that we open it read-only here, just to
6192 * keep a reference to the file we can pass around. */
6193 *fd = open(node, O_RDONLY|O_CLOEXEC);
6194 if (*fd < 0)
6195 return log_error_errno(errno, "Failed to open '%s' in order to adjust size: %m", node);
6196 }
6197
6198 if (fstat(*fd, &st) < 0)
6199 return log_error_errno(errno, "Failed to stat '%s': %m", node);
6200
252d6267
LP
6201 if (S_ISBLK(st.st_mode)) {
6202 if (!backing_file)
6203 return log_error_errno(SYNTHETIC_ERRNO(EBADF), "Cannot resize block device '%s'.", node);
6204
6205 assert(loop_device);
a26f4a49 6206
252d6267
LP
6207 if (ioctl(*fd, BLKGETSIZE64, &current_size) < 0)
6208 return log_error_errno(errno, "Failed to determine size of block device %s: %m", node);
6209 } else {
6210 r = stat_verify_regular(&st);
6211 if (r < 0)
6212 return log_error_errno(r, "Specified path '%s' is not a regular file or loopback block device, cannot resize: %m", node);
6213
6214 assert(!backing_file);
6215 assert(!loop_device);
6216 current_size = st.st_size;
6217 }
6218
252d6267 6219 if (current_size >= arg_size) {
2b59bf51
ZJS
6220 log_info("File '%s' already is of requested size or larger, not growing. (%s >= %s)",
6221 node, FORMAT_BYTES(current_size), FORMAT_BYTES(arg_size));
a26f4a49
LP
6222 return 0;
6223 }
6224
252d6267
LP
6225 if (S_ISBLK(st.st_mode)) {
6226 assert(backing_file);
6227
6228 /* This is a loopback device. We can't really grow those directly, but we can grow the
6229 * backing file, hence let's do that. */
6230
6231 writable_fd = open(backing_file, O_WRONLY|O_CLOEXEC|O_NONBLOCK);
6232 if (writable_fd < 0)
6233 return log_error_errno(errno, "Failed to open backing file '%s': %m", backing_file);
6234
6235 if (fstat(writable_fd, &st) < 0)
6236 return log_error_errno(errno, "Failed to stat() backing file '%s': %m", backing_file);
6237
6238 r = stat_verify_regular(&st);
6239 if (r < 0)
6240 return log_error_errno(r, "Backing file '%s' of block device is not a regular file: %m", backing_file);
6241
6242 if ((uint64_t) st.st_size != current_size)
6243 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2b59bf51
ZJS
6244 "Size of backing file '%s' of loopback block device '%s' don't match, refusing.",
6245 node, backing_file);
252d6267
LP
6246 } else {
6247 assert(S_ISREG(st.st_mode));
6248 assert(!backing_file);
a26f4a49 6249
252d6267
LP
6250 /* The file descriptor is read-only. In order to grow the file we need to have a writable fd. We
6251 * reopen the file for that temporarily. We keep the writable fd only open for this operation though,
6252 * as fdisk can't accept it anyway. */
6253
6254 writable_fd = fd_reopen(*fd, O_WRONLY|O_CLOEXEC);
6255 if (writable_fd < 0)
6256 return log_error_errno(writable_fd, "Failed to reopen backing file '%s' writable: %m", node);
6257 }
a26f4a49
LP
6258
6259 if (!arg_discard) {
6260 if (fallocate(writable_fd, 0, 0, arg_size) < 0) {
6261 if (!ERRNO_IS_NOT_SUPPORTED(errno))
6262 return log_error_errno(errno, "Failed to grow '%s' from %s to %s by allocation: %m",
2b59bf51 6263 node, FORMAT_BYTES(current_size), FORMAT_BYTES(arg_size));
a26f4a49
LP
6264
6265 /* Fallback to truncation, if fallocate() is not supported. */
6266 log_debug("Backing file system does not support fallocate(), falling back to ftruncate().");
6267 } else {
252d6267 6268 if (current_size == 0) /* Likely regular file just created by us */
2b59bf51 6269 log_info("Allocated %s for '%s'.", FORMAT_BYTES(arg_size), node);
a26f4a49 6270 else
2b59bf51
ZJS
6271 log_info("File '%s' grown from %s to %s by allocation.",
6272 node, FORMAT_BYTES(current_size), FORMAT_BYTES(arg_size));
a26f4a49 6273
252d6267 6274 goto done;
a26f4a49
LP
6275 }
6276 }
6277
6278 if (ftruncate(writable_fd, arg_size) < 0)
6279 return log_error_errno(errno, "Failed to grow '%s' from %s to %s by truncation: %m",
2b59bf51 6280 node, FORMAT_BYTES(current_size), FORMAT_BYTES(arg_size));
a26f4a49 6281
252d6267 6282 if (current_size == 0) /* Likely regular file just created by us */
2b59bf51 6283 log_info("Sized '%s' to %s.", node, FORMAT_BYTES(arg_size));
252d6267 6284 else
2b59bf51
ZJS
6285 log_info("File '%s' grown from %s to %s by truncation.",
6286 node, FORMAT_BYTES(current_size), FORMAT_BYTES(arg_size));
252d6267
LP
6287
6288done:
f9b3afae
LP
6289 r = resize_pt(writable_fd);
6290 if (r < 0)
6291 return r;
6292
252d6267
LP
6293 if (loop_device) {
6294 r = loop_device_refresh_size(loop_device, UINT64_MAX, arg_size);
6295 if (r < 0)
6296 return log_error_errno(r, "Failed to update loop device size: %m");
6297 }
a26f4a49
LP
6298
6299 return 1;
6300}
6301
170c9823 6302static int determine_auto_size(Context *c) {
994b3031 6303 uint64_t sum;
170c9823 6304
ac33e147 6305 assert(c);
170c9823 6306
994b3031
LP
6307 sum = round_up_size(GPT_METADATA_SIZE, 4096);
6308
170c9823
LP
6309 LIST_FOREACH(partitions, p, c->partitions) {
6310 uint64_t m;
6311
6312 if (p->dropped)
6313 continue;
6314
994b3031 6315 m = partition_min_size_with_padding(c, p);
170c9823
LP
6316 if (m > UINT64_MAX - sum)
6317 return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Image would grow too large, refusing.");
6318
6319 sum += m;
6320 }
6321
2b59bf51
ZJS
6322 if (c->total != UINT64_MAX)
6323 /* Image already allocated? Then show its size. */
6324 log_info("Automatically determined minimal disk image size as %s, current image size is %s.",
6325 FORMAT_BYTES(sum), FORMAT_BYTES(c->total));
6326 else
6327 /* If the image is being created right now, then it has no previous size, suppress any comment about it hence. */
6328 log_info("Automatically determined minimal disk image size as %s.",
6329 FORMAT_BYTES(sum));
170c9823
LP
6330
6331 arg_size = sum;
6332 return 0;
6333}
6334
e594a3b1 6335static int run(int argc, char *argv[]) {
252d6267 6336 _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
252d6267 6337 _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
e594a3b1 6338 _cleanup_(context_freep) Context* context = NULL;
cc751c75 6339 bool node_is_our_loop = false;
e594a3b1
LP
6340 int r;
6341
6342 log_show_color(true);
6343 log_parse_environment();
6344 log_open();
6345
e594a3b1
LP
6346 r = parse_argv(argc, argv);
6347 if (r <= 0)
6348 return r;
6349
6350 r = parse_proc_cmdline_factory_reset();
6351 if (r < 0)
6352 return r;
6353
6354 r = parse_efi_variable_factory_reset();
6355 if (r < 0)
6356 return r;
6357
30f19400
LP
6358#if HAVE_LIBCRYPTSETUP
6359 cryptsetup_enable_logging(NULL);
6360#endif
6361
252d6267
LP
6362 if (arg_image) {
6363 assert(!arg_root);
6364
6365 /* Mount this strictly read-only: we shall modify the partition table, not the file
6366 * systems */
6367 r = mount_image_privately_interactively(
6368 arg_image,
6369 DISSECT_IMAGE_MOUNT_READ_ONLY |
6370 (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) */
6371 DISSECT_IMAGE_GPT_ONLY |
6372 DISSECT_IMAGE_RELAX_VAR_CHECK |
6373 DISSECT_IMAGE_USR_NO_ROOT |
6374 DISSECT_IMAGE_REQUIRE_ROOT,
6375 &mounted_dir,
e330f97a 6376 &loop_device);
252d6267
LP
6377 if (r < 0)
6378 return r;
6379
6380 arg_root = strdup(mounted_dir);
6381 if (!arg_root)
6382 return log_oom();
6383
6384 if (!arg_node) {
6385 arg_node = strdup(loop_device->node);
6386 if (!arg_node)
6387 return log_oom();
6388
3d62af7d 6389 /* Remember that the device we are about to manipulate is actually the one we
252d6267
LP
6390 * allocated here, and thus to increase its backing file we know what to do */
6391 node_is_our_loop = true;
6392 }
6393 }
6394
e594a3b1
LP
6395 context = context_new(arg_seed);
6396 if (!context)
6397 return log_oom();
6398
224c853f
RP
6399 strv_uniq(arg_definitions);
6400
e594a3b1
LP
6401 r = context_read_definitions(context, arg_definitions, arg_root);
6402 if (r < 0)
6403 return r;
6404
cc751c75 6405 r = find_root(context);
0ae5ffe0
YW
6406 if (r < 0)
6407 return r;
6408
a26f4a49 6409 if (arg_size != UINT64_MAX) {
252d6267 6410 r = resize_backing_fd(
cc751c75
DDM
6411 context->node,
6412 &context->backing_fd,
252d6267
LP
6413 node_is_our_loop ? arg_image : NULL,
6414 node_is_our_loop ? loop_device : NULL);
a26f4a49
LP
6415 if (r < 0)
6416 return r;
6417 }
6418
cc751c75 6419 r = context_load_partition_table(context);
e594a3b1
LP
6420 if (r == -EHWPOISON)
6421 return 77; /* Special return value which means "Not GPT, so not doing anything". This isn't
6422 * really an error when called at boot. */
6423 if (r < 0)
6424 return r;
cc751c75 6425 context->from_scratch = r > 0; /* Starting from scratch */
e594a3b1
LP
6426
6427 if (arg_can_factory_reset) {
6428 r = context_can_factory_reset(context);
6429 if (r < 0)
6430 return r;
6431 if (r == 0)
6432 return EXIT_FAILURE;
6433
6434 return 0;
6435 }
6436
cc751c75 6437 r = context_factory_reset(context);
e594a3b1
LP
6438 if (r < 0)
6439 return r;
6440 if (r > 0) {
6441 /* We actually did a factory reset! */
6442 r = remove_efi_variable_factory_reset();
6443 if (r < 0)
6444 return r;
6445
6446 /* Reload the reduced partition table */
6447 context_unload_partition_table(context);
cc751c75 6448 r = context_load_partition_table(context);
e594a3b1
LP
6449 if (r < 0)
6450 return r;
6451 }
6452
e594a3b1
LP
6453 r = context_read_seed(context, arg_root);
6454 if (r < 0)
6455 return r;
6456
8bbbdfd7
DDM
6457 /* Make sure each partition has a unique UUID and unique label */
6458 r = context_acquire_partition_uuids_and_labels(context);
6459 if (r < 0)
6460 return r;
6461
c4a87b76
DDM
6462 r = context_minimize(context);
6463 if (r < 0)
6464 return r;
6465
757bc2e4 6466 /* Open all files to copy blocks from now, since we want to take their size into consideration */
5c08da58
LP
6467 r = context_open_copy_block_paths(
6468 context,
7802194a 6469 loop_device ? loop_device->devno : /* if --image= is specified, only allow partitions on the loopback device */
5c08da58
LP
6470 arg_root && !arg_image ? 0 : /* if --root= is specified, don't accept any block device */
6471 (dev_t) -1); /* if neither is specified, make no restrictions */
757bc2e4
LP
6472 if (r < 0)
6473 return r;
6474
170c9823
LP
6475 if (arg_size_auto) {
6476 r = determine_auto_size(context);
6477 if (r < 0)
6478 return r;
6479
6480 /* Flush out everything again, and let's grow the file first, then start fresh */
6481 context_unload_partition_table(context);
6482
ac33e147 6483 assert(arg_size != UINT64_MAX);
252d6267 6484 r = resize_backing_fd(
cc751c75
DDM
6485 context->node,
6486 &context->backing_fd,
252d6267
LP
6487 node_is_our_loop ? arg_image : NULL,
6488 node_is_our_loop ? loop_device : NULL);
170c9823
LP
6489 if (r < 0)
6490 return r;
6491
cc751c75 6492 r = context_load_partition_table(context);
170c9823
LP
6493 if (r < 0)
6494 return r;
6495 }
6496
e594a3b1
LP
6497 /* First try to fit new partitions in, dropping by priority until it fits */
6498 for (;;) {
14a4c4ed
LP
6499 uint64_t largest_free_area;
6500
6501 if (context_allocate_partitions(context, &largest_free_area))
e594a3b1
LP
6502 break; /* Success! */
6503
9ccceb9d 6504 if (!context_drop_or_foreignize_one_priority(context)) {
d17db7b2 6505 r = log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
14a4c4ed 6506 "Can't fit requested partitions into available free space (%s), refusing.",
2b59bf51 6507 FORMAT_BYTES(largest_free_area));
d17db7b2
LP
6508 determine_auto_size(context);
6509 return r;
6510 }
e594a3b1
LP
6511 }
6512
6513 /* Now assign free space according to the weight logic */
6514 r = context_grow_partitions(context);
6515 if (r < 0)
6516 return r;
6517
0b7f574f 6518 /* Now calculate where each new partition gets placed */
e594a3b1
LP
6519 context_place_partitions(context);
6520
cc751c75 6521 (void) context_dump(context, /*late=*/ false);
a26d463d 6522
cc751c75 6523 r = context_write_partition_table(context);
e594a3b1
LP
6524 if (r < 0)
6525 return r;
6526
4cee8333
DDM
6527 r = context_split(context);
6528 if (r < 0)
6529 return r;
6530
cc751c75 6531 (void) context_dump(context, /*late=*/ true);
b5b7879a 6532
db1d4e6b
DDM
6533 context->node = mfree(context->node);
6534
a2d7c42e
DDM
6535 LIST_FOREACH(partitions, p, context->partitions)
6536 p->split_path = mfree(p->split_path);
b5b7879a 6537
e594a3b1
LP
6538 return 0;
6539}
6540
6541DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);