Branch data 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 : 16 : STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep);
29 : :
30 : 12 : static void help(void) {
31 : 12 : 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 : 12 : }
43 : :
44 : 16 : 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 [ - + ]: 16 : assert(argc >= 0);
65 [ - + ]: 16 : assert(argv);
66 : :
67 [ + - ]: 16 : while ((c = getopt_long(argc, argv, "hmr", options, NULL)) >= 0) {
68 : :
69 [ + - - - : 16 : switch (c) {
- - + - ]
70 : :
71 : 12 : case 'h':
72 : 12 : help();
73 : 12 : 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 : 4 : case '?':
126 : 4 : 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 : 16 : static int run(int argc, char *argv[]) {
162 : 16 : _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
163 : 16 : _cleanup_(decrypted_image_unrefp) DecryptedImage *di = NULL;
164 : 16 : _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
165 : : int r;
166 : :
167 : 16 : log_parse_environment();
168 : 16 : log_open();
169 : :
170 : 16 : r = parse_argv(argc, argv);
171 [ + - ]: 16 : if (r <= 0)
172 : 16 : 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 : 16 : DEFINE_MAIN_FUNCTION(run);
|