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