VirtualBox

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

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

Rename identifiers to avoid conflicts with the new code.

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