VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VBoxHDD-new.cpp@ 2833

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

New open flag for just getting the image information, suppressing
base/diff image consistency check.

File size: 63.3 KB
Line 
1/** $Id$ */
2/** @file
3 * VBox HDD Container implementation.
4 */
5
6/*
7 * Copyright (C) 2006 InnoTek Systemberatung GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#define LOG_GROUP LOG_GROUP_VD
26#include <VBox/VBoxHDD-new.h>
27#include <VBox/err.h>
28
29#include <VBox/log.h>
30#include <iprt/alloc.h>
31#include <iprt/assert.h>
32#include <iprt/uuid.h>
33#include <iprt/file.h>
34#include <iprt/string.h>
35#include <iprt/asm.h>
36
37#include "VBoxHDD-newInternal.h"
38
39
40#define VBOXHDDDISK_SIGNATURE 0x6f0e2a7d
41
42
43/**
44 * VBox HDD Container image descriptor.
45 */
46typedef struct VDIMAGE
47{
48 /** Link to parent image descriptor, if any. */
49 struct VDIMAGE *pPrev;
50 /** Link to child image descriptor, if any. */
51 struct VDIMAGE *pNext;
52 /** Container base filename. (UTF-8) */
53 char *pszFilename;
54 /** Data managed by the backend which keeps the actual info. */
55 void *pvBackendData;
56 /** Image open flags (only those handled generically in this code and which
57 * the backends will never ever see). */
58 unsigned uOpenFlags;
59} VDIMAGE, *PVDIMAGE;
60
61/**
62 * uModified bit flags.
63 */
64#define VD_IMAGE_MODIFIED_FLAG BIT(0)
65#define VD_IMAGE_MODIFIED_FIRST BIT(1)
66#define VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE BIT(2)
67
68
69/**
70 * VBox HDD Container main structure, private part.
71 */
72struct VBOXHDD
73{
74 /** Structure signature (VBOXHDDDISK_SIGNATURE). */
75 uint32_t u32Signature;
76
77 /** Number of opened images. */
78 unsigned cImages;
79
80 /** Base image. */
81 PVDIMAGE pBase;
82
83 /** Last opened image in the chain.
84 * The same as pBase if only one image is used. */
85 PVDIMAGE pLast;
86
87 /** Flags representing the modification state. */
88 unsigned uModified;
89
90 /** Cached size of this disk. */
91 uint64_t cbSize;
92 /** Cached CHS geometry for this disk, cylinders. */
93 unsigned cCylinders;
94 /** Cached CHS geometry for this disk, heads. */
95 unsigned cHeads;
96 /** Cached CHS geometry for this disk, sectors. */
97 unsigned cSectors;
98 /** Cached translation mode for this disk. */
99 PDMBIOSTRANSLATION enmTranslation;
100
101 /** Error message processing callback. */
102 PFNVDERROR pfnError;
103 /** Opaque data for error callback. */
104 void *pvErrorUser;
105
106 /** Function pointers for the various backend methods. */
107 PVBOXHDDBACKEND Backend;
108};
109
110
111typedef struct
112{
113 const char *pszBackendName;
114 PVBOXHDDBACKEND Backend;
115} VBOXHDDBACKENDENTRY;
116
117
118extern VBOXHDDBACKEND g_VmdkBackend;
119
120
121static const VBOXHDDBACKENDENTRY aBackends[] =
122{
123 { "VMDK", &g_VmdkBackend },
124 { NULL, NULL }
125};
126
127
128/**
129 * internal: issue early error message.
130 */
131static int vdEarlyError(PFNVDERROR pfnError, void *pvErrorUser, int rc,
132 RT_SRC_POS_DECL, const char *pszFormat, ...)
133{
134 va_list va;
135 va_start(va, pszFormat);
136 if (pfnError)
137 pfnError(pvErrorUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
138 va_end(va);
139 return rc;
140}
141
142/**
143 * internal: issue error message.
144 */
145static int vdError(PVBOXHDD pDisk, int rc, RT_SRC_POS_DECL,
146 const char *pszFormat, ...)
147{
148 va_list va;
149 va_start(va, pszFormat);
150 if (pDisk->pfnError)
151 pDisk->pfnError(pDisk->pvErrorUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
152 va_end(va);
153 return rc;
154}
155
156/**
157 * internal: add image structure to the end of images list.
158 */
159static void vdAddImageToList(PVBOXHDD pDisk, PVDIMAGE pImage)
160{
161 pImage->pPrev = NULL;
162 pImage->pNext = NULL;
163
164 if (pDisk->pBase)
165 {
166 Assert(pDisk->cImages > 0);
167 pImage->pPrev = pDisk->pLast;
168 pDisk->pLast->pNext = pImage;
169 pDisk->pLast = pImage;
170 }
171 else
172 {
173 Assert(pDisk->cImages == 0);
174 pDisk->pBase = pImage;
175 pDisk->pLast = pImage;
176 }
177
178 pDisk->cImages++;
179}
180
181/**
182 * internal: remove image structure from the images list.
183 */
184static void vdRemoveImageFromList(PVBOXHDD pDisk, PVDIMAGE pImage)
185{
186 Assert(pDisk->cImages > 0);
187
188 if (pImage->pPrev)
189 pImage->pPrev->pNext = pImage->pNext;
190 else
191 pDisk->pBase = pImage->pNext;
192
193 if (pImage->pNext)
194 pImage->pNext->pPrev = pImage->pPrev;
195 else
196 pDisk->pLast = pImage->pPrev;
197
198 pImage->pPrev = NULL;
199 pImage->pNext = NULL;
200
201 pDisk->cImages--;
202}
203
204/**
205 * internal: find image by index into the images list.
206 */
207static PVDIMAGE vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
208{
209 PVDIMAGE pImage = pDisk->pBase;
210 while (pImage && nImage)
211 {
212 pImage = pImage->pNext;
213 nImage--;
214 }
215 return pImage;
216}
217
218/**
219 * internal: read the specified amount of data in whatever blocks the backend
220 * will give us.
221 */
222static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
223 void *pvBuf, size_t cbRead)
224{
225 int rc;
226 size_t cbThisRead;
227 PVDIMAGE pCurrImage;
228
229 /* Loop until all read. */
230 do
231 {
232 /* Search for image with allocated block. Do not attempt to read more
233 * than the previous reads marked as valid. Otherwise this would return
234 * stale data when different block sizes are used for the images. */
235 cbThisRead = cbRead;
236 rc = VINF_VDI_BLOCK_FREE;
237 for (pCurrImage = pImage;
238 pCurrImage != NULL && rc == VINF_VDI_BLOCK_FREE;
239 pCurrImage = pCurrImage->pPrev)
240 {
241 rc = pDisk->Backend->pfnRead(pCurrImage->pvBackendData, uOffset,
242 pvBuf, cbThisRead, &cbThisRead);
243 }
244
245 /* No image in the chain contains the data for the block. */
246 if (rc == VINF_VDI_BLOCK_FREE)
247 {
248 memset(pvBuf, '\0', cbThisRead);
249 rc = VINF_SUCCESS;
250 }
251
252 cbRead -= cbThisRead;
253 uOffset += cbThisRead;
254 pvBuf = (char *)pvBuf + cbThisRead;
255 } while (cbRead != 0 && VBOX_SUCCESS(rc));
256
257 return rc;
258}
259
260/**
261 * internal: mark the disk as not modified.
262 */
263static void vdResetModifiedFlag(PVBOXHDD pDisk)
264{
265 if (pDisk->uModified & VD_IMAGE_MODIFIED_FLAG)
266 {
267 /* generate new last-modified uuid */
268 if (!(pDisk->uModified | VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
269 {
270 RTUUID Uuid;
271
272 RTUuidCreate(&Uuid);
273 pDisk->Backend->pfnSetModificationUuid(pDisk->pLast->pvBackendData,
274 &Uuid);
275 }
276
277 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FLAG;
278 }
279}
280
281/**
282 * internal: mark the disk as modified.
283 */
284static void vdSetModifiedFlag(PVBOXHDD pDisk)
285{
286 pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
287 if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
288 {
289 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
290
291 /* First modify, so create a UUID and ensure it's written to disk. */
292 vdResetModifiedFlag(pDisk);
293
294 if (!(pDisk->uModified | VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
295 pDisk->Backend->pfnFlush(pDisk->pLast->pvBackendData);
296 }
297}
298
299/**
300 * internal: write a complete block (only used for diff images), taking the
301 * remaining data from parent images. This implementation does not optimize
302 * anything (except that it tries to read only that portions from parent
303 * images that are really needed).
304 */
305static int vdWriteHelperStandard(PVBOXHDD pDisk, PVDIMAGE pImage,
306 uint64_t uOffset, size_t cbWrite,
307 size_t cbThisWrite, size_t cbPreRead,
308 size_t cbPostRead, const void *pvBuf,
309 void *pvTmp)
310{
311 int rc = VINF_SUCCESS;
312
313 /* Read the data that goes before the write to fill the block. */
314 if (cbPreRead)
315 {
316 rc = vdReadHelper(pDisk, pImage->pPrev, uOffset - cbPreRead,
317 pvTmp, cbPreRead);
318 if (VBOX_FAILURE(rc))
319 return rc;
320 }
321
322 /* Copy the data to the right place in the buffer. */
323 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
324
325 /* Read the data that goes after the write to fill the block. */
326 if (cbPostRead)
327 {
328 /* If we have data to be written, use that instead of reading
329 * data from the image. */
330 size_t cbWriteCopy;
331 if (cbWrite > cbThisWrite)
332 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
333 else
334 cbWriteCopy = 0;
335 /* Figure out how much we cannnot read from the image, because
336 * the last block to write might exceed the nominal size of the
337 * image for technical reasons. */
338 size_t cbFill;
339 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
340 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
341 else
342 cbFill = 0;
343 /* The rest must be read from the image. */
344 size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
345
346 /* Now assemble the remaining data. */
347 if (cbWriteCopy)
348 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
349 (char *)pvBuf + cbThisWrite, cbWriteCopy);
350 if (cbReadImage)
351 rc = vdReadHelper(pDisk, pImage->pPrev,
352 uOffset + cbThisWrite + cbWriteCopy,
353 (char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy,
354 cbReadImage);
355 if (VBOX_FAILURE(rc))
356 return rc;
357 /* Zero out the remainder of this block. Will never be visible, as this
358 * is beyond the limit of the image. */
359 if (cbFill)
360 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
361 '\0', cbFill);
362 }
363
364 /* Write the full block to the virtual disk. */
365 rc = pDisk->Backend->pfnWrite(pImage->pvBackendData,
366 uOffset - cbPreRead, pvTmp,
367 cbPreRead + cbThisWrite + cbPostRead,
368 NULL,
369 &cbPreRead, &cbPostRead);
370 Assert(rc != VINF_VDI_BLOCK_FREE);
371 Assert(cbPreRead == 0);
372 Assert(cbPostRead == 0);
373
374 return rc;
375}
376
377/**
378 * internal: write a complete block (only used for diff images), taking the
379 * remaining data from parent images. This implementation optimized out writes
380 * that do not change the data relative to the state as of the parent images.
381 * All backends which support differential/growing images support this.
382 */
383static int vdWriteHelperOptimized(PVBOXHDD pDisk, PVDIMAGE pImage,
384 uint64_t uOffset, size_t cbWrite,
385 size_t cbThisWrite, size_t cbPreRead,
386 size_t cbPostRead, const void *pvBuf,
387 void *pvTmp)
388{
389 size_t cbFill = 0;
390 size_t cbWriteCopy = 0;
391 size_t cbReadImage = 0;
392 int rc;
393
394 if (cbPostRead)
395 {
396 /* Figure out how much we cannnot read from the image, because
397 * the last block to write might exceed the nominal size of the
398 * image for technical reasons. */
399 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
400 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
401
402 /* If we have data to be written, use that instead of reading
403 * data from the image. */
404 if (cbWrite > cbThisWrite)
405 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
406
407 /* The rest must be read from the image. */
408 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
409 }
410
411 /* Read the entire data of the block so that we can compare whether it will
412 * be modified by the write or not. */
413 rc = vdReadHelper(pDisk, pImage->pPrev, uOffset - cbPreRead,
414 pvTmp, cbPreRead + cbThisWrite + cbPostRead - cbFill);
415 if (VBOX_FAILURE(rc))
416 return rc;
417
418 /* Check if the write would modify anything in this block. */
419 if ( !memcmp((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite)
420 && (!cbWriteCopy || !memcmp((char *)pvTmp + cbPreRead + cbThisWrite,
421 (char *)pvBuf + cbThisWrite, cbWriteCopy)))
422 {
423 /* Block is completely unchanged, so no need to write anything. */
424 return VINF_SUCCESS;
425 }
426
427 /* Copy the data to the right place in the buffer. */
428 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
429
430 /* Handle the data that goes after the write to fill the block. */
431 if (cbPostRead)
432 {
433 /* Now assemble the remaining data. */
434 if (cbWriteCopy)
435 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
436 (char *)pvBuf + cbThisWrite, cbWriteCopy);
437 /* Zero out the remainder of this block. Will never be visible, as this
438 * is beyond the limit of the image. */
439 if (cbFill)
440 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
441 '\0', cbFill);
442 }
443
444 /* Write the full block to the virtual disk. */
445 rc = pDisk->Backend->pfnWrite(pImage->pvBackendData,
446 uOffset - cbPreRead, pvTmp,
447 cbPreRead + cbThisWrite + cbPostRead,
448 NULL,
449 &cbPreRead, &cbPostRead);
450 Assert(rc != VINF_VDI_BLOCK_FREE);
451 Assert(cbPreRead == 0);
452 Assert(cbPostRead == 0);
453
454 return rc;
455}
456
457/**
458 * Allocates and initializes an empty VBox HDD container.
459 * No image files are opened.
460 *
461 * @returns VBox status code.
462 * @param pszBackend Name of the image file backend to use.
463 * @param pfnError Callback for setting extended error information.
464 * @param pvErrorUser Opaque parameter for pfnError.
465 * @param ppDisk Where to store the reference to the VBox HDD container.
466 */
467VBOXDDU_DECL(int) VDCreate(const char *pszBackend, PFNVDERROR pfnError,
468 void *pvErrorUser, PVBOXHDD *ppDisk)
469{
470 int rc = VINF_SUCCESS;
471 PVBOXHDDBACKEND pBackend = NULL;
472 PVBOXHDD pDisk = NULL;
473
474 /* Passing an error callback is strictly not necessary any more. Any code
475 * calling the HDD container functions should provide one, as otherwise
476 * many detailed error messages will go unnoticed. If you find a situation
477 * where you get no sensible error message from this code but you think
478 * there should be one, shout loudly. There are no error messages for rare
479 * and obvious error codes such as VERR_NO_MEMORY, and for situations which
480 * the user cannot be made responsible for, such as program bugs causing
481 * parameter checks to fail etc. */
482 Assert(pfnError);
483
484 /* Find backend. */
485 for (unsigned i = 0; aBackends[i].pszBackendName != NULL; i++)
486 {
487 if (!strcmp(pszBackend, aBackends[i].pszBackendName))
488 {
489 pBackend = aBackends[i].Backend;
490 break;
491 }
492 }
493 if (pBackend)
494 {
495 pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
496 if (pDisk)
497 {
498 pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
499 pDisk->cImages = 0;
500 pDisk->pBase = NULL;
501 pDisk->pLast = NULL;
502 pDisk->cbSize = 0;
503 pDisk->cCylinders = 0;
504 pDisk->cHeads = 0;
505 pDisk->cSectors = 0;
506 pDisk->pfnError = pfnError;
507 pDisk->pvErrorUser = pvErrorUser;
508 pDisk->Backend = pBackend;
509 *ppDisk = pDisk;
510 }
511 else
512 rc = VERR_NO_MEMORY;
513 }
514 else
515 rc = vdEarlyError(pfnError, pvErrorUser, VERR_INVALID_PARAMETER,
516 RT_SRC_POS, "VD: unknown backend name '%s'",
517 pszBackend);
518
519 LogFlow(("%s: returns %Vrc (pDisk=%#p)\n", __FUNCTION__, rc, pDisk));
520 return rc;
521}
522
523/**
524 * Destroys the VBox HDD container.
525 * If container has opened image files they will be closed.
526 *
527 * @param pDisk Pointer to VBox HDD container.
528 */
529VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk)
530{
531 LogFlow(("%s: pDisk=%#p\n", pDisk));
532 /* sanity check */
533 Assert(pDisk);
534 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
535
536 if (pDisk)
537 {
538 VDCloseAll(pDisk);
539 RTMemFree(pDisk);
540 }
541}
542
543/**
544 * Opens an image file.
545 *
546 * The first opened image file in a HDD container must have a base image type,
547 * others (next opened images) must be a differencing or undo images.
548 * Linkage is checked for differencing image to be in consistence with the previously opened image.
549 * When another differencing image is opened and the last image was opened in read/write access
550 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
551 * other processes to use images in read-only mode too.
552 *
553 * Note that the image can be opened in read-only mode if a read/write open is not possible.
554 * Use VDIsReadOnly to check open mode.
555 *
556 * @returns VBox status code.
557 * @param pDisk Pointer to VBox HDD container.
558 * @param pszFilename Name of the image file to open.
559 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
560 */
561VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszFilename,
562 unsigned uOpenFlags)
563{
564 int rc = VINF_SUCCESS;
565 LogFlow(("%s: pszFilename=\"%s\" uOpenFlags=%#x\n", __FUNCTION__,
566 pszFilename, uOpenFlags));
567 /* sanity check */
568 Assert(pDisk);
569 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
570
571 /* Check arguments. */
572 if ( !pszFilename
573 || *pszFilename == '\0'
574 || (uOpenFlags & ~VD_OPEN_FLAGS_MASK))
575 {
576 AssertMsgFailed(("Invalid arguments: pszFilename=%#p uOpenFlags=%#x\n", pszFilename, uOpenFlags));
577 return VERR_INVALID_PARAMETER;
578 }
579
580 /* Force readonly for images without base/diff consistency checking. */
581 if (uOpenFlags & VD_OPEN_FLAGS_INFO)
582 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
583
584 /* Set up image descriptor. */
585 PVDIMAGE pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
586 if (!pImage)
587 return VERR_NO_MEMORY;
588 pImage->pszFilename = RTStrDup(pszFilename);
589 if (!pImage->pszFilename)
590 rc = VERR_NO_MEMORY;
591
592 if (VBOX_SUCCESS(rc))
593 {
594 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
595 rc = pDisk->Backend->pfnOpen(pImage->pszFilename,
596 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
597 pDisk->pfnError, pDisk->pvErrorUser,
598 &pImage->pvBackendData);
599 }
600 /* If the open in read-write mode failed, retry in read-only mode. */
601 if (VBOX_FAILURE(rc))
602 {
603 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
604 && (rc == VERR_ACCESS_DENIED
605 || rc == VERR_PERMISSION_DENIED
606 || rc == VERR_WRITE_PROTECT
607 || rc == VERR_SHARING_VIOLATION
608 || rc == VERR_FILE_LOCK_FAILED))
609 rc = pDisk->Backend->pfnOpen(pImage->pszFilename,
610 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
611 | VD_OPEN_FLAGS_READONLY,
612 pDisk->pfnError, pDisk->pvErrorUser,
613 &pImage->pvBackendData);
614 if (VBOX_FAILURE(rc))
615 rc = vdError(pDisk, rc, RT_SRC_POS,
616 N_("VD: error opening image file '%s'"), pszFilename);
617 }
618
619 if (VBOX_SUCCESS(rc))
620 {
621 VDIMAGETYPE enmImageType;
622 rc = pDisk->Backend->pfnGetImageType(pImage->pvBackendData,
623 &enmImageType);
624 /* Check image type. As the image itself has no idea whether it's a
625 * base image or not, this info is derived here. Image 0 can be fixed
626 * or normal, all others must be normal images. */
627 if ( VBOX_SUCCESS(rc)
628 && !(uOpenFlags & VD_OPEN_FLAGS_INFO)
629 && pDisk->cImages != 0
630 && enmImageType != VD_IMAGE_TYPE_NORMAL)
631 rc = VERR_VDI_INVALID_TYPE;
632
633 /** @todo optionally check UUIDs */
634
635 if (VBOX_SUCCESS(rc))
636 {
637 uint64_t cbSize = pDisk->Backend->pfnGetSize(pImage->pvBackendData);
638 if (pDisk->cImages == 0)
639 {
640 /* Cache disk information. */
641 pDisk->cbSize = cbSize;
642
643 /* Cache CHS geometry. */
644 int rc2 = pDisk->Backend->pfnGetGeometry(pImage->pvBackendData,
645 &pDisk->cCylinders,
646 &pDisk->cHeads,
647 &pDisk->cSectors);
648 if (VBOX_FAILURE(rc2))
649 {
650 pDisk->cCylinders = 0;
651 pDisk->cHeads = 0;
652 pDisk->cSectors = 0;
653 }
654 else
655 {
656 /* Make sure the CHS geometry is properly clipped. */
657 pDisk->cCylinders = RT_MIN(pDisk->cCylinders, 16383);
658 pDisk->cHeads = RT_MIN(pDisk->cHeads, 255);
659 pDisk->cSectors = RT_MIN(pDisk->cSectors, 255);
660 }
661
662 /* Cache translation mode. */
663 rc2 = pDisk->Backend->pfnGetTranslation(pImage->pvBackendData,
664 &pDisk->enmTranslation);
665 if (VBOX_FAILURE(rc2))
666 pDisk->enmTranslation = (PDMBIOSTRANSLATION)0;
667 }
668 else
669 {
670 /* Check image size/block size for consistency. */
671 if (cbSize != pDisk->cbSize)
672 rc = VERR_VDI_INVALID_TYPE;
673 }
674 }
675
676 if (VBOX_SUCCESS(rc) && pDisk->cImages != 0)
677 {
678 /* Switch previous image to read-only mode. */
679 unsigned uOpenFlagsPrevImg;
680 uOpenFlagsPrevImg = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
681 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
682 {
683 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
684 rc = pDisk->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
685 }
686 }
687
688 if (VBOX_SUCCESS(rc))
689 {
690 /* Image successfully opened, make it the last image. */
691 vdAddImageToList(pDisk, pImage);
692 }
693 else
694 {
695 /* Error detected, but image opened. Close image. */
696 int rc2;
697 rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, false);
698 AssertRC(rc2);
699 pImage->pvBackendData = NULL;
700 }
701 }
702
703 if (VBOX_FAILURE(rc))
704 {
705 if (pImage)
706 {
707 if (pImage->pszFilename)
708 RTStrFree(pImage->pszFilename);
709 RTMemFree(pImage);
710 }
711 }
712
713 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
714 return rc;
715}
716
717/**
718 * Creates and opens a new base image file.
719 *
720 * @returns VBox status code.
721 * @param pDisk Pointer to VBox HDD container.
722 * @param pszFilename Name of the image file to create.
723 * @param enmType Image type, only base image types are acceptable.
724 * @param cbSize Image size in bytes.
725 * @param uImageFlags Flags specifying special image features.
726 * @param pszComment Pointer to image comment. NULL is ok.
727 * @param cCylinders Number of cylinders (must be <= 16383).
728 * @param cHeads Number of heads (must be <= 16).
729 * @param cSectors Number of sectors (must be <= 63);
730 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
731 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
732 * @param pvUser User argument for the progress callback.
733 */
734VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszFilename,
735 VDIMAGETYPE enmType, uint64_t cbSize,
736 unsigned uImageFlags, const char *pszComment,
737 unsigned cCylinders, unsigned cHeads,
738 unsigned cSectors, unsigned uOpenFlags,
739 PFNVMPROGRESS pfnProgress, void *pvUser)
740{
741 int rc = VINF_SUCCESS;
742 LogFlow(("%s: pszFilename=\"%s\" uOpenFlags=%#x\n", __FUNCTION__,
743 pszFilename, uOpenFlags));
744 /* sanity check */
745 Assert(pDisk);
746 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
747
748 /* Check arguments. */
749 if ( !pszFilename
750 || *pszFilename == '\0'
751 || (enmType != VD_IMAGE_TYPE_NORMAL && enmType != VD_IMAGE_TYPE_FIXED)
752 || !cbSize
753 || (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
754 || cCylinders == 0
755 || cCylinders > 16383
756 || cHeads == 0
757 || cHeads > 16
758 || cSectors == 0
759 || cSectors > 63)
760 {
761 AssertMsgFailed(("Invalid arguments: pszFilename=%#p uOpenFlags=%#x\n", pszFilename, uOpenFlags));
762 return VERR_INVALID_PARAMETER;
763 }
764
765 /* Check state. */
766 if (pDisk->cImages != 0)
767 {
768 AssertMsgFailed(("Create base image cannot be done with other images open\n"));
769 return VERR_VDI_INVALID_STATE;
770 }
771
772 /* Set up image descriptor. */
773 PVDIMAGE pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
774 if (!pImage)
775 return VERR_NO_MEMORY;
776 pImage->pszFilename = RTStrDup(pszFilename);
777 if (!pImage->pszFilename)
778 rc = VERR_NO_MEMORY;
779
780 if (VBOX_SUCCESS(rc))
781 rc = pDisk->Backend->pfnCreate(pImage->pszFilename, enmType, cbSize,
782 uImageFlags, pszComment, cCylinders,
783 cHeads, cSectors, uOpenFlags,
784 pfnProgress, pvUser,
785 pDisk->pfnError, pDisk->pvErrorUser,
786 &pImage->pvBackendData);
787
788 if (VBOX_SUCCESS(rc))
789 {
790 /** @todo optionally check UUIDs */
791
792 if (VBOX_SUCCESS(rc))
793 {
794 uint64_t cbSize = pDisk->Backend->pfnGetSize(pImage->pvBackendData);
795 if (pDisk->cImages == 0)
796 {
797 /* Cache disk information. */
798 pDisk->cbSize = cbSize;
799
800 /* Cache CHS geometry. */
801 int rc2 = pDisk->Backend->pfnGetGeometry(pImage->pvBackendData,
802 &pDisk->cCylinders,
803 &pDisk->cHeads,
804 &pDisk->cSectors);
805 if (VBOX_FAILURE(rc2))
806 {
807 pDisk->cCylinders = 0;
808 pDisk->cHeads = 0;
809 pDisk->cSectors = 0;
810 }
811 else
812 {
813 /* Make sure the CHS geometry is properly clipped. */
814 pDisk->cCylinders = RT_MIN(pDisk->cCylinders, 16383);
815 pDisk->cHeads = RT_MIN(pDisk->cHeads, 255);
816 pDisk->cSectors = RT_MIN(pDisk->cSectors, 255);
817 }
818
819 /* Cache translation mode. */
820 rc2 = pDisk->Backend->pfnGetTranslation(pImage->pvBackendData,
821 &pDisk->enmTranslation);
822 if (VBOX_FAILURE(rc2))
823 pDisk->enmTranslation = (PDMBIOSTRANSLATION)0;
824 }
825 else
826 {
827 /* Check image size/block size for consistency. */
828 if (cbSize != pDisk->cbSize)
829 rc = VERR_VDI_INVALID_TYPE;
830 }
831 }
832
833 if (VBOX_SUCCESS(rc))
834 {
835 /* Image successfully opened, make it the last image. */
836 vdAddImageToList(pDisk, pImage);
837 }
838 else
839 {
840 /* Error detected, but image opened. Close and delete image. */
841 int rc2;
842 rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, true);
843 AssertRC(rc2);
844 pImage->pvBackendData = NULL;
845 }
846 }
847
848 if (VBOX_FAILURE(rc))
849 {
850 if (pImage)
851 {
852 if (pImage->pszFilename)
853 RTStrFree(pImage->pszFilename);
854 RTMemFree(pImage);
855 }
856 }
857
858 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
859 return rc;
860}
861
862/**
863 * Creates and opens a new differencing image file in HDD container.
864 * See comments for VDOpen function about differencing images.
865 *
866 * @returns VBox status code.
867 * @param pDisk Pointer to VBox HDD container.
868 * @param pszFilename Name of the differencing image file to create.
869 * @param uImageFlags Flags specifying special image features.
870 * @param pszComment Pointer to image comment. NULL is ok.
871 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
872 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
873 * @param pvUser User argument for the progress callback.
874 */
875VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszFilename,
876 unsigned uImageFlags, const char *pszComment,
877 unsigned uOpenFlags,
878 PFNVMPROGRESS pfnProgress, void *pvUser)
879{
880 return VERR_NOT_IMPLEMENTED;
881}
882
883/**
884 * Merges two images having a parent/child relationship (both directions).
885 * As a side effect the source image is deleted from both the disk and
886 * the images in the VBox HDD container.
887 *
888 * @returns VBox status code.
889 * @param pDisk Pointer to VBox HDD container.
890 * @param nImageFrom Name of the image file to merge from.
891 * @param nImageTo Name of the image file to merge to.
892 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
893 * @param pvUser User argument for the progress callback.
894 */
895VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
896 unsigned nImageTo, PFNVMPROGRESS pfnProgress,
897 void *pvUser)
898{
899 return VERR_NOT_IMPLEMENTED;
900}
901
902/**
903 * Copies an image from one VBox HDD container to another.
904 * The copy is opened in the target VBox HDD container.
905 * It is possible to convert between different image formats, because the
906 * backend for the destination VBox HDD container may be different from the
907 * source container.
908 * If both the source and destination reference the same VBox HDD container,
909 * then the image is moved (by copying/deleting) to the new location.
910 * The source container is unchanged if the move operation fails, otherwise
911 * the image at the new location is opened in the same way as the old one was.
912 *
913 * @returns VBox status code.
914 * @param pDiskFrom Pointer to source VBox HDD container.
915 * @param nImage Image number, counts from 0. 0 is always base image of container.
916 * @param pDiskTo Pointer to destination VBox HDD container.
917 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
918 * @param pvUser User argument for the progress callback.
919 */
920VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
921 PFNVMPROGRESS pfnProgress, void *pvUser)
922{
923 return VERR_NOT_IMPLEMENTED;
924}
925
926/**
927 * Compacts a growing image file by removing zeroed data blocks.
928 * Optionally defragments data in the image so that ascending sector numbers
929 * are stored in ascending location in the image file.
930 *
931 * @todo maybe include this function in VDCopy.
932 *
933 * @returns VBox status code.
934 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
935 * @param pDisk Pointer to VBox HDD container.
936 * @param nImage Image number, counts from 0. 0 is always base image of container.
937 * @param fDefragment If true, reorder file data so that sectors are stored in ascending order.
938 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
939 * @param pvUser User argument for the progress callback.
940 */
941VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
942 bool fDefragment,
943 PFNVMPROGRESS pfnProgress, void *pvUser)
944{
945 return VERR_NOT_IMPLEMENTED;
946}
947
948/**
949 * Resizes an image. Allows setting the disk size to both larger and smaller
950 * values than the current disk size.
951 *
952 * @returns VBox status code.
953 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
954 * @param pDisk Pointer to VBox HDD container.
955 * @param nImage Image number, counts from 0. 0 is always base image of container.
956 * @param cbSize New image size in bytes.
957 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
958 * @param pvUser User argument for the progress callback.
959 */
960VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, unsigned nImage, uint64_t cbSize,
961 PFNVMPROGRESS pfnProgress, void *pvUser)
962{
963 return VERR_NOT_IMPLEMENTED;
964}
965
966/**
967 * Closes the last opened image file in the HDD container. Leaves all changes inside it.
968 * If previous image file was opened in read-only mode (that is normal) and closing image
969 * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
970 * will be reopened in read/write mode.
971 *
972 * @param pDisk Pointer to VBox HDD container.
973 * @param fDelete If true, delete the image from the host disk.
974 */
975VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
976{
977 LogFlow(("%s: fDelete=%d\n", __FUNCTION__, fDelete));
978 /* sanity check */
979 Assert(pDisk);
980 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
981
982 PVDIMAGE pImage = pDisk->pLast;
983 unsigned uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pImage->pvBackendData);
984 /* Remove image from list of opened images. */
985 vdRemoveImageFromList(pDisk, pImage);
986 /* Close (and optionally delete) image. */
987 int rc = pDisk->Backend->pfnClose(pImage->pvBackendData, fDelete);
988 /* Free remaining resources related to the image. */
989 RTStrFree(pImage->pszFilename);
990 RTMemFree(pImage);
991
992 /* If disk was previously in read/write mode, make sure it will stay like
993 * this after closing this image. Set the open flags accordingly. */
994 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
995 {
996 uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
997 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
998 rc = pDisk->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData,
999 uOpenFlags);
1000 }
1001
1002 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1003 return rc;
1004}
1005
1006/**
1007 * Closes all opened image files in HDD container.
1008 *
1009 * @param pDisk Pointer to VDI HDD container.
1010 */
1011VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
1012{
1013 LogFlow(("%s:\n", __FUNCTION__));
1014 /* sanity check */
1015 Assert(pDisk);
1016 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1017
1018 PVDIMAGE pImage = pDisk->pLast;
1019 int rc = VINF_SUCCESS;
1020 while (pImage)
1021 {
1022 PVDIMAGE pPrev = pImage->pPrev;
1023 /* Remove image from list of opened images. */
1024 vdRemoveImageFromList(pDisk, pImage);
1025 /* Close image. */
1026 int rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, false);
1027 if (VBOX_FAILURE(rc2) && VBOX_SUCCESS(rc))
1028 rc = rc2;
1029 /* Free remaining resources related to the image. */
1030 RTStrFree(pImage->pszFilename);
1031 RTMemFree(pImage);
1032 pImage = pPrev;
1033 }
1034 Assert(pDisk->pLast == NULL);
1035
1036 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1037 return rc;
1038}
1039
1040/**
1041 * Read data from virtual HDD.
1042 *
1043 * @returns VBox status code.
1044 * @param pDisk Pointer to VBox HDD container.
1045 * @param uOffset Offset of first reading byte from start of disk.
1046 * @param pvBuf Pointer to buffer for reading data.
1047 * @param cbRead Number of bytes to read.
1048 */
1049VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf, size_t cbRead)
1050{
1051 /* sanity check */
1052 LogFlow(("%s: offset=%llu cbRead=%u\n", __FUNCTION__, uOffset, (unsigned)cbRead));
1053 Assert(pDisk);
1054 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1055
1056 int rc = VINF_SUCCESS;
1057 PVDIMAGE pImage = pDisk->pLast;
1058 if (RT_UNLIKELY(!pImage))
1059 {
1060 Assert(pImage);
1061 rc = VERR_VDI_NOT_OPENED;
1062 goto out;
1063 }
1064
1065 /* Check params. */
1066 if (uOffset + cbRead > pDisk->cbSize || cbRead == 0)
1067 {
1068 AssertMsgFailed(("uOffset=%llu cbRead=%u\n", uOffset, cbRead));
1069 return VERR_INVALID_PARAMETER;
1070 }
1071
1072 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead);
1073out:
1074 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1075 return rc;
1076}
1077
1078/**
1079 * Write data to virtual HDD.
1080 *
1081 * @returns VBox status code.
1082 * @param pDisk Pointer to VBox HDD container.
1083 * @param uOffset Offset of first reading byte from start of disk.
1084 * @param pvBuf Pointer to buffer for writing data.
1085 * @param cbWrite Number of bytes to write.
1086 */
1087VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf, size_t cbWrite)
1088{
1089 /* sanity check */
1090 Assert(pDisk);
1091 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1092
1093 int rc = VINF_SUCCESS;
1094 size_t cbThisWrite;
1095 size_t cbPreRead, cbPostRead;
1096 PVDIMAGE pImage = pDisk->pLast;
1097 if (RT_UNLIKELY(!pImage))
1098 {
1099 Assert(pImage);
1100 rc = VERR_VDI_NOT_OPENED;
1101 goto out;
1102 }
1103
1104 /* Check params. */
1105 if (uOffset + cbWrite > pDisk->cbSize || cbWrite == 0)
1106 {
1107 AssertMsgFailed(("uOffset=%llu cbWrite=%u\n", uOffset, cbWrite));
1108 rc = VERR_INVALID_PARAMETER;
1109 goto out;
1110 }
1111
1112 vdSetModifiedFlag(pDisk);
1113
1114 /* Loop until all written. */
1115 do
1116 {
1117 /* Try to write the possibly partial block to the last opened image.
1118 * This works when the block is already allocated in this image or
1119 * if it is a full-block write, which automatically allocates a new
1120 * block if needed. */
1121 cbThisWrite = cbWrite;
1122 rc = pDisk->Backend->pfnWrite(pImage->pvBackendData, uOffset, pvBuf,
1123 cbThisWrite, &cbThisWrite,
1124 &cbPreRead, &cbPostRead);
1125 if (rc == VINF_VDI_BLOCK_FREE)
1126 {
1127 void *pvTmp = RTMemTmpAlloc(cbPreRead + cbThisWrite + cbPostRead);
1128 if (!pvBuf)
1129 {
1130 Assert(!pvBuf);
1131 rc = VERR_NO_MEMORY;
1132 break;
1133 }
1134
1135 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME))
1136 {
1137 /* Optimized write, suppress writing to a so far unallocated
1138 * block when the data is identical than as of the parent. */
1139 rc = vdWriteHelperOptimized(pDisk, pImage, uOffset,
1140 cbWrite, cbThisWrite,
1141 cbPreRead, cbPostRead,
1142 pvBuf, pvTmp);
1143 }
1144 else
1145 {
1146 /* Normal write, not optimized in any way. The block will be
1147 * written no matter what. This will usually (unless the
1148 * backend has some further optimization enabled) cause the
1149 * block to be allocated. */
1150 rc = vdWriteHelperStandard(pDisk, pImage, uOffset,
1151 cbWrite, cbThisWrite,
1152 cbPreRead, cbPostRead,
1153 pvBuf, pvTmp);
1154 }
1155 RTMemTmpFree(pvTmp);
1156 if (VBOX_FAILURE(rc))
1157 break;
1158 }
1159
1160 cbWrite -= cbThisWrite;
1161 uOffset += cbThisWrite;
1162 pvBuf = (char *)pvBuf + cbThisWrite;
1163 } while (cbWrite != 0 && VBOX_SUCCESS(rc));
1164
1165out:
1166 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1167 return rc;
1168}
1169
1170/**
1171 * Make sure the on disk representation of a virtual HDD is up to date.
1172 *
1173 * @returns VBox status code.
1174 * @param pDisk Pointer to VBox HDD container.
1175 */
1176VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
1177{
1178 /* sanity check */
1179 Assert(pDisk);
1180 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1181
1182 int rc = VINF_SUCCESS;
1183 PVDIMAGE pImage = pDisk->pLast;
1184 if (RT_UNLIKELY(!pImage))
1185 {
1186 Assert(pImage);
1187 rc = VERR_VDI_NOT_OPENED;
1188 }
1189 else
1190 {
1191 vdResetModifiedFlag(pDisk);
1192 rc = pDisk->Backend->pfnFlush(pImage->pvBackendData);
1193 }
1194
1195 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1196 return rc;
1197}
1198
1199/**
1200 * Get number of opened images in HDD container.
1201 *
1202 * @returns Number of opened images for HDD container. 0 if no images have been opened.
1203 * @param pDisk Pointer to VBox HDD container.
1204 */
1205VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
1206{
1207 /* sanity check */
1208 Assert(pDisk);
1209 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1210
1211 unsigned c = pDisk->cImages;
1212 LogFlow(("%s: returns %d\n", __FUNCTION__, c));
1213 return c;
1214}
1215
1216/**
1217 * Get read/write mode of the VBox HDD container.
1218 *
1219 * @returns Virtual disk ReadOnly status.
1220 * @returns true if no image is opened in HDD container.
1221 * @param pDisk Pointer to VBox HDD container.
1222 */
1223VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
1224{
1225 /* sanity check */
1226 Assert(pDisk);
1227 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1228
1229 bool f;
1230 if (pDisk->pLast)
1231 {
1232 unsigned uOpenFlags;
1233 uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1234 f = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
1235 }
1236 else
1237 {
1238 AssertMsgFailed(("No disk image is opened!\n"));
1239 f = true;
1240 }
1241
1242 LogFlow(("%s: returns %d\n", __FUNCTION__, f));
1243 return f;
1244}
1245
1246/**
1247 * Get total disk size of the VBox HDD container.
1248 *
1249 * @returns Virtual disk size in bytes.
1250 * @returns 0 if no image is opened in HDD container.
1251 * @param pDisk Pointer to VBox HDD container.
1252 */
1253VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk)
1254{
1255 /* sanity check */
1256 Assert(pDisk);
1257 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1258
1259 uint64_t cb = pDisk->cbSize;
1260 LogFlow(("%s: returns %lld\n", __FUNCTION__, cb));
1261 return cb;
1262}
1263
1264/**
1265 * Get virtual disk geometry stored in HDD container.
1266 *
1267 * @returns VBox status code.
1268 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1269 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
1270 * @param pDisk Pointer to VBox HDD container.
1271 * @param pcCylinders Where to store the number of cylinders. NULL is ok.
1272 * @param pcHeads Where to store the number of heads. NULL is ok.
1273 * @param pcSectors Where to store the number of sectors. NULL is ok.
1274 */
1275VBOXDDU_DECL(int) VDGetGeometry(PVBOXHDD pDisk, unsigned *pcCylinders,
1276 unsigned *pcHeads, unsigned *pcSectors)
1277{
1278 /* sanity check */
1279 Assert(pDisk);
1280 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1281
1282 int rc = VINF_SUCCESS;
1283 PVDIMAGE pImage = pDisk->pBase;
1284 if (RT_UNLIKELY(!pImage))
1285 {
1286 Assert(pImage);
1287 rc = VERR_VDI_NOT_OPENED;
1288 }
1289 else
1290 {
1291 if (pDisk->cCylinders != 0)
1292 {
1293 if (pcCylinders)
1294 *pcCylinders = pDisk->cCylinders;
1295 if (pcHeads)
1296 *pcHeads = pDisk->cHeads;
1297 if (pcSectors)
1298 *pcSectors = pDisk->cSectors;
1299 }
1300 else
1301 rc = VERR_VDI_GEOMETRY_NOT_SET;
1302 }
1303 LogFlow(("%s: %Vrc (CHS=%u/%u/%u)\n", __FUNCTION__, rc,
1304 pDisk->cCylinders, pDisk->cHeads, pDisk->cSectors));
1305 return rc;
1306}
1307
1308/**
1309 * Store virtual disk geometry in HDD container.
1310 *
1311 * Note that in case of unrecoverable error all images in HDD container will be closed.
1312 *
1313 * @returns VBox status code.
1314 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1315 * @param pDisk Pointer to VBox HDD container.
1316 * @param cCylinders Number of cylinders.
1317 * @param cHeads Number of heads.
1318 * @param cSectors Number of sectors.
1319 */
1320VBOXDDU_DECL(int) VDSetGeometry(PVBOXHDD pDisk, unsigned cCylinders,
1321 unsigned cHeads, unsigned cSectors)
1322{
1323 /* sanity check */
1324 Assert(pDisk);
1325 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1326
1327 int rc = VINF_SUCCESS;
1328 PVDIMAGE pImage = pDisk->pBase;
1329 if (RT_UNLIKELY(!pImage))
1330 {
1331 Assert(pImage);
1332 rc = VERR_VDI_NOT_OPENED;
1333 }
1334 else
1335 {
1336 if ( cCylinders != pDisk->cCylinders
1337 || cHeads != pDisk->cHeads
1338 || cSectors != pDisk->cSectors)
1339 {
1340 /* Only update geometry if it is changed. Avoids similar checks
1341 * in every backend. Most of the time the new geometry is set to
1342 * the previous values, so no need to go through the hassle of
1343 * updating an image which could be opened in read-only mode right
1344 * now. */
1345 rc = pDisk->Backend->pfnSetGeometry(pImage->pvBackendData,
1346 cCylinders, cHeads, cSectors);
1347
1348 /* Cache new geometry values in any case, whether successful or not. */
1349 int rc2 = pDisk->Backend->pfnGetGeometry(pImage->pvBackendData,
1350 &pDisk->cCylinders,
1351 &pDisk->cHeads,
1352 &pDisk->cSectors);
1353 if (VBOX_FAILURE(rc2))
1354 {
1355 pDisk->cCylinders = 0;
1356 pDisk->cHeads = 0;
1357 pDisk->cSectors = 0;
1358 }
1359 else
1360 {
1361 /* Make sure the CHS geometry is properly clipped. */
1362 pDisk->cCylinders = RT_MIN(pDisk->cCylinders, 16383);
1363 pDisk->cHeads = RT_MIN(pDisk->cHeads, 255);
1364 pDisk->cSectors = RT_MIN(pDisk->cSectors, 255);
1365 }
1366 }
1367 }
1368
1369 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1370 return rc;
1371}
1372
1373/**
1374 * Get virtual disk translation mode stored in HDD container.
1375 *
1376 * @returns VBox status code.
1377 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1378 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
1379 * @param pDisk Pointer to VBox HDD container.
1380 * @param penmTranslation Where to store the translation mode (see pdm.h).
1381 */
1382VBOXDDU_DECL(int) VDGetTranslation(PVBOXHDD pDisk,
1383 PPDMBIOSTRANSLATION penmTranslation)
1384{
1385 /* sanity check */
1386 Assert(pDisk);
1387 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1388
1389 int rc = VINF_SUCCESS;
1390 PVDIMAGE pImage = pDisk->pBase;
1391 if (RT_UNLIKELY(!pImage))
1392 {
1393 Assert(pImage);
1394 rc = VERR_VDI_NOT_OPENED;
1395 }
1396 else
1397 {
1398 if (pDisk->enmTranslation != 0)
1399 *penmTranslation = pDisk->enmTranslation;
1400 else
1401 rc = VERR_VDI_GEOMETRY_NOT_SET;
1402 }
1403 LogFlow(("%s: %Vrc (translation=%u)\n", __FUNCTION__, rc,
1404 pDisk->enmTranslation));
1405 return rc;
1406}
1407
1408/**
1409 * Store virtual disk translation mode in HDD container.
1410 *
1411 * Note that in case of unrecoverable error all images in HDD container will be closed.
1412 *
1413 * @returns VBox status code.
1414 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1415 * @param pDisk Pointer to VBox HDD container.
1416 * @param enmTranslation Translation mode (see pdm.h).
1417 */
1418VBOXDDU_DECL(int) VDSetTranslation(PVBOXHDD pDisk,
1419 PDMBIOSTRANSLATION enmTranslation)
1420{
1421 /* sanity check */
1422 Assert(pDisk);
1423 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1424
1425 int rc = VINF_SUCCESS;
1426 PVDIMAGE pImage = pDisk->pBase;
1427 if (RT_UNLIKELY(!pImage))
1428 {
1429 Assert(pImage);
1430 rc = VERR_VDI_NOT_OPENED;
1431 }
1432 else
1433 {
1434 if (enmTranslation == 0)
1435 rc = VERR_INVALID_PARAMETER;
1436 else if (enmTranslation != pDisk->enmTranslation)
1437 {
1438 /* Only update translation mode if it is changed. Avoids similar
1439 * checks in every backend. Most of the time the new translation
1440 * mode is set to the previous value, so no need to go through the
1441 * hassle of updating an image which could be opened in read-only
1442 * mode right now. */
1443 rc = pDisk->Backend->pfnSetTranslation(pImage->pvBackendData,
1444 enmTranslation);
1445
1446 /* Cache new translation mode in any case, whether successful or not. */
1447 int rc2 = pDisk->Backend->pfnGetTranslation(pImage->pvBackendData,
1448 &pDisk->enmTranslation);
1449 if (VBOX_FAILURE(rc2))
1450 pDisk->enmTranslation = (PDMBIOSTRANSLATION)0;
1451 }
1452 }
1453
1454 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1455 return rc;
1456}
1457
1458/**
1459 * Get version of image in HDD container.
1460 *
1461 * @returns VBox status code.
1462 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1463 * @param pDisk Pointer to VBox HDD container.
1464 * @param nImage Image number, counts from 0. 0 is always base image of container.
1465 * @param puVersion Where to store the image version.
1466 */
1467VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
1468 unsigned *puVersion)
1469{
1470 return VERR_NOT_IMPLEMENTED;
1471}
1472
1473/**
1474 * Get type of image in HDD container.
1475 *
1476 * @returns VBox status code.
1477 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1478 * @param pDisk Pointer to VBox HDD container.
1479 * @param nImage Image number, counts from 0. 0 is always base image of container.
1480 * @param penmType Where to store the image type.
1481 */
1482VBOXDDU_DECL(int) VDGetImageType(PVBOXHDD pDisk, unsigned nImage,
1483 PVDIMAGETYPE penmType)
1484{
1485 return VERR_NOT_IMPLEMENTED;
1486}
1487
1488/**
1489 * Get flags of image in HDD container.
1490 *
1491 * @returns VBox status code.
1492 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1493 * @param pDisk Pointer to VBox HDD container.
1494 * @param nImage Image number, counts from 0. 0 is always base image of container.
1495 * @param puImageFlags Where to store the image flags.
1496 */
1497VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
1498 unsigned *puImageFlags)
1499{
1500 return VERR_NOT_IMPLEMENTED;
1501}
1502
1503/**
1504 * Get open flags of last opened image in HDD container.
1505 *
1506 * @returns VBox status code.
1507 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1508 * @param pDisk Pointer to VBox HDD container.
1509 * @param puOpenFlags Where to store the image open flags.
1510 */
1511VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned *puOpenFlags)
1512{
1513 /* sanity check */
1514 Assert(pDisk);
1515 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1516
1517 unsigned uOpenFlags = 0;
1518 int rc = VINF_SUCCESS;
1519 PVDIMAGE pImage = pDisk->pLast;
1520 if (RT_UNLIKELY(!pImage))
1521 {
1522 Assert(pImage);
1523 rc = VERR_VDI_NOT_OPENED;
1524 }
1525 else
1526 {
1527 uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1528 *puOpenFlags = uOpenFlags;
1529 }
1530 LogFlow(("%s: returns %Vrc uOpenFlags=%#x\n", __FUNCTION__, rc, uOpenFlags));
1531 return uOpenFlags;
1532}
1533
1534/**
1535 * Set open flags of last opened image in HDD container.
1536 * This operation may cause file locking changes and/or files being reopened.
1537 * Note that in case of unrecoverable error all images in HDD container will be closed.
1538 *
1539 * @returns Virtual disk block size in bytes.
1540 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1541 * @returns VBox status code.
1542 * @param pDisk Pointer to VBox HDD container.
1543 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1544 */
1545VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned uOpenFlags)
1546{
1547 /* sanity check */
1548 Assert(pDisk);
1549 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1550
1551 int rc = VINF_SUCCESS;
1552 PVDIMAGE pImage = pDisk->pLast;
1553 if (RT_UNLIKELY(!pImage))
1554 {
1555 Assert(pImage);
1556 rc = VERR_VDI_NOT_OPENED;
1557 }
1558 else
1559 rc = pDisk->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData,
1560 uOpenFlags);
1561 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1562 return rc;
1563}
1564
1565/**
1566 * Get base filename of image in HDD container. Some image formats use
1567 * other filenames as well, so don't use this for anything but for informational
1568 * purposes.
1569 *
1570 * @returns VBox status code.
1571 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1572 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
1573 * @param pDisk Pointer to VBox HDD container.
1574 * @param nImage Image number, counts from 0. 0 is always base image of container.
1575 * @param pszFilename Where to store the image file name.
1576 * @param cbFilename Size of buffer pszFilename points to.
1577 */
1578VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
1579 char *pszFilename, unsigned cbFilename)
1580{
1581 return VERR_NOT_IMPLEMENTED;
1582}
1583
1584/**
1585 * Get the comment line of image in HDD container.
1586 *
1587 * @returns VBox status code.
1588 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1589 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
1590 * @param pDisk Pointer to VBox HDD container.
1591 * @param nImage Image number, counts from 0. 0 is always base image of container.
1592 * @param pszComment Where to store the comment string of image. NULL is ok.
1593 * @param cbComment The size of pszComment buffer. 0 is ok.
1594 */
1595VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
1596 char *pszComment, unsigned cbComment)
1597{
1598 /* sanity check */
1599 Assert(pDisk);
1600 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1601 Assert(pszComment);
1602
1603 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1604 int rc;
1605 if (pImage)
1606 rc = pDisk->Backend->pfnGetComment(pImage->pvBackendData, pszComment,
1607 cbComment);
1608 else
1609 rc = VERR_VDI_IMAGE_NOT_FOUND;
1610
1611 LogFlow(("%s: returns %Vrc, comment='%s' nImage=%u\n", __FUNCTION__,
1612 rc, pszComment, nImage));
1613 return rc;
1614}
1615
1616/**
1617 * Changes the comment line of image in HDD container.
1618 *
1619 * @returns VBox status code.
1620 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1621 * @param pDisk Pointer to VBox HDD container.
1622 * @param nImage Image number, counts from 0. 0 is always base image of container.
1623 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
1624 */
1625VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
1626 const char *pszComment)
1627{
1628 /* sanity check */
1629 Assert(pDisk);
1630 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1631 LogFlow(("%s: comment='%s' nImage=%u\n", __FUNCTION__, pszComment, nImage));
1632
1633 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1634 int rc;
1635 if (pImage)
1636 rc = pDisk->Backend->pfnSetComment(pImage->pvBackendData, pszComment);
1637 else
1638 rc = VERR_VDI_IMAGE_NOT_FOUND;
1639
1640 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1641 return rc;
1642}
1643
1644
1645/**
1646 * Get UUID of image in HDD container.
1647 *
1648 * @returns VBox status code.
1649 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1650 * @param pDisk Pointer to VBox HDD container.
1651 * @param nImage Image number, counts from 0. 0 is always base image of container.
1652 * @param pUuid Where to store the image creation UUID.
1653 */
1654VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
1655{
1656 /* sanity check */
1657 Assert(pDisk);
1658 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1659 Assert(pUuid);
1660
1661 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1662 int rc;
1663 if (pImage)
1664 rc = pDisk->Backend->pfnGetUuid(pImage->pvBackendData, pUuid);
1665 else
1666 rc = VERR_VDI_IMAGE_NOT_FOUND;
1667
1668 LogFlow(("%s: returns %Vrc, uuid={%Vuuid} nImage=%u\n", __FUNCTION__,
1669 rc, pUuid, nImage));
1670 return rc;
1671}
1672
1673/**
1674 * Set the image's UUID. Should not be used by normal applications.
1675 *
1676 * @returns VBox status code.
1677 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1678 * @param pDisk Pointer to VBox HDD container.
1679 * @param nImage Image number, counts from 0. 0 is always base image of container.
1680 * @param pUuid Optional parameter, new UUID of the image.
1681 */
1682VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
1683{
1684 LogFlow(("%s: uuid={%Vuuid} nImage=%u\n", __FUNCTION__, pUuid, nImage));
1685 /* sanity check */
1686 Assert(pDisk);
1687 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1688 Assert(pUuid);
1689
1690 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1691 int rc;
1692 if (pImage)
1693 rc = pDisk->Backend->pfnSetUuid(pImage->pvBackendData, pUuid);
1694 else
1695 rc = VERR_VDI_IMAGE_NOT_FOUND;
1696
1697 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1698 return rc;
1699}
1700
1701/**
1702 * Get last modification UUID of image in HDD container.
1703 *
1704 * @returns VBox status code.
1705 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1706 * @param pDisk Pointer to VBox HDD container.
1707 * @param nImage Image number, counts from 0. 0 is always base image of container.
1708 * @param pUuid Where to store the image modification UUID.
1709 */
1710VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
1711{
1712 /* sanity check */
1713 Assert(pDisk);
1714 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1715 Assert(pUuid);
1716
1717 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1718 int rc;
1719 if (pImage)
1720 rc = pDisk->Backend->pfnGetModificationUuid(pImage->pvBackendData, pUuid);
1721 else
1722 rc = VERR_VDI_IMAGE_NOT_FOUND;
1723
1724 LogFlow(("%s: returns %Vrc, uuid={%Vuuid} nImage=%u\n", __FUNCTION__,
1725 rc, pUuid, nImage));
1726 return rc;
1727}
1728
1729/**
1730 * Set the image's last modification UUID. Should not be used by normal applications.
1731 *
1732 * @returns VBox status code.
1733 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1734 * @param pDisk Pointer to VBox HDD container.
1735 * @param nImage Image number, counts from 0. 0 is always base image of container.
1736 * @param pUuid Optional parameter, new last modification UUID of the image.
1737 */
1738VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
1739{
1740 LogFlow(("%s: uuid={%Vuuid} nImage=%u\n", __FUNCTION__, pUuid, nImage));
1741 /* sanity check */
1742 Assert(pDisk);
1743 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1744 Assert(pUuid);
1745
1746 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1747 int rc;
1748 if (pImage)
1749 rc = pDisk->Backend->pfnSetModificationUuid(pImage->pvBackendData, pUuid);
1750 else
1751 rc = VERR_VDI_IMAGE_NOT_FOUND;
1752
1753 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1754 return rc;
1755}
1756
1757/**
1758 * Get parent UUID of image in HDD container.
1759 *
1760 * @returns VBox status code.
1761 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1762 * @param pDisk Pointer to VBox HDD container.
1763 * @param nImage Image number, counts from 0. 0 is always base image of container.
1764 * @param pUuid Where to store the parent image UUID.
1765 */
1766VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
1767 PRTUUID pUuid)
1768{
1769 /* sanity check */
1770 Assert(pDisk);
1771 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1772 Assert(pUuid);
1773
1774 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1775 int rc;
1776 if (pImage)
1777 rc = pDisk->Backend->pfnGetParentUuid(pImage->pvBackendData, pUuid);
1778 else
1779 rc = VERR_VDI_IMAGE_NOT_FOUND;
1780
1781 LogFlow(("%s: returns %Vrc, uuid={%Vuuid} nImage=%u\n", __FUNCTION__,
1782 rc, pUuid, nImage));
1783 return rc;
1784}
1785
1786/**
1787 * Set the image's parent UUID. Should not be used by normal applications.
1788 *
1789 * @returns VBox status code.
1790 * @param pDisk Pointer to VBox HDD container.
1791 * @param nImage Image number, counts from 0. 0 is always base image of container.
1792 * @param pUuid Optional parameter, new parent UUID of the image.
1793 */
1794VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
1795 PCRTUUID pUuid)
1796{
1797 LogFlow(("%s: uuid={%Vuuid} nImage=%u\n", __FUNCTION__, pUuid, nImage));
1798 /* sanity check */
1799 Assert(pDisk);
1800 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1801 Assert(pUuid);
1802
1803 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1804 int rc;
1805 if (pImage)
1806 rc = pDisk->Backend->pfnSetParentUuid(pImage->pvBackendData, pUuid);
1807 else
1808 rc = VERR_VDI_IMAGE_NOT_FOUND;
1809
1810 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1811 return rc;
1812}
1813
1814
1815/**
1816 * Debug helper - dumps all opened images in HDD container into the log file.
1817 *
1818 * @param pDisk Pointer to VDI HDD container.
1819 */
1820VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
1821{
1822 /* sanity check */
1823 Assert(pDisk);
1824 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1825
1826 RTLogPrintf("--- Dumping VDI Disk, Images=%u\n", pDisk->cImages);
1827 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
1828 {
1829 RTLogPrintf("Dumping VDI image \"%s\" file=%#p\n", pImage->pszFilename);
1830 /** @todo call backend to print its part. */
1831 }
1832}
1833
Note: See TracBrowser for help on using the repository browser.

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