root / block-vmdk.c @ a8d3431a
History | View | Annotate | Download (8.2 kB)
1 | ea2384d3 | bellard | /*
|
---|---|---|---|
2 | ea2384d3 | bellard | * Block driver for the VMDK format
|
3 | ea2384d3 | bellard | *
|
4 | ea2384d3 | bellard | * Copyright (c) 2004 Fabrice Bellard
|
5 | ea2384d3 | bellard | *
|
6 | ea2384d3 | bellard | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
7 | ea2384d3 | bellard | * of this software and associated documentation files (the "Software"), to deal
|
8 | ea2384d3 | bellard | * in the Software without restriction, including without limitation the rights
|
9 | ea2384d3 | bellard | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10 | ea2384d3 | bellard | * copies of the Software, and to permit persons to whom the Software is
|
11 | ea2384d3 | bellard | * furnished to do so, subject to the following conditions:
|
12 | ea2384d3 | bellard | *
|
13 | ea2384d3 | bellard | * The above copyright notice and this permission notice shall be included in
|
14 | ea2384d3 | bellard | * all copies or substantial portions of the Software.
|
15 | ea2384d3 | bellard | *
|
16 | ea2384d3 | bellard | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17 | ea2384d3 | bellard | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18 | ea2384d3 | bellard | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
19 | ea2384d3 | bellard | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20 | ea2384d3 | bellard | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21 | ea2384d3 | bellard | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22 | ea2384d3 | bellard | * THE SOFTWARE.
|
23 | ea2384d3 | bellard | */
|
24 | ea2384d3 | bellard | #include "vl.h" |
25 | ea2384d3 | bellard | #include "block_int.h" |
26 | ea2384d3 | bellard | |
27 | ea2384d3 | bellard | /* XXX: this code is untested */
|
28 | ea2384d3 | bellard | /* XXX: add write support */
|
29 | ea2384d3 | bellard | |
30 | ea2384d3 | bellard | #define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D') |
31 | ea2384d3 | bellard | #define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V') |
32 | ea2384d3 | bellard | |
33 | ea2384d3 | bellard | typedef struct { |
34 | ea2384d3 | bellard | uint32_t version; |
35 | ea2384d3 | bellard | uint32_t flags; |
36 | ea2384d3 | bellard | uint32_t disk_sectors; |
37 | ea2384d3 | bellard | uint32_t granularity; |
38 | ea2384d3 | bellard | uint32_t l1dir_offset; |
39 | ea2384d3 | bellard | uint32_t l1dir_size; |
40 | ea2384d3 | bellard | uint32_t file_sectors; |
41 | ea2384d3 | bellard | uint32_t cylinders; |
42 | ea2384d3 | bellard | uint32_t heads; |
43 | ea2384d3 | bellard | uint32_t sectors_per_track; |
44 | ea2384d3 | bellard | } VMDK3Header; |
45 | ea2384d3 | bellard | |
46 | ea2384d3 | bellard | typedef struct { |
47 | ea2384d3 | bellard | uint32_t version; |
48 | ea2384d3 | bellard | uint32_t flags; |
49 | ea2384d3 | bellard | int64_t capacity; |
50 | ea2384d3 | bellard | int64_t granularity; |
51 | ea2384d3 | bellard | int64_t desc_offset; |
52 | ea2384d3 | bellard | int64_t desc_size; |
53 | ea2384d3 | bellard | int32_t num_gtes_per_gte; |
54 | ea2384d3 | bellard | int64_t rgd_offset; |
55 | ea2384d3 | bellard | int64_t gd_offset; |
56 | ea2384d3 | bellard | int64_t grain_offset; |
57 | ea2384d3 | bellard | char filler[1]; |
58 | ea2384d3 | bellard | char check_bytes[4]; |
59 | ea2384d3 | bellard | } VMDK4Header; |
60 | ea2384d3 | bellard | |
61 | ea2384d3 | bellard | #define L2_CACHE_SIZE 16 |
62 | ea2384d3 | bellard | |
63 | ea2384d3 | bellard | typedef struct BDRVVmdkState { |
64 | ea2384d3 | bellard | int fd;
|
65 | ea2384d3 | bellard | int64_t l1_table_offset; |
66 | ea2384d3 | bellard | uint32_t *l1_table; |
67 | ea2384d3 | bellard | unsigned int l1_size; |
68 | ea2384d3 | bellard | uint32_t l1_entry_sectors; |
69 | ea2384d3 | bellard | |
70 | ea2384d3 | bellard | unsigned int l2_size; |
71 | ea2384d3 | bellard | uint32_t *l2_cache; |
72 | ea2384d3 | bellard | uint32_t l2_cache_offsets[L2_CACHE_SIZE]; |
73 | ea2384d3 | bellard | uint32_t l2_cache_counts[L2_CACHE_SIZE]; |
74 | ea2384d3 | bellard | |
75 | ea2384d3 | bellard | unsigned int cluster_sectors; |
76 | ea2384d3 | bellard | } BDRVVmdkState; |
77 | ea2384d3 | bellard | |
78 | ea2384d3 | bellard | static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename) |
79 | ea2384d3 | bellard | { |
80 | ea2384d3 | bellard | uint32_t magic; |
81 | ea2384d3 | bellard | |
82 | ea2384d3 | bellard | if (buf_size < 4) |
83 | ea2384d3 | bellard | return 0; |
84 | ea2384d3 | bellard | magic = be32_to_cpu(*(uint32_t *)buf); |
85 | ea2384d3 | bellard | if (magic == VMDK3_MAGIC ||
|
86 | ea2384d3 | bellard | magic == VMDK4_MAGIC) |
87 | ea2384d3 | bellard | return 100; |
88 | ea2384d3 | bellard | else
|
89 | ea2384d3 | bellard | return 0; |
90 | ea2384d3 | bellard | } |
91 | ea2384d3 | bellard | |
92 | ea2384d3 | bellard | static int vmdk_open(BlockDriverState *bs, const char *filename) |
93 | ea2384d3 | bellard | { |
94 | ea2384d3 | bellard | BDRVVmdkState *s = bs->opaque; |
95 | ea2384d3 | bellard | int fd, i;
|
96 | ea2384d3 | bellard | uint32_t magic; |
97 | ea2384d3 | bellard | int l1_size;
|
98 | ea2384d3 | bellard | |
99 | ea2384d3 | bellard | fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); |
100 | ea2384d3 | bellard | if (fd < 0) |
101 | ea2384d3 | bellard | return -1; |
102 | ea2384d3 | bellard | if (read(fd, &magic, sizeof(magic)) != sizeof(magic)) |
103 | ea2384d3 | bellard | goto fail;
|
104 | 7143c62c | bellard | magic = be32_to_cpu(magic); |
105 | ea2384d3 | bellard | if (magic == VMDK3_MAGIC) {
|
106 | ea2384d3 | bellard | VMDK3Header header; |
107 | ea2384d3 | bellard | if (read(fd, &header, sizeof(header)) != |
108 | ea2384d3 | bellard | sizeof(header))
|
109 | ea2384d3 | bellard | goto fail;
|
110 | ea2384d3 | bellard | s->cluster_sectors = le32_to_cpu(header.granularity); |
111 | ea2384d3 | bellard | s->l2_size = 1 << 9; |
112 | ea2384d3 | bellard | s->l1_size = 1 << 6; |
113 | ea2384d3 | bellard | bs->total_sectors = le32_to_cpu(header.disk_sectors); |
114 | ea2384d3 | bellard | s->l1_table_offset = le32_to_cpu(header.l1dir_offset) * 512;
|
115 | ea2384d3 | bellard | s->l1_entry_sectors = s->l2_size * s->cluster_sectors; |
116 | ea2384d3 | bellard | } else if (magic == VMDK4_MAGIC) { |
117 | ea2384d3 | bellard | VMDK4Header header; |
118 | ea2384d3 | bellard | |
119 | ea2384d3 | bellard | if (read(fd, &header, sizeof(header)) != sizeof(header)) |
120 | ea2384d3 | bellard | goto fail;
|
121 | ea2384d3 | bellard | bs->total_sectors = le32_to_cpu(header.capacity); |
122 | ea2384d3 | bellard | s->cluster_sectors = le32_to_cpu(header.granularity); |
123 | ea2384d3 | bellard | s->l2_size = le32_to_cpu(header.num_gtes_per_gte); |
124 | ea2384d3 | bellard | s->l1_entry_sectors = s->l2_size * s->cluster_sectors; |
125 | ea2384d3 | bellard | if (s->l1_entry_sectors <= 0) |
126 | ea2384d3 | bellard | goto fail;
|
127 | ea2384d3 | bellard | s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1)
|
128 | ea2384d3 | bellard | / s->l1_entry_sectors; |
129 | ea2384d3 | bellard | s->l1_table_offset = le64_to_cpu(header.rgd_offset) * 512;
|
130 | ea2384d3 | bellard | } else {
|
131 | ea2384d3 | bellard | goto fail;
|
132 | ea2384d3 | bellard | } |
133 | ea2384d3 | bellard | /* read the L1 table */
|
134 | ea2384d3 | bellard | l1_size = s->l1_size * sizeof(uint32_t);
|
135 | ea2384d3 | bellard | s->l1_table = qemu_malloc(l1_size); |
136 | ea2384d3 | bellard | if (!s->l1_table)
|
137 | ea2384d3 | bellard | goto fail;
|
138 | 7143c62c | bellard | if (lseek(fd, s->l1_table_offset, SEEK_SET) == -1) |
139 | 7143c62c | bellard | goto fail;
|
140 | 7143c62c | bellard | if (read(fd, s->l1_table, l1_size) != l1_size)
|
141 | ea2384d3 | bellard | goto fail;
|
142 | ea2384d3 | bellard | for(i = 0; i < s->l1_size; i++) { |
143 | ea2384d3 | bellard | le32_to_cpus(&s->l1_table[i]); |
144 | ea2384d3 | bellard | } |
145 | ea2384d3 | bellard | |
146 | ea2384d3 | bellard | s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
|
147 | ea2384d3 | bellard | if (!s->l2_cache)
|
148 | ea2384d3 | bellard | goto fail;
|
149 | ea2384d3 | bellard | s->fd = fd; |
150 | ea2384d3 | bellard | /* XXX: currently only read only */
|
151 | ea2384d3 | bellard | bs->read_only = 1;
|
152 | ea2384d3 | bellard | return 0; |
153 | ea2384d3 | bellard | fail:
|
154 | ea2384d3 | bellard | qemu_free(s->l1_table); |
155 | ea2384d3 | bellard | qemu_free(s->l2_cache); |
156 | ea2384d3 | bellard | close(fd); |
157 | ea2384d3 | bellard | return -1; |
158 | ea2384d3 | bellard | } |
159 | ea2384d3 | bellard | |
160 | ea2384d3 | bellard | static uint64_t get_cluster_offset(BlockDriverState *bs,
|
161 | ea2384d3 | bellard | uint64_t offset) |
162 | ea2384d3 | bellard | { |
163 | ea2384d3 | bellard | BDRVVmdkState *s = bs->opaque; |
164 | ea2384d3 | bellard | unsigned int l1_index, l2_offset, l2_index; |
165 | ea2384d3 | bellard | int min_index, i, j;
|
166 | ea2384d3 | bellard | uint32_t min_count, *l2_table; |
167 | ea2384d3 | bellard | uint64_t cluster_offset; |
168 | ea2384d3 | bellard | |
169 | ea2384d3 | bellard | l1_index = (offset >> 9) / s->l1_entry_sectors;
|
170 | ea2384d3 | bellard | if (l1_index >= s->l1_size)
|
171 | ea2384d3 | bellard | return 0; |
172 | ea2384d3 | bellard | l2_offset = s->l1_table[l1_index]; |
173 | ea2384d3 | bellard | if (!l2_offset)
|
174 | ea2384d3 | bellard | return 0; |
175 | ea2384d3 | bellard | |
176 | ea2384d3 | bellard | for(i = 0; i < L2_CACHE_SIZE; i++) { |
177 | ea2384d3 | bellard | if (l2_offset == s->l2_cache_offsets[i]) {
|
178 | ea2384d3 | bellard | /* increment the hit count */
|
179 | ea2384d3 | bellard | if (++s->l2_cache_counts[i] == 0xffffffff) { |
180 | ea2384d3 | bellard | for(j = 0; j < L2_CACHE_SIZE; j++) { |
181 | ea2384d3 | bellard | s->l2_cache_counts[j] >>= 1;
|
182 | ea2384d3 | bellard | } |
183 | ea2384d3 | bellard | } |
184 | ea2384d3 | bellard | l2_table = s->l2_cache + (i * s->l2_size); |
185 | ea2384d3 | bellard | goto found;
|
186 | ea2384d3 | bellard | } |
187 | ea2384d3 | bellard | } |
188 | ea2384d3 | bellard | /* not found: load a new entry in the least used one */
|
189 | ea2384d3 | bellard | min_index = 0;
|
190 | ea2384d3 | bellard | min_count = 0xffffffff;
|
191 | ea2384d3 | bellard | for(i = 0; i < L2_CACHE_SIZE; i++) { |
192 | ea2384d3 | bellard | if (s->l2_cache_counts[i] < min_count) {
|
193 | ea2384d3 | bellard | min_count = s->l2_cache_counts[i]; |
194 | ea2384d3 | bellard | min_index = i; |
195 | ea2384d3 | bellard | } |
196 | ea2384d3 | bellard | } |
197 | ea2384d3 | bellard | l2_table = s->l2_cache + (min_index * s->l2_size); |
198 | ea2384d3 | bellard | lseek(s->fd, (int64_t)l2_offset * 512, SEEK_SET);
|
199 | ea2384d3 | bellard | if (read(s->fd, l2_table, s->l2_size * sizeof(uint32_t)) != |
200 | ea2384d3 | bellard | s->l2_size * sizeof(uint32_t))
|
201 | ea2384d3 | bellard | return 0; |
202 | ea2384d3 | bellard | s->l2_cache_offsets[min_index] = l2_offset; |
203 | ea2384d3 | bellard | s->l2_cache_counts[min_index] = 1;
|
204 | ea2384d3 | bellard | found:
|
205 | ea2384d3 | bellard | l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size;
|
206 | ea2384d3 | bellard | cluster_offset = le32_to_cpu(l2_table[l2_index]); |
207 | ea2384d3 | bellard | cluster_offset <<= 9;
|
208 | ea2384d3 | bellard | return cluster_offset;
|
209 | ea2384d3 | bellard | } |
210 | ea2384d3 | bellard | |
211 | ea2384d3 | bellard | static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num, |
212 | ea2384d3 | bellard | int nb_sectors, int *pnum) |
213 | ea2384d3 | bellard | { |
214 | ea2384d3 | bellard | BDRVVmdkState *s = bs->opaque; |
215 | ea2384d3 | bellard | int index_in_cluster, n;
|
216 | ea2384d3 | bellard | uint64_t cluster_offset; |
217 | ea2384d3 | bellard | |
218 | ea2384d3 | bellard | cluster_offset = get_cluster_offset(bs, sector_num << 9);
|
219 | ea2384d3 | bellard | index_in_cluster = sector_num % s->cluster_sectors; |
220 | ea2384d3 | bellard | n = s->cluster_sectors - index_in_cluster; |
221 | ea2384d3 | bellard | if (n > nb_sectors)
|
222 | ea2384d3 | bellard | n = nb_sectors; |
223 | ea2384d3 | bellard | *pnum = n; |
224 | ea2384d3 | bellard | return (cluster_offset != 0); |
225 | ea2384d3 | bellard | } |
226 | ea2384d3 | bellard | |
227 | ea2384d3 | bellard | static int vmdk_read(BlockDriverState *bs, int64_t sector_num, |
228 | ea2384d3 | bellard | uint8_t *buf, int nb_sectors)
|
229 | ea2384d3 | bellard | { |
230 | ea2384d3 | bellard | BDRVVmdkState *s = bs->opaque; |
231 | ea2384d3 | bellard | int ret, index_in_cluster, n;
|
232 | ea2384d3 | bellard | uint64_t cluster_offset; |
233 | ea2384d3 | bellard | |
234 | ea2384d3 | bellard | while (nb_sectors > 0) { |
235 | ea2384d3 | bellard | cluster_offset = get_cluster_offset(bs, sector_num << 9);
|
236 | ea2384d3 | bellard | index_in_cluster = sector_num % s->cluster_sectors; |
237 | ea2384d3 | bellard | n = s->cluster_sectors - index_in_cluster; |
238 | ea2384d3 | bellard | if (n > nb_sectors)
|
239 | ea2384d3 | bellard | n = nb_sectors; |
240 | ea2384d3 | bellard | if (!cluster_offset) {
|
241 | ea2384d3 | bellard | memset(buf, 0, 512 * n); |
242 | ea2384d3 | bellard | } else {
|
243 | d5249393 | bellard | lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
|
244 | ea2384d3 | bellard | ret = read(s->fd, buf, n * 512);
|
245 | ea2384d3 | bellard | if (ret != n * 512) |
246 | ea2384d3 | bellard | return -1; |
247 | ea2384d3 | bellard | } |
248 | ea2384d3 | bellard | nb_sectors -= n; |
249 | ea2384d3 | bellard | sector_num += n; |
250 | ea2384d3 | bellard | buf += n * 512;
|
251 | ea2384d3 | bellard | } |
252 | ea2384d3 | bellard | return 0; |
253 | ea2384d3 | bellard | } |
254 | ea2384d3 | bellard | |
255 | ea2384d3 | bellard | static int vmdk_write(BlockDriverState *bs, int64_t sector_num, |
256 | ea2384d3 | bellard | const uint8_t *buf, int nb_sectors) |
257 | ea2384d3 | bellard | { |
258 | ea2384d3 | bellard | return -1; |
259 | ea2384d3 | bellard | } |
260 | ea2384d3 | bellard | |
261 | e2731add | bellard | static void vmdk_close(BlockDriverState *bs) |
262 | ea2384d3 | bellard | { |
263 | ea2384d3 | bellard | BDRVVmdkState *s = bs->opaque; |
264 | ea2384d3 | bellard | qemu_free(s->l1_table); |
265 | ea2384d3 | bellard | qemu_free(s->l2_cache); |
266 | ea2384d3 | bellard | close(s->fd); |
267 | ea2384d3 | bellard | } |
268 | ea2384d3 | bellard | |
269 | ea2384d3 | bellard | BlockDriver bdrv_vmdk = { |
270 | ea2384d3 | bellard | "vmdk",
|
271 | ea2384d3 | bellard | sizeof(BDRVVmdkState),
|
272 | ea2384d3 | bellard | vmdk_probe, |
273 | ea2384d3 | bellard | vmdk_open, |
274 | ea2384d3 | bellard | vmdk_read, |
275 | ea2384d3 | bellard | vmdk_write, |
276 | ea2384d3 | bellard | vmdk_close, |
277 | ea2384d3 | bellard | NULL, /* no create yet */ |
278 | ea2384d3 | bellard | vmdk_is_allocated, |
279 | ea2384d3 | bellard | }; |