VirtualBox

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

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

#3230: Fixed memory leaks in VMDK, tstVD and VBoxHDD-new.

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