VirtualBox

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

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

Storage/VBoxHDD-new: mark VDBackendInfo as in need of cleanup. Comment only.

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