1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2016 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include "architecture.h"
26 #include "dissect-image.h"
27 #include "hexdecoct.h"
29 #include "loop-util.h"
30 #include "string-util.h"
32 #include "user-util.h"
38 } arg_action
= ACTION_DISSECT
;
39 static const char *arg_image
= NULL
;
40 static const char *arg_path
= NULL
;
41 static DissectImageFlags arg_flags
= DISSECT_IMAGE_REQUIRE_ROOT
|DISSECT_IMAGE_DISCARD_ON_LOOP
;
42 static void *arg_root_hash
= NULL
;
43 static size_t arg_root_hash_size
= 0;
45 static void help(void) {
46 printf("%s [OPTIONS...] IMAGE\n"
47 "%s [OPTIONS...] --mount IMAGE PATH\n"
48 "Dissect a file system OS image.\n\n"
49 " -h --help Show this help\n"
50 " --version Show package version\n"
51 " -m --mount Mount the image to the specified directory\n"
52 " -r --read-only Mount read-only\n"
53 " --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n"
54 " --root-hash=HASH Specify root hash for verity\n",
55 program_invocation_short_name
,
56 program_invocation_short_name
);
59 static int parse_argv(int argc
, char *argv
[]) {
67 static const struct option options
[] = {
68 { "help", no_argument
, NULL
, 'h' },
69 { "version", no_argument
, NULL
, ARG_VERSION
},
70 { "mount", no_argument
, NULL
, 'm' },
71 { "read-only", no_argument
, NULL
, 'r' },
72 { "discard", required_argument
, NULL
, ARG_DISCARD
},
73 { "root-hash", required_argument
, NULL
, ARG_ROOT_HASH
},
82 while ((c
= getopt_long(argc
, argv
, "hmr", options
, NULL
)) >= 0) {
94 arg_action
= ACTION_MOUNT
;
98 arg_flags
|= DISSECT_IMAGE_READ_ONLY
;
102 DissectImageFlags flags
;
104 if (streq(optarg
, "disabled"))
106 else if (streq(optarg
, "loop"))
107 flags
= DISSECT_IMAGE_DISCARD_ON_LOOP
;
108 else if (streq(optarg
, "all"))
109 flags
= DISSECT_IMAGE_DISCARD_ON_LOOP
| DISSECT_IMAGE_DISCARD
;
110 else if (streq(optarg
, "crypt"))
111 flags
= DISSECT_IMAGE_DISCARD_ANY
;
113 log_error("Unknown --discard= parameter: %s", optarg
);
116 arg_flags
= (arg_flags
& ~DISSECT_IMAGE_DISCARD_ANY
) | flags
;
121 case ARG_ROOT_HASH
: {
125 r
= unhexmem(optarg
, strlen(optarg
), &p
, &l
);
127 return log_error_errno(r
, "Failed to parse root hash: %s", optarg
);
128 if (l
< sizeof(sd_id128_t
)) {
129 log_error("Root hash must be at least 128bit long: %s", optarg
);
136 arg_root_hash_size
= l
;
144 assert_not_reached("Unhandled option");
149 switch (arg_action
) {
152 if (optind
+ 1 != argc
) {
153 log_error("Expected a file path as only argument.");
157 arg_image
= argv
[optind
];
158 arg_flags
|= DISSECT_IMAGE_READ_ONLY
;
162 if (optind
+ 2 != argc
) {
163 log_error("Expected a file path and mount point path as only arguments.");
167 arg_image
= argv
[optind
];
168 arg_path
= argv
[optind
+ 1];
172 assert_not_reached("Unknown action.");
178 int main(int argc
, char *argv
[]) {
179 _cleanup_(loop_device_unrefp
) LoopDevice
*d
= NULL
;
180 _cleanup_(decrypted_image_unrefp
) DecryptedImage
*di
= NULL
;
181 _cleanup_(dissected_image_unrefp
) DissectedImage
*m
= NULL
;
184 log_parse_environment();
187 r
= parse_argv(argc
, argv
);
191 r
= loop_device_make_by_path(arg_image
, (arg_flags
& DISSECT_IMAGE_READ_ONLY
) ? O_RDONLY
: O_RDWR
, &d
);
193 log_error_errno(r
, "Failed to set up loopback device: %m");
197 if (!arg_root_hash
) {
198 r
= root_hash_load(arg_image
, &arg_root_hash
, &arg_root_hash_size
);
200 log_error_errno(r
, "Failed to read root hash file for %s: %m", arg_image
);
205 r
= dissect_image(d
->fd
, arg_root_hash
, arg_root_hash_size
, arg_flags
, &m
);
207 log_error_errno(r
, "Couldn't identify a suitable partition table or file system in %s.", arg_image
);
210 if (r
== -EADDRNOTAVAIL
) {
211 log_error_errno(r
, "No root partition for specified root hash found in %s.", arg_image
);
214 if (r
== -ENOTUNIQ
) {
215 log_error_errno(r
, "Multiple suitable root partitions found in image %s.", arg_image
);
219 log_error_errno(r
, "No suitable root partition found in image %s.", arg_image
);
222 if (r
== -EPROTONOSUPPORT
) {
223 log_error_errno(r
, "Device %s is loopback block device with partition scanning turned off, please turn it on.", arg_image
);
227 log_error_errno(r
, "Failed to dissect image: %m");
231 switch (arg_action
) {
233 case ACTION_DISSECT
: {
236 for (i
= 0; i
< _PARTITION_DESIGNATOR_MAX
; i
++) {
237 DissectedPartition
*p
= m
->partitions
+ i
;
243 printf("Found %s '%s' partition",
244 p
->rw
? "writable" : "read-only",
245 partition_designator_to_string(i
));
247 if (!sd_id128_is_null(p
->uuid
))
248 printf(" (UUID " SD_ID128_FORMAT_STR
")", SD_ID128_FORMAT_VAL(p
->uuid
));
251 printf(" of type %s", p
->fstype
);
253 if (p
->architecture
!= _ARCHITECTURE_INVALID
)
254 printf(" for %s", architecture_to_string(p
->architecture
));
256 k
= PARTITION_VERITY_OF(i
);
258 printf(" %s verity", m
->partitions
[k
].found
? "with" : "without");
261 printf(" on partition #%i", p
->partno
);
264 printf(" (%s)", p
->node
);
269 r
= dissected_image_acquire_metadata(m
);
271 log_error_errno(r
, "Failed to acquire image metadata: %m");
276 printf(" Hostname: %s\n", m
->hostname
);
278 if (!sd_id128_is_null(m
->machine_id
))
279 printf("Machine ID: " SD_ID128_FORMAT_STR
"\n", SD_ID128_FORMAT_VAL(m
->machine_id
));
281 if (!strv_isempty(m
->machine_info
)) {
284 STRV_FOREACH_PAIR(p
, q
, m
->machine_info
)
286 p
== m
->machine_info
? "Mach. Info:" : " ",
290 if (!strv_isempty(m
->os_release
)) {
293 STRV_FOREACH_PAIR(p
, q
, m
->os_release
)
295 p
== m
->os_release
? "OS Release:" : " ",
303 r
= dissected_image_decrypt_interactively(m
, NULL
, arg_root_hash
, arg_root_hash_size
, arg_flags
, &di
);
307 r
= dissected_image_mount(m
, arg_path
, UID_INVALID
, arg_flags
);
309 log_error_errno(r
, "Failed to mount image: %m");
314 r
= decrypted_image_relinquish(di
);
316 log_error_errno(r
, "Failed to relinquish DM devices: %m");
321 loop_device_relinquish(d
);
325 assert_not_reached("Unknown action.");
330 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;