VirtualBox

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

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

Extended the interface with backends to support time stamps and parent's filename.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 122.3 KB
Line 
1/** $Id: VBoxHDD-new.cpp 9734 2008-06-16 17:20:49Z 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 /* Set up image descriptor. */
1048 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1049 if (!pImage)
1050 {
1051 rc = VERR_NO_MEMORY;
1052 break;
1053 }
1054 pImage->pszFilename = RTStrDup(pszFilename);
1055 if (!pImage->pszFilename)
1056 {
1057 rc = VERR_NO_MEMORY;
1058 break;
1059 }
1060
1061 rc = vdFindBackend(pszBackend, &pImage->Backend, &pImage->hPlugin);
1062 if (VBOX_FAILURE(rc))
1063 break;
1064 if (!pImage->Backend)
1065 {
1066 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1067 N_("VD: unknown backend name '%s'"), pszBackend);
1068 break;
1069 }
1070
1071 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1072 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
1073 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1074 pDisk->pfnError, pDisk->pvErrorUser,
1075 &pImage->pvBackendData);
1076 /* If the open in read-write mode failed, retry in read-only mode. */
1077 if (VBOX_FAILURE(rc))
1078 {
1079 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
1080 && (rc == VERR_ACCESS_DENIED
1081 || rc == VERR_PERMISSION_DENIED
1082 || rc == VERR_WRITE_PROTECT
1083 || rc == VERR_SHARING_VIOLATION
1084 || rc == VERR_FILE_LOCK_FAILED))
1085 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
1086 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
1087 | VD_OPEN_FLAGS_READONLY,
1088 pDisk->pfnError, pDisk->pvErrorUser,
1089 &pImage->pvBackendData);
1090 if (VBOX_FAILURE(rc))
1091 {
1092 rc = vdError(pDisk, rc, RT_SRC_POS,
1093 N_("VD: error opening image file '%s'"), pszFilename);
1094 break;
1095 }
1096 }
1097
1098 VDIMAGETYPE enmImageType;
1099 rc = pImage->Backend->pfnGetImageType(pImage->pvBackendData,
1100 &enmImageType);
1101 /* Check image type. As the image itself has only partial knowledge
1102 * whether it's a base image or not, this info is derived here. The
1103 * base image can be fixed or normal, all others must be normal or
1104 * diff images. Some image formats don't distinguish between normal
1105 * and diff images, so this must be corrected here. */
1106 if (VBOX_FAILURE(rc))
1107 enmImageType = VD_IMAGE_TYPE_INVALID;
1108 if ( VBOX_SUCCESS(rc)
1109 && !(uOpenFlags & VD_OPEN_FLAGS_INFO))
1110 {
1111 if ( pDisk->cImages == 0
1112 && enmImageType != VD_IMAGE_TYPE_FIXED
1113 && enmImageType != VD_IMAGE_TYPE_NORMAL)
1114 {
1115 rc = VERR_VDI_INVALID_TYPE;
1116 break;
1117 }
1118 else if (pDisk->cImages != 0)
1119 {
1120 if ( enmImageType != VD_IMAGE_TYPE_NORMAL
1121 && enmImageType != VD_IMAGE_TYPE_DIFF)
1122 {
1123 rc = VERR_VDI_INVALID_TYPE;
1124 break;
1125 }
1126 else
1127 enmImageType = VD_IMAGE_TYPE_DIFF;
1128 }
1129 }
1130 pImage->enmImageType = enmImageType;
1131
1132 /* Force sane optimization settings. It's not worth avoiding writes
1133 * to fixed size images. The overhead would have almost no payback. */
1134 if (enmImageType == VD_IMAGE_TYPE_FIXED)
1135 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
1136
1137 /** @todo optionally check UUIDs */
1138
1139 int rc2;
1140
1141 /* Cache disk information. */
1142 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
1143
1144 /* Cache PCHS geometry. */
1145 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1146 &pDisk->PCHSGeometry);
1147 if (VBOX_FAILURE(rc2))
1148 {
1149 pDisk->PCHSGeometry.cCylinders = 0;
1150 pDisk->PCHSGeometry.cHeads = 0;
1151 pDisk->PCHSGeometry.cSectors = 0;
1152 }
1153 else
1154 {
1155 /* Make sure the PCHS geometry is properly clipped. */
1156 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1157 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1158 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1159 }
1160
1161 /* Cache LCHS geometry. */
1162 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1163 &pDisk->LCHSGeometry);
1164 if (VBOX_FAILURE(rc2))
1165 {
1166 pDisk->LCHSGeometry.cCylinders = 0;
1167 pDisk->LCHSGeometry.cHeads = 0;
1168 pDisk->LCHSGeometry.cSectors = 0;
1169 }
1170 else
1171 {
1172 /* Make sure the LCHS geometry is properly clipped. */
1173 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
1174 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1175 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1176 }
1177
1178 if (pDisk->cImages != 0)
1179 {
1180 /* Switch previous image to read-only mode. */
1181 unsigned uOpenFlagsPrevImg;
1182 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1183 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
1184 {
1185 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
1186 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
1187 }
1188 }
1189
1190 if (VBOX_SUCCESS(rc))
1191 {
1192 /* Image successfully opened, make it the last image. */
1193 vdAddImageToList(pDisk, pImage);
1194 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1195 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1196 }
1197 else
1198 {
1199 /* Error detected, but image opened. Close image. */
1200 int rc2;
1201 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, false);
1202 AssertRC(rc2);
1203 pImage->pvBackendData = NULL;
1204 }
1205 } while (0);
1206
1207 if (VBOX_FAILURE(rc))
1208 {
1209 if (pImage)
1210 {
1211 if (pImage->hPlugin != NIL_RTLDRMOD)
1212 RTLdrClose(pImage->hPlugin);
1213
1214 if (pImage->pszFilename)
1215 RTStrFree(pImage->pszFilename);
1216 RTMemFree(pImage);
1217 }
1218 }
1219
1220 LogFlowFunc(("returns %Vrc\n", rc));
1221 return rc;
1222}
1223
1224/**
1225 * Creates and opens a new base image file.
1226 *
1227 * @returns VBox status code.
1228 * @param pDisk Pointer to HDD container.
1229 * @param pszBackend Name of the image file backend to use.
1230 * @param pszFilename Name of the image file to create.
1231 * @param enmType Image type, only base image types are acceptable.
1232 * @param cbSize Image size in bytes.
1233 * @param uImageFlags Flags specifying special image features.
1234 * @param pszComment Pointer to image comment. NULL is ok.
1235 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
1236 * @param pLCHSGeometry Pointer to logical disk geometry <= (1024,255,63). Not NULL.
1237 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1238 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1239 * @param pvUser User argument for the progress callback.
1240 */
1241VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
1242 const char *pszFilename, VDIMAGETYPE enmType,
1243 uint64_t cbSize, unsigned uImageFlags,
1244 const char *pszComment,
1245 PCPDMMEDIAGEOMETRY pPCHSGeometry,
1246 PCPDMMEDIAGEOMETRY pLCHSGeometry,
1247 unsigned uOpenFlags, PFNVMPROGRESS pfnProgress,
1248 void *pvUser)
1249{
1250 int rc = VINF_SUCCESS;
1251 PVDIMAGE pImage = NULL;
1252
1253 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",
1254 pDisk, pszBackend, pszFilename, enmType, cbSize, uImageFlags, pszComment,
1255 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1256 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
1257 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, uOpenFlags,
1258 pfnProgress, pvUser));
1259 do
1260 {
1261 /* sanity check */
1262 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1263 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1264
1265 /* Check arguments. */
1266 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
1267 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1268 rc = VERR_INVALID_PARAMETER);
1269 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1270 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1271 rc = VERR_INVALID_PARAMETER);
1272 AssertMsgBreakStmt(enmType == VD_IMAGE_TYPE_NORMAL || enmType == VD_IMAGE_TYPE_FIXED,
1273 ("enmType=%#x\n", enmType),
1274 rc = VERR_INVALID_PARAMETER);
1275 AssertMsgBreakStmt(cbSize,
1276 ("cbSize=%llu\n", cbSize),
1277 rc = VERR_INVALID_PARAMETER);
1278 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
1279 ("uImageFlags=%#x\n", uImageFlags),
1280 rc = VERR_INVALID_PARAMETER);
1281 /* The PCHS geometry fields may be 0 to leave it for later. */
1282 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
1283 && pPCHSGeometry->cCylinders <= 16383
1284 && pPCHSGeometry->cHeads <= 16
1285 && pPCHSGeometry->cSectors <= 63,
1286 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
1287 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1288 pPCHSGeometry->cSectors),
1289 rc = VERR_INVALID_PARAMETER);
1290 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
1291 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
1292 && pLCHSGeometry->cCylinders <= 1024
1293 && pLCHSGeometry->cHeads <= 255
1294 && pLCHSGeometry->cSectors <= 63,
1295 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
1296 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
1297 pLCHSGeometry->cSectors),
1298 rc = VERR_INVALID_PARAMETER);
1299 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1300 ("uOpenFlags=%#x\n", uOpenFlags),
1301 rc = VERR_INVALID_PARAMETER);
1302
1303 /* Check state. */
1304 AssertMsgBreakStmt(pDisk->cImages == 0,
1305 ("Create base image cannot be done with other images open\n"),
1306 rc = VERR_VDI_INVALID_STATE);
1307
1308 /* Set up image descriptor. */
1309 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1310 if (!pImage)
1311 {
1312 rc = VERR_NO_MEMORY;
1313 break;
1314 }
1315 pImage->pszFilename = RTStrDup(pszFilename);
1316 if (!pImage->pszFilename)
1317 {
1318 rc = VERR_NO_MEMORY;
1319 break;
1320 }
1321
1322 rc = vdFindBackend(pszBackend, &pImage->Backend, &pImage->hPlugin);
1323 if (VBOX_FAILURE(rc))
1324 break;
1325 if (!pImage->Backend)
1326 {
1327 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1328 N_("VD: unknown backend name '%s'"), pszBackend);
1329 break;
1330 }
1331
1332 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1333 rc = pImage->Backend->pfnCreate(pImage->pszFilename, enmType, cbSize,
1334 uImageFlags, pszComment, pPCHSGeometry,
1335 pLCHSGeometry,
1336 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1337 pfnProgress, pvUser, 0, 99,
1338 pDisk->pfnError, pDisk->pvErrorUser,
1339 &pImage->pvBackendData);
1340
1341 if (VBOX_SUCCESS(rc))
1342 {
1343 pImage->enmImageType = enmType;
1344
1345 /* Force sane optimization settings. It's not worth avoiding writes
1346 * to fixed size images. The overhead would have almost no payback. */
1347 if (enmType == VD_IMAGE_TYPE_FIXED)
1348 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
1349
1350 /** @todo optionally check UUIDs */
1351
1352 int rc2;
1353
1354 /* Cache disk information. */
1355 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
1356
1357 /* Cache PCHS geometry. */
1358 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1359 &pDisk->PCHSGeometry);
1360 if (VBOX_FAILURE(rc2))
1361 {
1362 pDisk->PCHSGeometry.cCylinders = 0;
1363 pDisk->PCHSGeometry.cHeads = 0;
1364 pDisk->PCHSGeometry.cSectors = 0;
1365 }
1366 else
1367 {
1368 /* Make sure the CHS geometry is properly clipped. */
1369 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1370 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1371 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1372 }
1373
1374 /* Cache LCHS geometry. */
1375 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1376 &pDisk->LCHSGeometry);
1377 if (VBOX_FAILURE(rc2))
1378 {
1379 pDisk->LCHSGeometry.cCylinders = 0;
1380 pDisk->LCHSGeometry.cHeads = 0;
1381 pDisk->LCHSGeometry.cSectors = 0;
1382 }
1383 else
1384 {
1385 /* Make sure the CHS geometry is properly clipped. */
1386 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
1387 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1388 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1389 }
1390 }
1391
1392 if (VBOX_SUCCESS(rc))
1393 {
1394 /* Image successfully opened, make it the last image. */
1395 vdAddImageToList(pDisk, pImage);
1396 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1397 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1398 }
1399 else
1400 {
1401 /* Error detected, but image opened. Close and delete image. */
1402 int rc2;
1403 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
1404 AssertRC(rc2);
1405 pImage->pvBackendData = NULL;
1406 }
1407 } while (0);
1408
1409 if (VBOX_FAILURE(rc))
1410 {
1411 if (pImage)
1412 {
1413 if (pImage->hPlugin != NIL_RTLDRMOD)
1414 RTLdrClose(pImage->hPlugin);
1415
1416 if (pImage->pszFilename)
1417 RTStrFree(pImage->pszFilename);
1418 RTMemFree(pImage);
1419 }
1420 }
1421
1422 if (VBOX_SUCCESS(rc) && pfnProgress)
1423 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1424
1425 LogFlowFunc(("returns %Vrc\n", rc));
1426 return rc;
1427}
1428
1429/**
1430 * Creates and opens a new differencing image file in HDD container.
1431 * See comments for VDOpen function about differencing images.
1432 *
1433 * @returns VBox status code.
1434 * @param pDisk Pointer to HDD container.
1435 * @param pszBackend Name of the image file backend to use.
1436 * @param pszFilename Name of the differencing image file to create.
1437 * @param uImageFlags Flags specifying special image features.
1438 * @param pszComment Pointer to image comment. NULL is ok.
1439 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1440 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1441 * @param pvUser User argument for the progress callback.
1442 */
1443VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
1444 const char *pszFilename, unsigned uImageFlags,
1445 const char *pszComment, unsigned uOpenFlags,
1446 PFNVMPROGRESS pfnProgress, void *pvUser)
1447{
1448 int rc = VINF_SUCCESS;
1449 PVDIMAGE pImage = NULL;
1450
1451 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" uOpenFlags=%#x pfnProgress=%#p pvUser=%#p\n",
1452 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, uOpenFlags,
1453 pfnProgress, pvUser));
1454 do
1455 {
1456 /* sanity check */
1457 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1458 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1459
1460 /* Check arguments. */
1461 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
1462 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1463 rc = VERR_INVALID_PARAMETER);
1464 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1465 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1466 rc = VERR_INVALID_PARAMETER);
1467 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
1468 ("uImageFlags=%#x\n", uImageFlags),
1469 rc = VERR_INVALID_PARAMETER);
1470 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1471 ("uOpenFlags=%#x\n", uOpenFlags),
1472 rc = VERR_INVALID_PARAMETER);
1473
1474 /* Check state. */
1475 AssertMsgBreakStmt(pDisk->cImages != 0,
1476 ("Create diff image cannot be done without other images open\n"),
1477 rc = VERR_VDI_INVALID_STATE);
1478
1479 /* Set up image descriptor. */
1480 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1481 if (!pImage)
1482 {
1483 rc = VERR_NO_MEMORY;
1484 break;
1485 }
1486 pImage->pszFilename = RTStrDup(pszFilename);
1487 if (!pImage->pszFilename)
1488 {
1489 rc = VERR_NO_MEMORY;
1490 break;
1491 }
1492
1493 rc = vdFindBackend(pszBackend, &pImage->Backend, &pImage->hPlugin);
1494 if (VBOX_FAILURE(rc))
1495 break;
1496 if (!pImage->Backend)
1497 {
1498 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1499 N_("VD: unknown backend name '%s'"), pszBackend);
1500 break;
1501 }
1502
1503 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1504 rc = pImage->Backend->pfnCreate(pImage->pszFilename,
1505 VD_IMAGE_TYPE_DIFF, pDisk->cbSize,
1506 uImageFlags, pszComment,
1507 &pDisk->PCHSGeometry,
1508 &pDisk->LCHSGeometry,
1509 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1510 pfnProgress, pvUser, 0, 99,
1511 pDisk->pfnError, pDisk->pvErrorUser,
1512 &pImage->pvBackendData);
1513
1514 if (VBOX_SUCCESS(rc) && pDisk->cImages != 0)
1515 {
1516 pImage->enmImageType = VD_IMAGE_TYPE_DIFF;
1517
1518 /* Switch previous image to read-only mode. */
1519 unsigned uOpenFlagsPrevImg;
1520 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1521 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
1522 {
1523 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
1524 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
1525 }
1526 }
1527
1528 if (VBOX_SUCCESS(rc))
1529 {
1530 RTUUID Uuid;
1531 RTTIMESPEC ts;
1532 int rc2;
1533
1534 rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pvBackendData,
1535 &Uuid);
1536 if (VBOX_SUCCESS(rc2))
1537 pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, &Uuid);
1538 rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pvBackendData,
1539 &Uuid);
1540 if (VBOX_SUCCESS(rc2))
1541 pImage->Backend->pfnSetParentModificationUuid(pImage->pvBackendData,
1542 &Uuid);
1543 rc2 = pDisk->pLast->Backend->pfnGetTimeStamp(pDisk->pLast->pvBackendData,
1544 &ts);
1545 if (VBOX_SUCCESS(rc2))
1546 pImage->Backend->pfnSetParentTimeStamp(pImage->pvBackendData, &ts);
1547
1548 rc2 = pImage->Backend->pfnSetParentFilename(pImage->pvBackendData, pDisk->pLast->pszFilename);
1549 }
1550
1551 if (VBOX_SUCCESS(rc))
1552 {
1553 /** @todo optionally check UUIDs */
1554 }
1555
1556 if (VBOX_SUCCESS(rc))
1557 {
1558 /* Image successfully opened, make it the last image. */
1559 vdAddImageToList(pDisk, pImage);
1560 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1561 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1562 }
1563 else
1564 {
1565 /* Error detected, but image opened. Close and delete image. */
1566 int rc2;
1567 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
1568 AssertRC(rc2);
1569 pImage->pvBackendData = NULL;
1570 }
1571 } while (0);
1572
1573 if (VBOX_FAILURE(rc))
1574 {
1575 if (pImage->hPlugin != NIL_RTLDRMOD)
1576 RTLdrClose(pImage->hPlugin);
1577
1578 if (pImage)
1579 {
1580 if (pImage->pszFilename)
1581 RTStrFree(pImage->pszFilename);
1582 RTMemFree(pImage);
1583 }
1584 }
1585
1586 if (VBOX_SUCCESS(rc) && pfnProgress)
1587 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1588
1589 LogFlowFunc(("returns %Vrc\n", rc));
1590 return rc;
1591}
1592
1593/**
1594 * Merges two images (not necessarily with direct parent/child relationship).
1595 * As a side effect the source image and potentially the other images which
1596 * are also merged to the destination are deleted from both the disk and the
1597 * images in the HDD container.
1598 *
1599 * @returns VBox status code.
1600 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1601 * @param pDisk Pointer to HDD container.
1602 * @param nImageFrom Name of the image file to merge from.
1603 * @param nImageTo Name of the image file to merge to.
1604 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1605 * @param pvUser User argument for the progress callback.
1606 */
1607VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
1608 unsigned nImageTo, PFNVMPROGRESS pfnProgress,
1609 void *pvUser)
1610{
1611 int rc = VINF_SUCCESS;
1612 void *pvBuf = NULL;
1613
1614 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pfnProgress=%#p pvUser=%#p\n",
1615 pDisk, nImageFrom, nImageTo, pfnProgress, pvUser));
1616 do
1617 {
1618 /* sanity check */
1619 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1620 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1621
1622 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
1623 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
1624 if (!pImageFrom || !pImageTo)
1625 {
1626 rc = VERR_VDI_IMAGE_NOT_FOUND;
1627 break;
1628 }
1629 AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
1630
1631 /* Make sure destination image is writable. */
1632 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
1633 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
1634 {
1635 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
1636 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pvBackendData,
1637 uOpenFlags);
1638 if (VBOX_FAILURE(rc))
1639 break;
1640 }
1641
1642 /* Get size of destination image. */
1643 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pvBackendData);
1644
1645 /* Allocate tmp buffer. */
1646 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
1647 if (!pvBuf)
1648 {
1649 rc = VERR_NO_MEMORY;
1650 break;
1651 }
1652
1653 /* Merging is done directly on the images itself. This potentially
1654 * causes trouble if the disk is full in the middle of operation. */
1655 /** @todo write alternative implementation which works with temporary
1656 * images (which is safer, but requires even more space). Also has the
1657 * drawback that merging into a raw disk parent simply isn't possible
1658 * this way (but in that case disk full isn't really a problem). */
1659 if (nImageFrom < nImageTo)
1660 {
1661 /* Merge parent state into child. This means writing all not
1662 * allocated blocks in the destination image which are allocated in
1663 * the images to be merged. */
1664 uint64_t uOffset = 0;
1665 uint64_t cbRemaining = cbSize;
1666 do
1667 {
1668 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1669 rc = pImageTo->Backend->pfnRead(pImageTo->pvBackendData,
1670 uOffset, pvBuf, cbThisRead,
1671 &cbThisRead);
1672 if (rc == VERR_VDI_BLOCK_FREE)
1673 {
1674 /* Search for image with allocated block. Do not attempt to
1675 * read more than the previous reads marked as valid.
1676 * Otherwise this would return stale data when different
1677 * block sizes are used for the images. */
1678 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
1679 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VDI_BLOCK_FREE;
1680 pCurrImage = pCurrImage->pPrev)
1681 {
1682 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
1683 uOffset, pvBuf,
1684 cbThisRead,
1685 &cbThisRead);
1686 }
1687
1688 if (rc != VERR_VDI_BLOCK_FREE)
1689 {
1690 if (VBOX_FAILURE(rc))
1691 break;
1692 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1693 cbThisRead);
1694 if (VBOX_FAILURE(rc))
1695 break;
1696 }
1697 else
1698 rc = VINF_SUCCESS;
1699 }
1700 else if (VBOX_FAILURE(rc))
1701 break;
1702
1703 uOffset += cbThisRead;
1704 cbRemaining -= cbThisRead;
1705 } while (uOffset < cbSize);
1706 }
1707 else
1708 {
1709 /* Merge child state into parent. This means writing all blocks
1710 * which are allocated in the image up to the source image to the
1711 * destination image. */
1712 uint64_t uOffset = 0;
1713 uint64_t cbRemaining = cbSize;
1714 do
1715 {
1716 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1717 rc = VERR_VDI_BLOCK_FREE;
1718 /* Search for image with allocated block. Do not attempt to
1719 * read more than the previous reads marked as valid. Otherwise
1720 * this would return stale data when different block sizes are
1721 * used for the images. */
1722 for (PVDIMAGE pCurrImage = pImageFrom;
1723 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VDI_BLOCK_FREE;
1724 pCurrImage = pCurrImage->pPrev)
1725 {
1726 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
1727 uOffset, pvBuf,
1728 cbThisRead, &cbThisRead);
1729 }
1730
1731 if (rc != VERR_VDI_BLOCK_FREE)
1732 {
1733 if (VBOX_FAILURE(rc))
1734 break;
1735 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1736 cbThisRead);
1737 if (VBOX_FAILURE(rc))
1738 break;
1739 }
1740 else
1741 rc = VINF_SUCCESS;
1742
1743 uOffset += cbThisRead;
1744 cbRemaining -= cbThisRead;
1745 } while (uOffset < cbSize);
1746 }
1747
1748 /* Update parent UUID so that image chain is consistent. */
1749 RTUUID Uuid;
1750 if (nImageFrom < nImageTo)
1751 {
1752 if (pImageTo->pPrev)
1753 {
1754 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pPrev->pvBackendData,
1755 &Uuid);
1756 AssertRC(rc);
1757 }
1758 else
1759 RTUuidClear(&Uuid);
1760 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pvBackendData,
1761 &Uuid);
1762 AssertRC(rc);
1763 }
1764 else
1765 {
1766 if (pImageFrom->pNext)
1767 {
1768 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pvBackendData,
1769 &Uuid);
1770 AssertRC(rc);
1771 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext,
1772 &Uuid);
1773 AssertRC(rc);
1774 }
1775 }
1776
1777 /* Make sure destination image is back to read only if necessary. */
1778 if (pImageTo != pDisk->pLast && pImageFrom != pDisk->pLast)
1779 {
1780 uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
1781 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
1782 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pvBackendData,
1783 uOpenFlags);
1784 if (VBOX_FAILURE(rc))
1785 break;
1786 }
1787
1788 /* Delete the no longer needed images. */
1789 PVDIMAGE pImg = pImageFrom, pTmp;
1790 while (pImg != pImageTo)
1791 {
1792 if (nImageFrom < nImageTo)
1793 pTmp = pImg->pNext;
1794 else
1795 pTmp = pImg->pPrev;
1796 vdRemoveImageFromList(pDisk, pImg);
1797 pImg->Backend->pfnClose(pImg->pvBackendData, true);
1798 pImg = pTmp;
1799 }
1800 } while (0);
1801
1802 if (pvBuf)
1803 RTMemTmpFree(pvBuf);
1804
1805 if (VBOX_SUCCESS(rc) && pfnProgress)
1806 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1807
1808 LogFlowFunc(("returns %Vrc\n", rc));
1809 return rc;
1810}
1811
1812/**
1813 * Copies an image from one HDD container to another.
1814 * The copy is opened in the target HDD container.
1815 * It is possible to convert between different image formats, because the
1816 * backend for the destination may be different from the source.
1817 * If both the source and destination reference the same HDD container,
1818 * then the image is moved (by copying/deleting or renaming) to the new location.
1819 * The source container is unchanged if the move operation fails, otherwise
1820 * the image at the new location is opened in the same way as the old one was.
1821 *
1822 * @returns VBox status code.
1823 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1824 * @param pDiskFrom Pointer to source HDD container.
1825 * @param nImage Image number, counts from 0. 0 is always base image of container.
1826 * @param pDiskTo Pointer to destination HDD container.
1827 * @param pszBackend Name of the image file backend to use.
1828 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
1829 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
1830 * @param cbSize New image size (0 means leave unchanged).
1831 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1832 * @param pvUser User argument for the progress callback.
1833 */
1834VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
1835 const char *pszBackend, const char *pszFilename,
1836 bool fMoveByRename, uint64_t cbSize,
1837 PFNVMPROGRESS pfnProgress, void *pvUser)
1838{
1839 int rc, rc2 = VINF_SUCCESS;
1840 void *pvBuf = NULL;
1841 PVDIMAGE pImageTo = NULL;
1842
1843 LogFlowFunc(("pDiskFrom=%#p nImage=%u pDiskTo=%#p pszBackend=\"%s\" pszFilename=\"%s\" fMoveByRename=%d cbSize=%llu pfnProgress=%#p pvUser=%#p\n",
1844 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, pfnProgress, pvUser));
1845
1846 do {
1847 /* Check arguments. */
1848 AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
1849 rc = VERR_INVALID_PARAMETER);
1850 AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
1851 ("u32Signature=%08x\n", pDiskFrom->u32Signature));
1852
1853 PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
1854 AssertPtrBreakStmt(pImageFrom, rc = VERR_VDI_IMAGE_NOT_FOUND);
1855 AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
1856 rc = VERR_INVALID_PARAMETER);
1857 AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
1858 ("u32Signature=%08x\n", pDiskTo->u32Signature));
1859
1860 /* If the containers are equal and the backend is the same, rename the image. */
1861 if ( (pDiskFrom == pDiskTo)
1862 && (!strcmp(pszBackend, pImageFrom->Backend->pszBackendName)))
1863 {
1864 /* Rename the image. */
1865 rc = pImageFrom->Backend->pfnRename(pImageFrom->pvBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
1866 break;
1867 }
1868
1869 /* If the fMoveByRename flag is set and the backend is the same, rename the image. */
1870 if ( (fMoveByRename == true)
1871 && (!strcmp(pszBackend, pImageFrom->Backend->pszBackendName)))
1872 {
1873 /* Close the source image. */
1874 rc = pImageFrom->Backend->pfnClose(pImageFrom->pvBackendData, false);
1875 if (VBOX_FAILURE(rc))
1876 break;
1877
1878 /* Open the source image in the destination container. */
1879 rc = VDOpen(pDiskTo, pImageFrom->Backend->pszBackendName, pImageFrom->pszFilename, pImageFrom->uOpenFlags);
1880 if (VBOX_FAILURE(rc))
1881 goto movefail;
1882
1883 pImageTo = pDiskTo->pLast;
1884
1885 /* Rename the image. */
1886 rc = pImageTo->Backend->pfnRename(pImageTo->pvBackendData, pszFilename ? pszFilename : pImageTo->pszFilename);
1887 if (VBOX_FAILURE(rc))
1888 goto movefail;
1889
1890 /* Cleanup the leftovers. */
1891 vdRemoveImageFromList(pDiskFrom, pImageFrom);
1892 pImageFrom->pvBackendData = NULL;
1893
1894 if (pImageFrom->hPlugin != NIL_RTLDRMOD)
1895 RTLdrClose(pImageFrom->hPlugin);
1896
1897 if (pImageFrom->pszFilename)
1898 RTStrFree(pImageFrom->pszFilename);
1899
1900 RTMemFree(pImageFrom);
1901
1902 break;
1903movefail:
1904 /* In case of failure, re-open the source image in the source container. */
1905 rc2 = VDOpen(pDiskFrom, pImageFrom->Backend->pszBackendName, pImageFrom->pszFilename, pImageFrom->uOpenFlags);
1906 if (VBOX_FAILURE(rc2))
1907 /** @todo Uncertain what to do on error. If this happens pImageFrom and pImageTo are both closed. */
1908 rc = rc2;
1909 break;
1910 }
1911
1912 /* If fMoveByRename is set pszFilename is allowed to be NULL, so do the parameter check here. */
1913 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1914 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1915 rc = VERR_INVALID_PARAMETER);
1916
1917 /* Collect properties of source image. */
1918 VDIMAGETYPE enmTypeFrom = pImageFrom->enmImageType;
1919
1920 uint64_t cbSizeFrom;
1921 cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pvBackendData);
1922 if (cbSizeFrom == 0)
1923 {
1924 rc = VERR_VDI_VALUE_NOT_FOUND;
1925 break;
1926 }
1927
1928 if (cbSize == 0)
1929 cbSize = cbSizeFrom;
1930
1931 unsigned uImageFlagsFrom;
1932 uImageFlagsFrom = pImageFrom->Backend->pfnGetImageFlags(pImageFrom->pvBackendData);
1933
1934 /** @todo Get this from the source image. */
1935 PDMMEDIAGEOMETRY PCHSGeometryFrom = {0, 0, 0};
1936 PDMMEDIAGEOMETRY LCHSGeometryFrom = {0, 0, 0};
1937
1938 unsigned uOpenFlagsFrom;
1939 uOpenFlagsFrom = pImageFrom->Backend->pfnGetOpenFlags(pImageFrom->pvBackendData);
1940
1941 /* Create destination image with the properties of the source image. */
1942 /** @todo Copy the comment. */
1943 /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
1944 * calls to the backend. Unifies the code and reduces the API
1945 * dependencies. */
1946 if (enmTypeFrom == VD_IMAGE_TYPE_DIFF)
1947 {
1948 rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename, uImageFlagsFrom,
1949 "", uOpenFlagsFrom, NULL, NULL);
1950 } else {
1951 rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, enmTypeFrom,
1952 cbSize, uImageFlagsFrom, "",
1953 &PCHSGeometryFrom, &LCHSGeometryFrom,
1954 uOpenFlagsFrom, NULL, NULL);
1955 }
1956 if (VBOX_FAILURE(rc))
1957 break;
1958
1959 pImageTo = pDiskTo->pLast;
1960 AssertPtrBreakStmt(pImageTo, rc = VERR_VDI_IMAGE_NOT_FOUND);
1961
1962 /* Allocate tmp buffer. */
1963 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
1964 if (!pvBuf)
1965 {
1966 rc = VERR_NO_MEMORY;
1967 break;
1968 }
1969
1970 /* Copy the data. */
1971 uint64_t uOffset = 0;
1972 uint64_t cbRemaining = cbSize;
1973
1974 do
1975 {
1976 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1977
1978 rc = vdReadHelper(pDiskFrom, pImageFrom, uOffset, pvBuf,
1979 cbThisRead);
1980 if (VBOX_FAILURE(rc))
1981 break;
1982
1983 rc = vdWriteHelper(pDiskTo, pImageTo, uOffset, pvBuf,
1984 cbThisRead);
1985 if (VBOX_FAILURE(rc))
1986 break;
1987
1988 uOffset += cbThisRead;
1989 cbRemaining -= cbThisRead;
1990 if (pfnProgress)
1991 {
1992 rc = pfnProgress(NULL /* WARNING! pVM=NULL */,
1993 ((cbSize - cbRemaining) * 100) / cbSize,
1994 pvUser);
1995 if (VBOX_FAILURE(rc))
1996 break;
1997 }
1998 } while (uOffset < cbSize);
1999
2000 /* If fMoveByRename is set but the backend is different, close and delete pImageFrom. */
2001 if ( (fMoveByRename == true)
2002 && (strcmp(pszBackend, pImageFrom->Backend->pszBackendName)))
2003 {
2004 vdRemoveImageFromList(pDiskFrom, pImageFrom);
2005
2006 /* Close and delete image. */
2007 rc2 = pImageFrom->Backend->pfnClose(pImageFrom->pvBackendData, true);
2008 AssertRC(rc2);
2009 pImageFrom->pvBackendData = NULL;
2010
2011 /* Free remaining resources. */
2012 if (pImageFrom->hPlugin != NIL_RTLDRMOD)
2013 RTLdrClose(pImageFrom->hPlugin);
2014
2015 if (pImageFrom->pszFilename)
2016 RTStrFree(pImageFrom->pszFilename);
2017
2018 RTMemFree(pImageFrom);
2019 }
2020 } while (0);
2021
2022 if (VBOX_FAILURE(rc) && pImageTo)
2023 {
2024 /* Error detected, but new image created. Remove image from list. */
2025 vdRemoveImageFromList(pDiskTo, pImageTo);
2026
2027 /* Close and delete image. */
2028 rc2 = pImageTo->Backend->pfnClose(pImageTo->pvBackendData, true);
2029 AssertRC(rc2);
2030 pImageTo->pvBackendData = NULL;
2031
2032 /* Free remaining resources. */
2033 if (pImageTo->hPlugin != NIL_RTLDRMOD)
2034 RTLdrClose(pImageTo->hPlugin);
2035
2036 if (pImageTo->pszFilename)
2037 RTStrFree(pImageTo->pszFilename);
2038
2039 RTMemFree(pImageTo);
2040 }
2041
2042 if (pvBuf)
2043 RTMemTmpFree(pvBuf);
2044
2045 if (VBOX_SUCCESS(rc) && pfnProgress)
2046 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
2047
2048 LogFlowFunc(("returns %Vrc\n", rc));
2049 return rc;
2050}
2051
2052/**
2053 * Closes the last opened image file in HDD container.
2054 * If previous image file was opened in read-only mode (that is normal) and closing image
2055 * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
2056 * will be reopened in read/write mode.
2057 *
2058 * @returns VBox status code.
2059 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2060 * @param pDisk Pointer to HDD container.
2061 * @param fDelete If true, delete the image from the host disk.
2062 */
2063VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
2064{
2065 int rc = VINF_SUCCESS;;
2066
2067 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
2068 do
2069 {
2070 /* sanity check */
2071 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2072 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2073
2074 PVDIMAGE pImage = pDisk->pLast;
2075 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2076 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2077 /* Remove image from list of opened images. */
2078 vdRemoveImageFromList(pDisk, pImage);
2079 /* Close (and optionally delete) image. */
2080 rc = pImage->Backend->pfnClose(pImage->pvBackendData, fDelete);
2081 /* Free remaining resources related to the image. */
2082 if (pImage->hPlugin != NIL_RTLDRMOD)
2083 {
2084 RTLdrClose(pImage->hPlugin);
2085 pImage->hPlugin = NIL_RTLDRMOD;
2086 }
2087 RTStrFree(pImage->pszFilename);
2088 RTMemFree(pImage);
2089
2090 pImage = pDisk->pLast;
2091 if (!pImage)
2092 break;
2093
2094 /* If disk was previously in read/write mode, make sure it will stay
2095 * like this (if possible) after closing this image. Set the open flags
2096 * accordingly. */
2097 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
2098 {
2099 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2100 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
2101 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData, uOpenFlags);
2102 }
2103
2104 int rc2;
2105
2106 /* Cache disk information. */
2107 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
2108
2109 /* Cache PCHS geometry. */
2110 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2111 &pDisk->PCHSGeometry);
2112 if (VBOX_FAILURE(rc2))
2113 {
2114 pDisk->PCHSGeometry.cCylinders = 0;
2115 pDisk->PCHSGeometry.cHeads = 0;
2116 pDisk->PCHSGeometry.cSectors = 0;
2117 }
2118 else
2119 {
2120 /* Make sure the PCHS geometry is properly clipped. */
2121 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
2122 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
2123 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
2124 }
2125
2126 /* Cache LCHS geometry. */
2127 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2128 &pDisk->LCHSGeometry);
2129 if (VBOX_FAILURE(rc2))
2130 {
2131 pDisk->LCHSGeometry.cCylinders = 0;
2132 pDisk->LCHSGeometry.cHeads = 0;
2133 pDisk->LCHSGeometry.cSectors = 0;
2134 }
2135 else
2136 {
2137 /* Make sure the LCHS geometry is properly clipped. */
2138 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
2139 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
2140 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
2141 }
2142 } while (0);
2143
2144 LogFlowFunc(("returns %Vrc\n", rc));
2145 return rc;
2146}
2147
2148/**
2149 * Closes all opened image files in HDD container.
2150 *
2151 * @returns VBox status code.
2152 * @param pDisk Pointer to HDD container.
2153 */
2154VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
2155{
2156 int rc = VINF_SUCCESS;
2157
2158 LogFlowFunc(("pDisk=%#p\n", pDisk));
2159 do
2160 {
2161 /* sanity check */
2162 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2163 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2164
2165 PVDIMAGE pImage = pDisk->pLast;
2166 while (VALID_PTR(pImage))
2167 {
2168 PVDIMAGE pPrev = pImage->pPrev;
2169 /* Remove image from list of opened images. */
2170 vdRemoveImageFromList(pDisk, pImage);
2171 /* Close image. */
2172 int rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, false);
2173 if (VBOX_FAILURE(rc2) && VBOX_SUCCESS(rc))
2174 rc = rc2;
2175 /* Free remaining resources related to the image. */
2176 if (pImage->hPlugin != NIL_RTLDRMOD)
2177 {
2178 RTLdrClose(pImage->hPlugin);
2179 pImage->hPlugin = NIL_RTLDRMOD;
2180 }
2181 RTStrFree(pImage->pszFilename);
2182 RTMemFree(pImage);
2183 pImage = pPrev;
2184 }
2185 Assert(!VALID_PTR(pDisk->pLast));
2186 } while (0);
2187
2188 LogFlowFunc(("returns %Vrc\n", rc));
2189 return rc;
2190}
2191
2192/**
2193 * Read data from virtual HDD.
2194 *
2195 * @returns VBox status code.
2196 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2197 * @param pDisk Pointer to HDD container.
2198 * @param uOffset Offset of first reading byte from start of disk.
2199 * @param pvBuf Pointer to buffer for reading data.
2200 * @param cbRead Number of bytes to read.
2201 */
2202VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
2203 size_t cbRead)
2204{
2205 int rc;
2206
2207 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
2208 pDisk, uOffset, pvBuf, cbRead));
2209 do
2210 {
2211 /* sanity check */
2212 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2213 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2214
2215 /* Check arguments. */
2216 AssertMsgBreakStmt(VALID_PTR(pvBuf),
2217 ("pvBuf=%#p\n", pvBuf),
2218 rc = VERR_INVALID_PARAMETER);
2219 AssertMsgBreakStmt(cbRead,
2220 ("cbRead=%zu\n", cbRead),
2221 rc = VERR_INVALID_PARAMETER);
2222 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
2223 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
2224 uOffset, cbRead, pDisk->cbSize),
2225 rc = VERR_INVALID_PARAMETER);
2226
2227 PVDIMAGE pImage = pDisk->pLast;
2228 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2229
2230 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead);
2231 } while (0);
2232
2233 LogFlowFunc(("returns %Vrc\n", rc));
2234 return rc;
2235}
2236
2237/**
2238 * Write data to virtual HDD.
2239 *
2240 * @returns VBox status code.
2241 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2242 * @param pDisk Pointer to HDD container.
2243 * @param uOffset Offset of the first byte being
2244 * written from start of disk.
2245 * @param pvBuf Pointer to buffer for writing data.
2246 * @param cbWrite Number of bytes to write.
2247 */
2248VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
2249 size_t cbWrite)
2250{
2251 int rc = VINF_SUCCESS;
2252
2253 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
2254 pDisk, uOffset, pvBuf, cbWrite));
2255 do
2256 {
2257 /* sanity check */
2258 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2259 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2260
2261 /* Check arguments. */
2262 AssertMsgBreakStmt(VALID_PTR(pvBuf),
2263 ("pvBuf=%#p\n", pvBuf),
2264 rc = VERR_INVALID_PARAMETER);
2265 AssertMsgBreakStmt(cbWrite,
2266 ("cbWrite=%zu\n", cbWrite),
2267 rc = VERR_INVALID_PARAMETER);
2268 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
2269 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
2270 uOffset, cbWrite, pDisk->cbSize),
2271 rc = VERR_INVALID_PARAMETER);
2272
2273 PVDIMAGE pImage = pDisk->pLast;
2274 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2275
2276 vdSetModifiedFlag(pDisk);
2277 rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite);
2278 } while (0);
2279
2280 LogFlowFunc(("returns %Vrc\n", rc));
2281 return rc;
2282}
2283
2284/**
2285 * Make sure the on disk representation of a virtual HDD is up to date.
2286 *
2287 * @returns VBox status code.
2288 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2289 * @param pDisk Pointer to HDD container.
2290 */
2291VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
2292{
2293 int rc = VINF_SUCCESS;
2294
2295 LogFlowFunc(("pDisk=%#p\n", pDisk));
2296 do
2297 {
2298 /* sanity check */
2299 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2300 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2301
2302 PVDIMAGE pImage = pDisk->pLast;
2303 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2304
2305 vdResetModifiedFlag(pDisk);
2306 rc = pImage->Backend->pfnFlush(pImage->pvBackendData);
2307 } while (0);
2308
2309 LogFlowFunc(("returns %Vrc\n", rc));
2310 return rc;
2311}
2312
2313/**
2314 * Get number of opened images in HDD container.
2315 *
2316 * @returns Number of opened images for HDD container. 0 if no images have been opened.
2317 * @param pDisk Pointer to HDD container.
2318 */
2319VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
2320{
2321 unsigned cImages;
2322
2323 LogFlowFunc(("pDisk=%#p\n", pDisk));
2324 do
2325 {
2326 /* sanity check */
2327 AssertPtrBreakStmt(pDisk, cImages = 0);
2328 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2329
2330 cImages = pDisk->cImages;
2331 } while (0);
2332
2333 LogFlowFunc(("returns %u\n", cImages));
2334 return cImages;
2335}
2336
2337/**
2338 * Get read/write mode of HDD container.
2339 *
2340 * @returns Virtual disk ReadOnly status.
2341 * @returns true if no image is opened in HDD container.
2342 * @param pDisk Pointer to HDD container.
2343 */
2344VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
2345{
2346 bool fReadOnly;
2347
2348 LogFlowFunc(("pDisk=%#p\n", pDisk));
2349 do
2350 {
2351 /* sanity check */
2352 AssertPtrBreakStmt(pDisk, fReadOnly = false);
2353 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2354
2355 PVDIMAGE pImage = pDisk->pLast;
2356 AssertPtrBreakStmt(pImage, fReadOnly = true);
2357
2358 unsigned uOpenFlags;
2359 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
2360 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
2361 } while (0);
2362
2363 LogFlowFunc(("returns %d\n", fReadOnly));
2364 return fReadOnly;
2365}
2366
2367/**
2368 * Get total capacity of an image in HDD container.
2369 *
2370 * @returns Virtual disk size in bytes.
2371 * @returns 0 if no image with specified number was not opened.
2372 * @param pDisk Pointer to HDD container.
2373 * @param nImage Image number, counds from 0. 0 is always base image of container.
2374 */
2375VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
2376{
2377 uint64_t cbSize;
2378
2379 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
2380 do
2381 {
2382 /* sanity check */
2383 AssertPtrBreakStmt(pDisk, cbSize = 0);
2384 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2385
2386 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2387 AssertPtrBreakStmt(pImage, cbSize = 0);
2388 cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
2389 } while (0);
2390
2391 LogFlowFunc(("returns %llu\n", cbSize));
2392 return cbSize;
2393}
2394
2395/**
2396 * Get total file size of an image in HDD container.
2397 *
2398 * @returns Virtual disk size in bytes.
2399 * @returns 0 if no image is opened in HDD container.
2400 * @param pDisk Pointer to HDD container.
2401 * @param nImage Image number, counts from 0. 0 is always base image of container.
2402 */
2403VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
2404{
2405 uint64_t cbSize;
2406
2407 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
2408 do
2409 {
2410 /* sanity check */
2411 AssertPtrBreakStmt(pDisk, cbSize = 0);
2412 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2413
2414 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2415 AssertPtrBreakStmt(pImage, cbSize = 0);
2416 cbSize = pImage->Backend->pfnGetFileSize(pImage->pvBackendData);
2417 } while (0);
2418
2419 LogFlowFunc(("returns %llu\n", cbSize));
2420 return cbSize;
2421}
2422
2423/**
2424 * Get virtual disk PCHS geometry stored in HDD container.
2425 *
2426 * @returns VBox status code.
2427 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2428 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2429 * @param pDisk Pointer to HDD container.
2430 * @param nImage Image number, counts from 0. 0 is always base image of container.
2431 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
2432 */
2433VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2434 PPDMMEDIAGEOMETRY pPCHSGeometry)
2435{
2436 int rc = VINF_SUCCESS;
2437
2438 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
2439 pDisk, nImage, pPCHSGeometry));
2440 do
2441 {
2442 /* sanity check */
2443 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2444 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2445
2446 /* Check arguments. */
2447 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
2448 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
2449 rc = VERR_INVALID_PARAMETER);
2450
2451 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2452 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2453
2454 if (pImage == pDisk->pLast)
2455 {
2456 /* Use cached information if possible. */
2457 if (pDisk->PCHSGeometry.cCylinders != 0)
2458 *pPCHSGeometry = pDisk->PCHSGeometry;
2459 else
2460 rc = VERR_VDI_GEOMETRY_NOT_SET;
2461 }
2462 else
2463 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2464 pPCHSGeometry);
2465 } while (0);
2466
2467 LogFlowFunc(("%s: %Vrc (PCHS=%u/%u/%u)\n", rc,
2468 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
2469 pDisk->PCHSGeometry.cSectors));
2470 return rc;
2471}
2472
2473/**
2474 * Store virtual disk PCHS geometry in HDD container.
2475 *
2476 * Note that in case of unrecoverable error all images in HDD container will be closed.
2477 *
2478 * @returns VBox status code.
2479 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2480 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2481 * @param pDisk Pointer to HDD container.
2482 * @param nImage Image number, counts from 0. 0 is always base image of container.
2483 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
2484 */
2485VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2486 PCPDMMEDIAGEOMETRY pPCHSGeometry)
2487{
2488 int rc = VINF_SUCCESS;
2489
2490 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
2491 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
2492 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
2493 do
2494 {
2495 /* sanity check */
2496 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2497 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2498
2499 /* Check arguments. */
2500 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
2501 && pPCHSGeometry->cCylinders <= 16383
2502 && pPCHSGeometry->cHeads <= 16
2503 && pPCHSGeometry->cSectors <= 63,
2504 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
2505 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
2506 pPCHSGeometry->cSectors),
2507 rc = VERR_INVALID_PARAMETER);
2508
2509 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2510 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2511
2512 if (pImage == pDisk->pLast)
2513 {
2514 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
2515 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
2516 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
2517 {
2518 /* Only update geometry if it is changed. Avoids similar checks
2519 * in every backend. Most of the time the new geometry is set
2520 * to the previous values, so no need to go through the hassle
2521 * of updating an image which could be opened in read-only mode
2522 * right now. */
2523 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2524 pPCHSGeometry);
2525
2526 /* Cache new geometry values in any case. */
2527 int rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2528 &pDisk->PCHSGeometry);
2529 if (VBOX_FAILURE(rc2))
2530 {
2531 pDisk->PCHSGeometry.cCylinders = 0;
2532 pDisk->PCHSGeometry.cHeads = 0;
2533 pDisk->PCHSGeometry.cSectors = 0;
2534 }
2535 else
2536 {
2537 /* Make sure the CHS geometry is properly clipped. */
2538 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 1024);
2539 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
2540 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
2541 }
2542 }
2543 }
2544 else
2545 {
2546 PDMMEDIAGEOMETRY PCHS;
2547 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2548 &PCHS);
2549 if ( VBOX_FAILURE(rc)
2550 || pPCHSGeometry->cCylinders != PCHS.cCylinders
2551 || pPCHSGeometry->cHeads != PCHS.cHeads
2552 || pPCHSGeometry->cSectors != PCHS.cSectors)
2553 {
2554 /* Only update geometry if it is changed. Avoids similar checks
2555 * in every backend. Most of the time the new geometry is set
2556 * to the previous values, so no need to go through the hassle
2557 * of updating an image which could be opened in read-only mode
2558 * right now. */
2559 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2560 pPCHSGeometry);
2561 }
2562 }
2563 } while (0);
2564
2565 LogFlowFunc(("returns %Vrc\n", rc));
2566 return rc;
2567}
2568
2569/**
2570 * Get virtual disk LCHS geometry stored in HDD container.
2571 *
2572 * @returns VBox status code.
2573 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2574 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2575 * @param pDisk Pointer to HDD container.
2576 * @param nImage Image number, counts from 0. 0 is always base image of container.
2577 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
2578 */
2579VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2580 PPDMMEDIAGEOMETRY pLCHSGeometry)
2581{
2582 int rc = VINF_SUCCESS;
2583
2584 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
2585 pDisk, nImage, pLCHSGeometry));
2586 do
2587 {
2588 /* sanity check */
2589 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2590 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2591
2592 /* Check arguments. */
2593 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
2594 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
2595 rc = VERR_INVALID_PARAMETER);
2596
2597 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2598 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2599
2600 if (pImage == pDisk->pLast)
2601 {
2602 /* Use cached information if possible. */
2603 if (pDisk->LCHSGeometry.cCylinders != 0)
2604 *pLCHSGeometry = pDisk->LCHSGeometry;
2605 else
2606 rc = VERR_VDI_GEOMETRY_NOT_SET;
2607 }
2608 else
2609 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2610 pLCHSGeometry);
2611 } while (0);
2612
2613 LogFlowFunc(("%s: %Vrc (LCHS=%u/%u/%u)\n", rc,
2614 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
2615 pDisk->LCHSGeometry.cSectors));
2616 return rc;
2617}
2618
2619/**
2620 * Store virtual disk LCHS geometry in HDD container.
2621 *
2622 * Note that in case of unrecoverable error all images in HDD container will be closed.
2623 *
2624 * @returns VBox status code.
2625 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2626 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2627 * @param pDisk Pointer to HDD container.
2628 * @param nImage Image number, counts from 0. 0 is always base image of container.
2629 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
2630 */
2631VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2632 PCPDMMEDIAGEOMETRY pLCHSGeometry)
2633{
2634 int rc = VINF_SUCCESS;
2635
2636 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
2637 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
2638 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2639 do
2640 {
2641 /* sanity check */
2642 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2643 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2644
2645 /* Check arguments. */
2646 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
2647 && pLCHSGeometry->cCylinders <= 1024
2648 && pLCHSGeometry->cHeads <= 255
2649 && pLCHSGeometry->cSectors <= 63,
2650 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
2651 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
2652 pLCHSGeometry->cSectors),
2653 rc = VERR_INVALID_PARAMETER);
2654
2655 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2656 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2657
2658 if (pImage == pDisk->pLast)
2659 {
2660 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
2661 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
2662 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
2663 {
2664 /* Only update geometry if it is changed. Avoids similar checks
2665 * in every backend. Most of the time the new geometry is set
2666 * to the previous values, so no need to go through the hassle
2667 * of updating an image which could be opened in read-only mode
2668 * right now. */
2669 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2670 pLCHSGeometry);
2671
2672 /* Cache new geometry values in any case. */
2673 int rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2674 &pDisk->LCHSGeometry);
2675 if (VBOX_FAILURE(rc2))
2676 {
2677 pDisk->LCHSGeometry.cCylinders = 0;
2678 pDisk->LCHSGeometry.cHeads = 0;
2679 pDisk->LCHSGeometry.cSectors = 0;
2680 }
2681 else
2682 {
2683 /* Make sure the CHS geometry is properly clipped. */
2684 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
2685 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
2686 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
2687 }
2688 }
2689 }
2690 else
2691 {
2692 PDMMEDIAGEOMETRY LCHS;
2693 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2694 &LCHS);
2695 if ( VBOX_FAILURE(rc)
2696 || pLCHSGeometry->cCylinders != LCHS.cCylinders
2697 || pLCHSGeometry->cHeads != LCHS.cHeads
2698 || pLCHSGeometry->cSectors != LCHS.cSectors)
2699 {
2700 /* Only update geometry if it is changed. Avoids similar checks
2701 * in every backend. Most of the time the new geometry is set
2702 * to the previous values, so no need to go through the hassle
2703 * of updating an image which could be opened in read-only mode
2704 * right now. */
2705 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2706 pLCHSGeometry);
2707 }
2708 }
2709 } while (0);
2710
2711 LogFlowFunc(("returns %Vrc\n", rc));
2712 return rc;
2713}
2714
2715/**
2716 * Get version of image in HDD container.
2717 *
2718 * @returns VBox status code.
2719 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2720 * @param pDisk Pointer to HDD container.
2721 * @param nImage Image number, counts from 0. 0 is always base image of container.
2722 * @param puVersion Where to store the image version.
2723 */
2724VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
2725 unsigned *puVersion)
2726{
2727 int rc = VINF_SUCCESS;
2728
2729 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
2730 pDisk, nImage, puVersion));
2731 do
2732 {
2733 /* sanity check */
2734 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2735 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2736
2737 /* Check arguments. */
2738 AssertMsgBreakStmt(VALID_PTR(puVersion),
2739 ("puVersion=%#p\n", puVersion),
2740 rc = VERR_INVALID_PARAMETER);
2741
2742 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2743 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2744
2745 *puVersion = pImage->Backend->pfnGetVersion(pImage->pvBackendData);
2746 } while (0);
2747
2748 LogFlowFunc(("returns %Vrc uVersion=%#x\n", rc, *puVersion));
2749 return rc;
2750}
2751
2752/**
2753 * Get type of image in HDD container.
2754 *
2755 * @returns VBox status code.
2756 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2757 * @param pDisk Pointer to HDD container.
2758 * @param nImage Image number, counts from 0. 0 is always base image of container.
2759 * @param penmType Where to store the image type.
2760 */
2761VBOXDDU_DECL(int) VDGetImageType(PVBOXHDD pDisk, unsigned nImage,
2762 PVDIMAGETYPE penmType)
2763{
2764 int rc = VINF_SUCCESS;
2765
2766 LogFlowFunc(("pDisk=%#p nImage=%u penmType=%#p\n",
2767 pDisk, nImage, penmType));
2768 do
2769 {
2770 /* sanity check */
2771 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2772 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2773
2774 /* Check arguments. */
2775 AssertMsgBreakStmt(VALID_PTR(penmType),
2776 ("penmType=%#p\n", penmType),
2777 rc = VERR_INVALID_PARAMETER);
2778
2779 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2780 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2781
2782 if ( pImage->enmImageType >= VD_IMAGE_TYPE_FIRST
2783 && pImage->enmImageType <= VD_IMAGE_TYPE_DIFF)
2784 {
2785 *penmType = pImage->enmImageType;
2786 rc = VINF_SUCCESS;
2787 }
2788 else
2789 rc = VERR_VDI_INVALID_TYPE;
2790 } while (0);
2791
2792 LogFlowFunc(("returns %Vrc uenmType=%u\n", rc, *penmType));
2793 return rc;
2794}
2795
2796/**
2797 * Get flags of image in HDD container.
2798 *
2799 * @returns VBox status code.
2800 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2801 * @param pDisk Pointer to HDD container.
2802 * @param nImage Image number, counts from 0. 0 is always base image of container.
2803 * @param puImageFlags Where to store the image flags.
2804 */
2805VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
2806 unsigned *puImageFlags)
2807{
2808 int rc = VINF_SUCCESS;
2809
2810 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
2811 pDisk, nImage, puImageFlags));
2812 do
2813 {
2814 /* sanity check */
2815 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2816 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2817
2818 /* Check arguments. */
2819 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
2820 ("puImageFlags=%#p\n", puImageFlags),
2821 rc = VERR_INVALID_PARAMETER);
2822
2823 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2824 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2825
2826 *puImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pvBackendData);
2827 } while (0);
2828
2829 LogFlowFunc(("returns %Vrc uImageFlags=%#x\n", rc, *puImageFlags));
2830 return rc;
2831}
2832
2833/**
2834 * Get open flags of image in HDD container.
2835 *
2836 * @returns VBox status code.
2837 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2838 * @param pDisk Pointer to HDD container.
2839 * @param nImage Image number, counts from 0. 0 is always base image of container.
2840 * @param puOpenFlags Where to store the image open flags.
2841 */
2842VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2843 unsigned *puOpenFlags)
2844{
2845 int rc = VINF_SUCCESS;
2846
2847 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
2848 pDisk, nImage, puOpenFlags));
2849 do
2850 {
2851 /* sanity check */
2852 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2853 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2854
2855 /* Check arguments. */
2856 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
2857 ("puOpenFlags=%#p\n", puOpenFlags),
2858 rc = VERR_INVALID_PARAMETER);
2859
2860 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2861 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2862
2863 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2864 } while (0);
2865
2866 LogFlowFunc(("returns %Vrc uOpenFlags=%#x\n", rc, *puOpenFlags));
2867 return rc;
2868}
2869
2870/**
2871 * Set open flags of image in HDD container.
2872 * This operation may cause file locking changes and/or files being reopened.
2873 * Note that in case of unrecoverable error all images in HDD container will be closed.
2874 *
2875 * @returns VBox status code.
2876 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2877 * @param pDisk Pointer to HDD container.
2878 * @param nImage Image number, counts from 0. 0 is always base image of container.
2879 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
2880 */
2881VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2882 unsigned uOpenFlags)
2883{
2884 int rc;
2885
2886 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
2887 do
2888 {
2889 /* sanity check */
2890 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2891 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2892
2893 /* Check arguments. */
2894 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
2895 ("uOpenFlags=%#x\n", uOpenFlags),
2896 rc = VERR_INVALID_PARAMETER);
2897
2898 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2899 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2900
2901 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData,
2902 uOpenFlags);
2903 } while (0);
2904
2905 LogFlowFunc(("returns %Vrc\n", rc));
2906 return rc;
2907}
2908
2909/**
2910 * Get base filename of image in HDD container. Some image formats use
2911 * other filenames as well, so don't use this for anything but informational
2912 * purposes.
2913 *
2914 * @returns VBox status code.
2915 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2916 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
2917 * @param pDisk Pointer to HDD container.
2918 * @param nImage Image number, counts from 0. 0 is always base image of container.
2919 * @param pszFilename Where to store the image file name.
2920 * @param cbFilename Size of buffer pszFilename points to.
2921 */
2922VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
2923 char *pszFilename, unsigned cbFilename)
2924{
2925 int rc;
2926
2927 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
2928 pDisk, nImage, pszFilename, cbFilename));
2929 do
2930 {
2931 /* sanity check */
2932 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2933 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2934
2935 /* Check arguments. */
2936 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
2937 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
2938 rc = VERR_INVALID_PARAMETER);
2939 AssertMsgBreakStmt(cbFilename,
2940 ("cbFilename=%u\n", cbFilename),
2941 rc = VERR_INVALID_PARAMETER);
2942
2943 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2944 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2945
2946 size_t cb = strlen(pImage->pszFilename);
2947 if (cb <= cbFilename)
2948 {
2949 strcpy(pszFilename, pImage->pszFilename);
2950 rc = VINF_SUCCESS;
2951 }
2952 else
2953 {
2954 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
2955 pszFilename[cbFilename - 1] = '\0';
2956 rc = VERR_BUFFER_OVERFLOW;
2957 }
2958 } while (0);
2959
2960 LogFlowFunc(("returns %Vrc, pszFilename=\"%s\"\n", rc, pszFilename));
2961 return rc;
2962}
2963
2964/**
2965 * Get the comment line of image in HDD container.
2966 *
2967 * @returns VBox status code.
2968 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2969 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
2970 * @param pDisk Pointer to HDD container.
2971 * @param nImage Image number, counts from 0. 0 is always base image of container.
2972 * @param pszComment Where to store the comment string of image. NULL is ok.
2973 * @param cbComment The size of pszComment buffer. 0 is ok.
2974 */
2975VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
2976 char *pszComment, unsigned cbComment)
2977{
2978 int rc;
2979
2980 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
2981 pDisk, nImage, pszComment, cbComment));
2982 do
2983 {
2984 /* sanity check */
2985 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2986 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2987
2988 /* Check arguments. */
2989 AssertMsgBreakStmt(VALID_PTR(pszComment),
2990 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
2991 rc = VERR_INVALID_PARAMETER);
2992 AssertMsgBreakStmt(cbComment,
2993 ("cbComment=%u\n", cbComment),
2994 rc = VERR_INVALID_PARAMETER);
2995
2996 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2997 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2998
2999 rc = pImage->Backend->pfnGetComment(pImage->pvBackendData, pszComment,
3000 cbComment);
3001 } while (0);
3002
3003 LogFlowFunc(("returns %Vrc, pszComment=\"%s\"\n", rc, pszComment));
3004 return rc;
3005}
3006
3007/**
3008 * Changes the comment line of image in HDD container.
3009 *
3010 * @returns VBox status code.
3011 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3012 * @param pDisk Pointer to HDD container.
3013 * @param nImage Image number, counts from 0. 0 is always base image of container.
3014 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
3015 */
3016VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
3017 const char *pszComment)
3018{
3019 int rc;
3020
3021 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
3022 pDisk, nImage, pszComment, pszComment));
3023 do
3024 {
3025 /* sanity check */
3026 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3027 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3028
3029 /* Check arguments. */
3030 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
3031 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
3032 rc = VERR_INVALID_PARAMETER);
3033
3034 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3035 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3036
3037 rc = pImage->Backend->pfnSetComment(pImage->pvBackendData, pszComment);
3038 } while (0);
3039
3040 LogFlowFunc(("returns %Vrc\n", rc));
3041 return rc;
3042}
3043
3044
3045/**
3046 * Get UUID of image in HDD container.
3047 *
3048 * @returns VBox status code.
3049 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3050 * @param pDisk Pointer to HDD container.
3051 * @param nImage Image number, counts from 0. 0 is always base image of container.
3052 * @param pUuid Where to store the image creation UUID.
3053 */
3054VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
3055{
3056 int rc;
3057
3058 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3059 do
3060 {
3061 /* sanity check */
3062 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3063 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3064
3065 /* Check arguments. */
3066 AssertMsgBreakStmt(VALID_PTR(pUuid),
3067 ("pUuid=%#p\n", pUuid),
3068 rc = VERR_INVALID_PARAMETER);
3069
3070 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3071 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3072
3073 rc = pImage->Backend->pfnGetUuid(pImage->pvBackendData, pUuid);
3074 } while (0);
3075
3076 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
3077 return rc;
3078}
3079
3080/**
3081 * Set the image's UUID. Should not be used by normal applications.
3082 *
3083 * @returns VBox status code.
3084 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3085 * @param pDisk Pointer to HDD container.
3086 * @param nImage Image number, counts from 0. 0 is always base image of container.
3087 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
3088 */
3089VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
3090{
3091 int rc;
3092
3093 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
3094 pDisk, nImage, pUuid, pUuid));
3095 do
3096 {
3097 /* sanity check */
3098 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3099 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3100
3101 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3102 ("pUuid=%#p\n", pUuid),
3103 rc = VERR_INVALID_PARAMETER);
3104
3105 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3106 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3107
3108 RTUUID Uuid;
3109 if (!pUuid)
3110 {
3111 RTUuidCreate(&Uuid);
3112 pUuid = &Uuid;
3113 }
3114 rc = pImage->Backend->pfnSetUuid(pImage->pvBackendData, pUuid);
3115 } while (0);
3116
3117 LogFlowFunc(("returns %Vrc\n", rc));
3118 return rc;
3119}
3120
3121/**
3122 * Get last modification UUID of image in HDD container.
3123 *
3124 * @returns VBox status code.
3125 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3126 * @param pDisk Pointer to HDD container.
3127 * @param nImage Image number, counts from 0. 0 is always base image of container.
3128 * @param pUuid Where to store the image modification UUID.
3129 */
3130VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
3131{
3132 int rc = VINF_SUCCESS;
3133
3134 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3135 do
3136 {
3137 /* sanity check */
3138 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3139 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3140
3141 /* Check arguments. */
3142 AssertMsgBreakStmt(VALID_PTR(pUuid),
3143 ("pUuid=%#p\n", pUuid),
3144 rc = VERR_INVALID_PARAMETER);
3145
3146 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3147 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3148
3149 rc = pImage->Backend->pfnGetModificationUuid(pImage->pvBackendData,
3150 pUuid);
3151 } while (0);
3152
3153 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
3154 return rc;
3155}
3156
3157/**
3158 * Set the image's last modification UUID. Should not be used by normal applications.
3159 *
3160 * @returns VBox status code.
3161 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3162 * @param pDisk Pointer to HDD container.
3163 * @param nImage Image number, counts from 0. 0 is always base image of container.
3164 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
3165 */
3166VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
3167{
3168 int rc;
3169
3170 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
3171 pDisk, nImage, pUuid, pUuid));
3172 do
3173 {
3174 /* sanity check */
3175 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3176 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3177
3178 /* Check arguments. */
3179 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3180 ("pUuid=%#p\n", pUuid),
3181 rc = VERR_INVALID_PARAMETER);
3182
3183 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3184 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3185
3186 RTUUID Uuid;
3187 if (!pUuid)
3188 {
3189 RTUuidCreate(&Uuid);
3190 pUuid = &Uuid;
3191 }
3192 rc = pImage->Backend->pfnSetModificationUuid(pImage->pvBackendData,
3193 pUuid);
3194 } while (0);
3195
3196 LogFlowFunc(("returns %Vrc\n", rc));
3197 return rc;
3198}
3199
3200/**
3201 * Get parent UUID of image in HDD container.
3202 *
3203 * @returns VBox status code.
3204 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3205 * @param pDisk Pointer to HDD container.
3206 * @param nImage Image number, counts from 0. 0 is always base image of container.
3207 * @param pUuid Where to store the parent image UUID.
3208 */
3209VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
3210 PRTUUID pUuid)
3211{
3212 int rc = VINF_SUCCESS;
3213
3214 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3215 do
3216 {
3217 /* sanity check */
3218 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3219 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3220
3221 /* Check arguments. */
3222 AssertMsgBreakStmt(VALID_PTR(pUuid),
3223 ("pUuid=%#p\n", pUuid),
3224 rc = VERR_INVALID_PARAMETER);
3225
3226 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3227 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3228
3229 rc = pImage->Backend->pfnGetParentUuid(pImage->pvBackendData, pUuid);
3230 } while (0);
3231
3232 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
3233 return rc;
3234}
3235
3236/**
3237 * Set the image's parent UUID. Should not be used by normal applications.
3238 *
3239 * @returns VBox status code.
3240 * @param pDisk Pointer to HDD container.
3241 * @param nImage Image number, counts from 0. 0 is always base image of container.
3242 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
3243 */
3244VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
3245 PCRTUUID pUuid)
3246{
3247 int rc;
3248
3249 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
3250 pDisk, nImage, pUuid, pUuid));
3251 do
3252 {
3253 /* sanity check */
3254 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3255 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3256
3257 /* Check arguments. */
3258 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3259 ("pUuid=%#p\n", pUuid),
3260 rc = VERR_INVALID_PARAMETER);
3261
3262 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3263 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3264
3265 RTUUID Uuid;
3266 if (!pUuid)
3267 {
3268 RTUuidCreate(&Uuid);
3269 pUuid = &Uuid;
3270 }
3271 rc = pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, pUuid);
3272 } while (0);
3273
3274 LogFlowFunc(("returns %Vrc\n", rc));
3275 return rc;
3276}
3277
3278
3279/**
3280 * Debug helper - dumps all opened images in HDD container into the log file.
3281 *
3282 * @param pDisk Pointer to HDD container.
3283 */
3284VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
3285{
3286 do
3287 {
3288 /* sanity check */
3289 AssertPtrBreak(pDisk);
3290 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3291
3292 RTLogPrintf("--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
3293 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
3294 {
3295 RTLogPrintf("Dumping VD image \"%s\" (Backend=%s)\n",
3296 pImage->pszFilename, pImage->Backend->pszBackendName);
3297 pImage->Backend->pfnDump(pImage->pvBackendData);
3298 }
3299 } while (0);
3300}
3301
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