VirtualBox

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

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

Fix segmentation fault because of a NULL pointer

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

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