VirtualBox

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

最後變更 在這個檔案從27213是 27213,由 vboxsync 提交於 15 年 前

VD: Move the readonly mode change after behind the differencing disk deletion to prevent an inconsistent chain if the mode change fails

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

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