]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/dissect/dissect.c
dissect: update dissect tool to show image metadata
[thirdparty/systemd.git] / src / dissect / dissect.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
a2ea3b2f
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2016 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
21#include <fcntl.h>
22#include <stdio.h>
23#include <getopt.h>
24
25#include "architecture.h"
26#include "dissect-image.h"
4623e8e6 27#include "hexdecoct.h"
a2ea3b2f
LP
28#include "log.h"
29#include "loop-util.h"
30#include "string-util.h"
a1edd22e 31#include "strv.h"
a2ea3b2f
LP
32#include "util.h"
33
34static enum {
35 ACTION_DISSECT,
36 ACTION_MOUNT,
37} arg_action = ACTION_DISSECT;
38static const char *arg_image = NULL;
39static const char *arg_path = NULL;
e0f9e7bd 40static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP;
4623e8e6
LP
41static void *arg_root_hash = NULL;
42static size_t arg_root_hash_size = 0;
a2ea3b2f
LP
43
44static void help(void) {
45 printf("%s [OPTIONS...] IMAGE\n"
46 "%s [OPTIONS...] --mount IMAGE PATH\n"
47 "Dissect a file system OS image.\n\n"
48 " -h --help Show this help\n"
49 " --version Show package version\n"
50 " -m --mount Mount the image to the specified directory\n"
18b5886e 51 " -r --read-only Mount read-only\n"
4623e8e6
LP
52 " --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n"
53 " --root-hash=HASH Specify root hash for verity\n",
a2ea3b2f
LP
54 program_invocation_short_name,
55 program_invocation_short_name);
56}
57
58static int parse_argv(int argc, char *argv[]) {
59
60 enum {
61 ARG_VERSION = 0x100,
18b5886e 62 ARG_DISCARD,
4623e8e6 63 ARG_ROOT_HASH,
a2ea3b2f
LP
64 };
65
66 static const struct option options[] = {
67 { "help", no_argument, NULL, 'h' },
68 { "version", no_argument, NULL, ARG_VERSION },
69 { "mount", no_argument, NULL, 'm' },
70 { "read-only", no_argument, NULL, 'r' },
18b5886e 71 { "discard", required_argument, NULL, ARG_DISCARD },
4623e8e6 72 { "root-hash", required_argument, NULL, ARG_ROOT_HASH },
a2ea3b2f
LP
73 {}
74 };
75
4623e8e6 76 int c, r;
a2ea3b2f
LP
77
78 assert(argc >= 0);
79 assert(argv);
80
81 while ((c = getopt_long(argc, argv, "hmr", options, NULL)) >= 0) {
82
83 switch (c) {
84
85 case 'h':
86 help();
87 return 0;
88
89 case ARG_VERSION:
90 return version();
91
92 case 'm':
93 arg_action = ACTION_MOUNT;
94 break;
95
96 case 'r':
18b5886e
LP
97 arg_flags |= DISSECT_IMAGE_READ_ONLY;
98 break;
99
971e2ef0
ZJS
100 case ARG_DISCARD: {
101 DissectImageFlags flags;
102
18b5886e 103 if (streq(optarg, "disabled"))
971e2ef0 104 flags = 0;
18b5886e 105 else if (streq(optarg, "loop"))
971e2ef0 106 flags = DISSECT_IMAGE_DISCARD_ON_LOOP;
18b5886e 107 else if (streq(optarg, "all"))
971e2ef0 108 flags = DISSECT_IMAGE_DISCARD_ON_LOOP | DISSECT_IMAGE_DISCARD;
18b5886e 109 else if (streq(optarg, "crypt"))
971e2ef0 110 flags = DISSECT_IMAGE_DISCARD_ANY;
18b5886e
LP
111 else {
112 log_error("Unknown --discard= parameter: %s", optarg);
113 return -EINVAL;
114 }
971e2ef0 115 arg_flags = (arg_flags & ~DISSECT_IMAGE_DISCARD_ANY) | flags;
18b5886e 116
a2ea3b2f 117 break;
971e2ef0 118 }
a2ea3b2f 119
4623e8e6
LP
120 case ARG_ROOT_HASH: {
121 void *p;
122 size_t l;
123
124 r = unhexmem(optarg, strlen(optarg), &p, &l);
125 if (r < 0)
126 return log_error_errno(r, "Failed to parse root hash: %s", optarg);
127 if (l < sizeof(sd_id128_t)) {
128 log_error("Root hash must be at least 128bit long: %s", optarg);
129 free(p);
130 return -EINVAL;
131 }
132
133 free(arg_root_hash);
134 arg_root_hash = p;
135 arg_root_hash_size = l;
136 break;
137 }
138
a2ea3b2f
LP
139 case '?':
140 return -EINVAL;
141
142 default:
143 assert_not_reached("Unhandled option");
144 }
145
146 }
147
148 switch (arg_action) {
149
150 case ACTION_DISSECT:
151 if (optind + 1 != argc) {
152 log_error("Expected a file path as only argument.");
153 return -EINVAL;
154 }
155
156 arg_image = argv[optind];
18b5886e 157 arg_flags |= DISSECT_IMAGE_READ_ONLY;
a2ea3b2f
LP
158 break;
159
160 case ACTION_MOUNT:
161 if (optind + 2 != argc) {
162 log_error("Expected a file path and mount point path as only arguments.");
163 return -EINVAL;
164 }
165
166 arg_image = argv[optind];
167 arg_path = argv[optind + 1];
168 break;
169
170 default:
171 assert_not_reached("Unknown action.");
172 }
173
174 return 1;
175}
176
177int main(int argc, char *argv[]) {
178 _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
18b5886e 179 _cleanup_(decrypted_image_unrefp) DecryptedImage *di = NULL;
a2ea3b2f
LP
180 _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
181 int r;
182
183 log_parse_environment();
184 log_open();
185
186 r = parse_argv(argc, argv);
187 if (r <= 0)
188 goto finish;
189
18b5886e 190 r = loop_device_make_by_path(arg_image, (arg_flags & DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR, &d);
a2ea3b2f
LP
191 if (r < 0) {
192 log_error_errno(r, "Failed to set up loopback device: %m");
193 goto finish;
194 }
195
78ebe980
LP
196 if (!arg_root_hash) {
197 r = root_hash_load(arg_image, &arg_root_hash, &arg_root_hash_size);
198 if (r < 0) {
199 log_error_errno(r, "Failed to read root hash file for %s: %m", arg_image);
200 goto finish;
201 }
202 }
203
e0f9e7bd 204 r = dissect_image(d->fd, arg_root_hash, arg_root_hash_size, arg_flags, &m);
a2ea3b2f
LP
205 if (r == -ENOPKG) {
206 log_error_errno(r, "Couldn't identify a suitable partition table or file system in %s.", arg_image);
207 goto finish;
208 }
4623e8e6
LP
209 if (r == -EADDRNOTAVAIL) {
210 log_error_errno(r, "No root partition for specified root hash found in %s.", arg_image);
211 goto finish;
212 }
94c4c622
LP
213 if (r == -ENOTUNIQ) {
214 log_error_errno(r, "Multiple suitable root partitions found in image %s.", arg_image);
215 goto finish;
216 }
217 if (r == -ENXIO) {
218 log_error_errno(r, "No suitable root partition found in image %s.", arg_image);
219 goto finish;
220 }
759aaedc
LP
221 if (r == -EPROTONOSUPPORT) {
222 log_error_errno(r, "Device %s is loopback block device with partition scanning turned off, please turn it on.", arg_image);
223 goto finish;
224 }
a2ea3b2f
LP
225 if (r < 0) {
226 log_error_errno(r, "Failed to dissect image: %m");
227 goto finish;
228 }
229
230 switch (arg_action) {
231
232 case ACTION_DISSECT: {
233 unsigned i;
234
235 for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
236 DissectedPartition *p = m->partitions + i;
4623e8e6 237 int k;
a2ea3b2f
LP
238
239 if (!p->found)
240 continue;
241
242 printf("Found %s '%s' partition",
243 p->rw ? "writable" : "read-only",
244 partition_designator_to_string(i));
245
be30ad41
LP
246 if (!sd_id128_is_null(p->uuid))
247 printf(" (UUID " SD_ID128_FORMAT_STR ")", SD_ID128_FORMAT_VAL(p->uuid));
248
a2ea3b2f
LP
249 if (p->fstype)
250 printf(" of type %s", p->fstype);
251
252 if (p->architecture != _ARCHITECTURE_INVALID)
253 printf(" for %s", architecture_to_string(p->architecture));
254
4623e8e6
LP
255 k = PARTITION_VERITY_OF(i);
256 if (k >= 0)
257 printf(" %s verity", m->partitions[k].found ? "with" : "without");
258
a2ea3b2f
LP
259 if (p->partno >= 0)
260 printf(" on partition #%i", p->partno);
261
262 if (p->node)
263 printf(" (%s)", p->node);
264
265 putchar('\n');
266 }
a1edd22e
LP
267
268 r = dissected_image_acquire_metadata(m);
269 if (r < 0) {
270 log_error_errno(r, "Failed to acquire image metadata: %m");
271 goto finish;
272 }
273
274 if (m->hostname)
275 printf(" Hostname: %s\n", m->hostname);
276
277 if (!sd_id128_is_null(m->machine_id))
278 printf("Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->machine_id));
279
280 if (!strv_isempty(m->machine_info)) {
281 char **p, **q;
282
283 STRV_FOREACH_PAIR(p, q, m->machine_info)
284 printf("%s %s=%s\n",
285 p == m->machine_info ? "Mach. Info:" : " ",
286 *p, *q);
287 }
288
289 if (!strv_isempty(m->os_release)) {
290 char **p, **q;
291
292 STRV_FOREACH_PAIR(p, q, m->os_release)
293 printf("%s %s=%s\n",
294 p == m->os_release ? "OS Release:" : " ",
295 *p, *q);
296 }
a2ea3b2f
LP
297
298 break;
299 }
300
301 case ACTION_MOUNT:
4623e8e6 302 r = dissected_image_decrypt_interactively(m, NULL, arg_root_hash, arg_root_hash_size, arg_flags, &di);
18b5886e
LP
303 if (r < 0)
304 goto finish;
305
306 r = dissected_image_mount(m, arg_path, arg_flags);
a2ea3b2f
LP
307 if (r < 0) {
308 log_error_errno(r, "Failed to mount image: %m");
309 goto finish;
310 }
311
18b5886e
LP
312 if (di) {
313 r = decrypted_image_relinquish(di);
314 if (r < 0) {
315 log_error_errno(r, "Failed to relinquish DM devices: %m");
316 goto finish;
317 }
318 }
319
a2ea3b2f
LP
320 loop_device_relinquish(d);
321 break;
322
323 default:
324 assert_not_reached("Unknown action.");
325 }
326
327finish:
4623e8e6 328 free(arg_root_hash);
a2ea3b2f
LP
329 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
330}