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