VirtualBox

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

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

Devices: %Vrc -> %Rrc (just preferred, not mandatory (yet))

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

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