VirtualBox

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

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

Storage/VBoxHDD: don't use deprecated printf formatters

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