@ -81,6 +81,9 @@ static void help(void)
" name=value format. Use -o ? for an overview of the options supported by the \n "
" used format \n "
" '-c' indicates that target image must be compressed (qcow format only) \n "
" '-u' enables unsafe rebasing. It is assumed that old and new backing file \n "
" match exactly. The image doesn't need a working backing file before \n "
" rebasing in this case (useful for renaming the backing file) \n "
" '-h' with or without a command shows this help and lists the supported formats \n "
" \n "
" Parameters to snapshot subcommand: \n "
@ -527,6 +530,37 @@ static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum)
return v ;
}
/*
* Compares two buffers sector by sector . Returns 0 if the first sector of both
* buffers matches , non - zero otherwise .
*
* pnum is set to the number of sectors ( including and immediately following
* the first one ) that are known to have the same comparison result
*/
static int compare_sectors ( const uint8_t * buf1 , const uint8_t * buf2 , int n ,
int * pnum )
{
int res , i ;
if ( n < = 0 ) {
* pnum = 0 ;
return 0 ;
}
res = ! ! memcmp ( buf1 , buf2 , 512 ) ;
for ( i = 1 ; i < n ; i + + ) {
buf1 + = 512 ;
buf2 + = 512 ;
if ( ! ! memcmp ( buf1 , buf2 , 512 ) ! = res ) {
break ;
}
}
* pnum = i ;
return res ;
}
# define IO_BUF_SIZE (2 * 1024 * 1024)
static int img_convert ( int argc , char * * argv )
@ -1033,6 +1067,197 @@ static int img_snapshot(int argc, char **argv)
return 0 ;
}
static int img_rebase ( int argc , char * * argv )
{
BlockDriverState * bs , * bs_old_backing , * bs_new_backing ;
BlockDriver * old_backing_drv , * new_backing_drv ;
char * filename ;
const char * out_basefmt , * out_baseimg ;
int c , flags , ret ;
int unsafe = 0 ;
/* Parse commandline parameters */
out_baseimg = NULL ;
out_basefmt = NULL ;
for ( ; ; ) {
c = getopt ( argc , argv , " uhF:b: " ) ;
if ( c = = - 1 )
break ;
switch ( c ) {
case ' h ' :
help ( ) ;
return 0 ;
case ' F ' :
out_basefmt = optarg ;
break ;
case ' b ' :
out_baseimg = optarg ;
break ;
case ' u ' :
unsafe = 1 ;
break ;
}
}
if ( ( optind > = argc ) | | ! out_baseimg )
help ( ) ;
filename = argv [ optind + + ] ;
/*
* Open the images .
*
* Ignore the old backing file for unsafe rebase in case we want to correct
* the reference to a renamed or moved backing file .
*/
bs = bdrv_new ( " " ) ;
if ( ! bs )
error ( " Not enough memory " ) ;
flags = BRDV_O_FLAGS | ( unsafe ? BDRV_O_NO_BACKING : 0 ) ;
if ( bdrv_open2 ( bs , filename , flags , NULL ) < 0 ) {
error ( " Could not open '%s' " , filename ) ;
}
/* Find the right drivers for the backing files */
old_backing_drv = NULL ;
new_backing_drv = NULL ;
if ( ! unsafe & & bs - > backing_format [ 0 ] ! = ' \0 ' ) {
old_backing_drv = bdrv_find_format ( bs - > backing_format ) ;
if ( old_backing_drv = = NULL ) {
error ( " Invalid format name: '%s' " , bs - > backing_format ) ;
}
}
if ( out_basefmt ! = NULL ) {
new_backing_drv = bdrv_find_format ( out_basefmt ) ;
if ( new_backing_drv = = NULL ) {
error ( " Invalid format name: '%s' " , out_basefmt ) ;
}
}
/* For safe rebasing we need to compare old and new backing file */
if ( unsafe ) {
/* Make the compiler happy */
bs_old_backing = NULL ;
bs_new_backing = NULL ;
} else {
char backing_name [ 1024 ] ;
bs_old_backing = bdrv_new ( " old_backing " ) ;
bdrv_get_backing_filename ( bs , backing_name , sizeof ( backing_name ) ) ;
if ( bdrv_open2 ( bs_old_backing , backing_name , BRDV_O_FLAGS ,
old_backing_drv ) )
{
error ( " Could not open old backing file '%s' " , backing_name ) ;
return - 1 ;
}
bs_new_backing = bdrv_new ( " new_backing " ) ;
if ( bdrv_open2 ( bs_new_backing , out_baseimg , BRDV_O_FLAGS ,
new_backing_drv ) )
{
error ( " Could not open new backing file '%s' " , backing_name ) ;
return - 1 ;
}
}
/*
* Check each unallocated cluster in the COW file . If it is unallocated ,
* accesses go to the backing file . We must therefore compare this cluster
* in the old and new backing file , and if they differ we need to copy it
* from the old backing file into the COW file .
*
* If qemu - img crashes during this step , no harm is done . The content of
* the image is the same as the original one at any time .
*/
if ( ! unsafe ) {
uint64_t num_sectors ;
uint64_t sector ;
int n , n1 ;
uint8_t buf_old [ IO_BUF_SIZE ] ;
uint8_t buf_new [ IO_BUF_SIZE ] ;
bdrv_get_geometry ( bs , & num_sectors ) ;
for ( sector = 0 ; sector < num_sectors ; sector + = n ) {
/* How many sectors can we handle with the next read? */
if ( sector + ( IO_BUF_SIZE / 512 ) < = num_sectors ) {
n = ( IO_BUF_SIZE / 512 ) ;
} else {
n = num_sectors - sector ;
}
/* If the cluster is allocated, we don't need to take action */
if ( bdrv_is_allocated ( bs , sector , n , & n1 ) ) {
n = n1 ;
continue ;
}
/* Read old and new backing file */
if ( bdrv_read ( bs_old_backing , sector , buf_old , n ) < 0 ) {
error ( " error while reading from old backing file " ) ;
}
if ( bdrv_read ( bs_new_backing , sector , buf_new , n ) < 0 ) {
error ( " error while reading from new backing file " ) ;
}
/* If they differ, we need to write to the COW file */
uint64_t written = 0 ;
while ( written < n ) {
int pnum ;
if ( compare_sectors ( buf_old + written * 512 ,
buf_new + written * 512 , n , & pnum ) )
{
ret = bdrv_write ( bs , sector + written ,
buf_old + written * 512 , pnum ) ;
if ( ret < 0 ) {
error ( " Error while writing to COW image: %s " ,
strerror ( - ret ) ) ;
}
}
written + = pnum ;
}
}
}
/*
* Change the backing file . All clusters that are different from the old
* backing file are overwritten in the COW file now , so the visible content
* doesn ' t change when we switch the backing file .
*/
ret = bdrv_change_backing_file ( bs , out_baseimg , out_basefmt ) ;
if ( ret = = - ENOSPC ) {
error ( " Could not change the backing file to '%s': No space left in "
" the file header " , out_baseimg ) ;
} else if ( ret < 0 ) {
error ( " Could not change the backing file to '%s': %s " ,
out_baseimg , strerror ( - ret ) ) ;
}
/*
* TODO At this point it is possible to check if any clusters that are
* allocated in the COW file are the same in the backing file . If so , they
* could be dropped from the COW file . Don ' t do this before switching the
* backing file , in case of a crash this would lead to corruption .
*/
/* Cleanup */
if ( ! unsafe ) {
bdrv_delete ( bs_old_backing ) ;
bdrv_delete ( bs_new_backing ) ;
}
bdrv_delete ( bs ) ;
return 0 ;
}
static const img_cmd_t img_cmds [ ] = {
# define DEF(option, callback, arg_string) \
{ option , callback } ,