1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#include "qemu-common.h"
26#include "block_int.h"
27#include "module.h"
28
29
30
31#define HEADER_MAGIC "Bochs Virtual HD Image"
32#define HEADER_VERSION 0x00020000
33#define HEADER_V1 0x00010000
34#define HEADER_SIZE 512
35
36#define REDOLOG_TYPE "Redolog"
37#define GROWING_TYPE "Growing"
38
39
40
41
42struct bochs_header_v1 {
43 char magic[32];
44 char type[16];
45 char subtype[16];
46 uint32_t version;
47 uint32_t header;
48
49 union {
50 struct {
51 uint32_t catalog;
52 uint32_t bitmap;
53 uint32_t extent;
54 uint64_t disk;
55 char padding[HEADER_SIZE - 64 - 8 - 20];
56 } redolog;
57 char padding[HEADER_SIZE - 64 - 8];
58 } extra;
59};
60
61
62struct bochs_header {
63 char magic[32];
64 char type[16];
65 char subtype[16];
66 uint32_t version;
67 uint32_t header;
68
69 union {
70 struct {
71 uint32_t catalog;
72 uint32_t bitmap;
73 uint32_t extent;
74 uint32_t reserved;
75 uint64_t disk;
76 char padding[HEADER_SIZE - 64 - 8 - 24];
77 } redolog;
78 char padding[HEADER_SIZE - 64 - 8];
79 } extra;
80};
81
82typedef struct BDRVBochsState {
83 int fd;
84
85 uint32_t *catalog_bitmap;
86 int catalog_size;
87
88 int data_offset;
89
90 int bitmap_blocks;
91 int extent_blocks;
92 int extent_size;
93} BDRVBochsState;
94
95static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
96{
97 const struct bochs_header *bochs = (const void *)buf;
98
99 if (buf_size < HEADER_SIZE)
100 return 0;
101
102 if (!strcmp(bochs->magic, HEADER_MAGIC) &&
103 !strcmp(bochs->type, REDOLOG_TYPE) &&
104 !strcmp(bochs->subtype, GROWING_TYPE) &&
105 ((le32_to_cpu(bochs->version) == HEADER_VERSION) ||
106 (le32_to_cpu(bochs->version) == HEADER_V1)))
107 return 100;
108
109 return 0;
110}
111
112static int bochs_open(BlockDriverState *bs, const char *filename, int flags)
113{
114 BDRVBochsState *s = bs->opaque;
115 int fd, i;
116 struct bochs_header bochs;
117 struct bochs_header_v1 header_v1;
118
119 fd = open(filename, O_RDWR | O_BINARY);
120 if (fd < 0) {
121 fd = open(filename, O_RDONLY | O_BINARY);
122 if (fd < 0)
123 return -1;
124 }
125
126 bs->read_only = 1;
127
128 s->fd = fd;
129
130 if (read(fd, &bochs, sizeof(bochs)) != sizeof(bochs)) {
131 goto fail;
132 }
133
134 if (strcmp(bochs.magic, HEADER_MAGIC) ||
135 strcmp(bochs.type, REDOLOG_TYPE) ||
136 strcmp(bochs.subtype, GROWING_TYPE) ||
137 ((le32_to_cpu(bochs.version) != HEADER_VERSION) &&
138 (le32_to_cpu(bochs.version) != HEADER_V1))) {
139 goto fail;
140 }
141
142 if (le32_to_cpu(bochs.version) == HEADER_V1) {
143 memcpy(&header_v1, &bochs, sizeof(bochs));
144 bs->total_sectors = le64_to_cpu(header_v1.extra.redolog.disk) / 512;
145 } else {
146 bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512;
147 }
148
149 lseek(s->fd, le32_to_cpu(bochs.header), SEEK_SET);
150
151 s->catalog_size = le32_to_cpu(bochs.extra.redolog.catalog);
152 s->catalog_bitmap = qemu_malloc(s->catalog_size * 4);
153 if (read(s->fd, s->catalog_bitmap, s->catalog_size * 4) !=
154 s->catalog_size * 4)
155 goto fail;
156 for (i = 0; i < s->catalog_size; i++)
157 le32_to_cpus(&s->catalog_bitmap[i]);
158
159 s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4);
160
161 s->bitmap_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.bitmap) - 1) / 512;
162 s->extent_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.extent) - 1) / 512;
163
164 s->extent_size = le32_to_cpu(bochs.extra.redolog.extent);
165
166 return 0;
167 fail:
168 close(fd);
169 return -1;
170}
171
172static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
173{
174 BDRVBochsState *s = bs->opaque;
175 int64_t offset = sector_num * 512;
176 int64_t extent_index, extent_offset, bitmap_offset, block_offset;
177 char bitmap_entry;
178
179
180 extent_index = offset / s->extent_size;
181 extent_offset = (offset % s->extent_size) / 512;
182
183 if (s->catalog_bitmap[extent_index] == 0xffffffff)
184 {
185
186
187 return -1;
188 }
189
190 bitmap_offset = s->data_offset + (512 * s->catalog_bitmap[extent_index] *
191 (s->extent_blocks + s->bitmap_blocks));
192 block_offset = bitmap_offset + (512 * (s->bitmap_blocks + extent_offset));
193
194
195
196
197
198
199
200 lseek(s->fd, bitmap_offset + (extent_offset / 8), SEEK_SET);
201
202 read(s->fd, &bitmap_entry, 1);
203
204 if (!((bitmap_entry >> (extent_offset % 8)) & 1))
205 {
206
207
208 return -1;
209 }
210
211 lseek(s->fd, block_offset, SEEK_SET);
212
213 return 0;
214}
215
216static int bochs_read(BlockDriverState *bs, int64_t sector_num,
217 uint8_t *buf, int nb_sectors)
218{
219 BDRVBochsState *s = bs->opaque;
220 int ret;
221
222 while (nb_sectors > 0) {
223 if (!seek_to_sector(bs, sector_num))
224 {
225 ret = read(s->fd, buf, 512);
226 if (ret != 512)
227 return -1;
228 }
229 else
230 memset(buf, 0, 512);
231 nb_sectors--;
232 sector_num++;
233 buf += 512;
234 }
235 return 0;
236}
237
238static void bochs_close(BlockDriverState *bs)
239{
240 BDRVBochsState *s = bs->opaque;
241 qemu_free(s->catalog_bitmap);
242 close(s->fd);
243}
244
245static BlockDriver bdrv_bochs = {
246 .format_name = "bochs",
247 .instance_size = sizeof(BDRVBochsState),
248 .bdrv_probe = bochs_probe,
249 .bdrv_open = bochs_open,
250 .bdrv_read = bochs_read,
251 .bdrv_close = bochs_close,
252};
253
254static void bdrv_bochs_init(void)
255{
256 bdrv_register(&bdrv_bochs);
257}
258
259block_init(bdrv_bochs_init);
260