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