2016-12-02 21:32:09 +03:00
/***
This file is part of systemd .
Copyright 2016 Lennart Poettering
systemd is free software ; you can redistribute it and / or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation ; either version 2.1 of the License , or
( at your option ) any later version .
systemd is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public License
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# 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"
# include "string-util.h"
# 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
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 ;
2016-12-05 18:26:48 +03:00
else {
log_error ( " Unknown --discard= parameter: %s " , optarg ) ;
return - EINVAL ;
}
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 )
return log_error_errno ( r , " Failed to parse root hash: %s " , optarg ) ;
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 :
if ( optind + 1 ! = argc ) {
log_error ( " Expected a file path as only argument. " ) ;
return - EINVAL ;
}
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 :
if ( optind + 2 ! = argc ) {
log_error ( " Expected a file path and mount point path as only arguments. " ) ;
return - EINVAL ;
}
arg_image = argv [ optind ] ;
arg_path = argv [ optind + 1 ] ;
break ;
default :
assert_not_reached ( " Unknown action. " ) ;
}
return 1 ;
}
int main ( int argc , char * argv [ ] ) {
_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 )
goto finish ;
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 ) ;
2016-12-02 21:32:09 +03:00
if ( r < 0 ) {
log_error_errno ( r , " Failed to set up loopback device: %m " ) ;
goto finish ;
}
2016-12-15 19:38:11 +03:00
r = dissect_image ( d - > fd , arg_root_hash , arg_root_hash_size , arg_flags , & m ) ;
2016-12-02 21:32:09 +03:00
if ( r = = - ENOPKG ) {
log_error_errno ( r , " Couldn't identify a suitable partition table or file system in %s. " , arg_image ) ;
goto finish ;
}
2016-12-07 20:28:13 +03:00
if ( r = = - EADDRNOTAVAIL ) {
log_error_errno ( r , " No root partition for specified root hash found in %s. " , arg_image ) ;
goto finish ;
}
2016-12-02 21:32:09 +03:00
if ( r < 0 ) {
log_error_errno ( r , " Failed to dissect image: %m " ) ;
goto finish ;
}
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 ' ) ;
}
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 )
goto finish ;
r = dissected_image_mount ( m , arg_path , arg_flags ) ;
2016-12-02 21:32:09 +03:00
if ( r < 0 ) {
log_error_errno ( r , " Failed to mount image: %m " ) ;
goto finish ;
}
2016-12-05 18:26:48 +03:00
if ( di ) {
r = decrypted_image_relinquish ( di ) ;
if ( r < 0 ) {
log_error_errno ( r , " Failed to relinquish DM devices: %m " ) ;
goto finish ;
}
}
2016-12-02 21:32:09 +03:00
loop_device_relinquish ( d ) ;
break ;
default :
assert_not_reached ( " Unknown action. " ) ;
}
finish :
2016-12-07 20:28:13 +03:00
free ( arg_root_hash ) ;
2016-12-02 21:32:09 +03:00
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS ;
}