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