VirtualBox

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

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

export to OSE

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