VirtualBox

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

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

AssertPtrBreakVoid -> AssertPtrBreak.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 120.9 KB
Line 
1/** $Id: VBoxHDD-new.cpp 8594 2008-05-05 14:11:24Z 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 AssertBreakStmt(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 AssertPtrBreak(pDisk);
824 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
825 VDCloseAll(pDisk);
826 RTMemFree(pDisk);
827 } while (0);
828 LogFlowFunc(("returns\n"));
829}
830
831/**
832 * Try to get the backend name which can use this image.
833 *
834 * @returns VBox status code.
835 * VINF_SUCCESS if a plugin was found.
836 * ppszFormat contains the string which can be used as backend name.
837 * VERR_NOT_SUPPORTED if no backend was found.
838 * @param pszFilename Name of the image file for which the backend is queried.
839 * @param ppszFormat Receives pointer of the UTF-8 string which contains the format name.
840 * The returned pointer must be freed using RTStrFree().
841 */
842VBOXDDU_DECL(int) VDGetFormat(const char *pszFilename, char **ppszFormat)
843{
844 PRTDIR pPluginDir = NULL;
845 int rc = VERR_NOT_SUPPORTED;
846 bool fPluginFound = false;
847
848 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
849 do
850 {
851 /* Check arguments. */
852 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
853 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
854 rc = VERR_INVALID_PARAMETER);
855 AssertMsgBreakStmt(VALID_PTR(ppszFormat),
856 ("ppszFormat=%#p\n", ppszFormat),
857 rc = VERR_INVALID_PARAMETER);
858
859 /* First check if static backends support this file format. */
860 for (unsigned i = 0; aBackends[i] != NULL; i++)
861 {
862 if (aBackends[i]->pfnCheckIfValid)
863 {
864 rc = aBackends[i]->pfnCheckIfValid(pszFilename);
865 if (VBOX_SUCCESS(rc))
866 {
867 fPluginFound = true;
868 /* Copy the name into the new string. */
869 char *pszFormat = RTStrDup(aBackends[i]->pszBackendName);
870 if (!pszFormat)
871 {
872 rc = VERR_NO_MEMORY;
873 break;
874 }
875 *ppszFormat = pszFormat;
876 break;
877 }
878 }
879 }
880 if (fPluginFound)
881 break;
882
883 /* Then check if plugin backends support this file format. */
884 char szPath[RTPATH_MAX];
885 rc = RTPathSharedLibs(szPath, sizeof(szPath));
886 if (VBOX_FAILURE(rc))
887 break;
888
889 /* To get all entries with VBoxHDD as prefix. */
890 char *pszPluginFilter;
891 rc = RTStrAPrintf(&pszPluginFilter, "%s/%s*", szPath,
892 VBOX_HDDFORMAT_PLUGIN_PREFIX);
893 if (VBOX_FAILURE(rc))
894 {
895 rc = VERR_NO_MEMORY;
896 break;
897 }
898
899 /* The plugins are in the same directory as the other shared libs. */
900 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
901 if (VBOX_FAILURE(rc))
902 break;
903
904 PRTDIRENTRY pPluginDirEntry = NULL;
905 unsigned cbPluginDirEntry = sizeof(RTDIRENTRY);
906 pPluginDirEntry = (PRTDIRENTRY)RTMemAllocZ(sizeof(RTDIRENTRY));
907 if (!pPluginDirEntry)
908 {
909 rc = VERR_NO_MEMORY;
910 break;
911 }
912
913 while ((rc = RTDirRead(pPluginDir, pPluginDirEntry, &cbPluginDirEntry)) != VERR_NO_MORE_FILES)
914 {
915 RTLDRMOD hPlugin = NIL_RTLDRMOD;
916 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad = NULL;
917 PVBOXHDDBACKEND pBackend = NULL;
918
919 if (rc == VERR_BUFFER_OVERFLOW)
920 {
921 /* allocate new buffer. */
922 RTMemFree(pPluginDirEntry);
923 pPluginDirEntry = (PRTDIRENTRY)RTMemAllocZ(cbPluginDirEntry);
924 /* Retry. */
925 rc = RTDirRead(pPluginDir, pPluginDirEntry, &cbPluginDirEntry);
926 if (VBOX_FAILURE(rc))
927 break;
928 }
929 else if (VBOX_FAILURE(rc))
930 break;
931
932 /* We got the new entry. */
933 if (pPluginDirEntry->enmType != RTDIRENTRYTYPE_FILE)
934 continue;
935
936 rc = RTLdrLoad(pPluginDirEntry->szName, &hPlugin);
937 if (VBOX_SUCCESS(rc))
938 {
939 rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad);
940 if (VBOX_FAILURE(rc) || !pfnHDDFormatLoad)
941 {
942 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Vrc, pfnHDDFormat=%#p\n", VBOX_HDDFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnHDDFormatLoad));
943 if (VBOX_SUCCESS(rc))
944 rc = VERR_SYMBOL_NOT_FOUND;
945 }
946
947 if (VBOX_SUCCESS(rc))
948 {
949 /* Get the function table. */
950 rc = pfnHDDFormatLoad(&pBackend);
951 if (VBOX_SUCCESS(rc) && pBackend->cbSize == sizeof(VBOXHDDBACKEND))
952 {
953
954 /* Check if the plugin can handle this file. */
955 rc = pBackend->pfnCheckIfValid(pszFilename);
956 if (VBOX_SUCCESS(rc))
957 {
958 fPluginFound = true;
959 rc = VINF_SUCCESS;
960
961 /* Report the format name. */
962 RTPathStripExt(pPluginDirEntry->szName);
963 AssertBreakStmt(strlen(pPluginDirEntry->szName) > VBOX_HDDFORMAT_PLUGIN_PREFIX_LENGTH,
964 rc = VERR_INVALID_NAME);
965
966 char *pszFormat = NULL;
967 pszFormat = RTStrDup(pPluginDirEntry->szName + VBOX_HDDFORMAT_PLUGIN_PREFIX_LENGTH);
968 if (!pszFormat)
969 rc = VERR_NO_MEMORY;
970
971 *ppszFormat = pszFormat;
972 }
973 }
974 }
975 else
976 pBackend = NULL;
977
978 RTLdrClose(hPlugin);
979 }
980
981 /*
982 * We take the first plugin which can handle this file.
983 */
984 if (fPluginFound)
985 break;
986 }
987 if (rc == VERR_NO_MORE_FILES)
988 rc = VERR_NOT_SUPPORTED;
989
990 RTStrFree(pszPluginFilter);
991 if (pPluginDirEntry)
992 RTMemFree(pPluginDirEntry);
993 if (pPluginDir)
994 RTDirClose(pPluginDir);
995 } while (0);
996
997 LogFlowFunc(("returns %Vrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
998 return rc;
999}
1000
1001/**
1002 * Opens an image file.
1003 *
1004 * The first opened image file in HDD container must have a base image type,
1005 * others (next opened images) must be a differencing or undo images.
1006 * Linkage is checked for differencing image to be in consistence with the previously opened image.
1007 * When another differencing image is opened and the last image was opened in read/write access
1008 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
1009 * other processes to use images in read-only mode too.
1010 *
1011 * Note that the image is opened in read-only mode if a read/write open is not possible.
1012 * Use VDIsReadOnly to check open mode.
1013 *
1014 * @returns VBox status code.
1015 * @param pDisk Pointer to HDD container.
1016 * @param pszBackend Name of the image file backend to use.
1017 * @param pszFilename Name of the image file to open.
1018 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1019 */
1020VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
1021 const char *pszFilename, unsigned uOpenFlags)
1022{
1023 int rc = VINF_SUCCESS;
1024 PVDIMAGE pImage = NULL;
1025
1026 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x\n",
1027 pDisk, pszBackend, pszFilename, uOpenFlags));
1028 do
1029 {
1030 /* sanity check */
1031 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1032 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1033
1034 /* Check arguments. */
1035 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
1036 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1037 rc = VERR_INVALID_PARAMETER);
1038 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1039 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1040 rc = VERR_INVALID_PARAMETER);
1041 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1042 ("uOpenFlags=%#x\n", uOpenFlags),
1043 rc = VERR_INVALID_PARAMETER);
1044
1045 /* Force readonly for images without base/diff consistency checking. */
1046 if (uOpenFlags & VD_OPEN_FLAGS_INFO)
1047 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
1048
1049 /* Set up image descriptor. */
1050 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1051 if (!pImage)
1052 {
1053 rc = VERR_NO_MEMORY;
1054 break;
1055 }
1056 pImage->pszFilename = RTStrDup(pszFilename);
1057 if (!pImage->pszFilename)
1058 {
1059 rc = VERR_NO_MEMORY;
1060 break;
1061 }
1062
1063 rc = vdFindBackend(pszBackend, &pImage->Backend, &pImage->hPlugin);
1064 if (VBOX_FAILURE(rc))
1065 break;
1066 if (!pImage->Backend)
1067 {
1068 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1069 N_("VD: unknown backend name '%s'"), pszBackend);
1070 break;
1071 }
1072
1073 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1074 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
1075 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1076 pDisk->pfnError, pDisk->pvErrorUser,
1077 &pImage->pvBackendData);
1078 /* If the open in read-write mode failed, retry in read-only mode. */
1079 if (VBOX_FAILURE(rc))
1080 {
1081 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
1082 && (rc == VERR_ACCESS_DENIED
1083 || rc == VERR_PERMISSION_DENIED
1084 || rc == VERR_WRITE_PROTECT
1085 || rc == VERR_SHARING_VIOLATION
1086 || rc == VERR_FILE_LOCK_FAILED))
1087 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
1088 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
1089 | VD_OPEN_FLAGS_READONLY,
1090 pDisk->pfnError, pDisk->pvErrorUser,
1091 &pImage->pvBackendData);
1092 if (VBOX_FAILURE(rc))
1093 {
1094 rc = vdError(pDisk, rc, RT_SRC_POS,
1095 N_("VD: error opening image file '%s'"), pszFilename);
1096 break;
1097 }
1098 }
1099
1100 VDIMAGETYPE enmImageType;
1101 rc = pImage->Backend->pfnGetImageType(pImage->pvBackendData,
1102 &enmImageType);
1103 /* Check image type. As the image itself has no idea whether it's a
1104 * base image or not, this info is derived here. Image 0 can be fixed
1105 * or normal, all others must be normal images. */
1106 if ( VBOX_SUCCESS(rc)
1107 && !(uOpenFlags & VD_OPEN_FLAGS_INFO)
1108 && pDisk->cImages != 0
1109 && enmImageType != VD_IMAGE_TYPE_NORMAL)
1110 {
1111 rc = VERR_VDI_INVALID_TYPE;
1112 break;
1113 }
1114
1115 /* Force sane optimization settings. It's not worth avoiding writes
1116 * to fixed size images. The overhead would have almost no payback. */
1117 if (enmImageType == VD_IMAGE_TYPE_FIXED)
1118 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
1119
1120 /** @todo optionally check UUIDs */
1121
1122 int rc2;
1123
1124 /* Cache disk information. */
1125 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
1126
1127 /* Cache PCHS geometry. */
1128 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1129 &pDisk->PCHSGeometry);
1130 if (VBOX_FAILURE(rc2))
1131 {
1132 pDisk->PCHSGeometry.cCylinders = 0;
1133 pDisk->PCHSGeometry.cHeads = 0;
1134 pDisk->PCHSGeometry.cSectors = 0;
1135 }
1136 else
1137 {
1138 /* Make sure the PCHS geometry is properly clipped. */
1139 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1140 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1141 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1142 }
1143
1144 /* Cache LCHS geometry. */
1145 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1146 &pDisk->LCHSGeometry);
1147 if (VBOX_FAILURE(rc2))
1148 {
1149 pDisk->LCHSGeometry.cCylinders = 0;
1150 pDisk->LCHSGeometry.cHeads = 0;
1151 pDisk->LCHSGeometry.cSectors = 0;
1152 }
1153 else
1154 {
1155 /* Make sure the LCHS geometry is properly clipped. */
1156 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
1157 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1158 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1159 }
1160
1161 if (pDisk->cImages != 0)
1162 {
1163 /* Switch previous image to read-only mode. */
1164 unsigned uOpenFlagsPrevImg;
1165 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1166 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
1167 {
1168 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
1169 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
1170 }
1171 }
1172
1173 if (VBOX_SUCCESS(rc))
1174 {
1175 /* Image successfully opened, make it the last image. */
1176 vdAddImageToList(pDisk, pImage);
1177 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1178 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1179 }
1180 else
1181 {
1182 /* Error detected, but image opened. Close image. */
1183 int rc2;
1184 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, false);
1185 AssertRC(rc2);
1186 pImage->pvBackendData = NULL;
1187 }
1188 } while (0);
1189
1190 if (VBOX_FAILURE(rc))
1191 {
1192 if (pImage)
1193 {
1194 if (pImage->hPlugin != NIL_RTLDRMOD)
1195 RTLdrClose(pImage->hPlugin);
1196
1197 if (pImage->pszFilename)
1198 RTStrFree(pImage->pszFilename);
1199 RTMemFree(pImage);
1200 }
1201 }
1202
1203 LogFlowFunc(("returns %Vrc\n", rc));
1204 return rc;
1205}
1206
1207/**
1208 * Creates and opens a new base image file.
1209 *
1210 * @returns VBox status code.
1211 * @param pDisk Pointer to HDD container.
1212 * @param pszBackend Name of the image file backend to use.
1213 * @param pszFilename Name of the image file to create.
1214 * @param enmType Image type, only base image types are acceptable.
1215 * @param cbSize Image size in bytes.
1216 * @param uImageFlags Flags specifying special image features.
1217 * @param pszComment Pointer to image comment. NULL is ok.
1218 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
1219 * @param pLCHSGeometry Pointer to logical disk geometry <= (1024,255,63). Not NULL.
1220 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1221 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1222 * @param pvUser User argument for the progress callback.
1223 */
1224VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
1225 const char *pszFilename, VDIMAGETYPE enmType,
1226 uint64_t cbSize, unsigned uImageFlags,
1227 const char *pszComment,
1228 PCPDMMEDIAGEOMETRY pPCHSGeometry,
1229 PCPDMMEDIAGEOMETRY pLCHSGeometry,
1230 unsigned uOpenFlags, PFNVMPROGRESS pfnProgress,
1231 void *pvUser)
1232{
1233 int rc = VINF_SUCCESS;
1234 PVDIMAGE pImage = NULL;
1235
1236 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",
1237 pDisk, pszBackend, pszFilename, enmType, cbSize, uImageFlags, pszComment,
1238 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1239 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
1240 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, uOpenFlags,
1241 pfnProgress, pvUser));
1242 do
1243 {
1244 /* sanity check */
1245 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1246 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1247
1248 /* Check arguments. */
1249 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
1250 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1251 rc = VERR_INVALID_PARAMETER);
1252 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1253 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1254 rc = VERR_INVALID_PARAMETER);
1255 AssertMsgBreakStmt(enmType == VD_IMAGE_TYPE_NORMAL || enmType == VD_IMAGE_TYPE_FIXED,
1256 ("enmType=%#x\n", enmType),
1257 rc = VERR_INVALID_PARAMETER);
1258 AssertMsgBreakStmt(cbSize,
1259 ("cbSize=%llu\n", cbSize),
1260 rc = VERR_INVALID_PARAMETER);
1261 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
1262 ("uImageFlags=%#x\n", uImageFlags),
1263 rc = VERR_INVALID_PARAMETER);
1264 /* The PCHS geometry fields may be 0 to leave it for later. */
1265 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
1266 && pPCHSGeometry->cCylinders <= 16383
1267 && pPCHSGeometry->cHeads <= 16
1268 && pPCHSGeometry->cSectors <= 63,
1269 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
1270 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1271 pPCHSGeometry->cSectors),
1272 rc = VERR_INVALID_PARAMETER);
1273 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
1274 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
1275 && pLCHSGeometry->cCylinders <= 1024
1276 && pLCHSGeometry->cHeads <= 255
1277 && pLCHSGeometry->cSectors <= 63,
1278 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
1279 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
1280 pLCHSGeometry->cSectors),
1281 rc = VERR_INVALID_PARAMETER);
1282 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1283 ("uOpenFlags=%#x\n", uOpenFlags),
1284 rc = VERR_INVALID_PARAMETER);
1285
1286 /* Check state. */
1287 AssertMsgBreakStmt(pDisk->cImages == 0,
1288 ("Create base image cannot be done with other images open\n"),
1289 rc = VERR_VDI_INVALID_STATE);
1290
1291 /* Set up image descriptor. */
1292 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1293 if (!pImage)
1294 {
1295 rc = VERR_NO_MEMORY;
1296 break;
1297 }
1298 pImage->pszFilename = RTStrDup(pszFilename);
1299 if (!pImage->pszFilename)
1300 {
1301 rc = VERR_NO_MEMORY;
1302 break;
1303 }
1304
1305 rc = vdFindBackend(pszBackend, &pImage->Backend, &pImage->hPlugin);
1306 if (VBOX_FAILURE(rc))
1307 break;
1308 if (!pImage->Backend)
1309 {
1310 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1311 N_("VD: unknown backend name '%s'"), pszBackend);
1312 break;
1313 }
1314
1315 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1316 rc = pImage->Backend->pfnCreate(pImage->pszFilename, enmType, cbSize,
1317 uImageFlags, pszComment, pPCHSGeometry,
1318 pLCHSGeometry,
1319 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1320 pfnProgress, pvUser, 0, 99,
1321 pDisk->pfnError, pDisk->pvErrorUser,
1322 &pImage->pvBackendData);
1323
1324 if (VBOX_SUCCESS(rc))
1325 {
1326 /* Force sane optimization settings. It's not worth avoiding writes
1327 * to fixed size images. The overhead would have almost no payback. */
1328 if (enmType == VD_IMAGE_TYPE_FIXED)
1329 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
1330
1331 /** @todo optionally check UUIDs */
1332
1333 int rc2;
1334
1335 /* Cache disk information. */
1336 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
1337
1338 /* Cache PCHS geometry. */
1339 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1340 &pDisk->PCHSGeometry);
1341 if (VBOX_FAILURE(rc2))
1342 {
1343 pDisk->PCHSGeometry.cCylinders = 0;
1344 pDisk->PCHSGeometry.cHeads = 0;
1345 pDisk->PCHSGeometry.cSectors = 0;
1346 }
1347 else
1348 {
1349 /* Make sure the CHS geometry is properly clipped. */
1350 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1351 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1352 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1353 }
1354
1355 /* Cache LCHS geometry. */
1356 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1357 &pDisk->LCHSGeometry);
1358 if (VBOX_FAILURE(rc2))
1359 {
1360 pDisk->LCHSGeometry.cCylinders = 0;
1361 pDisk->LCHSGeometry.cHeads = 0;
1362 pDisk->LCHSGeometry.cSectors = 0;
1363 }
1364 else
1365 {
1366 /* Make sure the CHS geometry is properly clipped. */
1367 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
1368 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1369 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1370 }
1371 }
1372
1373 if (VBOX_SUCCESS(rc))
1374 {
1375 /* Image successfully opened, make it the last image. */
1376 vdAddImageToList(pDisk, pImage);
1377 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1378 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1379 }
1380 else
1381 {
1382 /* Error detected, but image opened. Close and delete image. */
1383 int rc2;
1384 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
1385 AssertRC(rc2);
1386 pImage->pvBackendData = NULL;
1387 }
1388 } while (0);
1389
1390 if (VBOX_FAILURE(rc))
1391 {
1392 if (pImage)
1393 {
1394 if (pImage->hPlugin != NIL_RTLDRMOD)
1395 RTLdrClose(pImage->hPlugin);
1396
1397 if (pImage->pszFilename)
1398 RTStrFree(pImage->pszFilename);
1399 RTMemFree(pImage);
1400 }
1401 }
1402
1403 if (VBOX_SUCCESS(rc) && pfnProgress)
1404 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1405
1406 LogFlowFunc(("returns %Vrc\n", rc));
1407 return rc;
1408}
1409
1410/**
1411 * Creates and opens a new differencing image file in HDD container.
1412 * See comments for VDOpen function about differencing images.
1413 *
1414 * @returns VBox status code.
1415 * @param pDisk Pointer to HDD container.
1416 * @param pszBackend Name of the image file backend to use.
1417 * @param pszFilename Name of the differencing image file to create.
1418 * @param uImageFlags Flags specifying special image features.
1419 * @param pszComment Pointer to image comment. NULL is ok.
1420 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1421 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1422 * @param pvUser User argument for the progress callback.
1423 */
1424VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
1425 const char *pszFilename, unsigned uImageFlags,
1426 const char *pszComment, unsigned uOpenFlags,
1427 PFNVMPROGRESS pfnProgress, void *pvUser)
1428{
1429 int rc = VINF_SUCCESS;
1430 PVDIMAGE pImage = NULL;
1431
1432 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" uOpenFlags=%#x pfnProgress=%#p pvUser=%#p\n",
1433 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, uOpenFlags,
1434 pfnProgress, pvUser));
1435 do
1436 {
1437 /* sanity check */
1438 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1439 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1440
1441 /* Check arguments. */
1442 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
1443 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1444 rc = VERR_INVALID_PARAMETER);
1445 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1446 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1447 rc = VERR_INVALID_PARAMETER);
1448 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
1449 ("uImageFlags=%#x\n", uImageFlags),
1450 rc = VERR_INVALID_PARAMETER);
1451 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1452 ("uOpenFlags=%#x\n", uOpenFlags),
1453 rc = VERR_INVALID_PARAMETER);
1454
1455 /* Check state. */
1456 AssertMsgBreakStmt(pDisk->cImages != 0,
1457 ("Create diff image cannot be done without other images open\n"),
1458 rc = VERR_VDI_INVALID_STATE);
1459
1460 /* Set up image descriptor. */
1461 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1462 if (!pImage)
1463 {
1464 rc = VERR_NO_MEMORY;
1465 break;
1466 }
1467 pImage->pszFilename = RTStrDup(pszFilename);
1468 if (!pImage->pszFilename)
1469 {
1470 rc = VERR_NO_MEMORY;
1471 break;
1472 }
1473
1474 rc = vdFindBackend(pszBackend, &pImage->Backend, &pImage->hPlugin);
1475 if (VBOX_FAILURE(rc))
1476 break;
1477 if (!pImage->Backend)
1478 {
1479 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1480 N_("VD: unknown backend name '%s'"), pszBackend);
1481 break;
1482 }
1483
1484 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1485 rc = pImage->Backend->pfnCreate(pImage->pszFilename,
1486 VD_IMAGE_TYPE_NORMAL, pDisk->cbSize,
1487 uImageFlags, pszComment,
1488 &pDisk->PCHSGeometry,
1489 &pDisk->LCHSGeometry,
1490 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1491 pfnProgress, pvUser, 0, 99,
1492 pDisk->pfnError, pDisk->pvErrorUser,
1493 &pImage->pvBackendData);
1494
1495 if (VBOX_SUCCESS(rc) && pDisk->cImages != 0)
1496 {
1497 /* Switch previous image to read-only mode. */
1498 unsigned uOpenFlagsPrevImg;
1499 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1500 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
1501 {
1502 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
1503 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
1504 }
1505 }
1506
1507 if (VBOX_SUCCESS(rc))
1508 {
1509 RTUUID Uuid;
1510 int rc2;
1511
1512 rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pvBackendData,
1513 &Uuid);
1514 if (VBOX_SUCCESS(rc2))
1515 pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, &Uuid);
1516 rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pvBackendData,
1517 &Uuid);
1518 if (VBOX_SUCCESS(rc2))
1519 pImage->Backend->pfnSetParentModificationUuid(pImage->pvBackendData,
1520 &Uuid);
1521 }
1522
1523 if (VBOX_SUCCESS(rc))
1524 {
1525 /** @todo optionally check UUIDs */
1526 }
1527
1528 if (VBOX_SUCCESS(rc))
1529 {
1530 /* Image successfully opened, make it the last image. */
1531 vdAddImageToList(pDisk, pImage);
1532 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1533 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1534 }
1535 else
1536 {
1537 /* Error detected, but image opened. Close and delete image. */
1538 int rc2;
1539 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
1540 AssertRC(rc2);
1541 pImage->pvBackendData = NULL;
1542 }
1543 } while (0);
1544
1545 if (VBOX_FAILURE(rc))
1546 {
1547 if (pImage->hPlugin != NIL_RTLDRMOD)
1548 RTLdrClose(pImage->hPlugin);
1549
1550 if (pImage)
1551 {
1552 if (pImage->pszFilename)
1553 RTStrFree(pImage->pszFilename);
1554 RTMemFree(pImage);
1555 }
1556 }
1557
1558 if (VBOX_SUCCESS(rc) && pfnProgress)
1559 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1560
1561 LogFlowFunc(("returns %Vrc\n", rc));
1562 return rc;
1563}
1564
1565/**
1566 * Merges two images (not necessarily with direct parent/child relationship).
1567 * As a side effect the source image and potentially the other images which
1568 * are also merged to the destination are deleted from both the disk and the
1569 * images in the HDD container.
1570 *
1571 * @returns VBox status code.
1572 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1573 * @param pDisk Pointer to HDD container.
1574 * @param nImageFrom Name of the image file to merge from.
1575 * @param nImageTo Name of the image file to merge to.
1576 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1577 * @param pvUser User argument for the progress callback.
1578 */
1579VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
1580 unsigned nImageTo, PFNVMPROGRESS pfnProgress,
1581 void *pvUser)
1582{
1583 int rc = VINF_SUCCESS;
1584 void *pvBuf = NULL;
1585
1586 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pfnProgress=%#p pvUser=%#p\n",
1587 pDisk, nImageFrom, nImageTo, pfnProgress, pvUser));
1588 do
1589 {
1590 /* sanity check */
1591 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1592 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1593
1594 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
1595 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
1596 if (!pImageFrom || !pImageTo)
1597 {
1598 rc = VERR_VDI_IMAGE_NOT_FOUND;
1599 break;
1600 }
1601 AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
1602
1603 /* Make sure destination image is writable. */
1604 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
1605 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
1606 {
1607 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
1608 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pvBackendData,
1609 uOpenFlags);
1610 if (VBOX_FAILURE(rc))
1611 break;
1612 }
1613
1614 /* Get size of destination image. */
1615 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pvBackendData);
1616
1617 /* Allocate tmp buffer. */
1618 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
1619 if (!pvBuf)
1620 {
1621 rc = VERR_NO_MEMORY;
1622 break;
1623 }
1624
1625 /* Merging is done directly on the images itself. This potentially
1626 * causes trouble if the disk is full in the middle of operation. */
1627 /** @todo write alternative implementation which works with temporary
1628 * images (which is safer, but requires even more space). Also has the
1629 * drawback that merging into a raw disk parent simply isn't possible
1630 * this way (but in that case disk full isn't really a problem). */
1631 if (nImageFrom < nImageTo)
1632 {
1633 /* Merge parent state into child. This means writing all not
1634 * allocated blocks in the destination image which are allocated in
1635 * the images to be merged. */
1636 uint64_t uOffset = 0;
1637 uint64_t cbRemaining = cbSize;
1638 do
1639 {
1640 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1641 rc = pImageTo->Backend->pfnRead(pImageTo->pvBackendData,
1642 uOffset, pvBuf, cbThisRead,
1643 &cbThisRead);
1644 if (rc == VERR_VDI_BLOCK_FREE)
1645 {
1646 /* Search for image with allocated block. Do not attempt to
1647 * read more than the previous reads marked as valid.
1648 * Otherwise this would return stale data when different
1649 * block sizes are used for the images. */
1650 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
1651 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VDI_BLOCK_FREE;
1652 pCurrImage = pCurrImage->pPrev)
1653 {
1654 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
1655 uOffset, pvBuf,
1656 cbThisRead,
1657 &cbThisRead);
1658 }
1659
1660 if (rc != VERR_VDI_BLOCK_FREE)
1661 {
1662 if (VBOX_FAILURE(rc))
1663 break;
1664 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1665 cbThisRead);
1666 if (VBOX_FAILURE(rc))
1667 break;
1668 }
1669 else
1670 rc = VINF_SUCCESS;
1671 }
1672 else if (VBOX_FAILURE(rc))
1673 break;
1674
1675 uOffset += cbThisRead;
1676 cbRemaining -= cbThisRead;
1677 } while (uOffset < cbSize);
1678 }
1679 else
1680 {
1681 /* Merge child state into parent. This means writing all blocks
1682 * which are allocated in the image up to the source image to the
1683 * destination image. */
1684 uint64_t uOffset = 0;
1685 uint64_t cbRemaining = cbSize;
1686 do
1687 {
1688 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1689 rc = VERR_VDI_BLOCK_FREE;
1690 /* Search for image with allocated block. Do not attempt to
1691 * read more than the previous reads marked as valid. Otherwise
1692 * this would return stale data when different block sizes are
1693 * used for the images. */
1694 for (PVDIMAGE pCurrImage = pImageFrom;
1695 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VDI_BLOCK_FREE;
1696 pCurrImage = pCurrImage->pPrev)
1697 {
1698 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
1699 uOffset, pvBuf,
1700 cbThisRead, &cbThisRead);
1701 }
1702
1703 if (rc != VERR_VDI_BLOCK_FREE)
1704 {
1705 if (VBOX_FAILURE(rc))
1706 break;
1707 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1708 cbThisRead);
1709 if (VBOX_FAILURE(rc))
1710 break;
1711 }
1712 else
1713 rc = VINF_SUCCESS;
1714
1715 uOffset += cbThisRead;
1716 cbRemaining -= cbThisRead;
1717 } while (uOffset < cbSize);
1718 }
1719
1720 /* Update parent UUID so that image chain is consistent. */
1721 RTUUID Uuid;
1722 if (nImageFrom < nImageTo)
1723 {
1724 if (pImageTo->pPrev)
1725 {
1726 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pPrev->pvBackendData,
1727 &Uuid);
1728 AssertRC(rc);
1729 }
1730 else
1731 RTUuidClear(&Uuid);
1732 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pvBackendData,
1733 &Uuid);
1734 AssertRC(rc);
1735 }
1736 else
1737 {
1738 if (pImageFrom->pNext)
1739 {
1740 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pvBackendData,
1741 &Uuid);
1742 AssertRC(rc);
1743 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext,
1744 &Uuid);
1745 AssertRC(rc);
1746 }
1747 }
1748
1749 /* Make sure destination image is back to read only if necessary. */
1750 if (pImageTo != pDisk->pLast && pImageFrom != pDisk->pLast)
1751 {
1752 uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
1753 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
1754 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pvBackendData,
1755 uOpenFlags);
1756 if (VBOX_FAILURE(rc))
1757 break;
1758 }
1759
1760 /* Delete the no longer needed images. */
1761 PVDIMAGE pImg = pImageFrom, pTmp;
1762 while (pImg != pImageTo)
1763 {
1764 if (nImageFrom < nImageTo)
1765 pTmp = pImg->pNext;
1766 else
1767 pTmp = pImg->pPrev;
1768 vdRemoveImageFromList(pDisk, pImg);
1769 pImg->Backend->pfnClose(pImg->pvBackendData, true);
1770 pImg = pTmp;
1771 }
1772 } while (0);
1773
1774 if (pvBuf)
1775 RTMemTmpFree(pvBuf);
1776
1777 if (VBOX_SUCCESS(rc) && pfnProgress)
1778 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1779
1780 LogFlowFunc(("returns %Vrc\n", rc));
1781 return rc;
1782}
1783
1784/**
1785 * Copies an image from one HDD container to another.
1786 * The copy is opened in the target HDD container.
1787 * It is possible to convert between different image formats, because the
1788 * backend for the destination may be different from the source.
1789 * If both the source and destination reference the same HDD container,
1790 * then the image is moved (by copying/deleting or renaming) to the new location.
1791 * The source container is unchanged if the move operation fails, otherwise
1792 * the image at the new location is opened in the same way as the old one was.
1793 *
1794 * @returns VBox status code.
1795 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1796 * @param pDiskFrom Pointer to source HDD container.
1797 * @param nImage Image number, counts from 0. 0 is always base image of container.
1798 * @param pDiskTo Pointer to destination HDD container.
1799 * @param pszBackend Name of the image file backend to use.
1800 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
1801 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
1802 * @param cbSize New image size (0 means leave unchanged).
1803 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1804 * @param pvUser User argument for the progress callback.
1805 */
1806VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
1807 const char *pszBackend, const char *pszFilename,
1808 bool fMoveByRename, uint64_t cbSize,
1809 PFNVMPROGRESS pfnProgress, void *pvUser)
1810{
1811 int rc, rc2 = VINF_SUCCESS;
1812 void *pvBuf = NULL;
1813 PVDIMAGE pImageTo = NULL;
1814
1815 LogFlowFunc(("pDiskFrom=%#p nImage=%u pDiskTo=%#p pszBackend=\"%s\" pszFilename=\"%s\" fMoveByRename=%d cbSize=%llu pfnProgress=%#p pvUser=%#p\n",
1816 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, pfnProgress, pvUser));
1817
1818 do {
1819 /* Check arguments. */
1820 AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
1821 rc = VERR_INVALID_PARAMETER);
1822 AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
1823 ("u32Signature=%08x\n", pDiskFrom->u32Signature));
1824
1825 PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
1826 AssertPtrBreakStmt(pImageFrom, rc = VERR_VDI_IMAGE_NOT_FOUND);
1827 AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
1828 rc = VERR_INVALID_PARAMETER);
1829 AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
1830 ("u32Signature=%08x\n", pDiskTo->u32Signature));
1831
1832 /* If the containers are equal and the backend is the same, rename the image. */
1833 if ( (pDiskFrom == pDiskTo)
1834 && (!strcmp(pszBackend, pImageFrom->Backend->pszBackendName)))
1835 {
1836 /* Rename the image. */
1837 rc = pImageFrom->Backend->pfnRename(pImageFrom->pvBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
1838 break;
1839 }
1840
1841 /* If the fMoveByRename flag is set and the backend is the same, rename the image. */
1842 if ( (fMoveByRename == true)
1843 && (!strcmp(pszBackend, pImageFrom->Backend->pszBackendName)))
1844 {
1845 /* Close the source image. */
1846 rc = pImageFrom->Backend->pfnClose(pImageFrom->pvBackendData, false);
1847 if (VBOX_FAILURE(rc))
1848 break;
1849
1850 /* Open the source image in the destination container. */
1851 rc = VDOpen(pDiskTo, pImageFrom->Backend->pszBackendName, pImageFrom->pszFilename, pImageFrom->uOpenFlags);
1852 if (VBOX_FAILURE(rc))
1853 goto movefail;
1854
1855 pImageTo = pDiskTo->pLast;
1856
1857 /* Rename the image. */
1858 rc = pImageTo->Backend->pfnRename(pImageTo->pvBackendData, pszFilename ? pszFilename : pImageTo->pszFilename);
1859 if (VBOX_FAILURE(rc))
1860 goto movefail;
1861
1862 /* Cleanup the leftovers. */
1863 vdRemoveImageFromList(pDiskFrom, pImageFrom);
1864 pImageFrom->pvBackendData = NULL;
1865
1866 if (pImageFrom->hPlugin != NIL_RTLDRMOD)
1867 RTLdrClose(pImageFrom->hPlugin);
1868
1869 if (pImageFrom->pszFilename)
1870 RTStrFree(pImageFrom->pszFilename);
1871
1872 RTMemFree(pImageFrom);
1873
1874 break;
1875movefail:
1876 /* In case of failure, re-open the source image in the source container. */
1877 rc2 = VDOpen(pDiskFrom, pImageFrom->Backend->pszBackendName, pImageFrom->pszFilename, pImageFrom->uOpenFlags);
1878 if (VBOX_FAILURE(rc2))
1879 /* @todo Uncertain what to do on error. If this happens pImageFrom and pImageTo are both closed. */
1880 rc = rc2;
1881 break;
1882 }
1883
1884 /* If fMoveByRename is set pszFilename is allowed to be NULL, so do the parameter check here. */
1885 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1886 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1887 rc = VERR_INVALID_PARAMETER);
1888
1889 /* Collect properties of source image. */
1890 VDIMAGETYPE enmTypeFrom;
1891 rc = VDGetImageType(pDiskFrom, nImage, &enmTypeFrom);
1892 if (VBOX_FAILURE(rc))
1893 break;
1894
1895 uint64_t cbSizeFrom = VDGetSize(pDiskFrom, nImage);
1896 if (cbSizeFrom == 0)
1897 {
1898 rc = VERR_VDI_VALUE_NOT_FOUND;
1899 break;
1900 }
1901
1902 if (cbSize == 0)
1903 cbSize = cbSizeFrom;
1904
1905 unsigned uImageFlagsFrom;
1906 rc = VDGetImageFlags(pDiskFrom, nImage, &uImageFlagsFrom);
1907 if (VBOX_FAILURE(rc))
1908 break;
1909
1910 /* @todo Get this from the source image. */
1911 PDMMEDIAGEOMETRY PCHSGeometryFrom = {0, 0, 0};
1912 PDMMEDIAGEOMETRY LCHSGeometryFrom = {0, 0, 0};
1913
1914 unsigned uOpenFlagsFrom;
1915 rc = VDGetOpenFlags(pDiskFrom, nImage, &uOpenFlagsFrom);
1916 if (VBOX_FAILURE(rc))
1917 break;
1918
1919 /* Create destination image with the properties of the source image. */
1920 /* @todo Copy the comment. */
1921 if (enmTypeFrom == VD_IMAGE_TYPE_DIFF)
1922 {
1923 rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename, uImageFlagsFrom,
1924 "", uOpenFlagsFrom, NULL, NULL);
1925 } else {
1926 rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, enmTypeFrom,
1927 cbSize, uImageFlagsFrom, "",
1928 &PCHSGeometryFrom, &LCHSGeometryFrom,
1929 uOpenFlagsFrom, NULL, NULL);
1930 }
1931 if (VBOX_FAILURE(rc))
1932 break;
1933
1934 pImageTo = pDiskTo->pLast;
1935 AssertPtrBreakStmt(pImageTo, rc = VERR_VDI_IMAGE_NOT_FOUND);
1936
1937 /* Allocate tmp buffer. */
1938 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
1939 if (!pvBuf)
1940 {
1941 rc = VERR_NO_MEMORY;
1942 break;
1943 }
1944
1945 /* Copy the data. */
1946 uint64_t uOffset = 0;
1947 uint64_t cbRemaining = cbSize;
1948
1949 do
1950 {
1951 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1952
1953 rc = vdReadHelper(pDiskFrom, pImageFrom, uOffset, pvBuf,
1954 cbThisRead);
1955 if (VBOX_FAILURE(rc))
1956 break;
1957
1958 rc = vdWriteHelper(pDiskTo, pImageTo, uOffset, pvBuf,
1959 cbThisRead);
1960 if (VBOX_FAILURE(rc))
1961 break;
1962
1963 uOffset += cbThisRead;
1964 cbRemaining -= cbThisRead;
1965 if (pfnProgress)
1966 {
1967 rc = pfnProgress(NULL /* WARNING! pVM=NULL */,
1968 ((cbSize - cbRemaining) * 100) / cbSize,
1969 pvUser);
1970 if (VBOX_FAILURE(rc))
1971 break;
1972 }
1973 } while (uOffset < cbSize);
1974
1975 /* If fMoveByRename is set but the backend is different, close and delete pImageFrom. */
1976 if ( (fMoveByRename == true)
1977 && (strcmp(pszBackend, pImageFrom->Backend->pszBackendName)))
1978 {
1979 vdRemoveImageFromList(pDiskFrom, pImageFrom);
1980
1981 /* Close and delete image. */
1982 rc2 = pImageFrom->Backend->pfnClose(pImageFrom->pvBackendData, true);
1983 AssertRC(rc2);
1984 pImageFrom->pvBackendData = NULL;
1985
1986 /* Free remaining resources. */
1987 if (pImageFrom->hPlugin != NIL_RTLDRMOD)
1988 RTLdrClose(pImageFrom->hPlugin);
1989
1990 if (pImageFrom->pszFilename)
1991 RTStrFree(pImageFrom->pszFilename);
1992
1993 RTMemFree(pImageFrom);
1994 }
1995 } while (0);
1996
1997 if (VBOX_FAILURE(rc) && pImageTo)
1998 {
1999 /* Error detected, but new image created. Remove image from list. */
2000 vdRemoveImageFromList(pDiskTo, pImageTo);
2001
2002 /* Close and delete image. */
2003 rc2 = pImageTo->Backend->pfnClose(pImageTo->pvBackendData, true);
2004 AssertRC(rc2);
2005 pImageTo->pvBackendData = NULL;
2006
2007 /* Free remaining resources. */
2008 if (pImageTo->hPlugin != NIL_RTLDRMOD)
2009 RTLdrClose(pImageTo->hPlugin);
2010
2011 if (pImageTo->pszFilename)
2012 RTStrFree(pImageTo->pszFilename);
2013
2014 RTMemFree(pImageTo);
2015 }
2016
2017 if (pvBuf)
2018 RTMemTmpFree(pvBuf);
2019
2020 if (VBOX_SUCCESS(rc) && pfnProgress)
2021 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
2022
2023 LogFlowFunc(("returns %Vrc\n", rc));
2024 return rc;
2025}
2026
2027/**
2028 * Closes the last opened image file in HDD container.
2029 * If previous image file was opened in read-only mode (that is normal) and closing image
2030 * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
2031 * will be reopened in read/write mode.
2032 *
2033 * @returns VBox status code.
2034 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2035 * @param pDisk Pointer to HDD container.
2036 * @param fDelete If true, delete the image from the host disk.
2037 */
2038VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
2039{
2040 int rc = VINF_SUCCESS;;
2041
2042 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
2043 do
2044 {
2045 /* sanity check */
2046 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2047 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2048
2049 PVDIMAGE pImage = pDisk->pLast;
2050 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2051 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2052 /* Remove image from list of opened images. */
2053 vdRemoveImageFromList(pDisk, pImage);
2054 /* Close (and optionally delete) image. */
2055 rc = pImage->Backend->pfnClose(pImage->pvBackendData, fDelete);
2056 /* Free remaining resources related to the image. */
2057 if (pImage->hPlugin != NIL_RTLDRMOD)
2058 {
2059 RTLdrClose(pImage->hPlugin);
2060 pImage->hPlugin = NIL_RTLDRMOD;
2061 }
2062 RTStrFree(pImage->pszFilename);
2063 RTMemFree(pImage);
2064
2065 pImage = pDisk->pLast;
2066 if (!pImage)
2067 break;
2068
2069 /* If disk was previously in read/write mode, make sure it will stay
2070 * like this (if possible) after closing this image. Set the open flags
2071 * accordingly. */
2072 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
2073 {
2074 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2075 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
2076 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData, uOpenFlags);
2077 }
2078
2079 int rc2;
2080
2081 /* Cache disk information. */
2082 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
2083
2084 /* Cache PCHS geometry. */
2085 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2086 &pDisk->PCHSGeometry);
2087 if (VBOX_FAILURE(rc2))
2088 {
2089 pDisk->PCHSGeometry.cCylinders = 0;
2090 pDisk->PCHSGeometry.cHeads = 0;
2091 pDisk->PCHSGeometry.cSectors = 0;
2092 }
2093 else
2094 {
2095 /* Make sure the PCHS geometry is properly clipped. */
2096 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
2097 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
2098 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
2099 }
2100
2101 /* Cache LCHS geometry. */
2102 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2103 &pDisk->LCHSGeometry);
2104 if (VBOX_FAILURE(rc2))
2105 {
2106 pDisk->LCHSGeometry.cCylinders = 0;
2107 pDisk->LCHSGeometry.cHeads = 0;
2108 pDisk->LCHSGeometry.cSectors = 0;
2109 }
2110 else
2111 {
2112 /* Make sure the LCHS geometry is properly clipped. */
2113 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
2114 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
2115 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
2116 }
2117 } while (0);
2118
2119 LogFlowFunc(("returns %Vrc\n", rc));
2120 return rc;
2121}
2122
2123/**
2124 * Closes all opened image files in HDD container.
2125 *
2126 * @returns VBox status code.
2127 * @param pDisk Pointer to HDD container.
2128 */
2129VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
2130{
2131 int rc = VINF_SUCCESS;
2132
2133 LogFlowFunc(("pDisk=%#p\n", pDisk));
2134 do
2135 {
2136 /* sanity check */
2137 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2138 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2139
2140 PVDIMAGE pImage = pDisk->pLast;
2141 while (VALID_PTR(pImage))
2142 {
2143 PVDIMAGE pPrev = pImage->pPrev;
2144 /* Remove image from list of opened images. */
2145 vdRemoveImageFromList(pDisk, pImage);
2146 /* Close image. */
2147 int rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, false);
2148 if (VBOX_FAILURE(rc2) && VBOX_SUCCESS(rc))
2149 rc = rc2;
2150 /* Free remaining resources related to the image. */
2151 if (pImage->hPlugin != NIL_RTLDRMOD)
2152 {
2153 RTLdrClose(pImage->hPlugin);
2154 pImage->hPlugin = NIL_RTLDRMOD;
2155 }
2156 RTStrFree(pImage->pszFilename);
2157 RTMemFree(pImage);
2158 pImage = pPrev;
2159 }
2160 Assert(!VALID_PTR(pDisk->pLast));
2161 } while (0);
2162
2163 LogFlowFunc(("returns %Vrc\n", rc));
2164 return rc;
2165}
2166
2167/**
2168 * Read data from virtual HDD.
2169 *
2170 * @returns VBox status code.
2171 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2172 * @param pDisk Pointer to HDD container.
2173 * @param uOffset Offset of first reading byte from start of disk.
2174 * @param pvBuf Pointer to buffer for reading data.
2175 * @param cbRead Number of bytes to read.
2176 */
2177VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
2178 size_t cbRead)
2179{
2180 int rc;
2181
2182 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
2183 pDisk, uOffset, pvBuf, cbRead));
2184 do
2185 {
2186 /* sanity check */
2187 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2188 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2189
2190 /* Check arguments. */
2191 AssertMsgBreakStmt(VALID_PTR(pvBuf),
2192 ("pvBuf=%#p\n", pvBuf),
2193 rc = VERR_INVALID_PARAMETER);
2194 AssertMsgBreakStmt(cbRead,
2195 ("cbRead=%zu\n", cbRead),
2196 rc = VERR_INVALID_PARAMETER);
2197 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
2198 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
2199 uOffset, cbRead, pDisk->cbSize),
2200 rc = VERR_INVALID_PARAMETER);
2201
2202 PVDIMAGE pImage = pDisk->pLast;
2203 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2204
2205 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead);
2206 } while (0);
2207
2208 LogFlowFunc(("returns %Vrc\n", rc));
2209 return rc;
2210}
2211
2212/**
2213 * Write data to virtual HDD.
2214 *
2215 * @returns VBox status code.
2216 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2217 * @param pDisk Pointer to HDD container.
2218 * @param uOffset Offset of first reading byte from start of disk.
2219 * @param pvBuf Pointer to buffer for writing data.
2220 * @param cbWrite Number of bytes to write.
2221 */
2222VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
2223 size_t cbWrite)
2224{
2225 int rc = VINF_SUCCESS;
2226
2227 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
2228 pDisk, uOffset, pvBuf, cbWrite));
2229 do
2230 {
2231 /* sanity check */
2232 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2233 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2234
2235 /* Check arguments. */
2236 AssertMsgBreakStmt(VALID_PTR(pvBuf),
2237 ("pvBuf=%#p\n", pvBuf),
2238 rc = VERR_INVALID_PARAMETER);
2239 AssertMsgBreakStmt(cbWrite,
2240 ("cbWrite=%zu\n", cbWrite),
2241 rc = VERR_INVALID_PARAMETER);
2242 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
2243 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
2244 uOffset, cbWrite, pDisk->cbSize),
2245 rc = VERR_INVALID_PARAMETER);
2246
2247 PVDIMAGE pImage = pDisk->pLast;
2248 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2249
2250 vdSetModifiedFlag(pDisk);
2251 rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite);
2252 } while (0);
2253
2254 LogFlowFunc(("returns %Vrc\n", rc));
2255 return rc;
2256}
2257
2258/**
2259 * Make sure the on disk representation of a virtual HDD is up to date.
2260 *
2261 * @returns VBox status code.
2262 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2263 * @param pDisk Pointer to HDD container.
2264 */
2265VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
2266{
2267 int rc = VINF_SUCCESS;
2268
2269 LogFlowFunc(("pDisk=%#p\n", pDisk));
2270 do
2271 {
2272 /* sanity check */
2273 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2274 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2275
2276 PVDIMAGE pImage = pDisk->pLast;
2277 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2278
2279 vdResetModifiedFlag(pDisk);
2280 rc = pImage->Backend->pfnFlush(pImage->pvBackendData);
2281 } while (0);
2282
2283 LogFlowFunc(("returns %Vrc\n", rc));
2284 return rc;
2285}
2286
2287/**
2288 * Get number of opened images in HDD container.
2289 *
2290 * @returns Number of opened images for HDD container. 0 if no images have been opened.
2291 * @param pDisk Pointer to HDD container.
2292 */
2293VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
2294{
2295 unsigned cImages;
2296
2297 LogFlowFunc(("pDisk=%#p\n", pDisk));
2298 do
2299 {
2300 /* sanity check */
2301 AssertPtrBreakStmt(pDisk, cImages = 0);
2302 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2303
2304 cImages = pDisk->cImages;
2305 } while (0);
2306
2307 LogFlowFunc(("returns %u\n", cImages));
2308 return cImages;
2309}
2310
2311/**
2312 * Get read/write mode of HDD container.
2313 *
2314 * @returns Virtual disk ReadOnly status.
2315 * @returns true if no image is opened in HDD container.
2316 * @param pDisk Pointer to HDD container.
2317 */
2318VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
2319{
2320 bool fReadOnly;
2321
2322 LogFlowFunc(("pDisk=%#p\n", pDisk));
2323 do
2324 {
2325 /* sanity check */
2326 AssertPtrBreakStmt(pDisk, fReadOnly = false);
2327 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2328
2329 PVDIMAGE pImage = pDisk->pLast;
2330 AssertPtrBreakStmt(pImage, fReadOnly = true);
2331
2332 unsigned uOpenFlags;
2333 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
2334 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
2335 } while (0);
2336
2337 LogFlowFunc(("returns %d\n", fReadOnly));
2338 return fReadOnly;
2339}
2340
2341/**
2342 * Get total capacity of an image in HDD container.
2343 *
2344 * @returns Virtual disk size in bytes.
2345 * @returns 0 if no image with specified number was not opened.
2346 * @param pDisk Pointer to HDD container.
2347 * @param nImage Image number, counds from 0. 0 is always base image of container.
2348 */
2349VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
2350{
2351 uint64_t cbSize;
2352
2353 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
2354 do
2355 {
2356 /* sanity check */
2357 AssertPtrBreakStmt(pDisk, cbSize = 0);
2358 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2359
2360 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2361 AssertPtrBreakStmt(pImage, cbSize = 0);
2362 cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
2363 } while (0);
2364
2365 LogFlowFunc(("returns %llu\n", cbSize));
2366 return cbSize;
2367}
2368
2369/**
2370 * Get total file size of an image in HDD container.
2371 *
2372 * @returns Virtual disk size in bytes.
2373 * @returns 0 if no image is opened in HDD container.
2374 * @param pDisk Pointer to HDD container.
2375 * @param nImage Image number, counts from 0. 0 is always base image of container.
2376 */
2377VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
2378{
2379 uint64_t cbSize;
2380
2381 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
2382 do
2383 {
2384 /* sanity check */
2385 AssertPtrBreakStmt(pDisk, cbSize = 0);
2386 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2387
2388 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2389 AssertPtrBreakStmt(pImage, cbSize = 0);
2390 cbSize = pImage->Backend->pfnGetFileSize(pImage->pvBackendData);
2391 } while (0);
2392
2393 LogFlowFunc(("returns %llu\n", cbSize));
2394 return cbSize;
2395}
2396
2397/**
2398 * Get virtual disk PCHS geometry stored in HDD container.
2399 *
2400 * @returns VBox status code.
2401 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2402 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2403 * @param pDisk Pointer to HDD container.
2404 * @param nImage Image number, counts from 0. 0 is always base image of container.
2405 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
2406 */
2407VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2408 PPDMMEDIAGEOMETRY pPCHSGeometry)
2409{
2410 int rc = VINF_SUCCESS;
2411
2412 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
2413 pDisk, nImage, pPCHSGeometry));
2414 do
2415 {
2416 /* sanity check */
2417 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2418 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2419
2420 /* Check arguments. */
2421 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
2422 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
2423 rc = VERR_INVALID_PARAMETER);
2424
2425 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2426 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2427
2428 if (pImage == pDisk->pLast)
2429 {
2430 /* Use cached information if possible. */
2431 if (pDisk->PCHSGeometry.cCylinders != 0)
2432 *pPCHSGeometry = pDisk->PCHSGeometry;
2433 else
2434 rc = VERR_VDI_GEOMETRY_NOT_SET;
2435 }
2436 else
2437 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2438 pPCHSGeometry);
2439 } while (0);
2440
2441 LogFlowFunc(("%s: %Vrc (PCHS=%u/%u/%u)\n", rc,
2442 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
2443 pDisk->PCHSGeometry.cSectors));
2444 return rc;
2445}
2446
2447/**
2448 * Store virtual disk PCHS geometry in HDD container.
2449 *
2450 * Note that in case of unrecoverable error all images in HDD container will be closed.
2451 *
2452 * @returns VBox status code.
2453 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2454 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2455 * @param pDisk Pointer to HDD container.
2456 * @param nImage Image number, counts from 0. 0 is always base image of container.
2457 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
2458 */
2459VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2460 PCPDMMEDIAGEOMETRY pPCHSGeometry)
2461{
2462 int rc = VINF_SUCCESS;
2463
2464 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
2465 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
2466 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
2467 do
2468 {
2469 /* sanity check */
2470 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2471 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2472
2473 /* Check arguments. */
2474 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
2475 && pPCHSGeometry->cCylinders <= 16383
2476 && pPCHSGeometry->cHeads <= 16
2477 && pPCHSGeometry->cSectors <= 63,
2478 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
2479 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
2480 pPCHSGeometry->cSectors),
2481 rc = VERR_INVALID_PARAMETER);
2482
2483 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2484 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2485
2486 if (pImage == pDisk->pLast)
2487 {
2488 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
2489 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
2490 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
2491 {
2492 /* Only update geometry if it is changed. Avoids similar checks
2493 * in every backend. Most of the time the new geometry is set
2494 * to the previous values, so no need to go through the hassle
2495 * of updating an image which could be opened in read-only mode
2496 * right now. */
2497 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2498 pPCHSGeometry);
2499
2500 /* Cache new geometry values in any case. */
2501 int rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2502 &pDisk->PCHSGeometry);
2503 if (VBOX_FAILURE(rc2))
2504 {
2505 pDisk->PCHSGeometry.cCylinders = 0;
2506 pDisk->PCHSGeometry.cHeads = 0;
2507 pDisk->PCHSGeometry.cSectors = 0;
2508 }
2509 else
2510 {
2511 /* Make sure the CHS geometry is properly clipped. */
2512 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 1024);
2513 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
2514 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
2515 }
2516 }
2517 }
2518 else
2519 {
2520 PDMMEDIAGEOMETRY PCHS;
2521 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2522 &PCHS);
2523 if ( VBOX_FAILURE(rc)
2524 || pPCHSGeometry->cCylinders != PCHS.cCylinders
2525 || pPCHSGeometry->cHeads != PCHS.cHeads
2526 || pPCHSGeometry->cSectors != PCHS.cSectors)
2527 {
2528 /* Only update geometry if it is changed. Avoids similar checks
2529 * in every backend. Most of the time the new geometry is set
2530 * to the previous values, so no need to go through the hassle
2531 * of updating an image which could be opened in read-only mode
2532 * right now. */
2533 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2534 pPCHSGeometry);
2535 }
2536 }
2537 } while (0);
2538
2539 LogFlowFunc(("returns %Vrc\n", rc));
2540 return rc;
2541}
2542
2543/**
2544 * Get virtual disk LCHS geometry stored in HDD container.
2545 *
2546 * @returns VBox status code.
2547 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2548 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2549 * @param pDisk Pointer to HDD container.
2550 * @param nImage Image number, counts from 0. 0 is always base image of container.
2551 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
2552 */
2553VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2554 PPDMMEDIAGEOMETRY pLCHSGeometry)
2555{
2556 int rc = VINF_SUCCESS;
2557
2558 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
2559 pDisk, nImage, pLCHSGeometry));
2560 do
2561 {
2562 /* sanity check */
2563 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2564 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2565
2566 /* Check arguments. */
2567 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
2568 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
2569 rc = VERR_INVALID_PARAMETER);
2570
2571 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2572 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2573
2574 if (pImage == pDisk->pLast)
2575 {
2576 /* Use cached information if possible. */
2577 if (pDisk->LCHSGeometry.cCylinders != 0)
2578 *pLCHSGeometry = pDisk->LCHSGeometry;
2579 else
2580 rc = VERR_VDI_GEOMETRY_NOT_SET;
2581 }
2582 else
2583 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2584 pLCHSGeometry);
2585 } while (0);
2586
2587 LogFlowFunc(("%s: %Vrc (LCHS=%u/%u/%u)\n", rc,
2588 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
2589 pDisk->LCHSGeometry.cSectors));
2590 return rc;
2591}
2592
2593/**
2594 * Store virtual disk LCHS geometry in HDD container.
2595 *
2596 * Note that in case of unrecoverable error all images in HDD container will be closed.
2597 *
2598 * @returns VBox status code.
2599 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2600 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2601 * @param pDisk Pointer to HDD container.
2602 * @param nImage Image number, counts from 0. 0 is always base image of container.
2603 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
2604 */
2605VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2606 PCPDMMEDIAGEOMETRY pLCHSGeometry)
2607{
2608 int rc = VINF_SUCCESS;
2609
2610 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
2611 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
2612 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2613 do
2614 {
2615 /* sanity check */
2616 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2617 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2618
2619 /* Check arguments. */
2620 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
2621 && pLCHSGeometry->cCylinders <= 1024
2622 && pLCHSGeometry->cHeads <= 255
2623 && pLCHSGeometry->cSectors <= 63,
2624 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
2625 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
2626 pLCHSGeometry->cSectors),
2627 rc = VERR_INVALID_PARAMETER);
2628
2629 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2630 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2631
2632 if (pImage == pDisk->pLast)
2633 {
2634 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
2635 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
2636 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
2637 {
2638 /* Only update geometry if it is changed. Avoids similar checks
2639 * in every backend. Most of the time the new geometry is set
2640 * to the previous values, so no need to go through the hassle
2641 * of updating an image which could be opened in read-only mode
2642 * right now. */
2643 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2644 pLCHSGeometry);
2645
2646 /* Cache new geometry values in any case. */
2647 int rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2648 &pDisk->LCHSGeometry);
2649 if (VBOX_FAILURE(rc2))
2650 {
2651 pDisk->LCHSGeometry.cCylinders = 0;
2652 pDisk->LCHSGeometry.cHeads = 0;
2653 pDisk->LCHSGeometry.cSectors = 0;
2654 }
2655 else
2656 {
2657 /* Make sure the CHS geometry is properly clipped. */
2658 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
2659 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
2660 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
2661 }
2662 }
2663 }
2664 else
2665 {
2666 PDMMEDIAGEOMETRY LCHS;
2667 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2668 &LCHS);
2669 if ( VBOX_FAILURE(rc)
2670 || pLCHSGeometry->cCylinders != LCHS.cCylinders
2671 || pLCHSGeometry->cHeads != LCHS.cHeads
2672 || pLCHSGeometry->cSectors != LCHS.cSectors)
2673 {
2674 /* Only update geometry if it is changed. Avoids similar checks
2675 * in every backend. Most of the time the new geometry is set
2676 * to the previous values, so no need to go through the hassle
2677 * of updating an image which could be opened in read-only mode
2678 * right now. */
2679 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2680 pLCHSGeometry);
2681 }
2682 }
2683 } while (0);
2684
2685 LogFlowFunc(("returns %Vrc\n", rc));
2686 return rc;
2687}
2688
2689/**
2690 * Get version of image in HDD container.
2691 *
2692 * @returns VBox status code.
2693 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2694 * @param pDisk Pointer to HDD container.
2695 * @param nImage Image number, counts from 0. 0 is always base image of container.
2696 * @param puVersion Where to store the image version.
2697 */
2698VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
2699 unsigned *puVersion)
2700{
2701 int rc = VINF_SUCCESS;
2702
2703 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
2704 pDisk, nImage, puVersion));
2705 do
2706 {
2707 /* sanity check */
2708 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2709 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2710
2711 /* Check arguments. */
2712 AssertMsgBreakStmt(VALID_PTR(puVersion),
2713 ("puVersion=%#p\n", puVersion),
2714 rc = VERR_INVALID_PARAMETER);
2715
2716 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2717 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2718
2719 *puVersion = pImage->Backend->pfnGetVersion(pImage->pvBackendData);
2720 } while (0);
2721
2722 LogFlowFunc(("returns %Vrc uVersion=%#x\n", rc, *puVersion));
2723 return rc;
2724}
2725
2726/**
2727 * Get type of image in HDD container.
2728 *
2729 * @returns VBox status code.
2730 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2731 * @param pDisk Pointer to HDD container.
2732 * @param nImage Image number, counts from 0. 0 is always base image of container.
2733 * @param penmType Where to store the image type.
2734 */
2735VBOXDDU_DECL(int) VDGetImageType(PVBOXHDD pDisk, unsigned nImage,
2736 PVDIMAGETYPE penmType)
2737{
2738 int rc;
2739
2740 LogFlowFunc(("pDisk=%#p nImage=%u penmType=%#p\n",
2741 pDisk, nImage, penmType));
2742 do
2743 {
2744 /* sanity check */
2745 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2746 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2747
2748 /* Check arguments. */
2749 AssertMsgBreakStmt(VALID_PTR(penmType),
2750 ("penmType=%#p\n", penmType),
2751 rc = VERR_INVALID_PARAMETER);
2752
2753 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2754 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2755
2756 rc = pImage->Backend->pfnGetImageType(pImage->pvBackendData,
2757 penmType);
2758 } while (0);
2759
2760 LogFlowFunc(("returns %Vrc uenmType=%u\n", rc, *penmType));
2761 return rc;
2762}
2763
2764/**
2765 * Get flags of image in HDD container.
2766 *
2767 * @returns VBox status code.
2768 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2769 * @param pDisk Pointer to HDD container.
2770 * @param nImage Image number, counts from 0. 0 is always base image of container.
2771 * @param puImageFlags Where to store the image flags.
2772 */
2773VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
2774 unsigned *puImageFlags)
2775{
2776 int rc = VINF_SUCCESS;
2777
2778 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
2779 pDisk, nImage, puImageFlags));
2780 do
2781 {
2782 /* sanity check */
2783 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2784 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2785
2786 /* Check arguments. */
2787 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
2788 ("puImageFlags=%#p\n", puImageFlags),
2789 rc = VERR_INVALID_PARAMETER);
2790
2791 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2792 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2793
2794 *puImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pvBackendData);
2795 } while (0);
2796
2797 LogFlowFunc(("returns %Vrc uImageFlags=%#x\n", rc, *puImageFlags));
2798 return rc;
2799}
2800
2801/**
2802 * Get open flags of image in HDD container.
2803 *
2804 * @returns VBox status code.
2805 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2806 * @param pDisk Pointer to HDD container.
2807 * @param nImage Image number, counts from 0. 0 is always base image of container.
2808 * @param puOpenFlags Where to store the image open flags.
2809 */
2810VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2811 unsigned *puOpenFlags)
2812{
2813 int rc = VINF_SUCCESS;
2814
2815 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
2816 pDisk, nImage, puOpenFlags));
2817 do
2818 {
2819 /* sanity check */
2820 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2821 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2822
2823 /* Check arguments. */
2824 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
2825 ("puOpenFlags=%#p\n", puOpenFlags),
2826 rc = VERR_INVALID_PARAMETER);
2827
2828 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2829 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2830
2831 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2832 } while (0);
2833
2834 LogFlowFunc(("returns %Vrc uOpenFlags=%#x\n", rc, *puOpenFlags));
2835 return rc;
2836}
2837
2838/**
2839 * Set open flags of image in HDD container.
2840 * This operation may cause file locking changes and/or files being reopened.
2841 * Note that in case of unrecoverable error all images in HDD container will be closed.
2842 *
2843 * @returns VBox status code.
2844 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2845 * @param pDisk Pointer to HDD container.
2846 * @param nImage Image number, counts from 0. 0 is always base image of container.
2847 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
2848 */
2849VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2850 unsigned uOpenFlags)
2851{
2852 int rc;
2853
2854 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
2855 do
2856 {
2857 /* sanity check */
2858 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2859 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2860
2861 /* Check arguments. */
2862 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
2863 ("uOpenFlags=%#x\n", uOpenFlags),
2864 rc = VERR_INVALID_PARAMETER);
2865
2866 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2867 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2868
2869 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData,
2870 uOpenFlags);
2871 } while (0);
2872
2873 LogFlowFunc(("returns %Vrc\n", rc));
2874 return rc;
2875}
2876
2877/**
2878 * Get base filename of image in HDD container. Some image formats use
2879 * other filenames as well, so don't use this for anything but informational
2880 * purposes.
2881 *
2882 * @returns VBox status code.
2883 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2884 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
2885 * @param pDisk Pointer to HDD container.
2886 * @param nImage Image number, counts from 0. 0 is always base image of container.
2887 * @param pszFilename Where to store the image file name.
2888 * @param cbFilename Size of buffer pszFilename points to.
2889 */
2890VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
2891 char *pszFilename, unsigned cbFilename)
2892{
2893 int rc;
2894
2895 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
2896 pDisk, nImage, pszFilename, cbFilename));
2897 do
2898 {
2899 /* sanity check */
2900 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2901 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2902
2903 /* Check arguments. */
2904 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
2905 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
2906 rc = VERR_INVALID_PARAMETER);
2907 AssertMsgBreakStmt(cbFilename,
2908 ("cbFilename=%u\n", cbFilename),
2909 rc = VERR_INVALID_PARAMETER);
2910
2911 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2912 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2913
2914 size_t cb = strlen(pImage->pszFilename);
2915 if (cb <= cbFilename)
2916 {
2917 strcpy(pszFilename, pImage->pszFilename);
2918 rc = VINF_SUCCESS;
2919 }
2920 else
2921 {
2922 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
2923 pszFilename[cbFilename - 1] = '\0';
2924 rc = VERR_BUFFER_OVERFLOW;
2925 }
2926 } while (0);
2927
2928 LogFlowFunc(("returns %Vrc, pszFilename=\"%s\"\n", rc, pszFilename));
2929 return rc;
2930}
2931
2932/**
2933 * Get the comment line of image in HDD container.
2934 *
2935 * @returns VBox status code.
2936 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2937 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
2938 * @param pDisk Pointer to HDD container.
2939 * @param nImage Image number, counts from 0. 0 is always base image of container.
2940 * @param pszComment Where to store the comment string of image. NULL is ok.
2941 * @param cbComment The size of pszComment buffer. 0 is ok.
2942 */
2943VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
2944 char *pszComment, unsigned cbComment)
2945{
2946 int rc;
2947
2948 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
2949 pDisk, nImage, pszComment, cbComment));
2950 do
2951 {
2952 /* sanity check */
2953 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2954 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2955
2956 /* Check arguments. */
2957 AssertMsgBreakStmt(VALID_PTR(pszComment),
2958 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
2959 rc = VERR_INVALID_PARAMETER);
2960 AssertMsgBreakStmt(cbComment,
2961 ("cbComment=%u\n", cbComment),
2962 rc = VERR_INVALID_PARAMETER);
2963
2964 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2965 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2966
2967 rc = pImage->Backend->pfnGetComment(pImage->pvBackendData, pszComment,
2968 cbComment);
2969 } while (0);
2970
2971 LogFlowFunc(("returns %Vrc, pszComment=\"%s\"\n", rc, pszComment));
2972 return rc;
2973}
2974
2975/**
2976 * Changes the comment line of image in HDD container.
2977 *
2978 * @returns VBox status code.
2979 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2980 * @param pDisk Pointer to HDD container.
2981 * @param nImage Image number, counts from 0. 0 is always base image of container.
2982 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
2983 */
2984VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
2985 const char *pszComment)
2986{
2987 int rc;
2988
2989 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
2990 pDisk, nImage, pszComment, pszComment));
2991 do
2992 {
2993 /* sanity check */
2994 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2995 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2996
2997 /* Check arguments. */
2998 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
2999 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
3000 rc = VERR_INVALID_PARAMETER);
3001
3002 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3003 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3004
3005 rc = pImage->Backend->pfnSetComment(pImage->pvBackendData, pszComment);
3006 } while (0);
3007
3008 LogFlowFunc(("returns %Vrc\n", rc));
3009 return rc;
3010}
3011
3012
3013/**
3014 * Get UUID of image in HDD container.
3015 *
3016 * @returns VBox status code.
3017 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3018 * @param pDisk Pointer to HDD container.
3019 * @param nImage Image number, counts from 0. 0 is always base image of container.
3020 * @param pUuid Where to store the image creation UUID.
3021 */
3022VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
3023{
3024 int rc;
3025
3026 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3027 do
3028 {
3029 /* sanity check */
3030 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3031 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3032
3033 /* Check arguments. */
3034 AssertMsgBreakStmt(VALID_PTR(pUuid),
3035 ("pUuid=%#p\n", pUuid),
3036 rc = VERR_INVALID_PARAMETER);
3037
3038 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3039 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3040
3041 rc = pImage->Backend->pfnGetUuid(pImage->pvBackendData, pUuid);
3042 } while (0);
3043
3044 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
3045 return rc;
3046}
3047
3048/**
3049 * Set the image's UUID. Should not be used by normal applications.
3050 *
3051 * @returns VBox status code.
3052 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3053 * @param pDisk Pointer to HDD container.
3054 * @param nImage Image number, counts from 0. 0 is always base image of container.
3055 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
3056 */
3057VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
3058{
3059 int rc;
3060
3061 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
3062 pDisk, nImage, pUuid, pUuid));
3063 do
3064 {
3065 /* sanity check */
3066 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3067 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3068
3069 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3070 ("pUuid=%#p\n", pUuid),
3071 rc = VERR_INVALID_PARAMETER);
3072
3073 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3074 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3075
3076 RTUUID Uuid;
3077 if (!pUuid)
3078 {
3079 RTUuidCreate(&Uuid);
3080 pUuid = &Uuid;
3081 }
3082 rc = pImage->Backend->pfnSetUuid(pImage->pvBackendData, pUuid);
3083 } while (0);
3084
3085 LogFlowFunc(("returns %Vrc\n", rc));
3086 return rc;
3087}
3088
3089/**
3090 * Get last modification UUID of image in HDD container.
3091 *
3092 * @returns VBox status code.
3093 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3094 * @param pDisk Pointer to HDD container.
3095 * @param nImage Image number, counts from 0. 0 is always base image of container.
3096 * @param pUuid Where to store the image modification UUID.
3097 */
3098VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
3099{
3100 int rc = VINF_SUCCESS;
3101
3102 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3103 do
3104 {
3105 /* sanity check */
3106 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3107 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3108
3109 /* Check arguments. */
3110 AssertMsgBreakStmt(VALID_PTR(pUuid),
3111 ("pUuid=%#p\n", pUuid),
3112 rc = VERR_INVALID_PARAMETER);
3113
3114 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3115 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3116
3117 rc = pImage->Backend->pfnGetModificationUuid(pImage->pvBackendData,
3118 pUuid);
3119 } while (0);
3120
3121 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
3122 return rc;
3123}
3124
3125/**
3126 * Set the image's last modification UUID. Should not be used by normal applications.
3127 *
3128 * @returns VBox status code.
3129 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3130 * @param pDisk Pointer to HDD container.
3131 * @param nImage Image number, counts from 0. 0 is always base image of container.
3132 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
3133 */
3134VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
3135{
3136 int rc;
3137
3138 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
3139 pDisk, nImage, pUuid, pUuid));
3140 do
3141 {
3142 /* sanity check */
3143 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3144 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3145
3146 /* Check arguments. */
3147 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3148 ("pUuid=%#p\n", pUuid),
3149 rc = VERR_INVALID_PARAMETER);
3150
3151 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3152 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3153
3154 RTUUID Uuid;
3155 if (!pUuid)
3156 {
3157 RTUuidCreate(&Uuid);
3158 pUuid = &Uuid;
3159 }
3160 rc = pImage->Backend->pfnSetModificationUuid(pImage->pvBackendData,
3161 pUuid);
3162 } while (0);
3163
3164 LogFlowFunc(("returns %Vrc\n", rc));
3165 return rc;
3166}
3167
3168/**
3169 * Get parent UUID of image in HDD container.
3170 *
3171 * @returns VBox status code.
3172 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3173 * @param pDisk Pointer to HDD container.
3174 * @param nImage Image number, counts from 0. 0 is always base image of container.
3175 * @param pUuid Where to store the parent image UUID.
3176 */
3177VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
3178 PRTUUID pUuid)
3179{
3180 int rc = VINF_SUCCESS;
3181
3182 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3183 do
3184 {
3185 /* sanity check */
3186 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3187 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3188
3189 /* Check arguments. */
3190 AssertMsgBreakStmt(VALID_PTR(pUuid),
3191 ("pUuid=%#p\n", pUuid),
3192 rc = VERR_INVALID_PARAMETER);
3193
3194 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3195 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3196
3197 rc = pImage->Backend->pfnGetParentUuid(pImage->pvBackendData, pUuid);
3198 } while (0);
3199
3200 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
3201 return rc;
3202}
3203
3204/**
3205 * Set the image's parent UUID. Should not be used by normal applications.
3206 *
3207 * @returns VBox status code.
3208 * @param pDisk Pointer to HDD container.
3209 * @param nImage Image number, counts from 0. 0 is always base image of container.
3210 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
3211 */
3212VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
3213 PCRTUUID pUuid)
3214{
3215 int rc;
3216
3217 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
3218 pDisk, nImage, pUuid, pUuid));
3219 do
3220 {
3221 /* sanity check */
3222 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3223 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3224
3225 /* Check arguments. */
3226 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3227 ("pUuid=%#p\n", pUuid),
3228 rc = VERR_INVALID_PARAMETER);
3229
3230 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3231 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3232
3233 RTUUID Uuid;
3234 if (!pUuid)
3235 {
3236 RTUuidCreate(&Uuid);
3237 pUuid = &Uuid;
3238 }
3239 rc = pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, pUuid);
3240 } while (0);
3241
3242 LogFlowFunc(("returns %Vrc\n", rc));
3243 return rc;
3244}
3245
3246
3247/**
3248 * Debug helper - dumps all opened images in HDD container into the log file.
3249 *
3250 * @param pDisk Pointer to HDD container.
3251 */
3252VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
3253{
3254 do
3255 {
3256 /* sanity check */
3257 AssertPtrBreak(pDisk);
3258 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3259
3260 RTLogPrintf("--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
3261 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
3262 {
3263 RTLogPrintf("Dumping VD image \"%s\" (Backend=%s)\n",
3264 pImage->pszFilename, pImage->Backend->pszBackendName);
3265 pImage->Backend->pfnDump(pImage->pvBackendData);
3266 }
3267 } while (0);
3268}
3269
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