@ -28,6 +28,7 @@
# include "qemu-common.h"
# include "qemu-version.h"
# include "qapi/error.h"
# include "qapi/qapi-commands-block-core.h"
# include "qapi/qapi-visit-block-core.h"
# include "qapi/qobject-output-visitor.h"
# include "qapi/qmp/qjson.h"
@ -71,6 +72,12 @@ enum {
OPTION_SHRINK = 266 ,
OPTION_SALVAGE = 267 ,
OPTION_TARGET_IS_ZERO = 268 ,
OPTION_ADD = 269 ,
OPTION_REMOVE = 270 ,
OPTION_CLEAR = 271 ,
OPTION_ENABLE = 272 ,
OPTION_DISABLE = 273 ,
OPTION_MERGE = 274 ,
} ;
typedef enum OutputFormat {
@ -169,6 +176,14 @@ static void QEMU_NORETURN help(void)
" '-n' skips the target volume creation (useful if the volume is created \n "
" prior to running qemu-img) \n "
" \n "
" Parameters to bitmap subcommand: \n "
" 'bitmap' is the name of the bitmap to manipulate, through one or more \n "
" actions from '--add', '--remove', '--clear', '--enable', '--disable', \n "
" or '--merge source' \n "
" '-g granularity' sets the granularity for '--add' actions \n "
" '-b source' and '-F src_fmt' tell '--merge' actions to find the source \n "
" bitmaps from an alternative file \n "
" \n "
" Parameters to check subcommand: \n "
" '-r' tries to repair any inconsistencies that are found during the check. \n "
" '-r leaks' repairs only cluster leaks, whereas '-r all' fixes all \n "
@ -4502,6 +4517,239 @@ out:
return 0 ;
}
enum ImgBitmapAct {
BITMAP_ADD ,
BITMAP_REMOVE ,
BITMAP_CLEAR ,
BITMAP_ENABLE ,
BITMAP_DISABLE ,
BITMAP_MERGE ,
} ;
typedef struct ImgBitmapAction {
enum ImgBitmapAct act ;
const char * src ; /* only used for merge */
QSIMPLEQ_ENTRY ( ImgBitmapAction ) next ;
} ImgBitmapAction ;
static int img_bitmap ( int argc , char * * argv )
{
Error * err = NULL ;
int c , ret = 1 ;
QemuOpts * opts = NULL ;
const char * fmt = NULL , * src_fmt = NULL , * src_filename = NULL ;
const char * filename , * bitmap ;
BlockBackend * blk = NULL , * src = NULL ;
BlockDriverState * bs = NULL , * src_bs = NULL ;
bool image_opts = false ;
int64_t granularity = 0 ;
bool add = false , merge = false ;
QSIMPLEQ_HEAD ( , ImgBitmapAction ) actions ;
ImgBitmapAction * act , * act_next ;
const char * op ;
QSIMPLEQ_INIT ( & actions ) ;
for ( ; ; ) {
static const struct option long_options [ ] = {
{ " help " , no_argument , 0 , ' h ' } ,
{ " object " , required_argument , 0 , OPTION_OBJECT } ,
{ " image-opts " , no_argument , 0 , OPTION_IMAGE_OPTS } ,
{ " add " , no_argument , 0 , OPTION_ADD } ,
{ " remove " , no_argument , 0 , OPTION_REMOVE } ,
{ " clear " , no_argument , 0 , OPTION_CLEAR } ,
{ " enable " , no_argument , 0 , OPTION_ENABLE } ,
{ " disable " , no_argument , 0 , OPTION_DISABLE } ,
{ " merge " , required_argument , 0 , OPTION_MERGE } ,
{ " granularity " , required_argument , 0 , ' g ' } ,
{ " source-file " , required_argument , 0 , ' b ' } ,
{ " source-format " , required_argument , 0 , ' F ' } ,
{ 0 , 0 , 0 , 0 }
} ;
c = getopt_long ( argc , argv , " :b:f:F:g:h " , long_options , NULL ) ;
if ( c = = - 1 ) {
break ;
}
switch ( c ) {
case ' : ' :
missing_argument ( argv [ optind - 1 ] ) ;
break ;
case ' ? ' :
unrecognized_option ( argv [ optind - 1 ] ) ;
break ;
case ' h ' :
help ( ) ;
break ;
case ' b ' :
src_filename = optarg ;
break ;
case ' f ' :
fmt = optarg ;
break ;
case ' F ' :
src_fmt = optarg ;
break ;
case ' g ' :
granularity = cvtnum ( " granularity " , optarg ) ;
if ( granularity < 0 ) {
return 1 ;
}
break ;
case OPTION_ADD :
act = g_new0 ( ImgBitmapAction , 1 ) ;
act - > act = BITMAP_ADD ;
QSIMPLEQ_INSERT_TAIL ( & actions , act , next ) ;
add = true ;
break ;
case OPTION_REMOVE :
act = g_new0 ( ImgBitmapAction , 1 ) ;
act - > act = BITMAP_REMOVE ;
QSIMPLEQ_INSERT_TAIL ( & actions , act , next ) ;
break ;
case OPTION_CLEAR :
act = g_new0 ( ImgBitmapAction , 1 ) ;
act - > act = BITMAP_CLEAR ;
QSIMPLEQ_INSERT_TAIL ( & actions , act , next ) ;
break ;
case OPTION_ENABLE :
act = g_new0 ( ImgBitmapAction , 1 ) ;
act - > act = BITMAP_ENABLE ;
QSIMPLEQ_INSERT_TAIL ( & actions , act , next ) ;
break ;
case OPTION_DISABLE :
act = g_new0 ( ImgBitmapAction , 1 ) ;
act - > act = BITMAP_DISABLE ;
QSIMPLEQ_INSERT_TAIL ( & actions , act , next ) ;
break ;
case OPTION_MERGE :
act = g_new0 ( ImgBitmapAction , 1 ) ;
act - > act = BITMAP_MERGE ;
act - > src = optarg ;
QSIMPLEQ_INSERT_TAIL ( & actions , act , next ) ;
merge = true ;
break ;
case OPTION_OBJECT :
opts = qemu_opts_parse_noisily ( & qemu_object_opts , optarg , true ) ;
if ( ! opts ) {
goto out ;
}
break ;
case OPTION_IMAGE_OPTS :
image_opts = true ;
break ;
}
}
if ( qemu_opts_foreach ( & qemu_object_opts ,
user_creatable_add_opts_foreach ,
qemu_img_object_print_help , & error_fatal ) ) {
goto out ;
}
if ( QSIMPLEQ_EMPTY ( & actions ) ) {
error_report ( " Need at least one of --add, --remove, --clear, "
" --enable, --disable, or --merge " ) ;
goto out ;
}
if ( granularity & & ! add ) {
error_report ( " granularity only supported with --add " ) ;
goto out ;
}
if ( src_fmt & & ! src_filename ) {
error_report ( " -F only supported with -b " ) ;
goto out ;
}
if ( src_filename & & ! merge ) {
error_report ( " Merge bitmap source file only supported with "
" --merge " ) ;
goto out ;
}
if ( optind ! = argc - 2 ) {
error_report ( " Expecting filename and bitmap name " ) ;
goto out ;
}
filename = argv [ optind ] ;
bitmap = argv [ optind + 1 ] ;
blk = img_open ( image_opts , filename , fmt , BDRV_O_RDWR , false , false ,
false ) ;
if ( ! blk ) {
goto out ;
}
bs = blk_bs ( blk ) ;
if ( src_filename ) {
src = img_open ( false , src_filename , src_fmt , 0 , false , false , false ) ;
if ( ! src ) {
goto out ;
}
src_bs = blk_bs ( src ) ;
} else {
src_bs = bs ;
}
QSIMPLEQ_FOREACH_SAFE ( act , & actions , next , act_next ) {
switch ( act - > act ) {
case BITMAP_ADD :
qmp_block_dirty_bitmap_add ( bs - > node_name , bitmap ,
! ! granularity , granularity , true , true ,
false , false , & err ) ;
op = " add " ;
break ;
case BITMAP_REMOVE :
qmp_block_dirty_bitmap_remove ( bs - > node_name , bitmap , & err ) ;
op = " remove " ;
break ;
case BITMAP_CLEAR :
qmp_block_dirty_bitmap_clear ( bs - > node_name , bitmap , & err ) ;
op = " clear " ;
break ;
case BITMAP_ENABLE :
qmp_block_dirty_bitmap_enable ( bs - > node_name , bitmap , & err ) ;
op = " enable " ;
break ;
case BITMAP_DISABLE :
qmp_block_dirty_bitmap_disable ( bs - > node_name , bitmap , & err ) ;
op = " disable " ;
break ;
case BITMAP_MERGE : {
BlockDirtyBitmapMergeSource * merge_src ;
BlockDirtyBitmapMergeSourceList * list ;
merge_src = g_new0 ( BlockDirtyBitmapMergeSource , 1 ) ;
merge_src - > type = QTYPE_QDICT ;
merge_src - > u . external . node = g_strdup ( src_bs - > node_name ) ;
merge_src - > u . external . name = g_strdup ( act - > src ) ;
list = g_new0 ( BlockDirtyBitmapMergeSourceList , 1 ) ;
list - > value = merge_src ;
qmp_block_dirty_bitmap_merge ( bs - > node_name , bitmap , list , & err ) ;
qapi_free_BlockDirtyBitmapMergeSourceList ( list ) ;
op = " merge " ;
break ;
}
default :
g_assert_not_reached ( ) ;
}
if ( err ) {
error_reportf_err ( err , " Operation %s on bitmap %s failed: " ,
op , bitmap ) ;
goto out ;
}
g_free ( act ) ;
}
ret = 0 ;
out :
blk_unref ( src ) ;
blk_unref ( blk ) ;
qemu_opts_del ( opts ) ;
return ret ;
}
# define C_BS 01
# define C_COUNT 02
# define C_IF 04