VirtualBox

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

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

Fix a couple of wrong assertions and treat size_t variables correctly in
logging/assertion messages.

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