]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
a2ea3b2f LP |
2 | |
3 | #include <fcntl.h> | |
a2ea3b2f | 4 | #include <getopt.h> |
e08f94ac LP |
5 | #include <linux/loop.h> |
6 | #include <stdio.h> | |
19df770f | 7 | #include <sys/file.h> |
16b74592 LP |
8 | #include <sys/ioctl.h> |
9 | #include <sys/mount.h> | |
a2ea3b2f | 10 | |
ac1f1adf DDM |
11 | #include "sd-device.h" |
12 | ||
a2ea3b2f | 13 | #include "architecture.h" |
ac1f1adf | 14 | #include "blockdev-util.h" |
d6b4d1c7 | 15 | #include "build.h" |
f461a28d | 16 | #include "chase.h" |
33973b84 | 17 | #include "copy.h" |
ca822829 YW |
18 | #include "device-util.h" |
19 | #include "devnum-util.h" | |
0305cf6e | 20 | #include "discover-image.h" |
a2ea3b2f | 21 | #include "dissect-image.h" |
994c9c70 | 22 | #include "env-util.h" |
db02190e | 23 | #include "escape.h" |
33973b84 | 24 | #include "fd-util.h" |
89e62e0b | 25 | #include "fileio.h" |
89d00f2e | 26 | #include "format-table.h" |
16b74592 | 27 | #include "format-util.h" |
33973b84 | 28 | #include "fs-util.h" |
4623e8e6 | 29 | #include "hexdecoct.h" |
a2ea3b2f LP |
30 | #include "log.h" |
31 | #include "loop-util.h" | |
149afb45 | 32 | #include "main-func.h" |
33973b84 LP |
33 | #include "mkdir.h" |
34 | #include "mount-util.h" | |
ac1f1adf | 35 | #include "mountpoint-util.h" |
33973b84 | 36 | #include "namespace-util.h" |
614b022c | 37 | #include "parse-argument.h" |
e475f729 | 38 | #include "parse-util.h" |
e7cbe5cb | 39 | #include "path-util.h" |
5c05f062 | 40 | #include "pretty-print.h" |
06186c4c | 41 | #include "process-util.h" |
0cf16924 | 42 | #include "recurse-dir.h" |
db02190e | 43 | #include "sha256.h" |
33973b84 | 44 | #include "stat-util.h" |
a2ea3b2f | 45 | #include "string-util.h" |
a1edd22e | 46 | #include "strv.h" |
5c05f062 | 47 | #include "terminal-util.h" |
33973b84 | 48 | #include "tmpfile-util.h" |
12c0f4ff | 49 | #include "uid-alloc-range.h" |
2d3a5a73 | 50 | #include "user-util.h" |
a2ea3b2f LP |
51 | |
52 | static enum { | |
53 | ACTION_DISSECT, | |
54 | ACTION_MOUNT, | |
ac1f1adf | 55 | ACTION_UMOUNT, |
07d6072e LP |
56 | ACTION_ATTACH, |
57 | ACTION_DETACH, | |
0cf16924 | 58 | ACTION_LIST, |
db02190e | 59 | ACTION_MTREE, |
06186c4c | 60 | ACTION_WITH, |
33973b84 LP |
61 | ACTION_COPY_FROM, |
62 | ACTION_COPY_TO, | |
0305cf6e | 63 | ACTION_DISCOVER, |
a2ea3b2f | 64 | } arg_action = ACTION_DISSECT; |
a3c3386e LP |
65 | static char *arg_image = NULL; |
66 | static char *arg_path = NULL; | |
33973b84 LP |
67 | static const char *arg_source = NULL; |
68 | static const char *arg_target = NULL; | |
4b5de5dd LP |
69 | static DissectImageFlags arg_flags = |
70 | DISSECT_IMAGE_GENERIC_ROOT | | |
4b5de5dd LP |
71 | DISSECT_IMAGE_DISCARD_ON_LOOP | |
72 | DISSECT_IMAGE_RELAX_VAR_CHECK | | |
73 | DISSECT_IMAGE_FSCK | | |
74a54bae | 74 | DISSECT_IMAGE_USR_NO_ROOT | |
73d88b80 LP |
75 | DISSECT_IMAGE_GROWFS | |
76 | DISSECT_IMAGE_PIN_PARTITION_DEVICES | | |
77 | DISSECT_IMAGE_ADD_PARTITION_DEVICES; | |
aee36b4e | 78 | static VeritySettings arg_verity_settings = VERITY_SETTINGS_DEFAULT; |
6a01ea4a | 79 | static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; |
17547fb5 LP |
80 | static PagerFlags arg_pager_flags = 0; |
81 | static bool arg_legend = true; | |
ac1f1adf | 82 | static bool arg_rmdir = false; |
6c07d570 | 83 | static bool arg_in_memory = false; |
06186c4c | 84 | static char **arg_argv = NULL; |
236d1fa2 | 85 | static char *arg_loop_ref = NULL; |
84be0c71 | 86 | static ImagePolicy* arg_image_policy = NULL; |
a2ea3b2f | 87 | |
a3c3386e LP |
88 | STATIC_DESTRUCTOR_REGISTER(arg_image, freep); |
89 | STATIC_DESTRUCTOR_REGISTER(arg_path, freep); | |
89e62e0b | 90 | STATIC_DESTRUCTOR_REGISTER(arg_verity_settings, verity_settings_done); |
06186c4c | 91 | STATIC_DESTRUCTOR_REGISTER(arg_argv, strv_freep); |
236d1fa2 | 92 | STATIC_DESTRUCTOR_REGISTER(arg_loop_ref, freep); |
149afb45 | 93 | |
5c05f062 LP |
94 | static int help(void) { |
95 | _cleanup_free_ char *link = NULL; | |
96 | int r; | |
97 | ||
98 | r = terminal_urlify_man("systemd-dissect", "1", &link); | |
99 | if (r < 0) | |
100 | return log_oom(); | |
101 | ||
102 | printf("%1$s [OPTIONS...] IMAGE\n" | |
33973b84 | 103 | "%1$s [OPTIONS...] --mount IMAGE PATH\n" |
1b967529 | 104 | "%1$s [OPTIONS...] --umount PATH\n" |
07d6072e LP |
105 | "%1$s [OPTIONS...] --attach IMAGE\n" |
106 | "%1$s [OPTIONS...] --detach PATH\n" | |
0cf16924 | 107 | "%1$s [OPTIONS...] --list IMAGE\n" |
db02190e | 108 | "%1$s [OPTIONS...] --mtree IMAGE\n" |
06186c4c | 109 | "%1$s [OPTIONS...] --with IMAGE [COMMAND…]\n" |
33973b84 LP |
110 | "%1$s [OPTIONS...] --copy-from IMAGE PATH [TARGET]\n" |
111 | "%1$s [OPTIONS...] --copy-to IMAGE [SOURCE] PATH\n\n" | |
a94a199c | 112 | "%5$sDissect a Discoverable Disk Image (DDI).%6$s\n\n" |
5c05f062 | 113 | "%3$sOptions:%4$s\n" |
17547fb5 LP |
114 | " --no-pager Do not pipe output into a pager\n" |
115 | " --no-legend Do not show the headers and footers\n" | |
e7cbe5cb LB |
116 | " -r --read-only Mount read-only\n" |
117 | " --fsck=BOOL Run fsck before mounting\n" | |
74a54bae | 118 | " --growfs=BOOL Grow file system to partition size, if marked\n" |
5c05f062 | 119 | " --mkdir Make mount directory before mounting, if missing\n" |
ac1f1adf | 120 | " --rmdir Remove mount directory after unmounting\n" |
e7cbe5cb | 121 | " --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n" |
6c07d570 | 122 | " --in-memory Copy image into memory\n" |
e7cbe5cb | 123 | " --root-hash=HASH Specify root hash for verity\n" |
c2923fdc LB |
124 | " --root-hash-sig=SIG Specify pkcs7 signature of root hash for verity\n" |
125 | " as a DER encoded PKCS7, either as a path to a file\n" | |
126 | " or as an ASCII base64 encoded string prefixed by\n" | |
127 | " 'base64:'\n" | |
e7cbe5cb | 128 | " --verity-data=PATH Specify data file with hash tree for verity if it is\n" |
5c05f062 | 129 | " not embedded in IMAGE\n" |
84be0c71 LP |
130 | " --image-policy=POLICY\n" |
131 | " Specify image dissection policy\n" | |
de8231b0 LP |
132 | " --json=pretty|short|off\n" |
133 | " Generate JSON output\n" | |
236d1fa2 | 134 | " --loop-ref=NAME Set reference string for loopback device\n" |
5c05f062 LP |
135 | "\n%3$sCommands:%4$s\n" |
136 | " -h --help Show this help\n" | |
137 | " --version Show package version\n" | |
138 | " -m --mount Mount the image to the specified directory\n" | |
139 | " -M Shortcut for --mount --mkdir\n" | |
ac1f1adf DDM |
140 | " -u --umount Unmount the image from the specified directory\n" |
141 | " -U Shortcut for --umount --rmdir\n" | |
07d6072e LP |
142 | " --attach Attach the disk image to a loopback block device\n" |
143 | " --detach Detach a loopback block device gain\n" | |
0cf16924 AAF |
144 | " -l --list List all the files and directories of the specified\n" |
145 | " OS image\n" | |
db02190e | 146 | " --mtree Show BSD mtree manifest of OS image\n" |
06186c4c | 147 | " --with Mount, run command, unmount\n" |
33973b84 LP |
148 | " -x --copy-from Copy files from image to host\n" |
149 | " -a --copy-to Copy files from host to image\n" | |
0305cf6e | 150 | " --discover Discover DDIs in well known directories\n" |
bc556335 DDM |
151 | "\nSee the %2$s for details.\n", |
152 | program_invocation_short_name, | |
153 | link, | |
154 | ansi_underline(), | |
155 | ansi_normal(), | |
156 | ansi_highlight(), | |
157 | ansi_normal()); | |
5c05f062 LP |
158 | |
159 | return 0; | |
a2ea3b2f LP |
160 | } |
161 | ||
06186c4c LP |
162 | static int patch_argv(int *argc, char ***argv, char ***buf) { |
163 | _cleanup_free_ char **l = NULL; | |
164 | char **e; | |
165 | ||
166 | assert(argc); | |
167 | assert(*argc >= 0); | |
168 | assert(argv); | |
169 | assert(*argv); | |
170 | assert(buf); | |
171 | ||
172 | /* Ugly hack: if --with is included in command line, also insert "--" immediately after it, to make | |
173 | * getopt_long() stop processing switches */ | |
174 | ||
175 | for (e = *argv + 1; e < *argv + *argc; e++) { | |
176 | assert(*e); | |
177 | ||
178 | if (streq(*e, "--with")) | |
179 | break; | |
180 | } | |
181 | ||
182 | if (e >= *argv + *argc || streq_ptr(e[1], "--")) { | |
183 | /* No --with used? Or already followed by "--"? Then don't do anything */ | |
184 | *buf = NULL; | |
185 | return 0; | |
186 | } | |
187 | ||
188 | /* Insert the extra "--" right after the --with */ | |
189 | l = new(char*, *argc + 2); | |
190 | if (!l) | |
191 | return log_oom(); | |
192 | ||
193 | size_t idx = e - *argv + 1; | |
194 | memcpy(l, *argv, sizeof(char*) * idx); /* copy everything up to and including the --with */ | |
195 | l[idx] = (char*) "--"; /* insert "--" */ | |
196 | memcpy(l + idx + 1, e + 1, sizeof(char*) * (*argc - idx + 1)); /* copy the rest, including trailing NULL entry */ | |
197 | ||
198 | (*argc)++; | |
199 | (*argv) = l; | |
200 | ||
201 | *buf = TAKE_PTR(l); | |
202 | return 1; | |
203 | } | |
204 | ||
a2ea3b2f LP |
205 | static int parse_argv(int argc, char *argv[]) { |
206 | ||
207 | enum { | |
208 | ARG_VERSION = 0x100, | |
17547fb5 LP |
209 | ARG_NO_PAGER, |
210 | ARG_NO_LEGEND, | |
06186c4c | 211 | ARG_WITH, |
18b5886e | 212 | ARG_DISCARD, |
e475f729 | 213 | ARG_FSCK, |
74a54bae | 214 | ARG_GROWFS, |
89e62e0b | 215 | ARG_ROOT_HASH, |
c2923fdc | 216 | ARG_ROOT_HASH_SIG, |
89e62e0b | 217 | ARG_VERITY_DATA, |
5c05f062 | 218 | ARG_MKDIR, |
ac1f1adf | 219 | ARG_RMDIR, |
6c07d570 | 220 | ARG_IN_MEMORY, |
de8231b0 | 221 | ARG_JSON, |
db02190e | 222 | ARG_MTREE, |
0305cf6e | 223 | ARG_DISCOVER, |
07d6072e LP |
224 | ARG_ATTACH, |
225 | ARG_DETACH, | |
236d1fa2 | 226 | ARG_LOOP_REF, |
84be0c71 | 227 | ARG_IMAGE_POLICY, |
a2ea3b2f LP |
228 | }; |
229 | ||
230 | static const struct option options[] = { | |
c2923fdc LB |
231 | { "help", no_argument, NULL, 'h' }, |
232 | { "version", no_argument, NULL, ARG_VERSION }, | |
17547fb5 LP |
233 | { "no-pager", no_argument, NULL, ARG_NO_PAGER }, |
234 | { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, | |
c2923fdc | 235 | { "mount", no_argument, NULL, 'm' }, |
ac1f1adf | 236 | { "umount", no_argument, NULL, 'u' }, |
07d6072e LP |
237 | { "attach", no_argument, NULL, ARG_ATTACH }, |
238 | { "detach", no_argument, NULL, ARG_DETACH }, | |
06186c4c | 239 | { "with", no_argument, NULL, ARG_WITH }, |
c2923fdc LB |
240 | { "read-only", no_argument, NULL, 'r' }, |
241 | { "discard", required_argument, NULL, ARG_DISCARD }, | |
c2923fdc | 242 | { "fsck", required_argument, NULL, ARG_FSCK }, |
74a54bae | 243 | { "growfs", required_argument, NULL, ARG_GROWFS }, |
89e62e0b | 244 | { "root-hash", required_argument, NULL, ARG_ROOT_HASH }, |
c2923fdc | 245 | { "root-hash-sig", required_argument, NULL, ARG_ROOT_HASH_SIG }, |
89e62e0b | 246 | { "verity-data", required_argument, NULL, ARG_VERITY_DATA }, |
5c05f062 | 247 | { "mkdir", no_argument, NULL, ARG_MKDIR }, |
ac1f1adf | 248 | { "rmdir", no_argument, NULL, ARG_RMDIR }, |
6c07d570 | 249 | { "in-memory", no_argument, NULL, ARG_IN_MEMORY }, |
0cf16924 | 250 | { "list", no_argument, NULL, 'l' }, |
db02190e | 251 | { "mtree", no_argument, NULL, ARG_MTREE }, |
33973b84 LP |
252 | { "copy-from", no_argument, NULL, 'x' }, |
253 | { "copy-to", no_argument, NULL, 'a' }, | |
de8231b0 | 254 | { "json", required_argument, NULL, ARG_JSON }, |
0305cf6e | 255 | { "discover", no_argument, NULL, ARG_DISCOVER }, |
236d1fa2 | 256 | { "loop-ref", required_argument, NULL, ARG_LOOP_REF }, |
84be0c71 | 257 | { "image-policy", required_argument, NULL, ARG_IMAGE_POLICY }, |
a2ea3b2f LP |
258 | {} |
259 | }; | |
260 | ||
06186c4c | 261 | _cleanup_free_ char **buf = NULL; /* we use free(), not strv_free() here, as we don't copy the strings here */ |
4623e8e6 | 262 | int c, r; |
a2ea3b2f LP |
263 | |
264 | assert(argc >= 0); | |
265 | assert(argv); | |
266 | ||
06186c4c LP |
267 | r = patch_argv(&argc, &argv, &buf); |
268 | if (r < 0) | |
269 | return r; | |
270 | ||
0cf16924 | 271 | while ((c = getopt_long(argc, argv, "hmurMUlxa", options, NULL)) >= 0) { |
a2ea3b2f LP |
272 | |
273 | switch (c) { | |
274 | ||
275 | case 'h': | |
5c05f062 | 276 | return help(); |
a2ea3b2f LP |
277 | |
278 | case ARG_VERSION: | |
279 | return version(); | |
280 | ||
17547fb5 LP |
281 | case ARG_NO_PAGER: |
282 | arg_pager_flags |= PAGER_DISABLE; | |
283 | break; | |
284 | ||
285 | case ARG_NO_LEGEND: | |
286 | arg_legend = false; | |
287 | break; | |
288 | ||
a2ea3b2f LP |
289 | case 'm': |
290 | arg_action = ACTION_MOUNT; | |
291 | break; | |
292 | ||
5c05f062 LP |
293 | case ARG_MKDIR: |
294 | arg_flags |= DISSECT_IMAGE_MKDIR; | |
295 | break; | |
296 | ||
297 | case 'M': | |
298 | /* Shortcut combination of the above two */ | |
299 | arg_action = ACTION_MOUNT; | |
300 | arg_flags |= DISSECT_IMAGE_MKDIR; | |
301 | break; | |
302 | ||
ac1f1adf DDM |
303 | case 'u': |
304 | arg_action = ACTION_UMOUNT; | |
305 | break; | |
306 | ||
307 | case ARG_RMDIR: | |
308 | arg_rmdir = true; | |
309 | break; | |
310 | ||
311 | case 'U': | |
312 | /* Shortcut combination of the above two */ | |
313 | arg_action = ACTION_UMOUNT; | |
314 | arg_rmdir = true; | |
315 | break; | |
316 | ||
07d6072e LP |
317 | case ARG_ATTACH: |
318 | arg_action = ACTION_ATTACH; | |
319 | break; | |
320 | ||
321 | case ARG_DETACH: | |
322 | arg_action = ACTION_DETACH; | |
323 | break; | |
324 | ||
0cf16924 AAF |
325 | case 'l': |
326 | arg_action = ACTION_LIST; | |
327 | arg_flags |= DISSECT_IMAGE_READ_ONLY; | |
328 | break; | |
329 | ||
db02190e LP |
330 | case ARG_MTREE: |
331 | arg_action = ACTION_MTREE; | |
332 | arg_flags |= DISSECT_IMAGE_READ_ONLY; | |
333 | break; | |
334 | ||
06186c4c LP |
335 | case ARG_WITH: |
336 | arg_action = ACTION_WITH; | |
337 | break; | |
338 | ||
33973b84 LP |
339 | case 'x': |
340 | arg_action = ACTION_COPY_FROM; | |
341 | arg_flags |= DISSECT_IMAGE_READ_ONLY; | |
342 | break; | |
343 | ||
344 | case 'a': | |
345 | arg_action = ACTION_COPY_TO; | |
346 | break; | |
347 | ||
a2ea3b2f | 348 | case 'r': |
18b5886e LP |
349 | arg_flags |= DISSECT_IMAGE_READ_ONLY; |
350 | break; | |
351 | ||
971e2ef0 ZJS |
352 | case ARG_DISCARD: { |
353 | DissectImageFlags flags; | |
354 | ||
18b5886e | 355 | if (streq(optarg, "disabled")) |
971e2ef0 | 356 | flags = 0; |
18b5886e | 357 | else if (streq(optarg, "loop")) |
971e2ef0 | 358 | flags = DISSECT_IMAGE_DISCARD_ON_LOOP; |
18b5886e | 359 | else if (streq(optarg, "all")) |
971e2ef0 | 360 | flags = DISSECT_IMAGE_DISCARD_ON_LOOP | DISSECT_IMAGE_DISCARD; |
18b5886e | 361 | else if (streq(optarg, "crypt")) |
971e2ef0 | 362 | flags = DISSECT_IMAGE_DISCARD_ANY; |
140788f7 LP |
363 | else if (streq(optarg, "list")) { |
364 | puts("disabled\n" | |
365 | "all\n" | |
366 | "crypt\n" | |
367 | "loop"); | |
368 | return 0; | |
369 | } else | |
baaa35ad ZJS |
370 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), |
371 | "Unknown --discard= parameter: %s", | |
372 | optarg); | |
971e2ef0 | 373 | arg_flags = (arg_flags & ~DISSECT_IMAGE_DISCARD_ANY) | flags; |
18b5886e | 374 | |
a2ea3b2f | 375 | break; |
971e2ef0 | 376 | } |
a2ea3b2f | 377 | |
6c07d570 LP |
378 | case ARG_IN_MEMORY: |
379 | arg_in_memory = true; | |
380 | break; | |
381 | ||
4623e8e6 | 382 | case ARG_ROOT_HASH: { |
89e62e0b | 383 | _cleanup_free_ void *p = NULL; |
4623e8e6 LP |
384 | size_t l; |
385 | ||
386 | r = unhexmem(optarg, strlen(optarg), &p, &l); | |
387 | if (r < 0) | |
63cf2d75 | 388 | return log_error_errno(r, "Failed to parse root hash '%s': %m", optarg); |
89e62e0b LP |
389 | if (l < sizeof(sd_id128_t)) |
390 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
391 | "Root hash must be at least 128bit long: %s", optarg); | |
4623e8e6 | 392 | |
89e62e0b LP |
393 | free_and_replace(arg_verity_settings.root_hash, p); |
394 | arg_verity_settings.root_hash_size = l; | |
4623e8e6 LP |
395 | break; |
396 | } | |
397 | ||
c2923fdc LB |
398 | case ARG_ROOT_HASH_SIG: { |
399 | char *value; | |
89e62e0b LP |
400 | size_t l; |
401 | void *p; | |
c2923fdc LB |
402 | |
403 | if ((value = startswith(optarg, "base64:"))) { | |
c2923fdc LB |
404 | r = unbase64mem(value, strlen(value), &p, &l); |
405 | if (r < 0) | |
406 | return log_error_errno(r, "Failed to parse root hash signature '%s': %m", optarg); | |
c2923fdc | 407 | } else { |
89e62e0b | 408 | r = read_full_file(optarg, (char**) &p, &l); |
c2923fdc | 409 | if (r < 0) |
89e62e0b | 410 | return log_error_errno(r, "Failed to read root hash signature file '%s': %m", optarg); |
c2923fdc LB |
411 | } |
412 | ||
89e62e0b LP |
413 | free_and_replace(arg_verity_settings.root_hash_sig, p); |
414 | arg_verity_settings.root_hash_sig_size = l; | |
c2923fdc LB |
415 | break; |
416 | } | |
417 | ||
89e62e0b | 418 | case ARG_VERITY_DATA: |
614b022c | 419 | r = parse_path_argument(optarg, false, &arg_verity_settings.data_path); |
89e62e0b LP |
420 | if (r < 0) |
421 | return r; | |
422 | break; | |
423 | ||
e475f729 LP |
424 | case ARG_FSCK: |
425 | r = parse_boolean(optarg); | |
426 | if (r < 0) | |
427 | return log_error_errno(r, "Failed to parse --fsck= parameter: %s", optarg); | |
428 | ||
429 | SET_FLAG(arg_flags, DISSECT_IMAGE_FSCK, r); | |
430 | break; | |
431 | ||
74a54bae LP |
432 | case ARG_GROWFS: |
433 | r = parse_boolean(optarg); | |
434 | if (r < 0) | |
435 | return log_error_errno(r, "Failed to parse --growfs= parameter: %s", optarg); | |
436 | ||
437 | SET_FLAG(arg_flags, DISSECT_IMAGE_GROWFS, r); | |
438 | break; | |
439 | ||
de8231b0 | 440 | case ARG_JSON: |
b1e8f46c | 441 | r = parse_json_argument(optarg, &arg_json_format_flags); |
6a01ea4a LP |
442 | if (r <= 0) |
443 | return r; | |
de8231b0 LP |
444 | |
445 | break; | |
446 | ||
0305cf6e LP |
447 | case ARG_DISCOVER: |
448 | arg_action = ACTION_DISCOVER; | |
449 | break; | |
450 | ||
236d1fa2 LP |
451 | case ARG_LOOP_REF: |
452 | if (isempty(optarg)) { | |
453 | arg_loop_ref = mfree(arg_loop_ref); | |
454 | break; | |
455 | } | |
456 | ||
457 | if (strlen(optarg) >= sizeof_field(struct loop_info64, lo_file_name)) | |
458 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Loop device ref string '%s' is too long.", optarg); | |
459 | ||
460 | r = free_and_strdup_warn(&arg_loop_ref, optarg); | |
461 | if (r < 0) | |
462 | return r; | |
463 | break; | |
464 | ||
84be0c71 LP |
465 | case ARG_IMAGE_POLICY: { |
466 | _cleanup_(image_policy_freep) ImagePolicy *p = NULL; | |
467 | ||
468 | r = image_policy_from_string(optarg, &p); | |
469 | if (r < 0) | |
470 | return log_error_errno(r, "Failed to parse image policy: %s", optarg); | |
471 | ||
472 | image_policy_free(arg_image_policy); | |
473 | arg_image_policy = TAKE_PTR(p); | |
474 | break; | |
475 | } | |
476 | ||
a2ea3b2f LP |
477 | case '?': |
478 | return -EINVAL; | |
479 | ||
480 | default: | |
04499a70 | 481 | assert_not_reached(); |
a2ea3b2f | 482 | } |
a2ea3b2f LP |
483 | } |
484 | ||
485 | switch (arg_action) { | |
486 | ||
487 | case ACTION_DISSECT: | |
baaa35ad ZJS |
488 | if (optind + 1 != argc) |
489 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
33973b84 | 490 | "Expected an image file path as only argument."); |
a2ea3b2f | 491 | |
a3c3386e LP |
492 | r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_image); |
493 | if (r < 0) | |
494 | return r; | |
495 | ||
18b5886e | 496 | arg_flags |= DISSECT_IMAGE_READ_ONLY; |
a2ea3b2f LP |
497 | break; |
498 | ||
499 | case ACTION_MOUNT: | |
baaa35ad ZJS |
500 | if (optind + 2 != argc) |
501 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
33973b84 | 502 | "Expected an image file path and mount point path as only arguments."); |
a2ea3b2f | 503 | |
a3c3386e LP |
504 | r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_image); |
505 | if (r < 0) | |
506 | return r; | |
507 | ||
508 | r = parse_path_argument(argv[optind+1], /* suppress_root= */ false, &arg_path); | |
509 | if (r < 0) | |
510 | return r; | |
511 | ||
166ff731 | 512 | arg_flags |= DISSECT_IMAGE_REQUIRE_ROOT; |
a2ea3b2f LP |
513 | break; |
514 | ||
ac1f1adf DDM |
515 | case ACTION_UMOUNT: |
516 | if (optind + 1 != argc) | |
517 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
518 | "Expected a mount point path as only argument."); | |
519 | ||
a3c3386e LP |
520 | r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_path); |
521 | if (r < 0) | |
522 | return r; | |
ac1f1adf DDM |
523 | break; |
524 | ||
07d6072e LP |
525 | case ACTION_ATTACH: |
526 | if (optind + 1 != argc) | |
527 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
528 | "Expected an image file path as only argument."); | |
529 | ||
a3c3386e LP |
530 | r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_image); |
531 | if (r < 0) | |
532 | return r; | |
07d6072e LP |
533 | break; |
534 | ||
535 | case ACTION_DETACH: | |
536 | if (optind + 1 != argc) | |
537 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
538 | "Expected an image file path or loopback device as only argument."); | |
539 | ||
a3c3386e LP |
540 | r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_image); |
541 | if (r < 0) | |
542 | return r; | |
07d6072e LP |
543 | break; |
544 | ||
0cf16924 | 545 | case ACTION_LIST: |
db02190e | 546 | case ACTION_MTREE: |
0cf16924 AAF |
547 | if (optind + 1 != argc) |
548 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
549 | "Expected an image file path as only argument."); | |
550 | ||
a3c3386e LP |
551 | r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_image); |
552 | if (r < 0) | |
553 | return r; | |
554 | ||
0cf16924 AAF |
555 | arg_flags |= DISSECT_IMAGE_READ_ONLY | DISSECT_IMAGE_REQUIRE_ROOT; |
556 | break; | |
557 | ||
33973b84 LP |
558 | case ACTION_COPY_FROM: |
559 | if (argc < optind + 2 || argc > optind + 3) | |
560 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
561 | "Expected an image file path, a source path and an optional destination path as only arguments."); | |
562 | ||
a3c3386e LP |
563 | r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_image); |
564 | if (r < 0) | |
565 | return r; | |
33973b84 LP |
566 | arg_source = argv[optind + 1]; |
567 | arg_target = argc > optind + 2 ? argv[optind + 2] : "-" /* this means stdout */ ; | |
568 | ||
166ff731 | 569 | arg_flags |= DISSECT_IMAGE_READ_ONLY | DISSECT_IMAGE_REQUIRE_ROOT; |
33973b84 LP |
570 | break; |
571 | ||
572 | case ACTION_COPY_TO: | |
573 | if (argc < optind + 2 || argc > optind + 3) | |
574 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
575 | "Expected an image file path, an optional source path and a destination path as only arguments."); | |
576 | ||
a3c3386e LP |
577 | r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_image); |
578 | if (r < 0) | |
579 | return r; | |
33973b84 LP |
580 | |
581 | if (argc > optind + 2) { | |
582 | arg_source = argv[optind + 1]; | |
583 | arg_target = argv[optind + 2]; | |
584 | } else { | |
585 | arg_source = "-"; /* this means stdin */ | |
586 | arg_target = argv[optind + 1]; | |
587 | } | |
588 | ||
166ff731 | 589 | arg_flags |= DISSECT_IMAGE_REQUIRE_ROOT; |
33973b84 LP |
590 | break; |
591 | ||
06186c4c LP |
592 | case ACTION_WITH: |
593 | if (optind >= argc) | |
594 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
595 | "Expected an image file path and an optional command line."); | |
596 | ||
a3c3386e LP |
597 | r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_image); |
598 | if (r < 0) | |
599 | return r; | |
600 | ||
06186c4c LP |
601 | if (argc > optind + 1) { |
602 | arg_argv = strv_copy(argv + optind + 1); | |
603 | if (!arg_argv) | |
604 | return log_oom(); | |
605 | } | |
606 | ||
607 | break; | |
608 | ||
0305cf6e LP |
609 | case ACTION_DISCOVER: |
610 | if (optind != argc) | |
611 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
612 | "Expected no argument."); | |
613 | ||
614 | break; | |
615 | ||
a2ea3b2f | 616 | default: |
04499a70 | 617 | assert_not_reached(); |
a2ea3b2f LP |
618 | } |
619 | ||
620 | return 1; | |
621 | } | |
622 | ||
a164d9d5 LP |
623 | static int parse_argv_as_mount_helper(int argc, char *argv[]) { |
624 | const char *options = NULL; | |
625 | bool fake = false; | |
626 | int c, r; | |
627 | ||
628 | /* Implements util-linux "external helper" command line interface, as per mount(8) man page. */ | |
629 | ||
630 | while ((c = getopt(argc, argv, "sfnvN:o:t:")) >= 0) { | |
631 | switch(c) { | |
632 | ||
633 | case 'f': | |
634 | fake = true; | |
635 | break; | |
636 | ||
637 | case 'o': | |
638 | options = optarg; | |
639 | break; | |
640 | ||
641 | case 't': | |
642 | if (!streq(optarg, "ddi")) | |
643 | log_debug("Unexpected file system type '%s', ignoring.", optarg); | |
644 | break; | |
645 | ||
646 | case 's': /* sloppy mount options */ | |
647 | case 'n': /* aka --no-mtab */ | |
648 | case 'v': /* aka --verbose */ | |
649 | log_debug("Ignoring option -%c, not implemented.", c); | |
650 | break; | |
651 | ||
652 | case 'N': /* aka --namespace= */ | |
653 | return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Option -%c is not implemented, refusing.", c); | |
654 | ||
655 | case '?': | |
656 | return -EINVAL; | |
657 | } | |
658 | } | |
659 | ||
660 | if (optind + 2 != argc) | |
661 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
662 | "Expected an image file path and target directory as only argument."); | |
663 | ||
664 | for (const char *p = options;;) { | |
665 | _cleanup_free_ char *word = NULL; | |
666 | ||
667 | r = extract_first_word(&p, &word, ",", EXTRACT_KEEP_QUOTE); | |
668 | if (r < 0) | |
669 | return log_error_errno(r, "Failed to extract mount option: %m"); | |
670 | if (r == 0) | |
671 | break; | |
672 | ||
673 | if (streq(word, "ro")) | |
674 | SET_FLAG(arg_flags, DISSECT_IMAGE_READ_ONLY, true); | |
675 | else if (streq(word, "rw")) | |
676 | SET_FLAG(arg_flags, DISSECT_IMAGE_READ_ONLY, false); | |
677 | else if (streq(word, "discard")) | |
678 | SET_FLAG(arg_flags, DISSECT_IMAGE_DISCARD_ANY, true); | |
679 | else if (streq(word, "nodiscard")) | |
680 | SET_FLAG(arg_flags, DISSECT_IMAGE_DISCARD_ANY, false); | |
681 | else | |
682 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
683 | "Unknown mount option '%s'.", word); | |
684 | } | |
685 | ||
686 | if (fake) | |
687 | return 0; | |
688 | ||
a3c3386e LP |
689 | r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_image); |
690 | if (r < 0) | |
691 | return r; | |
692 | ||
693 | r = parse_path_argument(argv[optind+1], /* suppress_root= */ false, &arg_path); | |
694 | if (r < 0) | |
695 | return r; | |
a164d9d5 LP |
696 | |
697 | arg_flags |= DISSECT_IMAGE_REQUIRE_ROOT; | |
698 | arg_action = ACTION_MOUNT; | |
699 | return 1; | |
700 | } | |
701 | ||
de8231b0 LP |
702 | static int strv_pair_to_json(char **l, JsonVariant **ret) { |
703 | _cleanup_strv_free_ char **jl = NULL; | |
de8231b0 LP |
704 | |
705 | STRV_FOREACH_PAIR(a, b, l) { | |
706 | char *j; | |
707 | ||
708 | j = strjoin(*a, "=", *b); | |
709 | if (!j) | |
710 | return log_oom(); | |
711 | ||
712 | if (strv_consume(&jl, j) < 0) | |
713 | return log_oom(); | |
714 | } | |
715 | ||
716 | return json_variant_new_array_strv(ret, jl); | |
717 | } | |
718 | ||
8570b98b | 719 | static void strv_pair_print(char **l, const char *prefix) { |
8570b98b LP |
720 | assert(prefix); |
721 | ||
7f5b2615 | 722 | STRV_FOREACH_PAIR(p, q, l) |
8570b98b LP |
723 | if (p == l) |
724 | printf("%s %s=%s\n", prefix, *p, *q); | |
725 | else | |
726 | printf("%*s %s=%s\n", (int) strlen(prefix), "", *p, *q); | |
8570b98b LP |
727 | } |
728 | ||
994c9c70 LP |
729 | static int get_sysext_scopes(DissectedImage *m, char ***ret_scopes) { |
730 | _cleanup_strv_free_ char **l = NULL; | |
731 | const char *e; | |
732 | ||
733 | assert(m); | |
734 | assert(ret_scopes); | |
735 | ||
736 | /* If there's no extension-release file its not a system extension. Otherwise the SYSEXT_SCOPE field | |
737 | * indicates which scope it is for — and it defaults to "system" + "portable" if unset. */ | |
738 | ||
739 | if (!m->extension_release) { | |
740 | *ret_scopes = NULL; | |
741 | return 0; | |
742 | } | |
743 | ||
744 | e = strv_env_pairs_get(m->extension_release, "SYSEXT_SCOPE"); | |
745 | if (e) | |
746 | l = strv_split(e, WHITESPACE); | |
747 | else | |
748 | l = strv_new("system", "portable"); | |
749 | if (!l) | |
750 | return -ENOMEM; | |
751 | ||
752 | *ret_scopes = TAKE_PTR(l); | |
753 | return 1; | |
754 | } | |
755 | ||
37e44c3f | 756 | static int action_dissect(DissectedImage *m, LoopDevice *d) { |
de8231b0 | 757 | _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; |
37e44c3f | 758 | _cleanup_(table_unrefp) Table *t = NULL; |
cfb623b6 | 759 | _cleanup_free_ char *bn = NULL; |
de8231b0 | 760 | uint64_t size = UINT64_MAX; |
a2ea3b2f LP |
761 | int r; |
762 | ||
37e44c3f LP |
763 | assert(m); |
764 | assert(d); | |
a2ea3b2f | 765 | |
cfb623b6 LP |
766 | r = path_extract_filename(arg_image, &bn); |
767 | if (r < 0) | |
768 | return log_error_errno(r, "Failed to extract file name from image path '%s': %m", arg_image); | |
769 | ||
17547fb5 | 770 | if (arg_json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO)) |
384c2c32 | 771 | pager_open(arg_pager_flags); |
17547fb5 | 772 | |
6a01ea4a | 773 | if (arg_json_format_flags & JSON_FORMAT_OFF) |
5db8b0bc | 774 | printf(" Name: %s%s%s\n", ansi_highlight(), bn, ansi_normal()); |
a2ea3b2f | 775 | |
37e44c3f LP |
776 | if (ioctl(d->fd, BLKGETSIZE64, &size) < 0) |
777 | log_debug_errno(errno, "Failed to query size of loopback device: %m"); | |
2b59bf51 ZJS |
778 | else if (arg_json_format_flags & JSON_FORMAT_OFF) |
779 | printf(" Size: %s\n", FORMAT_BYTES(size)); | |
a2ea3b2f | 780 | |
1d93c003 LP |
781 | printf(" Sec. Size: %" PRIu32 "\n", m->sector_size); |
782 | ||
2348043f LP |
783 | printf(" Arch.: %s\n", |
784 | strna(architecture_to_string(dissected_image_architecture(m)))); | |
785 | ||
6a01ea4a | 786 | if (arg_json_format_flags & JSON_FORMAT_OFF) |
de8231b0 | 787 | putc('\n', stdout); |
af8219d5 | 788 | |
1d93c003 LP |
789 | fflush(stdout); |
790 | ||
22847508 | 791 | r = dissected_image_acquire_metadata(m, 0); |
af8219d5 LP |
792 | if (r == -ENXIO) |
793 | return log_error_errno(r, "No root partition discovered."); | |
af8219d5 LP |
794 | if (r == -EUCLEAN) |
795 | return log_error_errno(r, "File system check of image failed."); | |
4f309abb LP |
796 | if (r == -EMEDIUMTYPE) |
797 | log_warning_errno(r, "Not a valid OS image, no os-release file included. Proceeding anyway."); | |
798 | else if (r == -EUNATCH) | |
af8219d5 LP |
799 | log_warning_errno(r, "OS image is encrypted, proceeding without showing OS image metadata."); |
800 | else if (r == -EBUSY) | |
801 | log_warning_errno(r, "OS image is currently in use, proceeding without showing OS image metadata."); | |
802 | else if (r < 0) | |
803 | return log_error_errno(r, "Failed to acquire image metadata: %m"); | |
6a01ea4a | 804 | else if (arg_json_format_flags & JSON_FORMAT_OFF) { |
994c9c70 LP |
805 | _cleanup_strv_free_ char **sysext_scopes = NULL; |
806 | ||
b387778c LP |
807 | if (!sd_id128_is_null(m->image_uuid)) |
808 | printf("Image UUID: %s\n", SD_ID128_TO_UUID_STRING(m->image_uuid)); | |
809 | ||
af8219d5 LP |
810 | if (m->hostname) |
811 | printf(" Hostname: %s\n", m->hostname); | |
a2ea3b2f | 812 | |
af8219d5 LP |
813 | if (!sd_id128_is_null(m->machine_id)) |
814 | printf("Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->machine_id)); | |
a1edd22e | 815 | |
8570b98b LP |
816 | strv_pair_print(m->machine_info, |
817 | "Mach. Info:"); | |
818 | strv_pair_print(m->os_release, | |
819 | "OS Release:"); | |
fab22946 LP |
820 | strv_pair_print(m->initrd_release, |
821 | "initrd R.:"); | |
8570b98b LP |
822 | strv_pair_print(m->extension_release, |
823 | " Ext. Rel.:"); | |
7718ac97 | 824 | |
4f309abb LP |
825 | if (m->hostname || |
826 | !sd_id128_is_null(m->machine_id) || | |
827 | !strv_isempty(m->machine_info) || | |
8570b98b | 828 | !strv_isempty(m->os_release) || |
fab22946 | 829 | !strv_isempty(m->initrd_release) || |
8570b98b | 830 | !strv_isempty(m->extension_release)) |
4f309abb | 831 | putc('\n', stdout); |
994c9c70 LP |
832 | |
833 | printf(" Use As: %s bootable system for UEFI\n", COLOR_MARK_BOOL(m->partitions[PARTITION_ESP].found)); | |
834 | ||
835 | if (m->has_init_system >= 0) | |
836 | printf(" %s bootable system for container\n", COLOR_MARK_BOOL(m->has_init_system)); | |
837 | ||
838 | printf(" %s portable service\n", | |
839 | COLOR_MARK_BOOL(strv_env_pairs_get(m->os_release, "PORTABLE_PREFIXES"))); | |
fab22946 LP |
840 | printf(" %s initrd\n", |
841 | COLOR_MARK_BOOL(!strv_isempty(m->initrd_release))); | |
994c9c70 LP |
842 | |
843 | r = get_sysext_scopes(m, &sysext_scopes); | |
844 | if (r < 0) | |
845 | return log_error_errno(r, "Failed to parse SYSEXT_SCOPE: %m"); | |
846 | ||
847 | printf(" %s extension for system\n", | |
848 | COLOR_MARK_BOOL(strv_contains(sysext_scopes, "system"))); | |
849 | printf(" %s extension for initrd\n", | |
850 | COLOR_MARK_BOOL(strv_contains(sysext_scopes, "initrd"))); | |
851 | printf(" %s extension for portable service\n", | |
852 | COLOR_MARK_BOOL(strv_contains(sysext_scopes, "portable"))); | |
853 | ||
854 | putc('\n', stdout); | |
4f309abb | 855 | } else { |
fab22946 | 856 | _cleanup_(json_variant_unrefp) JsonVariant *mi = NULL, *osr = NULL, *irdr = NULL, *exr = NULL; |
994c9c70 | 857 | _cleanup_strv_free_ char **sysext_scopes = NULL; |
de8231b0 LP |
858 | |
859 | if (!strv_isempty(m->machine_info)) { | |
860 | r = strv_pair_to_json(m->machine_info, &mi); | |
861 | if (r < 0) | |
862 | return log_oom(); | |
863 | } | |
864 | ||
865 | if (!strv_isempty(m->os_release)) { | |
866 | r = strv_pair_to_json(m->os_release, &osr); | |
867 | if (r < 0) | |
868 | return log_oom(); | |
869 | } | |
870 | ||
fab22946 LP |
871 | if (!strv_isempty(m->initrd_release)) { |
872 | r = strv_pair_to_json(m->initrd_release, &irdr); | |
873 | if (r < 0) | |
874 | return log_oom(); | |
875 | } | |
876 | ||
7718ac97 LB |
877 | if (!strv_isempty(m->extension_release)) { |
878 | r = strv_pair_to_json(m->extension_release, &exr); | |
879 | if (r < 0) | |
880 | return log_oom(); | |
881 | } | |
882 | ||
994c9c70 LP |
883 | r = get_sysext_scopes(m, &sysext_scopes); |
884 | if (r < 0) | |
885 | return log_error_errno(r, "Failed to parse SYSEXT_SCOPE: %m"); | |
886 | ||
de8231b0 | 887 | r = json_build(&v, JSON_BUILD_OBJECT( |
cfb623b6 | 888 | JSON_BUILD_PAIR("name", JSON_BUILD_STRING(bn)), |
b387778c | 889 | JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->image_uuid), "imageUuid", JSON_BUILD_UUID(m->image_uuid)), |
de8231b0 | 890 | JSON_BUILD_PAIR("size", JSON_BUILD_INTEGER(size)), |
1d93c003 | 891 | JSON_BUILD_PAIR("sectorSize", JSON_BUILD_INTEGER(m->sector_size)), |
de8231b0 LP |
892 | JSON_BUILD_PAIR_CONDITION(m->hostname, "hostname", JSON_BUILD_STRING(m->hostname)), |
893 | JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->machine_id), "machineId", JSON_BUILD_ID128(m->machine_id)), | |
894 | JSON_BUILD_PAIR_CONDITION(mi, "machineInfo", JSON_BUILD_VARIANT(mi)), | |
7718ac97 | 895 | JSON_BUILD_PAIR_CONDITION(osr, "osRelease", JSON_BUILD_VARIANT(osr)), |
fab22946 | 896 | JSON_BUILD_PAIR_CONDITION(osr, "initrdRelease", JSON_BUILD_VARIANT(irdr)), |
994c9c70 LP |
897 | JSON_BUILD_PAIR_CONDITION(exr, "extensionRelease", JSON_BUILD_VARIANT(exr)), |
898 | JSON_BUILD_PAIR("useBootableUefi", JSON_BUILD_BOOLEAN(m->partitions[PARTITION_ESP].found)), | |
899 | JSON_BUILD_PAIR_CONDITION(m->has_init_system >= 0, "useBootableContainer", JSON_BUILD_BOOLEAN(m->has_init_system)), | |
fab22946 | 900 | JSON_BUILD_PAIR("useInitrd", JSON_BUILD_BOOLEAN(!strv_isempty(m->initrd_release))), |
994c9c70 LP |
901 | JSON_BUILD_PAIR("usePortableService", JSON_BUILD_BOOLEAN(strv_env_pairs_get(m->os_release, "PORTABLE_MATCHES"))), |
902 | JSON_BUILD_PAIR("useSystemExtension", JSON_BUILD_BOOLEAN(strv_contains(sysext_scopes, "system"))), | |
903 | JSON_BUILD_PAIR("useInitRDExtension", JSON_BUILD_BOOLEAN(strv_contains(sysext_scopes, "initrd"))), | |
904 | JSON_BUILD_PAIR("usePortableExtension", JSON_BUILD_BOOLEAN(strv_contains(sysext_scopes, "portable"))))); | |
de8231b0 LP |
905 | if (r < 0) |
906 | return log_oom(); | |
907 | } | |
908 | ||
ee8e497d | 909 | t = table_new("rw", "designator", "partition uuid", "partition label", "fstype", "architecture", "verity", "growfs", "node", "partno"); |
37e44c3f LP |
910 | if (!t) |
911 | return log_oom(); | |
a1edd22e | 912 | |
c8b62cf6 | 913 | table_set_ersatz_string(t, TABLE_ERSATZ_DASH); |
37e44c3f | 914 | (void) table_set_align_percent(t, table_get_cell(t, 0, 7), 100); |
a2ea3b2f | 915 | |
569a0e42 | 916 | for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) { |
37e44c3f | 917 | DissectedPartition *p = m->partitions + i; |
89d00f2e | 918 | |
37e44c3f LP |
919 | if (!p->found) |
920 | continue; | |
89d00f2e | 921 | |
37e44c3f LP |
922 | r = table_add_many( |
923 | t, | |
924 | TABLE_STRING, p->rw ? "rw" : "ro", | |
925 | TABLE_STRING, partition_designator_to_string(i)); | |
926 | if (r < 0) | |
927 | return table_log_add_error(r); | |
89d00f2e | 928 | |
37e44c3f LP |
929 | if (sd_id128_is_null(p->uuid)) |
930 | r = table_add_cell(t, NULL, TABLE_EMPTY, NULL); | |
931 | else | |
932 | r = table_add_cell(t, NULL, TABLE_UUID, &p->uuid); | |
933 | if (r < 0) | |
934 | return table_log_add_error(r); | |
89d00f2e | 935 | |
37e44c3f LP |
936 | r = table_add_many( |
937 | t, | |
1474d7ac | 938 | TABLE_STRING, p->label, |
37e44c3f LP |
939 | TABLE_STRING, p->fstype, |
940 | TABLE_STRING, architecture_to_string(p->architecture)); | |
941 | if (r < 0) | |
942 | return table_log_add_error(r); | |
943 | ||
89e62e0b | 944 | if (arg_verity_settings.data_path) |
37e44c3f | 945 | r = table_add_cell(t, NULL, TABLE_STRING, "external"); |
49536766 | 946 | else if (dissected_image_verity_candidate(m, i)) |
8ee9615e LP |
947 | r = table_add_cell(t, NULL, TABLE_STRING, |
948 | dissected_image_verity_sig_ready(m, i) ? "signed" : | |
949 | yes_no(dissected_image_verity_ready(m, i))); | |
37e44c3f LP |
950 | else |
951 | r = table_add_cell(t, NULL, TABLE_EMPTY, NULL); | |
952 | if (r < 0) | |
953 | return table_log_add_error(r); | |
89d00f2e | 954 | |
ee8e497d LP |
955 | r = table_add_many(t, TABLE_BOOLEAN, (int) p->growfs); |
956 | if (r < 0) | |
957 | return table_log_add_error(r); | |
958 | ||
37e44c3f | 959 | if (p->partno < 0) /* no partition table, naked file system */ { |
a3c3386e | 960 | r = table_add_cell(t, NULL, TABLE_PATH_BASENAME, arg_image); |
89d00f2e LP |
961 | if (r < 0) |
962 | return table_log_add_error(r); | |
963 | ||
37e44c3f LP |
964 | r = table_add_cell(t, NULL, TABLE_EMPTY, NULL); |
965 | } else { | |
966 | r = table_add_cell(t, NULL, TABLE_STRING, p->node); | |
89d00f2e LP |
967 | if (r < 0) |
968 | return table_log_add_error(r); | |
969 | ||
37e44c3f LP |
970 | r = table_add_cell(t, NULL, TABLE_INT, &p->partno); |
971 | } | |
972 | if (r < 0) | |
973 | return table_log_add_error(r); | |
974 | } | |
89d00f2e | 975 | |
6a01ea4a | 976 | if (arg_json_format_flags & JSON_FORMAT_OFF) { |
17547fb5 LP |
977 | (void) table_set_header(t, arg_legend); |
978 | ||
979 | r = table_print(t, NULL); | |
6a01ea4a LP |
980 | if (r < 0) |
981 | return table_log_print_error(r); | |
982 | } else { | |
de8231b0 LP |
983 | _cleanup_(json_variant_unrefp) JsonVariant *jt = NULL; |
984 | ||
985 | r = table_to_json(t, &jt); | |
986 | if (r < 0) | |
987 | return log_error_errno(r, "Failed to convert table to JSON: %m"); | |
988 | ||
989 | r = json_variant_set_field(&v, "mounts", jt); | |
990 | if (r < 0) | |
991 | return log_oom(); | |
992 | ||
993 | json_variant_dump(v, arg_json_format_flags, stdout, NULL); | |
de8231b0 | 994 | } |
89d00f2e | 995 | |
37e44c3f LP |
996 | return 0; |
997 | } | |
89d00f2e | 998 | |
37e44c3f | 999 | static int action_mount(DissectedImage *m, LoopDevice *d) { |
37e44c3f | 1000 | int r; |
89d00f2e | 1001 | |
37e44c3f LP |
1002 | assert(m); |
1003 | assert(d); | |
94abea2a | 1004 | assert(arg_action == ACTION_MOUNT); |
89d00f2e | 1005 | |
21b61b1d | 1006 | r = dissected_image_mount_and_warn(m, arg_path, UID_INVALID, UID_INVALID, arg_flags); |
37e44c3f | 1007 | if (r < 0) |
af187ab2 | 1008 | return r; |
89d00f2e | 1009 | |
41bc4849 LP |
1010 | r = loop_device_flock(d, LOCK_UN); |
1011 | if (r < 0) | |
1012 | return log_error_errno(r, "Failed to unlock loopback block device: %m"); | |
1013 | ||
3044d343 YW |
1014 | r = dissected_image_relinquish(m); |
1015 | if (r < 0) | |
1016 | return log_error_errno(r, "Failed to relinquish DM and loopback block devices: %m"); | |
a2ea3b2f | 1017 | |
37e44c3f LP |
1018 | return 0; |
1019 | } | |
18b5886e | 1020 | |
0cf16924 AAF |
1021 | static int list_print_item( |
1022 | RecurseDirEvent event, | |
1023 | const char *path, | |
1024 | int dir_fd, | |
1025 | int inode_fd, | |
1026 | const struct dirent *de, | |
1027 | const struct statx *sx, | |
1028 | void *userdata) { | |
1029 | ||
167f2c1a | 1030 | assert(path); |
0cf16924 AAF |
1031 | |
1032 | if (event == RECURSE_DIR_ENTER) | |
bb69ec95 | 1033 | printf("%s%s/%s\n", path, ansi_grey(), ansi_normal()); |
0cf16924 AAF |
1034 | else if (event == RECURSE_DIR_ENTRY) |
1035 | printf("%s\n", path); | |
1036 | ||
1037 | return RECURSE_DIR_CONTINUE; | |
1038 | } | |
1039 | ||
db02190e | 1040 | static int get_file_sha256(int inode_fd, uint8_t ret[static SHA256_DIGEST_SIZE]) { |
254d1313 | 1041 | _cleanup_close_ int fd = -EBADF; |
db02190e LP |
1042 | struct sha256_ctx ctx; |
1043 | ||
1044 | /* convert O_PATH fd into a regular one */ | |
1045 | fd = fd_reopen(inode_fd, O_RDONLY|O_CLOEXEC); | |
1046 | if (fd < 0) | |
1047 | return fd; | |
1048 | ||
1049 | /* Calculating the SHA sum might be slow, hence let's flush STDOUT first, to give user an idea where we are slow. */ | |
1050 | fflush(stdout); | |
1051 | ||
1052 | sha256_init_ctx(&ctx); | |
1053 | ||
1054 | for (;;) { | |
1055 | uint8_t buffer[64 * 1024]; | |
1056 | ssize_t n; | |
1057 | ||
1058 | n = read(fd, buffer, sizeof(buffer)); | |
1059 | if (n < 0) | |
1060 | return -errno; | |
1061 | if (n == 0) | |
1062 | break; | |
1063 | ||
1064 | sha256_process_bytes(buffer, n, &ctx); | |
1065 | } | |
1066 | ||
1067 | sha256_finish_ctx(&ctx, ret); | |
1068 | return 0; | |
1069 | } | |
1070 | ||
12c0f4ff LP |
1071 | static const char *pick_color_for_uid_gid(uid_t uid) { |
1072 | if (uid == UID_NOBODY) | |
1073 | return ansi_highlight_yellow4(); /* files should never be owned by 'nobody' (but might happen due to userns mapping) */ | |
1074 | if (uid_is_system(uid)) | |
1075 | return ansi_normal(); /* files in disk images are typically owned by root and other system users, no issue there */ | |
1076 | if (uid_is_dynamic(uid)) | |
1077 | return ansi_highlight_red(); /* files should never be owned persistently by dynamic users, and there are just no execuses */ | |
1078 | if (uid_is_container(uid)) | |
1079 | return ansi_highlight_cyan(); | |
1080 | ||
1081 | return ansi_highlight(); | |
1082 | } | |
1083 | ||
db02190e LP |
1084 | static int mtree_print_item( |
1085 | RecurseDirEvent event, | |
1086 | const char *path, | |
1087 | int dir_fd, | |
1088 | int inode_fd, | |
1089 | const struct dirent *de, | |
1090 | const struct statx *sx, | |
1091 | void *userdata) { | |
1092 | ||
01a33cf7 | 1093 | _cleanup_free_ char *escaped = NULL; |
db02190e LP |
1094 | int r; |
1095 | ||
167f2c1a | 1096 | assert(path); |
db02190e | 1097 | |
01a33cf7 YW |
1098 | if (!IN_SET(event, RECURSE_DIR_ENTER, RECURSE_DIR_ENTRY)) |
1099 | return RECURSE_DIR_CONTINUE; | |
db02190e | 1100 | |
01a33cf7 | 1101 | assert(sx); |
5ffa6a0a | 1102 | |
01a33cf7 YW |
1103 | if (isempty(path)) |
1104 | path = "."; | |
1105 | else { | |
1106 | /* BSD mtree uses either C or octal escaping, and covers whitespace, comments and glob characters. We use C style escaping and follow suit */ | |
1107 | path = escaped = xescape(path, WHITESPACE COMMENTS GLOB_CHARS); | |
1108 | if (!escaped) | |
1109 | return log_oom(); | |
1110 | } | |
db02190e | 1111 | |
01a33cf7 YW |
1112 | printf("%s", isempty(path) ? "." : path); |
1113 | ||
1114 | if (FLAGS_SET(sx->stx_mask, STATX_TYPE)) { | |
1115 | if (S_ISDIR(sx->stx_mode)) | |
1116 | printf("%s/%s", ansi_grey(), ansi_normal()); | |
1117 | ||
1118 | printf(" %stype=%s%s%s%s", | |
1119 | ansi_grey(), | |
1120 | ansi_normal(), | |
1121 | S_ISDIR(sx->stx_mode) ? ansi_highlight_blue() : | |
1122 | S_ISLNK(sx->stx_mode) ? ansi_highlight_cyan() : | |
1123 | (S_ISFIFO(sx->stx_mode) || S_ISCHR(sx->stx_mode) || S_ISBLK(sx->stx_mode)) ? ansi_highlight_yellow4() : | |
1124 | S_ISSOCK(sx->stx_mode) ? ansi_highlight_magenta() : "", | |
1125 | ASSERT_PTR(S_ISDIR(sx->stx_mode) ? "dir" : | |
1126 | S_ISREG(sx->stx_mode) ? "file" : | |
1127 | S_ISLNK(sx->stx_mode) ? "link" : | |
1128 | S_ISFIFO(sx->stx_mode) ? "fifo" : | |
1129 | S_ISBLK(sx->stx_mode) ? "block" : | |
1130 | S_ISCHR(sx->stx_mode) ? "char" : | |
1131 | S_ISSOCK(sx->stx_mode) ? "socket" : NULL), | |
1132 | ansi_normal()); | |
1133 | } | |
db02190e | 1134 | |
01a33cf7 YW |
1135 | if (FLAGS_SET(sx->stx_mask, STATX_MODE) && (!FLAGS_SET(sx->stx_mask, STATX_TYPE) || !S_ISLNK(sx->stx_mode))) |
1136 | printf(" %smode=%s%04o", | |
1137 | ansi_grey(), | |
1138 | ansi_normal(), | |
1139 | (unsigned) (sx->stx_mode & 0777)); | |
1140 | ||
1141 | if (FLAGS_SET(sx->stx_mask, STATX_UID)) | |
12c0f4ff | 1142 | printf(" %suid=%s" UID_FMT "%s", |
01a33cf7 | 1143 | ansi_grey(), |
12c0f4ff LP |
1144 | pick_color_for_uid_gid(sx->stx_uid), |
1145 | sx->stx_uid, | |
1146 | ansi_normal()); | |
01a33cf7 YW |
1147 | |
1148 | if (FLAGS_SET(sx->stx_mask, STATX_GID)) | |
12c0f4ff | 1149 | printf(" %sgid=%s" GID_FMT "%s", |
01a33cf7 | 1150 | ansi_grey(), |
12c0f4ff LP |
1151 | pick_color_for_uid_gid(sx->stx_gid), |
1152 | sx->stx_gid, | |
1153 | ansi_normal()); | |
01a33cf7 YW |
1154 | |
1155 | if (FLAGS_SET(sx->stx_mask, STATX_TYPE|STATX_SIZE) && S_ISREG(sx->stx_mode)) { | |
1156 | printf(" %ssize=%s%" PRIu64, | |
1157 | ansi_grey(), | |
1158 | ansi_normal(), | |
1159 | (uint64_t) sx->stx_size); | |
1160 | ||
1161 | if (inode_fd >= 0 && sx->stx_size > 0) { | |
1162 | uint8_t hash[SHA256_DIGEST_SIZE]; | |
1163 | ||
1164 | r = get_file_sha256(inode_fd, hash); | |
db02190e | 1165 | if (r < 0) |
01a33cf7 | 1166 | log_warning_errno(r, "Failed to calculate file SHA256 sum for '%s', ignoring: %m", path); |
db02190e | 1167 | else { |
01a33cf7 | 1168 | _cleanup_free_ char *h = NULL; |
db02190e | 1169 | |
01a33cf7 YW |
1170 | h = hexmem(hash, sizeof(hash)); |
1171 | if (!h) | |
db02190e LP |
1172 | return log_oom(); |
1173 | ||
01a33cf7 | 1174 | printf(" %ssha256sum=%s%s", |
db02190e LP |
1175 | ansi_grey(), |
1176 | ansi_normal(), | |
01a33cf7 | 1177 | h); |
db02190e LP |
1178 | } |
1179 | } | |
01a33cf7 | 1180 | } |
db02190e | 1181 | |
01a33cf7 YW |
1182 | if (FLAGS_SET(sx->stx_mask, STATX_TYPE) && S_ISLNK(sx->stx_mode) && inode_fd >= 0) { |
1183 | _cleanup_free_ char *target = NULL; | |
1184 | ||
1185 | r = readlinkat_malloc(inode_fd, "", &target); | |
1186 | if (r < 0) | |
1187 | log_warning_errno(r, "Failed to read symlink '%s', ignoring: %m", path); | |
1188 | else { | |
1189 | _cleanup_free_ char *target_escaped = NULL; | |
1190 | ||
1191 | target_escaped = xescape(target, WHITESPACE COMMENTS GLOB_CHARS); | |
1192 | if (!target_escaped) | |
1193 | return log_oom(); | |
1194 | ||
1195 | printf(" %slink=%s%s", | |
db02190e LP |
1196 | ansi_grey(), |
1197 | ansi_normal(), | |
01a33cf7 YW |
1198 | target_escaped); |
1199 | } | |
db02190e LP |
1200 | } |
1201 | ||
01a33cf7 YW |
1202 | if (FLAGS_SET(sx->stx_mask, STATX_TYPE) && (S_ISBLK(sx->stx_mode) || S_ISCHR(sx->stx_mode))) |
1203 | printf(" %sdevice=%slinux,%" PRIu64 ",%" PRIu64, | |
1204 | ansi_grey(), | |
1205 | ansi_normal(), | |
1206 | (uint64_t) sx->stx_rdev_major, | |
1207 | (uint64_t) sx->stx_rdev_minor); | |
1208 | ||
1209 | printf("\n"); | |
1210 | ||
db02190e LP |
1211 | return RECURSE_DIR_CONTINUE; |
1212 | } | |
1213 | ||
1214 | static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) { | |
37e44c3f | 1215 | _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; |
37e44c3f LP |
1216 | _cleanup_(rmdir_and_freep) char *created_dir = NULL; |
1217 | _cleanup_free_ char *temp = NULL; | |
1218 | int r; | |
a2ea3b2f | 1219 | |
37e44c3f LP |
1220 | assert(m); |
1221 | assert(d); | |
94abea2a | 1222 | assert(IN_SET(arg_action, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO)); |
a2ea3b2f | 1223 | |
37e44c3f LP |
1224 | r = detach_mount_namespace(); |
1225 | if (r < 0) | |
1226 | return log_error_errno(r, "Failed to detach mount namespace: %m"); | |
33973b84 | 1227 | |
37e44c3f LP |
1228 | r = tempfn_random_child(NULL, program_invocation_short_name, &temp); |
1229 | if (r < 0) | |
1230 | return log_error_errno(r, "Failed to generate temporary mount directory: %m"); | |
33973b84 | 1231 | |
37e44c3f LP |
1232 | r = mkdir_p(temp, 0700); |
1233 | if (r < 0) | |
1234 | return log_error_errno(r, "Failed to create mount point: %m"); | |
33973b84 | 1235 | |
37e44c3f LP |
1236 | created_dir = TAKE_PTR(temp); |
1237 | ||
21b61b1d | 1238 | r = dissected_image_mount_and_warn(m, created_dir, UID_INVALID, UID_INVALID, arg_flags); |
37e44c3f | 1239 | if (r < 0) |
af187ab2 | 1240 | return r; |
33973b84 | 1241 | |
37e44c3f | 1242 | mounted_dir = TAKE_PTR(created_dir); |
33973b84 | 1243 | |
41bc4849 LP |
1244 | r = loop_device_flock(d, LOCK_UN); |
1245 | if (r < 0) | |
1246 | return log_error_errno(r, "Failed to unlock loopback block device: %m"); | |
1247 | ||
3044d343 YW |
1248 | r = dissected_image_relinquish(m); |
1249 | if (r < 0) | |
1250 | return log_error_errno(r, "Failed to relinquish DM and loopback block devices: %m"); | |
33973b84 | 1251 | |
b7df8622 LP |
1252 | switch (arg_action) { |
1253 | ||
1254 | case ACTION_COPY_FROM: { | |
254d1313 | 1255 | _cleanup_close_ int source_fd = -EBADF, target_fd = -EBADF; |
33973b84 | 1256 | |
f461a28d | 1257 | source_fd = chase_and_open(arg_source, mounted_dir, CHASE_PREFIX_ROOT|CHASE_WARN, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL); |
37e44c3f LP |
1258 | if (source_fd < 0) |
1259 | return log_error_errno(source_fd, "Failed to open source path '%s' in image '%s': %m", arg_source, arg_image); | |
1260 | ||
1261 | /* Copying to stdout? */ | |
1262 | if (streq(arg_target, "-")) { | |
f5fbe71d | 1263 | r = copy_bytes(source_fd, STDOUT_FILENO, UINT64_MAX, COPY_REFLINK); |
33973b84 | 1264 | if (r < 0) |
37e44c3f LP |
1265 | return log_error_errno(r, "Failed to copy bytes from %s in mage '%s' to stdout: %m", arg_source, arg_image); |
1266 | ||
38db55ab | 1267 | /* When we copy to stdout we don't copy any attributes (i.e. no access mode, no ownership, no xattr, no times) */ |
37e44c3f | 1268 | return 0; |
33973b84 LP |
1269 | } |
1270 | ||
37e44c3f | 1271 | /* Try to copy as directory? */ |
dd480f78 | 1272 | r = copy_directory_fd(source_fd, arg_target, COPY_REFLINK|COPY_MERGE_EMPTY|COPY_SIGINT|COPY_HARDLINKS); |
37e44c3f LP |
1273 | if (r >= 0) |
1274 | return 0; | |
1275 | if (r != -ENOTDIR) | |
1276 | return log_error_errno(r, "Failed to copy %s in image '%s' to '%s': %m", arg_source, arg_image, arg_target); | |
1277 | ||
1278 | r = fd_verify_regular(source_fd); | |
1279 | if (r == -EISDIR) | |
1280 | return log_error_errno(r, "Target '%s' exists already and is not a directory.", arg_target); | |
1281 | if (r < 0) | |
1282 | return log_error_errno(r, "Source path %s in image '%s' is neither regular file nor directory, refusing: %m", arg_source, arg_image); | |
33973b84 | 1283 | |
37e44c3f LP |
1284 | /* Nah, it's a plain file! */ |
1285 | target_fd = open(arg_target, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600); | |
1286 | if (target_fd < 0) | |
1287 | return log_error_errno(errno, "Failed to create regular file at target path '%s': %m", arg_target); | |
33973b84 | 1288 | |
f5fbe71d | 1289 | r = copy_bytes(source_fd, target_fd, UINT64_MAX, COPY_REFLINK); |
37e44c3f LP |
1290 | if (r < 0) |
1291 | return log_error_errno(r, "Failed to copy bytes from %s in mage '%s' to '%s': %m", arg_source, arg_image, arg_target); | |
33973b84 | 1292 | |
c17cfe6e | 1293 | (void) copy_xattr(source_fd, NULL, target_fd, NULL, 0); |
37e44c3f LP |
1294 | (void) copy_access(source_fd, target_fd); |
1295 | (void) copy_times(source_fd, target_fd, 0); | |
33973b84 | 1296 | |
37e44c3f | 1297 | /* When this is a regular file we don't copy ownership! */ |
b7df8622 LP |
1298 | return 0; |
1299 | } | |
33973b84 | 1300 | |
b7df8622 | 1301 | case ACTION_COPY_TO: { |
254d1313 | 1302 | _cleanup_close_ int source_fd = -EBADF, target_fd = -EBADF, dfd = -EBADF; |
cfb623b6 | 1303 | _cleanup_free_ char *dn = NULL, *bn = NULL; |
83802e9a | 1304 | bool is_dir; |
33973b84 | 1305 | |
45519d13 LP |
1306 | r = path_extract_directory(arg_target, &dn); |
1307 | if (r < 0) | |
cfb623b6 LP |
1308 | return log_error_errno(r, "Failed to extract directory from target path '%s': %m", arg_target); |
1309 | r = path_extract_filename(arg_target, &bn); | |
1310 | if (r < 0) | |
1311 | return log_error_errno(r, "Failed to extract filename from target path '%s': %m", arg_target); | |
83802e9a | 1312 | is_dir = r == O_DIRECTORY; |
37e44c3f | 1313 | |
f461a28d | 1314 | r = chase(dn, mounted_dir, CHASE_PREFIX_ROOT|CHASE_WARN, NULL, &dfd); |
37e44c3f LP |
1315 | if (r < 0) |
1316 | return log_error_errno(r, "Failed to open '%s': %m", dn); | |
33973b84 | 1317 | |
37e44c3f LP |
1318 | /* Are we reading from stdin? */ |
1319 | if (streq(arg_source, "-")) { | |
83802e9a LP |
1320 | if (is_dir) |
1321 | return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Cannot copy STDIN to a directory, refusing."); | |
1322 | ||
cfb623b6 | 1323 | target_fd = openat(dfd, bn, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_EXCL, 0644); |
33973b84 | 1324 | if (target_fd < 0) |
37e44c3f | 1325 | return log_error_errno(errno, "Failed to open target file '%s': %m", arg_target); |
33973b84 | 1326 | |
f5fbe71d | 1327 | r = copy_bytes(STDIN_FILENO, target_fd, UINT64_MAX, COPY_REFLINK); |
33973b84 | 1328 | if (r < 0) |
37e44c3f | 1329 | return log_error_errno(r, "Failed to copy bytes from stdin to '%s' in image '%s': %m", arg_target, arg_image); |
33973b84 | 1330 | |
37e44c3f LP |
1331 | /* When we copy from stdin we don't copy any attributes (i.e. no access mode, no ownership, no xattr, no times) */ |
1332 | return 0; | |
1333 | } | |
33973b84 | 1334 | |
37e44c3f LP |
1335 | source_fd = open(arg_source, O_RDONLY|O_CLOEXEC|O_NOCTTY); |
1336 | if (source_fd < 0) | |
1337 | return log_error_errno(source_fd, "Failed to open source path '%s': %m", arg_source); | |
33973b84 | 1338 | |
37e44c3f LP |
1339 | r = fd_verify_regular(source_fd); |
1340 | if (r < 0) { | |
1341 | if (r != -EISDIR) | |
1342 | return log_error_errno(r, "Source '%s' is neither regular file nor directory: %m", arg_source); | |
33973b84 | 1343 | |
37e44c3f | 1344 | /* We are looking at a directory. */ |
33973b84 | 1345 | |
cfb623b6 | 1346 | target_fd = openat(dfd, bn, O_RDONLY|O_DIRECTORY|O_CLOEXEC); |
37e44c3f LP |
1347 | if (target_fd < 0) { |
1348 | if (errno != ENOENT) | |
1349 | return log_error_errno(errno, "Failed to open destination '%s': %m", arg_target); | |
33973b84 | 1350 | |
a424958a | 1351 | r = copy_tree_at(source_fd, ".", dfd, bn, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS, NULL); |
37e44c3f | 1352 | } else |
a424958a | 1353 | r = copy_tree_at(source_fd, ".", target_fd, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS, NULL); |
33973b84 | 1354 | if (r < 0) |
37e44c3f | 1355 | return log_error_errno(r, "Failed to copy '%s' to '%s' in image '%s': %m", arg_source, arg_target, arg_image); |
33973b84 | 1356 | |
37e44c3f LP |
1357 | return 0; |
1358 | } | |
33973b84 | 1359 | |
83802e9a LP |
1360 | if (is_dir) |
1361 | return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Source is a regular file, but target is not, refusing."); | |
1362 | ||
37e44c3f | 1363 | /* We area looking at a regular file */ |
83802e9a | 1364 | target_fd = openat(dfd, bn, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_EXCL, 0600); |
37e44c3f LP |
1365 | if (target_fd < 0) |
1366 | return log_error_errno(errno, "Failed to open target file '%s': %m", arg_target); | |
33973b84 | 1367 | |
f5fbe71d | 1368 | r = copy_bytes(source_fd, target_fd, UINT64_MAX, COPY_REFLINK); |
37e44c3f LP |
1369 | if (r < 0) |
1370 | return log_error_errno(r, "Failed to copy bytes from '%s' to '%s' in image '%s': %m", arg_source, arg_target, arg_image); | |
33973b84 | 1371 | |
c17cfe6e | 1372 | (void) copy_xattr(source_fd, NULL, target_fd, NULL, 0); |
37e44c3f LP |
1373 | (void) copy_access(source_fd, target_fd); |
1374 | (void) copy_times(source_fd, target_fd, 0); | |
33973b84 | 1375 | |
37e44c3f | 1376 | /* When this is a regular file we don't copy ownership! */ |
b7df8622 LP |
1377 | return 0; |
1378 | } | |
0cf16924 | 1379 | |
b7df8622 LP |
1380 | case ACTION_LIST: |
1381 | case ACTION_MTREE: { | |
254d1313 | 1382 | _cleanup_close_ int dfd = -EBADF; |
0cf16924 | 1383 | |
0cf16924 AAF |
1384 | dfd = open(mounted_dir, O_DIRECTORY|O_CLOEXEC|O_RDONLY); |
1385 | if (dfd < 0) | |
1386 | return log_error_errno(errno, "Failed to open mount directory: %m"); | |
1387 | ||
2e1f76f6 LP |
1388 | pager_open(arg_pager_flags); |
1389 | ||
db02190e LP |
1390 | if (arg_action == ACTION_LIST) |
1391 | r = recurse_dir(dfd, NULL, 0, UINT_MAX, RECURSE_DIR_SORT, list_print_item, NULL); | |
1392 | else if (arg_action == ACTION_MTREE) | |
1393 | r = recurse_dir(dfd, ".", STATX_TYPE|STATX_MODE|STATX_UID|STATX_GID|STATX_SIZE, UINT_MAX, RECURSE_DIR_SORT|RECURSE_DIR_INODE_FD|RECURSE_DIR_TOPLEVEL, mtree_print_item, NULL); | |
1394 | else | |
1395 | assert_not_reached(); | |
0cf16924 AAF |
1396 | if (r < 0) |
1397 | return log_error_errno(r, "Failed to list image: %m"); | |
b7df8622 | 1398 | return 0; |
37e44c3f | 1399 | } |
33973b84 | 1400 | |
b7df8622 LP |
1401 | default: |
1402 | assert_not_reached(); | |
1403 | } | |
37e44c3f | 1404 | } |
33973b84 | 1405 | |
ac1f1adf | 1406 | static int action_umount(const char *path) { |
254d1313 | 1407 | _cleanup_close_ int fd = -EBADF; |
040d3439 | 1408 | _cleanup_free_ char *canonical = NULL; |
ac1f1adf | 1409 | _cleanup_(loop_device_unrefp) LoopDevice *d = NULL; |
040d3439 | 1410 | _cleanup_(sd_device_unrefp) sd_device *dev = NULL; |
e8383058 | 1411 | int r; |
ac1f1adf | 1412 | |
f461a28d | 1413 | fd = chase_and_open(path, NULL, 0, O_DIRECTORY, &canonical); |
ac1f1adf DDM |
1414 | if (fd == -ENOTDIR) |
1415 | return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "'%s' is not a directory", path); | |
1416 | if (fd < 0) | |
1417 | return log_error_errno(fd, "Failed to resolve path '%s': %m", path); | |
1418 | ||
1419 | r = fd_is_mount_point(fd, NULL, 0); | |
1420 | if (r == 0) | |
1421 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "'%s' is not a mount point", canonical); | |
1422 | if (r < 0) | |
1423 | return log_error_errno(r, "Failed to determine whether '%s' is a mount point: %m", canonical); | |
1424 | ||
040d3439 | 1425 | r = block_device_new_from_fd(fd, BLOCK_DEVICE_LOOKUP_WHOLE_DISK | BLOCK_DEVICE_LOOKUP_BACKING, &dev); |
41a95b18 | 1426 | if (r < 0) { |
254d1313 | 1427 | _cleanup_close_ int usr_fd = -EBADF; |
41a95b18 YW |
1428 | |
1429 | /* The command `systemd-dissect --mount` expects that the image at least has the root or /usr | |
1430 | * partition. If it does not have the root partition, then we mount the /usr partition on a | |
1431 | * tmpfs. Hence, let's try to find the backing block device through the /usr partition. */ | |
1432 | ||
1433 | usr_fd = openat(fd, "usr", O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW); | |
1434 | if (usr_fd < 0) | |
1435 | return log_error_errno(errno, "Failed to open '%s/usr': %m", canonical); | |
1436 | ||
1437 | r = block_device_new_from_fd(usr_fd, BLOCK_DEVICE_LOOKUP_WHOLE_DISK | BLOCK_DEVICE_LOOKUP_BACKING, &dev); | |
1438 | } | |
ac1f1adf DDM |
1439 | if (r < 0) |
1440 | return log_error_errno(r, "Failed to find backing block device for '%s': %m", canonical); | |
1441 | ||
040d3439 | 1442 | r = loop_device_open(dev, 0, LOCK_EX, &d); |
ac1f1adf | 1443 | if (r < 0) |
040d3439 | 1444 | return log_device_error_errno(dev, r, "Failed to open loopback block device: %m"); |
ac1f1adf | 1445 | |
ac1f1adf DDM |
1446 | /* We've locked the loop device, now we're ready to unmount. To allow the unmount to succeed, we have |
1447 | * to close the O_PATH fd we opened earlier. */ | |
1448 | fd = safe_close(fd); | |
1449 | ||
1450 | r = umount_recursive(canonical, 0); | |
1451 | if (r < 0) | |
1452 | return log_error_errno(r, "Failed to unmount '%s': %m", canonical); | |
1453 | ||
0b75493d | 1454 | /* We managed to lock and unmount successfully? That means we can try to remove the loop device. */ |
ac1f1adf DDM |
1455 | loop_device_unrelinquish(d); |
1456 | ||
1457 | if (arg_rmdir) { | |
e8383058 LP |
1458 | r = RET_NERRNO(rmdir(canonical)); |
1459 | if (r < 0) | |
1460 | return log_error_errno(r, "Failed to remove mount directory '%s': %m", canonical); | |
ac1f1adf | 1461 | } |
ac1f1adf | 1462 | |
e8383058 | 1463 | return 0; |
ac1f1adf DDM |
1464 | } |
1465 | ||
06186c4c LP |
1466 | static int action_with(DissectedImage *m, LoopDevice *d) { |
1467 | _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; | |
1468 | _cleanup_(rmdir_and_freep) char *created_dir = NULL; | |
1469 | _cleanup_free_ char *temp = NULL; | |
1470 | int r, rcode; | |
1471 | ||
94abea2a LP |
1472 | assert(m); |
1473 | assert(d); | |
1474 | assert(arg_action == ACTION_WITH); | |
06186c4c LP |
1475 | |
1476 | r = tempfn_random_child(NULL, program_invocation_short_name, &temp); | |
1477 | if (r < 0) | |
1478 | return log_error_errno(r, "Failed to generate temporary mount directory: %m"); | |
1479 | ||
1480 | r = mkdir_p(temp, 0700); | |
1481 | if (r < 0) | |
1482 | return log_error_errno(r, "Failed to create mount point: %m"); | |
1483 | ||
1484 | created_dir = TAKE_PTR(temp); | |
1485 | ||
1486 | r = dissected_image_mount_and_warn(m, created_dir, UID_INVALID, UID_INVALID, arg_flags); | |
1487 | if (r < 0) | |
1488 | return r; | |
1489 | ||
1490 | mounted_dir = TAKE_PTR(created_dir); | |
1491 | ||
1492 | r = dissected_image_relinquish(m); | |
1493 | if (r < 0) | |
1494 | return log_error_errno(r, "Failed to relinquish DM and loopback block devices: %m"); | |
1495 | ||
1496 | r = loop_device_flock(d, LOCK_UN); | |
1497 | if (r < 0) | |
1498 | return log_error_errno(r, "Failed to unlock loopback block device: %m"); | |
1499 | ||
1500 | rcode = safe_fork("(with)", FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT, NULL); | |
1501 | if (rcode == 0) { | |
1502 | /* Child */ | |
1503 | ||
1504 | if (chdir(mounted_dir) < 0) { | |
1505 | log_error_errno(errno, "Failed to change to '%s' directory: %m", mounted_dir); | |
1506 | _exit(EXIT_FAILURE); | |
1507 | } | |
1508 | ||
1509 | if (setenv("SYSTEMD_DISSECT_ROOT", mounted_dir, /* overwrite= */ true) < 0) { | |
1510 | log_error_errno(errno, "Failed to set $SYSTEMD_DISSECT_ROOT: %m"); | |
1511 | _exit(EXIT_FAILURE); | |
1512 | } | |
1513 | ||
1514 | if (strv_isempty(arg_argv)) { | |
1515 | const char *sh; | |
1516 | ||
1517 | sh = secure_getenv("SHELL"); | |
1518 | if (sh) { | |
1519 | execvp(sh, STRV_MAKE(sh)); | |
1520 | log_warning_errno(errno, "Failed to execute $SHELL, falling back to /bin/sh: %m"); | |
1521 | } | |
1522 | ||
1523 | execl("/bin/sh", "sh", NULL); | |
1524 | log_error_errno(errno, "Failed to invoke /bin/sh: %m"); | |
1525 | } else { | |
1526 | execvp(arg_argv[0], arg_argv); | |
1527 | log_error_errno(errno, "Failed to execute '%s': %m", arg_argv[0]); | |
1528 | } | |
1529 | ||
1530 | _exit(EXIT_FAILURE); | |
1531 | } | |
1532 | ||
1533 | /* Let's manually detach everything, to make things synchronous */ | |
1534 | r = loop_device_flock(d, LOCK_SH); | |
1535 | if (r < 0) | |
1536 | log_warning_errno(r, "Failed to lock loopback block device, ignoring: %m"); | |
1537 | ||
1538 | r = umount_recursive(mounted_dir, 0); | |
1539 | if (r < 0) | |
1540 | log_warning_errno(r, "Failed to unmount '%s', ignoring: %m", mounted_dir); | |
1541 | else | |
1542 | loop_device_unrelinquish(d); /* Let's try to destroy the loopback device */ | |
1543 | ||
1544 | created_dir = TAKE_PTR(mounted_dir); | |
1545 | ||
1546 | if (rmdir(created_dir) < 0) | |
1547 | log_warning_errno(r, "Failed to remove directory '%s', ignoring: %m", created_dir); | |
1548 | ||
1549 | temp = TAKE_PTR(created_dir); | |
1550 | ||
1551 | return rcode; | |
1552 | } | |
1553 | ||
0305cf6e LP |
1554 | static int action_discover(void) { |
1555 | _cleanup_(hashmap_freep) Hashmap *images = NULL; | |
1556 | _cleanup_(table_unrefp) Table *t = NULL; | |
1557 | Image *img; | |
1558 | int r; | |
1559 | ||
1560 | images = hashmap_new(&image_hash_ops); | |
1561 | if (!images) | |
1562 | return log_oom(); | |
1563 | ||
1564 | for (ImageClass cl = 0; cl < _IMAGE_CLASS_MAX; cl++) { | |
1565 | r = image_discover(cl, NULL, images); | |
1566 | if (r < 0) | |
1567 | return log_error_errno(r, "Failed to discover images: %m"); | |
1568 | } | |
1569 | ||
1570 | if ((arg_json_format_flags & JSON_FORMAT_OFF) && hashmap_isempty(images)) { | |
1571 | log_info("No images found."); | |
1572 | return 0; | |
1573 | } | |
1574 | ||
1575 | t = table_new("name", "type", "class", "ro", "path", "time", "usage"); | |
1576 | if (!t) | |
1577 | return log_oom(); | |
1578 | ||
1579 | HASHMAP_FOREACH(img, images) { | |
1580 | ||
1581 | if (!IN_SET(img->type, IMAGE_RAW, IMAGE_BLOCK)) | |
1582 | continue; | |
1583 | ||
1584 | r = table_add_many( | |
1585 | t, | |
1586 | TABLE_STRING, img->name, | |
1587 | TABLE_STRING, image_type_to_string(img->type), | |
1588 | TABLE_STRING, image_class_to_string(img->class), | |
1589 | TABLE_BOOLEAN, img->read_only, | |
1590 | TABLE_PATH, img->path, | |
1591 | TABLE_TIMESTAMP, img->mtime != 0 ? img->mtime : img->crtime, | |
1592 | TABLE_SIZE, img->usage); | |
1593 | if (r < 0) | |
1594 | return table_log_add_error(r); | |
1595 | } | |
1596 | ||
1597 | (void) table_set_sort(t, (size_t) 0); | |
1598 | ||
1599 | return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend); | |
1600 | } | |
1601 | ||
07d6072e LP |
1602 | static int action_attach(DissectedImage *m, LoopDevice *d) { |
1603 | int r; | |
1604 | ||
1605 | assert(m); | |
1606 | assert(d); | |
1607 | ||
1608 | r = loop_device_set_autoclear(d, false); | |
1609 | if (r < 0) | |
1610 | return log_error_errno(r, "Failed to disable auto-clear logic on loopback device: %m"); | |
1611 | ||
1612 | r = dissected_image_relinquish(m); | |
1613 | if (r < 0) | |
1614 | return log_error_errno(r, "Failed to relinquish DM and loopback block devices: %m"); | |
1615 | ||
1616 | puts(d->node); | |
1617 | return 0; | |
1618 | } | |
1619 | ||
1620 | static int action_detach(const char *path) { | |
1621 | _cleanup_(loop_device_unrefp) LoopDevice *loop = NULL; | |
1622 | _cleanup_close_ int fd = -EBADF; | |
1623 | struct stat st; | |
1624 | int r; | |
1625 | ||
1626 | fd = open(path, O_PATH|O_CLOEXEC); | |
1627 | if (fd < 0) | |
1628 | return log_error_errno(errno, "Failed to open '%s': %m", path); | |
1629 | ||
1630 | if (fstat(fd, &st) < 0) | |
1631 | return log_error_errno(errno, "Failed to stat '%s': %m", path); | |
1632 | ||
1633 | if (S_ISBLK(st.st_mode)) { | |
1634 | r = loop_device_open_from_fd(fd, O_RDONLY, LOCK_EX, &loop); | |
1635 | if (r < 0) | |
1636 | return log_error_errno(r, "Failed to open '%s' as loopback block device: %m", path); | |
1637 | ||
1638 | } else if (S_ISREG(st.st_mode)) { | |
1639 | _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; | |
1640 | sd_device *d; | |
1641 | ||
1642 | /* If a regular file is specified, search for a loopback block device that is backed by it */ | |
1643 | ||
1644 | r = sd_device_enumerator_new(&e); | |
1645 | if (r < 0) | |
1646 | return log_error_errno(r, "Failed to allocate enumerator: %m"); | |
1647 | ||
1648 | r = sd_device_enumerator_add_match_subsystem(e, "block", true); | |
1649 | if (r < 0) | |
1650 | return log_error_errno(r, "Failed to match block devices: %m"); | |
1651 | ||
1652 | r = sd_device_enumerator_add_match_sysname(e, "loop*"); | |
1653 | if (r < 0) | |
1654 | return log_error_errno(r, "Failed to match loopback block devices: %m"); | |
1655 | ||
1656 | (void) sd_device_enumerator_allow_uninitialized(e); | |
1657 | ||
1658 | FOREACH_DEVICE(e, d) { | |
1659 | _cleanup_(loop_device_unrefp) LoopDevice *entry_loop = NULL; | |
1660 | const char *name, *devtype; | |
1661 | ||
1662 | r = sd_device_get_sysname(d, &name); | |
1663 | if (r < 0) { | |
1664 | log_warning_errno(r, "Failed to get enumerated device's sysname, skipping: %m"); | |
1665 | continue; | |
1666 | } | |
1667 | ||
1668 | r = sd_device_get_devtype(d, &devtype); | |
1669 | if (r < 0) { | |
1670 | log_warning_errno(r, "Failed to get devtype of '%s', skipping: %m", name); | |
1671 | continue; | |
1672 | } | |
1673 | ||
1674 | if (!streq(devtype, "disk")) /* Filter out partition block devices */ | |
1675 | continue; | |
1676 | ||
1677 | r = loop_device_open(d, O_RDONLY, LOCK_SH, &entry_loop); | |
1678 | if (r < 0) { | |
1679 | log_warning_errno(r, "Failed to open loopback block device '%s', skipping: %m", name); | |
1680 | continue; | |
1681 | } | |
1682 | ||
1683 | if (entry_loop->backing_devno == st.st_dev && entry_loop->backing_inode == st.st_ino) { | |
1684 | /* Found it! The kernel allows attaching a single file to multiple loopback | |
1685 | * devices. Let's destruct them in reverse order, i.e. find the last matching | |
1686 | * loopback device here, rather than the first. */ | |
1687 | ||
1688 | loop_device_unref(loop); | |
1689 | loop = TAKE_PTR(entry_loop); | |
1690 | } | |
1691 | } | |
1692 | ||
1693 | if (!loop) | |
1694 | return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "No loopback block device backed by '%s' found.", path); | |
1695 | ||
1696 | r = loop_device_flock(loop, LOCK_EX); | |
1697 | if (r < 0) | |
1698 | return log_error_errno(r, "Failed to upgrade device lock: %m"); | |
1699 | } | |
1700 | ||
1701 | r = loop_device_set_autoclear(loop, true); | |
1702 | if (r < 0) | |
1703 | log_warning_errno(r, "Failed to enable autoclear logic on '%s', ignoring: %m", loop->node); | |
1704 | ||
1705 | loop_device_unrelinquish(loop); | |
1706 | return 0; | |
1707 | } | |
1708 | ||
37e44c3f LP |
1709 | static int run(int argc, char *argv[]) { |
1710 | _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL; | |
1711 | _cleanup_(loop_device_unrefp) LoopDevice *d = NULL; | |
6c07d570 LP |
1712 | uint32_t loop_flags; |
1713 | int open_flags, r; | |
33973b84 | 1714 | |
5acb31a6 | 1715 | log_setup(); |
33973b84 | 1716 | |
a164d9d5 LP |
1717 | if (invoked_as(argv, "mount.ddi")) |
1718 | r = parse_argv_as_mount_helper(argc, argv); | |
1719 | else | |
1720 | r = parse_argv(argc, argv); | |
37e44c3f LP |
1721 | if (r <= 0) |
1722 | return r; | |
33973b84 | 1723 | |
b7df8622 LP |
1724 | switch (arg_action) { |
1725 | case ACTION_UMOUNT: | |
ac1f1adf | 1726 | return action_umount(arg_path); |
b7df8622 LP |
1727 | |
1728 | case ACTION_DETACH: | |
07d6072e | 1729 | return action_detach(arg_image); |
b7df8622 LP |
1730 | |
1731 | case ACTION_DISCOVER: | |
0305cf6e | 1732 | return action_discover(); |
ac1f1adf | 1733 | |
b7df8622 LP |
1734 | default: |
1735 | /* All other actions need the image dissected */ | |
1736 | break; | |
1737 | } | |
1738 | ||
89e62e0b LP |
1739 | r = verity_settings_load( |
1740 | &arg_verity_settings, | |
1741 | arg_image, NULL, NULL); | |
37e44c3f LP |
1742 | if (r < 0) |
1743 | return log_error_errno(r, "Failed to read verity artifacts for %s: %m", arg_image); | |
33973b84 | 1744 | |
89e62e0b LP |
1745 | if (arg_verity_settings.data_path) |
1746 | arg_flags |= DISSECT_IMAGE_NO_PARTITION_TABLE; /* We only support Verity per file system, | |
1747 | * hence if there's external Verity data | |
1748 | * available we turn off partition table | |
1749 | * support */ | |
1750 | ||
6c07d570 LP |
1751 | open_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR; |
1752 | loop_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN; | |
1753 | ||
1754 | if (arg_in_memory) | |
22ee78a8 | 1755 | r = loop_device_make_by_path_memory(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d); |
6c07d570 | 1756 | else |
22ee78a8 | 1757 | r = loop_device_make_by_path(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d); |
37e44c3f | 1758 | if (r < 0) |
7b87fe4c | 1759 | return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image); |
33973b84 | 1760 | |
236d1fa2 LP |
1761 | if (arg_loop_ref) { |
1762 | r = loop_device_set_filename(d, arg_loop_ref); | |
1763 | if (r < 0) | |
1764 | log_warning_errno(r, "Failed to set loop reference string to '%s', ignoring: %m", arg_loop_ref); | |
1765 | } | |
1766 | ||
bad31660 | 1767 | r = dissect_loop_device_and_warn( |
bad31660 | 1768 | d, |
89e62e0b | 1769 | &arg_verity_settings, |
84be0c71 LP |
1770 | /* mount_options= */ NULL, |
1771 | arg_image_policy, | |
37e44c3f LP |
1772 | arg_flags, |
1773 | &m); | |
1774 | if (r < 0) | |
1775 | return r; | |
33973b84 | 1776 | |
07d6072e LP |
1777 | if (arg_action == ACTION_ATTACH) |
1778 | return action_attach(m, d); | |
1779 | ||
88b3300f LP |
1780 | r = dissected_image_load_verity_sig_partition( |
1781 | m, | |
1782 | d->fd, | |
1783 | &arg_verity_settings); | |
1784 | if (r < 0) | |
738edfe6 | 1785 | return log_error_errno(r, "Failed to load verity signature partition: %m"); |
88b3300f | 1786 | |
94abea2a LP |
1787 | if (arg_action != ACTION_DISSECT) { |
1788 | r = dissected_image_decrypt_interactively( | |
1789 | m, NULL, | |
1790 | &arg_verity_settings, | |
1791 | arg_flags); | |
1792 | if (r < 0) | |
1793 | return r; | |
1794 | } | |
1795 | ||
37e44c3f | 1796 | switch (arg_action) { |
33973b84 | 1797 | |
37e44c3f | 1798 | case ACTION_DISSECT: |
6f5ef9e4 | 1799 | return action_dissect(m, d); |
37e44c3f LP |
1800 | |
1801 | case ACTION_MOUNT: | |
6f5ef9e4 | 1802 | return action_mount(m, d); |
37e44c3f | 1803 | |
0cf16924 | 1804 | case ACTION_LIST: |
db02190e | 1805 | case ACTION_MTREE: |
37e44c3f LP |
1806 | case ACTION_COPY_FROM: |
1807 | case ACTION_COPY_TO: | |
6f5ef9e4 | 1808 | return action_list_or_mtree_or_copy(m, d); |
33973b84 | 1809 | |
06186c4c | 1810 | case ACTION_WITH: |
6f5ef9e4 | 1811 | return action_with(m, d); |
06186c4c | 1812 | |
a2ea3b2f | 1813 | default: |
04499a70 | 1814 | assert_not_reached(); |
a2ea3b2f | 1815 | } |
a2ea3b2f | 1816 | } |
149afb45 YW |
1817 | |
1818 | DEFINE_MAIN_FUNCTION(run); |