VirtualBox

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

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

API/HardDisk, Storage/VBoxHDD, Frontend/VBoxManage: eliminated base image type, which led to much unnecessary code duplication. Was triggered by VBoxManage finally being able to create all image variants the backends can support.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 131.7 KB
Line 
1/* $Id: VBoxHDD.cpp 17970 2009-03-16 19:08:16Z vboxsync $ */
2/** @file
3 * VBoxHDD - VBox HDD Container implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#define LOG_GROUP LOG_GROUP_VD
26#include <VBox/VBoxHDD.h>
27#include <VBox/err.h>
28#include <VBox/sup.h>
29#include <VBox/log.h>
30
31#include <iprt/alloc.h>
32#include <iprt/assert.h>
33#include <iprt/uuid.h>
34#include <iprt/file.h>
35#include <iprt/string.h>
36#include <iprt/asm.h>
37#include <iprt/ldr.h>
38#include <iprt/dir.h>
39#include <iprt/path.h>
40#include <iprt/param.h>
41
42#include "VBoxHDD-Internal.h"
43
44
45#define VBOXHDDDISK_SIGNATURE 0x6f0e2a7d
46
47/** Buffer size used for merging images. */
48#define VD_MERGE_BUFFER_SIZE (16 * _1M)
49
50/**
51 * VBox HDD Container image descriptor.
52 */
53typedef struct VDIMAGE
54{
55 /** Link to parent image descriptor, if any. */
56 struct VDIMAGE *pPrev;
57 /** Link to child image descriptor, if any. */
58 struct VDIMAGE *pNext;
59 /** Container base filename. (UTF-8) */
60 char *pszFilename;
61 /** Data managed by the backend which keeps the actual info. */
62 void *pvBackendData;
63 /** Cached sanitized image flags. */
64 unsigned uImageFlags;
65 /** Image open flags (only those handled generically in this code and which
66 * the backends will never ever see). */
67 unsigned uOpenFlags;
68
69 /** Function pointers for the various backend methods. */
70 PCVBOXHDDBACKEND Backend;
71
72 /** Pointer to list of VD interfaces, per-image. */
73 PVDINTERFACE pVDIfsImage;
74} VDIMAGE, *PVDIMAGE;
75
76/**
77 * uModified bit flags.
78 */
79#define VD_IMAGE_MODIFIED_FLAG RT_BIT(0)
80#define VD_IMAGE_MODIFIED_FIRST RT_BIT(1)
81#define VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE RT_BIT(2)
82
83
84/**
85 * VBox HDD Container main structure, private part.
86 */
87struct VBOXHDD
88{
89 /** Structure signature (VBOXHDDDISK_SIGNATURE). */
90 uint32_t u32Signature;
91
92 /** Number of opened images. */
93 unsigned cImages;
94
95 /** Base image. */
96 PVDIMAGE pBase;
97
98 /** Last opened image in the chain.
99 * The same as pBase if only one image is used. */
100 PVDIMAGE pLast;
101
102 /** Flags representing the modification state. */
103 unsigned uModified;
104
105 /** Cached size of this disk. */
106 uint64_t cbSize;
107 /** Cached PCHS geometry for this disk. */
108 PDMMEDIAGEOMETRY PCHSGeometry;
109 /** Cached LCHS geometry for this disk. */
110 PDMMEDIAGEOMETRY LCHSGeometry;
111
112 /** Pointer to list of VD interfaces, per-disk. */
113 PVDINTERFACE pVDIfsDisk;
114 /** Pointer to the common interface structure for error reporting. */
115 PVDINTERFACE pInterfaceError;
116 /** Pointer to the error interface we use if available. */
117 PVDINTERFACEERROR pInterfaceErrorCallbacks;
118};
119
120
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 (!RTStrICmp(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_VD_BLOCK_FREE;
284 for (PVDIMAGE pCurrImage = pImage;
285 pCurrImage != NULL && rc == VERR_VD_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_VD_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_VD_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_VD_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_VD_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(void)
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(void)
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 (!RTStrICmp(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 /* The correct backend has been found, but there is a small
925 * incompatibility so that the file cannot be used. Stop here
926 * and signal success - the actual open will of course fail,
927 * but that will create a really sensible error message. */
928 || ( rc != VERR_VD_GEN_INVALID_HEADER
929 && rc != VERR_VD_VDI_INVALID_HEADER
930 && rc != VERR_VD_VMDK_INVALID_HEADER
931 && rc != VERR_VD_ISCSI_INVALID_HEADER
932 && rc != VERR_VD_VHD_INVALID_HEADER
933 && rc != VERR_VD_RAW_INVALID_HEADER))
934 {
935 /* Copy the name into the new string. */
936 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
937 if (!pszFormat)
938 {
939 rc = VERR_NO_MEMORY;
940 break;
941 }
942 *ppszFormat = pszFormat;
943 rc = VINF_SUCCESS;
944 break;
945 }
946 rc = VERR_NOT_SUPPORTED;
947 }
948 }
949
950 LogFlowFunc(("returns %Rrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
951 return rc;
952}
953
954/**
955 * Opens an image file.
956 *
957 * The first opened image file in HDD container must have a base image type,
958 * others (next opened images) must be a differencing or undo images.
959 * Linkage is checked for differencing image to be in consistence with the previously opened image.
960 * When another differencing image is opened and the last image was opened in read/write access
961 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
962 * other processes to use images in read-only mode too.
963 *
964 * Note that the image is opened in read-only mode if a read/write open is not possible.
965 * Use VDIsReadOnly to check open mode.
966 *
967 * @returns VBox status code.
968 * @param pDisk Pointer to HDD container.
969 * @param pszBackend Name of the image file backend to use.
970 * @param pszFilename Name of the image file to open.
971 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
972 * @param pVDIfsImage Pointer to the per-image VD interface list.
973 */
974VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
975 const char *pszFilename, unsigned uOpenFlags,
976 PVDINTERFACE pVDIfsImage)
977{
978 int rc = VINF_SUCCESS;
979 PVDIMAGE pImage = NULL;
980
981 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsImage=%#p\n",
982 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsImage));
983 do
984 {
985 /* sanity check */
986 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
987 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
988
989 /* Check arguments. */
990 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
991 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
992 rc = VERR_INVALID_PARAMETER);
993 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
994 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
995 rc = VERR_INVALID_PARAMETER);
996 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
997 ("uOpenFlags=%#x\n", uOpenFlags),
998 rc = VERR_INVALID_PARAMETER);
999
1000 /* Set up image descriptor. */
1001 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1002 if (!pImage)
1003 {
1004 rc = VERR_NO_MEMORY;
1005 break;
1006 }
1007 pImage->pszFilename = RTStrDup(pszFilename);
1008 if (!pImage->pszFilename)
1009 {
1010 rc = VERR_NO_MEMORY;
1011 break;
1012 }
1013 pImage->pVDIfsImage = pVDIfsImage;
1014
1015 rc = vdFindBackend(pszBackend, &pImage->Backend);
1016 if (RT_FAILURE(rc))
1017 break;
1018 if (!pImage->Backend)
1019 {
1020 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1021 N_("VD: unknown backend name '%s'"), pszBackend);
1022 break;
1023 }
1024
1025 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1026 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
1027 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1028 pDisk->pVDIfsDisk,
1029 pImage->pVDIfsImage,
1030 &pImage->pvBackendData);
1031 /* If the open in read-write mode failed, retry in read-only mode. */
1032 if (RT_FAILURE(rc))
1033 {
1034 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
1035 && (rc == VERR_ACCESS_DENIED
1036 || rc == VERR_PERMISSION_DENIED
1037 || rc == VERR_WRITE_PROTECT
1038 || rc == VERR_SHARING_VIOLATION
1039 || rc == VERR_FILE_LOCK_FAILED))
1040 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
1041 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
1042 | VD_OPEN_FLAGS_READONLY,
1043 pDisk->pVDIfsDisk,
1044 pImage->pVDIfsImage,
1045 &pImage->pvBackendData);
1046 if (RT_FAILURE(rc))
1047 {
1048 rc = vdError(pDisk, rc, RT_SRC_POS,
1049 N_("VD: error opening image file '%s'"), pszFilename);
1050 break;
1051 }
1052 }
1053
1054 unsigned uImageFlags;
1055 uImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pvBackendData);
1056 /* Check image type. As the image itself has only partial knowledge
1057 * whether it's a base image or not, this info is derived here. The
1058 * base image can be fixed or normal, all others must be normal or
1059 * diff images. Some image formats don't distinguish between normal
1060 * and diff images, so this must be corrected here. */
1061 if (RT_FAILURE(rc))
1062 uImageFlags = VD_IMAGE_FLAGS_NONE;
1063 if ( RT_SUCCESS(rc)
1064 && !(uOpenFlags & VD_OPEN_FLAGS_INFO))
1065 {
1066 if ( pDisk->cImages == 0
1067 && (uImageFlags & VD_IMAGE_FLAGS_DIFF))
1068 {
1069 rc = VERR_VD_INVALID_TYPE;
1070 break;
1071 }
1072 else if (pDisk->cImages != 0)
1073 {
1074 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
1075 {
1076 rc = VERR_VD_INVALID_TYPE;
1077 break;
1078 }
1079 else
1080 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
1081 }
1082 }
1083 pImage->uImageFlags = uImageFlags;
1084
1085 /* Force sane optimization settings. It's not worth avoiding writes
1086 * to fixed size images. The overhead would have almost no payback. */
1087 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
1088 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
1089
1090 /** @todo optionally check UUIDs */
1091
1092 int rc2;
1093
1094 /* Cache disk information. */
1095 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
1096
1097 /* Cache PCHS geometry. */
1098 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1099 &pDisk->PCHSGeometry);
1100 if (RT_FAILURE(rc2))
1101 {
1102 pDisk->PCHSGeometry.cCylinders = 0;
1103 pDisk->PCHSGeometry.cHeads = 0;
1104 pDisk->PCHSGeometry.cSectors = 0;
1105 }
1106 else
1107 {
1108 /* Make sure the PCHS geometry is properly clipped. */
1109 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1110 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1111 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1112 }
1113
1114 /* Cache LCHS geometry. */
1115 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1116 &pDisk->LCHSGeometry);
1117 if (RT_FAILURE(rc2))
1118 {
1119 pDisk->LCHSGeometry.cCylinders = 0;
1120 pDisk->LCHSGeometry.cHeads = 0;
1121 pDisk->LCHSGeometry.cSectors = 0;
1122 }
1123 else
1124 {
1125 /* Make sure the LCHS geometry is properly clipped. */
1126 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
1127 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1128 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1129 }
1130
1131 if (pDisk->cImages != 0)
1132 {
1133 /* Switch previous image to read-only mode. */
1134 unsigned uOpenFlagsPrevImg;
1135 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1136 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
1137 {
1138 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
1139 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
1140 }
1141 }
1142
1143 if (RT_SUCCESS(rc))
1144 {
1145 /* Image successfully opened, make it the last image. */
1146 vdAddImageToList(pDisk, pImage);
1147 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1148 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1149 }
1150 else
1151 {
1152 /* Error detected, but image opened. Close image. */
1153 int rc2;
1154 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, false);
1155 AssertRC(rc2);
1156 pImage->pvBackendData = NULL;
1157 }
1158 } while (0);
1159
1160 if (RT_FAILURE(rc))
1161 {
1162 if (pImage)
1163 {
1164 if (pImage->pszFilename)
1165 RTStrFree(pImage->pszFilename);
1166 RTMemFree(pImage);
1167 }
1168 }
1169
1170 LogFlowFunc(("returns %Rrc\n", rc));
1171 return rc;
1172}
1173
1174/**
1175 * Creates and opens a new base image file.
1176 *
1177 * @returns VBox status code.
1178 * @param pDisk Pointer to HDD container.
1179 * @param pszBackend Name of the image file backend to use.
1180 * @param pszFilename Name of the image file to create.
1181 * @param cbSize Image size in bytes.
1182 * @param uImageFlags Flags specifying special image features.
1183 * @param pszComment Pointer to image comment. NULL is ok.
1184 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
1185 * @param pLCHSGeometry Pointer to logical disk geometry <= (1024,255,63). Not NULL.
1186 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
1187 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1188 * @param pVDIfsImage Pointer to the per-image VD interface list.
1189 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
1190 */
1191VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
1192 const char *pszFilename, uint64_t cbSize,
1193 unsigned uImageFlags, const char *pszComment,
1194 PCPDMMEDIAGEOMETRY pPCHSGeometry,
1195 PCPDMMEDIAGEOMETRY pLCHSGeometry,
1196 PCRTUUID pUuid, unsigned uOpenFlags,
1197 PVDINTERFACE pVDIfsImage,
1198 PVDINTERFACE pVDIfsOperation)
1199{
1200 int rc = VINF_SUCCESS;
1201 PVDIMAGE pImage = NULL;
1202 RTUUID uuid;
1203
1204 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" PCHS=%u/%u/%u LCHS=%u/%u/%u Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
1205 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment,
1206 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1207 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
1208 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
1209 uOpenFlags, pVDIfsImage, pVDIfsOperation));
1210
1211 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
1212 VDINTERFACETYPE_PROGRESS);
1213 PVDINTERFACEPROGRESS pCbProgress = NULL;
1214 if (pIfProgress)
1215 pCbProgress = VDGetInterfaceProgress(pIfProgress);
1216
1217 do
1218 {
1219 /* sanity check */
1220 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1221 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1222
1223 /* Check arguments. */
1224 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
1225 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1226 rc = VERR_INVALID_PARAMETER);
1227 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1228 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1229 rc = VERR_INVALID_PARAMETER);
1230 AssertMsgBreakStmt(cbSize,
1231 ("cbSize=%llu\n", cbSize),
1232 rc = VERR_INVALID_PARAMETER);
1233 AssertMsgBreakStmt( ((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0)
1234 || ((uImageFlags & (VD_IMAGE_FLAGS_FIXED | VD_IMAGE_FLAGS_DIFF)) != VD_IMAGE_FLAGS_FIXED),
1235 ("uImageFlags=%#x\n", uImageFlags),
1236 rc = VERR_INVALID_PARAMETER);
1237 /* The PCHS geometry fields may be 0 to leave it for later. */
1238 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
1239 && pPCHSGeometry->cCylinders <= 16383
1240 && pPCHSGeometry->cHeads <= 16
1241 && pPCHSGeometry->cSectors <= 63,
1242 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
1243 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1244 pPCHSGeometry->cSectors),
1245 rc = VERR_INVALID_PARAMETER);
1246 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
1247 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
1248 && pLCHSGeometry->cCylinders <= 1024
1249 && pLCHSGeometry->cHeads <= 255
1250 && pLCHSGeometry->cSectors <= 63,
1251 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
1252 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
1253 pLCHSGeometry->cSectors),
1254 rc = VERR_INVALID_PARAMETER);
1255 /* The UUID may be NULL. */
1256 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
1257 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
1258 rc = VERR_INVALID_PARAMETER);
1259 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1260 ("uOpenFlags=%#x\n", uOpenFlags),
1261 rc = VERR_INVALID_PARAMETER);
1262
1263 /* Check state. */
1264 AssertMsgBreakStmt(pDisk->cImages == 0,
1265 ("Create base image cannot be done with other images open\n"),
1266 rc = VERR_VD_INVALID_STATE);
1267
1268 /* Set up image descriptor. */
1269 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1270 if (!pImage)
1271 {
1272 rc = VERR_NO_MEMORY;
1273 break;
1274 }
1275 pImage->pszFilename = RTStrDup(pszFilename);
1276 if (!pImage->pszFilename)
1277 {
1278 rc = VERR_NO_MEMORY;
1279 break;
1280 }
1281 pImage->pVDIfsImage = pVDIfsImage;
1282
1283 rc = vdFindBackend(pszBackend, &pImage->Backend);
1284 if (RT_FAILURE(rc))
1285 break;
1286 if (!pImage->Backend)
1287 {
1288 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1289 N_("VD: unknown backend name '%s'"), pszBackend);
1290 break;
1291 }
1292
1293 /* Create UUID if the caller didn't specify one. */
1294 if (!pUuid)
1295 {
1296 rc = RTUuidCreate(&uuid);
1297 if (RT_FAILURE(rc))
1298 {
1299 rc = vdError(pDisk, rc, RT_SRC_POS,
1300 N_("VD: cannot generate UUID for image '%s'"),
1301 pszFilename);
1302 break;
1303 }
1304 pUuid = &uuid;
1305 }
1306
1307 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1308 uImageFlags &= ~VD_IMAGE_FLAGS_DIFF;
1309 rc = pImage->Backend->pfnCreate(pImage->pszFilename, cbSize,
1310 uImageFlags, pszComment, pPCHSGeometry,
1311 pLCHSGeometry, pUuid,
1312 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1313 0, 99,
1314 pDisk->pVDIfsDisk,
1315 pImage->pVDIfsImage,
1316 pVDIfsOperation,
1317 &pImage->pvBackendData);
1318
1319 if (RT_SUCCESS(rc))
1320 {
1321 pImage->uImageFlags = uImageFlags;
1322
1323 /* Force sane optimization settings. It's not worth avoiding writes
1324 * to fixed size images. The overhead would have almost no payback. */
1325 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
1326 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
1327
1328 /** @todo optionally check UUIDs */
1329
1330 int rc2;
1331
1332 /* Cache disk information. */
1333 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
1334
1335 /* Cache PCHS geometry. */
1336 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1337 &pDisk->PCHSGeometry);
1338 if (RT_FAILURE(rc2))
1339 {
1340 pDisk->PCHSGeometry.cCylinders = 0;
1341 pDisk->PCHSGeometry.cHeads = 0;
1342 pDisk->PCHSGeometry.cSectors = 0;
1343 }
1344 else
1345 {
1346 /* Make sure the CHS geometry is properly clipped. */
1347 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1348 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1349 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1350 }
1351
1352 /* Cache LCHS geometry. */
1353 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1354 &pDisk->LCHSGeometry);
1355 if (RT_FAILURE(rc2))
1356 {
1357 pDisk->LCHSGeometry.cCylinders = 0;
1358 pDisk->LCHSGeometry.cHeads = 0;
1359 pDisk->LCHSGeometry.cSectors = 0;
1360 }
1361 else
1362 {
1363 /* Make sure the CHS geometry is properly clipped. */
1364 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
1365 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1366 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1367 }
1368 }
1369
1370 if (RT_SUCCESS(rc))
1371 {
1372 /* Image successfully opened, make it the last image. */
1373 vdAddImageToList(pDisk, pImage);
1374 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1375 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1376 }
1377 else
1378 {
1379 /* Error detected, but image opened. Close and delete image. */
1380 int rc2;
1381 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
1382 AssertRC(rc2);
1383 pImage->pvBackendData = NULL;
1384 }
1385 } while (0);
1386
1387 if (RT_FAILURE(rc))
1388 {
1389 if (pImage)
1390 {
1391 if (pImage->pszFilename)
1392 RTStrFree(pImage->pszFilename);
1393 RTMemFree(pImage);
1394 }
1395 }
1396
1397 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
1398 pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */, 100,
1399 pIfProgress->pvUser);
1400
1401 LogFlowFunc(("returns %Rrc\n", rc));
1402 return rc;
1403}
1404
1405/**
1406 * Creates and opens a new differencing image file in HDD container.
1407 * See comments for VDOpen function about differencing images.
1408 *
1409 * @returns VBox status code.
1410 * @param pDisk Pointer to HDD container.
1411 * @param pszBackend Name of the image file backend to use.
1412 * @param pszFilename Name of the differencing image file to create.
1413 * @param uImageFlags Flags specifying special image features.
1414 * @param pszComment Pointer to image comment. NULL is ok.
1415 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
1416 * @param pParentUuid New parent UUID of the image. If NULL, the UUID is queried automatically.
1417 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1418 * @param pVDIfsImage Pointer to the per-image VD interface list.
1419 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
1420 */
1421VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
1422 const char *pszFilename, unsigned uImageFlags,
1423 const char *pszComment, PCRTUUID pUuid,
1424 PCRTUUID pParentUuid, unsigned uOpenFlags,
1425 PVDINTERFACE pVDIfsImage,
1426 PVDINTERFACE pVDIfsOperation)
1427{
1428 int rc = VINF_SUCCESS;
1429 PVDIMAGE pImage = NULL;
1430 RTUUID uuid;
1431
1432 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid ParentUuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
1433 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, pParentUuid, uOpenFlags,
1434 pVDIfsImage, pVDIfsOperation));
1435
1436 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
1437 VDINTERFACETYPE_PROGRESS);
1438 PVDINTERFACEPROGRESS pCbProgress = NULL;
1439 if (pIfProgress)
1440 pCbProgress = VDGetInterfaceProgress(pIfProgress);
1441
1442 do
1443 {
1444 /* sanity check */
1445 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1446 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1447
1448 /* Check arguments. */
1449 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
1450 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1451 rc = VERR_INVALID_PARAMETER);
1452 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1453 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1454 rc = VERR_INVALID_PARAMETER);
1455 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
1456 ("uImageFlags=%#x\n", uImageFlags),
1457 rc = VERR_INVALID_PARAMETER);
1458 /* The UUID may be NULL. */
1459 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
1460 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
1461 rc = VERR_INVALID_PARAMETER);
1462 /* The parent UUID may be NULL. */
1463 AssertMsgBreakStmt(pParentUuid == NULL || VALID_PTR(pParentUuid),
1464 ("pParentUuid=%#p ParentUUID=%RTuuid\n", pParentUuid, pParentUuid),
1465 rc = VERR_INVALID_PARAMETER);
1466 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1467 ("uOpenFlags=%#x\n", uOpenFlags),
1468 rc = VERR_INVALID_PARAMETER);
1469
1470 /* Check state. */
1471 AssertMsgBreakStmt(pDisk->cImages != 0,
1472 ("Create diff image cannot be done without other images open\n"),
1473 rc = VERR_VD_INVALID_STATE);
1474
1475 /* Set up image descriptor. */
1476 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1477 if (!pImage)
1478 {
1479 rc = VERR_NO_MEMORY;
1480 break;
1481 }
1482 pImage->pszFilename = RTStrDup(pszFilename);
1483 if (!pImage->pszFilename)
1484 {
1485 rc = VERR_NO_MEMORY;
1486 break;
1487 }
1488
1489 rc = vdFindBackend(pszBackend, &pImage->Backend);
1490 if (RT_FAILURE(rc))
1491 break;
1492 if (!pImage->Backend)
1493 {
1494 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1495 N_("VD: unknown backend name '%s'"), pszBackend);
1496 break;
1497 }
1498
1499 /* Create UUID if the caller didn't specify one. */
1500 if (!pUuid)
1501 {
1502 rc = RTUuidCreate(&uuid);
1503 if (RT_FAILURE(rc))
1504 {
1505 rc = vdError(pDisk, rc, RT_SRC_POS,
1506 N_("VD: cannot generate UUID for image '%s'"),
1507 pszFilename);
1508 break;
1509 }
1510 pUuid = &uuid;
1511 }
1512
1513 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1514 rc = pImage->Backend->pfnCreate(pImage->pszFilename, pDisk->cbSize,
1515 uImageFlags, pszComment,
1516 &pDisk->PCHSGeometry,
1517 &pDisk->LCHSGeometry, pUuid,
1518 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1519 0, 99,
1520 pDisk->pVDIfsDisk,
1521 pImage->pVDIfsImage,
1522 pVDIfsOperation,
1523 &pImage->pvBackendData);
1524
1525 if (RT_SUCCESS(rc) && pDisk->cImages != 0)
1526 {
1527 pImage->uImageFlags |= VD_IMAGE_FLAGS_DIFF;
1528
1529 /* Switch previous image to read-only mode. */
1530 unsigned uOpenFlagsPrevImg;
1531 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1532 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
1533 {
1534 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
1535 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
1536 }
1537 }
1538
1539 if (RT_SUCCESS(rc))
1540 {
1541 RTUUID Uuid;
1542 RTTIMESPEC ts;
1543 int rc2;
1544
1545 if (pParentUuid && !RTUuidIsNull(pParentUuid))
1546 {
1547 Uuid = *pParentUuid;
1548 pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, &Uuid);
1549 }
1550 else
1551 {
1552 rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pvBackendData,
1553 &Uuid);
1554 if (RT_SUCCESS(rc2))
1555 pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, &Uuid);
1556 }
1557 rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pvBackendData,
1558 &Uuid);
1559 if (RT_SUCCESS(rc2))
1560 pImage->Backend->pfnSetParentModificationUuid(pImage->pvBackendData,
1561 &Uuid);
1562 rc2 = pDisk->pLast->Backend->pfnGetTimeStamp(pDisk->pLast->pvBackendData,
1563 &ts);
1564 if (RT_SUCCESS(rc2))
1565 pImage->Backend->pfnSetParentTimeStamp(pImage->pvBackendData, &ts);
1566
1567 rc2 = pImage->Backend->pfnSetParentFilename(pImage->pvBackendData, pDisk->pLast->pszFilename);
1568 }
1569
1570 if (RT_SUCCESS(rc))
1571 {
1572 /** @todo optionally check UUIDs */
1573 }
1574
1575 if (RT_SUCCESS(rc))
1576 {
1577 /* Image successfully opened, make it the last image. */
1578 vdAddImageToList(pDisk, pImage);
1579 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1580 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1581 }
1582 else
1583 {
1584 /* Error detected, but image opened. Close and delete image. */
1585 int rc2;
1586 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
1587 AssertRC(rc2);
1588 pImage->pvBackendData = NULL;
1589 }
1590 } while (0);
1591
1592 if (RT_FAILURE(rc))
1593 {
1594 if (pImage)
1595 {
1596 if (pImage->pszFilename)
1597 RTStrFree(pImage->pszFilename);
1598 RTMemFree(pImage);
1599 }
1600 }
1601
1602 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
1603 pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */, 100,
1604 pIfProgress->pvUser);
1605
1606 LogFlowFunc(("returns %Rrc\n", rc));
1607 return rc;
1608}
1609
1610/**
1611 * Merges two images (not necessarily with direct parent/child relationship).
1612 * As a side effect the source image and potentially the other images which
1613 * are also merged to the destination are deleted from both the disk and the
1614 * images in the HDD container.
1615 *
1616 * @returns VBox status code.
1617 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
1618 * @param pDisk Pointer to HDD container.
1619 * @param nImageFrom Name of the image file to merge from.
1620 * @param nImageTo Name of the image file to merge to.
1621 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
1622 */
1623VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
1624 unsigned nImageTo, PVDINTERFACE pVDIfsOperation)
1625{
1626 int rc = VINF_SUCCESS;
1627 void *pvBuf = NULL;
1628
1629 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pVDIfsOperation=%#p\n",
1630 pDisk, nImageFrom, nImageTo, pVDIfsOperation));
1631
1632 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
1633 VDINTERFACETYPE_PROGRESS);
1634 PVDINTERFACEPROGRESS pCbProgress = NULL;
1635 if (pIfProgress)
1636 pCbProgress = VDGetInterfaceProgress(pIfProgress);
1637
1638 do
1639 {
1640 /* sanity check */
1641 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1642 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1643
1644 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
1645 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
1646 if (!pImageFrom || !pImageTo)
1647 {
1648 rc = VERR_VD_IMAGE_NOT_FOUND;
1649 break;
1650 }
1651 AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
1652
1653 /* Make sure destination image is writable. */
1654 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
1655 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
1656 {
1657 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
1658 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pvBackendData,
1659 uOpenFlags);
1660 if (RT_FAILURE(rc))
1661 break;
1662 }
1663
1664 /* Get size of destination image. */
1665 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pvBackendData);
1666
1667 /* Allocate tmp buffer. */
1668 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
1669 if (!pvBuf)
1670 {
1671 rc = VERR_NO_MEMORY;
1672 break;
1673 }
1674
1675 /* Merging is done directly on the images itself. This potentially
1676 * causes trouble if the disk is full in the middle of operation. */
1677 /** @todo write alternative implementation which works with temporary
1678 * images (which is safer, but requires even more space). Also has the
1679 * drawback that merging into a raw disk parent simply isn't possible
1680 * this way (but in that case disk full isn't really a problem). */
1681 if (nImageFrom < nImageTo)
1682 {
1683 /* Merge parent state into child. This means writing all not
1684 * allocated blocks in the destination image which are allocated in
1685 * the images to be merged. */
1686 uint64_t uOffset = 0;
1687 uint64_t cbRemaining = cbSize;
1688 do
1689 {
1690 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1691 rc = pImageTo->Backend->pfnRead(pImageTo->pvBackendData,
1692 uOffset, pvBuf, cbThisRead,
1693 &cbThisRead);
1694 if (rc == VERR_VD_BLOCK_FREE)
1695 {
1696 /* Search for image with allocated block. Do not attempt to
1697 * read more than the previous reads marked as valid.
1698 * Otherwise this would return stale data when different
1699 * block sizes are used for the images. */
1700 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
1701 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VD_BLOCK_FREE;
1702 pCurrImage = pCurrImage->pPrev)
1703 {
1704 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
1705 uOffset, pvBuf,
1706 cbThisRead,
1707 &cbThisRead);
1708 }
1709
1710 if (rc != VERR_VD_BLOCK_FREE)
1711 {
1712 if (RT_FAILURE(rc))
1713 break;
1714 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1715 cbThisRead);
1716 if (RT_FAILURE(rc))
1717 break;
1718 }
1719 else
1720 rc = VINF_SUCCESS;
1721 }
1722 else if (RT_FAILURE(rc))
1723 break;
1724
1725 uOffset += cbThisRead;
1726 cbRemaining -= cbThisRead;
1727
1728 if (pCbProgress && pCbProgress->pfnProgress)
1729 {
1730 rc = pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */,
1731 uOffset * 99 / cbSize,
1732 pIfProgress->pvUser);
1733 if (RT_FAILURE(rc))
1734 break;
1735 }
1736 } while (uOffset < cbSize);
1737 }
1738 else
1739 {
1740 /* Merge child state into parent. This means writing all blocks
1741 * which are allocated in the image up to the source image to the
1742 * destination image. */
1743 uint64_t uOffset = 0;
1744 uint64_t cbRemaining = cbSize;
1745 do
1746 {
1747 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1748 rc = VERR_VD_BLOCK_FREE;
1749 /* Search for image with allocated block. Do not attempt to
1750 * read more than the previous reads marked as valid. Otherwise
1751 * this would return stale data when different block sizes are
1752 * used for the images. */
1753 for (PVDIMAGE pCurrImage = pImageFrom;
1754 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VD_BLOCK_FREE;
1755 pCurrImage = pCurrImage->pPrev)
1756 {
1757 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
1758 uOffset, pvBuf,
1759 cbThisRead, &cbThisRead);
1760 }
1761
1762 if (rc != VERR_VD_BLOCK_FREE)
1763 {
1764 if (RT_FAILURE(rc))
1765 break;
1766 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1767 cbThisRead);
1768 if (RT_FAILURE(rc))
1769 break;
1770 }
1771 else
1772 rc = VINF_SUCCESS;
1773
1774 uOffset += cbThisRead;
1775 cbRemaining -= cbThisRead;
1776
1777 if (pCbProgress && pCbProgress->pfnProgress)
1778 {
1779 rc = pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */,
1780 uOffset * 99 / cbSize,
1781 pIfProgress->pvUser);
1782 if (RT_FAILURE(rc))
1783 break;
1784 }
1785 } while (uOffset < cbSize);
1786 }
1787
1788 /* Update parent UUID so that image chain is consistent. */
1789 RTUUID Uuid;
1790 if (nImageFrom < nImageTo)
1791 {
1792 if (pImageTo->pPrev)
1793 {
1794 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pPrev->pvBackendData,
1795 &Uuid);
1796 AssertRC(rc);
1797 }
1798 else
1799 RTUuidClear(&Uuid);
1800 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pvBackendData,
1801 &Uuid);
1802 AssertRC(rc);
1803 }
1804 else
1805 {
1806 if (pImageFrom->pNext)
1807 {
1808 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pvBackendData,
1809 &Uuid);
1810 AssertRC(rc);
1811 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext,
1812 &Uuid);
1813 AssertRC(rc);
1814 }
1815 }
1816
1817 /* Make sure destination image is back to read only if necessary. */
1818 if (pImageTo != pDisk->pLast && pImageFrom != pDisk->pLast)
1819 {
1820 uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
1821 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
1822 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pvBackendData,
1823 uOpenFlags);
1824 if (RT_FAILURE(rc))
1825 break;
1826 }
1827
1828 /* Delete the no longer needed images. */
1829 PVDIMAGE pImg = pImageFrom, pTmp;
1830 while (pImg != pImageTo)
1831 {
1832 if (nImageFrom < nImageTo)
1833 pTmp = pImg->pNext;
1834 else
1835 pTmp = pImg->pPrev;
1836 vdRemoveImageFromList(pDisk, pImg);
1837 pImg->Backend->pfnClose(pImg->pvBackendData, true);
1838 RTMemFree(pImg->pszFilename);
1839 RTMemFree(pImg);
1840 pImg = pTmp;
1841 }
1842 } while (0);
1843
1844 if (pvBuf)
1845 RTMemTmpFree(pvBuf);
1846
1847 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
1848 pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */, 100,
1849 pIfProgress->pvUser);
1850
1851 LogFlowFunc(("returns %Rrc\n", rc));
1852 return rc;
1853}
1854
1855/**
1856 * Copies an image from one HDD container to another.
1857 * The copy is opened in the target HDD container.
1858 * It is possible to convert between different image formats, because the
1859 * backend for the destination may be different from the source.
1860 * If both the source and destination reference the same HDD container,
1861 * then the image is moved (by copying/deleting or renaming) to the new location.
1862 * The source container is unchanged if the move operation fails, otherwise
1863 * the image at the new location is opened in the same way as the old one was.
1864 *
1865 * @returns VBox status code.
1866 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
1867 * @param pDiskFrom Pointer to source HDD container.
1868 * @param nImage Image number, counts from 0. 0 is always base image of container.
1869 * @param pDiskTo Pointer to destination HDD container.
1870 * @param pszBackend Name of the image file backend to use.
1871 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
1872 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
1873 * @param cbSize New image size (0 means leave unchanged).
1874 * @param uImageFlags Flags specifying special destination image features.
1875 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
1876 * This parameter is used if and only if a true copy is created.
1877 * In all rename/move cases the UUIDs are copied over.
1878 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
1879 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
1880 * destination image.
1881 * @param pDstVDIfsOperation Pointer to the per-image VD interface list,
1882 * for the destination image.
1883 */
1884VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
1885 const char *pszBackend, const char *pszFilename,
1886 bool fMoveByRename, uint64_t cbSize,
1887 unsigned uImageFlags, PCRTUUID pDstUuid,
1888 PVDINTERFACE pVDIfsOperation,
1889 PVDINTERFACE pDstVDIfsImage,
1890 PVDINTERFACE pDstVDIfsOperation)
1891{
1892 int rc, rc2 = VINF_SUCCESS;
1893 void *pvBuf = NULL;
1894 PVDIMAGE pImageTo = NULL;
1895
1896 LogFlowFunc(("pDiskFrom=%#p nImage=%u pDiskTo=%#p pszBackend=\"%s\" pszFilename=\"%s\" fMoveByRename=%d cbSize=%llu pVDIfsOperation=%#p pDstVDIfsImage=%#p pDstVDIfsOperation=%#p\n",
1897 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
1898
1899 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
1900 VDINTERFACETYPE_PROGRESS);
1901 PVDINTERFACEPROGRESS pCbProgress = NULL;
1902 if (pIfProgress)
1903 pCbProgress = VDGetInterfaceProgress(pIfProgress);
1904
1905 PVDINTERFACE pDstIfProgress = VDInterfaceGet(pDstVDIfsOperation,
1906 VDINTERFACETYPE_PROGRESS);
1907 PVDINTERFACEPROGRESS pDstCbProgress = NULL;
1908 if (pDstIfProgress)
1909 pDstCbProgress = VDGetInterfaceProgress(pDstIfProgress);
1910
1911 do {
1912 /* Check arguments. */
1913 AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
1914 rc = VERR_INVALID_PARAMETER);
1915 AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
1916 ("u32Signature=%08x\n", pDiskFrom->u32Signature));
1917
1918 PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
1919 AssertPtrBreakStmt(pImageFrom, rc = VERR_VD_IMAGE_NOT_FOUND);
1920 AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
1921 rc = VERR_INVALID_PARAMETER);
1922 AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
1923 ("u32Signature=%08x\n", pDiskTo->u32Signature));
1924
1925 /* Move the image. */
1926 if (pDiskFrom == pDiskTo)
1927 {
1928 /* Rename only works when backends are the same. */
1929 if ( fMoveByRename
1930 && !RTStrICmp(pszBackend, pImageFrom->Backend->pszBackendName))
1931 {
1932 rc = pImageFrom->Backend->pfnRename(pImageFrom->pvBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
1933 break;
1934 }
1935
1936 /** @todo Moving (including shrinking/growing) of the image is
1937 * requested, but the rename attempt failed or it wasn't possible.
1938 * Must now copy image to temp location. */
1939 AssertReleaseMsgFailed(("VDCopy: moving by copy/delete not implemented\n"));
1940 }
1941
1942 /* When moving an image pszFilename is allowed to be NULL, so do the parameter check here. */
1943 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1944 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1945 rc = VERR_INVALID_PARAMETER);
1946
1947 uint64_t cbSizeFrom;
1948 cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pvBackendData);
1949 if (cbSizeFrom == 0)
1950 {
1951 rc = VERR_VD_VALUE_NOT_FOUND;
1952 break;
1953 }
1954
1955 if (cbSize == 0)
1956 cbSize = cbSizeFrom;
1957
1958 PDMMEDIAGEOMETRY PCHSGeometryFrom = {0, 0, 0};
1959 PDMMEDIAGEOMETRY LCHSGeometryFrom = {0, 0, 0};
1960 pImageFrom->Backend->pfnGetPCHSGeometry(pImageFrom->pvBackendData, &PCHSGeometryFrom);
1961 pImageFrom->Backend->pfnGetLCHSGeometry(pImageFrom->pvBackendData, &LCHSGeometryFrom);
1962
1963 RTUUID ImageUuid, ImageModificationUuid;
1964 RTUUID ParentUuid, ParentModificationUuid;
1965 if (pDiskFrom != pDiskTo)
1966 {
1967 if (pDstUuid)
1968 ImageUuid = *pDstUuid;
1969 else
1970 RTUuidCreate(&ImageUuid);
1971 }
1972 else
1973 {
1974 rc = pImageFrom->Backend->pfnGetUuid(pImageFrom->pvBackendData, &ImageUuid);
1975 if (RT_FAILURE(rc))
1976 RTUuidCreate(&ImageUuid);
1977 }
1978 rc = pImageFrom->Backend->pfnGetModificationUuid(pImageFrom->pvBackendData, &ImageModificationUuid);
1979 if (RT_FAILURE(rc))
1980 RTUuidClear(&ImageModificationUuid);
1981 rc = pImageFrom->Backend->pfnGetParentUuid(pImageFrom->pvBackendData, &ParentUuid);
1982 if (RT_FAILURE(rc))
1983 RTUuidClear(&ParentUuid);
1984 rc = pImageFrom->Backend->pfnGetParentModificationUuid(pImageFrom->pvBackendData, &ParentModificationUuid);
1985 if (RT_FAILURE(rc))
1986 RTUuidClear(&ParentModificationUuid);
1987
1988 char szComment[1024];
1989 rc = pImageFrom->Backend->pfnGetComment(pImageFrom->pvBackendData, szComment, sizeof(szComment));
1990 if (RT_FAILURE(rc))
1991 szComment[0] = '\0';
1992 else
1993 szComment[sizeof(szComment) - 1] = '\0';
1994
1995 unsigned uOpenFlagsFrom;
1996 uOpenFlagsFrom = pImageFrom->Backend->pfnGetOpenFlags(pImageFrom->pvBackendData);
1997
1998 /* Create destination image with the properties of the source image. */
1999 /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
2000 * calls to the backend. Unifies the code and reduces the API
2001 * dependencies. */
2002 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
2003 {
2004 rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename, uImageFlags,
2005 szComment, &ImageUuid, &ParentUuid, uOpenFlagsFrom & ~VD_OPEN_FLAGS_READONLY, NULL, NULL);
2006 } else {
2007 /** @todo Please, review this! It's an ugly hack I think... */
2008 if (!RTStrICmp(pszBackend, "RAW"))
2009 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
2010
2011 rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, cbSize,
2012 uImageFlags, szComment,
2013 &PCHSGeometryFrom, &LCHSGeometryFrom,
2014 NULL, uOpenFlagsFrom & ~VD_OPEN_FLAGS_READONLY, NULL, NULL);
2015 if (RT_SUCCESS(rc) && !RTUuidIsNull(&ImageUuid))
2016 pDiskTo->pLast->Backend->pfnSetUuid(pDiskTo->pLast->pvBackendData, &ImageUuid);
2017 if (RT_SUCCESS(rc) && !RTUuidIsNull(&ParentUuid))
2018 pDiskTo->pLast->Backend->pfnSetParentUuid(pDiskTo->pLast->pvBackendData, &ParentUuid);
2019 }
2020 if (RT_FAILURE(rc))
2021 break;
2022
2023 pImageTo = pDiskTo->pLast;
2024 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
2025
2026 /* Allocate tmp buffer. */
2027 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
2028 if (!pvBuf)
2029 {
2030 rc = VERR_NO_MEMORY;
2031 break;
2032 }
2033
2034 /* Copy the data. */
2035 uint64_t uOffset = 0;
2036 uint64_t cbRemaining = cbSize;
2037
2038 do
2039 {
2040 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
2041
2042 rc = vdReadHelper(pDiskFrom, pImageFrom, uOffset, pvBuf,
2043 cbThisRead);
2044 if (RT_FAILURE(rc))
2045 break;
2046
2047 rc = vdWriteHelper(pDiskTo, pImageTo, uOffset, pvBuf,
2048 cbThisRead);
2049 if (RT_FAILURE(rc))
2050 break;
2051
2052 uOffset += cbThisRead;
2053 cbRemaining -= cbThisRead;
2054
2055 if (pCbProgress && pCbProgress->pfnProgress)
2056 {
2057 rc = pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */,
2058 uOffset * 99 / cbSize,
2059 pIfProgress->pvUser);
2060 if (RT_FAILURE(rc))
2061 break;
2062 }
2063 if (pDstCbProgress && pDstCbProgress->pfnProgress)
2064 {
2065 rc = pDstCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */,
2066 uOffset * 99 / cbSize,
2067 pDstIfProgress->pvUser);
2068 if (RT_FAILURE(rc))
2069 break;
2070 }
2071 } while (uOffset < cbSize);
2072
2073 if (RT_SUCCESS(rc))
2074 {
2075 pImageTo->Backend->pfnSetModificationUuid(pImageTo->pvBackendData, &ImageModificationUuid);
2076 pImageTo->Backend->pfnGetParentModificationUuid(pImageTo->pvBackendData, &ParentModificationUuid);
2077 }
2078 } while (0);
2079
2080 if (RT_FAILURE(rc) && pImageTo)
2081 {
2082 /* Error detected, but new image created. Remove image from list. */
2083 vdRemoveImageFromList(pDiskTo, pImageTo);
2084
2085 /* Close and delete image. */
2086 rc2 = pImageTo->Backend->pfnClose(pImageTo->pvBackendData, true);
2087 AssertRC(rc2);
2088 pImageTo->pvBackendData = NULL;
2089
2090 /* Free remaining resources. */
2091 if (pImageTo->pszFilename)
2092 RTStrFree(pImageTo->pszFilename);
2093
2094 RTMemFree(pImageTo);
2095 }
2096
2097 if (pvBuf)
2098 RTMemTmpFree(pvBuf);
2099
2100 if (RT_SUCCESS(rc))
2101 {
2102 if (pCbProgress && pCbProgress->pfnProgress)
2103 pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */, 100,
2104 pIfProgress->pvUser);
2105 if (pDstCbProgress && pDstCbProgress->pfnProgress)
2106 pDstCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */, 100,
2107 pDstIfProgress->pvUser);
2108 }
2109
2110 LogFlowFunc(("returns %Rrc\n", rc));
2111 return rc;
2112}
2113
2114/**
2115 * Closes the last opened image file in HDD container.
2116 * If previous image file was opened in read-only mode (that is normal) and closing image
2117 * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
2118 * will be reopened in read/write mode.
2119 *
2120 * @returns VBox status code.
2121 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
2122 * @param pDisk Pointer to HDD container.
2123 * @param fDelete If true, delete the image from the host disk.
2124 */
2125VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
2126{
2127 int rc = VINF_SUCCESS;;
2128
2129 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
2130 do
2131 {
2132 /* sanity check */
2133 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2134 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2135
2136 PVDIMAGE pImage = pDisk->pLast;
2137 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
2138 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2139 /* Remove image from list of opened images. */
2140 vdRemoveImageFromList(pDisk, pImage);
2141 /* Close (and optionally delete) image. */
2142 rc = pImage->Backend->pfnClose(pImage->pvBackendData, fDelete);
2143 /* Free remaining resources related to the image. */
2144 RTStrFree(pImage->pszFilename);
2145 RTMemFree(pImage);
2146
2147 pImage = pDisk->pLast;
2148 if (!pImage)
2149 break;
2150
2151 /* If disk was previously in read/write mode, make sure it will stay
2152 * like this (if possible) after closing this image. Set the open flags
2153 * accordingly. */
2154 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
2155 {
2156 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2157 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
2158 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData, uOpenFlags);
2159 }
2160
2161 int rc2;
2162
2163 /* Cache disk information. */
2164 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
2165
2166 /* Cache PCHS geometry. */
2167 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2168 &pDisk->PCHSGeometry);
2169 if (RT_FAILURE(rc2))
2170 {
2171 pDisk->PCHSGeometry.cCylinders = 0;
2172 pDisk->PCHSGeometry.cHeads = 0;
2173 pDisk->PCHSGeometry.cSectors = 0;
2174 }
2175 else
2176 {
2177 /* Make sure the PCHS geometry is properly clipped. */
2178 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
2179 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
2180 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
2181 }
2182
2183 /* Cache LCHS geometry. */
2184 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2185 &pDisk->LCHSGeometry);
2186 if (RT_FAILURE(rc2))
2187 {
2188 pDisk->LCHSGeometry.cCylinders = 0;
2189 pDisk->LCHSGeometry.cHeads = 0;
2190 pDisk->LCHSGeometry.cSectors = 0;
2191 }
2192 else
2193 {
2194 /* Make sure the LCHS geometry is properly clipped. */
2195 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
2196 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
2197 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
2198 }
2199 } while (0);
2200
2201 LogFlowFunc(("returns %Rrc\n", rc));
2202 return rc;
2203}
2204
2205/**
2206 * Closes all opened image files in HDD container.
2207 *
2208 * @returns VBox status code.
2209 * @param pDisk Pointer to HDD container.
2210 */
2211VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
2212{
2213 int rc = VINF_SUCCESS;
2214
2215 LogFlowFunc(("pDisk=%#p\n", pDisk));
2216 do
2217 {
2218 /* sanity check */
2219 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2220 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2221
2222 PVDIMAGE pImage = pDisk->pLast;
2223 while (VALID_PTR(pImage))
2224 {
2225 PVDIMAGE pPrev = pImage->pPrev;
2226 /* Remove image from list of opened images. */
2227 vdRemoveImageFromList(pDisk, pImage);
2228 /* Close image. */
2229 int rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, false);
2230 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
2231 rc = rc2;
2232 /* Free remaining resources related to the image. */
2233 RTStrFree(pImage->pszFilename);
2234 RTMemFree(pImage);
2235 pImage = pPrev;
2236 }
2237 Assert(!VALID_PTR(pDisk->pLast));
2238 } while (0);
2239
2240 LogFlowFunc(("returns %Rrc\n", rc));
2241 return rc;
2242}
2243
2244/**
2245 * Read data from virtual HDD.
2246 *
2247 * @returns VBox status code.
2248 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
2249 * @param pDisk Pointer to HDD container.
2250 * @param uOffset Offset of first reading byte from start of disk.
2251 * @param pvBuf Pointer to buffer for reading data.
2252 * @param cbRead Number of bytes to read.
2253 */
2254VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
2255 size_t cbRead)
2256{
2257 int rc;
2258
2259 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
2260 pDisk, uOffset, pvBuf, cbRead));
2261 do
2262 {
2263 /* sanity check */
2264 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2265 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2266
2267 /* Check arguments. */
2268 AssertMsgBreakStmt(VALID_PTR(pvBuf),
2269 ("pvBuf=%#p\n", pvBuf),
2270 rc = VERR_INVALID_PARAMETER);
2271 AssertMsgBreakStmt(cbRead,
2272 ("cbRead=%zu\n", cbRead),
2273 rc = VERR_INVALID_PARAMETER);
2274 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
2275 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
2276 uOffset, cbRead, pDisk->cbSize),
2277 rc = VERR_INVALID_PARAMETER);
2278
2279 PVDIMAGE pImage = pDisk->pLast;
2280 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
2281
2282 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead);
2283 } while (0);
2284
2285 LogFlowFunc(("returns %Rrc\n", rc));
2286 return rc;
2287}
2288
2289/**
2290 * Write data to virtual HDD.
2291 *
2292 * @returns VBox status code.
2293 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
2294 * @param pDisk Pointer to HDD container.
2295 * @param uOffset Offset of the first byte being
2296 * written from start of disk.
2297 * @param pvBuf Pointer to buffer for writing data.
2298 * @param cbWrite Number of bytes to write.
2299 */
2300VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
2301 size_t cbWrite)
2302{
2303 int rc = VINF_SUCCESS;
2304
2305 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
2306 pDisk, uOffset, pvBuf, cbWrite));
2307 do
2308 {
2309 /* sanity check */
2310 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2311 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2312
2313 /* Check arguments. */
2314 AssertMsgBreakStmt(VALID_PTR(pvBuf),
2315 ("pvBuf=%#p\n", pvBuf),
2316 rc = VERR_INVALID_PARAMETER);
2317 AssertMsgBreakStmt(cbWrite,
2318 ("cbWrite=%zu\n", cbWrite),
2319 rc = VERR_INVALID_PARAMETER);
2320 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
2321 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
2322 uOffset, cbWrite, pDisk->cbSize),
2323 rc = VERR_INVALID_PARAMETER);
2324
2325 PVDIMAGE pImage = pDisk->pLast;
2326 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
2327
2328 vdSetModifiedFlag(pDisk);
2329 rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite);
2330 } while (0);
2331
2332 LogFlowFunc(("returns %Rrc\n", rc));
2333 return rc;
2334}
2335
2336/**
2337 * Make sure the on disk representation of a virtual HDD is up to date.
2338 *
2339 * @returns VBox status code.
2340 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
2341 * @param pDisk Pointer to HDD container.
2342 */
2343VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
2344{
2345 int rc = VINF_SUCCESS;
2346
2347 LogFlowFunc(("pDisk=%#p\n", pDisk));
2348 do
2349 {
2350 /* sanity check */
2351 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2352 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2353
2354 PVDIMAGE pImage = pDisk->pLast;
2355 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
2356
2357 vdResetModifiedFlag(pDisk);
2358 rc = pImage->Backend->pfnFlush(pImage->pvBackendData);
2359 } while (0);
2360
2361 LogFlowFunc(("returns %Rrc\n", rc));
2362 return rc;
2363}
2364
2365/**
2366 * Get number of opened images in HDD container.
2367 *
2368 * @returns Number of opened images for HDD container. 0 if no images have been opened.
2369 * @param pDisk Pointer to HDD container.
2370 */
2371VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
2372{
2373 unsigned cImages;
2374
2375 LogFlowFunc(("pDisk=%#p\n", pDisk));
2376 do
2377 {
2378 /* sanity check */
2379 AssertPtrBreakStmt(pDisk, cImages = 0);
2380 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2381
2382 cImages = pDisk->cImages;
2383 } while (0);
2384
2385 LogFlowFunc(("returns %u\n", cImages));
2386 return cImages;
2387}
2388
2389/**
2390 * Get read/write mode of HDD container.
2391 *
2392 * @returns Virtual disk ReadOnly status.
2393 * @returns true if no image is opened in HDD container.
2394 * @param pDisk Pointer to HDD container.
2395 */
2396VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
2397{
2398 bool fReadOnly;
2399
2400 LogFlowFunc(("pDisk=%#p\n", pDisk));
2401 do
2402 {
2403 /* sanity check */
2404 AssertPtrBreakStmt(pDisk, fReadOnly = false);
2405 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2406
2407 PVDIMAGE pImage = pDisk->pLast;
2408 AssertPtrBreakStmt(pImage, fReadOnly = true);
2409
2410 unsigned uOpenFlags;
2411 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
2412 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
2413 } while (0);
2414
2415 LogFlowFunc(("returns %d\n", fReadOnly));
2416 return fReadOnly;
2417}
2418
2419/**
2420 * Get total capacity of an image in HDD container.
2421 *
2422 * @returns Virtual disk size in bytes.
2423 * @returns 0 if no image with specified number was not opened.
2424 * @param pDisk Pointer to HDD container.
2425 * @param nImage Image number, counds from 0. 0 is always base image of container.
2426 */
2427VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
2428{
2429 uint64_t cbSize;
2430
2431 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
2432 do
2433 {
2434 /* sanity check */
2435 AssertPtrBreakStmt(pDisk, cbSize = 0);
2436 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2437
2438 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2439 AssertPtrBreakStmt(pImage, cbSize = 0);
2440 cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
2441 } while (0);
2442
2443 LogFlowFunc(("returns %llu\n", cbSize));
2444 return cbSize;
2445}
2446
2447/**
2448 * Get total file size of an image in HDD container.
2449 *
2450 * @returns Virtual disk size in bytes.
2451 * @returns 0 if no image is opened in HDD container.
2452 * @param pDisk Pointer to HDD container.
2453 * @param nImage Image number, counts from 0. 0 is always base image of container.
2454 */
2455VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
2456{
2457 uint64_t cbSize;
2458
2459 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
2460 do
2461 {
2462 /* sanity check */
2463 AssertPtrBreakStmt(pDisk, cbSize = 0);
2464 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2465
2466 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2467 AssertPtrBreakStmt(pImage, cbSize = 0);
2468 cbSize = pImage->Backend->pfnGetFileSize(pImage->pvBackendData);
2469 } while (0);
2470
2471 LogFlowFunc(("returns %llu\n", cbSize));
2472 return cbSize;
2473}
2474
2475/**
2476 * Get virtual disk PCHS geometry stored in HDD container.
2477 *
2478 * @returns VBox status code.
2479 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
2480 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2481 * @param pDisk Pointer to HDD container.
2482 * @param nImage Image number, counts from 0. 0 is always base image of container.
2483 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
2484 */
2485VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2486 PPDMMEDIAGEOMETRY pPCHSGeometry)
2487{
2488 int rc = VINF_SUCCESS;
2489
2490 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
2491 pDisk, nImage, pPCHSGeometry));
2492 do
2493 {
2494 /* sanity check */
2495 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2496 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2497
2498 /* Check arguments. */
2499 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
2500 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
2501 rc = VERR_INVALID_PARAMETER);
2502
2503 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2504 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
2505
2506 if (pImage == pDisk->pLast)
2507 {
2508 /* Use cached information if possible. */
2509 if (pDisk->PCHSGeometry.cCylinders != 0)
2510 *pPCHSGeometry = pDisk->PCHSGeometry;
2511 else
2512 rc = VERR_VD_GEOMETRY_NOT_SET;
2513 }
2514 else
2515 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2516 pPCHSGeometry);
2517 } while (0);
2518
2519 LogFlowFunc(("%s: %Rrc (PCHS=%u/%u/%u)\n", __FUNCTION__, rc,
2520 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
2521 pDisk->PCHSGeometry.cSectors));
2522 return rc;
2523}
2524
2525/**
2526 * Store virtual disk PCHS geometry in HDD container.
2527 *
2528 * Note that in case of unrecoverable error all images in HDD container will be closed.
2529 *
2530 * @returns VBox status code.
2531 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
2532 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2533 * @param pDisk Pointer to HDD container.
2534 * @param nImage Image number, counts from 0. 0 is always base image of container.
2535 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
2536 */
2537VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2538 PCPDMMEDIAGEOMETRY pPCHSGeometry)
2539{
2540 int rc = VINF_SUCCESS;
2541
2542 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
2543 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
2544 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
2545 do
2546 {
2547 /* sanity check */
2548 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2549 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2550
2551 /* Check arguments. */
2552 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
2553 && pPCHSGeometry->cCylinders <= 16383
2554 && pPCHSGeometry->cHeads <= 16
2555 && pPCHSGeometry->cSectors <= 63,
2556 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
2557 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
2558 pPCHSGeometry->cSectors),
2559 rc = VERR_INVALID_PARAMETER);
2560
2561 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2562 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
2563
2564 if (pImage == pDisk->pLast)
2565 {
2566 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
2567 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
2568 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
2569 {
2570 /* Only update geometry if it is changed. Avoids similar checks
2571 * in every backend. Most of the time the new geometry is set
2572 * to the previous values, so no need to go through the hassle
2573 * of updating an image which could be opened in read-only mode
2574 * right now. */
2575 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2576 pPCHSGeometry);
2577
2578 /* Cache new geometry values in any case. */
2579 int rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2580 &pDisk->PCHSGeometry);
2581 if (RT_FAILURE(rc2))
2582 {
2583 pDisk->PCHSGeometry.cCylinders = 0;
2584 pDisk->PCHSGeometry.cHeads = 0;
2585 pDisk->PCHSGeometry.cSectors = 0;
2586 }
2587 else
2588 {
2589 /* Make sure the CHS geometry is properly clipped. */
2590 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 1024);
2591 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
2592 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
2593 }
2594 }
2595 }
2596 else
2597 {
2598 PDMMEDIAGEOMETRY PCHS;
2599 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2600 &PCHS);
2601 if ( RT_FAILURE(rc)
2602 || pPCHSGeometry->cCylinders != PCHS.cCylinders
2603 || pPCHSGeometry->cHeads != PCHS.cHeads
2604 || pPCHSGeometry->cSectors != PCHS.cSectors)
2605 {
2606 /* Only update geometry if it is changed. Avoids similar checks
2607 * in every backend. Most of the time the new geometry is set
2608 * to the previous values, so no need to go through the hassle
2609 * of updating an image which could be opened in read-only mode
2610 * right now. */
2611 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2612 pPCHSGeometry);
2613 }
2614 }
2615 } while (0);
2616
2617 LogFlowFunc(("returns %Rrc\n", rc));
2618 return rc;
2619}
2620
2621/**
2622 * Get virtual disk LCHS geometry stored in HDD container.
2623 *
2624 * @returns VBox status code.
2625 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
2626 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2627 * @param pDisk Pointer to HDD container.
2628 * @param nImage Image number, counts from 0. 0 is always base image of container.
2629 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
2630 */
2631VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2632 PPDMMEDIAGEOMETRY pLCHSGeometry)
2633{
2634 int rc = VINF_SUCCESS;
2635
2636 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
2637 pDisk, nImage, pLCHSGeometry));
2638 do
2639 {
2640 /* sanity check */
2641 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2642 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2643
2644 /* Check arguments. */
2645 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
2646 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
2647 rc = VERR_INVALID_PARAMETER);
2648
2649 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2650 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
2651
2652 if (pImage == pDisk->pLast)
2653 {
2654 /* Use cached information if possible. */
2655 if (pDisk->LCHSGeometry.cCylinders != 0)
2656 *pLCHSGeometry = pDisk->LCHSGeometry;
2657 else
2658 rc = VERR_VD_GEOMETRY_NOT_SET;
2659 }
2660 else
2661 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2662 pLCHSGeometry);
2663 } while (0);
2664
2665 LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
2666 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
2667 pDisk->LCHSGeometry.cSectors));
2668 return rc;
2669}
2670
2671/**
2672 * Store virtual disk LCHS geometry in HDD container.
2673 *
2674 * Note that in case of unrecoverable error all images in HDD container will be closed.
2675 *
2676 * @returns VBox status code.
2677 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
2678 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2679 * @param pDisk Pointer to HDD container.
2680 * @param nImage Image number, counts from 0. 0 is always base image of container.
2681 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
2682 */
2683VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2684 PCPDMMEDIAGEOMETRY pLCHSGeometry)
2685{
2686 int rc = VINF_SUCCESS;
2687
2688 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
2689 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
2690 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2691 do
2692 {
2693 /* sanity check */
2694 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2695 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2696
2697 /* Check arguments. */
2698 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
2699 && pLCHSGeometry->cCylinders <= 1024
2700 && pLCHSGeometry->cHeads <= 255
2701 && pLCHSGeometry->cSectors <= 63,
2702 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
2703 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
2704 pLCHSGeometry->cSectors),
2705 rc = VERR_INVALID_PARAMETER);
2706
2707 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2708 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
2709
2710 if (pImage == pDisk->pLast)
2711 {
2712 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
2713 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
2714 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
2715 {
2716 /* Only update geometry if it is changed. Avoids similar checks
2717 * in every backend. Most of the time the new geometry is set
2718 * to the previous values, so no need to go through the hassle
2719 * of updating an image which could be opened in read-only mode
2720 * right now. */
2721 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2722 pLCHSGeometry);
2723
2724 /* Cache new geometry values in any case. */
2725 int rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2726 &pDisk->LCHSGeometry);
2727 if (RT_FAILURE(rc2))
2728 {
2729 pDisk->LCHSGeometry.cCylinders = 0;
2730 pDisk->LCHSGeometry.cHeads = 0;
2731 pDisk->LCHSGeometry.cSectors = 0;
2732 }
2733 else
2734 {
2735 /* Make sure the CHS geometry is properly clipped. */
2736 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
2737 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
2738 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
2739 }
2740 }
2741 }
2742 else
2743 {
2744 PDMMEDIAGEOMETRY LCHS;
2745 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2746 &LCHS);
2747 if ( RT_FAILURE(rc)
2748 || pLCHSGeometry->cCylinders != LCHS.cCylinders
2749 || pLCHSGeometry->cHeads != LCHS.cHeads
2750 || pLCHSGeometry->cSectors != LCHS.cSectors)
2751 {
2752 /* Only update geometry if it is changed. Avoids similar checks
2753 * in every backend. Most of the time the new geometry is set
2754 * to the previous values, so no need to go through the hassle
2755 * of updating an image which could be opened in read-only mode
2756 * right now. */
2757 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2758 pLCHSGeometry);
2759 }
2760 }
2761 } while (0);
2762
2763 LogFlowFunc(("returns %Rrc\n", rc));
2764 return rc;
2765}
2766
2767/**
2768 * Get version of image in HDD container.
2769 *
2770 * @returns VBox status code.
2771 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
2772 * @param pDisk Pointer to HDD container.
2773 * @param nImage Image number, counts from 0. 0 is always base image of container.
2774 * @param puVersion Where to store the image version.
2775 */
2776VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
2777 unsigned *puVersion)
2778{
2779 int rc = VINF_SUCCESS;
2780
2781 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
2782 pDisk, nImage, puVersion));
2783 do
2784 {
2785 /* sanity check */
2786 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2787 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2788
2789 /* Check arguments. */
2790 AssertMsgBreakStmt(VALID_PTR(puVersion),
2791 ("puVersion=%#p\n", puVersion),
2792 rc = VERR_INVALID_PARAMETER);
2793
2794 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2795 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
2796
2797 *puVersion = pImage->Backend->pfnGetVersion(pImage->pvBackendData);
2798 } while (0);
2799
2800 LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
2801 return rc;
2802}
2803
2804/**
2805 * List the capabilities of image backend in HDD container.
2806 *
2807 * @returns VBox status code.
2808 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
2809 * @param pDisk Pointer to the HDD container.
2810 * @param nImage Image number, counts from 0. 0 is always base image of container.
2811 * @param pbackendInfo Where to store the backend information.
2812 */
2813VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
2814 PVDBACKENDINFO pBackendInfo)
2815{
2816 int rc = VINF_SUCCESS;
2817
2818 LogFlowFunc(("pDisk=%#p nImage=%u pBackendInfo=%#p\n",
2819 pDisk, nImage, pBackendInfo));
2820 do
2821 {
2822 /* sanity check */
2823 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2824 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2825
2826 /* Check arguments. */
2827 AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
2828 ("pBackendInfo=%#p\n", pBackendInfo),
2829 rc = VERR_INVALID_PARAMETER);
2830
2831 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2832 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
2833
2834 pBackendInfo->pszBackend = RTStrDup(pImage->Backend->pszBackendName);
2835 pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
2836 pBackendInfo->papszFileExtensions = pImage->Backend->papszFileExtensions;
2837 pBackendInfo->paConfigInfo = pImage->Backend->paConfigInfo;
2838 } while (0);
2839
2840 LogFlowFunc(("returns %Rrc\n", rc));
2841 return rc;
2842}
2843
2844/**
2845 * Get flags of image in HDD container.
2846 *
2847 * @returns VBox status code.
2848 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
2849 * @param pDisk Pointer to HDD container.
2850 * @param nImage Image number, counts from 0. 0 is always base image of container.
2851 * @param puImageFlags Where to store the image flags.
2852 */
2853VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
2854 unsigned *puImageFlags)
2855{
2856 int rc = VINF_SUCCESS;
2857
2858 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
2859 pDisk, nImage, puImageFlags));
2860 do
2861 {
2862 /* sanity check */
2863 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2864 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2865
2866 /* Check arguments. */
2867 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
2868 ("puImageFlags=%#p\n", puImageFlags),
2869 rc = VERR_INVALID_PARAMETER);
2870
2871 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2872 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
2873
2874 *puImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pvBackendData);
2875 } while (0);
2876
2877 LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
2878 return rc;
2879}
2880
2881/**
2882 * Get open flags of image in HDD container.
2883 *
2884 * @returns VBox status code.
2885 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
2886 * @param pDisk Pointer to HDD container.
2887 * @param nImage Image number, counts from 0. 0 is always base image of container.
2888 * @param puOpenFlags Where to store the image open flags.
2889 */
2890VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2891 unsigned *puOpenFlags)
2892{
2893 int rc = VINF_SUCCESS;
2894
2895 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
2896 pDisk, nImage, puOpenFlags));
2897 do
2898 {
2899 /* sanity check */
2900 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2901 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2902
2903 /* Check arguments. */
2904 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
2905 ("puOpenFlags=%#p\n", puOpenFlags),
2906 rc = VERR_INVALID_PARAMETER);
2907
2908 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2909 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
2910
2911 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2912 } while (0);
2913
2914 LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
2915 return rc;
2916}
2917
2918/**
2919 * Set open flags of image in HDD container.
2920 * This operation may cause file locking changes and/or files being reopened.
2921 * Note that in case of unrecoverable error all images in HDD container will be closed.
2922 *
2923 * @returns VBox status code.
2924 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
2925 * @param pDisk Pointer to HDD container.
2926 * @param nImage Image number, counts from 0. 0 is always base image of container.
2927 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
2928 */
2929VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2930 unsigned uOpenFlags)
2931{
2932 int rc;
2933
2934 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
2935 do
2936 {
2937 /* sanity check */
2938 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2939 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2940
2941 /* Check arguments. */
2942 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
2943 ("uOpenFlags=%#x\n", uOpenFlags),
2944 rc = VERR_INVALID_PARAMETER);
2945
2946 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2947 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
2948
2949 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData,
2950 uOpenFlags);
2951 } while (0);
2952
2953 LogFlowFunc(("returns %Rrc\n", rc));
2954 return rc;
2955}
2956
2957/**
2958 * Get base filename of image in HDD container. Some image formats use
2959 * other filenames as well, so don't use this for anything but informational
2960 * purposes.
2961 *
2962 * @returns VBox status code.
2963 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
2964 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
2965 * @param pDisk Pointer to HDD container.
2966 * @param nImage Image number, counts from 0. 0 is always base image of container.
2967 * @param pszFilename Where to store the image file name.
2968 * @param cbFilename Size of buffer pszFilename points to.
2969 */
2970VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
2971 char *pszFilename, unsigned cbFilename)
2972{
2973 int rc;
2974
2975 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
2976 pDisk, nImage, pszFilename, cbFilename));
2977 do
2978 {
2979 /* sanity check */
2980 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2981 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2982
2983 /* Check arguments. */
2984 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
2985 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
2986 rc = VERR_INVALID_PARAMETER);
2987 AssertMsgBreakStmt(cbFilename,
2988 ("cbFilename=%u\n", cbFilename),
2989 rc = VERR_INVALID_PARAMETER);
2990
2991 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2992 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
2993
2994 size_t cb = strlen(pImage->pszFilename);
2995 if (cb <= cbFilename)
2996 {
2997 strcpy(pszFilename, pImage->pszFilename);
2998 rc = VINF_SUCCESS;
2999 }
3000 else
3001 {
3002 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
3003 pszFilename[cbFilename - 1] = '\0';
3004 rc = VERR_BUFFER_OVERFLOW;
3005 }
3006 } while (0);
3007
3008 LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
3009 return rc;
3010}
3011
3012/**
3013 * Get the comment line of image in HDD container.
3014 *
3015 * @returns VBox status code.
3016 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3017 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
3018 * @param pDisk Pointer to HDD container.
3019 * @param nImage Image number, counts from 0. 0 is always base image of container.
3020 * @param pszComment Where to store the comment string of image. NULL is ok.
3021 * @param cbComment The size of pszComment buffer. 0 is ok.
3022 */
3023VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
3024 char *pszComment, unsigned cbComment)
3025{
3026 int rc;
3027
3028 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
3029 pDisk, nImage, pszComment, cbComment));
3030 do
3031 {
3032 /* sanity check */
3033 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3034 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3035
3036 /* Check arguments. */
3037 AssertMsgBreakStmt(VALID_PTR(pszComment),
3038 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
3039 rc = VERR_INVALID_PARAMETER);
3040 AssertMsgBreakStmt(cbComment,
3041 ("cbComment=%u\n", cbComment),
3042 rc = VERR_INVALID_PARAMETER);
3043
3044 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3045 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3046
3047 rc = pImage->Backend->pfnGetComment(pImage->pvBackendData, pszComment,
3048 cbComment);
3049 } while (0);
3050
3051 LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
3052 return rc;
3053}
3054
3055/**
3056 * Changes the comment line of image in HDD container.
3057 *
3058 * @returns VBox status code.
3059 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3060 * @param pDisk Pointer to HDD container.
3061 * @param nImage Image number, counts from 0. 0 is always base image of container.
3062 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
3063 */
3064VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
3065 const char *pszComment)
3066{
3067 int rc;
3068
3069 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
3070 pDisk, nImage, pszComment, pszComment));
3071 do
3072 {
3073 /* sanity check */
3074 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3075 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3076
3077 /* Check arguments. */
3078 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
3079 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
3080 rc = VERR_INVALID_PARAMETER);
3081
3082 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3083 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3084
3085 rc = pImage->Backend->pfnSetComment(pImage->pvBackendData, pszComment);
3086 } while (0);
3087
3088 LogFlowFunc(("returns %Rrc\n", rc));
3089 return rc;
3090}
3091
3092
3093/**
3094 * Get UUID of image in HDD container.
3095 *
3096 * @returns VBox status code.
3097 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3098 * @param pDisk Pointer to HDD container.
3099 * @param nImage Image number, counts from 0. 0 is always base image of container.
3100 * @param pUuid Where to store the image creation UUID.
3101 */
3102VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
3103{
3104 int rc;
3105
3106 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3107 do
3108 {
3109 /* sanity check */
3110 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3111 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3112
3113 /* Check arguments. */
3114 AssertMsgBreakStmt(VALID_PTR(pUuid),
3115 ("pUuid=%#p\n", pUuid),
3116 rc = VERR_INVALID_PARAMETER);
3117
3118 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3119 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3120
3121 rc = pImage->Backend->pfnGetUuid(pImage->pvBackendData, pUuid);
3122 } while (0);
3123
3124 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
3125 return rc;
3126}
3127
3128/**
3129 * Set the image's UUID. Should not be used by normal applications.
3130 *
3131 * @returns VBox status code.
3132 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3133 * @param pDisk Pointer to HDD container.
3134 * @param nImage Image number, counts from 0. 0 is always base image of container.
3135 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
3136 */
3137VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
3138{
3139 int rc;
3140
3141 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
3142 pDisk, nImage, pUuid, pUuid));
3143 do
3144 {
3145 /* sanity check */
3146 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3147 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3148
3149 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3150 ("pUuid=%#p\n", pUuid),
3151 rc = VERR_INVALID_PARAMETER);
3152
3153 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3154 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3155
3156 RTUUID Uuid;
3157 if (!pUuid)
3158 {
3159 RTUuidCreate(&Uuid);
3160 pUuid = &Uuid;
3161 }
3162 rc = pImage->Backend->pfnSetUuid(pImage->pvBackendData, pUuid);
3163 } while (0);
3164
3165 LogFlowFunc(("returns %Rrc\n", rc));
3166 return rc;
3167}
3168
3169/**
3170 * Get last modification UUID of image in HDD container.
3171 *
3172 * @returns VBox status code.
3173 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3174 * @param pDisk Pointer to HDD container.
3175 * @param nImage Image number, counts from 0. 0 is always base image of container.
3176 * @param pUuid Where to store the image modification UUID.
3177 */
3178VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
3179{
3180 int rc = VINF_SUCCESS;
3181
3182 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, 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 /* Check arguments. */
3190 AssertMsgBreakStmt(VALID_PTR(pUuid),
3191 ("pUuid=%#p\n", pUuid),
3192 rc = VERR_INVALID_PARAMETER);
3193
3194 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3195 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3196
3197 rc = pImage->Backend->pfnGetModificationUuid(pImage->pvBackendData,
3198 pUuid);
3199 } while (0);
3200
3201 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
3202 return rc;
3203}
3204
3205/**
3206 * Set the image's last modification UUID. Should not be used by normal applications.
3207 *
3208 * @returns VBox status code.
3209 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3210 * @param pDisk Pointer to HDD container.
3211 * @param nImage Image number, counts from 0. 0 is always base image of container.
3212 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
3213 */
3214VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
3215{
3216 int rc;
3217
3218 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
3219 pDisk, nImage, pUuid, pUuid));
3220 do
3221 {
3222 /* sanity check */
3223 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3224 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3225
3226 /* Check arguments. */
3227 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3228 ("pUuid=%#p\n", pUuid),
3229 rc = VERR_INVALID_PARAMETER);
3230
3231 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3232 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3233
3234 RTUUID Uuid;
3235 if (!pUuid)
3236 {
3237 RTUuidCreate(&Uuid);
3238 pUuid = &Uuid;
3239 }
3240 rc = pImage->Backend->pfnSetModificationUuid(pImage->pvBackendData,
3241 pUuid);
3242 } while (0);
3243
3244 LogFlowFunc(("returns %Rrc\n", rc));
3245 return rc;
3246}
3247
3248/**
3249 * Get parent UUID of image in HDD container.
3250 *
3251 * @returns VBox status code.
3252 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3253 * @param pDisk Pointer to HDD container.
3254 * @param nImage Image number, counts from 0. 0 is always base image of container.
3255 * @param pUuid Where to store the parent image UUID.
3256 */
3257VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
3258 PRTUUID pUuid)
3259{
3260 int rc = VINF_SUCCESS;
3261
3262 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3263 do
3264 {
3265 /* sanity check */
3266 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3267 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3268
3269 /* Check arguments. */
3270 AssertMsgBreakStmt(VALID_PTR(pUuid),
3271 ("pUuid=%#p\n", pUuid),
3272 rc = VERR_INVALID_PARAMETER);
3273
3274 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3275 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3276
3277 rc = pImage->Backend->pfnGetParentUuid(pImage->pvBackendData, pUuid);
3278 } while (0);
3279
3280 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
3281 return rc;
3282}
3283
3284/**
3285 * Set the image's parent UUID. Should not be used by normal applications.
3286 *
3287 * @returns VBox status code.
3288 * @param pDisk Pointer to HDD container.
3289 * @param nImage Image number, counts from 0. 0 is always base image of container.
3290 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
3291 */
3292VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
3293 PCRTUUID pUuid)
3294{
3295 int rc;
3296
3297 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
3298 pDisk, nImage, pUuid, pUuid));
3299 do
3300 {
3301 /* sanity check */
3302 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3303 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3304
3305 /* Check arguments. */
3306 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3307 ("pUuid=%#p\n", pUuid),
3308 rc = VERR_INVALID_PARAMETER);
3309
3310 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3311 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3312
3313 RTUUID Uuid;
3314 if (!pUuid)
3315 {
3316 RTUuidCreate(&Uuid);
3317 pUuid = &Uuid;
3318 }
3319 rc = pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, pUuid);
3320 } while (0);
3321
3322 LogFlowFunc(("returns %Rrc\n", rc));
3323 return rc;
3324}
3325
3326
3327/**
3328 * Debug helper - dumps all opened images in HDD container into the log file.
3329 *
3330 * @param pDisk Pointer to HDD container.
3331 */
3332VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
3333{
3334 do
3335 {
3336 /* sanity check */
3337 AssertPtrBreak(pDisk);
3338 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3339
3340 RTLogPrintf("--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
3341 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
3342 {
3343 RTLogPrintf("Dumping VD image \"%s\" (Backend=%s)\n",
3344 pImage->pszFilename, pImage->Backend->pszBackendName);
3345 pImage->Backend->pfnDump(pImage->pvBackendData);
3346 }
3347 } while (0);
3348}
3349
3350/**
3351 * Query if asynchronous operations are supported for this disk.
3352 *
3353 * @returns VBox status code.
3354 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3355 * @param pDisk Pointer to the HDD container.
3356 * @param nImage Image number, counts from 0. 0 is always base image of container.
3357 * @param pfAIOSupported Where to store if async IO is supported.
3358 */
3359VBOXDDU_DECL(int) VDImageIsAsyncIOSupported(PVBOXHDD pDisk, unsigned nImage, bool *pfAIOSupported)
3360{
3361 int rc = VINF_SUCCESS;
3362
3363 LogFlowFunc(("pDisk=%#p nImage=%u pfAIOSupported=%#p\n", pDisk, nImage, pfAIOSupported));
3364 do
3365 {
3366 /* sanity check */
3367 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3368 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3369
3370 /* Check arguments. */
3371 AssertMsgBreakStmt(VALID_PTR(pfAIOSupported),
3372 ("pfAIOSupported=%#p\n", pfAIOSupported),
3373 rc = VERR_INVALID_PARAMETER);
3374
3375 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3376 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3377
3378 if (pImage->Backend->uBackendCaps & VD_CAP_ASYNC)
3379 *pfAIOSupported = pImage->Backend->pfnIsAsyncIOSupported(pImage->pvBackendData);
3380 else
3381 *pfAIOSupported = false;
3382 } while (0);
3383
3384 LogFlowFunc(("returns %Rrc, fAIOSupported=%u\n", rc, *pfAIOSupported));
3385 return rc;
3386}
3387
3388/**
3389 * Start a asynchronous read request.
3390 *
3391 * @returns VBox status code.
3392 * @param pDisk Pointer to the HDD container.
3393 * @param uOffset The offset of the virtual disk to read from.
3394 * @param cbRead How many bytes to read.
3395 * @param paSeg Pointer to an array of segments.
3396 * @param cSeg Number of segments in the array.
3397 * @param pvUser User data which is passed on completion
3398 */
3399VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
3400 PPDMDATASEG paSeg, unsigned cSeg,
3401 void *pvUser)
3402{
3403 int rc = VERR_VD_BLOCK_FREE;
3404
3405 LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbRead=%zu\n",
3406 pDisk, uOffset, paSeg, cSeg, cbRead));
3407 do
3408 {
3409 /* sanity check */
3410 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3411 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3412
3413 /* Check arguments. */
3414 AssertMsgBreakStmt(cbRead,
3415 ("cbRead=%zu\n", cbRead),
3416 rc = VERR_INVALID_PARAMETER);
3417 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
3418 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
3419 uOffset, cbRead, pDisk->cbSize),
3420 rc = VERR_INVALID_PARAMETER);
3421 AssertMsgBreakStmt(VALID_PTR(paSeg),
3422 ("paSeg=%#p\n", paSeg),
3423 rc = VERR_INVALID_PARAMETER);
3424 AssertMsgBreakStmt(cSeg,
3425 ("cSeg=%zu\n", cSeg),
3426 rc = VERR_INVALID_PARAMETER);
3427
3428
3429 PVDIMAGE pImage = pDisk->pLast;
3430 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
3431
3432 /* @todo: This does not work for images which do not have all meta data in memory. */
3433 for (PVDIMAGE pCurrImage = pImage;
3434 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
3435 pCurrImage = pCurrImage->pPrev)
3436 {
3437 rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pvBackendData,
3438 uOffset, cbRead, paSeg, cSeg,
3439 pvUser);
3440 }
3441
3442 /* No image in the chain contains the data for the block. */
3443 if (rc == VERR_VD_BLOCK_FREE)
3444 {
3445 for (unsigned i = 0; i < cSeg && (cbRead > 0); i++)
3446 {
3447 memset(paSeg[i].pvSeg, '\0', paSeg[i].cbSeg);
3448 cbRead -= paSeg[i].cbSeg;
3449 }
3450 /* Request finished without the need to enqueue a async I/O request. Tell caller. */
3451 rc = VINF_VD_ASYNC_IO_FINISHED;
3452 }
3453
3454 } while (0);
3455
3456 LogFlowFunc(("returns %Rrc\n", rc));
3457 return rc;
3458}
3459
3460
3461/**
3462 * Start a asynchronous write request.
3463 *
3464 * @returns VBox status code.
3465 * @param pDisk Pointer to the HDD container.
3466 * @param uOffset The offset of the virtual disk to write to.
3467 * @param cbWrtie How many bytes to write.
3468 * @param paSeg Pointer to an array of segments.
3469 * @param cSeg Number of segments in the array.
3470 * @param pvUser User data which is passed on completion.
3471 */
3472VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
3473 PPDMDATASEG paSeg, unsigned cSeg,
3474 void *pvUser)
3475{
3476 int rc;
3477
3478 LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbWrite=%zu\n",
3479 pDisk, uOffset, paSeg, cSeg, cbWrite));
3480 do
3481 {
3482 /* sanity check */
3483 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3484 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3485
3486 /* Check arguments. */
3487 AssertMsgBreakStmt(cbWrite,
3488 ("cbWrite=%zu\n", cbWrite),
3489 rc = VERR_INVALID_PARAMETER);
3490 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
3491 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
3492 uOffset, cbWrite, pDisk->cbSize),
3493 rc = VERR_INVALID_PARAMETER);
3494 AssertMsgBreakStmt(VALID_PTR(paSeg),
3495 ("paSeg=%#p\n", paSeg),
3496 rc = VERR_INVALID_PARAMETER);
3497 AssertMsgBreakStmt(cSeg,
3498 ("cSeg=%zu\n", cSeg),
3499 rc = VERR_INVALID_PARAMETER);
3500
3501
3502 PVDIMAGE pImage = pDisk->pLast;
3503 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
3504
3505 vdSetModifiedFlag(pDisk);
3506 rc = pImage->Backend->pfnAsyncWrite(pImage->pvBackendData,
3507 uOffset, cbWrite,
3508 paSeg, cSeg, pvUser);
3509 } while (0);
3510
3511 LogFlowFunc(("returns %Rrc\n", rc));
3512 return rc;
3513
3514}
3515
3516#if 0
3517/** @copydoc VBOXHDDBACKEND::pfnComposeLocation */
3518int genericFileComposeLocation(PVDINTERFACE pConfig, char **pszLocation)
3519{
3520 return NULL;
3521}
3522
3523
3524/** @copydoc VBOXHDDBACKEND::pfnComposeName */
3525int genericFileComposeName(PVDINTERFACE pConfig, char **pszName)
3526{
3527 return NULL;
3528}
3529#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