VirtualBox

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

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

Introduce a new harddisk object which supports virtual harddisk through plugins

File size: 70.6 KB
Line 
1/** $Id$ */
2/** @file
3 * VBox HDD Container implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2007 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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * 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
36#include "VBoxHDD-newInternal.h"
37
38
39#define VBOXHDDDISK_SIGNATURE 0x6f0e2a7d
40
41
42/**
43 * VBox HDD Container image descriptor.
44 */
45typedef struct VDIMAGE
46{
47 /** Link to parent image descriptor, if any. */
48 struct VDIMAGE *pPrev;
49 /** Link to child image descriptor, if any. */
50 struct VDIMAGE *pNext;
51 /** Container base filename. (UTF-8) */
52 char *pszFilename;
53 /** Data managed by the backend which keeps the actual info. */
54 void *pvBackendData;
55 /** Image open flags (only those handled generically in this code and which
56 * the backends will never ever see). */
57 unsigned uOpenFlags;
58} VDIMAGE, *PVDIMAGE;
59
60/**
61 * uModified bit flags.
62 */
63#define VD_IMAGE_MODIFIED_FLAG BIT(0)
64#define VD_IMAGE_MODIFIED_FIRST BIT(1)
65#define VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE BIT(2)
66
67
68/**
69 * VBox HDD Container main structure, private part.
70 */
71struct VBOXHDD
72{
73 /** Structure signature (VBOXHDDDISK_SIGNATURE). */
74 uint32_t u32Signature;
75
76 /** Number of opened images. */
77 unsigned cImages;
78
79 /** Base image. */
80 PVDIMAGE pBase;
81
82 /** Last opened image in the chain.
83 * The same as pBase if only one image is used. */
84 PVDIMAGE pLast;
85
86 /** Flags representing the modification state. */
87 unsigned uModified;
88
89 /** Cached size of this disk. */
90 uint64_t cbSize;
91 /** Cached CHS geometry for this disk, cylinders. */
92 unsigned cCylinders;
93 /** Cached CHS geometry for this disk, heads. */
94 unsigned cHeads;
95 /** Cached CHS geometry for this disk, sectors. */
96 unsigned cSectors;
97 /** Cached translation mode for this disk. */
98 PDMBIOSTRANSLATION enmTranslation;
99
100 /** Error message processing callback. */
101 PFNVDERROR pfnError;
102 /** Opaque data for error callback. */
103 void *pvErrorUser;
104
105 /** Handle for the shared object / DLL. */
106 RTLDRMOD hPlugin;
107 /** Function pointers for the various backend methods. */
108 PVBOXHDDBACKEND Backend;
109};
110
111
112typedef struct
113{
114 const char *pszBackendName;
115 PVBOXHDDBACKEND Backend;
116} VBOXHDDBACKENDENTRY;
117
118
119extern VBOXHDDBACKEND g_VmdkBackend;
120
121
122static const VBOXHDDBACKENDENTRY aBackends[] =
123{
124 { "VMDK", &g_VmdkBackend },
125 { NULL, NULL }
126};
127
128
129/**
130 * internal: issue early error message.
131 */
132static int vdEarlyError(PFNVDERROR pfnError, void *pvErrorUser, int rc,
133 RT_SRC_POS_DECL, const char *pszFormat, ...)
134{
135 va_list va;
136 va_start(va, pszFormat);
137 if (pfnError)
138 pfnError(pvErrorUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
139 va_end(va);
140 return rc;
141}
142
143/**
144 * internal: issue error message.
145 */
146static int vdError(PVBOXHDD pDisk, int rc, RT_SRC_POS_DECL,
147 const char *pszFormat, ...)
148{
149 va_list va;
150 va_start(va, pszFormat);
151 if (pDisk->pfnError)
152 pDisk->pfnError(pDisk->pvErrorUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
153 va_end(va);
154 return rc;
155}
156
157/**
158 * internal: add image structure to the end of images list.
159 */
160static void vdAddImageToList(PVBOXHDD pDisk, PVDIMAGE pImage)
161{
162 pImage->pPrev = NULL;
163 pImage->pNext = NULL;
164
165 if (pDisk->pBase)
166 {
167 Assert(pDisk->cImages > 0);
168 pImage->pPrev = pDisk->pLast;
169 pDisk->pLast->pNext = pImage;
170 pDisk->pLast = pImage;
171 }
172 else
173 {
174 Assert(pDisk->cImages == 0);
175 pDisk->pBase = pImage;
176 pDisk->pLast = pImage;
177 }
178
179 pDisk->cImages++;
180}
181
182/**
183 * internal: remove image structure from the images list.
184 */
185static void vdRemoveImageFromList(PVBOXHDD pDisk, PVDIMAGE pImage)
186{
187 Assert(pDisk->cImages > 0);
188
189 if (pImage->pPrev)
190 pImage->pPrev->pNext = pImage->pNext;
191 else
192 pDisk->pBase = pImage->pNext;
193
194 if (pImage->pNext)
195 pImage->pNext->pPrev = pImage->pPrev;
196 else
197 pDisk->pLast = pImage->pPrev;
198
199 pImage->pPrev = NULL;
200 pImage->pNext = NULL;
201
202 pDisk->cImages--;
203}
204
205/**
206 * internal: find image by index into the images list.
207 */
208static PVDIMAGE vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
209{
210 PVDIMAGE pImage = pDisk->pBase;
211 while (pImage && nImage)
212 {
213 pImage = pImage->pNext;
214 nImage--;
215 }
216 return pImage;
217}
218
219/**
220 * internal: read the specified amount of data in whatever blocks the backend
221 * will give us.
222 */
223static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
224 void *pvBuf, size_t cbRead)
225{
226 int rc;
227 size_t cbThisRead;
228 PVDIMAGE pCurrImage;
229
230 /* Loop until all read. */
231 do
232 {
233 /* Search for image with allocated block. Do not attempt to read more
234 * than the previous reads marked as valid. Otherwise this would return
235 * stale data when different block sizes are used for the images. */
236 cbThisRead = cbRead;
237 rc = VINF_VDI_BLOCK_FREE;
238 for (pCurrImage = pImage;
239 pCurrImage != NULL && rc == VINF_VDI_BLOCK_FREE;
240 pCurrImage = pCurrImage->pPrev)
241 {
242 rc = pDisk->Backend->pfnRead(pCurrImage->pvBackendData, uOffset,
243 pvBuf, cbThisRead, &cbThisRead);
244 }
245
246 /* No image in the chain contains the data for the block. */
247 if (rc == VINF_VDI_BLOCK_FREE)
248 {
249 memset(pvBuf, '\0', cbThisRead);
250 rc = VINF_SUCCESS;
251 }
252
253 cbRead -= cbThisRead;
254 uOffset += cbThisRead;
255 pvBuf = (char *)pvBuf + cbThisRead;
256 } while (cbRead != 0 && VBOX_SUCCESS(rc));
257
258 return rc;
259}
260
261/**
262 * internal: mark the disk as not modified.
263 */
264static void vdResetModifiedFlag(PVBOXHDD pDisk)
265{
266 if (pDisk->uModified & VD_IMAGE_MODIFIED_FLAG)
267 {
268 /* generate new last-modified uuid */
269 if (!(pDisk->uModified | VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
270 {
271 RTUUID Uuid;
272
273 RTUuidCreate(&Uuid);
274 pDisk->Backend->pfnSetModificationUuid(pDisk->pLast->pvBackendData,
275 &Uuid);
276 }
277
278 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FLAG;
279 }
280}
281
282/**
283 * internal: mark the disk as modified.
284 */
285static void vdSetModifiedFlag(PVBOXHDD pDisk)
286{
287 pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
288 if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
289 {
290 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
291
292 /* First modify, so create a UUID and ensure it's written to disk. */
293 vdResetModifiedFlag(pDisk);
294
295 if (!(pDisk->uModified | VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
296 pDisk->Backend->pfnFlush(pDisk->pLast->pvBackendData);
297 }
298}
299
300/**
301 * internal: write a complete block (only used for diff images), taking the
302 * remaining data from parent images. This implementation does not optimize
303 * anything (except that it tries to read only that portions from parent
304 * images that are really needed).
305 */
306static int vdWriteHelperStandard(PVBOXHDD pDisk, PVDIMAGE pImage,
307 uint64_t uOffset, size_t cbWrite,
308 size_t cbThisWrite, size_t cbPreRead,
309 size_t cbPostRead, const void *pvBuf,
310 void *pvTmp)
311{
312 int rc = VINF_SUCCESS;
313
314 /* Read the data that goes before the write to fill the block. */
315 if (cbPreRead)
316 {
317 rc = vdReadHelper(pDisk, pImage->pPrev, uOffset - cbPreRead,
318 pvTmp, cbPreRead);
319 if (VBOX_FAILURE(rc))
320 return rc;
321 }
322
323 /* Copy the data to the right place in the buffer. */
324 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
325
326 /* Read the data that goes after the write to fill the block. */
327 if (cbPostRead)
328 {
329 /* If we have data to be written, use that instead of reading
330 * data from the image. */
331 size_t cbWriteCopy;
332 if (cbWrite > cbThisWrite)
333 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
334 else
335 cbWriteCopy = 0;
336 /* Figure out how much we cannnot read from the image, because
337 * the last block to write might exceed the nominal size of the
338 * image for technical reasons. */
339 size_t cbFill;
340 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
341 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
342 else
343 cbFill = 0;
344 /* The rest must be read from the image. */
345 size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
346
347 /* Now assemble the remaining data. */
348 if (cbWriteCopy)
349 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
350 (char *)pvBuf + cbThisWrite, cbWriteCopy);
351 if (cbReadImage)
352 rc = vdReadHelper(pDisk, pImage->pPrev,
353 uOffset + cbThisWrite + cbWriteCopy,
354 (char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy,
355 cbReadImage);
356 if (VBOX_FAILURE(rc))
357 return rc;
358 /* Zero out the remainder of this block. Will never be visible, as this
359 * is beyond the limit of the image. */
360 if (cbFill)
361 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
362 '\0', cbFill);
363 }
364
365 /* Write the full block to the virtual disk. */
366 rc = pDisk->Backend->pfnWrite(pImage->pvBackendData,
367 uOffset - cbPreRead, pvTmp,
368 cbPreRead + cbThisWrite + cbPostRead,
369 NULL,
370 &cbPreRead, &cbPostRead);
371 Assert(rc != VINF_VDI_BLOCK_FREE);
372 Assert(cbPreRead == 0);
373 Assert(cbPostRead == 0);
374
375 return rc;
376}
377
378/**
379 * internal: write a complete block (only used for diff images), taking the
380 * remaining data from parent images. This implementation optimized out writes
381 * that do not change the data relative to the state as of the parent images.
382 * All backends which support differential/growing images support this.
383 */
384static int vdWriteHelperOptimized(PVBOXHDD pDisk, PVDIMAGE pImage,
385 uint64_t uOffset, size_t cbWrite,
386 size_t cbThisWrite, size_t cbPreRead,
387 size_t cbPostRead, const void *pvBuf,
388 void *pvTmp)
389{
390 size_t cbFill = 0;
391 size_t cbWriteCopy = 0;
392 size_t cbReadImage = 0;
393 int rc;
394
395 if (cbPostRead)
396 {
397 /* Figure out how much we cannnot read from the image, because
398 * the last block to write might exceed the nominal size of the
399 * image for technical reasons. */
400 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
401 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
402
403 /* If we have data to be written, use that instead of reading
404 * data from the image. */
405 if (cbWrite > cbThisWrite)
406 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
407
408 /* The rest must be read from the image. */
409 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
410 }
411
412 /* Read the entire data of the block so that we can compare whether it will
413 * be modified by the write or not. */
414 rc = vdReadHelper(pDisk, pImage->pPrev, uOffset - cbPreRead,
415 pvTmp, cbPreRead + cbThisWrite + cbPostRead - cbFill);
416 if (VBOX_FAILURE(rc))
417 return rc;
418
419 /* Check if the write would modify anything in this block. */
420 if ( !memcmp((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite)
421 && (!cbWriteCopy || !memcmp((char *)pvTmp + cbPreRead + cbThisWrite,
422 (char *)pvBuf + cbThisWrite, cbWriteCopy)))
423 {
424 /* Block is completely unchanged, so no need to write anything. */
425 return VINF_SUCCESS;
426 }
427
428 /* Copy the data to the right place in the buffer. */
429 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
430
431 /* Handle the data that goes after the write to fill the block. */
432 if (cbPostRead)
433 {
434 /* Now assemble the remaining data. */
435 if (cbWriteCopy)
436 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
437 (char *)pvBuf + cbThisWrite, cbWriteCopy);
438 /* Zero out the remainder of this block. Will never be visible, as this
439 * is beyond the limit of the image. */
440 if (cbFill)
441 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
442 '\0', cbFill);
443 }
444
445 /* Write the full block to the virtual disk. */
446 rc = pDisk->Backend->pfnWrite(pImage->pvBackendData,
447 uOffset - cbPreRead, pvTmp,
448 cbPreRead + cbThisWrite + cbPostRead,
449 NULL,
450 &cbPreRead, &cbPostRead);
451 Assert(rc != VINF_VDI_BLOCK_FREE);
452 Assert(cbPreRead == 0);
453 Assert(cbPostRead == 0);
454
455 return rc;
456}
457
458/**
459 * Allocates and initializes an empty VBox HDD container.
460 * No image files are opened.
461 *
462 * @returns VBox status code.
463 * @param pszBackend Name of the image file backend to use.
464 * @param pfnError Callback for setting extended error information.
465 * @param pvErrorUser Opaque parameter for pfnError.
466 * @param ppDisk Where to store the reference to the VBox HDD container.
467 */
468VBOXDDU_DECL(int) VDCreate(const char *pszBackend, PFNVDERROR pfnError,
469 void *pvErrorUser, PVBOXHDD *ppDisk)
470{
471 int rc = VINF_SUCCESS;
472 PVBOXHDDBACKEND pBackend = NULL;
473 PVBOXHDD pDisk = NULL;
474
475 /* Passing an error callback is strictly not necessary any more. Any code
476 * calling the HDD container functions should provide one, as otherwise
477 * many detailed error messages will go unnoticed. If you find a situation
478 * where you get no sensible error message from this code but you think
479 * there should be one, shout loudly. There are no error messages for rare
480 * and obvious error codes such as VERR_NO_MEMORY, and for situations which
481 * the user cannot be made responsible for, such as program bugs causing
482 * parameter checks to fail etc. */
483 Assert(pfnError);
484
485 /* Find backend. */
486 for (unsigned i = 0; aBackends[i].pszBackendName != NULL; i++)
487 {
488 if (!strcmp(pszBackend, aBackends[i].pszBackendName))
489 {
490 pBackend = aBackends[i].Backend;
491 break;
492 }
493 }
494
495 /* If no static backend is found try loading a shared module with pszBackend as filename. */
496 if (!pBackend)
497 {
498 RTLDRMOD hPlugin;
499 char *pszPluginName;
500 int cbPluginName;
501
502 /* HDD Format Plugins have VBoxHDD as prefix, thatswhy we have to prepend it.
503 * @todo: find out what to do if filenames are case sensitive.
504 */
505 cbPluginName = RTStrAPrintf(&pszPluginName, "%s%s", VBOX_HDDFORMAT_PLUGIN_PREFIX, pszBackend);
506 if (cbPluginName == -1)
507 {
508 rc = VERR_NO_MEMORY;
509 }
510 else
511 {
512 /* try to load the plugin (RTldrLoad appends the suffix for the shared object/DLL). */
513 rc = RTLdrLoad(pszPluginName, &hPlugin);
514 if (VBOX_SUCCESS(rc))
515 {
516 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad;
517
518 rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad);
519 if (VBOX_FAILURE(rc))
520 {
521 Log(("%s: Error resolving the entry point %s, rc = %d, pfnHDDFormat = %p\n", VBOX_HDDFORMAT_LOAD_NAME, rc, pfnHDDFormatLoad));
522 if (VBOX_SUCCESS(rc))
523 rc = VERR_SYMBOL_NOT_FOUND;
524 }
525 else
526 {
527 /* Get the function table. */
528 pBackend = (PVBOXHDDBACKEND)RTMemAllocZ(sizeof(VBOXHDDBACKEND));
529 if (!pBackend)
530 {
531 rc = VERR_NO_MEMORY;
532 }
533 else
534 {
535 pBackend->cbSize = sizeof(VBOXHDDBACKEND);
536 rc = pfnHDDFormatLoad(pBackend);
537 if (VBOX_FAILURE(rc))
538 {
539 RTMemFree(pBackend);
540 pBackend = NULL;
541 }
542 }
543 }
544 }
545 RTStrFree(pszPluginName);
546 }
547 }
548
549 if (pBackend)
550 {
551 pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
552 if (pDisk)
553 {
554 pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
555 pDisk->cImages = 0;
556 pDisk->pBase = NULL;
557 pDisk->pLast = NULL;
558 pDisk->cbSize = 0;
559 pDisk->cCylinders = 0;
560 pDisk->cHeads = 0;
561 pDisk->cSectors = 0;
562 pDisk->pfnError = pfnError;
563 pDisk->pvErrorUser = pvErrorUser;
564 pDisk->Backend = pBackend;
565 pDisk->hPlugin = NIL_RTLDRMOD;
566 *ppDisk = pDisk;
567 }
568 else
569 rc = VERR_NO_MEMORY;
570 }
571 else
572 rc = vdEarlyError(pfnError, pvErrorUser, VERR_INVALID_PARAMETER,
573 RT_SRC_POS, "VD: unknown backend name '%s'",
574 pszBackend);
575
576 LogFlow(("%s: returns %Vrc (pDisk=%#p)\n", __FUNCTION__, rc, pDisk));
577 return rc;
578}
579
580/**
581 * Try to get the backend name which can use this image.
582 *
583 * @returns VBox status code.
584 * @param pszFilename Name of the image file for which the backend is queried.
585 * @param ppszFormat Where to store the name of the plugin.
586 */
587
588VBOXDDU_DECL(int) VDGetFormat(const char *pszFilename, char **ppszFormat)
589{
590 char pszProgramPath[1024]; /* Far too much I think but to be on the safe side. */
591 char *pszPluginFilter;
592 PRTDIR pPluginDir = NULL;
593 PRTDIRENTRY pPluginDirEntry = NULL;
594 unsigned cbPluginDirEntry;
595 int rc = VERR_NOT_SUPPORTED;
596 bool fPluginFound = false;
597
598 if (!ppszFormat)
599 return VERR_INVALID_PARAMETER;
600
601 memset(pszProgramPath, 0, 1024);
602 rc = RTPathProgram(pszProgramPath, 1024);
603 if (VBOX_FAILURE(rc))
604 {
605 return rc;
606 }
607
608 /* To get all entries with VBoxHDD as prefix. */
609 rc = RTStrAPrintf(&pszPluginFilter, "%s/%s*", pszProgramPath, VBOX_HDDFORMAT_PLUGIN_PREFIX);
610 if (VBOX_FAILURE(rc))
611 {
612 RTStrFree(pszProgramPath);
613 rc = VERR_NO_MEMORY;
614 return rc;
615 }
616
617 /* The plugins are in the same directory as the program. */
618 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
619 if (VBOX_FAILURE(rc))
620 goto out;
621
622 pPluginDirEntry = (PRTDIRENTRY)RTMemAllocZ(sizeof(RTDIRENTRY));
623 if (!pPluginDir)
624 {
625 rc = VERR_NO_MEMORY;
626 goto out;
627 }
628
629 while ((rc = RTDirRead(pPluginDir, pPluginDirEntry, &cbPluginDirEntry)) != VERR_NO_MORE_FILES)
630 {
631 RTLDRMOD hPlugin = NIL_RTLDRMOD;
632 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad = NULL;
633 PVBOXHDDBACKEND pBackend = NULL;
634
635 if (rc == VERR_BUFFER_OVERFLOW)
636 {
637 /* allocate new buffer. */
638 RTMemFree(pPluginDirEntry);
639 pPluginDirEntry = (PRTDIRENTRY)RTMemAllocZ(cbPluginDirEntry);
640 /* Retry. */
641 rc = RTDirRead(pPluginDir, pPluginDirEntry, &cbPluginDirEntry);
642 if (VBOX_FAILURE(rc))
643 break;
644 }
645 else if (VBOX_FAILURE(rc))
646 break;
647
648 /* We got the new entry. */
649 if (pPluginDirEntry->enmType != RTDIRENTRYTYPE_FILE)
650 continue;
651
652 rc = RTLdrLoad(pPluginDirEntry->szName, &hPlugin);
653 if (VBOX_SUCCESS(rc))
654 {
655 rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad);
656 if (VBOX_FAILURE(rc) || !pfnHDDFormatLoad)
657 {
658 Log(("%s: Error resolving the entry point %s, rc = %d, pfnHDDFormat = %p\n", VBOX_HDDFORMAT_LOAD_NAME, rc, pfnHDDFormatLoad));
659 if (VBOX_SUCCESS(rc))
660 rc = VERR_SYMBOL_NOT_FOUND;
661 }
662 else
663 {
664 /* Get the function table. */
665 pBackend = (PVBOXHDDBACKEND)RTMemAllocZ(sizeof(VBOXHDDBACKEND));
666 if (!pBackend)
667 {
668 rc = VERR_NO_MEMORY;
669 }
670 else
671 {
672 pBackend->cbSize = sizeof(VBOXHDDBACKEND);
673 rc = pfnHDDFormatLoad(pBackend);
674 if (VBOX_FAILURE(rc))
675 {
676 RTMemFree(pBackend);
677 pBackend = NULL;
678 }
679
680 /* Check if the plugin can handle this file. */
681 rc = pBackend->pfnCheckIfValid(pszFilename);
682 if (VBOX_FAILURE(rc))
683 {
684 RTMemFree(pBackend);
685 RTLdrClose(hPlugin);
686 }
687 else
688 {
689 RTMemFree(pBackend);
690 RTLdrClose(hPlugin);
691 fPluginFound = true;
692
693 /* Report the format name. */
694 char *pszName = pPluginDirEntry->szName + VBOX_HDDFORMAT_PLUGIN_PREFIX_LENGTH; /* Point to the rest after the prefix. */
695 char *pszFormat = NULL;
696 unsigned cbFormat = 0;
697
698 while((*pszName != '.') && (*pszName != '\0'))
699 {
700 cbFormat++;
701 pszName++;
702 }
703
704 pszName = pPluginDirEntry->szName + VBOX_HDDFORMAT_PLUGIN_PREFIX_LENGTH;
705
706 /* Copy the name into the new string. */
707 pszFormat = (char *)RTMemAllocZ(cbFormat+1);
708
709 if (!pszFormat)
710 {
711 rc = VERR_NO_MEMORY;
712 break;
713 }
714
715 memcpy(pszFormat, pszName, cbFormat);
716
717 *ppszFormat = pszFormat;
718
719 break;
720 }
721 }
722 }
723 }
724 }
725
726out:
727 if (pPluginDirEntry)
728 RTMemFree(pPluginDirEntry);
729 if (pPluginDir)
730 RTDirClose(pPluginDir);
731
732 RTStrFree(pszPluginFilter);
733 RTStrFree(pszProgramPath);
734
735 if ((fPluginFound == true) && (*ppszFormat != NULL))
736 rc = VINF_SUCCESS;
737
738 return rc;
739}
740
741/**
742 * Destroys the VBox HDD container.
743 * If container has opened image files they will be closed.
744 *
745 * @param pDisk Pointer to VBox HDD container.
746 */
747VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk)
748{
749 LogFlow(("%s: pDisk=%#p\n", __FUNCTION__, pDisk));
750 /* sanity check */
751 Assert(pDisk);
752 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
753
754 if (pDisk)
755 {
756 VDCloseAll(pDisk);
757 if (pDisk->hPlugin != NIL_RTLDRMOD)
758 {
759 RTLdrClose(pDisk->hPlugin);
760 pDisk->hPlugin = NIL_RTLDRMOD;
761 }
762 RTMemFree(pDisk->Backend);
763 RTMemFree(pDisk);
764 }
765}
766
767/**
768 * Opens an image file.
769 *
770 * The first opened image file in a HDD container must have a base image type,
771 * others (next opened images) must be a differencing or undo images.
772 * Linkage is checked for differencing image to be in consistence with the previously opened image.
773 * When another differencing image is opened and the last image was opened in read/write access
774 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
775 * other processes to use images in read-only mode too.
776 *
777 * Note that the image can be opened in read-only mode if a read/write open is not possible.
778 * Use VDIsReadOnly to check open mode.
779 *
780 * @returns VBox status code.
781 * @param pDisk Pointer to VBox HDD container.
782 * @param pszFilename Name of the image file to open.
783 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
784 */
785VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszFilename,
786 unsigned uOpenFlags)
787{
788 int rc = VINF_SUCCESS;
789 LogFlow(("%s: pszFilename=\"%s\" uOpenFlags=%#x\n", __FUNCTION__,
790 pszFilename, uOpenFlags));
791 /* sanity check */
792 Assert(pDisk);
793 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
794
795 /* Check arguments. */
796 if ( !pszFilename
797 || *pszFilename == '\0'
798 || (uOpenFlags & ~VD_OPEN_FLAGS_MASK))
799 {
800 AssertMsgFailed(("Invalid arguments: pszFilename=%#p uOpenFlags=%#x\n", pszFilename, uOpenFlags));
801 return VERR_INVALID_PARAMETER;
802 }
803
804 /* Force readonly for images without base/diff consistency checking. */
805 if (uOpenFlags & VD_OPEN_FLAGS_INFO)
806 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
807
808 /* Set up image descriptor. */
809 PVDIMAGE pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
810 if (!pImage)
811 return VERR_NO_MEMORY;
812 pImage->pszFilename = RTStrDup(pszFilename);
813 if (!pImage->pszFilename)
814 rc = VERR_NO_MEMORY;
815
816 if (VBOX_SUCCESS(rc))
817 {
818 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
819 rc = pDisk->Backend->pfnOpen(pImage->pszFilename,
820 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
821 pDisk->pfnError, pDisk->pvErrorUser,
822 &pImage->pvBackendData);
823 }
824 /* If the open in read-write mode failed, retry in read-only mode. */
825 if (VBOX_FAILURE(rc))
826 {
827 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
828 && (rc == VERR_ACCESS_DENIED
829 || rc == VERR_PERMISSION_DENIED
830 || rc == VERR_WRITE_PROTECT
831 || rc == VERR_SHARING_VIOLATION
832 || rc == VERR_FILE_LOCK_FAILED))
833 rc = pDisk->Backend->pfnOpen(pImage->pszFilename,
834 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
835 | VD_OPEN_FLAGS_READONLY,
836 pDisk->pfnError, pDisk->pvErrorUser,
837 &pImage->pvBackendData);
838 if (VBOX_FAILURE(rc))
839 rc = vdError(pDisk, rc, RT_SRC_POS,
840 N_("VD: error opening image file '%s'"), pszFilename);
841 }
842
843 if (VBOX_SUCCESS(rc))
844 {
845 VDIMAGETYPE enmImageType;
846 rc = pDisk->Backend->pfnGetImageType(pImage->pvBackendData,
847 &enmImageType);
848 /* Check image type. As the image itself has no idea whether it's a
849 * base image or not, this info is derived here. Image 0 can be fixed
850 * or normal, all others must be normal images. */
851 if ( VBOX_SUCCESS(rc)
852 && !(uOpenFlags & VD_OPEN_FLAGS_INFO)
853 && pDisk->cImages != 0
854 && enmImageType != VD_IMAGE_TYPE_NORMAL)
855 rc = VERR_VDI_INVALID_TYPE;
856
857 /** @todo optionally check UUIDs */
858
859 if (VBOX_SUCCESS(rc))
860 {
861 uint64_t cbSize = pDisk->Backend->pfnGetSize(pImage->pvBackendData);
862 if (pDisk->cImages == 0)
863 {
864 /* Cache disk information. */
865 pDisk->cbSize = cbSize;
866
867 /* Cache CHS geometry. */
868 int rc2 = pDisk->Backend->pfnGetGeometry(pImage->pvBackendData,
869 &pDisk->cCylinders,
870 &pDisk->cHeads,
871 &pDisk->cSectors);
872 if (VBOX_FAILURE(rc2))
873 {
874 pDisk->cCylinders = 0;
875 pDisk->cHeads = 0;
876 pDisk->cSectors = 0;
877 }
878 else
879 {
880 /* Make sure the CHS geometry is properly clipped. */
881 pDisk->cCylinders = RT_MIN(pDisk->cCylinders, 16383);
882 pDisk->cHeads = RT_MIN(pDisk->cHeads, 255);
883 pDisk->cSectors = RT_MIN(pDisk->cSectors, 255);
884 }
885
886 /* Cache translation mode. */
887 rc2 = pDisk->Backend->pfnGetTranslation(pImage->pvBackendData,
888 &pDisk->enmTranslation);
889 if (VBOX_FAILURE(rc2))
890 pDisk->enmTranslation = (PDMBIOSTRANSLATION)0;
891 }
892 else
893 {
894 /* Check image size/block size for consistency. */
895 if (cbSize != pDisk->cbSize)
896 rc = VERR_VDI_INVALID_TYPE;
897 }
898 }
899
900 if (VBOX_SUCCESS(rc) && pDisk->cImages != 0)
901 {
902 /* Switch previous image to read-only mode. */
903 unsigned uOpenFlagsPrevImg;
904 uOpenFlagsPrevImg = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
905 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
906 {
907 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
908 rc = pDisk->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
909 }
910 }
911
912 if (VBOX_SUCCESS(rc))
913 {
914 /* Image successfully opened, make it the last image. */
915 vdAddImageToList(pDisk, pImage);
916 }
917 else
918 {
919 /* Error detected, but image opened. Close image. */
920 int rc2;
921 rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, false);
922 AssertRC(rc2);
923 pImage->pvBackendData = NULL;
924 }
925 }
926
927 if (VBOX_FAILURE(rc))
928 {
929 if (pImage)
930 {
931 if (pImage->pszFilename)
932 RTStrFree(pImage->pszFilename);
933 RTMemFree(pImage);
934 }
935 }
936
937 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
938 return rc;
939}
940
941/**
942 * Creates and opens a new base image file.
943 *
944 * @returns VBox status code.
945 * @param pDisk Pointer to VBox HDD container.
946 * @param pszFilename Name of the image file to create.
947 * @param enmType Image type, only base image types are acceptable.
948 * @param cbSize Image size in bytes.
949 * @param uImageFlags Flags specifying special image features.
950 * @param pszComment Pointer to image comment. NULL is ok.
951 * @param cCylinders Number of cylinders (must be <= 16383).
952 * @param cHeads Number of heads (must be <= 16).
953 * @param cSectors Number of sectors (must be <= 63);
954 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
955 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
956 * @param pvUser User argument for the progress callback.
957 */
958VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszFilename,
959 VDIMAGETYPE enmType, uint64_t cbSize,
960 unsigned uImageFlags, const char *pszComment,
961 unsigned cCylinders, unsigned cHeads,
962 unsigned cSectors, unsigned uOpenFlags,
963 PFNVMPROGRESS pfnProgress, void *pvUser)
964{
965 int rc = VINF_SUCCESS;
966 LogFlow(("%s: pszFilename=\"%s\" uOpenFlags=%#x\n", __FUNCTION__,
967 pszFilename, uOpenFlags));
968 /* sanity check */
969 Assert(pDisk);
970 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
971
972 /* Check arguments. */
973 if ( !pszFilename
974 || *pszFilename == '\0'
975 || (enmType != VD_IMAGE_TYPE_NORMAL && enmType != VD_IMAGE_TYPE_FIXED)
976 || !cbSize
977 || (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
978 || cCylinders == 0
979 || cCylinders > 16383
980 || cHeads == 0
981 || cHeads > 16
982 || cSectors == 0
983 || cSectors > 63)
984 {
985 AssertMsgFailed(("Invalid arguments: pszFilename=%#p uOpenFlags=%#x\n", pszFilename, uOpenFlags));
986 return VERR_INVALID_PARAMETER;
987 }
988
989 /* Check state. */
990 if (pDisk->cImages != 0)
991 {
992 AssertMsgFailed(("Create base image cannot be done with other images open\n"));
993 return VERR_VDI_INVALID_STATE;
994 }
995
996 /* Set up image descriptor. */
997 PVDIMAGE pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
998 if (!pImage)
999 return VERR_NO_MEMORY;
1000 pImage->pszFilename = RTStrDup(pszFilename);
1001 if (!pImage->pszFilename)
1002 rc = VERR_NO_MEMORY;
1003
1004 if (VBOX_SUCCESS(rc))
1005 rc = pDisk->Backend->pfnCreate(pImage->pszFilename, enmType, cbSize,
1006 uImageFlags, pszComment, cCylinders,
1007 cHeads, cSectors, uOpenFlags,
1008 pfnProgress, pvUser,
1009 pDisk->pfnError, pDisk->pvErrorUser,
1010 &pImage->pvBackendData);
1011
1012 if (VBOX_SUCCESS(rc))
1013 {
1014 /** @todo optionally check UUIDs */
1015
1016 if (VBOX_SUCCESS(rc))
1017 {
1018 uint64_t cbSize = pDisk->Backend->pfnGetSize(pImage->pvBackendData);
1019 if (pDisk->cImages == 0)
1020 {
1021 /* Cache disk information. */
1022 pDisk->cbSize = cbSize;
1023
1024 /* Cache CHS geometry. */
1025 int rc2 = pDisk->Backend->pfnGetGeometry(pImage->pvBackendData,
1026 &pDisk->cCylinders,
1027 &pDisk->cHeads,
1028 &pDisk->cSectors);
1029 if (VBOX_FAILURE(rc2))
1030 {
1031 pDisk->cCylinders = 0;
1032 pDisk->cHeads = 0;
1033 pDisk->cSectors = 0;
1034 }
1035 else
1036 {
1037 /* Make sure the CHS geometry is properly clipped. */
1038 pDisk->cCylinders = RT_MIN(pDisk->cCylinders, 16383);
1039 pDisk->cHeads = RT_MIN(pDisk->cHeads, 255);
1040 pDisk->cSectors = RT_MIN(pDisk->cSectors, 255);
1041 }
1042
1043 /* Cache translation mode. */
1044 rc2 = pDisk->Backend->pfnGetTranslation(pImage->pvBackendData,
1045 &pDisk->enmTranslation);
1046 if (VBOX_FAILURE(rc2))
1047 pDisk->enmTranslation = (PDMBIOSTRANSLATION)0;
1048 }
1049 else
1050 {
1051 /* Check image size/block size for consistency. */
1052 if (cbSize != pDisk->cbSize)
1053 rc = VERR_VDI_INVALID_TYPE;
1054 }
1055 }
1056
1057 if (VBOX_SUCCESS(rc))
1058 {
1059 /* Image successfully opened, make it the last image. */
1060 vdAddImageToList(pDisk, pImage);
1061 }
1062 else
1063 {
1064 /* Error detected, but image opened. Close and delete image. */
1065 int rc2;
1066 rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, true);
1067 AssertRC(rc2);
1068 pImage->pvBackendData = NULL;
1069 }
1070 }
1071
1072 if (VBOX_FAILURE(rc))
1073 {
1074 if (pImage)
1075 {
1076 if (pImage->pszFilename)
1077 RTStrFree(pImage->pszFilename);
1078 RTMemFree(pImage);
1079 }
1080 }
1081
1082 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1083 return rc;
1084}
1085
1086/**
1087 * Creates and opens a new differencing image file in HDD container.
1088 * See comments for VDOpen function about differencing images.
1089 *
1090 * @returns VBox status code.
1091 * @param pDisk Pointer to VBox HDD container.
1092 * @param pszFilename Name of the differencing image file to create.
1093 * @param uImageFlags Flags specifying special image features.
1094 * @param pszComment Pointer to image comment. NULL is ok.
1095 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1096 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1097 * @param pvUser User argument for the progress callback.
1098 */
1099VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszFilename,
1100 unsigned uImageFlags, const char *pszComment,
1101 unsigned uOpenFlags,
1102 PFNVMPROGRESS pfnProgress, void *pvUser)
1103{
1104 return VERR_NOT_IMPLEMENTED;
1105}
1106
1107/**
1108 * Merges two images having a parent/child relationship (both directions).
1109 * As a side effect the source image is deleted from both the disk and
1110 * the images in the VBox HDD container.
1111 *
1112 * @returns VBox status code.
1113 * @param pDisk Pointer to VBox HDD container.
1114 * @param nImageFrom Name of the image file to merge from.
1115 * @param nImageTo Name of the image file to merge to.
1116 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1117 * @param pvUser User argument for the progress callback.
1118 */
1119VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
1120 unsigned nImageTo, PFNVMPROGRESS pfnProgress,
1121 void *pvUser)
1122{
1123 return VERR_NOT_IMPLEMENTED;
1124}
1125
1126/**
1127 * Copies an image from one VBox HDD container to another.
1128 * The copy is opened in the target VBox HDD container.
1129 * It is possible to convert between different image formats, because the
1130 * backend for the destination VBox HDD container may be different from the
1131 * source container.
1132 * If both the source and destination reference the same VBox HDD container,
1133 * then the image is moved (by copying/deleting) to the new location.
1134 * The source container is unchanged if the move operation fails, otherwise
1135 * the image at the new location is opened in the same way as the old one was.
1136 *
1137 * @returns VBox status code.
1138 * @param pDiskFrom Pointer to source VBox HDD container.
1139 * @param nImage Image number, counts from 0. 0 is always base image of container.
1140 * @param pDiskTo Pointer to destination VBox HDD container.
1141 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1142 * @param pvUser User argument for the progress callback.
1143 */
1144VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
1145 PFNVMPROGRESS pfnProgress, void *pvUser)
1146{
1147 return VERR_NOT_IMPLEMENTED;
1148}
1149
1150/**
1151 * Compacts a growing image file by removing zeroed data blocks.
1152 * Optionally defragments data in the image so that ascending sector numbers
1153 * are stored in ascending location in the image file.
1154 *
1155 * @todo maybe include this function in VDCopy.
1156 *
1157 * @returns VBox status code.
1158 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1159 * @param pDisk Pointer to VBox HDD container.
1160 * @param nImage Image number, counts from 0. 0 is always base image of container.
1161 * @param fDefragment If true, reorder file data so that sectors are stored in ascending order.
1162 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1163 * @param pvUser User argument for the progress callback.
1164 */
1165VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
1166 bool fDefragment,
1167 PFNVMPROGRESS pfnProgress, void *pvUser)
1168{
1169 return VERR_NOT_IMPLEMENTED;
1170}
1171
1172/**
1173 * Resizes an image. Allows setting the disk size to both larger and smaller
1174 * values than the current disk size.
1175 *
1176 * @returns VBox status code.
1177 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1178 * @param pDisk Pointer to VBox HDD container.
1179 * @param nImage Image number, counts from 0. 0 is always base image of container.
1180 * @param cbSize New image size in bytes.
1181 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1182 * @param pvUser User argument for the progress callback.
1183 */
1184VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, unsigned nImage, uint64_t cbSize,
1185 PFNVMPROGRESS pfnProgress, void *pvUser)
1186{
1187 return VERR_NOT_IMPLEMENTED;
1188}
1189
1190/**
1191 * Closes the last opened image file in the HDD container. Leaves all changes inside it.
1192 * If previous image file was opened in read-only mode (that is normal) and closing image
1193 * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
1194 * will be reopened in read/write mode.
1195 *
1196 * @param pDisk Pointer to VBox HDD container.
1197 * @param fDelete If true, delete the image from the host disk.
1198 */
1199VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
1200{
1201 LogFlow(("%s: fDelete=%d\n", __FUNCTION__, fDelete));
1202 /* sanity check */
1203 Assert(pDisk);
1204 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1205
1206 PVDIMAGE pImage = pDisk->pLast;
1207 unsigned uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pImage->pvBackendData);
1208 /* Remove image from list of opened images. */
1209 vdRemoveImageFromList(pDisk, pImage);
1210 /* Close (and optionally delete) image. */
1211 int rc = pDisk->Backend->pfnClose(pImage->pvBackendData, fDelete);
1212 /* Free remaining resources related to the image. */
1213 RTStrFree(pImage->pszFilename);
1214 RTMemFree(pImage);
1215
1216 /* If disk was previously in read/write mode, make sure it will stay like
1217 * this after closing this image. Set the open flags accordingly. */
1218 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1219 {
1220 uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1221 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
1222 rc = pDisk->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData,
1223 uOpenFlags);
1224 }
1225
1226 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1227 return rc;
1228}
1229
1230/**
1231 * Closes all opened image files in HDD container.
1232 *
1233 * @param pDisk Pointer to VDI HDD container.
1234 */
1235VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
1236{
1237 LogFlow(("%s:\n", __FUNCTION__));
1238 /* sanity check */
1239 Assert(pDisk);
1240 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1241
1242 PVDIMAGE pImage = pDisk->pLast;
1243 int rc = VINF_SUCCESS;
1244 while (pImage)
1245 {
1246 PVDIMAGE pPrev = pImage->pPrev;
1247 /* Remove image from list of opened images. */
1248 vdRemoveImageFromList(pDisk, pImage);
1249 /* Close image. */
1250 int rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, false);
1251 if (VBOX_FAILURE(rc2) && VBOX_SUCCESS(rc))
1252 rc = rc2;
1253 /* Free remaining resources related to the image. */
1254 RTStrFree(pImage->pszFilename);
1255 RTMemFree(pImage);
1256 pImage = pPrev;
1257 }
1258 Assert(pDisk->pLast == NULL);
1259
1260 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1261 return rc;
1262}
1263
1264/**
1265 * Read data from virtual HDD.
1266 *
1267 * @returns VBox status code.
1268 * @param pDisk Pointer to VBox HDD container.
1269 * @param uOffset Offset of first reading byte from start of disk.
1270 * @param pvBuf Pointer to buffer for reading data.
1271 * @param cbRead Number of bytes to read.
1272 */
1273VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf, size_t cbRead)
1274{
1275 /* sanity check */
1276 LogFlow(("%s: offset=%llu cbRead=%u\n", __FUNCTION__, uOffset, (unsigned)cbRead));
1277 Assert(pDisk);
1278 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1279
1280 int rc = VINF_SUCCESS;
1281 PVDIMAGE pImage = pDisk->pLast;
1282 if (RT_UNLIKELY(!pImage))
1283 {
1284 Assert(pImage);
1285 rc = VERR_VDI_NOT_OPENED;
1286 goto out;
1287 }
1288
1289 /* Check params. */
1290 if (uOffset + cbRead > pDisk->cbSize || cbRead == 0)
1291 {
1292 AssertMsgFailed(("uOffset=%llu cbRead=%u\n", uOffset, cbRead));
1293 return VERR_INVALID_PARAMETER;
1294 }
1295
1296 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead);
1297out:
1298 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1299 return rc;
1300}
1301
1302/**
1303 * Write data to virtual HDD.
1304 *
1305 * @returns VBox status code.
1306 * @param pDisk Pointer to VBox HDD container.
1307 * @param uOffset Offset of first reading byte from start of disk.
1308 * @param pvBuf Pointer to buffer for writing data.
1309 * @param cbWrite Number of bytes to write.
1310 */
1311VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf, size_t cbWrite)
1312{
1313 /* sanity check */
1314 Assert(pDisk);
1315 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1316
1317 int rc = VINF_SUCCESS;
1318 size_t cbThisWrite;
1319 size_t cbPreRead, cbPostRead;
1320 PVDIMAGE pImage = pDisk->pLast;
1321 if (RT_UNLIKELY(!pImage))
1322 {
1323 Assert(pImage);
1324 rc = VERR_VDI_NOT_OPENED;
1325 goto out;
1326 }
1327
1328 /* Check params. */
1329 if (uOffset + cbWrite > pDisk->cbSize || cbWrite == 0)
1330 {
1331 AssertMsgFailed(("uOffset=%llu cbWrite=%u\n", uOffset, cbWrite));
1332 rc = VERR_INVALID_PARAMETER;
1333 goto out;
1334 }
1335
1336 vdSetModifiedFlag(pDisk);
1337
1338 /* Loop until all written. */
1339 do
1340 {
1341 /* Try to write the possibly partial block to the last opened image.
1342 * This works when the block is already allocated in this image or
1343 * if it is a full-block write, which automatically allocates a new
1344 * block if needed. */
1345 cbThisWrite = cbWrite;
1346 rc = pDisk->Backend->pfnWrite(pImage->pvBackendData, uOffset, pvBuf,
1347 cbThisWrite, &cbThisWrite,
1348 &cbPreRead, &cbPostRead);
1349 if (rc == VINF_VDI_BLOCK_FREE)
1350 {
1351 void *pvTmp = RTMemTmpAlloc(cbPreRead + cbThisWrite + cbPostRead);
1352 if (!pvBuf)
1353 {
1354 Assert(!pvBuf);
1355 rc = VERR_NO_MEMORY;
1356 break;
1357 }
1358
1359 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME))
1360 {
1361 /* Optimized write, suppress writing to a so far unallocated
1362 * block when the data is identical than as of the parent. */
1363 rc = vdWriteHelperOptimized(pDisk, pImage, uOffset,
1364 cbWrite, cbThisWrite,
1365 cbPreRead, cbPostRead,
1366 pvBuf, pvTmp);
1367 }
1368 else
1369 {
1370 /* Normal write, not optimized in any way. The block will be
1371 * written no matter what. This will usually (unless the
1372 * backend has some further optimization enabled) cause the
1373 * block to be allocated. */
1374 rc = vdWriteHelperStandard(pDisk, pImage, uOffset,
1375 cbWrite, cbThisWrite,
1376 cbPreRead, cbPostRead,
1377 pvBuf, pvTmp);
1378 }
1379 RTMemTmpFree(pvTmp);
1380 if (VBOX_FAILURE(rc))
1381 break;
1382 }
1383
1384 cbWrite -= cbThisWrite;
1385 uOffset += cbThisWrite;
1386 pvBuf = (char *)pvBuf + cbThisWrite;
1387 } while (cbWrite != 0 && VBOX_SUCCESS(rc));
1388
1389out:
1390 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1391 return rc;
1392}
1393
1394/**
1395 * Make sure the on disk representation of a virtual HDD is up to date.
1396 *
1397 * @returns VBox status code.
1398 * @param pDisk Pointer to VBox HDD container.
1399 */
1400VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
1401{
1402 /* sanity check */
1403 Assert(pDisk);
1404 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1405
1406 int rc = VINF_SUCCESS;
1407 PVDIMAGE pImage = pDisk->pLast;
1408 if (RT_UNLIKELY(!pImage))
1409 {
1410 Assert(pImage);
1411 rc = VERR_VDI_NOT_OPENED;
1412 }
1413 else
1414 {
1415 vdResetModifiedFlag(pDisk);
1416 rc = pDisk->Backend->pfnFlush(pImage->pvBackendData);
1417 }
1418
1419 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1420 return rc;
1421}
1422
1423/**
1424 * Get number of opened images in HDD container.
1425 *
1426 * @returns Number of opened images for HDD container. 0 if no images have been opened.
1427 * @param pDisk Pointer to VBox HDD container.
1428 */
1429VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
1430{
1431 /* sanity check */
1432 Assert(pDisk);
1433 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1434
1435 unsigned c = pDisk->cImages;
1436 LogFlow(("%s: returns %d\n", __FUNCTION__, c));
1437 return c;
1438}
1439
1440/**
1441 * Get read/write mode of the VBox HDD container.
1442 *
1443 * @returns Virtual disk ReadOnly status.
1444 * @returns true if no image is opened in HDD container.
1445 * @param pDisk Pointer to VBox HDD container.
1446 */
1447VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
1448{
1449 /* sanity check */
1450 Assert(pDisk);
1451 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1452
1453 bool f;
1454 if (pDisk->pLast)
1455 {
1456 unsigned uOpenFlags;
1457 uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1458 f = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
1459 }
1460 else
1461 {
1462 AssertMsgFailed(("No disk image is opened!\n"));
1463 f = true;
1464 }
1465
1466 LogFlow(("%s: returns %d\n", __FUNCTION__, f));
1467 return f;
1468}
1469
1470/**
1471 * Get total disk size of the VBox HDD container.
1472 *
1473 * @returns Virtual disk size in bytes.
1474 * @returns 0 if no image is opened in HDD container.
1475 * @param pDisk Pointer to VBox HDD container.
1476 */
1477VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk)
1478{
1479 /* sanity check */
1480 Assert(pDisk);
1481 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1482
1483 uint64_t cb = pDisk->cbSize;
1484 LogFlow(("%s: returns %lld\n", __FUNCTION__, cb));
1485 return cb;
1486}
1487
1488/**
1489 * Get virtual disk geometry stored in HDD container.
1490 *
1491 * @returns VBox status code.
1492 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1493 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
1494 * @param pDisk Pointer to VBox HDD container.
1495 * @param pcCylinders Where to store the number of cylinders. NULL is ok.
1496 * @param pcHeads Where to store the number of heads. NULL is ok.
1497 * @param pcSectors Where to store the number of sectors. NULL is ok.
1498 */
1499VBOXDDU_DECL(int) VDGetGeometry(PVBOXHDD pDisk, unsigned *pcCylinders,
1500 unsigned *pcHeads, unsigned *pcSectors)
1501{
1502 /* sanity check */
1503 Assert(pDisk);
1504 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1505
1506 int rc = VINF_SUCCESS;
1507 PVDIMAGE pImage = pDisk->pBase;
1508 if (RT_UNLIKELY(!pImage))
1509 {
1510 Assert(pImage);
1511 rc = VERR_VDI_NOT_OPENED;
1512 }
1513 else
1514 {
1515 if (pDisk->cCylinders != 0)
1516 {
1517 if (pcCylinders)
1518 *pcCylinders = pDisk->cCylinders;
1519 if (pcHeads)
1520 *pcHeads = pDisk->cHeads;
1521 if (pcSectors)
1522 *pcSectors = pDisk->cSectors;
1523 }
1524 else
1525 rc = VERR_VDI_GEOMETRY_NOT_SET;
1526 }
1527 LogFlow(("%s: %Vrc (CHS=%u/%u/%u)\n", __FUNCTION__, rc,
1528 pDisk->cCylinders, pDisk->cHeads, pDisk->cSectors));
1529 return rc;
1530}
1531
1532/**
1533 * Store virtual disk geometry in HDD container.
1534 *
1535 * Note that in case of unrecoverable error all images in HDD container will be closed.
1536 *
1537 * @returns VBox status code.
1538 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1539 * @param pDisk Pointer to VBox HDD container.
1540 * @param cCylinders Number of cylinders.
1541 * @param cHeads Number of heads.
1542 * @param cSectors Number of sectors.
1543 */
1544VBOXDDU_DECL(int) VDSetGeometry(PVBOXHDD pDisk, unsigned cCylinders,
1545 unsigned cHeads, unsigned cSectors)
1546{
1547 /* sanity check */
1548 Assert(pDisk);
1549 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1550
1551 int rc = VINF_SUCCESS;
1552 PVDIMAGE pImage = pDisk->pBase;
1553 if (RT_UNLIKELY(!pImage))
1554 {
1555 Assert(pImage);
1556 rc = VERR_VDI_NOT_OPENED;
1557 }
1558 else
1559 {
1560 if ( cCylinders != pDisk->cCylinders
1561 || cHeads != pDisk->cHeads
1562 || cSectors != pDisk->cSectors)
1563 {
1564 /* Only update geometry if it is changed. Avoids similar checks
1565 * in every backend. Most of the time the new geometry is set to
1566 * the previous values, so no need to go through the hassle of
1567 * updating an image which could be opened in read-only mode right
1568 * now. */
1569 rc = pDisk->Backend->pfnSetGeometry(pImage->pvBackendData,
1570 cCylinders, cHeads, cSectors);
1571
1572 /* Cache new geometry values in any case, whether successful or not. */
1573 int rc2 = pDisk->Backend->pfnGetGeometry(pImage->pvBackendData,
1574 &pDisk->cCylinders,
1575 &pDisk->cHeads,
1576 &pDisk->cSectors);
1577 if (VBOX_FAILURE(rc2))
1578 {
1579 pDisk->cCylinders = 0;
1580 pDisk->cHeads = 0;
1581 pDisk->cSectors = 0;
1582 }
1583 else
1584 {
1585 /* Make sure the CHS geometry is properly clipped. */
1586 pDisk->cCylinders = RT_MIN(pDisk->cCylinders, 16383);
1587 pDisk->cHeads = RT_MIN(pDisk->cHeads, 255);
1588 pDisk->cSectors = RT_MIN(pDisk->cSectors, 255);
1589 }
1590 }
1591 }
1592
1593 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1594 return rc;
1595}
1596
1597/**
1598 * Get virtual disk translation mode stored in HDD container.
1599 *
1600 * @returns VBox status code.
1601 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1602 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
1603 * @param pDisk Pointer to VBox HDD container.
1604 * @param penmTranslation Where to store the translation mode (see pdm.h).
1605 */
1606VBOXDDU_DECL(int) VDGetTranslation(PVBOXHDD pDisk,
1607 PPDMBIOSTRANSLATION penmTranslation)
1608{
1609 /* sanity check */
1610 Assert(pDisk);
1611 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1612
1613 int rc = VINF_SUCCESS;
1614 PVDIMAGE pImage = pDisk->pBase;
1615 if (RT_UNLIKELY(!pImage))
1616 {
1617 Assert(pImage);
1618 rc = VERR_VDI_NOT_OPENED;
1619 }
1620 else
1621 {
1622 if (pDisk->enmTranslation != 0)
1623 *penmTranslation = pDisk->enmTranslation;
1624 else
1625 rc = VERR_VDI_GEOMETRY_NOT_SET;
1626 }
1627 LogFlow(("%s: %Vrc (translation=%u)\n", __FUNCTION__, rc,
1628 pDisk->enmTranslation));
1629 return rc;
1630}
1631
1632/**
1633 * Store virtual disk translation mode in HDD container.
1634 *
1635 * Note that in case of unrecoverable error all images in HDD container will be closed.
1636 *
1637 * @returns VBox status code.
1638 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1639 * @param pDisk Pointer to VBox HDD container.
1640 * @param enmTranslation Translation mode (see pdm.h).
1641 */
1642VBOXDDU_DECL(int) VDSetTranslation(PVBOXHDD pDisk,
1643 PDMBIOSTRANSLATION enmTranslation)
1644{
1645 /* sanity check */
1646 Assert(pDisk);
1647 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1648
1649 int rc = VINF_SUCCESS;
1650 PVDIMAGE pImage = pDisk->pBase;
1651 if (RT_UNLIKELY(!pImage))
1652 {
1653 Assert(pImage);
1654 rc = VERR_VDI_NOT_OPENED;
1655 }
1656 else
1657 {
1658 if (enmTranslation == 0)
1659 rc = VERR_INVALID_PARAMETER;
1660 else if (enmTranslation != pDisk->enmTranslation)
1661 {
1662 /* Only update translation mode if it is changed. Avoids similar
1663 * checks in every backend. Most of the time the new translation
1664 * mode is set to the previous value, so no need to go through the
1665 * hassle of updating an image which could be opened in read-only
1666 * mode right now. */
1667 rc = pDisk->Backend->pfnSetTranslation(pImage->pvBackendData,
1668 enmTranslation);
1669
1670 /* Cache new translation mode in any case, whether successful or not. */
1671 int rc2 = pDisk->Backend->pfnGetTranslation(pImage->pvBackendData,
1672 &pDisk->enmTranslation);
1673 if (VBOX_FAILURE(rc2))
1674 pDisk->enmTranslation = (PDMBIOSTRANSLATION)0;
1675 }
1676 }
1677
1678 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1679 return rc;
1680}
1681
1682/**
1683 * Get version of image in HDD container.
1684 *
1685 * @returns VBox status code.
1686 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1687 * @param pDisk Pointer to VBox HDD container.
1688 * @param nImage Image number, counts from 0. 0 is always base image of container.
1689 * @param puVersion Where to store the image version.
1690 */
1691VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
1692 unsigned *puVersion)
1693{
1694 return VERR_NOT_IMPLEMENTED;
1695}
1696
1697/**
1698 * Get type of image in HDD container.
1699 *
1700 * @returns VBox status code.
1701 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1702 * @param pDisk Pointer to VBox HDD container.
1703 * @param nImage Image number, counts from 0. 0 is always base image of container.
1704 * @param penmType Where to store the image type.
1705 */
1706VBOXDDU_DECL(int) VDGetImageType(PVBOXHDD pDisk, unsigned nImage,
1707 PVDIMAGETYPE penmType)
1708{
1709 return VERR_NOT_IMPLEMENTED;
1710}
1711
1712/**
1713 * Get flags of image in HDD container.
1714 *
1715 * @returns VBox status code.
1716 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1717 * @param pDisk Pointer to VBox HDD container.
1718 * @param nImage Image number, counts from 0. 0 is always base image of container.
1719 * @param puImageFlags Where to store the image flags.
1720 */
1721VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
1722 unsigned *puImageFlags)
1723{
1724 return VERR_NOT_IMPLEMENTED;
1725}
1726
1727/**
1728 * Get open flags of last opened image in HDD container.
1729 *
1730 * @returns VBox status code.
1731 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1732 * @param pDisk Pointer to VBox HDD container.
1733 * @param puOpenFlags Where to store the image open flags.
1734 */
1735VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned *puOpenFlags)
1736{
1737 /* sanity check */
1738 Assert(pDisk);
1739 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1740
1741 unsigned uOpenFlags = 0;
1742 int rc = VINF_SUCCESS;
1743 PVDIMAGE pImage = pDisk->pLast;
1744 if (RT_UNLIKELY(!pImage))
1745 {
1746 Assert(pImage);
1747 rc = VERR_VDI_NOT_OPENED;
1748 }
1749 else
1750 {
1751 uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1752 *puOpenFlags = uOpenFlags;
1753 }
1754 LogFlow(("%s: returns %Vrc uOpenFlags=%#x\n", __FUNCTION__, rc, uOpenFlags));
1755 return uOpenFlags;
1756}
1757
1758/**
1759 * Set open flags of last opened image in HDD container.
1760 * This operation may cause file locking changes and/or files being reopened.
1761 * Note that in case of unrecoverable error all images in HDD container will be closed.
1762 *
1763 * @returns Virtual disk block size in bytes.
1764 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1765 * @returns VBox status code.
1766 * @param pDisk Pointer to VBox HDD container.
1767 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1768 */
1769VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned uOpenFlags)
1770{
1771 /* sanity check */
1772 Assert(pDisk);
1773 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1774
1775 int rc = VINF_SUCCESS;
1776 PVDIMAGE pImage = pDisk->pLast;
1777 if (RT_UNLIKELY(!pImage))
1778 {
1779 Assert(pImage);
1780 rc = VERR_VDI_NOT_OPENED;
1781 }
1782 else
1783 rc = pDisk->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData,
1784 uOpenFlags);
1785 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1786 return rc;
1787}
1788
1789/**
1790 * Get base filename of image in HDD container. Some image formats use
1791 * other filenames as well, so don't use this for anything but for informational
1792 * purposes.
1793 *
1794 * @returns VBox status code.
1795 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1796 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
1797 * @param pDisk Pointer to VBox HDD container.
1798 * @param nImage Image number, counts from 0. 0 is always base image of container.
1799 * @param pszFilename Where to store the image file name.
1800 * @param cbFilename Size of buffer pszFilename points to.
1801 */
1802VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
1803 char *pszFilename, unsigned cbFilename)
1804{
1805 return VERR_NOT_IMPLEMENTED;
1806}
1807
1808/**
1809 * Get the comment line of image in HDD container.
1810 *
1811 * @returns VBox status code.
1812 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1813 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
1814 * @param pDisk Pointer to VBox HDD container.
1815 * @param nImage Image number, counts from 0. 0 is always base image of container.
1816 * @param pszComment Where to store the comment string of image. NULL is ok.
1817 * @param cbComment The size of pszComment buffer. 0 is ok.
1818 */
1819VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
1820 char *pszComment, unsigned cbComment)
1821{
1822 /* sanity check */
1823 Assert(pDisk);
1824 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1825 Assert(pszComment);
1826
1827 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1828 int rc;
1829 if (pImage)
1830 rc = pDisk->Backend->pfnGetComment(pImage->pvBackendData, pszComment,
1831 cbComment);
1832 else
1833 rc = VERR_VDI_IMAGE_NOT_FOUND;
1834
1835 LogFlow(("%s: returns %Vrc, comment='%s' nImage=%u\n", __FUNCTION__,
1836 rc, pszComment, nImage));
1837 return rc;
1838}
1839
1840/**
1841 * Changes the comment line of image in HDD container.
1842 *
1843 * @returns VBox status code.
1844 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1845 * @param pDisk Pointer to VBox HDD container.
1846 * @param nImage Image number, counts from 0. 0 is always base image of container.
1847 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
1848 */
1849VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
1850 const char *pszComment)
1851{
1852 /* sanity check */
1853 Assert(pDisk);
1854 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1855 LogFlow(("%s: comment='%s' nImage=%u\n", __FUNCTION__, pszComment, nImage));
1856
1857 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1858 int rc;
1859 if (pImage)
1860 rc = pDisk->Backend->pfnSetComment(pImage->pvBackendData, pszComment);
1861 else
1862 rc = VERR_VDI_IMAGE_NOT_FOUND;
1863
1864 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1865 return rc;
1866}
1867
1868
1869/**
1870 * Get UUID of image in HDD container.
1871 *
1872 * @returns VBox status code.
1873 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1874 * @param pDisk Pointer to VBox HDD container.
1875 * @param nImage Image number, counts from 0. 0 is always base image of container.
1876 * @param pUuid Where to store the image creation UUID.
1877 */
1878VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
1879{
1880 /* sanity check */
1881 Assert(pDisk);
1882 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1883 Assert(pUuid);
1884
1885 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1886 int rc;
1887 if (pImage)
1888 rc = pDisk->Backend->pfnGetUuid(pImage->pvBackendData, pUuid);
1889 else
1890 rc = VERR_VDI_IMAGE_NOT_FOUND;
1891
1892 LogFlow(("%s: returns %Vrc, uuid={%Vuuid} nImage=%u\n", __FUNCTION__,
1893 rc, pUuid, nImage));
1894 return rc;
1895}
1896
1897/**
1898 * Set the image's UUID. Should not be used by normal applications.
1899 *
1900 * @returns VBox status code.
1901 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1902 * @param pDisk Pointer to VBox HDD container.
1903 * @param nImage Image number, counts from 0. 0 is always base image of container.
1904 * @param pUuid Optional parameter, new UUID of the image.
1905 */
1906VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
1907{
1908 LogFlow(("%s: uuid={%Vuuid} nImage=%u\n", __FUNCTION__, pUuid, nImage));
1909 /* sanity check */
1910 Assert(pDisk);
1911 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1912 Assert(pUuid);
1913
1914 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1915 int rc;
1916 if (pImage)
1917 rc = pDisk->Backend->pfnSetUuid(pImage->pvBackendData, pUuid);
1918 else
1919 rc = VERR_VDI_IMAGE_NOT_FOUND;
1920
1921 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1922 return rc;
1923}
1924
1925/**
1926 * Get last modification UUID of image in HDD container.
1927 *
1928 * @returns VBox status code.
1929 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1930 * @param pDisk Pointer to VBox HDD container.
1931 * @param nImage Image number, counts from 0. 0 is always base image of container.
1932 * @param pUuid Where to store the image modification UUID.
1933 */
1934VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
1935{
1936 /* sanity check */
1937 Assert(pDisk);
1938 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1939 Assert(pUuid);
1940
1941 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1942 int rc;
1943 if (pImage)
1944 rc = pDisk->Backend->pfnGetModificationUuid(pImage->pvBackendData, pUuid);
1945 else
1946 rc = VERR_VDI_IMAGE_NOT_FOUND;
1947
1948 LogFlow(("%s: returns %Vrc, uuid={%Vuuid} nImage=%u\n", __FUNCTION__,
1949 rc, pUuid, nImage));
1950 return rc;
1951}
1952
1953/**
1954 * Set the image's last modification UUID. Should not be used by normal applications.
1955 *
1956 * @returns VBox status code.
1957 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1958 * @param pDisk Pointer to VBox HDD container.
1959 * @param nImage Image number, counts from 0. 0 is always base image of container.
1960 * @param pUuid Optional parameter, new last modification UUID of the image.
1961 */
1962VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
1963{
1964 LogFlow(("%s: uuid={%Vuuid} nImage=%u\n", __FUNCTION__, pUuid, nImage));
1965 /* sanity check */
1966 Assert(pDisk);
1967 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1968 Assert(pUuid);
1969
1970 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1971 int rc;
1972 if (pImage)
1973 rc = pDisk->Backend->pfnSetModificationUuid(pImage->pvBackendData, pUuid);
1974 else
1975 rc = VERR_VDI_IMAGE_NOT_FOUND;
1976
1977 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1978 return rc;
1979}
1980
1981/**
1982 * Get parent UUID of image in HDD container.
1983 *
1984 * @returns VBox status code.
1985 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1986 * @param pDisk Pointer to VBox HDD container.
1987 * @param nImage Image number, counts from 0. 0 is always base image of container.
1988 * @param pUuid Where to store the parent image UUID.
1989 */
1990VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
1991 PRTUUID pUuid)
1992{
1993 /* sanity check */
1994 Assert(pDisk);
1995 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1996 Assert(pUuid);
1997
1998 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1999 int rc;
2000 if (pImage)
2001 rc = pDisk->Backend->pfnGetParentUuid(pImage->pvBackendData, pUuid);
2002 else
2003 rc = VERR_VDI_IMAGE_NOT_FOUND;
2004
2005 LogFlow(("%s: returns %Vrc, uuid={%Vuuid} nImage=%u\n", __FUNCTION__,
2006 rc, pUuid, nImage));
2007 return rc;
2008}
2009
2010/**
2011 * Set the image's parent UUID. Should not be used by normal applications.
2012 *
2013 * @returns VBox status code.
2014 * @param pDisk Pointer to VBox HDD container.
2015 * @param nImage Image number, counts from 0. 0 is always base image of container.
2016 * @param pUuid Optional parameter, new parent UUID of the image.
2017 */
2018VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
2019 PCRTUUID pUuid)
2020{
2021 LogFlow(("%s: uuid={%Vuuid} nImage=%u\n", __FUNCTION__, pUuid, nImage));
2022 /* sanity check */
2023 Assert(pDisk);
2024 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2025 Assert(pUuid);
2026
2027 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2028 int rc;
2029 if (pImage)
2030 rc = pDisk->Backend->pfnSetParentUuid(pImage->pvBackendData, pUuid);
2031 else
2032 rc = VERR_VDI_IMAGE_NOT_FOUND;
2033
2034 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
2035 return rc;
2036}
2037
2038
2039/**
2040 * Debug helper - dumps all opened images in HDD container into the log file.
2041 *
2042 * @param pDisk Pointer to VDI HDD container.
2043 */
2044VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
2045{
2046 /* sanity check */
2047 Assert(pDisk);
2048 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2049
2050 RTLogPrintf("--- Dumping VDI Disk, Images=%u\n", pDisk->cImages);
2051 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
2052 {
2053 RTLogPrintf("Dumping VDI image \"%s\" file=%#p\n", pImage->pszFilename);
2054 /** @todo call backend to print its part. */
2055 }
2056}
2057
Note: See TracBrowser for help on using the repository browser.

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