VirtualBox

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

Last change on this file since 4849 was 4798, checked in by vboxsync, 17 years ago

fixed annoying debug assertion.

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