VirtualBox

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

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

RTDirRead/RTDirReadEx: sizes are size_t not unsigned.

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