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