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