VirtualBox

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

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

AssertMsgBreak -> AssertMsgBreakStmt.

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