]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/dissect/dissect.c
Add SPDX license identifiers to source files under the LGPL
[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"
31#include "util.h"
32
33static enum {
34 ACTION_DISSECT,
35 ACTION_MOUNT,
36} arg_action = ACTION_DISSECT;
37static const char *arg_image = NULL;
38static const char *arg_path = NULL;
e0f9e7bd 39static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP;
4623e8e6
LP
40static void *arg_root_hash = NULL;
41static size_t arg_root_hash_size = 0;
a2ea3b2f
LP
42
43static 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"
18b5886e 50 " -r --read-only Mount read-only\n"
4623e8e6
LP
51 " --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n"
52 " --root-hash=HASH Specify root hash for verity\n",
a2ea3b2f
LP
53 program_invocation_short_name,
54 program_invocation_short_name);
55}
56
57static int parse_argv(int argc, char *argv[]) {
58
59 enum {
60 ARG_VERSION = 0x100,
18b5886e 61 ARG_DISCARD,
4623e8e6 62 ARG_ROOT_HASH,
a2ea3b2f
LP
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' },
18b5886e 70 { "discard", required_argument, NULL, ARG_DISCARD },
4623e8e6 71 { "root-hash", required_argument, NULL, ARG_ROOT_HASH },
a2ea3b2f
LP
72 {}
73 };
74
4623e8e6 75 int c, r;
a2ea3b2f
LP
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':
18b5886e
LP
96 arg_flags |= DISSECT_IMAGE_READ_ONLY;
97 break;
98
971e2ef0
ZJS
99 case ARG_DISCARD: {
100 DissectImageFlags flags;
101
18b5886e 102 if (streq(optarg, "disabled"))
971e2ef0 103 flags = 0;
18b5886e 104 else if (streq(optarg, "loop"))
971e2ef0 105 flags = DISSECT_IMAGE_DISCARD_ON_LOOP;
18b5886e 106 else if (streq(optarg, "all"))
971e2ef0 107 flags = DISSECT_IMAGE_DISCARD_ON_LOOP | DISSECT_IMAGE_DISCARD;
18b5886e 108 else if (streq(optarg, "crypt"))
971e2ef0 109 flags = DISSECT_IMAGE_DISCARD_ANY;
18b5886e
LP
110 else {
111 log_error("Unknown --discard= parameter: %s", optarg);
112 return -EINVAL;
113 }
971e2ef0 114 arg_flags = (arg_flags & ~DISSECT_IMAGE_DISCARD_ANY) | flags;
18b5886e 115
a2ea3b2f 116 break;
971e2ef0 117 }
a2ea3b2f 118
4623e8e6
LP
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
a2ea3b2f
LP
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];
18b5886e 156 arg_flags |= DISSECT_IMAGE_READ_ONLY;
a2ea3b2f
LP
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
176int main(int argc, char *argv[]) {
177 _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
18b5886e 178 _cleanup_(decrypted_image_unrefp) DecryptedImage *di = NULL;
a2ea3b2f
LP
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
18b5886e 189 r = loop_device_make_by_path(arg_image, (arg_flags & DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR, &d);
a2ea3b2f
LP
190 if (r < 0) {
191 log_error_errno(r, "Failed to set up loopback device: %m");
192 goto finish;
193 }
194
78ebe980
LP
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
e0f9e7bd 203 r = dissect_image(d->fd, arg_root_hash, arg_root_hash_size, arg_flags, &m);
a2ea3b2f
LP
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 }
4623e8e6
LP
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 }
94c4c622
LP
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 }
759aaedc
LP
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 }
a2ea3b2f
LP
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;
4623e8e6 236 int k;
a2ea3b2f
LP
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
be30ad41
LP
245 if (!sd_id128_is_null(p->uuid))
246 printf(" (UUID " SD_ID128_FORMAT_STR ")", SD_ID128_FORMAT_VAL(p->uuid));
247
a2ea3b2f
LP
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
4623e8e6
LP
254 k = PARTITION_VERITY_OF(i);
255 if (k >= 0)
256 printf(" %s verity", m->partitions[k].found ? "with" : "without");
257
a2ea3b2f
LP
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:
4623e8e6 271 r = dissected_image_decrypt_interactively(m, NULL, arg_root_hash, arg_root_hash_size, arg_flags, &di);
18b5886e
LP
272 if (r < 0)
273 goto finish;
274
275 r = dissected_image_mount(m, arg_path, arg_flags);
a2ea3b2f
LP
276 if (r < 0) {
277 log_error_errno(r, "Failed to mount image: %m");
278 goto finish;
279 }
280
18b5886e
LP
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
a2ea3b2f
LP
289 loop_device_relinquish(d);
290 break;
291
292 default:
293 assert_not_reached("Unknown action.");
294 }
295
296finish:
4623e8e6 297 free(arg_root_hash);
a2ea3b2f
LP
298 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
299}