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