VirtualBox

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

Last change on this file since 3647 was 3647, checked in by vboxsync, 18 years ago

don't depend on the deprecated vl_vbox.h header anymore

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