Revision 9ac228e0 block/qcow2-refcount.c
b/block/qcow2-refcount.c | ||
---|---|---|
884 | 884 |
* This is used to construct a temporary refcount table out of L1 and L2 tables |
885 | 885 |
* which can be compared the the refcount table saved in the image. |
886 | 886 |
* |
887 |
* Returns the number of errors in the image that were found
|
|
887 |
* Modifies the number of errors in res.
|
|
888 | 888 |
*/ |
889 |
static int inc_refcounts(BlockDriverState *bs, |
|
889 |
static void inc_refcounts(BlockDriverState *bs, |
|
890 |
BdrvCheckResult *res, |
|
890 | 891 |
uint16_t *refcount_table, |
891 | 892 |
int refcount_table_size, |
892 | 893 |
int64_t offset, int64_t size) |
... | ... | |
894 | 895 |
BDRVQcowState *s = bs->opaque; |
895 | 896 |
int64_t start, last, cluster_offset; |
896 | 897 |
int k; |
897 |
int errors = 0; |
|
898 | 898 |
|
899 | 899 |
if (size <= 0) |
900 |
return 0;
|
|
900 |
return; |
|
901 | 901 |
|
902 | 902 |
start = offset & ~(s->cluster_size - 1); |
903 | 903 |
last = (offset + size - 1) & ~(s->cluster_size - 1); |
904 | 904 |
for(cluster_offset = start; cluster_offset <= last; |
905 | 905 |
cluster_offset += s->cluster_size) { |
906 | 906 |
k = cluster_offset >> s->cluster_bits; |
907 |
if (k < 0 || k >= refcount_table_size) {
|
|
907 |
if (k < 0) { |
|
908 | 908 |
fprintf(stderr, "ERROR: invalid cluster offset=0x%" PRIx64 "\n", |
909 | 909 |
cluster_offset); |
910 |
errors++; |
|
910 |
res->corruptions++; |
|
911 |
} else if (k >= refcount_table_size) { |
|
912 |
fprintf(stderr, "Warning: cluster offset=0x%" PRIx64 " is after " |
|
913 |
"the end of the image file, can't properly check refcounts.\n", |
|
914 |
cluster_offset); |
|
915 |
res->check_errors++; |
|
911 | 916 |
} else { |
912 | 917 |
if (++refcount_table[k] == 0) { |
913 | 918 |
fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64 |
914 | 919 |
"\n", cluster_offset); |
915 |
errors++;
|
|
920 |
res->corruptions++;
|
|
916 | 921 |
} |
917 | 922 |
} |
918 | 923 |
} |
919 |
|
|
920 |
return errors; |
|
921 | 924 |
} |
922 | 925 |
|
923 | 926 |
/* |
... | ... | |
928 | 931 |
* Returns the number of errors found by the checks or -errno if an internal |
929 | 932 |
* error occurred. |
930 | 933 |
*/ |
931 |
static int check_refcounts_l2(BlockDriverState *bs, |
|
934 |
static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
|
932 | 935 |
uint16_t *refcount_table, int refcount_table_size, int64_t l2_offset, |
933 | 936 |
int check_copied) |
934 | 937 |
{ |
935 | 938 |
BDRVQcowState *s = bs->opaque; |
936 | 939 |
uint64_t *l2_table, offset; |
937 | 940 |
int i, l2_size, nb_csectors, refcount; |
938 |
int errors = 0; |
|
939 | 941 |
|
940 | 942 |
/* Read L2 table from disk */ |
941 | 943 |
l2_size = s->l2_size * sizeof(uint64_t); |
... | ... | |
955 | 957 |
"copied flag must never be set for compressed " |
956 | 958 |
"clusters\n", offset >> s->cluster_bits); |
957 | 959 |
offset &= ~QCOW_OFLAG_COPIED; |
958 |
errors++;
|
|
960 |
res->corruptions++;
|
|
959 | 961 |
} |
960 | 962 |
|
961 | 963 |
/* Mark cluster as used */ |
962 | 964 |
nb_csectors = ((offset >> s->csize_shift) & |
963 | 965 |
s->csize_mask) + 1; |
964 | 966 |
offset &= s->cluster_offset_mask; |
965 |
errors += inc_refcounts(bs, refcount_table, |
|
966 |
refcount_table_size, |
|
967 |
offset & ~511, nb_csectors * 512); |
|
967 |
inc_refcounts(bs, res, refcount_table, refcount_table_size, |
|
968 |
offset & ~511, nb_csectors * 512); |
|
968 | 969 |
} else { |
969 | 970 |
/* QCOW_OFLAG_COPIED must be set iff refcount == 1 */ |
970 | 971 |
if (check_copied) { |
... | ... | |
974 | 975 |
if (refcount < 0) { |
975 | 976 |
fprintf(stderr, "Can't get refcount for offset %" |
976 | 977 |
PRIx64 ": %s\n", entry, strerror(-refcount)); |
978 |
goto fail; |
|
977 | 979 |
} |
978 | 980 |
if ((refcount == 1) != ((entry & QCOW_OFLAG_COPIED) != 0)) { |
979 | 981 |
fprintf(stderr, "ERROR OFLAG_COPIED: offset=%" |
980 | 982 |
PRIx64 " refcount=%d\n", entry, refcount); |
981 |
errors++;
|
|
983 |
res->corruptions++;
|
|
982 | 984 |
} |
983 | 985 |
} |
984 | 986 |
|
985 | 987 |
/* Mark cluster as used */ |
986 | 988 |
offset &= ~QCOW_OFLAG_COPIED; |
987 |
errors += inc_refcounts(bs, refcount_table, |
|
988 |
refcount_table_size, |
|
989 |
offset, s->cluster_size); |
|
989 |
inc_refcounts(bs, res, refcount_table,refcount_table_size, |
|
990 |
offset, s->cluster_size); |
|
990 | 991 |
|
991 | 992 |
/* Correct offsets are cluster aligned */ |
992 | 993 |
if (offset & (s->cluster_size - 1)) { |
993 | 994 |
fprintf(stderr, "ERROR offset=%" PRIx64 ": Cluster is not " |
994 | 995 |
"properly aligned; L2 entry corrupted.\n", offset); |
995 |
errors++;
|
|
996 |
res->corruptions++;
|
|
996 | 997 |
} |
997 | 998 |
} |
998 | 999 |
} |
999 | 1000 |
} |
1000 | 1001 |
|
1001 | 1002 |
qemu_free(l2_table); |
1002 |
return errors;
|
|
1003 |
return 0;
|
|
1003 | 1004 |
|
1004 | 1005 |
fail: |
1005 |
fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n");
|
|
1006 |
fprintf(stderr, "ERROR: I/O error in check_refcounts_l2\n");
|
|
1006 | 1007 |
qemu_free(l2_table); |
1007 | 1008 |
return -EIO; |
1008 | 1009 |
} |
... | ... | |
1016 | 1017 |
* error occurred. |
1017 | 1018 |
*/ |
1018 | 1019 |
static int check_refcounts_l1(BlockDriverState *bs, |
1020 |
BdrvCheckResult *res, |
|
1019 | 1021 |
uint16_t *refcount_table, |
1020 | 1022 |
int refcount_table_size, |
1021 | 1023 |
int64_t l1_table_offset, int l1_size, |
... | ... | |
1024 | 1026 |
BDRVQcowState *s = bs->opaque; |
1025 | 1027 |
uint64_t *l1_table, l2_offset, l1_size2; |
1026 | 1028 |
int i, refcount, ret; |
1027 |
int errors = 0; |
|
1028 | 1029 |
|
1029 | 1030 |
l1_size2 = l1_size * sizeof(uint64_t); |
1030 | 1031 |
|
1031 | 1032 |
/* Mark L1 table as used */ |
1032 |
errors += inc_refcounts(bs, refcount_table, refcount_table_size,
|
|
1033 |
l1_table_offset, l1_size2);
|
|
1033 |
inc_refcounts(bs, res, refcount_table, refcount_table_size,
|
|
1034 |
l1_table_offset, l1_size2); |
|
1034 | 1035 |
|
1035 | 1036 |
/* Read L1 table entries from disk */ |
1036 | 1037 |
if (l1_size2 == 0) { |
... | ... | |
1055 | 1056 |
if (refcount < 0) { |
1056 | 1057 |
fprintf(stderr, "Can't get refcount for l2_offset %" |
1057 | 1058 |
PRIx64 ": %s\n", l2_offset, strerror(-refcount)); |
1059 |
goto fail; |
|
1058 | 1060 |
} |
1059 | 1061 |
if ((refcount == 1) != ((l2_offset & QCOW_OFLAG_COPIED) != 0)) { |
1060 | 1062 |
fprintf(stderr, "ERROR OFLAG_COPIED: l2_offset=%" PRIx64 |
1061 | 1063 |
" refcount=%d\n", l2_offset, refcount); |
1062 |
errors++;
|
|
1064 |
res->corruptions++;
|
|
1063 | 1065 |
} |
1064 | 1066 |
} |
1065 | 1067 |
|
1066 | 1068 |
/* Mark L2 table as used */ |
1067 | 1069 |
l2_offset &= ~QCOW_OFLAG_COPIED; |
1068 |
errors += inc_refcounts(bs, refcount_table, |
|
1069 |
refcount_table_size, |
|
1070 |
l2_offset, |
|
1071 |
s->cluster_size); |
|
1070 |
inc_refcounts(bs, res, refcount_table, refcount_table_size, |
|
1071 |
l2_offset, s->cluster_size); |
|
1072 | 1072 |
|
1073 | 1073 |
/* L2 tables are cluster aligned */ |
1074 | 1074 |
if (l2_offset & (s->cluster_size - 1)) { |
1075 | 1075 |
fprintf(stderr, "ERROR l2_offset=%" PRIx64 ": Table is not " |
1076 | 1076 |
"cluster aligned; L1 entry corrupted\n", l2_offset); |
1077 |
errors++;
|
|
1077 |
res->corruptions++;
|
|
1078 | 1078 |
} |
1079 | 1079 |
|
1080 | 1080 |
/* Process and check L2 entries */ |
1081 |
ret = check_refcounts_l2(bs, refcount_table, refcount_table_size,
|
|
1082 |
l2_offset, check_copied); |
|
1081 |
ret = check_refcounts_l2(bs, res, refcount_table,
|
|
1082 |
refcount_table_size, l2_offset, check_copied);
|
|
1083 | 1083 |
if (ret < 0) { |
1084 | 1084 |
goto fail; |
1085 | 1085 |
} |
1086 |
errors += ret; |
|
1087 | 1086 |
} |
1088 | 1087 |
} |
1089 | 1088 |
qemu_free(l1_table); |
1090 |
return errors;
|
|
1089 |
return 0;
|
|
1091 | 1090 |
|
1092 | 1091 |
fail: |
1093 | 1092 |
fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n"); |
1093 |
res->check_errors++; |
|
1094 | 1094 |
qemu_free(l1_table); |
1095 | 1095 |
return -EIO; |
1096 | 1096 |
} |
... | ... | |
1101 | 1101 |
* Returns 0 if no errors are found, the number of errors in case the image is |
1102 | 1102 |
* detected as corrupted, and -errno when an internal error occured. |
1103 | 1103 |
*/ |
1104 |
int qcow2_check_refcounts(BlockDriverState *bs) |
|
1104 |
int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res)
|
|
1105 | 1105 |
{ |
1106 | 1106 |
BDRVQcowState *s = bs->opaque; |
1107 | 1107 |
int64_t size; |
1108 | 1108 |
int nb_clusters, refcount1, refcount2, i; |
1109 | 1109 |
QCowSnapshot *sn; |
1110 | 1110 |
uint16_t *refcount_table; |
1111 |
int ret, errors = 0;
|
|
1111 |
int ret; |
|
1112 | 1112 |
|
1113 | 1113 |
size = bdrv_getlength(bs->file); |
1114 | 1114 |
nb_clusters = size_to_clusters(s, size); |
1115 | 1115 |
refcount_table = qemu_mallocz(nb_clusters * sizeof(uint16_t)); |
1116 | 1116 |
|
1117 | 1117 |
/* header */ |
1118 |
errors += inc_refcounts(bs, refcount_table, nb_clusters,
|
|
1119 |
0, s->cluster_size);
|
|
1118 |
inc_refcounts(bs, res, refcount_table, nb_clusters,
|
|
1119 |
0, s->cluster_size); |
|
1120 | 1120 |
|
1121 | 1121 |
/* current L1 table */ |
1122 |
ret = check_refcounts_l1(bs, refcount_table, nb_clusters, |
|
1122 |
ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
|
|
1123 | 1123 |
s->l1_table_offset, s->l1_size, 1); |
1124 | 1124 |
if (ret < 0) { |
1125 | 1125 |
return ret; |
1126 | 1126 |
} |
1127 |
errors += ret; |
|
1128 | 1127 |
|
1129 | 1128 |
/* snapshots */ |
1130 | 1129 |
for(i = 0; i < s->nb_snapshots; i++) { |
1131 | 1130 |
sn = s->snapshots + i; |
1132 |
check_refcounts_l1(bs, refcount_table, nb_clusters, |
|
1133 |
sn->l1_table_offset, sn->l1_size, 0); |
|
1131 |
ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, |
|
1132 |
sn->l1_table_offset, sn->l1_size, 0); |
|
1133 |
if (ret < 0) { |
|
1134 |
return ret; |
|
1135 |
} |
|
1134 | 1136 |
} |
1135 |
errors += inc_refcounts(bs, refcount_table, nb_clusters,
|
|
1136 |
s->snapshots_offset, s->snapshots_size);
|
|
1137 |
inc_refcounts(bs, res, refcount_table, nb_clusters,
|
|
1138 |
s->snapshots_offset, s->snapshots_size); |
|
1137 | 1139 |
|
1138 | 1140 |
/* refcount data */ |
1139 |
errors += inc_refcounts(bs, refcount_table, nb_clusters, |
|
1140 |
s->refcount_table_offset, |
|
1141 |
s->refcount_table_size * sizeof(uint64_t)); |
|
1141 |
inc_refcounts(bs, res, refcount_table, nb_clusters, |
|
1142 |
s->refcount_table_offset, |
|
1143 |
s->refcount_table_size * sizeof(uint64_t)); |
|
1144 |
|
|
1142 | 1145 |
for(i = 0; i < s->refcount_table_size; i++) { |
1143 | 1146 |
uint64_t offset, cluster; |
1144 | 1147 |
offset = s->refcount_table[i]; |
... | ... | |
1148 | 1151 |
if (offset & (s->cluster_size - 1)) { |
1149 | 1152 |
fprintf(stderr, "ERROR refcount block %d is not " |
1150 | 1153 |
"cluster aligned; refcount table entry corrupted\n", i); |
1151 |
errors++;
|
|
1154 |
res->corruptions++;
|
|
1152 | 1155 |
continue; |
1153 | 1156 |
} |
1154 | 1157 |
|
1155 | 1158 |
if (cluster >= nb_clusters) { |
1156 | 1159 |
fprintf(stderr, "ERROR refcount block %d is outside image\n", i); |
1157 |
errors++;
|
|
1160 |
res->corruptions++;
|
|
1158 | 1161 |
continue; |
1159 | 1162 |
} |
1160 | 1163 |
|
1161 | 1164 |
if (offset != 0) { |
1162 |
errors += inc_refcounts(bs, refcount_table, nb_clusters,
|
|
1163 |
offset, s->cluster_size);
|
|
1165 |
inc_refcounts(bs, res, refcount_table, nb_clusters,
|
|
1166 |
offset, s->cluster_size); |
|
1164 | 1167 |
if (refcount_table[cluster] != 1) { |
1165 | 1168 |
fprintf(stderr, "ERROR refcount block %d refcount=%d\n", |
1166 | 1169 |
i, refcount_table[cluster]); |
1170 |
res->corruptions++; |
|
1167 | 1171 |
} |
1168 | 1172 |
} |
1169 | 1173 |
} |
... | ... | |
1174 | 1178 |
if (refcount1 < 0) { |
1175 | 1179 |
fprintf(stderr, "Can't get refcount for cluster %d: %s\n", |
1176 | 1180 |
i, strerror(-refcount1)); |
1181 |
res->check_errors++; |
|
1177 | 1182 |
continue; |
1178 | 1183 |
} |
1179 | 1184 |
|
1180 | 1185 |
refcount2 = refcount_table[i]; |
1181 | 1186 |
if (refcount1 != refcount2) { |
1182 |
fprintf(stderr, "ERROR cluster %d refcount=%d reference=%d\n", |
|
1187 |
fprintf(stderr, "%s cluster %d refcount=%d reference=%d\n", |
|
1188 |
refcount1 < refcount2 ? "ERROR" : "Leaked", |
|
1183 | 1189 |
i, refcount1, refcount2); |
1184 |
errors++; |
|
1190 |
if (refcount1 < refcount2) { |
|
1191 |
res->corruptions++; |
|
1192 |
} else { |
|
1193 |
res->leaks++; |
|
1194 |
} |
|
1185 | 1195 |
} |
1186 | 1196 |
} |
1187 | 1197 |
|
1188 | 1198 |
qemu_free(refcount_table); |
1189 | 1199 |
|
1190 |
return errors;
|
|
1200 |
return 0;
|
|
1191 | 1201 |
} |
1192 | 1202 |
|
Also available in: Unified diff