@@ -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
2426char * 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
4750uint64_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 {
0 commit comments