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"
36 } arg_action
= ACTION_DISSECT
;
37 static const char *arg_image
= NULL
;
38 static const char *arg_path
= NULL
;
39 static DissectImageFlags arg_flags
= DISSECT_IMAGE_REQUIRE_ROOT
|DISSECT_IMAGE_DISCARD_ON_LOOP
;
40 static void *arg_root_hash
= NULL
;
41 static size_t arg_root_hash_size
= 0;
43 static void help(void) {
44 printf("%s [OPTIONS...] IMAGE\n"
45 "%s [OPTIONS...] --mount IMAGE PATH\n"
46 "Dissect a file system OS image.\n\n"
47 " -h --help Show this help\n"
48 " --version Show package version\n"
49 " -m --mount Mount the image to the specified directory\n"
50 " -r --read-only Mount read-only\n"
51 " --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n"
52 " --root-hash=HASH Specify root hash for verity\n",
53 program_invocation_short_name
,
54 program_invocation_short_name
);
57 static int parse_argv(int argc
, char *argv
[]) {
65 static const struct option options
[] = {
66 { "help", no_argument
, NULL
, 'h' },
67 { "version", no_argument
, NULL
, ARG_VERSION
},
68 { "mount", no_argument
, NULL
, 'm' },
69 { "read-only", no_argument
, NULL
, 'r' },
70 { "discard", required_argument
, NULL
, ARG_DISCARD
},
71 { "root-hash", required_argument
, NULL
, ARG_ROOT_HASH
},
80 while ((c
= getopt_long(argc
, argv
, "hmr", options
, NULL
)) >= 0) {
92 arg_action
= ACTION_MOUNT
;
96 arg_flags
|= DISSECT_IMAGE_READ_ONLY
;
100 DissectImageFlags flags
;
102 if (streq(optarg
, "disabled"))
104 else if (streq(optarg
, "loop"))
105 flags
= DISSECT_IMAGE_DISCARD_ON_LOOP
;
106 else if (streq(optarg
, "all"))
107 flags
= DISSECT_IMAGE_DISCARD_ON_LOOP
| DISSECT_IMAGE_DISCARD
;
108 else if (streq(optarg
, "crypt"))
109 flags
= DISSECT_IMAGE_DISCARD_ANY
;
111 log_error("Unknown --discard= parameter: %s", optarg
);
114 arg_flags
= (arg_flags
& ~DISSECT_IMAGE_DISCARD_ANY
) | flags
;
119 case ARG_ROOT_HASH
: {
123 r
= unhexmem(optarg
, strlen(optarg
), &p
, &l
);
125 return log_error_errno(r
, "Failed to parse root hash: %s", optarg
);
126 if (l
< sizeof(sd_id128_t
)) {
127 log_error("Root hash must be at least 128bit long: %s", optarg
);
134 arg_root_hash_size
= l
;
142 assert_not_reached("Unhandled option");
147 switch (arg_action
) {
150 if (optind
+ 1 != argc
) {
151 log_error("Expected a file path as only argument.");
155 arg_image
= argv
[optind
];
156 arg_flags
|= DISSECT_IMAGE_READ_ONLY
;
160 if (optind
+ 2 != argc
) {
161 log_error("Expected a file path and mount point path as only arguments.");
165 arg_image
= argv
[optind
];
166 arg_path
= argv
[optind
+ 1];
170 assert_not_reached("Unknown action.");
176 int main(int argc
, char *argv
[]) {
177 _cleanup_(loop_device_unrefp
) LoopDevice
*d
= NULL
;
178 _cleanup_(decrypted_image_unrefp
) DecryptedImage
*di
= NULL
;
179 _cleanup_(dissected_image_unrefp
) DissectedImage
*m
= NULL
;
182 log_parse_environment();
185 r
= parse_argv(argc
, argv
);
189 r
= loop_device_make_by_path(arg_image
, (arg_flags
& DISSECT_IMAGE_READ_ONLY
) ? O_RDONLY
: O_RDWR
, &d
);
191 log_error_errno(r
, "Failed to set up loopback device: %m");
195 if (!arg_root_hash
) {
196 r
= root_hash_load(arg_image
, &arg_root_hash
, &arg_root_hash_size
);
198 log_error_errno(r
, "Failed to read root hash file for %s: %m", arg_image
);
203 r
= dissect_image(d
->fd
, arg_root_hash
, arg_root_hash_size
, arg_flags
, &m
);
205 log_error_errno(r
, "Couldn't identify a suitable partition table or file system in %s.", arg_image
);
208 if (r
== -EADDRNOTAVAIL
) {
209 log_error_errno(r
, "No root partition for specified root hash found in %s.", arg_image
);
212 if (r
== -ENOTUNIQ
) {
213 log_error_errno(r
, "Multiple suitable root partitions found in image %s.", arg_image
);
217 log_error_errno(r
, "No suitable root partition found in image %s.", arg_image
);
220 if (r
== -EPROTONOSUPPORT
) {
221 log_error_errno(r
, "Device %s is loopback block device with partition scanning turned off, please turn it on.", arg_image
);
225 log_error_errno(r
, "Failed to dissect image: %m");
229 switch (arg_action
) {
231 case ACTION_DISSECT
: {
234 for (i
= 0; i
< _PARTITION_DESIGNATOR_MAX
; i
++) {
235 DissectedPartition
*p
= m
->partitions
+ i
;
241 printf("Found %s '%s' partition",
242 p
->rw
? "writable" : "read-only",
243 partition_designator_to_string(i
));
245 if (!sd_id128_is_null(p
->uuid
))
246 printf(" (UUID " SD_ID128_FORMAT_STR
")", SD_ID128_FORMAT_VAL(p
->uuid
));
249 printf(" of type %s", p
->fstype
);
251 if (p
->architecture
!= _ARCHITECTURE_INVALID
)
252 printf(" for %s", architecture_to_string(p
->architecture
));
254 k
= PARTITION_VERITY_OF(i
);
256 printf(" %s verity", m
->partitions
[k
].found
? "with" : "without");
259 printf(" on partition #%i", p
->partno
);
262 printf(" (%s)", p
->node
);
271 r
= dissected_image_decrypt_interactively(m
, NULL
, arg_root_hash
, arg_root_hash_size
, arg_flags
, &di
);
275 r
= dissected_image_mount(m
, arg_path
, arg_flags
);
277 log_error_errno(r
, "Failed to mount image: %m");
282 r
= decrypted_image_relinquish(di
);
284 log_error_errno(r
, "Failed to relinquish DM devices: %m");
289 loop_device_relinquish(d
);
293 assert_not_reached("Unknown action.");
298 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;