VirtualBox

Ignore:
Timestamp:
Apr 26, 2007 4:53:00 PM (18 years ago)
Author:
vboxsync
Message:

New VMDK code.

File:
1 moved

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Storage/DrvVD.cpp

    r2000 r2358  
     1/** $Id$ */
    12/** @file
    23 *
    34 * VBox storage devices:
    4  * VBox VMDK container implementation
     5 * Media implementation for VBox disk container
    56 */
    67
     
    1920 * distribution, then only the terms of your commercial VirtualBox
    2021 * 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  */
     22 */
     23
    4924
    5025/*******************************************************************************
    51 *   Header Files                                                               *
     26*   Header files                                                               *
    5227*******************************************************************************/
    53 #define LOG_GROUP LOG_GROUP_DRV_VBOXHDD
     28#define LOG_GROUP LOG_GROUP_DRV_VD
     29#include <VBox/VBoxHDD-new.h>
    5430#include <VBox/pdm.h>
    5531#include <VBox/mm.h>
     
    6339#include <iprt/string.h>
    6440
    65 #include "vl_vbox.h"
    6641#include "Builtins.h"
    6742
     43
    6844/*******************************************************************************
    69 *   Constants And Macros, Structures and Typedefs                              *
     45*   Defined types, constants and macros                                        *
    7046*******************************************************************************/
    7147
    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)
    83 typedef 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)
    98 typedef 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 
    116 typedef 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
     48/** Converts a pointer to VDIDISK::IMedia to a PVBOXDISK. */
     49#define PDMIMEDIA_2_VBOXDISK(pInterface) \
     50    ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMedia)) )
     51
     52/** Converts a pointer to PDMDRVINS::IBase to a PPDMDRVINS. */
     53#define PDMIBASE_2_DRVINS(pInterface) \
     54    ( (PPDMDRVINS)((uintptr_t)pInterface - RT_OFFSETOF(PDMDRVINS, IBase)) )
     55
     56/** Converts a pointer to PDMDRVINS::IBase to a PVBOXDISK. */
     57#define PDMIBASE_2_VBOXDISK(pInterface) \
     58    ( PDMINS2DATA(PDMIBASE_2_DRVINS(pInterface), PVBOXDISK) )
    14159
    14260
    14361/**
    144  * Harddisk geometry.
    145  */
    146 #pragma pack(1)
    147 typedef 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  */
    163 typedef struct VMDKDISK
    164 {
    165     uint32_t        u32Signature;
    166 
    167     BDRVVmdkState   VmdkState;
    168 
    169     /** Hard disk geometry. */
    170     VMDKDISKGEOMETRY Geometry;
    171 
     62 * VBox disk container media main structure, private part.
     63 */
     64typedef struct VBOXDISK
     65{
     66    /** The VBox disk container. */
     67    PVBOXHDD        pDisk;
    17268    /** The media interface. */
    17369    PDMIMEDIA       IMedia;
    17470    /** Pointer to the driver instance. */
    17571    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 
     72    /** Name of the image format backend. */
     73    char            szFormat[16];
     74    /** Flag whether suspend has changed image open mode to read only. */
     75    bool            fTempReadOnly;
     76} VBOXDISK, *PVBOXDISK;
    18877
    18978/*******************************************************************************
    190 *   Internal Functions                                                         *
     79*   Error reporting callback                                                   *
    19180*******************************************************************************/
    192 static DECLCALLBACK(int)  vmdkConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle);
    193 static DECLCALLBACK(void) vmdkDestruct(PPDMDRVINS pDrvIns);
    194 static DECLCALLBACK(int)  vmdkRead(PPDMIMEDIA pInterface,
    195                                   uint64_t off, void *pvBuf, size_t cbRead);
    196 static DECLCALLBACK(int)  vmdkWrite(PPDMIMEDIA pInterface,
    197                                    uint64_t off, const void *pvBuf, size_t cbWrite);
    198 static DECLCALLBACK(int)  vmdkFlush(PPDMIMEDIA pInterface);
    199 static DECLCALLBACK(uint64_t) vmdkGetSize(PPDMIMEDIA pInterface);
    200 static DECLCALLBACK(int)  vmdkBiosGetGeometry(PPDMIMEDIA pInterface, uint32_t *pcCylinders,
    201                                              uint32_t *pcHeads, uint32_t *pcSectors);
    202 static DECLCALLBACK(int)  vmdkBiosSetGeometry(PPDMIMEDIA pInterface, uint32_t cCylinders,
    203                                              uint32_t cHeads, uint32_t cSectors);
    204 static DECLCALLBACK(int)  vmdkGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid);
    205 static DECLCALLBACK(bool) vmdkIsReadOnly(PPDMIMEDIA pInterface);
    206 static DECLCALLBACK(int)  vmdkBiosGetTranslation(PPDMIMEDIA pInterface,
    207                                                 PPDMBIOSTRANSLATION penmTranslation);
    208 static DECLCALLBACK(int)  vmdkBiosSetTranslation(PPDMIMEDIA pInterface,
    209                                                 PDMBIOSTRANSLATION enmTranslation);
    210 static DECLCALLBACK(void *) vmdkQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface);
    211 
    212 #if 0
    213 static 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 
    228 static 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);
     81
     82static void vdErrorCallback(void *pvUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va)
     83{
     84    PPDMDRVINS pDrvIns = (PPDMDRVINS)pvUser;
     85    pDrvIns->pDrvHlp->pfnVMSetErrorV(pDrvIns, rc, RT_SRC_POS_ARGS, pszFormat, va);
     86}
     87
     88/*******************************************************************************
     89*   Media interface methods                                                    *
     90*******************************************************************************/
     91
     92/** @copydoc PDMIMEDIA::pfnRead */
     93static DECLCALLBACK(int) vdRead(PPDMIMEDIA pInterface,
     94                                uint64_t off, void *pvBuf, size_t cbRead)
     95{
     96    LogFlow(("%s: off=%#llx pvBuf=%p cbRead=%d\n", __FUNCTION__,
     97             off, pvBuf, cbRead));
     98    PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
     99    int rc = VDRead(pData->pDisk, off, pvBuf, cbRead);
     100    if (VBOX_SUCCESS(rc))
     101        Log2(("%s: off=%#llx pvBuf=%p cbRead=%d %.*Vhxd\n", __FUNCTION__,
     102              off, pvBuf, cbRead, cbRead, pvBuf));
     103    LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
     104    return rc;
     105}
     106
     107/** @copydoc PDMIMEDIA::pfnWrite */
     108static DECLCALLBACK(int) vdWrite(PPDMIMEDIA pInterface,
     109                                 uint64_t off, const void *pvBuf,
     110                                 size_t cbWrite)
     111{
     112    LogFlow(("%s: off=%#llx pvBuf=%p cbWrite=%d\n", __FUNCTION__,
     113             off, pvBuf, cbWrite));
     114    PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
     115    Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d %.*Vhxd\n", __FUNCTION__,
     116          off, pvBuf, cbWrite, cbWrite, pvBuf));
     117    int rc = VDWrite(pData->pDisk, off, pvBuf, cbWrite);
     118    LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
     119    return rc;
     120}
     121
     122/** @copydoc PDMIMEDIA::pfnFlush */
     123static DECLCALLBACK(int) vdFlush(PPDMIMEDIA pInterface)
     124{
     125    LogFlow(("%s:\n", __FUNCTION__));
     126    PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
     127    int rc = VDFlush(pData->pDisk);
     128    LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
     129    return rc;
     130}
     131
     132/** @copydoc PDMIMEDIA::pfnGetSize */
     133static DECLCALLBACK(uint64_t) vdGetSize(PPDMIMEDIA pInterface)
     134{
     135    LogFlow(("%s:\n", __FUNCTION__));
     136    PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
     137    uint64_t cb = VDGetSize(pData->pDisk);
     138    LogFlow(("%s: returns %#llx (%llu)\n", __FUNCTION__, cb, cb));
     139    return cb;
     140}
     141
     142/** @copydoc PDMIMEDIA::pfnIsReadOnly */
     143static DECLCALLBACK(bool) vdIsReadOnly(PPDMIMEDIA pInterface)
     144{
     145    LogFlow(("%s:\n", __FUNCTION__));
     146    PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
     147    bool f = VDIsReadOnly(pData->pDisk);
     148    LogFlow(("%s: returns %d\n", __FUNCTION__, f));
     149    return f;
     150}
     151
     152/** @copydoc PDMIMEDIA::pfnBiosGetGeometry */
     153static DECLCALLBACK(int) vdBiosGetGeometry(PPDMIMEDIA pInterface,
     154                                           uint32_t *pcCylinders,
     155                                           uint32_t *pcHeads,
     156                                           uint32_t *pcSectors)
     157{
     158    LogFlow(("%s:\n", __FUNCTION__));
     159    PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
     160    int rc = VDGetGeometry(pData->pDisk, pcCylinders, pcHeads, pcSectors);
    244161    if (VBOX_FAILURE(rc))
    245162    {
    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 = (le64_to_cpu(header.capacity) /
    312                                       (16 * 63));
    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 
    383 static 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
    475 static 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 
    491 static 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 
    523 static 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 
    555 static void vmdk_close(BDRVVmdkState *s)
    556 {
    557     RTMemFree(s->l1_table);
    558     RTMemFree(s->l2_cache);
    559     RTFileClose(s->File);
    560 }
    561 
    562 static 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  */
    574 IDER3DECL(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  */
    591 IDER3DECL(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  */
    606 IDER3DECL(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  */
    624 IDER3DECL(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  */
    666 IDER3DECL(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  */
    688 IDER3DECL(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 }
     163        Log(("%s: geometry not available.\n", __FUNCTION__));
     164        rc = VERR_PDM_GEOMETRY_NOT_SET;
     165    }
     166    LogFlow(("%s: returns %Vrc (CHS=%d/%d/%d)\n", __FUNCTION__,
     167             rc, *pcCylinders, *pcHeads, *pcSectors));
     168    return rc;
     169}
     170
     171/** @copydoc PDMIMEDIA::pfnBiosSetGeometry */
     172static DECLCALLBACK(int) vdBiosSetGeometry(PPDMIMEDIA pInterface,
     173                                           uint32_t cCylinders,
     174                                           uint32_t cHeads,
     175                                           uint32_t cSectors)
     176{
     177    LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
     178             cCylinders, cHeads, cSectors));
     179    PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
     180    int rc = VDSetGeometry(pData->pDisk, cCylinders, cHeads, cSectors);
     181    LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
     182    return rc;
     183}
     184
     185/** @copydoc PDMIMEDIA::pfnBiosGetTranslation */
     186static DECLCALLBACK(int) vdBiosGetTranslation(PPDMIMEDIA pInterface,
     187                                              PPDMBIOSTRANSLATION penmTranslation)
     188{
     189    LogFlow(("%s:\n", __FUNCTION__));
     190    PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
     191    int rc = VDGetTranslation(pData->pDisk, penmTranslation);
     192    LogFlow(("%s: returns %Vrc (%d)\n", __FUNCTION__, rc, *penmTranslation));
     193    return rc;
     194}
     195
     196/** @copydoc PDMIMEDIA::pfnBiosSetTranslation */
     197static DECLCALLBACK(int) vdBiosSetTranslation(PPDMIMEDIA pInterface,
     198                                              PDMBIOSTRANSLATION enmTranslation)
     199{
     200    LogFlow(("%s:\n", __FUNCTION__));
     201    PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
     202    int rc = VDSetTranslation(pData->pDisk, enmTranslation);
     203    LogFlow(("%s: returns %Vrc (%d)\n", __FUNCTION__, rc, enmTranslation));
     204    return rc;
     205}
     206
     207/** @copydoc PDMIMEDIA::pfnGetUuid */
     208static DECLCALLBACK(int) vdGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
     209{
     210    LogFlow(("%s:\n", __FUNCTION__));
     211    PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
     212    int rc = VDGetUuid(pData->pDisk, 0, pUuid);
     213    LogFlow(("%s: returns %Vrc ({%Vuuid})\n", __FUNCTION__, rc, pUuid));
     214    return rc;
     215}
     216
    696217
    697218/*******************************************************************************
    698 *   PDM interface                                                              *
     219*   Base interface methods                                                     *
    699220*******************************************************************************/
    700221
    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  */
    711 static DECLCALLBACK(int) vmdkConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
    712 {
    713     LogFlow(("vmdkConstruct:\n"));
    714     PVMDKDISK pData = PDMINS2DATA(pDrvIns, PVMDKDISK);
    715 
    716     /*
    717      * Init the static parts.
    718      */
    719     pDrvIns->IBase.pfnQueryInterface    = vmdkQueryInterface;
    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               = vmdkRead;
    731     pData->IMedia.pfnWrite              = vmdkWrite;
    732     pData->IMedia.pfnFlush              = vmdkFlush;
    733     pData->IMedia.pfnGetSize            = vmdkGetSize;
    734     pData->IMedia.pfnGetUuid            = vmdkGetUuid;
    735     pData->IMedia.pfnIsReadOnly         = vmdkIsReadOnly;
    736     pData->IMedia.pfnBiosGetGeometry    = vmdkBiosGetGeometry;
    737     pData->IMedia.pfnBiosSetGeometry    = vmdkBiosSetGeometry;
    738     pData->IMedia.pfnBiosGetTranslation = vmdkBiosGetTranslation;
    739     pData->IMedia.pfnBiosSetTranslation = vmdkBiosSetTranslation;
    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(("vmdkConstruct: 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  */
    785 static DECLCALLBACK(void) vmdkDestruct(PPDMDRVINS pDrvIns)
    786 {
    787     LogFlow(("vmdkDestruct:\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  */
    799 static DECLCALLBACK(void) vmdkSuspend(PPDMDRVINS pDrvIns)
    800 {
    801     LogFlow(("vmdkSuspend:\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 vmdkSuspend.
    814  *
    815  * @param   pDrvIns     The driver instance data.
    816  */
    817 static DECLCALLBACK(void) vmdkResume(PPDMDRVINS pDrvIns)
    818 {
    819     LogFlow(("vmdkSuspend:\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 */
    831 static DECLCALLBACK(uint64_t) vmdkGetSize(PPDMIMEDIA pInterface)
    832 {
    833     PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
    834     uint64_t cb = VMDKDiskGetSize(pData);
    835     LogFlow(("vmdkGetSize: 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  */
    844 static DECLCALLBACK(int) vmdkBiosGetGeometry(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(("vmdkBiosGetGeometry: returns VINF_SUCCESS\n"));
    851         return VINF_SUCCESS;
    852     }
    853     Log(("vmdkBiosGetGeometry: 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  */
    862 static DECLCALLBACK(int) vmdkBiosSetGeometry(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(("vmdkBiosSetGeometry: 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  */
    875 static DECLCALLBACK(int) vmdkRead(PPDMIMEDIA pInterface, uint64_t off, void *pvBuf, size_t cbRead)
    876 {
    877     LogFlow(("vmdkRead: 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(("vmdkRead: off=%#llx pvBuf=%p cbRead=%d\n"
    882               "%.*Vhxd\n",
    883               off, pvBuf, cbRead, cbRead, pvBuf));
    884     LogFlow(("vmdkRead: returns %Vrc\n", rc));
    885     return rc;
    886 }
    887 
    888 /**
    889  * Write bits.
    890  *
    891  * @see PDMIMEDIA::pfnWrite for details.
    892  */
    893 static DECLCALLBACK(int) vmdkWrite(PPDMIMEDIA pInterface, uint64_t off, const void *pvBuf, size_t cbWrite)
    894 {
    895     LogFlow(("vmdkWrite: off=%#llx pvBuf=%p cbWrite=%d\n", off, pvBuf, cbWrite));
    896     PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
    897     Log2(("vmdkWrite: 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(("vmdkWrite: returns %Vrc\n", rc));
    902     return rc;
    903 }
    904 
    905 /**
    906  * Flush bits to media.
    907  *
    908  * @see PDMIMEDIA::pfnFlush for details.
    909  */
    910 static DECLCALLBACK(int) vmdkFlush(PPDMIMEDIA pInterface)
    911 {
    912     LogFlow(("vmdkFlush:\n"));
    913     PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
    914     vmdk_flush(&pData->VmdkState);
    915     int rc = VINF_SUCCESS;
    916     LogFlow(("vmdkFlush: returns %Vrc\n", rc));
    917     return rc;
    918 }
    919 
    920 /** @copydoc PDMIMEDIA::pfnGetUuid */
    921 static DECLCALLBACK(int) vmdkGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
    922 {
    923     PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
    924     /** @todo */
    925     int rc = VINF_SUCCESS;
    926     NOREF(pData);
    927     LogFlow(("vmdkGetUuid: returns %Vrc ({%Vuuid})\n", rc, pUuid));
    928     return rc;
    929 }
    930 
    931 /** @copydoc PDMIMEDIA::pfnIsReadOnly */
    932 static DECLCALLBACK(bool) vmdkIsReadOnly(PPDMIMEDIA pInterface)
    933 {
    934     PVMDKDISK pData = PDMIMEDIA_2_VMDKDISK(pInterface);
    935     LogFlow(("vmdkIsReadOnly: returns %d\n", VMDKDiskIsReadOnly(pData)));
    936     return VMDKDiskIsReadOnly(pData);
    937 }
    938 
    939 /** @copydoc PDMIMEDIA::pfnBiosGetTranslation */
    940 static DECLCALLBACK(int) vmdkBiosGetTranslation(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(("vmdkBiosGetTranslation: returns %Vrc (%d)\n", rc, *penmTranslation));
    948     return rc;
    949 }
    950 
    951 /** @copydoc PDMIMEDIA::pfnBiosSetTranslation */
    952 static DECLCALLBACK(int) vmdkBiosSetTranslation(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(("vmdkBiosSetTranslation: 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  */
    973 static DECLCALLBACK(void *) vmdkQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
     222/** @copydoc PDMIBASE::pfnQueryInterface */
     223static DECLCALLBACK(void *) vdQueryInterface(PPDMIBASE pInterface,
     224                                             PDMINTERFACE enmInterface)
    974225{
    975226    PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
    976     PVMDKDISK pData = PDMINS2DATA(pDrvIns, PVMDKDISK);
     227    PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
    977228    switch (enmInterface)
    978229    {
     
    987238
    988239
     240/*******************************************************************************
     241*   Driver methods                                                             *
     242*******************************************************************************/
     243
     244
    989245/**
    990  * VMDK driver registration record.
    991  */
    992 const PDMDRVREG g_DrvVmdkHDD =
     246 * Construct a VBox disk media driver instance.
     247 *
     248 * @returns VBox status.
     249 * @param   pDrvIns     The driver instance data.
     250 *                      If the registration structure is needed, pDrvIns->pDrvReg points to it.
     251 * @param   pCfgHandle  Configuration node handle for the driver. Use this to obtain the configuration
     252 *                      of the driver instance. It's also found in pDrvIns->pCfgHandle as it's expected
     253 *                      to be used frequently in this function.
     254 */
     255static DECLCALLBACK(int) vdConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
     256{
     257    LogFlow(("%s:\n", __FUNCTION__));
     258    PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
     259    int rc = VINF_SUCCESS;
     260    char *pszName;          /**< The path of the disk image file. */
     261    bool fReadOnly;         /**< True if the media is readonly. */
     262    bool fHonorZeroWrites;  /**< True if zero blocks should be written. */
     263
     264    /*
     265     * Init the static parts.
     266     */
     267    pDrvIns->IBase.pfnQueryInterface    = vdQueryInterface;
     268    pData->pDrvIns = pDrvIns;
     269    pData->fTempReadOnly = false;
     270
     271    /* IMedia */
     272    pData->IMedia.pfnRead               = vdRead;
     273    pData->IMedia.pfnWrite              = vdWrite;
     274    pData->IMedia.pfnFlush              = vdFlush;
     275    pData->IMedia.pfnGetSize            = vdGetSize;
     276    pData->IMedia.pfnIsReadOnly         = vdIsReadOnly;
     277    pData->IMedia.pfnBiosGetGeometry    = vdBiosGetGeometry;
     278    pData->IMedia.pfnBiosSetGeometry    = vdBiosSetGeometry;
     279    pData->IMedia.pfnBiosGetTranslation = vdBiosGetTranslation;
     280    pData->IMedia.pfnBiosSetTranslation = vdBiosSetTranslation;
     281    pData->IMedia.pfnGetUuid            = vdGetUuid;
     282
     283    /*
     284     * Validate configuration and find all parent images.
     285     * It's sort of up side down from the image dependency tree.
     286     */
     287    unsigned    iLevel = 0;
     288    PCFGMNODE   pCurNode = pCfgHandle;
     289    for (;;)
     290    {
     291        bool fValid;
     292
     293        if (pCurNode == pCfgHandle)
     294        {
     295            /* Toplevel configuration contains the format backend name and
     296             * full image open information. */
     297            fValid = CFGMR3AreValuesValid(pCurNode,
     298                                          "Format\0"
     299                                          "Path\0ReadOnly\0HonorZeroWrites\0");
     300        }
     301        else
     302        {
     303            /* All other image configurations only contain image name. */
     304            fValid = CFGMR3AreValuesValid(pCurNode, "Path\0");
     305        }
     306        if (!fValid)
     307        {
     308            rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
     309                                     RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
     310            break;
     311        }
     312
     313        PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
     314        if (!pParent)
     315            break;
     316        pCurNode = pParent;
     317        iLevel++;
     318    }
     319
     320    /*
     321     * Open the images.
     322     */
     323    if (VBOX_SUCCESS(rc))
     324    {
     325        rc = CFGMR3QueryString(pCfgHandle, "Format", &pData->szFormat[0],
     326                               sizeof(pData->szFormat));
     327        if (rc == VERR_CFGM_VALUE_NOT_FOUND)
     328        {
     329            /* Default disk image format is VMDK. */
     330            rc = VINF_SUCCESS;
     331            strncpy(&pData->szFormat[0], "VMDK", sizeof(pData->szFormat));
     332            pData->szFormat[sizeof(pData->szFormat) - 1] = '\0';
     333        }
     334        if (VBOX_SUCCESS(rc))
     335        {
     336            rc = VDCreate(pData->szFormat, vdErrorCallback, pDrvIns, &pData->pDisk);
     337            /* Error message is already set correctly. */
     338        }
     339        else
     340            rc = PDMDRV_SET_ERROR(pDrvIns, rc,
     341                                  N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
     342    }
     343
     344    while (pCurNode && VBOX_SUCCESS(rc))
     345    {
     346        /*
     347         * Read the image configuration.
     348         */
     349        rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
     350        if (VBOX_FAILURE(rc))
     351        {
     352            VDDestroy(pData->pDisk);
     353            rc = PDMDRV_SET_ERROR(pDrvIns, rc,
     354                                  N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
     355            break;
     356        }
     357
     358        if (iLevel == 0)
     359        {
     360            rc = CFGMR3QueryBool(pCurNode, "ReadOnly", &fReadOnly);
     361            if (rc == VERR_CFGM_VALUE_NOT_FOUND)
     362                fReadOnly = false;
     363            else if (VBOX_FAILURE(rc))
     364            {
     365                MMR3HeapFree(pszName);
     366                VDDestroy(pData->pDisk);
     367                rc = PDMDRV_SET_ERROR(pDrvIns, rc,
     368                                      N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
     369                break;
     370            }
     371
     372            rc = CFGMR3QueryBool(pCfgHandle, "HonorZeroWrites", &fHonorZeroWrites);
     373            if (rc == VERR_CFGM_VALUE_NOT_FOUND)
     374                fHonorZeroWrites = false;
     375            else if (VBOX_FAILURE(rc))
     376            {
     377                MMR3HeapFree(pszName);
     378                VDDestroy(pData->pDisk);
     379                rc = PDMDRV_SET_ERROR(pDrvIns, rc,
     380                                      N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
     381                break;
     382            }
     383        }
     384        else
     385        {
     386            fReadOnly = true;
     387            fHonorZeroWrites = false;
     388        }
     389
     390        /*
     391         * Open the image.
     392         */
     393        unsigned uOpenFlags;
     394        if (fReadOnly)
     395            uOpenFlags = VD_OPEN_FLAGS_READONLY;
     396        else
     397            uOpenFlags = VD_OPEN_FLAGS_NORMAL;
     398        if (fHonorZeroWrites)
     399            uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
     400        rc = VDOpen(pData->pDisk, pszName, uOpenFlags);
     401        if (VBOX_SUCCESS(rc))
     402            Log(("%s: %d - Opened '%s' in %s mode\n", __FUNCTION__,
     403                 iLevel, pszName,
     404                 VDIsReadOnly(pData->pDisk) ? "read-only" : "read-write"));
     405        else
     406        {
     407            AssertMsgFailed(("Failed to open image '%s' rc=%Vrc\n", pszName, rc));
     408            VDDestroy(pData->pDisk);
     409            break;
     410        }
     411        MMR3HeapFree(pszName);
     412
     413        /* next */
     414        iLevel--;
     415        pCurNode = CFGMR3GetParent(pCurNode);
     416    }
     417
     418    LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
     419    return rc;
     420}
     421
     422/**
     423 * Destruct a driver instance.
     424 *
     425 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
     426 * resources can be freed correctly.
     427 *
     428 * @param   pDrvIns     The driver instance data.
     429 */
     430static DECLCALLBACK(void) vdDestruct(PPDMDRVINS pDrvIns)
     431{
     432    LogFlow(("%s:\n", __FUNCTION__));
     433    PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
     434    int rc = VDCloseAll(pData->pDisk);
     435    AssertRC(rc);
     436}
     437
     438
     439/**
     440 * When the VM has been suspended we'll change the image mode to read-only
     441 * so that main and others can read the VDIs. This is important when
     442 * saving state and so forth.
     443 *
     444 * @param   pDrvIns     The driver instance data.
     445 */
     446static DECLCALLBACK(void) vdSuspend(PPDMDRVINS pDrvIns)
     447{
     448    LogFlow(("%s:\n", __FUNCTION__));
     449    PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
     450    if (!VDIsReadOnly(pData->pDisk))
     451    {
     452        unsigned uOpenFlags;
     453        int rc = VDGetOpenFlags(pData->pDisk, &uOpenFlags);
     454        AssertRC(rc);
     455        uOpenFlags |= VD_OPEN_FLAGS_READONLY;
     456        rc = VDSetOpenFlags(pData->pDisk, uOpenFlags);
     457        AssertRC(rc);
     458        pData->fTempReadOnly = true;
     459    }
     460}
     461
     462/**
     463 * Before the VM resumes we'll have to undo the read-only mode change
     464 * done in vdSuspend.
     465 *
     466 * @param   pDrvIns     The driver instance data.
     467 */
     468static DECLCALLBACK(void) vdResume(PPDMDRVINS pDrvIns)
     469{
     470    LogFlow(("%s:\n", __FUNCTION__));
     471    PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
     472    if (pData->fTempReadOnly)
     473    {
     474        unsigned uOpenFlags;
     475        int rc = VDGetOpenFlags(pData->pDisk, &uOpenFlags);
     476        AssertRC(rc);
     477        uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
     478        rc = VDSetOpenFlags(pData->pDisk, uOpenFlags);
     479        AssertRC(rc);
     480        pData->fTempReadOnly = false;
     481    }
     482}
     483
     484
     485
     486/**
     487 * VBox disk container media driver registration record.
     488 */
     489const PDMDRVREG g_DrvVD =
    993490{
    994491    /* u32Version */
    995492    PDM_DRVREG_VERSION,
    996493    /* szDriverName */
    997     "VmdkHDD",
     494    "VD",
    998495    /* pszDescription */
    999     "VMDK media driver.",
     496    "Generic VBox disk media driver.",
    1000497    /* fFlags */
    1001498    PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
     
    1005502    ~0,
    1006503    /* cbInstance */
    1007     sizeof(VMDKDISK),
     504    sizeof(VBOXDISK),
    1008505    /* pfnConstruct */
    1009     vmdkConstruct,
     506    vdConstruct,
    1010507    /* pfnDestruct */
    1011     vmdkDestruct,
     508    vdDestruct,
    1012509    /* pfnIOCtl */
    1013510    NULL,
     
    1017514    NULL,
    1018515    /* pfnSuspend */
    1019     vmdkSuspend,
     516    vdSuspend,
    1020517    /* pfnResume */
    1021     vmdkResume,
     518    vdResume,
    1022519    /* pfnDetach */
    1023520    NULL
Note: See TracChangeset for help on using the changeset viewer.

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