VirtualBox

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

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

Make the backend type a per-image property and get away from the per container property. Required e.g. for snapshotting iSCSI disks (whenever we get there).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 103.7 KB
Line 
1/** $Id: VBoxHDD-new.cpp 7277 2008-03-04 14:12:17Z vboxsync $ */
2/** @file
3 * VBox HDD Container implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2008 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 (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will 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#include <iprt/dir.h>
34#include <iprt/path.h>
35#include <iprt/param.h>
36
37#include "VBoxHDD-newInternal.h"
38
39
40#define VBOXHDDDISK_SIGNATURE 0x6f0e2a7d
41
42/** Buffer size used for merging images. */
43#define VD_MERGE_BUFFER_SIZE (1024 * 1024)
44
45/**
46 * VBox HDD Container image descriptor.
47 */
48typedef struct VDIMAGE
49{
50 /** Link to parent image descriptor, if any. */
51 struct VDIMAGE *pPrev;
52 /** Link to child image descriptor, if any. */
53 struct VDIMAGE *pNext;
54 /** Container base filename. (UTF-8) */
55 char *pszFilename;
56 /** Data managed by the backend which keeps the actual info. */
57 void *pvBackendData;
58 /** Image open flags (only those handled generically in this code and which
59 * the backends will never ever see). */
60 unsigned uOpenFlags;
61
62 /** Handle for the shared object / DLL. */
63 RTLDRMOD hPlugin;
64 /** Function pointers for the various backend methods. */
65 PCVBOXHDDBACKEND Backend;
66} VDIMAGE, *PVDIMAGE;
67
68/**
69 * uModified bit flags.
70 */
71#define VD_IMAGE_MODIFIED_FLAG RT_BIT(0)
72#define VD_IMAGE_MODIFIED_FIRST RT_BIT(1)
73#define VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE RT_BIT(2)
74
75
76/**
77 * VBox HDD Container main structure, private part.
78 */
79struct VBOXHDD
80{
81 /** Structure signature (VBOXHDDDISK_SIGNATURE). */
82 uint32_t u32Signature;
83
84 /** Number of opened images. */
85 unsigned cImages;
86
87 /** Base image. */
88 PVDIMAGE pBase;
89
90 /** Last opened image in the chain.
91 * The same as pBase if only one image is used. */
92 PVDIMAGE pLast;
93
94 /** Flags representing the modification state. */
95 unsigned uModified;
96
97 /** Cached size of this disk. */
98 uint64_t cbSize;
99 /** Cached PCHS geometry for this disk. */
100 PDMMEDIAGEOMETRY PCHSGeometry;
101 /** Cached LCHS geometry for this disk. */
102 PDMMEDIAGEOMETRY LCHSGeometry;
103
104 /** Error message processing callback. */
105 PFNVDERROR pfnError;
106 /** Opaque data for error callback. */
107 void *pvErrorUser;
108};
109
110
111extern VBOXHDDBACKEND g_VmdkBackend;
112extern VBOXHDDBACKEND g_VDIBackend;
113#ifndef VBOX_OSE
114extern VBOXHDDBACKEND g_VhdBackend;
115#endif
116
117static PCVBOXHDDBACKEND aBackends[] =
118{
119 &g_VmdkBackend,
120 &g_VDIBackend,
121#ifndef VBOX_OSE
122 &g_VhdBackend,
123#endif
124 NULL
125};
126
127
128/**
129 * internal: issue error message.
130 */
131static int vdError(PVBOXHDD pDisk, int rc, RT_SRC_POS_DECL,
132 const char *pszFormat, ...)
133{
134 va_list va;
135 va_start(va, pszFormat);
136 if (pDisk->pfnError)
137 pDisk->pfnError(pDisk->pvErrorUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
138 va_end(va);
139 return rc;
140}
141
142/**
143 * internal: find image format backend.
144 */
145static int vdFindBackend(const char *pszBackend, PCVBOXHDDBACKEND *ppBackend,
146 RTLDRMOD *phPlugin)
147{
148 int rc = VINF_SUCCESS;
149 PCVBOXHDDBACKEND pBackend = NULL;
150 RTLDRMOD hPlugin = NIL_RTLDRMOD;
151
152 for (unsigned i = 0; aBackends[i] != NULL; i++)
153 {
154 if (!strcmp(pszBackend, aBackends[i]->pszBackendName))
155 {
156 pBackend = aBackends[i];
157 break;
158 }
159 }
160
161 /* If no static backend is found try loading a shared module with
162 * pszBackend as filename. */
163 if (!pBackend)
164 {
165 char *pszPluginName;
166
167 /* HDD Format Plugins have VBoxHDD as prefix, prepend it. */
168 RTStrAPrintf(&pszPluginName, "%s%s",
169 VBOX_HDDFORMAT_PLUGIN_PREFIX, pszBackend);
170 if (!pszPluginName)
171 {
172 rc = VERR_NO_MEMORY;
173 goto out;
174 }
175
176 /* Try to load the plugin (RTldrLoad takes care of the suffix). */
177 rc = RTLdrLoad(pszPluginName, &hPlugin);
178 if (VBOX_SUCCESS(rc))
179 {
180 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad;
181
182 rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME,
183 (void**)&pfnHDDFormatLoad);
184 if (VBOX_FAILURE(rc) || !pfnHDDFormatLoad)
185 {
186 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Vrc, pfnHDDFormat=%#p\n", VBOX_HDDFORMAT_LOAD_NAME, pszPluginName, rc, pfnHDDFormatLoad));
187 if (VBOX_SUCCESS(rc))
188 rc = VERR_SYMBOL_NOT_FOUND;
189 goto out;
190 }
191
192 /* Get the function table. */
193 PVBOXHDDBACKEND pBE;
194 rc = pfnHDDFormatLoad(&pBE);
195 if (VBOX_FAILURE(rc))
196 goto out;
197 /* Check if the sizes match. If not this plugin is too old. */
198 if (pBE->cbSize != sizeof(VBOXHDDBACKEND))
199 {
200 rc = VERR_VDI_UNSUPPORTED_VERSION;
201 goto out;
202 }
203 pBackend = pBE;
204 }
205 else
206 {
207 /* If the backend plugin doesn't exist, don't treat this as an
208 * error. Just return the NULL pointers. */
209 rc = VINF_SUCCESS;
210 }
211
212 RTStrFree(pszPluginName);
213 }
214
215out:
216 if (VBOX_FAILURE(rc))
217 {
218 if (hPlugin != NIL_RTLDRMOD)
219 RTLdrClose(hPlugin);
220 hPlugin = NIL_RTLDRMOD;
221 pBackend = NULL;
222 }
223
224 *ppBackend = pBackend;
225 *phPlugin = hPlugin;
226 return rc;
227}
228
229/**
230 * internal: add image structure to the end of images list.
231 */
232static void vdAddImageToList(PVBOXHDD pDisk, PVDIMAGE pImage)
233{
234 pImage->pPrev = NULL;
235 pImage->pNext = NULL;
236
237 if (pDisk->pBase)
238 {
239 Assert(pDisk->cImages > 0);
240 pImage->pPrev = pDisk->pLast;
241 pDisk->pLast->pNext = pImage;
242 pDisk->pLast = pImage;
243 }
244 else
245 {
246 Assert(pDisk->cImages == 0);
247 pDisk->pBase = pImage;
248 pDisk->pLast = pImage;
249 }
250
251 pDisk->cImages++;
252}
253
254/**
255 * internal: remove image structure from the images list.
256 */
257static void vdRemoveImageFromList(PVBOXHDD pDisk, PVDIMAGE pImage)
258{
259 Assert(pDisk->cImages > 0);
260
261 if (pImage->pPrev)
262 pImage->pPrev->pNext = pImage->pNext;
263 else
264 pDisk->pBase = pImage->pNext;
265
266 if (pImage->pNext)
267 pImage->pNext->pPrev = pImage->pPrev;
268 else
269 pDisk->pLast = pImage->pPrev;
270
271 pImage->pPrev = NULL;
272 pImage->pNext = NULL;
273
274 pDisk->cImages--;
275}
276
277/**
278 * internal: find image by index into the images list.
279 */
280static PVDIMAGE vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
281{
282 PVDIMAGE pImage = pDisk->pBase;
283 if (nImage == VD_LAST_IMAGE)
284 return pDisk->pLast;
285 while (pImage && nImage)
286 {
287 pImage = pImage->pNext;
288 nImage--;
289 }
290 return pImage;
291}
292
293/**
294 * internal: read the specified amount of data in whatever blocks the backend
295 * will give us.
296 */
297static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
298 void *pvBuf, size_t cbRead)
299{
300 int rc;
301 size_t cbThisRead;
302
303 /* Loop until all read. */
304 do
305 {
306 /* Search for image with allocated block. Do not attempt to read more
307 * than the previous reads marked as valid. Otherwise this would return
308 * stale data when different block sizes are used for the images. */
309 cbThisRead = cbRead;
310 rc = VERR_VDI_BLOCK_FREE;
311 for (PVDIMAGE pCurrImage = pImage;
312 pCurrImage != NULL && rc == VERR_VDI_BLOCK_FREE;
313 pCurrImage = pCurrImage->pPrev)
314 {
315 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
316 uOffset, pvBuf, cbThisRead,
317 &cbThisRead);
318 }
319
320 /* No image in the chain contains the data for the block. */
321 if (rc == VERR_VDI_BLOCK_FREE)
322 {
323 memset(pvBuf, '\0', cbThisRead);
324 rc = VINF_SUCCESS;
325 }
326
327 cbRead -= cbThisRead;
328 uOffset += cbThisRead;
329 pvBuf = (char *)pvBuf + cbThisRead;
330 } while (cbRead != 0 && VBOX_SUCCESS(rc));
331
332 return rc;
333}
334
335/**
336 * internal: mark the disk as not modified.
337 */
338static void vdResetModifiedFlag(PVBOXHDD pDisk)
339{
340 if (pDisk->uModified & VD_IMAGE_MODIFIED_FLAG)
341 {
342 /* generate new last-modified uuid */
343 if (!(pDisk->uModified | VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
344 {
345 RTUUID Uuid;
346
347 RTUuidCreate(&Uuid);
348 pDisk->pLast->Backend->pfnSetModificationUuid(pDisk->pLast->pvBackendData,
349 &Uuid);
350 }
351
352 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FLAG;
353 }
354}
355
356/**
357 * internal: mark the disk as modified.
358 */
359static void vdSetModifiedFlag(PVBOXHDD pDisk)
360{
361 pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
362 if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
363 {
364 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
365
366 /* First modify, so create a UUID and ensure it's written to disk. */
367 vdResetModifiedFlag(pDisk);
368
369 if (!(pDisk->uModified | VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
370 pDisk->pLast->Backend->pfnFlush(pDisk->pLast->pvBackendData);
371 }
372}
373
374/**
375 * internal: write a complete block (only used for diff images), taking the
376 * remaining data from parent images. This implementation does not optimize
377 * anything (except that it tries to read only that portions from parent
378 * images that are really needed).
379 */
380static int vdWriteHelperStandard(PVBOXHDD pDisk, PVDIMAGE pImage,
381 uint64_t uOffset, size_t cbWrite,
382 size_t cbThisWrite, size_t cbPreRead,
383 size_t cbPostRead, const void *pvBuf,
384 void *pvTmp)
385{
386 int rc = VINF_SUCCESS;
387
388 /* Read the data that goes before the write to fill the block. */
389 if (cbPreRead)
390 {
391 rc = vdReadHelper(pDisk, pImage->pPrev, uOffset - cbPreRead, pvTmp,
392 cbPreRead);
393 if (VBOX_FAILURE(rc))
394 return rc;
395 }
396
397 /* Copy the data to the right place in the buffer. */
398 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
399
400 /* Read the data that goes after the write to fill the block. */
401 if (cbPostRead)
402 {
403 /* If we have data to be written, use that instead of reading
404 * data from the image. */
405 size_t cbWriteCopy;
406 if (cbWrite > cbThisWrite)
407 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
408 else
409 cbWriteCopy = 0;
410 /* Figure out how much we cannnot read from the image, because
411 * the last block to write might exceed the nominal size of the
412 * image for technical reasons. */
413 size_t cbFill;
414 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
415 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
416 else
417 cbFill = 0;
418 /* The rest must be read from the image. */
419 size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
420
421 /* Now assemble the remaining data. */
422 if (cbWriteCopy)
423 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
424 (char *)pvBuf + cbThisWrite, cbWriteCopy);
425 if (cbReadImage)
426 rc = vdReadHelper(pDisk, pImage->pPrev,
427 uOffset + cbThisWrite + cbWriteCopy,
428 (char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy,
429 cbReadImage);
430 if (VBOX_FAILURE(rc))
431 return rc;
432 /* Zero out the remainder of this block. Will never be visible, as this
433 * is beyond the limit of the image. */
434 if (cbFill)
435 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
436 '\0', cbFill);
437 }
438
439 /* Write the full block to the virtual disk. */
440 rc = pImage->Backend->pfnWrite(pImage->pvBackendData,
441 uOffset - cbPreRead, pvTmp,
442 cbPreRead + cbThisWrite + cbPostRead,
443 NULL,
444 &cbPreRead, &cbPostRead);
445 Assert(rc != VERR_VDI_BLOCK_FREE);
446 Assert(cbPreRead == 0);
447 Assert(cbPostRead == 0);
448
449 return rc;
450}
451
452/**
453 * internal: write a complete block (only used for diff images), taking the
454 * remaining data from parent images. This implementation optimized out writes
455 * that do not change the data relative to the state as of the parent images.
456 * All backends which support differential/growing images support this.
457 */
458static int vdWriteHelperOptimized(PVBOXHDD pDisk, PVDIMAGE pImage,
459 uint64_t uOffset, size_t cbWrite,
460 size_t cbThisWrite, size_t cbPreRead,
461 size_t cbPostRead, const void *pvBuf,
462 void *pvTmp)
463{
464 size_t cbFill = 0;
465 size_t cbWriteCopy = 0;
466 size_t cbReadImage = 0;
467 int rc;
468
469 if (cbPostRead)
470 {
471 /* Figure out how much we cannnot read from the image, because
472 * the last block to write might exceed the nominal size of the
473 * image for technical reasons. */
474 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
475 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
476
477 /* If we have data to be written, use that instead of reading
478 * data from the image. */
479 if (cbWrite > cbThisWrite)
480 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
481
482 /* The rest must be read from the image. */
483 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
484 }
485
486 /* Read the entire data of the block so that we can compare whether it will
487 * be modified by the write or not. */
488 rc = vdReadHelper(pDisk, pImage->pPrev, uOffset - cbPreRead, pvTmp,
489 cbPreRead + cbThisWrite + cbPostRead - cbFill);
490 if (VBOX_FAILURE(rc))
491 return rc;
492
493 /* Check if the write would modify anything in this block. */
494 if ( !memcmp((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite)
495 && (!cbWriteCopy || !memcmp((char *)pvTmp + cbPreRead + cbThisWrite,
496 (char *)pvBuf + cbThisWrite, cbWriteCopy)))
497 {
498 /* Block is completely unchanged, so no need to write anything. */
499 return VINF_SUCCESS;
500 }
501
502 /* Copy the data to the right place in the buffer. */
503 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
504
505 /* Handle the data that goes after the write to fill the block. */
506 if (cbPostRead)
507 {
508 /* Now assemble the remaining data. */
509 if (cbWriteCopy)
510 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
511 (char *)pvBuf + cbThisWrite, cbWriteCopy);
512 /* Zero out the remainder of this block. Will never be visible, as this
513 * is beyond the limit of the image. */
514 if (cbFill)
515 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
516 '\0', cbFill);
517 }
518
519 /* Write the full block to the virtual disk. */
520 rc = pImage->Backend->pfnWrite(pImage->pvBackendData,
521 uOffset - cbPreRead, pvTmp,
522 cbPreRead + cbThisWrite + cbPostRead,
523 NULL,
524 &cbPreRead, &cbPostRead);
525 Assert(rc != VERR_VDI_BLOCK_FREE);
526 Assert(cbPreRead == 0);
527 Assert(cbPostRead == 0);
528
529 return rc;
530}
531
532/**
533 * internal: write buffer to the image, taking care of block boundaries and
534 * write optimizations.
535 */
536static int vdWriteHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
537 const void *pvBuf, size_t cbWrite)
538{
539 int rc;
540 size_t cbThisWrite;
541 size_t cbPreRead, cbPostRead;
542
543 /* Loop until all written. */
544 do
545 {
546 /* Try to write the possibly partial block to the last opened image.
547 * This works when the block is already allocated in this image or
548 * if it is a full-block write, which automatically allocates a new
549 * block if needed. */
550 cbThisWrite = cbWrite;
551 rc = pImage->Backend->pfnWrite(pImage->pvBackendData, uOffset, pvBuf,
552 cbThisWrite, &cbThisWrite, &cbPreRead,
553 &cbPostRead);
554 if (rc == VERR_VDI_BLOCK_FREE)
555 {
556 void *pvTmp = RTMemTmpAlloc(cbPreRead + cbThisWrite + cbPostRead);
557 AssertBreak(VALID_PTR(pvTmp), rc = VERR_NO_MEMORY);
558
559 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME))
560 {
561 /* Optimized write, suppress writing to a so far unallocated
562 * block if the data is in fact not changed. */
563 rc = vdWriteHelperOptimized(pDisk, pImage, uOffset, cbWrite,
564 cbThisWrite, cbPreRead, cbPostRead,
565 pvBuf, pvTmp);
566 }
567 else
568 {
569 /* Normal write, not optimized in any way. The block will
570 * be written no matter what. This will usually (unless the
571 * backend has some further optimization enabled) cause the
572 * block to be allocated. */
573 rc = vdWriteHelperStandard(pDisk, pImage, uOffset, cbWrite,
574 cbThisWrite, cbPreRead, cbPostRead,
575 pvBuf, pvTmp);
576 }
577 RTMemTmpFree(pvTmp);
578 if (VBOX_FAILURE(rc))
579 break;
580 }
581
582 cbWrite -= cbThisWrite;
583 uOffset += cbThisWrite;
584 pvBuf = (char *)pvBuf + cbThisWrite;
585 } while (cbWrite != 0 && VBOX_SUCCESS(rc));
586
587 return rc;
588}
589
590
591/**
592 * Allocates and initializes an empty HDD container.
593 * No image files are opened.
594 *
595 * @returns VBox status code.
596 * @param pfnError Callback for setting extended error information.
597 * @param pvErrorUser Opaque parameter for pfnError.
598 * @param ppDisk Where to store the reference to HDD container.
599 */
600VBOXDDU_DECL(int) VDCreate(PFNVDERROR pfnError, void *pvErrorUser,
601 PVBOXHDD *ppDisk)
602{
603 int rc = VINF_SUCCESS;
604 PVBOXHDD pDisk = NULL;
605
606 LogFlowFunc(("pfnError=%#p pvErrorUser=%#p\n", pfnError, pvErrorUser));
607 do
608 {
609 /* Check arguments. */
610 AssertMsgBreak(VALID_PTR(pfnError),
611 ("pfnError=%#p\n", pfnError),
612 rc = VERR_INVALID_PARAMETER);
613 AssertMsgBreak(VALID_PTR(ppDisk),
614 ("ppDisk=%#p\n", ppDisk),
615 rc = VERR_INVALID_PARAMETER);
616
617 pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
618 if (pDisk)
619 {
620 pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
621 pDisk->cImages = 0;
622 pDisk->pBase = NULL;
623 pDisk->pLast = NULL;
624 pDisk->cbSize = 0;
625 pDisk->PCHSGeometry.cCylinders = 0;
626 pDisk->PCHSGeometry.cHeads = 0;
627 pDisk->PCHSGeometry.cSectors = 0;
628 pDisk->LCHSGeometry.cCylinders = 0;
629 pDisk->LCHSGeometry.cHeads = 0;
630 pDisk->LCHSGeometry.cSectors = 0;
631 pDisk->pfnError = pfnError;
632 pDisk->pvErrorUser = pvErrorUser;
633 *ppDisk = pDisk;
634 }
635 else
636 {
637 rc = VERR_NO_MEMORY;
638 break;
639 }
640 } while (0);
641
642 LogFlowFunc(("returns %Vrc (pDisk=%#p)\n", rc, pDisk));
643 return rc;
644}
645
646/**
647 * Destroys HDD container.
648 * If container has opened image files they will be closed.
649 *
650 * @param pDisk Pointer to HDD container.
651 */
652VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk)
653{
654 LogFlowFunc(("pDisk=%#p\n", pDisk));
655 do
656 {
657 /* sanity check */
658 AssertBreak(VALID_PTR(pDisk), );
659 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
660
661 if (pDisk)
662 {
663 VDCloseAll(pDisk);
664 RTMemFree(pDisk);
665 }
666 } while (0);
667 LogFlowFunc(("returns\n"));
668}
669
670/**
671 * Try to get the backend name which can use this image.
672 *
673 * @returns VBox status code.
674 * VINF_SUCCESS if a plugin was found.
675 * ppszFormat contains the string which can be used as backend name.
676 * VERR_NOT_SUPPORTED if no plugin was found.
677 * @param pszFilename Name of the image file for which the backend is queried.
678 * @param ppszFormat Receives pointer of the UTF-8 string which contains the format name.
679 * The returned pointer must be freed using RTStrFree().
680 */
681VBOXDDU_DECL(int) VDGetFormat(const char *pszFilename, char **ppszFormat)
682{
683 PRTDIR pPluginDir = NULL;
684 int rc = VERR_NOT_SUPPORTED;
685 bool fPluginFound = false;
686
687 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
688 do
689 {
690 /* Check arguments. */
691 AssertMsgBreak(VALID_PTR(pszFilename) && *pszFilename,
692 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
693 rc = VERR_INVALID_PARAMETER);
694 AssertMsgBreak(VALID_PTR(ppszFormat),
695 ("ppszFormat=%#p\n", ppszFormat),
696 rc = VERR_INVALID_PARAMETER);
697
698 /* First check if static backends support this file format. */
699 for (unsigned i = 0; aBackends[i] != NULL; i++)
700 {
701 if (aBackends[i]->pfnCheckIfValid)
702 {
703 rc = aBackends[i]->pfnCheckIfValid(pszFilename);
704 if (VBOX_SUCCESS(rc))
705 {
706 fPluginFound = true;
707 /* Copy the name into the new string. */
708 char *pszFormat = RTStrDup(aBackends[i]->pszBackendName);
709 if (!pszFormat)
710 {
711 rc = VERR_NO_MEMORY;
712 break;
713 }
714 *ppszFormat = pszFormat;
715 break;
716 }
717 }
718 }
719
720 /* Then check if plugin backends support this file format. */
721 char szPath[RTPATH_MAX];
722 rc = RTPathSharedLibs(szPath, sizeof(szPath));
723 if (VBOX_FAILURE(rc))
724 break;
725
726 /* To get all entries with VBoxHDD as prefix. */
727 char *pszPluginFilter;
728 rc = RTStrAPrintf(&pszPluginFilter, "%s/%s*", szPath,
729 VBOX_HDDFORMAT_PLUGIN_PREFIX);
730 if (VBOX_FAILURE(rc))
731 {
732 rc = VERR_NO_MEMORY;
733 break;
734 }
735
736 /* The plugins are in the same directory as the other shared libs. */
737 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
738 if (VBOX_FAILURE(rc))
739 break;
740
741 PRTDIRENTRY pPluginDirEntry = NULL;
742 unsigned cbPluginDirEntry = sizeof(RTDIRENTRY);
743 pPluginDirEntry = (PRTDIRENTRY)RTMemAllocZ(sizeof(RTDIRENTRY));
744 if (!pPluginDirEntry)
745 {
746 rc = VERR_NO_MEMORY;
747 break;
748 }
749
750 while ((rc = RTDirRead(pPluginDir, pPluginDirEntry, &cbPluginDirEntry)) != VERR_NO_MORE_FILES)
751 {
752 RTLDRMOD hPlugin = NIL_RTLDRMOD;
753 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad = NULL;
754 PVBOXHDDBACKEND pBackend = NULL;
755
756 if (rc == VERR_BUFFER_OVERFLOW)
757 {
758 /* allocate new buffer. */
759 RTMemFree(pPluginDirEntry);
760 pPluginDirEntry = (PRTDIRENTRY)RTMemAllocZ(cbPluginDirEntry);
761 /* Retry. */
762 rc = RTDirRead(pPluginDir, pPluginDirEntry, &cbPluginDirEntry);
763 if (VBOX_FAILURE(rc))
764 break;
765 }
766 else if (VBOX_FAILURE(rc))
767 break;
768
769 /* We got the new entry. */
770 if (pPluginDirEntry->enmType != RTDIRENTRYTYPE_FILE)
771 continue;
772
773 rc = RTLdrLoad(pPluginDirEntry->szName, &hPlugin);
774 if (VBOX_SUCCESS(rc))
775 {
776 rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad);
777 if (VBOX_FAILURE(rc) || !pfnHDDFormatLoad)
778 {
779 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Vrc, pfnHDDFormat=%#p\n", VBOX_HDDFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnHDDFormatLoad));
780 if (VBOX_SUCCESS(rc))
781 rc = VERR_SYMBOL_NOT_FOUND;
782 }
783
784 if (VBOX_SUCCESS(rc))
785 {
786 /* Get the function table. */
787 rc = pfnHDDFormatLoad(&pBackend);
788 if (VBOX_SUCCESS(rc) && pBackend->cbSize == sizeof(VBOXHDDBACKEND))
789 {
790
791 /* Check if the plugin can handle this file. */
792 rc = pBackend->pfnCheckIfValid(pszFilename);
793 if (VBOX_SUCCESS(rc))
794 {
795 fPluginFound = true;
796 rc = VINF_SUCCESS;
797
798 /* Report the format name. */
799 RTPathStripExt(pPluginDirEntry->szName);
800 AssertBreak(strlen(pPluginDirEntry->szName) <= VBOX_HDDFORMAT_PLUGIN_PREFIX_LENGTH,
801 rc = VERR_INVALID_NAME);
802
803 char *pszFormat = NULL;
804 pszFormat = RTStrDup(pPluginDirEntry->szName + VBOX_HDDFORMAT_PLUGIN_PREFIX_LENGTH);
805 if (!pszFormat)
806 rc = VERR_NO_MEMORY;
807
808 *ppszFormat = pszFormat;
809 }
810 }
811 }
812 else
813 pBackend = NULL;
814
815 RTLdrClose(hPlugin);
816 }
817
818 /*
819 * We take the first plugin which can handle this file.
820 */
821 if (fPluginFound)
822 break;
823 }
824 RTStrFree(pszPluginFilter);
825 if (pPluginDirEntry)
826 RTMemFree(pPluginDirEntry);
827 if (pPluginDir)
828 RTDirClose(pPluginDir);
829 } while (0);
830
831 LogFlowFunc(("returns %Vrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
832 return rc;
833}
834
835/**
836 * Opens an image file.
837 *
838 * The first opened image file in HDD container must have a base image type,
839 * others (next opened images) must be a differencing or undo images.
840 * Linkage is checked for differencing image to be in consistence with the previously opened image.
841 * When another differencing image is opened and the last image was opened in read/write access
842 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
843 * other processes to use images in read-only mode too.
844 *
845 * Note that the image is opened in read-only mode if a read/write open is not possible.
846 * Use VDIsReadOnly to check open mode.
847 *
848 * @returns VBox status code.
849 * @param pDisk Pointer to HDD container.
850 * @param pszBackend Name of the image file backend to use.
851 * @param pszFilename Name of the image file to open.
852 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
853 */
854VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
855 const char *pszFilename, unsigned uOpenFlags)
856{
857 int rc = VINF_SUCCESS;
858 PVDIMAGE pImage = NULL;
859
860 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x\n",
861 pDisk, pszBackend, pszFilename, uOpenFlags));
862 do
863 {
864 /* sanity check */
865 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
866 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
867
868 /* Check arguments. */
869 AssertMsgBreak(VALID_PTR(pszBackend) && *pszBackend,
870 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
871 rc = VERR_INVALID_PARAMETER);
872 AssertMsgBreak(VALID_PTR(pszFilename) && *pszFilename,
873 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
874 rc = VERR_INVALID_PARAMETER);
875 AssertMsgBreak((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
876 ("uOpenFlags=%#x\n", uOpenFlags),
877 rc = VERR_INVALID_PARAMETER);
878
879 /* Force readonly for images without base/diff consistency checking. */
880 if (uOpenFlags & VD_OPEN_FLAGS_INFO)
881 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
882
883 /* Set up image descriptor. */
884 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
885 if (!pImage)
886 {
887 rc = VERR_NO_MEMORY;
888 break;
889 }
890 pImage->pszFilename = RTStrDup(pszFilename);
891 if (!pImage->pszFilename)
892 {
893 rc = VERR_NO_MEMORY;
894 break;
895 }
896
897 rc = vdFindBackend(pszBackend, &pImage->Backend, &pImage->hPlugin);
898 if (VBOX_FAILURE(rc))
899 break;
900 if (!pImage->Backend)
901 {
902 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
903 N_("VD: unknown backend name '%s'"), pszBackend);
904 break;
905 }
906
907 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
908 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
909 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
910 pDisk->pfnError, pDisk->pvErrorUser,
911 &pImage->pvBackendData);
912 /* If the open in read-write mode failed, retry in read-only mode. */
913 if (VBOX_FAILURE(rc))
914 {
915 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
916 && (rc == VERR_ACCESS_DENIED
917 || rc == VERR_PERMISSION_DENIED
918 || rc == VERR_WRITE_PROTECT
919 || rc == VERR_SHARING_VIOLATION
920 || rc == VERR_FILE_LOCK_FAILED))
921 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
922 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
923 | VD_OPEN_FLAGS_READONLY,
924 pDisk->pfnError, pDisk->pvErrorUser,
925 &pImage->pvBackendData);
926 if (VBOX_FAILURE(rc))
927 {
928 rc = vdError(pDisk, rc, RT_SRC_POS,
929 N_("VD: error opening image file '%s'"), pszFilename);
930 break;
931 }
932 }
933
934 VDIMAGETYPE enmImageType;
935 rc = pImage->Backend->pfnGetImageType(pImage->pvBackendData,
936 &enmImageType);
937 /* Check image type. As the image itself has no idea whether it's a
938 * base image or not, this info is derived here. Image 0 can be fixed
939 * or normal, all others must be normal images. */
940 if ( VBOX_SUCCESS(rc)
941 && !(uOpenFlags & VD_OPEN_FLAGS_INFO)
942 && pDisk->cImages != 0
943 && enmImageType != VD_IMAGE_TYPE_NORMAL)
944 {
945 rc = VERR_VDI_INVALID_TYPE;
946 break;
947 }
948
949 /** @todo optionally check UUIDs */
950
951 int rc2;
952
953 /* Cache disk information. */
954 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
955
956 /* Cache PCHS geometry. */
957 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
958 &pDisk->PCHSGeometry);
959 if (VBOX_FAILURE(rc2))
960 {
961 pDisk->PCHSGeometry.cCylinders = 0;
962 pDisk->PCHSGeometry.cHeads = 0;
963 pDisk->PCHSGeometry.cSectors = 0;
964 }
965 else
966 {
967 /* Make sure the PCHS geometry is properly clipped. */
968 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
969 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
970 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
971 }
972
973 /* Cache LCHS geometry. */
974 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
975 &pDisk->LCHSGeometry);
976 if (VBOX_FAILURE(rc2))
977 {
978 pDisk->LCHSGeometry.cCylinders = 0;
979 pDisk->LCHSGeometry.cHeads = 0;
980 pDisk->LCHSGeometry.cSectors = 0;
981 }
982 else
983 {
984 /* Make sure the LCHS geometry is properly clipped. */
985 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
986 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
987 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
988 }
989
990 if (pDisk->cImages != 0)
991 {
992 /* Switch previous image to read-only mode. */
993 unsigned uOpenFlagsPrevImg;
994 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
995 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
996 {
997 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
998 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
999 }
1000 }
1001
1002 if (VBOX_SUCCESS(rc))
1003 {
1004 /* Image successfully opened, make it the last image. */
1005 vdAddImageToList(pDisk, pImage);
1006 }
1007 else
1008 {
1009 /* Error detected, but image opened. Close image. */
1010 int rc2;
1011 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, false);
1012 AssertRC(rc2);
1013 pImage->pvBackendData = NULL;
1014 }
1015 } while (0);
1016
1017 if (VBOX_FAILURE(rc))
1018 {
1019 if (pImage->hPlugin != NIL_RTLDRMOD)
1020 RTLdrClose(pImage->hPlugin);
1021
1022 if (pImage)
1023 {
1024 if (pImage->pszFilename)
1025 RTStrFree(pImage->pszFilename);
1026 RTMemFree(pImage);
1027 }
1028 }
1029
1030 LogFlowFunc(("returns %Vrc\n", rc));
1031 return rc;
1032}
1033
1034/**
1035 * Creates and opens a new base image file.
1036 *
1037 * @returns VBox status code.
1038 * @param pDisk Pointer to HDD container.
1039 * @param pszBackend Name of the image file backend to use.
1040 * @param pszFilename Name of the image file to create.
1041 * @param enmType Image type, only base image types are acceptable.
1042 * @param cbSize Image size in bytes.
1043 * @param uImageFlags Flags specifying special image features.
1044 * @param pszComment Pointer to image comment. NULL is ok.
1045 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
1046 * @param pLCHSGeometry Pointer to logical disk geometry <= (1024,255,63). Not NULL.
1047 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1048 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1049 * @param pvUser User argument for the progress callback.
1050 */
1051VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
1052 const char *pszFilename, VDIMAGETYPE enmType,
1053 uint64_t cbSize, unsigned uImageFlags,
1054 const char *pszComment,
1055 PCPDMMEDIAGEOMETRY pPCHSGeometry,
1056 PCPDMMEDIAGEOMETRY pLCHSGeometry,
1057 unsigned uOpenFlags, PFNVMPROGRESS pfnProgress,
1058 void *pvUser)
1059{
1060 int rc = VINF_SUCCESS;
1061 PVDIMAGE pImage = NULL;
1062
1063 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" enmType=%#x cbSize=%llu uImageFlags=%#x pszComment=\"%s\" PCHS=%u/%u/%u LCHS=%u/%u/%u uOpenFlags=%#x pfnProgress=%#p pvUser=%#p\n",
1064 pDisk, pszBackend, pszFilename, enmType, cbSize, uImageFlags, pszComment,
1065 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1066 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
1067 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, uOpenFlags,
1068 pfnProgress, pvUser));
1069 do
1070 {
1071 /* sanity check */
1072 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1073 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1074
1075 /* Check arguments. */
1076 AssertMsgBreak(VALID_PTR(pszBackend) && *pszBackend,
1077 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1078 rc = VERR_INVALID_PARAMETER);
1079 AssertMsgBreak(VALID_PTR(pszFilename) && *pszFilename,
1080 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1081 rc = VERR_INVALID_PARAMETER);
1082 AssertMsgBreak(enmType == VD_IMAGE_TYPE_NORMAL || enmType == VD_IMAGE_TYPE_FIXED,
1083 ("enmType=%#x\n", enmType),
1084 rc = VERR_INVALID_PARAMETER);
1085 AssertMsgBreak(cbSize,
1086 ("cbSize=%llu\n", cbSize),
1087 rc = VERR_INVALID_PARAMETER);
1088 AssertMsgBreak((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
1089 ("uImageFlags=%#x\n", uImageFlags),
1090 rc = VERR_INVALID_PARAMETER);
1091 AssertMsgBreak( VALID_PTR(pPCHSGeometry)
1092 && pPCHSGeometry->cCylinders <= 16383
1093 && pPCHSGeometry->cCylinders != 0
1094 && pPCHSGeometry->cHeads <= 16
1095 && pPCHSGeometry->cHeads != 0
1096 && pPCHSGeometry->cSectors <= 63
1097 && pPCHSGeometry->cSectors != 0,
1098 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
1099 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1100 pPCHSGeometry->cSectors),
1101 rc = VERR_INVALID_PARAMETER);
1102 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
1103 AssertMsgBreak( VALID_PTR(pLCHSGeometry)
1104 && pLCHSGeometry->cCylinders <= 16383
1105 && pLCHSGeometry->cHeads <= 16
1106 && pLCHSGeometry->cSectors <= 63,
1107 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
1108 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
1109 pLCHSGeometry->cSectors),
1110 rc = VERR_INVALID_PARAMETER);
1111 AssertMsgBreak((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1112 ("uOpenFlags=%#x\n", uOpenFlags),
1113 rc = VERR_INVALID_PARAMETER);
1114
1115 /* Check state. */
1116 AssertMsgBreak(pDisk->cImages == 0,
1117 ("Create base image cannot be done with other images open\n"),
1118 rc = VERR_VDI_INVALID_STATE);
1119
1120 /* Set up image descriptor. */
1121 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1122 if (!pImage)
1123 {
1124 rc = VERR_NO_MEMORY;
1125 break;
1126 }
1127 pImage->pszFilename = RTStrDup(pszFilename);
1128 if (!pImage->pszFilename)
1129 {
1130 rc = VERR_NO_MEMORY;
1131 break;
1132 }
1133
1134 rc = vdFindBackend(pszBackend, &pImage->Backend, &pImage->hPlugin);
1135 if (VBOX_FAILURE(rc))
1136 break;
1137 if (!pImage->Backend)
1138 {
1139 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1140 N_("VD: unknown backend name '%s'"), pszBackend);
1141 break;
1142 }
1143
1144 rc = pImage->Backend->pfnCreate(pImage->pszFilename, enmType, cbSize,
1145 uImageFlags, pszComment, pPCHSGeometry,
1146 pLCHSGeometry, uOpenFlags, pfnProgress,
1147 pvUser, 0, 99, pDisk->pfnError,
1148 pDisk->pvErrorUser,
1149 &pImage->pvBackendData);
1150
1151 if (VBOX_SUCCESS(rc))
1152 {
1153 /** @todo optionally check UUIDs */
1154
1155 int rc2;
1156
1157 /* Cache disk information. */
1158 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
1159
1160 /* Cache PCHS geometry. */
1161 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1162 &pDisk->PCHSGeometry);
1163 if (VBOX_FAILURE(rc2))
1164 {
1165 pDisk->PCHSGeometry.cCylinders = 0;
1166 pDisk->PCHSGeometry.cHeads = 0;
1167 pDisk->PCHSGeometry.cSectors = 0;
1168 }
1169 else
1170 {
1171 /* Make sure the CHS geometry is properly clipped. */
1172 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1173 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1174 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1175 }
1176
1177 /* Cache LCHS geometry. */
1178 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1179 &pDisk->LCHSGeometry);
1180 if (VBOX_FAILURE(rc2))
1181 {
1182 pDisk->LCHSGeometry.cCylinders = 0;
1183 pDisk->LCHSGeometry.cHeads = 0;
1184 pDisk->LCHSGeometry.cSectors = 0;
1185 }
1186 else
1187 {
1188 /* Make sure the CHS geometry is properly clipped. */
1189 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
1190 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1191 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1192 }
1193 }
1194
1195 if (VBOX_SUCCESS(rc))
1196 {
1197 /* Image successfully opened, make it the last image. */
1198 vdAddImageToList(pDisk, pImage);
1199 }
1200 else
1201 {
1202 /* Error detected, but image opened. Close and delete image. */
1203 int rc2;
1204 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
1205 AssertRC(rc2);
1206 pImage->pvBackendData = NULL;
1207 }
1208 } while (0);
1209
1210 if (VBOX_FAILURE(rc))
1211 {
1212 if (pImage->hPlugin != NIL_RTLDRMOD)
1213 RTLdrClose(pImage->hPlugin);
1214
1215 if (pImage)
1216 {
1217 if (pImage->pszFilename)
1218 RTStrFree(pImage->pszFilename);
1219 RTMemFree(pImage);
1220 }
1221 }
1222
1223 if (VBOX_SUCCESS(rc) && pfnProgress)
1224 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1225
1226 LogFlowFunc(("returns %Vrc\n", rc));
1227 return rc;
1228}
1229
1230/**
1231 * Creates and opens a new differencing image file in HDD container.
1232 * See comments for VDOpen function about differencing images.
1233 *
1234 * @returns VBox status code.
1235 * @param pDisk Pointer to HDD container.
1236 * @param pszBackend Name of the image file backend to use.
1237 * @param pszFilename Name of the differencing image file to create.
1238 * @param uImageFlags Flags specifying special image features.
1239 * @param pszComment Pointer to image comment. NULL is ok.
1240 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1241 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1242 * @param pvUser User argument for the progress callback.
1243 */
1244VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
1245 const char *pszFilename, unsigned uImageFlags,
1246 const char *pszComment, unsigned uOpenFlags,
1247 PFNVMPROGRESS pfnProgress, void *pvUser)
1248{
1249 int rc = VINF_SUCCESS;
1250 PVDIMAGE pImage = NULL;
1251
1252 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" uOpenFlags=%#x pfnProgress=%#p pvUser=%#p\n",
1253 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, uOpenFlags,
1254 pfnProgress, pvUser));
1255 do
1256 {
1257 /* sanity check */
1258 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1259 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1260
1261 /* Check arguments. */
1262 AssertMsgBreak(VALID_PTR(pszBackend) && *pszBackend,
1263 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1264 rc = VERR_INVALID_PARAMETER);
1265 AssertMsgBreak(VALID_PTR(pszFilename) && *pszFilename,
1266 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1267 rc = VERR_INVALID_PARAMETER);
1268 AssertMsgBreak((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
1269 ("uImageFlags=%#x\n", uImageFlags),
1270 rc = VERR_INVALID_PARAMETER);
1271 AssertMsgBreak((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1272 ("uOpenFlags=%#x\n", uOpenFlags),
1273 rc = VERR_INVALID_PARAMETER);
1274
1275 /* Check state. */
1276 AssertMsgBreak(pDisk->cImages != 0,
1277 ("Create diff image cannot be done without other images open\n"),
1278 rc = VERR_VDI_INVALID_STATE);
1279
1280 /* Set up image descriptor. */
1281 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1282 if (!pImage)
1283 {
1284 rc = VERR_NO_MEMORY;
1285 break;
1286 }
1287 pImage->pszFilename = RTStrDup(pszFilename);
1288 if (!pImage->pszFilename)
1289 {
1290 rc = VERR_NO_MEMORY;
1291 break;
1292 }
1293
1294 rc = vdFindBackend(pszBackend, &pImage->Backend, &pImage->hPlugin);
1295 if (VBOX_FAILURE(rc))
1296 break;
1297 if (!pImage->Backend)
1298 {
1299 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1300 N_("VD: unknown backend name '%s'"), pszBackend);
1301 break;
1302 }
1303
1304 rc = pImage->Backend->pfnCreate(pImage->pszFilename,
1305 VD_IMAGE_TYPE_NORMAL, pDisk->cbSize,
1306 uImageFlags, pszComment,
1307 &pDisk->PCHSGeometry,
1308 &pDisk->LCHSGeometry, uOpenFlags,
1309 pfnProgress, pvUser, 0, 99,
1310 pDisk->pfnError, pDisk->pvErrorUser,
1311 &pImage->pvBackendData);
1312
1313 if (VBOX_SUCCESS(rc))
1314 {
1315 /** @todo optionally check UUIDs */
1316 }
1317
1318 if (VBOX_SUCCESS(rc))
1319 {
1320 /* Image successfully opened, make it the last image. */
1321 vdAddImageToList(pDisk, pImage);
1322 }
1323 else
1324 {
1325 /* Error detected, but image opened. Close and delete image. */
1326 int rc2;
1327 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
1328 AssertRC(rc2);
1329 pImage->pvBackendData = NULL;
1330 }
1331 } while (0);
1332
1333 if (VBOX_FAILURE(rc))
1334 {
1335 if (pImage->hPlugin != NIL_RTLDRMOD)
1336 RTLdrClose(pImage->hPlugin);
1337
1338 if (pImage)
1339 {
1340 if (pImage->pszFilename)
1341 RTStrFree(pImage->pszFilename);
1342 RTMemFree(pImage);
1343 }
1344 }
1345
1346 if (VBOX_SUCCESS(rc) && pfnProgress)
1347 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1348
1349 LogFlowFunc(("returns %Vrc\n", rc));
1350 return rc;
1351}
1352
1353/**
1354 * Merges two images (not necessarily with direct parent/child relationship).
1355 * As a side effect the source image and potentially the other images which
1356 * are also merged to the destination are deleted from both the disk and the
1357 * images in the HDD container.
1358 *
1359 * @returns VBox status code.
1360 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1361 * @param pDisk Pointer to HDD container.
1362 * @param nImageFrom Name of the image file to merge from.
1363 * @param nImageTo Name of the image file to merge to.
1364 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1365 * @param pvUser User argument for the progress callback.
1366 */
1367VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
1368 unsigned nImageTo, PFNVMPROGRESS pfnProgress,
1369 void *pvUser)
1370{
1371 int rc = VINF_SUCCESS;
1372 void *pvBuf = NULL;
1373
1374 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pfnProgress=%#p pvUser=%#p\n",
1375 pDisk, nImageFrom, nImageTo, pfnProgress, pvUser));
1376 do
1377 {
1378 /* sanity check */
1379 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1380 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1381
1382 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
1383 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
1384 if (!pImageFrom || !pImageTo)
1385 {
1386 rc = VERR_VDI_IMAGE_NOT_FOUND;
1387 break;
1388 }
1389 AssertBreak(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
1390
1391 /* Check if destination image is writable. */
1392 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
1393 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
1394 {
1395 rc = VERR_VDI_IMAGE_READ_ONLY;
1396 break;
1397 }
1398
1399 /* Get size of destination image. */
1400 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pvBackendData);
1401
1402 /* Allocate tmp buffer. */
1403 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
1404 if (!pvBuf)
1405 {
1406 rc = VERR_NO_MEMORY;
1407 break;
1408 }
1409
1410 /* Merging is done directly on the images itself. This potentially
1411 * causes trouble if the disk is full in the middle of operation. */
1412 /** @todo write alternative implementation which works with temporary
1413 * images (which is safer, but requires even more space). Also has the
1414 * drawback that merging into a raw disk parent simply isn't possible
1415 * this way (but in that case disk full isn't really a problem). */
1416 if (nImageFrom < nImageTo)
1417 {
1418 /* Merge parent state into child. This means writing all not
1419 * allocated blocks in the destination image which are allocated in
1420 * the images to be merged. */
1421 uint64_t uOffset = 0;
1422 uint64_t cbRemaining = cbSize;
1423 do
1424 {
1425 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1426 rc = pImageTo->Backend->pfnRead(pImageTo->pvBackendData,
1427 uOffset, pvBuf, cbThisRead,
1428 &cbThisRead);
1429 if (VBOX_FAILURE(rc))
1430 break;
1431 if (rc == VERR_VDI_BLOCK_FREE)
1432 {
1433 /* Search for image with allocated block. Do not attempt to
1434 * read more than the previous reads marked as valid.
1435 * Otherwise this would return stale data when different
1436 * block sizes are used for the images. */
1437 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
1438 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VDI_BLOCK_FREE;
1439 pCurrImage = pCurrImage->pPrev)
1440 {
1441 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
1442 uOffset, pvBuf,
1443 cbThisRead,
1444 &cbThisRead);
1445 }
1446 if (VBOX_FAILURE(rc))
1447 break;
1448
1449 if (rc != VERR_VDI_BLOCK_FREE)
1450 {
1451 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1452 cbThisRead);
1453 if (VBOX_FAILURE(rc))
1454 break;
1455 }
1456 }
1457
1458 uOffset += cbThisRead;
1459 cbRemaining -= cbThisRead;
1460 } while (uOffset < cbSize);
1461 }
1462 else
1463 {
1464 /* Merge child state into parent. This means writing all blocks
1465 * which are allocated in the image up to the source image to the
1466 * destination image. */
1467 uint64_t uOffset = 0;
1468 uint64_t cbRemaining = cbSize;
1469 do
1470 {
1471 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1472 /* Search for image with allocated block. Do not attempt to
1473 * read more than the previous reads marked as valid. Otherwise
1474 * this would return stale data when different block sizes are
1475 * used for the images. */
1476 for (PVDIMAGE pCurrImage = pImageFrom;
1477 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VDI_BLOCK_FREE;
1478 pCurrImage = pCurrImage->pPrev)
1479 {
1480 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
1481 uOffset, pvBuf,
1482 cbThisRead, &cbThisRead);
1483 }
1484 if (VBOX_FAILURE(rc))
1485 break;
1486
1487 if (rc != VERR_VDI_BLOCK_FREE)
1488 {
1489 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1490 cbThisRead);
1491 if (VBOX_FAILURE(rc))
1492 break;
1493 }
1494
1495 uOffset += cbThisRead;
1496 cbRemaining -= cbThisRead;
1497 } while (uOffset < cbSize);
1498 }
1499
1500 /* Update parent UUID so that image chain is consistent. */
1501 RTUUID Uuid;
1502 if (nImageFrom < nImageTo)
1503 {
1504 if (pImageTo->pPrev)
1505 {
1506 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pPrev->pvBackendData,
1507 &Uuid);
1508 AssertRC(rc);
1509 }
1510 else
1511 RTUuidClear(&Uuid);
1512 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pvBackendData,
1513 &Uuid);
1514 AssertRC(rc);
1515 }
1516 else
1517 {
1518 if (pImageFrom->pNext)
1519 {
1520 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pvBackendData,
1521 &Uuid);
1522 AssertRC(rc);
1523 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext,
1524 &Uuid);
1525 AssertRC(rc);
1526 }
1527 }
1528
1529 /* Delete the no longer needed images. */
1530 PVDIMAGE pImg = pImageFrom, pTmp;
1531 while (pImg != pImageTo)
1532 {
1533 if (nImageFrom < nImageTo)
1534 pTmp = pImg->pNext;
1535 else
1536 pTmp = pImg->pPrev;
1537 vdRemoveImageFromList(pDisk, pImg);
1538 pImg->Backend->pfnClose(pImg->pvBackendData, true);
1539 pImg = pTmp;
1540 }
1541 } while (0);
1542
1543 if (pvBuf)
1544 RTMemTmpFree(pvBuf);
1545
1546 if (VBOX_SUCCESS(rc) && pfnProgress)
1547 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1548
1549 LogFlowFunc(("returns %Vrc\n", rc));
1550 return rc;
1551}
1552
1553/**
1554 * Copies an image from one HDD container to another.
1555 * The copy is opened in the target HDD container.
1556 * It is possible to convert between different image formats, because the
1557 * backend for the destination may be different from the source.
1558 * If both the source and destination reference the same HDD container,
1559 * then the image is moved (by copying/deleting or renaming) to the new location.
1560 * The source container is unchanged if the move operation fails, otherwise
1561 * the image at the new location is opened in the same way as the old one was.
1562 *
1563 * @returns VBox status code.
1564 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1565 * @param pDiskFrom Pointer to source HDD container.
1566 * @param nImage Image number, counts from 0. 0 is always base image of container.
1567 * @param pDiskTo Pointer to destination HDD container.
1568 * @param pszBackend Name of the image file backend to use.
1569 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
1570 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
1571 * @param cbSize New image size (0 means leave unchanged).
1572 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1573 * @param pvUser User argument for the progress callback.
1574 */
1575VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
1576 const char *pszBackend, const char *pszFilename,
1577 bool fMoveByRename, uint64_t cbSize,
1578 PFNVMPROGRESS pfnProgress, void *pvUser)
1579{
1580 return VERR_NOT_IMPLEMENTED;
1581}
1582
1583/**
1584 * Closes the last opened image file in HDD container.
1585 * If previous image file was opened in read-only mode (that is normal) and closing image
1586 * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
1587 * will be reopened in read/write mode.
1588 *
1589 * @returns VBox status code.
1590 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1591 * @param pDisk Pointer to HDD container.
1592 * @param fDelete If true, delete the image from the host disk.
1593 */
1594VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
1595{
1596 int rc = VINF_SUCCESS;;
1597
1598 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
1599 do
1600 {
1601 /* sanity check */
1602 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1603 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1604
1605 PVDIMAGE pImage = pDisk->pLast;
1606 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_NOT_OPENED);
1607 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
1608 /* Remove image from list of opened images. */
1609 vdRemoveImageFromList(pDisk, pImage);
1610 /* Close (and optionally delete) image. */
1611 rc = pImage->Backend->pfnClose(pImage->pvBackendData, fDelete);
1612 /* Free remaining resources related to the image. */
1613 if (pImage->hPlugin != NIL_RTLDRMOD)
1614 {
1615 RTLdrClose(pImage->hPlugin);
1616 pImage->hPlugin = NIL_RTLDRMOD;
1617 }
1618 RTStrFree(pImage->pszFilename);
1619 RTMemFree(pImage);
1620
1621 pImage = pDisk->pLast;
1622 if (!pImage)
1623 break;
1624
1625 /* If disk was previously in read/write mode, make sure it will stay
1626 * like this (if possible) after closing this image. Set the open flags
1627 * accordingly. */
1628 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1629 {
1630 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
1631 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
1632 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData, uOpenFlags);
1633 }
1634
1635 int rc2;
1636
1637 /* Cache disk information. */
1638 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
1639
1640 /* Cache PCHS geometry. */
1641 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1642 &pDisk->PCHSGeometry);
1643 if (VBOX_FAILURE(rc2))
1644 {
1645 pDisk->PCHSGeometry.cCylinders = 0;
1646 pDisk->PCHSGeometry.cHeads = 0;
1647 pDisk->PCHSGeometry.cSectors = 0;
1648 }
1649 else
1650 {
1651 /* Make sure the PCHS geometry is properly clipped. */
1652 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1653 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1654 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1655 }
1656
1657 /* Cache LCHS geometry. */
1658 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1659 &pDisk->LCHSGeometry);
1660 if (VBOX_FAILURE(rc2))
1661 {
1662 pDisk->LCHSGeometry.cCylinders = 0;
1663 pDisk->LCHSGeometry.cHeads = 0;
1664 pDisk->LCHSGeometry.cSectors = 0;
1665 }
1666 else
1667 {
1668 /* Make sure the LCHS geometry is properly clipped. */
1669 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
1670 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1671 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1672 }
1673 } while (0);
1674
1675 LogFlowFunc(("returns %Vrc\n", rc));
1676 return rc;
1677}
1678
1679/**
1680 * Closes all opened image files in HDD container.
1681 *
1682 * @returns VBox status code.
1683 * @param pDisk Pointer to HDD container.
1684 */
1685VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
1686{
1687 int rc = VINF_SUCCESS;
1688
1689 LogFlowFunc(("pDisk=%#p\n", pDisk));
1690 do
1691 {
1692 /* sanity check */
1693 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1694 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1695
1696 PVDIMAGE pImage = pDisk->pLast;
1697 while (VALID_PTR(pImage))
1698 {
1699 PVDIMAGE pPrev = pImage->pPrev;
1700 /* Remove image from list of opened images. */
1701 vdRemoveImageFromList(pDisk, pImage);
1702 /* Close image. */
1703 int rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, false);
1704 if (VBOX_FAILURE(rc2) && VBOX_SUCCESS(rc))
1705 rc = rc2;
1706 /* Free remaining resources related to the image. */
1707 if (pImage->hPlugin != NIL_RTLDRMOD)
1708 {
1709 RTLdrClose(pImage->hPlugin);
1710 pImage->hPlugin = NIL_RTLDRMOD;
1711 }
1712 RTStrFree(pImage->pszFilename);
1713 RTMemFree(pImage);
1714 pImage = pPrev;
1715 }
1716 Assert(!VALID_PTR(pDisk->pLast));
1717 } while (0);
1718
1719 LogFlowFunc(("returns %Vrc\n", rc));
1720 return rc;
1721}
1722
1723/**
1724 * Read data from virtual HDD.
1725 *
1726 * @returns VBox status code.
1727 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1728 * @param pDisk Pointer to HDD container.
1729 * @param uOffset Offset of first reading byte from start of disk.
1730 * @param pvBuf Pointer to buffer for reading data.
1731 * @param cbRead Number of bytes to read.
1732 */
1733VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
1734 size_t cbRead)
1735{
1736 int rc;
1737
1738 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
1739 pDisk, uOffset, pvBuf, cbRead));
1740 do
1741 {
1742 /* sanity check */
1743 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1744 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1745
1746 /* Check arguments. */
1747 AssertMsgBreak(VALID_PTR(pvBuf),
1748 ("pvBuf=%#p\n", pvBuf),
1749 rc = VERR_INVALID_PARAMETER);
1750 AssertMsgBreak(cbRead,
1751 ("cbRead=%zu\n", cbRead),
1752 rc = VERR_INVALID_PARAMETER);
1753 AssertMsgBreak(uOffset + cbRead <= pDisk->cbSize,
1754 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
1755 uOffset, cbRead, pDisk->cbSize),
1756 rc = VERR_INVALID_PARAMETER);
1757
1758 PVDIMAGE pImage = pDisk->pLast;
1759 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_NOT_OPENED);
1760
1761 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead);
1762 } while (0);
1763
1764 LogFlowFunc(("returns %Vrc\n", rc));
1765 return rc;
1766}
1767
1768/**
1769 * Write data to virtual HDD.
1770 *
1771 * @returns VBox status code.
1772 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1773 * @param pDisk Pointer to HDD container.
1774 * @param uOffset Offset of first reading byte from start of disk.
1775 * @param pvBuf Pointer to buffer for writing data.
1776 * @param cbWrite Number of bytes to write.
1777 */
1778VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
1779 size_t cbWrite)
1780{
1781 int rc = VINF_SUCCESS;
1782
1783 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
1784 pDisk, uOffset, pvBuf, cbWrite));
1785 do
1786 {
1787 /* sanity check */
1788 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1789 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1790
1791 /* Check arguments. */
1792 AssertMsgBreak(VALID_PTR(pvBuf),
1793 ("pvBuf=%#p\n", pvBuf),
1794 rc = VERR_INVALID_PARAMETER);
1795 AssertMsgBreak(cbWrite,
1796 ("cbWrite=%zu\n", cbWrite),
1797 rc = VERR_INVALID_PARAMETER);
1798 AssertMsgBreak(uOffset + cbWrite <= pDisk->cbSize,
1799 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
1800 uOffset, cbWrite, pDisk->cbSize),
1801 rc = VERR_INVALID_PARAMETER);
1802
1803 PVDIMAGE pImage = pDisk->pLast;
1804 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_NOT_OPENED);
1805
1806 vdSetModifiedFlag(pDisk);
1807 rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite);
1808 } while (0);
1809
1810 LogFlowFunc(("returns %Vrc\n", rc));
1811 return rc;
1812}
1813
1814/**
1815 * Make sure the on disk representation of a virtual HDD is up to date.
1816 *
1817 * @returns VBox status code.
1818 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1819 * @param pDisk Pointer to HDD container.
1820 */
1821VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
1822{
1823 int rc = VINF_SUCCESS;
1824
1825 LogFlowFunc(("pDisk=%#p\n", pDisk));
1826 do
1827 {
1828 /* sanity check */
1829 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1830 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1831
1832 PVDIMAGE pImage = pDisk->pLast;
1833 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_NOT_OPENED);
1834
1835 vdResetModifiedFlag(pDisk);
1836 rc = pImage->Backend->pfnFlush(pImage->pvBackendData);
1837 } while (0);
1838
1839 LogFlowFunc(("returns %Vrc\n", rc));
1840 return rc;
1841}
1842
1843/**
1844 * Get number of opened images in HDD container.
1845 *
1846 * @returns Number of opened images for HDD container. 0 if no images have been opened.
1847 * @param pDisk Pointer to HDD container.
1848 */
1849VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
1850{
1851 unsigned cImages;
1852
1853 LogFlowFunc(("pDisk=%#p\n", pDisk));
1854 do
1855 {
1856 /* sanity check */
1857 AssertBreak(VALID_PTR(pDisk), cImages = 0);
1858 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1859
1860 cImages = pDisk->cImages;
1861 } while (0);
1862
1863 LogFlowFunc(("returns %u\n", cImages));
1864 return cImages;
1865}
1866
1867/**
1868 * Get read/write mode of HDD container.
1869 *
1870 * @returns Virtual disk ReadOnly status.
1871 * @returns true if no image is opened in HDD container.
1872 * @param pDisk Pointer to HDD container.
1873 */
1874VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
1875{
1876 bool fReadOnly;
1877
1878 LogFlowFunc(("pDisk=%#p\n", pDisk));
1879 do
1880 {
1881 /* sanity check */
1882 AssertBreak(VALID_PTR(pDisk), fReadOnly = false);
1883 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1884
1885 PVDIMAGE pImage = pDisk->pLast;
1886 AssertBreak(VALID_PTR(pImage), fReadOnly = true);
1887
1888 unsigned uOpenFlags;
1889 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1890 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
1891 } while (0);
1892
1893 LogFlowFunc(("returns %d\n", fReadOnly));
1894 return fReadOnly;
1895}
1896
1897/**
1898 * Get total capacity of an image in HDD container.
1899 *
1900 * @returns Virtual disk size in bytes.
1901 * @returns 0 if no image with specified number was not opened.
1902 * @param pDisk Pointer to HDD container.
1903 * @param nImage Image number, counds from 0. 0 is always base image of container.
1904 */
1905VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
1906{
1907 uint64_t cbSize;
1908
1909 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
1910 do
1911 {
1912 /* sanity check */
1913 AssertBreak(VALID_PTR(pDisk), cbSize = 0);
1914 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1915
1916 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1917 AssertBreak(VALID_PTR(pImage), cbSize = 0);
1918 cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
1919 } while (0);
1920
1921 LogFlowFunc(("returns %llu\n", cbSize));
1922 return cbSize;
1923}
1924
1925/**
1926 * Get total file size of an image in HDD container.
1927 *
1928 * @returns Virtual disk size in bytes.
1929 * @returns 0 if no image is opened in HDD container.
1930 * @param pDisk Pointer to HDD container.
1931 * @param nImage Image number, counts from 0. 0 is always base image of container.
1932 */
1933VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
1934{
1935 uint64_t cbSize;
1936
1937 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
1938 do
1939 {
1940 /* sanity check */
1941 AssertBreak(VALID_PTR(pDisk), cbSize = 0);
1942 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1943
1944 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1945 AssertBreak(VALID_PTR(pImage), cbSize = 0);
1946 cbSize = pImage->Backend->pfnGetFileSize(pImage->pvBackendData);
1947 } while (0);
1948
1949 LogFlowFunc(("returns %llu\n", cbSize));
1950 return cbSize;
1951}
1952
1953/**
1954 * Get virtual disk PCHS geometry stored in HDD container.
1955 *
1956 * @returns VBox status code.
1957 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1958 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
1959 * @param pDisk Pointer to HDD container.
1960 * @param nImage Image number, counts from 0. 0 is always base image of container.
1961 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
1962 */
1963VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
1964 PPDMMEDIAGEOMETRY pPCHSGeometry)
1965{
1966 int rc = VINF_SUCCESS;
1967
1968 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
1969 pDisk, nImage, pPCHSGeometry));
1970 do
1971 {
1972 /* sanity check */
1973 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1974 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1975
1976 /* Check arguments. */
1977 AssertMsgBreak(VALID_PTR(pPCHSGeometry),
1978 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
1979 rc = VERR_INVALID_PARAMETER);
1980
1981 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1982 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
1983
1984 if (pImage == pDisk->pLast)
1985 {
1986 /* Use cached information if possible. */
1987 if (pDisk->PCHSGeometry.cCylinders != 0)
1988 *pPCHSGeometry = pDisk->PCHSGeometry;
1989 else
1990 rc = VERR_VDI_GEOMETRY_NOT_SET;
1991 }
1992 else
1993 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1994 pPCHSGeometry);
1995 } while (0);
1996
1997 LogFlowFunc(("%s: %Vrc (PCHS=%u/%u/%u)\n", rc,
1998 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
1999 pDisk->PCHSGeometry.cSectors));
2000 return rc;
2001}
2002
2003/**
2004 * Store virtual disk PCHS geometry in HDD container.
2005 *
2006 * Note that in case of unrecoverable error all images in HDD container will be closed.
2007 *
2008 * @returns VBox status code.
2009 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2010 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2011 * @param pDisk Pointer to HDD container.
2012 * @param nImage Image number, counts from 0. 0 is always base image of container.
2013 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
2014 */
2015VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2016 PCPDMMEDIAGEOMETRY pPCHSGeometry)
2017{
2018 int rc = VINF_SUCCESS;
2019
2020 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
2021 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
2022 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
2023 do
2024 {
2025 /* sanity check */
2026 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2027 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2028
2029 /* Check arguments. */
2030 AssertMsgBreak( VALID_PTR(pPCHSGeometry)
2031 && pPCHSGeometry->cCylinders <= 16383
2032 && pPCHSGeometry->cCylinders != 0
2033 && pPCHSGeometry->cHeads <= 16
2034 && pPCHSGeometry->cHeads != 0
2035 && pPCHSGeometry->cSectors <= 63
2036 && pPCHSGeometry->cSectors != 0,
2037 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
2038 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
2039 pPCHSGeometry->cSectors),
2040 rc = VERR_INVALID_PARAMETER);
2041
2042 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2043 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2044
2045 if (pImage == pDisk->pLast)
2046 {
2047 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
2048 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
2049 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
2050 {
2051 /* Only update geometry if it is changed. Avoids similar checks
2052 * in every backend. Most of the time the new geometry is set
2053 * to the previous values, so no need to go through the hassle
2054 * of updating an image which could be opened in read-only mode
2055 * right now. */
2056 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2057 pPCHSGeometry);
2058
2059 /* Cache new geometry values in any case. */
2060 int rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2061 &pDisk->PCHSGeometry);
2062 if (VBOX_FAILURE(rc2))
2063 {
2064 pDisk->PCHSGeometry.cCylinders = 0;
2065 pDisk->PCHSGeometry.cHeads = 0;
2066 pDisk->PCHSGeometry.cSectors = 0;
2067 }
2068 else
2069 {
2070 /* Make sure the CHS geometry is properly clipped. */
2071 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 1024);
2072 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
2073 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
2074 }
2075 }
2076 }
2077 else
2078 {
2079 PDMMEDIAGEOMETRY PCHS;
2080 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2081 &PCHS);
2082 if ( VBOX_FAILURE(rc)
2083 || pPCHSGeometry->cCylinders != PCHS.cCylinders
2084 || pPCHSGeometry->cHeads != PCHS.cHeads
2085 || pPCHSGeometry->cSectors != PCHS.cSectors)
2086 {
2087 /* Only update geometry if it is changed. Avoids similar checks
2088 * in every backend. Most of the time the new geometry is set
2089 * to the previous values, so no need to go through the hassle
2090 * of updating an image which could be opened in read-only mode
2091 * right now. */
2092 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2093 pPCHSGeometry);
2094 }
2095 }
2096 } while (0);
2097
2098 LogFlowFunc(("returns %Vrc\n", rc));
2099 return rc;
2100}
2101
2102/**
2103 * Get virtual disk LCHS geometry stored in HDD container.
2104 *
2105 * @returns VBox status code.
2106 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2107 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2108 * @param pDisk Pointer to HDD container.
2109 * @param nImage Image number, counts from 0. 0 is always base image of container.
2110 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
2111 */
2112VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2113 PPDMMEDIAGEOMETRY pLCHSGeometry)
2114{
2115 int rc = VINF_SUCCESS;
2116
2117 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
2118 pDisk, nImage, pLCHSGeometry));
2119 do
2120 {
2121 /* sanity check */
2122 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2123 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2124
2125 /* Check arguments. */
2126 AssertMsgBreak(VALID_PTR(pLCHSGeometry),
2127 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
2128 rc = VERR_INVALID_PARAMETER);
2129
2130 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2131 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2132
2133 if (pImage == pDisk->pLast)
2134 {
2135 /* Use cached information if possible. */
2136 if (pDisk->LCHSGeometry.cCylinders != 0)
2137 *pLCHSGeometry = pDisk->LCHSGeometry;
2138 else
2139 rc = VERR_VDI_GEOMETRY_NOT_SET;
2140 }
2141 else
2142 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2143 pLCHSGeometry);
2144 } while (0);
2145
2146 LogFlowFunc(("%s: %Vrc (LCHS=%u/%u/%u)\n", rc,
2147 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
2148 pDisk->LCHSGeometry.cSectors));
2149 return rc;
2150}
2151
2152/**
2153 * Store virtual disk LCHS geometry in HDD container.
2154 *
2155 * Note that in case of unrecoverable error all images in HDD container will be closed.
2156 *
2157 * @returns VBox status code.
2158 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2159 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2160 * @param pDisk Pointer to HDD container.
2161 * @param nImage Image number, counts from 0. 0 is always base image of container.
2162 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
2163 */
2164VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2165 PCPDMMEDIAGEOMETRY pLCHSGeometry)
2166{
2167 int rc = VINF_SUCCESS;
2168
2169 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
2170 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
2171 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2172 do
2173 {
2174 /* sanity check */
2175 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2176 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2177
2178 /* Check arguments. */
2179 AssertMsgBreak( VALID_PTR(pLCHSGeometry)
2180 && pLCHSGeometry->cCylinders <= 1024
2181 && pLCHSGeometry->cHeads <= 255
2182 && pLCHSGeometry->cSectors <= 63,
2183 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
2184 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
2185 pLCHSGeometry->cSectors),
2186 rc = VERR_INVALID_PARAMETER);
2187
2188 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2189 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2190
2191 if (pImage == pDisk->pLast)
2192 {
2193 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
2194 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
2195 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
2196 {
2197 /* Only update geometry if it is changed. Avoids similar checks
2198 * in every backend. Most of the time the new geometry is set
2199 * to the previous values, so no need to go through the hassle
2200 * of updating an image which could be opened in read-only mode
2201 * right now. */
2202 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2203 pLCHSGeometry);
2204
2205 /* Cache new geometry values in any case. */
2206 int rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2207 &pDisk->LCHSGeometry);
2208 if (VBOX_FAILURE(rc2))
2209 {
2210 pDisk->LCHSGeometry.cCylinders = 0;
2211 pDisk->LCHSGeometry.cHeads = 0;
2212 pDisk->LCHSGeometry.cSectors = 0;
2213 }
2214 else
2215 {
2216 /* Make sure the CHS geometry is properly clipped. */
2217 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
2218 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
2219 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
2220 }
2221 }
2222 }
2223 else
2224 {
2225 PDMMEDIAGEOMETRY LCHS;
2226 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2227 &LCHS);
2228 if ( VBOX_FAILURE(rc)
2229 || pLCHSGeometry->cCylinders != LCHS.cCylinders
2230 || pLCHSGeometry->cHeads != LCHS.cHeads
2231 || pLCHSGeometry->cSectors != LCHS.cSectors)
2232 {
2233 /* Only update geometry if it is changed. Avoids similar checks
2234 * in every backend. Most of the time the new geometry is set
2235 * to the previous values, so no need to go through the hassle
2236 * of updating an image which could be opened in read-only mode
2237 * right now. */
2238 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2239 pLCHSGeometry);
2240 }
2241 }
2242 } while (0);
2243
2244 LogFlowFunc(("returns %Vrc\n", rc));
2245 return rc;
2246}
2247
2248/**
2249 * Get version of image in HDD container.
2250 *
2251 * @returns VBox status code.
2252 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2253 * @param pDisk Pointer to HDD container.
2254 * @param nImage Image number, counts from 0. 0 is always base image of container.
2255 * @param puVersion Where to store the image version.
2256 */
2257VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
2258 unsigned *puVersion)
2259{
2260 int rc = VINF_SUCCESS;
2261
2262 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
2263 pDisk, nImage, puVersion));
2264 do
2265 {
2266 /* sanity check */
2267 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2268 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2269
2270 /* Check arguments. */
2271 AssertMsgBreak(VALID_PTR(puVersion),
2272 ("puVersion=%#p\n", puVersion),
2273 rc = VERR_INVALID_PARAMETER);
2274
2275 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2276 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2277
2278 *puVersion = pImage->Backend->pfnGetVersion(pImage->pvBackendData);
2279 } while (0);
2280
2281 LogFlowFunc(("returns %Vrc uVersion=%#x\n", rc, *puVersion));
2282 return rc;
2283}
2284
2285/**
2286 * Get type of image in HDD container.
2287 *
2288 * @returns VBox status code.
2289 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2290 * @param pDisk Pointer to HDD container.
2291 * @param nImage Image number, counts from 0. 0 is always base image of container.
2292 * @param penmType Where to store the image type.
2293 */
2294VBOXDDU_DECL(int) VDGetImageType(PVBOXHDD pDisk, unsigned nImage,
2295 PVDIMAGETYPE penmType)
2296{
2297 int rc;
2298
2299 LogFlowFunc(("pDisk=%#p nImage=%u penmType=%#p\n",
2300 pDisk, nImage, penmType));
2301 do
2302 {
2303 /* sanity check */
2304 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2305 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2306
2307 /* Check arguments. */
2308 AssertMsgBreak(VALID_PTR(penmType),
2309 ("penmType=%#p\n", penmType),
2310 rc = VERR_INVALID_PARAMETER);
2311
2312 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2313 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2314
2315 rc = pImage->Backend->pfnGetImageType(pImage->pvBackendData,
2316 penmType);
2317 } while (0);
2318
2319 LogFlowFunc(("returns %Vrc uenmType=%u\n", rc, *penmType));
2320 return rc;
2321}
2322
2323/**
2324 * Get flags of image in HDD container.
2325 *
2326 * @returns VBox status code.
2327 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2328 * @param pDisk Pointer to HDD container.
2329 * @param nImage Image number, counts from 0. 0 is always base image of container.
2330 * @param puImageFlags Where to store the image flags.
2331 */
2332VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
2333 unsigned *puImageFlags)
2334{
2335 int rc = VINF_SUCCESS;
2336
2337 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
2338 pDisk, nImage, puImageFlags));
2339 do
2340 {
2341 /* sanity check */
2342 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2343 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2344
2345 /* Check arguments. */
2346 AssertMsgBreak(VALID_PTR(puImageFlags),
2347 ("puImageFlags=%#p\n", puImageFlags),
2348 rc = VERR_INVALID_PARAMETER);
2349
2350 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2351 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2352
2353 *puImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pvBackendData);
2354 } while (0);
2355
2356 LogFlowFunc(("returns %Vrc uImageFlags=%#x\n", rc, *puImageFlags));
2357 return rc;
2358}
2359
2360/**
2361 * Get open flags of image in HDD container.
2362 *
2363 * @returns VBox status code.
2364 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2365 * @param pDisk Pointer to HDD container.
2366 * @param nImage Image number, counts from 0. 0 is always base image of container.
2367 * @param puOpenFlags Where to store the image open flags.
2368 */
2369VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2370 unsigned *puOpenFlags)
2371{
2372 int rc = VINF_SUCCESS;
2373
2374 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
2375 pDisk, nImage, puOpenFlags));
2376 do
2377 {
2378 /* sanity check */
2379 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2380 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2381
2382 /* Check arguments. */
2383 AssertMsgBreak(VALID_PTR(puOpenFlags),
2384 ("puOpenFlags=%#p\n", puOpenFlags),
2385 rc = VERR_INVALID_PARAMETER);
2386
2387 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2388 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2389
2390 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2391 } while (0);
2392
2393 LogFlowFunc(("returns %Vrc uOpenFlags=%#x\n", rc, *puOpenFlags));
2394 return rc;
2395}
2396
2397/**
2398 * Set open flags of image in HDD container.
2399 * This operation may cause file locking changes and/or files being reopened.
2400 * Note that in case of unrecoverable error all images in HDD container will be closed.
2401 *
2402 * @returns VBox status code.
2403 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2404 * @param pDisk Pointer to HDD container.
2405 * @param nImage Image number, counts from 0. 0 is always base image of container.
2406 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
2407 */
2408VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2409 unsigned uOpenFlags)
2410{
2411 int rc;
2412
2413 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
2414 do
2415 {
2416 /* sanity check */
2417 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2418 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2419
2420 /* Check arguments. */
2421 AssertMsgBreak((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
2422 ("uOpenFlags=%#x\n", uOpenFlags),
2423 rc = VERR_INVALID_PARAMETER);
2424
2425 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2426 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2427
2428 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData,
2429 uOpenFlags);
2430 } while (0);
2431
2432 LogFlowFunc(("returns %Vrc\n", rc));
2433 return rc;
2434}
2435
2436/**
2437 * Get base filename of image in HDD container. Some image formats use
2438 * other filenames as well, so don't use this for anything but informational
2439 * purposes.
2440 *
2441 * @returns VBox status code.
2442 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2443 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
2444 * @param pDisk Pointer to HDD container.
2445 * @param nImage Image number, counts from 0. 0 is always base image of container.
2446 * @param pszFilename Where to store the image file name.
2447 * @param cbFilename Size of buffer pszFilename points to.
2448 */
2449VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
2450 char *pszFilename, unsigned cbFilename)
2451{
2452 int rc;
2453
2454 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
2455 pDisk, nImage, pszFilename, cbFilename));
2456 do
2457 {
2458 /* sanity check */
2459 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2460 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2461
2462 /* Check arguments. */
2463 AssertMsgBreak(VALID_PTR(pszFilename) && *pszFilename,
2464 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
2465 rc = VERR_INVALID_PARAMETER);
2466 AssertMsgBreak(cbFilename,
2467 ("cbFilename=%u\n", cbFilename),
2468 rc = VERR_INVALID_PARAMETER);
2469
2470 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2471 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2472
2473 size_t cb = strlen(pImage->pszFilename);
2474 if (cb <= cbFilename)
2475 {
2476 strcpy(pszFilename, pImage->pszFilename);
2477 rc = VINF_SUCCESS;
2478 }
2479 else
2480 {
2481 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
2482 pszFilename[cbFilename - 1] = '\0';
2483 rc = VERR_BUFFER_OVERFLOW;
2484 }
2485 } while (0);
2486
2487 LogFlowFunc(("returns %Vrc, pszFilename=\"%s\"\n", rc, pszFilename));
2488 return rc;
2489}
2490
2491/**
2492 * Get the comment line of image in HDD container.
2493 *
2494 * @returns VBox status code.
2495 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2496 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
2497 * @param pDisk Pointer to HDD container.
2498 * @param nImage Image number, counts from 0. 0 is always base image of container.
2499 * @param pszComment Where to store the comment string of image. NULL is ok.
2500 * @param cbComment The size of pszComment buffer. 0 is ok.
2501 */
2502VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
2503 char *pszComment, unsigned cbComment)
2504{
2505 int rc;
2506
2507 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
2508 pDisk, nImage, pszComment, cbComment));
2509 do
2510 {
2511 /* sanity check */
2512 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2513 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2514
2515 /* Check arguments. */
2516 AssertMsgBreak(VALID_PTR(pszComment),
2517 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
2518 rc = VERR_INVALID_PARAMETER);
2519 AssertMsgBreak(cbComment,
2520 ("cbComment=%u\n", cbComment),
2521 rc = VERR_INVALID_PARAMETER);
2522
2523 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2524 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2525
2526 rc = pImage->Backend->pfnGetComment(pImage->pvBackendData, pszComment,
2527 cbComment);
2528 } while (0);
2529
2530 LogFlowFunc(("returns %Vrc, pszComment=\"%s\"\n", rc, pszComment));
2531 return rc;
2532}
2533
2534/**
2535 * Changes the comment line of image in HDD container.
2536 *
2537 * @returns VBox status code.
2538 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2539 * @param pDisk Pointer to HDD container.
2540 * @param nImage Image number, counts from 0. 0 is always base image of container.
2541 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
2542 */
2543VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
2544 const char *pszComment)
2545{
2546 int rc;
2547
2548 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
2549 pDisk, nImage, pszComment, pszComment));
2550 do
2551 {
2552 /* sanity check */
2553 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2554 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2555
2556 /* Check arguments. */
2557 AssertMsgBreak(VALID_PTR(pszComment) || pszComment == NULL,
2558 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
2559 rc = VERR_INVALID_PARAMETER);
2560
2561 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2562 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2563
2564 rc = pImage->Backend->pfnSetComment(pImage->pvBackendData, pszComment);
2565 } while (0);
2566
2567 LogFlowFunc(("returns %Vrc\n", rc));
2568 return rc;
2569}
2570
2571
2572/**
2573 * Get UUID of image in HDD container.
2574 *
2575 * @returns VBox status code.
2576 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2577 * @param pDisk Pointer to HDD container.
2578 * @param nImage Image number, counts from 0. 0 is always base image of container.
2579 * @param pUuid Where to store the image creation UUID.
2580 */
2581VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
2582{
2583 int rc;
2584
2585 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
2586 do
2587 {
2588 /* sanity check */
2589 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2590 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2591
2592 /* Check arguments. */
2593 AssertMsgBreak(VALID_PTR(pUuid),
2594 ("pUuid=%#p\n", pUuid),
2595 rc = VERR_INVALID_PARAMETER);
2596
2597 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2598 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2599
2600 rc = pImage->Backend->pfnGetUuid(pImage->pvBackendData, pUuid);
2601 } while (0);
2602
2603 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
2604 return rc;
2605}
2606
2607/**
2608 * Set the image's UUID. Should not be used by normal applications.
2609 *
2610 * @returns VBox status code.
2611 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2612 * @param pDisk Pointer to HDD container.
2613 * @param nImage Image number, counts from 0. 0 is always base image of container.
2614 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
2615 */
2616VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
2617{
2618 int rc;
2619
2620 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
2621 pDisk, nImage, pUuid, pUuid));
2622 do
2623 {
2624 /* sanity check */
2625 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2626 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2627
2628 AssertMsgBreak(VALID_PTR(pUuid) || pUuid == NULL,
2629 ("pUuid=%#p\n", pUuid),
2630 rc = VERR_INVALID_PARAMETER);
2631
2632 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2633 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2634
2635 RTUUID Uuid;
2636 if (!pUuid)
2637 {
2638 RTUuidCreate(&Uuid);
2639 pUuid = &Uuid;
2640 }
2641 rc = pImage->Backend->pfnSetUuid(pImage->pvBackendData, pUuid);
2642 } while (0);
2643
2644 LogFlowFunc(("returns %Vrc\n", rc));
2645 return rc;
2646}
2647
2648/**
2649 * Get last modification UUID of image in HDD container.
2650 *
2651 * @returns VBox status code.
2652 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2653 * @param pDisk Pointer to HDD container.
2654 * @param nImage Image number, counts from 0. 0 is always base image of container.
2655 * @param pUuid Where to store the image modification UUID.
2656 */
2657VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
2658{
2659 int rc = VINF_SUCCESS;
2660
2661 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
2662 do
2663 {
2664 /* sanity check */
2665 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2666 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2667
2668 /* Check arguments. */
2669 AssertMsgBreak(VALID_PTR(pUuid),
2670 ("pUuid=%#p\n", pUuid),
2671 rc = VERR_INVALID_PARAMETER);
2672
2673 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2674 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2675
2676 rc = pImage->Backend->pfnGetModificationUuid(pImage->pvBackendData,
2677 pUuid);
2678 } while (0);
2679
2680 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
2681 return rc;
2682}
2683
2684/**
2685 * Set the image's last modification UUID. Should not be used by normal applications.
2686 *
2687 * @returns VBox status code.
2688 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2689 * @param pDisk Pointer to HDD container.
2690 * @param nImage Image number, counts from 0. 0 is always base image of container.
2691 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
2692 */
2693VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
2694{
2695 int rc;
2696
2697 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
2698 pDisk, nImage, pUuid, pUuid));
2699 do
2700 {
2701 /* sanity check */
2702 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2703 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2704
2705 /* Check arguments. */
2706 AssertMsgBreak(VALID_PTR(pUuid) || pUuid == NULL,
2707 ("pUuid=%#p\n", pUuid),
2708 rc = VERR_INVALID_PARAMETER);
2709
2710 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2711 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2712
2713 RTUUID Uuid;
2714 if (!pUuid)
2715 {
2716 RTUuidCreate(&Uuid);
2717 pUuid = &Uuid;
2718 }
2719 rc = pImage->Backend->pfnSetModificationUuid(pImage->pvBackendData,
2720 pUuid);
2721 } while (0);
2722
2723 LogFlowFunc(("returns %Vrc\n", rc));
2724 return rc;
2725}
2726
2727/**
2728 * Get parent UUID of image in HDD container.
2729 *
2730 * @returns VBox status code.
2731 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2732 * @param pDisk Pointer to HDD container.
2733 * @param nImage Image number, counts from 0. 0 is always base image of container.
2734 * @param pUuid Where to store the parent image UUID.
2735 */
2736VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
2737 PRTUUID pUuid)
2738{
2739 int rc = VINF_SUCCESS;
2740
2741 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
2742 do
2743 {
2744 /* sanity check */
2745 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2746 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2747
2748 /* Check arguments. */
2749 AssertMsgBreak(VALID_PTR(pUuid),
2750 ("pUuid=%#p\n", pUuid),
2751 rc = VERR_INVALID_PARAMETER);
2752
2753 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2754 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2755
2756 rc = pImage->Backend->pfnGetParentUuid(pImage->pvBackendData, pUuid);
2757 } while (0);
2758
2759 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
2760 return rc;
2761}
2762
2763/**
2764 * Set the image's parent UUID. Should not be used by normal applications.
2765 *
2766 * @returns VBox status code.
2767 * @param pDisk Pointer to HDD container.
2768 * @param nImage Image number, counts from 0. 0 is always base image of container.
2769 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
2770 */
2771VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
2772 PCRTUUID pUuid)
2773{
2774 int rc;
2775
2776 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
2777 pDisk, nImage, pUuid, pUuid));
2778 do
2779 {
2780 /* sanity check */
2781 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2782 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2783
2784 /* Check arguments. */
2785 AssertMsgBreak(VALID_PTR(pUuid) || pUuid == NULL,
2786 ("pUuid=%#p\n", pUuid),
2787 rc = VERR_INVALID_PARAMETER);
2788
2789 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2790 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2791
2792 RTUUID Uuid;
2793 if (!pUuid)
2794 {
2795 RTUuidCreate(&Uuid);
2796 pUuid = &Uuid;
2797 }
2798 rc = pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, pUuid);
2799 } while (0);
2800
2801 LogFlowFunc(("returns %Vrc\n", rc));
2802 return rc;
2803}
2804
2805
2806/**
2807 * Debug helper - dumps all opened images in HDD container into the log file.
2808 *
2809 * @param pDisk Pointer to HDD container.
2810 */
2811VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
2812{
2813 do
2814 {
2815 /* sanity check */
2816 AssertBreak(VALID_PTR(pDisk), );
2817 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2818
2819 RTLogPrintf("--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
2820 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
2821 {
2822 RTLogPrintf("Dumping VD image \"%s\" (Backend=%s)\n",
2823 pImage->Backend->pszBackendName, pImage->pszFilename);
2824 pImage->Backend->pfnDump(pImage->pvBackendData);
2825 }
2826 } while (0);
2827}
2828
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