@@ -103,18 +103,103 @@ uint64_t get_cluster_to_byte(uint64_t allocated_clusters, uint64_t cluster_size)
103103 return allocated_clusters * cluster_size ;
104104}
105105
106+ void mark_l1_unallocated (char * bitmap , int i ){}
107+
108+ void mark_l2_unallocated (char * bitmap , int i , int j ){}
109+
110+ void set_bit (char * m , int bit , int val ){
111+ * m |= (val << bit );
112+ }
113+
114+ void dump_bitmap (struct qcow2_header * header , int fd , uint64_t * l1_table ){
115+ int i , n ;
116+ char * bitmap = NULL ;
117+ uint64_t cluster_size = (1 << header -> cluster_bits ); //cluster size in bytes
118+ uint64_t total_blocks , bitmap_size ;
119+ uint64_t nb_l2_entries = (cluster_size / sizeof (uint64_t )); //Number of L2 in a L1 entry
120+
121+ total_blocks = header -> size / cluster_size ;
122+ bitmap_size = total_blocks >> 3 ; // This transform our number of bits in a number of bytes for allocation
123+ //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.
124+ bitmap = malloc (bitmap_size );
125+ memset (bitmap , 0 , bitmap_size );
126+ uint64_t nb_byte_for_l1 = nb_l2_entries / 8 ; //Number of byte in the bitmap for a full L1
127+
128+ #pragma omp parallel for num_threads(4)
129+ for (i = 0 ; i < header -> l1_size ; i ++ ){
130+ int j ;
131+ uint64_t * l2_table = NULL ;
132+ uint64_t l1_entry = l1_table [i ];
133+ if (l1_entry != 0 ){
134+ l2_table = get_l2_table (header , fd , l1_entry ); if (l2_table == NULL ) { fprintf (stderr , "Couldn't get L2 entry\n" ); exit (EXIT_FAILURE ); }
135+ char * base_l1_bitmap = bitmap + (i * nb_byte_for_l1 );
136+ for (j = 0 ; j < nb_l2_entries ; j ++ ){
137+ if (is_l2_allocated (l2_table [j ])){
138+ //Mark L2 entry allocated
139+
140+ int mementry = j /8 ;
141+ int bit = j %8 ;
142+ set_bit (& (base_l1_bitmap [mementry ]), bit , 1 );
143+ }
144+ else {
145+ // Mark L2 entry as not allocated
146+ mark_l2_unallocated (bitmap , i , j ); // The bytes are already zeroed, we don't need to do anything
147+ }
148+ }
149+ free (l2_table );
150+ }
151+ else {
152+ //Mark L1 (and subsequent L2) non allocated
153+ mark_l1_unallocated (bitmap , i ); // The bytes are already zeroed, we don't need to do anything
154+ // memset(base_l1_bitmap, 0, nb_byte_for_l1); //Mark the whole L1 as being unused
155+ }
156+ }
157+
158+ n = write (STDOUT_FILENO , bitmap , bitmap_size );
159+ if (n < 0 ){
160+ fprintf (stderr , "Error writing bitmap to stdout" );
161+ }
162+ free (bitmap );
163+ }
164+
165+ int get_allocated_blocks (struct qcow2_header * header , int fd , uint64_t * l1_table ){
166+ uint64_t allocated_clusters = 0 ;
167+ int i , cluster_size = (1 << header -> cluster_bits ), nb_l2_entries = cluster_size / (sizeof (uint64_t ));
168+
169+
170+ #pragma omp parallel for num_threads(4) reduction (+:allocated_clusters)
171+ for (i = 0 ; i < header -> l1_size ; i ++ ){
172+ int j ;
173+ uint64_t * l2_table = NULL ;
174+ uint64_t l1_entry = l1_table [i ];
175+ if (l1_entry != 0 ){
176+ l2_table = get_l2_table (header , fd , l1_entry );
177+ if (l2_table == NULL ){
178+ fprintf (stderr , "Couldn't get L2 Table" );
179+ }
180+ for (j = 0 ; j < nb_l2_entries ; j ++ ){
181+ if (is_l2_allocated (l2_table [j ])){
182+ allocated_clusters += 1 ;
183+ }
184+ }
185+ free (l2_table );
186+ }
187+ }
188+ return allocated_clusters ;
189+ }
190+
106191int main (int argc , char * argv []){
107192 struct qcow2_header * header = NULL ;
108- char * filename = NULL , * backing_file_name = NULL ;
109- int fd , nb_l2_entries = 0 , err = 0 , i = 0 , ret = EXIT_SUCCESS ;
110- uint64_t * l1_table = NULL , cluster_size = 0 , allocated_clusters = 0 , allocated = 0 ;
193+ char * command , * filename = NULL , * backing_file_name = NULL ;
194+ int fd , err = 0 , ret = EXIT_SUCCESS ;
195+ uint64_t * l1_table = NULL , cluster_size = 0 , allocated_clusters = 0 , allocated_byte = 0 ;
111196
112- if (argc < 2 ){
197+ if (argc != 3 ){
113198 fprintf (stderr , "Need an argument\n" );
114199 exit (EXIT_FAILURE );
115200 }
116-
117- filename = argv [1 ];
201+ command = argv [ 1 ];
202+ filename = argv [2 ];
118203 fd = open (filename , O_RDONLY );
119204 if (fd < 0 ){
120205 fprintf (stderr , "Opening file %s failed with error %s (%d)\n" , filename , strerror (errno ), errno );
@@ -158,30 +243,18 @@ int main(int argc, char* argv[]){
158243 goto free_backing ;
159244 }
160245
161- nb_l2_entries = cluster_size / (sizeof (uint64_t ));
162-
163- #pragma omp parallel for num_threads(4) reduction (+:allocated_clusters)
164- for (i = 0 ; i < header -> l1_size ; i ++ ){
165- int j ;
166- uint64_t * l2_table = NULL ;
167- uint64_t l1_entry = l1_table [i ];
168- if (l1_entry != 0 ){
169- l2_table = get_l2_table (header , fd , l1_entry );
170- if (l2_table == NULL ){
171- fprintf (stderr , "Couldn't get L2 Table" );
172- }
173- for (j = 0 ; j < nb_l2_entries ; j ++ ){
174- if (is_l2_allocated (l2_table [j ])){
175- allocated_clusters += 1 ;
176- }
177- }
178- free (l2_table );
179- }
246+ if (!strcmp ("bitmap" , command )){
247+ dump_bitmap (header , fd , l1_table );
248+ }
249+ else if (!strcmp ("allocated" , command )){
250+ allocated_clusters = get_allocated_blocks (header , fd , l1_table );
251+ allocated_byte = get_cluster_to_byte (allocated_clusters , cluster_size );
252+ printf ("%lu\n" , allocated_byte );
253+ }
254+ else {
255+ fprintf (stderr , "Command %s is unknown.\n" , command );
256+ ret = EXIT_FAILURE ;
180257 }
181-
182- allocated = get_cluster_to_byte (allocated_clusters , cluster_size );
183- // printf("Allocated: %lu\n", allocated);
184- printf ("%lu\n" , allocated );
185258
186259 if (l1_table != NULL ){
187260 free (l1_table );
0 commit comments