VirtualBox

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

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

Fix assertion

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

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