Skip to content

Commit 9fbcd2a

Browse files
committed
qcow2_helper: support extended_l2 feature
Signed-off-by: Anthoine Bourgeois <anthoine.bourgeois@vates.tech>
1 parent 552609c commit 9fbcd2a

2 files changed

Lines changed: 112 additions & 39 deletions

File tree

qcow2/qcow2_helper.c

Lines changed: 110 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ static void transform_header_be_to_le(struct qcow2_header* header){
2121
SWAP_BE_TO_LE(32, header_length);
2222
}
2323

24+
//#define DEBUG
25+
#ifdef DEBUG
2426
char* qcow2_get_backing_file(struct qcow2_header* header, int fd){
2527
int err, backing_file_name_size;
2628
char* backing_file_name;
@@ -43,6 +45,7 @@ char* qcow2_get_backing_file(struct qcow2_header* header, int fd){
4345
}
4446
return NULL;
4547
}
48+
#endif
4649

4750
uint64_t* get_l1_offset(struct qcow2_header* header, int fd){
4851
int i, err = 0;
@@ -71,12 +74,11 @@ uint64_t* get_l1_offset(struct qcow2_header* header, int fd){
7174
return raw_l1;
7275
}
7376

74-
uint64_t* get_l2_table(struct qcow2_header* header, int fd, uint64_t offset){
77+
uint64_t* get_l2_table(struct qcow2_header* header, int fd, uint64_t offset, uint64_t nb_l2_entries, int extended_l2){
7578
int i;
7679
ssize_t bytes_read;
7780
uint64_t* raw_l2 = NULL;
7881
uint64_t cluster_size = (1 << header->cluster_bits);
79-
uint64_t nb_l2_entries = (cluster_size / sizeof(uint64_t));
8082

8183
raw_l2 = malloc(cluster_size);
8284
if(raw_l2 == NULL){
@@ -92,7 +94,7 @@ uint64_t* get_l2_table(struct qcow2_header* header, int fd, uint64_t offset){
9294
return NULL;
9395
}
9496

95-
for(i = 0; i < nb_l2_entries; i++){
97+
for(i = 0; i < nb_l2_entries * (extended_l2 ? 2 : 1); i++){
9698
raw_l2[i] = __builtin_bswap64(raw_l2[i]);
9799
}
98100

@@ -107,7 +109,57 @@ int is_l2_allocated(uint64_t l2_entry){
107109
return ((l2_entry & ALLOCATED_ENTRY_BIT) != 0) || ((l2_entry & STANDARD_CLUSTER_OFFSET_MASK) != 0);
108110
}
109111

110-
uint64_t get_cluster_to_byte(uint64_t allocated_clusters, uint64_t cluster_size){
112+
int is_extended_l2_allocated(uint64_t l2_entry_lo, uint64_t l2_entry_hi){
113+
if((l2_entry_lo & CLUSTER_TYPE_BIT) != 0){
114+
fprintf(stderr, "Cluster is compressed\n");
115+
exit(EXIT_FAILURE); //TODO: Read compressed clusters
116+
}
117+
return l2_entry_hi & 0xffffffff;
118+
}
119+
120+
uint32_t count_set_bits(uint32_t alloc_status_bitmap)
121+
{
122+
uint32_t count = 0;
123+
124+
if (alloc_status_bitmap == 0)
125+
return 0;
126+
127+
while (alloc_status_bitmap) {
128+
alloc_status_bitmap &= alloc_status_bitmap - 1;
129+
count++;
130+
}
131+
132+
return count;
133+
}
134+
135+
uint32_t get_extended_l2_allocated(uint64_t l2_entry_lo, uint64_t l2_entry_hi){
136+
if((l2_entry_lo & CLUSTER_TYPE_BIT) != 0){
137+
fprintf(stderr, "Cluster is compressed\n");
138+
exit(EXIT_FAILURE); //TODO: Read compressed clusters
139+
}
140+
return count_set_bits(l2_entry_hi & 0xffffffff);
141+
}
142+
143+
uint64_t get_allocated_clusters(uint64_t nb_l2_entries, uint64_t *l2_table, int extended_l2)
144+
{
145+
uint64_t allocated_clusters = 0;
146+
int j;
147+
for(j = 0; j < nb_l2_entries; j++){
148+
if (extended_l2) {
149+
allocated_clusters += get_extended_l2_allocated(l2_table[j*2], l2_table[j*2+1]);
150+
} else {
151+
if(is_l2_allocated(l2_table[j])){
152+
allocated_clusters += 1;
153+
}
154+
}
155+
}
156+
return allocated_clusters;
157+
}
158+
159+
uint64_t get_cluster_to_byte(uint64_t allocated_clusters, uint64_t cluster_size, int extended_l2){
160+
if (extended_l2) {
161+
return allocated_clusters * (cluster_size / 32);
162+
}
111163
return allocated_clusters * cluster_size;
112164
}
113165

@@ -119,14 +171,35 @@ void set_bit(char* m, int bit, int val){
119171
*m |= (val << bit);
120172
}
121173

122-
void dump_bitmap(struct qcow2_header* header, int fd, uint64_t *l1_table){
174+
void set_l1_bitmap(char *base_l1_bitmap, uint64_t *l2_table, uint64_t nb_l2_entries, int extended_l2) {
175+
int j;
176+
int mementry;
177+
int bit;
178+
179+
for (j = 0; j < nb_l2_entries; j++) {
180+
if (extended_l2) {
181+
if(!is_extended_l2_allocated(l2_table[j*2], l2_table[j*2+1])) {
182+
continue;
183+
}
184+
} else {
185+
if(!is_l2_allocated(l2_table[j])){
186+
continue;
187+
}
188+
}
189+
mementry = j/8;
190+
bit = j%8;
191+
//Mark L2 entry allocated
192+
set_bit(&(base_l1_bitmap[mementry]), bit, 1);
193+
}
194+
}
195+
196+
void dump_bitmap(struct qcow2_header* header, int fd, uint64_t *l1_table, uint64_t nb_l2_entries, int extended_l2){
123197
int i, n;
124198
char* bitmap = NULL;
125199
uint64_t cluster_size = (1 << header->cluster_bits); //cluster size in bytes
126200
uint64_t total_blocks, bitmap_size;
127-
uint64_t nb_l2_entries = (cluster_size / sizeof(uint64_t)); //Number of L2 in a L1 entry
128201

129-
total_blocks = header->size / cluster_size;
202+
total_blocks = header->size / cluster_size;
130203
bitmap_size = total_blocks >> 3; // This transform our number of bits in a number of bytes for allocation
131204
//Does VHD use sectors of 512 for the bitmap it dumps? Nope, it uses 2MiB. Do we want to use 2MiB to reduce QCOW2 size allocation? We would need a way to transform x 64KiB blocks in a 2MiB block.
132205
bitmap = malloc(bitmap_size);
@@ -135,25 +208,16 @@ void dump_bitmap(struct qcow2_header* header, int fd, uint64_t *l1_table){
135208

136209
#pragma omp parallel for num_threads(4)
137210
for(i = 0; i < header->l1_size; i++){
138-
int j;
139211
uint64_t *l2_table = NULL;
140212
uint64_t l1_entry = l1_table[i];
141213
if(l1_entry != 0){
142-
l2_table = get_l2_table(header, fd, l1_entry); if(l2_table == NULL) { fprintf(stderr, "Couldn't get L2 entry\n"); exit(EXIT_FAILURE); }
143-
char* base_l1_bitmap = bitmap + (i * nb_byte_for_l1);
144-
for(j = 0; j < nb_l2_entries; j++){
145-
if(is_l2_allocated(l2_table[j])){
146-
//Mark L2 entry allocated
147-
148-
int mementry = j/8;
149-
int bit = j%8;
150-
set_bit(&(base_l1_bitmap[mementry]), bit, 1);
151-
}
152-
else {
153-
// Mark L2 entry as not allocated
154-
mark_l2_unallocated(bitmap, i, j); // The bytes are already zeroed, we don't need to do anything
155-
}
214+
l2_table = get_l2_table(header, fd, l1_entry, nb_l2_entries, extended_l2);
215+
if(l2_table == NULL) {
216+
fprintf(stderr, "Couldn't get L2 table\n");
217+
exit(EXIT_FAILURE);
156218
}
219+
char* base_l1_bitmap = bitmap + (i * nb_byte_for_l1);
220+
set_l1_bitmap(base_l1_bitmap, l2_table, nb_l2_entries, extended_l2);
157221
free(l2_table);
158222
}
159223
else{
@@ -170,26 +234,21 @@ void dump_bitmap(struct qcow2_header* header, int fd, uint64_t *l1_table){
170234
free(bitmap);
171235
}
172236

173-
int get_allocated_blocks(struct qcow2_header* header, int fd, uint64_t *l1_table){
237+
int get_allocated_blocks(struct qcow2_header* header, int fd, uint64_t *l1_table, uint64_t nb_l2_entries, int extended_l2){
174238
uint64_t allocated_clusters = 0;
175-
int i, cluster_size = (1 << header->cluster_bits), nb_l2_entries = cluster_size / (sizeof(uint64_t));
176-
239+
int i;
177240

178241
#pragma omp parallel for num_threads(4) reduction (+:allocated_clusters)
179242
for(i = 0; i < header->l1_size; i++){
180-
int j;
181243
uint64_t *l2_table = NULL;
182244
uint64_t l1_entry = l1_table[i];
183245
if(l1_entry != 0){
184-
l2_table = get_l2_table(header, fd, l1_entry);
246+
l2_table = get_l2_table(header, fd, l1_entry, nb_l2_entries, extended_l2);
185247
if(l2_table == NULL){
186248
fprintf(stderr, "Couldn't get L2 Table");
249+
exit(EXIT_FAILURE);
187250
}
188-
for(j = 0; j < nb_l2_entries; j++){
189-
if(is_l2_allocated(l2_table[j])){
190-
allocated_clusters += 1;
191-
}
192-
}
251+
allocated_clusters += get_allocated_clusters(nb_l2_entries, l2_table, extended_l2);
193252
free(l2_table);
194253
}
195254
}
@@ -200,7 +259,9 @@ int main(int argc, char* argv[]){
200259
struct qcow2_header* header = NULL;
201260
char * command, * filename = NULL, * backing_file_name = NULL;
202261
int fd, err = 0, ret = EXIT_SUCCESS;
262+
int extended_l2;
203263
uint64_t *l1_table = NULL, cluster_size = 0, allocated_clusters = 0, allocated_byte = 0;
264+
uint64_t nb_l2_entries;
204265

205266
if(argc != 3){
206267
fprintf(stderr, "Need an argument\n");
@@ -239,10 +300,20 @@ int main(int argc, char* argv[]){
239300
}
240301

241302
cluster_size = (1 << header->cluster_bits);
242-
243-
// printf("Version: %d\n", header->version);
244-
// backing_file_name = qcow2_get_backing_file(header, fd);
245-
// printf("Backing file: %s\n", backing_file_name);
303+
extended_l2 = (header->version == 3) && (header->incompatible_features & INCOMPATIBLE_FEATURE_EXTENDED_L2);
304+
305+
#ifdef DEBUG
306+
printf("Version: %d\n", header->version);
307+
backing_file_name = qcow2_get_backing_file(header, fd);
308+
printf("Backing file: %s\n", backing_file_name);
309+
printf("Extended L2: %d\n", extended_l2);
310+
#endif
311+
312+
if (extended_l2) {
313+
nb_l2_entries = cluster_size / (sizeof(uint64_t) * 2);
314+
} else {
315+
nb_l2_entries = cluster_size / (sizeof(uint64_t));
316+
}
246317

247318
l1_table = get_l1_offset(header, fd);
248319
if(l1_table == NULL){
@@ -252,11 +323,11 @@ int main(int argc, char* argv[]){
252323
}
253324

254325
if(!strcmp("bitmap", command)){
255-
dump_bitmap(header, fd, l1_table);
326+
dump_bitmap(header, fd, l1_table, nb_l2_entries, extended_l2);
256327
}
257328
else if(!strcmp("allocated", command)){
258-
allocated_clusters = get_allocated_blocks(header, fd, l1_table);
259-
allocated_byte = get_cluster_to_byte(allocated_clusters, cluster_size);
329+
allocated_clusters = get_allocated_blocks(header, fd, l1_table, nb_l2_entries, extended_l2);
330+
allocated_byte = get_cluster_to_byte(allocated_clusters, cluster_size, extended_l2);
260331
printf("%lu\n", allocated_byte);
261332
}
262333
else{

qcow2/qcow_helper.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#define CLUSTER_TYPE_BIT (1UL << 62) /* 0 for standard, 1 for compressed cluster */
1515
#define ALLOCATED_ENTRY_BIT (1UL << 63) /* Bit 63 is the allocated bit for standard cluster */
1616

17+
/* Incompatible features */
18+
#define INCOMPATIBLE_FEATURE_EXTENDED_L2 0x0010
1719

1820
struct qcow2_header {
1921
uint32_t magic;

0 commit comments

Comments
 (0)