VirtualBox

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

Last change on this file since 11444 was 11444, checked in by vboxsync, 16 years ago

Storage/VBoxHDD-new: introduced VD interfaces per image and per operation, completely unifying callback handling.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 136.6 KB
Line 
1/** $Id: VBoxHDD-new.cpp 11444 2008-08-15 14:33:02Z vboxsync $ */
2/** @file
3 * VBox HDD Container implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#define LOG_GROUP LOG_GROUP_VD
26#include <VBox/VBoxHDD-new.h>
27#include <VBox/err.h>
28
29#include <VBox/log.h>
30#include <iprt/alloc.h>
31#include <iprt/assert.h>
32#include <iprt/uuid.h>
33#include <iprt/file.h>
34#include <iprt/string.h>
35#include <iprt/asm.h>
36#include <iprt/ldr.h>
37#include <iprt/dir.h>
38#include <iprt/path.h>
39#include <iprt/param.h>
40
41#include "VBoxHDD-newInternal.h"
42
43
44#define VBOXHDDDISK_SIGNATURE 0x6f0e2a7d
45
46/** Buffer size used for merging images. */
47#define VD_MERGE_BUFFER_SIZE (1024 * 1024)
48
49/**
50 * VBox HDD Container image descriptor.
51 */
52typedef struct VDIMAGE
53{
54 /** Link to parent image descriptor, if any. */
55 struct VDIMAGE *pPrev;
56 /** Link to child image descriptor, if any. */
57 struct VDIMAGE *pNext;
58 /** Container base filename. (UTF-8) */
59 char *pszFilename;
60 /** Data managed by the backend which keeps the actual info. */
61 void *pvBackendData;
62 /** Cached sanitized image type. */
63 VDIMAGETYPE enmImageType;
64 /** Image open flags (only those handled generically in this code and which
65 * the backends will never ever see). */
66 unsigned uOpenFlags;
67
68 /** Handle for the shared object / DLL. */
69 RTLDRMOD hPlugin;
70 /** Function pointers for the various backend methods. */
71 PCVBOXHDDBACKEND Backend;
72
73 /** Pointer to list of VD interfaces, per-image. */
74 PVDINTERFACE pVDIfsImage;
75} VDIMAGE, *PVDIMAGE;
76
77/**
78 * uModified bit flags.
79 */
80#define VD_IMAGE_MODIFIED_FLAG RT_BIT(0)
81#define VD_IMAGE_MODIFIED_FIRST RT_BIT(1)
82#define VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE RT_BIT(2)
83
84
85/**
86 * VBox HDD Container main structure, private part.
87 */
88struct VBOXHDD
89{
90 /** Structure signature (VBOXHDDDISK_SIGNATURE). */
91 uint32_t u32Signature;
92
93 /** Number of opened images. */
94 unsigned cImages;
95
96 /** Base image. */
97 PVDIMAGE pBase;
98
99 /** Last opened image in the chain.
100 * The same as pBase if only one image is used. */
101 PVDIMAGE pLast;
102
103 /** Flags representing the modification state. */
104 unsigned uModified;
105
106 /** Cached size of this disk. */
107 uint64_t cbSize;
108 /** Cached PCHS geometry for this disk. */
109 PDMMEDIAGEOMETRY PCHSGeometry;
110 /** Cached LCHS geometry for this disk. */
111 PDMMEDIAGEOMETRY LCHSGeometry;
112
113 /** Pointer to list of VD interfaces, per-disk. */
114 PVDINTERFACE pVDIfsDisk;
115 /** Pointer to the common interface structure for error reporting. */
116 PVDINTERFACE pInterfaceError;
117 /** Pointer to the error interface we use if available. */
118 PVDINTERFACEERROR pInterfaceErrorCallbacks;
119};
120
121
122extern VBOXHDDBACKEND g_RawBackend;
123extern VBOXHDDBACKEND g_VmdkBackend;
124extern VBOXHDDBACKEND g_VDIBackend;
125#ifndef VBOX_OSE
126extern VBOXHDDBACKEND g_VhdBackend;
127#endif
128#ifdef VBOX_WITH_ISCSI
129extern VBOXHDDBACKEND g_ISCSIBackend;
130#endif
131
132static PCVBOXHDDBACKEND aBackends[] =
133{
134 &g_RawBackend,
135 &g_VmdkBackend,
136 &g_VDIBackend,
137#ifndef VBOX_OSE
138 &g_VhdBackend,
139#endif
140#ifdef VBOX_WITH_ISCSI
141 &g_ISCSIBackend,
142#endif
143 NULL
144};
145
146
147/**
148 * internal: issue error message.
149 */
150static int vdError(PVBOXHDD pDisk, int rc, RT_SRC_POS_DECL,
151 const char *pszFormat, ...)
152{
153 va_list va;
154 va_start(va, pszFormat);
155 if (pDisk->pInterfaceErrorCallbacks)
156 pDisk->pInterfaceErrorCallbacks->pfnError(pDisk->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
157 va_end(va);
158 return rc;
159}
160
161/**
162 * internal: find image format backend.
163 */
164static int vdFindBackend(const char *pszBackend, PCVBOXHDDBACKEND *ppBackend,
165 RTLDRMOD *phPlugin)
166{
167 int rc = VINF_SUCCESS;
168 PCVBOXHDDBACKEND pBackend = NULL;
169 RTLDRMOD hPlugin = NIL_RTLDRMOD;
170
171 for (unsigned i = 0; aBackends[i] != NULL; i++)
172 {
173 if (!strcmp(pszBackend, aBackends[i]->pszBackendName))
174 {
175 pBackend = aBackends[i];
176 break;
177 }
178 }
179
180 /* If no static backend is found try loading a shared module with
181 * pszBackend as filename. */
182 if (!pBackend)
183 {
184 char *pszPluginName;
185
186 /* HDD Format Plugins have VBoxHDD as prefix, prepend it. */
187 RTStrAPrintf(&pszPluginName, "%s%s",
188 VBOX_HDDFORMAT_PLUGIN_PREFIX, pszBackend);
189 if (!pszPluginName)
190 {
191 rc = VERR_NO_MEMORY;
192 goto out;
193 }
194
195 /* Try to load the plugin (RTldrLoad takes care of the suffix). */
196 rc = RTLdrLoad(pszPluginName, &hPlugin);
197 if (RT_SUCCESS(rc))
198 {
199 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad;
200
201 rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME,
202 (void**)&pfnHDDFormatLoad);
203 if (RT_FAILURE(rc) || !pfnHDDFormatLoad)
204 {
205 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnHDDFormat=%#p\n", VBOX_HDDFORMAT_LOAD_NAME, pszPluginName, rc, pfnHDDFormatLoad));
206 if (RT_SUCCESS(rc))
207 rc = VERR_SYMBOL_NOT_FOUND;
208 goto out;
209 }
210
211 /* Get the function table. */
212 PVBOXHDDBACKEND pBE;
213 rc = pfnHDDFormatLoad(&pBE);
214 if (RT_FAILURE(rc))
215 goto out;
216 /* Check if the sizes match. If not this plugin is too old. */
217 if (pBE->cbSize != sizeof(VBOXHDDBACKEND))
218 {
219 rc = VERR_VDI_UNSUPPORTED_VERSION;
220 goto out;
221 }
222 pBackend = pBE;
223 }
224 else
225 {
226 /* If the backend plugin doesn't exist, don't treat this as an
227 * error. Just return the NULL pointers. */
228 rc = VINF_SUCCESS;
229 }
230
231 RTStrFree(pszPluginName);
232 }
233
234out:
235 if (RT_FAILURE(rc))
236 {
237 if (hPlugin != NIL_RTLDRMOD)
238 RTLdrClose(hPlugin);
239 hPlugin = NIL_RTLDRMOD;
240 pBackend = NULL;
241 }
242
243 *ppBackend = pBackend;
244 *phPlugin = hPlugin;
245 return rc;
246}
247
248/**
249 * internal: add image structure to the end of images list.
250 */
251static void vdAddImageToList(PVBOXHDD pDisk, PVDIMAGE pImage)
252{
253 pImage->pPrev = NULL;
254 pImage->pNext = NULL;
255
256 if (pDisk->pBase)
257 {
258 Assert(pDisk->cImages > 0);
259 pImage->pPrev = pDisk->pLast;
260 pDisk->pLast->pNext = pImage;
261 pDisk->pLast = pImage;
262 }
263 else
264 {
265 Assert(pDisk->cImages == 0);
266 pDisk->pBase = pImage;
267 pDisk->pLast = pImage;
268 }
269
270 pDisk->cImages++;
271}
272
273/**
274 * internal: remove image structure from the images list.
275 */
276static void vdRemoveImageFromList(PVBOXHDD pDisk, PVDIMAGE pImage)
277{
278 Assert(pDisk->cImages > 0);
279
280 if (pImage->pPrev)
281 pImage->pPrev->pNext = pImage->pNext;
282 else
283 pDisk->pBase = pImage->pNext;
284
285 if (pImage->pNext)
286 pImage->pNext->pPrev = pImage->pPrev;
287 else
288 pDisk->pLast = pImage->pPrev;
289
290 pImage->pPrev = NULL;
291 pImage->pNext = NULL;
292
293 pDisk->cImages--;
294}
295
296/**
297 * internal: find image by index into the images list.
298 */
299static PVDIMAGE vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
300{
301 PVDIMAGE pImage = pDisk->pBase;
302 if (nImage == VD_LAST_IMAGE)
303 return pDisk->pLast;
304 while (pImage && nImage)
305 {
306 pImage = pImage->pNext;
307 nImage--;
308 }
309 return pImage;
310}
311
312/**
313 * internal: read the specified amount of data in whatever blocks the backend
314 * will give us.
315 */
316static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
317 void *pvBuf, size_t cbRead)
318{
319 int rc;
320 size_t cbThisRead;
321
322 /* Loop until all read. */
323 do
324 {
325 /* Search for image with allocated block. Do not attempt to read more
326 * than the previous reads marked as valid. Otherwise this would return
327 * stale data when different block sizes are used for the images. */
328 cbThisRead = cbRead;
329 rc = VERR_VDI_BLOCK_FREE;
330 for (PVDIMAGE pCurrImage = pImage;
331 pCurrImage != NULL && rc == VERR_VDI_BLOCK_FREE;
332 pCurrImage = pCurrImage->pPrev)
333 {
334 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
335 uOffset, pvBuf, cbThisRead,
336 &cbThisRead);
337 }
338
339 /* No image in the chain contains the data for the block. */
340 if (rc == VERR_VDI_BLOCK_FREE)
341 {
342 memset(pvBuf, '\0', cbThisRead);
343 rc = VINF_SUCCESS;
344 }
345
346 cbRead -= cbThisRead;
347 uOffset += cbThisRead;
348 pvBuf = (char *)pvBuf + cbThisRead;
349 } while (cbRead != 0 && RT_SUCCESS(rc));
350
351 return rc;
352}
353
354/**
355 * internal: mark the disk as not modified.
356 */
357static void vdResetModifiedFlag(PVBOXHDD pDisk)
358{
359 if (pDisk->uModified & VD_IMAGE_MODIFIED_FLAG)
360 {
361 /* generate new last-modified uuid */
362 if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
363 {
364 RTUUID Uuid;
365
366 RTUuidCreate(&Uuid);
367 pDisk->pLast->Backend->pfnSetModificationUuid(pDisk->pLast->pvBackendData,
368 &Uuid);
369 }
370
371 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FLAG;
372 }
373}
374
375/**
376 * internal: mark the disk as modified.
377 */
378static void vdSetModifiedFlag(PVBOXHDD pDisk)
379{
380 pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
381 if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
382 {
383 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
384
385 /* First modify, so create a UUID and ensure it's written to disk. */
386 vdResetModifiedFlag(pDisk);
387
388 if (!(pDisk->uModified | VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
389 pDisk->pLast->Backend->pfnFlush(pDisk->pLast->pvBackendData);
390 }
391}
392
393/**
394 * internal: write a complete block (only used for diff images), taking the
395 * remaining data from parent images. This implementation does not optimize
396 * anything (except that it tries to read only that portions from parent
397 * images that are really needed).
398 */
399static int vdWriteHelperStandard(PVBOXHDD pDisk, PVDIMAGE pImage,
400 uint64_t uOffset, size_t cbWrite,
401 size_t cbThisWrite, size_t cbPreRead,
402 size_t cbPostRead, const void *pvBuf,
403 void *pvTmp)
404{
405 int rc = VINF_SUCCESS;
406
407 /* Read the data that goes before the write to fill the block. */
408 if (cbPreRead)
409 {
410 rc = vdReadHelper(pDisk, pImage->pPrev, uOffset - cbPreRead, pvTmp,
411 cbPreRead);
412 if (RT_FAILURE(rc))
413 return rc;
414 }
415
416 /* Copy the data to the right place in the buffer. */
417 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
418
419 /* Read the data that goes after the write to fill the block. */
420 if (cbPostRead)
421 {
422 /* If we have data to be written, use that instead of reading
423 * data from the image. */
424 size_t cbWriteCopy;
425 if (cbWrite > cbThisWrite)
426 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
427 else
428 cbWriteCopy = 0;
429 /* Figure out how much we cannnot read from the image, because
430 * the last block to write might exceed the nominal size of the
431 * image for technical reasons. */
432 size_t cbFill;
433 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
434 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
435 else
436 cbFill = 0;
437 /* The rest must be read from the image. */
438 size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
439
440 /* Now assemble the remaining data. */
441 if (cbWriteCopy)
442 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
443 (char *)pvBuf + cbThisWrite, cbWriteCopy);
444 if (cbReadImage)
445 rc = vdReadHelper(pDisk, pImage->pPrev,
446 uOffset + cbThisWrite + cbWriteCopy,
447 (char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy,
448 cbReadImage);
449 if (RT_FAILURE(rc))
450 return rc;
451 /* Zero out the remainder of this block. Will never be visible, as this
452 * is beyond the limit of the image. */
453 if (cbFill)
454 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
455 '\0', cbFill);
456 }
457
458 /* Write the full block to the virtual disk. */
459 rc = pImage->Backend->pfnWrite(pImage->pvBackendData,
460 uOffset - cbPreRead, pvTmp,
461 cbPreRead + cbThisWrite + cbPostRead,
462 NULL, &cbPreRead, &cbPostRead, 0);
463 Assert(rc != VERR_VDI_BLOCK_FREE);
464 Assert(cbPreRead == 0);
465 Assert(cbPostRead == 0);
466
467 return rc;
468}
469
470/**
471 * internal: write a complete block (only used for diff images), taking the
472 * remaining data from parent images. This implementation optimizes out writes
473 * that do not change the data relative to the state as of the parent images.
474 * All backends which support differential/growing images support this.
475 */
476static int vdWriteHelperOptimized(PVBOXHDD pDisk, PVDIMAGE pImage,
477 uint64_t uOffset, size_t cbWrite,
478 size_t cbThisWrite, size_t cbPreRead,
479 size_t cbPostRead, const void *pvBuf,
480 void *pvTmp)
481{
482 size_t cbFill = 0;
483 size_t cbWriteCopy = 0;
484 size_t cbReadImage = 0;
485 int rc;
486
487 if (cbPostRead)
488 {
489 /* Figure out how much we cannnot read from the image, because
490 * the last block to write might exceed the nominal size of the
491 * image for technical reasons. */
492 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
493 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
494
495 /* If we have data to be written, use that instead of reading
496 * data from the image. */
497 if (cbWrite > cbThisWrite)
498 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
499
500 /* The rest must be read from the image. */
501 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
502 }
503
504 /* Read the entire data of the block so that we can compare whether it will
505 * be modified by the write or not. */
506 rc = vdReadHelper(pDisk, pImage->pPrev, uOffset - cbPreRead, pvTmp,
507 cbPreRead + cbThisWrite + cbPostRead - cbFill);
508 if (RT_FAILURE(rc))
509 return rc;
510
511 /* Check if the write would modify anything in this block. */
512 if ( !memcmp((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite)
513 && (!cbWriteCopy || !memcmp((char *)pvTmp + cbPreRead + cbThisWrite,
514 (char *)pvBuf + cbThisWrite, cbWriteCopy)))
515 {
516 /* Block is completely unchanged, so no need to write anything. */
517 return VINF_SUCCESS;
518 }
519
520 /* Copy the data to the right place in the buffer. */
521 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
522
523 /* Handle the data that goes after the write to fill the block. */
524 if (cbPostRead)
525 {
526 /* Now assemble the remaining data. */
527 if (cbWriteCopy)
528 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
529 (char *)pvBuf + cbThisWrite, cbWriteCopy);
530 /* Zero out the remainder of this block. Will never be visible, as this
531 * is beyond the limit of the image. */
532 if (cbFill)
533 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
534 '\0', cbFill);
535 }
536
537 /* Write the full block to the virtual disk. */
538 rc = pImage->Backend->pfnWrite(pImage->pvBackendData,
539 uOffset - cbPreRead, pvTmp,
540 cbPreRead + cbThisWrite + cbPostRead,
541 NULL, &cbPreRead, &cbPostRead, 0);
542 Assert(rc != VERR_VDI_BLOCK_FREE);
543 Assert(cbPreRead == 0);
544 Assert(cbPostRead == 0);
545
546 return rc;
547}
548
549/**
550 * internal: write buffer to the image, taking care of block boundaries and
551 * write optimizations.
552 */
553static int vdWriteHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
554 const void *pvBuf, size_t cbWrite)
555{
556 int rc;
557 unsigned fWrite;
558 size_t cbThisWrite;
559 size_t cbPreRead, cbPostRead;
560
561 /* Loop until all written. */
562 do
563 {
564 /* Try to write the possibly partial block to the last opened image.
565 * This works when the block is already allocated in this image or
566 * if it is a full-block write (and allocation isn't suppressed below).
567 * For image formats which don't support zero blocks, it's beneficial
568 * to avoid unnecessarily allocating unchanged blocks. This prevents
569 * unwanted expanding of images. VMDK is an example. */
570 cbThisWrite = cbWrite;
571 fWrite = (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
572 ? 0 : VD_WRITE_NO_ALLOC;
573 rc = pImage->Backend->pfnWrite(pImage->pvBackendData, uOffset, pvBuf,
574 cbThisWrite, &cbThisWrite, &cbPreRead,
575 &cbPostRead, fWrite);
576 if (rc == VERR_VDI_BLOCK_FREE)
577 {
578 void *pvTmp = RTMemTmpAlloc(cbPreRead + cbThisWrite + cbPostRead);
579 AssertBreakStmt(VALID_PTR(pvTmp), rc = VERR_NO_MEMORY);
580
581 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME))
582 {
583 /* Optimized write, suppress writing to a so far unallocated
584 * block if the data is in fact not changed. */
585 rc = vdWriteHelperOptimized(pDisk, pImage, uOffset, cbWrite,
586 cbThisWrite, cbPreRead, cbPostRead,
587 pvBuf, pvTmp);
588 }
589 else
590 {
591 /* Normal write, not optimized in any way. The block will
592 * be written no matter what. This will usually (unless the
593 * backend has some further optimization enabled) cause the
594 * block to be allocated. */
595 rc = vdWriteHelperStandard(pDisk, pImage, uOffset, cbWrite,
596 cbThisWrite, cbPreRead, cbPostRead,
597 pvBuf, pvTmp);
598 }
599 RTMemTmpFree(pvTmp);
600 if (RT_FAILURE(rc))
601 break;
602 }
603
604 cbWrite -= cbThisWrite;
605 uOffset += cbThisWrite;
606 pvBuf = (char *)pvBuf + cbThisWrite;
607 } while (cbWrite != 0 && RT_SUCCESS(rc));
608
609 return rc;
610}
611
612
613/**
614 * Lists all HDD backends and their capabilities in a caller-provided buffer.
615 *
616 * @returns VBox status code.
617 * VERR_BUFFER_OVERFLOW if not enough space is passed.
618 * @param cEntriesAlloc Number of list entries available.
619 * @param pEntries Pointer to array for the entries.
620 * @param pcEntriesUsed Number of entries returned.
621 */
622VBOXDDU_DECL(int) VDBackendInfo(unsigned cEntriesAlloc, PVDBACKENDINFO pEntries,
623 unsigned *pcEntriesUsed)
624{
625 int rc = VINF_SUCCESS;
626 PRTDIR pPluginDir = NULL;
627 unsigned cEntries = 0;
628
629 LogFlowFunc(("cEntriesAlloc=%u pEntries=%#p pcEntriesUsed=%#p\n", cEntriesAlloc, pEntries, pcEntriesUsed));
630 do
631 {
632 /* Check arguments. */
633 AssertMsgBreakStmt(cEntriesAlloc,
634 ("cEntriesAlloc=%u\n", cEntriesAlloc),
635 rc = VERR_INVALID_PARAMETER);
636 AssertMsgBreakStmt(VALID_PTR(pEntries),
637 ("pEntries=%#p\n", pEntries),
638 rc = VERR_INVALID_PARAMETER);
639 AssertMsgBreakStmt(VALID_PTR(pcEntriesUsed),
640 ("pcEntriesUsed=%#p\n", pcEntriesUsed),
641 rc = VERR_INVALID_PARAMETER);
642
643 /* First enumerate static backends. */
644 for (unsigned i = 0; aBackends[i] != NULL; i++)
645 {
646 char *pszName = RTStrDup(aBackends[i]->pszBackendName);
647 if (!pszName)
648 {
649 rc = VERR_NO_MEMORY;
650 break;
651 }
652 pEntries[cEntries].pszBackend = pszName;
653 pEntries[cEntries].uBackendCaps = aBackends[i]->uBackendCaps;
654 pEntries[cEntries].papszFileExtensions = aBackends[i]->papszFileExtensions;
655 cEntries++;
656 if (cEntries >= cEntriesAlloc)
657 {
658 rc = VERR_BUFFER_OVERFLOW;
659 break;
660 }
661 }
662 if (RT_FAILURE(rc))
663 break;
664
665 /* Then enumerate plugin backends. */
666 char szPath[RTPATH_MAX];
667 rc = RTPathSharedLibs(szPath, sizeof(szPath));
668 if (RT_FAILURE(rc))
669 break;
670
671 /* To get all entries with VBoxHDD as prefix. */
672 char *pszPluginFilter;
673 rc = RTStrAPrintf(&pszPluginFilter, "%s/%s*", szPath,
674 VBOX_HDDFORMAT_PLUGIN_PREFIX);
675 if (RT_FAILURE(rc))
676 {
677 rc = VERR_NO_MEMORY;
678 break;
679 }
680
681 /* The plugins are in the same directory as the other shared libs. */
682 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
683 if (RT_FAILURE(rc))
684 break;
685
686 PRTDIRENTRY pPluginDirEntry = NULL;
687 unsigned cbPluginDirEntry = sizeof(RTDIRENTRY);
688 pPluginDirEntry = (PRTDIRENTRY)RTMemAllocZ(sizeof(RTDIRENTRY));
689 if (!pPluginDirEntry)
690 {
691 rc = VERR_NO_MEMORY;
692 break;
693 }
694
695 while ((rc = RTDirRead(pPluginDir, pPluginDirEntry, &cbPluginDirEntry)) != VERR_NO_MORE_FILES)
696 {
697 RTLDRMOD hPlugin = NIL_RTLDRMOD;
698 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad = NULL;
699 PVBOXHDDBACKEND pBackend = NULL;
700
701 if (rc == VERR_BUFFER_OVERFLOW)
702 {
703 /* allocate new buffer. */
704 RTMemFree(pPluginDirEntry);
705 pPluginDirEntry = (PRTDIRENTRY)RTMemAllocZ(cbPluginDirEntry);
706 /* Retry. */
707 rc = RTDirRead(pPluginDir, pPluginDirEntry, &cbPluginDirEntry);
708 if (RT_FAILURE(rc))
709 break;
710 }
711 else if (RT_FAILURE(rc))
712 break;
713
714 /* We got the new entry. */
715 if (pPluginDirEntry->enmType != RTDIRENTRYTYPE_FILE)
716 continue;
717
718 rc = RTLdrLoad(pPluginDirEntry->szName, &hPlugin);
719 if (RT_SUCCESS(rc))
720 {
721 rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad);
722 if (RT_FAILURE(rc) || !pfnHDDFormatLoad)
723 {
724 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnHDDFormat=%#p\n", VBOX_HDDFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnHDDFormatLoad));
725 if (RT_SUCCESS(rc))
726 rc = VERR_SYMBOL_NOT_FOUND;
727 }
728
729 if (RT_SUCCESS(rc))
730 {
731 /* Get the function table. */
732 rc = pfnHDDFormatLoad(&pBackend);
733 if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VBOXHDDBACKEND))
734 {
735 char *pszName = RTStrDup(pBackend->pszBackendName);
736 if (!pszName)
737 {
738 rc = VERR_NO_MEMORY;
739 break;
740 }
741 pEntries[cEntries].pszBackend = pszName;
742 pEntries[cEntries].uBackendCaps = pBackend->uBackendCaps;
743 pEntries[cEntries].papszFileExtensions = pBackend->papszFileExtensions;
744 cEntries++;
745 if (cEntries >= cEntriesAlloc)
746 {
747 rc = VERR_BUFFER_OVERFLOW;
748 break;
749 }
750 }
751 }
752 else
753 pBackend = NULL;
754
755 RTLdrClose(hPlugin);
756 }
757 }
758 if (rc == VERR_NO_MORE_FILES)
759 rc = VINF_SUCCESS;
760 RTStrFree(pszPluginFilter);
761 if (pPluginDirEntry)
762 RTMemFree(pPluginDirEntry);
763 if (pPluginDir)
764 RTDirClose(pPluginDir);
765 } while (0);
766
767 LogFlowFunc(("returns %Rrc *pcEntriesUsed=%u\n", rc, cEntries));
768 *pcEntriesUsed = cEntries;
769 return rc;
770}
771
772/**
773 * Lists the capablities of a backend indentified by its name.
774 * Free all returned names with RTStrFree() when you no longer need them.
775 *
776 * @returns VBox status code.
777 * @param pszBackend The backend name.
778 * @param pEntries Pointer to an entry.
779 */
780VBOXDDU_DECL(int) VDBackendInfoOne(const char *pszBackend, PVDBACKENDINFO pEntry)
781{
782 return VERR_NOT_IMPLEMENTED;
783}
784
785/**
786 * Allocates and initializes an empty HDD container.
787 * No image files are opened.
788 *
789 * @returns VBox status code.
790 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
791 * @param ppDisk Where to store the reference to HDD container.
792 */
793VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, PVBOXHDD *ppDisk)
794{
795 int rc = VINF_SUCCESS;
796 PVBOXHDD pDisk = NULL;
797
798 LogFlowFunc(("pVDIfsDisk=%#p\n", pVDIfsDisk));
799 do
800 {
801 /* Check arguments. */
802 AssertMsgBreakStmt(VALID_PTR(ppDisk),
803 ("ppDisk=%#p\n", ppDisk),
804 rc = VERR_INVALID_PARAMETER);
805
806 pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
807 if (pDisk)
808 {
809 pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
810 pDisk->cImages = 0;
811 pDisk->pBase = NULL;
812 pDisk->pLast = NULL;
813 pDisk->cbSize = 0;
814 pDisk->PCHSGeometry.cCylinders = 0;
815 pDisk->PCHSGeometry.cHeads = 0;
816 pDisk->PCHSGeometry.cSectors = 0;
817 pDisk->LCHSGeometry.cCylinders = 0;
818 pDisk->LCHSGeometry.cHeads = 0;
819 pDisk->LCHSGeometry.cSectors = 0;
820 pDisk->pVDIfsDisk = pVDIfsDisk;
821 pDisk->pInterfaceError = NULL;
822 pDisk->pInterfaceErrorCallbacks = NULL;
823
824 pDisk->pInterfaceError = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_ERROR);
825 if (pDisk->pInterfaceError)
826 pDisk->pInterfaceErrorCallbacks = VDGetInterfaceError(pDisk->pInterfaceError);
827 *ppDisk = pDisk;
828 }
829 else
830 {
831 rc = VERR_NO_MEMORY;
832 break;
833 }
834 } while (0);
835
836 LogFlowFunc(("returns %Rrc (pDisk=%#p)\n", rc, pDisk));
837 return rc;
838}
839
840/**
841 * Destroys HDD container.
842 * If container has opened image files they will be closed.
843 *
844 * @param pDisk Pointer to HDD container.
845 */
846VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk)
847{
848 LogFlowFunc(("pDisk=%#p\n", pDisk));
849 do
850 {
851 /* sanity check */
852 AssertPtrBreak(pDisk);
853 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
854 VDCloseAll(pDisk);
855 RTMemFree(pDisk);
856 } while (0);
857 LogFlowFunc(("returns\n"));
858}
859
860/**
861 * Try to get the backend name which can use this image.
862 *
863 * @returns VBox status code.
864 * VINF_SUCCESS if a plugin was found.
865 * ppszFormat contains the string which can be used as backend name.
866 * VERR_NOT_SUPPORTED if no backend was found.
867 * @param pszFilename Name of the image file for which the backend is queried.
868 * @param ppszFormat Receives pointer of the UTF-8 string which contains the format name.
869 * The returned pointer must be freed using RTStrFree().
870 */
871VBOXDDU_DECL(int) VDGetFormat(const char *pszFilename, char **ppszFormat)
872{
873 PRTDIR pPluginDir = NULL;
874 int rc = VERR_NOT_SUPPORTED;
875 bool fPluginFound = false;
876
877 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
878 do
879 {
880 /* Check arguments. */
881 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
882 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
883 rc = VERR_INVALID_PARAMETER);
884 AssertMsgBreakStmt(VALID_PTR(ppszFormat),
885 ("ppszFormat=%#p\n", ppszFormat),
886 rc = VERR_INVALID_PARAMETER);
887
888 /* First check if static backends support this file format. */
889 for (unsigned i = 0; aBackends[i] != NULL; i++)
890 {
891 if (aBackends[i]->pfnCheckIfValid)
892 {
893 rc = aBackends[i]->pfnCheckIfValid(pszFilename);
894 if (RT_SUCCESS(rc))
895 {
896 fPluginFound = true;
897 /* Copy the name into the new string. */
898 char *pszFormat = RTStrDup(aBackends[i]->pszBackendName);
899 if (!pszFormat)
900 {
901 rc = VERR_NO_MEMORY;
902 break;
903 }
904 *ppszFormat = pszFormat;
905 break;
906 }
907 }
908 }
909 if (fPluginFound)
910 break;
911
912 /* Then check if plugin backends support this file format. */
913 char szPath[RTPATH_MAX];
914 rc = RTPathSharedLibs(szPath, sizeof(szPath));
915 if (RT_FAILURE(rc))
916 break;
917
918 /* To get all entries with VBoxHDD as prefix. */
919 char *pszPluginFilter;
920 rc = RTStrAPrintf(&pszPluginFilter, "%s/%s*", szPath,
921 VBOX_HDDFORMAT_PLUGIN_PREFIX);
922 if (RT_FAILURE(rc))
923 {
924 rc = VERR_NO_MEMORY;
925 break;
926 }
927
928 /* The plugins are in the same directory as the other shared libs. */
929 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
930 if (RT_FAILURE(rc))
931 break;
932
933 PRTDIRENTRY pPluginDirEntry = NULL;
934 unsigned cbPluginDirEntry = sizeof(RTDIRENTRY);
935 pPluginDirEntry = (PRTDIRENTRY)RTMemAllocZ(sizeof(RTDIRENTRY));
936 if (!pPluginDirEntry)
937 {
938 rc = VERR_NO_MEMORY;
939 break;
940 }
941
942 while ((rc = RTDirRead(pPluginDir, pPluginDirEntry, &cbPluginDirEntry)) != VERR_NO_MORE_FILES)
943 {
944 RTLDRMOD hPlugin = NIL_RTLDRMOD;
945 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad = NULL;
946 PVBOXHDDBACKEND pBackend = NULL;
947
948 if (rc == VERR_BUFFER_OVERFLOW)
949 {
950 /* allocate new buffer. */
951 RTMemFree(pPluginDirEntry);
952 pPluginDirEntry = (PRTDIRENTRY)RTMemAllocZ(cbPluginDirEntry);
953 /* Retry. */
954 rc = RTDirRead(pPluginDir, pPluginDirEntry, &cbPluginDirEntry);
955 if (RT_FAILURE(rc))
956 break;
957 }
958 else if (RT_FAILURE(rc))
959 break;
960
961 /* We got the new entry. */
962 if (pPluginDirEntry->enmType != RTDIRENTRYTYPE_FILE)
963 continue;
964
965 rc = RTLdrLoad(pPluginDirEntry->szName, &hPlugin);
966 if (RT_SUCCESS(rc))
967 {
968 rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad);
969 if (RT_FAILURE(rc) || !pfnHDDFormatLoad)
970 {
971 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnHDDFormat=%#p\n", VBOX_HDDFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnHDDFormatLoad));
972 if (RT_SUCCESS(rc))
973 rc = VERR_SYMBOL_NOT_FOUND;
974 }
975
976 if (RT_SUCCESS(rc))
977 {
978 /* Get the function table. */
979 rc = pfnHDDFormatLoad(&pBackend);
980 if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VBOXHDDBACKEND))
981 {
982
983 /* Check if the plugin can handle this file. */
984 rc = pBackend->pfnCheckIfValid(pszFilename);
985 if (RT_SUCCESS(rc))
986 {
987 fPluginFound = true;
988 rc = VINF_SUCCESS;
989
990 /* Report the format name. */
991 RTPathStripExt(pPluginDirEntry->szName);
992 AssertBreakStmt(strlen(pPluginDirEntry->szName) > VBOX_HDDFORMAT_PLUGIN_PREFIX_LENGTH,
993 rc = VERR_INVALID_NAME);
994
995 char *pszFormat = NULL;
996 pszFormat = RTStrDup(pPluginDirEntry->szName + VBOX_HDDFORMAT_PLUGIN_PREFIX_LENGTH);
997 if (!pszFormat)
998 rc = VERR_NO_MEMORY;
999
1000 *ppszFormat = pszFormat;
1001 }
1002 }
1003 }
1004 else
1005 pBackend = NULL;
1006
1007 RTLdrClose(hPlugin);
1008 }
1009
1010 /*
1011 * We take the first plugin which can handle this file.
1012 */
1013 if (fPluginFound)
1014 break;
1015 }
1016 if (rc == VERR_NO_MORE_FILES)
1017 rc = VERR_NOT_SUPPORTED;
1018
1019 RTStrFree(pszPluginFilter);
1020 if (pPluginDirEntry)
1021 RTMemFree(pPluginDirEntry);
1022 if (pPluginDir)
1023 RTDirClose(pPluginDir);
1024 } while (0);
1025
1026 LogFlowFunc(("returns %Rrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
1027 return rc;
1028}
1029
1030/**
1031 * Opens an image file.
1032 *
1033 * The first opened image file in HDD container must have a base image type,
1034 * others (next opened images) must be a differencing or undo images.
1035 * Linkage is checked for differencing image to be in consistence with the previously opened image.
1036 * When another differencing image is opened and the last image was opened in read/write access
1037 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
1038 * other processes to use images in read-only mode too.
1039 *
1040 * Note that the image is opened in read-only mode if a read/write open is not possible.
1041 * Use VDIsReadOnly to check open mode.
1042 *
1043 * @returns VBox status code.
1044 * @param pDisk Pointer to HDD container.
1045 * @param pszBackend Name of the image file backend to use.
1046 * @param pszFilename Name of the image file to open.
1047 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1048 * @param pVDIfsImage Pointer to the per-image VD interface list.
1049 */
1050VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
1051 const char *pszFilename, unsigned uOpenFlags,
1052 PVDINTERFACE pVDIfsImage)
1053{
1054 int rc = VINF_SUCCESS;
1055 PVDIMAGE pImage = NULL;
1056
1057 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x\n, pVDIfsImage=%#p",
1058 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsImage));
1059 do
1060 {
1061 /* sanity check */
1062 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1063 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1064
1065 /* Check arguments. */
1066 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
1067 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1068 rc = VERR_INVALID_PARAMETER);
1069 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1070 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1071 rc = VERR_INVALID_PARAMETER);
1072 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1073 ("uOpenFlags=%#x\n", uOpenFlags),
1074 rc = VERR_INVALID_PARAMETER);
1075
1076 /* Set up image descriptor. */
1077 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1078 if (!pImage)
1079 {
1080 rc = VERR_NO_MEMORY;
1081 break;
1082 }
1083 pImage->pszFilename = RTStrDup(pszFilename);
1084 if (!pImage->pszFilename)
1085 {
1086 rc = VERR_NO_MEMORY;
1087 break;
1088 }
1089 pImage->pVDIfsImage = pVDIfsImage;
1090
1091 rc = vdFindBackend(pszBackend, &pImage->Backend, &pImage->hPlugin);
1092 if (RT_FAILURE(rc))
1093 break;
1094 if (!pImage->Backend)
1095 {
1096 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1097 N_("VD: unknown backend name '%s'"), pszBackend);
1098 break;
1099 }
1100
1101 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1102 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
1103 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1104 pDisk->pVDIfsDisk,
1105 pImage->pVDIfsImage,
1106 &pImage->pvBackendData);
1107 /* If the open in read-write mode failed, retry in read-only mode. */
1108 if (RT_FAILURE(rc))
1109 {
1110 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
1111 && (rc == VERR_ACCESS_DENIED
1112 || rc == VERR_PERMISSION_DENIED
1113 || rc == VERR_WRITE_PROTECT
1114 || rc == VERR_SHARING_VIOLATION
1115 || rc == VERR_FILE_LOCK_FAILED))
1116 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
1117 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
1118 | VD_OPEN_FLAGS_READONLY,
1119 pDisk->pVDIfsDisk,
1120 pImage->pVDIfsImage,
1121 &pImage->pvBackendData);
1122 if (RT_FAILURE(rc))
1123 {
1124 rc = vdError(pDisk, rc, RT_SRC_POS,
1125 N_("VD: error opening image file '%s'"), pszFilename);
1126 break;
1127 }
1128 }
1129
1130 VDIMAGETYPE enmImageType;
1131 rc = pImage->Backend->pfnGetImageType(pImage->pvBackendData,
1132 &enmImageType);
1133 /* Check image type. As the image itself has only partial knowledge
1134 * whether it's a base image or not, this info is derived here. The
1135 * base image can be fixed or normal, all others must be normal or
1136 * diff images. Some image formats don't distinguish between normal
1137 * and diff images, so this must be corrected here. */
1138 if (RT_FAILURE(rc))
1139 enmImageType = VD_IMAGE_TYPE_INVALID;
1140 if ( RT_SUCCESS(rc)
1141 && !(uOpenFlags & VD_OPEN_FLAGS_INFO))
1142 {
1143 if ( pDisk->cImages == 0
1144 && enmImageType != VD_IMAGE_TYPE_FIXED
1145 && enmImageType != VD_IMAGE_TYPE_NORMAL)
1146 {
1147 rc = VERR_VDI_INVALID_TYPE;
1148 break;
1149 }
1150 else if (pDisk->cImages != 0)
1151 {
1152 if ( enmImageType != VD_IMAGE_TYPE_NORMAL
1153 && enmImageType != VD_IMAGE_TYPE_DIFF)
1154 {
1155 rc = VERR_VDI_INVALID_TYPE;
1156 break;
1157 }
1158 else
1159 enmImageType = VD_IMAGE_TYPE_DIFF;
1160 }
1161 }
1162 pImage->enmImageType = enmImageType;
1163
1164 /* Force sane optimization settings. It's not worth avoiding writes
1165 * to fixed size images. The overhead would have almost no payback. */
1166 if (enmImageType == VD_IMAGE_TYPE_FIXED)
1167 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
1168
1169 /** @todo optionally check UUIDs */
1170
1171 int rc2;
1172
1173 /* Cache disk information. */
1174 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
1175
1176 /* Cache PCHS geometry. */
1177 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1178 &pDisk->PCHSGeometry);
1179 if (RT_FAILURE(rc2))
1180 {
1181 pDisk->PCHSGeometry.cCylinders = 0;
1182 pDisk->PCHSGeometry.cHeads = 0;
1183 pDisk->PCHSGeometry.cSectors = 0;
1184 }
1185 else
1186 {
1187 /* Make sure the PCHS geometry is properly clipped. */
1188 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1189 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1190 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1191 }
1192
1193 /* Cache LCHS geometry. */
1194 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1195 &pDisk->LCHSGeometry);
1196 if (RT_FAILURE(rc2))
1197 {
1198 pDisk->LCHSGeometry.cCylinders = 0;
1199 pDisk->LCHSGeometry.cHeads = 0;
1200 pDisk->LCHSGeometry.cSectors = 0;
1201 }
1202 else
1203 {
1204 /* Make sure the LCHS geometry is properly clipped. */
1205 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
1206 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1207 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1208 }
1209
1210 if (pDisk->cImages != 0)
1211 {
1212 /* Switch previous image to read-only mode. */
1213 unsigned uOpenFlagsPrevImg;
1214 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1215 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
1216 {
1217 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
1218 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
1219 }
1220 }
1221
1222 if (RT_SUCCESS(rc))
1223 {
1224 /* Image successfully opened, make it the last image. */
1225 vdAddImageToList(pDisk, pImage);
1226 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1227 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1228 }
1229 else
1230 {
1231 /* Error detected, but image opened. Close image. */
1232 int rc2;
1233 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, false);
1234 AssertRC(rc2);
1235 pImage->pvBackendData = NULL;
1236 }
1237 } while (0);
1238
1239 if (RT_FAILURE(rc))
1240 {
1241 if (pImage)
1242 {
1243 if (pImage->hPlugin != NIL_RTLDRMOD)
1244 RTLdrClose(pImage->hPlugin);
1245
1246 if (pImage->pszFilename)
1247 RTStrFree(pImage->pszFilename);
1248 RTMemFree(pImage);
1249 }
1250 }
1251
1252 LogFlowFunc(("returns %Rrc\n", rc));
1253 return rc;
1254}
1255
1256/**
1257 * Creates and opens a new base image file.
1258 *
1259 * @returns VBox status code.
1260 * @param pDisk Pointer to HDD container.
1261 * @param pszBackend Name of the image file backend to use.
1262 * @param pszFilename Name of the image file to create.
1263 * @param enmType Image type, only base image types are acceptable.
1264 * @param cbSize Image size in bytes.
1265 * @param uImageFlags Flags specifying special image features.
1266 * @param pszComment Pointer to image comment. NULL is ok.
1267 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
1268 * @param pLCHSGeometry Pointer to logical disk geometry <= (1024,255,63). Not NULL.
1269 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
1270 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1271 * @param pVDIfsImage Pointer to the per-image VD interface list.
1272 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
1273 */
1274VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
1275 const char *pszFilename, VDIMAGETYPE enmType,
1276 uint64_t cbSize, unsigned uImageFlags,
1277 const char *pszComment,
1278 PCPDMMEDIAGEOMETRY pPCHSGeometry,
1279 PCPDMMEDIAGEOMETRY pLCHSGeometry,
1280 PCRTUUID pUuid, unsigned uOpenFlags,
1281 PVDINTERFACE pVDIfsImage,
1282 PVDINTERFACE pVDIfsOperation)
1283{
1284 int rc = VINF_SUCCESS;
1285 PVDIMAGE pImage = NULL;
1286 RTUUID uuid;
1287
1288 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" enmType=%#x cbSize=%llu uImageFlags=%#x pszComment=\"%s\" PCHS=%u/%u/%u LCHS=%u/%u/%u Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
1289 pDisk, pszBackend, pszFilename, enmType, cbSize, uImageFlags, pszComment,
1290 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1291 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
1292 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
1293 uOpenFlags, pVDIfsImage, pVDIfsOperation));
1294
1295 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
1296 VDINTERFACETYPE_PROGRESS);
1297 PVDINTERFACEPROGRESS pCbProgress = NULL;
1298 if (pIfProgress)
1299 pCbProgress = VDGetInterfaceProgress(pIfProgress);
1300
1301 do
1302 {
1303 /* sanity check */
1304 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1305 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1306
1307 /* Check arguments. */
1308 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
1309 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1310 rc = VERR_INVALID_PARAMETER);
1311 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1312 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1313 rc = VERR_INVALID_PARAMETER);
1314 AssertMsgBreakStmt(enmType == VD_IMAGE_TYPE_NORMAL || enmType == VD_IMAGE_TYPE_FIXED,
1315 ("enmType=%#x\n", enmType),
1316 rc = VERR_INVALID_PARAMETER);
1317 AssertMsgBreakStmt(cbSize,
1318 ("cbSize=%llu\n", cbSize),
1319 rc = VERR_INVALID_PARAMETER);
1320 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
1321 ("uImageFlags=%#x\n", uImageFlags),
1322 rc = VERR_INVALID_PARAMETER);
1323 /* The PCHS geometry fields may be 0 to leave it for later. */
1324 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
1325 && pPCHSGeometry->cCylinders <= 16383
1326 && pPCHSGeometry->cHeads <= 16
1327 && pPCHSGeometry->cSectors <= 63,
1328 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
1329 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1330 pPCHSGeometry->cSectors),
1331 rc = VERR_INVALID_PARAMETER);
1332 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
1333 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
1334 && pLCHSGeometry->cCylinders <= 1024
1335 && pLCHSGeometry->cHeads <= 255
1336 && pLCHSGeometry->cSectors <= 63,
1337 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
1338 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
1339 pLCHSGeometry->cSectors),
1340 rc = VERR_INVALID_PARAMETER);
1341 /* The UUID may be NULL. */
1342 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
1343 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
1344 rc = VERR_INVALID_PARAMETER);
1345 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1346 ("uOpenFlags=%#x\n", uOpenFlags),
1347 rc = VERR_INVALID_PARAMETER);
1348
1349 /* Check state. */
1350 AssertMsgBreakStmt(pDisk->cImages == 0,
1351 ("Create base image cannot be done with other images open\n"),
1352 rc = VERR_VDI_INVALID_STATE);
1353
1354 /* Set up image descriptor. */
1355 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1356 if (!pImage)
1357 {
1358 rc = VERR_NO_MEMORY;
1359 break;
1360 }
1361 pImage->pszFilename = RTStrDup(pszFilename);
1362 if (!pImage->pszFilename)
1363 {
1364 rc = VERR_NO_MEMORY;
1365 break;
1366 }
1367 pImage->pVDIfsImage = pVDIfsImage;
1368
1369 rc = vdFindBackend(pszBackend, &pImage->Backend, &pImage->hPlugin);
1370 if (RT_FAILURE(rc))
1371 break;
1372 if (!pImage->Backend)
1373 {
1374 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1375 N_("VD: unknown backend name '%s'"), pszBackend);
1376 break;
1377 }
1378
1379 /* Create UUID if the caller didn't specify one. */
1380 if (!pUuid)
1381 {
1382 rc = RTUuidCreate(&uuid);
1383 if (RT_FAILURE(rc))
1384 {
1385 rc = vdError(pDisk, rc, RT_SRC_POS,
1386 N_("VD: cannot generate UUID for image '%s'"),
1387 pszFilename);
1388 break;
1389 }
1390 pUuid = &uuid;
1391 }
1392
1393 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1394 rc = pImage->Backend->pfnCreate(pImage->pszFilename, enmType, cbSize,
1395 uImageFlags, pszComment, pPCHSGeometry,
1396 pLCHSGeometry, pUuid,
1397 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1398 0, 99,
1399 pDisk->pVDIfsDisk,
1400 pImage->pVDIfsImage,
1401 pVDIfsOperation,
1402 &pImage->pvBackendData);
1403
1404 if (RT_SUCCESS(rc))
1405 {
1406 pImage->enmImageType = enmType;
1407
1408 /* Force sane optimization settings. It's not worth avoiding writes
1409 * to fixed size images. The overhead would have almost no payback. */
1410 if (enmType == VD_IMAGE_TYPE_FIXED)
1411 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
1412
1413 /** @todo optionally check UUIDs */
1414
1415 int rc2;
1416
1417 /* Cache disk information. */
1418 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
1419
1420 /* Cache PCHS geometry. */
1421 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1422 &pDisk->PCHSGeometry);
1423 if (RT_FAILURE(rc2))
1424 {
1425 pDisk->PCHSGeometry.cCylinders = 0;
1426 pDisk->PCHSGeometry.cHeads = 0;
1427 pDisk->PCHSGeometry.cSectors = 0;
1428 }
1429 else
1430 {
1431 /* Make sure the CHS geometry is properly clipped. */
1432 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1433 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1434 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1435 }
1436
1437 /* Cache LCHS geometry. */
1438 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1439 &pDisk->LCHSGeometry);
1440 if (RT_FAILURE(rc2))
1441 {
1442 pDisk->LCHSGeometry.cCylinders = 0;
1443 pDisk->LCHSGeometry.cHeads = 0;
1444 pDisk->LCHSGeometry.cSectors = 0;
1445 }
1446 else
1447 {
1448 /* Make sure the CHS geometry is properly clipped. */
1449 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
1450 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1451 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1452 }
1453 }
1454
1455 if (RT_SUCCESS(rc))
1456 {
1457 /* Image successfully opened, make it the last image. */
1458 vdAddImageToList(pDisk, pImage);
1459 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1460 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1461 }
1462 else
1463 {
1464 /* Error detected, but image opened. Close and delete image. */
1465 int rc2;
1466 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
1467 AssertRC(rc2);
1468 pImage->pvBackendData = NULL;
1469 }
1470 } while (0);
1471
1472 if (RT_FAILURE(rc))
1473 {
1474 if (pImage)
1475 {
1476 if (pImage->hPlugin != NIL_RTLDRMOD)
1477 RTLdrClose(pImage->hPlugin);
1478
1479 if (pImage->pszFilename)
1480 RTStrFree(pImage->pszFilename);
1481 RTMemFree(pImage);
1482 }
1483 }
1484
1485 if (RT_SUCCESS(rc) && pCbProgress->pfnProgress)
1486 pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */, 100,
1487 pIfProgress->pvUser);
1488
1489 LogFlowFunc(("returns %Rrc\n", rc));
1490 return rc;
1491}
1492
1493/**
1494 * Creates and opens a new differencing image file in HDD container.
1495 * See comments for VDOpen function about differencing images.
1496 *
1497 * @returns VBox status code.
1498 * @param pDisk Pointer to HDD container.
1499 * @param pszBackend Name of the image file backend to use.
1500 * @param pszFilename Name of the differencing image file to create.
1501 * @param uImageFlags Flags specifying special image features.
1502 * @param pszComment Pointer to image comment. NULL is ok.
1503 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
1504 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1505 * @param pVDIfsImage Pointer to the per-image VD interface list.
1506 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
1507 */
1508VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
1509 const char *pszFilename, unsigned uImageFlags,
1510 const char *pszComment, PCRTUUID pUuid,
1511 unsigned uOpenFlags, PVDINTERFACE pVDIfsImage,
1512 PVDINTERFACE pVDIfsOperation)
1513{
1514 int rc = VINF_SUCCESS;
1515 PVDIMAGE pImage = NULL;
1516 RTUUID uuid;
1517
1518 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
1519 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, uOpenFlags,
1520 pVDIfsImage, pVDIfsOperation));
1521
1522 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
1523 VDINTERFACETYPE_PROGRESS);
1524 PVDINTERFACEPROGRESS pCbProgress = NULL;
1525 if (pIfProgress)
1526 pCbProgress = VDGetInterfaceProgress(pIfProgress);
1527
1528 do
1529 {
1530 /* sanity check */
1531 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1532 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1533
1534 /* Check arguments. */
1535 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
1536 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1537 rc = VERR_INVALID_PARAMETER);
1538 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1539 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1540 rc = VERR_INVALID_PARAMETER);
1541 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
1542 ("uImageFlags=%#x\n", uImageFlags),
1543 rc = VERR_INVALID_PARAMETER);
1544 /* The UUID may be NULL. */
1545 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
1546 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
1547 rc = VERR_INVALID_PARAMETER);
1548 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1549 ("uOpenFlags=%#x\n", uOpenFlags),
1550 rc = VERR_INVALID_PARAMETER);
1551
1552 /* Check state. */
1553 AssertMsgBreakStmt(pDisk->cImages != 0,
1554 ("Create diff image cannot be done without other images open\n"),
1555 rc = VERR_VDI_INVALID_STATE);
1556
1557 /* Set up image descriptor. */
1558 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1559 if (!pImage)
1560 {
1561 rc = VERR_NO_MEMORY;
1562 break;
1563 }
1564 pImage->pszFilename = RTStrDup(pszFilename);
1565 if (!pImage->pszFilename)
1566 {
1567 rc = VERR_NO_MEMORY;
1568 break;
1569 }
1570
1571 rc = vdFindBackend(pszBackend, &pImage->Backend, &pImage->hPlugin);
1572 if (RT_FAILURE(rc))
1573 break;
1574 if (!pImage->Backend)
1575 {
1576 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1577 N_("VD: unknown backend name '%s'"), pszBackend);
1578 break;
1579 }
1580
1581 /* Create UUID if the caller didn't specify one. */
1582 if (!pUuid)
1583 {
1584 rc = RTUuidCreate(&uuid);
1585 if (RT_FAILURE(rc))
1586 {
1587 rc = vdError(pDisk, rc, RT_SRC_POS,
1588 N_("VD: cannot generate UUID for image '%s'"),
1589 pszFilename);
1590 break;
1591 }
1592 pUuid = &uuid;
1593 }
1594
1595 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1596 rc = pImage->Backend->pfnCreate(pImage->pszFilename,
1597 VD_IMAGE_TYPE_DIFF, pDisk->cbSize,
1598 uImageFlags, pszComment,
1599 &pDisk->PCHSGeometry,
1600 &pDisk->LCHSGeometry, pUuid,
1601 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1602 0, 99,
1603 pDisk->pVDIfsDisk,
1604 pImage->pVDIfsImage,
1605 pVDIfsOperation,
1606 &pImage->pvBackendData);
1607
1608 if (RT_SUCCESS(rc) && pDisk->cImages != 0)
1609 {
1610 pImage->enmImageType = VD_IMAGE_TYPE_DIFF;
1611
1612 /* Switch previous image to read-only mode. */
1613 unsigned uOpenFlagsPrevImg;
1614 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1615 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
1616 {
1617 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
1618 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
1619 }
1620 }
1621
1622 if (RT_SUCCESS(rc))
1623 {
1624 RTUUID Uuid;
1625 RTTIMESPEC ts;
1626 int rc2;
1627
1628 rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pvBackendData,
1629 &Uuid);
1630 if (RT_SUCCESS(rc2))
1631 pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, &Uuid);
1632 rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pvBackendData,
1633 &Uuid);
1634 if (RT_SUCCESS(rc2))
1635 pImage->Backend->pfnSetParentModificationUuid(pImage->pvBackendData,
1636 &Uuid);
1637 rc2 = pDisk->pLast->Backend->pfnGetTimeStamp(pDisk->pLast->pvBackendData,
1638 &ts);
1639 if (RT_SUCCESS(rc2))
1640 pImage->Backend->pfnSetParentTimeStamp(pImage->pvBackendData, &ts);
1641
1642 rc2 = pImage->Backend->pfnSetParentFilename(pImage->pvBackendData, pDisk->pLast->pszFilename);
1643 }
1644
1645 if (RT_SUCCESS(rc))
1646 {
1647 /** @todo optionally check UUIDs */
1648 }
1649
1650 if (RT_SUCCESS(rc))
1651 {
1652 /* Image successfully opened, make it the last image. */
1653 vdAddImageToList(pDisk, pImage);
1654 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1655 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1656 }
1657 else
1658 {
1659 /* Error detected, but image opened. Close and delete image. */
1660 int rc2;
1661 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
1662 AssertRC(rc2);
1663 pImage->pvBackendData = NULL;
1664 }
1665 } while (0);
1666
1667 if (RT_FAILURE(rc))
1668 {
1669 if (pImage->hPlugin != NIL_RTLDRMOD)
1670 RTLdrClose(pImage->hPlugin);
1671
1672 if (pImage)
1673 {
1674 if (pImage->pszFilename)
1675 RTStrFree(pImage->pszFilename);
1676 RTMemFree(pImage);
1677 }
1678 }
1679
1680 if (RT_SUCCESS(rc) && pCbProgress->pfnProgress)
1681 pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */, 100,
1682 pIfProgress->pvUser);
1683
1684 LogFlowFunc(("returns %Rrc\n", rc));
1685 return rc;
1686}
1687
1688/**
1689 * Merges two images (not necessarily with direct parent/child relationship).
1690 * As a side effect the source image and potentially the other images which
1691 * are also merged to the destination are deleted from both the disk and the
1692 * images in the HDD container.
1693 *
1694 * @returns VBox status code.
1695 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1696 * @param pDisk Pointer to HDD container.
1697 * @param nImageFrom Name of the image file to merge from.
1698 * @param nImageTo Name of the image file to merge to.
1699 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
1700 */
1701VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
1702 unsigned nImageTo, PVDINTERFACE pVDIfsOperation)
1703{
1704 int rc = VINF_SUCCESS;
1705 void *pvBuf = NULL;
1706
1707 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pVDIfsOperation=%#p\n",
1708 pDisk, nImageFrom, nImageTo, pVDIfsOperation));
1709
1710 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
1711 VDINTERFACETYPE_PROGRESS);
1712 PVDINTERFACEPROGRESS pCbProgress = NULL;
1713 if (pIfProgress)
1714 pCbProgress = VDGetInterfaceProgress(pIfProgress);
1715
1716 do
1717 {
1718 /* sanity check */
1719 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1720 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1721
1722 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
1723 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
1724 if (!pImageFrom || !pImageTo)
1725 {
1726 rc = VERR_VDI_IMAGE_NOT_FOUND;
1727 break;
1728 }
1729 AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
1730
1731 /* Make sure destination image is writable. */
1732 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
1733 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
1734 {
1735 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
1736 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pvBackendData,
1737 uOpenFlags);
1738 if (RT_FAILURE(rc))
1739 break;
1740 }
1741
1742 /* Get size of destination image. */
1743 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pvBackendData);
1744
1745 /* Allocate tmp buffer. */
1746 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
1747 if (!pvBuf)
1748 {
1749 rc = VERR_NO_MEMORY;
1750 break;
1751 }
1752
1753 /* Merging is done directly on the images itself. This potentially
1754 * causes trouble if the disk is full in the middle of operation. */
1755 /** @todo write alternative implementation which works with temporary
1756 * images (which is safer, but requires even more space). Also has the
1757 * drawback that merging into a raw disk parent simply isn't possible
1758 * this way (but in that case disk full isn't really a problem). */
1759 if (nImageFrom < nImageTo)
1760 {
1761 /* Merge parent state into child. This means writing all not
1762 * allocated blocks in the destination image which are allocated in
1763 * the images to be merged. */
1764 uint64_t uOffset = 0;
1765 uint64_t cbRemaining = cbSize;
1766 do
1767 {
1768 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1769 rc = pImageTo->Backend->pfnRead(pImageTo->pvBackendData,
1770 uOffset, pvBuf, cbThisRead,
1771 &cbThisRead);
1772 if (rc == VERR_VDI_BLOCK_FREE)
1773 {
1774 /* Search for image with allocated block. Do not attempt to
1775 * read more than the previous reads marked as valid.
1776 * Otherwise this would return stale data when different
1777 * block sizes are used for the images. */
1778 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
1779 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VDI_BLOCK_FREE;
1780 pCurrImage = pCurrImage->pPrev)
1781 {
1782 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
1783 uOffset, pvBuf,
1784 cbThisRead,
1785 &cbThisRead);
1786 }
1787
1788 if (rc != VERR_VDI_BLOCK_FREE)
1789 {
1790 if (RT_FAILURE(rc))
1791 break;
1792 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1793 cbThisRead);
1794 if (RT_FAILURE(rc))
1795 break;
1796 }
1797 else
1798 rc = VINF_SUCCESS;
1799 }
1800 else if (RT_FAILURE(rc))
1801 break;
1802
1803 uOffset += cbThisRead;
1804 cbRemaining -= cbThisRead;
1805
1806 if (pCbProgress->pfnProgress)
1807 {
1808 rc = pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */,
1809 uOffset * 99 / cbSize,
1810 pIfProgress->pvUser);
1811 if (RT_FAILURE(rc))
1812 break;
1813 }
1814 } while (uOffset < cbSize);
1815 }
1816 else
1817 {
1818 /* Merge child state into parent. This means writing all blocks
1819 * which are allocated in the image up to the source image to the
1820 * destination image. */
1821 uint64_t uOffset = 0;
1822 uint64_t cbRemaining = cbSize;
1823 do
1824 {
1825 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1826 rc = VERR_VDI_BLOCK_FREE;
1827 /* Search for image with allocated block. Do not attempt to
1828 * read more than the previous reads marked as valid. Otherwise
1829 * this would return stale data when different block sizes are
1830 * used for the images. */
1831 for (PVDIMAGE pCurrImage = pImageFrom;
1832 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VDI_BLOCK_FREE;
1833 pCurrImage = pCurrImage->pPrev)
1834 {
1835 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
1836 uOffset, pvBuf,
1837 cbThisRead, &cbThisRead);
1838 }
1839
1840 if (rc != VERR_VDI_BLOCK_FREE)
1841 {
1842 if (RT_FAILURE(rc))
1843 break;
1844 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1845 cbThisRead);
1846 if (RT_FAILURE(rc))
1847 break;
1848 }
1849 else
1850 rc = VINF_SUCCESS;
1851
1852 uOffset += cbThisRead;
1853 cbRemaining -= cbThisRead;
1854
1855 if (pCbProgress->pfnProgress)
1856 {
1857 rc = pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */,
1858 uOffset * 99 / cbSize,
1859 pIfProgress->pvUser);
1860 if (RT_FAILURE(rc))
1861 break;
1862 }
1863 } while (uOffset < cbSize);
1864 }
1865
1866 /* Update parent UUID so that image chain is consistent. */
1867 RTUUID Uuid;
1868 if (nImageFrom < nImageTo)
1869 {
1870 if (pImageTo->pPrev)
1871 {
1872 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pPrev->pvBackendData,
1873 &Uuid);
1874 AssertRC(rc);
1875 }
1876 else
1877 RTUuidClear(&Uuid);
1878 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pvBackendData,
1879 &Uuid);
1880 AssertRC(rc);
1881 }
1882 else
1883 {
1884 if (pImageFrom->pNext)
1885 {
1886 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pvBackendData,
1887 &Uuid);
1888 AssertRC(rc);
1889 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext,
1890 &Uuid);
1891 AssertRC(rc);
1892 }
1893 }
1894
1895 /* Make sure destination image is back to read only if necessary. */
1896 if (pImageTo != pDisk->pLast && pImageFrom != pDisk->pLast)
1897 {
1898 uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
1899 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
1900 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pvBackendData,
1901 uOpenFlags);
1902 if (RT_FAILURE(rc))
1903 break;
1904 }
1905
1906 /* Delete the no longer needed images. */
1907 PVDIMAGE pImg = pImageFrom, pTmp;
1908 while (pImg != pImageTo)
1909 {
1910 if (nImageFrom < nImageTo)
1911 pTmp = pImg->pNext;
1912 else
1913 pTmp = pImg->pPrev;
1914 vdRemoveImageFromList(pDisk, pImg);
1915 pImg->Backend->pfnClose(pImg->pvBackendData, true);
1916 pImg = pTmp;
1917 }
1918 } while (0);
1919
1920 if (pvBuf)
1921 RTMemTmpFree(pvBuf);
1922
1923 if (RT_SUCCESS(rc) && pCbProgress->pfnProgress)
1924 pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */, 100,
1925 pIfProgress->pvUser);
1926
1927 LogFlowFunc(("returns %Rrc\n", rc));
1928 return rc;
1929}
1930
1931/**
1932 * Copies an image from one HDD container to another.
1933 * The copy is opened in the target HDD container.
1934 * It is possible to convert between different image formats, because the
1935 * backend for the destination may be different from the source.
1936 * If both the source and destination reference the same HDD container,
1937 * then the image is moved (by copying/deleting or renaming) to the new location.
1938 * The source container is unchanged if the move operation fails, otherwise
1939 * the image at the new location is opened in the same way as the old one was.
1940 *
1941 * @returns VBox status code.
1942 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1943 * @param pDiskFrom Pointer to source HDD container.
1944 * @param nImage Image number, counts from 0. 0 is always base image of container.
1945 * @param pDiskTo Pointer to destination HDD container.
1946 * @param pszBackend Name of the image file backend to use.
1947 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
1948 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
1949 * @param cbSize New image size (0 means leave unchanged).
1950 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
1951 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
1952 * destination image.
1953 * @param pDstVDIfsOperation Pointer to the per-image VD interface list,
1954 * for the destination image.
1955 */
1956VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
1957 const char *pszBackend, const char *pszFilename,
1958 bool fMoveByRename, uint64_t cbSize,
1959 PVDINTERFACE pVDIfsOperation,
1960 PVDINTERFACE pDstVDIfsImage,
1961 PVDINTERFACE pDstVDIfsOperation)
1962{
1963 int rc, rc2 = VINF_SUCCESS;
1964 void *pvBuf = NULL;
1965 PVDIMAGE pImageTo = NULL;
1966
1967 LogFlowFunc(("pDiskFrom=%#p nImage=%u pDiskTo=%#p pszBackend=\"%s\" pszFilename=\"%s\" fMoveByRename=%d cbSize=%llu pVDIfsOperation=%#p pDstVDIfsImage=%#p pDstVDIfsOperation=%#p\n",
1968 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
1969
1970 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
1971 VDINTERFACETYPE_PROGRESS);
1972 PVDINTERFACEPROGRESS pCbProgress = NULL;
1973 if (pIfProgress)
1974 pCbProgress = VDGetInterfaceProgress(pIfProgress);
1975
1976 PVDINTERFACE pDstIfProgress = VDInterfaceGet(pDstVDIfsOperation,
1977 VDINTERFACETYPE_PROGRESS);
1978 PVDINTERFACEPROGRESS pDstCbProgress = NULL;
1979 if (pDstIfProgress)
1980 pDstCbProgress = VDGetInterfaceProgress(pDstIfProgress);
1981
1982 do {
1983 /* Check arguments. */
1984 AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
1985 rc = VERR_INVALID_PARAMETER);
1986 AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
1987 ("u32Signature=%08x\n", pDiskFrom->u32Signature));
1988
1989 PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
1990 AssertPtrBreakStmt(pImageFrom, rc = VERR_VDI_IMAGE_NOT_FOUND);
1991 AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
1992 rc = VERR_INVALID_PARAMETER);
1993 AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
1994 ("u32Signature=%08x\n", pDiskTo->u32Signature));
1995
1996 /* If the containers are equal and the backend is the same, rename the image. */
1997 if ( (pDiskFrom == pDiskTo)
1998 && (!strcmp(pszBackend, pImageFrom->Backend->pszBackendName)))
1999 {
2000 /* Rename the image. */
2001 rc = pImageFrom->Backend->pfnRename(pImageFrom->pvBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
2002 break;
2003 }
2004
2005 /* If the fMoveByRename flag is set and the backend is the same, rename the image. */
2006 if ( (fMoveByRename == true)
2007 && (!strcmp(pszBackend, pImageFrom->Backend->pszBackendName)))
2008 {
2009 /* Close the source image. */
2010 rc = pImageFrom->Backend->pfnClose(pImageFrom->pvBackendData, false);
2011 if (RT_FAILURE(rc))
2012 break;
2013
2014 /* Open the source image in the destination container. */
2015 rc = VDOpen(pDiskTo, pImageFrom->Backend->pszBackendName, pImageFrom->pszFilename, pImageFrom->uOpenFlags, pDstVDIfsImage);
2016 if (RT_FAILURE(rc))
2017 goto movefail;
2018
2019 pImageTo = pDiskTo->pLast;
2020
2021 /* Rename the image. */
2022 rc = pImageTo->Backend->pfnRename(pImageTo->pvBackendData, pszFilename ? pszFilename : pImageTo->pszFilename);
2023 if (RT_FAILURE(rc))
2024 goto movefail;
2025
2026 /* Cleanup the leftovers. */
2027 vdRemoveImageFromList(pDiskFrom, pImageFrom);
2028 pImageFrom->pvBackendData = NULL;
2029
2030 if (pImageFrom->hPlugin != NIL_RTLDRMOD)
2031 RTLdrClose(pImageFrom->hPlugin);
2032
2033 if (pImageFrom->pszFilename)
2034 RTStrFree(pImageFrom->pszFilename);
2035
2036 RTMemFree(pImageFrom);
2037
2038 break;
2039movefail:
2040 /* In case of failure, re-open the source image in the source container. */
2041 rc2 = VDOpen(pDiskFrom, pImageFrom->Backend->pszBackendName, pImageFrom->pszFilename, pImageFrom->uOpenFlags, pImageFrom->pVDIfsImage);
2042 if (RT_FAILURE(rc2))
2043 /** @todo Uncertain what to do on error. If this happens pImageFrom and pImageTo are both closed. */
2044 rc = rc2;
2045 break;
2046 }
2047
2048 /* If fMoveByRename is set pszFilename is allowed to be NULL, so do the parameter check here. */
2049 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
2050 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
2051 rc = VERR_INVALID_PARAMETER);
2052
2053 /* Collect properties of source image. */
2054 VDIMAGETYPE enmTypeFrom = pImageFrom->enmImageType;
2055
2056 uint64_t cbSizeFrom;
2057 cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pvBackendData);
2058 if (cbSizeFrom == 0)
2059 {
2060 rc = VERR_VDI_VALUE_NOT_FOUND;
2061 break;
2062 }
2063
2064 if (cbSize == 0)
2065 cbSize = cbSizeFrom;
2066
2067 unsigned uImageFlagsFrom;
2068 uImageFlagsFrom = pImageFrom->Backend->pfnGetImageFlags(pImageFrom->pvBackendData);
2069
2070 /** @todo Get this from the source image. */
2071 PDMMEDIAGEOMETRY PCHSGeometryFrom = {0, 0, 0};
2072 PDMMEDIAGEOMETRY LCHSGeometryFrom = {0, 0, 0};
2073
2074 unsigned uOpenFlagsFrom;
2075 uOpenFlagsFrom = pImageFrom->Backend->pfnGetOpenFlags(pImageFrom->pvBackendData);
2076
2077 /* Create destination image with the properties of the source image. */
2078 /** @todo Copy the comment. */
2079 /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
2080 * calls to the backend. Unifies the code and reduces the API
2081 * dependencies. */
2082 if (enmTypeFrom == VD_IMAGE_TYPE_DIFF)
2083 {
2084 rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename, uImageFlagsFrom,
2085 "", NULL, uOpenFlagsFrom, NULL, NULL);
2086 } else {
2087 rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, enmTypeFrom,
2088 cbSize, uImageFlagsFrom, "",
2089 &PCHSGeometryFrom, &LCHSGeometryFrom,
2090 NULL, uOpenFlagsFrom, NULL, NULL);
2091 }
2092 if (RT_FAILURE(rc))
2093 break;
2094
2095 pImageTo = pDiskTo->pLast;
2096 AssertPtrBreakStmt(pImageTo, rc = VERR_VDI_IMAGE_NOT_FOUND);
2097
2098 /* Allocate tmp buffer. */
2099 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
2100 if (!pvBuf)
2101 {
2102 rc = VERR_NO_MEMORY;
2103 break;
2104 }
2105
2106 /* Copy the data. */
2107 uint64_t uOffset = 0;
2108 uint64_t cbRemaining = cbSize;
2109
2110 do
2111 {
2112 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
2113
2114 rc = vdReadHelper(pDiskFrom, pImageFrom, uOffset, pvBuf,
2115 cbThisRead);
2116 if (RT_FAILURE(rc))
2117 break;
2118
2119 rc = vdWriteHelper(pDiskTo, pImageTo, uOffset, pvBuf,
2120 cbThisRead);
2121 if (RT_FAILURE(rc))
2122 break;
2123
2124 uOffset += cbThisRead;
2125 cbRemaining -= cbThisRead;
2126
2127 if (pCbProgress->pfnProgress)
2128 {
2129 rc = pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */,
2130 uOffset * 99 / cbSize,
2131 pIfProgress->pvUser);
2132 if (RT_FAILURE(rc))
2133 break;
2134 }
2135 if (pDstCbProgress->pfnProgress)
2136 {
2137 rc = pDstCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */,
2138 uOffset * 99 / cbSize,
2139 pDstIfProgress->pvUser);
2140 if (RT_FAILURE(rc))
2141 break;
2142 }
2143 } while (uOffset < cbSize);
2144
2145 /* If fMoveByRename is set but the backend is different, close and delete pImageFrom. */
2146 if ( (fMoveByRename == true)
2147 && (strcmp(pszBackend, pImageFrom->Backend->pszBackendName)))
2148 {
2149 vdRemoveImageFromList(pDiskFrom, pImageFrom);
2150
2151 /* Close and delete image. */
2152 rc2 = pImageFrom->Backend->pfnClose(pImageFrom->pvBackendData, true);
2153 AssertRC(rc2);
2154 pImageFrom->pvBackendData = NULL;
2155
2156 /* Free remaining resources. */
2157 if (pImageFrom->hPlugin != NIL_RTLDRMOD)
2158 RTLdrClose(pImageFrom->hPlugin);
2159
2160 if (pImageFrom->pszFilename)
2161 RTStrFree(pImageFrom->pszFilename);
2162
2163 RTMemFree(pImageFrom);
2164 }
2165 } while (0);
2166
2167 if (RT_FAILURE(rc) && pImageTo)
2168 {
2169 /* Error detected, but new image created. Remove image from list. */
2170 vdRemoveImageFromList(pDiskTo, pImageTo);
2171
2172 /* Close and delete image. */
2173 rc2 = pImageTo->Backend->pfnClose(pImageTo->pvBackendData, true);
2174 AssertRC(rc2);
2175 pImageTo->pvBackendData = NULL;
2176
2177 /* Free remaining resources. */
2178 if (pImageTo->hPlugin != NIL_RTLDRMOD)
2179 RTLdrClose(pImageTo->hPlugin);
2180
2181 if (pImageTo->pszFilename)
2182 RTStrFree(pImageTo->pszFilename);
2183
2184 RTMemFree(pImageTo);
2185 }
2186
2187 if (pvBuf)
2188 RTMemTmpFree(pvBuf);
2189
2190 if (RT_SUCCESS(rc))
2191 {
2192 if (pCbProgress->pfnProgress)
2193 pCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */, 100,
2194 pIfProgress->pvUser);
2195 if (pDstCbProgress->pfnProgress)
2196 pDstCbProgress->pfnProgress(NULL /* WARNING! pVM=NULL */, 100,
2197 pDstIfProgress->pvUser);
2198 }
2199
2200 LogFlowFunc(("returns %Rrc\n", rc));
2201 return rc;
2202}
2203
2204/**
2205 * Closes the last opened image file in HDD container.
2206 * If previous image file was opened in read-only mode (that is normal) and closing image
2207 * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
2208 * will be reopened in read/write mode.
2209 *
2210 * @returns VBox status code.
2211 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2212 * @param pDisk Pointer to HDD container.
2213 * @param fDelete If true, delete the image from the host disk.
2214 */
2215VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
2216{
2217 int rc = VINF_SUCCESS;;
2218
2219 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
2220 do
2221 {
2222 /* sanity check */
2223 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2224 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2225
2226 PVDIMAGE pImage = pDisk->pLast;
2227 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2228 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2229 /* Remove image from list of opened images. */
2230 vdRemoveImageFromList(pDisk, pImage);
2231 /* Close (and optionally delete) image. */
2232 rc = pImage->Backend->pfnClose(pImage->pvBackendData, fDelete);
2233 /* Free remaining resources related to the image. */
2234 if (pImage->hPlugin != NIL_RTLDRMOD)
2235 {
2236 RTLdrClose(pImage->hPlugin);
2237 pImage->hPlugin = NIL_RTLDRMOD;
2238 }
2239 RTStrFree(pImage->pszFilename);
2240 RTMemFree(pImage);
2241
2242 pImage = pDisk->pLast;
2243 if (!pImage)
2244 break;
2245
2246 /* If disk was previously in read/write mode, make sure it will stay
2247 * like this (if possible) after closing this image. Set the open flags
2248 * accordingly. */
2249 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
2250 {
2251 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2252 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
2253 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData, uOpenFlags);
2254 }
2255
2256 int rc2;
2257
2258 /* Cache disk information. */
2259 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
2260
2261 /* Cache PCHS geometry. */
2262 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2263 &pDisk->PCHSGeometry);
2264 if (RT_FAILURE(rc2))
2265 {
2266 pDisk->PCHSGeometry.cCylinders = 0;
2267 pDisk->PCHSGeometry.cHeads = 0;
2268 pDisk->PCHSGeometry.cSectors = 0;
2269 }
2270 else
2271 {
2272 /* Make sure the PCHS geometry is properly clipped. */
2273 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
2274 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
2275 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
2276 }
2277
2278 /* Cache LCHS geometry. */
2279 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2280 &pDisk->LCHSGeometry);
2281 if (RT_FAILURE(rc2))
2282 {
2283 pDisk->LCHSGeometry.cCylinders = 0;
2284 pDisk->LCHSGeometry.cHeads = 0;
2285 pDisk->LCHSGeometry.cSectors = 0;
2286 }
2287 else
2288 {
2289 /* Make sure the LCHS geometry is properly clipped. */
2290 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
2291 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
2292 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
2293 }
2294 } while (0);
2295
2296 LogFlowFunc(("returns %Rrc\n", rc));
2297 return rc;
2298}
2299
2300/**
2301 * Closes all opened image files in HDD container.
2302 *
2303 * @returns VBox status code.
2304 * @param pDisk Pointer to HDD container.
2305 */
2306VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
2307{
2308 int rc = VINF_SUCCESS;
2309
2310 LogFlowFunc(("pDisk=%#p\n", pDisk));
2311 do
2312 {
2313 /* sanity check */
2314 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2315 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2316
2317 PVDIMAGE pImage = pDisk->pLast;
2318 while (VALID_PTR(pImage))
2319 {
2320 PVDIMAGE pPrev = pImage->pPrev;
2321 /* Remove image from list of opened images. */
2322 vdRemoveImageFromList(pDisk, pImage);
2323 /* Close image. */
2324 int rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, false);
2325 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
2326 rc = rc2;
2327 /* Free remaining resources related to the image. */
2328 if (pImage->hPlugin != NIL_RTLDRMOD)
2329 {
2330 RTLdrClose(pImage->hPlugin);
2331 pImage->hPlugin = NIL_RTLDRMOD;
2332 }
2333 RTStrFree(pImage->pszFilename);
2334 RTMemFree(pImage);
2335 pImage = pPrev;
2336 }
2337 Assert(!VALID_PTR(pDisk->pLast));
2338 } while (0);
2339
2340 LogFlowFunc(("returns %Rrc\n", rc));
2341 return rc;
2342}
2343
2344/**
2345 * Read data from virtual HDD.
2346 *
2347 * @returns VBox status code.
2348 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2349 * @param pDisk Pointer to HDD container.
2350 * @param uOffset Offset of first reading byte from start of disk.
2351 * @param pvBuf Pointer to buffer for reading data.
2352 * @param cbRead Number of bytes to read.
2353 */
2354VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
2355 size_t cbRead)
2356{
2357 int rc;
2358
2359 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
2360 pDisk, uOffset, pvBuf, cbRead));
2361 do
2362 {
2363 /* sanity check */
2364 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2365 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2366
2367 /* Check arguments. */
2368 AssertMsgBreakStmt(VALID_PTR(pvBuf),
2369 ("pvBuf=%#p\n", pvBuf),
2370 rc = VERR_INVALID_PARAMETER);
2371 AssertMsgBreakStmt(cbRead,
2372 ("cbRead=%zu\n", cbRead),
2373 rc = VERR_INVALID_PARAMETER);
2374 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
2375 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
2376 uOffset, cbRead, pDisk->cbSize),
2377 rc = VERR_INVALID_PARAMETER);
2378
2379 PVDIMAGE pImage = pDisk->pLast;
2380 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2381
2382 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead);
2383 } while (0);
2384
2385 LogFlowFunc(("returns %Rrc\n", rc));
2386 return rc;
2387}
2388
2389/**
2390 * Write data to virtual HDD.
2391 *
2392 * @returns VBox status code.
2393 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2394 * @param pDisk Pointer to HDD container.
2395 * @param uOffset Offset of the first byte being
2396 * written from start of disk.
2397 * @param pvBuf Pointer to buffer for writing data.
2398 * @param cbWrite Number of bytes to write.
2399 */
2400VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
2401 size_t cbWrite)
2402{
2403 int rc = VINF_SUCCESS;
2404
2405 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
2406 pDisk, uOffset, pvBuf, cbWrite));
2407 do
2408 {
2409 /* sanity check */
2410 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2411 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2412
2413 /* Check arguments. */
2414 AssertMsgBreakStmt(VALID_PTR(pvBuf),
2415 ("pvBuf=%#p\n", pvBuf),
2416 rc = VERR_INVALID_PARAMETER);
2417 AssertMsgBreakStmt(cbWrite,
2418 ("cbWrite=%zu\n", cbWrite),
2419 rc = VERR_INVALID_PARAMETER);
2420 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
2421 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
2422 uOffset, cbWrite, pDisk->cbSize),
2423 rc = VERR_INVALID_PARAMETER);
2424
2425 PVDIMAGE pImage = pDisk->pLast;
2426 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2427
2428 vdSetModifiedFlag(pDisk);
2429 rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite);
2430 } while (0);
2431
2432 LogFlowFunc(("returns %Rrc\n", rc));
2433 return rc;
2434}
2435
2436/**
2437 * Make sure the on disk representation of a virtual HDD is up to date.
2438 *
2439 * @returns VBox status code.
2440 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
2441 * @param pDisk Pointer to HDD container.
2442 */
2443VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
2444{
2445 int rc = VINF_SUCCESS;
2446
2447 LogFlowFunc(("pDisk=%#p\n", pDisk));
2448 do
2449 {
2450 /* sanity check */
2451 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2452 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2453
2454 PVDIMAGE pImage = pDisk->pLast;
2455 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
2456
2457 vdResetModifiedFlag(pDisk);
2458 rc = pImage->Backend->pfnFlush(pImage->pvBackendData);
2459 } while (0);
2460
2461 LogFlowFunc(("returns %Rrc\n", rc));
2462 return rc;
2463}
2464
2465/**
2466 * Get number of opened images in HDD container.
2467 *
2468 * @returns Number of opened images for HDD container. 0 if no images have been opened.
2469 * @param pDisk Pointer to HDD container.
2470 */
2471VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
2472{
2473 unsigned cImages;
2474
2475 LogFlowFunc(("pDisk=%#p\n", pDisk));
2476 do
2477 {
2478 /* sanity check */
2479 AssertPtrBreakStmt(pDisk, cImages = 0);
2480 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2481
2482 cImages = pDisk->cImages;
2483 } while (0);
2484
2485 LogFlowFunc(("returns %u\n", cImages));
2486 return cImages;
2487}
2488
2489/**
2490 * Get read/write mode of HDD container.
2491 *
2492 * @returns Virtual disk ReadOnly status.
2493 * @returns true if no image is opened in HDD container.
2494 * @param pDisk Pointer to HDD container.
2495 */
2496VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
2497{
2498 bool fReadOnly;
2499
2500 LogFlowFunc(("pDisk=%#p\n", pDisk));
2501 do
2502 {
2503 /* sanity check */
2504 AssertPtrBreakStmt(pDisk, fReadOnly = false);
2505 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2506
2507 PVDIMAGE pImage = pDisk->pLast;
2508 AssertPtrBreakStmt(pImage, fReadOnly = true);
2509
2510 unsigned uOpenFlags;
2511 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
2512 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
2513 } while (0);
2514
2515 LogFlowFunc(("returns %d\n", fReadOnly));
2516 return fReadOnly;
2517}
2518
2519/**
2520 * Get total capacity of an image in HDD container.
2521 *
2522 * @returns Virtual disk size in bytes.
2523 * @returns 0 if no image with specified number was not opened.
2524 * @param pDisk Pointer to HDD container.
2525 * @param nImage Image number, counds from 0. 0 is always base image of container.
2526 */
2527VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
2528{
2529 uint64_t cbSize;
2530
2531 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
2532 do
2533 {
2534 /* sanity check */
2535 AssertPtrBreakStmt(pDisk, cbSize = 0);
2536 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2537
2538 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2539 AssertPtrBreakStmt(pImage, cbSize = 0);
2540 cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
2541 } while (0);
2542
2543 LogFlowFunc(("returns %llu\n", cbSize));
2544 return cbSize;
2545}
2546
2547/**
2548 * Get total file size of an image in HDD container.
2549 *
2550 * @returns Virtual disk size in bytes.
2551 * @returns 0 if no image is opened in HDD container.
2552 * @param pDisk Pointer to HDD container.
2553 * @param nImage Image number, counts from 0. 0 is always base image of container.
2554 */
2555VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
2556{
2557 uint64_t cbSize;
2558
2559 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
2560 do
2561 {
2562 /* sanity check */
2563 AssertPtrBreakStmt(pDisk, cbSize = 0);
2564 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2565
2566 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2567 AssertPtrBreakStmt(pImage, cbSize = 0);
2568 cbSize = pImage->Backend->pfnGetFileSize(pImage->pvBackendData);
2569 } while (0);
2570
2571 LogFlowFunc(("returns %llu\n", cbSize));
2572 return cbSize;
2573}
2574
2575/**
2576 * Get virtual disk PCHS geometry stored in HDD container.
2577 *
2578 * @returns VBox status code.
2579 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2580 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2581 * @param pDisk Pointer to HDD container.
2582 * @param nImage Image number, counts from 0. 0 is always base image of container.
2583 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
2584 */
2585VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2586 PPDMMEDIAGEOMETRY pPCHSGeometry)
2587{
2588 int rc = VINF_SUCCESS;
2589
2590 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
2591 pDisk, nImage, pPCHSGeometry));
2592 do
2593 {
2594 /* sanity check */
2595 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2596 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2597
2598 /* Check arguments. */
2599 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
2600 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
2601 rc = VERR_INVALID_PARAMETER);
2602
2603 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2604 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2605
2606 if (pImage == pDisk->pLast)
2607 {
2608 /* Use cached information if possible. */
2609 if (pDisk->PCHSGeometry.cCylinders != 0)
2610 *pPCHSGeometry = pDisk->PCHSGeometry;
2611 else
2612 rc = VERR_VDI_GEOMETRY_NOT_SET;
2613 }
2614 else
2615 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2616 pPCHSGeometry);
2617 } while (0);
2618
2619 LogFlowFunc(("%s: %Rrc (PCHS=%u/%u/%u)\n", __FUNCTION__, rc,
2620 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
2621 pDisk->PCHSGeometry.cSectors));
2622 return rc;
2623}
2624
2625/**
2626 * Store virtual disk PCHS geometry in HDD container.
2627 *
2628 * Note that in case of unrecoverable error all images in HDD container will be closed.
2629 *
2630 * @returns VBox status code.
2631 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2632 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2633 * @param pDisk Pointer to HDD container.
2634 * @param nImage Image number, counts from 0. 0 is always base image of container.
2635 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
2636 */
2637VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2638 PCPDMMEDIAGEOMETRY pPCHSGeometry)
2639{
2640 int rc = VINF_SUCCESS;
2641
2642 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
2643 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
2644 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
2645 do
2646 {
2647 /* sanity check */
2648 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2649 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2650
2651 /* Check arguments. */
2652 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
2653 && pPCHSGeometry->cCylinders <= 16383
2654 && pPCHSGeometry->cHeads <= 16
2655 && pPCHSGeometry->cSectors <= 63,
2656 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
2657 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
2658 pPCHSGeometry->cSectors),
2659 rc = VERR_INVALID_PARAMETER);
2660
2661 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2662 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2663
2664 if (pImage == pDisk->pLast)
2665 {
2666 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
2667 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
2668 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
2669 {
2670 /* Only update geometry if it is changed. Avoids similar checks
2671 * in every backend. Most of the time the new geometry is set
2672 * to the previous values, so no need to go through the hassle
2673 * of updating an image which could be opened in read-only mode
2674 * right now. */
2675 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2676 pPCHSGeometry);
2677
2678 /* Cache new geometry values in any case. */
2679 int rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2680 &pDisk->PCHSGeometry);
2681 if (RT_FAILURE(rc2))
2682 {
2683 pDisk->PCHSGeometry.cCylinders = 0;
2684 pDisk->PCHSGeometry.cHeads = 0;
2685 pDisk->PCHSGeometry.cSectors = 0;
2686 }
2687 else
2688 {
2689 /* Make sure the CHS geometry is properly clipped. */
2690 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 1024);
2691 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
2692 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
2693 }
2694 }
2695 }
2696 else
2697 {
2698 PDMMEDIAGEOMETRY PCHS;
2699 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2700 &PCHS);
2701 if ( RT_FAILURE(rc)
2702 || pPCHSGeometry->cCylinders != PCHS.cCylinders
2703 || pPCHSGeometry->cHeads != PCHS.cHeads
2704 || pPCHSGeometry->cSectors != PCHS.cSectors)
2705 {
2706 /* Only update geometry if it is changed. Avoids similar checks
2707 * in every backend. Most of the time the new geometry is set
2708 * to the previous values, so no need to go through the hassle
2709 * of updating an image which could be opened in read-only mode
2710 * right now. */
2711 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2712 pPCHSGeometry);
2713 }
2714 }
2715 } while (0);
2716
2717 LogFlowFunc(("returns %Rrc\n", rc));
2718 return rc;
2719}
2720
2721/**
2722 * Get virtual disk LCHS geometry stored in HDD container.
2723 *
2724 * @returns VBox status code.
2725 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2726 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2727 * @param pDisk Pointer to HDD container.
2728 * @param nImage Image number, counts from 0. 0 is always base image of container.
2729 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
2730 */
2731VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2732 PPDMMEDIAGEOMETRY pLCHSGeometry)
2733{
2734 int rc = VINF_SUCCESS;
2735
2736 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
2737 pDisk, nImage, pLCHSGeometry));
2738 do
2739 {
2740 /* sanity check */
2741 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2742 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2743
2744 /* Check arguments. */
2745 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
2746 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
2747 rc = VERR_INVALID_PARAMETER);
2748
2749 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2750 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2751
2752 if (pImage == pDisk->pLast)
2753 {
2754 /* Use cached information if possible. */
2755 if (pDisk->LCHSGeometry.cCylinders != 0)
2756 *pLCHSGeometry = pDisk->LCHSGeometry;
2757 else
2758 rc = VERR_VDI_GEOMETRY_NOT_SET;
2759 }
2760 else
2761 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2762 pLCHSGeometry);
2763 } while (0);
2764
2765 LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
2766 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
2767 pDisk->LCHSGeometry.cSectors));
2768 return rc;
2769}
2770
2771/**
2772 * Store virtual disk LCHS geometry in HDD container.
2773 *
2774 * Note that in case of unrecoverable error all images in HDD container will be closed.
2775 *
2776 * @returns VBox status code.
2777 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2778 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2779 * @param pDisk Pointer to HDD container.
2780 * @param nImage Image number, counts from 0. 0 is always base image of container.
2781 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
2782 */
2783VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2784 PCPDMMEDIAGEOMETRY pLCHSGeometry)
2785{
2786 int rc = VINF_SUCCESS;
2787
2788 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
2789 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
2790 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2791 do
2792 {
2793 /* sanity check */
2794 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2795 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2796
2797 /* Check arguments. */
2798 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
2799 && pLCHSGeometry->cCylinders <= 1024
2800 && pLCHSGeometry->cHeads <= 255
2801 && pLCHSGeometry->cSectors <= 63,
2802 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
2803 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
2804 pLCHSGeometry->cSectors),
2805 rc = VERR_INVALID_PARAMETER);
2806
2807 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2808 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2809
2810 if (pImage == pDisk->pLast)
2811 {
2812 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
2813 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
2814 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
2815 {
2816 /* Only update geometry if it is changed. Avoids similar checks
2817 * in every backend. Most of the time the new geometry is set
2818 * to the previous values, so no need to go through the hassle
2819 * of updating an image which could be opened in read-only mode
2820 * right now. */
2821 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2822 pLCHSGeometry);
2823
2824 /* Cache new geometry values in any case. */
2825 int rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2826 &pDisk->LCHSGeometry);
2827 if (RT_FAILURE(rc2))
2828 {
2829 pDisk->LCHSGeometry.cCylinders = 0;
2830 pDisk->LCHSGeometry.cHeads = 0;
2831 pDisk->LCHSGeometry.cSectors = 0;
2832 }
2833 else
2834 {
2835 /* Make sure the CHS geometry is properly clipped. */
2836 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
2837 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
2838 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
2839 }
2840 }
2841 }
2842 else
2843 {
2844 PDMMEDIAGEOMETRY LCHS;
2845 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2846 &LCHS);
2847 if ( RT_FAILURE(rc)
2848 || pLCHSGeometry->cCylinders != LCHS.cCylinders
2849 || pLCHSGeometry->cHeads != LCHS.cHeads
2850 || pLCHSGeometry->cSectors != LCHS.cSectors)
2851 {
2852 /* Only update geometry if it is changed. Avoids similar checks
2853 * in every backend. Most of the time the new geometry is set
2854 * to the previous values, so no need to go through the hassle
2855 * of updating an image which could be opened in read-only mode
2856 * right now. */
2857 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2858 pLCHSGeometry);
2859 }
2860 }
2861 } while (0);
2862
2863 LogFlowFunc(("returns %Rrc\n", rc));
2864 return rc;
2865}
2866
2867/**
2868 * Get version of image in HDD container.
2869 *
2870 * @returns VBox status code.
2871 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2872 * @param pDisk Pointer to HDD container.
2873 * @param nImage Image number, counts from 0. 0 is always base image of container.
2874 * @param puVersion Where to store the image version.
2875 */
2876VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
2877 unsigned *puVersion)
2878{
2879 int rc = VINF_SUCCESS;
2880
2881 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
2882 pDisk, nImage, puVersion));
2883 do
2884 {
2885 /* sanity check */
2886 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2887 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2888
2889 /* Check arguments. */
2890 AssertMsgBreakStmt(VALID_PTR(puVersion),
2891 ("puVersion=%#p\n", puVersion),
2892 rc = VERR_INVALID_PARAMETER);
2893
2894 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2895 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2896
2897 *puVersion = pImage->Backend->pfnGetVersion(pImage->pvBackendData);
2898 } while (0);
2899
2900 LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
2901 return rc;
2902}
2903
2904/**
2905 * Get type of image in HDD container.
2906 *
2907 * @returns VBox status code.
2908 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2909 * @param pDisk Pointer to HDD container.
2910 * @param nImage Image number, counts from 0. 0 is always base image of container.
2911 * @param penmType Where to store the image type.
2912 */
2913VBOXDDU_DECL(int) VDGetImageType(PVBOXHDD pDisk, unsigned nImage,
2914 PVDIMAGETYPE penmType)
2915{
2916 int rc = VINF_SUCCESS;
2917
2918 LogFlowFunc(("pDisk=%#p nImage=%u penmType=%#p\n",
2919 pDisk, nImage, penmType));
2920 do
2921 {
2922 /* sanity check */
2923 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2924 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2925
2926 /* Check arguments. */
2927 AssertMsgBreakStmt(VALID_PTR(penmType),
2928 ("penmType=%#p\n", penmType),
2929 rc = VERR_INVALID_PARAMETER);
2930
2931 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2932 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2933
2934 if ( pImage->enmImageType >= VD_IMAGE_TYPE_FIRST
2935 && pImage->enmImageType <= VD_IMAGE_TYPE_DIFF)
2936 {
2937 *penmType = pImage->enmImageType;
2938 rc = VINF_SUCCESS;
2939 }
2940 else
2941 rc = VERR_VDI_INVALID_TYPE;
2942 } while (0);
2943
2944 LogFlowFunc(("returns %Rrc uenmType=%u\n", rc, *penmType));
2945 return rc;
2946}
2947
2948
2949/**
2950 * List the capabilities of image backend in HDD container.
2951 *
2952 * @returns VBox status code.
2953 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2954 * @param pDisk Pointer to the HDD container.
2955 * @param nImage Image number, counts from 0. 0 is always base image of container.
2956 * @param pbackendInfo Where to store the backend information.
2957 */
2958VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
2959 PVDBACKENDINFO pBackendInfo)
2960{
2961 int rc = VINF_SUCCESS;
2962
2963 LogFlowFunc(("pDisk=%#p nImage=%u penmType=%#p\n",
2964 pDisk, nImage, pBackendInfo));
2965 do
2966 {
2967 /* sanity check */
2968 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2969 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2970
2971 /* Check arguments. */
2972 AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
2973 ("pBackendInfo=%#p\n", pBackendInfo),
2974 rc = VERR_INVALID_PARAMETER);
2975
2976 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2977 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2978
2979 if ( pImage->enmImageType >= VD_IMAGE_TYPE_FIRST
2980 && pImage->enmImageType <= VD_IMAGE_TYPE_DIFF)
2981 {
2982 pBackendInfo->pszBackend = RTStrDup(pImage->Backend->pszBackendName);
2983 pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
2984 pBackendInfo->papszFileExtensions = pImage->Backend->papszFileExtensions;
2985 rc = VINF_SUCCESS;
2986 }
2987 else
2988 rc = VERR_VDI_INVALID_TYPE;
2989 } while (0);
2990
2991 LogFlowFunc(("returns %Rrc\n", rc));
2992 return rc;
2993}
2994
2995/**
2996 * Get flags of image in HDD container.
2997 *
2998 * @returns VBox status code.
2999 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3000 * @param pDisk Pointer to HDD container.
3001 * @param nImage Image number, counts from 0. 0 is always base image of container.
3002 * @param puImageFlags Where to store the image flags.
3003 */
3004VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
3005 unsigned *puImageFlags)
3006{
3007 int rc = VINF_SUCCESS;
3008
3009 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
3010 pDisk, nImage, puImageFlags));
3011 do
3012 {
3013 /* sanity check */
3014 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3015 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3016
3017 /* Check arguments. */
3018 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
3019 ("puImageFlags=%#p\n", puImageFlags),
3020 rc = VERR_INVALID_PARAMETER);
3021
3022 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3023 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3024
3025 *puImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pvBackendData);
3026 } while (0);
3027
3028 LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
3029 return rc;
3030}
3031
3032/**
3033 * Get open flags of image in HDD container.
3034 *
3035 * @returns VBox status code.
3036 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3037 * @param pDisk Pointer to HDD container.
3038 * @param nImage Image number, counts from 0. 0 is always base image of container.
3039 * @param puOpenFlags Where to store the image open flags.
3040 */
3041VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
3042 unsigned *puOpenFlags)
3043{
3044 int rc = VINF_SUCCESS;
3045
3046 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
3047 pDisk, nImage, puOpenFlags));
3048 do
3049 {
3050 /* sanity check */
3051 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3052 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3053
3054 /* Check arguments. */
3055 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
3056 ("puOpenFlags=%#p\n", puOpenFlags),
3057 rc = VERR_INVALID_PARAMETER);
3058
3059 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3060 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3061
3062 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
3063 } while (0);
3064
3065 LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
3066 return rc;
3067}
3068
3069/**
3070 * Set open flags of image in HDD container.
3071 * This operation may cause file locking changes and/or files being reopened.
3072 * Note that in case of unrecoverable error all images in HDD container will be closed.
3073 *
3074 * @returns VBox status code.
3075 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3076 * @param pDisk Pointer to HDD container.
3077 * @param nImage Image number, counts from 0. 0 is always base image of container.
3078 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
3079 */
3080VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
3081 unsigned uOpenFlags)
3082{
3083 int rc;
3084
3085 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
3086 do
3087 {
3088 /* sanity check */
3089 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3090 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3091
3092 /* Check arguments. */
3093 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
3094 ("uOpenFlags=%#x\n", uOpenFlags),
3095 rc = VERR_INVALID_PARAMETER);
3096
3097 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3098 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3099
3100 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData,
3101 uOpenFlags);
3102 } while (0);
3103
3104 LogFlowFunc(("returns %Rrc\n", rc));
3105 return rc;
3106}
3107
3108/**
3109 * Get base filename of image in HDD container. Some image formats use
3110 * other filenames as well, so don't use this for anything but informational
3111 * purposes.
3112 *
3113 * @returns VBox status code.
3114 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3115 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
3116 * @param pDisk Pointer to HDD container.
3117 * @param nImage Image number, counts from 0. 0 is always base image of container.
3118 * @param pszFilename Where to store the image file name.
3119 * @param cbFilename Size of buffer pszFilename points to.
3120 */
3121VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
3122 char *pszFilename, unsigned cbFilename)
3123{
3124 int rc;
3125
3126 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
3127 pDisk, nImage, pszFilename, cbFilename));
3128 do
3129 {
3130 /* sanity check */
3131 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3132 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3133
3134 /* Check arguments. */
3135 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
3136 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
3137 rc = VERR_INVALID_PARAMETER);
3138 AssertMsgBreakStmt(cbFilename,
3139 ("cbFilename=%u\n", cbFilename),
3140 rc = VERR_INVALID_PARAMETER);
3141
3142 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3143 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3144
3145 size_t cb = strlen(pImage->pszFilename);
3146 if (cb <= cbFilename)
3147 {
3148 strcpy(pszFilename, pImage->pszFilename);
3149 rc = VINF_SUCCESS;
3150 }
3151 else
3152 {
3153 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
3154 pszFilename[cbFilename - 1] = '\0';
3155 rc = VERR_BUFFER_OVERFLOW;
3156 }
3157 } while (0);
3158
3159 LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
3160 return rc;
3161}
3162
3163/**
3164 * Get the comment line of image in HDD container.
3165 *
3166 * @returns VBox status code.
3167 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3168 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
3169 * @param pDisk Pointer to HDD container.
3170 * @param nImage Image number, counts from 0. 0 is always base image of container.
3171 * @param pszComment Where to store the comment string of image. NULL is ok.
3172 * @param cbComment The size of pszComment buffer. 0 is ok.
3173 */
3174VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
3175 char *pszComment, unsigned cbComment)
3176{
3177 int rc;
3178
3179 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
3180 pDisk, nImage, pszComment, cbComment));
3181 do
3182 {
3183 /* sanity check */
3184 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3185 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3186
3187 /* Check arguments. */
3188 AssertMsgBreakStmt(VALID_PTR(pszComment),
3189 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
3190 rc = VERR_INVALID_PARAMETER);
3191 AssertMsgBreakStmt(cbComment,
3192 ("cbComment=%u\n", cbComment),
3193 rc = VERR_INVALID_PARAMETER);
3194
3195 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3196 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3197
3198 rc = pImage->Backend->pfnGetComment(pImage->pvBackendData, pszComment,
3199 cbComment);
3200 } while (0);
3201
3202 LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
3203 return rc;
3204}
3205
3206/**
3207 * Changes the comment line of image in HDD container.
3208 *
3209 * @returns VBox status code.
3210 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3211 * @param pDisk Pointer to HDD container.
3212 * @param nImage Image number, counts from 0. 0 is always base image of container.
3213 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
3214 */
3215VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
3216 const char *pszComment)
3217{
3218 int rc;
3219
3220 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
3221 pDisk, nImage, pszComment, pszComment));
3222 do
3223 {
3224 /* sanity check */
3225 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3226 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3227
3228 /* Check arguments. */
3229 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
3230 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
3231 rc = VERR_INVALID_PARAMETER);
3232
3233 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3234 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3235
3236 rc = pImage->Backend->pfnSetComment(pImage->pvBackendData, pszComment);
3237 } while (0);
3238
3239 LogFlowFunc(("returns %Rrc\n", rc));
3240 return rc;
3241}
3242
3243
3244/**
3245 * Get UUID of image in HDD container.
3246 *
3247 * @returns VBox status code.
3248 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3249 * @param pDisk Pointer to HDD container.
3250 * @param nImage Image number, counts from 0. 0 is always base image of container.
3251 * @param pUuid Where to store the image creation UUID.
3252 */
3253VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
3254{
3255 int rc;
3256
3257 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3258 do
3259 {
3260 /* sanity check */
3261 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3262 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3263
3264 /* Check arguments. */
3265 AssertMsgBreakStmt(VALID_PTR(pUuid),
3266 ("pUuid=%#p\n", pUuid),
3267 rc = VERR_INVALID_PARAMETER);
3268
3269 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3270 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3271
3272 rc = pImage->Backend->pfnGetUuid(pImage->pvBackendData, pUuid);
3273 } while (0);
3274
3275 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
3276 return rc;
3277}
3278
3279/**
3280 * Set the image's UUID. Should not be used by normal applications.
3281 *
3282 * @returns VBox status code.
3283 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3284 * @param pDisk Pointer to HDD container.
3285 * @param nImage Image number, counts from 0. 0 is always base image of container.
3286 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
3287 */
3288VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
3289{
3290 int rc;
3291
3292 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
3293 pDisk, nImage, pUuid, pUuid));
3294 do
3295 {
3296 /* sanity check */
3297 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3298 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3299
3300 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3301 ("pUuid=%#p\n", pUuid),
3302 rc = VERR_INVALID_PARAMETER);
3303
3304 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3305 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3306
3307 RTUUID Uuid;
3308 if (!pUuid)
3309 {
3310 RTUuidCreate(&Uuid);
3311 pUuid = &Uuid;
3312 }
3313 rc = pImage->Backend->pfnSetUuid(pImage->pvBackendData, pUuid);
3314 } while (0);
3315
3316 LogFlowFunc(("returns %Rrc\n", rc));
3317 return rc;
3318}
3319
3320/**
3321 * Get last modification UUID of image in HDD container.
3322 *
3323 * @returns VBox status code.
3324 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3325 * @param pDisk Pointer to HDD container.
3326 * @param nImage Image number, counts from 0. 0 is always base image of container.
3327 * @param pUuid Where to store the image modification UUID.
3328 */
3329VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
3330{
3331 int rc = VINF_SUCCESS;
3332
3333 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3334 do
3335 {
3336 /* sanity check */
3337 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3338 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3339
3340 /* Check arguments. */
3341 AssertMsgBreakStmt(VALID_PTR(pUuid),
3342 ("pUuid=%#p\n", pUuid),
3343 rc = VERR_INVALID_PARAMETER);
3344
3345 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3346 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3347
3348 rc = pImage->Backend->pfnGetModificationUuid(pImage->pvBackendData,
3349 pUuid);
3350 } while (0);
3351
3352 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
3353 return rc;
3354}
3355
3356/**
3357 * Set the image's last modification UUID. Should not be used by normal applications.
3358 *
3359 * @returns VBox status code.
3360 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3361 * @param pDisk Pointer to HDD container.
3362 * @param nImage Image number, counts from 0. 0 is always base image of container.
3363 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
3364 */
3365VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
3366{
3367 int rc;
3368
3369 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
3370 pDisk, nImage, pUuid, pUuid));
3371 do
3372 {
3373 /* sanity check */
3374 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3375 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3376
3377 /* Check arguments. */
3378 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3379 ("pUuid=%#p\n", pUuid),
3380 rc = VERR_INVALID_PARAMETER);
3381
3382 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3383 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3384
3385 RTUUID Uuid;
3386 if (!pUuid)
3387 {
3388 RTUuidCreate(&Uuid);
3389 pUuid = &Uuid;
3390 }
3391 rc = pImage->Backend->pfnSetModificationUuid(pImage->pvBackendData,
3392 pUuid);
3393 } while (0);
3394
3395 LogFlowFunc(("returns %Rrc\n", rc));
3396 return rc;
3397}
3398
3399/**
3400 * Get parent UUID of image in HDD container.
3401 *
3402 * @returns VBox status code.
3403 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3404 * @param pDisk Pointer to HDD container.
3405 * @param nImage Image number, counts from 0. 0 is always base image of container.
3406 * @param pUuid Where to store the parent image UUID.
3407 */
3408VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
3409 PRTUUID pUuid)
3410{
3411 int rc = VINF_SUCCESS;
3412
3413 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
3414 do
3415 {
3416 /* sanity check */
3417 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3418 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3419
3420 /* Check arguments. */
3421 AssertMsgBreakStmt(VALID_PTR(pUuid),
3422 ("pUuid=%#p\n", pUuid),
3423 rc = VERR_INVALID_PARAMETER);
3424
3425 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3426 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3427
3428 rc = pImage->Backend->pfnGetParentUuid(pImage->pvBackendData, pUuid);
3429 } while (0);
3430
3431 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
3432 return rc;
3433}
3434
3435/**
3436 * Set the image's parent UUID. Should not be used by normal applications.
3437 *
3438 * @returns VBox status code.
3439 * @param pDisk Pointer to HDD container.
3440 * @param nImage Image number, counts from 0. 0 is always base image of container.
3441 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
3442 */
3443VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
3444 PCRTUUID pUuid)
3445{
3446 int rc;
3447
3448 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
3449 pDisk, nImage, pUuid, pUuid));
3450 do
3451 {
3452 /* sanity check */
3453 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3454 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3455
3456 /* Check arguments. */
3457 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
3458 ("pUuid=%#p\n", pUuid),
3459 rc = VERR_INVALID_PARAMETER);
3460
3461 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3462 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3463
3464 RTUUID Uuid;
3465 if (!pUuid)
3466 {
3467 RTUuidCreate(&Uuid);
3468 pUuid = &Uuid;
3469 }
3470 rc = pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, pUuid);
3471 } while (0);
3472
3473 LogFlowFunc(("returns %Rrc\n", rc));
3474 return rc;
3475}
3476
3477
3478/**
3479 * Debug helper - dumps all opened images in HDD container into the log file.
3480 *
3481 * @param pDisk Pointer to HDD container.
3482 */
3483VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
3484{
3485 do
3486 {
3487 /* sanity check */
3488 AssertPtrBreak(pDisk);
3489 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3490
3491 RTLogPrintf("--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
3492 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
3493 {
3494 RTLogPrintf("Dumping VD image \"%s\" (Backend=%s)\n",
3495 pImage->pszFilename, pImage->Backend->pszBackendName);
3496 pImage->Backend->pfnDump(pImage->pvBackendData);
3497 }
3498 } while (0);
3499}
3500
3501/**
3502 * Query if asynchronous operations are supported for this disk.
3503 *
3504 * @returns VBox status code.
3505 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3506 * @param pDisk Pointer to the HDD container.
3507 * @param nImage Image number, counts from 0. 0 is always base image of container.
3508 * @param pfAIOSupported Where to store if async IO is supported.
3509 */
3510VBOXDDU_DECL(int) VDImageIsAsyncIOSupported(PVBOXHDD pDisk, unsigned nImage, bool *pfAIOSupported)
3511{
3512 int rc = VINF_SUCCESS;
3513
3514 LogFlowFunc(("pDisk=%#p nImage=%u pfAIOSupported=%#p\n", pDisk, nImage, pfAIOSupported));
3515 do
3516 {
3517 /* sanity check */
3518 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3519 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3520
3521 /* Check arguments. */
3522 AssertMsgBreakStmt(VALID_PTR(pfAIOSupported),
3523 ("pfAIOSupported=%#p\n", pfAIOSupported),
3524 rc = VERR_INVALID_PARAMETER);
3525
3526 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3527 AssertPtrBreakStmt(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
3528
3529 if (pImage->Backend->uBackendCaps & VD_CAP_ASYNC)
3530 *pfAIOSupported = pImage->Backend->pfnIsAsyncIOSupported(pImage->pvBackendData);
3531 else
3532 *pfAIOSupported = false;
3533 } while (0);
3534
3535 LogFlowFunc(("returns %Rrc, fAIOSupported=%u\n", rc, *pfAIOSupported));
3536 return rc;
3537}
3538
3539/**
3540 * Start a asynchronous read request.
3541 *
3542 * @returns VBox status code.
3543 * @param pDisk Pointer to the HDD container.
3544 * @param uOffset The offset of the virtual disk to read from.
3545 * @param cbRead How many bytes to read.
3546 * @param paSeg Pointer to an array of segments.
3547 * @param cSeg Number of segments in the array.
3548 * @param pvUser User data which is passed on completion
3549 */
3550VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
3551 PPDMDATASEG paSeg, unsigned cSeg,
3552 void *pvUser)
3553{
3554 int rc = VERR_VDI_BLOCK_FREE;
3555
3556 LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbRead=%zu\n",
3557 pDisk, uOffset, paSeg, cSeg, cbRead));
3558 do
3559 {
3560 /* sanity check */
3561 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3562 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3563
3564 /* Check arguments. */
3565 AssertMsgBreakStmt(cbRead,
3566 ("cbRead=%zu\n", cbRead),
3567 rc = VERR_INVALID_PARAMETER);
3568 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
3569 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
3570 uOffset, cbRead, pDisk->cbSize),
3571 rc = VERR_INVALID_PARAMETER);
3572 AssertMsgBreakStmt(VALID_PTR(paSeg),
3573 ("paSeg=%#p\n", paSeg),
3574 rc = VERR_INVALID_PARAMETER);
3575 AssertMsgBreakStmt(cSeg,
3576 ("cSeg=%zu\n", cSeg),
3577 rc = VERR_INVALID_PARAMETER);
3578
3579
3580 PVDIMAGE pImage = pDisk->pLast;
3581 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
3582
3583 /* @todo: This does not work for images which do not have all meta data in memory. */
3584 for (PVDIMAGE pCurrImage = pImage;
3585 pCurrImage != NULL && rc == VERR_VDI_BLOCK_FREE;
3586 pCurrImage = pCurrImage->pPrev)
3587 {
3588 rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pvBackendData,
3589 uOffset, cbRead, paSeg, cSeg,
3590 pvUser);
3591 }
3592
3593 /* No image in the chain contains the data for the block. */
3594 if (rc == VERR_VDI_BLOCK_FREE)
3595 {
3596 for (unsigned i = 0; i < cSeg && (cbRead > 0); i++)
3597 {
3598 memset(paSeg[i].pvSeg, '\0', paSeg[i].cbSeg);
3599 cbRead -= paSeg[i].cbSeg;
3600 }
3601 /* Request finished without the need to enqueue a async I/O request. Tell caller. */
3602 rc = VINF_VDI_ASYNC_IO_FINISHED;
3603 }
3604
3605 } while (0);
3606
3607 LogFlowFunc(("returns %Rrc\n", rc));
3608 return rc;
3609}
3610
3611
3612/**
3613 * Start a asynchronous write request.
3614 *
3615 * @returns VBox status code.
3616 * @param pDisk Pointer to the HDD container.
3617 * @param uOffset The offset of the virtual disk to write to.
3618 * @param cbWrtie How many bytes to write.
3619 * @param paSeg Pointer to an array of segments.
3620 * @param cSeg Number of segments in the array.
3621 * @param pvUser User data which is passed on completion.
3622 */
3623VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
3624 PPDMDATASEG paSeg, unsigned cSeg,
3625 void *pvUser)
3626{
3627 int rc;
3628
3629 LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbWrite=%zu\n",
3630 pDisk, uOffset, paSeg, cSeg, cbWrite));
3631 do
3632 {
3633 /* sanity check */
3634 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3635 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3636
3637 /* Check arguments. */
3638 AssertMsgBreakStmt(cbWrite,
3639 ("cbWrite=%zu\n", cbWrite),
3640 rc = VERR_INVALID_PARAMETER);
3641 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
3642 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
3643 uOffset, cbWrite, pDisk->cbSize),
3644 rc = VERR_INVALID_PARAMETER);
3645 AssertMsgBreakStmt(VALID_PTR(paSeg),
3646 ("paSeg=%#p\n", paSeg),
3647 rc = VERR_INVALID_PARAMETER);
3648 AssertMsgBreakStmt(cSeg,
3649 ("cSeg=%zu\n", cSeg),
3650 rc = VERR_INVALID_PARAMETER);
3651
3652
3653 PVDIMAGE pImage = pDisk->pLast;
3654 AssertPtrBreakStmt(pImage, rc = VERR_VDI_NOT_OPENED);
3655
3656 vdSetModifiedFlag(pDisk);
3657 rc = pImage->Backend->pfnAsyncWrite(pImage->pvBackendData,
3658 uOffset, cbWrite,
3659 paSeg, cSeg, pvUser);
3660 } while (0);
3661
3662 LogFlowFunc(("returns %Rrc\n", rc));
3663 return rc;
3664
3665}
3666
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