1 /* SPDX-License-Identifier: LGPL-2.1+ */
5 #include <linux/loop.h>
8 #include "architecture.h"
9 #include "dissect-image.h"
10 #include "hexdecoct.h"
12 #include "loop-util.h"
13 #include "main-func.h"
14 #include "parse-util.h"
15 #include "path-util.h"
16 #include "string-util.h"
18 #include "user-util.h"
24 } arg_action
= ACTION_DISSECT
;
25 static const char *arg_image
= NULL
;
26 static const char *arg_path
= NULL
;
27 static DissectImageFlags arg_flags
= DISSECT_IMAGE_REQUIRE_ROOT
|DISSECT_IMAGE_DISCARD_ON_LOOP
|DISSECT_IMAGE_RELAX_VAR_CHECK
|DISSECT_IMAGE_FSCK
;
28 static void *arg_root_hash
= NULL
;
29 static char *arg_verity_data
= NULL
;
30 static size_t arg_root_hash_size
= 0;
31 static char *arg_root_hash_sig_path
= NULL
;
32 static void *arg_root_hash_sig
= NULL
;
33 static size_t arg_root_hash_sig_size
= 0;
35 STATIC_DESTRUCTOR_REGISTER(arg_root_hash
, freep
);
36 STATIC_DESTRUCTOR_REGISTER(arg_verity_data
, freep
);
37 STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig_path
, freep
);
38 STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig
, freep
);
40 static void help(void) {
41 printf("%s [OPTIONS...] IMAGE\n"
42 "%s [OPTIONS...] --mount IMAGE PATH\n"
43 "Dissect a file system OS image.\n\n"
44 " -h --help Show this help\n"
45 " --version Show package version\n"
46 " -m --mount Mount the image to the specified directory\n"
47 " -r --read-only Mount read-only\n"
48 " --fsck=BOOL Run fsck before mounting\n"
49 " --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n"
50 " --root-hash=HASH Specify root hash for verity\n"
51 " --root-hash-sig=SIG Specify pkcs7 signature of root hash for verity\n"
52 " as a DER encoded PKCS7, either as a path to a file\n"
53 " or as an ASCII base64 encoded string prefixed by\n"
55 " --verity-data=PATH Specify data file with hash tree for verity if it is\n"
56 " not embedded in IMAGE\n",
57 program_invocation_short_name
,
58 program_invocation_short_name
);
61 static int parse_argv(int argc
, char *argv
[]) {
72 static const struct option options
[] = {
73 { "help", no_argument
, NULL
, 'h' },
74 { "version", no_argument
, NULL
, ARG_VERSION
},
75 { "mount", no_argument
, NULL
, 'm' },
76 { "read-only", no_argument
, NULL
, 'r' },
77 { "discard", required_argument
, NULL
, ARG_DISCARD
},
78 { "root-hash", required_argument
, NULL
, ARG_ROOT_HASH
},
79 { "fsck", required_argument
, NULL
, ARG_FSCK
},
80 { "verity-data", required_argument
, NULL
, ARG_VERITY_DATA
},
81 { "root-hash-sig", required_argument
, NULL
, ARG_ROOT_HASH_SIG
},
90 while ((c
= getopt_long(argc
, argv
, "hmr", options
, NULL
)) >= 0) {
102 arg_action
= ACTION_MOUNT
;
106 arg_flags
|= DISSECT_IMAGE_READ_ONLY
;
110 DissectImageFlags flags
;
112 if (streq(optarg
, "disabled"))
114 else if (streq(optarg
, "loop"))
115 flags
= DISSECT_IMAGE_DISCARD_ON_LOOP
;
116 else if (streq(optarg
, "all"))
117 flags
= DISSECT_IMAGE_DISCARD_ON_LOOP
| DISSECT_IMAGE_DISCARD
;
118 else if (streq(optarg
, "crypt"))
119 flags
= DISSECT_IMAGE_DISCARD_ANY
;
121 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
122 "Unknown --discard= parameter: %s",
124 arg_flags
= (arg_flags
& ~DISSECT_IMAGE_DISCARD_ANY
) | flags
;
129 case ARG_ROOT_HASH
: {
133 r
= unhexmem(optarg
, strlen(optarg
), &p
, &l
);
135 return log_error_errno(r
, "Failed to parse root hash '%s': %m", optarg
);
136 if (l
< sizeof(sd_id128_t
)) {
137 log_error("Root hash must be at least 128bit long: %s", optarg
);
144 arg_root_hash_size
= l
;
148 case ARG_VERITY_DATA
:
149 r
= parse_path_argument_and_warn(optarg
, false, &arg_verity_data
);
154 case ARG_ROOT_HASH_SIG
: {
157 if ((value
= startswith(optarg
, "base64:"))) {
161 r
= unbase64mem(value
, strlen(value
), &p
, &l
);
163 return log_error_errno(r
, "Failed to parse root hash signature '%s': %m", optarg
);
165 free_and_replace(arg_root_hash_sig
, p
);
166 arg_root_hash_sig_size
= l
;
167 arg_root_hash_sig_path
= mfree(arg_root_hash_sig_path
);
169 r
= parse_path_argument_and_warn(optarg
, false, &arg_root_hash_sig_path
);
172 arg_root_hash_sig
= mfree(arg_root_hash_sig
);
173 arg_root_hash_sig_size
= 0;
180 r
= parse_boolean(optarg
);
182 return log_error_errno(r
, "Failed to parse --fsck= parameter: %s", optarg
);
184 SET_FLAG(arg_flags
, DISSECT_IMAGE_FSCK
, r
);
191 assert_not_reached("Unhandled option");
196 switch (arg_action
) {
199 if (optind
+ 1 != argc
)
200 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
201 "Expected a file path as only argument.");
203 arg_image
= argv
[optind
];
204 arg_flags
|= DISSECT_IMAGE_READ_ONLY
;
208 if (optind
+ 2 != argc
)
209 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
210 "Expected a file path and mount point path as only arguments.");
212 arg_image
= argv
[optind
];
213 arg_path
= argv
[optind
+ 1];
217 assert_not_reached("Unknown action.");
223 static int run(int argc
, char *argv
[]) {
224 _cleanup_(loop_device_unrefp
) LoopDevice
*d
= NULL
;
225 _cleanup_(decrypted_image_unrefp
) DecryptedImage
*di
= NULL
;
226 _cleanup_(dissected_image_unrefp
) DissectedImage
*m
= NULL
;
229 log_parse_environment();
232 r
= parse_argv(argc
, argv
);
236 r
= loop_device_make_by_path(arg_image
, (arg_flags
& DISSECT_IMAGE_READ_ONLY
) ? O_RDONLY
: O_RDWR
, LO_FLAGS_PARTSCAN
, &d
);
238 return log_error_errno(r
, "Failed to set up loopback device: %m");
240 r
= verity_metadata_load(arg_image
, NULL
, arg_root_hash
? NULL
: &arg_root_hash
, &arg_root_hash_size
,
241 arg_verity_data
? NULL
: &arg_verity_data
,
242 arg_root_hash_sig_path
|| arg_root_hash_sig
? NULL
: &arg_root_hash_sig_path
);
244 return log_error_errno(r
, "Failed to read verity artefacts for %s: %m", arg_image
);
245 arg_flags
|= arg_verity_data
? DISSECT_IMAGE_NO_PARTITION_TABLE
: 0;
247 r
= dissect_image_and_warn(d
->fd
, arg_image
, arg_root_hash
, arg_root_hash_size
, arg_verity_data
, NULL
, arg_flags
, &m
);
251 switch (arg_action
) {
253 case ACTION_DISSECT
: {
256 for (i
= 0; i
< _PARTITION_DESIGNATOR_MAX
; i
++) {
257 DissectedPartition
*p
= m
->partitions
+ i
;
262 printf("Found %s '%s' partition",
263 p
->rw
? "writable" : "read-only",
264 partition_designator_to_string(i
));
266 if (!sd_id128_is_null(p
->uuid
))
267 printf(" (UUID " SD_ID128_FORMAT_STR
")", SD_ID128_FORMAT_VAL(p
->uuid
));
270 printf(" of type %s", p
->fstype
);
272 if (p
->architecture
!= _ARCHITECTURE_INVALID
)
273 printf(" for %s", architecture_to_string(p
->architecture
));
275 if (dissected_image_can_do_verity(m
, i
))
276 printf(" %s verity", dissected_image_has_verity(m
, i
) ? "with" : "without");
279 printf(" on partition #%i", p
->partno
);
282 printf(" (%s)", p
->node
);
287 r
= dissected_image_acquire_metadata(m
);
289 return log_error_errno(r
, "Failed to acquire image metadata: %m");
292 printf(" Hostname: %s\n", m
->hostname
);
294 if (!sd_id128_is_null(m
->machine_id
))
295 printf("Machine ID: " SD_ID128_FORMAT_STR
"\n", SD_ID128_FORMAT_VAL(m
->machine_id
));
297 if (!strv_isempty(m
->machine_info
)) {
300 STRV_FOREACH_PAIR(p
, q
, m
->machine_info
)
302 p
== m
->machine_info
? "Mach. Info:" : " ",
306 if (!strv_isempty(m
->os_release
)) {
309 STRV_FOREACH_PAIR(p
, q
, m
->os_release
)
311 p
== m
->os_release
? "OS Release:" : " ",
319 r
= dissected_image_decrypt_interactively(m
, NULL
, arg_root_hash
, arg_root_hash_size
, arg_verity_data
, arg_root_hash_sig_path
, arg_root_hash_sig
, arg_root_hash_sig_size
, arg_flags
, &di
);
323 r
= dissected_image_mount(m
, arg_path
, UID_INVALID
, arg_flags
);
325 return log_error_errno(r
, "File system check on image failed: %m");
327 return log_error_errno(r
, "Failed to mount image: %m");
330 r
= decrypted_image_relinquish(di
);
332 return log_error_errno(r
, "Failed to relinquish DM devices: %m");
335 loop_device_relinquish(d
);
339 assert_not_reached("Unknown action.");
345 DEFINE_MAIN_FUNCTION(run
);