@ -884,9 +884,10 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
* This is used to construct a temporary refcount table out of L1 and L2 tables
* which can be compared the the refcount table saved in the image .
*
* Return s the number of errors in the image that were found
* Modifie s the number of errors in res .
*/
static int inc_refcounts ( BlockDriverState * bs ,
static void inc_refcounts ( BlockDriverState * bs ,
BdrvCheckResult * res ,
uint16_t * refcount_table ,
int refcount_table_size ,
int64_t offset , int64_t size )
@ -894,30 +895,32 @@ static int inc_refcounts(BlockDriverState *bs,
BDRVQcowState * s = bs - > opaque ;
int64_t start , last , cluster_offset ;
int k ;
int errors = 0 ;
if ( size < = 0 )
return 0 ;
return ;
start = offset & ~ ( s - > cluster_size - 1 ) ;
last = ( offset + size - 1 ) & ~ ( s - > cluster_size - 1 ) ;
for ( cluster_offset = start ; cluster_offset < = last ;
cluster_offset + = s - > cluster_size ) {
k = cluster_offset > > s - > cluster_bits ;
if ( k < 0 | | k > = refcount_table_size ) {
if ( k < 0 ) {
fprintf ( stderr , " ERROR: invalid cluster offset=0x% " PRIx64 " \n " ,
cluster_offset ) ;
errors + + ;
res - > corruptions + + ;
} else if ( k > = refcount_table_size ) {
fprintf ( stderr , " Warning: cluster offset=0x% " PRIx64 " is after "
" the end of the image file, can't properly check refcounts. \n " ,
cluster_offset ) ;
res - > check_errors + + ;
} else {
if ( + + refcount_table [ k ] = = 0 ) {
fprintf ( stderr , " ERROR: overflow cluster offset=0x% " PRIx64
" \n " , cluster_offset ) ;
errors + + ;
res - > corruption s+ + ;
}
}
}
return errors ;
}
/*
@ -928,14 +931,13 @@ static int inc_refcounts(BlockDriverState *bs,
* Returns the number of errors found by the checks or - errno if an internal
* error occurred .
*/
static int check_refcounts_l2 ( BlockDriverState * bs ,
static int check_refcounts_l2 ( BlockDriverState * bs , BdrvCheckResult * res ,
uint16_t * refcount_table , int refcount_table_size , int64_t l2_offset ,
int check_copied )
{
BDRVQcowState * s = bs - > opaque ;
uint64_t * l2_table , offset ;
int i , l2_size , nb_csectors , refcount ;
int errors = 0 ;
/* Read L2 table from disk */
l2_size = s - > l2_size * sizeof ( uint64_t ) ;
@ -955,16 +957,15 @@ static int check_refcounts_l2(BlockDriverState *bs,
" copied flag must never be set for compressed "
" clusters \n " , offset > > s - > cluster_bits ) ;
offset & = ~ QCOW_OFLAG_COPIED ;
error s+ + ;
res - > corruption s+ + ;
}
/* Mark cluster as used */
nb_csectors = ( ( offset > > s - > csize_shift ) &
s - > csize_mask ) + 1 ;
offset & = s - > cluster_offset_mask ;
errors + = inc_refcounts ( bs , refcount_table ,
refcount_table_size ,
offset & ~ 511 , nb_csectors * 512 ) ;
inc_refcounts ( bs , res , refcount_table , refcount_table_size ,
offset & ~ 511 , nb_csectors * 512 ) ;
} else {
/* QCOW_OFLAG_COPIED must be set iff refcount == 1 */
if ( check_copied ) {
@ -974,35 +975,35 @@ static int check_refcounts_l2(BlockDriverState *bs,
if ( refcount < 0 ) {
fprintf ( stderr , " Can't get refcount for offset % "
PRIx64 " : %s \n " , entry , strerror ( - refcount ) ) ;
goto fail ;
}
if ( ( refcount = = 1 ) ! = ( ( entry & QCOW_OFLAG_COPIED ) ! = 0 ) ) {
fprintf ( stderr , " ERROR OFLAG_COPIED: offset=% "
PRIx64 " refcount=%d \n " , entry , refcount ) ;
error s+ + ;
res - > corruption s+ + ;
}
}
/* Mark cluster as used */
offset & = ~ QCOW_OFLAG_COPIED ;
errors + = inc_refcounts ( bs , refcount_table ,
refcount_table_size ,
offset , s - > cluster_size ) ;
inc_refcounts ( bs , res , refcount_table , refcount_table_size ,
offset , s - > cluster_size ) ;
/* Correct offsets are cluster aligned */
if ( offset & ( s - > cluster_size - 1 ) ) {
fprintf ( stderr , " ERROR offset=% " PRIx64 " : Cluster is not "
" properly aligned; L2 entry corrupted. \n " , offset ) ;
error s+ + ;
res - > corruption s+ + ;
}
}
}
}
qemu_free ( l2_table ) ;
return errors ;
return 0 ;
fail :
fprintf ( stderr , " ERROR: I/O error in check_refcounts_l1 \n " ) ;
fprintf ( stderr , " ERROR: I/O error in check_refcounts_l2 \n " ) ;
qemu_free ( l2_table ) ;
return - EIO ;
}
@ -1016,6 +1017,7 @@ fail:
* error occurred .
*/
static int check_refcounts_l1 ( BlockDriverState * bs ,
BdrvCheckResult * res ,
uint16_t * refcount_table ,
int refcount_table_size ,
int64_t l1_table_offset , int l1_size ,
@ -1024,13 +1026,12 @@ static int check_refcounts_l1(BlockDriverState *bs,
BDRVQcowState * s = bs - > opaque ;
uint64_t * l1_table , l2_offset , l1_size2 ;
int i , refcount , ret ;
int errors = 0 ;
l1_size2 = l1_size * sizeof ( uint64_t ) ;
/* Mark L1 table as used */
errors + = inc_refcounts ( bs , refcount_table , refcount_table_size ,
l1_table_offset , l1_size2 ) ;
inc_refcounts ( bs , re s , refcount_table , refcount_table_size ,
l1_table_offset , l1_size2 ) ;
/* Read L1 table entries from disk */
if ( l1_size2 = = 0 ) {
@ -1055,42 +1056,41 @@ static int check_refcounts_l1(BlockDriverState *bs,
if ( refcount < 0 ) {
fprintf ( stderr , " Can't get refcount for l2_offset % "
PRIx64 " : %s \n " , l2_offset , strerror ( - refcount ) ) ;
goto fail ;
}
if ( ( refcount = = 1 ) ! = ( ( l2_offset & QCOW_OFLAG_COPIED ) ! = 0 ) ) {
fprintf ( stderr , " ERROR OFLAG_COPIED: l2_offset=% " PRIx64
" refcount=%d \n " , l2_offset , refcount ) ;
error s+ + ;
res - > corruption s+ + ;
}
}
/* Mark L2 table as used */
l2_offset & = ~ QCOW_OFLAG_COPIED ;
errors + = inc_refcounts ( bs , refcount_table ,
refcount_table_size ,
l2_offset ,
s - > cluster_size ) ;
inc_refcounts ( bs , res , refcount_table , refcount_table_size ,
l2_offset , s - > cluster_size ) ;
/* L2 tables are cluster aligned */
if ( l2_offset & ( s - > cluster_size - 1 ) ) {
fprintf ( stderr , " ERROR l2_offset=% " PRIx64 " : Table is not "
" cluster aligned; L1 entry corrupted \n " , l2_offset ) ;
error s+ + ;
res - > corruption s+ + ;
}
/* Process and check L2 entries */
ret = check_refcounts_l2 ( bs , refcount_table , refcount_table_siz e ,
l2_offset , check_copied ) ;
ret = check_refcounts_l2 ( bs , res , refcount_table ,
refcount_table_size , l2_offset , check_copied ) ;
if ( ret < 0 ) {
goto fail ;
}
errors + = ret ;
}
}
qemu_free ( l1_table ) ;
return errors ;
return 0 ;
fail :
fprintf ( stderr , " ERROR: I/O error in check_refcounts_l1 \n " ) ;
res - > check_errors + + ;
qemu_free ( l1_table ) ;
return - EIO ;
}
@ -1101,44 +1101,47 @@ fail:
* Returns 0 if no errors are found , the number of errors in case the image is
* detected as corrupted , and - errno when an internal error occured .
*/
int qcow2_check_refcounts ( BlockDriverState * bs )
int qcow2_check_refcounts ( BlockDriverState * bs , BdrvCheckResult * res )
{
BDRVQcowState * s = bs - > opaque ;
int64_t size ;
int nb_clusters , refcount1 , refcount2 , i ;
QCowSnapshot * sn ;
uint16_t * refcount_table ;
int ret , errors = 0 ;
int ret ;
size = bdrv_getlength ( bs - > file ) ;
nb_clusters = size_to_clusters ( s , size ) ;
refcount_table = qemu_mallocz ( nb_clusters * sizeof ( uint16_t ) ) ;
/* header */
errors + = inc_refcounts ( bs , refcount_table , nb_clusters ,
0 , s - > cluster_size ) ;
inc_refcounts ( bs , re s , refcount_table , nb_clusters ,
0 , s - > cluster_size ) ;
/* current L1 table */
ret = check_refcounts_l1 ( bs , refcount_table , nb_clusters ,
ret = check_refcounts_l1 ( bs , res , re fcount_table , nb_clusters ,
s - > l1_table_offset , s - > l1_size , 1 ) ;
if ( ret < 0 ) {
return ret ;
}
errors + = ret ;
/* snapshots */
for ( i = 0 ; i < s - > nb_snapshots ; i + + ) {
sn = s - > snapshots + i ;
check_refcounts_l1 ( bs , refcount_table , nb_clusters ,
sn - > l1_table_offset , sn - > l1_size , 0 ) ;
ret = check_refcounts_l1 ( bs , res , refcount_table , nb_clusters ,
sn - > l1_table_offset , sn - > l1_size , 0 ) ;
if ( ret < 0 ) {
return ret ;
}
}
errors + = inc_refcounts ( bs , refcount_table , nb_clusters ,
s - > snapshots_offset , s - > snapshots_size ) ;
inc_refcounts ( bs , re s , refcount_table , nb_clusters ,
s - > snapshots_offset , s - > snapshots_size ) ;
/* refcount data */
errors + = inc_refcounts ( bs , refcount_table , nb_clusters ,
s - > refcount_table_offset ,
s - > refcount_table_size * sizeof ( uint64_t ) ) ;
inc_refcounts ( bs , res , refcount_table , nb_clusters ,
s - > refcount_table_offset ,
s - > refcount_table_size * sizeof ( uint64_t ) ) ;
for ( i = 0 ; i < s - > refcount_table_size ; i + + ) {
uint64_t offset , cluster ;
offset = s - > refcount_table [ i ] ;
@ -1148,22 +1151,23 @@ int qcow2_check_refcounts(BlockDriverState *bs)
if ( offset & ( s - > cluster_size - 1 ) ) {
fprintf ( stderr , " ERROR refcount block %d is not "
" cluster aligned; refcount table entry corrupted \n " , i ) ;
error s+ + ;
res - > corruption s+ + ;
continue ;
}
if ( cluster > = nb_clusters ) {
fprintf ( stderr , " ERROR refcount block %d is outside image \n " , i ) ;
error s+ + ;
res - > corruption s+ + ;
continue ;
}
if ( offset ! = 0 ) {
errors + = inc_refcounts ( bs , refcount_table , nb_clusters ,
offset , s - > cluster_size ) ;
inc_refcounts ( bs , re s , refcount_table , nb_clusters ,
offset , s - > cluster_size ) ;
if ( refcount_table [ cluster ] ! = 1 ) {
fprintf ( stderr , " ERROR refcount block %d refcount=%d \n " ,
i , refcount_table [ cluster ] ) ;
res - > corruptions + + ;
}
}
}
@ -1174,19 +1178,25 @@ int qcow2_check_refcounts(BlockDriverState *bs)
if ( refcount1 < 0 ) {
fprintf ( stderr , " Can't get refcount for cluster %d: %s \n " ,
i , strerror ( - refcount1 ) ) ;
res - > check_errors + + ;
continue ;
}
refcount2 = refcount_table [ i ] ;
if ( refcount1 ! = refcount2 ) {
fprintf ( stderr , " ERROR cluster %d refcount=%d reference=%d \n " ,
fprintf ( stderr , " %s cluster %d refcount=%d reference=%d \n " ,
refcount1 < refcount2 ? " ERROR " : " Leaked " ,
i , refcount1 , refcount2 ) ;
errors + + ;
if ( refcount1 < refcount2 ) {
res - > corruptions + + ;
} else {
res - > leaks + + ;
}
}
}
qemu_free ( refcount_table ) ;
return errors ;
return 0 ;
}