VirtualBox

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

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

Storage/VBoxHDD-new: Add information about configuration settings supported by a backend.
Storage/iSCSI: Move initiator name default to backend.
Main: Move iSCSI initiator name default to backend.

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