VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VBoxHDD.cpp@ 21094

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

Storage/VBoxHDD: paranoia, for cloning to newly created image which is bigger. not usable via API right now.

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