Skip to content

Commit 37aafac

Browse files
committed
Try bitmap dump with qcow2helper
Signed-off-by: Damien Thenot <damien.thenot@vates.tech>
1 parent f135d4b commit 37aafac

2 files changed

Lines changed: 106 additions & 32 deletions

File tree

drivers/qcow2util.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,7 @@ def setSizePhys(self, path: str, size: int, debug: bool = True) -> None:
663663

664664
@override
665665
def getAllocatedSize(self, path: str) -> int:
666-
cmd = [QCOW2_HELPER, path]
666+
cmd = [QCOW2_HELPER, "allocated", path]
667667
return int(self._ioretry(cmd))
668668

669669
@override
@@ -703,8 +703,9 @@ def getDepth(self, path: str) -> int:
703703

704704
@override
705705
def getBlockBitmap(self, path: str) -> bytes:
706-
self._read_qcow2(path, read_clusters=True) #TODO: Add read L2 info here, we want to use an external application to do this eventually
707-
return zlib.compress(self._create_bitmap())
706+
cmd = [QCOW2_HELPER, "bitmap", path]
707+
text = cast(bytes, self._ioretry(cmd, text=False))
708+
return zlib.compress(text)
708709

709710
def _getTapdisk(self, path: str) -> Tuple[int, int]:
710711
"""

qcow2/qcow2_helper.c

Lines changed: 102 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
106191
int 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

Comments
 (0)