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