VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VmdkHDD.cpp@ 1248

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

Storage/Main/GUI: Implemented preliminary support of VMDK images version 3 and 4 (no separate Disk DescriptionFile support).

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