VirtualBox

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

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

Add StorageCraft Backup images as HDD format

File size: 65.3 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 pluginHandle;
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 ldrHandle;
497 char *pszPluginName;
498 size_t 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, &ldrHandle);
512 if (VBOX_SUCCESS(rc))
513 {
514 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad;
515
516 rc = RTLdrGetSymbol (ldrHandle, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad);
517 if (VBOX_FAILURE(rc) || !pfnHDDFormatLoad)
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 *ppDisk = pDisk;
564 }
565 else
566 rc = VERR_NO_MEMORY;
567 }
568 else
569 rc = vdEarlyError(pfnError, pvErrorUser, VERR_INVALID_PARAMETER,
570 RT_SRC_POS, "VD: unknown backend name '%s'",
571 pszBackend);
572
573 LogFlow(("%s: returns %Vrc (pDisk=%#p)\n", __FUNCTION__, rc, pDisk));
574 return rc;
575}
576
577/**
578 * Destroys the VBox HDD container.
579 * If container has opened image files they will be closed.
580 *
581 * @param pDisk Pointer to VBox HDD container.
582 */
583VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk)
584{
585 LogFlow(("%s: pDisk=%#p\n", pDisk));
586 /* sanity check */
587 Assert(pDisk);
588 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
589
590 if (pDisk)
591 {
592 VDCloseAll(pDisk);
593 RTLdrClose(pDisk->pluginHandle);
594 RTMemFree(pDisk->Backend);
595 RTMemFree(pDisk);
596 }
597}
598
599/**
600 * Opens an image file.
601 *
602 * The first opened image file in a HDD container must have a base image type,
603 * others (next opened images) must be a differencing or undo images.
604 * Linkage is checked for differencing image to be in consistence with the previously opened image.
605 * When another differencing image is opened and the last image was opened in read/write access
606 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
607 * other processes to use images in read-only mode too.
608 *
609 * Note that the image can be opened in read-only mode if a read/write open is not possible.
610 * Use VDIsReadOnly to check open mode.
611 *
612 * @returns VBox status code.
613 * @param pDisk Pointer to VBox HDD container.
614 * @param pszFilename Name of the image file to open.
615 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
616 */
617VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszFilename,
618 unsigned uOpenFlags)
619{
620 int rc = VINF_SUCCESS;
621 LogFlow(("%s: pszFilename=\"%s\" uOpenFlags=%#x\n", __FUNCTION__,
622 pszFilename, uOpenFlags));
623 /* sanity check */
624 Assert(pDisk);
625 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
626
627 /* Check arguments. */
628 if ( !pszFilename
629 || *pszFilename == '\0'
630 || (uOpenFlags & ~VD_OPEN_FLAGS_MASK))
631 {
632 AssertMsgFailed(("Invalid arguments: pszFilename=%#p uOpenFlags=%#x\n", pszFilename, uOpenFlags));
633 return VERR_INVALID_PARAMETER;
634 }
635
636 /* Force readonly for images without base/diff consistency checking. */
637 if (uOpenFlags & VD_OPEN_FLAGS_INFO)
638 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
639
640 /* Set up image descriptor. */
641 PVDIMAGE pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
642 if (!pImage)
643 return VERR_NO_MEMORY;
644 pImage->pszFilename = RTStrDup(pszFilename);
645 if (!pImage->pszFilename)
646 rc = VERR_NO_MEMORY;
647
648 if (VBOX_SUCCESS(rc))
649 {
650 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
651 rc = pDisk->Backend->pfnOpen(pImage->pszFilename,
652 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
653 pDisk->pfnError, pDisk->pvErrorUser,
654 &pImage->pvBackendData);
655 }
656 /* If the open in read-write mode failed, retry in read-only mode. */
657 if (VBOX_FAILURE(rc))
658 {
659 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
660 && (rc == VERR_ACCESS_DENIED
661 || rc == VERR_PERMISSION_DENIED
662 || rc == VERR_WRITE_PROTECT
663 || rc == VERR_SHARING_VIOLATION
664 || rc == VERR_FILE_LOCK_FAILED))
665 rc = pDisk->Backend->pfnOpen(pImage->pszFilename,
666 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
667 | VD_OPEN_FLAGS_READONLY,
668 pDisk->pfnError, pDisk->pvErrorUser,
669 &pImage->pvBackendData);
670 if (VBOX_FAILURE(rc))
671 rc = vdError(pDisk, rc, RT_SRC_POS,
672 N_("VD: error opening image file '%s'"), pszFilename);
673 }
674
675 if (VBOX_SUCCESS(rc))
676 {
677 VDIMAGETYPE enmImageType;
678 rc = pDisk->Backend->pfnGetImageType(pImage->pvBackendData,
679 &enmImageType);
680 /* Check image type. As the image itself has no idea whether it's a
681 * base image or not, this info is derived here. Image 0 can be fixed
682 * or normal, all others must be normal images. */
683 if ( VBOX_SUCCESS(rc)
684 && !(uOpenFlags & VD_OPEN_FLAGS_INFO)
685 && pDisk->cImages != 0
686 && enmImageType != VD_IMAGE_TYPE_NORMAL)
687 rc = VERR_VDI_INVALID_TYPE;
688
689 /** @todo optionally check UUIDs */
690
691 if (VBOX_SUCCESS(rc))
692 {
693 uint64_t cbSize = pDisk->Backend->pfnGetSize(pImage->pvBackendData);
694 if (pDisk->cImages == 0)
695 {
696 /* Cache disk information. */
697 pDisk->cbSize = cbSize;
698
699 /* Cache CHS geometry. */
700 int rc2 = pDisk->Backend->pfnGetGeometry(pImage->pvBackendData,
701 &pDisk->cCylinders,
702 &pDisk->cHeads,
703 &pDisk->cSectors);
704 if (VBOX_FAILURE(rc2))
705 {
706 pDisk->cCylinders = 0;
707 pDisk->cHeads = 0;
708 pDisk->cSectors = 0;
709 }
710 else
711 {
712 /* Make sure the CHS geometry is properly clipped. */
713 pDisk->cCylinders = RT_MIN(pDisk->cCylinders, 16383);
714 pDisk->cHeads = RT_MIN(pDisk->cHeads, 255);
715 pDisk->cSectors = RT_MIN(pDisk->cSectors, 255);
716 }
717
718 /* Cache translation mode. */
719 rc2 = pDisk->Backend->pfnGetTranslation(pImage->pvBackendData,
720 &pDisk->enmTranslation);
721 if (VBOX_FAILURE(rc2))
722 pDisk->enmTranslation = (PDMBIOSTRANSLATION)0;
723 }
724 else
725 {
726 /* Check image size/block size for consistency. */
727 if (cbSize != pDisk->cbSize)
728 rc = VERR_VDI_INVALID_TYPE;
729 }
730 }
731
732 if (VBOX_SUCCESS(rc) && pDisk->cImages != 0)
733 {
734 /* Switch previous image to read-only mode. */
735 unsigned uOpenFlagsPrevImg;
736 uOpenFlagsPrevImg = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
737 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
738 {
739 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
740 rc = pDisk->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
741 }
742 }
743
744 if (VBOX_SUCCESS(rc))
745 {
746 /* Image successfully opened, make it the last image. */
747 vdAddImageToList(pDisk, pImage);
748 }
749 else
750 {
751 /* Error detected, but image opened. Close image. */
752 int rc2;
753 rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, false);
754 AssertRC(rc2);
755 pImage->pvBackendData = NULL;
756 }
757 }
758
759 if (VBOX_FAILURE(rc))
760 {
761 if (pImage)
762 {
763 if (pImage->pszFilename)
764 RTStrFree(pImage->pszFilename);
765 RTMemFree(pImage);
766 }
767 }
768
769 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
770 return rc;
771}
772
773/**
774 * Creates and opens a new base image file.
775 *
776 * @returns VBox status code.
777 * @param pDisk Pointer to VBox HDD container.
778 * @param pszFilename Name of the image file to create.
779 * @param enmType Image type, only base image types are acceptable.
780 * @param cbSize Image size in bytes.
781 * @param uImageFlags Flags specifying special image features.
782 * @param pszComment Pointer to image comment. NULL is ok.
783 * @param cCylinders Number of cylinders (must be <= 16383).
784 * @param cHeads Number of heads (must be <= 16).
785 * @param cSectors Number of sectors (must be <= 63);
786 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
787 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
788 * @param pvUser User argument for the progress callback.
789 */
790VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszFilename,
791 VDIMAGETYPE enmType, uint64_t cbSize,
792 unsigned uImageFlags, const char *pszComment,
793 unsigned cCylinders, unsigned cHeads,
794 unsigned cSectors, unsigned uOpenFlags,
795 PFNVMPROGRESS pfnProgress, void *pvUser)
796{
797 int rc = VINF_SUCCESS;
798 LogFlow(("%s: pszFilename=\"%s\" uOpenFlags=%#x\n", __FUNCTION__,
799 pszFilename, uOpenFlags));
800 /* sanity check */
801 Assert(pDisk);
802 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
803
804 /* Check arguments. */
805 if ( !pszFilename
806 || *pszFilename == '\0'
807 || (enmType != VD_IMAGE_TYPE_NORMAL && enmType != VD_IMAGE_TYPE_FIXED)
808 || !cbSize
809 || (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
810 || cCylinders == 0
811 || cCylinders > 16383
812 || cHeads == 0
813 || cHeads > 16
814 || cSectors == 0
815 || cSectors > 63)
816 {
817 AssertMsgFailed(("Invalid arguments: pszFilename=%#p uOpenFlags=%#x\n", pszFilename, uOpenFlags));
818 return VERR_INVALID_PARAMETER;
819 }
820
821 /* Check state. */
822 if (pDisk->cImages != 0)
823 {
824 AssertMsgFailed(("Create base image cannot be done with other images open\n"));
825 return VERR_VDI_INVALID_STATE;
826 }
827
828 /* Set up image descriptor. */
829 PVDIMAGE pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
830 if (!pImage)
831 return VERR_NO_MEMORY;
832 pImage->pszFilename = RTStrDup(pszFilename);
833 if (!pImage->pszFilename)
834 rc = VERR_NO_MEMORY;
835
836 if (VBOX_SUCCESS(rc))
837 rc = pDisk->Backend->pfnCreate(pImage->pszFilename, enmType, cbSize,
838 uImageFlags, pszComment, cCylinders,
839 cHeads, cSectors, uOpenFlags,
840 pfnProgress, pvUser,
841 pDisk->pfnError, pDisk->pvErrorUser,
842 &pImage->pvBackendData);
843
844 if (VBOX_SUCCESS(rc))
845 {
846 /** @todo optionally check UUIDs */
847
848 if (VBOX_SUCCESS(rc))
849 {
850 uint64_t cbSize = pDisk->Backend->pfnGetSize(pImage->pvBackendData);
851 if (pDisk->cImages == 0)
852 {
853 /* Cache disk information. */
854 pDisk->cbSize = cbSize;
855
856 /* Cache CHS geometry. */
857 int rc2 = pDisk->Backend->pfnGetGeometry(pImage->pvBackendData,
858 &pDisk->cCylinders,
859 &pDisk->cHeads,
860 &pDisk->cSectors);
861 if (VBOX_FAILURE(rc2))
862 {
863 pDisk->cCylinders = 0;
864 pDisk->cHeads = 0;
865 pDisk->cSectors = 0;
866 }
867 else
868 {
869 /* Make sure the CHS geometry is properly clipped. */
870 pDisk->cCylinders = RT_MIN(pDisk->cCylinders, 16383);
871 pDisk->cHeads = RT_MIN(pDisk->cHeads, 255);
872 pDisk->cSectors = RT_MIN(pDisk->cSectors, 255);
873 }
874
875 /* Cache translation mode. */
876 rc2 = pDisk->Backend->pfnGetTranslation(pImage->pvBackendData,
877 &pDisk->enmTranslation);
878 if (VBOX_FAILURE(rc2))
879 pDisk->enmTranslation = (PDMBIOSTRANSLATION)0;
880 }
881 else
882 {
883 /* Check image size/block size for consistency. */
884 if (cbSize != pDisk->cbSize)
885 rc = VERR_VDI_INVALID_TYPE;
886 }
887 }
888
889 if (VBOX_SUCCESS(rc))
890 {
891 /* Image successfully opened, make it the last image. */
892 vdAddImageToList(pDisk, pImage);
893 }
894 else
895 {
896 /* Error detected, but image opened. Close and delete image. */
897 int rc2;
898 rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, true);
899 AssertRC(rc2);
900 pImage->pvBackendData = NULL;
901 }
902 }
903
904 if (VBOX_FAILURE(rc))
905 {
906 if (pImage)
907 {
908 if (pImage->pszFilename)
909 RTStrFree(pImage->pszFilename);
910 RTMemFree(pImage);
911 }
912 }
913
914 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
915 return rc;
916}
917
918/**
919 * Creates and opens a new differencing image file in HDD container.
920 * See comments for VDOpen function about differencing images.
921 *
922 * @returns VBox status code.
923 * @param pDisk Pointer to VBox HDD container.
924 * @param pszFilename Name of the differencing image file to create.
925 * @param uImageFlags Flags specifying special image features.
926 * @param pszComment Pointer to image comment. NULL is ok.
927 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
928 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
929 * @param pvUser User argument for the progress callback.
930 */
931VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszFilename,
932 unsigned uImageFlags, const char *pszComment,
933 unsigned uOpenFlags,
934 PFNVMPROGRESS pfnProgress, void *pvUser)
935{
936 return VERR_NOT_IMPLEMENTED;
937}
938
939/**
940 * Merges two images having a parent/child relationship (both directions).
941 * As a side effect the source image is deleted from both the disk and
942 * the images in the VBox HDD container.
943 *
944 * @returns VBox status code.
945 * @param pDisk Pointer to VBox HDD container.
946 * @param nImageFrom Name of the image file to merge from.
947 * @param nImageTo Name of the image file to merge to.
948 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
949 * @param pvUser User argument for the progress callback.
950 */
951VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
952 unsigned nImageTo, PFNVMPROGRESS pfnProgress,
953 void *pvUser)
954{
955 return VERR_NOT_IMPLEMENTED;
956}
957
958/**
959 * Copies an image from one VBox HDD container to another.
960 * The copy is opened in the target VBox HDD container.
961 * It is possible to convert between different image formats, because the
962 * backend for the destination VBox HDD container may be different from the
963 * source container.
964 * If both the source and destination reference the same VBox HDD container,
965 * then the image is moved (by copying/deleting) to the new location.
966 * The source container is unchanged if the move operation fails, otherwise
967 * the image at the new location is opened in the same way as the old one was.
968 *
969 * @returns VBox status code.
970 * @param pDiskFrom Pointer to source VBox HDD container.
971 * @param nImage Image number, counts from 0. 0 is always base image of container.
972 * @param pDiskTo Pointer to destination VBox HDD container.
973 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
974 * @param pvUser User argument for the progress callback.
975 */
976VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
977 PFNVMPROGRESS pfnProgress, void *pvUser)
978{
979 return VERR_NOT_IMPLEMENTED;
980}
981
982/**
983 * Compacts a growing image file by removing zeroed data blocks.
984 * Optionally defragments data in the image so that ascending sector numbers
985 * are stored in ascending location in the image file.
986 *
987 * @todo maybe include this function in VDCopy.
988 *
989 * @returns VBox status code.
990 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
991 * @param pDisk Pointer to VBox HDD container.
992 * @param nImage Image number, counts from 0. 0 is always base image of container.
993 * @param fDefragment If true, reorder file data so that sectors are stored in ascending order.
994 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
995 * @param pvUser User argument for the progress callback.
996 */
997VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
998 bool fDefragment,
999 PFNVMPROGRESS pfnProgress, void *pvUser)
1000{
1001 return VERR_NOT_IMPLEMENTED;
1002}
1003
1004/**
1005 * Resizes an image. Allows setting the disk size to both larger and smaller
1006 * values than the current disk size.
1007 *
1008 * @returns VBox status code.
1009 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1010 * @param pDisk Pointer to VBox HDD container.
1011 * @param nImage Image number, counts from 0. 0 is always base image of container.
1012 * @param cbSize New image size in bytes.
1013 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1014 * @param pvUser User argument for the progress callback.
1015 */
1016VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, unsigned nImage, uint64_t cbSize,
1017 PFNVMPROGRESS pfnProgress, void *pvUser)
1018{
1019 return VERR_NOT_IMPLEMENTED;
1020}
1021
1022/**
1023 * Closes the last opened image file in the HDD container. Leaves all changes inside it.
1024 * If previous image file was opened in read-only mode (that is normal) and closing image
1025 * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
1026 * will be reopened in read/write mode.
1027 *
1028 * @param pDisk Pointer to VBox HDD container.
1029 * @param fDelete If true, delete the image from the host disk.
1030 */
1031VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
1032{
1033 LogFlow(("%s: fDelete=%d\n", __FUNCTION__, fDelete));
1034 /* sanity check */
1035 Assert(pDisk);
1036 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1037
1038 PVDIMAGE pImage = pDisk->pLast;
1039 unsigned uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pImage->pvBackendData);
1040 /* Remove image from list of opened images. */
1041 vdRemoveImageFromList(pDisk, pImage);
1042 /* Close (and optionally delete) image. */
1043 int rc = pDisk->Backend->pfnClose(pImage->pvBackendData, fDelete);
1044 /* Free remaining resources related to the image. */
1045 RTStrFree(pImage->pszFilename);
1046 RTMemFree(pImage);
1047
1048 /* If disk was previously in read/write mode, make sure it will stay like
1049 * this after closing this image. Set the open flags accordingly. */
1050 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1051 {
1052 uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1053 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
1054 rc = pDisk->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData,
1055 uOpenFlags);
1056 }
1057
1058 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1059 return rc;
1060}
1061
1062/**
1063 * Closes all opened image files in HDD container.
1064 *
1065 * @param pDisk Pointer to VDI HDD container.
1066 */
1067VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
1068{
1069 LogFlow(("%s:\n", __FUNCTION__));
1070 /* sanity check */
1071 Assert(pDisk);
1072 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1073
1074 PVDIMAGE pImage = pDisk->pLast;
1075 int rc = VINF_SUCCESS;
1076 while (pImage)
1077 {
1078 PVDIMAGE pPrev = pImage->pPrev;
1079 /* Remove image from list of opened images. */
1080 vdRemoveImageFromList(pDisk, pImage);
1081 /* Close image. */
1082 int rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, false);
1083 if (VBOX_FAILURE(rc2) && VBOX_SUCCESS(rc))
1084 rc = rc2;
1085 /* Free remaining resources related to the image. */
1086 RTStrFree(pImage->pszFilename);
1087 RTMemFree(pImage);
1088 pImage = pPrev;
1089 }
1090 Assert(pDisk->pLast == NULL);
1091
1092 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1093 return rc;
1094}
1095
1096/**
1097 * Read data from virtual HDD.
1098 *
1099 * @returns VBox status code.
1100 * @param pDisk Pointer to VBox HDD container.
1101 * @param uOffset Offset of first reading byte from start of disk.
1102 * @param pvBuf Pointer to buffer for reading data.
1103 * @param cbRead Number of bytes to read.
1104 */
1105VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf, size_t cbRead)
1106{
1107 /* sanity check */
1108 LogFlow(("%s: offset=%llu cbRead=%u\n", __FUNCTION__, uOffset, (unsigned)cbRead));
1109 Assert(pDisk);
1110 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1111
1112 int rc = VINF_SUCCESS;
1113 PVDIMAGE pImage = pDisk->pLast;
1114 if (RT_UNLIKELY(!pImage))
1115 {
1116 Assert(pImage);
1117 rc = VERR_VDI_NOT_OPENED;
1118 goto out;
1119 }
1120
1121 /* Check params. */
1122 if (uOffset + cbRead > pDisk->cbSize || cbRead == 0)
1123 {
1124 AssertMsgFailed(("uOffset=%llu cbRead=%u\n", uOffset, cbRead));
1125 return VERR_INVALID_PARAMETER;
1126 }
1127
1128 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead);
1129out:
1130 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1131 return rc;
1132}
1133
1134/**
1135 * Write data to virtual HDD.
1136 *
1137 * @returns VBox status code.
1138 * @param pDisk Pointer to VBox HDD container.
1139 * @param uOffset Offset of first reading byte from start of disk.
1140 * @param pvBuf Pointer to buffer for writing data.
1141 * @param cbWrite Number of bytes to write.
1142 */
1143VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf, size_t cbWrite)
1144{
1145 /* sanity check */
1146 Assert(pDisk);
1147 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1148
1149 int rc = VINF_SUCCESS;
1150 size_t cbThisWrite;
1151 size_t cbPreRead, cbPostRead;
1152 PVDIMAGE pImage = pDisk->pLast;
1153 if (RT_UNLIKELY(!pImage))
1154 {
1155 Assert(pImage);
1156 rc = VERR_VDI_NOT_OPENED;
1157 goto out;
1158 }
1159
1160 /* Check params. */
1161 if (uOffset + cbWrite > pDisk->cbSize || cbWrite == 0)
1162 {
1163 AssertMsgFailed(("uOffset=%llu cbWrite=%u\n", uOffset, cbWrite));
1164 rc = VERR_INVALID_PARAMETER;
1165 goto out;
1166 }
1167
1168 vdSetModifiedFlag(pDisk);
1169
1170 /* Loop until all written. */
1171 do
1172 {
1173 /* Try to write the possibly partial block to the last opened image.
1174 * This works when the block is already allocated in this image or
1175 * if it is a full-block write, which automatically allocates a new
1176 * block if needed. */
1177 cbThisWrite = cbWrite;
1178 rc = pDisk->Backend->pfnWrite(pImage->pvBackendData, uOffset, pvBuf,
1179 cbThisWrite, &cbThisWrite,
1180 &cbPreRead, &cbPostRead);
1181 if (rc == VINF_VDI_BLOCK_FREE)
1182 {
1183 void *pvTmp = RTMemTmpAlloc(cbPreRead + cbThisWrite + cbPostRead);
1184 if (!pvBuf)
1185 {
1186 Assert(!pvBuf);
1187 rc = VERR_NO_MEMORY;
1188 break;
1189 }
1190
1191 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME))
1192 {
1193 /* Optimized write, suppress writing to a so far unallocated
1194 * block when the data is identical than as of the parent. */
1195 rc = vdWriteHelperOptimized(pDisk, pImage, uOffset,
1196 cbWrite, cbThisWrite,
1197 cbPreRead, cbPostRead,
1198 pvBuf, pvTmp);
1199 }
1200 else
1201 {
1202 /* Normal write, not optimized in any way. The block will be
1203 * written no matter what. This will usually (unless the
1204 * backend has some further optimization enabled) cause the
1205 * block to be allocated. */
1206 rc = vdWriteHelperStandard(pDisk, pImage, uOffset,
1207 cbWrite, cbThisWrite,
1208 cbPreRead, cbPostRead,
1209 pvBuf, pvTmp);
1210 }
1211 RTMemTmpFree(pvTmp);
1212 if (VBOX_FAILURE(rc))
1213 break;
1214 }
1215
1216 cbWrite -= cbThisWrite;
1217 uOffset += cbThisWrite;
1218 pvBuf = (char *)pvBuf + cbThisWrite;
1219 } while (cbWrite != 0 && VBOX_SUCCESS(rc));
1220
1221out:
1222 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1223 return rc;
1224}
1225
1226/**
1227 * Make sure the on disk representation of a virtual HDD is up to date.
1228 *
1229 * @returns VBox status code.
1230 * @param pDisk Pointer to VBox HDD container.
1231 */
1232VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
1233{
1234 /* sanity check */
1235 Assert(pDisk);
1236 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1237
1238 int rc = VINF_SUCCESS;
1239 PVDIMAGE pImage = pDisk->pLast;
1240 if (RT_UNLIKELY(!pImage))
1241 {
1242 Assert(pImage);
1243 rc = VERR_VDI_NOT_OPENED;
1244 }
1245 else
1246 {
1247 vdResetModifiedFlag(pDisk);
1248 rc = pDisk->Backend->pfnFlush(pImage->pvBackendData);
1249 }
1250
1251 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1252 return rc;
1253}
1254
1255/**
1256 * Get number of opened images in HDD container.
1257 *
1258 * @returns Number of opened images for HDD container. 0 if no images have been opened.
1259 * @param pDisk Pointer to VBox HDD container.
1260 */
1261VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
1262{
1263 /* sanity check */
1264 Assert(pDisk);
1265 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1266
1267 unsigned c = pDisk->cImages;
1268 LogFlow(("%s: returns %d\n", __FUNCTION__, c));
1269 return c;
1270}
1271
1272/**
1273 * Get read/write mode of the VBox HDD container.
1274 *
1275 * @returns Virtual disk ReadOnly status.
1276 * @returns true if no image is opened in HDD container.
1277 * @param pDisk Pointer to VBox HDD container.
1278 */
1279VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
1280{
1281 /* sanity check */
1282 Assert(pDisk);
1283 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1284
1285 bool f;
1286 if (pDisk->pLast)
1287 {
1288 unsigned uOpenFlags;
1289 uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1290 f = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
1291 }
1292 else
1293 {
1294 AssertMsgFailed(("No disk image is opened!\n"));
1295 f = true;
1296 }
1297
1298 LogFlow(("%s: returns %d\n", __FUNCTION__, f));
1299 return f;
1300}
1301
1302/**
1303 * Get total disk size of the VBox HDD container.
1304 *
1305 * @returns Virtual disk size in bytes.
1306 * @returns 0 if no image is opened in HDD container.
1307 * @param pDisk Pointer to VBox HDD container.
1308 */
1309VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk)
1310{
1311 /* sanity check */
1312 Assert(pDisk);
1313 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1314
1315 uint64_t cb = pDisk->cbSize;
1316 LogFlow(("%s: returns %lld\n", __FUNCTION__, cb));
1317 return cb;
1318}
1319
1320/**
1321 * Get virtual disk geometry stored in HDD container.
1322 *
1323 * @returns VBox status code.
1324 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1325 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
1326 * @param pDisk Pointer to VBox HDD container.
1327 * @param pcCylinders Where to store the number of cylinders. NULL is ok.
1328 * @param pcHeads Where to store the number of heads. NULL is ok.
1329 * @param pcSectors Where to store the number of sectors. NULL is ok.
1330 */
1331VBOXDDU_DECL(int) VDGetGeometry(PVBOXHDD pDisk, unsigned *pcCylinders,
1332 unsigned *pcHeads, unsigned *pcSectors)
1333{
1334 /* sanity check */
1335 Assert(pDisk);
1336 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1337
1338 int rc = VINF_SUCCESS;
1339 PVDIMAGE pImage = pDisk->pBase;
1340 if (RT_UNLIKELY(!pImage))
1341 {
1342 Assert(pImage);
1343 rc = VERR_VDI_NOT_OPENED;
1344 }
1345 else
1346 {
1347 if (pDisk->cCylinders != 0)
1348 {
1349 if (pcCylinders)
1350 *pcCylinders = pDisk->cCylinders;
1351 if (pcHeads)
1352 *pcHeads = pDisk->cHeads;
1353 if (pcSectors)
1354 *pcSectors = pDisk->cSectors;
1355 }
1356 else
1357 rc = VERR_VDI_GEOMETRY_NOT_SET;
1358 }
1359 LogFlow(("%s: %Vrc (CHS=%u/%u/%u)\n", __FUNCTION__, rc,
1360 pDisk->cCylinders, pDisk->cHeads, pDisk->cSectors));
1361 return rc;
1362}
1363
1364/**
1365 * Store virtual disk geometry in HDD container.
1366 *
1367 * Note that in case of unrecoverable error all images in HDD container will be closed.
1368 *
1369 * @returns VBox status code.
1370 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1371 * @param pDisk Pointer to VBox HDD container.
1372 * @param cCylinders Number of cylinders.
1373 * @param cHeads Number of heads.
1374 * @param cSectors Number of sectors.
1375 */
1376VBOXDDU_DECL(int) VDSetGeometry(PVBOXHDD pDisk, unsigned cCylinders,
1377 unsigned cHeads, unsigned cSectors)
1378{
1379 /* sanity check */
1380 Assert(pDisk);
1381 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1382
1383 int rc = VINF_SUCCESS;
1384 PVDIMAGE pImage = pDisk->pBase;
1385 if (RT_UNLIKELY(!pImage))
1386 {
1387 Assert(pImage);
1388 rc = VERR_VDI_NOT_OPENED;
1389 }
1390 else
1391 {
1392 if ( cCylinders != pDisk->cCylinders
1393 || cHeads != pDisk->cHeads
1394 || cSectors != pDisk->cSectors)
1395 {
1396 /* Only update geometry if it is changed. Avoids similar checks
1397 * in every backend. Most of the time the new geometry is set to
1398 * the previous values, so no need to go through the hassle of
1399 * updating an image which could be opened in read-only mode right
1400 * now. */
1401 rc = pDisk->Backend->pfnSetGeometry(pImage->pvBackendData,
1402 cCylinders, cHeads, cSectors);
1403
1404 /* Cache new geometry values in any case, whether successful or not. */
1405 int rc2 = pDisk->Backend->pfnGetGeometry(pImage->pvBackendData,
1406 &pDisk->cCylinders,
1407 &pDisk->cHeads,
1408 &pDisk->cSectors);
1409 if (VBOX_FAILURE(rc2))
1410 {
1411 pDisk->cCylinders = 0;
1412 pDisk->cHeads = 0;
1413 pDisk->cSectors = 0;
1414 }
1415 else
1416 {
1417 /* Make sure the CHS geometry is properly clipped. */
1418 pDisk->cCylinders = RT_MIN(pDisk->cCylinders, 16383);
1419 pDisk->cHeads = RT_MIN(pDisk->cHeads, 255);
1420 pDisk->cSectors = RT_MIN(pDisk->cSectors, 255);
1421 }
1422 }
1423 }
1424
1425 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1426 return rc;
1427}
1428
1429/**
1430 * Get virtual disk translation mode stored in HDD container.
1431 *
1432 * @returns VBox status code.
1433 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1434 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
1435 * @param pDisk Pointer to VBox HDD container.
1436 * @param penmTranslation Where to store the translation mode (see pdm.h).
1437 */
1438VBOXDDU_DECL(int) VDGetTranslation(PVBOXHDD pDisk,
1439 PPDMBIOSTRANSLATION penmTranslation)
1440{
1441 /* sanity check */
1442 Assert(pDisk);
1443 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1444
1445 int rc = VINF_SUCCESS;
1446 PVDIMAGE pImage = pDisk->pBase;
1447 if (RT_UNLIKELY(!pImage))
1448 {
1449 Assert(pImage);
1450 rc = VERR_VDI_NOT_OPENED;
1451 }
1452 else
1453 {
1454 if (pDisk->enmTranslation != 0)
1455 *penmTranslation = pDisk->enmTranslation;
1456 else
1457 rc = VERR_VDI_GEOMETRY_NOT_SET;
1458 }
1459 LogFlow(("%s: %Vrc (translation=%u)\n", __FUNCTION__, rc,
1460 pDisk->enmTranslation));
1461 return rc;
1462}
1463
1464/**
1465 * Store virtual disk translation mode in HDD container.
1466 *
1467 * Note that in case of unrecoverable error all images in HDD container will be closed.
1468 *
1469 * @returns VBox status code.
1470 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1471 * @param pDisk Pointer to VBox HDD container.
1472 * @param enmTranslation Translation mode (see pdm.h).
1473 */
1474VBOXDDU_DECL(int) VDSetTranslation(PVBOXHDD pDisk,
1475 PDMBIOSTRANSLATION enmTranslation)
1476{
1477 /* sanity check */
1478 Assert(pDisk);
1479 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1480
1481 int rc = VINF_SUCCESS;
1482 PVDIMAGE pImage = pDisk->pBase;
1483 if (RT_UNLIKELY(!pImage))
1484 {
1485 Assert(pImage);
1486 rc = VERR_VDI_NOT_OPENED;
1487 }
1488 else
1489 {
1490 if (enmTranslation == 0)
1491 rc = VERR_INVALID_PARAMETER;
1492 else if (enmTranslation != pDisk->enmTranslation)
1493 {
1494 /* Only update translation mode if it is changed. Avoids similar
1495 * checks in every backend. Most of the time the new translation
1496 * mode is set to the previous value, so no need to go through the
1497 * hassle of updating an image which could be opened in read-only
1498 * mode right now. */
1499 rc = pDisk->Backend->pfnSetTranslation(pImage->pvBackendData,
1500 enmTranslation);
1501
1502 /* Cache new translation mode in any case, whether successful or not. */
1503 int rc2 = pDisk->Backend->pfnGetTranslation(pImage->pvBackendData,
1504 &pDisk->enmTranslation);
1505 if (VBOX_FAILURE(rc2))
1506 pDisk->enmTranslation = (PDMBIOSTRANSLATION)0;
1507 }
1508 }
1509
1510 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1511 return rc;
1512}
1513
1514/**
1515 * Get version of image in HDD container.
1516 *
1517 * @returns VBox status code.
1518 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1519 * @param pDisk Pointer to VBox HDD container.
1520 * @param nImage Image number, counts from 0. 0 is always base image of container.
1521 * @param puVersion Where to store the image version.
1522 */
1523VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
1524 unsigned *puVersion)
1525{
1526 return VERR_NOT_IMPLEMENTED;
1527}
1528
1529/**
1530 * Get type of image in HDD container.
1531 *
1532 * @returns VBox status code.
1533 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1534 * @param pDisk Pointer to VBox HDD container.
1535 * @param nImage Image number, counts from 0. 0 is always base image of container.
1536 * @param penmType Where to store the image type.
1537 */
1538VBOXDDU_DECL(int) VDGetImageType(PVBOXHDD pDisk, unsigned nImage,
1539 PVDIMAGETYPE penmType)
1540{
1541 return VERR_NOT_IMPLEMENTED;
1542}
1543
1544/**
1545 * Get flags of image in HDD container.
1546 *
1547 * @returns VBox status code.
1548 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1549 * @param pDisk Pointer to VBox HDD container.
1550 * @param nImage Image number, counts from 0. 0 is always base image of container.
1551 * @param puImageFlags Where to store the image flags.
1552 */
1553VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
1554 unsigned *puImageFlags)
1555{
1556 return VERR_NOT_IMPLEMENTED;
1557}
1558
1559/**
1560 * Get open flags of last opened image in HDD container.
1561 *
1562 * @returns VBox status code.
1563 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1564 * @param pDisk Pointer to VBox HDD container.
1565 * @param puOpenFlags Where to store the image open flags.
1566 */
1567VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned *puOpenFlags)
1568{
1569 /* sanity check */
1570 Assert(pDisk);
1571 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1572
1573 unsigned uOpenFlags = 0;
1574 int rc = VINF_SUCCESS;
1575 PVDIMAGE pImage = pDisk->pLast;
1576 if (RT_UNLIKELY(!pImage))
1577 {
1578 Assert(pImage);
1579 rc = VERR_VDI_NOT_OPENED;
1580 }
1581 else
1582 {
1583 uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1584 *puOpenFlags = uOpenFlags;
1585 }
1586 LogFlow(("%s: returns %Vrc uOpenFlags=%#x\n", __FUNCTION__, rc, uOpenFlags));
1587 return uOpenFlags;
1588}
1589
1590/**
1591 * Set open flags of last opened image in HDD container.
1592 * This operation may cause file locking changes and/or files being reopened.
1593 * Note that in case of unrecoverable error all images in HDD container will be closed.
1594 *
1595 * @returns Virtual disk block size in bytes.
1596 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1597 * @returns VBox status code.
1598 * @param pDisk Pointer to VBox HDD container.
1599 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1600 */
1601VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned uOpenFlags)
1602{
1603 /* sanity check */
1604 Assert(pDisk);
1605 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1606
1607 int rc = VINF_SUCCESS;
1608 PVDIMAGE pImage = pDisk->pLast;
1609 if (RT_UNLIKELY(!pImage))
1610 {
1611 Assert(pImage);
1612 rc = VERR_VDI_NOT_OPENED;
1613 }
1614 else
1615 rc = pDisk->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData,
1616 uOpenFlags);
1617 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1618 return rc;
1619}
1620
1621/**
1622 * Get base filename of image in HDD container. Some image formats use
1623 * other filenames as well, so don't use this for anything but for informational
1624 * purposes.
1625 *
1626 * @returns VBox status code.
1627 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1628 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
1629 * @param pDisk Pointer to VBox HDD container.
1630 * @param nImage Image number, counts from 0. 0 is always base image of container.
1631 * @param pszFilename Where to store the image file name.
1632 * @param cbFilename Size of buffer pszFilename points to.
1633 */
1634VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
1635 char *pszFilename, unsigned cbFilename)
1636{
1637 return VERR_NOT_IMPLEMENTED;
1638}
1639
1640/**
1641 * Get the comment line of image in HDD container.
1642 *
1643 * @returns VBox status code.
1644 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1645 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
1646 * @param pDisk Pointer to VBox HDD container.
1647 * @param nImage Image number, counts from 0. 0 is always base image of container.
1648 * @param pszComment Where to store the comment string of image. NULL is ok.
1649 * @param cbComment The size of pszComment buffer. 0 is ok.
1650 */
1651VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
1652 char *pszComment, unsigned cbComment)
1653{
1654 /* sanity check */
1655 Assert(pDisk);
1656 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1657 Assert(pszComment);
1658
1659 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1660 int rc;
1661 if (pImage)
1662 rc = pDisk->Backend->pfnGetComment(pImage->pvBackendData, pszComment,
1663 cbComment);
1664 else
1665 rc = VERR_VDI_IMAGE_NOT_FOUND;
1666
1667 LogFlow(("%s: returns %Vrc, comment='%s' nImage=%u\n", __FUNCTION__,
1668 rc, pszComment, nImage));
1669 return rc;
1670}
1671
1672/**
1673 * Changes the comment line of image in HDD container.
1674 *
1675 * @returns VBox status code.
1676 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1677 * @param pDisk Pointer to VBox HDD container.
1678 * @param nImage Image number, counts from 0. 0 is always base image of container.
1679 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
1680 */
1681VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
1682 const char *pszComment)
1683{
1684 /* sanity check */
1685 Assert(pDisk);
1686 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1687 LogFlow(("%s: comment='%s' nImage=%u\n", __FUNCTION__, pszComment, nImage));
1688
1689 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1690 int rc;
1691 if (pImage)
1692 rc = pDisk->Backend->pfnSetComment(pImage->pvBackendData, pszComment);
1693 else
1694 rc = VERR_VDI_IMAGE_NOT_FOUND;
1695
1696 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1697 return rc;
1698}
1699
1700
1701/**
1702 * Get 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 creation UUID.
1709 */
1710VBOXDDU_DECL(int) VDGetUuid(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->pfnGetUuid(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 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 UUID of the image.
1737 */
1738VBOXDDU_DECL(int) VDSetUuid(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->pfnSetUuid(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 last modification 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 image modification UUID.
1765 */
1766VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
1767{
1768 /* sanity check */
1769 Assert(pDisk);
1770 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1771 Assert(pUuid);
1772
1773 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1774 int rc;
1775 if (pImage)
1776 rc = pDisk->Backend->pfnGetModificationUuid(pImage->pvBackendData, pUuid);
1777 else
1778 rc = VERR_VDI_IMAGE_NOT_FOUND;
1779
1780 LogFlow(("%s: returns %Vrc, uuid={%Vuuid} nImage=%u\n", __FUNCTION__,
1781 rc, pUuid, nImage));
1782 return rc;
1783}
1784
1785/**
1786 * Set the image's last modification UUID. Should not be used by normal applications.
1787 *
1788 * @returns VBox status code.
1789 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
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 last modification UUID of the image.
1793 */
1794VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
1795{
1796 LogFlow(("%s: uuid={%Vuuid} nImage=%u\n", __FUNCTION__, pUuid, nImage));
1797 /* sanity check */
1798 Assert(pDisk);
1799 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1800 Assert(pUuid);
1801
1802 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1803 int rc;
1804 if (pImage)
1805 rc = pDisk->Backend->pfnSetModificationUuid(pImage->pvBackendData, pUuid);
1806 else
1807 rc = VERR_VDI_IMAGE_NOT_FOUND;
1808
1809 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1810 return rc;
1811}
1812
1813/**
1814 * Get parent UUID of image in HDD container.
1815 *
1816 * @returns VBox status code.
1817 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1818 * @param pDisk Pointer to VBox HDD container.
1819 * @param nImage Image number, counts from 0. 0 is always base image of container.
1820 * @param pUuid Where to store the parent image UUID.
1821 */
1822VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
1823 PRTUUID pUuid)
1824{
1825 /* sanity check */
1826 Assert(pDisk);
1827 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1828 Assert(pUuid);
1829
1830 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1831 int rc;
1832 if (pImage)
1833 rc = pDisk->Backend->pfnGetParentUuid(pImage->pvBackendData, pUuid);
1834 else
1835 rc = VERR_VDI_IMAGE_NOT_FOUND;
1836
1837 LogFlow(("%s: returns %Vrc, uuid={%Vuuid} nImage=%u\n", __FUNCTION__,
1838 rc, pUuid, nImage));
1839 return rc;
1840}
1841
1842/**
1843 * Set the image's parent UUID. Should not be used by normal applications.
1844 *
1845 * @returns VBox status code.
1846 * @param pDisk Pointer to VBox HDD container.
1847 * @param nImage Image number, counts from 0. 0 is always base image of container.
1848 * @param pUuid Optional parameter, new parent UUID of the image.
1849 */
1850VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
1851 PCRTUUID pUuid)
1852{
1853 LogFlow(("%s: uuid={%Vuuid} nImage=%u\n", __FUNCTION__, pUuid, nImage));
1854 /* sanity check */
1855 Assert(pDisk);
1856 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1857 Assert(pUuid);
1858
1859 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1860 int rc;
1861 if (pImage)
1862 rc = pDisk->Backend->pfnSetParentUuid(pImage->pvBackendData, pUuid);
1863 else
1864 rc = VERR_VDI_IMAGE_NOT_FOUND;
1865
1866 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1867 return rc;
1868}
1869
1870
1871/**
1872 * Debug helper - dumps all opened images in HDD container into the log file.
1873 *
1874 * @param pDisk Pointer to VDI HDD container.
1875 */
1876VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
1877{
1878 /* sanity check */
1879 Assert(pDisk);
1880 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1881
1882 RTLogPrintf("--- Dumping VDI Disk, Images=%u\n", pDisk->cImages);
1883 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
1884 {
1885 RTLogPrintf("Dumping VDI image \"%s\" file=%#p\n", pImage->pszFilename);
1886 /** @todo call backend to print its part. */
1887 }
1888}
1889
Note: See TracBrowser for help on using the repository browser.

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