VirtualBox

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

Last change on this file since 11371 was 11353, checked in by vboxsync, 17 years ago

VBoxHDD-new: add new UUID parameter to image creation, specifying which UUID the new image should have.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 132.4 KB
Line 
1/** $Id: VBoxHDD-new.cpp 11353 2008-08-12 12:55:15Z 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 pUuid New UUID of the image. If NULL, a new UUID is created.
1258 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1259 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1260 * @param pvUser User argument for the progress callback.
1261 */
1262VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
1263 const char *pszFilename, VDIMAGETYPE enmType,
1264 uint64_t cbSize, unsigned uImageFlags,
1265 const char *pszComment,
1266 PCPDMMEDIAGEOMETRY pPCHSGeometry,
1267 PCPDMMEDIAGEOMETRY pLCHSGeometry,
1268 PCRTUUID pUuid, unsigned uOpenFlags,
1269 PFNVMPROGRESS pfnProgress, void *pvUser)
1270{
1271 int rc = VINF_SUCCESS;
1272 PVDIMAGE pImage = NULL;
1273 RTUUID uuid;
1274
1275 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" enmType=%#x cbSize=%llu uImageFlags=%#x pszComment=\"%s\" PCHS=%u/%u/%u LCHS=%u/%u/%u Uuid=%RTuuid uOpenFlags=%#x pfnProgress=%#p pvUser=%#p\n",
1276 pDisk, pszBackend, pszFilename, enmType, cbSize, uImageFlags, pszComment,
1277 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1278 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
1279 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
1280 uOpenFlags, pfnProgress, pvUser));
1281 do
1282 {
1283 /* sanity check */
1284 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1285 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1286
1287 /* Check arguments. */
1288 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
1289 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1290 rc = VERR_INVALID_PARAMETER);
1291 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1292 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1293 rc = VERR_INVALID_PARAMETER);
1294 AssertMsgBreakStmt(enmType == VD_IMAGE_TYPE_NORMAL || enmType == VD_IMAGE_TYPE_FIXED,
1295 ("enmType=%#x\n", enmType),
1296 rc = VERR_INVALID_PARAMETER);
1297 AssertMsgBreakStmt(cbSize,
1298 ("cbSize=%llu\n", cbSize),
1299 rc = VERR_INVALID_PARAMETER);
1300 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
1301 ("uImageFlags=%#x\n", uImageFlags),
1302 rc = VERR_INVALID_PARAMETER);
1303 /* The PCHS geometry fields may be 0 to leave it for later. */
1304 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
1305 && pPCHSGeometry->cCylinders <= 16383
1306 && pPCHSGeometry->cHeads <= 16
1307 && pPCHSGeometry->cSectors <= 63,
1308 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
1309 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1310 pPCHSGeometry->cSectors),
1311 rc = VERR_INVALID_PARAMETER);
1312 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
1313 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
1314 && pLCHSGeometry->cCylinders <= 1024
1315 && pLCHSGeometry->cHeads <= 255
1316 && pLCHSGeometry->cSectors <= 63,
1317 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
1318 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
1319 pLCHSGeometry->cSectors),
1320 rc = VERR_INVALID_PARAMETER);
1321 /* The UUID may be NULL. */
1322 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
1323 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
1324 rc = VERR_INVALID_PARAMETER);
1325 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1326 ("uOpenFlags=%#x\n", uOpenFlags),
1327 rc = VERR_INVALID_PARAMETER);
1328
1329 /* Check state. */
1330 AssertMsgBreakStmt(pDisk->cImages == 0,
1331 ("Create base image cannot be done with other images open\n"),
1332 rc = VERR_VDI_INVALID_STATE);
1333
1334 /* Set up image descriptor. */
1335 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1336 if (!pImage)
1337 {
1338 rc = VERR_NO_MEMORY;
1339 break;
1340 }
1341 pImage->pszFilename = RTStrDup(pszFilename);
1342 if (!pImage->pszFilename)
1343 {
1344 rc = VERR_NO_MEMORY;
1345 break;
1346 }
1347
1348 rc = vdFindBackend(pszBackend, &pImage->Backend, &pImage->hPlugin);
1349 if (RT_FAILURE(rc))
1350 break;
1351 if (!pImage->Backend)
1352 {
1353 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1354 N_("VD: unknown backend name '%s'"), pszBackend);
1355 break;
1356 }
1357
1358 /* Create UUID if the caller didn't specify one. */
1359 if (!pUuid)
1360 {
1361 rc = RTUuidCreate(&uuid);
1362 if (RT_FAILURE(rc))
1363 {
1364 rc = vdError(pDisk, rc, RT_SRC_POS,
1365 N_("VD: cannot generate UUID for image '%s'"),
1366 pszFilename);
1367 break;
1368 }
1369 pUuid = &uuid;
1370 }
1371
1372 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1373 rc = pImage->Backend->pfnCreate(pImage->pszFilename, enmType, cbSize,
1374 uImageFlags, pszComment, pPCHSGeometry,
1375 pLCHSGeometry, pUuid,
1376 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1377 pfnProgress, pvUser, 0, 99,
1378 pDisk->pInterfaces,
1379 &pImage->pvBackendData);
1380
1381 if (RT_SUCCESS(rc))
1382 {
1383 pImage->enmImageType = enmType;
1384
1385 /* Force sane optimization settings. It's not worth avoiding writes
1386 * to fixed size images. The overhead would have almost no payback. */
1387 if (enmType == VD_IMAGE_TYPE_FIXED)
1388 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
1389
1390 /** @todo optionally check UUIDs */
1391
1392 int rc2;
1393
1394 /* Cache disk information. */
1395 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
1396
1397 /* Cache PCHS geometry. */
1398 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1399 &pDisk->PCHSGeometry);
1400 if (RT_FAILURE(rc2))
1401 {
1402 pDisk->PCHSGeometry.cCylinders = 0;
1403 pDisk->PCHSGeometry.cHeads = 0;
1404 pDisk->PCHSGeometry.cSectors = 0;
1405 }
1406 else
1407 {
1408 /* Make sure the CHS geometry is properly clipped. */
1409 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1410 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1411 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1412 }
1413
1414 /* Cache LCHS geometry. */
1415 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1416 &pDisk->LCHSGeometry);
1417 if (RT_FAILURE(rc2))
1418 {
1419 pDisk->LCHSGeometry.cCylinders = 0;
1420 pDisk->LCHSGeometry.cHeads = 0;
1421 pDisk->LCHSGeometry.cSectors = 0;
1422 }
1423 else
1424 {
1425 /* Make sure the CHS geometry is properly clipped. */
1426 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
1427 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1428 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1429 }
1430 }
1431
1432 if (RT_SUCCESS(rc))
1433 {
1434 /* Image successfully opened, make it the last image. */
1435 vdAddImageToList(pDisk, pImage);
1436 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1437 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1438 }
1439 else
1440 {
1441 /* Error detected, but image opened. Close and delete image. */
1442 int rc2;
1443 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
1444 AssertRC(rc2);
1445 pImage->pvBackendData = NULL;
1446 }
1447 } while (0);
1448
1449 if (RT_FAILURE(rc))
1450 {
1451 if (pImage)
1452 {
1453 if (pImage->hPlugin != NIL_RTLDRMOD)
1454 RTLdrClose(pImage->hPlugin);
1455
1456 if (pImage->pszFilename)
1457 RTStrFree(pImage->pszFilename);
1458 RTMemFree(pImage);
1459 }
1460 }
1461
1462 if (RT_SUCCESS(rc) && pfnProgress)
1463 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1464
1465 LogFlowFunc(("returns %Rrc\n", rc));
1466 return rc;
1467}
1468
1469/**
1470 * Creates and opens a new differencing image file in HDD container.
1471 * See comments for VDOpen function about differencing images.
1472 *
1473 * @returns VBox status code.
1474 * @param pDisk Pointer to HDD container.
1475 * @param pszBackend Name of the image file backend to use.
1476 * @param pszFilename Name of the differencing image file to create.
1477 * @param uImageFlags Flags specifying special image features.
1478 * @param pszComment Pointer to image comment. NULL is ok.
1479 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
1480 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1481 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1482 * @param pvUser User argument for the progress callback.
1483 */
1484VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
1485 const char *pszFilename, unsigned uImageFlags,
1486 const char *pszComment, PCRTUUID pUuid,
1487 unsigned uOpenFlags, PFNVMPROGRESS pfnProgress,
1488 void *pvUser)
1489{
1490 int rc = VINF_SUCCESS;
1491 PVDIMAGE pImage = NULL;
1492 RTUUID uuid;
1493
1494 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pfnProgress=%#p pvUser=%#p\n",
1495 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, uOpenFlags,
1496 pfnProgress, pvUser));
1497 do
1498 {
1499 /* sanity check */
1500 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1501 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1502
1503 /* Check arguments. */
1504 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
1505 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1506 rc = VERR_INVALID_PARAMETER);
1507 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1508 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1509 rc = VERR_INVALID_PARAMETER);
1510 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
1511 ("uImageFlags=%#x\n", uImageFlags),
1512 rc = VERR_INVALID_PARAMETER);
1513 /* The UUID may be NULL. */
1514 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
1515 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
1516 rc = VERR_INVALID_PARAMETER);
1517 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1518 ("uOpenFlags=%#x\n", uOpenFlags),
1519 rc = VERR_INVALID_PARAMETER);
1520
1521 /* Check state. */
1522 AssertMsgBreakStmt(pDisk->cImages != 0,
1523 ("Create diff image cannot be done without other images open\n"),
1524 rc = VERR_VDI_INVALID_STATE);
1525
1526 /* Set up image descriptor. */
1527 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1528 if (!pImage)
1529 {
1530 rc = VERR_NO_MEMORY;
1531 break;
1532 }
1533 pImage->pszFilename = RTStrDup(pszFilename);
1534 if (!pImage->pszFilename)
1535 {
1536 rc = VERR_NO_MEMORY;
1537 break;
1538 }
1539
1540 rc = vdFindBackend(pszBackend, &pImage->Backend, &pImage->hPlugin);
1541 if (RT_FAILURE(rc))
1542 break;
1543 if (!pImage->Backend)
1544 {
1545 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1546 N_("VD: unknown backend name '%s'"), pszBackend);
1547 break;
1548 }
1549
1550 /* Create UUID if the caller didn't specify one. */
1551 if (!pUuid)
1552 {
1553 rc = RTUuidCreate(&uuid);
1554 if (RT_FAILURE(rc))
1555 {
1556 rc = vdError(pDisk, rc, RT_SRC_POS,
1557 N_("VD: cannot generate UUID for image '%s'"),
1558 pszFilename);
1559 break;
1560 }
1561 pUuid = &uuid;
1562 }
1563
1564 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1565 rc = pImage->Backend->pfnCreate(pImage->pszFilename,
1566 VD_IMAGE_TYPE_DIFF, pDisk->cbSize,
1567 uImageFlags, pszComment,
1568 &pDisk->PCHSGeometry,
1569 &pDisk->LCHSGeometry, pUuid,
1570 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1571 pfnProgress, pvUser, 0, 99,
1572 pDisk->pInterfaces,
1573 &pImage->pvBackendData);
1574
1575 if (RT_SUCCESS(rc) && pDisk->cImages != 0)
1576 {
1577 pImage->enmImageType = VD_IMAGE_TYPE_DIFF;
1578
1579 /* Switch previous image to read-only mode. */
1580 unsigned uOpenFlagsPrevImg;
1581 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1582 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
1583 {
1584 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
1585 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
1586 }
1587 }
1588
1589 if (RT_SUCCESS(rc))
1590 {
1591 RTUUID Uuid;
1592 RTTIMESPEC ts;
1593 int rc2;
1594
1595 rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pvBackendData,
1596 &Uuid);
1597 if (RT_SUCCESS(rc2))
1598 pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, &Uuid);
1599 rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pvBackendData,
1600 &Uuid);
1601 if (RT_SUCCESS(rc2))
1602 pImage->Backend->pfnSetParentModificationUuid(pImage->pvBackendData,
1603 &Uuid);
1604 rc2 = pDisk->pLast->Backend->pfnGetTimeStamp(pDisk->pLast->pvBackendData,
1605 &ts);
1606 if (RT_SUCCESS(rc2))
1607 pImage->Backend->pfnSetParentTimeStamp(pImage->pvBackendData, &ts);
1608
1609 rc2 = pImage->Backend->pfnSetParentFilename(pImage->pvBackendData, pDisk->pLast->pszFilename);
1610 }
1611
1612 if (RT_SUCCESS(rc))
1613 {
1614 /** @todo optionally check UUIDs */
1615 }
1616
1617 if (RT_SUCCESS(rc))
1618 {
1619 /* Image successfully opened, make it the last image. */
1620 vdAddImageToList(pDisk, pImage);
1621 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1622 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1623 }
1624 else
1625 {
1626 /* Error detected, but image opened. Close and delete image. */
1627 int rc2;
1628 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
1629 AssertRC(rc2);
1630 pImage->pvBackendData = NULL;
1631 }
1632 } while (0);
1633
1634 if (RT_FAILURE(rc))
1635 {
1636 if (pImage->hPlugin != NIL_RTLDRMOD)
1637 RTLdrClose(pImage->hPlugin);
1638
1639 if (pImage)
1640 {
1641 if (pImage->pszFilename)
1642 RTStrFree(pImage->pszFilename);
1643 RTMemFree(pImage);
1644 }
1645 }
1646
1647 if (RT_SUCCESS(rc) && pfnProgress)
1648 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1649
1650 LogFlowFunc(("returns %Rrc\n", rc));
1651 return rc;
1652}
1653
1654/**
1655 * Merges two images (not necessarily with direct parent/child relationship).
1656 * As a side effect the source image and potentially the other images which
1657 * are also merged to the destination are deleted from both the disk and the
1658 * images in the HDD container.
1659 *
1660 * @returns VBox status code.
1661 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1662 * @param pDisk Pointer to HDD container.
1663 * @param nImageFrom Name of the image file to merge from.
1664 * @param nImageTo Name of the image file to merge to.
1665 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1666 * @param pvUser User argument for the progress callback.
1667 */
1668VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
1669 unsigned nImageTo, PFNVMPROGRESS pfnProgress,
1670 void *pvUser)
1671{
1672 int rc = VINF_SUCCESS;
1673 void *pvBuf = NULL;
1674
1675 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pfnProgress=%#p pvUser=%#p\n",
1676 pDisk, nImageFrom, nImageTo, pfnProgress, pvUser));
1677 do
1678 {
1679 /* sanity check */
1680 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1681 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1682
1683 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
1684 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
1685 if (!pImageFrom || !pImageTo)
1686 {
1687 rc = VERR_VDI_IMAGE_NOT_FOUND;
1688 break;
1689 }
1690 AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
1691
1692 /* Make sure destination image is writable. */
1693 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
1694 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
1695 {
1696 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
1697 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pvBackendData,
1698 uOpenFlags);
1699 if (RT_FAILURE(rc))
1700 break;
1701 }
1702
1703 /* Get size of destination image. */
1704 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pvBackendData);
1705
1706 /* Allocate tmp buffer. */
1707 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
1708 if (!pvBuf)
1709 {
1710 rc = VERR_NO_MEMORY;
1711 break;
1712 }
1713
1714 /* Merging is done directly on the images itself. This potentially
1715 * causes trouble if the disk is full in the middle of operation. */
1716 /** @todo write alternative implementation which works with temporary
1717 * images (which is safer, but requires even more space). Also has the
1718 * drawback that merging into a raw disk parent simply isn't possible
1719 * this way (but in that case disk full isn't really a problem). */
1720 if (nImageFrom < nImageTo)
1721 {
1722 /* Merge parent state into child. This means writing all not
1723 * allocated blocks in the destination image which are allocated in
1724 * the images to be merged. */
1725 uint64_t uOffset = 0;
1726 uint64_t cbRemaining = cbSize;
1727 do
1728 {
1729 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1730 rc = pImageTo->Backend->pfnRead(pImageTo->pvBackendData,
1731 uOffset, pvBuf, cbThisRead,
1732 &cbThisRead);
1733 if (rc == VERR_VDI_BLOCK_FREE)
1734 {
1735 /* Search for image with allocated block. Do not attempt to
1736 * read more than the previous reads marked as valid.
1737 * Otherwise this would return stale data when different
1738 * block sizes are used for the images. */
1739 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
1740 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VDI_BLOCK_FREE;
1741 pCurrImage = pCurrImage->pPrev)
1742 {
1743 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
1744 uOffset, pvBuf,
1745 cbThisRead,
1746 &cbThisRead);
1747 }
1748
1749 if (rc != VERR_VDI_BLOCK_FREE)
1750 {
1751 if (RT_FAILURE(rc))
1752 break;
1753 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1754 cbThisRead);
1755 if (RT_FAILURE(rc))
1756 break;
1757 }
1758 else
1759 rc = VINF_SUCCESS;
1760 }
1761 else if (RT_FAILURE(rc))
1762 break;
1763
1764 uOffset += cbThisRead;
1765 cbRemaining -= cbThisRead;
1766 } while (uOffset < cbSize);
1767 }
1768 else
1769 {
1770 /* Merge child state into parent. This means writing all blocks
1771 * which are allocated in the image up to the source image to the
1772 * destination image. */
1773 uint64_t uOffset = 0;
1774 uint64_t cbRemaining = cbSize;
1775 do
1776 {
1777 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1778 rc = VERR_VDI_BLOCK_FREE;
1779 /* Search for image with allocated block. Do not attempt to
1780 * read more than the previous reads marked as valid. Otherwise
1781 * this would return stale data when different block sizes are
1782 * used for the images. */
1783 for (PVDIMAGE pCurrImage = pImageFrom;
1784 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VDI_BLOCK_FREE;
1785 pCurrImage = pCurrImage->pPrev)
1786 {
1787 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
1788 uOffset, pvBuf,
1789 cbThisRead, &cbThisRead);
1790 }
1791
1792 if (rc != VERR_VDI_BLOCK_FREE)
1793 {
1794 if (RT_FAILURE(rc))
1795 break;
1796 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1797 cbThisRead);
1798 if (RT_FAILURE(rc))
1799 break;
1800 }
1801 else
1802 rc = VINF_SUCCESS;
1803
1804 uOffset += cbThisRead;
1805 cbRemaining -= cbThisRead;
1806 } while (uOffset < cbSize);
1807 }
1808
1809 /* Update parent UUID so that image chain is consistent. */
1810 RTUUID Uuid;
1811 if (nImageFrom < nImageTo)
1812 {
1813 if (pImageTo->pPrev)
1814 {
1815 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pPrev->pvBackendData,
1816 &Uuid);
1817 AssertRC(rc);
1818 }
1819 else
1820 RTUuidClear(&Uuid);
1821 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pvBackendData,
1822 &Uuid);
1823 AssertRC(rc);
1824 }
1825 else
1826 {
1827 if (pImageFrom->pNext)
1828 {
1829 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pvBackendData,
1830 &Uuid);
1831 AssertRC(rc);
1832 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext,
1833 &Uuid);
1834 AssertRC(rc);
1835 }
1836 }
1837
1838 /* Make sure destination image is back to read only if necessary. */
1839 if (pImageTo != pDisk->pLast && pImageFrom != pDisk->pLast)
1840 {
1841 uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
1842 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
1843 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pvBackendData,
1844 uOpenFlags);
1845 if (RT_FAILURE(rc))
1846 break;
1847 }
1848
1849 /* Delete the no longer needed images. */
1850 PVDIMAGE pImg = pImageFrom, pTmp;
1851 while (pImg != pImageTo)
1852 {
1853 if (nImageFrom < nImageTo)
1854 pTmp = pImg->pNext;
1855 else
1856 pTmp = pImg->pPrev;
1857 vdRemoveImageFromList(pDisk, pImg);
1858 pImg->Backend->pfnClose(pImg->pvBackendData, true);
1859 pImg = pTmp;
1860 }
1861 } while (0);
1862
1863 if (pvBuf)
1864 RTMemTmpFree(pvBuf);
1865
1866 if (RT_SUCCESS(rc) && pfnProgress)
1867 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1868
1869 LogFlowFunc(("returns %Rrc\n", rc));
1870 return rc;
1871}
1872
1873/**
1874 * Copies an image from one HDD container to another.
1875 * The copy is opened in the target HDD container.
1876 * It is possible to convert between different image formats, because the
1877 * backend for the destination may be different from the source.
1878 * If both the source and destination reference the same HDD container,
1879 * then the image is moved (by copying/deleting or renaming) to the new location.
1880 * The source container is unchanged if the move operation fails, otherwise
1881 * the image at the new location is opened in the same way as the old one was.
1882 *
1883 * @returns VBox status code.
1884 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1885 * @param pDiskFrom Pointer to source HDD container.
1886 * @param nImage Image number, counts from 0. 0 is always base image of container.
1887 * @param pDiskTo Pointer to destination HDD container.
1888 * @param pszBackend Name of the image file backend to use.
1889 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
1890 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
1891 * @param cbSize New image size (0 means leave unchanged).
1892 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1893 * @param pvUser User argument for the progress callback.
1894 */
1895VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
1896 const char *pszBackend, const char *pszFilename,
1897 bool fMoveByRename, uint64_t cbSize,
1898 PFNVMPROGRESS pfnProgress, void *pvUser)
1899{
1900 int rc, rc2 = VINF_SUCCESS;
1901 void *pvBuf = NULL;
1902 PVDIMAGE pImageTo = NULL;
1903
1904 LogFlowFunc(("pDiskFrom=%#p nImage=%u pDiskTo=%#p pszBackend=\"%s\" pszFilename=\"%s\" fMoveByRename=%d cbSize=%llu pfnProgress=%#p pvUser=%#p\n",
1905 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, pfnProgress, pvUser));
1906
1907 do {
1908 /* Check arguments. */
1909 AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
1910 rc = VERR_INVALID_PARAMETER);
1911 AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
1912 ("u32Signature=%08x\n", pDiskFrom->u32Signature));
1913
1914 PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
1915 AssertPtrBreakStmt(pImageFrom, rc = VERR_VDI_IMAGE_NOT_FOUND);
1916 AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
1917 rc = VERR_INVALID_PARAMETER);
1918 AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
1919 ("u32Signature=%08x\n", pDiskTo->u32Signature));
1920
1921 /* If the containers are equal and the backend is the same, rename the image. */
1922 if ( (pDiskFrom == pDiskTo)
1923 && (!strcmp(pszBackend, pImageFrom->Backend->pszBackendName)))
1924 {
1925 /* Rename the image. */
1926 rc = pImageFrom->Backend->pfnRename(pImageFrom->pvBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
1927 break;
1928 }
1929
1930 /* If the fMoveByRename flag is set and the backend is the same, rename the image. */
1931 if ( (fMoveByRename == true)
1932 && (!strcmp(pszBackend, pImageFrom->Backend->pszBackendName)))
1933 {
1934 /* Close the source image. */
1935 rc = pImageFrom->Backend->pfnClose(pImageFrom->pvBackendData, false);
1936 if (RT_FAILURE(rc))
1937 break;
1938
1939 /* Open the source image in the destination container. */
1940 rc = VDOpen(pDiskTo, pImageFrom->Backend->pszBackendName, pImageFrom->pszFilename, pImageFrom->uOpenFlags);
1941 if (RT_FAILURE(rc))
1942 goto movefail;
1943
1944 pImageTo = pDiskTo->pLast;
1945
1946 /* Rename the image. */
1947 rc = pImageTo->Backend->pfnRename(pImageTo->pvBackendData, pszFilename ? pszFilename : pImageTo->pszFilename);
1948 if (RT_FAILURE(rc))
1949 goto movefail;
1950
1951 /* Cleanup the leftovers. */
1952 vdRemoveImageFromList(pDiskFrom, pImageFrom);
1953 pImageFrom->pvBackendData = NULL;
1954
1955 if (pImageFrom->hPlugin != NIL_RTLDRMOD)
1956 RTLdrClose(pImageFrom->hPlugin);
1957
1958 if (pImageFrom->pszFilename)
1959 RTStrFree(pImageFrom->pszFilename);
1960
1961 RTMemFree(pImageFrom);
1962
1963 break;
1964movefail:
1965 /* In case of failure, re-open the source image in the source container. */
1966 rc2 = VDOpen(pDiskFrom, pImageFrom->Backend->pszBackendName, pImageFrom->pszFilename, pImageFrom->uOpenFlags);
1967 if (RT_FAILURE(rc2))
1968 /** @todo Uncertain what to do on error. If this happens pImageFrom and pImageTo are both closed. */
1969 rc = rc2;
1970 break;
1971 }
1972
1973 /* If fMoveByRename is set pszFilename is allowed to be NULL, so do the parameter check here. */
1974 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1975 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1976 rc = VERR_INVALID_PARAMETER);
1977
1978 /* Collect properties of source image. */
1979 VDIMAGETYPE enmTypeFrom = pImageFrom->enmImageType;
1980
1981 uint64_t cbSizeFrom;
1982 cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pvBackendData);
1983 if (cbSizeFrom == 0)
1984 {
1985 rc = VERR_VDI_VALUE_NOT_FOUND;
1986 break;
1987 }
1988
1989 if (cbSize == 0)
1990 cbSize = cbSizeFrom;
1991
1992 unsigned uImageFlagsFrom;
1993 uImageFlagsFrom = pImageFrom->Backend->pfnGetImageFlags(pImageFrom->pvBackendData);
1994
1995 /** @todo Get this from the source image. */
1996 PDMMEDIAGEOMETRY PCHSGeometryFrom = {0, 0, 0};
1997 PDMMEDIAGEOMETRY LCHSGeometryFrom = {0, 0, 0};
1998
1999 unsigned uOpenFlagsFrom;
2000 uOpenFlagsFrom = pImageFrom->Backend->pfnGetOpenFlags(pImageFrom->pvBackendData);
2001
2002 /* Create destination image with the properties of the source image. */
2003 /** @todo Copy the comment. */
2004 /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
2005 * calls to the backend. Unifies the code and reduces the API
2006 * dependencies. */
2007 if (enmTypeFrom == VD_IMAGE_TYPE_DIFF)
2008 {
2009 rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename, uImageFlagsFrom,
2010 "", NULL, uOpenFlagsFrom, NULL, NULL);
2011 } else {
2012 rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, enmTypeFrom,
2013 cbSize, uImageFlagsFrom, "",
2014 &PCHSGeometryFrom, &LCHSGeometryFrom,
2015 NULL, uOpenFlagsFrom, NULL, NULL);
2016 }
2017 if (RT_FAILURE(rc))
2018 break;
2019
2020 pImageTo = pDiskTo->pLast;
2021 AssertPtrBreakStmt(pImageTo, rc = VERR_VDI_IMAGE_NOT_FOUND);
2022
2023 /* Allocate tmp buffer. */
2024 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
2025 if (!pvBuf)
2026 {
2027 rc = VERR_NO_MEMORY;
2028 break;
2029 }
2030
2031 /* Copy the data. */
2032 uint64_t uOffset = 0;
2033 uint64_t cbRemaining = cbSize;
2034
2035 do
2036 {
2037 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
2038
2039 rc = vdReadHelper(pDiskFrom, pImageFrom, uOffset, pvBuf,
2040 cbThisRead);
2041 if (RT_FAILURE(rc))
2042 break;
2043
2044 rc = vdWriteHelper(pDiskTo, pImageTo, uOffset, pvBuf,
2045 cbThisRead);
2046 if (RT_FAILURE(rc))
2047 break;
2048
2049 uOffset += cbThisRead;
2050 cbRemaining -= cbThisRead;
2051 if (pfnProgress)
2052 {
2053 rc = pfnProgress(NULL /* WARNING! pVM=NULL */,
2054 ((cbSize - cbRemaining) * 100) / cbSize,
2055 pvUser);
2056 if (RT_FAILURE(rc))
2057 break;
2058 }
2059 } while (uOffset < cbSize);
2060
2061 /* If fMoveByRename is set but the backend is different, close and delete pImageFrom. */
2062 if ( (fMoveByRename == true)
2063 && (strcmp(pszBackend, pImageFrom->Backend->pszBackendName)))
2064 {
2065 vdRemoveImageFromList(pDiskFrom, pImageFrom);
2066
2067 /* Close and delete image. */
2068 rc2 = pImageFrom->Backend->pfnClose(pImageFrom->pvBackendData, true);
2069 AssertRC(rc2);
2070 pImageFrom->pvBackendData = NULL;
2071
2072 /* Free remaining resources. */
2073 if (pImageFrom->hPlugin != NIL_RTLDRMOD)
2074 RTLdrClose(pImageFrom->hPlugin);
2075
2076 if (pImageFrom->pszFilename)
2077 RTStrFree(pImageFrom->pszFilename);
2078
2079 RTMemFree(pImageFrom);
2080 }
2081 } while (0);
2082
2083 if (RT_FAILURE(rc) && pImageTo)
2084 {
2085 /* Error detected, but new image created. Remove image from list. */
2086 vdRemoveImageFromList(pDiskTo, pImageTo);
2087
2088 /* Close and delete image. */
2089 rc2 = pImageTo->Backend->pfnClose(pImageTo->pvBackendData, true);
2090 AssertRC(rc2);
2091 pImageTo->pvBackendData = NULL;
2092
2093 /* Free remaining resources. */
2094 if (pImageTo->hPlugin != NIL_RTLDRMOD)
2095 RTLdrClose(pImageTo->hPlugin);
2096
2097 if (pImageTo->pszFilename)
2098 RTStrFree(pImageTo->pszFilename);
2099
2100 RTMemFree(pImageTo);
2101 }
2102
2103 if (pvBuf)
2104 RTMemTmpFree(pvBuf);
2105
2106 if (RT_SUCCESS(rc) && pfnProgress)
2107 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
2108
2109 LogFlowFunc(("returns %Rrc\n", rc));
2110 return rc;
2111}
2112
2113/**
2114 * Closes the last opened image file in HDD container.
2115 * If previous image file was opened in read-only mode (that is normal) and closing image
2116 * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
2117 * will be reopened in read/write mode.
2118 *
2119 * @returns VBox status code.
2120 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2121 * @param pDisk Pointer to HDD container.
2122 * @param fDelete If true, delete the image from the host disk.
2123 */
2124VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
2125{
2126 int rc = VINF_SUCCESS;;
2127
2128 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
2129 do
2130 {
2131 /* sanity check */
2132 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2133 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2134
2135 PVDIMAGE pImage = pDisk->pLast;
2136 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2137 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2138 /* Remove image from list of opened images. */
2139 vdRemoveImageFromList(pDisk, pImage);
2140 /* Close (and optionally delete) image. */
2141 rc = pImage->Backend->pfnClose(pImage->pvBackendData, fDelete);
2142 /* Free remaining resources related to the image. */
2143 if (pImage->hPlugin != NIL_RTLDRMOD)
2144 {
2145 RTLdrClose(pImage->hPlugin);
2146 pImage->hPlugin = NIL_RTLDRMOD;
2147 }
2148 RTStrFree(pImage->pszFilename);
2149 RTMemFree(pImage);
2150
2151 pImage = pDisk->pLast;
2152 if (!pImage)
2153 break;
2154
2155 /* If disk was previously in read/write mode, make sure it will stay
2156 * like this (if possible) after closing this image. Set the open flags
2157 * accordingly. */
2158 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
2159 {
2160 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2161 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
2162 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData, uOpenFlags);
2163 }
2164
2165 int rc2;
2166
2167 /* Cache disk information. */
2168 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
2169
2170 /* Cache PCHS geometry. */
2171 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2172 &pDisk->PCHSGeometry);
2173 if (RT_FAILURE(rc2))
2174 {
2175 pDisk->PCHSGeometry.cCylinders = 0;
2176 pDisk->PCHSGeometry.cHeads = 0;
2177 pDisk->PCHSGeometry.cSectors = 0;
2178 }
2179 else
2180 {
2181 /* Make sure the PCHS geometry is properly clipped. */
2182 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
2183 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
2184 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
2185 }
2186
2187 /* Cache LCHS geometry. */
2188 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2189 &pDisk->LCHSGeometry);
2190 if (RT_FAILURE(rc2))
2191 {
2192 pDisk->LCHSGeometry.cCylinders = 0;
2193 pDisk->LCHSGeometry.cHeads = 0;
2194 pDisk->LCHSGeometry.cSectors = 0;
2195 }
2196 else
2197 {
2198 /* Make sure the LCHS geometry is properly clipped. */
2199 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
2200 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
2201 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
2202 }
2203 } while (0);
2204
2205 LogFlowFunc(("returns %Rrc\n", rc));
2206 return rc;
2207}
2208
2209/**
2210 * Closes all opened image files in HDD container.
2211 *
2212 * @returns VBox status code.
2213 * @param pDisk Pointer to HDD container.
2214 */
2215VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
2216{
2217 int rc = VINF_SUCCESS;
2218
2219 LogFlowFunc(("pDisk=%#p\n", pDisk));
2220 do
2221 {
2222 /* sanity check */
2223 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2224 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2225
2226 PVDIMAGE pImage = pDisk->pLast;
2227 while (VALID_PTR(pImage))
2228 {
2229 PVDIMAGE pPrev = pImage->pPrev;
2230 /* Remove image from list of opened images. */
2231 vdRemoveImageFromList(pDisk, pImage);
2232 /* Close image. */
2233 int rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, false);
2234 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
2235 rc = rc2;
2236 /* Free remaining resources related to the image. */
2237 if (pImage->hPlugin != NIL_RTLDRMOD)
2238 {
2239 RTLdrClose(pImage->hPlugin);
2240 pImage->hPlugin = NIL_RTLDRMOD;
2241 }
2242 RTStrFree(pImage->pszFilename);
2243 RTMemFree(pImage);
2244 pImage = pPrev;
2245 }
2246 Assert(!VALID_PTR(pDisk->pLast));
2247 } while (0);
2248
2249 LogFlowFunc(("returns %Rrc\n", rc));
2250 return rc;
2251}
2252
2253/**
2254 * Read data from virtual HDD.
2255 *
2256 * @returns VBox status code.
2257 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2258 * @param pDisk Pointer to HDD container.
2259 * @param uOffset Offset of first reading byte from start of disk.
2260 * @param pvBuf Pointer to buffer for reading data.
2261 * @param cbRead Number of bytes to read.
2262 */
2263VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
2264 size_t cbRead)
2265{
2266 int rc;
2267
2268 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
2269 pDisk, uOffset, pvBuf, cbRead));
2270 do
2271 {
2272 /* sanity check */
2273 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2274 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2275
2276 /* Check arguments. */
2277 AssertMsgBreakStmt(VALID_PTR(pvBuf),
2278 ("pvBuf=%#p\n", pvBuf),
2279 rc = VERR_INVALID_PARAMETER);
2280 AssertMsgBreakStmt(cbRead,
2281 ("cbRead=%zu\n", cbRead),
2282 rc = VERR_INVALID_PARAMETER);
2283 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
2284 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
2285 uOffset, cbRead, pDisk->cbSize),
2286 rc = VERR_INVALID_PARAMETER);
2287
2288 PVDIMAGE pImage = pDisk->pLast;
2289 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2290
2291 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead);
2292 } while (0);
2293
2294 LogFlowFunc(("returns %Rrc\n", rc));
2295 return rc;
2296}
2297
2298/**
2299 * Write data to virtual HDD.
2300 *
2301 * @returns VBox status code.
2302 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2303 * @param pDisk Pointer to HDD container.
2304 * @param uOffset Offset of the first byte being
2305 * written from start of disk.
2306 * @param pvBuf Pointer to buffer for writing data.
2307 * @param cbWrite Number of bytes to write.
2308 */
2309VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
2310 size_t cbWrite)
2311{
2312 int rc = VINF_SUCCESS;
2313
2314 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
2315 pDisk, uOffset, pvBuf, cbWrite));
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 /* Check arguments. */
2323 AssertMsgBreakStmt(VALID_PTR(pvBuf),
2324 ("pvBuf=%#p\n", pvBuf),
2325 rc = VERR_INVALID_PARAMETER);
2326 AssertMsgBreakStmt(cbWrite,
2327 ("cbWrite=%zu\n", cbWrite),
2328 rc = VERR_INVALID_PARAMETER);
2329 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
2330 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
2331 uOffset, cbWrite, pDisk->cbSize),
2332 rc = VERR_INVALID_PARAMETER);
2333
2334 PVDIMAGE pImage = pDisk->pLast;
2335 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2336
2337 vdSetModifiedFlag(pDisk);
2338 rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite);
2339 } while (0);
2340
2341 LogFlowFunc(("returns %Rrc\n", rc));
2342 return rc;
2343}
2344
2345/**
2346 * Make sure the on disk representation of a virtual HDD is up to date.
2347 *
2348 * @returns VBox status code.
2349 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2350 * @param pDisk Pointer to HDD container.
2351 */
2352VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
2353{
2354 int rc = VINF_SUCCESS;
2355
2356 LogFlowFunc(("pDisk=%#p\n", pDisk));
2357 do
2358 {
2359 /* sanity check */
2360 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2361 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2362
2363 PVDIMAGE pImage = pDisk->pLast;
2364 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2365
2366 vdResetModifiedFlag(pDisk);
2367 rc = pImage->Backend->pfnFlush(pImage->pvBackendData);
2368 } while (0);
2369
2370 LogFlowFunc(("returns %Rrc\n", rc));
2371 return rc;
2372}
2373
2374/**
2375 * Get number of opened images in HDD container.
2376 *
2377 * @returns Number of opened images for HDD container. 0 if no images have been opened.
2378 * @param pDisk Pointer to HDD container.
2379 */
2380VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
2381{
2382 unsigned cImages;
2383
2384 LogFlowFunc(("pDisk=%#p\n", pDisk));
2385 do
2386 {
2387 /* sanity check */
2388 AssertPtrBreakStmt(pDisk, cImages = 0);
2389 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2390
2391 cImages = pDisk->cImages;
2392 } while (0);
2393
2394 LogFlowFunc(("returns %u\n", cImages));
2395 return cImages;
2396}
2397
2398/**
2399 * Get read/write mode of HDD container.
2400 *
2401 * @returns Virtual disk ReadOnly status.
2402 * @returns true if no image is opened in HDD container.
2403 * @param pDisk Pointer to HDD container.
2404 */
2405VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
2406{
2407 bool fReadOnly;
2408
2409 LogFlowFunc(("pDisk=%#p\n", pDisk));
2410 do
2411 {
2412 /* sanity check */
2413 AssertPtrBreakStmt(pDisk, fReadOnly = false);
2414 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2415
2416 PVDIMAGE pImage = pDisk->pLast;
2417 AssertPtrBreakStmt(pImage, fReadOnly = true);
2418
2419 unsigned uOpenFlags;
2420 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
2421 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
2422 } while (0);
2423
2424 LogFlowFunc(("returns %d\n", fReadOnly));
2425 return fReadOnly;
2426}
2427
2428/**
2429 * Get total capacity of an image in HDD container.
2430 *
2431 * @returns Virtual disk size in bytes.
2432 * @returns 0 if no image with specified number was not opened.
2433 * @param pDisk Pointer to HDD container.
2434 * @param nImage Image number, counds from 0. 0 is always base image of container.
2435 */
2436VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
2437{
2438 uint64_t cbSize;
2439
2440 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
2441 do
2442 {
2443 /* sanity check */
2444 AssertPtrBreakStmt(pDisk, cbSize = 0);
2445 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2446
2447 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2448 AssertPtrBreakStmt(pImage, cbSize = 0);
2449 cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
2450 } while (0);
2451
2452 LogFlowFunc(("returns %llu\n", cbSize));
2453 return cbSize;
2454}
2455
2456/**
2457 * Get total file size of an image in HDD container.
2458 *
2459 * @returns Virtual disk size in bytes.
2460 * @returns 0 if no image is opened in HDD container.
2461 * @param pDisk Pointer to HDD container.
2462 * @param nImage Image number, counts from 0. 0 is always base image of container.
2463 */
2464VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
2465{
2466 uint64_t cbSize;
2467
2468 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
2469 do
2470 {
2471 /* sanity check */
2472 AssertPtrBreakStmt(pDisk, cbSize = 0);
2473 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2474
2475 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2476 AssertPtrBreakStmt(pImage, cbSize = 0);
2477 cbSize = pImage->Backend->pfnGetFileSize(pImage->pvBackendData);
2478 } while (0);
2479
2480 LogFlowFunc(("returns %llu\n", cbSize));
2481 return cbSize;
2482}
2483
2484/**
2485 * Get virtual disk PCHS geometry stored in HDD container.
2486 *
2487 * @returns VBox status code.
2488 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2489 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2490 * @param pDisk Pointer to HDD container.
2491 * @param nImage Image number, counts from 0. 0 is always base image of container.
2492 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
2493 */
2494VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2495 PPDMMEDIAGEOMETRY pPCHSGeometry)
2496{
2497 int rc = VINF_SUCCESS;
2498
2499 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
2500 pDisk, nImage, pPCHSGeometry));
2501 do
2502 {
2503 /* sanity check */
2504 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2505 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2506
2507 /* Check arguments. */
2508 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
2509 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
2510 rc = VERR_INVALID_PARAMETER);
2511
2512 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2513 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2514
2515 if (pImage == pDisk->pLast)
2516 {
2517 /* Use cached information if possible. */
2518 if (pDisk->PCHSGeometry.cCylinders != 0)
2519 *pPCHSGeometry = pDisk->PCHSGeometry;
2520 else
2521 rc = VERR_VDI_GEOMETRY_NOT_SET;
2522 }
2523 else
2524 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2525 pPCHSGeometry);
2526 } while (0);
2527
2528 LogFlowFunc(("%s: %Rrc (PCHS=%u/%u/%u)\n", __FUNCTION__, rc,
2529 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
2530 pDisk->PCHSGeometry.cSectors));
2531 return rc;
2532}
2533
2534/**
2535 * Store virtual disk PCHS geometry in HDD container.
2536 *
2537 * Note that in case of unrecoverable error all images in HDD container will be closed.
2538 *
2539 * @returns VBox status code.
2540 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2541 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2542 * @param pDisk Pointer to HDD container.
2543 * @param nImage Image number, counts from 0. 0 is always base image of container.
2544 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
2545 */
2546VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2547 PCPDMMEDIAGEOMETRY pPCHSGeometry)
2548{
2549 int rc = VINF_SUCCESS;
2550
2551 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
2552 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
2553 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
2554 do
2555 {
2556 /* sanity check */
2557 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2558 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2559
2560 /* Check arguments. */
2561 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
2562 && pPCHSGeometry->cCylinders <= 16383
2563 && pPCHSGeometry->cHeads <= 16
2564 && pPCHSGeometry->cSectors <= 63,
2565 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
2566 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
2567 pPCHSGeometry->cSectors),
2568 rc = VERR_INVALID_PARAMETER);
2569
2570 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2571 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2572
2573 if (pImage == pDisk->pLast)
2574 {
2575 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
2576 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
2577 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
2578 {
2579 /* Only update geometry if it is changed. Avoids similar checks
2580 * in every backend. Most of the time the new geometry is set
2581 * to the previous values, so no need to go through the hassle
2582 * of updating an image which could be opened in read-only mode
2583 * right now. */
2584 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2585 pPCHSGeometry);
2586
2587 /* Cache new geometry values in any case. */
2588 int rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2589 &pDisk->PCHSGeometry);
2590 if (RT_FAILURE(rc2))
2591 {
2592 pDisk->PCHSGeometry.cCylinders = 0;
2593 pDisk->PCHSGeometry.cHeads = 0;
2594 pDisk->PCHSGeometry.cSectors = 0;
2595 }
2596 else
2597 {
2598 /* Make sure the CHS geometry is properly clipped. */
2599 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 1024);
2600 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
2601 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
2602 }
2603 }
2604 }
2605 else
2606 {
2607 PDMMEDIAGEOMETRY PCHS;
2608 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2609 &PCHS);
2610 if ( RT_FAILURE(rc)
2611 || pPCHSGeometry->cCylinders != PCHS.cCylinders
2612 || pPCHSGeometry->cHeads != PCHS.cHeads
2613 || pPCHSGeometry->cSectors != PCHS.cSectors)
2614 {
2615 /* Only update geometry if it is changed. Avoids similar checks
2616 * in every backend. Most of the time the new geometry is set
2617 * to the previous values, so no need to go through the hassle
2618 * of updating an image which could be opened in read-only mode
2619 * right now. */
2620 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2621 pPCHSGeometry);
2622 }
2623 }
2624 } while (0);
2625
2626 LogFlowFunc(("returns %Rrc\n", rc));
2627 return rc;
2628}
2629
2630/**
2631 * Get virtual disk LCHS geometry stored in HDD container.
2632 *
2633 * @returns VBox status code.
2634 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2635 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2636 * @param pDisk Pointer to HDD container.
2637 * @param nImage Image number, counts from 0. 0 is always base image of container.
2638 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
2639 */
2640VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2641 PPDMMEDIAGEOMETRY pLCHSGeometry)
2642{
2643 int rc = VINF_SUCCESS;
2644
2645 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
2646 pDisk, nImage, pLCHSGeometry));
2647 do
2648 {
2649 /* sanity check */
2650 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2651 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2652
2653 /* Check arguments. */
2654 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
2655 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
2656 rc = VERR_INVALID_PARAMETER);
2657
2658 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2659 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2660
2661 if (pImage == pDisk->pLast)
2662 {
2663 /* Use cached information if possible. */
2664 if (pDisk->LCHSGeometry.cCylinders != 0)
2665 *pLCHSGeometry = pDisk->LCHSGeometry;
2666 else
2667 rc = VERR_VDI_GEOMETRY_NOT_SET;
2668 }
2669 else
2670 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2671 pLCHSGeometry);
2672 } while (0);
2673
2674 LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
2675 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
2676 pDisk->LCHSGeometry.cSectors));
2677 return rc;
2678}
2679
2680/**
2681 * Store virtual disk LCHS geometry in HDD container.
2682 *
2683 * Note that in case of unrecoverable error all images in HDD container will be closed.
2684 *
2685 * @returns VBox status code.
2686 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2687 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2688 * @param pDisk Pointer to HDD container.
2689 * @param nImage Image number, counts from 0. 0 is always base image of container.
2690 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
2691 */
2692VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2693 PCPDMMEDIAGEOMETRY pLCHSGeometry)
2694{
2695 int rc = VINF_SUCCESS;
2696
2697 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
2698 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
2699 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2700 do
2701 {
2702 /* sanity check */
2703 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2704 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2705
2706 /* Check arguments. */
2707 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
2708 && pLCHSGeometry->cCylinders <= 1024
2709 && pLCHSGeometry->cHeads <= 255
2710 && pLCHSGeometry->cSectors <= 63,
2711 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
2712 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
2713 pLCHSGeometry->cSectors),
2714 rc = VERR_INVALID_PARAMETER);
2715
2716 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2717 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2718
2719 if (pImage == pDisk->pLast)
2720 {
2721 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
2722 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
2723 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
2724 {
2725 /* Only update geometry if it is changed. Avoids similar checks
2726 * in every backend. Most of the time the new geometry is set
2727 * to the previous values, so no need to go through the hassle
2728 * of updating an image which could be opened in read-only mode
2729 * right now. */
2730 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2731 pLCHSGeometry);
2732
2733 /* Cache new geometry values in any case. */
2734 int rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2735 &pDisk->LCHSGeometry);
2736 if (RT_FAILURE(rc2))
2737 {
2738 pDisk->LCHSGeometry.cCylinders = 0;
2739 pDisk->LCHSGeometry.cHeads = 0;
2740 pDisk->LCHSGeometry.cSectors = 0;
2741 }
2742 else
2743 {
2744 /* Make sure the CHS geometry is properly clipped. */
2745 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
2746 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
2747 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
2748 }
2749 }
2750 }
2751 else
2752 {
2753 PDMMEDIAGEOMETRY LCHS;
2754 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2755 &LCHS);
2756 if ( RT_FAILURE(rc)
2757 || pLCHSGeometry->cCylinders != LCHS.cCylinders
2758 || pLCHSGeometry->cHeads != LCHS.cHeads
2759 || pLCHSGeometry->cSectors != LCHS.cSectors)
2760 {
2761 /* Only update geometry if it is changed. Avoids similar checks
2762 * in every backend. Most of the time the new geometry is set
2763 * to the previous values, so no need to go through the hassle
2764 * of updating an image which could be opened in read-only mode
2765 * right now. */
2766 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2767 pLCHSGeometry);
2768 }
2769 }
2770 } while (0);
2771
2772 LogFlowFunc(("returns %Rrc\n", rc));
2773 return rc;
2774}
2775
2776/**
2777 * Get version of image in HDD container.
2778 *
2779 * @returns VBox status code.
2780 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2781 * @param pDisk Pointer to HDD container.
2782 * @param nImage Image number, counts from 0. 0 is always base image of container.
2783 * @param puVersion Where to store the image version.
2784 */
2785VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
2786 unsigned *puVersion)
2787{
2788 int rc = VINF_SUCCESS;
2789
2790 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
2791 pDisk, nImage, puVersion));
2792 do
2793 {
2794 /* sanity check */
2795 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2796 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2797
2798 /* Check arguments. */
2799 AssertMsgBreakStmt(VALID_PTR(puVersion),
2800 ("puVersion=%#p\n", puVersion),
2801 rc = VERR_INVALID_PARAMETER);
2802
2803 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2804 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2805
2806 *puVersion = pImage->Backend->pfnGetVersion(pImage->pvBackendData);
2807 } while (0);
2808
2809 LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
2810 return rc;
2811}
2812
2813/**
2814 * Get type of image in HDD container.
2815 *
2816 * @returns VBox status code.
2817 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2818 * @param pDisk Pointer to HDD container.
2819 * @param nImage Image number, counts from 0. 0 is always base image of container.
2820 * @param penmType Where to store the image type.
2821 */
2822VBOXDDU_DECL(int) VDGetImageType(PVBOXHDD pDisk, unsigned nImage,
2823 PVDIMAGETYPE penmType)
2824{
2825 int rc = VINF_SUCCESS;
2826
2827 LogFlowFunc(("pDisk=%#p nImage=%u penmType=%#p\n",
2828 pDisk, nImage, penmType));
2829 do
2830 {
2831 /* sanity check */
2832 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2833 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2834
2835 /* Check arguments. */
2836 AssertMsgBreakStmt(VALID_PTR(penmType),
2837 ("penmType=%#p\n", penmType),
2838 rc = VERR_INVALID_PARAMETER);
2839
2840 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2841 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2842
2843 if ( pImage->enmImageType >= VD_IMAGE_TYPE_FIRST
2844 && pImage->enmImageType <= VD_IMAGE_TYPE_DIFF)
2845 {
2846 *penmType = pImage->enmImageType;
2847 rc = VINF_SUCCESS;
2848 }
2849 else
2850 rc = VERR_VDI_INVALID_TYPE;
2851 } while (0);
2852
2853 LogFlowFunc(("returns %Rrc uenmType=%u\n", rc, *penmType));
2854 return rc;
2855}
2856
2857
2858/**
2859 * List the capabilities of image backend in HDD container.
2860 *
2861 * @returns VBox status code.
2862 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2863 * @param pDisk Pointer to the HDD container.
2864 * @param nImage Image number, counts from 0. 0 is always base image of container.
2865 * @param pbackendInfo Where to store the backend information.
2866 */
2867VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
2868 PVDBACKENDINFO pBackendInfo)
2869{
2870 int rc = VINF_SUCCESS;
2871
2872 LogFlowFunc(("pDisk=%#p nImage=%u penmType=%#p\n",
2873 pDisk, nImage, pBackendInfo));
2874 do
2875 {
2876 /* sanity check */
2877 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2878 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2879
2880 /* Check arguments. */
2881 AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
2882 ("pBackendInfo=%#p\n", pBackendInfo),
2883 rc = VERR_INVALID_PARAMETER);
2884
2885 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2886 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2887
2888 if ( pImage->enmImageType >= VD_IMAGE_TYPE_FIRST
2889 && pImage->enmImageType <= VD_IMAGE_TYPE_DIFF)
2890 {
2891 pBackendInfo->pszBackend = RTStrDup(pImage->Backend->pszBackendName);
2892 pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
2893 rc = VINF_SUCCESS;
2894 }
2895 else
2896 rc = VERR_VDI_INVALID_TYPE;
2897 } while (0);
2898
2899 LogFlowFunc(("returns %Rrc\n", rc));
2900 return rc;
2901}
2902
2903/**
2904 * Get flags of image in HDD container.
2905 *
2906 * @returns VBox status code.
2907 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2908 * @param pDisk Pointer to HDD container.
2909 * @param nImage Image number, counts from 0. 0 is always base image of container.
2910 * @param puImageFlags Where to store the image flags.
2911 */
2912VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
2913 unsigned *puImageFlags)
2914{
2915 int rc = VINF_SUCCESS;
2916
2917 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
2918 pDisk, nImage, puImageFlags));
2919 do
2920 {
2921 /* sanity check */
2922 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2923 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2924
2925 /* Check arguments. */
2926 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
2927 ("puImageFlags=%#p\n", puImageFlags),
2928 rc = VERR_INVALID_PARAMETER);
2929
2930 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2931 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2932
2933 *puImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pvBackendData);
2934 } while (0);
2935
2936 LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
2937 return rc;
2938}
2939
2940/**
2941 * Get open flags of image in HDD container.
2942 *
2943 * @returns VBox status code.
2944 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2945 * @param pDisk Pointer to HDD container.
2946 * @param nImage Image number, counts from 0. 0 is always base image of container.
2947 * @param puOpenFlags Where to store the image open flags.
2948 */
2949VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2950 unsigned *puOpenFlags)
2951{
2952 int rc = VINF_SUCCESS;
2953
2954 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
2955 pDisk, nImage, puOpenFlags));
2956 do
2957 {
2958 /* sanity check */
2959 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2960 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2961
2962 /* Check arguments. */
2963 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
2964 ("puOpenFlags=%#p\n", puOpenFlags),
2965 rc = VERR_INVALID_PARAMETER);
2966
2967 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2968 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2969
2970 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2971 } while (0);
2972
2973 LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
2974 return rc;
2975}
2976
2977/**
2978 * Set open flags of image in HDD container.
2979 * This operation may cause file locking changes and/or files being reopened.
2980 * Note that in case of unrecoverable error all images in HDD container will be closed.
2981 *
2982 * @returns VBox status code.
2983 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2984 * @param pDisk Pointer to HDD container.
2985 * @param nImage Image number, counts from 0. 0 is always base image of container.
2986 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
2987 */
2988VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2989 unsigned uOpenFlags)
2990{
2991 int rc;
2992
2993 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
2994 do
2995 {
2996 /* sanity check */
2997 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2998 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2999
3000 /* Check arguments. */
3001 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
3002 ("uOpenFlags=%#x\n", uOpenFlags),
3003 rc = VERR_INVALID_PARAMETER);
3004
3005 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3006 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3007
3008 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData,
3009 uOpenFlags);
3010 } while (0);
3011
3012 LogFlowFunc(("returns %Rrc\n", rc));
3013 return rc;
3014}
3015
3016/**
3017 * Get base filename of image in HDD container. Some image formats use
3018 * other filenames as well, so don't use this for anything but informational
3019 * purposes.
3020 *
3021 * @returns VBox status code.
3022 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3023 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
3024 * @param pDisk Pointer to HDD container.
3025 * @param nImage Image number, counts from 0. 0 is always base image of container.
3026 * @param pszFilename Where to store the image file name.
3027 * @param cbFilename Size of buffer pszFilename points to.
3028 */
3029VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
3030 char *pszFilename, unsigned cbFilename)
3031{
3032 int rc;
3033
3034 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
3035 pDisk, nImage, pszFilename, cbFilename));
3036 do
3037 {
3038 /* sanity check */
3039 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3040 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3041
3042 /* Check arguments. */
3043 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
3044 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
3045 rc = VERR_INVALID_PARAMETER);
3046 AssertMsgBreakStmt(cbFilename,
3047 ("cbFilename=%u\n", cbFilename),
3048 rc = VERR_INVALID_PARAMETER);
3049
3050 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3051 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3052
3053 size_t cb = strlen(pImage->pszFilename);
3054 if (cb <= cbFilename)
3055 {
3056 strcpy(pszFilename, pImage->pszFilename);
3057 rc = VINF_SUCCESS;
3058 }
3059 else
3060 {
3061 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
3062 pszFilename[cbFilename - 1] = '\0';
3063 rc = VERR_BUFFER_OVERFLOW;
3064 }
3065 } while (0);
3066
3067 LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
3068 return rc;
3069}
3070
3071/**
3072 * Get the comment line of image in HDD container.
3073 *
3074 * @returns VBox status code.
3075 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3076 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
3077 * @param pDisk Pointer to HDD container.
3078 * @param nImage Image number, counts from 0. 0 is always base image of container.
3079 * @param pszComment Where to store the comment string of image. NULL is ok.
3080 * @param cbComment The size of pszComment buffer. 0 is ok.
3081 */
3082VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
3083 char *pszComment, unsigned cbComment)
3084{
3085 int rc;
3086
3087 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
3088 pDisk, nImage, pszComment, cbComment));
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),
3097 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
3098 rc = VERR_INVALID_PARAMETER);
3099 AssertMsgBreakStmt(cbComment,
3100 ("cbComment=%u\n", cbComment),
3101 rc = VERR_INVALID_PARAMETER);
3102
3103 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3104 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3105
3106 rc = pImage->Backend->pfnGetComment(pImage->pvBackendData, pszComment,
3107 cbComment);
3108 } while (0);
3109
3110 LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
3111 return rc;
3112}
3113
3114/**
3115 * Changes the comment line of image in HDD container.
3116 *
3117 * @returns VBox status code.
3118 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3119 * @param pDisk Pointer to HDD container.
3120 * @param nImage Image number, counts from 0. 0 is always base image of container.
3121 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
3122 */
3123VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
3124 const char *pszComment)
3125{
3126 int rc;
3127
3128 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
3129 pDisk, nImage, pszComment, pszComment));
3130 do
3131 {
3132 /* sanity check */
3133 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3134 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3135
3136 /* Check arguments. */
3137 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
3138 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
3139 rc = VERR_INVALID_PARAMETER);
3140
3141 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3142 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3143
3144 rc = pImage->Backend->pfnSetComment(pImage->pvBackendData, pszComment);
3145 } while (0);
3146
3147 LogFlowFunc(("returns %Rrc\n", rc));
3148 return rc;
3149}
3150
3151
3152/**
3153 * Get UUID of image in HDD container.
3154 *
3155 * @returns VBox status code.
3156 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3157 * @param pDisk Pointer to HDD container.
3158 * @param nImage Image number, counts from 0. 0 is always base image of container.
3159 * @param pUuid Where to store the image creation UUID.
3160 */
3161VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
3162{
3163 int rc;
3164
3165 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3166 do
3167 {
3168 /* sanity check */
3169 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3170 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3171
3172 /* Check arguments. */
3173 AssertMsgBreakStmt(VALID_PTR(pUuid),
3174 ("pUuid=%#p\n", pUuid),
3175 rc = VERR_INVALID_PARAMETER);
3176
3177 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3178 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3179
3180 rc = pImage->Backend->pfnGetUuid(pImage->pvBackendData, pUuid);
3181 } while (0);
3182
3183 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
3184 return rc;
3185}
3186
3187/**
3188 * Set the image's UUID. Should not be used by normal applications.
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 New UUID of the image. If NULL, a new UUID is created.
3195 */
3196VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
3197{
3198 int rc;
3199
3200 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
3201 pDisk, nImage, pUuid, pUuid));
3202 do
3203 {
3204 /* sanity check */
3205 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3206 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3207
3208 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
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 RTUUID Uuid;
3216 if (!pUuid)
3217 {
3218 RTUuidCreate(&Uuid);
3219 pUuid = &Uuid;
3220 }
3221 rc = pImage->Backend->pfnSetUuid(pImage->pvBackendData, pUuid);
3222 } while (0);
3223
3224 LogFlowFunc(("returns %Rrc\n", rc));
3225 return rc;
3226}
3227
3228/**
3229 * Get last modification UUID of image in HDD container.
3230 *
3231 * @returns VBox status code.
3232 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3233 * @param pDisk Pointer to HDD container.
3234 * @param nImage Image number, counts from 0. 0 is always base image of container.
3235 * @param pUuid Where to store the image modification UUID.
3236 */
3237VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
3238{
3239 int rc = VINF_SUCCESS;
3240
3241 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3242 do
3243 {
3244 /* sanity check */
3245 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3246 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3247
3248 /* Check arguments. */
3249 AssertMsgBreakStmt(VALID_PTR(pUuid),
3250 ("pUuid=%#p\n", pUuid),
3251 rc = VERR_INVALID_PARAMETER);
3252
3253 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3254 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3255
3256 rc = pImage->Backend->pfnGetModificationUuid(pImage->pvBackendData,
3257 pUuid);
3258 } while (0);
3259
3260 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
3261 return rc;
3262}
3263
3264/**
3265 * Set the image's last modification UUID. Should not be used by normal applications.
3266 *
3267 * @returns VBox status code.
3268 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3269 * @param pDisk Pointer to HDD container.
3270 * @param nImage Image number, counts from 0. 0 is always base image of container.
3271 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
3272 */
3273VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
3274{
3275 int rc;
3276
3277 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
3278 pDisk, nImage, pUuid, pUuid));
3279 do
3280 {
3281 /* sanity check */
3282 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3283 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3284
3285 /* Check arguments. */
3286 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3287 ("pUuid=%#p\n", pUuid),
3288 rc = VERR_INVALID_PARAMETER);
3289
3290 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3291 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3292
3293 RTUUID Uuid;
3294 if (!pUuid)
3295 {
3296 RTUuidCreate(&Uuid);
3297 pUuid = &Uuid;
3298 }
3299 rc = pImage->Backend->pfnSetModificationUuid(pImage->pvBackendData,
3300 pUuid);
3301 } while (0);
3302
3303 LogFlowFunc(("returns %Rrc\n", rc));
3304 return rc;
3305}
3306
3307/**
3308 * Get parent UUID of image in HDD container.
3309 *
3310 * @returns VBox status code.
3311 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3312 * @param pDisk Pointer to HDD container.
3313 * @param nImage Image number, counts from 0. 0 is always base image of container.
3314 * @param pUuid Where to store the parent image UUID.
3315 */
3316VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
3317 PRTUUID pUuid)
3318{
3319 int rc = VINF_SUCCESS;
3320
3321 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3322 do
3323 {
3324 /* sanity check */
3325 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3326 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3327
3328 /* Check arguments. */
3329 AssertMsgBreakStmt(VALID_PTR(pUuid),
3330 ("pUuid=%#p\n", pUuid),
3331 rc = VERR_INVALID_PARAMETER);
3332
3333 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3334 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3335
3336 rc = pImage->Backend->pfnGetParentUuid(pImage->pvBackendData, pUuid);
3337 } while (0);
3338
3339 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
3340 return rc;
3341}
3342
3343/**
3344 * Set the image's parent UUID. Should not be used by normal applications.
3345 *
3346 * @returns VBox status code.
3347 * @param pDisk Pointer to HDD container.
3348 * @param nImage Image number, counts from 0. 0 is always base image of container.
3349 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
3350 */
3351VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
3352 PCRTUUID pUuid)
3353{
3354 int rc;
3355
3356 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
3357 pDisk, nImage, pUuid, pUuid));
3358 do
3359 {
3360 /* sanity check */
3361 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3362 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3363
3364 /* Check arguments. */
3365 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3366 ("pUuid=%#p\n", pUuid),
3367 rc = VERR_INVALID_PARAMETER);
3368
3369 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3370 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3371
3372 RTUUID Uuid;
3373 if (!pUuid)
3374 {
3375 RTUuidCreate(&Uuid);
3376 pUuid = &Uuid;
3377 }
3378 rc = pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, pUuid);
3379 } while (0);
3380
3381 LogFlowFunc(("returns %Rrc\n", rc));
3382 return rc;
3383}
3384
3385
3386/**
3387 * Debug helper - dumps all opened images in HDD container into the log file.
3388 *
3389 * @param pDisk Pointer to HDD container.
3390 */
3391VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
3392{
3393 do
3394 {
3395 /* sanity check */
3396 AssertPtrBreak(pDisk);
3397 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3398
3399 RTLogPrintf("--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
3400 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
3401 {
3402 RTLogPrintf("Dumping VD image \"%s\" (Backend=%s)\n",
3403 pImage->pszFilename, pImage->Backend->pszBackendName);
3404 pImage->Backend->pfnDump(pImage->pvBackendData);
3405 }
3406 } while (0);
3407}
3408
3409/**
3410 * Query if asynchronous operations are supported for this disk.
3411 *
3412 * @returns VBox status code.
3413 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3414 * @param pDisk Pointer to the HDD container.
3415 * @param nImage Image number, counts from 0. 0 is always base image of container.
3416 * @param pfAIOSupported Where to store if async IO is supported.
3417 */
3418VBOXDDU_DECL(int) VDImageIsAsyncIOSupported(PVBOXHDD pDisk, unsigned nImage, bool *pfAIOSupported)
3419{
3420 int rc = VINF_SUCCESS;
3421
3422 LogFlowFunc(("pDisk=%#p nImage=%u pfAIOSupported=%#p\n", pDisk, nImage, pfAIOSupported));
3423 do
3424 {
3425 /* sanity check */
3426 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3427 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3428
3429 /* Check arguments. */
3430 AssertMsgBreakStmt(VALID_PTR(pfAIOSupported),
3431 ("pfAIOSupported=%#p\n", pfAIOSupported),
3432 rc = VERR_INVALID_PARAMETER);
3433
3434 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3435 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3436
3437 if (pImage->Backend->uBackendCaps & VD_CAP_ASYNC)
3438 *pfAIOSupported = pImage->Backend->pfnIsAsyncIOSupported(pImage->pvBackendData);
3439 else
3440 *pfAIOSupported = false;
3441 } while (0);
3442
3443 LogFlowFunc(("returns %Rrc, fAIOSupported=%u\n", rc, *pfAIOSupported));
3444 return rc;
3445}
3446
3447/**
3448 * Start a asynchronous read request.
3449 *
3450 * @returns VBox status code.
3451 * @param pDisk Pointer to the HDD container.
3452 * @param uOffset The offset of the virtual disk to read from.
3453 * @param cbRead How many bytes to read.
3454 * @param paSeg Pointer to an array of segments.
3455 * @param cSeg Number of segments in the array.
3456 * @param pvUser User data which is passed on completion
3457 */
3458VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
3459 PPDMDATASEG paSeg, unsigned cSeg,
3460 void *pvUser)
3461{
3462 int rc = VERR_VDI_BLOCK_FREE;
3463
3464 LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbRead=%zu\n",
3465 pDisk, uOffset, paSeg, cSeg, cbRead));
3466 do
3467 {
3468 /* sanity check */
3469 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3470 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3471
3472 /* Check arguments. */
3473 AssertMsgBreakStmt(cbRead,
3474 ("cbRead=%zu\n", cbRead),
3475 rc = VERR_INVALID_PARAMETER);
3476 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
3477 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
3478 uOffset, cbRead, pDisk->cbSize),
3479 rc = VERR_INVALID_PARAMETER);
3480 AssertMsgBreakStmt(VALID_PTR(paSeg),
3481 ("paSeg=%#p\n", paSeg),
3482 rc = VERR_INVALID_PARAMETER);
3483 AssertMsgBreakStmt(cSeg,
3484 ("cSeg=%zu\n", cSeg),
3485 rc = VERR_INVALID_PARAMETER);
3486
3487
3488 PVDIMAGE pImage = pDisk->pLast;
3489 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
3490
3491 /* @todo: This does not work for images which do not have all meta data in memory. */
3492 for (PVDIMAGE pCurrImage = pImage;
3493 pCurrImage != NULL && rc == VERR_VDI_BLOCK_FREE;
3494 pCurrImage = pCurrImage->pPrev)
3495 {
3496 rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pvBackendData,
3497 uOffset, cbRead, paSeg, cSeg,
3498 pvUser);
3499 }
3500
3501 /* No image in the chain contains the data for the block. */
3502 if (rc == VERR_VDI_BLOCK_FREE)
3503 {
3504 for (unsigned i = 0; i < cSeg && (cbRead > 0); i++)
3505 {
3506 memset(paSeg[i].pvSeg, '\0', paSeg[i].cbSeg);
3507 cbRead -= paSeg[i].cbSeg;
3508 }
3509 /* Request finished without the need to enqueue a async I/O request. Tell caller. */
3510 rc = VINF_VDI_ASYNC_IO_FINISHED;
3511 }
3512
3513 } while (0);
3514
3515 LogFlowFunc(("returns %Rrc\n", rc));
3516 return rc;
3517}
3518
3519
3520/**
3521 * Start a asynchronous write request.
3522 *
3523 * @returns VBox status code.
3524 * @param pDisk Pointer to the HDD container.
3525 * @param uOffset The offset of the virtual disk to write to.
3526 * @param cbWrtie How many bytes to write.
3527 * @param paSeg Pointer to an array of segments.
3528 * @param cSeg Number of segments in the array.
3529 * @param pvUser User data which is passed on completion.
3530 */
3531VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
3532 PPDMDATASEG paSeg, unsigned cSeg,
3533 void *pvUser)
3534{
3535 int rc;
3536
3537 LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbWrite=%zu\n",
3538 pDisk, uOffset, paSeg, cSeg, cbWrite));
3539 do
3540 {
3541 /* sanity check */
3542 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3543 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3544
3545 /* Check arguments. */
3546 AssertMsgBreakStmt(cbWrite,
3547 ("cbWrite=%zu\n", cbWrite),
3548 rc = VERR_INVALID_PARAMETER);
3549 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
3550 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
3551 uOffset, cbWrite, pDisk->cbSize),
3552 rc = VERR_INVALID_PARAMETER);
3553 AssertMsgBreakStmt(VALID_PTR(paSeg),
3554 ("paSeg=%#p\n", paSeg),
3555 rc = VERR_INVALID_PARAMETER);
3556 AssertMsgBreakStmt(cSeg,
3557 ("cSeg=%zu\n", cSeg),
3558 rc = VERR_INVALID_PARAMETER);
3559
3560
3561 PVDIMAGE pImage = pDisk->pLast;
3562 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
3563
3564 vdSetModifiedFlag(pDisk);
3565 rc = pImage->Backend->pfnAsyncWrite(pImage->pvBackendData,
3566 uOffset, cbWrite,
3567 paSeg, cSeg, pvUser);
3568 } while (0);
3569
3570 LogFlowFunc(("returns %Rrc\n", rc));
3571 return rc;
3572
3573}
3574
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