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