VirtualBox

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

最後變更 在這個檔案從7636是 7281,由 vboxsync 提交於 17 年 前

Accept PCHS=0/0/0 when creating images. Will be fixed when the image is used first anyway.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 103.4 KB
 
1/** $Id: VBoxHDD-new.cpp 7281 2008-03-04 16:24:09Z 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.alldomusa.eu.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 /* The PCHS geometry fields may be 0 to leave it for later. */
1092 AssertMsgBreak( VALID_PTR(pPCHSGeometry)
1093 && pPCHSGeometry->cCylinders <= 16383
1094 && pPCHSGeometry->cHeads <= 16
1095 && pPCHSGeometry->cSectors <= 63,
1096 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
1097 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1098 pPCHSGeometry->cSectors),
1099 rc = VERR_INVALID_PARAMETER);
1100 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
1101 AssertMsgBreak( VALID_PTR(pLCHSGeometry)
1102 && pLCHSGeometry->cCylinders <= 16383
1103 && pLCHSGeometry->cHeads <= 16
1104 && pLCHSGeometry->cSectors <= 63,
1105 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
1106 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
1107 pLCHSGeometry->cSectors),
1108 rc = VERR_INVALID_PARAMETER);
1109 AssertMsgBreak((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1110 ("uOpenFlags=%#x\n", uOpenFlags),
1111 rc = VERR_INVALID_PARAMETER);
1112
1113 /* Check state. */
1114 AssertMsgBreak(pDisk->cImages == 0,
1115 ("Create base image cannot be done with other images open\n"),
1116 rc = VERR_VDI_INVALID_STATE);
1117
1118 /* Set up image descriptor. */
1119 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1120 if (!pImage)
1121 {
1122 rc = VERR_NO_MEMORY;
1123 break;
1124 }
1125 pImage->pszFilename = RTStrDup(pszFilename);
1126 if (!pImage->pszFilename)
1127 {
1128 rc = VERR_NO_MEMORY;
1129 break;
1130 }
1131
1132 rc = vdFindBackend(pszBackend, &pImage->Backend, &pImage->hPlugin);
1133 if (VBOX_FAILURE(rc))
1134 break;
1135 if (!pImage->Backend)
1136 {
1137 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1138 N_("VD: unknown backend name '%s'"), pszBackend);
1139 break;
1140 }
1141
1142 rc = pImage->Backend->pfnCreate(pImage->pszFilename, enmType, cbSize,
1143 uImageFlags, pszComment, pPCHSGeometry,
1144 pLCHSGeometry, uOpenFlags, pfnProgress,
1145 pvUser, 0, 99, pDisk->pfnError,
1146 pDisk->pvErrorUser,
1147 &pImage->pvBackendData);
1148
1149 if (VBOX_SUCCESS(rc))
1150 {
1151 /** @todo optionally check UUIDs */
1152
1153 int rc2;
1154
1155 /* Cache disk information. */
1156 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
1157
1158 /* Cache PCHS geometry. */
1159 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1160 &pDisk->PCHSGeometry);
1161 if (VBOX_FAILURE(rc2))
1162 {
1163 pDisk->PCHSGeometry.cCylinders = 0;
1164 pDisk->PCHSGeometry.cHeads = 0;
1165 pDisk->PCHSGeometry.cSectors = 0;
1166 }
1167 else
1168 {
1169 /* Make sure the CHS geometry is properly clipped. */
1170 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1171 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1172 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1173 }
1174
1175 /* Cache LCHS geometry. */
1176 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1177 &pDisk->LCHSGeometry);
1178 if (VBOX_FAILURE(rc2))
1179 {
1180 pDisk->LCHSGeometry.cCylinders = 0;
1181 pDisk->LCHSGeometry.cHeads = 0;
1182 pDisk->LCHSGeometry.cSectors = 0;
1183 }
1184 else
1185 {
1186 /* Make sure the CHS geometry is properly clipped. */
1187 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
1188 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1189 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1190 }
1191 }
1192
1193 if (VBOX_SUCCESS(rc))
1194 {
1195 /* Image successfully opened, make it the last image. */
1196 vdAddImageToList(pDisk, pImage);
1197 }
1198 else
1199 {
1200 /* Error detected, but image opened. Close and delete image. */
1201 int rc2;
1202 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
1203 AssertRC(rc2);
1204 pImage->pvBackendData = NULL;
1205 }
1206 } while (0);
1207
1208 if (VBOX_FAILURE(rc))
1209 {
1210 if (pImage->hPlugin != NIL_RTLDRMOD)
1211 RTLdrClose(pImage->hPlugin);
1212
1213 if (pImage)
1214 {
1215 if (pImage->pszFilename)
1216 RTStrFree(pImage->pszFilename);
1217 RTMemFree(pImage);
1218 }
1219 }
1220
1221 if (VBOX_SUCCESS(rc) && pfnProgress)
1222 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1223
1224 LogFlowFunc(("returns %Vrc\n", rc));
1225 return rc;
1226}
1227
1228/**
1229 * Creates and opens a new differencing image file in HDD container.
1230 * See comments for VDOpen function about differencing images.
1231 *
1232 * @returns VBox status code.
1233 * @param pDisk Pointer to HDD container.
1234 * @param pszBackend Name of the image file backend to use.
1235 * @param pszFilename Name of the differencing image file to create.
1236 * @param uImageFlags Flags specifying special image features.
1237 * @param pszComment Pointer to image comment. NULL is ok.
1238 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1239 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1240 * @param pvUser User argument for the progress callback.
1241 */
1242VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
1243 const char *pszFilename, unsigned uImageFlags,
1244 const char *pszComment, unsigned uOpenFlags,
1245 PFNVMPROGRESS pfnProgress, void *pvUser)
1246{
1247 int rc = VINF_SUCCESS;
1248 PVDIMAGE pImage = NULL;
1249
1250 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" uOpenFlags=%#x pfnProgress=%#p pvUser=%#p\n",
1251 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, uOpenFlags,
1252 pfnProgress, pvUser));
1253 do
1254 {
1255 /* sanity check */
1256 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1257 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1258
1259 /* Check arguments. */
1260 AssertMsgBreak(VALID_PTR(pszBackend) && *pszBackend,
1261 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1262 rc = VERR_INVALID_PARAMETER);
1263 AssertMsgBreak(VALID_PTR(pszFilename) && *pszFilename,
1264 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1265 rc = VERR_INVALID_PARAMETER);
1266 AssertMsgBreak((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
1267 ("uImageFlags=%#x\n", uImageFlags),
1268 rc = VERR_INVALID_PARAMETER);
1269 AssertMsgBreak((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1270 ("uOpenFlags=%#x\n", uOpenFlags),
1271 rc = VERR_INVALID_PARAMETER);
1272
1273 /* Check state. */
1274 AssertMsgBreak(pDisk->cImages != 0,
1275 ("Create diff image cannot be done without other images open\n"),
1276 rc = VERR_VDI_INVALID_STATE);
1277
1278 /* Set up image descriptor. */
1279 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1280 if (!pImage)
1281 {
1282 rc = VERR_NO_MEMORY;
1283 break;
1284 }
1285 pImage->pszFilename = RTStrDup(pszFilename);
1286 if (!pImage->pszFilename)
1287 {
1288 rc = VERR_NO_MEMORY;
1289 break;
1290 }
1291
1292 rc = vdFindBackend(pszBackend, &pImage->Backend, &pImage->hPlugin);
1293 if (VBOX_FAILURE(rc))
1294 break;
1295 if (!pImage->Backend)
1296 {
1297 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1298 N_("VD: unknown backend name '%s'"), pszBackend);
1299 break;
1300 }
1301
1302 rc = pImage->Backend->pfnCreate(pImage->pszFilename,
1303 VD_IMAGE_TYPE_NORMAL, pDisk->cbSize,
1304 uImageFlags, pszComment,
1305 &pDisk->PCHSGeometry,
1306 &pDisk->LCHSGeometry, uOpenFlags,
1307 pfnProgress, pvUser, 0, 99,
1308 pDisk->pfnError, pDisk->pvErrorUser,
1309 &pImage->pvBackendData);
1310
1311 if (VBOX_SUCCESS(rc))
1312 {
1313 /** @todo optionally check UUIDs */
1314 }
1315
1316 if (VBOX_SUCCESS(rc))
1317 {
1318 /* Image successfully opened, make it the last image. */
1319 vdAddImageToList(pDisk, pImage);
1320 }
1321 else
1322 {
1323 /* Error detected, but image opened. Close and delete image. */
1324 int rc2;
1325 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
1326 AssertRC(rc2);
1327 pImage->pvBackendData = NULL;
1328 }
1329 } while (0);
1330
1331 if (VBOX_FAILURE(rc))
1332 {
1333 if (pImage->hPlugin != NIL_RTLDRMOD)
1334 RTLdrClose(pImage->hPlugin);
1335
1336 if (pImage)
1337 {
1338 if (pImage->pszFilename)
1339 RTStrFree(pImage->pszFilename);
1340 RTMemFree(pImage);
1341 }
1342 }
1343
1344 if (VBOX_SUCCESS(rc) && pfnProgress)
1345 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1346
1347 LogFlowFunc(("returns %Vrc\n", rc));
1348 return rc;
1349}
1350
1351/**
1352 * Merges two images (not necessarily with direct parent/child relationship).
1353 * As a side effect the source image and potentially the other images which
1354 * are also merged to the destination are deleted from both the disk and the
1355 * images in the HDD container.
1356 *
1357 * @returns VBox status code.
1358 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1359 * @param pDisk Pointer to HDD container.
1360 * @param nImageFrom Name of the image file to merge from.
1361 * @param nImageTo Name of the image file to merge to.
1362 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1363 * @param pvUser User argument for the progress callback.
1364 */
1365VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
1366 unsigned nImageTo, PFNVMPROGRESS pfnProgress,
1367 void *pvUser)
1368{
1369 int rc = VINF_SUCCESS;
1370 void *pvBuf = NULL;
1371
1372 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pfnProgress=%#p pvUser=%#p\n",
1373 pDisk, nImageFrom, nImageTo, pfnProgress, pvUser));
1374 do
1375 {
1376 /* sanity check */
1377 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1378 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1379
1380 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
1381 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
1382 if (!pImageFrom || !pImageTo)
1383 {
1384 rc = VERR_VDI_IMAGE_NOT_FOUND;
1385 break;
1386 }
1387 AssertBreak(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
1388
1389 /* Check if destination image is writable. */
1390 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
1391 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
1392 {
1393 rc = VERR_VDI_IMAGE_READ_ONLY;
1394 break;
1395 }
1396
1397 /* Get size of destination image. */
1398 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pvBackendData);
1399
1400 /* Allocate tmp buffer. */
1401 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
1402 if (!pvBuf)
1403 {
1404 rc = VERR_NO_MEMORY;
1405 break;
1406 }
1407
1408 /* Merging is done directly on the images itself. This potentially
1409 * causes trouble if the disk is full in the middle of operation. */
1410 /** @todo write alternative implementation which works with temporary
1411 * images (which is safer, but requires even more space). Also has the
1412 * drawback that merging into a raw disk parent simply isn't possible
1413 * this way (but in that case disk full isn't really a problem). */
1414 if (nImageFrom < nImageTo)
1415 {
1416 /* Merge parent state into child. This means writing all not
1417 * allocated blocks in the destination image which are allocated in
1418 * the images to be merged. */
1419 uint64_t uOffset = 0;
1420 uint64_t cbRemaining = cbSize;
1421 do
1422 {
1423 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1424 rc = pImageTo->Backend->pfnRead(pImageTo->pvBackendData,
1425 uOffset, pvBuf, cbThisRead,
1426 &cbThisRead);
1427 if (VBOX_FAILURE(rc))
1428 break;
1429 if (rc == VERR_VDI_BLOCK_FREE)
1430 {
1431 /* Search for image with allocated block. Do not attempt to
1432 * read more than the previous reads marked as valid.
1433 * Otherwise this would return stale data when different
1434 * block sizes are used for the images. */
1435 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
1436 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VDI_BLOCK_FREE;
1437 pCurrImage = pCurrImage->pPrev)
1438 {
1439 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
1440 uOffset, pvBuf,
1441 cbThisRead,
1442 &cbThisRead);
1443 }
1444 if (VBOX_FAILURE(rc))
1445 break;
1446
1447 if (rc != VERR_VDI_BLOCK_FREE)
1448 {
1449 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1450 cbThisRead);
1451 if (VBOX_FAILURE(rc))
1452 break;
1453 }
1454 }
1455
1456 uOffset += cbThisRead;
1457 cbRemaining -= cbThisRead;
1458 } while (uOffset < cbSize);
1459 }
1460 else
1461 {
1462 /* Merge child state into parent. This means writing all blocks
1463 * which are allocated in the image up to the source image to the
1464 * destination image. */
1465 uint64_t uOffset = 0;
1466 uint64_t cbRemaining = cbSize;
1467 do
1468 {
1469 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1470 /* Search for image with allocated block. Do not attempt to
1471 * read more than the previous reads marked as valid. Otherwise
1472 * this would return stale data when different block sizes are
1473 * used for the images. */
1474 for (PVDIMAGE pCurrImage = pImageFrom;
1475 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VDI_BLOCK_FREE;
1476 pCurrImage = pCurrImage->pPrev)
1477 {
1478 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
1479 uOffset, pvBuf,
1480 cbThisRead, &cbThisRead);
1481 }
1482 if (VBOX_FAILURE(rc))
1483 break;
1484
1485 if (rc != VERR_VDI_BLOCK_FREE)
1486 {
1487 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1488 cbThisRead);
1489 if (VBOX_FAILURE(rc))
1490 break;
1491 }
1492
1493 uOffset += cbThisRead;
1494 cbRemaining -= cbThisRead;
1495 } while (uOffset < cbSize);
1496 }
1497
1498 /* Update parent UUID so that image chain is consistent. */
1499 RTUUID Uuid;
1500 if (nImageFrom < nImageTo)
1501 {
1502 if (pImageTo->pPrev)
1503 {
1504 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pPrev->pvBackendData,
1505 &Uuid);
1506 AssertRC(rc);
1507 }
1508 else
1509 RTUuidClear(&Uuid);
1510 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pvBackendData,
1511 &Uuid);
1512 AssertRC(rc);
1513 }
1514 else
1515 {
1516 if (pImageFrom->pNext)
1517 {
1518 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pvBackendData,
1519 &Uuid);
1520 AssertRC(rc);
1521 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext,
1522 &Uuid);
1523 AssertRC(rc);
1524 }
1525 }
1526
1527 /* Delete the no longer needed images. */
1528 PVDIMAGE pImg = pImageFrom, pTmp;
1529 while (pImg != pImageTo)
1530 {
1531 if (nImageFrom < nImageTo)
1532 pTmp = pImg->pNext;
1533 else
1534 pTmp = pImg->pPrev;
1535 vdRemoveImageFromList(pDisk, pImg);
1536 pImg->Backend->pfnClose(pImg->pvBackendData, true);
1537 pImg = pTmp;
1538 }
1539 } while (0);
1540
1541 if (pvBuf)
1542 RTMemTmpFree(pvBuf);
1543
1544 if (VBOX_SUCCESS(rc) && pfnProgress)
1545 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1546
1547 LogFlowFunc(("returns %Vrc\n", rc));
1548 return rc;
1549}
1550
1551/**
1552 * Copies an image from one HDD container to another.
1553 * The copy is opened in the target HDD container.
1554 * It is possible to convert between different image formats, because the
1555 * backend for the destination may be different from the source.
1556 * If both the source and destination reference the same HDD container,
1557 * then the image is moved (by copying/deleting or renaming) to the new location.
1558 * The source container is unchanged if the move operation fails, otherwise
1559 * the image at the new location is opened in the same way as the old one was.
1560 *
1561 * @returns VBox status code.
1562 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1563 * @param pDiskFrom Pointer to source HDD container.
1564 * @param nImage Image number, counts from 0. 0 is always base image of container.
1565 * @param pDiskTo Pointer to destination HDD container.
1566 * @param pszBackend Name of the image file backend to use.
1567 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
1568 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
1569 * @param cbSize New image size (0 means leave unchanged).
1570 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1571 * @param pvUser User argument for the progress callback.
1572 */
1573VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
1574 const char *pszBackend, const char *pszFilename,
1575 bool fMoveByRename, uint64_t cbSize,
1576 PFNVMPROGRESS pfnProgress, void *pvUser)
1577{
1578 return VERR_NOT_IMPLEMENTED;
1579}
1580
1581/**
1582 * Closes the last opened image file in HDD container.
1583 * If previous image file was opened in read-only mode (that is normal) and closing image
1584 * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
1585 * will be reopened in read/write mode.
1586 *
1587 * @returns VBox status code.
1588 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1589 * @param pDisk Pointer to HDD container.
1590 * @param fDelete If true, delete the image from the host disk.
1591 */
1592VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
1593{
1594 int rc = VINF_SUCCESS;;
1595
1596 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
1597 do
1598 {
1599 /* sanity check */
1600 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1601 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1602
1603 PVDIMAGE pImage = pDisk->pLast;
1604 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_NOT_OPENED);
1605 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
1606 /* Remove image from list of opened images. */
1607 vdRemoveImageFromList(pDisk, pImage);
1608 /* Close (and optionally delete) image. */
1609 rc = pImage->Backend->pfnClose(pImage->pvBackendData, fDelete);
1610 /* Free remaining resources related to the image. */
1611 if (pImage->hPlugin != NIL_RTLDRMOD)
1612 {
1613 RTLdrClose(pImage->hPlugin);
1614 pImage->hPlugin = NIL_RTLDRMOD;
1615 }
1616 RTStrFree(pImage->pszFilename);
1617 RTMemFree(pImage);
1618
1619 pImage = pDisk->pLast;
1620 if (!pImage)
1621 break;
1622
1623 /* If disk was previously in read/write mode, make sure it will stay
1624 * like this (if possible) after closing this image. Set the open flags
1625 * accordingly. */
1626 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1627 {
1628 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
1629 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
1630 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData, uOpenFlags);
1631 }
1632
1633 int rc2;
1634
1635 /* Cache disk information. */
1636 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
1637
1638 /* Cache PCHS geometry. */
1639 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1640 &pDisk->PCHSGeometry);
1641 if (VBOX_FAILURE(rc2))
1642 {
1643 pDisk->PCHSGeometry.cCylinders = 0;
1644 pDisk->PCHSGeometry.cHeads = 0;
1645 pDisk->PCHSGeometry.cSectors = 0;
1646 }
1647 else
1648 {
1649 /* Make sure the PCHS geometry is properly clipped. */
1650 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1651 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1652 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1653 }
1654
1655 /* Cache LCHS geometry. */
1656 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1657 &pDisk->LCHSGeometry);
1658 if (VBOX_FAILURE(rc2))
1659 {
1660 pDisk->LCHSGeometry.cCylinders = 0;
1661 pDisk->LCHSGeometry.cHeads = 0;
1662 pDisk->LCHSGeometry.cSectors = 0;
1663 }
1664 else
1665 {
1666 /* Make sure the LCHS geometry is properly clipped. */
1667 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
1668 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1669 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1670 }
1671 } while (0);
1672
1673 LogFlowFunc(("returns %Vrc\n", rc));
1674 return rc;
1675}
1676
1677/**
1678 * Closes all opened image files in HDD container.
1679 *
1680 * @returns VBox status code.
1681 * @param pDisk Pointer to HDD container.
1682 */
1683VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
1684{
1685 int rc = VINF_SUCCESS;
1686
1687 LogFlowFunc(("pDisk=%#p\n", pDisk));
1688 do
1689 {
1690 /* sanity check */
1691 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1692 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1693
1694 PVDIMAGE pImage = pDisk->pLast;
1695 while (VALID_PTR(pImage))
1696 {
1697 PVDIMAGE pPrev = pImage->pPrev;
1698 /* Remove image from list of opened images. */
1699 vdRemoveImageFromList(pDisk, pImage);
1700 /* Close image. */
1701 int rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, false);
1702 if (VBOX_FAILURE(rc2) && VBOX_SUCCESS(rc))
1703 rc = rc2;
1704 /* Free remaining resources related to the image. */
1705 if (pImage->hPlugin != NIL_RTLDRMOD)
1706 {
1707 RTLdrClose(pImage->hPlugin);
1708 pImage->hPlugin = NIL_RTLDRMOD;
1709 }
1710 RTStrFree(pImage->pszFilename);
1711 RTMemFree(pImage);
1712 pImage = pPrev;
1713 }
1714 Assert(!VALID_PTR(pDisk->pLast));
1715 } while (0);
1716
1717 LogFlowFunc(("returns %Vrc\n", rc));
1718 return rc;
1719}
1720
1721/**
1722 * Read data from virtual HDD.
1723 *
1724 * @returns VBox status code.
1725 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1726 * @param pDisk Pointer to HDD container.
1727 * @param uOffset Offset of first reading byte from start of disk.
1728 * @param pvBuf Pointer to buffer for reading data.
1729 * @param cbRead Number of bytes to read.
1730 */
1731VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
1732 size_t cbRead)
1733{
1734 int rc;
1735
1736 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
1737 pDisk, uOffset, pvBuf, cbRead));
1738 do
1739 {
1740 /* sanity check */
1741 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1742 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1743
1744 /* Check arguments. */
1745 AssertMsgBreak(VALID_PTR(pvBuf),
1746 ("pvBuf=%#p\n", pvBuf),
1747 rc = VERR_INVALID_PARAMETER);
1748 AssertMsgBreak(cbRead,
1749 ("cbRead=%zu\n", cbRead),
1750 rc = VERR_INVALID_PARAMETER);
1751 AssertMsgBreak(uOffset + cbRead <= pDisk->cbSize,
1752 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
1753 uOffset, cbRead, pDisk->cbSize),
1754 rc = VERR_INVALID_PARAMETER);
1755
1756 PVDIMAGE pImage = pDisk->pLast;
1757 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_NOT_OPENED);
1758
1759 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead);
1760 } while (0);
1761
1762 LogFlowFunc(("returns %Vrc\n", rc));
1763 return rc;
1764}
1765
1766/**
1767 * Write data to virtual HDD.
1768 *
1769 * @returns VBox status code.
1770 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1771 * @param pDisk Pointer to HDD container.
1772 * @param uOffset Offset of first reading byte from start of disk.
1773 * @param pvBuf Pointer to buffer for writing data.
1774 * @param cbWrite Number of bytes to write.
1775 */
1776VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
1777 size_t cbWrite)
1778{
1779 int rc = VINF_SUCCESS;
1780
1781 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
1782 pDisk, uOffset, pvBuf, cbWrite));
1783 do
1784 {
1785 /* sanity check */
1786 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1787 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1788
1789 /* Check arguments. */
1790 AssertMsgBreak(VALID_PTR(pvBuf),
1791 ("pvBuf=%#p\n", pvBuf),
1792 rc = VERR_INVALID_PARAMETER);
1793 AssertMsgBreak(cbWrite,
1794 ("cbWrite=%zu\n", cbWrite),
1795 rc = VERR_INVALID_PARAMETER);
1796 AssertMsgBreak(uOffset + cbWrite <= pDisk->cbSize,
1797 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
1798 uOffset, cbWrite, pDisk->cbSize),
1799 rc = VERR_INVALID_PARAMETER);
1800
1801 PVDIMAGE pImage = pDisk->pLast;
1802 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_NOT_OPENED);
1803
1804 vdSetModifiedFlag(pDisk);
1805 rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite);
1806 } while (0);
1807
1808 LogFlowFunc(("returns %Vrc\n", rc));
1809 return rc;
1810}
1811
1812/**
1813 * Make sure the on disk representation of a virtual HDD is up to date.
1814 *
1815 * @returns VBox status code.
1816 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1817 * @param pDisk Pointer to HDD container.
1818 */
1819VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
1820{
1821 int rc = VINF_SUCCESS;
1822
1823 LogFlowFunc(("pDisk=%#p\n", pDisk));
1824 do
1825 {
1826 /* sanity check */
1827 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1828 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1829
1830 PVDIMAGE pImage = pDisk->pLast;
1831 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_NOT_OPENED);
1832
1833 vdResetModifiedFlag(pDisk);
1834 rc = pImage->Backend->pfnFlush(pImage->pvBackendData);
1835 } while (0);
1836
1837 LogFlowFunc(("returns %Vrc\n", rc));
1838 return rc;
1839}
1840
1841/**
1842 * Get number of opened images in HDD container.
1843 *
1844 * @returns Number of opened images for HDD container. 0 if no images have been opened.
1845 * @param pDisk Pointer to HDD container.
1846 */
1847VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
1848{
1849 unsigned cImages;
1850
1851 LogFlowFunc(("pDisk=%#p\n", pDisk));
1852 do
1853 {
1854 /* sanity check */
1855 AssertBreak(VALID_PTR(pDisk), cImages = 0);
1856 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1857
1858 cImages = pDisk->cImages;
1859 } while (0);
1860
1861 LogFlowFunc(("returns %u\n", cImages));
1862 return cImages;
1863}
1864
1865/**
1866 * Get read/write mode of HDD container.
1867 *
1868 * @returns Virtual disk ReadOnly status.
1869 * @returns true if no image is opened in HDD container.
1870 * @param pDisk Pointer to HDD container.
1871 */
1872VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
1873{
1874 bool fReadOnly;
1875
1876 LogFlowFunc(("pDisk=%#p\n", pDisk));
1877 do
1878 {
1879 /* sanity check */
1880 AssertBreak(VALID_PTR(pDisk), fReadOnly = false);
1881 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1882
1883 PVDIMAGE pImage = pDisk->pLast;
1884 AssertBreak(VALID_PTR(pImage), fReadOnly = true);
1885
1886 unsigned uOpenFlags;
1887 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1888 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
1889 } while (0);
1890
1891 LogFlowFunc(("returns %d\n", fReadOnly));
1892 return fReadOnly;
1893}
1894
1895/**
1896 * Get total capacity of an image in HDD container.
1897 *
1898 * @returns Virtual disk size in bytes.
1899 * @returns 0 if no image with specified number was not opened.
1900 * @param pDisk Pointer to HDD container.
1901 * @param nImage Image number, counds from 0. 0 is always base image of container.
1902 */
1903VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
1904{
1905 uint64_t cbSize;
1906
1907 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
1908 do
1909 {
1910 /* sanity check */
1911 AssertBreak(VALID_PTR(pDisk), cbSize = 0);
1912 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1913
1914 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1915 AssertBreak(VALID_PTR(pImage), cbSize = 0);
1916 cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
1917 } while (0);
1918
1919 LogFlowFunc(("returns %llu\n", cbSize));
1920 return cbSize;
1921}
1922
1923/**
1924 * Get total file size of an image in HDD container.
1925 *
1926 * @returns Virtual disk size in bytes.
1927 * @returns 0 if no image is opened in HDD container.
1928 * @param pDisk Pointer to HDD container.
1929 * @param nImage Image number, counts from 0. 0 is always base image of container.
1930 */
1931VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
1932{
1933 uint64_t cbSize;
1934
1935 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
1936 do
1937 {
1938 /* sanity check */
1939 AssertBreak(VALID_PTR(pDisk), cbSize = 0);
1940 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1941
1942 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1943 AssertBreak(VALID_PTR(pImage), cbSize = 0);
1944 cbSize = pImage->Backend->pfnGetFileSize(pImage->pvBackendData);
1945 } while (0);
1946
1947 LogFlowFunc(("returns %llu\n", cbSize));
1948 return cbSize;
1949}
1950
1951/**
1952 * Get virtual disk PCHS geometry stored in HDD container.
1953 *
1954 * @returns VBox status code.
1955 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1956 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
1957 * @param pDisk Pointer to HDD container.
1958 * @param nImage Image number, counts from 0. 0 is always base image of container.
1959 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
1960 */
1961VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
1962 PPDMMEDIAGEOMETRY pPCHSGeometry)
1963{
1964 int rc = VINF_SUCCESS;
1965
1966 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
1967 pDisk, nImage, pPCHSGeometry));
1968 do
1969 {
1970 /* sanity check */
1971 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1972 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1973
1974 /* Check arguments. */
1975 AssertMsgBreak(VALID_PTR(pPCHSGeometry),
1976 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
1977 rc = VERR_INVALID_PARAMETER);
1978
1979 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1980 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
1981
1982 if (pImage == pDisk->pLast)
1983 {
1984 /* Use cached information if possible. */
1985 if (pDisk->PCHSGeometry.cCylinders != 0)
1986 *pPCHSGeometry = pDisk->PCHSGeometry;
1987 else
1988 rc = VERR_VDI_GEOMETRY_NOT_SET;
1989 }
1990 else
1991 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1992 pPCHSGeometry);
1993 } while (0);
1994
1995 LogFlowFunc(("%s: %Vrc (PCHS=%u/%u/%u)\n", rc,
1996 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
1997 pDisk->PCHSGeometry.cSectors));
1998 return rc;
1999}
2000
2001/**
2002 * Store virtual disk PCHS geometry in HDD container.
2003 *
2004 * Note that in case of unrecoverable error all images in HDD container will be closed.
2005 *
2006 * @returns VBox status code.
2007 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2008 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2009 * @param pDisk Pointer to HDD container.
2010 * @param nImage Image number, counts from 0. 0 is always base image of container.
2011 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
2012 */
2013VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2014 PCPDMMEDIAGEOMETRY pPCHSGeometry)
2015{
2016 int rc = VINF_SUCCESS;
2017
2018 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
2019 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
2020 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
2021 do
2022 {
2023 /* sanity check */
2024 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2025 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2026
2027 /* Check arguments. */
2028 AssertMsgBreak( VALID_PTR(pPCHSGeometry)
2029 && pPCHSGeometry->cCylinders <= 16383
2030 && pPCHSGeometry->cHeads <= 16
2031 && pPCHSGeometry->cSectors <= 63,
2032 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
2033 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
2034 pPCHSGeometry->cSectors),
2035 rc = VERR_INVALID_PARAMETER);
2036
2037 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2038 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2039
2040 if (pImage == pDisk->pLast)
2041 {
2042 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
2043 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
2044 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
2045 {
2046 /* Only update geometry if it is changed. Avoids similar checks
2047 * in every backend. Most of the time the new geometry is set
2048 * to the previous values, so no need to go through the hassle
2049 * of updating an image which could be opened in read-only mode
2050 * right now. */
2051 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2052 pPCHSGeometry);
2053
2054 /* Cache new geometry values in any case. */
2055 int rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2056 &pDisk->PCHSGeometry);
2057 if (VBOX_FAILURE(rc2))
2058 {
2059 pDisk->PCHSGeometry.cCylinders = 0;
2060 pDisk->PCHSGeometry.cHeads = 0;
2061 pDisk->PCHSGeometry.cSectors = 0;
2062 }
2063 else
2064 {
2065 /* Make sure the CHS geometry is properly clipped. */
2066 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 1024);
2067 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
2068 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
2069 }
2070 }
2071 }
2072 else
2073 {
2074 PDMMEDIAGEOMETRY PCHS;
2075 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2076 &PCHS);
2077 if ( VBOX_FAILURE(rc)
2078 || pPCHSGeometry->cCylinders != PCHS.cCylinders
2079 || pPCHSGeometry->cHeads != PCHS.cHeads
2080 || pPCHSGeometry->cSectors != PCHS.cSectors)
2081 {
2082 /* Only update geometry if it is changed. Avoids similar checks
2083 * in every backend. Most of the time the new geometry is set
2084 * to the previous values, so no need to go through the hassle
2085 * of updating an image which could be opened in read-only mode
2086 * right now. */
2087 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2088 pPCHSGeometry);
2089 }
2090 }
2091 } while (0);
2092
2093 LogFlowFunc(("returns %Vrc\n", rc));
2094 return rc;
2095}
2096
2097/**
2098 * Get virtual disk LCHS geometry stored in HDD container.
2099 *
2100 * @returns VBox status code.
2101 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2102 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2103 * @param pDisk Pointer to HDD container.
2104 * @param nImage Image number, counts from 0. 0 is always base image of container.
2105 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
2106 */
2107VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2108 PPDMMEDIAGEOMETRY pLCHSGeometry)
2109{
2110 int rc = VINF_SUCCESS;
2111
2112 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
2113 pDisk, nImage, pLCHSGeometry));
2114 do
2115 {
2116 /* sanity check */
2117 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2118 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2119
2120 /* Check arguments. */
2121 AssertMsgBreak(VALID_PTR(pLCHSGeometry),
2122 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
2123 rc = VERR_INVALID_PARAMETER);
2124
2125 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2126 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2127
2128 if (pImage == pDisk->pLast)
2129 {
2130 /* Use cached information if possible. */
2131 if (pDisk->LCHSGeometry.cCylinders != 0)
2132 *pLCHSGeometry = pDisk->LCHSGeometry;
2133 else
2134 rc = VERR_VDI_GEOMETRY_NOT_SET;
2135 }
2136 else
2137 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2138 pLCHSGeometry);
2139 } while (0);
2140
2141 LogFlowFunc(("%s: %Vrc (LCHS=%u/%u/%u)\n", rc,
2142 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
2143 pDisk->LCHSGeometry.cSectors));
2144 return rc;
2145}
2146
2147/**
2148 * Store virtual disk LCHS geometry in HDD container.
2149 *
2150 * Note that in case of unrecoverable error all images in HDD container will be closed.
2151 *
2152 * @returns VBox status code.
2153 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2154 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2155 * @param pDisk Pointer to HDD container.
2156 * @param nImage Image number, counts from 0. 0 is always base image of container.
2157 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
2158 */
2159VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2160 PCPDMMEDIAGEOMETRY pLCHSGeometry)
2161{
2162 int rc = VINF_SUCCESS;
2163
2164 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
2165 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
2166 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2167 do
2168 {
2169 /* sanity check */
2170 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2171 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2172
2173 /* Check arguments. */
2174 AssertMsgBreak( VALID_PTR(pLCHSGeometry)
2175 && pLCHSGeometry->cCylinders <= 1024
2176 && pLCHSGeometry->cHeads <= 255
2177 && pLCHSGeometry->cSectors <= 63,
2178 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
2179 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
2180 pLCHSGeometry->cSectors),
2181 rc = VERR_INVALID_PARAMETER);
2182
2183 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2184 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2185
2186 if (pImage == pDisk->pLast)
2187 {
2188 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
2189 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
2190 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
2191 {
2192 /* Only update geometry if it is changed. Avoids similar checks
2193 * in every backend. Most of the time the new geometry is set
2194 * to the previous values, so no need to go through the hassle
2195 * of updating an image which could be opened in read-only mode
2196 * right now. */
2197 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2198 pLCHSGeometry);
2199
2200 /* Cache new geometry values in any case. */
2201 int rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2202 &pDisk->LCHSGeometry);
2203 if (VBOX_FAILURE(rc2))
2204 {
2205 pDisk->LCHSGeometry.cCylinders = 0;
2206 pDisk->LCHSGeometry.cHeads = 0;
2207 pDisk->LCHSGeometry.cSectors = 0;
2208 }
2209 else
2210 {
2211 /* Make sure the CHS geometry is properly clipped. */
2212 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
2213 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
2214 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
2215 }
2216 }
2217 }
2218 else
2219 {
2220 PDMMEDIAGEOMETRY LCHS;
2221 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2222 &LCHS);
2223 if ( VBOX_FAILURE(rc)
2224 || pLCHSGeometry->cCylinders != LCHS.cCylinders
2225 || pLCHSGeometry->cHeads != LCHS.cHeads
2226 || pLCHSGeometry->cSectors != LCHS.cSectors)
2227 {
2228 /* Only update geometry if it is changed. Avoids similar checks
2229 * in every backend. Most of the time the new geometry is set
2230 * to the previous values, so no need to go through the hassle
2231 * of updating an image which could be opened in read-only mode
2232 * right now. */
2233 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2234 pLCHSGeometry);
2235 }
2236 }
2237 } while (0);
2238
2239 LogFlowFunc(("returns %Vrc\n", rc));
2240 return rc;
2241}
2242
2243/**
2244 * Get version of image in HDD container.
2245 *
2246 * @returns VBox status code.
2247 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2248 * @param pDisk Pointer to HDD container.
2249 * @param nImage Image number, counts from 0. 0 is always base image of container.
2250 * @param puVersion Where to store the image version.
2251 */
2252VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
2253 unsigned *puVersion)
2254{
2255 int rc = VINF_SUCCESS;
2256
2257 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
2258 pDisk, nImage, puVersion));
2259 do
2260 {
2261 /* sanity check */
2262 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2263 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2264
2265 /* Check arguments. */
2266 AssertMsgBreak(VALID_PTR(puVersion),
2267 ("puVersion=%#p\n", puVersion),
2268 rc = VERR_INVALID_PARAMETER);
2269
2270 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2271 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2272
2273 *puVersion = pImage->Backend->pfnGetVersion(pImage->pvBackendData);
2274 } while (0);
2275
2276 LogFlowFunc(("returns %Vrc uVersion=%#x\n", rc, *puVersion));
2277 return rc;
2278}
2279
2280/**
2281 * Get type of image in HDD container.
2282 *
2283 * @returns VBox status code.
2284 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2285 * @param pDisk Pointer to HDD container.
2286 * @param nImage Image number, counts from 0. 0 is always base image of container.
2287 * @param penmType Where to store the image type.
2288 */
2289VBOXDDU_DECL(int) VDGetImageType(PVBOXHDD pDisk, unsigned nImage,
2290 PVDIMAGETYPE penmType)
2291{
2292 int rc;
2293
2294 LogFlowFunc(("pDisk=%#p nImage=%u penmType=%#p\n",
2295 pDisk, nImage, penmType));
2296 do
2297 {
2298 /* sanity check */
2299 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2300 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2301
2302 /* Check arguments. */
2303 AssertMsgBreak(VALID_PTR(penmType),
2304 ("penmType=%#p\n", penmType),
2305 rc = VERR_INVALID_PARAMETER);
2306
2307 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2308 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2309
2310 rc = pImage->Backend->pfnGetImageType(pImage->pvBackendData,
2311 penmType);
2312 } while (0);
2313
2314 LogFlowFunc(("returns %Vrc uenmType=%u\n", rc, *penmType));
2315 return rc;
2316}
2317
2318/**
2319 * Get flags of image in HDD container.
2320 *
2321 * @returns VBox status code.
2322 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2323 * @param pDisk Pointer to HDD container.
2324 * @param nImage Image number, counts from 0. 0 is always base image of container.
2325 * @param puImageFlags Where to store the image flags.
2326 */
2327VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
2328 unsigned *puImageFlags)
2329{
2330 int rc = VINF_SUCCESS;
2331
2332 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
2333 pDisk, nImage, puImageFlags));
2334 do
2335 {
2336 /* sanity check */
2337 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2338 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2339
2340 /* Check arguments. */
2341 AssertMsgBreak(VALID_PTR(puImageFlags),
2342 ("puImageFlags=%#p\n", puImageFlags),
2343 rc = VERR_INVALID_PARAMETER);
2344
2345 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2346 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2347
2348 *puImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pvBackendData);
2349 } while (0);
2350
2351 LogFlowFunc(("returns %Vrc uImageFlags=%#x\n", rc, *puImageFlags));
2352 return rc;
2353}
2354
2355/**
2356 * Get open flags of image in HDD container.
2357 *
2358 * @returns VBox status code.
2359 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2360 * @param pDisk Pointer to HDD container.
2361 * @param nImage Image number, counts from 0. 0 is always base image of container.
2362 * @param puOpenFlags Where to store the image open flags.
2363 */
2364VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2365 unsigned *puOpenFlags)
2366{
2367 int rc = VINF_SUCCESS;
2368
2369 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
2370 pDisk, nImage, puOpenFlags));
2371 do
2372 {
2373 /* sanity check */
2374 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2375 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2376
2377 /* Check arguments. */
2378 AssertMsgBreak(VALID_PTR(puOpenFlags),
2379 ("puOpenFlags=%#p\n", puOpenFlags),
2380 rc = VERR_INVALID_PARAMETER);
2381
2382 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2383 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2384
2385 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2386 } while (0);
2387
2388 LogFlowFunc(("returns %Vrc uOpenFlags=%#x\n", rc, *puOpenFlags));
2389 return rc;
2390}
2391
2392/**
2393 * Set open flags of image in HDD container.
2394 * This operation may cause file locking changes and/or files being reopened.
2395 * Note that in case of unrecoverable error all images in HDD container will be closed.
2396 *
2397 * @returns VBox status code.
2398 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2399 * @param pDisk Pointer to HDD container.
2400 * @param nImage Image number, counts from 0. 0 is always base image of container.
2401 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
2402 */
2403VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2404 unsigned uOpenFlags)
2405{
2406 int rc;
2407
2408 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
2409 do
2410 {
2411 /* sanity check */
2412 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2413 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2414
2415 /* Check arguments. */
2416 AssertMsgBreak((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
2417 ("uOpenFlags=%#x\n", uOpenFlags),
2418 rc = VERR_INVALID_PARAMETER);
2419
2420 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2421 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2422
2423 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData,
2424 uOpenFlags);
2425 } while (0);
2426
2427 LogFlowFunc(("returns %Vrc\n", rc));
2428 return rc;
2429}
2430
2431/**
2432 * Get base filename of image in HDD container. Some image formats use
2433 * other filenames as well, so don't use this for anything but informational
2434 * purposes.
2435 *
2436 * @returns VBox status code.
2437 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2438 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
2439 * @param pDisk Pointer to HDD container.
2440 * @param nImage Image number, counts from 0. 0 is always base image of container.
2441 * @param pszFilename Where to store the image file name.
2442 * @param cbFilename Size of buffer pszFilename points to.
2443 */
2444VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
2445 char *pszFilename, unsigned cbFilename)
2446{
2447 int rc;
2448
2449 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
2450 pDisk, nImage, pszFilename, cbFilename));
2451 do
2452 {
2453 /* sanity check */
2454 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2455 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2456
2457 /* Check arguments. */
2458 AssertMsgBreak(VALID_PTR(pszFilename) && *pszFilename,
2459 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
2460 rc = VERR_INVALID_PARAMETER);
2461 AssertMsgBreak(cbFilename,
2462 ("cbFilename=%u\n", cbFilename),
2463 rc = VERR_INVALID_PARAMETER);
2464
2465 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2466 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2467
2468 size_t cb = strlen(pImage->pszFilename);
2469 if (cb <= cbFilename)
2470 {
2471 strcpy(pszFilename, pImage->pszFilename);
2472 rc = VINF_SUCCESS;
2473 }
2474 else
2475 {
2476 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
2477 pszFilename[cbFilename - 1] = '\0';
2478 rc = VERR_BUFFER_OVERFLOW;
2479 }
2480 } while (0);
2481
2482 LogFlowFunc(("returns %Vrc, pszFilename=\"%s\"\n", rc, pszFilename));
2483 return rc;
2484}
2485
2486/**
2487 * Get the comment line of image in HDD container.
2488 *
2489 * @returns VBox status code.
2490 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2491 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
2492 * @param pDisk Pointer to HDD container.
2493 * @param nImage Image number, counts from 0. 0 is always base image of container.
2494 * @param pszComment Where to store the comment string of image. NULL is ok.
2495 * @param cbComment The size of pszComment buffer. 0 is ok.
2496 */
2497VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
2498 char *pszComment, unsigned cbComment)
2499{
2500 int rc;
2501
2502 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
2503 pDisk, nImage, pszComment, cbComment));
2504 do
2505 {
2506 /* sanity check */
2507 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2508 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2509
2510 /* Check arguments. */
2511 AssertMsgBreak(VALID_PTR(pszComment),
2512 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
2513 rc = VERR_INVALID_PARAMETER);
2514 AssertMsgBreak(cbComment,
2515 ("cbComment=%u\n", cbComment),
2516 rc = VERR_INVALID_PARAMETER);
2517
2518 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2519 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2520
2521 rc = pImage->Backend->pfnGetComment(pImage->pvBackendData, pszComment,
2522 cbComment);
2523 } while (0);
2524
2525 LogFlowFunc(("returns %Vrc, pszComment=\"%s\"\n", rc, pszComment));
2526 return rc;
2527}
2528
2529/**
2530 * Changes the comment line of image in HDD container.
2531 *
2532 * @returns VBox status code.
2533 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2534 * @param pDisk Pointer to HDD container.
2535 * @param nImage Image number, counts from 0. 0 is always base image of container.
2536 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
2537 */
2538VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
2539 const char *pszComment)
2540{
2541 int rc;
2542
2543 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
2544 pDisk, nImage, pszComment, pszComment));
2545 do
2546 {
2547 /* sanity check */
2548 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2549 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2550
2551 /* Check arguments. */
2552 AssertMsgBreak(VALID_PTR(pszComment) || pszComment == NULL,
2553 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
2554 rc = VERR_INVALID_PARAMETER);
2555
2556 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2557 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2558
2559 rc = pImage->Backend->pfnSetComment(pImage->pvBackendData, pszComment);
2560 } while (0);
2561
2562 LogFlowFunc(("returns %Vrc\n", rc));
2563 return rc;
2564}
2565
2566
2567/**
2568 * Get UUID of image in HDD container.
2569 *
2570 * @returns VBox status code.
2571 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2572 * @param pDisk Pointer to HDD container.
2573 * @param nImage Image number, counts from 0. 0 is always base image of container.
2574 * @param pUuid Where to store the image creation UUID.
2575 */
2576VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
2577{
2578 int rc;
2579
2580 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
2581 do
2582 {
2583 /* sanity check */
2584 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2585 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2586
2587 /* Check arguments. */
2588 AssertMsgBreak(VALID_PTR(pUuid),
2589 ("pUuid=%#p\n", pUuid),
2590 rc = VERR_INVALID_PARAMETER);
2591
2592 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2593 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2594
2595 rc = pImage->Backend->pfnGetUuid(pImage->pvBackendData, pUuid);
2596 } while (0);
2597
2598 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
2599 return rc;
2600}
2601
2602/**
2603 * Set the image's UUID. Should not be used by normal applications.
2604 *
2605 * @returns VBox status code.
2606 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2607 * @param pDisk Pointer to HDD container.
2608 * @param nImage Image number, counts from 0. 0 is always base image of container.
2609 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
2610 */
2611VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
2612{
2613 int rc;
2614
2615 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
2616 pDisk, nImage, pUuid, pUuid));
2617 do
2618 {
2619 /* sanity check */
2620 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2621 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2622
2623 AssertMsgBreak(VALID_PTR(pUuid) || pUuid == NULL,
2624 ("pUuid=%#p\n", pUuid),
2625 rc = VERR_INVALID_PARAMETER);
2626
2627 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2628 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2629
2630 RTUUID Uuid;
2631 if (!pUuid)
2632 {
2633 RTUuidCreate(&Uuid);
2634 pUuid = &Uuid;
2635 }
2636 rc = pImage->Backend->pfnSetUuid(pImage->pvBackendData, pUuid);
2637 } while (0);
2638
2639 LogFlowFunc(("returns %Vrc\n", rc));
2640 return rc;
2641}
2642
2643/**
2644 * Get last modification UUID of image in HDD container.
2645 *
2646 * @returns VBox status code.
2647 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2648 * @param pDisk Pointer to HDD container.
2649 * @param nImage Image number, counts from 0. 0 is always base image of container.
2650 * @param pUuid Where to store the image modification UUID.
2651 */
2652VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
2653{
2654 int rc = VINF_SUCCESS;
2655
2656 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
2657 do
2658 {
2659 /* sanity check */
2660 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2661 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2662
2663 /* Check arguments. */
2664 AssertMsgBreak(VALID_PTR(pUuid),
2665 ("pUuid=%#p\n", pUuid),
2666 rc = VERR_INVALID_PARAMETER);
2667
2668 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2669 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2670
2671 rc = pImage->Backend->pfnGetModificationUuid(pImage->pvBackendData,
2672 pUuid);
2673 } while (0);
2674
2675 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
2676 return rc;
2677}
2678
2679/**
2680 * Set the image's last modification UUID. Should not be used by normal applications.
2681 *
2682 * @returns VBox status code.
2683 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2684 * @param pDisk Pointer to HDD container.
2685 * @param nImage Image number, counts from 0. 0 is always base image of container.
2686 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
2687 */
2688VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
2689{
2690 int rc;
2691
2692 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
2693 pDisk, nImage, pUuid, pUuid));
2694 do
2695 {
2696 /* sanity check */
2697 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2698 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2699
2700 /* Check arguments. */
2701 AssertMsgBreak(VALID_PTR(pUuid) || pUuid == NULL,
2702 ("pUuid=%#p\n", pUuid),
2703 rc = VERR_INVALID_PARAMETER);
2704
2705 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2706 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2707
2708 RTUUID Uuid;
2709 if (!pUuid)
2710 {
2711 RTUuidCreate(&Uuid);
2712 pUuid = &Uuid;
2713 }
2714 rc = pImage->Backend->pfnSetModificationUuid(pImage->pvBackendData,
2715 pUuid);
2716 } while (0);
2717
2718 LogFlowFunc(("returns %Vrc\n", rc));
2719 return rc;
2720}
2721
2722/**
2723 * Get parent UUID of image in HDD container.
2724 *
2725 * @returns VBox status code.
2726 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2727 * @param pDisk Pointer to HDD container.
2728 * @param nImage Image number, counts from 0. 0 is always base image of container.
2729 * @param pUuid Where to store the parent image UUID.
2730 */
2731VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
2732 PRTUUID pUuid)
2733{
2734 int rc = VINF_SUCCESS;
2735
2736 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
2737 do
2738 {
2739 /* sanity check */
2740 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2741 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2742
2743 /* Check arguments. */
2744 AssertMsgBreak(VALID_PTR(pUuid),
2745 ("pUuid=%#p\n", pUuid),
2746 rc = VERR_INVALID_PARAMETER);
2747
2748 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2749 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2750
2751 rc = pImage->Backend->pfnGetParentUuid(pImage->pvBackendData, pUuid);
2752 } while (0);
2753
2754 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
2755 return rc;
2756}
2757
2758/**
2759 * Set the image's parent UUID. Should not be used by normal applications.
2760 *
2761 * @returns VBox status code.
2762 * @param pDisk Pointer to HDD container.
2763 * @param nImage Image number, counts from 0. 0 is always base image of container.
2764 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
2765 */
2766VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
2767 PCRTUUID pUuid)
2768{
2769 int rc;
2770
2771 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
2772 pDisk, nImage, pUuid, pUuid));
2773 do
2774 {
2775 /* sanity check */
2776 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2777 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2778
2779 /* Check arguments. */
2780 AssertMsgBreak(VALID_PTR(pUuid) || pUuid == NULL,
2781 ("pUuid=%#p\n", pUuid),
2782 rc = VERR_INVALID_PARAMETER);
2783
2784 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2785 AssertBreak(VALID_PTR(pImage), rc = VERR_VDI_IMAGE_NOT_FOUND);
2786
2787 RTUUID Uuid;
2788 if (!pUuid)
2789 {
2790 RTUuidCreate(&Uuid);
2791 pUuid = &Uuid;
2792 }
2793 rc = pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, pUuid);
2794 } while (0);
2795
2796 LogFlowFunc(("returns %Vrc\n", rc));
2797 return rc;
2798}
2799
2800
2801/**
2802 * Debug helper - dumps all opened images in HDD container into the log file.
2803 *
2804 * @param pDisk Pointer to HDD container.
2805 */
2806VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
2807{
2808 do
2809 {
2810 /* sanity check */
2811 AssertBreak(VALID_PTR(pDisk), );
2812 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2813
2814 RTLogPrintf("--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
2815 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
2816 {
2817 RTLogPrintf("Dumping VD image \"%s\" (Backend=%s)\n",
2818 pImage->Backend->pszBackendName, pImage->pszFilename);
2819 pImage->Backend->pfnDump(pImage->pvBackendData);
2820 }
2821 } while (0);
2822}
2823
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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