VirtualBox

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

Last change on this file since 14855 was 14855, checked in by vboxsync, 16 years ago

#3162: Fixed a newly introduced memory leak. Added VDShutdown() to test cases.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 131.9 KB
Line 
1/* $Id: VBoxHDD-new.cpp 14855 2008-12-01 13:28:58Z vboxsync $ */
2/** @file
3 * VBoxHDD - VBox HDD Container implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#define LOG_GROUP LOG_GROUP_VD
26#include <VBox/VBoxHDD-new.h>
27#include <VBox/err.h>
28#include <VBox/sup.h>
29#include <VBox/log.h>
30
31#include <iprt/alloc.h>
32#include <iprt/assert.h>
33#include <iprt/uuid.h>
34#include <iprt/file.h>
35#include <iprt/string.h>
36#include <iprt/asm.h>
37#include <iprt/ldr.h>
38#include <iprt/dir.h>
39#include <iprt/path.h>
40#include <iprt/param.h>
41
42#include "VBoxHDD-newInternal.h"
43
44
45#define VBOXHDDDISK_SIGNATURE 0x6f0e2a7d
46
47/** Buffer size used for merging images. */
48#define VD_MERGE_BUFFER_SIZE (1024 * 1024)
49
50/**
51 * VBox HDD Container image descriptor.
52 */
53typedef struct VDIMAGE
54{
55 /** Link to parent image descriptor, if any. */
56 struct VDIMAGE *pPrev;
57 /** Link to child image descriptor, if any. */
58 struct VDIMAGE *pNext;
59 /** Container base filename. (UTF-8) */
60 char *pszFilename;
61 /** Data managed by the backend which keeps the actual info. */
62 void *pvBackendData;
63 /** Cached sanitized image type. */
64 VDIMAGETYPE enmImageType;
65 /** Image open flags (only those handled generically in this code and which
66 * the backends will never ever see). */
67 unsigned uOpenFlags;
68
69 /** Function pointers for the various backend methods. */
70 PCVBOXHDDBACKEND Backend;
71
72 /** Pointer to list of VD interfaces, per-image. */
73 PVDINTERFACE pVDIfsImage;
74} VDIMAGE, *PVDIMAGE;
75
76/**
77 * uModified bit flags.
78 */
79#define VD_IMAGE_MODIFIED_FLAG RT_BIT(0)
80#define VD_IMAGE_MODIFIED_FIRST RT_BIT(1)
81#define VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE RT_BIT(2)
82
83
84/**
85 * VBox HDD Container main structure, private part.
86 */
87struct VBOXHDD
88{
89 /** Structure signature (VBOXHDDDISK_SIGNATURE). */
90 uint32_t u32Signature;
91
92 /** Number of opened images. */
93 unsigned cImages;
94
95 /** Base image. */
96 PVDIMAGE pBase;
97
98 /** Last opened image in the chain.
99 * The same as pBase if only one image is used. */
100 PVDIMAGE pLast;
101
102 /** Flags representing the modification state. */
103 unsigned uModified;
104
105 /** Cached size of this disk. */
106 uint64_t cbSize;
107 /** Cached PCHS geometry for this disk. */
108 PDMMEDIAGEOMETRY PCHSGeometry;
109 /** Cached LCHS geometry for this disk. */
110 PDMMEDIAGEOMETRY LCHSGeometry;
111
112 /** Pointer to list of VD interfaces, per-disk. */
113 PVDINTERFACE pVDIfsDisk;
114 /** Pointer to the common interface structure for error reporting. */
115 PVDINTERFACE pInterfaceError;
116 /** Pointer to the error interface we use if available. */
117 PVDINTERFACEERROR pInterfaceErrorCallbacks;
118};
119
120
121extern VBOXHDDBACKEND g_RawBackend;
122extern VBOXHDDBACKEND g_VmdkBackend;
123extern VBOXHDDBACKEND g_VDIBackend;
124extern VBOXHDDBACKEND g_VhdBackend;
125#ifdef VBOX_WITH_ISCSI
126extern VBOXHDDBACKEND g_ISCSIBackend;
127#endif
128
129static unsigned g_cBackends = 0;
130static PVBOXHDDBACKEND *g_apBackends = NULL;
131static PVBOXHDDBACKEND aStaticBackends[] =
132{
133 &g_RawBackend,
134 &g_VmdkBackend,
135 &g_VDIBackend,
136 &g_VhdBackend
137#ifdef VBOX_WITH_ISCSI
138 ,&g_ISCSIBackend
139#endif
140};
141
142/**
143 * internal: add several backends.
144 */
145static int vdAddBackends(PVBOXHDDBACKEND *ppBackends, unsigned cBackends)
146{
147 PVBOXHDDBACKEND *pTmp = (PVBOXHDDBACKEND*)RTMemRealloc(g_apBackends,
148 (g_cBackends + cBackends) * sizeof(PVBOXHDDBACKEND));
149 if (RT_UNLIKELY(!pTmp))
150 return VERR_NO_MEMORY;
151 g_apBackends = pTmp;
152 memcpy(&g_apBackends[g_cBackends], ppBackends, cBackends * sizeof(PVBOXHDDBACKEND));
153 g_cBackends += cBackends;
154 return VINF_SUCCESS;
155}
156
157/**
158 * internal: add single backend.
159 */
160DECLINLINE(int) vdAddBackend(PVBOXHDDBACKEND pBackend)
161{
162 return vdAddBackends(&pBackend, 1);
163}
164
165/**
166 * internal: issue error message.
167 */
168static int vdError(PVBOXHDD pDisk, int rc, RT_SRC_POS_DECL,
169 const char *pszFormat, ...)
170{
171 va_list va;
172 va_start(va, pszFormat);
173 if (pDisk->pInterfaceErrorCallbacks)
174 pDisk->pInterfaceErrorCallbacks->pfnError(pDisk->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
175 va_end(va);
176 return rc;
177}
178
179/**
180 * internal: find image format backend.
181 */
182static int vdFindBackend(const char *pszBackend, PCVBOXHDDBACKEND *ppBackend)
183{
184 int rc = VINF_SUCCESS;
185 PCVBOXHDDBACKEND pBackend = NULL;
186
187 if (!g_apBackends)
188 VDInit();
189
190 for (unsigned i = 0; i < g_cBackends; i++)
191 {
192 if (!strcmp(pszBackend, g_apBackends[i]->pszBackendName))
193 {
194 pBackend = g_apBackends[i];
195 break;
196 }
197 }
198 *ppBackend = pBackend;
199 return rc;
200}
201
202/**
203 * internal: add image structure to the end of images list.
204 */
205static void vdAddImageToList(PVBOXHDD pDisk, PVDIMAGE pImage)
206{
207 pImage->pPrev = NULL;
208 pImage->pNext = NULL;
209
210 if (pDisk->pBase)
211 {
212 Assert(pDisk->cImages > 0);
213 pImage->pPrev = pDisk->pLast;
214 pDisk->pLast->pNext = pImage;
215 pDisk->pLast = pImage;
216 }
217 else
218 {
219 Assert(pDisk->cImages == 0);
220 pDisk->pBase = pImage;
221 pDisk->pLast = pImage;
222 }
223
224 pDisk->cImages++;
225}
226
227/**
228 * internal: remove image structure from the images list.
229 */
230static void vdRemoveImageFromList(PVBOXHDD pDisk, PVDIMAGE pImage)
231{
232 Assert(pDisk->cImages > 0);
233
234 if (pImage->pPrev)
235 pImage->pPrev->pNext = pImage->pNext;
236 else
237 pDisk->pBase = pImage->pNext;
238
239 if (pImage->pNext)
240 pImage->pNext->pPrev = pImage->pPrev;
241 else
242 pDisk->pLast = pImage->pPrev;
243
244 pImage->pPrev = NULL;
245 pImage->pNext = NULL;
246
247 pDisk->cImages--;
248}
249
250/**
251 * internal: find image by index into the images list.
252 */
253static PVDIMAGE vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
254{
255 PVDIMAGE pImage = pDisk->pBase;
256 if (nImage == VD_LAST_IMAGE)
257 return pDisk->pLast;
258 while (pImage && nImage)
259 {
260 pImage = pImage->pNext;
261 nImage--;
262 }
263 return pImage;
264}
265
266/**
267 * internal: read the specified amount of data in whatever blocks the backend
268 * will give us.
269 */
270static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
271 void *pvBuf, size_t cbRead)
272{
273 int rc;
274 size_t cbThisRead;
275
276 /* Loop until all read. */
277 do
278 {
279 /* Search for image with allocated block. Do not attempt to read more
280 * than the previous reads marked as valid. Otherwise this would return
281 * stale data when different block sizes are used for the images. */
282 cbThisRead = cbRead;
283 rc = VERR_VDI_BLOCK_FREE;
284 for (PVDIMAGE pCurrImage = pImage;
285 pCurrImage != NULL && rc == VERR_VDI_BLOCK_FREE;
286 pCurrImage = pCurrImage->pPrev)
287 {
288 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
289 uOffset, pvBuf, cbThisRead,
290 &cbThisRead);
291 }
292
293 /* No image in the chain contains the data for the block. */
294 if (rc == VERR_VDI_BLOCK_FREE)
295 {
296 memset(pvBuf, '\0', cbThisRead);
297 rc = VINF_SUCCESS;
298 }
299
300 cbRead -= cbThisRead;
301 uOffset += cbThisRead;
302 pvBuf = (char *)pvBuf + cbThisRead;
303 } while (cbRead != 0 && RT_SUCCESS(rc));
304
305 return rc;
306}
307
308/**
309 * internal: mark the disk as not modified.
310 */
311static void vdResetModifiedFlag(PVBOXHDD pDisk)
312{
313 if (pDisk->uModified & VD_IMAGE_MODIFIED_FLAG)
314 {
315 /* generate new last-modified uuid */
316 if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
317 {
318 RTUUID Uuid;
319
320 RTUuidCreate(&Uuid);
321 pDisk->pLast->Backend->pfnSetModificationUuid(pDisk->pLast->pvBackendData,
322 &Uuid);
323 }
324
325 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FLAG;
326 }
327}
328
329/**
330 * internal: mark the disk as modified.
331 */
332static void vdSetModifiedFlag(PVBOXHDD pDisk)
333{
334 pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
335 if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
336 {
337 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
338
339 /* First modify, so create a UUID and ensure it's written to disk. */
340 vdResetModifiedFlag(pDisk);
341
342 if (!(pDisk->uModified | VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
343 pDisk->pLast->Backend->pfnFlush(pDisk->pLast->pvBackendData);
344 }
345}
346
347/**
348 * internal: write a complete block (only used for diff images), taking the
349 * remaining data from parent images. This implementation does not optimize
350 * anything (except that it tries to read only that portions from parent
351 * images that are really needed).
352 */
353static int vdWriteHelperStandard(PVBOXHDD pDisk, PVDIMAGE pImage,
354 uint64_t uOffset, size_t cbWrite,
355 size_t cbThisWrite, size_t cbPreRead,
356 size_t cbPostRead, const void *pvBuf,
357 void *pvTmp)
358{
359 int rc = VINF_SUCCESS;
360
361 /* Read the data that goes before the write to fill the block. */
362 if (cbPreRead)
363 {
364 rc = vdReadHelper(pDisk, pImage->pPrev, uOffset - cbPreRead, pvTmp,
365 cbPreRead);
366 if (RT_FAILURE(rc))
367 return rc;
368 }
369
370 /* Copy the data to the right place in the buffer. */
371 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
372
373 /* Read the data that goes after the write to fill the block. */
374 if (cbPostRead)
375 {
376 /* If we have data to be written, use that instead of reading
377 * data from the image. */
378 size_t cbWriteCopy;
379 if (cbWrite > cbThisWrite)
380 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
381 else
382 cbWriteCopy = 0;
383 /* Figure out how much we cannnot read from the image, because
384 * the last block to write might exceed the nominal size of the
385 * image for technical reasons. */
386 size_t cbFill;
387 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
388 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
389 else
390 cbFill = 0;
391 /* The rest must be read from the image. */
392 size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
393
394 /* Now assemble the remaining data. */
395 if (cbWriteCopy)
396 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
397 (char *)pvBuf + cbThisWrite, cbWriteCopy);
398 if (cbReadImage)
399 rc = vdReadHelper(pDisk, pImage->pPrev,
400 uOffset + cbThisWrite + cbWriteCopy,
401 (char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy,
402 cbReadImage);
403 if (RT_FAILURE(rc))
404 return rc;
405 /* Zero out the remainder of this block. Will never be visible, as this
406 * is beyond the limit of the image. */
407 if (cbFill)
408 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
409 '\0', cbFill);
410 }
411
412 /* Write the full block to the virtual disk. */
413 rc = pImage->Backend->pfnWrite(pImage->pvBackendData,
414 uOffset - cbPreRead, pvTmp,
415 cbPreRead + cbThisWrite + cbPostRead,
416 NULL, &cbPreRead, &cbPostRead, 0);
417 Assert(rc != VERR_VDI_BLOCK_FREE);
418 Assert(cbPreRead == 0);
419 Assert(cbPostRead == 0);
420
421 return rc;
422}
423
424/**
425 * internal: write a complete block (only used for diff images), taking the
426 * remaining data from parent images. This implementation optimizes out writes
427 * that do not change the data relative to the state as of the parent images.
428 * All backends which support differential/growing images support this.
429 */
430static int vdWriteHelperOptimized(PVBOXHDD pDisk, PVDIMAGE pImage,
431 uint64_t uOffset, size_t cbWrite,
432 size_t cbThisWrite, size_t cbPreRead,
433 size_t cbPostRead, const void *pvBuf,
434 void *pvTmp)
435{
436 size_t cbFill = 0;
437 size_t cbWriteCopy = 0;
438 size_t cbReadImage = 0;
439 int rc;
440
441 if (cbPostRead)
442 {
443 /* Figure out how much we cannnot read from the image, because
444 * the last block to write might exceed the nominal size of the
445 * image for technical reasons. */
446 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
447 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
448
449 /* If we have data to be written, use that instead of reading
450 * data from the image. */
451 if (cbWrite > cbThisWrite)
452 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
453
454 /* The rest must be read from the image. */
455 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
456 }
457
458 /* Read the entire data of the block so that we can compare whether it will
459 * be modified by the write or not. */
460 rc = vdReadHelper(pDisk, pImage->pPrev, uOffset - cbPreRead, pvTmp,
461 cbPreRead + cbThisWrite + cbPostRead - cbFill);
462 if (RT_FAILURE(rc))
463 return rc;
464
465 /* Check if the write would modify anything in this block. */
466 if ( !memcmp((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite)
467 && (!cbWriteCopy || !memcmp((char *)pvTmp + cbPreRead + cbThisWrite,
468 (char *)pvBuf + cbThisWrite, cbWriteCopy)))
469 {
470 /* Block is completely unchanged, so no need to write anything. */
471 return VINF_SUCCESS;
472 }
473
474 /* Copy the data to the right place in the buffer. */
475 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
476
477 /* Handle the data that goes after the write to fill the block. */
478 if (cbPostRead)
479 {
480 /* Now assemble the remaining data. */
481 if (cbWriteCopy)
482 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
483 (char *)pvBuf + cbThisWrite, cbWriteCopy);
484 /* Zero out the remainder of this block. Will never be visible, as this
485 * is beyond the limit of the image. */
486 if (cbFill)
487 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
488 '\0', cbFill);
489 }
490
491 /* Write the full block to the virtual disk. */
492 rc = pImage->Backend->pfnWrite(pImage->pvBackendData,
493 uOffset - cbPreRead, pvTmp,
494 cbPreRead + cbThisWrite + cbPostRead,
495 NULL, &cbPreRead, &cbPostRead, 0);
496 Assert(rc != VERR_VDI_BLOCK_FREE);
497 Assert(cbPreRead == 0);
498 Assert(cbPostRead == 0);
499
500 return rc;
501}
502
503/**
504 * internal: write buffer to the image, taking care of block boundaries and
505 * write optimizations.
506 */
507static int vdWriteHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
508 const void *pvBuf, size_t cbWrite)
509{
510 int rc;
511 unsigned fWrite;
512 size_t cbThisWrite;
513 size_t cbPreRead, cbPostRead;
514
515 /* Loop until all written. */
516 do
517 {
518 /* Try to write the possibly partial block to the last opened image.
519 * This works when the block is already allocated in this image or
520 * if it is a full-block write (and allocation isn't suppressed below).
521 * For image formats which don't support zero blocks, it's beneficial
522 * to avoid unnecessarily allocating unchanged blocks. This prevents
523 * unwanted expanding of images. VMDK is an example. */
524 cbThisWrite = cbWrite;
525 fWrite = (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
526 ? 0 : VD_WRITE_NO_ALLOC;
527 rc = pImage->Backend->pfnWrite(pImage->pvBackendData, uOffset, pvBuf,
528 cbThisWrite, &cbThisWrite, &cbPreRead,
529 &cbPostRead, fWrite);
530 if (rc == VERR_VDI_BLOCK_FREE)
531 {
532 void *pvTmp = RTMemTmpAlloc(cbPreRead + cbThisWrite + cbPostRead);
533 AssertBreakStmt(VALID_PTR(pvTmp), rc = VERR_NO_MEMORY);
534
535 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME))
536 {
537 /* Optimized write, suppress writing to a so far unallocated
538 * block if the data is in fact not changed. */
539 rc = vdWriteHelperOptimized(pDisk, pImage, uOffset, cbWrite,
540 cbThisWrite, cbPreRead, cbPostRead,
541 pvBuf, pvTmp);
542 }
543 else
544 {
545 /* Normal write, not optimized in any way. The block will
546 * be written no matter what. This will usually (unless the
547 * backend has some further optimization enabled) cause the
548 * block to be allocated. */
549 rc = vdWriteHelperStandard(pDisk, pImage, uOffset, cbWrite,
550 cbThisWrite, cbPreRead, cbPostRead,
551 pvBuf, pvTmp);
552 }
553 RTMemTmpFree(pvTmp);
554 if (RT_FAILURE(rc))
555 break;
556 }
557
558 cbWrite -= cbThisWrite;
559 uOffset += cbThisWrite;
560 pvBuf = (char *)pvBuf + cbThisWrite;
561 } while (cbWrite != 0 && RT_SUCCESS(rc));
562
563 return rc;
564}
565
566
567/**
568 * internal: scans plugin directory and loads the backends have been found.
569 */
570static int vdLoadDynamicBackends()
571{
572 int rc = VINF_SUCCESS;
573 PRTDIR pPluginDir = NULL;
574
575 /* Enumerate plugin backends. */
576 char szPath[RTPATH_MAX];
577 rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
578 if (RT_FAILURE(rc))
579 return rc;
580
581 /* To get all entries with VBoxHDD as prefix. */
582 char *pszPluginFilter;
583 rc = RTStrAPrintf(&pszPluginFilter, "%s/%s*", szPath,
584 VBOX_HDDFORMAT_PLUGIN_PREFIX);
585 if (RT_FAILURE(rc))
586 {
587 rc = VERR_NO_MEMORY;
588 return rc;
589 }
590
591 PRTDIRENTRYEX pPluginDirEntry = NULL;
592 size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
593 /* The plugins are in the same directory as the other shared libs. */
594 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
595 if (RT_FAILURE(rc))
596 {
597 /* On Windows the above immediately signals that there are no
598 * files matching, while on other platforms enumerating the
599 * files below fails. Either way: no plugins. */
600 goto out;
601 }
602
603 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
604 if (!pPluginDirEntry)
605 {
606 rc = VERR_NO_MEMORY;
607 goto out;
608 }
609
610 while ((rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING)) != VERR_NO_MORE_FILES)
611 {
612 RTLDRMOD hPlugin = NIL_RTLDRMOD;
613 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad = NULL;
614 PVBOXHDDBACKEND pBackend = NULL;
615 char *pszPluginPath = NULL;
616
617 if (rc == VERR_BUFFER_OVERFLOW)
618 {
619 /* allocate new buffer. */
620 RTMemFree(pPluginDirEntry);
621 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
622 /* Retry. */
623 rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING);
624 if (RT_FAILURE(rc))
625 break;
626 }
627 else if (RT_FAILURE(rc))
628 break;
629
630 /* We got the new entry. */
631 if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
632 continue;
633
634 /* Prepend the path to the libraries. */
635 rc = RTStrAPrintf(&pszPluginPath, "%s/%s", szPath, pPluginDirEntry->szName);
636 if (RT_FAILURE(rc))
637 {
638 rc = VERR_NO_MEMORY;
639 break;
640 }
641
642 rc = SUPR3HardenedLdrLoad(pszPluginPath, &hPlugin);
643 if (RT_SUCCESS(rc))
644 {
645 rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad);
646 if (RT_FAILURE(rc) || !pfnHDDFormatLoad)
647 {
648 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnHDDFormat=%#p\n", VBOX_HDDFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnHDDFormatLoad));
649 if (RT_SUCCESS(rc))
650 rc = VERR_SYMBOL_NOT_FOUND;
651 }
652
653 if (RT_SUCCESS(rc))
654 {
655 /* Get the function table. */
656 rc = pfnHDDFormatLoad(&pBackend);
657 if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VBOXHDDBACKEND))
658 {
659 pBackend->hPlugin = hPlugin;
660 vdAddBackend(pBackend);
661 }
662 else
663 LogFunc(("ignored plugin '%s': pBackend->cbSize=%d rc=%Rrc\n", pszPluginPath, pBackend->cbSize, rc));
664 }
665 else
666 LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszPluginPath, rc));
667
668 if (RT_FAILURE(rc))
669 RTLdrClose(hPlugin);
670 }
671 RTStrFree(pszPluginPath);
672 }
673out:
674 if (rc == VERR_NO_MORE_FILES)
675 rc = VINF_SUCCESS;
676 RTStrFree(pszPluginFilter);
677 if (pPluginDirEntry)
678 RTMemFree(pPluginDirEntry);
679 if (pPluginDir)
680 RTDirClose(pPluginDir);
681 return rc;
682}
683
684/**
685 * Initializes HDD backends.
686 *
687 * @returns VBox status code.
688 */
689VBOXDDU_DECL(int) VDInit()
690{
691 int rc = vdAddBackends(aStaticBackends, RT_ELEMENTS(aStaticBackends));
692 if (RT_SUCCESS(rc))
693 rc = vdLoadDynamicBackends();
694 LogRel(("VDInit finished\n"));
695 return rc;
696}
697
698/**
699 * Destroys loaded HDD backends.
700 *
701 * @returns VBox status code.
702 */
703VBOXDDU_DECL(int) VDShutdown()
704{
705 PVBOXHDDBACKEND *pBackends = g_apBackends;
706 unsigned cBackends = g_cBackends;
707
708 if (!pBackends)
709 return VERR_INTERNAL_ERROR;
710
711 g_cBackends = 0;
712 g_apBackends = NULL;
713
714 for (unsigned i = 0; i < cBackends; i++)
715 if (pBackends[i]->hPlugin != NIL_RTLDRMOD)
716 RTLdrClose(pBackends[i]->hPlugin);
717
718 RTMemFree(pBackends);
719 return VINF_SUCCESS;
720}
721
722
723
724/**
725 * Lists all HDD backends and their capabilities in a caller-provided buffer.
726 *
727 * @todo this code contains memory leaks, inconsistent (and probably buggy)
728 * allocation, and it lacks documentation what the caller needs to free.
729 *
730 * @returns VBox status code.
731 * VERR_BUFFER_OVERFLOW if not enough space is passed.
732 * @param cEntriesAlloc Number of list entries available.
733 * @param pEntries Pointer to array for the entries.
734 * @param pcEntriesUsed Number of entries returned.
735 */
736VBOXDDU_DECL(int) VDBackendInfo(unsigned cEntriesAlloc, PVDBACKENDINFO pEntries,
737 unsigned *pcEntriesUsed)
738{
739 int rc = VINF_SUCCESS;
740 PRTDIR pPluginDir = NULL;
741 unsigned cEntries = 0;
742
743 LogFlowFunc(("cEntriesAlloc=%u pEntries=%#p pcEntriesUsed=%#p\n", cEntriesAlloc, pEntries, pcEntriesUsed));
744 /* Check arguments. */
745 AssertMsgReturn(cEntriesAlloc,
746 ("cEntriesAlloc=%u\n", cEntriesAlloc),
747 VERR_INVALID_PARAMETER);
748 AssertMsgReturn(VALID_PTR(pEntries),
749 ("pEntries=%#p\n", pEntries),
750 VERR_INVALID_PARAMETER);
751 AssertMsgReturn(VALID_PTR(pcEntriesUsed),
752 ("pcEntriesUsed=%#p\n", pcEntriesUsed),
753 VERR_INVALID_PARAMETER);
754 if (!g_apBackends)
755 VDInit();
756
757 if (cEntriesAlloc < g_cBackends)
758 {
759 *pcEntriesUsed = g_cBackends;
760 return VERR_BUFFER_OVERFLOW;
761 }
762
763 for (unsigned i = 0; i < g_cBackends; i++)
764 {
765 pEntries[i].pszBackend = g_apBackends[i]->pszBackendName;
766 pEntries[i].uBackendCaps = g_apBackends[i]->uBackendCaps;
767 pEntries[i].papszFileExtensions = g_apBackends[i]->papszFileExtensions;
768 pEntries[i].paConfigInfo = g_apBackends[i]->paConfigInfo;
769 }
770
771 LogFlowFunc(("returns %Rrc *pcEntriesUsed=%u\n", rc, cEntries));
772 *pcEntriesUsed = g_cBackends;
773 return rc;
774}
775
776/**
777 * Lists the capablities of a backend indentified by its name.
778 * Free all returned names with RTStrFree() when you no longer need them.
779 *
780 * @returns VBox status code.
781 * @param pszBackend The backend name.
782 * @param pEntries Pointer to an entry.
783 */
784VBOXDDU_DECL(int) VDBackendInfoOne(const char *pszBackend, PVDBACKENDINFO pEntry)
785{
786 LogFlowFunc(("pszBackend=%#p pEntry=%#p\n", pszBackend, pEntry));
787 /* Check arguments. */
788 AssertMsgReturn(VALID_PTR(pszBackend),
789 ("pszBackend=%#p\n", pszBackend),
790 VERR_INVALID_PARAMETER);
791 AssertMsgReturn(VALID_PTR(pEntry),
792 ("pEntry=%#p\n", pEntry),
793 VERR_INVALID_PARAMETER);
794 if (!g_apBackends)
795 VDInit();
796
797 /* Go through loaded backends. */
798 for (unsigned i = 0; i < g_cBackends; i++)
799 {
800 if (!strcmp(pszBackend, g_apBackends[i]->pszBackendName))
801 {
802 pEntry->pszBackend = g_apBackends[i]->pszBackendName;
803 pEntry->uBackendCaps = g_apBackends[i]->uBackendCaps;
804 pEntry->papszFileExtensions = g_apBackends[i]->papszFileExtensions;
805 pEntry->paConfigInfo = g_apBackends[i]->paConfigInfo;
806 return VINF_SUCCESS;
807 }
808 }
809
810 return VERR_NOT_FOUND;
811}
812
813/**
814 * Allocates and initializes an empty HDD container.
815 * No image files are opened.
816 *
817 * @returns VBox status code.
818 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
819 * @param ppDisk Where to store the reference to HDD container.
820 */
821VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, PVBOXHDD *ppDisk)
822{
823 int rc = VINF_SUCCESS;
824 PVBOXHDD pDisk = NULL;
825
826 LogFlowFunc(("pVDIfsDisk=%#p\n", pVDIfsDisk));
827 do
828 {
829 /* Check arguments. */
830 AssertMsgBreakStmt(VALID_PTR(ppDisk),
831 ("ppDisk=%#p\n", ppDisk),
832 rc = VERR_INVALID_PARAMETER);
833
834 pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
835 if (pDisk)
836 {
837 pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
838 pDisk->cImages = 0;
839 pDisk->pBase = NULL;
840 pDisk->pLast = NULL;
841 pDisk->cbSize = 0;
842 pDisk->PCHSGeometry.cCylinders = 0;
843 pDisk->PCHSGeometry.cHeads = 0;
844 pDisk->PCHSGeometry.cSectors = 0;
845 pDisk->LCHSGeometry.cCylinders = 0;
846 pDisk->LCHSGeometry.cHeads = 0;
847 pDisk->LCHSGeometry.cSectors = 0;
848 pDisk->pVDIfsDisk = pVDIfsDisk;
849 pDisk->pInterfaceError = NULL;
850 pDisk->pInterfaceErrorCallbacks = NULL;
851
852 pDisk->pInterfaceError = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_ERROR);
853 if (pDisk->pInterfaceError)
854 pDisk->pInterfaceErrorCallbacks = VDGetInterfaceError(pDisk->pInterfaceError);
855 *ppDisk = pDisk;
856 }
857 else
858 {
859 rc = VERR_NO_MEMORY;
860 break;
861 }
862 } while (0);
863
864 LogFlowFunc(("returns %Rrc (pDisk=%#p)\n", rc, pDisk));
865 return rc;
866}
867
868/**
869 * Destroys HDD container.
870 * If container has opened image files they will be closed.
871 *
872 * @param pDisk Pointer to HDD container.
873 */
874VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk)
875{
876 LogFlowFunc(("pDisk=%#p\n", pDisk));
877 do
878 {
879 /* sanity check */
880 AssertPtrBreak(pDisk);
881 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
882 VDCloseAll(pDisk);
883 RTMemFree(pDisk);
884 } while (0);
885 LogFlowFunc(("returns\n"));
886}
887
888/**
889 * Try to get the backend name which can use this image.
890 *
891 * @returns VBox status code.
892 * VINF_SUCCESS if a plugin was found.
893 * ppszFormat contains the string which can be used as backend name.
894 * VERR_NOT_SUPPORTED if no backend was found.
895 * @param pszFilename Name of the image file for which the backend is queried.
896 * @param ppszFormat Receives pointer of the UTF-8 string which contains the format name.
897 * The returned pointer must be freed using RTStrFree().
898 */
899VBOXDDU_DECL(int) VDGetFormat(const char *pszFilename, char **ppszFormat)
900{
901 int rc = VERR_NOT_SUPPORTED;
902
903 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
904 /* Check arguments. */
905 AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
906 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
907 VERR_INVALID_PARAMETER);
908 AssertMsgReturn(VALID_PTR(ppszFormat),
909 ("ppszFormat=%#p\n", ppszFormat),
910 VERR_INVALID_PARAMETER);
911
912 if (!g_apBackends)
913 VDInit();
914
915 /* Find the backend supporting this file format. */
916 for (unsigned i = 0; i < g_cBackends; i++)
917 {
918 if (g_apBackends[i]->pfnCheckIfValid)
919 {
920 rc = g_apBackends[i]->pfnCheckIfValid(pszFilename);
921 if (RT_SUCCESS(rc))
922 {
923 /* Copy the name into the new string. */
924 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
925 if (!pszFormat)
926 {
927 rc = VERR_NO_MEMORY;
928 break;
929 }
930 *ppszFormat = pszFormat;
931 rc = VINF_SUCCESS;
932 break;
933 }
934 }
935 }
936
937 LogFlowFunc(("returns %Rrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
938 return rc;
939}
940
941/**
942 * Opens an image file.
943 *
944 * The first opened image file in HDD container must have a base image type,
945 * others (next opened images) must be a differencing or undo images.
946 * Linkage is checked for differencing image to be in consistence with the previously opened image.
947 * When another differencing image is opened and the last image was opened in read/write access
948 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
949 * other processes to use images in read-only mode too.
950 *
951 * Note that the image is opened in read-only mode if a read/write open is not possible.
952 * Use VDIsReadOnly to check open mode.
953 *
954 * @returns VBox status code.
955 * @param pDisk Pointer to HDD container.
956 * @param pszBackend Name of the image file backend to use.
957 * @param pszFilename Name of the image file to open.
958 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
959 * @param pVDIfsImage Pointer to the per-image VD interface list.
960 */
961VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
962 const char *pszFilename, unsigned uOpenFlags,
963 PVDINTERFACE pVDIfsImage)
964{
965 int rc = VINF_SUCCESS;
966 PVDIMAGE pImage = NULL;
967
968 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x\n, pVDIfsImage=%#p",
969 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsImage));
970 do
971 {
972 /* sanity check */
973 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
974 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
975
976 /* Check arguments. */
977 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
978 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
979 rc = VERR_INVALID_PARAMETER);
980 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
981 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
982 rc = VERR_INVALID_PARAMETER);
983 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
984 ("uOpenFlags=%#x\n", uOpenFlags),
985 rc = VERR_INVALID_PARAMETER);
986
987 /* Set up image descriptor. */
988 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
989 if (!pImage)
990 {
991 rc = VERR_NO_MEMORY;
992 break;
993 }
994 pImage->pszFilename = RTStrDup(pszFilename);
995 if (!pImage->pszFilename)
996 {
997 rc = VERR_NO_MEMORY;
998 break;
999 }
1000 pImage->pVDIfsImage = pVDIfsImage;
1001
1002 rc = vdFindBackend(pszBackend, &pImage->Backend);
1003 if (RT_FAILURE(rc))
1004 break;
1005 if (!pImage->Backend)
1006 {
1007 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1008 N_("VD: unknown backend name '%s'"), pszBackend);
1009 break;
1010 }
1011
1012 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1013 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
1014 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1015 pDisk->pVDIfsDisk,
1016 pImage->pVDIfsImage,
1017 &pImage->pvBackendData);
1018 /* If the open in read-write mode failed, retry in read-only mode. */
1019 if (RT_FAILURE(rc))
1020 {
1021 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
1022 && (rc == VERR_ACCESS_DENIED
1023 || rc == VERR_PERMISSION_DENIED
1024 || rc == VERR_WRITE_PROTECT
1025 || rc == VERR_SHARING_VIOLATION
1026 || rc == VERR_FILE_LOCK_FAILED))
1027 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
1028 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
1029 | VD_OPEN_FLAGS_READONLY,
1030 pDisk->pVDIfsDisk,
1031 pImage->pVDIfsImage,
1032 &pImage->pvBackendData);
1033 if (RT_FAILURE(rc))
1034 {
1035 rc = vdError(pDisk, rc, RT_SRC_POS,
1036 N_("VD: error opening image file '%s'"), pszFilename);
1037 break;
1038 }
1039 }
1040
1041 VDIMAGETYPE enmImageType;
1042 rc = pImage->Backend->pfnGetImageType(pImage->pvBackendData,
1043 &enmImageType);
1044 /* Check image type. As the image itself has only partial knowledge
1045 * whether it's a base image or not, this info is derived here. The
1046 * base image can be fixed or normal, all others must be normal or
1047 * diff images. Some image formats don't distinguish between normal
1048 * and diff images, so this must be corrected here. */
1049 if (RT_FAILURE(rc))
1050 enmImageType = VD_IMAGE_TYPE_INVALID;
1051 if ( RT_SUCCESS(rc)
1052 && !(uOpenFlags & VD_OPEN_FLAGS_INFO))
1053 {
1054 if ( pDisk->cImages == 0
1055 && enmImageType != VD_IMAGE_TYPE_FIXED
1056 && enmImageType != VD_IMAGE_TYPE_NORMAL)
1057 {
1058 rc = VERR_VDI_INVALID_TYPE;
1059 break;
1060 }
1061 else if (pDisk->cImages != 0)
1062 {
1063 if ( enmImageType != VD_IMAGE_TYPE_NORMAL
1064 && enmImageType != VD_IMAGE_TYPE_DIFF)
1065 {
1066 rc = VERR_VDI_INVALID_TYPE;
1067 break;
1068 }
1069 else
1070 enmImageType = VD_IMAGE_TYPE_DIFF;
1071 }
1072 }
1073 pImage->enmImageType = enmImageType;
1074
1075 /* Force sane optimization settings. It's not worth avoiding writes
1076 * to fixed size images. The overhead would have almost no payback. */
1077 if (enmImageType == VD_IMAGE_TYPE_FIXED)
1078 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
1079
1080 /** @todo optionally check UUIDs */
1081
1082 int rc2;
1083
1084 /* Cache disk information. */
1085 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
1086
1087 /* Cache PCHS geometry. */
1088 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1089 &pDisk->PCHSGeometry);
1090 if (RT_FAILURE(rc2))
1091 {
1092 pDisk->PCHSGeometry.cCylinders = 0;
1093 pDisk->PCHSGeometry.cHeads = 0;
1094 pDisk->PCHSGeometry.cSectors = 0;
1095 }
1096 else
1097 {
1098 /* Make sure the PCHS geometry is properly clipped. */
1099 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1100 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1101 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1102 }
1103
1104 /* Cache LCHS geometry. */
1105 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1106 &pDisk->LCHSGeometry);
1107 if (RT_FAILURE(rc2))
1108 {
1109 pDisk->LCHSGeometry.cCylinders = 0;
1110 pDisk->LCHSGeometry.cHeads = 0;
1111 pDisk->LCHSGeometry.cSectors = 0;
1112 }
1113 else
1114 {
1115 /* Make sure the LCHS geometry is properly clipped. */
1116 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
1117 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1118 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1119 }
1120
1121 if (pDisk->cImages != 0)
1122 {
1123 /* Switch previous image to read-only mode. */
1124 unsigned uOpenFlagsPrevImg;
1125 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1126 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
1127 {
1128 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
1129 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
1130 }
1131 }
1132
1133 if (RT_SUCCESS(rc))
1134 {
1135 /* Image successfully opened, make it the last image. */
1136 vdAddImageToList(pDisk, pImage);
1137 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1138 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1139 }
1140 else
1141 {
1142 /* Error detected, but image opened. Close image. */
1143 int rc2;
1144 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, false);
1145 AssertRC(rc2);
1146 pImage->pvBackendData = NULL;
1147 }
1148 } while (0);
1149
1150 if (RT_FAILURE(rc))
1151 {
1152 if (pImage)
1153 {
1154 if (pImage->pszFilename)
1155 RTStrFree(pImage->pszFilename);
1156 RTMemFree(pImage);
1157 }
1158 }
1159
1160 LogFlowFunc(("returns %Rrc\n", rc));
1161 return rc;
1162}
1163
1164/**
1165 * Creates and opens a new base image file.
1166 *
1167 * @returns VBox status code.
1168 * @param pDisk Pointer to HDD container.
1169 * @param pszBackend Name of the image file backend to use.
1170 * @param pszFilename Name of the image file to create.
1171 * @param enmType Image type, only base image types are acceptable.
1172 * @param cbSize Image size in bytes.
1173 * @param uImageFlags Flags specifying special image features.
1174 * @param pszComment Pointer to image comment. NULL is ok.
1175 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
1176 * @param pLCHSGeometry Pointer to logical disk geometry <= (1024,255,63). Not NULL.
1177 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
1178 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1179 * @param pVDIfsImage Pointer to the per-image VD interface list.
1180 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
1181 */
1182VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
1183 const char *pszFilename, VDIMAGETYPE enmType,
1184 uint64_t cbSize, unsigned uImageFlags,
1185 const char *pszComment,
1186 PCPDMMEDIAGEOMETRY pPCHSGeometry,
1187 PCPDMMEDIAGEOMETRY pLCHSGeometry,
1188 PCRTUUID pUuid, unsigned uOpenFlags,
1189 PVDINTERFACE pVDIfsImage,
1190 PVDINTERFACE pVDIfsOperation)
1191{
1192 int rc = VINF_SUCCESS;
1193 PVDIMAGE pImage = NULL;
1194 RTUUID uuid;
1195
1196 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" enmType=%#x cbSize=%llu uImageFlags=%#x pszComment=\"%s\" PCHS=%u/%u/%u LCHS=%u/%u/%u Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
1197 pDisk, pszBackend, pszFilename, enmType, cbSize, uImageFlags, pszComment,
1198 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1199 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
1200 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
1201 uOpenFlags, pVDIfsImage, pVDIfsOperation));
1202
1203 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
1204 VDINTERFACETYPE_PROGRESS);
1205 PVDINTERFACEPROGRESS pCbProgress = NULL;
1206 if (pIfProgress)
1207 pCbProgress = VDGetInterfaceProgress(pIfProgress);
1208
1209 do
1210 {
1211 /* sanity check */
1212 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1213 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1214
1215 /* Check arguments. */
1216 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
1217 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1218 rc = VERR_INVALID_PARAMETER);
1219 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1220 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1221 rc = VERR_INVALID_PARAMETER);
1222 AssertMsgBreakStmt(enmType == VD_IMAGE_TYPE_NORMAL || enmType == VD_IMAGE_TYPE_FIXED,
1223 ("enmType=%#x\n", enmType),
1224 rc = VERR_INVALID_PARAMETER);
1225 AssertMsgBreakStmt(cbSize,
1226 ("cbSize=%llu\n", cbSize),
1227 rc = VERR_INVALID_PARAMETER);
1228 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
1229 ("uImageFlags=%#x\n", uImageFlags),
1230 rc = VERR_INVALID_PARAMETER);
1231 /* The PCHS geometry fields may be 0 to leave it for later. */
1232 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
1233 && pPCHSGeometry->cCylinders <= 16383
1234 && pPCHSGeometry->cHeads <= 16
1235 && pPCHSGeometry->cSectors <= 63,
1236 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
1237 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1238 pPCHSGeometry->cSectors),
1239 rc = VERR_INVALID_PARAMETER);
1240 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
1241 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
1242 && pLCHSGeometry->cCylinders <= 1024
1243 && pLCHSGeometry->cHeads <= 255
1244 && pLCHSGeometry->cSectors <= 63,
1245 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
1246 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
1247 pLCHSGeometry->cSectors),
1248 rc = VERR_INVALID_PARAMETER);
1249 /* The UUID may be NULL. */
1250 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
1251 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
1252 rc = VERR_INVALID_PARAMETER);
1253 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1254 ("uOpenFlags=%#x\n", uOpenFlags),
1255 rc = VERR_INVALID_PARAMETER);
1256
1257 /* Check state. */
1258 AssertMsgBreakStmt(pDisk->cImages == 0,
1259 ("Create base image cannot be done with other images open\n"),
1260 rc = VERR_VDI_INVALID_STATE);
1261
1262 /* Set up image descriptor. */
1263 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1264 if (!pImage)
1265 {
1266 rc = VERR_NO_MEMORY;
1267 break;
1268 }
1269 pImage->pszFilename = RTStrDup(pszFilename);
1270 if (!pImage->pszFilename)
1271 {
1272 rc = VERR_NO_MEMORY;
1273 break;
1274 }
1275 pImage->pVDIfsImage = pVDIfsImage;
1276
1277 rc = vdFindBackend(pszBackend, &pImage->Backend);
1278 if (RT_FAILURE(rc))
1279 break;
1280 if (!pImage->Backend)
1281 {
1282 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1283 N_("VD: unknown backend name '%s'"), pszBackend);
1284 break;
1285 }
1286
1287 /* Create UUID if the caller didn't specify one. */
1288 if (!pUuid)
1289 {
1290 rc = RTUuidCreate(&uuid);
1291 if (RT_FAILURE(rc))
1292 {
1293 rc = vdError(pDisk, rc, RT_SRC_POS,
1294 N_("VD: cannot generate UUID for image '%s'"),
1295 pszFilename);
1296 break;
1297 }
1298 pUuid = &uuid;
1299 }
1300
1301 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1302 rc = pImage->Backend->pfnCreate(pImage->pszFilename, enmType, cbSize,
1303 uImageFlags, pszComment, pPCHSGeometry,
1304 pLCHSGeometry, pUuid,
1305 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1306 0, 99,
1307 pDisk->pVDIfsDisk,
1308 pImage->pVDIfsImage,
1309 pVDIfsOperation,
1310 &pImage->pvBackendData);
1311
1312 if (RT_SUCCESS(rc))
1313 {
1314 pImage->enmImageType = enmType;
1315
1316 /* Force sane optimization settings. It's not worth avoiding writes
1317 * to fixed size images. The overhead would have almost no payback. */
1318 if (enmType == VD_IMAGE_TYPE_FIXED)
1319 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
1320
1321 /** @todo optionally check UUIDs */
1322
1323 int rc2;
1324
1325 /* Cache disk information. */
1326 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
1327
1328 /* Cache PCHS geometry. */
1329 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1330 &pDisk->PCHSGeometry);
1331 if (RT_FAILURE(rc2))
1332 {
1333 pDisk->PCHSGeometry.cCylinders = 0;
1334 pDisk->PCHSGeometry.cHeads = 0;
1335 pDisk->PCHSGeometry.cSectors = 0;
1336 }
1337 else
1338 {
1339 /* Make sure the CHS geometry is properly clipped. */
1340 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1341 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1342 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1343 }
1344
1345 /* Cache LCHS geometry. */
1346 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1347 &pDisk->LCHSGeometry);
1348 if (RT_FAILURE(rc2))
1349 {
1350 pDisk->LCHSGeometry.cCylinders = 0;
1351 pDisk->LCHSGeometry.cHeads = 0;
1352 pDisk->LCHSGeometry.cSectors = 0;
1353 }
1354 else
1355 {
1356 /* Make sure the CHS geometry is properly clipped. */
1357 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
1358 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1359 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1360 }
1361 }
1362
1363 if (RT_SUCCESS(rc))
1364 {
1365 /* Image successfully opened, make it the last image. */
1366 vdAddImageToList(pDisk, pImage);
1367 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1368 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1369 }
1370 else
1371 {
1372 /* Error detected, but image opened. Close and delete image. */
1373 int rc2;
1374 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
1375 AssertRC(rc2);
1376 pImage->pvBackendData = NULL;
1377 }
1378 } while (0);
1379
1380 if (RT_FAILURE(rc))
1381 {
1382 if (pImage)
1383 {
1384 if (pImage->pszFilename)
1385 RTStrFree(pImage->pszFilename);
1386 RTMemFree(pImage);
1387 }
1388 }
1389
1390 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
1391 pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */, 100,
1392 pIfProgress->pvUser);
1393
1394 LogFlowFunc(("returns %Rrc\n", rc));
1395 return rc;
1396}
1397
1398/**
1399 * Creates and opens a new differencing image file in HDD container.
1400 * See comments for VDOpen function about differencing images.
1401 *
1402 * @returns VBox status code.
1403 * @param pDisk Pointer to HDD container.
1404 * @param pszBackend Name of the image file backend to use.
1405 * @param pszFilename Name of the differencing image file to create.
1406 * @param uImageFlags Flags specifying special image features.
1407 * @param pszComment Pointer to image comment. NULL is ok.
1408 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
1409 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1410 * @param pVDIfsImage Pointer to the per-image VD interface list.
1411 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
1412 */
1413VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
1414 const char *pszFilename, unsigned uImageFlags,
1415 const char *pszComment, PCRTUUID pUuid,
1416 unsigned uOpenFlags, PVDINTERFACE pVDIfsImage,
1417 PVDINTERFACE pVDIfsOperation)
1418{
1419 int rc = VINF_SUCCESS;
1420 PVDIMAGE pImage = NULL;
1421 RTUUID uuid;
1422
1423 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
1424 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, uOpenFlags,
1425 pVDIfsImage, pVDIfsOperation));
1426
1427 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
1428 VDINTERFACETYPE_PROGRESS);
1429 PVDINTERFACEPROGRESS pCbProgress = NULL;
1430 if (pIfProgress)
1431 pCbProgress = VDGetInterfaceProgress(pIfProgress);
1432
1433 do
1434 {
1435 /* sanity check */
1436 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1437 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1438
1439 /* Check arguments. */
1440 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
1441 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1442 rc = VERR_INVALID_PARAMETER);
1443 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1444 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1445 rc = VERR_INVALID_PARAMETER);
1446 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
1447 ("uImageFlags=%#x\n", uImageFlags),
1448 rc = VERR_INVALID_PARAMETER);
1449 /* The UUID may be NULL. */
1450 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
1451 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
1452 rc = VERR_INVALID_PARAMETER);
1453 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1454 ("uOpenFlags=%#x\n", uOpenFlags),
1455 rc = VERR_INVALID_PARAMETER);
1456
1457 /* Check state. */
1458 AssertMsgBreakStmt(pDisk->cImages != 0,
1459 ("Create diff image cannot be done without other images open\n"),
1460 rc = VERR_VDI_INVALID_STATE);
1461
1462 /* Set up image descriptor. */
1463 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1464 if (!pImage)
1465 {
1466 rc = VERR_NO_MEMORY;
1467 break;
1468 }
1469 pImage->pszFilename = RTStrDup(pszFilename);
1470 if (!pImage->pszFilename)
1471 {
1472 rc = VERR_NO_MEMORY;
1473 break;
1474 }
1475
1476 rc = vdFindBackend(pszBackend, &pImage->Backend);
1477 if (RT_FAILURE(rc))
1478 break;
1479 if (!pImage->Backend)
1480 {
1481 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1482 N_("VD: unknown backend name '%s'"), pszBackend);
1483 break;
1484 }
1485
1486 /* Create UUID if the caller didn't specify one. */
1487 if (!pUuid)
1488 {
1489 rc = RTUuidCreate(&uuid);
1490 if (RT_FAILURE(rc))
1491 {
1492 rc = vdError(pDisk, rc, RT_SRC_POS,
1493 N_("VD: cannot generate UUID for image '%s'"),
1494 pszFilename);
1495 break;
1496 }
1497 pUuid = &uuid;
1498 }
1499
1500 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1501 rc = pImage->Backend->pfnCreate(pImage->pszFilename,
1502 VD_IMAGE_TYPE_DIFF, pDisk->cbSize,
1503 uImageFlags, pszComment,
1504 &pDisk->PCHSGeometry,
1505 &pDisk->LCHSGeometry, pUuid,
1506 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1507 0, 99,
1508 pDisk->pVDIfsDisk,
1509 pImage->pVDIfsImage,
1510 pVDIfsOperation,
1511 &pImage->pvBackendData);
1512
1513 if (RT_SUCCESS(rc) && pDisk->cImages != 0)
1514 {
1515 pImage->enmImageType = VD_IMAGE_TYPE_DIFF;
1516
1517 /* Switch previous image to read-only mode. */
1518 unsigned uOpenFlagsPrevImg;
1519 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1520 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
1521 {
1522 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
1523 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
1524 }
1525 }
1526
1527 if (RT_SUCCESS(rc))
1528 {
1529 RTUUID Uuid;
1530 RTTIMESPEC ts;
1531 int rc2;
1532
1533 rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pvBackendData,
1534 &Uuid);
1535 if (RT_SUCCESS(rc2))
1536 pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, &Uuid);
1537 rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pvBackendData,
1538 &Uuid);
1539 if (RT_SUCCESS(rc2))
1540 pImage->Backend->pfnSetParentModificationUuid(pImage->pvBackendData,
1541 &Uuid);
1542 rc2 = pDisk->pLast->Backend->pfnGetTimeStamp(pDisk->pLast->pvBackendData,
1543 &ts);
1544 if (RT_SUCCESS(rc2))
1545 pImage->Backend->pfnSetParentTimeStamp(pImage->pvBackendData, &ts);
1546
1547 rc2 = pImage->Backend->pfnSetParentFilename(pImage->pvBackendData, pDisk->pLast->pszFilename);
1548 }
1549
1550 if (RT_SUCCESS(rc))
1551 {
1552 /** @todo optionally check UUIDs */
1553 }
1554
1555 if (RT_SUCCESS(rc))
1556 {
1557 /* Image successfully opened, make it the last image. */
1558 vdAddImageToList(pDisk, pImage);
1559 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1560 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1561 }
1562 else
1563 {
1564 /* Error detected, but image opened. Close and delete image. */
1565 int rc2;
1566 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
1567 AssertRC(rc2);
1568 pImage->pvBackendData = NULL;
1569 }
1570 } while (0);
1571
1572 if (RT_FAILURE(rc))
1573 {
1574 if (pImage)
1575 {
1576 if (pImage->pszFilename)
1577 RTStrFree(pImage->pszFilename);
1578 RTMemFree(pImage);
1579 }
1580 }
1581
1582 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
1583 pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */, 100,
1584 pIfProgress->pvUser);
1585
1586 LogFlowFunc(("returns %Rrc\n", rc));
1587 return rc;
1588}
1589
1590/**
1591 * Merges two images (not necessarily with direct parent/child relationship).
1592 * As a side effect the source image and potentially the other images which
1593 * are also merged to the destination are deleted from both the disk and the
1594 * images in the HDD container.
1595 *
1596 * @returns VBox status code.
1597 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1598 * @param pDisk Pointer to HDD container.
1599 * @param nImageFrom Name of the image file to merge from.
1600 * @param nImageTo Name of the image file to merge to.
1601 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
1602 */
1603VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
1604 unsigned nImageTo, PVDINTERFACE pVDIfsOperation)
1605{
1606 int rc = VINF_SUCCESS;
1607 void *pvBuf = NULL;
1608
1609 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pVDIfsOperation=%#p\n",
1610 pDisk, nImageFrom, nImageTo, pVDIfsOperation));
1611
1612 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
1613 VDINTERFACETYPE_PROGRESS);
1614 PVDINTERFACEPROGRESS pCbProgress = NULL;
1615 if (pIfProgress)
1616 pCbProgress = VDGetInterfaceProgress(pIfProgress);
1617
1618 do
1619 {
1620 /* sanity check */
1621 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1622 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1623
1624 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
1625 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
1626 if (!pImageFrom || !pImageTo)
1627 {
1628 rc = VERR_VDI_IMAGE_NOT_FOUND;
1629 break;
1630 }
1631 AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
1632
1633 /* Make sure destination image is writable. */
1634 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
1635 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
1636 {
1637 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
1638 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pvBackendData,
1639 uOpenFlags);
1640 if (RT_FAILURE(rc))
1641 break;
1642 }
1643
1644 /* Get size of destination image. */
1645 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pvBackendData);
1646
1647 /* Allocate tmp buffer. */
1648 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
1649 if (!pvBuf)
1650 {
1651 rc = VERR_NO_MEMORY;
1652 break;
1653 }
1654
1655 /* Merging is done directly on the images itself. This potentially
1656 * causes trouble if the disk is full in the middle of operation. */
1657 /** @todo write alternative implementation which works with temporary
1658 * images (which is safer, but requires even more space). Also has the
1659 * drawback that merging into a raw disk parent simply isn't possible
1660 * this way (but in that case disk full isn't really a problem). */
1661 if (nImageFrom < nImageTo)
1662 {
1663 /* Merge parent state into child. This means writing all not
1664 * allocated blocks in the destination image which are allocated in
1665 * the images to be merged. */
1666 uint64_t uOffset = 0;
1667 uint64_t cbRemaining = cbSize;
1668 do
1669 {
1670 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1671 rc = pImageTo->Backend->pfnRead(pImageTo->pvBackendData,
1672 uOffset, pvBuf, cbThisRead,
1673 &cbThisRead);
1674 if (rc == VERR_VDI_BLOCK_FREE)
1675 {
1676 /* Search for image with allocated block. Do not attempt to
1677 * read more than the previous reads marked as valid.
1678 * Otherwise this would return stale data when different
1679 * block sizes are used for the images. */
1680 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
1681 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VDI_BLOCK_FREE;
1682 pCurrImage = pCurrImage->pPrev)
1683 {
1684 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
1685 uOffset, pvBuf,
1686 cbThisRead,
1687 &cbThisRead);
1688 }
1689
1690 if (rc != VERR_VDI_BLOCK_FREE)
1691 {
1692 if (RT_FAILURE(rc))
1693 break;
1694 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1695 cbThisRead);
1696 if (RT_FAILURE(rc))
1697 break;
1698 }
1699 else
1700 rc = VINF_SUCCESS;
1701 }
1702 else if (RT_FAILURE(rc))
1703 break;
1704
1705 uOffset += cbThisRead;
1706 cbRemaining -= cbThisRead;
1707
1708 if (pCbProgress && pCbProgress->pfnProgress)
1709 {
1710 rc = pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */,
1711 uOffset * 99 / cbSize,
1712 pIfProgress->pvUser);
1713 if (RT_FAILURE(rc))
1714 break;
1715 }
1716 } while (uOffset < cbSize);
1717 }
1718 else
1719 {
1720 /* Merge child state into parent. This means writing all blocks
1721 * which are allocated in the image up to the source image to the
1722 * destination image. */
1723 uint64_t uOffset = 0;
1724 uint64_t cbRemaining = cbSize;
1725 do
1726 {
1727 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1728 rc = VERR_VDI_BLOCK_FREE;
1729 /* Search for image with allocated block. Do not attempt to
1730 * read more than the previous reads marked as valid. Otherwise
1731 * this would return stale data when different block sizes are
1732 * used for the images. */
1733 for (PVDIMAGE pCurrImage = pImageFrom;
1734 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VDI_BLOCK_FREE;
1735 pCurrImage = pCurrImage->pPrev)
1736 {
1737 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
1738 uOffset, pvBuf,
1739 cbThisRead, &cbThisRead);
1740 }
1741
1742 if (rc != VERR_VDI_BLOCK_FREE)
1743 {
1744 if (RT_FAILURE(rc))
1745 break;
1746 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1747 cbThisRead);
1748 if (RT_FAILURE(rc))
1749 break;
1750 }
1751 else
1752 rc = VINF_SUCCESS;
1753
1754 uOffset += cbThisRead;
1755 cbRemaining -= cbThisRead;
1756
1757 if (pCbProgress && pCbProgress->pfnProgress)
1758 {
1759 rc = pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */,
1760 uOffset * 99 / cbSize,
1761 pIfProgress->pvUser);
1762 if (RT_FAILURE(rc))
1763 break;
1764 }
1765 } while (uOffset < cbSize);
1766 }
1767
1768 /* Update parent UUID so that image chain is consistent. */
1769 RTUUID Uuid;
1770 if (nImageFrom < nImageTo)
1771 {
1772 if (pImageTo->pPrev)
1773 {
1774 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pPrev->pvBackendData,
1775 &Uuid);
1776 AssertRC(rc);
1777 }
1778 else
1779 RTUuidClear(&Uuid);
1780 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pvBackendData,
1781 &Uuid);
1782 AssertRC(rc);
1783 }
1784 else
1785 {
1786 if (pImageFrom->pNext)
1787 {
1788 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pvBackendData,
1789 &Uuid);
1790 AssertRC(rc);
1791 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext,
1792 &Uuid);
1793 AssertRC(rc);
1794 }
1795 }
1796
1797 /* Make sure destination image is back to read only if necessary. */
1798 if (pImageTo != pDisk->pLast && pImageFrom != pDisk->pLast)
1799 {
1800 uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
1801 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
1802 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pvBackendData,
1803 uOpenFlags);
1804 if (RT_FAILURE(rc))
1805 break;
1806 }
1807
1808 /* Delete the no longer needed images. */
1809 PVDIMAGE pImg = pImageFrom, pTmp;
1810 while (pImg != pImageTo)
1811 {
1812 if (nImageFrom < nImageTo)
1813 pTmp = pImg->pNext;
1814 else
1815 pTmp = pImg->pPrev;
1816 vdRemoveImageFromList(pDisk, pImg);
1817 pImg->Backend->pfnClose(pImg->pvBackendData, true);
1818 RTMemFree(pImg->pszFilename);
1819 RTMemFree(pImg);
1820 pImg = pTmp;
1821 }
1822 } while (0);
1823
1824 if (pvBuf)
1825 RTMemTmpFree(pvBuf);
1826
1827 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
1828 pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */, 100,
1829 pIfProgress->pvUser);
1830
1831 LogFlowFunc(("returns %Rrc\n", rc));
1832 return rc;
1833}
1834
1835/**
1836 * Copies an image from one HDD container to another.
1837 * The copy is opened in the target HDD container.
1838 * It is possible to convert between different image formats, because the
1839 * backend for the destination may be different from the source.
1840 * If both the source and destination reference the same HDD container,
1841 * then the image is moved (by copying/deleting or renaming) to the new location.
1842 * The source container is unchanged if the move operation fails, otherwise
1843 * the image at the new location is opened in the same way as the old one was.
1844 *
1845 * @returns VBox status code.
1846 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1847 * @param pDiskFrom Pointer to source HDD container.
1848 * @param nImage Image number, counts from 0. 0 is always base image of container.
1849 * @param pDiskTo Pointer to destination HDD container.
1850 * @param pszBackend Name of the image file backend to use.
1851 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
1852 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
1853 * @param cbSize New image size (0 means leave unchanged).
1854 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
1855 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
1856 * destination image.
1857 * @param pDstVDIfsOperation Pointer to the per-image VD interface list,
1858 * for the destination image.
1859 */
1860VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
1861 const char *pszBackend, const char *pszFilename,
1862 bool fMoveByRename, uint64_t cbSize,
1863 PVDINTERFACE pVDIfsOperation,
1864 PVDINTERFACE pDstVDIfsImage,
1865 PVDINTERFACE pDstVDIfsOperation)
1866{
1867 int rc, rc2 = VINF_SUCCESS;
1868 void *pvBuf = NULL;
1869 PVDIMAGE pImageTo = NULL;
1870
1871 LogFlowFunc(("pDiskFrom=%#p nImage=%u pDiskTo=%#p pszBackend=\"%s\" pszFilename=\"%s\" fMoveByRename=%d cbSize=%llu pVDIfsOperation=%#p pDstVDIfsImage=%#p pDstVDIfsOperation=%#p\n",
1872 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
1873
1874 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
1875 VDINTERFACETYPE_PROGRESS);
1876 PVDINTERFACEPROGRESS pCbProgress = NULL;
1877 if (pIfProgress)
1878 pCbProgress = VDGetInterfaceProgress(pIfProgress);
1879
1880 PVDINTERFACE pDstIfProgress = VDInterfaceGet(pDstVDIfsOperation,
1881 VDINTERFACETYPE_PROGRESS);
1882 PVDINTERFACEPROGRESS pDstCbProgress = NULL;
1883 if (pDstIfProgress)
1884 pDstCbProgress = VDGetInterfaceProgress(pDstIfProgress);
1885
1886 do {
1887 /* Check arguments. */
1888 AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
1889 rc = VERR_INVALID_PARAMETER);
1890 AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
1891 ("u32Signature=%08x\n", pDiskFrom->u32Signature));
1892
1893 PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
1894 AssertPtrBreakStmt(pImageFrom, rc = VERR_VDI_IMAGE_NOT_FOUND);
1895 AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
1896 rc = VERR_INVALID_PARAMETER);
1897 AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
1898 ("u32Signature=%08x\n", pDiskTo->u32Signature));
1899
1900 /* If the containers are equal and the backend is the same, rename the image. */
1901 if ( (pDiskFrom == pDiskTo)
1902 && (!strcmp(pszBackend, pImageFrom->Backend->pszBackendName)))
1903 {
1904 /* Rename the image. */
1905 rc = pImageFrom->Backend->pfnRename(pImageFrom->pvBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
1906 break;
1907 }
1908
1909 /* If the fMoveByRename flag is set and the backend is the same, rename the image. */
1910 if ( (fMoveByRename == true)
1911 && (!strcmp(pszBackend, pImageFrom->Backend->pszBackendName)))
1912 {
1913 /* Close the source image. */
1914 rc = pImageFrom->Backend->pfnClose(pImageFrom->pvBackendData, false);
1915 if (RT_FAILURE(rc))
1916 break;
1917
1918 /* Open the source image in the destination container. */
1919 /** @todo fix open flags - the flags in pImageFrom are not the right info, the info from the image format backend is relevant */
1920 rc = VDOpen(pDiskTo, pImageFrom->Backend->pszBackendName, pImageFrom->pszFilename, pImageFrom->uOpenFlags, pDstVDIfsImage);
1921 if (RT_FAILURE(rc))
1922 goto movefail;
1923
1924 pImageTo = pDiskTo->pLast;
1925
1926 /* Rename the image. */
1927 rc = pImageTo->Backend->pfnRename(pImageTo->pvBackendData, pszFilename ? pszFilename : pImageTo->pszFilename);
1928 if (RT_FAILURE(rc))
1929 goto movefail;
1930
1931 /* Cleanup the leftovers. */
1932 vdRemoveImageFromList(pDiskFrom, pImageFrom);
1933 pImageFrom->pvBackendData = NULL;
1934
1935 if (pImageFrom->pszFilename)
1936 RTStrFree(pImageFrom->pszFilename);
1937
1938 RTMemFree(pImageFrom);
1939
1940 break;
1941movefail:
1942 /* In case of failure, re-open the source image in the source container. */
1943 rc2 = VDOpen(pDiskFrom, pImageFrom->Backend->pszBackendName, pImageFrom->pszFilename, pImageFrom->uOpenFlags, pImageFrom->pVDIfsImage);
1944 if (RT_FAILURE(rc2))
1945 /** @todo Uncertain what to do on error. If this happens pImageFrom and pImageTo are both closed. */
1946 rc = rc2;
1947 break;
1948 }
1949
1950 /* If fMoveByRename is set pszFilename is allowed to be NULL, so do the parameter check here. */
1951 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1952 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1953 rc = VERR_INVALID_PARAMETER);
1954
1955 /* Collect properties of source image. */
1956 VDIMAGETYPE enmTypeFrom = pImageFrom->enmImageType;
1957
1958 uint64_t cbSizeFrom;
1959 cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pvBackendData);
1960 if (cbSizeFrom == 0)
1961 {
1962 rc = VERR_VDI_VALUE_NOT_FOUND;
1963 break;
1964 }
1965
1966 if (cbSize == 0)
1967 cbSize = cbSizeFrom;
1968
1969 unsigned uImageFlagsFrom;
1970 uImageFlagsFrom = pImageFrom->Backend->pfnGetImageFlags(pImageFrom->pvBackendData);
1971
1972 /** @todo Get this from the source image. */
1973 PDMMEDIAGEOMETRY PCHSGeometryFrom = {0, 0, 0};
1974 PDMMEDIAGEOMETRY LCHSGeometryFrom = {0, 0, 0};
1975
1976 unsigned uOpenFlagsFrom;
1977 uOpenFlagsFrom = pImageFrom->Backend->pfnGetOpenFlags(pImageFrom->pvBackendData);
1978
1979 /* Create destination image with the properties of the source image. */
1980 /** @todo Copy the comment. */
1981 /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
1982 * calls to the backend. Unifies the code and reduces the API
1983 * dependencies. */
1984 if (enmTypeFrom == VD_IMAGE_TYPE_DIFF)
1985 {
1986 rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename, uImageFlagsFrom,
1987 "", NULL, uOpenFlagsFrom & ~VD_OPEN_FLAGS_READONLY, NULL, NULL);
1988 } else {
1989 rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, enmTypeFrom,
1990 cbSize, uImageFlagsFrom, "",
1991 &PCHSGeometryFrom, &LCHSGeometryFrom,
1992 NULL, uOpenFlagsFrom & ~VD_OPEN_FLAGS_READONLY, NULL, NULL);
1993 }
1994 if (RT_FAILURE(rc))
1995 break;
1996
1997 pImageTo = pDiskTo->pLast;
1998 AssertPtrBreakStmt(pImageTo, rc = VERR_VDI_IMAGE_NOT_FOUND);
1999
2000 /* Allocate tmp buffer. */
2001 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
2002 if (!pvBuf)
2003 {
2004 rc = VERR_NO_MEMORY;
2005 break;
2006 }
2007
2008 /* Copy the data. */
2009 uint64_t uOffset = 0;
2010 uint64_t cbRemaining = cbSize;
2011
2012 do
2013 {
2014 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
2015
2016 rc = vdReadHelper(pDiskFrom, pImageFrom, uOffset, pvBuf,
2017 cbThisRead);
2018 if (RT_FAILURE(rc))
2019 break;
2020
2021 rc = vdWriteHelper(pDiskTo, pImageTo, uOffset, pvBuf,
2022 cbThisRead);
2023 if (RT_FAILURE(rc))
2024 break;
2025
2026 uOffset += cbThisRead;
2027 cbRemaining -= cbThisRead;
2028
2029 if (pCbProgress && pCbProgress->pfnProgress)
2030 {
2031 rc = pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */,
2032 uOffset * 99 / cbSize,
2033 pIfProgress->pvUser);
2034 if (RT_FAILURE(rc))
2035 break;
2036 }
2037 if (pDstCbProgress && pDstCbProgress->pfnProgress)
2038 {
2039 rc = pDstCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */,
2040 uOffset * 99 / cbSize,
2041 pDstIfProgress->pvUser);
2042 if (RT_FAILURE(rc))
2043 break;
2044 }
2045 } while (uOffset < cbSize);
2046
2047 /* If fMoveByRename is set but the backend is different, close and delete pImageFrom. */
2048 if ( (fMoveByRename == true)
2049 && (strcmp(pszBackend, pImageFrom->Backend->pszBackendName)))
2050 {
2051 vdRemoveImageFromList(pDiskFrom, pImageFrom);
2052
2053 /* Close and delete image. */
2054 rc2 = pImageFrom->Backend->pfnClose(pImageFrom->pvBackendData, true);
2055 AssertRC(rc2);
2056 pImageFrom->pvBackendData = NULL;
2057
2058 /* Free remaining resources. */
2059 if (pImageFrom->pszFilename)
2060 RTStrFree(pImageFrom->pszFilename);
2061
2062 RTMemFree(pImageFrom);
2063 }
2064 } while (0);
2065
2066 if (RT_FAILURE(rc) && pImageTo)
2067 {
2068 /* Error detected, but new image created. Remove image from list. */
2069 vdRemoveImageFromList(pDiskTo, pImageTo);
2070
2071 /* Close and delete image. */
2072 rc2 = pImageTo->Backend->pfnClose(pImageTo->pvBackendData, true);
2073 AssertRC(rc2);
2074 pImageTo->pvBackendData = NULL;
2075
2076 /* Free remaining resources. */
2077 if (pImageTo->pszFilename)
2078 RTStrFree(pImageTo->pszFilename);
2079
2080 RTMemFree(pImageTo);
2081 }
2082
2083 if (pvBuf)
2084 RTMemTmpFree(pvBuf);
2085
2086 if (RT_SUCCESS(rc))
2087 {
2088 if (pCbProgress && pCbProgress->pfnProgress)
2089 pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */, 100,
2090 pIfProgress->pvUser);
2091 if (pDstCbProgress && pDstCbProgress->pfnProgress)
2092 pDstCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */, 100,
2093 pDstIfProgress->pvUser);
2094 }
2095
2096 LogFlowFunc(("returns %Rrc\n", rc));
2097 return rc;
2098}
2099
2100/**
2101 * Closes the last opened image file in HDD container.
2102 * If previous image file was opened in read-only mode (that is normal) and closing image
2103 * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
2104 * will be reopened in read/write mode.
2105 *
2106 * @returns VBox status code.
2107 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2108 * @param pDisk Pointer to HDD container.
2109 * @param fDelete If true, delete the image from the host disk.
2110 */
2111VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
2112{
2113 int rc = VINF_SUCCESS;;
2114
2115 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
2116 do
2117 {
2118 /* sanity check */
2119 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2120 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2121
2122 PVDIMAGE pImage = pDisk->pLast;
2123 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2124 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2125 /* Remove image from list of opened images. */
2126 vdRemoveImageFromList(pDisk, pImage);
2127 /* Close (and optionally delete) image. */
2128 rc = pImage->Backend->pfnClose(pImage->pvBackendData, fDelete);
2129 /* Free remaining resources related to the image. */
2130 RTStrFree(pImage->pszFilename);
2131 RTMemFree(pImage);
2132
2133 pImage = pDisk->pLast;
2134 if (!pImage)
2135 break;
2136
2137 /* If disk was previously in read/write mode, make sure it will stay
2138 * like this (if possible) after closing this image. Set the open flags
2139 * accordingly. */
2140 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
2141 {
2142 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2143 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
2144 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData, uOpenFlags);
2145 }
2146
2147 int rc2;
2148
2149 /* Cache disk information. */
2150 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
2151
2152 /* Cache PCHS geometry. */
2153 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2154 &pDisk->PCHSGeometry);
2155 if (RT_FAILURE(rc2))
2156 {
2157 pDisk->PCHSGeometry.cCylinders = 0;
2158 pDisk->PCHSGeometry.cHeads = 0;
2159 pDisk->PCHSGeometry.cSectors = 0;
2160 }
2161 else
2162 {
2163 /* Make sure the PCHS geometry is properly clipped. */
2164 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
2165 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
2166 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
2167 }
2168
2169 /* Cache LCHS geometry. */
2170 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2171 &pDisk->LCHSGeometry);
2172 if (RT_FAILURE(rc2))
2173 {
2174 pDisk->LCHSGeometry.cCylinders = 0;
2175 pDisk->LCHSGeometry.cHeads = 0;
2176 pDisk->LCHSGeometry.cSectors = 0;
2177 }
2178 else
2179 {
2180 /* Make sure the LCHS geometry is properly clipped. */
2181 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
2182 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
2183 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
2184 }
2185 } while (0);
2186
2187 LogFlowFunc(("returns %Rrc\n", rc));
2188 return rc;
2189}
2190
2191/**
2192 * Closes all opened image files in HDD container.
2193 *
2194 * @returns VBox status code.
2195 * @param pDisk Pointer to HDD container.
2196 */
2197VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
2198{
2199 int rc = VINF_SUCCESS;
2200
2201 LogFlowFunc(("pDisk=%#p\n", pDisk));
2202 do
2203 {
2204 /* sanity check */
2205 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2206 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2207
2208 PVDIMAGE pImage = pDisk->pLast;
2209 while (VALID_PTR(pImage))
2210 {
2211 PVDIMAGE pPrev = pImage->pPrev;
2212 /* Remove image from list of opened images. */
2213 vdRemoveImageFromList(pDisk, pImage);
2214 /* Close image. */
2215 int rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, false);
2216 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
2217 rc = rc2;
2218 /* Free remaining resources related to the image. */
2219 RTStrFree(pImage->pszFilename);
2220 RTMemFree(pImage);
2221 pImage = pPrev;
2222 }
2223 Assert(!VALID_PTR(pDisk->pLast));
2224 } while (0);
2225
2226 LogFlowFunc(("returns %Rrc\n", rc));
2227 return rc;
2228}
2229
2230/**
2231 * Read data from virtual HDD.
2232 *
2233 * @returns VBox status code.
2234 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2235 * @param pDisk Pointer to HDD container.
2236 * @param uOffset Offset of first reading byte from start of disk.
2237 * @param pvBuf Pointer to buffer for reading data.
2238 * @param cbRead Number of bytes to read.
2239 */
2240VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
2241 size_t cbRead)
2242{
2243 int rc;
2244
2245 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
2246 pDisk, uOffset, pvBuf, cbRead));
2247 do
2248 {
2249 /* sanity check */
2250 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2251 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2252
2253 /* Check arguments. */
2254 AssertMsgBreakStmt(VALID_PTR(pvBuf),
2255 ("pvBuf=%#p\n", pvBuf),
2256 rc = VERR_INVALID_PARAMETER);
2257 AssertMsgBreakStmt(cbRead,
2258 ("cbRead=%zu\n", cbRead),
2259 rc = VERR_INVALID_PARAMETER);
2260 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
2261 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
2262 uOffset, cbRead, pDisk->cbSize),
2263 rc = VERR_INVALID_PARAMETER);
2264
2265 PVDIMAGE pImage = pDisk->pLast;
2266 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2267
2268 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead);
2269 } while (0);
2270
2271 LogFlowFunc(("returns %Rrc\n", rc));
2272 return rc;
2273}
2274
2275/**
2276 * Write data to virtual HDD.
2277 *
2278 * @returns VBox status code.
2279 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2280 * @param pDisk Pointer to HDD container.
2281 * @param uOffset Offset of the first byte being
2282 * written from start of disk.
2283 * @param pvBuf Pointer to buffer for writing data.
2284 * @param cbWrite Number of bytes to write.
2285 */
2286VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
2287 size_t cbWrite)
2288{
2289 int rc = VINF_SUCCESS;
2290
2291 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
2292 pDisk, uOffset, pvBuf, cbWrite));
2293 do
2294 {
2295 /* sanity check */
2296 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2297 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2298
2299 /* Check arguments. */
2300 AssertMsgBreakStmt(VALID_PTR(pvBuf),
2301 ("pvBuf=%#p\n", pvBuf),
2302 rc = VERR_INVALID_PARAMETER);
2303 AssertMsgBreakStmt(cbWrite,
2304 ("cbWrite=%zu\n", cbWrite),
2305 rc = VERR_INVALID_PARAMETER);
2306 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
2307 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
2308 uOffset, cbWrite, pDisk->cbSize),
2309 rc = VERR_INVALID_PARAMETER);
2310
2311 PVDIMAGE pImage = pDisk->pLast;
2312 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2313
2314 vdSetModifiedFlag(pDisk);
2315 rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite);
2316 } while (0);
2317
2318 LogFlowFunc(("returns %Rrc\n", rc));
2319 return rc;
2320}
2321
2322/**
2323 * Make sure the on disk representation of a virtual HDD is up to date.
2324 *
2325 * @returns VBox status code.
2326 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2327 * @param pDisk Pointer to HDD container.
2328 */
2329VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
2330{
2331 int rc = VINF_SUCCESS;
2332
2333 LogFlowFunc(("pDisk=%#p\n", pDisk));
2334 do
2335 {
2336 /* sanity check */
2337 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2338 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2339
2340 PVDIMAGE pImage = pDisk->pLast;
2341 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2342
2343 vdResetModifiedFlag(pDisk);
2344 rc = pImage->Backend->pfnFlush(pImage->pvBackendData);
2345 } while (0);
2346
2347 LogFlowFunc(("returns %Rrc\n", rc));
2348 return rc;
2349}
2350
2351/**
2352 * Get number of opened images in HDD container.
2353 *
2354 * @returns Number of opened images for HDD container. 0 if no images have been opened.
2355 * @param pDisk Pointer to HDD container.
2356 */
2357VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
2358{
2359 unsigned cImages;
2360
2361 LogFlowFunc(("pDisk=%#p\n", pDisk));
2362 do
2363 {
2364 /* sanity check */
2365 AssertPtrBreakStmt(pDisk, cImages = 0);
2366 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2367
2368 cImages = pDisk->cImages;
2369 } while (0);
2370
2371 LogFlowFunc(("returns %u\n", cImages));
2372 return cImages;
2373}
2374
2375/**
2376 * Get read/write mode of HDD container.
2377 *
2378 * @returns Virtual disk ReadOnly status.
2379 * @returns true if no image is opened in HDD container.
2380 * @param pDisk Pointer to HDD container.
2381 */
2382VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
2383{
2384 bool fReadOnly;
2385
2386 LogFlowFunc(("pDisk=%#p\n", pDisk));
2387 do
2388 {
2389 /* sanity check */
2390 AssertPtrBreakStmt(pDisk, fReadOnly = false);
2391 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2392
2393 PVDIMAGE pImage = pDisk->pLast;
2394 AssertPtrBreakStmt(pImage, fReadOnly = true);
2395
2396 unsigned uOpenFlags;
2397 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
2398 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
2399 } while (0);
2400
2401 LogFlowFunc(("returns %d\n", fReadOnly));
2402 return fReadOnly;
2403}
2404
2405/**
2406 * Get total capacity of an image in HDD container.
2407 *
2408 * @returns Virtual disk size in bytes.
2409 * @returns 0 if no image with specified number was not opened.
2410 * @param pDisk Pointer to HDD container.
2411 * @param nImage Image number, counds from 0. 0 is always base image of container.
2412 */
2413VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
2414{
2415 uint64_t cbSize;
2416
2417 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
2418 do
2419 {
2420 /* sanity check */
2421 AssertPtrBreakStmt(pDisk, cbSize = 0);
2422 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2423
2424 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2425 AssertPtrBreakStmt(pImage, cbSize = 0);
2426 cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
2427 } while (0);
2428
2429 LogFlowFunc(("returns %llu\n", cbSize));
2430 return cbSize;
2431}
2432
2433/**
2434 * Get total file size of an image in HDD container.
2435 *
2436 * @returns Virtual disk size in bytes.
2437 * @returns 0 if no image is opened in HDD container.
2438 * @param pDisk Pointer to HDD container.
2439 * @param nImage Image number, counts from 0. 0 is always base image of container.
2440 */
2441VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
2442{
2443 uint64_t cbSize;
2444
2445 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
2446 do
2447 {
2448 /* sanity check */
2449 AssertPtrBreakStmt(pDisk, cbSize = 0);
2450 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2451
2452 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2453 AssertPtrBreakStmt(pImage, cbSize = 0);
2454 cbSize = pImage->Backend->pfnGetFileSize(pImage->pvBackendData);
2455 } while (0);
2456
2457 LogFlowFunc(("returns %llu\n", cbSize));
2458 return cbSize;
2459}
2460
2461/**
2462 * Get virtual disk PCHS geometry stored in HDD container.
2463 *
2464 * @returns VBox status code.
2465 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2466 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2467 * @param pDisk Pointer to HDD container.
2468 * @param nImage Image number, counts from 0. 0 is always base image of container.
2469 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
2470 */
2471VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2472 PPDMMEDIAGEOMETRY pPCHSGeometry)
2473{
2474 int rc = VINF_SUCCESS;
2475
2476 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
2477 pDisk, nImage, pPCHSGeometry));
2478 do
2479 {
2480 /* sanity check */
2481 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2482 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2483
2484 /* Check arguments. */
2485 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
2486 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
2487 rc = VERR_INVALID_PARAMETER);
2488
2489 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2490 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2491
2492 if (pImage == pDisk->pLast)
2493 {
2494 /* Use cached information if possible. */
2495 if (pDisk->PCHSGeometry.cCylinders != 0)
2496 *pPCHSGeometry = pDisk->PCHSGeometry;
2497 else
2498 rc = VERR_VDI_GEOMETRY_NOT_SET;
2499 }
2500 else
2501 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2502 pPCHSGeometry);
2503 } while (0);
2504
2505 LogFlowFunc(("%s: %Rrc (PCHS=%u/%u/%u)\n", __FUNCTION__, rc,
2506 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
2507 pDisk->PCHSGeometry.cSectors));
2508 return rc;
2509}
2510
2511/**
2512 * Store virtual disk PCHS geometry in HDD container.
2513 *
2514 * Note that in case of unrecoverable error all images in HDD container will be closed.
2515 *
2516 * @returns VBox status code.
2517 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2518 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2519 * @param pDisk Pointer to HDD container.
2520 * @param nImage Image number, counts from 0. 0 is always base image of container.
2521 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
2522 */
2523VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2524 PCPDMMEDIAGEOMETRY pPCHSGeometry)
2525{
2526 int rc = VINF_SUCCESS;
2527
2528 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
2529 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
2530 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
2531 do
2532 {
2533 /* sanity check */
2534 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2535 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2536
2537 /* Check arguments. */
2538 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
2539 && pPCHSGeometry->cCylinders <= 16383
2540 && pPCHSGeometry->cHeads <= 16
2541 && pPCHSGeometry->cSectors <= 63,
2542 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
2543 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
2544 pPCHSGeometry->cSectors),
2545 rc = VERR_INVALID_PARAMETER);
2546
2547 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2548 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2549
2550 if (pImage == pDisk->pLast)
2551 {
2552 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
2553 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
2554 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
2555 {
2556 /* Only update geometry if it is changed. Avoids similar checks
2557 * in every backend. Most of the time the new geometry is set
2558 * to the previous values, so no need to go through the hassle
2559 * of updating an image which could be opened in read-only mode
2560 * right now. */
2561 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2562 pPCHSGeometry);
2563
2564 /* Cache new geometry values in any case. */
2565 int rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2566 &pDisk->PCHSGeometry);
2567 if (RT_FAILURE(rc2))
2568 {
2569 pDisk->PCHSGeometry.cCylinders = 0;
2570 pDisk->PCHSGeometry.cHeads = 0;
2571 pDisk->PCHSGeometry.cSectors = 0;
2572 }
2573 else
2574 {
2575 /* Make sure the CHS geometry is properly clipped. */
2576 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 1024);
2577 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
2578 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
2579 }
2580 }
2581 }
2582 else
2583 {
2584 PDMMEDIAGEOMETRY PCHS;
2585 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2586 &PCHS);
2587 if ( RT_FAILURE(rc)
2588 || pPCHSGeometry->cCylinders != PCHS.cCylinders
2589 || pPCHSGeometry->cHeads != PCHS.cHeads
2590 || pPCHSGeometry->cSectors != PCHS.cSectors)
2591 {
2592 /* Only update geometry if it is changed. Avoids similar checks
2593 * in every backend. Most of the time the new geometry is set
2594 * to the previous values, so no need to go through the hassle
2595 * of updating an image which could be opened in read-only mode
2596 * right now. */
2597 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2598 pPCHSGeometry);
2599 }
2600 }
2601 } while (0);
2602
2603 LogFlowFunc(("returns %Rrc\n", rc));
2604 return rc;
2605}
2606
2607/**
2608 * Get virtual disk LCHS geometry stored in HDD container.
2609 *
2610 * @returns VBox status code.
2611 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2612 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2613 * @param pDisk Pointer to HDD container.
2614 * @param nImage Image number, counts from 0. 0 is always base image of container.
2615 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
2616 */
2617VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2618 PPDMMEDIAGEOMETRY pLCHSGeometry)
2619{
2620 int rc = VINF_SUCCESS;
2621
2622 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
2623 pDisk, nImage, pLCHSGeometry));
2624 do
2625 {
2626 /* sanity check */
2627 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2628 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2629
2630 /* Check arguments. */
2631 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
2632 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
2633 rc = VERR_INVALID_PARAMETER);
2634
2635 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2636 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2637
2638 if (pImage == pDisk->pLast)
2639 {
2640 /* Use cached information if possible. */
2641 if (pDisk->LCHSGeometry.cCylinders != 0)
2642 *pLCHSGeometry = pDisk->LCHSGeometry;
2643 else
2644 rc = VERR_VDI_GEOMETRY_NOT_SET;
2645 }
2646 else
2647 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2648 pLCHSGeometry);
2649 } while (0);
2650
2651 LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
2652 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
2653 pDisk->LCHSGeometry.cSectors));
2654 return rc;
2655}
2656
2657/**
2658 * Store virtual disk LCHS geometry in HDD container.
2659 *
2660 * Note that in case of unrecoverable error all images in HDD container will be closed.
2661 *
2662 * @returns VBox status code.
2663 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2664 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2665 * @param pDisk Pointer to HDD container.
2666 * @param nImage Image number, counts from 0. 0 is always base image of container.
2667 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
2668 */
2669VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2670 PCPDMMEDIAGEOMETRY pLCHSGeometry)
2671{
2672 int rc = VINF_SUCCESS;
2673
2674 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
2675 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
2676 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2677 do
2678 {
2679 /* sanity check */
2680 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2681 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2682
2683 /* Check arguments. */
2684 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
2685 && pLCHSGeometry->cCylinders <= 1024
2686 && pLCHSGeometry->cHeads <= 255
2687 && pLCHSGeometry->cSectors <= 63,
2688 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
2689 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
2690 pLCHSGeometry->cSectors),
2691 rc = VERR_INVALID_PARAMETER);
2692
2693 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2694 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2695
2696 if (pImage == pDisk->pLast)
2697 {
2698 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
2699 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
2700 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
2701 {
2702 /* Only update geometry if it is changed. Avoids similar checks
2703 * in every backend. Most of the time the new geometry is set
2704 * to the previous values, so no need to go through the hassle
2705 * of updating an image which could be opened in read-only mode
2706 * right now. */
2707 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2708 pLCHSGeometry);
2709
2710 /* Cache new geometry values in any case. */
2711 int rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2712 &pDisk->LCHSGeometry);
2713 if (RT_FAILURE(rc2))
2714 {
2715 pDisk->LCHSGeometry.cCylinders = 0;
2716 pDisk->LCHSGeometry.cHeads = 0;
2717 pDisk->LCHSGeometry.cSectors = 0;
2718 }
2719 else
2720 {
2721 /* Make sure the CHS geometry is properly clipped. */
2722 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
2723 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
2724 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
2725 }
2726 }
2727 }
2728 else
2729 {
2730 PDMMEDIAGEOMETRY LCHS;
2731 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2732 &LCHS);
2733 if ( RT_FAILURE(rc)
2734 || pLCHSGeometry->cCylinders != LCHS.cCylinders
2735 || pLCHSGeometry->cHeads != LCHS.cHeads
2736 || pLCHSGeometry->cSectors != LCHS.cSectors)
2737 {
2738 /* Only update geometry if it is changed. Avoids similar checks
2739 * in every backend. Most of the time the new geometry is set
2740 * to the previous values, so no need to go through the hassle
2741 * of updating an image which could be opened in read-only mode
2742 * right now. */
2743 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2744 pLCHSGeometry);
2745 }
2746 }
2747 } while (0);
2748
2749 LogFlowFunc(("returns %Rrc\n", rc));
2750 return rc;
2751}
2752
2753/**
2754 * Get version of image in HDD container.
2755 *
2756 * @returns VBox status code.
2757 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2758 * @param pDisk Pointer to HDD container.
2759 * @param nImage Image number, counts from 0. 0 is always base image of container.
2760 * @param puVersion Where to store the image version.
2761 */
2762VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
2763 unsigned *puVersion)
2764{
2765 int rc = VINF_SUCCESS;
2766
2767 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
2768 pDisk, nImage, puVersion));
2769 do
2770 {
2771 /* sanity check */
2772 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2773 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2774
2775 /* Check arguments. */
2776 AssertMsgBreakStmt(VALID_PTR(puVersion),
2777 ("puVersion=%#p\n", puVersion),
2778 rc = VERR_INVALID_PARAMETER);
2779
2780 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2781 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2782
2783 *puVersion = pImage->Backend->pfnGetVersion(pImage->pvBackendData);
2784 } while (0);
2785
2786 LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
2787 return rc;
2788}
2789
2790/**
2791 * Get type of image in HDD container.
2792 *
2793 * @returns VBox status code.
2794 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2795 * @param pDisk Pointer to HDD container.
2796 * @param nImage Image number, counts from 0. 0 is always base image of container.
2797 * @param penmType Where to store the image type.
2798 */
2799VBOXDDU_DECL(int) VDGetImageType(PVBOXHDD pDisk, unsigned nImage,
2800 PVDIMAGETYPE penmType)
2801{
2802 int rc = VINF_SUCCESS;
2803
2804 LogFlowFunc(("pDisk=%#p nImage=%u penmType=%#p\n",
2805 pDisk, nImage, penmType));
2806 do
2807 {
2808 /* sanity check */
2809 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2810 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2811
2812 /* Check arguments. */
2813 AssertMsgBreakStmt(VALID_PTR(penmType),
2814 ("penmType=%#p\n", penmType),
2815 rc = VERR_INVALID_PARAMETER);
2816
2817 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2818 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2819
2820 if ( pImage->enmImageType >= VD_IMAGE_TYPE_FIRST
2821 && pImage->enmImageType <= VD_IMAGE_TYPE_DIFF)
2822 {
2823 *penmType = pImage->enmImageType;
2824 rc = VINF_SUCCESS;
2825 }
2826 else
2827 rc = VERR_VDI_INVALID_TYPE;
2828 } while (0);
2829
2830 LogFlowFunc(("returns %Rrc uenmType=%u\n", rc, *penmType));
2831 return rc;
2832}
2833
2834
2835/**
2836 * List the capabilities of image backend in HDD container.
2837 *
2838 * @returns VBox status code.
2839 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2840 * @param pDisk Pointer to the HDD container.
2841 * @param nImage Image number, counts from 0. 0 is always base image of container.
2842 * @param pbackendInfo Where to store the backend information.
2843 */
2844VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
2845 PVDBACKENDINFO pBackendInfo)
2846{
2847 int rc = VINF_SUCCESS;
2848
2849 LogFlowFunc(("pDisk=%#p nImage=%u penmType=%#p\n",
2850 pDisk, nImage, pBackendInfo));
2851 do
2852 {
2853 /* sanity check */
2854 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2855 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2856
2857 /* Check arguments. */
2858 AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
2859 ("pBackendInfo=%#p\n", pBackendInfo),
2860 rc = VERR_INVALID_PARAMETER);
2861
2862 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2863 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2864
2865 if ( pImage->enmImageType >= VD_IMAGE_TYPE_FIRST
2866 && pImage->enmImageType <= VD_IMAGE_TYPE_DIFF)
2867 {
2868 pBackendInfo->pszBackend = RTStrDup(pImage->Backend->pszBackendName);
2869 pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
2870 pBackendInfo->papszFileExtensions = pImage->Backend->papszFileExtensions;
2871 pBackendInfo->paConfigInfo = pImage->Backend->paConfigInfo;
2872 rc = VINF_SUCCESS;
2873 }
2874 else
2875 rc = VERR_VDI_INVALID_TYPE;
2876 } while (0);
2877
2878 LogFlowFunc(("returns %Rrc\n", rc));
2879 return rc;
2880}
2881
2882/**
2883 * Get flags of image in HDD container.
2884 *
2885 * @returns VBox status code.
2886 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2887 * @param pDisk Pointer to HDD container.
2888 * @param nImage Image number, counts from 0. 0 is always base image of container.
2889 * @param puImageFlags Where to store the image flags.
2890 */
2891VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
2892 unsigned *puImageFlags)
2893{
2894 int rc = VINF_SUCCESS;
2895
2896 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
2897 pDisk, nImage, puImageFlags));
2898 do
2899 {
2900 /* sanity check */
2901 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2902 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2903
2904 /* Check arguments. */
2905 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
2906 ("puImageFlags=%#p\n", puImageFlags),
2907 rc = VERR_INVALID_PARAMETER);
2908
2909 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2910 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2911
2912 *puImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pvBackendData);
2913 } while (0);
2914
2915 LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
2916 return rc;
2917}
2918
2919/**
2920 * Get open flags of image in HDD container.
2921 *
2922 * @returns VBox status code.
2923 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2924 * @param pDisk Pointer to HDD container.
2925 * @param nImage Image number, counts from 0. 0 is always base image of container.
2926 * @param puOpenFlags Where to store the image open flags.
2927 */
2928VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2929 unsigned *puOpenFlags)
2930{
2931 int rc = VINF_SUCCESS;
2932
2933 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
2934 pDisk, nImage, puOpenFlags));
2935 do
2936 {
2937 /* sanity check */
2938 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2939 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2940
2941 /* Check arguments. */
2942 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
2943 ("puOpenFlags=%#p\n", puOpenFlags),
2944 rc = VERR_INVALID_PARAMETER);
2945
2946 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2947 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2948
2949 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2950 } while (0);
2951
2952 LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
2953 return rc;
2954}
2955
2956/**
2957 * Set open flags of image in HDD container.
2958 * This operation may cause file locking changes and/or files being reopened.
2959 * Note that in case of unrecoverable error all images in HDD container will be closed.
2960 *
2961 * @returns VBox status code.
2962 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2963 * @param pDisk Pointer to HDD container.
2964 * @param nImage Image number, counts from 0. 0 is always base image of container.
2965 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
2966 */
2967VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2968 unsigned uOpenFlags)
2969{
2970 int rc;
2971
2972 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
2973 do
2974 {
2975 /* sanity check */
2976 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2977 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2978
2979 /* Check arguments. */
2980 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
2981 ("uOpenFlags=%#x\n", uOpenFlags),
2982 rc = VERR_INVALID_PARAMETER);
2983
2984 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2985 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2986
2987 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData,
2988 uOpenFlags);
2989 } while (0);
2990
2991 LogFlowFunc(("returns %Rrc\n", rc));
2992 return rc;
2993}
2994
2995/**
2996 * Get base filename of image in HDD container. Some image formats use
2997 * other filenames as well, so don't use this for anything but informational
2998 * purposes.
2999 *
3000 * @returns VBox status code.
3001 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3002 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
3003 * @param pDisk Pointer to HDD container.
3004 * @param nImage Image number, counts from 0. 0 is always base image of container.
3005 * @param pszFilename Where to store the image file name.
3006 * @param cbFilename Size of buffer pszFilename points to.
3007 */
3008VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
3009 char *pszFilename, unsigned cbFilename)
3010{
3011 int rc;
3012
3013 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
3014 pDisk, nImage, pszFilename, cbFilename));
3015 do
3016 {
3017 /* sanity check */
3018 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3019 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3020
3021 /* Check arguments. */
3022 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
3023 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
3024 rc = VERR_INVALID_PARAMETER);
3025 AssertMsgBreakStmt(cbFilename,
3026 ("cbFilename=%u\n", cbFilename),
3027 rc = VERR_INVALID_PARAMETER);
3028
3029 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3030 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3031
3032 size_t cb = strlen(pImage->pszFilename);
3033 if (cb <= cbFilename)
3034 {
3035 strcpy(pszFilename, pImage->pszFilename);
3036 rc = VINF_SUCCESS;
3037 }
3038 else
3039 {
3040 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
3041 pszFilename[cbFilename - 1] = '\0';
3042 rc = VERR_BUFFER_OVERFLOW;
3043 }
3044 } while (0);
3045
3046 LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
3047 return rc;
3048}
3049
3050/**
3051 * Get the comment line of image in HDD container.
3052 *
3053 * @returns VBox status code.
3054 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3055 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
3056 * @param pDisk Pointer to HDD container.
3057 * @param nImage Image number, counts from 0. 0 is always base image of container.
3058 * @param pszComment Where to store the comment string of image. NULL is ok.
3059 * @param cbComment The size of pszComment buffer. 0 is ok.
3060 */
3061VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
3062 char *pszComment, unsigned cbComment)
3063{
3064 int rc;
3065
3066 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
3067 pDisk, nImage, pszComment, cbComment));
3068 do
3069 {
3070 /* sanity check */
3071 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3072 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3073
3074 /* Check arguments. */
3075 AssertMsgBreakStmt(VALID_PTR(pszComment),
3076 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
3077 rc = VERR_INVALID_PARAMETER);
3078 AssertMsgBreakStmt(cbComment,
3079 ("cbComment=%u\n", cbComment),
3080 rc = VERR_INVALID_PARAMETER);
3081
3082 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3083 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3084
3085 rc = pImage->Backend->pfnGetComment(pImage->pvBackendData, pszComment,
3086 cbComment);
3087 } while (0);
3088
3089 LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
3090 return rc;
3091}
3092
3093/**
3094 * Changes the comment line of image in HDD container.
3095 *
3096 * @returns VBox status code.
3097 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3098 * @param pDisk Pointer to HDD container.
3099 * @param nImage Image number, counts from 0. 0 is always base image of container.
3100 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
3101 */
3102VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
3103 const char *pszComment)
3104{
3105 int rc;
3106
3107 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
3108 pDisk, nImage, pszComment, pszComment));
3109 do
3110 {
3111 /* sanity check */
3112 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3113 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3114
3115 /* Check arguments. */
3116 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
3117 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
3118 rc = VERR_INVALID_PARAMETER);
3119
3120 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3121 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3122
3123 rc = pImage->Backend->pfnSetComment(pImage->pvBackendData, pszComment);
3124 } while (0);
3125
3126 LogFlowFunc(("returns %Rrc\n", rc));
3127 return rc;
3128}
3129
3130
3131/**
3132 * Get UUID of image in HDD container.
3133 *
3134 * @returns VBox status code.
3135 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3136 * @param pDisk Pointer to HDD container.
3137 * @param nImage Image number, counts from 0. 0 is always base image of container.
3138 * @param pUuid Where to store the image creation UUID.
3139 */
3140VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
3141{
3142 int rc;
3143
3144 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3145 do
3146 {
3147 /* sanity check */
3148 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3149 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3150
3151 /* Check arguments. */
3152 AssertMsgBreakStmt(VALID_PTR(pUuid),
3153 ("pUuid=%#p\n", pUuid),
3154 rc = VERR_INVALID_PARAMETER);
3155
3156 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3157 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3158
3159 rc = pImage->Backend->pfnGetUuid(pImage->pvBackendData, pUuid);
3160 } while (0);
3161
3162 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
3163 return rc;
3164}
3165
3166/**
3167 * Set the image's UUID. Should not be used by normal applications.
3168 *
3169 * @returns VBox status code.
3170 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3171 * @param pDisk Pointer to HDD container.
3172 * @param nImage Image number, counts from 0. 0 is always base image of container.
3173 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
3174 */
3175VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
3176{
3177 int rc;
3178
3179 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
3180 pDisk, nImage, pUuid, pUuid));
3181 do
3182 {
3183 /* sanity check */
3184 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3185 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3186
3187 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3188 ("pUuid=%#p\n", pUuid),
3189 rc = VERR_INVALID_PARAMETER);
3190
3191 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3192 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3193
3194 RTUUID Uuid;
3195 if (!pUuid)
3196 {
3197 RTUuidCreate(&Uuid);
3198 pUuid = &Uuid;
3199 }
3200 rc = pImage->Backend->pfnSetUuid(pImage->pvBackendData, pUuid);
3201 } while (0);
3202
3203 LogFlowFunc(("returns %Rrc\n", rc));
3204 return rc;
3205}
3206
3207/**
3208 * Get last modification UUID of image in HDD container.
3209 *
3210 * @returns VBox status code.
3211 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3212 * @param pDisk Pointer to HDD container.
3213 * @param nImage Image number, counts from 0. 0 is always base image of container.
3214 * @param pUuid Where to store the image modification UUID.
3215 */
3216VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
3217{
3218 int rc = VINF_SUCCESS;
3219
3220 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3221 do
3222 {
3223 /* sanity check */
3224 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3225 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3226
3227 /* Check arguments. */
3228 AssertMsgBreakStmt(VALID_PTR(pUuid),
3229 ("pUuid=%#p\n", pUuid),
3230 rc = VERR_INVALID_PARAMETER);
3231
3232 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3233 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3234
3235 rc = pImage->Backend->pfnGetModificationUuid(pImage->pvBackendData,
3236 pUuid);
3237 } while (0);
3238
3239 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
3240 return rc;
3241}
3242
3243/**
3244 * Set the image's last modification UUID. Should not be used by normal applications.
3245 *
3246 * @returns VBox status code.
3247 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3248 * @param pDisk Pointer to HDD container.
3249 * @param nImage Image number, counts from 0. 0 is always base image of container.
3250 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
3251 */
3252VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
3253{
3254 int rc;
3255
3256 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
3257 pDisk, nImage, pUuid, pUuid));
3258 do
3259 {
3260 /* sanity check */
3261 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3262 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3263
3264 /* Check arguments. */
3265 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3266 ("pUuid=%#p\n", pUuid),
3267 rc = VERR_INVALID_PARAMETER);
3268
3269 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3270 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3271
3272 RTUUID Uuid;
3273 if (!pUuid)
3274 {
3275 RTUuidCreate(&Uuid);
3276 pUuid = &Uuid;
3277 }
3278 rc = pImage->Backend->pfnSetModificationUuid(pImage->pvBackendData,
3279 pUuid);
3280 } while (0);
3281
3282 LogFlowFunc(("returns %Rrc\n", rc));
3283 return rc;
3284}
3285
3286/**
3287 * Get parent UUID of image in HDD container.
3288 *
3289 * @returns VBox status code.
3290 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3291 * @param pDisk Pointer to HDD container.
3292 * @param nImage Image number, counts from 0. 0 is always base image of container.
3293 * @param pUuid Where to store the parent image UUID.
3294 */
3295VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
3296 PRTUUID pUuid)
3297{
3298 int rc = VINF_SUCCESS;
3299
3300 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3301 do
3302 {
3303 /* sanity check */
3304 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3305 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3306
3307 /* Check arguments. */
3308 AssertMsgBreakStmt(VALID_PTR(pUuid),
3309 ("pUuid=%#p\n", pUuid),
3310 rc = VERR_INVALID_PARAMETER);
3311
3312 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3313 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3314
3315 rc = pImage->Backend->pfnGetParentUuid(pImage->pvBackendData, pUuid);
3316 } while (0);
3317
3318 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
3319 return rc;
3320}
3321
3322/**
3323 * Set the image's parent UUID. Should not be used by normal applications.
3324 *
3325 * @returns VBox status code.
3326 * @param pDisk Pointer to HDD container.
3327 * @param nImage Image number, counts from 0. 0 is always base image of container.
3328 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
3329 */
3330VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
3331 PCRTUUID pUuid)
3332{
3333 int rc;
3334
3335 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
3336 pDisk, nImage, pUuid, pUuid));
3337 do
3338 {
3339 /* sanity check */
3340 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3341 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3342
3343 /* Check arguments. */
3344 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3345 ("pUuid=%#p\n", pUuid),
3346 rc = VERR_INVALID_PARAMETER);
3347
3348 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3349 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3350
3351 RTUUID Uuid;
3352 if (!pUuid)
3353 {
3354 RTUuidCreate(&Uuid);
3355 pUuid = &Uuid;
3356 }
3357 rc = pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, pUuid);
3358 } while (0);
3359
3360 LogFlowFunc(("returns %Rrc\n", rc));
3361 return rc;
3362}
3363
3364
3365/**
3366 * Debug helper - dumps all opened images in HDD container into the log file.
3367 *
3368 * @param pDisk Pointer to HDD container.
3369 */
3370VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
3371{
3372 do
3373 {
3374 /* sanity check */
3375 AssertPtrBreak(pDisk);
3376 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3377
3378 RTLogPrintf("--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
3379 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
3380 {
3381 RTLogPrintf("Dumping VD image \"%s\" (Backend=%s)\n",
3382 pImage->pszFilename, pImage->Backend->pszBackendName);
3383 pImage->Backend->pfnDump(pImage->pvBackendData);
3384 }
3385 } while (0);
3386}
3387
3388/**
3389 * Query if asynchronous operations are supported for this disk.
3390 *
3391 * @returns VBox status code.
3392 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3393 * @param pDisk Pointer to the HDD container.
3394 * @param nImage Image number, counts from 0. 0 is always base image of container.
3395 * @param pfAIOSupported Where to store if async IO is supported.
3396 */
3397VBOXDDU_DECL(int) VDImageIsAsyncIOSupported(PVBOXHDD pDisk, unsigned nImage, bool *pfAIOSupported)
3398{
3399 int rc = VINF_SUCCESS;
3400
3401 LogFlowFunc(("pDisk=%#p nImage=%u pfAIOSupported=%#p\n", pDisk, nImage, pfAIOSupported));
3402 do
3403 {
3404 /* sanity check */
3405 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3406 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3407
3408 /* Check arguments. */
3409 AssertMsgBreakStmt(VALID_PTR(pfAIOSupported),
3410 ("pfAIOSupported=%#p\n", pfAIOSupported),
3411 rc = VERR_INVALID_PARAMETER);
3412
3413 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3414 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3415
3416 if (pImage->Backend->uBackendCaps & VD_CAP_ASYNC)
3417 *pfAIOSupported = pImage->Backend->pfnIsAsyncIOSupported(pImage->pvBackendData);
3418 else
3419 *pfAIOSupported = false;
3420 } while (0);
3421
3422 LogFlowFunc(("returns %Rrc, fAIOSupported=%u\n", rc, *pfAIOSupported));
3423 return rc;
3424}
3425
3426/**
3427 * Start a asynchronous read request.
3428 *
3429 * @returns VBox status code.
3430 * @param pDisk Pointer to the HDD container.
3431 * @param uOffset The offset of the virtual disk to read from.
3432 * @param cbRead How many bytes to read.
3433 * @param paSeg Pointer to an array of segments.
3434 * @param cSeg Number of segments in the array.
3435 * @param pvUser User data which is passed on completion
3436 */
3437VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
3438 PPDMDATASEG paSeg, unsigned cSeg,
3439 void *pvUser)
3440{
3441 int rc = VERR_VDI_BLOCK_FREE;
3442
3443 LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbRead=%zu\n",
3444 pDisk, uOffset, paSeg, cSeg, cbRead));
3445 do
3446 {
3447 /* sanity check */
3448 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3449 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3450
3451 /* Check arguments. */
3452 AssertMsgBreakStmt(cbRead,
3453 ("cbRead=%zu\n", cbRead),
3454 rc = VERR_INVALID_PARAMETER);
3455 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
3456 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
3457 uOffset, cbRead, pDisk->cbSize),
3458 rc = VERR_INVALID_PARAMETER);
3459 AssertMsgBreakStmt(VALID_PTR(paSeg),
3460 ("paSeg=%#p\n", paSeg),
3461 rc = VERR_INVALID_PARAMETER);
3462 AssertMsgBreakStmt(cSeg,
3463 ("cSeg=%zu\n", cSeg),
3464 rc = VERR_INVALID_PARAMETER);
3465
3466
3467 PVDIMAGE pImage = pDisk->pLast;
3468 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
3469
3470 /* @todo: This does not work for images which do not have all meta data in memory. */
3471 for (PVDIMAGE pCurrImage = pImage;
3472 pCurrImage != NULL && rc == VERR_VDI_BLOCK_FREE;
3473 pCurrImage = pCurrImage->pPrev)
3474 {
3475 rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pvBackendData,
3476 uOffset, cbRead, paSeg, cSeg,
3477 pvUser);
3478 }
3479
3480 /* No image in the chain contains the data for the block. */
3481 if (rc == VERR_VDI_BLOCK_FREE)
3482 {
3483 for (unsigned i = 0; i < cSeg && (cbRead > 0); i++)
3484 {
3485 memset(paSeg[i].pvSeg, '\0', paSeg[i].cbSeg);
3486 cbRead -= paSeg[i].cbSeg;
3487 }
3488 /* Request finished without the need to enqueue a async I/O request. Tell caller. */
3489 rc = VINF_VDI_ASYNC_IO_FINISHED;
3490 }
3491
3492 } while (0);
3493
3494 LogFlowFunc(("returns %Rrc\n", rc));
3495 return rc;
3496}
3497
3498
3499/**
3500 * Start a asynchronous write request.
3501 *
3502 * @returns VBox status code.
3503 * @param pDisk Pointer to the HDD container.
3504 * @param uOffset The offset of the virtual disk to write to.
3505 * @param cbWrtie How many bytes to write.
3506 * @param paSeg Pointer to an array of segments.
3507 * @param cSeg Number of segments in the array.
3508 * @param pvUser User data which is passed on completion.
3509 */
3510VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
3511 PPDMDATASEG paSeg, unsigned cSeg,
3512 void *pvUser)
3513{
3514 int rc;
3515
3516 LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbWrite=%zu\n",
3517 pDisk, uOffset, paSeg, cSeg, cbWrite));
3518 do
3519 {
3520 /* sanity check */
3521 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3522 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3523
3524 /* Check arguments. */
3525 AssertMsgBreakStmt(cbWrite,
3526 ("cbWrite=%zu\n", cbWrite),
3527 rc = VERR_INVALID_PARAMETER);
3528 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
3529 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
3530 uOffset, cbWrite, pDisk->cbSize),
3531 rc = VERR_INVALID_PARAMETER);
3532 AssertMsgBreakStmt(VALID_PTR(paSeg),
3533 ("paSeg=%#p\n", paSeg),
3534 rc = VERR_INVALID_PARAMETER);
3535 AssertMsgBreakStmt(cSeg,
3536 ("cSeg=%zu\n", cSeg),
3537 rc = VERR_INVALID_PARAMETER);
3538
3539
3540 PVDIMAGE pImage = pDisk->pLast;
3541 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
3542
3543 vdSetModifiedFlag(pDisk);
3544 rc = pImage->Backend->pfnAsyncWrite(pImage->pvBackendData,
3545 uOffset, cbWrite,
3546 paSeg, cSeg, pvUser);
3547 } while (0);
3548
3549 LogFlowFunc(("returns %Rrc\n", rc));
3550 return rc;
3551
3552}
3553
Note: See TracBrowser for help on using the repository browser.

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