2013-08-22 19:57:00 -05:00
|
|
|
/*
|
|
|
|
* Block driver for the QCOW format
|
2024-10-22 19:36:41 +02:00
|
|
|
*
|
2013-08-22 19:57:00 -05:00
|
|
|
* Copyright (c) 2004-2006 Fabrice Bellard
|
2024-10-22 19:36:41 +02:00
|
|
|
*
|
2013-08-22 19:57:00 -05:00
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
* THE SOFTWARE.
|
|
|
|
*/
|
2024-10-21 22:35:20 +02:00
|
|
|
#include "qemu-git/qemu-common.h"
|
2024-10-22 19:36:18 +02:00
|
|
|
#include "block.h"
|
|
|
|
#include "block_int.h"
|
2013-08-22 19:57:00 -05:00
|
|
|
#include <zlib.h>
|
|
|
|
#if 0
|
2024-10-22 19:36:41 +02:00
|
|
|
# include "aes.h"
|
2013-08-22 19:57:00 -05:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/**************************************************************/
|
|
|
|
/* QEMU COW block driver with compression and encryption support */
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
#define QCOW_MAGIC ( ( 'Q' << 24 ) | ( 'F' << 16 ) | ( 'I' << 8 ) | 0xfb )
|
2013-08-22 19:57:00 -05:00
|
|
|
#define QCOW_VERSION 1
|
|
|
|
|
|
|
|
#define QCOW_CRYPT_NONE 0
|
2024-10-22 19:36:41 +02:00
|
|
|
#define QCOW_CRYPT_AES 1
|
2013-08-22 19:57:00 -05:00
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
#define QCOW_OFLAG_COMPRESSED ( 1LL << 63 )
|
2013-08-22 19:57:00 -05:00
|
|
|
|
|
|
|
typedef struct QCowHeader {
|
|
|
|
uint32_t magic;
|
|
|
|
uint32_t version;
|
|
|
|
uint64_t backing_file_offset;
|
|
|
|
uint32_t backing_file_size;
|
|
|
|
uint32_t mtime;
|
|
|
|
uint64_t size; /* in bytes */
|
|
|
|
uint8_t cluster_bits;
|
|
|
|
uint8_t l2_bits;
|
|
|
|
uint32_t crypt_method;
|
|
|
|
uint64_t l1_table_offset;
|
|
|
|
} QCowHeader;
|
|
|
|
|
|
|
|
#define L2_CACHE_SIZE 16
|
|
|
|
|
|
|
|
typedef struct BDRVQcowState {
|
2024-10-22 19:36:41 +02:00
|
|
|
BlockDriverState* hd;
|
2013-08-22 19:57:00 -05:00
|
|
|
int cluster_bits;
|
|
|
|
int cluster_size;
|
|
|
|
int cluster_sectors;
|
|
|
|
int l2_bits;
|
|
|
|
int l2_size;
|
|
|
|
int l1_size;
|
|
|
|
uint64_t cluster_offset_mask;
|
|
|
|
uint64_t l1_table_offset;
|
2024-10-22 19:36:41 +02:00
|
|
|
uint64_t* l1_table;
|
|
|
|
uint64_t* l2_cache;
|
|
|
|
uint64_t l2_cache_offsets[ L2_CACHE_SIZE ];
|
|
|
|
uint32_t l2_cache_counts[ L2_CACHE_SIZE ];
|
|
|
|
uint8_t* cluster_cache;
|
|
|
|
uint8_t* cluster_data;
|
2013-08-22 19:57:00 -05:00
|
|
|
uint64_t cluster_cache_offset;
|
|
|
|
} BDRVQcowState;
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
static int decompress_cluster( BDRVQcowState* s, uint64_t cluster_offset );
|
2013-08-22 19:57:00 -05:00
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
static int qcow_probe( const uint8_t* buf, int buf_size, const char* filename )
|
2013-08-22 19:57:00 -05:00
|
|
|
{
|
2024-10-22 19:36:41 +02:00
|
|
|
const QCowHeader* cow_header = ( const void* )buf;
|
|
|
|
|
|
|
|
if ( buf_size >= sizeof( QCowHeader ) && be32_to_cpu( cow_header->magic ) == QCOW_MAGIC &&
|
|
|
|
be32_to_cpu( cow_header->version ) == QCOW_VERSION )
|
2013-08-22 19:57:00 -05:00
|
|
|
return 100;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
static int qcow_open( BlockDriverState* bs, const char* filename, int flags )
|
2013-08-22 19:57:00 -05:00
|
|
|
{
|
2024-10-22 19:36:41 +02:00
|
|
|
BDRVQcowState* s = bs->opaque;
|
2013-08-22 19:57:00 -05:00
|
|
|
int len, i, shift, ret;
|
|
|
|
QCowHeader header;
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
ret = bdrv_file_open( &s->hd, filename, flags );
|
|
|
|
if ( ret < 0 )
|
2013-08-22 19:57:00 -05:00
|
|
|
return ret;
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( bdrv_pread( s->hd, 0, &header, sizeof( header ) ) != sizeof( header ) )
|
2013-08-22 19:57:00 -05:00
|
|
|
goto fail;
|
2024-10-22 19:36:41 +02:00
|
|
|
be32_to_cpus( &header.magic );
|
|
|
|
be32_to_cpus( &header.version );
|
|
|
|
be64_to_cpus( &header.backing_file_offset );
|
|
|
|
be32_to_cpus( &header.backing_file_size );
|
|
|
|
be32_to_cpus( &header.mtime );
|
|
|
|
be64_to_cpus( &header.size );
|
|
|
|
be32_to_cpus( &header.crypt_method );
|
|
|
|
be64_to_cpus( &header.l1_table_offset );
|
|
|
|
|
|
|
|
if ( header.magic != QCOW_MAGIC || header.version != QCOW_VERSION )
|
2013-08-22 19:57:00 -05:00
|
|
|
goto fail;
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( header.size <= 1 || header.cluster_bits < 9 )
|
2013-08-22 19:57:00 -05:00
|
|
|
goto fail;
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( header.crypt_method > QCOW_CRYPT_AES )
|
2013-08-22 19:57:00 -05:00
|
|
|
goto fail;
|
|
|
|
s->cluster_bits = header.cluster_bits;
|
|
|
|
s->cluster_size = 1 << s->cluster_bits;
|
2024-10-22 19:36:41 +02:00
|
|
|
s->cluster_sectors = 1 << ( s->cluster_bits - 9 );
|
2013-08-22 19:57:00 -05:00
|
|
|
s->l2_bits = header.l2_bits;
|
|
|
|
s->l2_size = 1 << s->l2_bits;
|
|
|
|
bs->total_sectors = header.size / 512;
|
2024-10-22 19:36:41 +02:00
|
|
|
s->cluster_offset_mask = ( 1LL << ( 63 - s->cluster_bits ) ) - 1;
|
2013-08-22 19:57:00 -05:00
|
|
|
|
|
|
|
/* read the level 1 table */
|
|
|
|
shift = s->cluster_bits + s->l2_bits;
|
2024-10-22 19:36:41 +02:00
|
|
|
s->l1_size = ( header.size + ( 1LL << shift ) - 1 ) >> shift;
|
2013-08-22 19:57:00 -05:00
|
|
|
|
|
|
|
s->l1_table_offset = header.l1_table_offset;
|
2024-10-22 19:36:41 +02:00
|
|
|
s->l1_table = qemu_malloc( s->l1_size * sizeof( uint64_t ) );
|
|
|
|
if ( !s->l1_table )
|
2013-08-22 19:57:00 -05:00
|
|
|
goto fail;
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( bdrv_pread( s->hd, s->l1_table_offset, s->l1_table, s->l1_size * sizeof( uint64_t ) ) != s->l1_size * sizeof( uint64_t ) )
|
2013-08-22 19:57:00 -05:00
|
|
|
goto fail;
|
2024-10-22 19:36:41 +02:00
|
|
|
for ( i = 0; i < s->l1_size; i++ ) {
|
|
|
|
be64_to_cpus( &s->l1_table[ i ] );
|
2013-08-22 19:57:00 -05:00
|
|
|
}
|
|
|
|
/* alloc L2 cache */
|
2024-10-22 19:36:41 +02:00
|
|
|
s->l2_cache = qemu_malloc( s->l2_size * L2_CACHE_SIZE * sizeof( uint64_t ) );
|
|
|
|
if ( !s->l2_cache )
|
2013-08-22 19:57:00 -05:00
|
|
|
goto fail;
|
2024-10-22 19:36:41 +02:00
|
|
|
s->cluster_cache = qemu_malloc( s->cluster_size );
|
|
|
|
if ( !s->cluster_cache )
|
2013-08-22 19:57:00 -05:00
|
|
|
goto fail;
|
2024-10-22 19:36:41 +02:00
|
|
|
s->cluster_data = qemu_malloc( s->cluster_size );
|
|
|
|
if ( !s->cluster_data )
|
2013-08-22 19:57:00 -05:00
|
|
|
goto fail;
|
|
|
|
s->cluster_cache_offset = -1;
|
2024-10-22 19:36:41 +02:00
|
|
|
|
2013-08-22 19:57:00 -05:00
|
|
|
/* read the backing file name */
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( header.backing_file_offset != 0 ) {
|
2013-08-22 19:57:00 -05:00
|
|
|
len = header.backing_file_size;
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( len > 1023 )
|
2013-08-22 19:57:00 -05:00
|
|
|
len = 1023;
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( bdrv_pread( s->hd, header.backing_file_offset, bs->backing_file, len ) != len )
|
2013-08-22 19:57:00 -05:00
|
|
|
goto fail;
|
2024-10-22 19:36:41 +02:00
|
|
|
bs->backing_file[ len ] = '\0';
|
2013-08-22 19:57:00 -05:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
fail:
|
|
|
|
qemu_free( s->l1_table );
|
|
|
|
qemu_free( s->l2_cache );
|
|
|
|
qemu_free( s->cluster_cache );
|
|
|
|
qemu_free( s->cluster_data );
|
|
|
|
bdrv_delete( s->hd );
|
2013-08-22 19:57:00 -05:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 'allocate' is:
|
|
|
|
*
|
|
|
|
* 0 to not allocate.
|
|
|
|
*
|
|
|
|
* 1 to allocate a normal cluster (for sector indexes 'n_start' to
|
|
|
|
* 'n_end')
|
|
|
|
*
|
|
|
|
* 2 to allocate a compressed cluster of size
|
|
|
|
* 'compressed_size'. 'compressed_size' must be > 0 and <
|
2024-10-22 19:36:41 +02:00
|
|
|
* cluster_size
|
2013-08-22 19:57:00 -05:00
|
|
|
*
|
|
|
|
* return 0 if not allocated.
|
|
|
|
*/
|
2024-10-22 19:36:41 +02:00
|
|
|
static uint64_t get_cluster_offset( BlockDriverState* bs, uint64_t offset, int allocate, int compressed_size, int n_start, int n_end )
|
2013-08-22 19:57:00 -05:00
|
|
|
{
|
2024-10-22 19:36:41 +02:00
|
|
|
BDRVQcowState* s = bs->opaque;
|
2013-08-22 19:57:00 -05:00
|
|
|
int min_index, i, j, l1_index, l2_index;
|
|
|
|
uint64_t l2_offset, *l2_table, cluster_offset, tmp;
|
|
|
|
uint32_t min_count;
|
|
|
|
int new_l2_table;
|
2024-10-22 19:36:41 +02:00
|
|
|
|
|
|
|
l1_index = offset >> ( s->l2_bits + s->cluster_bits );
|
|
|
|
l2_offset = s->l1_table[ l1_index ];
|
2013-08-22 19:57:00 -05:00
|
|
|
new_l2_table = 0;
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( !l2_offset ) {
|
|
|
|
if ( !allocate )
|
2013-08-22 19:57:00 -05:00
|
|
|
return 0;
|
|
|
|
/* allocate a new l2 entry */
|
2024-10-22 19:36:41 +02:00
|
|
|
l2_offset = bdrv_getlength( s->hd );
|
2013-08-22 19:57:00 -05:00
|
|
|
/* round to cluster size */
|
2024-10-22 19:36:41 +02:00
|
|
|
l2_offset = ( l2_offset + s->cluster_size - 1 ) & ~( s->cluster_size - 1 );
|
2013-08-22 19:57:00 -05:00
|
|
|
/* update the L1 entry */
|
2024-10-22 19:36:41 +02:00
|
|
|
s->l1_table[ l1_index ] = l2_offset;
|
|
|
|
tmp = cpu_to_be64( l2_offset );
|
|
|
|
if ( bdrv_pwrite( s->hd, s->l1_table_offset + l1_index * sizeof( tmp ), &tmp, sizeof( tmp ) ) != sizeof( tmp ) )
|
2013-08-22 19:57:00 -05:00
|
|
|
return 0;
|
|
|
|
new_l2_table = 1;
|
|
|
|
}
|
2024-10-22 19:36:41 +02:00
|
|
|
for ( i = 0; i < L2_CACHE_SIZE; i++ ) {
|
|
|
|
if ( l2_offset == s->l2_cache_offsets[ i ] ) {
|
2013-08-22 19:57:00 -05:00
|
|
|
/* increment the hit count */
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( ++s->l2_cache_counts[ i ] == 0xffffffff ) {
|
|
|
|
for ( j = 0; j < L2_CACHE_SIZE; j++ ) {
|
|
|
|
s->l2_cache_counts[ j ] >>= 1;
|
2013-08-22 19:57:00 -05:00
|
|
|
}
|
|
|
|
}
|
2024-10-22 19:36:41 +02:00
|
|
|
l2_table = s->l2_cache + ( i << s->l2_bits );
|
2013-08-22 19:57:00 -05:00
|
|
|
goto found;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* not found: load a new entry in the least used one */
|
|
|
|
min_index = 0;
|
|
|
|
min_count = 0xffffffff;
|
2024-10-22 19:36:41 +02:00
|
|
|
for ( i = 0; i < L2_CACHE_SIZE; i++ ) {
|
|
|
|
if ( s->l2_cache_counts[ i ] < min_count ) {
|
|
|
|
min_count = s->l2_cache_counts[ i ];
|
2013-08-22 19:57:00 -05:00
|
|
|
min_index = i;
|
|
|
|
}
|
|
|
|
}
|
2024-10-22 19:36:41 +02:00
|
|
|
l2_table = s->l2_cache + ( min_index << s->l2_bits );
|
|
|
|
if ( new_l2_table ) {
|
|
|
|
memset( l2_table, 0, s->l2_size * sizeof( uint64_t ) );
|
|
|
|
if ( bdrv_pwrite( s->hd, l2_offset, l2_table, s->l2_size * sizeof( uint64_t ) ) != s->l2_size * sizeof( uint64_t ) )
|
2013-08-22 19:57:00 -05:00
|
|
|
return 0;
|
|
|
|
} else {
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( bdrv_pread( s->hd, l2_offset, l2_table, s->l2_size * sizeof( uint64_t ) ) != s->l2_size * sizeof( uint64_t ) )
|
2013-08-22 19:57:00 -05:00
|
|
|
return 0;
|
|
|
|
}
|
2024-10-22 19:36:41 +02:00
|
|
|
s->l2_cache_offsets[ min_index ] = l2_offset;
|
|
|
|
s->l2_cache_counts[ min_index ] = 1;
|
|
|
|
found:
|
|
|
|
l2_index = ( offset >> s->cluster_bits ) & ( s->l2_size - 1 );
|
|
|
|
cluster_offset = be64_to_cpu( l2_table[ l2_index ] );
|
|
|
|
if ( !cluster_offset || ( ( cluster_offset & QCOW_OFLAG_COMPRESSED ) && allocate == 1 ) ) {
|
|
|
|
if ( !allocate )
|
2013-08-22 19:57:00 -05:00
|
|
|
return 0;
|
|
|
|
/* allocate a new cluster */
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( ( cluster_offset & QCOW_OFLAG_COMPRESSED ) && ( n_end - n_start ) < s->cluster_sectors ) {
|
2013-08-22 19:57:00 -05:00
|
|
|
/* if the cluster is already compressed, we must
|
|
|
|
decompress it in the case it is not completely
|
|
|
|
overwritten */
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( decompress_cluster( s, cluster_offset ) < 0 )
|
2013-08-22 19:57:00 -05:00
|
|
|
return 0;
|
2024-10-22 19:36:41 +02:00
|
|
|
cluster_offset = bdrv_getlength( s->hd );
|
|
|
|
cluster_offset = ( cluster_offset + s->cluster_size - 1 ) & ~( s->cluster_size - 1 );
|
2013-08-22 19:57:00 -05:00
|
|
|
/* write the cluster content */
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( bdrv_pwrite( s->hd, cluster_offset, s->cluster_cache, s->cluster_size ) != s->cluster_size )
|
2013-08-22 19:57:00 -05:00
|
|
|
return -1;
|
|
|
|
} else {
|
2024-10-22 19:36:41 +02:00
|
|
|
cluster_offset = bdrv_getlength( s->hd );
|
|
|
|
if ( allocate == 1 ) {
|
2013-08-22 19:57:00 -05:00
|
|
|
/* round to cluster size */
|
2024-10-22 19:36:41 +02:00
|
|
|
cluster_offset = ( cluster_offset + s->cluster_size - 1 ) & ~( s->cluster_size - 1 );
|
|
|
|
bdrv_truncate( s->hd, cluster_offset + s->cluster_size );
|
2013-08-22 19:57:00 -05:00
|
|
|
} else {
|
2024-10-22 19:36:41 +02:00
|
|
|
cluster_offset |= QCOW_OFLAG_COMPRESSED | ( uint64_t )compressed_size << ( 63 - s->cluster_bits );
|
2013-08-22 19:57:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/* update L2 table */
|
2024-10-22 19:36:41 +02:00
|
|
|
tmp = cpu_to_be64( cluster_offset );
|
|
|
|
l2_table[ l2_index ] = tmp;
|
|
|
|
if ( bdrv_pwrite( s->hd, l2_offset + l2_index * sizeof( tmp ), &tmp, sizeof( tmp ) ) != sizeof( tmp ) )
|
2013-08-22 19:57:00 -05:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return cluster_offset;
|
|
|
|
}
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
static int qcow_is_allocated( BlockDriverState* bs, int64_t sector_num, int nb_sectors, int* pnum )
|
2013-08-22 19:57:00 -05:00
|
|
|
{
|
2024-10-22 19:36:41 +02:00
|
|
|
BDRVQcowState* s = bs->opaque;
|
2013-08-22 19:57:00 -05:00
|
|
|
int index_in_cluster, n;
|
|
|
|
uint64_t cluster_offset;
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
cluster_offset = get_cluster_offset( bs, sector_num << 9, 0, 0, 0, 0 );
|
|
|
|
index_in_cluster = sector_num & ( s->cluster_sectors - 1 );
|
2013-08-22 19:57:00 -05:00
|
|
|
n = s->cluster_sectors - index_in_cluster;
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( n > nb_sectors )
|
2013-08-22 19:57:00 -05:00
|
|
|
n = nb_sectors;
|
|
|
|
*pnum = n;
|
2024-10-22 19:36:41 +02:00
|
|
|
return ( cluster_offset != 0 );
|
2013-08-22 19:57:00 -05:00
|
|
|
}
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
static int decompress_buffer( uint8_t* out_buf, int out_buf_size, const uint8_t* buf, int buf_size )
|
2013-08-22 19:57:00 -05:00
|
|
|
{
|
|
|
|
z_stream strm1, *strm = &strm1;
|
|
|
|
int ret, out_len;
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
memset( strm, 0, sizeof( *strm ) );
|
2013-08-22 19:57:00 -05:00
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
strm->next_in = ( uint8_t* )buf;
|
2013-08-22 19:57:00 -05:00
|
|
|
strm->avail_in = buf_size;
|
|
|
|
strm->next_out = out_buf;
|
|
|
|
strm->avail_out = out_buf_size;
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
ret = inflateInit2( strm, -12 );
|
|
|
|
if ( ret != Z_OK )
|
2013-08-22 19:57:00 -05:00
|
|
|
return -1;
|
2024-10-22 19:36:41 +02:00
|
|
|
ret = inflate( strm, Z_FINISH );
|
2013-08-22 19:57:00 -05:00
|
|
|
out_len = strm->next_out - out_buf;
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( ( ret != Z_STREAM_END && ret != Z_BUF_ERROR ) || out_len != out_buf_size ) {
|
|
|
|
inflateEnd( strm );
|
2013-08-22 19:57:00 -05:00
|
|
|
return -1;
|
|
|
|
}
|
2024-10-22 19:36:41 +02:00
|
|
|
inflateEnd( strm );
|
2013-08-22 19:57:00 -05:00
|
|
|
return 0;
|
|
|
|
}
|
2024-10-22 19:36:41 +02:00
|
|
|
|
|
|
|
static int decompress_cluster( BDRVQcowState* s, uint64_t cluster_offset )
|
2013-08-22 19:57:00 -05:00
|
|
|
{
|
|
|
|
int ret, csize;
|
|
|
|
uint64_t coffset;
|
|
|
|
|
|
|
|
coffset = cluster_offset & s->cluster_offset_mask;
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( s->cluster_cache_offset != coffset ) {
|
|
|
|
csize = cluster_offset >> ( 63 - s->cluster_bits );
|
|
|
|
csize &= ( s->cluster_size - 1 );
|
|
|
|
ret = bdrv_pread( s->hd, coffset, s->cluster_data, csize );
|
|
|
|
if ( ret != csize )
|
2013-08-22 19:57:00 -05:00
|
|
|
return -1;
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( decompress_buffer( s->cluster_cache, s->cluster_size, s->cluster_data, csize ) < 0 ) {
|
2013-08-22 19:57:00 -05:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
s->cluster_cache_offset = coffset;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
static int qcow_read( BlockDriverState* bs, int64_t sector_num, uint8_t* buf, int nb_sectors )
|
2013-08-22 19:57:00 -05:00
|
|
|
{
|
2024-10-22 19:36:41 +02:00
|
|
|
BDRVQcowState* s = bs->opaque;
|
2013-08-22 19:57:00 -05:00
|
|
|
int ret, index_in_cluster, n;
|
|
|
|
uint64_t cluster_offset;
|
2024-10-22 19:36:41 +02:00
|
|
|
|
|
|
|
while ( nb_sectors > 0 ) {
|
|
|
|
cluster_offset = get_cluster_offset( bs, sector_num << 9, 0, 0, 0, 0 );
|
|
|
|
index_in_cluster = sector_num & ( s->cluster_sectors - 1 );
|
2013-08-22 19:57:00 -05:00
|
|
|
n = s->cluster_sectors - index_in_cluster;
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( n > nb_sectors )
|
2013-08-22 19:57:00 -05:00
|
|
|
n = nb_sectors;
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( !cluster_offset ) {
|
|
|
|
if ( bs->backing_hd ) {
|
2013-08-22 19:57:00 -05:00
|
|
|
/* read from the base image */
|
2024-10-22 19:36:41 +02:00
|
|
|
ret = bdrv_read( bs->backing_hd, sector_num, buf, n );
|
|
|
|
if ( ret < 0 )
|
2013-08-22 19:57:00 -05:00
|
|
|
return -1;
|
|
|
|
} else {
|
2024-10-22 19:36:41 +02:00
|
|
|
memset( buf, 0, 512 * n );
|
2013-08-22 19:57:00 -05:00
|
|
|
}
|
2024-10-22 19:36:41 +02:00
|
|
|
} else if ( cluster_offset & QCOW_OFLAG_COMPRESSED ) {
|
|
|
|
if ( decompress_cluster( s, cluster_offset ) < 0 )
|
2013-08-22 19:57:00 -05:00
|
|
|
return -1;
|
2024-10-22 19:36:41 +02:00
|
|
|
memcpy( buf, s->cluster_cache + index_in_cluster * 512, 512 * n );
|
2013-08-22 19:57:00 -05:00
|
|
|
} else {
|
2024-10-22 19:36:41 +02:00
|
|
|
ret = bdrv_pread( s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512 );
|
|
|
|
if ( ret != n * 512 )
|
2013-08-22 19:57:00 -05:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
nb_sectors -= n;
|
|
|
|
sector_num += n;
|
|
|
|
buf += n * 512;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
static int qcow_write( BlockDriverState* bs, int64_t sector_num, const uint8_t* buf, int nb_sectors )
|
2013-08-22 19:57:00 -05:00
|
|
|
{
|
2024-10-22 19:36:41 +02:00
|
|
|
BDRVQcowState* s = bs->opaque;
|
2013-08-22 19:57:00 -05:00
|
|
|
int ret, index_in_cluster, n;
|
|
|
|
uint64_t cluster_offset;
|
2024-10-22 19:36:41 +02:00
|
|
|
|
|
|
|
while ( nb_sectors > 0 ) {
|
|
|
|
index_in_cluster = sector_num & ( s->cluster_sectors - 1 );
|
2013-08-22 19:57:00 -05:00
|
|
|
n = s->cluster_sectors - index_in_cluster;
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( n > nb_sectors )
|
2013-08-22 19:57:00 -05:00
|
|
|
n = nb_sectors;
|
2024-10-22 19:36:41 +02:00
|
|
|
cluster_offset = get_cluster_offset( bs, sector_num << 9, 1, 0, index_in_cluster, index_in_cluster + n );
|
|
|
|
if ( !cluster_offset )
|
2013-08-22 19:57:00 -05:00
|
|
|
return -1;
|
2024-10-22 19:36:41 +02:00
|
|
|
{
|
|
|
|
ret = bdrv_pwrite( s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512 );
|
2013-08-22 19:57:00 -05:00
|
|
|
}
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( ret != n * 512 )
|
2013-08-22 19:57:00 -05:00
|
|
|
return -1;
|
|
|
|
nb_sectors -= n;
|
|
|
|
sector_num += n;
|
|
|
|
buf += n * 512;
|
|
|
|
}
|
|
|
|
s->cluster_cache_offset = -1; /* disable compressed cache */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
static void qcow_close( BlockDriverState* bs )
|
2013-08-22 19:57:00 -05:00
|
|
|
{
|
2024-10-22 19:36:41 +02:00
|
|
|
BDRVQcowState* s = bs->opaque;
|
|
|
|
qemu_free( s->l1_table );
|
|
|
|
qemu_free( s->l2_cache );
|
|
|
|
qemu_free( s->cluster_cache );
|
|
|
|
qemu_free( s->cluster_data );
|
|
|
|
bdrv_delete( s->hd );
|
2013-08-22 19:57:00 -05:00
|
|
|
}
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
static int qcow_create( const char* filename, int64_t total_size, const char* backing_file, int flags )
|
2013-08-22 19:57:00 -05:00
|
|
|
{
|
|
|
|
int fd, header_size, backing_filename_len, l1_size, i, shift;
|
|
|
|
QCowHeader header;
|
|
|
|
uint64_t tmp;
|
2024-10-22 19:36:41 +02:00
|
|
|
int ret;
|
2013-08-22 19:57:00 -05:00
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
fd = open( filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644 );
|
|
|
|
if ( fd < 0 )
|
2013-08-22 19:57:00 -05:00
|
|
|
return -1;
|
2024-10-22 19:36:41 +02:00
|
|
|
memset( &header, 0, sizeof( header ) );
|
|
|
|
header.magic = cpu_to_be32( QCOW_MAGIC );
|
|
|
|
header.version = cpu_to_be32( QCOW_VERSION );
|
|
|
|
header.size = cpu_to_be64( total_size * 512 );
|
|
|
|
header_size = sizeof( header );
|
2013-08-22 19:57:00 -05:00
|
|
|
backing_filename_len = 0;
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( backing_file ) {
|
|
|
|
header.backing_file_offset = cpu_to_be64( header_size );
|
|
|
|
backing_filename_len = strlen( backing_file );
|
|
|
|
header.backing_file_size = cpu_to_be32( backing_filename_len );
|
2013-08-22 19:57:00 -05:00
|
|
|
header_size += backing_filename_len;
|
2024-10-22 19:36:41 +02:00
|
|
|
header.mtime = cpu_to_be32( 0 );
|
2013-08-22 19:57:00 -05:00
|
|
|
header.cluster_bits = 9; /* 512 byte cluster to avoid copying
|
|
|
|
unmodifyed sectors */
|
2024-10-22 19:36:41 +02:00
|
|
|
header.l2_bits = 12; /* 32 KB L2 tables */
|
2013-08-22 19:57:00 -05:00
|
|
|
} else {
|
|
|
|
header.cluster_bits = 12; /* 4 KB clusters */
|
2024-10-22 19:36:41 +02:00
|
|
|
header.l2_bits = 9; /* 4 KB L2 tables */
|
2013-08-22 19:57:00 -05:00
|
|
|
}
|
2024-10-22 19:36:41 +02:00
|
|
|
header_size = ( header_size + 7 ) & ~7;
|
2013-08-22 19:57:00 -05:00
|
|
|
shift = header.cluster_bits + header.l2_bits;
|
2024-10-22 19:36:41 +02:00
|
|
|
l1_size = ( ( total_size * 512 ) + ( 1LL << shift ) - 1 ) >> shift;
|
2013-08-22 19:57:00 -05:00
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
header.l1_table_offset = cpu_to_be64( header_size );
|
|
|
|
if ( flags ) {
|
|
|
|
header.crypt_method = cpu_to_be32( QCOW_CRYPT_AES );
|
2013-08-22 19:57:00 -05:00
|
|
|
} else {
|
2024-10-22 19:36:41 +02:00
|
|
|
header.crypt_method = cpu_to_be32( QCOW_CRYPT_NONE );
|
2013-08-22 19:57:00 -05:00
|
|
|
}
|
2024-10-22 19:36:41 +02:00
|
|
|
|
2013-08-22 19:57:00 -05:00
|
|
|
/* write all the data */
|
2024-10-22 19:36:41 +02:00
|
|
|
ret = write( fd, &header, sizeof( header ) );
|
|
|
|
if ( ret != sizeof( header ) ) {
|
2018-08-28 12:46:52 -04:00
|
|
|
ret = -1;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( backing_file ) {
|
|
|
|
ret = write( fd, backing_file, backing_filename_len );
|
|
|
|
if ( ret != backing_filename_len ) {
|
2018-08-28 12:46:52 -04:00
|
|
|
ret = -1;
|
|
|
|
goto exit;
|
|
|
|
}
|
2013-08-22 19:57:00 -05:00
|
|
|
}
|
2024-10-22 19:36:41 +02:00
|
|
|
lseek( fd, header_size, SEEK_SET );
|
2013-08-22 19:57:00 -05:00
|
|
|
tmp = 0;
|
2024-10-22 19:36:41 +02:00
|
|
|
for ( i = 0; i < l1_size; i++ ) {
|
|
|
|
ret = write( fd, &tmp, sizeof( tmp ) );
|
|
|
|
if ( ret != sizeof( tmp ) ) {
|
2018-08-28 12:46:52 -04:00
|
|
|
ret = -1;
|
|
|
|
goto exit;
|
|
|
|
}
|
2013-08-22 19:57:00 -05:00
|
|
|
}
|
2018-08-28 12:46:52 -04:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
exit:
|
2024-10-22 19:36:41 +02:00
|
|
|
close( fd );
|
2018-08-28 12:46:52 -04:00
|
|
|
return ret;
|
2013-08-22 19:57:00 -05:00
|
|
|
}
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
static int qcow_make_empty( BlockDriverState* bs )
|
2013-08-22 19:57:00 -05:00
|
|
|
{
|
2024-10-22 19:36:41 +02:00
|
|
|
BDRVQcowState* s = bs->opaque;
|
|
|
|
uint32_t l1_length = s->l1_size * sizeof( uint64_t );
|
2013-08-22 19:57:00 -05:00
|
|
|
int ret;
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
memset( s->l1_table, 0, l1_length );
|
|
|
|
if ( bdrv_pwrite( s->hd, s->l1_table_offset, s->l1_table, l1_length ) < 0 )
|
|
|
|
return -1;
|
|
|
|
ret = bdrv_truncate( s->hd, s->l1_table_offset + l1_length );
|
|
|
|
if ( ret < 0 )
|
2013-08-22 19:57:00 -05:00
|
|
|
return ret;
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
memset( s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof( uint64_t ) );
|
|
|
|
memset( s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof( uint64_t ) );
|
|
|
|
memset( s->l2_cache_counts, 0, L2_CACHE_SIZE * sizeof( uint32_t ) );
|
2013-08-22 19:57:00 -05:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX: put compressed sectors first, then all the cluster aligned
|
|
|
|
tables to avoid losing bytes in alignment */
|
2024-10-22 19:36:41 +02:00
|
|
|
static int qcow_write_compressed( BlockDriverState* bs, int64_t sector_num, const uint8_t* buf, int nb_sectors )
|
2013-08-22 19:57:00 -05:00
|
|
|
{
|
2024-10-22 19:36:41 +02:00
|
|
|
BDRVQcowState* s = bs->opaque;
|
2013-08-22 19:57:00 -05:00
|
|
|
z_stream strm;
|
|
|
|
int ret, out_len;
|
2024-10-22 19:36:41 +02:00
|
|
|
uint8_t* out_buf;
|
2013-08-22 19:57:00 -05:00
|
|
|
uint64_t cluster_offset;
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( nb_sectors != s->cluster_sectors )
|
2013-08-22 19:57:00 -05:00
|
|
|
return -EINVAL;
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
out_buf = qemu_malloc( s->cluster_size + ( s->cluster_size / 1000 ) + 128 );
|
|
|
|
if ( !out_buf )
|
2013-08-22 19:57:00 -05:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* best compression, small window, no zlib header */
|
2024-10-22 19:36:41 +02:00
|
|
|
memset( &strm, 0, sizeof( strm ) );
|
|
|
|
ret = deflateInit2( &strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -12, 9, Z_DEFAULT_STRATEGY );
|
|
|
|
if ( ret != 0 ) {
|
|
|
|
qemu_free( out_buf );
|
2013-08-22 19:57:00 -05:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
strm.avail_in = s->cluster_size;
|
2024-10-22 19:36:41 +02:00
|
|
|
strm.next_in = ( uint8_t* )buf;
|
2013-08-22 19:57:00 -05:00
|
|
|
strm.avail_out = s->cluster_size;
|
|
|
|
strm.next_out = out_buf;
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
ret = deflate( &strm, Z_FINISH );
|
|
|
|
if ( ret != Z_STREAM_END && ret != Z_OK ) {
|
|
|
|
qemu_free( out_buf );
|
|
|
|
deflateEnd( &strm );
|
2013-08-22 19:57:00 -05:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
out_len = strm.next_out - out_buf;
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
deflateEnd( &strm );
|
2013-08-22 19:57:00 -05:00
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( ret != Z_STREAM_END || out_len >= s->cluster_size ) {
|
2013-08-22 19:57:00 -05:00
|
|
|
/* could not compress: write normal cluster */
|
2024-10-22 19:36:41 +02:00
|
|
|
qcow_write( bs, sector_num, buf, s->cluster_sectors );
|
2013-08-22 19:57:00 -05:00
|
|
|
} else {
|
2024-10-22 19:36:41 +02:00
|
|
|
cluster_offset = get_cluster_offset( bs, sector_num << 9, 2, out_len, 0, 0 );
|
2013-08-22 19:57:00 -05:00
|
|
|
cluster_offset &= s->cluster_offset_mask;
|
2024-10-22 19:36:41 +02:00
|
|
|
if ( bdrv_pwrite( s->hd, cluster_offset, out_buf, out_len ) != out_len ) {
|
|
|
|
qemu_free( out_buf );
|
2013-08-22 19:57:00 -05:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2024-10-22 19:36:41 +02:00
|
|
|
|
|
|
|
qemu_free( out_buf );
|
2013-08-22 19:57:00 -05:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
static void qcow_flush( BlockDriverState* bs )
|
2013-08-22 19:57:00 -05:00
|
|
|
{
|
2024-10-22 19:36:41 +02:00
|
|
|
BDRVQcowState* s = bs->opaque;
|
|
|
|
bdrv_flush( s->hd );
|
2013-08-22 19:57:00 -05:00
|
|
|
}
|
|
|
|
|
2024-10-22 19:36:41 +02:00
|
|
|
static int qcow_get_info( BlockDriverState* bs, BlockDriverInfo* bdi )
|
2013-08-22 19:57:00 -05:00
|
|
|
{
|
2024-10-22 19:36:41 +02:00
|
|
|
BDRVQcowState* s = bs->opaque;
|
2013-08-22 19:57:00 -05:00
|
|
|
bdi->cluster_size = s->cluster_size;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
BlockDriver bdrv_qcow = {
|
|
|
|
"qcow",
|
2024-10-22 19:36:41 +02:00
|
|
|
sizeof( BDRVQcowState ),
|
2013-08-22 19:57:00 -05:00
|
|
|
qcow_probe,
|
|
|
|
qcow_open,
|
|
|
|
qcow_read,
|
|
|
|
qcow_write,
|
|
|
|
qcow_close,
|
|
|
|
qcow_create,
|
|
|
|
qcow_flush,
|
|
|
|
qcow_is_allocated,
|
|
|
|
NULL,
|
|
|
|
qcow_make_empty,
|
|
|
|
|
|
|
|
.bdrv_write_compressed = qcow_write_compressed,
|
|
|
|
.bdrv_get_info = qcow_get_info,
|
|
|
|
};
|