2017-11-18 19:09:20 +03:00
/* SPDX-License-Identifier: LGPL-2.1+ */
2016-12-02 21:32:09 +03:00
# include <fcntl.h>
# include <stdio.h>
# include <getopt.h>
# include "architecture.h"
# include "dissect-image.h"
2016-12-07 20:28:13 +03:00
# include "hexdecoct.h"
2016-12-02 21:32:09 +03:00
# include "log.h"
# include "loop-util.h"
2018-11-20 11:46:14 +03:00
# include "main-func.h"
2016-12-02 21:32:09 +03:00
# include "string-util.h"
2017-11-15 01:24:13 +03:00
# include "strv.h"
2017-11-28 18:46:26 +03:00
# include "user-util.h"
2016-12-02 21:32:09 +03:00
# include "util.h"
static enum {
ACTION_DISSECT ,
ACTION_MOUNT ,
} arg_action = ACTION_DISSECT ;
static const char * arg_image = NULL ;
static const char * arg_path = NULL ;
2016-12-15 19:38:11 +03:00
static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT | DISSECT_IMAGE_DISCARD_ON_LOOP ;
2016-12-07 20:28:13 +03:00
static void * arg_root_hash = NULL ;
static size_t arg_root_hash_size = 0 ;
2016-12-02 21:32:09 +03:00
2018-11-20 11:46:14 +03:00
STATIC_DESTRUCTOR_REGISTER ( arg_root_hash , freep ) ;
2016-12-02 21:32:09 +03:00
static void help ( void ) {
printf ( " %s [OPTIONS...] IMAGE \n "
" %s [OPTIONS...] --mount IMAGE PATH \n "
" Dissect a file system OS image. \n \n "
" -h --help Show this help \n "
" --version Show package version \n "
" -m --mount Mount the image to the specified directory \n "
2016-12-05 18:26:48 +03:00
" -r --read-only Mount read-only \n "
2016-12-07 20:28:13 +03:00
" --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto) \n "
" --root-hash=HASH Specify root hash for verity \n " ,
2016-12-02 21:32:09 +03:00
program_invocation_short_name ,
program_invocation_short_name ) ;
}
static int parse_argv ( int argc , char * argv [ ] ) {
enum {
ARG_VERSION = 0x100 ,
2016-12-05 18:26:48 +03:00
ARG_DISCARD ,
2016-12-07 20:28:13 +03:00
ARG_ROOT_HASH ,
2016-12-02 21:32:09 +03:00
} ;
static const struct option options [ ] = {
{ " help " , no_argument , NULL , ' h ' } ,
{ " version " , no_argument , NULL , ARG_VERSION } ,
{ " mount " , no_argument , NULL , ' m ' } ,
{ " read-only " , no_argument , NULL , ' r ' } ,
2016-12-05 18:26:48 +03:00
{ " discard " , required_argument , NULL , ARG_DISCARD } ,
2016-12-07 20:28:13 +03:00
{ " root-hash " , required_argument , NULL , ARG_ROOT_HASH } ,
2016-12-02 21:32:09 +03:00
{ }
} ;
2016-12-07 20:28:13 +03:00
int c , r ;
2016-12-02 21:32:09 +03:00
assert ( argc > = 0 ) ;
assert ( argv ) ;
while ( ( c = getopt_long ( argc , argv , " hmr " , options , NULL ) ) > = 0 ) {
switch ( c ) {
case ' h ' :
help ( ) ;
return 0 ;
case ARG_VERSION :
return version ( ) ;
case ' m ' :
arg_action = ACTION_MOUNT ;
break ;
case ' r ' :
2016-12-05 18:26:48 +03:00
arg_flags | = DISSECT_IMAGE_READ_ONLY ;
break ;
2016-12-07 23:26:11 +03:00
case ARG_DISCARD : {
DissectImageFlags flags ;
2016-12-05 18:26:48 +03:00
if ( streq ( optarg , " disabled " ) )
2016-12-07 23:26:11 +03:00
flags = 0 ;
2016-12-05 18:26:48 +03:00
else if ( streq ( optarg , " loop " ) )
2016-12-07 23:26:11 +03:00
flags = DISSECT_IMAGE_DISCARD_ON_LOOP ;
2016-12-05 18:26:48 +03:00
else if ( streq ( optarg , " all " ) )
2016-12-07 23:26:11 +03:00
flags = DISSECT_IMAGE_DISCARD_ON_LOOP | DISSECT_IMAGE_DISCARD ;
2016-12-05 18:26:48 +03:00
else if ( streq ( optarg , " crypt " ) )
2016-12-07 23:26:11 +03:00
flags = DISSECT_IMAGE_DISCARD_ANY ;
2018-11-21 01:40:44 +03:00
else
return log_error_errno ( SYNTHETIC_ERRNO ( EINVAL ) ,
" Unknown --discard= parameter: %s " ,
optarg ) ;
2016-12-07 23:26:11 +03:00
arg_flags = ( arg_flags & ~ DISSECT_IMAGE_DISCARD_ANY ) | flags ;
2016-12-05 18:26:48 +03:00
2016-12-02 21:32:09 +03:00
break ;
2016-12-07 23:26:11 +03:00
}
2016-12-02 21:32:09 +03:00
2016-12-07 20:28:13 +03:00
case ARG_ROOT_HASH : {
void * p ;
size_t l ;
r = unhexmem ( optarg , strlen ( optarg ) , & p , & l ) ;
if ( r < 0 )
2018-10-19 19:54:29 +03:00
return log_error_errno ( r , " Failed to parse root hash '%s': %m " , optarg ) ;
2016-12-07 20:28:13 +03:00
if ( l < sizeof ( sd_id128_t ) ) {
log_error ( " Root hash must be at least 128bit long: %s " , optarg ) ;
free ( p ) ;
return - EINVAL ;
}
free ( arg_root_hash ) ;
arg_root_hash = p ;
arg_root_hash_size = l ;
break ;
}
2016-12-02 21:32:09 +03:00
case ' ? ' :
return - EINVAL ;
default :
assert_not_reached ( " Unhandled option " ) ;
}
}
switch ( arg_action ) {
case ACTION_DISSECT :
2018-11-21 01:40:44 +03:00
if ( optind + 1 ! = argc )
return log_error_errno ( SYNTHETIC_ERRNO ( EINVAL ) ,
" Expected a file path as only argument. " ) ;
2016-12-02 21:32:09 +03:00
arg_image = argv [ optind ] ;
2016-12-05 18:26:48 +03:00
arg_flags | = DISSECT_IMAGE_READ_ONLY ;
2016-12-02 21:32:09 +03:00
break ;
case ACTION_MOUNT :
2018-11-21 01:40:44 +03:00
if ( optind + 2 ! = argc )
return log_error_errno ( SYNTHETIC_ERRNO ( EINVAL ) ,
" Expected a file path and mount point path as only arguments. " ) ;
2016-12-02 21:32:09 +03:00
arg_image = argv [ optind ] ;
arg_path = argv [ optind + 1 ] ;
break ;
default :
assert_not_reached ( " Unknown action. " ) ;
}
return 1 ;
}
2018-11-20 11:46:14 +03:00
static int run ( int argc , char * argv [ ] ) {
2016-12-02 21:32:09 +03:00
_cleanup_ ( loop_device_unrefp ) LoopDevice * d = NULL ;
2016-12-05 18:26:48 +03:00
_cleanup_ ( decrypted_image_unrefp ) DecryptedImage * di = NULL ;
2016-12-02 21:32:09 +03:00
_cleanup_ ( dissected_image_unrefp ) DissectedImage * m = NULL ;
int r ;
log_parse_environment ( ) ;
log_open ( ) ;
r = parse_argv ( argc , argv ) ;
if ( r < = 0 )
2018-11-20 11:46:14 +03:00
return r ;
2016-12-02 21:32:09 +03:00
2016-12-05 18:26:48 +03:00
r = loop_device_make_by_path ( arg_image , ( arg_flags & DISSECT_IMAGE_READ_ONLY ) ? O_RDONLY : O_RDWR , & d ) ;
2018-11-20 11:46:14 +03:00
if ( r < 0 )
return log_error_errno ( r , " Failed to set up loopback device: %m " ) ;
2016-12-02 21:32:09 +03:00
2016-12-23 19:10:42 +03:00
if ( ! arg_root_hash ) {
r = root_hash_load ( arg_image , & arg_root_hash , & arg_root_hash_size ) ;
2018-11-20 11:46:14 +03:00
if ( r < 0 )
return log_error_errno ( r , " Failed to read root hash file for %s: %m " , arg_image ) ;
2016-12-23 19:10:42 +03:00
}
2018-03-21 14:10:01 +03:00
r = dissect_image_and_warn ( d - > fd , arg_image , arg_root_hash , arg_root_hash_size , arg_flags , & m ) ;
if ( r < 0 )
2018-11-20 11:46:14 +03:00
return r ;
2016-12-02 21:32:09 +03:00
switch ( arg_action ) {
case ACTION_DISSECT : {
unsigned i ;
for ( i = 0 ; i < _PARTITION_DESIGNATOR_MAX ; i + + ) {
DissectedPartition * p = m - > partitions + i ;
2016-12-07 20:28:13 +03:00
int k ;
2016-12-02 21:32:09 +03:00
if ( ! p - > found )
continue ;
printf ( " Found %s '%s' partition " ,
p - > rw ? " writable " : " read-only " ,
partition_designator_to_string ( i ) ) ;
2016-12-15 19:17:43 +03:00
if ( ! sd_id128_is_null ( p - > uuid ) )
printf ( " (UUID " SD_ID128_FORMAT_STR " ) " , SD_ID128_FORMAT_VAL ( p - > uuid ) ) ;
2016-12-02 21:32:09 +03:00
if ( p - > fstype )
printf ( " of type %s " , p - > fstype ) ;
if ( p - > architecture ! = _ARCHITECTURE_INVALID )
printf ( " for %s " , architecture_to_string ( p - > architecture ) ) ;
2016-12-07 20:28:13 +03:00
k = PARTITION_VERITY_OF ( i ) ;
if ( k > = 0 )
printf ( " %s verity " , m - > partitions [ k ] . found ? " with " : " without " ) ;
2016-12-02 21:32:09 +03:00
if ( p - > partno > = 0 )
printf ( " on partition #%i " , p - > partno ) ;
if ( p - > node )
printf ( " (%s) " , p - > node ) ;
putchar ( ' \n ' ) ;
}
2017-11-15 01:24:13 +03:00
r = dissected_image_acquire_metadata ( m ) ;
2018-11-20 11:46:14 +03:00
if ( r < 0 )
return log_error_errno ( r , " Failed to acquire image metadata: %m " ) ;
2017-11-15 01:24:13 +03:00
if ( m - > hostname )
printf ( " Hostname: %s \n " , m - > hostname ) ;
if ( ! sd_id128_is_null ( m - > machine_id ) )
printf ( " Machine ID: " SD_ID128_FORMAT_STR " \n " , SD_ID128_FORMAT_VAL ( m - > machine_id ) ) ;
if ( ! strv_isempty ( m - > machine_info ) ) {
char * * p , * * q ;
STRV_FOREACH_PAIR ( p , q , m - > machine_info )
printf ( " %s %s=%s \n " ,
p = = m - > machine_info ? " Mach. Info: " : " " ,
* p , * q ) ;
}
if ( ! strv_isempty ( m - > os_release ) ) {
char * * p , * * q ;
STRV_FOREACH_PAIR ( p , q , m - > os_release )
printf ( " %s %s=%s \n " ,
p = = m - > os_release ? " OS Release: " : " " ,
* p , * q ) ;
}
2016-12-02 21:32:09 +03:00
break ;
}
case ACTION_MOUNT :
2016-12-07 20:28:13 +03:00
r = dissected_image_decrypt_interactively ( m , NULL , arg_root_hash , arg_root_hash_size , arg_flags , & di ) ;
2016-12-05 18:26:48 +03:00
if ( r < 0 )
2018-11-20 11:46:14 +03:00
return r ;
2016-12-05 18:26:48 +03:00
2017-11-28 18:46:26 +03:00
r = dissected_image_mount ( m , arg_path , UID_INVALID , arg_flags ) ;
2018-11-20 11:46:14 +03:00
if ( r < 0 )
return log_error_errno ( r , " Failed to mount image: %m " ) ;
2016-12-02 21:32:09 +03:00
2016-12-05 18:26:48 +03:00
if ( di ) {
r = decrypted_image_relinquish ( di ) ;
2018-11-20 11:46:14 +03:00
if ( r < 0 )
return log_error_errno ( r , " Failed to relinquish DM devices: %m " ) ;
2016-12-05 18:26:48 +03:00
}
2016-12-02 21:32:09 +03:00
loop_device_relinquish ( d ) ;
break ;
default :
assert_not_reached ( " Unknown action. " ) ;
}
2018-11-20 11:46:14 +03:00
return 0 ;
2016-12-02 21:32:09 +03:00
}
2018-11-20 11:46:14 +03:00
DEFINE_MAIN_FUNCTION ( run ) ;