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