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