VirtualBox

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

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

#3162: Another attempt to introduce compose name and location callbacks, better version of r40315.

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

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