VirtualBox

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

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

VDMerge testcase with pseudo random writes.

  • 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 9262 2008-05-30 17:32:54Z 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 the first byte being
2219 * written from start of disk.
2220 * @param pvBuf Pointer to buffer for writing data.
2221 * @param cbWrite Number of bytes to write.
2222 */
2223VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
2224 size_t cbWrite)
2225{
2226 int rc = VINF_SUCCESS;
2227
2228 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
2229 pDisk, uOffset, pvBuf, cbWrite));
2230 do
2231 {
2232 /* sanity check */
2233 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2234 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2235
2236 /* Check arguments. */
2237 AssertMsgBreakStmt(VALID_PTR(pvBuf),
2238 ("pvBuf=%#p\n", pvBuf),
2239 rc = VERR_INVALID_PARAMETER);
2240 AssertMsgBreakStmt(cbWrite,
2241 ("cbWrite=%zu\n", cbWrite),
2242 rc = VERR_INVALID_PARAMETER);
2243 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
2244 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
2245 uOffset, cbWrite, pDisk->cbSize),
2246 rc = VERR_INVALID_PARAMETER);
2247
2248 PVDIMAGE pImage = pDisk->pLast;
2249 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2250
2251 vdSetModifiedFlag(pDisk);
2252 rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite);
2253 } while (0);
2254
2255 LogFlowFunc(("returns %Vrc\n", rc));
2256 return rc;
2257}
2258
2259/**
2260 * Make sure the on disk representation of a virtual HDD is up to date.
2261 *
2262 * @returns VBox status code.
2263 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2264 * @param pDisk Pointer to HDD container.
2265 */
2266VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
2267{
2268 int rc = VINF_SUCCESS;
2269
2270 LogFlowFunc(("pDisk=%#p\n", pDisk));
2271 do
2272 {
2273 /* sanity check */
2274 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2275 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2276
2277 PVDIMAGE pImage = pDisk->pLast;
2278 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2279
2280 vdResetModifiedFlag(pDisk);
2281 rc = pImage->Backend->pfnFlush(pImage->pvBackendData);
2282 } while (0);
2283
2284 LogFlowFunc(("returns %Vrc\n", rc));
2285 return rc;
2286}
2287
2288/**
2289 * Get number of opened images in HDD container.
2290 *
2291 * @returns Number of opened images for HDD container. 0 if no images have been opened.
2292 * @param pDisk Pointer to HDD container.
2293 */
2294VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
2295{
2296 unsigned cImages;
2297
2298 LogFlowFunc(("pDisk=%#p\n", pDisk));
2299 do
2300 {
2301 /* sanity check */
2302 AssertPtrBreakStmt(pDisk, cImages = 0);
2303 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2304
2305 cImages = pDisk->cImages;
2306 } while (0);
2307
2308 LogFlowFunc(("returns %u\n", cImages));
2309 return cImages;
2310}
2311
2312/**
2313 * Get read/write mode of HDD container.
2314 *
2315 * @returns Virtual disk ReadOnly status.
2316 * @returns true if no image is opened in HDD container.
2317 * @param pDisk Pointer to HDD container.
2318 */
2319VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
2320{
2321 bool fReadOnly;
2322
2323 LogFlowFunc(("pDisk=%#p\n", pDisk));
2324 do
2325 {
2326 /* sanity check */
2327 AssertPtrBreakStmt(pDisk, fReadOnly = false);
2328 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2329
2330 PVDIMAGE pImage = pDisk->pLast;
2331 AssertPtrBreakStmt(pImage, fReadOnly = true);
2332
2333 unsigned uOpenFlags;
2334 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
2335 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
2336 } while (0);
2337
2338 LogFlowFunc(("returns %d\n", fReadOnly));
2339 return fReadOnly;
2340}
2341
2342/**
2343 * Get total capacity of an image in HDD container.
2344 *
2345 * @returns Virtual disk size in bytes.
2346 * @returns 0 if no image with specified number was not opened.
2347 * @param pDisk Pointer to HDD container.
2348 * @param nImage Image number, counds from 0. 0 is always base image of container.
2349 */
2350VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
2351{
2352 uint64_t cbSize;
2353
2354 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
2355 do
2356 {
2357 /* sanity check */
2358 AssertPtrBreakStmt(pDisk, cbSize = 0);
2359 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2360
2361 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2362 AssertPtrBreakStmt(pImage, cbSize = 0);
2363 cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
2364 } while (0);
2365
2366 LogFlowFunc(("returns %llu\n", cbSize));
2367 return cbSize;
2368}
2369
2370/**
2371 * Get total file size of an image in HDD container.
2372 *
2373 * @returns Virtual disk size in bytes.
2374 * @returns 0 if no image is opened in HDD container.
2375 * @param pDisk Pointer to HDD container.
2376 * @param nImage Image number, counts from 0. 0 is always base image of container.
2377 */
2378VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
2379{
2380 uint64_t cbSize;
2381
2382 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
2383 do
2384 {
2385 /* sanity check */
2386 AssertPtrBreakStmt(pDisk, cbSize = 0);
2387 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2388
2389 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2390 AssertPtrBreakStmt(pImage, cbSize = 0);
2391 cbSize = pImage->Backend->pfnGetFileSize(pImage->pvBackendData);
2392 } while (0);
2393
2394 LogFlowFunc(("returns %llu\n", cbSize));
2395 return cbSize;
2396}
2397
2398/**
2399 * Get virtual disk PCHS geometry stored in HDD container.
2400 *
2401 * @returns VBox status code.
2402 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2403 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2404 * @param pDisk Pointer to HDD container.
2405 * @param nImage Image number, counts from 0. 0 is always base image of container.
2406 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
2407 */
2408VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2409 PPDMMEDIAGEOMETRY pPCHSGeometry)
2410{
2411 int rc = VINF_SUCCESS;
2412
2413 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
2414 pDisk, nImage, pPCHSGeometry));
2415 do
2416 {
2417 /* sanity check */
2418 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2419 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2420
2421 /* Check arguments. */
2422 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
2423 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
2424 rc = VERR_INVALID_PARAMETER);
2425
2426 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2427 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2428
2429 if (pImage == pDisk->pLast)
2430 {
2431 /* Use cached information if possible. */
2432 if (pDisk->PCHSGeometry.cCylinders != 0)
2433 *pPCHSGeometry = pDisk->PCHSGeometry;
2434 else
2435 rc = VERR_VDI_GEOMETRY_NOT_SET;
2436 }
2437 else
2438 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2439 pPCHSGeometry);
2440 } while (0);
2441
2442 LogFlowFunc(("%s: %Vrc (PCHS=%u/%u/%u)\n", rc,
2443 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
2444 pDisk->PCHSGeometry.cSectors));
2445 return rc;
2446}
2447
2448/**
2449 * Store virtual disk PCHS geometry in HDD container.
2450 *
2451 * Note that in case of unrecoverable error all images in HDD container will be closed.
2452 *
2453 * @returns VBox status code.
2454 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2455 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2456 * @param pDisk Pointer to HDD container.
2457 * @param nImage Image number, counts from 0. 0 is always base image of container.
2458 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
2459 */
2460VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2461 PCPDMMEDIAGEOMETRY pPCHSGeometry)
2462{
2463 int rc = VINF_SUCCESS;
2464
2465 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
2466 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
2467 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
2468 do
2469 {
2470 /* sanity check */
2471 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2472 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2473
2474 /* Check arguments. */
2475 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
2476 && pPCHSGeometry->cCylinders <= 16383
2477 && pPCHSGeometry->cHeads <= 16
2478 && pPCHSGeometry->cSectors <= 63,
2479 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
2480 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
2481 pPCHSGeometry->cSectors),
2482 rc = VERR_INVALID_PARAMETER);
2483
2484 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2485 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2486
2487 if (pImage == pDisk->pLast)
2488 {
2489 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
2490 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
2491 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
2492 {
2493 /* Only update geometry if it is changed. Avoids similar checks
2494 * in every backend. Most of the time the new geometry is set
2495 * to the previous values, so no need to go through the hassle
2496 * of updating an image which could be opened in read-only mode
2497 * right now. */
2498 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2499 pPCHSGeometry);
2500
2501 /* Cache new geometry values in any case. */
2502 int rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2503 &pDisk->PCHSGeometry);
2504 if (VBOX_FAILURE(rc2))
2505 {
2506 pDisk->PCHSGeometry.cCylinders = 0;
2507 pDisk->PCHSGeometry.cHeads = 0;
2508 pDisk->PCHSGeometry.cSectors = 0;
2509 }
2510 else
2511 {
2512 /* Make sure the CHS geometry is properly clipped. */
2513 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 1024);
2514 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
2515 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
2516 }
2517 }
2518 }
2519 else
2520 {
2521 PDMMEDIAGEOMETRY PCHS;
2522 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2523 &PCHS);
2524 if ( VBOX_FAILURE(rc)
2525 || pPCHSGeometry->cCylinders != PCHS.cCylinders
2526 || pPCHSGeometry->cHeads != PCHS.cHeads
2527 || pPCHSGeometry->cSectors != PCHS.cSectors)
2528 {
2529 /* Only update geometry if it is changed. Avoids similar checks
2530 * in every backend. Most of the time the new geometry is set
2531 * to the previous values, so no need to go through the hassle
2532 * of updating an image which could be opened in read-only mode
2533 * right now. */
2534 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2535 pPCHSGeometry);
2536 }
2537 }
2538 } while (0);
2539
2540 LogFlowFunc(("returns %Vrc\n", rc));
2541 return rc;
2542}
2543
2544/**
2545 * Get virtual disk LCHS geometry stored in HDD container.
2546 *
2547 * @returns VBox status code.
2548 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2549 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2550 * @param pDisk Pointer to HDD container.
2551 * @param nImage Image number, counts from 0. 0 is always base image of container.
2552 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
2553 */
2554VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2555 PPDMMEDIAGEOMETRY pLCHSGeometry)
2556{
2557 int rc = VINF_SUCCESS;
2558
2559 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
2560 pDisk, nImage, pLCHSGeometry));
2561 do
2562 {
2563 /* sanity check */
2564 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2565 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2566
2567 /* Check arguments. */
2568 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
2569 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
2570 rc = VERR_INVALID_PARAMETER);
2571
2572 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2573 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2574
2575 if (pImage == pDisk->pLast)
2576 {
2577 /* Use cached information if possible. */
2578 if (pDisk->LCHSGeometry.cCylinders != 0)
2579 *pLCHSGeometry = pDisk->LCHSGeometry;
2580 else
2581 rc = VERR_VDI_GEOMETRY_NOT_SET;
2582 }
2583 else
2584 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2585 pLCHSGeometry);
2586 } while (0);
2587
2588 LogFlowFunc(("%s: %Vrc (LCHS=%u/%u/%u)\n", rc,
2589 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
2590 pDisk->LCHSGeometry.cSectors));
2591 return rc;
2592}
2593
2594/**
2595 * Store virtual disk LCHS geometry in HDD container.
2596 *
2597 * Note that in case of unrecoverable error all images in HDD container will be closed.
2598 *
2599 * @returns VBox status code.
2600 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2601 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2602 * @param pDisk Pointer to HDD container.
2603 * @param nImage Image number, counts from 0. 0 is always base image of container.
2604 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
2605 */
2606VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2607 PCPDMMEDIAGEOMETRY pLCHSGeometry)
2608{
2609 int rc = VINF_SUCCESS;
2610
2611 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
2612 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
2613 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2614 do
2615 {
2616 /* sanity check */
2617 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2618 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2619
2620 /* Check arguments. */
2621 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
2622 && pLCHSGeometry->cCylinders <= 1024
2623 && pLCHSGeometry->cHeads <= 255
2624 && pLCHSGeometry->cSectors <= 63,
2625 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
2626 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
2627 pLCHSGeometry->cSectors),
2628 rc = VERR_INVALID_PARAMETER);
2629
2630 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2631 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2632
2633 if (pImage == pDisk->pLast)
2634 {
2635 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
2636 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
2637 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
2638 {
2639 /* Only update geometry if it is changed. Avoids similar checks
2640 * in every backend. Most of the time the new geometry is set
2641 * to the previous values, so no need to go through the hassle
2642 * of updating an image which could be opened in read-only mode
2643 * right now. */
2644 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2645 pLCHSGeometry);
2646
2647 /* Cache new geometry values in any case. */
2648 int rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2649 &pDisk->LCHSGeometry);
2650 if (VBOX_FAILURE(rc2))
2651 {
2652 pDisk->LCHSGeometry.cCylinders = 0;
2653 pDisk->LCHSGeometry.cHeads = 0;
2654 pDisk->LCHSGeometry.cSectors = 0;
2655 }
2656 else
2657 {
2658 /* Make sure the CHS geometry is properly clipped. */
2659 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
2660 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
2661 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
2662 }
2663 }
2664 }
2665 else
2666 {
2667 PDMMEDIAGEOMETRY LCHS;
2668 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2669 &LCHS);
2670 if ( VBOX_FAILURE(rc)
2671 || pLCHSGeometry->cCylinders != LCHS.cCylinders
2672 || pLCHSGeometry->cHeads != LCHS.cHeads
2673 || pLCHSGeometry->cSectors != LCHS.cSectors)
2674 {
2675 /* Only update geometry if it is changed. Avoids similar checks
2676 * in every backend. Most of the time the new geometry is set
2677 * to the previous values, so no need to go through the hassle
2678 * of updating an image which could be opened in read-only mode
2679 * right now. */
2680 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2681 pLCHSGeometry);
2682 }
2683 }
2684 } while (0);
2685
2686 LogFlowFunc(("returns %Vrc\n", rc));
2687 return rc;
2688}
2689
2690/**
2691 * Get version of image in HDD container.
2692 *
2693 * @returns VBox status code.
2694 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2695 * @param pDisk Pointer to HDD container.
2696 * @param nImage Image number, counts from 0. 0 is always base image of container.
2697 * @param puVersion Where to store the image version.
2698 */
2699VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
2700 unsigned *puVersion)
2701{
2702 int rc = VINF_SUCCESS;
2703
2704 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
2705 pDisk, nImage, puVersion));
2706 do
2707 {
2708 /* sanity check */
2709 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2710 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2711
2712 /* Check arguments. */
2713 AssertMsgBreakStmt(VALID_PTR(puVersion),
2714 ("puVersion=%#p\n", puVersion),
2715 rc = VERR_INVALID_PARAMETER);
2716
2717 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2718 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2719
2720 *puVersion = pImage->Backend->pfnGetVersion(pImage->pvBackendData);
2721 } while (0);
2722
2723 LogFlowFunc(("returns %Vrc uVersion=%#x\n", rc, *puVersion));
2724 return rc;
2725}
2726
2727/**
2728 * Get type of image in HDD container.
2729 *
2730 * @returns VBox status code.
2731 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2732 * @param pDisk Pointer to HDD container.
2733 * @param nImage Image number, counts from 0. 0 is always base image of container.
2734 * @param penmType Where to store the image type.
2735 */
2736VBOXDDU_DECL(int) VDGetImageType(PVBOXHDD pDisk, unsigned nImage,
2737 PVDIMAGETYPE penmType)
2738{
2739 int rc;
2740
2741 LogFlowFunc(("pDisk=%#p nImage=%u penmType=%#p\n",
2742 pDisk, nImage, penmType));
2743 do
2744 {
2745 /* sanity check */
2746 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2747 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2748
2749 /* Check arguments. */
2750 AssertMsgBreakStmt(VALID_PTR(penmType),
2751 ("penmType=%#p\n", penmType),
2752 rc = VERR_INVALID_PARAMETER);
2753
2754 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2755 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2756
2757 rc = pImage->Backend->pfnGetImageType(pImage->pvBackendData,
2758 penmType);
2759 } while (0);
2760
2761 LogFlowFunc(("returns %Vrc uenmType=%u\n", rc, *penmType));
2762 return rc;
2763}
2764
2765/**
2766 * Get flags of image in HDD container.
2767 *
2768 * @returns VBox status code.
2769 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2770 * @param pDisk Pointer to HDD container.
2771 * @param nImage Image number, counts from 0. 0 is always base image of container.
2772 * @param puImageFlags Where to store the image flags.
2773 */
2774VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
2775 unsigned *puImageFlags)
2776{
2777 int rc = VINF_SUCCESS;
2778
2779 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
2780 pDisk, nImage, puImageFlags));
2781 do
2782 {
2783 /* sanity check */
2784 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2785 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2786
2787 /* Check arguments. */
2788 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
2789 ("puImageFlags=%#p\n", puImageFlags),
2790 rc = VERR_INVALID_PARAMETER);
2791
2792 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2793 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2794
2795 *puImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pvBackendData);
2796 } while (0);
2797
2798 LogFlowFunc(("returns %Vrc uImageFlags=%#x\n", rc, *puImageFlags));
2799 return rc;
2800}
2801
2802/**
2803 * Get open flags of image in HDD container.
2804 *
2805 * @returns VBox status code.
2806 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2807 * @param pDisk Pointer to HDD container.
2808 * @param nImage Image number, counts from 0. 0 is always base image of container.
2809 * @param puOpenFlags Where to store the image open flags.
2810 */
2811VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2812 unsigned *puOpenFlags)
2813{
2814 int rc = VINF_SUCCESS;
2815
2816 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
2817 pDisk, nImage, puOpenFlags));
2818 do
2819 {
2820 /* sanity check */
2821 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2822 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2823
2824 /* Check arguments. */
2825 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
2826 ("puOpenFlags=%#p\n", puOpenFlags),
2827 rc = VERR_INVALID_PARAMETER);
2828
2829 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2830 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2831
2832 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2833 } while (0);
2834
2835 LogFlowFunc(("returns %Vrc uOpenFlags=%#x\n", rc, *puOpenFlags));
2836 return rc;
2837}
2838
2839/**
2840 * Set open flags of image in HDD container.
2841 * This operation may cause file locking changes and/or files being reopened.
2842 * Note that in case of unrecoverable error all images in HDD container will be closed.
2843 *
2844 * @returns VBox status code.
2845 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2846 * @param pDisk Pointer to HDD container.
2847 * @param nImage Image number, counts from 0. 0 is always base image of container.
2848 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
2849 */
2850VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2851 unsigned uOpenFlags)
2852{
2853 int rc;
2854
2855 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
2856 do
2857 {
2858 /* sanity check */
2859 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2860 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2861
2862 /* Check arguments. */
2863 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
2864 ("uOpenFlags=%#x\n", uOpenFlags),
2865 rc = VERR_INVALID_PARAMETER);
2866
2867 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2868 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2869
2870 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData,
2871 uOpenFlags);
2872 } while (0);
2873
2874 LogFlowFunc(("returns %Vrc\n", rc));
2875 return rc;
2876}
2877
2878/**
2879 * Get base filename of image in HDD container. Some image formats use
2880 * other filenames as well, so don't use this for anything but informational
2881 * purposes.
2882 *
2883 * @returns VBox status code.
2884 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2885 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
2886 * @param pDisk Pointer to HDD container.
2887 * @param nImage Image number, counts from 0. 0 is always base image of container.
2888 * @param pszFilename Where to store the image file name.
2889 * @param cbFilename Size of buffer pszFilename points to.
2890 */
2891VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
2892 char *pszFilename, unsigned cbFilename)
2893{
2894 int rc;
2895
2896 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
2897 pDisk, nImage, pszFilename, cbFilename));
2898 do
2899 {
2900 /* sanity check */
2901 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2902 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2903
2904 /* Check arguments. */
2905 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
2906 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
2907 rc = VERR_INVALID_PARAMETER);
2908 AssertMsgBreakStmt(cbFilename,
2909 ("cbFilename=%u\n", cbFilename),
2910 rc = VERR_INVALID_PARAMETER);
2911
2912 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2913 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2914
2915 size_t cb = strlen(pImage->pszFilename);
2916 if (cb <= cbFilename)
2917 {
2918 strcpy(pszFilename, pImage->pszFilename);
2919 rc = VINF_SUCCESS;
2920 }
2921 else
2922 {
2923 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
2924 pszFilename[cbFilename - 1] = '\0';
2925 rc = VERR_BUFFER_OVERFLOW;
2926 }
2927 } while (0);
2928
2929 LogFlowFunc(("returns %Vrc, pszFilename=\"%s\"\n", rc, pszFilename));
2930 return rc;
2931}
2932
2933/**
2934 * Get the comment line of image in HDD container.
2935 *
2936 * @returns VBox status code.
2937 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2938 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
2939 * @param pDisk Pointer to HDD container.
2940 * @param nImage Image number, counts from 0. 0 is always base image of container.
2941 * @param pszComment Where to store the comment string of image. NULL is ok.
2942 * @param cbComment The size of pszComment buffer. 0 is ok.
2943 */
2944VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
2945 char *pszComment, unsigned cbComment)
2946{
2947 int rc;
2948
2949 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
2950 pDisk, nImage, pszComment, cbComment));
2951 do
2952 {
2953 /* sanity check */
2954 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2955 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2956
2957 /* Check arguments. */
2958 AssertMsgBreakStmt(VALID_PTR(pszComment),
2959 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
2960 rc = VERR_INVALID_PARAMETER);
2961 AssertMsgBreakStmt(cbComment,
2962 ("cbComment=%u\n", cbComment),
2963 rc = VERR_INVALID_PARAMETER);
2964
2965 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2966 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2967
2968 rc = pImage->Backend->pfnGetComment(pImage->pvBackendData, pszComment,
2969 cbComment);
2970 } while (0);
2971
2972 LogFlowFunc(("returns %Vrc, pszComment=\"%s\"\n", rc, pszComment));
2973 return rc;
2974}
2975
2976/**
2977 * Changes the comment line of image in HDD container.
2978 *
2979 * @returns VBox status code.
2980 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2981 * @param pDisk Pointer to HDD container.
2982 * @param nImage Image number, counts from 0. 0 is always base image of container.
2983 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
2984 */
2985VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
2986 const char *pszComment)
2987{
2988 int rc;
2989
2990 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
2991 pDisk, nImage, pszComment, pszComment));
2992 do
2993 {
2994 /* sanity check */
2995 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2996 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2997
2998 /* Check arguments. */
2999 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
3000 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
3001 rc = VERR_INVALID_PARAMETER);
3002
3003 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3004 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3005
3006 rc = pImage->Backend->pfnSetComment(pImage->pvBackendData, pszComment);
3007 } while (0);
3008
3009 LogFlowFunc(("returns %Vrc\n", rc));
3010 return rc;
3011}
3012
3013
3014/**
3015 * Get UUID of image in HDD container.
3016 *
3017 * @returns VBox status code.
3018 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3019 * @param pDisk Pointer to HDD container.
3020 * @param nImage Image number, counts from 0. 0 is always base image of container.
3021 * @param pUuid Where to store the image creation UUID.
3022 */
3023VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
3024{
3025 int rc;
3026
3027 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3028 do
3029 {
3030 /* sanity check */
3031 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3032 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3033
3034 /* Check arguments. */
3035 AssertMsgBreakStmt(VALID_PTR(pUuid),
3036 ("pUuid=%#p\n", pUuid),
3037 rc = VERR_INVALID_PARAMETER);
3038
3039 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3040 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3041
3042 rc = pImage->Backend->pfnGetUuid(pImage->pvBackendData, pUuid);
3043 } while (0);
3044
3045 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
3046 return rc;
3047}
3048
3049/**
3050 * Set the image's UUID. Should not be used by normal applications.
3051 *
3052 * @returns VBox status code.
3053 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3054 * @param pDisk Pointer to HDD container.
3055 * @param nImage Image number, counts from 0. 0 is always base image of container.
3056 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
3057 */
3058VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
3059{
3060 int rc;
3061
3062 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
3063 pDisk, nImage, pUuid, pUuid));
3064 do
3065 {
3066 /* sanity check */
3067 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3068 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3069
3070 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3071 ("pUuid=%#p\n", pUuid),
3072 rc = VERR_INVALID_PARAMETER);
3073
3074 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3075 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3076
3077 RTUUID Uuid;
3078 if (!pUuid)
3079 {
3080 RTUuidCreate(&Uuid);
3081 pUuid = &Uuid;
3082 }
3083 rc = pImage->Backend->pfnSetUuid(pImage->pvBackendData, pUuid);
3084 } while (0);
3085
3086 LogFlowFunc(("returns %Vrc\n", rc));
3087 return rc;
3088}
3089
3090/**
3091 * Get last modification UUID of image in HDD container.
3092 *
3093 * @returns VBox status code.
3094 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3095 * @param pDisk Pointer to HDD container.
3096 * @param nImage Image number, counts from 0. 0 is always base image of container.
3097 * @param pUuid Where to store the image modification UUID.
3098 */
3099VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
3100{
3101 int rc = VINF_SUCCESS;
3102
3103 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3104 do
3105 {
3106 /* sanity check */
3107 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3108 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3109
3110 /* Check arguments. */
3111 AssertMsgBreakStmt(VALID_PTR(pUuid),
3112 ("pUuid=%#p\n", pUuid),
3113 rc = VERR_INVALID_PARAMETER);
3114
3115 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3116 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3117
3118 rc = pImage->Backend->pfnGetModificationUuid(pImage->pvBackendData,
3119 pUuid);
3120 } while (0);
3121
3122 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
3123 return rc;
3124}
3125
3126/**
3127 * Set the image's last modification UUID. Should not be used by normal applications.
3128 *
3129 * @returns VBox status code.
3130 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3131 * @param pDisk Pointer to HDD container.
3132 * @param nImage Image number, counts from 0. 0 is always base image of container.
3133 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
3134 */
3135VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
3136{
3137 int rc;
3138
3139 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
3140 pDisk, nImage, pUuid, pUuid));
3141 do
3142 {
3143 /* sanity check */
3144 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3145 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3146
3147 /* Check arguments. */
3148 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3149 ("pUuid=%#p\n", pUuid),
3150 rc = VERR_INVALID_PARAMETER);
3151
3152 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3153 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3154
3155 RTUUID Uuid;
3156 if (!pUuid)
3157 {
3158 RTUuidCreate(&Uuid);
3159 pUuid = &Uuid;
3160 }
3161 rc = pImage->Backend->pfnSetModificationUuid(pImage->pvBackendData,
3162 pUuid);
3163 } while (0);
3164
3165 LogFlowFunc(("returns %Vrc\n", rc));
3166 return rc;
3167}
3168
3169/**
3170 * Get parent UUID of image in HDD container.
3171 *
3172 * @returns VBox status code.
3173 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3174 * @param pDisk Pointer to HDD container.
3175 * @param nImage Image number, counts from 0. 0 is always base image of container.
3176 * @param pUuid Where to store the parent image UUID.
3177 */
3178VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
3179 PRTUUID pUuid)
3180{
3181 int rc = VINF_SUCCESS;
3182
3183 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3184 do
3185 {
3186 /* sanity check */
3187 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3188 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3189
3190 /* Check arguments. */
3191 AssertMsgBreakStmt(VALID_PTR(pUuid),
3192 ("pUuid=%#p\n", pUuid),
3193 rc = VERR_INVALID_PARAMETER);
3194
3195 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3196 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3197
3198 rc = pImage->Backend->pfnGetParentUuid(pImage->pvBackendData, pUuid);
3199 } while (0);
3200
3201 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
3202 return rc;
3203}
3204
3205/**
3206 * Set the image's parent UUID. Should not be used by normal applications.
3207 *
3208 * @returns VBox status code.
3209 * @param pDisk Pointer to HDD container.
3210 * @param nImage Image number, counts from 0. 0 is always base image of container.
3211 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
3212 */
3213VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
3214 PCRTUUID pUuid)
3215{
3216 int rc;
3217
3218 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
3219 pDisk, nImage, pUuid, pUuid));
3220 do
3221 {
3222 /* sanity check */
3223 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3224 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3225
3226 /* Check arguments. */
3227 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3228 ("pUuid=%#p\n", pUuid),
3229 rc = VERR_INVALID_PARAMETER);
3230
3231 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3232 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3233
3234 RTUUID Uuid;
3235 if (!pUuid)
3236 {
3237 RTUuidCreate(&Uuid);
3238 pUuid = &Uuid;
3239 }
3240 rc = pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, pUuid);
3241 } while (0);
3242
3243 LogFlowFunc(("returns %Vrc\n", rc));
3244 return rc;
3245}
3246
3247
3248/**
3249 * Debug helper - dumps all opened images in HDD container into the log file.
3250 *
3251 * @param pDisk Pointer to HDD container.
3252 */
3253VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
3254{
3255 do
3256 {
3257 /* sanity check */
3258 AssertPtrBreak(pDisk);
3259 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3260
3261 RTLogPrintf("--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
3262 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
3263 {
3264 RTLogPrintf("Dumping VD image \"%s\" (Backend=%s)\n",
3265 pImage->pszFilename, pImage->Backend->pszBackendName);
3266 pImage->Backend->pfnDump(pImage->pvBackendData);
3267 }
3268 } while (0);
3269}
3270
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette