474c847d18203a3d3c2136c30d5987c2dca4532e
[sdk/emulator/qemu.git] / block / qed-check.c
1 /*
2  * QEMU Enhanced Disk Format Consistency Check
3  *
4  * Copyright IBM, Corp. 2010
5  *
6  * Authors:
7  *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com>
8  *
9  * This work is licensed under the terms of the GNU LGPL, version 2 or later.
10  * See the COPYING.LIB file in the top-level directory.
11  *
12  */
13
14 #include "qed.h"
15
16 typedef struct {
17     BDRVQEDState *s;
18     BdrvCheckResult *result;
19     bool fix;                           /* whether to fix invalid offsets */
20
21     uint64_t nclusters;
22     uint32_t *used_clusters;            /* referenced cluster bitmap */
23
24     QEDRequest request;
25 } QEDCheck;
26
27 static bool qed_test_bit(uint32_t *bitmap, uint64_t n) {
28     return !!(bitmap[n / 32] & (1 << (n % 32)));
29 }
30
31 static void qed_set_bit(uint32_t *bitmap, uint64_t n) {
32     bitmap[n / 32] |= 1 << (n % 32);
33 }
34
35 /**
36  * Set bitmap bits for clusters
37  *
38  * @check:          Check structure
39  * @offset:         Starting offset in bytes
40  * @n:              Number of clusters
41  */
42 static bool qed_set_used_clusters(QEDCheck *check, uint64_t offset,
43                                   unsigned int n)
44 {
45     uint64_t cluster = qed_bytes_to_clusters(check->s, offset);
46     unsigned int corruptions = 0;
47
48     while (n-- != 0) {
49         /* Clusters should only be referenced once */
50         if (qed_test_bit(check->used_clusters, cluster)) {
51             corruptions++;
52         }
53
54         qed_set_bit(check->used_clusters, cluster);
55         cluster++;
56     }
57
58     check->result->corruptions += corruptions;
59     return corruptions == 0;
60 }
61
62 /**
63  * Check an L2 table
64  *
65  * @ret:            Number of invalid cluster offsets
66  */
67 static unsigned int qed_check_l2_table(QEDCheck *check, QEDTable *table)
68 {
69     BDRVQEDState *s = check->s;
70     unsigned int i, num_invalid = 0;
71
72     for (i = 0; i < s->table_nelems; i++) {
73         uint64_t offset = table->offsets[i];
74
75         if (!offset) {
76             continue;
77         }
78
79         /* Detect invalid cluster offset */
80         if (!qed_check_cluster_offset(s, offset)) {
81             if (check->fix) {
82                 table->offsets[i] = 0;
83             } else {
84                 check->result->corruptions++;
85             }
86
87             num_invalid++;
88             continue;
89         }
90
91         qed_set_used_clusters(check, offset, 1);
92     }
93
94     return num_invalid;
95 }
96
97 /**
98  * Descend tables and check each cluster is referenced once only
99  */
100 static int qed_check_l1_table(QEDCheck *check, QEDTable *table)
101 {
102     BDRVQEDState *s = check->s;
103     unsigned int i, num_invalid_l1 = 0;
104     int ret, last_error = 0;
105
106     /* Mark L1 table clusters used */
107     qed_set_used_clusters(check, s->header.l1_table_offset,
108                           s->header.table_size);
109
110     for (i = 0; i < s->table_nelems; i++) {
111         unsigned int num_invalid_l2;
112         uint64_t offset = table->offsets[i];
113
114         if (!offset) {
115             continue;
116         }
117
118         /* Detect invalid L2 offset */
119         if (!qed_check_table_offset(s, offset)) {
120             /* Clear invalid offset */
121             if (check->fix) {
122                 table->offsets[i] = 0;
123             } else {
124                 check->result->corruptions++;
125             }
126
127             num_invalid_l1++;
128             continue;
129         }
130
131         if (!qed_set_used_clusters(check, offset, s->header.table_size)) {
132             continue; /* skip an invalid table */
133         }
134
135         ret = qed_read_l2_table_sync(s, &check->request, offset);
136         if (ret) {
137             check->result->check_errors++;
138             last_error = ret;
139             continue;
140         }
141
142         num_invalid_l2 = qed_check_l2_table(check,
143                                             check->request.l2_table->table);
144
145         /* Write out fixed L2 table */
146         if (num_invalid_l2 > 0 && check->fix) {
147             ret = qed_write_l2_table_sync(s, &check->request, 0,
148                                           s->table_nelems, false);
149             if (ret) {
150                 check->result->check_errors++;
151                 last_error = ret;
152                 continue;
153             }
154         }
155     }
156
157     /* Drop reference to final table */
158     qed_unref_l2_cache_entry(check->request.l2_table);
159     check->request.l2_table = NULL;
160
161     /* Write out fixed L1 table */
162     if (num_invalid_l1 > 0 && check->fix) {
163         ret = qed_write_l1_table_sync(s, 0, s->table_nelems);
164         if (ret) {
165             check->result->check_errors++;
166             last_error = ret;
167         }
168     }
169
170     return last_error;
171 }
172
173 /**
174  * Check for unreferenced (leaked) clusters
175  */
176 static void qed_check_for_leaks(QEDCheck *check)
177 {
178     BDRVQEDState *s = check->s;
179     uint64_t i;
180
181     for (i = s->header.header_size; i < check->nclusters; i++) {
182         if (!qed_test_bit(check->used_clusters, i)) {
183             check->result->leaks++;
184         }
185     }
186 }
187
188 int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix)
189 {
190     QEDCheck check = {
191         .s = s,
192         .result = result,
193         .nclusters = qed_bytes_to_clusters(s, s->file_size),
194         .request = { .l2_table = NULL },
195         .fix = fix,
196     };
197     int ret;
198
199     check.used_clusters = qemu_mallocz(((check.nclusters + 31) / 32) *
200                                        sizeof(check.used_clusters[0]));
201
202     ret = qed_check_l1_table(&check, s->l1_table);
203     if (ret == 0) {
204         /* Only check for leaks if entire image was scanned successfully */
205         qed_check_for_leaks(&check);
206     }
207
208     qemu_free(check.used_clusters);
209     return ret;
210 }