VirtualBox

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

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

Hook up the new VDI backend to VBoxHDD-new.

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