VirtualBox

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

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

Editing the type without ajdusting the curly brackets work with MS
VCC, but not with gcc.

  • 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 6292 2008-01-09 11:15:37Z 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 int rcCheck = VINF_SUCCESS;
690 bool fPluginFound = false;
691
692 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
693 do
694 {
695 /* Check arguments. */
696 AssertMsgBreak(VALID_PTR(pszFilename) && *pszFilename,
697 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
698 rc = VERR_INVALID_PARAMETER);
699 AssertMsgBreak(VALID_PTR(ppszFormat),
700 ("ppszFormat=%#p\n", ppszFormat),
701 rc = VERR_INVALID_PARAMETER);
702
703 /* First check if static backends support this file format. */
704 for (unsigned i = 0; aBackends[i] != NULL; i++)
705 {
706 if (aBackends[i]->pfnCheckIfValid)
707 {
708 rc = aBackends[i]->pfnCheckIfValid(pszFilename);
709 if (VBOX_SUCCESS(rc))
710 {
711 fPluginFound = true;
712 /* Copy the name into the new string. */
713 char *pszFormat = RTStrDup(aBackends[i]->pszBackendName);
714 if (!pszFormat)
715 {
716 rc = VERR_NO_MEMORY;
717 break;
718 }
719 *ppszFormat = pszFormat;
720 break;
721 }
722 }
723 }
724
725 /* Then check if plugin backends support this file format. */
726 char szPath[RTPATH_MAX];
727 rc = RTPathSharedLibs(szPath, sizeof(szPath));
728 if (VBOX_FAILURE(rc))
729 break;
730
731 /* To get all entries with VBoxHDD as prefix. */
732 char *pszPluginFilter;
733 rc = RTStrAPrintf(&pszPluginFilter, "%s/%s*", szPath,
734 VBOX_HDDFORMAT_PLUGIN_PREFIX);
735 if (VBOX_FAILURE(rc))
736 {
737 rc = VERR_NO_MEMORY;
738 break;
739 }
740
741 /* The plugins are in the same directory as the other shared libs. */
742 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
743 if (VBOX_FAILURE(rc))
744 break;
745
746 PRTDIRENTRY pPluginDirEntry = NULL;
747 unsigned cbPluginDirEntry;
748 pPluginDirEntry = (PRTDIRENTRY)RTMemAllocZ(sizeof(RTDIRENTRY));
749 if (!pPluginDir)
750 {
751 rc = VERR_NO_MEMORY;
752 break;
753 }
754
755 while ((rc = RTDirRead(pPluginDir, pPluginDirEntry, &cbPluginDirEntry)) != VERR_NO_MORE_FILES)
756 {
757 RTLDRMOD hPlugin = NIL_RTLDRMOD;
758 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad = NULL;
759 PVBOXHDDBACKEND pBackend = NULL;
760
761 if (rc == VERR_BUFFER_OVERFLOW)
762 {
763 /* allocate new buffer. */
764 RTMemFree(pPluginDirEntry);
765 pPluginDirEntry = (PRTDIRENTRY)RTMemAllocZ(cbPluginDirEntry);
766 /* Retry. */
767 rc = RTDirRead(pPluginDir, pPluginDirEntry, &cbPluginDirEntry);
768 if (VBOX_FAILURE(rc))
769 break;
770 }
771 else if (VBOX_FAILURE(rc))
772 break;
773
774 /* We got the new entry. */
775 if (pPluginDirEntry->enmType != RTDIRENTRYTYPE_FILE)
776 continue;
777
778 rc = RTLdrLoad(pPluginDirEntry->szName, &hPlugin);
779 if (VBOX_SUCCESS(rc))
780 {
781 rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad);
782 if (VBOX_FAILURE(rc) || !pfnHDDFormatLoad)
783 {
784 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Vrc, pfnHDDFormat=%#p\n", VBOX_HDDFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnHDDFormatLoad));
785 if (VBOX_SUCCESS(rc))
786 rc = VERR_SYMBOL_NOT_FOUND;
787 }
788
789 if (VBOX_SUCCESS(rc))
790 {
791 /* Get the function table. */
792 rc = pfnHDDFormatLoad(&pBackend);
793 if (VBOX_SUCCESS(rc) && pBackend->cbSize == sizeof(VBOXHDDBACKEND))
794 {
795
796 /* Check if the plugin can handle this file. */
797 rc = pBackend->pfnCheckIfValid(pszFilename);
798 if (VBOX_SUCCESS(rc))
799 {
800 fPluginFound = true;
801 rc = VINF_SUCCESS;
802
803 /* Report the format name. */
804 RTPathStripExt(pPluginDirEntry->szName);
805 char *pszFormat = NULL;
806 AssertBreak(strlen(pszFormat) >= VBOX_HDDFORMAT_PLUGIN_PREFIX_LENGTH,
807 rc = VERR_INVALID_NAME);
808 pszFormat = RTStrDup(pPluginDirEntry->szName + VBOX_HDDFORMAT_PLUGIN_PREFIX_LENGTH);
809 if (!pszFormat)
810 rc = VERR_NO_MEMORY;
811 *ppszFormat = pszFormat;
812 }
813 }
814 }
815 else
816 pBackend = NULL;
817 }
818 RTLdrClose(hPlugin);
819 /*
820 * We take the first plugin which can handle this file.
821 */
822 if (fPluginFound)
823 break;
824 }
825 RTStrFree(pszPluginFilter);
826 if (pPluginDirEntry)
827 RTMemFree(pPluginDirEntry);
828 if (pPluginDir)
829 RTDirClose(pPluginDir);
830 } while (0);
831
832 LogFlowFunc(("returns %Vrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
833 return rc;
834}
835
836/**
837 * Opens an image file.
838 *
839 * The first opened image file in HDD container must have a base image type,
840 * others (next opened images) must be a differencing or undo images.
841 * Linkage is checked for differencing image to be in consistence with the previously opened image.
842 * When another differencing image is opened and the last image was opened in read/write access
843 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
844 * other processes to use images in read-only mode too.
845 *
846 * Note that the image is opened in read-only mode if a read/write open is not possible.
847 * Use VDIsReadOnly to check open mode.
848 *
849 * @returns VBox status code.
850 * @param pDisk Pointer to HDD container.
851 * @param pszFilename Name of the image file to open.
852 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
853 */
854VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszFilename,
855 unsigned uOpenFlags)
856{
857 int rc = VINF_SUCCESS;
858 PVDIMAGE pImage = NULL;
859
860 LogFlowFunc(("pDisk=%#p pszFilename=\"%s\" uOpenFlags=%#x\n",
861 pszFilename, uOpenFlags));
862 do
863 {
864 /* sanity check */
865 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
866 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
867
868 /* Check arguments. */
869 AssertMsgBreak(VALID_PTR(pszFilename) && *pszFilename,
870 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
871 rc = VERR_INVALID_PARAMETER);
872 AssertMsgBreak((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
873 ("uOpenFlags=%#x\n", uOpenFlags),
874 rc = VERR_INVALID_PARAMETER);
875
876 /* Force readonly for images without base/diff consistency checking. */
877 if (uOpenFlags & VD_OPEN_FLAGS_INFO)
878 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
879
880 /* Set up image descriptor. */
881 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
882 if (!pImage)
883 {
884 rc = VERR_NO_MEMORY;
885 break;
886 }
887 pImage->pszFilename = RTStrDup(pszFilename);
888 if (!pImage->pszFilename)
889 {
890 rc = VERR_NO_MEMORY;
891 break;
892 }
893
894 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
895 rc = pDisk->Backend->pfnOpen(pImage->pszFilename,
896 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
897 pDisk->pfnError, pDisk->pvErrorUser,
898 &pImage->pvBackendData);
899 /* If the open in read-write mode failed, retry in read-only mode. */
900 if (VBOX_FAILURE(rc))
901 {
902 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
903 && (rc == VERR_ACCESS_DENIED
904 || rc == VERR_PERMISSION_DENIED
905 || rc == VERR_WRITE_PROTECT
906 || rc == VERR_SHARING_VIOLATION
907 || rc == VERR_FILE_LOCK_FAILED))
908 rc = pDisk->Backend->pfnOpen(pImage->pszFilename,
909 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
910 | VD_OPEN_FLAGS_READONLY,
911 pDisk->pfnError, pDisk->pvErrorUser,
912 &pImage->pvBackendData);
913 if (VBOX_FAILURE(rc))
914 {
915 rc = vdError(pDisk, rc, RT_SRC_POS,
916 N_("VD: error opening image file '%s'"), pszFilename);
917 break;
918 }
919 }
920
921 VDIMAGETYPE enmImageType;
922 rc = pDisk->Backend->pfnGetImageType(pImage->pvBackendData,
923 &enmImageType);
924 /* Check image type. As the image itself has no idea whether it's a
925 * base image or not, this info is derived here. Image 0 can be fixed
926 * or normal, all others must be normal images. */
927 if ( VBOX_SUCCESS(rc)
928 && !(uOpenFlags & VD_OPEN_FLAGS_INFO)
929 && pDisk->cImages != 0
930 && enmImageType != VD_IMAGE_TYPE_NORMAL)
931 {
932 rc = VERR_VDI_INVALID_TYPE;
933 break;
934 }
935
936 /** @todo optionally check UUIDs */
937
938 int rc2;
939
940 /* Cache disk information. */
941 pDisk->cbSize = pDisk->Backend->pfnGetSize(pImage->pvBackendData);
942
943 /* Cache PCHS geometry. */
944 rc2 = pDisk->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
945 &pDisk->PCHSGeometry);
946 if (VBOX_FAILURE(rc2))
947 {
948 pDisk->PCHSGeometry.cCylinders = 0;
949 pDisk->PCHSGeometry.cHeads = 0;
950 pDisk->PCHSGeometry.cSectors = 0;
951 }
952 else
953 {
954 /* Make sure the PCHS geometry is properly clipped. */
955 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
956 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
957 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
958 }
959
960 /* Cache LCHS geometry. */
961 rc2 = pDisk->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
962 &pDisk->LCHSGeometry);
963 if (VBOX_FAILURE(rc2))
964 {
965 pDisk->LCHSGeometry.cCylinders = 0;
966 pDisk->LCHSGeometry.cHeads = 0;
967 pDisk->LCHSGeometry.cSectors = 0;
968 }
969 else
970 {
971 /* Make sure the LCHS geometry is properly clipped. */
972 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
973 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
974 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
975 }
976
977 if (pDisk->cImages != 0)
978 {
979 /* Switch previous image to read-only mode. */
980 unsigned uOpenFlagsPrevImg;
981 uOpenFlagsPrevImg = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
982 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
983 {
984 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
985 rc = pDisk->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
986 }
987 }
988
989 if (VBOX_SUCCESS(rc))
990 {
991 /* Image successfully opened, make it the last image. */
992 vdAddImageToList(pDisk, pImage);
993 }
994 else
995 {
996 /* Error detected, but image opened. Close image. */
997 int rc2;
998 rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, false);
999 AssertRC(rc2);
1000 pImage->pvBackendData = NULL;
1001 }
1002 } while (0);
1003
1004 if (VBOX_FAILURE(rc))
1005 {
1006 if (pImage)
1007 {
1008 if (pImage->pszFilename)
1009 RTStrFree(pImage->pszFilename);
1010 RTMemFree(pImage);
1011 }
1012 }
1013
1014 LogFlowFunc(("returns %Vrc\n", rc));
1015 return rc;
1016}
1017
1018/**
1019 * Creates and opens a new base image file.
1020 *
1021 * @returns VBox status code.
1022 * @param pDisk Pointer to HDD container.
1023 * @param pszFilename Name of the image file to create.
1024 * @param enmType Image type, only base image types are acceptable.
1025 * @param cbSize Image size in bytes.
1026 * @param uImageFlags Flags specifying special image features.
1027 * @param pszComment Pointer to image comment. NULL is ok.
1028 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
1029 * @param pLCHSGeometry Pointer to logical disk geometry <= (1024,255,63). Not NULL.
1030 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1031 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1032 * @param pvUser User argument for the progress callback.
1033 */
1034VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszFilename,
1035 VDIMAGETYPE enmType, uint64_t cbSize,
1036 unsigned uImageFlags, const char *pszComment,
1037 PCPDMMEDIAGEOMETRY pPCHSGeometry,
1038 PCPDMMEDIAGEOMETRY pLCHSGeometry,
1039 unsigned uOpenFlags, PFNVMPROGRESS pfnProgress,
1040 void *pvUser)
1041{
1042 int rc = VINF_SUCCESS;
1043 PVDIMAGE pImage = NULL;
1044
1045 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",
1046 pszFilename, enmType, cbSize, uImageFlags, pszComment,
1047 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1048 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
1049 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, uOpenFlags,
1050 pfnProgress, pvUser));
1051 do
1052 {
1053 /* sanity check */
1054 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1055 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1056
1057 /* Check arguments. */
1058 AssertMsgBreak(VALID_PTR(pszFilename) && *pszFilename,
1059 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1060 rc = VERR_INVALID_PARAMETER);
1061 AssertMsgBreak(enmType == VD_IMAGE_TYPE_NORMAL || enmType == VD_IMAGE_TYPE_FIXED,
1062 ("enmType=%#x\n", enmType),
1063 rc = VERR_INVALID_PARAMETER);
1064 AssertMsgBreak(cbSize,
1065 ("cbSize=%llu\n", cbSize),
1066 rc = VERR_INVALID_PARAMETER);
1067 AssertMsgBreak((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
1068 ("uImageFlags=%#x\n", uImageFlags),
1069 rc = VERR_INVALID_PARAMETER);
1070 AssertMsgBreak( VALID_PTR(pPCHSGeometry)
1071 && pPCHSGeometry->cCylinders <= 16383
1072 && pPCHSGeometry->cCylinders != 0
1073 && pPCHSGeometry->cHeads <= 16
1074 && pPCHSGeometry->cHeads != 0
1075 && pPCHSGeometry->cSectors <= 63
1076 && pPCHSGeometry->cSectors != 0,
1077 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
1078 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1079 pPCHSGeometry->cSectors),
1080 rc = VERR_INVALID_PARAMETER);
1081 AssertMsgBreak( VALID_PTR(pLCHSGeometry)
1082 && pLCHSGeometry->cCylinders <= 16383
1083 && pLCHSGeometry->cCylinders != 0
1084 && pLCHSGeometry->cHeads <= 16
1085 && pLCHSGeometry->cHeads != 0
1086 && pLCHSGeometry->cSectors <= 63
1087 && pLCHSGeometry->cSectors != 0,
1088 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
1089 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
1090 pLCHSGeometry->cSectors),
1091 rc = VERR_INVALID_PARAMETER);
1092 AssertMsgBreak((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1093 ("uOpenFlags=%#x\n", uOpenFlags),
1094 rc = VERR_INVALID_PARAMETER);
1095
1096 /* Check state. */
1097 if (pDisk->cImages != 0)
1098 AssertMsgFailedBreak(("Create base image cannot be done with other images open\n"),
1099 rc = VERR_VDI_INVALID_STATE);
1100
1101 /* Set up image descriptor. */
1102 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1103 if (!pImage)
1104 {
1105 rc = VERR_NO_MEMORY;
1106 break;
1107 }
1108 pImage->pszFilename = RTStrDup(pszFilename);
1109 if (!pImage->pszFilename)
1110 {
1111 rc = VERR_NO_MEMORY;
1112 break;
1113 }
1114
1115 rc = pDisk->Backend->pfnCreate(pImage->pszFilename, enmType, cbSize,
1116 uImageFlags, pszComment, pPCHSGeometry,
1117 pLCHSGeometry, uOpenFlags, pfnProgress,
1118 pvUser, 0, 99, pDisk->pfnError,
1119 pDisk->pvErrorUser,
1120 &pImage->pvBackendData);
1121
1122 if (VBOX_SUCCESS(rc))
1123 {
1124 /** @todo optionally check UUIDs */
1125
1126 int rc2;
1127
1128 /* Cache disk information. */
1129 pDisk->cbSize = pDisk->Backend->pfnGetSize(pImage->pvBackendData);
1130
1131 /* Cache PCHS geometry. */
1132 rc2 = pDisk->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1133 &pDisk->PCHSGeometry);
1134 if (VBOX_FAILURE(rc2))
1135 {
1136 pDisk->PCHSGeometry.cCylinders = 0;
1137 pDisk->PCHSGeometry.cHeads = 0;
1138 pDisk->PCHSGeometry.cSectors = 0;
1139 }
1140 else
1141 {
1142 /* Make sure the CHS geometry is properly clipped. */
1143 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1144 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1145 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1146 }
1147
1148 /* Cache LCHS geometry. */
1149 rc2 = pDisk->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1150 &pDisk->LCHSGeometry);
1151 if (VBOX_FAILURE(rc2))
1152 {
1153 pDisk->LCHSGeometry.cCylinders = 0;
1154 pDisk->LCHSGeometry.cHeads = 0;
1155 pDisk->LCHSGeometry.cSectors = 0;
1156 }
1157 else
1158 {
1159 /* Make sure the CHS geometry is properly clipped. */
1160 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
1161 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1162 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1163 }
1164 }
1165
1166 if (VBOX_SUCCESS(rc))
1167 {
1168 /* Image successfully opened, make it the last image. */
1169 vdAddImageToList(pDisk, pImage);
1170 }
1171 else
1172 {
1173 /* Error detected, but image opened. Close and delete image. */
1174 int rc2;
1175 rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, true);
1176 AssertRC(rc2);
1177 pImage->pvBackendData = NULL;
1178 }
1179 } while (0);
1180
1181 if (VBOX_FAILURE(rc))
1182 {
1183 if (pImage)
1184 {
1185 if (pImage->pszFilename)
1186 RTStrFree(pImage->pszFilename);
1187 RTMemFree(pImage);
1188 }
1189 }
1190
1191 if (VBOX_SUCCESS(rc) && pfnProgress)
1192 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1193
1194 LogFlowFunc(("returns %Vrc\n", rc));
1195 return rc;
1196}
1197
1198/**
1199 * Creates and opens a new differencing image file in HDD container.
1200 * See comments for VDOpen function about differencing images.
1201 *
1202 * @returns VBox status code.
1203 * @param pDisk Pointer to HDD container.
1204 * @param pszFilename Name of the differencing image file to create.
1205 * @param uImageFlags Flags specifying special image features.
1206 * @param pszComment Pointer to image comment. NULL is ok.
1207 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1208 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1209 * @param pvUser User argument for the progress callback.
1210 */
1211VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszFilename,
1212 unsigned uImageFlags, const char *pszComment,
1213 unsigned uOpenFlags, PFNVMPROGRESS pfnProgress,
1214 void *pvUser)
1215{
1216 int rc = VINF_SUCCESS;
1217 PVDIMAGE pImage = NULL;
1218
1219 LogFlowFunc(("pDisk=%#p pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" uOpenFlags=%#x pfnProgress=%#p pvUser=%#p\n",
1220 pDisk, pszFilename, uImageFlags, pszComment, uOpenFlags,
1221 pfnProgress, pvUser));
1222 do
1223 {
1224 /* sanity check */
1225 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1226 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1227
1228 /* Check arguments. */
1229 AssertMsgBreak(VALID_PTR(pszFilename) && *pszFilename,
1230 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1231 rc = VERR_INVALID_PARAMETER);
1232 AssertMsgBreak((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
1233 ("uImageFlags=%#x\n", uImageFlags),
1234 rc = VERR_INVALID_PARAMETER);
1235 AssertMsgBreak((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1236 ("uOpenFlags=%#x\n", uOpenFlags),
1237 rc = VERR_INVALID_PARAMETER);
1238
1239 /* Check state. */
1240 if (pDisk->cImages == 0)
1241 AssertMsgFailedBreak(("Create diff image cannot be done without other images open\n"),
1242 rc = VERR_VDI_INVALID_STATE);
1243
1244 /* Set up image descriptor. */
1245 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1246 if (!pImage)
1247 {
1248 rc = VERR_NO_MEMORY;
1249 break;
1250 }
1251 pImage->pszFilename = RTStrDup(pszFilename);
1252 if (!pImage->pszFilename)
1253 {
1254 rc = VERR_NO_MEMORY;
1255 break;
1256 }
1257
1258 rc = pDisk->Backend->pfnCreate(pImage->pszFilename,
1259 VD_IMAGE_TYPE_NORMAL, pDisk->cbSize,
1260 uImageFlags, pszComment,
1261 &pDisk->PCHSGeometry,
1262 &pDisk->LCHSGeometry, uOpenFlags,
1263 pfnProgress, pvUser, 0, 99,
1264 pDisk->pfnError, pDisk->pvErrorUser,
1265 &pImage->pvBackendData);
1266
1267 if (VBOX_SUCCESS(rc))
1268 {
1269 /** @todo optionally check UUIDs */
1270 }
1271
1272 if (VBOX_SUCCESS(rc))
1273 {
1274 /* Image successfully opened, make it the last image. */
1275 vdAddImageToList(pDisk, pImage);
1276 }
1277 else
1278 {
1279 /* Error detected, but image opened. Close and delete image. */
1280 int rc2;
1281 rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, true);
1282 AssertRC(rc2);
1283 pImage->pvBackendData = NULL;
1284 }
1285 } while (0);
1286
1287 if (VBOX_FAILURE(rc))
1288 {
1289 if (pImage)
1290 {
1291 if (pImage->pszFilename)
1292 RTStrFree(pImage->pszFilename);
1293 RTMemFree(pImage);
1294 }
1295 }
1296
1297 if (VBOX_SUCCESS(rc) && pfnProgress)
1298 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1299
1300 LogFlowFunc(("returns %Vrc\n", rc));
1301 return rc;
1302}
1303
1304/**
1305 * Merges two images (not necessarily with direct parent/child relationship).
1306 * As a side effect the source image and potentially the other images which
1307 * are also merged to the destination are deleted from both the disk and the
1308 * images in the HDD container.
1309 *
1310 * @returns VBox status code.
1311 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1312 * @param pDisk Pointer to HDD container.
1313 * @param nImageFrom Name of the image file to merge from.
1314 * @param nImageTo Name of the image file to merge to.
1315 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1316 * @param pvUser User argument for the progress callback.
1317 */
1318VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
1319 unsigned nImageTo, PFNVMPROGRESS pfnProgress,
1320 void *pvUser)
1321{
1322 int rc = VINF_SUCCESS;
1323 void *pvBuf = NULL;
1324
1325 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pfnProgress=%#p pvUser=%#p\n",
1326 pDisk, nImageFrom, nImageTo, pfnProgress, pvUser));
1327 do
1328 {
1329 /* sanity check */
1330 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1331 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1332
1333 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
1334 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
1335 if (!pImageFrom || !pImageTo)
1336 {
1337 rc = VERR_VDI_IMAGE_NOT_FOUND;
1338 break;
1339 }
1340 AssertBreak(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
1341
1342 /* Check if destination image is writable. */
1343 unsigned uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
1344 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
1345 {
1346 rc = VERR_VDI_IMAGE_READ_ONLY;
1347 break;
1348 }
1349
1350 /* Get size of destination image. */
1351 uint64_t cbSize = pDisk->Backend->pfnGetSize(pImageTo->pvBackendData);
1352
1353 /* Allocate tmp buffer. */
1354 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
1355 if (!pvBuf)
1356 {
1357 rc = VERR_NO_MEMORY;
1358 break;
1359 }
1360
1361 /* Merging is done directly on the images itself. This potentially
1362 * causes trouble if the disk is full in the middle of operation. */
1363 /** @todo write alternative implementation which works with temporary
1364 * images (which is safer, but requires even more space). Also has the
1365 * drawback that merging into a raw disk parent simply isn't possible
1366 * this way (but in that case disk full isn't really a problem). */
1367 if (nImageFrom < nImageTo)
1368 {
1369 /* Merge parent state into child. This means writing all not
1370 * allocated blocks in the destination image which are allocated in
1371 * the images to be merged. */
1372 uint64_t uOffset = 0;
1373 uint64_t cbRemaining = cbSize;
1374 do
1375 {
1376 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1377 rc = pDisk->Backend->pfnRead(pImageTo->pvBackendData, uOffset,
1378 pvBuf, cbThisRead, &cbThisRead);
1379 if (VBOX_FAILURE(rc))
1380 break;
1381 if (rc == VINF_VDI_BLOCK_FREE)
1382 {
1383 /* Search for image with allocated block. Do not attempt to
1384 * read more than the previous reads marked as valid.
1385 * Otherwise this would return stale data when different
1386 * block sizes are used for the images. */
1387 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
1388 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VINF_VDI_BLOCK_FREE;
1389 pCurrImage = pCurrImage->pPrev)
1390 {
1391 rc = pDisk->Backend->pfnRead(pCurrImage->pvBackendData,
1392 uOffset, pvBuf,
1393 cbThisRead, &cbThisRead);
1394 }
1395 if (VBOX_FAILURE(rc))
1396 break;
1397
1398 if (rc != VINF_VDI_BLOCK_FREE)
1399 {
1400 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1401 cbThisRead);
1402 if (VBOX_FAILURE(rc))
1403 break;
1404 }
1405 }
1406
1407 uOffset += cbThisRead;
1408 cbRemaining -= cbThisRead;
1409 } while (uOffset < cbSize);
1410 }
1411 else
1412 {
1413 /* Merge child state into parent. This means writing all blocks
1414 * which are allocated in the image up to the source image to the
1415 * destination image. */
1416 uint64_t uOffset = 0;
1417 uint64_t cbRemaining = cbSize;
1418 do
1419 {
1420 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1421 /* Search for image with allocated block. Do not attempt to
1422 * read more than the previous reads marked as valid. Otherwise
1423 * this would return stale data when different block sizes are
1424 * used for the images. */
1425 for (PVDIMAGE pCurrImage = pImageFrom;
1426 pCurrImage != NULL && pCurrImage != pImageTo && rc == VINF_VDI_BLOCK_FREE;
1427 pCurrImage = pCurrImage->pPrev)
1428 {
1429 rc = pDisk->Backend->pfnRead(pCurrImage->pvBackendData,
1430 uOffset, pvBuf,
1431 cbThisRead, &cbThisRead);
1432 }
1433 if (VBOX_FAILURE(rc))
1434 break;
1435
1436 if (rc != VINF_VDI_BLOCK_FREE)
1437 {
1438 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1439 cbThisRead);
1440 if (VBOX_FAILURE(rc))
1441 break;
1442 }
1443
1444 uOffset += cbThisRead;
1445 cbRemaining -= cbThisRead;
1446 } while (uOffset < cbSize);
1447 }
1448
1449 /* Update parent UUID so that image chain is consistent. */
1450 RTUUID Uuid;
1451 if (nImageFrom < nImageTo)
1452 {
1453 if (pImageTo->pPrev)
1454 {
1455 rc = pDisk->Backend->pfnGetUuid(pImageTo->pPrev->pvBackendData,
1456 &Uuid);
1457 AssertRC(rc);
1458 }
1459 else
1460 RTUuidClear(&Uuid);
1461 rc = pDisk->Backend->pfnSetParentUuid(pImageTo->pvBackendData,
1462 &Uuid);
1463 AssertRC(rc);
1464 }
1465 else
1466 {
1467 if (pImageFrom->pNext)
1468 {
1469 rc = pDisk->Backend->pfnGetUuid(pImageTo->pvBackendData,
1470 &Uuid);
1471 AssertRC(rc);
1472 rc = pDisk->Backend->pfnSetParentUuid(pImageFrom->pNext,
1473 &Uuid);
1474 AssertRC(rc);
1475 }
1476 }
1477
1478 /* Delete the no longer needed images. */
1479 PVDIMAGE pImg = pImageFrom, pTmp;
1480 while (pImg != pImageTo)
1481 {
1482 if (nImageFrom < nImageTo)
1483 pTmp = pImg->pNext;
1484 else
1485 pTmp = pImg->pPrev;
1486 vdRemoveImageFromList(pDisk, pImg);
1487 pDisk->Backend->pfnClose(pImg->pvBackendData, true);
1488 pImg = pTmp;
1489 }
1490 } while (0);
1491
1492 if (pvBuf)
1493 RTMemTmpFree(pvBuf);
1494
1495 if (VBOX_SUCCESS(rc) && pfnProgress)
1496 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1497
1498 LogFlowFunc(("returns %Vrc\n", rc));
1499 return rc;
1500}
1501
1502/**
1503 * Copies an image from one HDD container to another.
1504 * The copy is opened in the target HDD container.
1505 * It is possible to convert between different image formats, because the
1506 * backend for the destination HDD container may be different from the
1507 * source container.
1508 * If both the source and destination reference the same HDD container,
1509 * then the image is moved (by copying/deleting or renaming) to the new location.
1510 * The source container is unchanged if the move operation fails, otherwise
1511 * the image at the new location is opened in the same way as the old one was.
1512 *
1513 * @returns VBox status code.
1514 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1515 * @param pDiskFrom Pointer to source HDD container.
1516 * @param nImage Image number, counts from 0. 0 is always base image of container.
1517 * @param pDiskTo Pointer to destination HDD container.
1518 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
1519 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
1520 * @param cbSize New image size (0 means leave unchanged).
1521 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1522 * @param pvUser User argument for the progress callback.
1523 */
1524VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
1525 const char *pszFilename, bool fMoveByRename,
1526 uint64_t cbSize, PFNVMPROGRESS pfnProgress,
1527 void *pvUser)
1528{
1529 return VERR_NOT_IMPLEMENTED;
1530}
1531
1532/**
1533 * Closes the last opened image file in HDD container.
1534 * If previous image file was opened in read-only mode (that is normal) and closing image
1535 * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
1536 * will be reopened in read/write mode.
1537 *
1538 * @returns VBox status code.
1539 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1540 * @param pDisk Pointer to HDD container.
1541 * @param fDelete If true, delete the image from the host disk.
1542 */
1543VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
1544{
1545 int rc = VINF_SUCCESS;;
1546
1547 LogFlowFunc(("fDelete=%d\n", fDelete));
1548 do
1549 {
1550 /* sanity check */
1551 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1552 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1553
1554 PVDIMAGE pImage = pDisk->pLast;
1555 if (RT_UNLIKELY(!pImage))
1556 {
1557 Assert(pImage);
1558 rc = VERR_VDI_NOT_OPENED;
1559 break;
1560 }
1561 unsigned uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pImage->pvBackendData);
1562 /* Remove image from list of opened images. */
1563 vdRemoveImageFromList(pDisk, pImage);
1564 /* Close (and optionally delete) image. */
1565 rc = pDisk->Backend->pfnClose(pImage->pvBackendData, fDelete);
1566 /* Free remaining resources related to the image. */
1567 RTStrFree(pImage->pszFilename);
1568 RTMemFree(pImage);
1569
1570 pImage = pDisk->pLast;
1571 if (!pImage)
1572 break;
1573
1574 /* If disk was previously in read/write mode, make sure it will stay
1575 * like this (if possible) after closing this image. Set the open flags
1576 * accordingly. */
1577 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1578 {
1579 uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pImage->pvBackendData);
1580 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
1581 rc = pDisk->Backend->pfnSetOpenFlags(pImage->pvBackendData, uOpenFlags);
1582 }
1583
1584 int rc2;
1585
1586 /* Cache disk information. */
1587 pDisk->cbSize = pDisk->Backend->pfnGetSize(pImage->pvBackendData);
1588
1589 /* Cache PCHS geometry. */
1590 rc2 = pDisk->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1591 &pDisk->PCHSGeometry);
1592 if (VBOX_FAILURE(rc2))
1593 {
1594 pDisk->PCHSGeometry.cCylinders = 0;
1595 pDisk->PCHSGeometry.cHeads = 0;
1596 pDisk->PCHSGeometry.cSectors = 0;
1597 }
1598 else
1599 {
1600 /* Make sure the PCHS geometry is properly clipped. */
1601 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1602 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1603 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1604 }
1605
1606 /* Cache LCHS geometry. */
1607 rc2 = pDisk->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1608 &pDisk->LCHSGeometry);
1609 if (VBOX_FAILURE(rc2))
1610 {
1611 pDisk->LCHSGeometry.cCylinders = 0;
1612 pDisk->LCHSGeometry.cHeads = 0;
1613 pDisk->LCHSGeometry.cSectors = 0;
1614 }
1615 else
1616 {
1617 /* Make sure the LCHS geometry is properly clipped. */
1618 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
1619 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1620 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1621 }
1622 } while (0);
1623
1624 LogFlowFunc(("returns %Vrc\n", rc));
1625 return rc;
1626}
1627
1628/**
1629 * Closes all opened image files in HDD container.
1630 *
1631 * @returns VBox status code.
1632 * @param pDisk Pointer to HDD container.
1633 */
1634VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
1635{
1636 int rc = VINF_SUCCESS;
1637
1638 LogFlowFunc(("pDisk=%#p\n", pDisk));
1639 do
1640 {
1641 /* sanity check */
1642 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1643 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1644
1645 PVDIMAGE pImage = pDisk->pLast;
1646 while (pImage)
1647 {
1648 PVDIMAGE pPrev = pImage->pPrev;
1649 /* Remove image from list of opened images. */
1650 vdRemoveImageFromList(pDisk, pImage);
1651 /* Close image. */
1652 int rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, false);
1653 if (VBOX_FAILURE(rc2) && VBOX_SUCCESS(rc))
1654 rc = rc2;
1655 /* Free remaining resources related to the image. */
1656 RTStrFree(pImage->pszFilename);
1657 RTMemFree(pImage);
1658 pImage = pPrev;
1659 }
1660 Assert(pDisk->pLast == NULL);
1661 } while (0);
1662
1663 LogFlowFunc(("returns %Vrc\n", rc));
1664 return rc;
1665}
1666
1667/**
1668 * Read data from virtual HDD.
1669 *
1670 * @returns VBox status code.
1671 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1672 * @param pDisk Pointer to HDD container.
1673 * @param uOffset Offset of first reading byte from start of disk.
1674 * @param pvBuf Pointer to buffer for reading data.
1675 * @param cbRead Number of bytes to read.
1676 */
1677VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
1678 size_t cbRead)
1679{
1680 int rc;
1681
1682 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%llu\n",
1683 pDisk, uOffset, pvBuf, cbRead));
1684 do
1685 {
1686 /* sanity check */
1687 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1688 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1689
1690 /* Check arguments. */
1691 AssertMsgBreak(VALID_PTR(pvBuf),
1692 ("pvBuf=%#p\n", pvBuf),
1693 rc = VERR_INVALID_PARAMETER);
1694 AssertMsgBreak(cbRead,
1695 ("cbRead=%llu\n", cbRead),
1696 rc = VERR_INVALID_PARAMETER);
1697 AssertMsgBreak(uOffset + cbRead > pDisk->cbSize,
1698 ("uOffset=%llu cbRead=%llu pDisk->cbSize=%llu\n",
1699 uOffset, cbRead, pDisk->cbSize),
1700 rc = VERR_INVALID_PARAMETER);
1701
1702 PVDIMAGE pImage = pDisk->pLast;
1703 AssertBreak(pImage, rc = VERR_VDI_NOT_OPENED);
1704
1705 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead);
1706 } while (0);
1707
1708 LogFlowFunc(("returns %Vrc\n", rc));
1709 return rc;
1710}
1711
1712/**
1713 * Write data to virtual HDD.
1714 *
1715 * @returns VBox status code.
1716 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1717 * @param pDisk Pointer to HDD container.
1718 * @param uOffset Offset of first reading byte from start of disk.
1719 * @param pvBuf Pointer to buffer for writing data.
1720 * @param cbWrite Number of bytes to write.
1721 */
1722VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf, size_t cbWrite)
1723{
1724 int rc = VINF_SUCCESS;
1725
1726 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%llu\n",
1727 pDisk, uOffset, pvBuf, cbWrite));
1728 do
1729 {
1730 /* sanity check */
1731 AssertBreak(VALID_PTR(pDisk), VERR_INVALID_PARAMETER);
1732 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1733
1734 /* Check arguments. */
1735 AssertMsgBreak(VALID_PTR(pvBuf),
1736 ("pvBuf=%#p\n", pvBuf),
1737 rc = VERR_INVALID_PARAMETER);
1738 AssertMsgBreak(cbWrite,
1739 ("cbWrite=%llu\n", cbWrite),
1740 rc = VERR_INVALID_PARAMETER);
1741 AssertMsgBreak(uOffset + cbWrite > pDisk->cbSize,
1742 ("uOffset=%llu cbWrite=%llu pDisk->cbSize=%llu\n",
1743 uOffset, cbWrite, pDisk->cbSize),
1744 rc = VERR_INVALID_PARAMETER);
1745
1746 PVDIMAGE pImage = pDisk->pLast;
1747 AssertBreak(pImage, rc = VERR_VDI_NOT_OPENED);
1748
1749 vdSetModifiedFlag(pDisk);
1750 rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite);
1751 } while (0);
1752
1753 LogFlowFunc(("returns %Vrc\n", rc));
1754 return rc;
1755}
1756
1757/**
1758 * Make sure the on disk representation of a virtual HDD is up to date.
1759 *
1760 * @returns VBox status code.
1761 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1762 * @param pDisk Pointer to HDD container.
1763 */
1764VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
1765{
1766 int rc = VINF_SUCCESS;
1767
1768 LogFlowFunc(("pDisk=%#p\n", pDisk));
1769 do
1770 {
1771 /* sanity check */
1772 AssertBreak(VALID_PTR(pDisk), VERR_INVALID_PARAMETER);
1773 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1774
1775 PVDIMAGE pImage = pDisk->pLast;
1776 AssertBreak(pImage, rc = VERR_VDI_NOT_OPENED);
1777
1778 vdResetModifiedFlag(pDisk);
1779 rc = pDisk->Backend->pfnFlush(pImage->pvBackendData);
1780 } while (0);
1781
1782 LogFlowFunc(("returns %Vrc\n", rc));
1783 return rc;
1784}
1785
1786/**
1787 * Get number of opened images in HDD container.
1788 *
1789 * @returns Number of opened images for HDD container. 0 if no images have been opened.
1790 * @param pDisk Pointer to HDD container.
1791 */
1792VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
1793{
1794 unsigned cImages;
1795
1796 LogFlowFunc(("pDisk=%#p\n", pDisk));
1797 do
1798 {
1799 /* sanity check */
1800 AssertBreak(VALID_PTR(pDisk), cImages = 0);
1801 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1802
1803 cImages = pDisk->cImages;
1804 } while (0);
1805
1806 LogFlowFunc(("returns %u\n", cImages));
1807 return cImages;
1808}
1809
1810/**
1811 * Get read/write mode of HDD container.
1812 *
1813 * @returns Virtual disk ReadOnly status.
1814 * @returns true if no image is opened in HDD container.
1815 * @param pDisk Pointer to HDD container.
1816 */
1817VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
1818{
1819 bool fReadOnly;
1820
1821 LogFlowFunc(("pDisk=%#p\n", pDisk));
1822 do
1823 {
1824 /* sanity check */
1825 AssertBreak(VALID_PTR(pDisk), fReadOnly = false);
1826 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1827
1828 PVDIMAGE pImage = pDisk->pLast;
1829 AssertBreak(pImage, fReadOnly = true);
1830
1831 unsigned uOpenFlags;
1832 uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1833 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
1834 } while (0);
1835
1836 LogFlowFunc(("returns %d\n", fReadOnly));
1837 return fReadOnly;
1838}
1839
1840/**
1841 * Get total capacity of an image in HDD container.
1842 *
1843 * @returns Virtual disk size in bytes.
1844 * @returns 0 if no image with specified number was not opened.
1845 * @param pDisk Pointer to HDD container.
1846 * @param nImage Image number, counds from 0. 0 is always base image of container.
1847 */
1848VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
1849{
1850 uint64_t cbSize;
1851
1852 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
1853 do
1854 {
1855 /* sanity check */
1856 AssertBreak(VALID_PTR(pDisk), cbSize = 0);
1857 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1858
1859 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1860 AssertBreak(pImage, cbSize = 0);
1861 cbSize = pDisk->Backend->pfnGetSize(pImage->pvBackendData);
1862 } while (0);
1863
1864 LogFlowFunc(("returns %llu\n", cbSize));
1865 return cbSize;
1866}
1867
1868/**
1869 * Get total file size of an image in HDD container.
1870 *
1871 * @returns Virtual disk size in bytes.
1872 * @returns 0 if no image is opened in HDD container.
1873 * @param pDisk Pointer to HDD container.
1874 * @param nImage Image number, counts from 0. 0 is always base image of container.
1875 */
1876VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
1877{
1878 uint64_t cbSize;
1879
1880 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
1881 do
1882 {
1883 /* sanity check */
1884 AssertBreak(VALID_PTR(pDisk), cbSize = 0);
1885 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1886
1887 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1888 AssertBreak(pImage, cbSize = 0);
1889 cbSize = pDisk->Backend->pfnGetFileSize(pImage->pvBackendData);
1890 } while (0);
1891
1892 LogFlowFunc(("returns %llu\n", cbSize));
1893 return cbSize;
1894}
1895
1896/**
1897 * Get virtual disk PCHS geometry stored in HDD container.
1898 *
1899 * @returns VBox status code.
1900 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1901 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
1902 * @param pDisk Pointer to HDD container.
1903 * @param nImage Image number, counts from 0. 0 is always base image of container.
1904 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
1905 */
1906VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
1907 PPDMMEDIAGEOMETRY pPCHSGeometry)
1908{
1909 int rc = VINF_SUCCESS;
1910
1911 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
1912 pDisk, nImage, pPCHSGeometry));
1913 do
1914 {
1915 /* sanity check */
1916 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1917 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1918
1919 /* Check arguments. */
1920 AssertMsgBreak(VALID_PTR(pPCHSGeometry),
1921 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
1922 rc = VERR_INVALID_PARAMETER);
1923
1924 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1925 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
1926
1927 if (pImage == pDisk->pLast)
1928 {
1929 /* Use cached information if possible. */
1930 if (pDisk->PCHSGeometry.cCylinders != 0)
1931 *pPCHSGeometry = pDisk->PCHSGeometry;
1932 else
1933 rc = VERR_VDI_GEOMETRY_NOT_SET;
1934 }
1935 else
1936 rc = pDisk->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1937 pPCHSGeometry);
1938 } while (0);
1939
1940 LogFlowFunc(("%s: %Vrc (PCHS=%u/%u/%u)\n", rc,
1941 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
1942 pDisk->PCHSGeometry.cSectors));
1943 return rc;
1944}
1945
1946/**
1947 * Store virtual disk PCHS geometry in HDD container.
1948 *
1949 * Note that in case of unrecoverable error all images in HDD container will be closed.
1950 *
1951 * @returns VBox status code.
1952 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1953 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
1954 * @param pDisk Pointer to HDD container.
1955 * @param nImage Image number, counts from 0. 0 is always base image of container.
1956 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
1957 */
1958VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
1959 PCPDMMEDIAGEOMETRY pPCHSGeometry)
1960{
1961 int rc = VINF_SUCCESS;
1962
1963 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
1964 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
1965 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1966 do
1967 {
1968 /* sanity check */
1969 AssertBreak(VALID_PTR(pDisk), VERR_INVALID_PARAMETER);
1970 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1971
1972 /* Check arguments. */
1973 AssertMsgBreak( VALID_PTR(pPCHSGeometry)
1974 && pPCHSGeometry->cCylinders <= 16383
1975 && pPCHSGeometry->cCylinders != 0
1976 && pPCHSGeometry->cHeads <= 16
1977 && pPCHSGeometry->cHeads != 0
1978 && pPCHSGeometry->cSectors <= 63
1979 && pPCHSGeometry->cSectors != 0,
1980 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
1981 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1982 pPCHSGeometry->cSectors),
1983 rc = VERR_INVALID_PARAMETER);
1984
1985 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1986 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
1987
1988 if (pImage == pDisk->pLast)
1989 {
1990 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
1991 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
1992 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
1993 {
1994 /* Only update geometry if it is changed. Avoids similar checks
1995 * in every backend. Most of the time the new geometry is set
1996 * to the previous values, so no need to go through the hassle
1997 * of updating an image which could be opened in read-only mode
1998 * right now. */
1999 rc = pDisk->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2000 pPCHSGeometry);
2001
2002 /* Cache new geometry values in any case. */
2003 int rc2 = pDisk->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2004 &pDisk->PCHSGeometry);
2005 if (VBOX_FAILURE(rc2))
2006 {
2007 pDisk->PCHSGeometry.cCylinders = 0;
2008 pDisk->PCHSGeometry.cHeads = 0;
2009 pDisk->PCHSGeometry.cSectors = 0;
2010 }
2011 else
2012 {
2013 /* Make sure the CHS geometry is properly clipped. */
2014 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 1024);
2015 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
2016 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
2017 }
2018 }
2019 }
2020 else
2021 {
2022 PDMMEDIAGEOMETRY PCHS;
2023 rc = pDisk->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2024 &PCHS);
2025 if ( VBOX_FAILURE(rc)
2026 || pPCHSGeometry->cCylinders != PCHS.cCylinders
2027 || pPCHSGeometry->cHeads != PCHS.cHeads
2028 || pPCHSGeometry->cSectors != PCHS.cSectors)
2029 {
2030 /* Only update geometry if it is changed. Avoids similar checks
2031 * in every backend. Most of the time the new geometry is set
2032 * to the previous values, so no need to go through the hassle
2033 * of updating an image which could be opened in read-only mode
2034 * right now. */
2035 rc = pDisk->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2036 pPCHSGeometry);
2037 }
2038 }
2039 } while (0);
2040
2041 LogFlowFunc(("returns %Vrc\n", rc));
2042 return rc;
2043}
2044
2045/**
2046 * Get virtual disk LCHS geometry stored in HDD container.
2047 *
2048 * @returns VBox status code.
2049 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2050 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2051 * @param pDisk Pointer to HDD container.
2052 * @param nImage Image number, counts from 0. 0 is always base image of container.
2053 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
2054 */
2055VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2056 PPDMMEDIAGEOMETRY pLCHSGeometry)
2057{
2058 int rc = VINF_SUCCESS;
2059
2060 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
2061 pDisk, nImage, pLCHSGeometry));
2062 do
2063 {
2064 /* sanity check */
2065 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2066 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2067
2068 /* Check arguments. */
2069 AssertMsgBreak(VALID_PTR(pLCHSGeometry),
2070 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
2071 rc = VERR_INVALID_PARAMETER);
2072
2073 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2074 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2075
2076 if (pImage == pDisk->pLast)
2077 {
2078 /* Use cached information if possible. */
2079 if (pDisk->LCHSGeometry.cCylinders != 0)
2080 *pLCHSGeometry = pDisk->LCHSGeometry;
2081 else
2082 rc = VERR_VDI_GEOMETRY_NOT_SET;
2083 }
2084 else
2085 rc = pDisk->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2086 pLCHSGeometry);
2087 } while (0);
2088
2089 LogFlowFunc(("%s: %Vrc (LCHS=%u/%u/%u)\n", rc,
2090 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
2091 pDisk->LCHSGeometry.cSectors));
2092 return rc;
2093}
2094
2095/**
2096 * Store virtual disk LCHS geometry in HDD container.
2097 *
2098 * Note that in case of unrecoverable error all images in HDD container will be closed.
2099 *
2100 * @returns VBox status code.
2101 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2102 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2103 * @param pDisk Pointer to HDD container.
2104 * @param nImage Image number, counts from 0. 0 is always base image of container.
2105 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
2106 */
2107VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2108 PCPDMMEDIAGEOMETRY pLCHSGeometry)
2109{
2110 int rc = VINF_SUCCESS;
2111
2112 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
2113 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
2114 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2115 do
2116 {
2117 /* sanity check */
2118 AssertBreak(VALID_PTR(pDisk), VERR_INVALID_PARAMETER);
2119 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2120
2121 /* Check arguments. */
2122 AssertMsgBreak( VALID_PTR(pLCHSGeometry)
2123 && pLCHSGeometry->cCylinders <= 1024
2124 && pLCHSGeometry->cCylinders != 0
2125 && pLCHSGeometry->cHeads <= 255
2126 && pLCHSGeometry->cHeads != 0
2127 && pLCHSGeometry->cSectors <= 63
2128 && pLCHSGeometry->cSectors != 0,
2129 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
2130 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
2131 pLCHSGeometry->cSectors),
2132 rc = VERR_INVALID_PARAMETER);
2133
2134 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2135 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2136
2137 if (pImage == pDisk->pLast)
2138 {
2139 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
2140 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
2141 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
2142 {
2143 /* Only update geometry if it is changed. Avoids similar checks
2144 * in every backend. Most of the time the new geometry is set
2145 * to the previous values, so no need to go through the hassle
2146 * of updating an image which could be opened in read-only mode
2147 * right now. */
2148 rc = pDisk->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2149 pLCHSGeometry);
2150
2151 /* Cache new geometry values in any case. */
2152 int rc2 = pDisk->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2153 &pDisk->LCHSGeometry);
2154 if (VBOX_FAILURE(rc2))
2155 {
2156 pDisk->LCHSGeometry.cCylinders = 0;
2157 pDisk->LCHSGeometry.cHeads = 0;
2158 pDisk->LCHSGeometry.cSectors = 0;
2159 }
2160 else
2161 {
2162 /* Make sure the CHS geometry is properly clipped. */
2163 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
2164 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
2165 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
2166 }
2167 }
2168 }
2169 else
2170 {
2171 PDMMEDIAGEOMETRY LCHS;
2172 rc = pDisk->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2173 &LCHS);
2174 if ( VBOX_FAILURE(rc)
2175 || pLCHSGeometry->cCylinders != LCHS.cCylinders
2176 || pLCHSGeometry->cHeads != LCHS.cHeads
2177 || pLCHSGeometry->cSectors != LCHS.cSectors)
2178 {
2179 /* Only update geometry if it is changed. Avoids similar checks
2180 * in every backend. Most of the time the new geometry is set
2181 * to the previous values, so no need to go through the hassle
2182 * of updating an image which could be opened in read-only mode
2183 * right now. */
2184 rc = pDisk->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2185 pLCHSGeometry);
2186 }
2187 }
2188 } while (0);
2189
2190 LogFlowFunc(("returns %Vrc\n", rc));
2191 return rc;
2192}
2193
2194/**
2195 * Get version of image in HDD container.
2196 *
2197 * @returns VBox status code.
2198 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2199 * @param pDisk Pointer to HDD container.
2200 * @param nImage Image number, counts from 0. 0 is always base image of container.
2201 * @param puVersion Where to store the image version.
2202 */
2203VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
2204 unsigned *puVersion)
2205{
2206 int rc = VINF_SUCCESS;
2207
2208 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
2209 pDisk, nImage, puVersion));
2210 do
2211 {
2212 /* sanity check */
2213 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2214 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2215
2216 /* Check arguments. */
2217 AssertMsgBreak(VALID_PTR(puVersion),
2218 ("puVersion=%#p\n", puVersion),
2219 rc = VERR_INVALID_PARAMETER);
2220
2221 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2222 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2223
2224 *puVersion = pDisk->Backend->pfnGetVersion(pImage->pvBackendData);
2225 } while (0);
2226
2227 LogFlowFunc(("returns %Vrc uVersion=%#x\n", rc, *puVersion));
2228 return rc;
2229}
2230
2231/**
2232 * Get type of image in HDD container.
2233 *
2234 * @returns VBox status code.
2235 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2236 * @param pDisk Pointer to HDD container.
2237 * @param nImage Image number, counts from 0. 0 is always base image of container.
2238 * @param penmType Where to store the image type.
2239 */
2240VBOXDDU_DECL(int) VDGetImageType(PVBOXHDD pDisk, unsigned nImage,
2241 PVDIMAGETYPE penmType)
2242{
2243 int rc;
2244
2245 LogFlowFunc(("pDisk=%#p nImage=%u penmType=%#p\n",
2246 pDisk, nImage, penmType));
2247 do
2248 {
2249 /* sanity check */
2250 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2251 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2252
2253 /* Check arguments. */
2254 AssertMsgBreak(VALID_PTR(penmType),
2255 ("penmType=%#p\n", penmType),
2256 rc = VERR_INVALID_PARAMETER);
2257
2258 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2259 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2260
2261 rc = pDisk->Backend->pfnGetImageType(pImage->pvBackendData,
2262 penmType);
2263 } while (0);
2264
2265 LogFlowFunc(("returns %Vrc uenmType=%u\n", rc, *penmType));
2266 return rc;
2267}
2268
2269/**
2270 * Get flags of image in HDD container.
2271 *
2272 * @returns VBox status code.
2273 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2274 * @param pDisk Pointer to HDD container.
2275 * @param nImage Image number, counts from 0. 0 is always base image of container.
2276 * @param puImageFlags Where to store the image flags.
2277 */
2278VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
2279 unsigned *puImageFlags)
2280{
2281 int rc = VINF_SUCCESS;
2282
2283 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
2284 pDisk, nImage, puImageFlags));
2285 do
2286 {
2287 /* sanity check */
2288 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2289 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2290
2291 /* Check arguments. */
2292 AssertMsgBreak(VALID_PTR(puImageFlags),
2293 ("puImageFlags=%#p\n", puImageFlags),
2294 rc = VERR_INVALID_PARAMETER);
2295
2296 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2297 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2298
2299 *puImageFlags = pDisk->Backend->pfnGetImageFlags(pImage->pvBackendData);
2300 } while (0);
2301
2302 LogFlowFunc(("returns %Vrc uImageFlags=%#x\n", rc, *puImageFlags));
2303 return rc;
2304}
2305
2306/**
2307 * Get open flags of image in HDD container.
2308 *
2309 * @returns VBox status code.
2310 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2311 * @param pDisk Pointer to HDD container.
2312 * @param nImage Image number, counts from 0. 0 is always base image of container.
2313 * @param puOpenFlags Where to store the image open flags.
2314 */
2315VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2316 unsigned *puOpenFlags)
2317{
2318 int rc = VINF_SUCCESS;
2319
2320 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
2321 pDisk, nImage, puOpenFlags));
2322 do
2323 {
2324 /* sanity check */
2325 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2326 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2327
2328 /* Check arguments. */
2329 AssertMsgBreak(VALID_PTR(puOpenFlags),
2330 ("puOpenFlags=%#p\n", puOpenFlags),
2331 rc = VERR_INVALID_PARAMETER);
2332
2333 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2334 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2335
2336 *puOpenFlags = pDisk->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2337 } while (0);
2338
2339 LogFlowFunc(("returns %Vrc uOpenFlags=%#x\n", rc, *puOpenFlags));
2340 return rc;
2341}
2342
2343/**
2344 * Set open flags of image in HDD container.
2345 * This operation may cause file locking changes and/or files being reopened.
2346 * Note that in case of unrecoverable error all images in HDD container will be closed.
2347 *
2348 * @returns VBox status code.
2349 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2350 * @param pDisk Pointer to HDD container.
2351 * @param nImage Image number, counts from 0. 0 is always base image of container.
2352 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
2353 */
2354VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2355 unsigned uOpenFlags)
2356{
2357 int rc;
2358
2359 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
2360 do
2361 {
2362 /* sanity check */
2363 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2364 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2365
2366 /* Check arguments. */
2367 AssertMsgBreak((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
2368 ("uOpenFlags=%#x\n", uOpenFlags),
2369 rc = VERR_INVALID_PARAMETER);
2370
2371 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2372 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2373
2374 rc = pDisk->Backend->pfnSetOpenFlags(pImage->pvBackendData,
2375 uOpenFlags);
2376 } while (0);
2377
2378 LogFlowFunc(("returns %Vrc\n", rc));
2379 return rc;
2380}
2381
2382/**
2383 * Get base filename of image in HDD container. Some image formats use
2384 * other filenames as well, so don't use this for anything but informational
2385 * purposes.
2386 *
2387 * @returns VBox status code.
2388 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2389 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
2390 * @param pDisk Pointer to HDD container.
2391 * @param nImage Image number, counts from 0. 0 is always base image of container.
2392 * @param pszFilename Where to store the image file name.
2393 * @param cbFilename Size of buffer pszFilename points to.
2394 */
2395VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
2396 char *pszFilename, unsigned cbFilename)
2397{
2398 int rc;
2399
2400 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
2401 pDisk, nImage, pszFilename, cbFilename));
2402 do
2403 {
2404 /* sanity check */
2405 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2406 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2407
2408 /* Check arguments. */
2409 AssertMsgBreak(VALID_PTR(pszFilename) && *pszFilename,
2410 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
2411 rc = VERR_INVALID_PARAMETER);
2412 AssertMsgBreak(cbFilename,
2413 ("cbFilename=%u\n", cbFilename),
2414 rc = VERR_INVALID_PARAMETER);
2415
2416 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2417 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2418
2419 size_t cb = strlen(pImage->pszFilename);
2420 if (cb <= cbFilename)
2421 {
2422 strcpy(pszFilename, pImage->pszFilename);
2423 rc = VINF_SUCCESS;
2424 }
2425 else
2426 {
2427 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
2428 pszFilename[cbFilename - 1] = '\0';
2429 rc = VERR_BUFFER_OVERFLOW;
2430 }
2431 } while (0);
2432
2433 LogFlowFunc(("returns %Vrc, pszFilename=\"%s\"\n", rc, pszFilename));
2434 return rc;
2435}
2436
2437/**
2438 * Get the comment line of image in HDD container.
2439 *
2440 * @returns VBox status code.
2441 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2442 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
2443 * @param pDisk Pointer to HDD container.
2444 * @param nImage Image number, counts from 0. 0 is always base image of container.
2445 * @param pszComment Where to store the comment string of image. NULL is ok.
2446 * @param cbComment The size of pszComment buffer. 0 is ok.
2447 */
2448VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
2449 char *pszComment, unsigned cbComment)
2450{
2451 int rc;
2452
2453 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
2454 pDisk, nImage, pszComment, cbComment));
2455 do
2456 {
2457 /* sanity check */
2458 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2459 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2460
2461 /* Check arguments. */
2462 AssertMsgBreak(VALID_PTR(pszComment),
2463 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
2464 rc = VERR_INVALID_PARAMETER);
2465 AssertMsgBreak(cbComment,
2466 ("cbComment=%u\n", cbComment),
2467 rc = VERR_INVALID_PARAMETER);
2468
2469 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2470 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2471
2472 rc = pDisk->Backend->pfnGetComment(pImage->pvBackendData, pszComment,
2473 cbComment);
2474 } while (0);
2475
2476 LogFlowFunc(("returns %Vrc, pszComment=\"%s\"\n", rc, pszComment));
2477 return rc;
2478}
2479
2480/**
2481 * Changes the comment line of image in HDD container.
2482 *
2483 * @returns VBox status code.
2484 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2485 * @param pDisk Pointer to HDD container.
2486 * @param nImage Image number, counts from 0. 0 is always base image of container.
2487 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
2488 */
2489VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
2490 const char *pszComment)
2491{
2492 int rc;
2493
2494 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
2495 pDisk, nImage, pszComment, pszComment));
2496 do
2497 {
2498 /* sanity check */
2499 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2500 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2501
2502 /* Check arguments. */
2503 AssertMsgBreak(VALID_PTR(pszComment) || pszComment == NULL,
2504 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
2505 rc = VERR_INVALID_PARAMETER);
2506
2507 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2508 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2509
2510 rc = pDisk->Backend->pfnSetComment(pImage->pvBackendData, pszComment);
2511 } while (0);
2512
2513 LogFlowFunc(("returns %Vrc\n", rc));
2514 return rc;
2515}
2516
2517
2518/**
2519 * Get UUID of image in HDD container.
2520 *
2521 * @returns VBox status code.
2522 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2523 * @param pDisk Pointer to HDD container.
2524 * @param nImage Image number, counts from 0. 0 is always base image of container.
2525 * @param pUuid Where to store the image creation UUID.
2526 */
2527VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
2528{
2529 int rc;
2530
2531 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
2532 do
2533 {
2534 /* sanity check */
2535 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2536 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2537
2538 /* Check arguments. */
2539 AssertMsgBreak(VALID_PTR(pUuid),
2540 ("pUuid=%#p\n", pUuid),
2541 rc = VERR_INVALID_PARAMETER);
2542
2543 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2544 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2545
2546 rc = pDisk->Backend->pfnGetUuid(pImage->pvBackendData, pUuid);
2547 } while (0);
2548
2549 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
2550 return rc;
2551}
2552
2553/**
2554 * Set the image's UUID. Should not be used by normal applications.
2555 *
2556 * @returns VBox status code.
2557 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2558 * @param pDisk Pointer to HDD container.
2559 * @param nImage Image number, counts from 0. 0 is always base image of container.
2560 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
2561 */
2562VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
2563{
2564 int rc;
2565
2566 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
2567 pDisk, nImage, pUuid, pUuid));
2568 do
2569 {
2570 /* sanity check */
2571 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2572 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2573
2574 AssertMsgBreak(VALID_PTR(pUuid) || pUuid == NULL,
2575 ("pUuid=%#p\n", pUuid),
2576 rc = VERR_INVALID_PARAMETER);
2577
2578 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2579 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2580
2581 RTUUID Uuid;
2582 if (!pUuid)
2583 {
2584 RTUuidCreate(&Uuid);
2585 pUuid = &Uuid;
2586 }
2587 rc = pDisk->Backend->pfnSetUuid(pImage->pvBackendData, pUuid);
2588 } while (0);
2589
2590 LogFlowFunc(("returns %Vrc\n", rc));
2591 return rc;
2592}
2593
2594/**
2595 * Get last modification UUID of image in HDD container.
2596 *
2597 * @returns VBox status code.
2598 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2599 * @param pDisk Pointer to HDD container.
2600 * @param nImage Image number, counts from 0. 0 is always base image of container.
2601 * @param pUuid Where to store the image modification UUID.
2602 */
2603VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
2604{
2605 int rc = VINF_SUCCESS;
2606
2607 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
2608 do
2609 {
2610 /* sanity check */
2611 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2612 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2613
2614 /* Check arguments. */
2615 AssertMsgBreak(VALID_PTR(pUuid),
2616 ("pUuid=%#p\n", pUuid),
2617 rc = VERR_INVALID_PARAMETER);
2618
2619 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2620 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2621
2622 rc = pDisk->Backend->pfnGetModificationUuid(pImage->pvBackendData,
2623 pUuid);
2624 } while (0);
2625
2626 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
2627 return rc;
2628}
2629
2630/**
2631 * Set the image's last modification UUID. Should not be used by normal applications.
2632 *
2633 * @returns VBox status code.
2634 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2635 * @param pDisk Pointer to HDD container.
2636 * @param nImage Image number, counts from 0. 0 is always base image of container.
2637 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
2638 */
2639VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
2640{
2641 int rc;
2642
2643 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
2644 pDisk, nImage, pUuid, pUuid));
2645 do
2646 {
2647 /* sanity check */
2648 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2649 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2650
2651 /* Check arguments. */
2652 AssertMsgBreak(VALID_PTR(pUuid) || pUuid == NULL,
2653 ("pUuid=%#p\n", pUuid),
2654 rc = VERR_INVALID_PARAMETER);
2655
2656 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2657 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2658
2659 RTUUID Uuid;
2660 if (!pUuid)
2661 {
2662 RTUuidCreate(&Uuid);
2663 pUuid = &Uuid;
2664 }
2665 rc = pDisk->Backend->pfnSetModificationUuid(pImage->pvBackendData,
2666 pUuid);
2667 } while (0);
2668
2669 LogFlowFunc(("returns %Vrc\n", rc));
2670 return rc;
2671}
2672
2673/**
2674 * Get parent UUID of image in HDD container.
2675 *
2676 * @returns VBox status code.
2677 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2678 * @param pDisk Pointer to HDD container.
2679 * @param nImage Image number, counts from 0. 0 is always base image of container.
2680 * @param pUuid Where to store the parent image UUID.
2681 */
2682VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
2683 PRTUUID pUuid)
2684{
2685 int rc = VINF_SUCCESS;
2686
2687 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
2688 do
2689 {
2690 /* sanity check */
2691 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2692 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2693
2694 /* Check arguments. */
2695 AssertMsgBreak(VALID_PTR(pUuid),
2696 ("pUuid=%#p\n", pUuid),
2697 rc = VERR_INVALID_PARAMETER);
2698
2699 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2700 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2701
2702 rc = pDisk->Backend->pfnGetParentUuid(pImage->pvBackendData, pUuid);
2703 } while (0);
2704
2705 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
2706 return rc;
2707}
2708
2709/**
2710 * Set the image's parent UUID. Should not be used by normal applications.
2711 *
2712 * @returns VBox status code.
2713 * @param pDisk Pointer to HDD container.
2714 * @param nImage Image number, counts from 0. 0 is always base image of container.
2715 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
2716 */
2717VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
2718 PCRTUUID pUuid)
2719{
2720 int rc;
2721
2722 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
2723 pDisk, nImage, pUuid, pUuid));
2724 do
2725 {
2726 /* sanity check */
2727 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2728 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2729
2730 /* Check arguments. */
2731 AssertMsgBreak(VALID_PTR(pUuid) || pUuid == NULL,
2732 ("pUuid=%#p\n", pUuid),
2733 rc = VERR_INVALID_PARAMETER);
2734
2735 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2736 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2737
2738 RTUUID Uuid;
2739 if (!pUuid)
2740 {
2741 RTUuidCreate(&Uuid);
2742 pUuid = &Uuid;
2743 }
2744 rc = pDisk->Backend->pfnSetParentUuid(pImage->pvBackendData, pUuid);
2745 } while (0);
2746
2747 LogFlowFunc(("returns %Vrc\n", rc));
2748 return rc;
2749}
2750
2751
2752/**
2753 * Debug helper - dumps all opened images in HDD container into the log file.
2754 *
2755 * @param pDisk Pointer to HDD container.
2756 */
2757VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
2758{
2759 do
2760 {
2761 /* sanity check */
2762 AssertBreak(VALID_PTR(pDisk), );
2763 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2764
2765 RTLogPrintf("--- Dumping VD Disk, Backend=%s, Images=%u\n",
2766 pDisk->Backend->pszBackendName, pDisk->cImages);
2767 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
2768 {
2769 RTLogPrintf("Dumping VD image \"%s\"\n", pImage->pszFilename);
2770 pDisk->Backend->pfnDump(pImage->pvBackendData);
2771 }
2772 } while (0);
2773}
2774
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