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) drvVmdkConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle);
|
---|
193 | static DECLCALLBACK(void) drvVmdkDestruct(PPDMDRVINS pDrvIns);
|
---|
194 | static DECLCALLBACK(int) drvVmdkRead(PPDMIMEDIA pInterface,
|
---|
195 | uint64_t off, void *pvBuf, size_t cbRead);
|
---|
196 | static DECLCALLBACK(int) drvVmdkWrite(PPDMIMEDIA pInterface,
|
---|
197 | uint64_t off, const void *pvBuf, size_t cbWrite);
|
---|
198 | static DECLCALLBACK(int) drvVmdkFlush(PPDMIMEDIA pInterface);
|
---|
199 | static DECLCALLBACK(uint64_t) drvVmdkGetSize(PPDMIMEDIA pInterface);
|
---|
200 | static DECLCALLBACK(int) drvVmdkBiosGetGeometry(PPDMIMEDIA pInterface, uint32_t *pcCylinders,
|
---|
201 | uint32_t *pcHeads, uint32_t *pcSectors);
|
---|
202 | static DECLCALLBACK(int) drvVmdkBiosSetGeometry(PPDMIMEDIA pInterface, uint32_t cCylinders,
|
---|
203 | uint32_t cHeads, uint32_t cSectors);
|
---|
204 | static DECLCALLBACK(int) drvVmdkGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid);
|
---|
205 | static DECLCALLBACK(bool) drvVmdkIsReadOnly(PPDMIMEDIA pInterface);
|
---|
206 | static DECLCALLBACK(int) drvVmdkBiosGetTranslation(PPDMIMEDIA pInterface,
|
---|
207 | PPDMBIOSTRANSLATION penmTranslation);
|
---|
208 | static DECLCALLBACK(int) drvVmdkBiosSetTranslation(PPDMIMEDIA pInterface,
|
---|
209 | PDMBIOSTRANSLATION enmTranslation);
|
---|
210 | static DECLCALLBACK(void *) drvVmdkQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface);
|
---|
211 |
|
---|
212 | #if 0
|
---|
213 | static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename)
|
---|
214 | {
|
---|
215 | uint32_t magic;
|
---|
216 |
|
---|
217 | if (buf_size < 4)
|
---|
218 | return 0;
|
---|
219 | magic = be32_to_cpu(*(uint32_t *)buf);
|
---|
220 | if (magic == VMDK3_MAGIC ||
|
---|
221 | magic == VMDK4_MAGIC)
|
---|
222 | return 100;
|
---|
223 | else
|
---|
224 | return 0;
|
---|
225 | }
|
---|
226 | #endif
|
---|
227 |
|
---|
228 | static int vmdk_open(PVMDKDISK pDisk, const char *filename, bool fReadOnly)
|
---|
229 | {
|
---|
230 | uint32_t magic, i;
|
---|
231 | int l1_size;
|
---|
232 |
|
---|
233 | BDRVVmdkState *s = &pDisk->VmdkState;
|
---|
234 |
|
---|
235 | /*
|
---|
236 | * Open the image.
|
---|
237 | */
|
---|
238 | s->fReadOnly = fReadOnly;
|
---|
239 | int rc = RTFileOpen(&s->File,
|
---|
240 | filename,
|
---|
241 | fReadOnly
|
---|
242 | ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
|
---|
243 | : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
|
---|
244 | if (VBOX_FAILURE(rc))
|
---|
245 | {
|
---|
246 | if (!fReadOnly)
|
---|
247 | {
|
---|
248 | /* Try to open image for reading only. */
|
---|
249 | rc = RTFileOpen(&s->File,
|
---|
250 | filename,
|
---|
251 | RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
|
---|
252 | if (VBOX_SUCCESS(rc))
|
---|
253 | s->fReadOnly = true;
|
---|
254 | }
|
---|
255 | if (VBOX_FAILURE(rc))
|
---|
256 | return rc;
|
---|
257 | }
|
---|
258 | rc = RTFileRead(s->File, &magic, sizeof(magic), NULL);
|
---|
259 | AssertRC(rc);
|
---|
260 | if (VBOX_FAILURE(rc))
|
---|
261 | goto fail;
|
---|
262 |
|
---|
263 | magic = be32_to_cpu(magic);
|
---|
264 | if (magic == VMDK3_MAGIC)
|
---|
265 | {
|
---|
266 | VMDK3Header header;
|
---|
267 | rc = RTFileRead(s->File, &header, sizeof(header), NULL);
|
---|
268 | AssertRC(rc);
|
---|
269 | if (VBOX_FAILURE(rc))
|
---|
270 | goto fail;
|
---|
271 | s->cluster_sectors = le32_to_cpu(header.granularity);
|
---|
272 | s->l2_size = 1 << 9;
|
---|
273 | s->l1_size = 1 << 6;
|
---|
274 | s->total_sectors = le32_to_cpu(header.disk_sectors);
|
---|
275 | s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9;
|
---|
276 | s->l1_backup_table_offset = 0;
|
---|
277 | s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
|
---|
278 |
|
---|
279 | /* fill in the geometry structure */
|
---|
280 | pDisk->Geometry.cCylinders = le32_to_cpu(header.cylinders);
|
---|
281 | pDisk->Geometry.cHeads = le32_to_cpu(header.heads);
|
---|
282 | pDisk->Geometry.cSectors = le32_to_cpu(header.sectors_per_track);
|
---|
283 | pDisk->Geometry.cbSector = VMDK_GEOMETRY_SECTOR_SIZE;
|
---|
284 | }
|
---|
285 | else if (magic == VMDK4_MAGIC)
|
---|
286 | {
|
---|
287 | VMDK4Header header;
|
---|
288 |
|
---|
289 | rc = RTFileRead(s->File, &header, sizeof(header), NULL);
|
---|
290 | AssertRC(rc);
|
---|
291 | if (VBOX_FAILURE(rc))
|
---|
292 | goto fail;
|
---|
293 | s->total_sectors = le64_to_cpu(header.capacity);
|
---|
294 | s->cluster_sectors = le64_to_cpu(header.granularity);
|
---|
295 | s->l2_size = le32_to_cpu(header.num_gtes_per_gte);
|
---|
296 | s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
|
---|
297 | if (s->l1_entry_sectors <= 0)
|
---|
298 | {
|
---|
299 | rc = VERR_VDI_INVALID_HEADER;
|
---|
300 | goto fail;
|
---|
301 | }
|
---|
302 | s->l1_size = (s->total_sectors + s->l1_entry_sectors - 1)
|
---|
303 | / s->l1_entry_sectors;
|
---|
304 | s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9;
|
---|
305 | s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9;
|
---|
306 |
|
---|
307 | /* fill in the geometry structure */
|
---|
308 | /// @todo we should read these properties from the DDB section
|
---|
309 | // of the Disk DescriptorFile. So, the below values are just a
|
---|
310 | // quick hack.
|
---|
311 | pDisk->Geometry.cCylinders = RT_MIN((le64_to_cpu(header.capacity) /
|
---|
312 | (16 * 63)), 16383);
|
---|
313 | pDisk->Geometry.cHeads = 16;
|
---|
314 | pDisk->Geometry.cSectors = 63;
|
---|
315 | pDisk->Geometry.cbSector = VMDK_GEOMETRY_SECTOR_SIZE;
|
---|
316 | }
|
---|
317 | else
|
---|
318 | {
|
---|
319 | rc = VERR_VDI_INVALID_HEADER;
|
---|
320 | goto fail;
|
---|
321 | }
|
---|
322 | /* read the L1 table */
|
---|
323 | l1_size = s->l1_size * sizeof(uint32_t);
|
---|
324 | s->l1_table = (uint32_t *)RTMemAllocZ(l1_size);
|
---|
325 | if (!s->l1_table)
|
---|
326 | {
|
---|
327 | rc = VERR_NO_MEMORY;
|
---|
328 | goto fail;
|
---|
329 | }
|
---|
330 | rc = RTFileSeek(s->File, s->l1_table_offset, RTFILE_SEEK_BEGIN, NULL);
|
---|
331 | AssertRC(rc);
|
---|
332 | if (VBOX_FAILURE(rc))
|
---|
333 | goto fail;
|
---|
334 | rc = RTFileRead(s->File, s->l1_table, l1_size, NULL);
|
---|
335 | AssertRC(rc);
|
---|
336 | if (VBOX_FAILURE(rc))
|
---|
337 | goto fail;
|
---|
338 | for(i = 0; i < s->l1_size; i++) {
|
---|
339 | le32_to_cpus(&s->l1_table[i]);
|
---|
340 | }
|
---|
341 |
|
---|
342 | if (s->l1_backup_table_offset) {
|
---|
343 | s->l1_backup_table = (uint32_t *)RTMemAllocZ(l1_size);
|
---|
344 | if (!s->l1_backup_table)
|
---|
345 | {
|
---|
346 | rc = VERR_NO_MEMORY;
|
---|
347 | goto fail;
|
---|
348 | }
|
---|
349 | rc = RTFileSeek(s->File, s->l1_backup_table_offset, RTFILE_SEEK_BEGIN, NULL);
|
---|
350 | AssertRC(rc);
|
---|
351 | if (VBOX_FAILURE(rc))
|
---|
352 | goto fail;
|
---|
353 | rc = RTFileRead(s->File, s->l1_backup_table, l1_size, NULL);
|
---|
354 | AssertRC(rc);
|
---|
355 | if (VBOX_FAILURE(rc))
|
---|
356 | goto fail;
|
---|
357 | for(i = 0; i < s->l1_size; i++) {
|
---|
358 | le32_to_cpus(&s->l1_backup_table[i]);
|
---|
359 | }
|
---|
360 | }
|
---|
361 |
|
---|
362 | s->l2_cache = (uint32_t *)RTMemAllocZ(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
|
---|
363 | if (!s->l2_cache)
|
---|
364 | {
|
---|
365 | rc = VERR_NO_MEMORY;
|
---|
366 | goto fail;
|
---|
367 | }
|
---|
368 |
|
---|
369 | return VINF_SUCCESS;
|
---|
370 |
|
---|
371 | fail:
|
---|
372 | Log(("vmdk_open failed with %Vrc\n", rc));
|
---|
373 | if (s->l1_backup_table)
|
---|
374 | RTMemFree(s->l1_backup_table);
|
---|
375 | if (s->l1_table)
|
---|
376 | RTMemFree(s->l1_table);
|
---|
377 | if (s->l2_cache)
|
---|
378 | RTMemFree(s->l2_cache);
|
---|
379 | RTFileClose(s->File);
|
---|
380 | return rc;
|
---|
381 | }
|
---|
382 |
|
---|
383 | static uint64_t get_cluster_offset(BDRVVmdkState *s,
|
---|
384 | uint64_t offset, int allocate)
|
---|
385 | {
|
---|
386 | unsigned int l1_index, l2_offset, l2_index;
|
---|
387 | int min_index, i, j;
|
---|
388 | uint32_t min_count, *l2_table, tmp;
|
---|
389 | uint64_t cluster_offset;
|
---|
390 | int rc;
|
---|
391 |
|
---|
392 | l1_index = (offset >> 9) / s->l1_entry_sectors;
|
---|
393 | if (l1_index >= s->l1_size)
|
---|
394 | return 0;
|
---|
395 | l2_offset = s->l1_table[l1_index];
|
---|
396 | if (!l2_offset)
|
---|
397 | return 0;
|
---|
398 | for(i = 0; i < L2_CACHE_SIZE; i++) {
|
---|
399 | if (l2_offset == s->l2_cache_offsets[i]) {
|
---|
400 | /* increment the hit count */
|
---|
401 | if (++s->l2_cache_counts[i] == 0xffffffff) {
|
---|
402 | for(j = 0; j < L2_CACHE_SIZE; j++) {
|
---|
403 | s->l2_cache_counts[j] >>= 1;
|
---|
404 | }
|
---|
405 | }
|
---|
406 | l2_table = s->l2_cache + (i * s->l2_size);
|
---|
407 | goto found;
|
---|
408 | }
|
---|
409 | }
|
---|
410 | /* not found: load a new entry in the least used one */
|
---|
411 | min_index = 0;
|
---|
412 | min_count = 0xffffffff;
|
---|
413 | for(i = 0; i < L2_CACHE_SIZE; i++) {
|
---|
414 | if (s->l2_cache_counts[i] < min_count) {
|
---|
415 | min_count = s->l2_cache_counts[i];
|
---|
416 | min_index = i;
|
---|
417 | }
|
---|
418 | }
|
---|
419 | l2_table = s->l2_cache + (min_index * s->l2_size);
|
---|
420 | rc = RTFileSeek(s->File, (int64_t)l2_offset * VMDK_GEOMETRY_SECTOR_SIZE, RTFILE_SEEK_BEGIN, NULL);
|
---|
421 | AssertRC(rc);
|
---|
422 | if (VBOX_FAILURE(rc))
|
---|
423 | return 0;
|
---|
424 | rc = RTFileRead(s->File, l2_table, s->l2_size * sizeof(uint32_t), NULL);
|
---|
425 | AssertRC(rc);
|
---|
426 | if (VBOX_FAILURE(rc))
|
---|
427 | return 0;
|
---|
428 | s->l2_cache_offsets[min_index] = l2_offset;
|
---|
429 | s->l2_cache_counts[min_index] = 1;
|
---|
430 | found:
|
---|
431 | l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size;
|
---|
432 | cluster_offset = le32_to_cpu(l2_table[l2_index]);
|
---|
433 | if (!cluster_offset) {
|
---|
434 | if (!allocate)
|
---|
435 | return 0;
|
---|
436 | rc = RTFileSeek(s->File, 0, RTFILE_SEEK_END, &cluster_offset);
|
---|
437 | AssertRC(rc);
|
---|
438 | if (VBOX_FAILURE(rc))
|
---|
439 | return 0;
|
---|
440 | rc = RTFileSetSize(s->File, cluster_offset + (s->cluster_sectors << 9));
|
---|
441 | AssertRC(rc);
|
---|
442 | if (VBOX_FAILURE(rc))
|
---|
443 | return 0;
|
---|
444 | cluster_offset >>= 9;
|
---|
445 | /* update L2 table */
|
---|
446 | tmp = cpu_to_le32(cluster_offset);
|
---|
447 | l2_table[l2_index] = tmp;
|
---|
448 | rc = RTFileSeek(s->File, ((int64_t)l2_offset * VMDK_GEOMETRY_SECTOR_SIZE) + (l2_index * sizeof(tmp)), RTFILE_SEEK_BEGIN, NULL);
|
---|
449 | AssertRC(rc);
|
---|
450 | if (VBOX_FAILURE(rc))
|
---|
451 | return 0;
|
---|
452 | rc = RTFileWrite(s->File, &tmp, sizeof(tmp), NULL);
|
---|
453 | AssertRC(rc);
|
---|
454 | if (VBOX_FAILURE(rc))
|
---|
455 | return 0;
|
---|
456 | /* update backup L2 table */
|
---|
457 | if (s->l1_backup_table_offset != 0) {
|
---|
458 | l2_offset = s->l1_backup_table[l1_index];
|
---|
459 |
|
---|
460 | rc = RTFileSeek(s->File, ((int64_t)l2_offset * VMDK_GEOMETRY_SECTOR_SIZE) + (l2_index * sizeof(tmp)), RTFILE_SEEK_BEGIN, NULL);
|
---|
461 | AssertRC(rc);
|
---|
462 | if (VBOX_FAILURE(rc))
|
---|
463 | return 0;
|
---|
464 | rc = RTFileWrite(s->File, &tmp, sizeof(tmp), NULL);
|
---|
465 | AssertRC(rc);
|
---|
466 | if (VBOX_FAILURE(rc))
|
---|
467 | return 0;
|
---|
468 | }
|
---|
469 | }
|
---|
470 | cluster_offset <<= 9;
|
---|
471 | return cluster_offset;
|
---|
472 | }
|
---|
473 |
|
---|
474 | #if 0
|
---|
475 | static int vmdk_is_allocated(BDRVVmdkState *s, int64_t sector_num,
|
---|
476 | int nb_sectors, int *pnum)
|
---|
477 | {
|
---|
478 | int index_in_cluster, n;
|
---|
479 | uint64_t cluster_offset;
|
---|
480 |
|
---|
481 | cluster_offset = get_cluster_offset(s, sector_num << 9, 0);
|
---|
482 | index_in_cluster = sector_num % s->cluster_sectors;
|
---|
483 | n = s->cluster_sectors - index_in_cluster;
|
---|
484 | if (n > nb_sectors)
|
---|
485 | n = nb_sectors;
|
---|
486 | *pnum = n;
|
---|
487 | return (cluster_offset != 0);
|
---|
488 | }
|
---|
489 | #endif
|
---|
490 |
|
---|
491 | static int vmdk_read(BDRVVmdkState *s, int64_t sector_num,
|
---|
492 | uint8_t *buf, int nb_sectors)
|
---|
493 | {
|
---|
494 | int index_in_cluster, n;
|
---|
495 | uint64_t cluster_offset;
|
---|
496 |
|
---|
497 | while (nb_sectors > 0) {
|
---|
498 | cluster_offset = get_cluster_offset(s, sector_num << 9, 0);
|
---|
499 | index_in_cluster = sector_num % s->cluster_sectors;
|
---|
500 | n = s->cluster_sectors - index_in_cluster;
|
---|
501 | if (n > nb_sectors)
|
---|
502 | n = nb_sectors;
|
---|
503 | if (!cluster_offset) {
|
---|
504 | memset(buf, 0, VMDK_GEOMETRY_SECTOR_SIZE * n);
|
---|
505 | } else {
|
---|
506 | int rc = RTFileSeek(s->File, cluster_offset + index_in_cluster * VMDK_GEOMETRY_SECTOR_SIZE, RTFILE_SEEK_BEGIN, NULL);
|
---|
507 | AssertRC(rc);
|
---|
508 | if (VBOX_FAILURE(rc))
|
---|
509 | return rc;
|
---|
510 |
|
---|
511 | rc = RTFileRead(s->File, buf, n * VMDK_GEOMETRY_SECTOR_SIZE, NULL);
|
---|
512 | AssertRC(rc);
|
---|
513 | if (VBOX_FAILURE(rc))
|
---|
514 | return rc;
|
---|
515 | }
|
---|
516 | nb_sectors -= n;
|
---|
517 | sector_num += n;
|
---|
518 | buf += n * VMDK_GEOMETRY_SECTOR_SIZE;
|
---|
519 | }
|
---|
520 | return VINF_SUCCESS;
|
---|
521 | }
|
---|
522 |
|
---|
523 | static int vmdk_write(BDRVVmdkState *s, int64_t sector_num,
|
---|
524 | const uint8_t *buf, int nb_sectors)
|
---|
525 | {
|
---|
526 | int index_in_cluster, n;
|
---|
527 | uint64_t cluster_offset;
|
---|
528 |
|
---|
529 | while (nb_sectors > 0) {
|
---|
530 | index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
---|
531 | n = s->cluster_sectors - index_in_cluster;
|
---|
532 | if (n > nb_sectors)
|
---|
533 | n = nb_sectors;
|
---|
534 | cluster_offset = get_cluster_offset(s, sector_num << 9, 1);
|
---|
535 | if (!cluster_offset)
|
---|
536 | return VERR_IO_SECTOR_NOT_FOUND;
|
---|
537 |
|
---|
538 | int rc = RTFileSeek(s->File, cluster_offset + index_in_cluster * VMDK_GEOMETRY_SECTOR_SIZE, RTFILE_SEEK_BEGIN, NULL);
|
---|
539 | AssertRC(rc);
|
---|
540 | if (VBOX_FAILURE(rc))
|
---|
541 | return rc;
|
---|
542 |
|
---|
543 | rc = RTFileWrite(s->File, buf, n * VMDK_GEOMETRY_SECTOR_SIZE, NULL);
|
---|
544 | AssertRC(rc);
|
---|
545 | if (VBOX_FAILURE(rc))
|
---|
546 | return rc;
|
---|
547 |
|
---|
548 | nb_sectors -= n;
|
---|
549 | sector_num += n;
|
---|
550 | buf += n * VMDK_GEOMETRY_SECTOR_SIZE;
|
---|
551 | }
|
---|
552 | return VINF_SUCCESS;
|
---|
553 | }
|
---|
554 |
|
---|
555 | static void vmdk_close(BDRVVmdkState *s)
|
---|
556 | {
|
---|
557 | RTMemFree(s->l1_table);
|
---|
558 | RTMemFree(s->l2_cache);
|
---|
559 | RTFileClose(s->File);
|
---|
560 | }
|
---|
561 |
|
---|
562 | static void vmdk_flush(BDRVVmdkState *s)
|
---|
563 | {
|
---|
564 | RTFileFlush(s->File);
|
---|
565 | }
|
---|
566 |
|
---|
567 |
|
---|
568 | /**
|
---|
569 | * Get read/write mode of VMDK HDD.
|
---|
570 | *
|
---|
571 | * @returns Disk ReadOnly status.
|
---|
572 | * @returns true if no one VMDK image is opened in HDD container.
|
---|
573 | */
|
---|
574 | IDER3DECL(bool) VMDKDiskIsReadOnly(PVMDKDISK pDisk)
|
---|
575 | {
|
---|
576 | /* sanity check */
|
---|
577 | Assert(pDisk);
|
---|
578 | AssertMsg(pDisk->u32Signature == VMDKDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
|
---|
579 |
|
---|
580 | LogFlow(("VmdkDiskIsReadOnly: returns %u\n", pDisk->VmdkState.fReadOnly));
|
---|
581 | return pDisk->VmdkState.fReadOnly;
|
---|
582 | }
|
---|
583 |
|
---|
584 |
|
---|
585 | /**
|
---|
586 | * Get disk size of VMDK HDD container.
|
---|
587 | *
|
---|
588 | * @returns Virtual disk size in bytes.
|
---|
589 | * @returns 0 if no one VMDK image is opened in HDD container.
|
---|
590 | */
|
---|
591 | IDER3DECL(uint64_t) VMDKDiskGetSize(PVMDKDISK pDisk)
|
---|
592 | {
|
---|
593 | /* sanity check */
|
---|
594 | Assert(pDisk);
|
---|
595 | AssertMsg(pDisk->u32Signature == VMDKDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
|
---|
596 |
|
---|
597 | return pDisk->VmdkState.total_sectors * VMDK_GEOMETRY_SECTOR_SIZE;
|
---|
598 | }
|
---|
599 |
|
---|
600 | /**
|
---|
601 | * Get block size of VMDK HDD container.
|
---|
602 | *
|
---|
603 | * @returns VDI image block size in bytes.
|
---|
604 | * @returns 0 if no one VMDK image is opened in HDD container.
|
---|
605 | */
|
---|
606 | IDER3DECL(unsigned) VMDKDiskGetBlockSize(PVMDKDISK pDisk)
|
---|
607 | {
|
---|
608 | /* sanity check */
|
---|
609 | Assert(pDisk);
|
---|
610 | AssertMsg(pDisk->u32Signature == VMDKDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
|
---|
611 |
|
---|
612 | return VMDK_GEOMETRY_SECTOR_SIZE;
|
---|
613 | }
|
---|
614 |
|
---|
615 | /**
|
---|
616 | * Get virtual disk geometry stored in image file.
|
---|
617 | *
|
---|
618 | * @returns VBox status code.
|
---|
619 | * @param pDisk Pointer to VMDK HDD container.
|
---|
620 | * @param pcCylinders Where to store the number of cylinders. NULL is ok.
|
---|
621 | * @param pcHeads Where to store the number of heads. NULL is ok.
|
---|
622 | * @param pcSectors Where to store the number of sectors. NULL is ok.
|
---|
623 | */
|
---|
624 | IDER3DECL(int) VMDKDiskGetGeometry(PVMDKDISK pDisk, unsigned *pcCylinders, unsigned *pcHeads, unsigned *pcSectors)
|
---|
625 | {
|
---|
626 | /* sanity check */
|
---|
627 | Assert(pDisk);
|
---|
628 | AssertMsg(pDisk->u32Signature == VMDKDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
|
---|
629 |
|
---|
630 | PVMDKDISKGEOMETRY pGeometry = &pDisk->Geometry;
|
---|
631 |
|
---|
632 | LogFlow(("VDIDiskGetGeometry: C/H/S = %u/%u/%u\n",
|
---|
633 | pGeometry->cCylinders, pGeometry->cHeads, pGeometry->cSectors));
|
---|
634 |
|
---|
635 | int rc = VINF_SUCCESS;
|
---|
636 |
|
---|
637 | if ( pGeometry->cCylinders > 0
|
---|
638 | && pGeometry->cHeads > 0
|
---|
639 | && pGeometry->cSectors > 0)
|
---|
640 | {
|
---|
641 | if (pcCylinders)
|
---|
642 | *pcCylinders = pDisk->Geometry.cCylinders;
|
---|
643 | if (pcHeads)
|
---|
644 | *pcHeads = pDisk->Geometry.cHeads;
|
---|
645 | if (pcSectors)
|
---|
646 | *pcSectors = pDisk->Geometry.cSectors;
|
---|
647 | }
|
---|
648 | else
|
---|
649 | rc = VERR_VDI_GEOMETRY_NOT_SET;
|
---|
650 |
|
---|
651 | LogFlow(("VDIDiskGetGeometry: returns %Vrc\n", rc));
|
---|
652 | return rc;
|
---|
653 | }
|
---|
654 |
|
---|
655 | /**
|
---|
656 | * Store virtual disk geometry into base image file of HDD container.
|
---|
657 | *
|
---|
658 | * Note that in case of unrecoverable error all images of HDD container will be closed.
|
---|
659 | *
|
---|
660 | * @returns VBox status code.
|
---|
661 | * @param pDisk Pointer to VMDK HDD container.
|
---|
662 | * @param cCylinders Number of cylinders.
|
---|
663 | * @param cHeads Number of heads.
|
---|
664 | * @param cSectors Number of sectors.
|
---|
665 | */
|
---|
666 | IDER3DECL(int) VMDKDiskSetGeometry(PVMDKDISK pDisk, unsigned cCylinders, unsigned cHeads, unsigned cSectors)
|
---|
667 | {
|
---|
668 | LogFlow(("VMDKDiskSetGeometry: C/H/S = %u/%u/%u\n", cCylinders, cHeads, cSectors));
|
---|
669 | /* sanity check */
|
---|
670 | Assert(pDisk);
|
---|
671 | AssertMsg(pDisk->u32Signature == VMDKDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
|
---|
672 |
|
---|
673 | pDisk->Geometry.cCylinders = cCylinders;
|
---|
674 | pDisk->Geometry.cHeads = cHeads;
|
---|
675 | pDisk->Geometry.cSectors = cSectors;
|
---|
676 | pDisk->Geometry.cbSector = VMDK_GEOMETRY_SECTOR_SIZE;
|
---|
677 |
|
---|
678 | /** @todo Update header information in base image file. */
|
---|
679 | return VINF_SUCCESS;
|
---|
680 | }
|
---|
681 |
|
---|
682 | /**
|
---|
683 | * Get number of opened images in HDD container.
|
---|
684 | *
|
---|
685 | * @returns Number of opened images for HDD container. 0 if no images is opened.
|
---|
686 | * @param pDisk Pointer to VMDK HDD container.
|
---|
687 | */
|
---|
688 | IDER3DECL(int) VMDKDiskGetImagesCount(PVMDKDISK pDisk)
|
---|
689 | {
|
---|
690 | /* sanity check */
|
---|
691 | Assert(pDisk);
|
---|
692 | AssertMsg(pDisk->u32Signature == VMDKDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
|
---|
693 |
|
---|
694 | return 1;
|
---|
695 | }
|
---|
696 |
|
---|
697 | /*******************************************************************************
|
---|
698 | * PDM interface *
|
---|
699 | *******************************************************************************/
|
---|
700 |
|
---|
701 | /**
|
---|
702 | * Construct a VBox HDD media driver instance.
|
---|
703 | *
|
---|
704 | * @returns VBox status.
|
---|
705 | * @param pDrvIns The driver instance data.
|
---|
706 | * If the registration structure is needed, pDrvIns->pDrvReg points to it.
|
---|
707 | * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
|
---|
708 | * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
|
---|
709 | * iInstance it's expected to be used a bit in this function.
|
---|
710 | */
|
---|
711 | static DECLCALLBACK(int) drvVmdkConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
|
---|
712 | {
|
---|
713 | LogFlow(("drvVmdkConstruct:\n"));
|
---|
714 | PVMDKDISK pData = PDMINS2DATA(pDrvIns, PVMDKDISK);
|
---|
715 |
|
---|
716 | /*
|
---|
717 | * Init the static parts.
|
---|
718 | */
|
---|
719 | pDrvIns->IBase.pfnQueryInterface = drvVmdkQueryInterface;
|
---|
720 | pData->pDrvIns = pDrvIns;
|
---|
721 |
|
---|
722 | pData->u32Signature = VMDKDISK_SIGNATURE;
|
---|
723 |
|
---|
724 | pData->Geometry.cCylinders = 0;
|
---|
725 | pData->Geometry.cHeads = 0;
|
---|
726 | pData->Geometry.cSectors = 0;
|
---|
727 | pData->Geometry.cbSector = 0;
|
---|
728 |
|
---|
729 | /* IMedia */
|
---|
730 | pData->IMedia.pfnRead = drvVmdkRead;
|
---|
731 | pData->IMedia.pfnWrite = drvVmdkWrite;
|
---|
732 | pData->IMedia.pfnFlush = drvVmdkFlush;
|
---|
733 | pData->IMedia.pfnGetSize = drvVmdkGetSize;
|
---|
734 | pData->IMedia.pfnGetUuid = drvVmdkGetUuid;
|
---|
735 | pData->IMedia.pfnIsReadOnly = drvVmdkIsReadOnly;
|
---|
736 | pData->IMedia.pfnBiosGetGeometry = drvVmdkBiosGetGeometry;
|
---|
737 | pData->IMedia.pfnBiosSetGeometry = drvVmdkBiosSetGeometry;
|
---|
738 | pData->IMedia.pfnBiosGetTranslation = drvVmdkBiosGetTranslation;
|
---|
739 | pData->IMedia.pfnBiosSetTranslation = drvVmdkBiosSetTranslation;
|
---|
740 |
|
---|
741 | /*
|
---|
742 | * Validate and read top level configuration.
|
---|
743 | */
|
---|
744 | char *pszName;
|
---|
745 | int rc = CFGMR3QueryStringAlloc(pCfgHandle, "Path", &pszName);
|
---|
746 | if (VBOX_FAILURE(rc))
|
---|
747 | return PDMDRV_SET_ERROR(pDrvIns, rc,
|
---|
748 | N_("VHDD: Configuration error: Querying \"Path\" as string failed"));
|
---|
749 |
|
---|
750 | /** True if the media is readonly. */
|
---|
751 | bool fReadOnly;
|
---|
752 | rc = CFGMR3QueryBool(pCfgHandle, "ReadOnly", &fReadOnly);
|
---|
753 | if (rc == VERR_CFGM_VALUE_NOT_FOUND)
|
---|
754 | fReadOnly = false;
|
---|
755 | else if (VBOX_FAILURE(rc))
|
---|
756 | {
|
---|
757 | MMR3HeapFree(pszName);
|
---|
758 | return PDMDRV_SET_ERROR(pDrvIns, rc,
|
---|
759 | N_("VHDD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
|
---|
760 | }
|
---|
761 |
|
---|
762 | /*
|
---|
763 | * Open the image.
|
---|
764 | */
|
---|
765 | rc = vmdk_open(pData, pszName, fReadOnly);
|
---|
766 | if (VBOX_SUCCESS(rc))
|
---|
767 | Log(("drvVmdkConstruct: Opened '%s' in %s mode\n", pszName, VMDKDiskIsReadOnly(pData) ? "read-only" : "read-write"));
|
---|
768 | else
|
---|
769 | AssertMsgFailed(("Failed to open image '%s' rc=%Vrc\n", pszName, rc));
|
---|
770 |
|
---|
771 | MMR3HeapFree(pszName);
|
---|
772 | pszName = NULL;
|
---|
773 |
|
---|
774 | return rc;
|
---|
775 | }
|
---|
776 |
|
---|
777 | /**
|
---|
778 | * Destruct a driver instance.
|
---|
779 | *
|
---|
780 | * Most VM resources are freed by the VM. This callback is provided so that any non-VM
|
---|
781 | * resources can be freed correctly.
|
---|
782 | *
|
---|
783 | * @param pDrvIns The driver instance data.
|
---|
784 | */
|
---|
785 | static DECLCALLBACK(void) drvVmdkDestruct(PPDMDRVINS pDrvIns)
|
---|
786 | {
|
---|
787 | LogFlow(("drvVmdkDestruct:\n"));
|
---|
788 | PVMDKDISK pData = PDMINS2DATA(pDrvIns, PVMDKDISK);
|
---|
789 | vmdk_close(&pData->VmdkState);
|
---|
790 | }
|
---|
791 |
|
---|
792 | /**
|
---|
793 | * When the VM has been suspended we'll change the image mode to read-only
|
---|
794 | * so that main and others can read the VDIs. This is important when
|
---|
795 | * saving state and so forth.
|
---|
796 | *
|
---|
797 | * @param pDrvIns The driver instance data.
|
---|
798 | */
|
---|
799 | static DECLCALLBACK(void) drvVmdkSuspend(PPDMDRVINS pDrvIns)
|
---|
800 | {
|
---|
801 | LogFlow(("drvVmdkSuspend:\n"));
|
---|
802 | PVMDKDISK pData = PDMINS2DATA(pDrvIns, PVMDKDISK);
|
---|
803 | if (!VMDKDiskIsReadOnly(pData))
|
---|
804 | {
|
---|
805 | /** @todo does this even make sense? the vdi method locks the whole file, but don't we close it afterwards?? */
|
---|
806 | //int rc = vmdkChangeImageMode(pData, true);
|
---|
807 | //AssertRC(rc);
|
---|
808 | }
|
---|
809 | }
|
---|
810 |
|
---|
811 | /**
|
---|
812 | * Before the VM resumes we'll have to undo the read-only mode change
|
---|
813 | * done in drvVmdkSuspend.
|
---|
814 | *
|
---|
815 | * @param pDrvIns The driver instance data.
|
---|
816 | */
|
---|
817 | static DECLCALLBACK(void) drvVmdkResume(PPDMDRVINS pDrvIns)
|
---|
818 | {
|
---|
819 | LogFlow(("drvVmdkSuspend:\n"));
|
---|
820 | PVMDKDISK pData = PDMINS2DATA(pDrvIns, PVMDKDISK);
|
---|
821 | if (!VMDKDiskIsReadOnly(pData))
|
---|
822 | {
|
---|
823 | /** @todo does this even make sense? the vdi method locks the whole file, but don't we close it afterwards?? */
|
---|
824 | //int rc = vmdkChangeImageMode(pData, false);
|
---|
825 | //AssertRC(rc);
|
---|
826 | }
|
---|
827 | }
|
---|
828 |
|
---|
829 |
|
---|
830 | /** @copydoc PDMIMEDIA::pfnGetSize */
|
---|
831 | static DECLCALLBACK(uint64_t) drvVmdkGetSize(PPDMIMEDIA pInterface)
|
---|
832 | {
|
---|
833 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
834 | uint64_t cb = VMDKDiskGetSize(pData);
|
---|
835 | LogFlow(("drvVmdkGetSize: returns %#llx (%llu)\n", cb, cb));
|
---|
836 | return cb;
|
---|
837 | }
|
---|
838 |
|
---|
839 | /**
|
---|
840 | * Get stored media geometry - BIOS property.
|
---|
841 | *
|
---|
842 | * @see PDMIMEDIA::pfnBiosGetGeometry for details.
|
---|
843 | */
|
---|
844 | static DECLCALLBACK(int) drvVmdkBiosGetGeometry(PPDMIMEDIA pInterface, uint32_t *pcCylinders, uint32_t *pcHeads, uint32_t *pcSectors)
|
---|
845 | {
|
---|
846 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
847 | int rc = VMDKDiskGetGeometry(pData, pcCylinders, pcHeads, pcSectors);
|
---|
848 | if (VBOX_SUCCESS(rc))
|
---|
849 | {
|
---|
850 | LogFlow(("drvVmdkBiosGetGeometry: returns VINF_SUCCESS\n"));
|
---|
851 | return VINF_SUCCESS;
|
---|
852 | }
|
---|
853 | Log(("drvVmdkBiosGetGeometry: The Bios geometry data was not available.\n"));
|
---|
854 | return VERR_PDM_GEOMETRY_NOT_SET;
|
---|
855 | }
|
---|
856 |
|
---|
857 | /**
|
---|
858 | * Set stored media geometry - BIOS property.
|
---|
859 | *
|
---|
860 | * @see PDMIMEDIA::pfnBiosSetGeometry for details.
|
---|
861 | */
|
---|
862 | static DECLCALLBACK(int) drvVmdkBiosSetGeometry(PPDMIMEDIA pInterface, uint32_t cCylinders, uint32_t cHeads, uint32_t cSectors)
|
---|
863 | {
|
---|
864 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
865 | int rc = VMDKDiskSetGeometry(pData, cCylinders, cHeads, cSectors);
|
---|
866 | LogFlow(("drvVmdkBiosSetGeometry: returns %Vrc (%d,%d,%d)\n", rc, cCylinders, cHeads, cSectors));
|
---|
867 | return rc;
|
---|
868 | }
|
---|
869 |
|
---|
870 | /**
|
---|
871 | * Read bits.
|
---|
872 | *
|
---|
873 | * @see PDMIMEDIA::pfnRead for details.
|
---|
874 | */
|
---|
875 | static DECLCALLBACK(int) drvVmdkRead(PPDMIMEDIA pInterface, uint64_t off, void *pvBuf, size_t cbRead)
|
---|
876 | {
|
---|
877 | LogFlow(("drvVmdkRead: off=%#llx pvBuf=%p cbRead=%d\n", off, pvBuf, cbRead));
|
---|
878 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
879 | int rc = vmdk_read(&pData->VmdkState, off >> VMDK_GEOMETRY_SECTOR_SHIFT, (uint8_t *)pvBuf, cbRead >> VMDK_GEOMETRY_SECTOR_SHIFT);
|
---|
880 | if (VBOX_SUCCESS(rc))
|
---|
881 | Log2(("drvVmdkRead: off=%#llx pvBuf=%p cbRead=%d\n"
|
---|
882 | "%.*Vhxd\n",
|
---|
883 | off, pvBuf, cbRead, cbRead, pvBuf));
|
---|
884 | LogFlow(("drvVmdkRead: returns %Vrc\n", rc));
|
---|
885 | return rc;
|
---|
886 | }
|
---|
887 |
|
---|
888 | /**
|
---|
889 | * Write bits.
|
---|
890 | *
|
---|
891 | * @see PDMIMEDIA::pfnWrite for details.
|
---|
892 | */
|
---|
893 | static DECLCALLBACK(int) drvVmdkWrite(PPDMIMEDIA pInterface, uint64_t off, const void *pvBuf, size_t cbWrite)
|
---|
894 | {
|
---|
895 | LogFlow(("drvVmdkWrite: off=%#llx pvBuf=%p cbWrite=%d\n", off, pvBuf, cbWrite));
|
---|
896 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
897 | Log2(("drvVmdkWrite: off=%#llx pvBuf=%p cbWrite=%d\n"
|
---|
898 | "%.*Vhxd\n",
|
---|
899 | off, pvBuf, cbWrite, cbWrite, pvBuf));
|
---|
900 | int rc = vmdk_write(&pData->VmdkState, off >> VMDK_GEOMETRY_SECTOR_SHIFT, (const uint8_t *)pvBuf, cbWrite >> VMDK_GEOMETRY_SECTOR_SHIFT);
|
---|
901 | LogFlow(("drvVmdkWrite: returns %Vrc\n", rc));
|
---|
902 | return rc;
|
---|
903 | }
|
---|
904 |
|
---|
905 | /**
|
---|
906 | * Flush bits to media.
|
---|
907 | *
|
---|
908 | * @see PDMIMEDIA::pfnFlush for details.
|
---|
909 | */
|
---|
910 | static DECLCALLBACK(int) drvVmdkFlush(PPDMIMEDIA pInterface)
|
---|
911 | {
|
---|
912 | LogFlow(("drvVmdkFlush:\n"));
|
---|
913 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
914 | vmdk_flush(&pData->VmdkState);
|
---|
915 | int rc = VINF_SUCCESS;
|
---|
916 | LogFlow(("drvVmdkFlush: returns %Vrc\n", rc));
|
---|
917 | return rc;
|
---|
918 | }
|
---|
919 |
|
---|
920 | /** @copydoc PDMIMEDIA::pfnGetUuid */
|
---|
921 | static DECLCALLBACK(int) drvVmdkGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
|
---|
922 | {
|
---|
923 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
924 | /** @todo */
|
---|
925 | int rc = VINF_SUCCESS;
|
---|
926 | NOREF(pData);
|
---|
927 | LogFlow(("drvVmdkGetUuid: returns %Vrc ({%Vuuid})\n", rc, pUuid));
|
---|
928 | return rc;
|
---|
929 | }
|
---|
930 |
|
---|
931 | /** @copydoc PDMIMEDIA::pfnIsReadOnly */
|
---|
932 | static DECLCALLBACK(bool) drvVmdkIsReadOnly(PPDMIMEDIA pInterface)
|
---|
933 | {
|
---|
934 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
935 | LogFlow(("drvVmdkIsReadOnly: returns %d\n", VMDKDiskIsReadOnly(pData)));
|
---|
936 | return VMDKDiskIsReadOnly(pData);
|
---|
937 | }
|
---|
938 |
|
---|
939 | /** @copydoc PDMIMEDIA::pfnBiosGetTranslation */
|
---|
940 | static DECLCALLBACK(int) drvVmdkBiosGetTranslation(PPDMIMEDIA pInterface,
|
---|
941 | PPDMBIOSTRANSLATION penmTranslation)
|
---|
942 | {
|
---|
943 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
944 | int rc = VINF_SUCCESS;
|
---|
945 | NOREF(pData);
|
---|
946 | *penmTranslation = PDMBIOSTRANSLATION_AUTO; /** @todo */
|
---|
947 | LogFlow(("drvVmdkBiosGetTranslation: returns %Vrc (%d)\n", rc, *penmTranslation));
|
---|
948 | return rc;
|
---|
949 | }
|
---|
950 |
|
---|
951 | /** @copydoc PDMIMEDIA::pfnBiosSetTranslation */
|
---|
952 | static DECLCALLBACK(int) drvVmdkBiosSetTranslation(PPDMIMEDIA pInterface,
|
---|
953 | PDMBIOSTRANSLATION enmTranslation)
|
---|
954 | {
|
---|
955 | PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
|
---|
956 | /** @todo */
|
---|
957 | int rc = VINF_SUCCESS;
|
---|
958 | NOREF(pData);
|
---|
959 | LogFlow(("drvVmdkBiosSetTranslation: returns %Vrc (%d)\n", rc, enmTranslation));
|
---|
960 | return rc;
|
---|
961 | }
|
---|
962 |
|
---|
963 |
|
---|
964 | /**
|
---|
965 | * Queries an interface to the driver.
|
---|
966 | *
|
---|
967 | * @returns Pointer to interface.
|
---|
968 | * @returns NULL if the interface was not supported by the driver.
|
---|
969 | * @param pInterface Pointer to this interface structure.
|
---|
970 | * @param enmInterface The requested interface identification.
|
---|
971 | * @thread Any thread.
|
---|
972 | */
|
---|
973 | static DECLCALLBACK(void *) drvVmdkQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
|
---|
974 | {
|
---|
975 | PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
|
---|
976 | PVMDKDISK pData = PDMINS2DATA(pDrvIns, PVMDKDISK);
|
---|
977 | switch (enmInterface)
|
---|
978 | {
|
---|
979 | case PDMINTERFACE_BASE:
|
---|
980 | return &pDrvIns->IBase;
|
---|
981 | case PDMINTERFACE_MEDIA:
|
---|
982 | return &pData->IMedia;
|
---|
983 | default:
|
---|
984 | return NULL;
|
---|
985 | }
|
---|
986 | }
|
---|
987 |
|
---|
988 |
|
---|
989 | /**
|
---|
990 | * VMDK driver registration record.
|
---|
991 | */
|
---|
992 | const PDMDRVREG g_DrvVmdkHDD =
|
---|
993 | {
|
---|
994 | /* u32Version */
|
---|
995 | PDM_DRVREG_VERSION,
|
---|
996 | /* szDriverName */
|
---|
997 | "VmdkHDD",
|
---|
998 | /* pszDescription */
|
---|
999 | "VMDK media driver.",
|
---|
1000 | /* fFlags */
|
---|
1001 | PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
|
---|
1002 | /* fClass. */
|
---|
1003 | PDM_DRVREG_CLASS_MEDIA,
|
---|
1004 | /* cMaxInstances */
|
---|
1005 | ~0,
|
---|
1006 | /* cbInstance */
|
---|
1007 | sizeof(VMDKDISK),
|
---|
1008 | /* pfnConstruct */
|
---|
1009 | drvVmdkConstruct,
|
---|
1010 | /* pfnDestruct */
|
---|
1011 | drvVmdkDestruct,
|
---|
1012 | /* pfnIOCtl */
|
---|
1013 | NULL,
|
---|
1014 | /* pfnPowerOn */
|
---|
1015 | NULL,
|
---|
1016 | /* pfnReset */
|
---|
1017 | NULL,
|
---|
1018 | /* pfnSuspend */
|
---|
1019 | drvVmdkSuspend,
|
---|
1020 | /* pfnResume */
|
---|
1021 | drvVmdkResume,
|
---|
1022 | /* pfnDetach */
|
---|
1023 | NULL
|
---|
1024 | };
|
---|