VirtualBox

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

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

Storage: Eradicated the last bits using the old VDI only backend, keeping only the testcases for now (no longer built).

Completely removed old iSCSI driver.

Added intnet option to addiscsidisk and adjusted documentation.

Made backend name comparisons case-insensitive.

Detect VMDK files not according to VMDK 1.0 and reject with clear error message.

Changed format probing logic to not fall through to the "unsupported" case if it's a known format, i.e. has valid header.

VBoxManage converthd generic format converter made official.

Added format flag to VBoxManage createhd, allows creating VMDK files.

VBoxManage convertdd reimplemented based on new framework, supporting any image format.

VBoxManage internalcommands sethduuid reimplemented based on new framework, supporting any image format.

Cleaned up error codes.

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