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