1 | /** @file
|
---|
2 | *
|
---|
3 | * VBox storage devices:
|
---|
4 | * VBox VMDK container implementation
|
---|
5 | */
|
---|
6 |
|
---|
7 | /*
|
---|
8 | * Copyright (C) 2006 InnoTek Systemberatung GmbH
|
---|
9 | *
|
---|
10 | * This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
11 | * available from http://www.virtualbox.org. This file is free software;
|
---|
12 | * you can redistribute it and/or modify it under the terms of the GNU
|
---|
13 | * General Public License as published by the Free Software Foundation,
|
---|
14 | * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
|
---|
15 | * distribution. VirtualBox OSE is distributed in the hope that it will
|
---|
16 | * be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
17 | *
|
---|
18 | * If you received this file as part of a commercial VirtualBox
|
---|
19 | * distribution, then only the terms of your commercial VirtualBox
|
---|
20 | * license agreement apply instead of the previous paragraph.
|
---|
21 | *
|
---|
22 | * --------------------------------------------------------------------
|
---|
23 | *
|
---|
24 | * This code is based on:
|
---|
25 | *
|
---|
26 | * Block driver for the VMDK format
|
---|
27 | *
|
---|
28 | * Copyright (c) 2004 Fabrice Bellard
|
---|
29 | * Copyright (c) 2005 Filip Navara
|
---|
30 | *
|
---|
31 | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
---|
32 | * of this software and associated documentation files (the "Software"), to deal
|
---|
33 | * in the Software without restriction, including without limitation the rights
|
---|
34 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
---|
35 | * copies of the Software, and to permit persons to whom the Software is
|
---|
36 | * furnished to do so, subject to the following conditions:
|
---|
37 | *
|
---|
38 | * The above copyright notice and this permission notice shall be included in
|
---|
39 | * all copies or substantial portions of the Software.
|
---|
40 | *
|
---|
41 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
---|
42 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
---|
43 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
---|
44 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
---|
45 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
---|
46 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
---|
47 | * THE SOFTWARE.
|
---|
48 | */
|
---|
49 |
|
---|
50 | /*******************************************************************************
|
---|
51 | * Header Files *
|
---|
52 | *******************************************************************************/
|
---|
53 | #define LOG_GROUP LOG_GROUP_DRV_VBOXHDD
|
---|
54 | #include <VBox/pdm.h>
|
---|
55 | #include <VBox/mm.h>
|
---|
56 | #include <VBox/err.h>
|
---|
57 |
|
---|
58 | #include <VBox/log.h>
|
---|
59 | #include <iprt/alloc.h>
|
---|
60 | #include <iprt/assert.h>
|
---|
61 | #include <iprt/uuid.h>
|
---|
62 | #include <iprt/file.h>
|
---|
63 | #include <iprt/string.h>
|
---|
64 |
|
---|
65 | #include "vl_vbox.h"
|
---|
66 | #include "Builtins.h"
|
---|
67 |
|
---|
68 | /*******************************************************************************
|
---|
69 | * Constants And Macros, Structures and Typedefs *
|
---|
70 | *******************************************************************************/
|
---|
71 |
|
---|
72 | /** The Sector size.
|
---|
73 | * Currently we support only 512 bytes sectors.
|
---|
74 | */
|
---|
75 | #define VMDK_GEOMETRY_SECTOR_SIZE (512)
|
---|
76 | /** 512 = 2^^9 */
|
---|
77 | #define VMDK_GEOMETRY_SECTOR_SHIFT (9)
|
---|
78 |
|
---|
79 | #define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D')
|
---|
80 | #define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V')
|
---|
81 |
|
---|
82 | #pragma pack(1)
|
---|
83 | typedef struct {
|
---|
84 | uint32_t version;
|
---|
85 | uint32_t flags;
|
---|
86 | uint32_t disk_sectors;
|
---|
87 | uint32_t granularity;
|
---|
88 | uint32_t l1dir_offset;
|
---|
89 | uint32_t l1dir_size;
|
---|
90 | uint32_t file_sectors;
|
---|
91 | uint32_t cylinders;
|
---|
92 | uint32_t heads;
|
---|
93 | uint32_t sectors_per_track;
|
---|
94 | } VMDK3Header;
|
---|
95 | #pragma pack()
|
---|
96 |
|
---|
97 | #pragma pack(1)
|
---|
98 | typedef struct {
|
---|
99 | uint32_t version;
|
---|
100 | uint32_t flags;
|
---|
101 | int64_t capacity;
|
---|
102 | int64_t granularity;
|
---|
103 | int64_t desc_offset;
|
---|
104 | int64_t desc_size;
|
---|
105 | int32_t num_gtes_per_gte;
|
---|
106 | int64_t rgd_offset;
|
---|
107 | int64_t gd_offset;
|
---|
108 | int64_t grain_offset;
|
---|
109 | char filler[1];
|
---|
110 | char check_bytes[4];
|
---|
111 | } VMDK4Header;
|
---|
112 | #pragma pack()
|
---|
113 |
|
---|
114 | #define L2_CACHE_SIZE 16
|
---|
115 |
|
---|
116 | typedef struct BDRVVmdkState {
|
---|
117 | /** File handle. */
|
---|
118 | RTFILE File;
|
---|
119 |
|
---|
120 | bool fReadOnly;
|
---|
121 |
|
---|
122 | uint64_t total_sectors;
|
---|
123 |
|
---|
124 | int64_t l1_table_offset;
|
---|
125 | int64_t l1_backup_table_offset;
|
---|
126 | uint32_t *l1_table;
|
---|
127 | uint32_t *l1_backup_table;
|
---|
128 | unsigned int l1_size;
|
---|
129 | uint32_t l1_entry_sectors;
|
---|
130 |
|
---|
131 | unsigned int l2_size;
|
---|
132 | uint32_t *l2_cache;
|
---|
133 | uint32_t l2_cache_offsets[L2_CACHE_SIZE];
|
---|
134 | uint32_t l2_cache_counts[L2_CACHE_SIZE];
|
---|
135 |
|
---|
136 | unsigned int cluster_sectors;
|
---|
137 | } BDRVVmdkState;
|
---|
138 |
|
---|
139 |
|
---|
140 | #define VMDKDISK_SIGNATURE 0x8013ABCD
|
---|
141 |
|
---|
142 |
|
---|
143 | /**
|
---|
144 | * Harddisk geometry.
|
---|
145 | */
|
---|
146 | #pragma pack(1)
|
---|
147 | typedef struct VMDKDISKGEOMETRY
|
---|
148 | {
|
---|
149 | /** Cylinders. */
|
---|
150 | uint32_t cCylinders;
|
---|
151 | /** Heads. */
|
---|
152 | uint32_t cHeads;
|
---|
153 | /** Sectors per track. */
|
---|
154 | uint32_t cSectors;
|
---|
155 | /** Sector size. (bytes per sector) */
|
---|
156 | uint32_t cbSector;
|
---|
157 | } VMDKDISKGEOMETRY, *PVMDKDISKGEOMETRY;
|
---|
158 | #pragma pack()
|
---|
159 |
|
---|
160 | /**
|
---|
161 | * VMDK HDD Container main structure, private part.
|
---|
162 | */
|
---|
163 | typedef struct VMDKDISK
|
---|
164 | {
|
---|
165 | uint32_t u32Signature;
|
---|
166 |
|
---|
167 | BDRVVmdkState VmdkState;
|
---|
168 |
|
---|
169 | /** Hard disk geometry. */
|
---|
170 | VMDKDISKGEOMETRY Geometry;
|
---|
171 |
|
---|
172 | /** The media interface. */
|
---|
173 | PDMIMEDIA IMedia;
|
---|
174 | /** Pointer to the driver instance. */
|
---|
175 | PPDMDRVINS pDrvIns;
|
---|
176 | } VMDKDISK, *PVMDKDISK;
|
---|
177 |
|
---|
178 |
|
---|
179 | /** Converts a pointer to VDIDISK::IMedia to a PVMDKDISK. */
|
---|
180 | #define PDMIMEDIA_2_VMDKDISK(pInterface) ( (PVMDKDISK)((uintptr_t)pInterface - RT_OFFSETOF(VMDKDISK, IMedia)) )
|
---|
181 |
|
---|
182 | /** Converts a pointer to PDMDRVINS::IBase to a PPDMDRVINS. */
|
---|
183 | #define PDMIBASE_2_DRVINS(pInterface) ( (PPDMDRVINS)((uintptr_t)pInterface - RT_OFFSETOF(PDMDRVINS, IBase)) )
|
---|
184 |
|
---|
185 | /** Converts a pointer to PDMDRVINS::IBase to a PVMDKDISK. */
|
---|
186 | #define PDMIBASE_2_VMDKDISK(pInterface) ( PDMINS2DATA(PDMIBASE_2_DRVINS(pInterface), PVMDKDISK) )
|
---|
187 |
|
---|
188 |
|
---|
189 | /*******************************************************************************
|
---|
190 | * Internal Functions *
|
---|
191 | *******************************************************************************/
|
---|
192 | static DECLCALLBACK(int) vmdkConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle);
|
---|
193 | static DECLCALLBACK(void) vmdkDestruct(PPDMDRVINS pDrvIns);
|
---|
194 | static DECLCALLBACK(int) vmdkRead(PPDMIMEDIA pInterface,
|
---|
195 | uint64_t off, void *pvBuf, size_t cbRead);
|
---|
196 | static DECLCALLBACK(int) vmdkWrite(PPDMIMEDIA pInterface,
|
---|
197 | uint64_t off, const void *pvBuf, size_t cbWrite);
|
---|
198 | static DECLCALLBACK(int) vmdkFlush(PPDMIMEDIA pInterface);
|
---|
199 | static DECLCALLBACK(uint64_t) vmdkGetSize(PPDMIMEDIA pInterface);
|
---|
200 | static DECLCALLBACK(int) vmdkBiosGetGeometry(PPDMIMEDIA pInterface, uint32_t *pcCylinders,
|
---|
201 | uint32_t *pcHeads, uint32_t *pcSectors);
|
---|
202 | static DECLCALLBACK(int) vmdkBiosSetGeometry(PPDMIMEDIA pInterface, uint32_t cCylinders,
|
---|
203 | uint32_t cHeads, uint32_t cSectors);
|
---|
204 | static DECLCALLBACK(int) vmdkGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid);
|
---|
205 | static DECLCALLBACK(bool) vmdkIsReadOnly(PPDMIMEDIA pInterface);
|
---|
206 | static DECLCALLBACK(int) vmdkBiosGetTranslation(PPDMIMEDIA pInterface,
|
---|
207 | PPDMBIOSTRANSLATION penmTranslation);
|
---|
208 | static DECLCALLBACK(int) vmdkBiosSetTranslation(PPDMIMEDIA pInterface,
|
---|
209 | PDMBIOSTRANSLATION enmTranslation);
|
---|
210 | static DECLCALLBACK(void *) vmdkQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface);
|
---|
211 |
|
---|
212 | static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename)
|
---|
213 | {
|
---|
214 | uint32_t magic;
|
---|
215 |
|
---|
216 | if (buf_size < 4)
|
---|
217 | return 0;
|
---|
218 | magic = be32_to_cpu(*(uint32_t *)buf);
|
---|
219 | if (magic == VMDK3_MAGIC ||
|
---|
220 | magic == VMDK4_MAGIC)
|
---|
221 | return 100;
|
---|
222 | else
|
---|
223 | return 0;
|
---|
224 | }
|
---|
225 |
|
---|
226 | static int vmdk_open(BDRVVmdkState *s, const char *filename, bool fReadOnly)
|
---|
227 | {
|
---|
228 | uint32_t magic, i;
|
---|
229 | int l1_size;
|
---|
230 |
|
---|
231 | /*
|
---|
232 | * Open the image.
|
---|
233 | */
|
---|
234 | s->fReadOnly = fReadOnly;
|
---|
235 | int rc = RTFileOpen(&s->File,
|
---|
236 | filename,
|
---|
237 | fReadOnly
|
---|
238 | ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
|
---|
239 | : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
|
---|
240 | if (VBOX_FAILURE(rc))
|
---|
241 | {
|
---|
242 | if (!fReadOnly)
|
---|
243 | {
|
---|
244 | /* Try to open image for reading only. */
|
---|
245 | rc = RTFileOpen(&s->File,
|
---|
246 | filename,
|
---|
247 | RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
|
---|
248 | if (VBOX_SUCCESS(rc))
|
---|
249 | s->fReadOnly = true;
|
---|
250 | }
|
---|
251 | if (VBOX_FAILURE(rc))
|
---|
252 | return rc;
|
---|
253 | }
|
---|
254 | rc = RTFileRead(s->File, &magic, sizeof(magic), NULL);
|
---|
255 | AssertRC(rc);
|
---|
256 | if (VBOX_FAILURE(rc))
|
---|
257 | goto fail;
|
---|
258 |
|
---|
259 | magic = be32_to_cpu(magic);
|
---|
260 | if (magic == VMDK3_MAGIC) {
|
---|
261 | VMDK3Header header;
|
---|
262 | rc = RTFileRead(s->File, &header, sizeof(header), NULL);
|
---|
263 | AssertRC(rc);
|
---|
264 | if (VBOX_FAILURE(rc))
|
---|
265 | goto fail;
|
---|
266 | s->cluster_sectors = le32_to_cpu(header.granularity);
|
---|
267 | s->l2_size = 1 << 9;
|
---|
268 | s->l1_size = 1 << 6;
|
---|
269 | s->total_sectors = le32_to_cpu(header.disk_sectors);
|
---|
270 | s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9;
|
---|
271 | s->l1_backup_table_offset = 0;
|
---|
272 | s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
|
---|
273 | } else if (magic == VMDK4_MAGIC) {
|
---|
274 | VMDK4Header header;
|
---|
275 |
|
---|
276 | rc = RTFileRead(s->File, &header, sizeof(header), NULL);
|
---|
277 | AssertRC(rc);
|
---|
278 | if (VBOX_FAILURE(rc))
|
---|
279 | goto fail;
|
---|
280 | s->total_sectors = le64_to_cpu(header.capacity);
|
---|
281 | s->cluster_sectors = le64_to_cpu(header.granularity);
|
---|
282 | s->l2_size = le32_to_cpu(header.num_gtes_per_gte);
|
---|
283 | s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
|
---|
284 | if (s->l1_entry_sectors <= 0)
|
---|
285 | {
|
---|
286 | rc = VERR_VDI_INVALID_HEADER;
|
---|
287 | goto fail;
|
---|
288 | }
|
---|
289 | s->l1_size = (s->total_sectors + s->l1_entry_sectors - 1)
|
---|
290 | / s->l1_entry_sectors;
|
---|
291 | s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9;
|
---|
292 | s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9;
|
---|
293 | } else {
|
---|
294 | rc = VERR_VDI_INVALID_HEADER;
|
---|
295 | goto fail;
|
---|
296 | }
|
---|
297 | /* read the L1 table */
|
---|
298 | l1_size = s->l1_size * sizeof(uint32_t);
|
---|
299 | s->l1_table = (uint32_t *)RTMemAllocZ(l1_size);
|
---|
300 | if (!s->l1_table)
|
---|
301 | {
|
---|
302 | rc = VERR_NO_MEMORY;
|
---|
303 | goto fail;
|
---|
304 | }
|
---|
305 | rc = RTFileSeek(s->File, s->l1_table_offset, RTFILE_SEEK_BEGIN, NULL);
|
---|
306 | AssertRC(rc);
|
---|
307 | if (VBOX_FAILURE(rc))
|
---|
308 | goto fail;
|
---|
309 | rc = RTFileRead(s->File, s->l1_table, l1_size, NULL);
|
---|
310 | AssertRC(rc);
|
---|
311 | if (VBOX_FAILURE(rc))
|
---|
312 | goto fail;
|
---|
313 | for(i = 0; i < s->l1_size; i++) {
|
---|
314 | le32_to_cpus(&s->l1_table[i]);
|
---|
315 | }
|
---|
316 |
|
---|
317 | if (s->l1_backup_table_offset) {
|
---|
318 | s->l1_backup_table = (uint32_t *)RTMemAllocZ(l1_size);
|
---|
319 | if (!s->l1_backup_table)
|
---|
320 | {
|
---|
321 | rc = VERR_NO_MEMORY;
|
---|
322 | goto fail;
|
---|
323 | }
|
---|
324 | rc = RTFileSeek(s->File, s->l1_backup_table_offset, RTFILE_SEEK_BEGIN, NULL);
|
---|
325 | AssertRC(rc);
|
---|
326 | if (VBOX_FAILURE(rc))
|
---|
327 | goto fail;
|
---|
328 | rc = RTFileRead(s->File, s->l1_backup_table, l1_size, NULL);
|
---|
329 | AssertRC(rc);
|
---|
330 | if (VBOX_FAILURE(rc))
|
---|
331 | goto fail;
|
---|
332 | for(i = 0; i < s->l1_size; i++) {
|
---|
333 | le32_to_cpus(&s->l1_backup_table[i]);
|
---|
334 | }
|
---|
335 | }
|
---|
336 |
|
---|
337 | s->l2_cache = (uint32_t *)RTMemAllocZ(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
|
---|
338 | if (!s->l2_cache)
|
---|
339 | {
|
---|
340 | rc = VERR_NO_MEMORY;
|
---|
341 | goto fail;
|
---|
342 | }
|
---|
343 |
|
---|
344 | return VINF_SUCCESS;
|
---|
345 |
|
---|
346 | fail:
|
---|
347 | Log(("vmdk_open failed with %Vrc\n", rc));
|
---|
348 | if (s->l1_backup_table)
|
---|
349 | RTMemFree(s->l1_backup_table);
|
---|
350 | if (s->l1_table)
|
---|
351 | RTMemFree(s->l1_table);
|
---|
352 | if (s->l2_cache)
|
---|
353 | RTMemFree(s->l2_cache);
|
---|
354 | RTFileClose(s->File);
|
---|
355 | return rc;
|
---|
356 | }
|
---|
357 |
|
---|
358 | static uint64_t get_cluster_offset(BDRVVmdkState *s,
|
---|
359 | uint64_t offset, int allocate)
|
---|
360 | {
|
---|
361 | unsigned int l1_index, l2_offset, l2_index;
|
---|
362 | int min_index, i, j;
|
---|
363 | uint32_t min_count, *l2_table, tmp;
|
---|
364 | uint64_t cluster_offset;
|
---|
365 | int rc;
|
---|
366 |
|
---|
367 | l1_index = (offset >> 9) / s->l1_entry_sectors;
|
---|
368 | if (l1_index >= s->l1_size)
|
---|
369 | return 0;
|
---|
370 | l2_offset = s->l1_table[l1_index];
|
---|
371 | if (!l2_offset)
|
---|
372 | return 0;
|
---|
373 | for(i = 0; i < L2_CACHE_SIZE; i++) {
|
---|
374 | if (l2_offset == s->l2_cache_offsets[i]) {
|
---|
375 | /* increment the hit count */
|
---|
376 | if (++s->l2_cache_counts[i] == 0xffffffff) {
|
---|
377 | for(j = 0; j < L2_CACHE_SIZE; j++) {
|
---|
378 | s->l2_cache_counts[j] >>= 1;
|
---|
379 | }
|
---|
380 | }
|
---|
381 | l2_table = s->l2_cache + (i * s->l2_size);
|
---|
382 | goto found;
|
---|
383 | }
|
---|
384 | }
|
---|
385 | /* not found: load a new entry in the least used one */
|
---|
386 | min_index = 0;
|
---|
387 | min_count = 0xffffffff;
|
---|
388 | for(i = 0; i < L2_CACHE_SIZE; i++) {
|
---|
389 | if (s->l2_cache_counts[i] < min_count) {
|
---|
390 | min_count = s->l2_cache_counts[i];
|
---|
391 | min_index = i;
|
---|
392 | }
|
---|
393 | }
|
---|
394 | l2_table = s->l2_cache + (min_index * s->l2_size);
|
---|
395 | rc = RTFileSeek(s->File, (int64_t)l2_offset * VMDK_GEOMETRY_SECTOR_SIZE, RTFILE_SEEK_BEGIN, NULL);
|
---|
396 | AssertRC(rc);
|
---|
397 | if (VBOX_FAILURE(rc))
|
---|
398 | return 0;
|
---|
399 | rc = RTFileRead(s->File, l2_table, s->l2_size * sizeof(uint32_t), NULL);
|
---|
400 | AssertRC(rc);
|
---|
401 | if (VBOX_FAILURE(rc))
|
---|
402 | return 0;
|
---|
403 | s->l2_cache_offsets[min_index] = l2_offset;
|
---|
404 | s->l2_cache_counts[min_index] = 1;
|
---|
405 | found:
|
---|
406 | l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size;
|
---|
407 | cluster_offset = le32_to_cpu(l2_table[l2_index]);
|
---|
408 | if (!cluster_offset) {
|
---|
409 | if (!allocate)
|
---|
410 | return 0;
|
---|
411 | rc = RTFileSeek(s->File, 0, RTFILE_SEEK_END, &cluster_offset);
|
---|
412 | AssertRC(rc);
|
---|
413 | if (VBOX_FAILURE(rc))
|
---|
414 | return 0;
|
---|
415 | rc = RTFileSetSize(s->File, cluster_offset + (s->cluster_sectors << 9));
|
---|
416 | AssertRC(rc);
|
---|
417 | if (VBOX_FAILURE(rc))
|
---|
418 | return 0;
|
---|
419 | cluster_offset >>= 9;
|
---|
420 | /* update L2 table */
|
---|
421 | tmp = cpu_to_le32(cluster_offset);
|
---|
422 | l2_table[l2_index] = tmp;
|
---|
423 | rc = RTFileSeek(s->File, ((int64_t)l2_offset * VMDK_GEOMETRY_SECTOR_SIZE) + (l2_index * sizeof(tmp)), RTFILE_SEEK_BEGIN, NULL);
|
---|
424 | AssertRC(rc);
|
---|
425 | if (VBOX_FAILURE(rc))
|
---|
426 | return 0;
|
---|
427 | rc = RTFileWrite(s->File, &tmp, sizeof(tmp), NULL);
|
---|
428 | AssertRC(rc);
|
---|
429 | if (VBOX_FAILURE(rc))
|
---|
430 | return 0;
|
---|
431 | /* update backup L2 table */
|
---|
432 | if (s->l1_backup_table_offset != 0) {
|
---|
433 | l2_offset = s->l1_backup_table[l1_index];
|
---|
434 |
|
---|
435 | rc = RTFileSeek(s->File, ((int64_t)l2_offset * VMDK_GEOMETRY_SECTOR_SIZE) + (l2_index * sizeof(tmp)), RTFILE_SEEK_BEGIN, NULL);
|
---|
436 | AssertRC(rc);
|
---|
437 | if (VBOX_FAILURE(rc))
|
---|
438 | return 0;
|
---|
439 | rc = RTFileWrite(s->File, &tmp, sizeof(tmp), NULL);
|
---|
440 | AssertRC(rc);
|
---|
441 | if (VBOX_FAILURE(rc))
|
---|
442 | return 0;
|
---|
443 | }
|
---|
444 | }
|
---|
445 | cluster_offset <<= 9;
|
---|
446 | return cluster_offset;
|
---|
447 | }
|
---|
448 |
|
---|
449 | static int vmdk_is_allocated(BDRVVmdkState *s, int64_t sector_num,
|
---|
450 | int nb_sectors, int *pnum)
|
---|
451 | {
|
---|
452 | int index_in_cluster, n;
|
---|
453 | uint64_t cluster_offset;
|
---|
454 |
|
---|
455 | cluster_offset = get_cluster_offset(s, sector_num << 9, 0);
|
---|
456 | index_in_cluster = sector_num % s->cluster_sectors;
|
---|
457 | n = s->cluster_sectors - index_in_cluster;
|
---|
458 | if (n > nb_sectors)
|
---|
459 | n = nb_sectors;
|
---|
460 | *pnum = n;
|
---|
461 | return (cluster_offset != 0);
|
---|
462 | }
|
---|
463 |
|
---|
464 | static int vmdk_read(BDRVVmdkState *s, int64_t sector_num,
|
---|
465 | uint8_t *buf, int nb_sectors)
|
---|
466 | {
|
---|
467 | int index_in_cluster, n;
|
---|
468 | uint64_t cluster_offset;
|
---|
469 |
|
---|
470 | while (nb_sectors > 0) {
|
---|
471 | cluster_offset = get_cluster_offset(s, sector_num << 9, 0);
|
---|
472 | index_in_cluster = sector_num % s->cluster_sectors;
|
---|
473 | n = s->cluster_sectors - index_in_cluster;
|
---|
474 | if (n > nb_sectors)
|
---|
475 | n = nb_sectors;
|
---|
476 | if (!cluster_offset) {
|
---|
477 | memset(buf, 0, VMDK_GEOMETRY_SECTOR_SIZE * n);
|
---|
478 | } else {
|
---|
479 | int rc = RTFileSeek(s->File, cluster_offset + index_in_cluster * VMDK_GEOMETRY_SECTOR_SIZE, RTFILE_SEEK_BEGIN, NULL);
|
---|
480 | AssertRC(rc);
|
---|
481 | if (VBOX_FAILURE(rc))
|
---|
482 | return rc;
|
---|
483 |
|
---|
484 | rc = RTFileRead(s->File, buf, n * VMDK_GEOMETRY_SECTOR_SIZE, NULL);
|
---|
485 | AssertRC(rc);
|
---|
486 | if (VBOX_FAILURE(rc))
|
---|
487 | return rc;
|
---|
488 | }
|
---|
489 | nb_sectors -= n;
|
---|
490 | sector_num += n;
|
---|
491 | buf += n * VMDK_GEOMETRY_SECTOR_SIZE;
|
---|
492 | }
|
---|
493 | return VINF_SUCCESS;
|
---|
494 | }
|
---|
495 |
|
---|
496 | static int vmdk_write(BDRVVmdkState *s, int64_t sector_num,
|
---|
497 | const uint8_t *buf, int nb_sectors)
|
---|
498 | {
|
---|
499 | int index_in_cluster, n;
|
---|
500 | uint64_t cluster_offset;
|
---|
501 |
|
---|
502 | while (nb_sectors > 0) {
|
---|
503 | index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
---|
504 | n = s->cluster_sectors - index_in_cluster;
|
---|
505 | if (n > nb_sectors)
|
---|
506 | n = nb_sectors;
|
---|
507 | cluster_offset = get_cluster_offset(s, sector_num << 9, 1);
|
---|
508 | if (!cluster_offset)
|
---|
509 | return VERR_IO_SECTOR_NOT_FOUND;
|
---|
510 |
|
---|
511 | int rc = RTFileSeek(s->File, cluster_offset + index_in_cluster * VMDK_GEOMETRY_SECTOR_SIZE, RTFILE_SEEK_BEGIN, NULL);
|
---|
512 | AssertRC(rc);
|
---|
513 | if (VBOX_FAILURE(rc))
|
---|
514 | return rc;
|
---|
515 |
|
---|
516 | rc = RTFileWrite(s->File, buf, n * VMDK_GEOMETRY_SECTOR_SIZE, NULL);
|
---|
517 | AssertRC(rc);
|
---|
518 | if (VBOX_FAILURE(rc))
|
---|
519 | return rc;
|
---|
520 |
|
---|
521 | nb_sectors -= n;
|
---|
522 | sector_num += n;
|
---|
523 | buf += n * VMDK_GEOMETRY_SECTOR_SIZE;
|
---|
524 | }
|
---|
525 | return VINF_SUCCESS;
|
---|
526 | }
|
---|
527 |
|
---|
528 | static void vmdk_close(BDRVVmdkState *s)
|
---|
529 | {
|
---|
530 | RTMemFree(s->l1_table);
|
---|
531 | RTMemFree(s->l2_cache);
|
---|
532 | RTFileClose(s->File);
|
---|
533 | }
|
---|
534 |
|
---|
535 | static void vmdk_flush(BDRVVmdkState *s)
|
---|
536 | {
|
---|
537 | RTFileFlush(s->File);
|
---|
538 | }
|
---|
539 |
|
---|
540 |
|
---|
541 | /**
|
---|
542 | * Get read/write mode of VMDK HDD.
|
---|
543 | *
|
---|
544 | * @returns Disk ReadOnly status.
|
---|
545 | * @returns true if no one VMDK image is opened in HDD container.
|
---|
546 | */
|
---|
547 | IDER3DECL(bool) VMDKDiskIsReadOnly(PVMDKDISK pDisk)
|
---|
548 | {
|
---|
549 | /* sanity check */
|
---|
550 | Assert(pDisk);
|
---|
551 | AssertMsg(pDisk->u32Signature == VMDKDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
|
---|
552 |
|
---|
553 | LogFlow(("VmdkDiskIsReadOnly: returns %u\n", pDisk->VmdkState.fReadOnly));
|
---|
554 | return pDisk->VmdkState.fReadOnly;
|
---|
555 | }
|
---|
556 |
|
---|
557 |
|
---|
558 | /**
|
---|
559 | * Get disk size of VMDK HDD container.
|
---|
560 | *
|
---|
561 | * @returns Virtual disk size in bytes.
|
---|
562 | * @returns 0 if no one VMDK image is opened in HDD container.
|
---|
563 | */
|
---|
564 | IDER3DECL(uint64_t) VMDKDiskGetSize(PVMDKDISK pDisk)
|
---|
565 | {
|
---|
566 | /* sanity check */
|
---|
567 | Assert(pDisk);
|
---|
568 | AssertMsg(pDisk->u32Signature == VMDKDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
|
---|
569 |
|
---|
570 | return pDisk->VmdkState.total_sectors * VMDK_GEOMETRY_SECTOR_SIZE;
|
---|
571 | }
|
---|
572 |
|
---|
573 | /**
|
---|
574 | * Get block size of VMDK HDD container.
|
---|
575 | *
|
---|
576 | * @returns VDI image block size in bytes.
|
---|
577 | * @returns 0 if no one VMDK image is opened in HDD container.
|
---|
578 | */
|
---|
579 | IDER3DECL(unsigned) VMDKDiskGetBlockSize(PVMDKDISK pDisk)
|
---|
580 | {
|
---|
581 | /* sanity check */
|
---|
582 | Assert(pDisk);
|
---|
583 | AssertMsg(pDisk->u32Signature == VMDKDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
|
---|
584 |
|
---|
585 | return VMDK_GEOMETRY_SECTOR_SIZE;
|
---|
586 | }
|
---|
587 |
|
---|
588 | /**
|
---|
589 | * Get virtual disk geometry stored in image file.
|
---|
590 | *
|
---|
591 | * @returns VBox status code.
|
---|
592 | * @param pDisk Pointer to VMDK HDD container.
|
---|
593 | * @param pcCylinders Where to store the number of cylinders. NULL is ok.
|
---|
594 | * @param pcHeads Where to store the number of heads. NULL is ok.
|
---|
595 | * @param pcSectors Where to store the number of sectors. NULL is ok.
|
---|
596 | */
|
---|
597 | IDER3DECL(int) VMDKDiskGetGeometry(PVMDKDISK pDisk, unsigned *pcCylinders, unsigned *pcHeads, unsigned *pcSectors)
|
---|
598 | {
|
---|
599 | /* sanity check */
|
---|
600 | Assert(pDisk);
|
---|
601 | AssertMsg(pDisk->u32Signature == VMDKDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
|
---|
602 |
|
---|
603 | /** @todo not initialized */
|
---|
604 | if (pcCylinders)
|
---|
605 | *pcCylinders = pDisk->Geometry.cCylinders;
|
---|
606 | if (pcHeads)
|
---|
607 | *pcHeads = pDisk->Geometry.cHeads;
|
---|
608 | if (pcSectors)
|
---|
609 | *pcSectors = pDisk->Geometry.cSectors;
|
---|
610 | return VINF_SUCCESS;
|
---|
611 | }
|
---|
612 |
|
---|
613 | /**
|
---|
614 | * Store virtual disk geometry into base image file of HDD container.
|
---|
615 | *
|
---|
616 | * Note that in case of unrecoverable error all images of HDD container will be closed.
|
---|
617 | *
|
---|
618 | * @returns VBox status code.
|
---|
619 | * @param pDisk Pointer to VMDK HDD container.
|
---|
620 | * @param cCylinders Number of cylinders.
|
---|
621 | * @param cHeads Number of heads.
|
---|
622 | * @param cSectors Number of sectors.
|
---|
623 | */
|
---|
624 | IDER3DECL(int) VMDKDiskSetGeometry(PVMDKDISK pDisk, unsigned cCylinders, unsigned cHeads, unsigned cSectors)
|
---|
625 | {
|
---|
626 | LogFlow(("VMDKDiskSetGeometry: C/H/S = %u/%u/%u\n", cCylinders, cHeads, cSectors));
|
---|
627 | /* sanity check */
|
---|
628 | Assert(pDisk);
|
---|
629 | AssertMsg(pDisk->u32Signature == VMDKDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
|
---|
630 |
|
---|
631 | pDisk->Geometry.cCylinders = cCylinders;
|
---|
632 | pDisk->Geometry.cHeads = cHeads;
|
---|
633 | pDisk->Geometry.cSectors = cSectors;
|
---|
634 | pDisk->Geometry.cbSector = VMDK_GEOMETRY_SECTOR_SIZE;
|
---|
635 |
|
---|
636 | /** @todo Update header information in base image file. */
|
---|
637 | return VINF_SUCCESS;
|
---|
638 | }
|
---|
639 |
|
---|
640 | /**
|
---|
641 | * Get number of opened images in HDD container.
|
---|
642 | *
|
---|
643 | * @returns Number of opened images for HDD container. 0 if no images is opened.
|
---|
644 | * @param pDisk Pointer to VMDK HDD container.
|
---|
645 | */
|
---|
646 | IDER3DECL(int) VMDKDiskGetImagesCount(PVMDKDISK pDisk)
|
---|
647 | {
|
---|
648 | /* sanity check */
|
---|
649 | Assert(pDisk);
|
---|
650 | AssertMsg(pDisk->u32Signature == VMDKDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
|
---|
651 |
|
---|
652 | return 1;
|
---|
653 | }
|
---|
654 |
|
---|
655 | /*******************************************************************************
|
---|
656 | * PDM interface *
|
---|
657 | *******************************************************************************/
|
---|
658 |
|
---|
659 | /**
|
---|
660 | * Construct a VBox HDD media driver instance.
|
---|
661 | *
|
---|
662 | * @returns VBox status.
|
---|
663 | * @param pDrvIns The driver instance data.
|
---|
664 | * If the registration structure is needed, pDrvIns->pDrvReg points to it.
|
---|
665 | * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
|
---|
666 | * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
|
---|
667 | * iInstance it's expected to be used a bit in this function.
|
---|
668 | */
|
---|
669 | static DECLCALLBACK(int) vmdkConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
|
---|
670 | {
|
---|
671 | LogFlow(("vmdkConstruct:\n"));
|
---|
672 | PVMDKDISK pData = PDMINS2DATA(pDrvIns, PVMDKDISK);
|
---|
673 |
|
---|
674 | /*
|
---|
675 | * Init the static parts.
|
---|
676 | */
|
---|
677 | pDrvIns->IBase.pfnQueryInterface = vmdkQueryInterface;
|
---|
678 | pData->pDrvIns = pDrvIns;
|
---|
679 |
|
---|
680 | /* IMedia */
|
---|
681 | pData->IMedia.pfnRead = vmdkRead;
|
---|
682 | pData->IMedia.pfnWrite = vmdkWrite;
|
---|
683 | pData->IMedia.pfnFlush = vmdkFlush;
|
---|
684 | pData->IMedia.pfnGetSize = vmdkGetSize;
|
---|
685 | pData->IMedia.pfnGetUuid = vmdkGetUuid;
|
---|
686 | pData->IMedia.pfnIsReadOnly = vmdkIsReadOnly;
|
---|
687 | pData->IMedia.pfnBiosGetGeometry = vmdkBiosGetGeometry;
|
---|
688 | pData->IMedia.pfnBiosSetGeometry = vmdkBiosSetGeometry;
|
---|
689 | pData->IMedia.pfnBiosGetTranslation = vmdkBiosGetTranslation;
|
---|
690 | pData->IMedia.pfnBiosSetTranslation = vmdkBiosSetTranslation;
|
---|
691 |
|
---|
692 | /*
|
---|
693 | * Validate and read top level configuration.
|
---|
694 | */
|
---|
695 | char *pszName;
|
---|
696 | int rc = CFGMR3QueryStringAlloc(pCfgHandle, "Path", &pszName);
|
---|
697 | if (VBOX_FAILURE(rc))
|
---|
698 | return PDMDRV_SET_ERROR(pDrvIns, rc,
|
---|
699 | N_("VHDD: Configuration error: Querying \"Path\" as string failed"));
|
---|
700 |
|
---|
701 | /** True if the media is readonly. */
|
---|
702 | bool fReadOnly;
|
---|
703 | rc = CFGMR3QueryBool(pCfgHandle, "ReadOnly", &fReadOnly);
|
---|
704 | if (rc == VERR_CFGM_VALUE_NOT_FOUND)
|
---|
705 | fReadOnly = false;
|
---|
706 | else if (VBOX_FAILURE(rc))
|
---|
707 | {
|
---|
708 | MMR3HeapFree(pszName);
|
---|
709 | return PDMDRV_SET_ERROR(pDrvIns, rc,
|
---|
710 | N_("VHDD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
|
---|
711 | }
|
---|
712 |
|
---|
713 | /*
|
---|
714 | * Open the image.
|
---|
715 | */
|
---|
716 | rc = vmdk_open(&pData->VmdkState, pszName, fReadOnly);
|
---|
717 | if (VBOX_SUCCESS(rc))
|
---|
718 | Log(("vmdkConstruct: Opened '%s' in %s mode\n", pszName, VMDKDiskIsReadOnly(pData) ? "read-only" : "read-write"));
|
---|
719 | else
|
---|
720 | AssertMsgFailed(("Failed to open image '%s' rc=%Vrc\n", pszName, rc));
|
---|
721 |
|
---|
722 | MMR3HeapFree(pszName);
|
---|
723 | pszName = NULL;
|
---|
724 |
|
---|
725 | return rc;
|
---|
726 | }
|
---|
727 |
|
---|
728 | /**
|
---|
729 | * Destruct a driver instance.
|
---|
730 | *
|
---|
731 | * Most VM resources are freed by the VM. This callback is provided so that any non-VM
|
---|
732 | * resources can be freed correctly.
|
---|
733 | *
|
---|
734 | * @param pDrvIns The driver instance data.
|
---|
735 | */
|
---|
736 | static DECLCALLBACK(void) vmdkDestruct(PPDMDRVINS pDrvIns)
|
---|
737 | {
|
---|
738 | LogFlow(("vmdkDestruct:\n"));
|
---|
739 | PVMDKDISK pData = PDMINS2DATA(pDrvIns, PVMDKDISK);
|
---|
740 | vmdk_close(&pData->VmdkState);
|
---|
741 | }
|
---|
742 |
|
---|
743 | /**
|
---|
744 | * When the VM has been suspended we'll change the image mode to read-only
|
---|
745 | * so that main and others can read the VDIs. This is important when
|
---|
746 | * saving state and so forth.
|
---|
747 | *
|
---|
748 | * @param pDrvIns The driver instance data.
|
---|
749 | */
|
---|
750 | static DECLCALLBACK(void) vmdkSuspend(PPDMDRVINS pDrvIns)
|
---|
751 | {
|
---|
752 | LogFlow(("vmdkSuspend:\n"));
|
---|
753 | PVMDKDISK pData = PDMINS2DATA(pDrvIns, PVMDKDISK);
|
---|
754 | if (!VMDKDiskIsReadOnly(pData))
|
---|
755 | {
|
---|
756 | /** @todo does this even make sense? the vdi method locks the whole file, but don't we close it afterwards?? */
|
---|
757 | //int rc = vmdkChangeImageMode(pData, true);
|
---|
758 | //AssertRC(rc);
|
---|
759 | }
|
---|
760 | }
|
---|
761 |
|
---|
762 | /**
|
---|
763 | * Before the VM resumes we'll have to undo the read-only mode change
|
---|
764 | * done in vmdkSuspend.
|
---|
765 | *
|
---|
766 | * @param pDrvIns The driver instance data.
|
---|
767 | */
|
---|
768 | static DECLCALLBACK(void) vmdkResume(PPDMDRVINS pDrvIns)
|
---|
769 | {
|
---|
770 | LogFlow(("vmdkSuspend:\n"));
|
---|
771 | PVMDKDISK pData = PDMINS2DATA(pDrvIns, PVMDKDISK);
|
---|
772 | if (!VMDKDiskIsReadOnly(pData))
|
---|
773 | {
|
---|
774 | /** @todo does this even make sense? the vdi method locks the whole file, but don't we close it afterwards?? */
|
---|
775 | //int rc = vmdkChangeImageMode(pData, false);
|
---|
776 | //AssertRC(rc);
|
---|
777 | }
|
---|
778 | }
|
---|
779 |
|
---|
780 |
|
---|
781 | /** @copydoc PDMIMEDIA::pfnGetSize */
|
---|
782 | static DECLCALLBACK(uint64_t) vmdkGetSize(PPDMIMEDIA pInterface)
|
---|
783 | {
|
---|
784 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
785 | uint64_t cb = VMDKDiskGetSize(pData);
|
---|
786 | LogFlow(("vmdkGetSize: returns %#llx (%llu)\n", cb, cb));
|
---|
787 | return cb;
|
---|
788 | }
|
---|
789 |
|
---|
790 | /**
|
---|
791 | * Get stored media geometry - BIOS property.
|
---|
792 | *
|
---|
793 | * @see PDMIMEDIA::pfnBiosGetGeometry for details.
|
---|
794 | */
|
---|
795 | static DECLCALLBACK(int) vmdkBiosGetGeometry(PPDMIMEDIA pInterface, uint32_t *pcCylinders, uint32_t *pcHeads, uint32_t *pcSectors)
|
---|
796 | {
|
---|
797 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
798 | int rc = VMDKDiskGetGeometry(pData, pcCylinders, pcHeads, pcSectors);
|
---|
799 | if (VBOX_SUCCESS(rc))
|
---|
800 | {
|
---|
801 | LogFlow(("vmdkBiosGetGeometry: returns VINF_SUCCESS\n"));
|
---|
802 | return VINF_SUCCESS;
|
---|
803 | }
|
---|
804 | Log(("vmdkBiosGetGeometry: The Bios geometry data was not available.\n"));
|
---|
805 | return VERR_PDM_GEOMETRY_NOT_SET;
|
---|
806 | }
|
---|
807 |
|
---|
808 | /**
|
---|
809 | * Set stored media geometry - BIOS property.
|
---|
810 | *
|
---|
811 | * @see PDMIMEDIA::pfnBiosSetGeometry for details.
|
---|
812 | */
|
---|
813 | static DECLCALLBACK(int) vmdkBiosSetGeometry(PPDMIMEDIA pInterface, uint32_t cCylinders, uint32_t cHeads, uint32_t cSectors)
|
---|
814 | {
|
---|
815 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
816 | int rc = VMDKDiskSetGeometry(pData, cCylinders, cHeads, cSectors);
|
---|
817 | LogFlow(("vmdkBiosSetGeometry: returns %Vrc (%d,%d,%d)\n", rc, cCylinders, cHeads, cSectors));
|
---|
818 | return rc;
|
---|
819 | }
|
---|
820 |
|
---|
821 | /**
|
---|
822 | * Read bits.
|
---|
823 | *
|
---|
824 | * @see PDMIMEDIA::pfnRead for details.
|
---|
825 | */
|
---|
826 | static DECLCALLBACK(int) vmdkRead(PPDMIMEDIA pInterface, uint64_t off, void *pvBuf, size_t cbRead)
|
---|
827 | {
|
---|
828 | LogFlow(("vmdkRead: off=%#llx pvBuf=%p cbRead=%d\n", off, pvBuf, cbRead));
|
---|
829 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
830 | int rc = vmdk_read(&pData->VmdkState, off >> VMDK_GEOMETRY_SECTOR_SHIFT, (uint8_t *)pvBuf, cbRead >> VMDK_GEOMETRY_SECTOR_SHIFT);
|
---|
831 | if (VBOX_SUCCESS(rc))
|
---|
832 | Log2(("vmdkRead: off=%#llx pvBuf=%p cbRead=%d\n"
|
---|
833 | "%.*Vhxd\n",
|
---|
834 | off, pvBuf, cbRead, cbRead, pvBuf));
|
---|
835 | LogFlow(("vmdkRead: returns %Vrc\n", rc));
|
---|
836 | return rc;
|
---|
837 | }
|
---|
838 |
|
---|
839 | /**
|
---|
840 | * Write bits.
|
---|
841 | *
|
---|
842 | * @see PDMIMEDIA::pfnWrite for details.
|
---|
843 | */
|
---|
844 | static DECLCALLBACK(int) vmdkWrite(PPDMIMEDIA pInterface, uint64_t off, const void *pvBuf, size_t cbWrite)
|
---|
845 | {
|
---|
846 | LogFlow(("vmdkWrite: off=%#llx pvBuf=%p cbWrite=%d\n", off, pvBuf, cbWrite));
|
---|
847 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
848 | Log2(("vmdkWrite: off=%#llx pvBuf=%p cbWrite=%d\n"
|
---|
849 | "%.*Vhxd\n",
|
---|
850 | off, pvBuf, cbWrite, cbWrite, pvBuf));
|
---|
851 | int rc = vmdk_write(&pData->VmdkState, off >> VMDK_GEOMETRY_SECTOR_SHIFT, (const uint8_t *)pvBuf, cbWrite >> VMDK_GEOMETRY_SECTOR_SHIFT);
|
---|
852 | LogFlow(("vmdkWrite: returns %Vrc\n", rc));
|
---|
853 | return rc;
|
---|
854 | }
|
---|
855 |
|
---|
856 | /**
|
---|
857 | * Flush bits to media.
|
---|
858 | *
|
---|
859 | * @see PDMIMEDIA::pfnFlush for details.
|
---|
860 | */
|
---|
861 | static DECLCALLBACK(int) vmdkFlush(PPDMIMEDIA pInterface)
|
---|
862 | {
|
---|
863 | LogFlow(("vmdkFlush:\n"));
|
---|
864 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
865 | vmdk_flush(&pData->VmdkState);
|
---|
866 | int rc = VINF_SUCCESS;
|
---|
867 | LogFlow(("vmdkFlush: returns %Vrc\n", rc));
|
---|
868 | return rc;
|
---|
869 | }
|
---|
870 |
|
---|
871 | /** @copydoc PDMIMEDIA::pfnGetUuid */
|
---|
872 | static DECLCALLBACK(int) vmdkGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
|
---|
873 | {
|
---|
874 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
875 | /** @todo */
|
---|
876 | int rc = VINF_SUCCESS;
|
---|
877 | NOREF(pData);
|
---|
878 | LogFlow(("vmdkGetUuid: returns %Vrc ({%Vuuid})\n", rc, pUuid));
|
---|
879 | return rc;
|
---|
880 | }
|
---|
881 |
|
---|
882 | /** @copydoc PDMIMEDIA::pfnIsReadOnly */
|
---|
883 | static DECLCALLBACK(bool) vmdkIsReadOnly(PPDMIMEDIA pInterface)
|
---|
884 | {
|
---|
885 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
886 | LogFlow(("vmdkIsReadOnly: returns %d\n", VMDKDiskIsReadOnly(pData)));
|
---|
887 | return VMDKDiskIsReadOnly(pData);
|
---|
888 | }
|
---|
889 |
|
---|
890 | /** @copydoc PDMIMEDIA::pfnBiosGetTranslation */
|
---|
891 | static DECLCALLBACK(int) vmdkBiosGetTranslation(PPDMIMEDIA pInterface,
|
---|
892 | PPDMBIOSTRANSLATION penmTranslation)
|
---|
893 | {
|
---|
894 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
895 | int rc = VINF_SUCCESS;
|
---|
896 | NOREF(pData);
|
---|
897 | *penmTranslation = PDMBIOSTRANSLATION_AUTO; /** @todo */
|
---|
898 | LogFlow(("vmdkBiosGetTranslation: returns %Vrc (%d)\n", rc, *penmTranslation));
|
---|
899 | return rc;
|
---|
900 | }
|
---|
901 |
|
---|
902 | /** @copydoc PDMIMEDIA::pfnBiosSetTranslation */
|
---|
903 | static DECLCALLBACK(int) vmdkBiosSetTranslation(PPDMIMEDIA pInterface,
|
---|
904 | PDMBIOSTRANSLATION enmTranslation)
|
---|
905 | {
|
---|
906 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
907 | /** @todo */
|
---|
908 | int rc = VINF_SUCCESS;
|
---|
909 | NOREF(pData);
|
---|
910 | LogFlow(("vmdkBiosSetTranslation: returns %Vrc (%d)\n", rc, enmTranslation));
|
---|
911 | return rc;
|
---|
912 | }
|
---|
913 |
|
---|
914 |
|
---|
915 | /**
|
---|
916 | * Queries an interface to the driver.
|
---|
917 | *
|
---|
918 | * @returns Pointer to interface.
|
---|
919 | * @returns NULL if the interface was not supported by the driver.
|
---|
920 | * @param pInterface Pointer to this interface structure.
|
---|
921 | * @param enmInterface The requested interface identification.
|
---|
922 | * @thread Any thread.
|
---|
923 | */
|
---|
924 | static DECLCALLBACK(void *) vmdkQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
|
---|
925 | {
|
---|
926 | PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
|
---|
927 | PVMDKDISK pData = PDMINS2DATA(pDrvIns, PVMDKDISK);
|
---|
928 | switch (enmInterface)
|
---|
929 | {
|
---|
930 | case PDMINTERFACE_BASE:
|
---|
931 | return &pDrvIns->IBase;
|
---|
932 | case PDMINTERFACE_MEDIA:
|
---|
933 | return &pData->IMedia;
|
---|
934 | default:
|
---|
935 | return NULL;
|
---|
936 | }
|
---|
937 | }
|
---|
938 |
|
---|
939 |
|
---|
940 | /**
|
---|
941 | * VMDK driver registration record.
|
---|
942 | */
|
---|
943 | const PDMDRVREG g_DrvVmdkHDD =
|
---|
944 | {
|
---|
945 | /* u32Version */
|
---|
946 | PDM_DRVREG_VERSION,
|
---|
947 | /* szDriverName */
|
---|
948 | "VdmkHDD",
|
---|
949 | /* pszDescription */
|
---|
950 | "VMDK media driver.",
|
---|
951 | /* fFlags */
|
---|
952 | PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
|
---|
953 | /* fClass. */
|
---|
954 | PDM_DRVREG_CLASS_MEDIA,
|
---|
955 | /* cMaxInstances */
|
---|
956 | ~0,
|
---|
957 | /* cbInstance */
|
---|
958 | sizeof(VMDKDISK),
|
---|
959 | /* pfnConstruct */
|
---|
960 | vmdkConstruct,
|
---|
961 | /* pfnDestruct */
|
---|
962 | vmdkDestruct,
|
---|
963 | /* pfnIOCtl */
|
---|
964 | NULL,
|
---|
965 | /* pfnPowerOn */
|
---|
966 | NULL,
|
---|
967 | /* pfnReset */
|
---|
968 | NULL,
|
---|
969 | /* pfnSuspend */
|
---|
970 | vmdkSuspend,
|
---|
971 | /* pfnResume */
|
---|
972 | vmdkResume,
|
---|
973 | /* pfnDetach */
|
---|
974 | NULL
|
---|
975 | };
|
---|