VirtualBox

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

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

Change the meaninf of VD_OPEN_FLAGS_INFO. The previous definition caused trouble by overoptimizing. Zero UUIDs for VMDK files created by VMware is not acceptable, as it breaks Main.

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