VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvVmdk.cpp@ 4672

Last change on this file since 4672 was 4071, checked in by vboxsync, 17 years ago

Biggest check-in ever. New source code headers for all (C) innotek files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.4 KB
Line 
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)
73typedef 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)
88typedef 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
106typedef 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)
137typedef 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 */
153typedef 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*******************************************************************************/
182static DECLCALLBACK(int) drvVmdkConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle);
183static DECLCALLBACK(void) drvVmdkDestruct(PPDMDRVINS pDrvIns);
184static DECLCALLBACK(int) drvVmdkRead(PPDMIMEDIA pInterface,
185 uint64_t off, void *pvBuf, size_t cbRead);
186static DECLCALLBACK(int) drvVmdkWrite(PPDMIMEDIA pInterface,
187 uint64_t off, const void *pvBuf, size_t cbWrite);
188static DECLCALLBACK(int) drvVmdkFlush(PPDMIMEDIA pInterface);
189static DECLCALLBACK(uint64_t) drvVmdkGetSize(PPDMIMEDIA pInterface);
190static DECLCALLBACK(int) drvVmdkBiosGetGeometry(PPDMIMEDIA pInterface, uint32_t *pcCylinders,
191 uint32_t *pcHeads, uint32_t *pcSectors);
192static DECLCALLBACK(int) drvVmdkBiosSetGeometry(PPDMIMEDIA pInterface, uint32_t cCylinders,
193 uint32_t cHeads, uint32_t cSectors);
194static DECLCALLBACK(int) drvVmdkGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid);
195static DECLCALLBACK(bool) drvVmdkIsReadOnly(PPDMIMEDIA pInterface);
196static DECLCALLBACK(int) drvVmdkBiosGetTranslation(PPDMIMEDIA pInterface,
197 PPDMBIOSTRANSLATION penmTranslation);
198static DECLCALLBACK(int) drvVmdkBiosSetTranslation(PPDMIMEDIA pInterface,
199 PDMBIOSTRANSLATION enmTranslation);
200static DECLCALLBACK(void *) drvVmdkQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface);
201
202#if 0
203static 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
218static 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
373static 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
465static 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
481static 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
513static 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
545static void vmdk_close(BDRVVmdkState *s)
546{
547 RTMemFree(s->l1_table);
548 RTMemFree(s->l2_cache);
549 RTFileClose(s->File);
550}
551
552static 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 */
564IDER3DECL(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 */
581IDER3DECL(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 */
596IDER3DECL(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 */
614IDER3DECL(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 */
656IDER3DECL(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 */
678IDER3DECL(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 */
701static 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 */
775static 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 */
789static 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 */
807static 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 */
821static 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 */
834static 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 */
852static 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 */
865static 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 */
883static 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 */
900static 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 */
911static 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 */
922static 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 */
930static 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 */
942static 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 */
963static 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 */
982const 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};
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette