Changeset 2358 in vbox for trunk/src/VBox/Devices/Storage/DrvVD.cpp
- Timestamp:
- Apr 26, 2007 4:53:00 PM (18 years ago)
- File:
-
- 1 moved
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Storage/DrvVD.cpp
r2000 r2358 1 /** $Id$ */ 1 2 /** @file 2 3 * 3 4 * VBox storage devices: 4 * VBox VMDK container implementation5 * Media implementation for VBox disk container 5 6 */ 6 7 … … 19 20 * distribution, then only the terms of your commercial VirtualBox 20 21 * 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 49 24 50 25 /******************************************************************************* 51 * Header Files *26 * Header files * 52 27 *******************************************************************************/ 53 #define LOG_GROUP LOG_GROUP_DRV_VBOXHDD 28 #define LOG_GROUP LOG_GROUP_DRV_VD 29 #include <VBox/VBoxHDD-new.h> 54 30 #include <VBox/pdm.h> 55 31 #include <VBox/mm.h> … … 63 39 #include <iprt/string.h> 64 40 65 #include "vl_vbox.h"66 41 #include "Builtins.h" 67 42 43 68 44 /******************************************************************************* 69 * Constants And Macros, Structures and Typedefs*45 * Defined types, constants and macros * 70 46 *******************************************************************************/ 71 47 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) ) 141 59 142 60 143 61 /** 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 */ 64 typedef struct VBOXDISK 65 { 66 /** The VBox disk container. */ 67 PVBOXHDD pDisk; 172 68 /** The media interface. */ 173 69 PDMIMEDIA IMedia; 174 70 /** Pointer to the driver instance. */ 175 71 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; 188 77 189 78 /******************************************************************************* 190 * Internal Functions*79 * Error reporting callback * 191 80 *******************************************************************************/ 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 82 static 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 */ 93 static 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 */ 108 static 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 */ 123 static 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 */ 133 static 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 */ 143 static 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 */ 153 static 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); 244 161 if (VBOX_FAILURE(rc)) 245 162 { 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 */ 172 static 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 */ 186 static 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 */ 197 static 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 */ 208 static 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 696 217 697 218 /******************************************************************************* 698 * PDM interface*219 * Base interface methods * 699 220 *******************************************************************************/ 700 221 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 */ 223 static DECLCALLBACK(void *) vdQueryInterface(PPDMIBASE pInterface, 224 PDMINTERFACE enmInterface) 974 225 { 975 226 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface); 976 PV MDKDISK pData = PDMINS2DATA(pDrvIns, PVMDKDISK);227 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK); 977 228 switch (enmInterface) 978 229 { … … 987 238 988 239 240 /******************************************************************************* 241 * Driver methods * 242 *******************************************************************************/ 243 244 989 245 /** 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 */ 255 static 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 */ 430 static 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 */ 446 static 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 */ 468 static 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 */ 489 const PDMDRVREG g_DrvVD = 993 490 { 994 491 /* u32Version */ 995 492 PDM_DRVREG_VERSION, 996 493 /* szDriverName */ 997 "V mdkHDD",494 "VD", 998 495 /* pszDescription */ 999 " VMDKmedia driver.",496 "Generic VBox disk media driver.", 1000 497 /* fFlags */ 1001 498 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT, … … 1005 502 ~0, 1006 503 /* cbInstance */ 1007 sizeof(V MDKDISK),504 sizeof(VBOXDISK), 1008 505 /* pfnConstruct */ 1009 v mdkConstruct,506 vdConstruct, 1010 507 /* pfnDestruct */ 1011 v mdkDestruct,508 vdDestruct, 1012 509 /* pfnIOCtl */ 1013 510 NULL, … … 1017 514 NULL, 1018 515 /* pfnSuspend */ 1019 v mdkSuspend,516 vdSuspend, 1020 517 /* pfnResume */ 1021 v mdkResume,518 vdResume, 1022 519 /* pfnDetach */ 1023 520 NULL
Note:
See TracChangeset
for help on using the changeset viewer.