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