VirtualBox

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

Last change on this file since 22978 was 22966, checked in by vboxsync, 15 years ago

Storage: Convert the backends to use the interface for I/O rather than using the RTFile* API directly.

For a VM the PDMAsyncCompletion is used now as the I/O layer (fixing the I/O load problems).
Still disabled by default until it was tested on all supported platforms to not risk corruption
of images. To enable compile with VBOX_WITH_NEW_IO_CODE set.

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

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