VirtualBox

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

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

Fixed too aggressive checking of LCHS geometry values. Triggered an assertion when creating a raw disk vmdk.

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