VirtualBox

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

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

Fix condition once more.

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