]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/dissect/dissect.c
dissect: show proper error strings for more errors
[thirdparty/systemd.git] / src / dissect / dissect.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <fcntl.h>
4 #include <getopt.h>
5 #include <linux/loop.h>
6 #include <stdio.h>
7 #include <sys/ioctl.h>
8 #include <sys/mount.h>
9
10 #include "architecture.h"
11 #include "copy.h"
12 #include "dissect-image.h"
13 #include "fd-util.h"
14 #include "format-table.h"
15 #include "format-util.h"
16 #include "fs-util.h"
17 #include "hexdecoct.h"
18 #include "log.h"
19 #include "loop-util.h"
20 #include "main-func.h"
21 #include "mkdir.h"
22 #include "mount-util.h"
23 #include "namespace-util.h"
24 #include "parse-util.h"
25 #include "path-util.h"
26 #include "pretty-print.h"
27 #include "stat-util.h"
28 #include "string-util.h"
29 #include "strv.h"
30 #include "terminal-util.h"
31 #include "tmpfile-util.h"
32 #include "user-util.h"
33 #include "util.h"
34
35 static enum {
36 ACTION_DISSECT,
37 ACTION_MOUNT,
38 ACTION_COPY_FROM,
39 ACTION_COPY_TO,
40 } arg_action = ACTION_DISSECT;
41 static const char *arg_image = NULL;
42 static const char *arg_path = NULL;
43 static const char *arg_source = NULL;
44 static const char *arg_target = NULL;
45 static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK;
46 static void *arg_root_hash = NULL;
47 static char *arg_verity_data = NULL;
48 static size_t arg_root_hash_size = 0;
49 static char *arg_root_hash_sig_path = NULL;
50 static void *arg_root_hash_sig = NULL;
51 static size_t arg_root_hash_sig_size = 0;
52
53 STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep);
54 STATIC_DESTRUCTOR_REGISTER(arg_verity_data, freep);
55 STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig_path, freep);
56 STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig, freep);
57
58 static int help(void) {
59 _cleanup_free_ char *link = NULL;
60 int r;
61
62 r = terminal_urlify_man("systemd-dissect", "1", &link);
63 if (r < 0)
64 return log_oom();
65
66 printf("%1$s [OPTIONS...] IMAGE\n"
67 "%1$s [OPTIONS...] --mount IMAGE PATH\n"
68 "%1$s [OPTIONS...] --copy-from IMAGE PATH [TARGET]\n"
69 "%1$s [OPTIONS...] --copy-to IMAGE [SOURCE] PATH\n\n"
70 "%5$sDissect a file system OS image.%6$s\n\n"
71 "%3$sOptions:%4$s\n"
72 " -r --read-only Mount read-only\n"
73 " --fsck=BOOL Run fsck before mounting\n"
74 " --mkdir Make mount directory before mounting, if missing\n"
75 " --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n"
76 " --root-hash=HASH Specify root hash for verity\n"
77 " --root-hash-sig=SIG Specify pkcs7 signature of root hash for verity\n"
78 " as a DER encoded PKCS7, either as a path to a file\n"
79 " or as an ASCII base64 encoded string prefixed by\n"
80 " 'base64:'\n"
81 " --verity-data=PATH Specify data file with hash tree for verity if it is\n"
82 " not embedded in IMAGE\n"
83 "\n%3$sCommands:%4$s\n"
84 " -h --help Show this help\n"
85 " --version Show package version\n"
86 " -m --mount Mount the image to the specified directory\n"
87 " -M Shortcut for --mount --mkdir\n"
88 " -x --copy-from Copy files from image to host\n"
89 " -a --copy-to Copy files from host to image\n"
90 "\nSee the %2$s for details.\n"
91 , program_invocation_short_name
92 , link
93 , ansi_underline(), ansi_normal()
94 , ansi_highlight(), ansi_normal());
95
96 return 0;
97 }
98
99 static int parse_argv(int argc, char *argv[]) {
100
101 enum {
102 ARG_VERSION = 0x100,
103 ARG_DISCARD,
104 ARG_ROOT_HASH,
105 ARG_FSCK,
106 ARG_VERITY_DATA,
107 ARG_ROOT_HASH_SIG,
108 ARG_MKDIR,
109 };
110
111 static const struct option options[] = {
112 { "help", no_argument, NULL, 'h' },
113 { "version", no_argument, NULL, ARG_VERSION },
114 { "mount", no_argument, NULL, 'm' },
115 { "read-only", no_argument, NULL, 'r' },
116 { "discard", required_argument, NULL, ARG_DISCARD },
117 { "root-hash", required_argument, NULL, ARG_ROOT_HASH },
118 { "fsck", required_argument, NULL, ARG_FSCK },
119 { "verity-data", required_argument, NULL, ARG_VERITY_DATA },
120 { "root-hash-sig", required_argument, NULL, ARG_ROOT_HASH_SIG },
121 { "mkdir", no_argument, NULL, ARG_MKDIR },
122 { "copy-from", no_argument, NULL, 'x' },
123 { "copy-to", no_argument, NULL, 'a' },
124 {}
125 };
126
127 int c, r;
128
129 assert(argc >= 0);
130 assert(argv);
131
132 while ((c = getopt_long(argc, argv, "hmrMxa", options, NULL)) >= 0) {
133
134 switch (c) {
135
136 case 'h':
137 return help();
138
139 case ARG_VERSION:
140 return version();
141
142 case 'm':
143 arg_action = ACTION_MOUNT;
144 break;
145
146 case ARG_MKDIR:
147 arg_flags |= DISSECT_IMAGE_MKDIR;
148 break;
149
150 case 'M':
151 /* Shortcut combination of the above two */
152 arg_action = ACTION_MOUNT;
153 arg_flags |= DISSECT_IMAGE_MKDIR;
154 break;
155
156 case 'x':
157 arg_action = ACTION_COPY_FROM;
158 arg_flags |= DISSECT_IMAGE_READ_ONLY;
159 break;
160
161 case 'a':
162 arg_action = ACTION_COPY_TO;
163 break;
164
165 case 'r':
166 arg_flags |= DISSECT_IMAGE_READ_ONLY;
167 break;
168
169 case ARG_DISCARD: {
170 DissectImageFlags flags;
171
172 if (streq(optarg, "disabled"))
173 flags = 0;
174 else if (streq(optarg, "loop"))
175 flags = DISSECT_IMAGE_DISCARD_ON_LOOP;
176 else if (streq(optarg, "all"))
177 flags = DISSECT_IMAGE_DISCARD_ON_LOOP | DISSECT_IMAGE_DISCARD;
178 else if (streq(optarg, "crypt"))
179 flags = DISSECT_IMAGE_DISCARD_ANY;
180 else if (streq(optarg, "list")) {
181 puts("disabled\n"
182 "all\n"
183 "crypt\n"
184 "loop");
185 return 0;
186 } else
187 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
188 "Unknown --discard= parameter: %s",
189 optarg);
190 arg_flags = (arg_flags & ~DISSECT_IMAGE_DISCARD_ANY) | flags;
191
192 break;
193 }
194
195 case ARG_ROOT_HASH: {
196 void *p;
197 size_t l;
198
199 r = unhexmem(optarg, strlen(optarg), &p, &l);
200 if (r < 0)
201 return log_error_errno(r, "Failed to parse root hash '%s': %m", optarg);
202 if (l < sizeof(sd_id128_t)) {
203 log_error("Root hash must be at least 128bit long: %s", optarg);
204 free(p);
205 return -EINVAL;
206 }
207
208 free(arg_root_hash);
209 arg_root_hash = p;
210 arg_root_hash_size = l;
211 break;
212 }
213
214 case ARG_VERITY_DATA:
215 r = parse_path_argument_and_warn(optarg, false, &arg_verity_data);
216 if (r < 0)
217 return r;
218 break;
219
220 case ARG_ROOT_HASH_SIG: {
221 char *value;
222
223 if ((value = startswith(optarg, "base64:"))) {
224 void *p;
225 size_t l;
226
227 r = unbase64mem(value, strlen(value), &p, &l);
228 if (r < 0)
229 return log_error_errno(r, "Failed to parse root hash signature '%s': %m", optarg);
230
231 free_and_replace(arg_root_hash_sig, p);
232 arg_root_hash_sig_size = l;
233 arg_root_hash_sig_path = mfree(arg_root_hash_sig_path);
234 } else {
235 r = parse_path_argument_and_warn(optarg, false, &arg_root_hash_sig_path);
236 if (r < 0)
237 return r;
238 arg_root_hash_sig = mfree(arg_root_hash_sig);
239 arg_root_hash_sig_size = 0;
240 }
241
242 break;
243 }
244
245 case ARG_FSCK:
246 r = parse_boolean(optarg);
247 if (r < 0)
248 return log_error_errno(r, "Failed to parse --fsck= parameter: %s", optarg);
249
250 SET_FLAG(arg_flags, DISSECT_IMAGE_FSCK, r);
251 break;
252
253 case '?':
254 return -EINVAL;
255
256 default:
257 assert_not_reached("Unhandled option");
258 }
259
260 }
261
262 switch (arg_action) {
263
264 case ACTION_DISSECT:
265 if (optind + 1 != argc)
266 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
267 "Expected an image file path as only argument.");
268
269 arg_image = argv[optind];
270 arg_flags |= DISSECT_IMAGE_READ_ONLY;
271 break;
272
273 case ACTION_MOUNT:
274 if (optind + 2 != argc)
275 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
276 "Expected an image file path and mount point path as only arguments.");
277
278 arg_image = argv[optind];
279 arg_path = argv[optind + 1];
280 break;
281
282 case ACTION_COPY_FROM:
283 if (argc < optind + 2 || argc > optind + 3)
284 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
285 "Expected an image file path, a source path and an optional destination path as only arguments.");
286
287 arg_image = argv[optind];
288 arg_source = argv[optind + 1];
289 arg_target = argc > optind + 2 ? argv[optind + 2] : "-" /* this means stdout */ ;
290
291 arg_flags |= DISSECT_IMAGE_READ_ONLY;
292 break;
293
294 case ACTION_COPY_TO:
295 if (argc < optind + 2 || argc > optind + 3)
296 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
297 "Expected an image file path, an optional source path and a destination path as only arguments.");
298
299 arg_image = argv[optind];
300
301 if (argc > optind + 2) {
302 arg_source = argv[optind + 1];
303 arg_target = argv[optind + 2];
304 } else {
305 arg_source = "-"; /* this means stdin */
306 arg_target = argv[optind + 1];
307 }
308
309 break;
310
311 default:
312 assert_not_reached("Unknown action.");
313 }
314
315 return 1;
316 }
317
318 static int action_dissect(DissectedImage *m, LoopDevice *d) {
319 _cleanup_(table_unrefp) Table *t = NULL;
320 uint64_t size;
321 int r;
322
323 assert(m);
324 assert(d);
325
326 printf(" Name: %s\n", basename(arg_image));
327
328 if (ioctl(d->fd, BLKGETSIZE64, &size) < 0)
329 log_debug_errno(errno, "Failed to query size of loopback device: %m");
330 else {
331 char s[FORMAT_BYTES_MAX];
332 printf(" Size: %s\n", format_bytes(s, sizeof(s), size));
333 }
334
335 putc('\n', stdout);
336
337 r = dissected_image_acquire_metadata(m);
338 if (r == -ENXIO)
339 return log_error_errno(r, "No root partition discovered.");
340 if (r == -EMEDIUMTYPE)
341 return log_error_errno(r, "Not a valid OS image, no os-release file included.");
342 if (r == -EUCLEAN)
343 return log_error_errno(r, "File system check of image failed.");
344 if (r == -EUNATCH)
345 log_warning_errno(r, "OS image is encrypted, proceeding without showing OS image metadata.");
346 else if (r == -EBUSY)
347 log_warning_errno(r, "OS image is currently in use, proceeding without showing OS image metadata.");
348 else if (r < 0)
349 return log_error_errno(r, "Failed to acquire image metadata: %m");
350 else {
351 if (m->hostname)
352 printf(" Hostname: %s\n", m->hostname);
353
354 if (!sd_id128_is_null(m->machine_id))
355 printf("Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->machine_id));
356
357 if (!strv_isempty(m->machine_info)) {
358 char **p, **q;
359
360 STRV_FOREACH_PAIR(p, q, m->machine_info)
361 printf("%s %s=%s\n",
362 p == m->machine_info ? "Mach. Info:" : " ",
363 *p, *q);
364 }
365
366 if (!strv_isempty(m->os_release)) {
367 char **p, **q;
368
369 STRV_FOREACH_PAIR(p, q, m->os_release)
370 printf("%s %s=%s\n",
371 p == m->os_release ? "OS Release:" : " ",
372 *p, *q);
373 }
374 }
375
376 putc('\n', stdout);
377
378 t = table_new("rw", "designator", "partition uuid", "fstype", "architecture", "verity", "node", "partno");
379 if (!t)
380 return log_oom();
381
382 (void) table_set_empty_string(t, "-");
383 (void) table_set_align_percent(t, table_get_cell(t, 0, 7), 100);
384
385 for (unsigned i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
386 DissectedPartition *p = m->partitions + i;
387
388 if (!p->found)
389 continue;
390
391 r = table_add_many(
392 t,
393 TABLE_STRING, p->rw ? "rw" : "ro",
394 TABLE_STRING, partition_designator_to_string(i));
395 if (r < 0)
396 return table_log_add_error(r);
397
398 if (sd_id128_is_null(p->uuid))
399 r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
400 else
401 r = table_add_cell(t, NULL, TABLE_UUID, &p->uuid);
402 if (r < 0)
403 return table_log_add_error(r);
404
405 r = table_add_many(
406 t,
407 TABLE_STRING, p->fstype,
408 TABLE_STRING, architecture_to_string(p->architecture));
409 if (r < 0)
410 return table_log_add_error(r);
411
412 if (arg_verity_data)
413 r = table_add_cell(t, NULL, TABLE_STRING, "external");
414 else if (dissected_image_can_do_verity(m, i))
415 r = table_add_cell(t, NULL, TABLE_STRING, yes_no(dissected_image_has_verity(m, i)));
416 else
417 r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
418 if (r < 0)
419 return table_log_add_error(r);
420
421 if (p->partno < 0) /* no partition table, naked file system */ {
422 r = table_add_cell(t, NULL, TABLE_STRING, arg_image);
423 if (r < 0)
424 return table_log_add_error(r);
425
426 r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
427 } else {
428 r = table_add_cell(t, NULL, TABLE_STRING, p->node);
429 if (r < 0)
430 return table_log_add_error(r);
431
432 r = table_add_cell(t, NULL, TABLE_INT, &p->partno);
433 }
434 if (r < 0)
435 return table_log_add_error(r);
436 }
437
438 r = table_print(t, stdout);
439 if (r < 0)
440 return log_error_errno(r, "Failed to dump table: %m");
441
442 return 0;
443 }
444
445 static int action_mount(DissectedImage *m, LoopDevice *d) {
446 _cleanup_(decrypted_image_unrefp) DecryptedImage *di = NULL;
447 int r;
448
449 assert(m);
450 assert(d);
451
452 r = dissected_image_decrypt_interactively(
453 m, NULL,
454 arg_root_hash, arg_root_hash_size,
455 arg_verity_data,
456 arg_root_hash_sig_path, arg_root_hash_sig, arg_root_hash_sig_size,
457 arg_flags,
458 &di);
459 if (r < 0)
460 return r;
461
462 r = dissected_image_mount_and_warn(m, arg_path, UID_INVALID, arg_flags);
463 if (r < 0)
464 return r;
465
466 if (di) {
467 r = decrypted_image_relinquish(di);
468 if (r < 0)
469 return log_error_errno(r, "Failed to relinquish DM devices: %m");
470 }
471
472 loop_device_relinquish(d);
473 return 0;
474 }
475
476 static int action_copy(DissectedImage *m, LoopDevice *d) {
477 _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
478 _cleanup_(decrypted_image_unrefp) DecryptedImage *di = NULL;
479 _cleanup_(rmdir_and_freep) char *created_dir = NULL;
480 _cleanup_free_ char *temp = NULL;
481 int r;
482
483 assert(m);
484 assert(d);
485
486 r = dissected_image_decrypt_interactively(
487 m, NULL,
488 arg_root_hash, arg_root_hash_size,
489 arg_verity_data,
490 arg_root_hash_sig_path, arg_root_hash_sig, arg_root_hash_sig_size,
491 arg_flags,
492 &di);
493 if (r < 0)
494 return r;
495
496 r = detach_mount_namespace();
497 if (r < 0)
498 return log_error_errno(r, "Failed to detach mount namespace: %m");
499
500 r = tempfn_random_child(NULL, program_invocation_short_name, &temp);
501 if (r < 0)
502 return log_error_errno(r, "Failed to generate temporary mount directory: %m");
503
504 r = mkdir_p(temp, 0700);
505 if (r < 0)
506 return log_error_errno(r, "Failed to create mount point: %m");
507
508 created_dir = TAKE_PTR(temp);
509
510 r = dissected_image_mount_and_warn(m, created_dir, UID_INVALID, arg_flags);
511 if (r < 0)
512 return r;
513
514 mounted_dir = TAKE_PTR(created_dir);
515
516 if (di) {
517 r = decrypted_image_relinquish(di);
518 if (r < 0)
519 return log_error_errno(r, "Failed to relinquish DM devices: %m");
520 }
521
522 loop_device_relinquish(d);
523
524 if (arg_action == ACTION_COPY_FROM) {
525 _cleanup_close_ int source_fd = -1, target_fd = -1;
526
527 source_fd = chase_symlinks_and_open(arg_source, mounted_dir, CHASE_PREFIX_ROOT|CHASE_WARN, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
528 if (source_fd < 0)
529 return log_error_errno(source_fd, "Failed to open source path '%s' in image '%s': %m", arg_source, arg_image);
530
531 /* Copying to stdout? */
532 if (streq(arg_target, "-")) {
533 r = copy_bytes(source_fd, STDOUT_FILENO, (uint64_t) -1, COPY_REFLINK);
534 if (r < 0)
535 return log_error_errno(r, "Failed to copy bytes from %s in mage '%s' to stdout: %m", arg_source, arg_image);
536
537 /* When we copy to stdou we don't copy any attributes (i.e. no access mode, no ownership, no xattr, no times) */
538 return 0;
539 }
540
541 /* Try to copy as directory? */
542 r = copy_directory_fd(source_fd, arg_target, COPY_REFLINK|COPY_MERGE_EMPTY|COPY_SIGINT);
543 if (r >= 0)
544 return 0;
545 if (r != -ENOTDIR)
546 return log_error_errno(r, "Failed to copy %s in image '%s' to '%s': %m", arg_source, arg_image, arg_target);
547
548 r = fd_verify_regular(source_fd);
549 if (r == -EISDIR)
550 return log_error_errno(r, "Target '%s' exists already and is not a directory.", arg_target);
551 if (r < 0)
552 return log_error_errno(r, "Source path %s in image '%s' is neither regular file nor directory, refusing: %m", arg_source, arg_image);
553
554 /* Nah, it's a plain file! */
555 target_fd = open(arg_target, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
556 if (target_fd < 0)
557 return log_error_errno(errno, "Failed to create regular file at target path '%s': %m", arg_target);
558
559 r = copy_bytes(source_fd, target_fd, (uint64_t) -1, COPY_REFLINK);
560 if (r < 0)
561 return log_error_errno(r, "Failed to copy bytes from %s in mage '%s' to '%s': %m", arg_source, arg_image, arg_target);
562
563 (void) copy_xattr(source_fd, target_fd);
564 (void) copy_access(source_fd, target_fd);
565 (void) copy_times(source_fd, target_fd, 0);
566
567 /* When this is a regular file we don't copy ownership! */
568
569 } else {
570 _cleanup_close_ int source_fd = -1, target_fd = -1;
571 _cleanup_close_ int dfd = -1;
572 _cleanup_free_ char *dn = NULL;
573
574 assert(arg_action == ACTION_COPY_TO);
575
576 dn = dirname_malloc(arg_target);
577 if (!dn)
578 return log_oom();
579
580 r = chase_symlinks(dn, mounted_dir, CHASE_PREFIX_ROOT|CHASE_WARN, NULL, &dfd);
581 if (r < 0)
582 return log_error_errno(r, "Failed to open '%s': %m", dn);
583
584 /* Are we reading from stdin? */
585 if (streq(arg_source, "-")) {
586 target_fd = openat(dfd, basename(arg_target), O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_EXCL, 0644);
587 if (target_fd < 0)
588 return log_error_errno(errno, "Failed to open target file '%s': %m", arg_target);
589
590 r = copy_bytes(STDIN_FILENO, target_fd, (uint64_t) -1, COPY_REFLINK);
591 if (r < 0)
592 return log_error_errno(r, "Failed to copy bytes from stdin to '%s' in image '%s': %m", arg_target, arg_image);
593
594 /* When we copy from stdin we don't copy any attributes (i.e. no access mode, no ownership, no xattr, no times) */
595 return 0;
596 }
597
598 source_fd = open(arg_source, O_RDONLY|O_CLOEXEC|O_NOCTTY);
599 if (source_fd < 0)
600 return log_error_errno(source_fd, "Failed to open source path '%s': %m", arg_source);
601
602 r = fd_verify_regular(source_fd);
603 if (r < 0) {
604 if (r != -EISDIR)
605 return log_error_errno(r, "Source '%s' is neither regular file nor directory: %m", arg_source);
606
607 /* We are looking at a directory. */
608
609 target_fd = openat(dfd, basename(arg_target), O_RDONLY|O_DIRECTORY|O_CLOEXEC);
610 if (target_fd < 0) {
611 if (errno != ENOENT)
612 return log_error_errno(errno, "Failed to open destination '%s': %m", arg_target);
613
614 r = copy_tree_at(source_fd, ".", dfd, basename(arg_target), UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT);
615 } else
616 r = copy_tree_at(source_fd, ".", target_fd, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT);
617 if (r < 0)
618 return log_error_errno(r, "Failed to copy '%s' to '%s' in image '%s': %m", arg_source, arg_target, arg_image);
619
620 return 0;
621 }
622
623 /* We area looking at a regular file */
624 target_fd = openat(dfd, basename(arg_target), O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_EXCL, 0600);
625 if (target_fd < 0)
626 return log_error_errno(errno, "Failed to open target file '%s': %m", arg_target);
627
628 r = copy_bytes(source_fd, target_fd, (uint64_t) -1, COPY_REFLINK);
629 if (r < 0)
630 return log_error_errno(r, "Failed to copy bytes from '%s' to '%s' in image '%s': %m", arg_source, arg_target, arg_image);
631
632 (void) copy_xattr(source_fd, target_fd);
633 (void) copy_access(source_fd, target_fd);
634 (void) copy_times(source_fd, target_fd, 0);
635
636 /* When this is a regular file we don't copy ownership! */
637 }
638
639 return 0;
640 }
641
642 static int run(int argc, char *argv[]) {
643 _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
644 _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
645 int r;
646
647 log_parse_environment();
648 log_open();
649
650 r = parse_argv(argc, argv);
651 if (r <= 0)
652 return r;
653
654 r = verity_metadata_load(
655 arg_image, NULL,
656 arg_root_hash ? NULL : &arg_root_hash,
657 &arg_root_hash_size,
658 arg_verity_data ? NULL : &arg_verity_data,
659 arg_root_hash_sig_path || arg_root_hash_sig ? NULL : &arg_root_hash_sig_path);
660 if (r < 0)
661 return log_error_errno(r, "Failed to read verity artifacts for %s: %m", arg_image);
662
663 r = loop_device_make_by_path(
664 arg_image,
665 (arg_flags & DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR,
666 arg_verity_data ? 0 : LO_FLAGS_PARTSCAN,
667 &d);
668 if (r < 0)
669 return log_error_errno(r, "Failed to set up loopback device: %m");
670
671 if (arg_verity_data)
672 arg_flags |= DISSECT_IMAGE_NO_PARTITION_TABLE; /* We only support Verity per file system,
673 * hence if there's external Verity data
674 * available we turn off partition table
675 * support */
676 r = dissect_image_and_warn(
677 d->fd,
678 arg_image,
679 arg_root_hash,
680 arg_root_hash_size,
681 arg_verity_data,
682 NULL,
683 arg_flags,
684 &m);
685 if (r < 0)
686 return r;
687
688 switch (arg_action) {
689
690 case ACTION_DISSECT:
691 r = action_dissect(m, d);
692 break;
693
694 case ACTION_MOUNT:
695 r = action_mount(m, d);
696 break;
697
698 case ACTION_COPY_FROM:
699 case ACTION_COPY_TO:
700 r = action_copy(m, d);
701 break;
702
703 default:
704 assert_not_reached("Unknown action.");
705 }
706
707 return r;
708 }
709
710 DEFINE_MAIN_FUNCTION(run);