@ -29,6 +29,7 @@
# include "block/qcow2.h"
# include "qemu/range.h"
# include "qemu/bswap.h"
# include "qemu/cutils.h"
static int64_t alloc_clusters_noref ( BlockDriverState * bs , uint64_t size ) ;
static int QEMU_WARN_UNUSED_RESULT update_refcount ( BlockDriverState * bs ,
@ -3061,3 +3062,122 @@ done:
qemu_vfree ( new_refblock ) ;
return ret ;
}
static int qcow2_discard_refcount_block ( BlockDriverState * bs ,
uint64_t discard_block_offs )
{
BDRVQcow2State * s = bs - > opaque ;
uint64_t refblock_offs = get_refblock_offset ( s , discard_block_offs ) ;
uint64_t cluster_index = discard_block_offs > > s - > cluster_bits ;
uint32_t block_index = cluster_index & ( s - > refcount_block_size - 1 ) ;
void * refblock ;
int ret ;
assert ( discard_block_offs ! = 0 ) ;
ret = qcow2_cache_get ( bs , s - > refcount_block_cache , refblock_offs ,
& refblock ) ;
if ( ret < 0 ) {
return ret ;
}
if ( s - > get_refcount ( refblock , block_index ) ! = 1 ) {
qcow2_signal_corruption ( bs , true , - 1 , - 1 , " Invalid refcount: "
" refblock offset %# " PRIx64
" , reftable index %u "
" , block offset %# " PRIx64
" , refcount %# " PRIx64 ,
refblock_offs ,
offset_to_reftable_index ( s , discard_block_offs ) ,
discard_block_offs ,
s - > get_refcount ( refblock , block_index ) ) ;
qcow2_cache_put ( bs , s - > refcount_block_cache , & refblock ) ;
return - EINVAL ;
}
s - > set_refcount ( refblock , block_index , 0 ) ;
qcow2_cache_entry_mark_dirty ( bs , s - > refcount_block_cache , refblock ) ;
qcow2_cache_put ( bs , s - > refcount_block_cache , & refblock ) ;
if ( cluster_index < s - > free_cluster_index ) {
s - > free_cluster_index = cluster_index ;
}
refblock = qcow2_cache_is_table_offset ( bs , s - > refcount_block_cache ,
discard_block_offs ) ;
if ( refblock ) {
/* discard refblock from the cache if refblock is cached */
qcow2_cache_discard ( bs , s - > refcount_block_cache , refblock ) ;
}
update_refcount_discard ( bs , discard_block_offs , s - > cluster_size ) ;
return 0 ;
}
int qcow2_shrink_reftable ( BlockDriverState * bs )
{
BDRVQcow2State * s = bs - > opaque ;
uint64_t * reftable_tmp =
g_malloc ( s - > refcount_table_size * sizeof ( uint64_t ) ) ;
int i , ret ;
for ( i = 0 ; i < s - > refcount_table_size ; i + + ) {
int64_t refblock_offs = s - > refcount_table [ i ] & REFT_OFFSET_MASK ;
void * refblock ;
bool unused_block ;
if ( refblock_offs = = 0 ) {
reftable_tmp [ i ] = 0 ;
continue ;
}
ret = qcow2_cache_get ( bs , s - > refcount_block_cache , refblock_offs ,
& refblock ) ;
if ( ret < 0 ) {
goto out ;
}
/* the refblock has own reference */
if ( i = = offset_to_reftable_index ( s , refblock_offs ) ) {
uint64_t block_index = ( refblock_offs > > s - > cluster_bits ) &
( s - > refcount_block_size - 1 ) ;
uint64_t refcount = s - > get_refcount ( refblock , block_index ) ;
s - > set_refcount ( refblock , block_index , 0 ) ;
unused_block = buffer_is_zero ( refblock , s - > cluster_size ) ;
s - > set_refcount ( refblock , block_index , refcount ) ;
} else {
unused_block = buffer_is_zero ( refblock , s - > cluster_size ) ;
}
qcow2_cache_put ( bs , s - > refcount_block_cache , & refblock ) ;
reftable_tmp [ i ] = unused_block ? 0 : cpu_to_be64 ( s - > refcount_table [ i ] ) ;
}
ret = bdrv_pwrite_sync ( bs - > file , s - > refcount_table_offset , reftable_tmp ,
s - > refcount_table_size * sizeof ( uint64_t ) ) ;
/*
* If the write in the reftable failed the image may contain a partially
* overwritten reftable . In this case it would be better to clear the
* reftable in memory to avoid possible image corruption .
*/
for ( i = 0 ; i < s - > refcount_table_size ; i + + ) {
if ( s - > refcount_table [ i ] & & ! reftable_tmp [ i ] ) {
if ( ret = = 0 ) {
ret = qcow2_discard_refcount_block ( bs , s - > refcount_table [ i ] &
REFT_OFFSET_MASK ) ;
}
s - > refcount_table [ i ] = 0 ;
}
}
if ( ! s - > cache_discards ) {
qcow2_process_discards ( bs , ret ) ;
}
out :
g_free ( reftable_tmp ) ;
return ret ;
}