VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VBoxHDD.cpp@ 27359

最後變更 在這個檔案從27359是 27359,由 vboxsync 提交於 15 年 前

VD: Update with the correct parent uuid in the forward merge direction

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 162.7 KB
 
1/* $Id: VBoxHDD.cpp 27359 2010-03-15 13:07:52Z vboxsync $ */
2/** @file
3 * VBoxHDD - VBox HDD Container implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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.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 <VBox/VBoxHDD-Plugin.h>
43
44
45#define VBOXHDDDISK_SIGNATURE 0x6f0e2a7d
46
47/** Buffer size used for merging images. */
48#define VD_MERGE_BUFFER_SIZE (16 * _1M)
49
50/**
51 * VD async I/O interface storage descriptor.
52 */
53typedef struct VDIASYNCIOSTORAGE
54{
55 /** File handle. */
56 RTFILE File;
57 /** Completion callback. */
58 PFNVDCOMPLETED pfnCompleted;
59 /** Thread for async access. */
60 RTTHREAD ThreadAsync;
61} VDIASYNCIOSTORAGE, *PVDIASYNCIOSTORAGE;
62
63/**
64 * VBox HDD Container image descriptor.
65 */
66typedef struct VDIMAGE
67{
68 /** Link to parent image descriptor, if any. */
69 struct VDIMAGE *pPrev;
70 /** Link to child image descriptor, if any. */
71 struct VDIMAGE *pNext;
72 /** Container base filename. (UTF-8) */
73 char *pszFilename;
74 /** Data managed by the backend which keeps the actual info. */
75 void *pvBackendData;
76 /** Cached sanitized image flags. */
77 unsigned uImageFlags;
78 /** Image open flags (only those handled generically in this code and which
79 * the backends will never ever see). */
80 unsigned uOpenFlags;
81
82 /** Function pointers for the various backend methods. */
83 PCVBOXHDDBACKEND Backend;
84
85 /** Pointer to list of VD interfaces, per-image. */
86 PVDINTERFACE pVDIfsImage;
87} VDIMAGE, *PVDIMAGE;
88
89/**
90 * uModified bit flags.
91 */
92#define VD_IMAGE_MODIFIED_FLAG RT_BIT(0)
93#define VD_IMAGE_MODIFIED_FIRST RT_BIT(1)
94#define VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE RT_BIT(2)
95
96
97/**
98 * VBox HDD Container main structure, private part.
99 */
100struct VBOXHDD
101{
102 /** Structure signature (VBOXHDDDISK_SIGNATURE). */
103 uint32_t u32Signature;
104
105 /** Number of opened images. */
106 unsigned cImages;
107
108 /** Base image. */
109 PVDIMAGE pBase;
110
111 /** Last opened image in the chain.
112 * The same as pBase if only one image is used. */
113 PVDIMAGE pLast;
114
115 /** Flags representing the modification state. */
116 unsigned uModified;
117
118 /** Cached size of this disk. */
119 uint64_t cbSize;
120 /** Cached PCHS geometry for this disk. */
121 PDMMEDIAGEOMETRY PCHSGeometry;
122 /** Cached LCHS geometry for this disk. */
123 PDMMEDIAGEOMETRY LCHSGeometry;
124
125 /** Pointer to list of VD interfaces, per-disk. */
126 PVDINTERFACE pVDIfsDisk;
127 /** Pointer to the common interface structure for error reporting. */
128 PVDINTERFACE pInterfaceError;
129 /** Pointer to the error interface callbacks we use if available. */
130 PVDINTERFACEERROR pInterfaceErrorCallbacks;
131
132 /** Pointer to the optional thread synchronization interface. */
133 PVDINTERFACE pInterfaceThreadSync;
134 /** Pointer to the optional thread synchronization callbacks. */
135 PVDINTERFACETHREADSYNC pInterfaceThreadSyncCallbacks;
136
137 /** Fallback async interface. */
138 VDINTERFACE VDIAsyncIO;
139 /** Fallback async I/O interface callback table. */
140 VDINTERFACEASYNCIO VDIAsyncIOCallbacks;
141};
142
143
144/**
145 * VBox parent read descriptor, used internally for compaction.
146 */
147typedef struct VDPARENTSTATEDESC
148{
149 /** Pointer to disk descriptor. */
150 PVBOXHDD pDisk;
151 /** Pointer to image descriptor. */
152 PVDIMAGE pImage;
153} VDPARENTSTATEDESC, *PVDPARENTSTATEDESC;
154
155
156extern VBOXHDDBACKEND g_RawBackend;
157extern VBOXHDDBACKEND g_VmdkBackend;
158extern VBOXHDDBACKEND g_VDIBackend;
159extern VBOXHDDBACKEND g_VhdBackend;
160extern VBOXHDDBACKEND g_ParallelsBackend;
161#ifdef VBOX_WITH_ISCSI
162extern VBOXHDDBACKEND g_ISCSIBackend;
163#endif
164
165static unsigned g_cBackends = 0;
166static PVBOXHDDBACKEND *g_apBackends = NULL;
167static PVBOXHDDBACKEND aStaticBackends[] =
168{
169 &g_RawBackend,
170 &g_VmdkBackend,
171 &g_VDIBackend,
172 &g_VhdBackend,
173 &g_ParallelsBackend
174#ifdef VBOX_WITH_ISCSI
175 ,&g_ISCSIBackend
176#endif
177};
178
179/**
180 * internal: add several backends.
181 */
182static int vdAddBackends(PVBOXHDDBACKEND *ppBackends, unsigned cBackends)
183{
184 PVBOXHDDBACKEND *pTmp = (PVBOXHDDBACKEND*)RTMemRealloc(g_apBackends,
185 (g_cBackends + cBackends) * sizeof(PVBOXHDDBACKEND));
186 if (RT_UNLIKELY(!pTmp))
187 return VERR_NO_MEMORY;
188 g_apBackends = pTmp;
189 memcpy(&g_apBackends[g_cBackends], ppBackends, cBackends * sizeof(PVBOXHDDBACKEND));
190 g_cBackends += cBackends;
191 return VINF_SUCCESS;
192}
193
194/**
195 * internal: add single backend.
196 */
197DECLINLINE(int) vdAddBackend(PVBOXHDDBACKEND pBackend)
198{
199 return vdAddBackends(&pBackend, 1);
200}
201
202/**
203 * internal: issue error message.
204 */
205static int vdError(PVBOXHDD pDisk, int rc, RT_SRC_POS_DECL,
206 const char *pszFormat, ...)
207{
208 va_list va;
209 va_start(va, pszFormat);
210 if (pDisk->pInterfaceErrorCallbacks)
211 pDisk->pInterfaceErrorCallbacks->pfnError(pDisk->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
212 va_end(va);
213 return rc;
214}
215
216/**
217 * internal: thread synchronization, start read.
218 */
219DECLINLINE(int) vdThreadStartRead(PVBOXHDD pDisk)
220{
221 int rc = VINF_SUCCESS;
222 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
223 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnStartRead(pDisk->pInterfaceThreadSync->pvUser);
224 return rc;
225}
226
227/**
228 * internal: thread synchronization, finish read.
229 */
230DECLINLINE(int) vdThreadFinishRead(PVBOXHDD pDisk)
231{
232 int rc = VINF_SUCCESS;
233 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
234 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnFinishRead(pDisk->pInterfaceThreadSync->pvUser);
235 return rc;
236}
237
238/**
239 * internal: thread synchronization, start write.
240 */
241DECLINLINE(int) vdThreadStartWrite(PVBOXHDD pDisk)
242{
243 int rc = VINF_SUCCESS;
244 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
245 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnStartWrite(pDisk->pInterfaceThreadSync->pvUser);
246 return rc;
247}
248
249/**
250 * internal: thread synchronization, finish write.
251 */
252DECLINLINE(int) vdThreadFinishWrite(PVBOXHDD pDisk)
253{
254 int rc = VINF_SUCCESS;
255 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
256 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnFinishWrite(pDisk->pInterfaceThreadSync->pvUser);
257 return rc;
258}
259
260/**
261 * internal: find image format backend.
262 */
263static int vdFindBackend(const char *pszBackend, PCVBOXHDDBACKEND *ppBackend)
264{
265 int rc = VINF_SUCCESS;
266 PCVBOXHDDBACKEND pBackend = NULL;
267
268 if (!g_apBackends)
269 VDInit();
270
271 for (unsigned i = 0; i < g_cBackends; i++)
272 {
273 if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
274 {
275 pBackend = g_apBackends[i];
276 break;
277 }
278 }
279 *ppBackend = pBackend;
280 return rc;
281}
282
283/**
284 * internal: add image structure to the end of images list.
285 */
286static void vdAddImageToList(PVBOXHDD pDisk, PVDIMAGE pImage)
287{
288 pImage->pPrev = NULL;
289 pImage->pNext = NULL;
290
291 if (pDisk->pBase)
292 {
293 Assert(pDisk->cImages > 0);
294 pImage->pPrev = pDisk->pLast;
295 pDisk->pLast->pNext = pImage;
296 pDisk->pLast = pImage;
297 }
298 else
299 {
300 Assert(pDisk->cImages == 0);
301 pDisk->pBase = pImage;
302 pDisk->pLast = pImage;
303 }
304
305 pDisk->cImages++;
306}
307
308/**
309 * internal: remove image structure from the images list.
310 */
311static void vdRemoveImageFromList(PVBOXHDD pDisk, PVDIMAGE pImage)
312{
313 Assert(pDisk->cImages > 0);
314
315 if (pImage->pPrev)
316 pImage->pPrev->pNext = pImage->pNext;
317 else
318 pDisk->pBase = pImage->pNext;
319
320 if (pImage->pNext)
321 pImage->pNext->pPrev = pImage->pPrev;
322 else
323 pDisk->pLast = pImage->pPrev;
324
325 pImage->pPrev = NULL;
326 pImage->pNext = NULL;
327
328 pDisk->cImages--;
329}
330
331/**
332 * internal: find image by index into the images list.
333 */
334static PVDIMAGE vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
335{
336 PVDIMAGE pImage = pDisk->pBase;
337 if (nImage == VD_LAST_IMAGE)
338 return pDisk->pLast;
339 while (pImage && nImage)
340 {
341 pImage = pImage->pNext;
342 nImage--;
343 }
344 return pImage;
345}
346
347/**
348 * internal: read the specified amount of data in whatever blocks the backend
349 * will give us.
350 */
351static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
352 uint64_t uOffset, void *pvBuf, size_t cbRead)
353{
354 int rc;
355 size_t cbThisRead;
356
357 /* Loop until all read. */
358 do
359 {
360 /* Search for image with allocated block. Do not attempt to read more
361 * than the previous reads marked as valid. Otherwise this would return
362 * stale data when different block sizes are used for the images. */
363 cbThisRead = cbRead;
364
365 /*
366 * Try to read from the given image.
367 * If the block is not allocated read from override chain if present.
368 */
369 rc = pImage->Backend->pfnRead(pImage->pvBackendData,
370 uOffset, pvBuf, cbThisRead,
371 &cbThisRead);
372
373 if (rc == VERR_VD_BLOCK_FREE)
374 {
375 for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
376 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
377 pCurrImage = pCurrImage->pPrev)
378 {
379 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
380 uOffset, pvBuf, cbThisRead,
381 &cbThisRead);
382 }
383 }
384
385 /* No image in the chain contains the data for the block. */
386 if (rc == VERR_VD_BLOCK_FREE)
387 {
388 memset(pvBuf, '\0', cbThisRead);
389 rc = VINF_SUCCESS;
390 }
391
392 cbRead -= cbThisRead;
393 uOffset += cbThisRead;
394 pvBuf = (char *)pvBuf + cbThisRead;
395 } while (cbRead != 0 && RT_SUCCESS(rc));
396
397 return rc;
398}
399
400/**
401 * internal: parent image read wrapper for compacting.
402 */
403static int vdParentRead(void *pvUser, uint64_t uOffset, void *pvBuf,
404 size_t cbRead)
405{
406 PVDPARENTSTATEDESC pParentState = (PVDPARENTSTATEDESC)pvUser;
407 return vdReadHelper(pParentState->pDisk, pParentState->pImage, NULL, uOffset,
408 pvBuf, cbRead);
409}
410
411/**
412 * internal: mark the disk as not modified.
413 */
414static void vdResetModifiedFlag(PVBOXHDD pDisk)
415{
416 if (pDisk->uModified & VD_IMAGE_MODIFIED_FLAG)
417 {
418 /* generate new last-modified uuid */
419 if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
420 {
421 RTUUID Uuid;
422
423 RTUuidCreate(&Uuid);
424 pDisk->pLast->Backend->pfnSetModificationUuid(pDisk->pLast->pvBackendData,
425 &Uuid);
426 }
427
428 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FLAG;
429 }
430}
431
432/**
433 * internal: mark the disk as modified.
434 */
435static void vdSetModifiedFlag(PVBOXHDD pDisk)
436{
437 pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
438 if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
439 {
440 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
441
442 /* First modify, so create a UUID and ensure it's written to disk. */
443 vdResetModifiedFlag(pDisk);
444
445 if (!(pDisk->uModified | VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
446 pDisk->pLast->Backend->pfnFlush(pDisk->pLast->pvBackendData);
447 }
448}
449
450/**
451 * internal: write a complete block (only used for diff images), taking the
452 * remaining data from parent images. This implementation does not optimize
453 * anything (except that it tries to read only that portions from parent
454 * images that are really needed).
455 */
456static int vdWriteHelperStandard(PVBOXHDD pDisk, PVDIMAGE pImage,
457 PVDIMAGE pImageParentOverride,
458 uint64_t uOffset, size_t cbWrite,
459 size_t cbThisWrite, size_t cbPreRead,
460 size_t cbPostRead, const void *pvBuf,
461 void *pvTmp)
462{
463 int rc = VINF_SUCCESS;
464
465 /* Read the data that goes before the write to fill the block. */
466 if (cbPreRead)
467 {
468 rc = vdReadHelper(pDisk, pImage, pImageParentOverride,
469 uOffset - cbPreRead, pvTmp, cbPreRead);
470 if (RT_FAILURE(rc))
471 return rc;
472 }
473
474 /* Copy the data to the right place in the buffer. */
475 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
476
477 /* Read the data that goes after the write to fill the block. */
478 if (cbPostRead)
479 {
480 /* If we have data to be written, use that instead of reading
481 * data from the image. */
482 size_t cbWriteCopy;
483 if (cbWrite > cbThisWrite)
484 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
485 else
486 cbWriteCopy = 0;
487 /* Figure out how much we cannnot read from the image, because
488 * the last block to write might exceed the nominal size of the
489 * image for technical reasons. */
490 size_t cbFill;
491 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
492 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
493 else
494 cbFill = 0;
495 /* The rest must be read from the image. */
496 size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
497
498 /* Now assemble the remaining data. */
499 if (cbWriteCopy)
500 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
501 (char *)pvBuf + cbThisWrite, cbWriteCopy);
502 if (cbReadImage)
503 rc = vdReadHelper(pDisk, pImage, pImageParentOverride,
504 uOffset + cbThisWrite + cbWriteCopy,
505 (char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy,
506 cbReadImage);
507 if (RT_FAILURE(rc))
508 return rc;
509 /* Zero out the remainder of this block. Will never be visible, as this
510 * is beyond the limit of the image. */
511 if (cbFill)
512 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
513 '\0', cbFill);
514 }
515
516 /* Write the full block to the virtual disk. */
517 rc = pImage->Backend->pfnWrite(pImage->pvBackendData,
518 uOffset - cbPreRead, pvTmp,
519 cbPreRead + cbThisWrite + cbPostRead,
520 NULL, &cbPreRead, &cbPostRead, 0);
521 Assert(rc != VERR_VD_BLOCK_FREE);
522 Assert(cbPreRead == 0);
523 Assert(cbPostRead == 0);
524
525 return rc;
526}
527
528/**
529 * internal: write a complete block (only used for diff images), taking the
530 * remaining data from parent images. This implementation optimizes out writes
531 * that do not change the data relative to the state as of the parent images.
532 * All backends which support differential/growing images support this.
533 */
534static int vdWriteHelperOptimized(PVBOXHDD pDisk, PVDIMAGE pImage,
535 PVDIMAGE pImageParentOverride,
536 uint64_t uOffset, size_t cbWrite,
537 size_t cbThisWrite, size_t cbPreRead,
538 size_t cbPostRead, const void *pvBuf,
539 void *pvTmp)
540{
541 size_t cbFill = 0;
542 size_t cbWriteCopy = 0;
543 size_t cbReadImage = 0;
544 int rc;
545
546 if (cbPostRead)
547 {
548 /* Figure out how much we cannnot read from the image, because
549 * the last block to write might exceed the nominal size of the
550 * image for technical reasons. */
551 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
552 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
553
554 /* If we have data to be written, use that instead of reading
555 * data from the image. */
556 if (cbWrite > cbThisWrite)
557 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
558
559 /* The rest must be read from the image. */
560 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
561 }
562
563 /* Read the entire data of the block so that we can compare whether it will
564 * be modified by the write or not. */
565 rc = vdReadHelper(pDisk, pImage, pImageParentOverride, uOffset - cbPreRead, pvTmp,
566 cbPreRead + cbThisWrite + cbPostRead - cbFill);
567 if (RT_FAILURE(rc))
568 return rc;
569
570 /* Check if the write would modify anything in this block. */
571 if ( !memcmp((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite)
572 && (!cbWriteCopy || !memcmp((char *)pvTmp + cbPreRead + cbThisWrite,
573 (char *)pvBuf + cbThisWrite, cbWriteCopy)))
574 {
575 /* Block is completely unchanged, so no need to write anything. */
576 return VINF_SUCCESS;
577 }
578
579 /* Copy the data to the right place in the buffer. */
580 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
581
582 /* Handle the data that goes after the write to fill the block. */
583 if (cbPostRead)
584 {
585 /* Now assemble the remaining data. */
586 if (cbWriteCopy)
587 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
588 (char *)pvBuf + cbThisWrite, cbWriteCopy);
589 /* Zero out the remainder of this block. Will never be visible, as this
590 * is beyond the limit of the image. */
591 if (cbFill)
592 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
593 '\0', cbFill);
594 }
595
596 /* Write the full block to the virtual disk. */
597 rc = pImage->Backend->pfnWrite(pImage->pvBackendData,
598 uOffset - cbPreRead, pvTmp,
599 cbPreRead + cbThisWrite + cbPostRead,
600 NULL, &cbPreRead, &cbPostRead, 0);
601 Assert(rc != VERR_VD_BLOCK_FREE);
602 Assert(cbPreRead == 0);
603 Assert(cbPostRead == 0);
604
605 return rc;
606}
607
608/**
609 * internal: write buffer to the image, taking care of block boundaries and
610 * write optimizations.
611 */
612static int vdWriteHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
613 uint64_t uOffset, const void *pvBuf, size_t cbWrite)
614{
615 int rc;
616 unsigned fWrite;
617 size_t cbThisWrite;
618 size_t cbPreRead, cbPostRead;
619
620 /* Loop until all written. */
621 do
622 {
623 /* Try to write the possibly partial block to the last opened image.
624 * This works when the block is already allocated in this image or
625 * if it is a full-block write (and allocation isn't suppressed below).
626 * For image formats which don't support zero blocks, it's beneficial
627 * to avoid unnecessarily allocating unchanged blocks. This prevents
628 * unwanted expanding of images. VMDK is an example. */
629 cbThisWrite = cbWrite;
630 fWrite = (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
631 ? 0 : VD_WRITE_NO_ALLOC;
632 rc = pImage->Backend->pfnWrite(pImage->pvBackendData, uOffset, pvBuf,
633 cbThisWrite, &cbThisWrite, &cbPreRead,
634 &cbPostRead, fWrite);
635 if (rc == VERR_VD_BLOCK_FREE)
636 {
637 void *pvTmp = RTMemTmpAlloc(cbPreRead + cbThisWrite + cbPostRead);
638 AssertBreakStmt(VALID_PTR(pvTmp), rc = VERR_NO_MEMORY);
639
640 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME))
641 {
642 /* Optimized write, suppress writing to a so far unallocated
643 * block if the data is in fact not changed. */
644 rc = vdWriteHelperOptimized(pDisk, pImage, pImageParentOverride,
645 uOffset, cbWrite,
646 cbThisWrite, cbPreRead, cbPostRead,
647 pvBuf, pvTmp);
648 }
649 else
650 {
651 /* Normal write, not optimized in any way. The block will
652 * be written no matter what. This will usually (unless the
653 * backend has some further optimization enabled) cause the
654 * block to be allocated. */
655 rc = vdWriteHelperStandard(pDisk, pImage, pImageParentOverride,
656 uOffset, cbWrite,
657 cbThisWrite, cbPreRead, cbPostRead,
658 pvBuf, pvTmp);
659 }
660 RTMemTmpFree(pvTmp);
661 if (RT_FAILURE(rc))
662 break;
663 }
664
665 cbWrite -= cbThisWrite;
666 uOffset += cbThisWrite;
667 pvBuf = (char *)pvBuf + cbThisWrite;
668 } while (cbWrite != 0 && RT_SUCCESS(rc));
669
670 return rc;
671}
672
673
674/**
675 * internal: scans plugin directory and loads the backends have been found.
676 */
677static int vdLoadDynamicBackends()
678{
679 int rc = VINF_SUCCESS;
680 PRTDIR pPluginDir = NULL;
681
682 /* Enumerate plugin backends. */
683 char szPath[RTPATH_MAX];
684 rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
685 if (RT_FAILURE(rc))
686 return rc;
687
688 /* To get all entries with VBoxHDD as prefix. */
689 char *pszPluginFilter;
690 rc = RTStrAPrintf(&pszPluginFilter, "%s/%s*", szPath,
691 VBOX_HDDFORMAT_PLUGIN_PREFIX);
692 if (RT_FAILURE(rc))
693 {
694 rc = VERR_NO_MEMORY;
695 return rc;
696 }
697
698 PRTDIRENTRYEX pPluginDirEntry = NULL;
699 size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
700 /* The plugins are in the same directory as the other shared libs. */
701 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
702 if (RT_FAILURE(rc))
703 {
704 /* On Windows the above immediately signals that there are no
705 * files matching, while on other platforms enumerating the
706 * files below fails. Either way: no plugins. */
707 goto out;
708 }
709
710 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
711 if (!pPluginDirEntry)
712 {
713 rc = VERR_NO_MEMORY;
714 goto out;
715 }
716
717 while ((rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) != VERR_NO_MORE_FILES)
718 {
719 RTLDRMOD hPlugin = NIL_RTLDRMOD;
720 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad = NULL;
721 PVBOXHDDBACKEND pBackend = NULL;
722 char *pszPluginPath = NULL;
723
724 if (rc == VERR_BUFFER_OVERFLOW)
725 {
726 /* allocate new buffer. */
727 RTMemFree(pPluginDirEntry);
728 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
729 /* Retry. */
730 rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
731 if (RT_FAILURE(rc))
732 break;
733 }
734 else if (RT_FAILURE(rc))
735 break;
736
737 /* We got the new entry. */
738 if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
739 continue;
740
741 /* Prepend the path to the libraries. */
742 rc = RTStrAPrintf(&pszPluginPath, "%s/%s", szPath, pPluginDirEntry->szName);
743 if (RT_FAILURE(rc))
744 {
745 rc = VERR_NO_MEMORY;
746 break;
747 }
748
749 rc = SUPR3HardenedLdrLoad(pszPluginPath, &hPlugin);
750 if (RT_SUCCESS(rc))
751 {
752 rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad);
753 if (RT_FAILURE(rc) || !pfnHDDFormatLoad)
754 {
755 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnHDDFormat=%#p\n", VBOX_HDDFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnHDDFormatLoad));
756 if (RT_SUCCESS(rc))
757 rc = VERR_SYMBOL_NOT_FOUND;
758 }
759
760 if (RT_SUCCESS(rc))
761 {
762 /* Get the function table. */
763 rc = pfnHDDFormatLoad(&pBackend);
764 if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VBOXHDDBACKEND))
765 {
766 pBackend->hPlugin = hPlugin;
767 vdAddBackend(pBackend);
768 }
769 else
770 LogFunc(("ignored plugin '%s': pBackend->cbSize=%d rc=%Rrc\n", pszPluginPath, pBackend->cbSize, rc));
771 }
772 else
773 LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszPluginPath, rc));
774
775 if (RT_FAILURE(rc))
776 RTLdrClose(hPlugin);
777 }
778 RTStrFree(pszPluginPath);
779 }
780out:
781 if (rc == VERR_NO_MORE_FILES)
782 rc = VINF_SUCCESS;
783 RTStrFree(pszPluginFilter);
784 if (pPluginDirEntry)
785 RTMemFree(pPluginDirEntry);
786 if (pPluginDir)
787 RTDirClose(pPluginDir);
788 return rc;
789}
790
791/**
792 * VD async I/O interface open callback.
793 */
794static int vdAsyncIOOpen(void *pvUser, const char *pszLocation, unsigned uOpenFlags,
795 PFNVDCOMPLETED pfnCompleted, PVDINTERFACE pVDIfsDisk,
796 void **ppStorage)
797{
798 PVDIASYNCIOSTORAGE pStorage = (PVDIASYNCIOSTORAGE)RTMemAllocZ(sizeof(VDIASYNCIOSTORAGE));
799
800 if (!pStorage)
801 return VERR_NO_MEMORY;
802
803 pStorage->pfnCompleted = pfnCompleted;
804
805 uint32_t fOpen = 0;
806
807 if (uOpenFlags & VD_INTERFACEASYNCIO_OPEN_FLAGS_READONLY)
808 fOpen |= RTFILE_O_READ | RTFILE_O_DENY_NONE;
809 else
810 fOpen |= RTFILE_O_READWRITE | RTFILE_O_DENY_WRITE;
811
812 if (uOpenFlags & VD_INTERFACEASYNCIO_OPEN_FLAGS_CREATE)
813 fOpen |= RTFILE_O_CREATE;
814 else
815 fOpen |= RTFILE_O_OPEN;
816
817 /* Open the file. */
818 int rc = RTFileOpen(&pStorage->File, pszLocation, fOpen);
819 if (RT_SUCCESS(rc))
820 {
821 *ppStorage = pStorage;
822 return VINF_SUCCESS;
823 }
824
825 RTMemFree(pStorage);
826 return rc;
827}
828
829/**
830 * VD async I/O interface close callback.
831 */
832static int vdAsyncIOClose(void *pvUser, void *pvStorage)
833{
834 PVDIASYNCIOSTORAGE pStorage = (PVDIASYNCIOSTORAGE)pvStorage;
835
836 RTFileClose(pStorage->File);
837 RTMemFree(pStorage);
838 return VINF_SUCCESS;
839}
840
841/**
842 * VD async I/O interface callback for retrieving the file size.
843 */
844static int vdAsyncIOGetSize(void *pvUser, void *pvStorage, uint64_t *pcbSize)
845{
846 PVDIASYNCIOSTORAGE pStorage = (PVDIASYNCIOSTORAGE)pvStorage;
847
848 return RTFileGetSize(pStorage->File, pcbSize);
849}
850
851/**
852 * VD async I/O interface callback for setting the file size.
853 */
854static int vdAsyncIOSetSize(void *pvUser, void *pvStorage, uint64_t cbSize)
855{
856 PVDIASYNCIOSTORAGE pStorage = (PVDIASYNCIOSTORAGE)pvStorage;
857
858 return RTFileSetSize(pStorage->File, cbSize);
859}
860
861/**
862 * VD async I/O interface callback for a synchronous write to the file.
863 */
864static int vdAsyncIOWriteSync(void *pvUser, void *pvStorage, uint64_t uOffset,
865 size_t cbWrite, const void *pvBuf, size_t *pcbWritten)
866{
867 PVDIASYNCIOSTORAGE pStorage = (PVDIASYNCIOSTORAGE)pvStorage;
868
869 return RTFileWriteAt(pStorage->File, uOffset, pvBuf, cbWrite, pcbWritten);
870}
871
872/**
873 * VD async I/O interface callback for a synchronous read from the file.
874 */
875static int vdAsyncIOReadSync(void *pvUser, void *pvStorage, uint64_t uOffset,
876 size_t cbRead, void *pvBuf, size_t *pcbRead)
877{
878 PVDIASYNCIOSTORAGE pStorage = (PVDIASYNCIOSTORAGE)pvStorage;
879
880 return RTFileReadAt(pStorage->File, uOffset, pvBuf, cbRead, pcbRead);
881}
882
883/**
884 * VD async I/O interface callback for a synchronous flush of the file data.
885 */
886static int vdAsyncIOFlushSync(void *pvUser, void *pvStorage)
887{
888 PVDIASYNCIOSTORAGE pStorage = (PVDIASYNCIOSTORAGE)pvStorage;
889
890 return RTFileFlush(pStorage->File);
891}
892
893/**
894 * VD async I/O interface callback for a asynchronous read from the file.
895 */
896static int vdAsyncIOReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
897 PCPDMDATASEG paSegments, size_t cSegments,
898 size_t cbRead, void *pvCompletion,
899 void **ppTask)
900{
901 return VERR_NOT_IMPLEMENTED;
902}
903
904/**
905 * VD async I/O interface callback for a asynchronous write to the file.
906 */
907static int vdAsyncIOWriteAsync(void *pvUser, void *pStorage, uint64_t uOffset,
908 PCPDMDATASEG paSegments, size_t cSegments,
909 size_t cbWrite, void *pvCompletion,
910 void **ppTask)
911{
912 return VERR_NOT_IMPLEMENTED;
913}
914
915/**
916 * VD async I/O interface callback for a asynchronous flush of the file data.
917 */
918static int vdAsyncIOFlushAsync(void *pvUser, void *pStorage,
919 void *pvCompletion, void **ppTask)
920{
921 return VERR_NOT_IMPLEMENTED;
922}
923
924/**
925 * internal: send output to the log (unconditionally).
926 */
927int vdLogMessage(void *pvUser, const char *pszFormat, ...)
928{
929 NOREF(pvUser);
930 va_list args;
931 va_start(args, pszFormat);
932 RTLogPrintf(pszFormat, args);
933 va_end(args);
934 return VINF_SUCCESS;
935}
936
937
938/**
939 * Initializes HDD backends.
940 *
941 * @returns VBox status code.
942 */
943VBOXDDU_DECL(int) VDInit(void)
944{
945 int rc = vdAddBackends(aStaticBackends, RT_ELEMENTS(aStaticBackends));
946 if (RT_SUCCESS(rc))
947 rc = vdLoadDynamicBackends();
948 LogRel(("VDInit finished\n"));
949 return rc;
950}
951
952/**
953 * Destroys loaded HDD backends.
954 *
955 * @returns VBox status code.
956 */
957VBOXDDU_DECL(int) VDShutdown(void)
958{
959 PVBOXHDDBACKEND *pBackends = g_apBackends;
960 unsigned cBackends = g_cBackends;
961
962 if (!pBackends)
963 return VERR_INTERNAL_ERROR;
964
965 g_cBackends = 0;
966 g_apBackends = NULL;
967
968 for (unsigned i = 0; i < cBackends; i++)
969 if (pBackends[i]->hPlugin != NIL_RTLDRMOD)
970 RTLdrClose(pBackends[i]->hPlugin);
971
972 RTMemFree(pBackends);
973 return VINF_SUCCESS;
974}
975
976
977/**
978 * Lists all HDD backends and their capabilities in a caller-provided buffer.
979 *
980 * @returns VBox status code.
981 * VERR_BUFFER_OVERFLOW if not enough space is passed.
982 * @param cEntriesAlloc Number of list entries available.
983 * @param pEntries Pointer to array for the entries.
984 * @param pcEntriesUsed Number of entries returned.
985 */
986VBOXDDU_DECL(int) VDBackendInfo(unsigned cEntriesAlloc, PVDBACKENDINFO pEntries,
987 unsigned *pcEntriesUsed)
988{
989 int rc = VINF_SUCCESS;
990 PRTDIR pPluginDir = NULL;
991 unsigned cEntries = 0;
992
993 LogFlowFunc(("cEntriesAlloc=%u pEntries=%#p pcEntriesUsed=%#p\n", cEntriesAlloc, pEntries, pcEntriesUsed));
994 /* Check arguments. */
995 AssertMsgReturn(cEntriesAlloc,
996 ("cEntriesAlloc=%u\n", cEntriesAlloc),
997 VERR_INVALID_PARAMETER);
998 AssertMsgReturn(VALID_PTR(pEntries),
999 ("pEntries=%#p\n", pEntries),
1000 VERR_INVALID_PARAMETER);
1001 AssertMsgReturn(VALID_PTR(pcEntriesUsed),
1002 ("pcEntriesUsed=%#p\n", pcEntriesUsed),
1003 VERR_INVALID_PARAMETER);
1004 if (!g_apBackends)
1005 VDInit();
1006
1007 if (cEntriesAlloc < g_cBackends)
1008 {
1009 *pcEntriesUsed = g_cBackends;
1010 return VERR_BUFFER_OVERFLOW;
1011 }
1012
1013 for (unsigned i = 0; i < g_cBackends; i++)
1014 {
1015 pEntries[i].pszBackend = g_apBackends[i]->pszBackendName;
1016 pEntries[i].uBackendCaps = g_apBackends[i]->uBackendCaps;
1017 pEntries[i].papszFileExtensions = g_apBackends[i]->papszFileExtensions;
1018 pEntries[i].paConfigInfo = g_apBackends[i]->paConfigInfo;
1019 pEntries[i].pfnComposeLocation = g_apBackends[i]->pfnComposeLocation;
1020 pEntries[i].pfnComposeName = g_apBackends[i]->pfnComposeName;
1021 }
1022
1023 LogFlowFunc(("returns %Rrc *pcEntriesUsed=%u\n", rc, cEntries));
1024 *pcEntriesUsed = g_cBackends;
1025 return rc;
1026}
1027
1028/**
1029 * Lists the capablities of a backend indentified by its name.
1030 *
1031 * @returns VBox status code.
1032 * @param pszBackend The backend name.
1033 * @param pEntries Pointer to an entry.
1034 */
1035VBOXDDU_DECL(int) VDBackendInfoOne(const char *pszBackend, PVDBACKENDINFO pEntry)
1036{
1037 LogFlowFunc(("pszBackend=%#p pEntry=%#p\n", pszBackend, pEntry));
1038 /* Check arguments. */
1039 AssertMsgReturn(VALID_PTR(pszBackend),
1040 ("pszBackend=%#p\n", pszBackend),
1041 VERR_INVALID_PARAMETER);
1042 AssertMsgReturn(VALID_PTR(pEntry),
1043 ("pEntry=%#p\n", pEntry),
1044 VERR_INVALID_PARAMETER);
1045 if (!g_apBackends)
1046 VDInit();
1047
1048 /* Go through loaded backends. */
1049 for (unsigned i = 0; i < g_cBackends; i++)
1050 {
1051 if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
1052 {
1053 pEntry->pszBackend = g_apBackends[i]->pszBackendName;
1054 pEntry->uBackendCaps = g_apBackends[i]->uBackendCaps;
1055 pEntry->papszFileExtensions = g_apBackends[i]->papszFileExtensions;
1056 pEntry->paConfigInfo = g_apBackends[i]->paConfigInfo;
1057 return VINF_SUCCESS;
1058 }
1059 }
1060
1061 return VERR_NOT_FOUND;
1062}
1063
1064/**
1065 * Allocates and initializes an empty HDD container.
1066 * No image files are opened.
1067 *
1068 * @returns VBox status code.
1069 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
1070 * @param ppDisk Where to store the reference to HDD container.
1071 */
1072VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, PVBOXHDD *ppDisk)
1073{
1074 int rc = VINF_SUCCESS;
1075 PVBOXHDD pDisk = NULL;
1076
1077 LogFlowFunc(("pVDIfsDisk=%#p\n", pVDIfsDisk));
1078 do
1079 {
1080 /* Check arguments. */
1081 AssertMsgBreakStmt(VALID_PTR(ppDisk),
1082 ("ppDisk=%#p\n", ppDisk),
1083 rc = VERR_INVALID_PARAMETER);
1084
1085 pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
1086 if (pDisk)
1087 {
1088 pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
1089 pDisk->cImages = 0;
1090 pDisk->pBase = NULL;
1091 pDisk->pLast = NULL;
1092 pDisk->cbSize = 0;
1093 pDisk->PCHSGeometry.cCylinders = 0;
1094 pDisk->PCHSGeometry.cHeads = 0;
1095 pDisk->PCHSGeometry.cSectors = 0;
1096 pDisk->LCHSGeometry.cCylinders = 0;
1097 pDisk->LCHSGeometry.cHeads = 0;
1098 pDisk->LCHSGeometry.cSectors = 0;
1099 pDisk->pVDIfsDisk = pVDIfsDisk;
1100 pDisk->pInterfaceError = NULL;
1101 pDisk->pInterfaceErrorCallbacks = NULL;
1102 pDisk->pInterfaceThreadSync = NULL;
1103 pDisk->pInterfaceThreadSyncCallbacks = NULL;
1104
1105 pDisk->pInterfaceError = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_ERROR);
1106 if (pDisk->pInterfaceError)
1107 pDisk->pInterfaceErrorCallbacks = VDGetInterfaceError(pDisk->pInterfaceError);
1108
1109 pDisk->pInterfaceThreadSync = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_THREADSYNC);
1110 if (pDisk->pInterfaceThreadSync)
1111 pDisk->pInterfaceThreadSyncCallbacks = VDGetInterfaceThreadSync(pDisk->pInterfaceThreadSync);
1112
1113 /* Use the fallback async I/O interface if the caller doesn't provide one. */
1114 PVDINTERFACE pVDIfAsyncIO = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_ASYNCIO);
1115 if (!pVDIfAsyncIO)
1116 {
1117 pDisk->VDIAsyncIOCallbacks.cbSize = sizeof(VDINTERFACEASYNCIO);
1118 pDisk->VDIAsyncIOCallbacks.enmInterface = VDINTERFACETYPE_ASYNCIO;
1119 pDisk->VDIAsyncIOCallbacks.pfnOpen = vdAsyncIOOpen;
1120 pDisk->VDIAsyncIOCallbacks.pfnClose = vdAsyncIOClose;
1121 pDisk->VDIAsyncIOCallbacks.pfnGetSize = vdAsyncIOGetSize;
1122 pDisk->VDIAsyncIOCallbacks.pfnSetSize = vdAsyncIOSetSize;
1123 pDisk->VDIAsyncIOCallbacks.pfnReadSync = vdAsyncIOReadSync;
1124 pDisk->VDIAsyncIOCallbacks.pfnWriteSync = vdAsyncIOWriteSync;
1125 pDisk->VDIAsyncIOCallbacks.pfnFlushSync = vdAsyncIOFlushSync;
1126 pDisk->VDIAsyncIOCallbacks.pfnReadAsync = vdAsyncIOReadAsync;
1127 pDisk->VDIAsyncIOCallbacks.pfnWriteAsync = vdAsyncIOWriteAsync;
1128 pDisk->VDIAsyncIOCallbacks.pfnFlushAsync = vdAsyncIOFlushAsync;
1129 rc = VDInterfaceAdd(&pDisk->VDIAsyncIO, "VD_AsyncIO", VDINTERFACETYPE_ASYNCIO,
1130 &pDisk->VDIAsyncIOCallbacks, pDisk, &pDisk->pVDIfsDisk);
1131 AssertRC(rc);
1132 }
1133
1134 *ppDisk = pDisk;
1135 }
1136 else
1137 {
1138 rc = VERR_NO_MEMORY;
1139 break;
1140 }
1141 } while (0);
1142
1143 LogFlowFunc(("returns %Rrc (pDisk=%#p)\n", rc, pDisk));
1144 return rc;
1145}
1146
1147/**
1148 * Destroys HDD container.
1149 * If container has opened image files they will be closed.
1150 *
1151 * @param pDisk Pointer to HDD container.
1152 */
1153VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk)
1154{
1155 LogFlowFunc(("pDisk=%#p\n", pDisk));
1156 do
1157 {
1158 /* sanity check */
1159 AssertPtrBreak(pDisk);
1160 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1161 VDCloseAll(pDisk);
1162 RTMemFree(pDisk);
1163 } while (0);
1164 LogFlowFunc(("returns\n"));
1165}
1166
1167/**
1168 * Try to get the backend name which can use this image.
1169 *
1170 * @returns VBox status code.
1171 * VINF_SUCCESS if a plugin was found.
1172 * ppszFormat contains the string which can be used as backend name.
1173 * VERR_NOT_SUPPORTED if no backend was found.
1174 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
1175 * @param pszFilename Name of the image file for which the backend is queried.
1176 * @param ppszFormat Receives pointer of the UTF-8 string which contains the format name.
1177 * The returned pointer must be freed using RTStrFree().
1178 */
1179VBOXDDU_DECL(int) VDGetFormat(PVDINTERFACE pVDIfsDisk, const char *pszFilename, char **ppszFormat)
1180{
1181 int rc = VERR_NOT_SUPPORTED;
1182 PVDINTERFACE pVDIfAsyncIO;
1183 VDINTERFACEASYNCIO VDIAsyncIOCallbacks;
1184 VDINTERFACE VDIAsyncIO;
1185
1186 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
1187 /* Check arguments. */
1188 AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
1189 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1190 VERR_INVALID_PARAMETER);
1191 AssertMsgReturn(VALID_PTR(ppszFormat),
1192 ("ppszFormat=%#p\n", ppszFormat),
1193 VERR_INVALID_PARAMETER);
1194
1195 if (!g_apBackends)
1196 VDInit();
1197
1198 /* Use the fallback async I/O interface if the caller doesn't provide one. */
1199 pVDIfAsyncIO = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_ASYNCIO);
1200 if (!pVDIfAsyncIO)
1201 {
1202 VDIAsyncIOCallbacks.cbSize = sizeof(VDINTERFACEASYNCIO);
1203 VDIAsyncIOCallbacks.enmInterface = VDINTERFACETYPE_ASYNCIO;
1204 VDIAsyncIOCallbacks.pfnOpen = vdAsyncIOOpen;
1205 VDIAsyncIOCallbacks.pfnClose = vdAsyncIOClose;
1206 VDIAsyncIOCallbacks.pfnGetSize = vdAsyncIOGetSize;
1207 VDIAsyncIOCallbacks.pfnSetSize = vdAsyncIOSetSize;
1208 VDIAsyncIOCallbacks.pfnReadSync = vdAsyncIOReadSync;
1209 VDIAsyncIOCallbacks.pfnWriteSync = vdAsyncIOWriteSync;
1210 VDIAsyncIOCallbacks.pfnFlushSync = vdAsyncIOFlushSync;
1211 VDIAsyncIOCallbacks.pfnReadAsync = vdAsyncIOReadAsync;
1212 VDIAsyncIOCallbacks.pfnWriteAsync = vdAsyncIOWriteAsync;
1213 VDIAsyncIOCallbacks.pfnFlushAsync = vdAsyncIOFlushAsync;
1214 rc = VDInterfaceAdd(&VDIAsyncIO, "VD_AsyncIO", VDINTERFACETYPE_ASYNCIO,
1215 &VDIAsyncIOCallbacks, NULL, &pVDIfsDisk);
1216 AssertRC(rc);
1217 }
1218
1219 /* Find the backend supporting this file format. */
1220 for (unsigned i = 0; i < g_cBackends; i++)
1221 {
1222 if (g_apBackends[i]->pfnCheckIfValid)
1223 {
1224 rc = g_apBackends[i]->pfnCheckIfValid(pszFilename, pVDIfsDisk);
1225 if ( RT_SUCCESS(rc)
1226 /* The correct backend has been found, but there is a small
1227 * incompatibility so that the file cannot be used. Stop here
1228 * and signal success - the actual open will of course fail,
1229 * but that will create a really sensible error message. */
1230 || ( rc != VERR_VD_GEN_INVALID_HEADER
1231 && rc != VERR_VD_VDI_INVALID_HEADER
1232 && rc != VERR_VD_VMDK_INVALID_HEADER
1233 && rc != VERR_VD_ISCSI_INVALID_HEADER
1234 && rc != VERR_VD_VHD_INVALID_HEADER
1235 && rc != VERR_VD_RAW_INVALID_HEADER))
1236 {
1237 /* Copy the name into the new string. */
1238 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
1239 if (!pszFormat)
1240 {
1241 rc = VERR_NO_MEMORY;
1242 break;
1243 }
1244 *ppszFormat = pszFormat;
1245 rc = VINF_SUCCESS;
1246 break;
1247 }
1248 rc = VERR_NOT_SUPPORTED;
1249 }
1250 }
1251
1252 LogFlowFunc(("returns %Rrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
1253 return rc;
1254}
1255
1256/**
1257 * Opens an image file.
1258 *
1259 * The first opened image file in HDD container must have a base image type,
1260 * others (next opened images) must be a differencing or undo images.
1261 * Linkage is checked for differencing image to be in consistence with the previously opened image.
1262 * When another differencing image is opened and the last image was opened in read/write access
1263 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
1264 * other processes to use images in read-only mode too.
1265 *
1266 * Note that the image is opened in read-only mode if a read/write open is not possible.
1267 * Use VDIsReadOnly to check open mode.
1268 *
1269 * @returns VBox status code.
1270 * @param pDisk Pointer to HDD container.
1271 * @param pszBackend Name of the image file backend to use.
1272 * @param pszFilename Name of the image file to open.
1273 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1274 * @param pVDIfsImage Pointer to the per-image VD interface list.
1275 */
1276VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
1277 const char *pszFilename, unsigned uOpenFlags,
1278 PVDINTERFACE pVDIfsImage)
1279{
1280 int rc = VINF_SUCCESS;
1281 int rc2;
1282 bool fLockWrite = false;
1283 PVDIMAGE pImage = NULL;
1284
1285 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsImage=%#p\n",
1286 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsImage));
1287
1288 do
1289 {
1290 /* sanity check */
1291 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1292 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1293
1294 /* Check arguments. */
1295 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
1296 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1297 rc = VERR_INVALID_PARAMETER);
1298 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1299 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1300 rc = VERR_INVALID_PARAMETER);
1301 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1302 ("uOpenFlags=%#x\n", uOpenFlags),
1303 rc = VERR_INVALID_PARAMETER);
1304
1305 /* Set up image descriptor. */
1306 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1307 if (!pImage)
1308 {
1309 rc = VERR_NO_MEMORY;
1310 break;
1311 }
1312 pImage->pszFilename = RTStrDup(pszFilename);
1313 if (!pImage->pszFilename)
1314 {
1315 rc = VERR_NO_MEMORY;
1316 break;
1317 }
1318 pImage->pVDIfsImage = pVDIfsImage;
1319
1320 rc = vdFindBackend(pszBackend, &pImage->Backend);
1321 if (RT_FAILURE(rc))
1322 break;
1323 if (!pImage->Backend)
1324 {
1325 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1326 N_("VD: unknown backend name '%s'"), pszBackend);
1327 break;
1328 }
1329
1330 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1331 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
1332 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1333 pDisk->pVDIfsDisk,
1334 pImage->pVDIfsImage,
1335 &pImage->pvBackendData);
1336 /* If the open in read-write mode failed, retry in read-only mode. */
1337 if (RT_FAILURE(rc))
1338 {
1339 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
1340 && ( rc == VERR_ACCESS_DENIED
1341 || rc == VERR_PERMISSION_DENIED
1342 || rc == VERR_WRITE_PROTECT
1343 || rc == VERR_SHARING_VIOLATION
1344 || rc == VERR_FILE_LOCK_FAILED))
1345 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
1346 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
1347 | VD_OPEN_FLAGS_READONLY,
1348 pDisk->pVDIfsDisk,
1349 pImage->pVDIfsImage,
1350 &pImage->pvBackendData);
1351 if (RT_FAILURE(rc))
1352 {
1353 rc = vdError(pDisk, rc, RT_SRC_POS,
1354 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
1355 break;
1356 }
1357 }
1358
1359 /* Lock disk for writing, as we modify pDisk information below. */
1360 rc2 = vdThreadStartWrite(pDisk);
1361 AssertRC(rc2);
1362 fLockWrite = true;
1363
1364 /* Check image type. As the image itself has only partial knowledge
1365 * whether it's a base image or not, this info is derived here. The
1366 * base image can be fixed or normal, all others must be normal or
1367 * diff images. Some image formats don't distinguish between normal
1368 * and diff images, so this must be corrected here. */
1369 unsigned uImageFlags;
1370 uImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pvBackendData);
1371 if (RT_FAILURE(rc))
1372 uImageFlags = VD_IMAGE_FLAGS_NONE;
1373 if ( RT_SUCCESS(rc)
1374 && !(uOpenFlags & VD_OPEN_FLAGS_INFO))
1375 {
1376 if ( pDisk->cImages == 0
1377 && (uImageFlags & VD_IMAGE_FLAGS_DIFF))
1378 {
1379 rc = VERR_VD_INVALID_TYPE;
1380 break;
1381 }
1382 else if (pDisk->cImages != 0)
1383 {
1384 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
1385 {
1386 rc = VERR_VD_INVALID_TYPE;
1387 break;
1388 }
1389 else
1390 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
1391 }
1392 }
1393 pImage->uImageFlags = uImageFlags;
1394
1395 /* Force sane optimization settings. It's not worth avoiding writes
1396 * to fixed size images. The overhead would have almost no payback. */
1397 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
1398 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
1399
1400 /** @todo optionally check UUIDs */
1401
1402 /* Cache disk information. */
1403 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
1404
1405 /* Cache PCHS geometry. */
1406 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1407 &pDisk->PCHSGeometry);
1408 if (RT_FAILURE(rc2))
1409 {
1410 pDisk->PCHSGeometry.cCylinders = 0;
1411 pDisk->PCHSGeometry.cHeads = 0;
1412 pDisk->PCHSGeometry.cSectors = 0;
1413 }
1414 else
1415 {
1416 /* Make sure the PCHS geometry is properly clipped. */
1417 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1418 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1419 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1420 }
1421
1422 /* Cache LCHS geometry. */
1423 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1424 &pDisk->LCHSGeometry);
1425 if (RT_FAILURE(rc2))
1426 {
1427 pDisk->LCHSGeometry.cCylinders = 0;
1428 pDisk->LCHSGeometry.cHeads = 0;
1429 pDisk->LCHSGeometry.cSectors = 0;
1430 }
1431 else
1432 {
1433 /* Make sure the LCHS geometry is properly clipped. */
1434 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1435 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1436 }
1437
1438 if (pDisk->cImages != 0)
1439 {
1440 /* Switch previous image to read-only mode. */
1441 unsigned uOpenFlagsPrevImg;
1442 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1443 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
1444 {
1445 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
1446 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
1447 }
1448 }
1449
1450 if (RT_SUCCESS(rc))
1451 {
1452 /* Image successfully opened, make it the last image. */
1453 vdAddImageToList(pDisk, pImage);
1454 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1455 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1456 }
1457 else
1458 {
1459 /* Error detected, but image opened. Close image. */
1460 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, false);
1461 AssertRC(rc2);
1462 pImage->pvBackendData = NULL;
1463 }
1464 } while (0);
1465
1466 if (RT_UNLIKELY(fLockWrite))
1467 {
1468 rc2 = vdThreadFinishWrite(pDisk);
1469 AssertRC(rc2);
1470 }
1471
1472 if (RT_FAILURE(rc))
1473 {
1474 if (pImage)
1475 {
1476 if (pImage->pszFilename)
1477 RTStrFree(pImage->pszFilename);
1478 RTMemFree(pImage);
1479 }
1480 }
1481
1482 LogFlowFunc(("returns %Rrc\n", rc));
1483 return rc;
1484}
1485
1486/**
1487 * Creates and opens a new base image file.
1488 *
1489 * @returns VBox status code.
1490 * @param pDisk Pointer to HDD container.
1491 * @param pszBackend Name of the image file backend to use.
1492 * @param pszFilename Name of the image file to create.
1493 * @param cbSize Image size in bytes.
1494 * @param uImageFlags Flags specifying special image features.
1495 * @param pszComment Pointer to image comment. NULL is ok.
1496 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
1497 * @param pLCHSGeometry Pointer to logical disk geometry <= (x,255,63). Not NULL.
1498 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
1499 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1500 * @param pVDIfsImage Pointer to the per-image VD interface list.
1501 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
1502 */
1503VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
1504 const char *pszFilename, uint64_t cbSize,
1505 unsigned uImageFlags, const char *pszComment,
1506 PCPDMMEDIAGEOMETRY pPCHSGeometry,
1507 PCPDMMEDIAGEOMETRY pLCHSGeometry,
1508 PCRTUUID pUuid, unsigned uOpenFlags,
1509 PVDINTERFACE pVDIfsImage,
1510 PVDINTERFACE pVDIfsOperation)
1511{
1512 int rc = VINF_SUCCESS;
1513 int rc2;
1514 bool fLockWrite = false, fLockRead = false;
1515 PVDIMAGE pImage = NULL;
1516 RTUUID uuid;
1517
1518 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" PCHS=%u/%u/%u LCHS=%u/%u/%u Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
1519 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment,
1520 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1521 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
1522 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
1523 uOpenFlags, pVDIfsImage, pVDIfsOperation));
1524
1525 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
1526 VDINTERFACETYPE_PROGRESS);
1527 PVDINTERFACEPROGRESS pCbProgress = NULL;
1528 if (pIfProgress)
1529 pCbProgress = VDGetInterfaceProgress(pIfProgress);
1530
1531 do
1532 {
1533 /* sanity check */
1534 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1535 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1536
1537 /* Check arguments. */
1538 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
1539 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1540 rc = VERR_INVALID_PARAMETER);
1541 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1542 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1543 rc = VERR_INVALID_PARAMETER);
1544 AssertMsgBreakStmt(cbSize,
1545 ("cbSize=%llu\n", cbSize),
1546 rc = VERR_INVALID_PARAMETER);
1547 AssertMsgBreakStmt( ((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0)
1548 || ((uImageFlags & (VD_IMAGE_FLAGS_FIXED | VD_IMAGE_FLAGS_DIFF)) != VD_IMAGE_FLAGS_FIXED),
1549 ("uImageFlags=%#x\n", uImageFlags),
1550 rc = VERR_INVALID_PARAMETER);
1551 /* The PCHS geometry fields may be 0 to leave it for later. */
1552 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
1553 && pPCHSGeometry->cHeads <= 16
1554 && pPCHSGeometry->cSectors <= 63,
1555 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
1556 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1557 pPCHSGeometry->cSectors),
1558 rc = VERR_INVALID_PARAMETER);
1559 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
1560 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
1561 && pLCHSGeometry->cHeads <= 255
1562 && pLCHSGeometry->cSectors <= 63,
1563 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
1564 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
1565 pLCHSGeometry->cSectors),
1566 rc = VERR_INVALID_PARAMETER);
1567 /* The UUID may be NULL. */
1568 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
1569 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
1570 rc = VERR_INVALID_PARAMETER);
1571 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1572 ("uOpenFlags=%#x\n", uOpenFlags),
1573 rc = VERR_INVALID_PARAMETER);
1574
1575 /* Check state. Needs a temporary read lock. Holding the write lock
1576 * all the time would be blocking other activities for too long. */
1577 rc2 = vdThreadStartRead(pDisk);
1578 AssertRC(rc2);
1579 fLockRead = true;
1580 AssertMsgBreakStmt(pDisk->cImages == 0,
1581 ("Create base image cannot be done with other images open\n"),
1582 rc = VERR_VD_INVALID_STATE);
1583 rc2 = vdThreadFinishRead(pDisk);
1584 AssertRC(rc2);
1585 fLockRead = false;
1586
1587 /* Set up image descriptor. */
1588 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1589 if (!pImage)
1590 {
1591 rc = VERR_NO_MEMORY;
1592 break;
1593 }
1594 pImage->pszFilename = RTStrDup(pszFilename);
1595 if (!pImage->pszFilename)
1596 {
1597 rc = VERR_NO_MEMORY;
1598 break;
1599 }
1600 pImage->pVDIfsImage = pVDIfsImage;
1601
1602 rc = vdFindBackend(pszBackend, &pImage->Backend);
1603 if (RT_FAILURE(rc))
1604 break;
1605 if (!pImage->Backend)
1606 {
1607 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1608 N_("VD: unknown backend name '%s'"), pszBackend);
1609 break;
1610 }
1611
1612 /* Create UUID if the caller didn't specify one. */
1613 if (!pUuid)
1614 {
1615 rc = RTUuidCreate(&uuid);
1616 if (RT_FAILURE(rc))
1617 {
1618 rc = vdError(pDisk, rc, RT_SRC_POS,
1619 N_("VD: cannot generate UUID for image '%s'"),
1620 pszFilename);
1621 break;
1622 }
1623 pUuid = &uuid;
1624 }
1625
1626 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1627 uImageFlags &= ~VD_IMAGE_FLAGS_DIFF;
1628 rc = pImage->Backend->pfnCreate(pImage->pszFilename, cbSize,
1629 uImageFlags, pszComment, pPCHSGeometry,
1630 pLCHSGeometry, pUuid,
1631 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1632 0, 99,
1633 pDisk->pVDIfsDisk,
1634 pImage->pVDIfsImage,
1635 pVDIfsOperation,
1636 &pImage->pvBackendData);
1637
1638 if (RT_SUCCESS(rc))
1639 {
1640 pImage->uImageFlags = uImageFlags;
1641
1642 /* Force sane optimization settings. It's not worth avoiding writes
1643 * to fixed size images. The overhead would have almost no payback. */
1644 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
1645 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
1646
1647 /* Lock disk for writing, as we modify pDisk information below. */
1648 rc2 = vdThreadStartWrite(pDisk);
1649 AssertRC(rc2);
1650 fLockWrite = true;
1651
1652 /** @todo optionally check UUIDs */
1653
1654 /* Re-check state, as the lock wasn't held and another image
1655 * creation call could have been done by another thread. */
1656 AssertMsgStmt(pDisk->cImages == 0,
1657 ("Create base image cannot be done with other images open\n"),
1658 rc = VERR_VD_INVALID_STATE);
1659 }
1660
1661 if (RT_SUCCESS(rc))
1662 {
1663 /* Cache disk information. */
1664 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
1665
1666 /* Cache PCHS geometry. */
1667 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1668 &pDisk->PCHSGeometry);
1669 if (RT_FAILURE(rc2))
1670 {
1671 pDisk->PCHSGeometry.cCylinders = 0;
1672 pDisk->PCHSGeometry.cHeads = 0;
1673 pDisk->PCHSGeometry.cSectors = 0;
1674 }
1675 else
1676 {
1677 /* Make sure the CHS geometry is properly clipped. */
1678 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1679 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1680 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1681 }
1682
1683 /* Cache LCHS geometry. */
1684 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1685 &pDisk->LCHSGeometry);
1686 if (RT_FAILURE(rc2))
1687 {
1688 pDisk->LCHSGeometry.cCylinders = 0;
1689 pDisk->LCHSGeometry.cHeads = 0;
1690 pDisk->LCHSGeometry.cSectors = 0;
1691 }
1692 else
1693 {
1694 /* Make sure the CHS geometry is properly clipped. */
1695 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1696 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1697 }
1698
1699 /* Image successfully opened, make it the last image. */
1700 vdAddImageToList(pDisk, pImage);
1701 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1702 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1703 }
1704 else
1705 {
1706 /* Error detected, but image opened. Close and delete image. */
1707 int rc2;
1708 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
1709 AssertRC(rc2);
1710 pImage->pvBackendData = NULL;
1711 }
1712 } while (0);
1713
1714 if (RT_UNLIKELY(fLockWrite))
1715 {
1716 rc2 = vdThreadFinishWrite(pDisk);
1717 AssertRC(rc2);
1718 }
1719 else if (RT_UNLIKELY(fLockRead))
1720 {
1721 rc2 = vdThreadFinishRead(pDisk);
1722 AssertRC(rc2);
1723 }
1724
1725 if (RT_FAILURE(rc))
1726 {
1727 if (pImage)
1728 {
1729 if (pImage->pszFilename)
1730 RTStrFree(pImage->pszFilename);
1731 RTMemFree(pImage);
1732 }
1733 }
1734
1735 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
1736 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
1737
1738 LogFlowFunc(("returns %Rrc\n", rc));
1739 return rc;
1740}
1741
1742/**
1743 * Creates and opens a new differencing image file in HDD container.
1744 * See comments for VDOpen function about differencing images.
1745 *
1746 * @returns VBox status code.
1747 * @param pDisk Pointer to HDD container.
1748 * @param pszBackend Name of the image file backend to use.
1749 * @param pszFilename Name of the differencing image file to create.
1750 * @param uImageFlags Flags specifying special image features.
1751 * @param pszComment Pointer to image comment. NULL is ok.
1752 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
1753 * @param pParentUuid New parent UUID of the image. If NULL, the UUID is queried automatically.
1754 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1755 * @param pVDIfsImage Pointer to the per-image VD interface list.
1756 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
1757 */
1758VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
1759 const char *pszFilename, unsigned uImageFlags,
1760 const char *pszComment, PCRTUUID pUuid,
1761 PCRTUUID pParentUuid, unsigned uOpenFlags,
1762 PVDINTERFACE pVDIfsImage,
1763 PVDINTERFACE pVDIfsOperation)
1764{
1765 int rc = VINF_SUCCESS;
1766 int rc2;
1767 bool fLockWrite = false, fLockRead = false;
1768 PVDIMAGE pImage = NULL;
1769 RTUUID uuid;
1770
1771 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid ParentUuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
1772 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, pParentUuid, uOpenFlags,
1773 pVDIfsImage, pVDIfsOperation));
1774
1775 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
1776 VDINTERFACETYPE_PROGRESS);
1777 PVDINTERFACEPROGRESS pCbProgress = NULL;
1778 if (pIfProgress)
1779 pCbProgress = VDGetInterfaceProgress(pIfProgress);
1780
1781 do
1782 {
1783 /* sanity check */
1784 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
1785 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1786
1787 /* Check arguments. */
1788 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
1789 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
1790 rc = VERR_INVALID_PARAMETER);
1791 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
1792 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1793 rc = VERR_INVALID_PARAMETER);
1794 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
1795 ("uImageFlags=%#x\n", uImageFlags),
1796 rc = VERR_INVALID_PARAMETER);
1797 /* The UUID may be NULL. */
1798 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
1799 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
1800 rc = VERR_INVALID_PARAMETER);
1801 /* The parent UUID may be NULL. */
1802 AssertMsgBreakStmt(pParentUuid == NULL || VALID_PTR(pParentUuid),
1803 ("pParentUuid=%#p ParentUUID=%RTuuid\n", pParentUuid, pParentUuid),
1804 rc = VERR_INVALID_PARAMETER);
1805 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1806 ("uOpenFlags=%#x\n", uOpenFlags),
1807 rc = VERR_INVALID_PARAMETER);
1808
1809 /* Check state. Needs a temporary read lock. Holding the write lock
1810 * all the time would be blocking other activities for too long. */
1811 rc2 = vdThreadStartRead(pDisk);
1812 AssertRC(rc2);
1813 fLockRead = true;
1814 AssertMsgBreakStmt(pDisk->cImages != 0,
1815 ("Create diff image cannot be done without other images open\n"),
1816 rc = VERR_VD_INVALID_STATE);
1817 rc2 = vdThreadFinishRead(pDisk);
1818 AssertRC(rc2);
1819 fLockRead = false;
1820
1821 /* Set up image descriptor. */
1822 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1823 if (!pImage)
1824 {
1825 rc = VERR_NO_MEMORY;
1826 break;
1827 }
1828 pImage->pszFilename = RTStrDup(pszFilename);
1829 if (!pImage->pszFilename)
1830 {
1831 rc = VERR_NO_MEMORY;
1832 break;
1833 }
1834
1835 rc = vdFindBackend(pszBackend, &pImage->Backend);
1836 if (RT_FAILURE(rc))
1837 break;
1838 if (!pImage->Backend)
1839 {
1840 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
1841 N_("VD: unknown backend name '%s'"), pszBackend);
1842 break;
1843 }
1844
1845 /* Create UUID if the caller didn't specify one. */
1846 if (!pUuid)
1847 {
1848 rc = RTUuidCreate(&uuid);
1849 if (RT_FAILURE(rc))
1850 {
1851 rc = vdError(pDisk, rc, RT_SRC_POS,
1852 N_("VD: cannot generate UUID for image '%s'"),
1853 pszFilename);
1854 break;
1855 }
1856 pUuid = &uuid;
1857 }
1858
1859 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
1860 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
1861 rc = pImage->Backend->pfnCreate(pImage->pszFilename, pDisk->cbSize,
1862 uImageFlags | VD_IMAGE_FLAGS_DIFF,
1863 pszComment, &pDisk->PCHSGeometry,
1864 &pDisk->LCHSGeometry, pUuid,
1865 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
1866 0, 99,
1867 pDisk->pVDIfsDisk,
1868 pImage->pVDIfsImage,
1869 pVDIfsOperation,
1870 &pImage->pvBackendData);
1871
1872 if (RT_SUCCESS(rc))
1873 {
1874 pImage->uImageFlags = uImageFlags;
1875
1876 /* Lock disk for writing, as we modify pDisk information below. */
1877 rc2 = vdThreadStartWrite(pDisk);
1878 AssertRC(rc2);
1879 fLockWrite = true;
1880
1881 /* Switch previous image to read-only mode. */
1882 unsigned uOpenFlagsPrevImg;
1883 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1884 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
1885 {
1886 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
1887 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
1888 }
1889
1890 /** @todo optionally check UUIDs */
1891
1892 /* Re-check state, as the lock wasn't held and another image
1893 * creation call could have been done by another thread. */
1894 AssertMsgStmt(pDisk->cImages != 0,
1895 ("Create diff image cannot be done without other images open\n"),
1896 rc = VERR_VD_INVALID_STATE);
1897 }
1898
1899 if (RT_SUCCESS(rc))
1900 {
1901 RTUUID Uuid;
1902 RTTIMESPEC ts;
1903
1904 if (pParentUuid && !RTUuidIsNull(pParentUuid))
1905 {
1906 Uuid = *pParentUuid;
1907 pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, &Uuid);
1908 }
1909 else
1910 {
1911 rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pvBackendData,
1912 &Uuid);
1913 if (RT_SUCCESS(rc2))
1914 pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, &Uuid);
1915 }
1916 rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pvBackendData,
1917 &Uuid);
1918 if (RT_SUCCESS(rc2))
1919 pImage->Backend->pfnSetParentModificationUuid(pImage->pvBackendData,
1920 &Uuid);
1921 rc2 = pDisk->pLast->Backend->pfnGetTimeStamp(pDisk->pLast->pvBackendData,
1922 &ts);
1923 if (RT_SUCCESS(rc2))
1924 pImage->Backend->pfnSetParentTimeStamp(pImage->pvBackendData, &ts);
1925
1926 rc2 = pImage->Backend->pfnSetParentFilename(pImage->pvBackendData, pDisk->pLast->pszFilename);
1927 }
1928
1929 if (RT_SUCCESS(rc))
1930 {
1931 /* Image successfully opened, make it the last image. */
1932 vdAddImageToList(pDisk, pImage);
1933 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1934 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
1935 }
1936 else
1937 {
1938 /* Error detected, but image opened. Close and delete image. */
1939 rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, true);
1940 AssertRC(rc2);
1941 pImage->pvBackendData = NULL;
1942 }
1943 } while (0);
1944
1945 if (RT_UNLIKELY(fLockWrite))
1946 {
1947 rc2 = vdThreadFinishWrite(pDisk);
1948 AssertRC(rc2);
1949 }
1950 else if (RT_UNLIKELY(fLockRead))
1951 {
1952 rc2 = vdThreadFinishRead(pDisk);
1953 AssertRC(rc2);
1954 }
1955
1956 if (RT_FAILURE(rc))
1957 {
1958 if (pImage)
1959 {
1960 if (pImage->pszFilename)
1961 RTStrFree(pImage->pszFilename);
1962 RTMemFree(pImage);
1963 }
1964 }
1965
1966 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
1967 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
1968
1969 LogFlowFunc(("returns %Rrc\n", rc));
1970 return rc;
1971}
1972
1973
1974/**
1975 * Merges two images (not necessarily with direct parent/child relationship).
1976 * As a side effect the source image and potentially the other images which
1977 * are also merged to the destination are deleted from both the disk and the
1978 * images in the HDD container.
1979 *
1980 * @returns VBox status code.
1981 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
1982 * @param pDisk Pointer to HDD container.
1983 * @param nImageFrom Name of the image file to merge from.
1984 * @param nImageTo Name of the image file to merge to.
1985 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
1986 */
1987VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
1988 unsigned nImageTo, PVDINTERFACE pVDIfsOperation)
1989{
1990 int rc = VINF_SUCCESS;
1991 int rc2;
1992 bool fLockWrite = false, fLockRead = false;
1993 void *pvBuf = NULL;
1994
1995 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pVDIfsOperation=%#p\n",
1996 pDisk, nImageFrom, nImageTo, pVDIfsOperation));
1997
1998 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
1999 VDINTERFACETYPE_PROGRESS);
2000 PVDINTERFACEPROGRESS pCbProgress = NULL;
2001 if (pIfProgress)
2002 pCbProgress = VDGetInterfaceProgress(pIfProgress);
2003
2004 do
2005 {
2006 /* sanity check */
2007 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2008 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2009
2010 rc2 = vdThreadStartRead(pDisk);
2011 AssertRC(rc2);
2012 fLockRead = true;
2013 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
2014 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
2015 if (!pImageFrom || !pImageTo)
2016 {
2017 rc = VERR_VD_IMAGE_NOT_FOUND;
2018 break;
2019 }
2020 AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
2021
2022 /* Make sure destination image is writable. */
2023 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
2024 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
2025 {
2026 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
2027 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pvBackendData,
2028 uOpenFlags);
2029 if (RT_FAILURE(rc))
2030 break;
2031 }
2032
2033 /* Get size of destination image. */
2034 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pvBackendData);
2035 rc2 = vdThreadFinishRead(pDisk);
2036 AssertRC(rc2);
2037 fLockRead = false;
2038
2039 /* Allocate tmp buffer. */
2040 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
2041 if (!pvBuf)
2042 {
2043 rc = VERR_NO_MEMORY;
2044 break;
2045 }
2046
2047 /* Merging is done directly on the images itself. This potentially
2048 * causes trouble if the disk is full in the middle of operation. */
2049 if (nImageFrom < nImageTo)
2050 {
2051 /* Merge parent state into child. This means writing all not
2052 * allocated blocks in the destination image which are allocated in
2053 * the images to be merged. */
2054 uint64_t uOffset = 0;
2055 uint64_t cbRemaining = cbSize;
2056 do
2057 {
2058 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
2059
2060 /* Need to hold the write lock during a read-write operation. */
2061 rc2 = vdThreadStartWrite(pDisk);
2062 AssertRC(rc2);
2063 fLockWrite = true;
2064
2065 rc = pImageTo->Backend->pfnRead(pImageTo->pvBackendData,
2066 uOffset, pvBuf, cbThisRead,
2067 &cbThisRead);
2068 if (rc == VERR_VD_BLOCK_FREE)
2069 {
2070 /* Search for image with allocated block. Do not attempt to
2071 * read more than the previous reads marked as valid.
2072 * Otherwise this would return stale data when different
2073 * block sizes are used for the images. */
2074 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
2075 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VD_BLOCK_FREE;
2076 pCurrImage = pCurrImage->pPrev)
2077 {
2078 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
2079 uOffset, pvBuf,
2080 cbThisRead,
2081 &cbThisRead);
2082 }
2083
2084 if (rc != VERR_VD_BLOCK_FREE)
2085 {
2086 if (RT_FAILURE(rc))
2087 break;
2088 rc = vdWriteHelper(pDisk, pImageTo, pImageFrom->pPrev,
2089 uOffset, pvBuf,
2090 cbThisRead);
2091 if (RT_FAILURE(rc))
2092 break;
2093 }
2094 else
2095 rc = VINF_SUCCESS;
2096 }
2097 else if (RT_FAILURE(rc))
2098 break;
2099
2100 rc2 = vdThreadFinishWrite(pDisk);
2101 AssertRC(rc2);
2102 fLockWrite = false;
2103
2104 uOffset += cbThisRead;
2105 cbRemaining -= cbThisRead;
2106
2107 if (pCbProgress && pCbProgress->pfnProgress)
2108 {
2109 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
2110 uOffset * 99 / cbSize);
2111 if (RT_FAILURE(rc))
2112 break;
2113 }
2114 } while (uOffset < cbSize);
2115 }
2116 else
2117 {
2118 /*
2119 * We may need to update the parent uuid of the child coming after the
2120 * last image to be merged. We have to reopen it read/write.
2121 *
2122 * This is done before we do the actual merge to prevent an incosistent
2123 * chain if the mode change fails for some reason.
2124 */
2125 if (pImageFrom->pNext)
2126 {
2127 PVDIMAGE pImageChild = pImageFrom->pNext;
2128
2129 /* We need to open the image in read/write mode. */
2130 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pvBackendData);
2131
2132 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
2133 {
2134 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
2135 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pvBackendData,
2136 uOpenFlags);
2137 if (RT_FAILURE(rc))
2138 break;
2139 }
2140 }
2141
2142 /* Merge child state into parent. This means writing all blocks
2143 * which are allocated in the image up to the source image to the
2144 * destination image. */
2145 uint64_t uOffset = 0;
2146 uint64_t cbRemaining = cbSize;
2147 do
2148 {
2149 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
2150 rc = VERR_VD_BLOCK_FREE;
2151
2152 /* Need to hold the write lock during a read-write operation. */
2153 rc2 = vdThreadStartWrite(pDisk);
2154 AssertRC(rc2);
2155 fLockWrite = true;
2156
2157 /* Search for image with allocated block. Do not attempt to
2158 * read more than the previous reads marked as valid. Otherwise
2159 * this would return stale data when different block sizes are
2160 * used for the images. */
2161 for (PVDIMAGE pCurrImage = pImageFrom;
2162 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VD_BLOCK_FREE;
2163 pCurrImage = pCurrImage->pPrev)
2164 {
2165 rc = pCurrImage->Backend->pfnRead(pCurrImage->pvBackendData,
2166 uOffset, pvBuf,
2167 cbThisRead, &cbThisRead);
2168 }
2169
2170 if (rc != VERR_VD_BLOCK_FREE)
2171 {
2172 if (RT_FAILURE(rc))
2173 break;
2174 rc = vdWriteHelper(pDisk, pImageTo, NULL, uOffset, pvBuf,
2175 cbThisRead);
2176 if (RT_FAILURE(rc))
2177 break;
2178 }
2179 else
2180 rc = VINF_SUCCESS;
2181
2182 rc2 = vdThreadFinishWrite(pDisk);
2183 AssertRC(rc2);
2184 fLockWrite = true;
2185
2186 uOffset += cbThisRead;
2187 cbRemaining -= cbThisRead;
2188
2189 if (pCbProgress && pCbProgress->pfnProgress)
2190 {
2191 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
2192 uOffset * 99 / cbSize);
2193 if (RT_FAILURE(rc))
2194 break;
2195 }
2196 } while (uOffset < cbSize);
2197 }
2198
2199 /* Need to hold the write lock while finishing the merge. */
2200 rc2 = vdThreadStartWrite(pDisk);
2201 AssertRC(rc2);
2202 fLockWrite = true;
2203
2204 /* Update parent UUID so that image chain is consistent. */
2205 RTUUID Uuid;
2206 PVDIMAGE pImageChild = NULL;
2207 if (nImageFrom < nImageTo)
2208 {
2209 if (pImageFrom->pPrev)
2210 {
2211 rc = pImageFrom->pPrev->Backend->pfnGetUuid(pImageFrom->pPrev->pvBackendData,
2212 &Uuid);
2213 AssertRC(rc);
2214 }
2215 else
2216 RTUuidClear(&Uuid);
2217 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pvBackendData,
2218 &Uuid);
2219 AssertRC(rc);
2220 }
2221 else
2222 {
2223 /* Update the parent uuid of the child of the last merged image. */
2224 if (pImageFrom->pNext)
2225 {
2226 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pvBackendData,
2227 &Uuid);
2228 AssertRC(rc);
2229
2230 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext->pvBackendData,
2231 &Uuid);
2232 AssertRC(rc);
2233
2234 pImageChild = pImageFrom->pNext;
2235 }
2236 }
2237
2238 /* Delete the no longer needed images. */
2239 PVDIMAGE pImg = pImageFrom, pTmp;
2240 while (pImg != pImageTo)
2241 {
2242 if (nImageFrom < nImageTo)
2243 pTmp = pImg->pNext;
2244 else
2245 pTmp = pImg->pPrev;
2246 vdRemoveImageFromList(pDisk, pImg);
2247 pImg->Backend->pfnClose(pImg->pvBackendData, true);
2248 RTMemFree(pImg->pszFilename);
2249 RTMemFree(pImg);
2250 pImg = pTmp;
2251 }
2252
2253 /* Make sure destination image is back to read only if necessary. */
2254 if (pImageTo != pDisk->pLast)
2255 {
2256 uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
2257 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
2258 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pvBackendData,
2259 uOpenFlags);
2260 if (RT_FAILURE(rc))
2261 break;
2262 }
2263
2264 /*
2265 * Make sure the child is readonly
2266 * for the child -> parent merge direction
2267 * if neccessary.
2268 */
2269 if ( nImageFrom > nImageTo
2270 && pImageChild
2271 && pImageChild != pDisk->pLast)
2272 {
2273 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pvBackendData);
2274 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
2275 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pvBackendData,
2276 uOpenFlags);
2277 if (RT_FAILURE(rc))
2278 break;
2279 }
2280 } while (0);
2281
2282 if (RT_UNLIKELY(fLockWrite))
2283 {
2284 rc2 = vdThreadFinishWrite(pDisk);
2285 AssertRC(rc2);
2286 }
2287 else if (RT_UNLIKELY(fLockRead))
2288 {
2289 rc2 = vdThreadFinishRead(pDisk);
2290 AssertRC(rc2);
2291 }
2292
2293 if (pvBuf)
2294 RTMemTmpFree(pvBuf);
2295
2296 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
2297 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
2298
2299 LogFlowFunc(("returns %Rrc\n", rc));
2300 return rc;
2301}
2302
2303/**
2304 * Copies an image from one HDD container to another.
2305 * The copy is opened in the target HDD container.
2306 * It is possible to convert between different image formats, because the
2307 * backend for the destination may be different from the source.
2308 * If both the source and destination reference the same HDD container,
2309 * then the image is moved (by copying/deleting or renaming) to the new location.
2310 * The source container is unchanged if the move operation fails, otherwise
2311 * the image at the new location is opened in the same way as the old one was.
2312 *
2313 * @returns VBox status code.
2314 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
2315 * @param pDiskFrom Pointer to source HDD container.
2316 * @param nImage Image number, counts from 0. 0 is always base image of container.
2317 * @param pDiskTo Pointer to destination HDD container.
2318 * @param pszBackend Name of the image file backend to use.
2319 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
2320 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
2321 * @param cbSize New image size (0 means leave unchanged).
2322 * @param uImageFlags Flags specifying special destination image features.
2323 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
2324 * This parameter is used if and only if a true copy is created.
2325 * In all rename/move cases the UUIDs are copied over.
2326 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
2327 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
2328 * destination image.
2329 * @param pDstVDIfsOperation Pointer to the per-image VD interface list,
2330 * for the destination image.
2331 */
2332VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
2333 const char *pszBackend, const char *pszFilename,
2334 bool fMoveByRename, uint64_t cbSize,
2335 unsigned uImageFlags, PCRTUUID pDstUuid,
2336 PVDINTERFACE pVDIfsOperation,
2337 PVDINTERFACE pDstVDIfsImage,
2338 PVDINTERFACE pDstVDIfsOperation)
2339{
2340 int rc = VINF_SUCCESS;
2341 int rc2;
2342 bool fLockReadFrom = false, fLockWriteFrom = false, fLockWriteTo = false;
2343 void *pvBuf = NULL;
2344 PVDIMAGE pImageTo = NULL;
2345
2346 LogFlowFunc(("pDiskFrom=%#p nImage=%u pDiskTo=%#p pszBackend=\"%s\" pszFilename=\"%s\" fMoveByRename=%d cbSize=%llu pVDIfsOperation=%#p pDstVDIfsImage=%#p pDstVDIfsOperation=%#p\n",
2347 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
2348
2349 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
2350 VDINTERFACETYPE_PROGRESS);
2351 PVDINTERFACEPROGRESS pCbProgress = NULL;
2352 if (pIfProgress)
2353 pCbProgress = VDGetInterfaceProgress(pIfProgress);
2354
2355 PVDINTERFACE pDstIfProgress = VDInterfaceGet(pDstVDIfsOperation,
2356 VDINTERFACETYPE_PROGRESS);
2357 PVDINTERFACEPROGRESS pDstCbProgress = NULL;
2358 if (pDstIfProgress)
2359 pDstCbProgress = VDGetInterfaceProgress(pDstIfProgress);
2360
2361 do {
2362 /* Check arguments. */
2363 AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
2364 rc = VERR_INVALID_PARAMETER);
2365 AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
2366 ("u32Signature=%08x\n", pDiskFrom->u32Signature));
2367
2368 rc2 = vdThreadStartRead(pDiskFrom);
2369 AssertRC(rc2);
2370 fLockReadFrom = true;
2371 PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
2372 AssertPtrBreakStmt(pImageFrom, rc = VERR_VD_IMAGE_NOT_FOUND);
2373 AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
2374 rc = VERR_INVALID_PARAMETER);
2375 AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
2376 ("u32Signature=%08x\n", pDiskTo->u32Signature));
2377
2378 /* Move the image. */
2379 if (pDiskFrom == pDiskTo)
2380 {
2381 /* Rename only works when backends are the same. */
2382 if ( fMoveByRename
2383 && !RTStrICmp(pszBackend, pImageFrom->Backend->pszBackendName))
2384 {
2385 rc2 = vdThreadFinishRead(pDiskFrom);
2386 AssertRC(rc2);
2387 fLockReadFrom = false;
2388
2389 rc2 = vdThreadStartWrite(pDiskFrom);
2390 AssertRC(rc2);
2391 fLockWriteFrom = true;
2392 rc = pImageFrom->Backend->pfnRename(pImageFrom->pvBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
2393 break;
2394 }
2395
2396 /** @todo Moving (including shrinking/growing) of the image is
2397 * requested, but the rename attempt failed or it wasn't possible.
2398 * Must now copy image to temp location. */
2399 AssertReleaseMsgFailed(("VDCopy: moving by copy/delete not implemented\n"));
2400 }
2401
2402 /* pszFilename is allowed to be NULL, as this indicates copy to the existing image. */
2403 AssertMsgBreakStmt(pszFilename == NULL || (VALID_PTR(pszFilename) && *pszFilename),
2404 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
2405 rc = VERR_INVALID_PARAMETER);
2406
2407 uint64_t cbSizeFrom;
2408 cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pvBackendData);
2409 if (cbSizeFrom == 0)
2410 {
2411 rc = VERR_VD_VALUE_NOT_FOUND;
2412 break;
2413 }
2414
2415 PDMMEDIAGEOMETRY PCHSGeometryFrom = {0, 0, 0};
2416 PDMMEDIAGEOMETRY LCHSGeometryFrom = {0, 0, 0};
2417 pImageFrom->Backend->pfnGetPCHSGeometry(pImageFrom->pvBackendData, &PCHSGeometryFrom);
2418 pImageFrom->Backend->pfnGetLCHSGeometry(pImageFrom->pvBackendData, &LCHSGeometryFrom);
2419
2420 RTUUID ImageUuid, ImageModificationUuid;
2421 RTUUID ParentUuid, ParentModificationUuid;
2422 if (pDiskFrom != pDiskTo)
2423 {
2424 if (pDstUuid)
2425 ImageUuid = *pDstUuid;
2426 else
2427 RTUuidCreate(&ImageUuid);
2428 }
2429 else
2430 {
2431 rc = pImageFrom->Backend->pfnGetUuid(pImageFrom->pvBackendData, &ImageUuid);
2432 if (RT_FAILURE(rc))
2433 RTUuidCreate(&ImageUuid);
2434 }
2435 rc = pImageFrom->Backend->pfnGetModificationUuid(pImageFrom->pvBackendData, &ImageModificationUuid);
2436 if (RT_FAILURE(rc))
2437 RTUuidClear(&ImageModificationUuid);
2438 rc = pImageFrom->Backend->pfnGetParentUuid(pImageFrom->pvBackendData, &ParentUuid);
2439 if (RT_FAILURE(rc))
2440 RTUuidClear(&ParentUuid);
2441 rc = pImageFrom->Backend->pfnGetParentModificationUuid(pImageFrom->pvBackendData, &ParentModificationUuid);
2442 if (RT_FAILURE(rc))
2443 RTUuidClear(&ParentModificationUuid);
2444
2445 char szComment[1024];
2446 rc = pImageFrom->Backend->pfnGetComment(pImageFrom->pvBackendData, szComment, sizeof(szComment));
2447 if (RT_FAILURE(rc))
2448 szComment[0] = '\0';
2449 else
2450 szComment[sizeof(szComment) - 1] = '\0';
2451
2452 unsigned uOpenFlagsFrom;
2453 uOpenFlagsFrom = pImageFrom->Backend->pfnGetOpenFlags(pImageFrom->pvBackendData);
2454
2455 rc2 = vdThreadFinishRead(pDiskFrom);
2456 AssertRC(rc2);
2457 fLockReadFrom = false;
2458
2459 if (pszFilename)
2460 {
2461 if (cbSize == 0)
2462 cbSize = cbSizeFrom;
2463
2464 /* Create destination image with the properties of the source image. */
2465 /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
2466 * calls to the backend. Unifies the code and reduces the API
2467 * dependencies. Would also make the synchronization explicit. */
2468 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
2469 {
2470 rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename, uImageFlags,
2471 szComment, &ImageUuid, &ParentUuid, uOpenFlagsFrom & ~VD_OPEN_FLAGS_READONLY, NULL, NULL);
2472
2473 rc2 = vdThreadStartWrite(pDiskTo);
2474 AssertRC(rc2);
2475 fLockWriteTo = true;
2476 } else {
2477 /** @todo hack to force creation of a fixed image for
2478 * the RAW backend, which can't handle anything else. */
2479 if (!RTStrICmp(pszBackend, "RAW"))
2480 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
2481
2482 /* Fix broken PCHS geometry. Can happen for two reasons: either
2483 * the backend mixes up PCHS and LCHS, or the application used
2484 * to create the source image has put garbage in it. */
2485 /** @todo double-check if the VHD backend correctly handles
2486 * PCHS and LCHS geometry. also reconsider our current paranoia
2487 * level when it comes to geometry settings here and in the
2488 * backends. */
2489 if (PCHSGeometryFrom.cHeads > 16 || PCHSGeometryFrom.cSectors > 63)
2490 {
2491 Assert(RT_MIN(cbSize / 512 / 16 / 63, 16383) - (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383));
2492 PCHSGeometryFrom.cCylinders = (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383);
2493 PCHSGeometryFrom.cHeads = 16;
2494 PCHSGeometryFrom.cSectors = 63;
2495 }
2496
2497 rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, cbSize,
2498 uImageFlags, szComment,
2499 &PCHSGeometryFrom, &LCHSGeometryFrom,
2500 NULL, uOpenFlagsFrom & ~VD_OPEN_FLAGS_READONLY, NULL, NULL);
2501
2502 rc2 = vdThreadStartWrite(pDiskTo);
2503 AssertRC(rc2);
2504 fLockWriteTo = true;
2505
2506 if (RT_SUCCESS(rc) && !RTUuidIsNull(&ImageUuid))
2507 pDiskTo->pLast->Backend->pfnSetUuid(pDiskTo->pLast->pvBackendData, &ImageUuid);
2508 if (RT_SUCCESS(rc) && !RTUuidIsNull(&ParentUuid))
2509 pDiskTo->pLast->Backend->pfnSetParentUuid(pDiskTo->pLast->pvBackendData, &ParentUuid);
2510 }
2511 if (RT_FAILURE(rc))
2512 break;
2513
2514 pImageTo = pDiskTo->pLast;
2515 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
2516
2517 cbSize = RT_MIN(cbSize, cbSizeFrom);
2518 }
2519 else
2520 {
2521 pImageTo = pDiskTo->pLast;
2522 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
2523
2524 uint64_t cbSizeTo;
2525 cbSizeTo = pImageTo->Backend->pfnGetSize(pImageTo->pvBackendData);
2526 if (cbSizeTo == 0)
2527 {
2528 rc = VERR_VD_VALUE_NOT_FOUND;
2529 break;
2530 }
2531
2532 if (cbSize == 0)
2533 cbSize = RT_MIN(cbSizeFrom, cbSizeTo);
2534 }
2535
2536 rc2 = vdThreadFinishWrite(pDiskTo);
2537 AssertRC(rc2);
2538 fLockWriteTo = false;
2539
2540 /* Allocate tmp buffer. */
2541 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
2542 if (!pvBuf)
2543 {
2544 rc = VERR_NO_MEMORY;
2545 break;
2546 }
2547
2548 /* Copy the data. */
2549 uint64_t uOffset = 0;
2550 uint64_t cbRemaining = cbSize;
2551
2552 do
2553 {
2554 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
2555
2556 /* Note that we don't attempt to synchronize cross-disk accesses.
2557 * It wouldn't be very difficult to do, just the lock order would
2558 * need to be defined somehow to prevent deadlocks. Postpone such
2559 * magic as there is no use case for this. */
2560
2561 rc2 = vdThreadStartRead(pDiskFrom);
2562 AssertRC(rc2);
2563 fLockReadFrom = true;
2564
2565 rc = vdReadHelper(pDiskFrom, pImageFrom, NULL, uOffset, pvBuf,
2566 cbThisRead);
2567 if (RT_FAILURE(rc))
2568 break;
2569
2570 rc2 = vdThreadFinishRead(pDiskFrom);
2571 AssertRC(rc2);
2572 fLockReadFrom = false;
2573
2574 rc2 = vdThreadStartWrite(pDiskTo);
2575 AssertRC(rc2);
2576 fLockWriteTo = true;
2577
2578 rc = vdWriteHelper(pDiskTo, pImageTo, NULL, uOffset, pvBuf,
2579 cbThisRead);
2580 if (RT_FAILURE(rc))
2581 break;
2582
2583 rc2 = vdThreadFinishWrite(pDiskTo);
2584 AssertRC(rc2);
2585 fLockWriteTo = false;
2586
2587 uOffset += cbThisRead;
2588 cbRemaining -= cbThisRead;
2589
2590 if (pCbProgress && pCbProgress->pfnProgress)
2591 {
2592 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
2593 uOffset * 99 / cbSize);
2594 if (RT_FAILURE(rc))
2595 break;
2596 }
2597 if (pDstCbProgress && pDstCbProgress->pfnProgress)
2598 {
2599 rc = pDstCbProgress->pfnProgress(pDstIfProgress->pvUser,
2600 uOffset * 99 / cbSize);
2601 if (RT_FAILURE(rc))
2602 break;
2603 }
2604 } while (uOffset < cbSize);
2605
2606 if (RT_SUCCESS(rc))
2607 {
2608 rc2 = vdThreadStartWrite(pDiskTo);
2609 AssertRC(rc2);
2610 fLockWriteTo = true;
2611
2612 /* Only set modification UUID if it is non-null, since the source
2613 * backend might not provide a valid modification UUID. */
2614 if (!RTUuidIsNull(&ImageModificationUuid))
2615 pImageTo->Backend->pfnSetModificationUuid(pImageTo->pvBackendData, &ImageModificationUuid);
2616 /** @todo double-check this - it makes little sense to copy over the parent modification uuid,
2617 * as the destination image can have a totally different parent. */
2618#if 0
2619 pImageTo->Backend->pfnSetParentModificationUuid(pImageTo->pvBackendData, &ParentModificationUuid);
2620#endif
2621 }
2622 } while (0);
2623
2624 if (RT_FAILURE(rc) && pImageTo && pszFilename)
2625 {
2626 /* Take the write lock only if it is not taken. Not worth making the
2627 * above code even more complicated. */
2628 if (RT_UNLIKELY(!fLockWriteTo))
2629 {
2630 rc2 = vdThreadStartWrite(pDiskTo);
2631 AssertRC(rc2);
2632 fLockWriteTo = true;
2633 }
2634 /* Error detected, but new image created. Remove image from list. */
2635 vdRemoveImageFromList(pDiskTo, pImageTo);
2636
2637 /* Close and delete image. */
2638 rc2 = pImageTo->Backend->pfnClose(pImageTo->pvBackendData, true);
2639 AssertRC(rc2);
2640 pImageTo->pvBackendData = NULL;
2641
2642 /* Free remaining resources. */
2643 if (pImageTo->pszFilename)
2644 RTStrFree(pImageTo->pszFilename);
2645
2646 RTMemFree(pImageTo);
2647 }
2648
2649 if (RT_UNLIKELY(fLockWriteTo))
2650 {
2651 rc2 = vdThreadFinishWrite(pDiskTo);
2652 AssertRC(rc2);
2653 }
2654 if (RT_UNLIKELY(fLockWriteFrom))
2655 {
2656 rc2 = vdThreadFinishWrite(pDiskFrom);
2657 AssertRC(rc2);
2658 }
2659 else if (RT_UNLIKELY(fLockReadFrom))
2660 {
2661 rc2 = vdThreadFinishRead(pDiskFrom);
2662 AssertRC(rc2);
2663 }
2664
2665 if (pvBuf)
2666 RTMemTmpFree(pvBuf);
2667
2668 if (RT_SUCCESS(rc))
2669 {
2670 if (pCbProgress && pCbProgress->pfnProgress)
2671 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
2672 if (pDstCbProgress && pDstCbProgress->pfnProgress)
2673 pDstCbProgress->pfnProgress(pDstIfProgress->pvUser, 100);
2674 }
2675
2676 LogFlowFunc(("returns %Rrc\n", rc));
2677 return rc;
2678}
2679
2680/**
2681 * Optimizes the storage consumption of an image. Typically the unused blocks
2682 * have to be wiped with zeroes to achieve a substantial reduced storage use.
2683 * Another optimization done is reordering the image blocks, which can provide
2684 * a significant performance boost, as reads and writes tend to use less random
2685 * file offsets.
2686 *
2687 * @return VBox status code.
2688 * @return VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
2689 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
2690 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
2691 * the code for this isn't implemented yet.
2692 * @param pDisk Pointer to HDD container.
2693 * @param nImage Image number, counts from 0. 0 is always base image of container.
2694 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
2695 */
2696VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
2697 PVDINTERFACE pVDIfsOperation)
2698{
2699 int rc = VINF_SUCCESS;
2700 int rc2;
2701 bool fLockRead = false, fLockWrite = false;
2702 void *pvBuf = NULL;
2703 void *pvTmp = NULL;
2704
2705 LogFlowFunc(("pDisk=%#p nImage=%u pVDIfsOperation=%#p\n",
2706 pDisk, nImage, pVDIfsOperation));
2707
2708 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
2709 VDINTERFACETYPE_PROGRESS);
2710 PVDINTERFACEPROGRESS pCbProgress = NULL;
2711 if (pIfProgress)
2712 pCbProgress = VDGetInterfaceProgress(pIfProgress);
2713
2714 do {
2715 /* Check arguments. */
2716 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
2717 rc = VERR_INVALID_PARAMETER);
2718 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
2719 ("u32Signature=%08x\n", pDisk->u32Signature));
2720
2721 rc2 = vdThreadStartRead(pDisk);
2722 AssertRC(rc2);
2723 fLockRead = true;
2724
2725 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2726 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
2727
2728 /* If there is no compact callback for not file based backends then
2729 * the backend doesn't need compaction. No need to make much fuss about
2730 * this. For file based ones signal this as not yet supported. */
2731 if (!pImage->Backend->pfnCompact)
2732 {
2733 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
2734 rc = VERR_NOT_SUPPORTED;
2735 else
2736 rc = VINF_SUCCESS;
2737 break;
2738 }
2739
2740 /* Insert interface for reading parent state into per-operation list,
2741 * if there is a parent image. */
2742 VDINTERFACE IfOpParent;
2743 VDINTERFACEPARENTSTATE ParentCb;
2744 VDPARENTSTATEDESC ParentUser;
2745 if (pImage->pPrev)
2746 {
2747 ParentCb.cbSize = sizeof(ParentCb);
2748 ParentCb.enmInterface = VDINTERFACETYPE_PARENTSTATE;
2749 ParentCb.pfnParentRead = vdParentRead;
2750 ParentUser.pDisk = pDisk;
2751 ParentUser.pImage = pImage->pPrev;
2752 rc = VDInterfaceAdd(&IfOpParent, "VDCompact_ParentState", VDINTERFACETYPE_PARENTSTATE,
2753 &ParentCb, &ParentUser, &pVDIfsOperation);
2754 AssertRC(rc);
2755 }
2756
2757 rc2 = vdThreadFinishRead(pDisk);
2758 AssertRC(rc2);
2759 fLockRead = false;
2760
2761 rc2 = vdThreadStartWrite(pDisk);
2762 AssertRC(rc2);
2763 fLockWrite = true;
2764
2765 rc = pImage->Backend->pfnCompact(pImage->pvBackendData,
2766 0, 99,
2767 pDisk->pVDIfsDisk,
2768 pImage->pVDIfsImage,
2769 pVDIfsOperation);
2770 } while (0);
2771
2772 if (RT_UNLIKELY(fLockWrite))
2773 {
2774 rc2 = vdThreadFinishWrite(pDisk);
2775 AssertRC(rc2);
2776 }
2777 else if (RT_UNLIKELY(fLockRead))
2778 {
2779 rc2 = vdThreadFinishRead(pDisk);
2780 AssertRC(rc2);
2781 }
2782
2783 if (pvBuf)
2784 RTMemTmpFree(pvBuf);
2785 if (pvTmp)
2786 RTMemTmpFree(pvTmp);
2787
2788 if (RT_SUCCESS(rc))
2789 {
2790 if (pCbProgress && pCbProgress->pfnProgress)
2791 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
2792 }
2793
2794 LogFlowFunc(("returns %Rrc\n", rc));
2795 return rc;
2796}
2797
2798/**
2799 * Closes the last opened image file in HDD container.
2800 * If previous image file was opened in read-only mode (the normal case) and
2801 * the last opened image is in read-write mode then the previous image will be
2802 * reopened in read/write mode.
2803 *
2804 * @returns VBox status code.
2805 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
2806 * @param pDisk Pointer to HDD container.
2807 * @param fDelete If true, delete the image from the host disk.
2808 */
2809VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
2810{
2811 int rc = VINF_SUCCESS;
2812 int rc2;
2813 bool fLockWrite = false;
2814
2815 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
2816 do
2817 {
2818 /* sanity check */
2819 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2820 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2821
2822 /* Not worth splitting this up into a read lock phase and write
2823 * lock phase, as closing an image is a relatively fast operation
2824 * dominated by the part which needs the write lock. */
2825 rc2 = vdThreadStartWrite(pDisk);
2826 AssertRC(rc2);
2827 fLockWrite = true;
2828
2829 PVDIMAGE pImage = pDisk->pLast;
2830 if (!pImage)
2831 {
2832 rc = VERR_VD_NOT_OPENED;
2833 break;
2834 }
2835 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2836 /* Remove image from list of opened images. */
2837 vdRemoveImageFromList(pDisk, pImage);
2838 /* Close (and optionally delete) image. */
2839 rc = pImage->Backend->pfnClose(pImage->pvBackendData, fDelete);
2840 /* Free remaining resources related to the image. */
2841 RTStrFree(pImage->pszFilename);
2842 RTMemFree(pImage);
2843
2844 pImage = pDisk->pLast;
2845 if (!pImage)
2846 break;
2847
2848 /* If disk was previously in read/write mode, make sure it will stay
2849 * like this (if possible) after closing this image. Set the open flags
2850 * accordingly. */
2851 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
2852 {
2853 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2854 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
2855 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData, uOpenFlags);
2856 }
2857
2858 /* Cache disk information. */
2859 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
2860
2861 /* Cache PCHS geometry. */
2862 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2863 &pDisk->PCHSGeometry);
2864 if (RT_FAILURE(rc2))
2865 {
2866 pDisk->PCHSGeometry.cCylinders = 0;
2867 pDisk->PCHSGeometry.cHeads = 0;
2868 pDisk->PCHSGeometry.cSectors = 0;
2869 }
2870 else
2871 {
2872 /* Make sure the PCHS geometry is properly clipped. */
2873 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
2874 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
2875 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
2876 }
2877
2878 /* Cache LCHS geometry. */
2879 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2880 &pDisk->LCHSGeometry);
2881 if (RT_FAILURE(rc2))
2882 {
2883 pDisk->LCHSGeometry.cCylinders = 0;
2884 pDisk->LCHSGeometry.cHeads = 0;
2885 pDisk->LCHSGeometry.cSectors = 0;
2886 }
2887 else
2888 {
2889 /* Make sure the LCHS geometry is properly clipped. */
2890 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
2891 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
2892 }
2893 } while (0);
2894
2895 if (RT_UNLIKELY(fLockWrite))
2896 {
2897 rc2 = vdThreadFinishWrite(pDisk);
2898 AssertRC(rc2);
2899 }
2900
2901 LogFlowFunc(("returns %Rrc\n", rc));
2902 return rc;
2903}
2904
2905/**
2906 * Closes all opened image files in HDD container.
2907 *
2908 * @returns VBox status code.
2909 * @param pDisk Pointer to HDD container.
2910 */
2911VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
2912{
2913 int rc = VINF_SUCCESS;
2914 int rc2;
2915 bool fLockWrite = false;
2916
2917 LogFlowFunc(("pDisk=%#p\n", pDisk));
2918 do
2919 {
2920 /* sanity check */
2921 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2922 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2923
2924 /* Lock the entire operation. */
2925 rc2 = vdThreadStartWrite(pDisk);
2926 AssertRC(rc2);
2927 fLockWrite = true;
2928
2929 PVDIMAGE pImage = pDisk->pLast;
2930 while (VALID_PTR(pImage))
2931 {
2932 PVDIMAGE pPrev = pImage->pPrev;
2933 /* Remove image from list of opened images. */
2934 vdRemoveImageFromList(pDisk, pImage);
2935 /* Close image. */
2936 int rc2 = pImage->Backend->pfnClose(pImage->pvBackendData, false);
2937 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
2938 rc = rc2;
2939 /* Free remaining resources related to the image. */
2940 RTStrFree(pImage->pszFilename);
2941 RTMemFree(pImage);
2942 pImage = pPrev;
2943 }
2944 Assert(!VALID_PTR(pDisk->pLast));
2945 } while (0);
2946
2947 if (RT_UNLIKELY(fLockWrite))
2948 {
2949 rc2 = vdThreadFinishWrite(pDisk);
2950 AssertRC(rc2);
2951 }
2952
2953 LogFlowFunc(("returns %Rrc\n", rc));
2954 return rc;
2955}
2956
2957/**
2958 * Read data from virtual HDD.
2959 *
2960 * @returns VBox status code.
2961 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
2962 * @param pDisk Pointer to HDD container.
2963 * @param uOffset Offset of first reading byte from start of disk.
2964 * @param pvBuf Pointer to buffer for reading data.
2965 * @param cbRead Number of bytes to read.
2966 */
2967VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
2968 size_t cbRead)
2969{
2970 int rc = VINF_SUCCESS;
2971 int rc2;
2972 bool fLockRead = false;
2973
2974 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
2975 pDisk, uOffset, pvBuf, cbRead));
2976 do
2977 {
2978 /* sanity check */
2979 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
2980 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2981
2982 /* Check arguments. */
2983 AssertMsgBreakStmt(VALID_PTR(pvBuf),
2984 ("pvBuf=%#p\n", pvBuf),
2985 rc = VERR_INVALID_PARAMETER);
2986 AssertMsgBreakStmt(cbRead,
2987 ("cbRead=%zu\n", cbRead),
2988 rc = VERR_INVALID_PARAMETER);
2989
2990 rc2 = vdThreadStartRead(pDisk);
2991 AssertRC(rc2);
2992 fLockRead = true;
2993
2994 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
2995 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
2996 uOffset, cbRead, pDisk->cbSize),
2997 rc = VERR_INVALID_PARAMETER);
2998
2999 PVDIMAGE pImage = pDisk->pLast;
3000 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
3001
3002 rc = vdReadHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbRead);
3003 } while (0);
3004
3005 if (RT_UNLIKELY(fLockRead))
3006 {
3007 rc2 = vdThreadFinishRead(pDisk);
3008 AssertRC(rc2);
3009 }
3010
3011 LogFlowFunc(("returns %Rrc\n", rc));
3012 return rc;
3013}
3014
3015/**
3016 * Write data to virtual HDD.
3017 *
3018 * @returns VBox status code.
3019 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
3020 * @param pDisk Pointer to HDD container.
3021 * @param uOffset Offset of the first byte being
3022 * written from start of disk.
3023 * @param pvBuf Pointer to buffer for writing data.
3024 * @param cbWrite Number of bytes to write.
3025 */
3026VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
3027 size_t cbWrite)
3028{
3029 int rc = VINF_SUCCESS;
3030 int rc2;
3031 bool fLockWrite = false;
3032
3033 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
3034 pDisk, uOffset, pvBuf, cbWrite));
3035 do
3036 {
3037 /* sanity check */
3038 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3039 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3040
3041 /* Check arguments. */
3042 AssertMsgBreakStmt(VALID_PTR(pvBuf),
3043 ("pvBuf=%#p\n", pvBuf),
3044 rc = VERR_INVALID_PARAMETER);
3045 AssertMsgBreakStmt(cbWrite,
3046 ("cbWrite=%zu\n", cbWrite),
3047 rc = VERR_INVALID_PARAMETER);
3048
3049 rc2 = vdThreadStartWrite(pDisk);
3050 AssertRC(rc2);
3051 fLockWrite = true;
3052
3053 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
3054 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
3055 uOffset, cbWrite, pDisk->cbSize),
3056 rc = VERR_INVALID_PARAMETER);
3057
3058 PVDIMAGE pImage = pDisk->pLast;
3059 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
3060
3061 vdSetModifiedFlag(pDisk);
3062 rc = vdWriteHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbWrite);
3063 } while (0);
3064
3065 if (RT_UNLIKELY(fLockWrite))
3066 {
3067 rc2 = vdThreadFinishWrite(pDisk);
3068 AssertRC(rc2);
3069 }
3070
3071 LogFlowFunc(("returns %Rrc\n", rc));
3072 return rc;
3073}
3074
3075/**
3076 * Make sure the on disk representation of a virtual HDD is up to date.
3077 *
3078 * @returns VBox status code.
3079 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
3080 * @param pDisk Pointer to HDD container.
3081 */
3082VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
3083{
3084 int rc = VINF_SUCCESS;
3085 int rc2;
3086 bool fLockWrite = false;
3087
3088 LogFlowFunc(("pDisk=%#p\n", pDisk));
3089 do
3090 {
3091 /* sanity check */
3092 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3093 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3094
3095 rc2 = vdThreadStartWrite(pDisk);
3096 AssertRC(rc2);
3097 fLockWrite = true;
3098
3099 PVDIMAGE pImage = pDisk->pLast;
3100 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
3101
3102 vdResetModifiedFlag(pDisk);
3103 rc = pImage->Backend->pfnFlush(pImage->pvBackendData);
3104 } while (0);
3105
3106 if (RT_UNLIKELY(fLockWrite))
3107 {
3108 rc2 = vdThreadFinishWrite(pDisk);
3109 AssertRC(rc2);
3110 }
3111
3112 LogFlowFunc(("returns %Rrc\n", rc));
3113 return rc;
3114}
3115
3116/**
3117 * Get number of opened images in HDD container.
3118 *
3119 * @returns Number of opened images for HDD container. 0 if no images have been opened.
3120 * @param pDisk Pointer to HDD container.
3121 */
3122VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
3123{
3124 unsigned cImages;
3125 int rc2;
3126 bool fLockRead = false;
3127
3128 LogFlowFunc(("pDisk=%#p\n", pDisk));
3129 do
3130 {
3131 /* sanity check */
3132 AssertPtrBreakStmt(pDisk, cImages = 0);
3133 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3134
3135 rc2 = vdThreadStartRead(pDisk);
3136 AssertRC(rc2);
3137 fLockRead = true;
3138
3139 cImages = pDisk->cImages;
3140 } while (0);
3141
3142 if (RT_UNLIKELY(fLockRead))
3143 {
3144 rc2 = vdThreadFinishRead(pDisk);
3145 AssertRC(rc2);
3146 }
3147
3148 LogFlowFunc(("returns %u\n", cImages));
3149 return cImages;
3150}
3151
3152/**
3153 * Get read/write mode of HDD container.
3154 *
3155 * @returns Virtual disk ReadOnly status.
3156 * @returns true if no image is opened in HDD container.
3157 * @param pDisk Pointer to HDD container.
3158 */
3159VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
3160{
3161 bool fReadOnly;
3162 int rc2;
3163 bool fLockRead = false;
3164
3165 LogFlowFunc(("pDisk=%#p\n", pDisk));
3166 do
3167 {
3168 /* sanity check */
3169 AssertPtrBreakStmt(pDisk, fReadOnly = false);
3170 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3171
3172 rc2 = vdThreadStartRead(pDisk);
3173 AssertRC(rc2);
3174 fLockRead = true;
3175
3176 PVDIMAGE pImage = pDisk->pLast;
3177 AssertPtrBreakStmt(pImage, fReadOnly = true);
3178
3179 unsigned uOpenFlags;
3180 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
3181 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
3182 } while (0);
3183
3184 if (RT_UNLIKELY(fLockRead))
3185 {
3186 rc2 = vdThreadFinishRead(pDisk);
3187 AssertRC(rc2);
3188 }
3189
3190 LogFlowFunc(("returns %d\n", fReadOnly));
3191 return fReadOnly;
3192}
3193
3194/**
3195 * Get total capacity of an image in HDD container.
3196 *
3197 * @returns Virtual disk size in bytes.
3198 * @returns 0 if no image with specified number was not opened.
3199 * @param pDisk Pointer to HDD container.
3200 * @param nImage Image number, counds from 0. 0 is always base image of container.
3201 */
3202VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
3203{
3204 uint64_t cbSize;
3205 int rc2;
3206 bool fLockRead = false;
3207
3208 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
3209 do
3210 {
3211 /* sanity check */
3212 AssertPtrBreakStmt(pDisk, cbSize = 0);
3213 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3214
3215 rc2 = vdThreadStartRead(pDisk);
3216 AssertRC(rc2);
3217 fLockRead = true;
3218
3219 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3220 AssertPtrBreakStmt(pImage, cbSize = 0);
3221 cbSize = pImage->Backend->pfnGetSize(pImage->pvBackendData);
3222 } while (0);
3223
3224 if (RT_UNLIKELY(fLockRead))
3225 {
3226 rc2 = vdThreadFinishRead(pDisk);
3227 AssertRC(rc2);
3228 }
3229
3230 LogFlowFunc(("returns %llu\n", cbSize));
3231 return cbSize;
3232}
3233
3234/**
3235 * Get total file size of an image in HDD container.
3236 *
3237 * @returns Virtual disk size in bytes.
3238 * @returns 0 if no image is opened in HDD container.
3239 * @param pDisk Pointer to HDD container.
3240 * @param nImage Image number, counts from 0. 0 is always base image of container.
3241 */
3242VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
3243{
3244 uint64_t cbSize;
3245 int rc2;
3246 bool fLockRead = false;
3247
3248 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
3249 do
3250 {
3251 /* sanity check */
3252 AssertPtrBreakStmt(pDisk, cbSize = 0);
3253 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3254
3255 rc2 = vdThreadStartRead(pDisk);
3256 AssertRC(rc2);
3257 fLockRead = true;
3258
3259 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3260 AssertPtrBreakStmt(pImage, cbSize = 0);
3261 cbSize = pImage->Backend->pfnGetFileSize(pImage->pvBackendData);
3262 } while (0);
3263
3264 if (RT_UNLIKELY(fLockRead))
3265 {
3266 rc2 = vdThreadFinishRead(pDisk);
3267 AssertRC(rc2);
3268 }
3269
3270 LogFlowFunc(("returns %llu\n", cbSize));
3271 return cbSize;
3272}
3273
3274/**
3275 * Get virtual disk PCHS geometry stored in HDD container.
3276 *
3277 * @returns VBox status code.
3278 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3279 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
3280 * @param pDisk Pointer to HDD container.
3281 * @param nImage Image number, counts from 0. 0 is always base image of container.
3282 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
3283 */
3284VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
3285 PPDMMEDIAGEOMETRY pPCHSGeometry)
3286{
3287 int rc = VINF_SUCCESS;
3288 int rc2;
3289 bool fLockRead = false;
3290
3291 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
3292 pDisk, nImage, pPCHSGeometry));
3293 do
3294 {
3295 /* sanity check */
3296 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3297 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3298
3299 /* Check arguments. */
3300 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
3301 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
3302 rc = VERR_INVALID_PARAMETER);
3303
3304 rc2 = vdThreadStartRead(pDisk);
3305 AssertRC(rc2);
3306 fLockRead = true;
3307
3308 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3309 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3310
3311 if (pImage == pDisk->pLast)
3312 {
3313 /* Use cached information if possible. */
3314 if (pDisk->PCHSGeometry.cCylinders != 0)
3315 *pPCHSGeometry = pDisk->PCHSGeometry;
3316 else
3317 rc = VERR_VD_GEOMETRY_NOT_SET;
3318 }
3319 else
3320 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
3321 pPCHSGeometry);
3322 } while (0);
3323
3324 if (RT_UNLIKELY(fLockRead))
3325 {
3326 rc2 = vdThreadFinishRead(pDisk);
3327 AssertRC(rc2);
3328 }
3329
3330 LogFlowFunc(("%s: %Rrc (PCHS=%u/%u/%u)\n", __FUNCTION__, rc,
3331 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
3332 pDisk->PCHSGeometry.cSectors));
3333 return rc;
3334}
3335
3336/**
3337 * Store virtual disk PCHS geometry in HDD container.
3338 *
3339 * Note that in case of unrecoverable error all images in HDD container will be closed.
3340 *
3341 * @returns VBox status code.
3342 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3343 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
3344 * @param pDisk Pointer to HDD container.
3345 * @param nImage Image number, counts from 0. 0 is always base image of container.
3346 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
3347 */
3348VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
3349 PCPDMMEDIAGEOMETRY pPCHSGeometry)
3350{
3351 int rc = VINF_SUCCESS;
3352 int rc2;
3353 bool fLockWrite = false;
3354
3355 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
3356 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
3357 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
3358 do
3359 {
3360 /* sanity check */
3361 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3362 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3363
3364 /* Check arguments. */
3365 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
3366 && pPCHSGeometry->cHeads <= 16
3367 && pPCHSGeometry->cSectors <= 63,
3368 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
3369 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
3370 pPCHSGeometry->cSectors),
3371 rc = VERR_INVALID_PARAMETER);
3372
3373 rc2 = vdThreadStartWrite(pDisk);
3374 AssertRC(rc2);
3375 fLockWrite = true;
3376
3377 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3378 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3379
3380 if (pImage == pDisk->pLast)
3381 {
3382 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
3383 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
3384 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
3385 {
3386 /* Only update geometry if it is changed. Avoids similar checks
3387 * in every backend. Most of the time the new geometry is set
3388 * to the previous values, so no need to go through the hassle
3389 * of updating an image which could be opened in read-only mode
3390 * right now. */
3391 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
3392 pPCHSGeometry);
3393
3394 /* Cache new geometry values in any case. */
3395 int rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
3396 &pDisk->PCHSGeometry);
3397 if (RT_FAILURE(rc2))
3398 {
3399 pDisk->PCHSGeometry.cCylinders = 0;
3400 pDisk->PCHSGeometry.cHeads = 0;
3401 pDisk->PCHSGeometry.cSectors = 0;
3402 }
3403 else
3404 {
3405 /* Make sure the CHS geometry is properly clipped. */
3406 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
3407 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
3408 }
3409 }
3410 }
3411 else
3412 {
3413 PDMMEDIAGEOMETRY PCHS;
3414 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
3415 &PCHS);
3416 if ( RT_FAILURE(rc)
3417 || pPCHSGeometry->cCylinders != PCHS.cCylinders
3418 || pPCHSGeometry->cHeads != PCHS.cHeads
3419 || pPCHSGeometry->cSectors != PCHS.cSectors)
3420 {
3421 /* Only update geometry if it is changed. Avoids similar checks
3422 * in every backend. Most of the time the new geometry is set
3423 * to the previous values, so no need to go through the hassle
3424 * of updating an image which could be opened in read-only mode
3425 * right now. */
3426 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
3427 pPCHSGeometry);
3428 }
3429 }
3430 } while (0);
3431
3432 if (RT_UNLIKELY(fLockWrite))
3433 {
3434 rc2 = vdThreadFinishWrite(pDisk);
3435 AssertRC(rc2);
3436 }
3437
3438 LogFlowFunc(("returns %Rrc\n", rc));
3439 return rc;
3440}
3441
3442/**
3443 * Get virtual disk LCHS geometry stored in HDD container.
3444 *
3445 * @returns VBox status code.
3446 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3447 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
3448 * @param pDisk Pointer to HDD container.
3449 * @param nImage Image number, counts from 0. 0 is always base image of container.
3450 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
3451 */
3452VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
3453 PPDMMEDIAGEOMETRY pLCHSGeometry)
3454{
3455 int rc = VINF_SUCCESS;
3456 int rc2;
3457 bool fLockRead = false;
3458
3459 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
3460 pDisk, nImage, pLCHSGeometry));
3461 do
3462 {
3463 /* sanity check */
3464 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3465 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3466
3467 /* Check arguments. */
3468 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
3469 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
3470 rc = VERR_INVALID_PARAMETER);
3471
3472 rc2 = vdThreadStartRead(pDisk);
3473 AssertRC(rc2);
3474 fLockRead = true;
3475
3476 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3477 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3478
3479 if (pImage == pDisk->pLast)
3480 {
3481 /* Use cached information if possible. */
3482 if (pDisk->LCHSGeometry.cCylinders != 0)
3483 *pLCHSGeometry = pDisk->LCHSGeometry;
3484 else
3485 rc = VERR_VD_GEOMETRY_NOT_SET;
3486 }
3487 else
3488 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
3489 pLCHSGeometry);
3490 } while (0);
3491
3492 if (RT_UNLIKELY(fLockRead))
3493 {
3494 rc2 = vdThreadFinishRead(pDisk);
3495 AssertRC(rc2);
3496 }
3497
3498 LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
3499 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
3500 pDisk->LCHSGeometry.cSectors));
3501 return rc;
3502}
3503
3504/**
3505 * Store virtual disk LCHS geometry in HDD container.
3506 *
3507 * Note that in case of unrecoverable error all images in HDD container will be closed.
3508 *
3509 * @returns VBox status code.
3510 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3511 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
3512 * @param pDisk Pointer to HDD container.
3513 * @param nImage Image number, counts from 0. 0 is always base image of container.
3514 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
3515 */
3516VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
3517 PCPDMMEDIAGEOMETRY pLCHSGeometry)
3518{
3519 int rc = VINF_SUCCESS;
3520 int rc2;
3521 bool fLockWrite = false;
3522
3523 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
3524 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
3525 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
3526 do
3527 {
3528 /* sanity check */
3529 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3530 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3531
3532 /* Check arguments. */
3533 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
3534 && pLCHSGeometry->cHeads <= 255
3535 && pLCHSGeometry->cSectors <= 63,
3536 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
3537 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
3538 pLCHSGeometry->cSectors),
3539 rc = VERR_INVALID_PARAMETER);
3540
3541 rc2 = vdThreadStartWrite(pDisk);
3542 AssertRC(rc2);
3543 fLockWrite = true;
3544
3545 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3546 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3547
3548 if (pImage == pDisk->pLast)
3549 {
3550 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
3551 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
3552 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
3553 {
3554 /* Only update geometry if it is changed. Avoids similar checks
3555 * in every backend. Most of the time the new geometry is set
3556 * to the previous values, so no need to go through the hassle
3557 * of updating an image which could be opened in read-only mode
3558 * right now. */
3559 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
3560 pLCHSGeometry);
3561
3562 /* Cache new geometry values in any case. */
3563 int rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
3564 &pDisk->LCHSGeometry);
3565 if (RT_FAILURE(rc2))
3566 {
3567 pDisk->LCHSGeometry.cCylinders = 0;
3568 pDisk->LCHSGeometry.cHeads = 0;
3569 pDisk->LCHSGeometry.cSectors = 0;
3570 }
3571 else
3572 {
3573 /* Make sure the CHS geometry is properly clipped. */
3574 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
3575 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
3576 }
3577 }
3578 }
3579 else
3580 {
3581 PDMMEDIAGEOMETRY LCHS;
3582 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
3583 &LCHS);
3584 if ( RT_FAILURE(rc)
3585 || pLCHSGeometry->cCylinders != LCHS.cCylinders
3586 || pLCHSGeometry->cHeads != LCHS.cHeads
3587 || pLCHSGeometry->cSectors != LCHS.cSectors)
3588 {
3589 /* Only update geometry if it is changed. Avoids similar checks
3590 * in every backend. Most of the time the new geometry is set
3591 * to the previous values, so no need to go through the hassle
3592 * of updating an image which could be opened in read-only mode
3593 * right now. */
3594 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
3595 pLCHSGeometry);
3596 }
3597 }
3598 } while (0);
3599
3600 if (RT_UNLIKELY(fLockWrite))
3601 {
3602 rc2 = vdThreadFinishWrite(pDisk);
3603 AssertRC(rc2);
3604 }
3605
3606 LogFlowFunc(("returns %Rrc\n", rc));
3607 return rc;
3608}
3609
3610/**
3611 * Get version of image in HDD container.
3612 *
3613 * @returns VBox status code.
3614 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3615 * @param pDisk Pointer to HDD container.
3616 * @param nImage Image number, counts from 0. 0 is always base image of container.
3617 * @param puVersion Where to store the image version.
3618 */
3619VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
3620 unsigned *puVersion)
3621{
3622 int rc = VINF_SUCCESS;
3623 int rc2;
3624 bool fLockRead = false;
3625
3626 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
3627 pDisk, nImage, puVersion));
3628 do
3629 {
3630 /* sanity check */
3631 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3632 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3633
3634 /* Check arguments. */
3635 AssertMsgBreakStmt(VALID_PTR(puVersion),
3636 ("puVersion=%#p\n", puVersion),
3637 rc = VERR_INVALID_PARAMETER);
3638
3639 rc2 = vdThreadStartRead(pDisk);
3640 AssertRC(rc2);
3641 fLockRead = true;
3642
3643 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3644 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3645
3646 *puVersion = pImage->Backend->pfnGetVersion(pImage->pvBackendData);
3647 } while (0);
3648
3649 if (RT_UNLIKELY(fLockRead))
3650 {
3651 rc2 = vdThreadFinishRead(pDisk);
3652 AssertRC(rc2);
3653 }
3654
3655 LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
3656 return rc;
3657}
3658
3659/**
3660 * List the capabilities of image backend in HDD container.
3661 *
3662 * @returns VBox status code.
3663 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3664 * @param pDisk Pointer to the HDD container.
3665 * @param nImage Image number, counts from 0. 0 is always base image of container.
3666 * @param pbackendInfo Where to store the backend information.
3667 */
3668VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
3669 PVDBACKENDINFO pBackendInfo)
3670{
3671 int rc = VINF_SUCCESS;
3672 int rc2;
3673 bool fLockRead = false;
3674
3675 LogFlowFunc(("pDisk=%#p nImage=%u pBackendInfo=%#p\n",
3676 pDisk, nImage, pBackendInfo));
3677 do
3678 {
3679 /* sanity check */
3680 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3681 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3682
3683 /* Check arguments. */
3684 AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
3685 ("pBackendInfo=%#p\n", pBackendInfo),
3686 rc = VERR_INVALID_PARAMETER);
3687
3688 rc2 = vdThreadStartRead(pDisk);
3689 AssertRC(rc2);
3690 fLockRead = true;
3691
3692 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3693 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3694
3695 pBackendInfo->pszBackend = pImage->Backend->pszBackendName;
3696 pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
3697 pBackendInfo->papszFileExtensions = pImage->Backend->papszFileExtensions;
3698 pBackendInfo->paConfigInfo = pImage->Backend->paConfigInfo;
3699 } while (0);
3700
3701 if (RT_UNLIKELY(fLockRead))
3702 {
3703 rc2 = vdThreadFinishRead(pDisk);
3704 AssertRC(rc2);
3705 }
3706
3707 LogFlowFunc(("returns %Rrc\n", rc));
3708 return rc;
3709}
3710
3711/**
3712 * Get flags of image in HDD container.
3713 *
3714 * @returns VBox status code.
3715 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3716 * @param pDisk Pointer to HDD container.
3717 * @param nImage Image number, counts from 0. 0 is always base image of container.
3718 * @param puImageFlags Where to store the image flags.
3719 */
3720VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
3721 unsigned *puImageFlags)
3722{
3723 int rc = VINF_SUCCESS;
3724 int rc2;
3725 bool fLockRead = false;
3726
3727 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
3728 pDisk, nImage, puImageFlags));
3729 do
3730 {
3731 /* sanity check */
3732 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3733 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3734
3735 /* Check arguments. */
3736 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
3737 ("puImageFlags=%#p\n", puImageFlags),
3738 rc = VERR_INVALID_PARAMETER);
3739
3740 rc2 = vdThreadStartRead(pDisk);
3741 AssertRC(rc2);
3742 fLockRead = true;
3743
3744 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3745 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3746
3747 *puImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pvBackendData);
3748 } while (0);
3749
3750 if (RT_UNLIKELY(fLockRead))
3751 {
3752 rc2 = vdThreadFinishRead(pDisk);
3753 AssertRC(rc2);
3754 }
3755
3756 LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
3757 return rc;
3758}
3759
3760/**
3761 * Get open flags of image in HDD container.
3762 *
3763 * @returns VBox status code.
3764 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3765 * @param pDisk Pointer to HDD container.
3766 * @param nImage Image number, counts from 0. 0 is always base image of container.
3767 * @param puOpenFlags Where to store the image open flags.
3768 */
3769VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
3770 unsigned *puOpenFlags)
3771{
3772 int rc = VINF_SUCCESS;
3773 int rc2;
3774 bool fLockRead = false;
3775
3776 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
3777 pDisk, nImage, puOpenFlags));
3778 do
3779 {
3780 /* sanity check */
3781 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3782 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3783
3784 /* Check arguments. */
3785 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
3786 ("puOpenFlags=%#p\n", puOpenFlags),
3787 rc = VERR_INVALID_PARAMETER);
3788
3789 rc2 = vdThreadStartRead(pDisk);
3790 AssertRC(rc2);
3791 fLockRead = true;
3792
3793 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3794 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3795
3796 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pvBackendData);
3797 } while (0);
3798
3799 if (RT_UNLIKELY(fLockRead))
3800 {
3801 rc2 = vdThreadFinishRead(pDisk);
3802 AssertRC(rc2);
3803 }
3804
3805 LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
3806 return rc;
3807}
3808
3809/**
3810 * Set open flags of image in HDD container.
3811 * This operation may cause file locking changes and/or files being reopened.
3812 * Note that in case of unrecoverable error all images in HDD container will be closed.
3813 *
3814 * @returns VBox status code.
3815 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3816 * @param pDisk Pointer to HDD container.
3817 * @param nImage Image number, counts from 0. 0 is always base image of container.
3818 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
3819 */
3820VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
3821 unsigned uOpenFlags)
3822{
3823 int rc;
3824 int rc2;
3825 bool fLockWrite = false;
3826
3827 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
3828 do
3829 {
3830 /* sanity check */
3831 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3832 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3833
3834 /* Check arguments. */
3835 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
3836 ("uOpenFlags=%#x\n", uOpenFlags),
3837 rc = VERR_INVALID_PARAMETER);
3838
3839 rc2 = vdThreadStartWrite(pDisk);
3840 AssertRC(rc2);
3841 fLockWrite = true;
3842
3843 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3844 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3845
3846 rc = pImage->Backend->pfnSetOpenFlags(pImage->pvBackendData,
3847 uOpenFlags);
3848 } while (0);
3849
3850 if (RT_UNLIKELY(fLockWrite))
3851 {
3852 rc2 = vdThreadFinishWrite(pDisk);
3853 AssertRC(rc2);
3854 }
3855
3856 LogFlowFunc(("returns %Rrc\n", rc));
3857 return rc;
3858}
3859
3860/**
3861 * Get base filename of image in HDD container. Some image formats use
3862 * other filenames as well, so don't use this for anything but informational
3863 * purposes.
3864 *
3865 * @returns VBox status code.
3866 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3867 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
3868 * @param pDisk Pointer to HDD container.
3869 * @param nImage Image number, counts from 0. 0 is always base image of container.
3870 * @param pszFilename Where to store the image file name.
3871 * @param cbFilename Size of buffer pszFilename points to.
3872 */
3873VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
3874 char *pszFilename, unsigned cbFilename)
3875{
3876 int rc;
3877 int rc2;
3878 bool fLockRead = false;
3879
3880 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
3881 pDisk, nImage, pszFilename, cbFilename));
3882 do
3883 {
3884 /* sanity check */
3885 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3886 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3887
3888 /* Check arguments. */
3889 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
3890 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
3891 rc = VERR_INVALID_PARAMETER);
3892 AssertMsgBreakStmt(cbFilename,
3893 ("cbFilename=%u\n", cbFilename),
3894 rc = VERR_INVALID_PARAMETER);
3895
3896 rc2 = vdThreadStartRead(pDisk);
3897 AssertRC(rc2);
3898 fLockRead = true;
3899
3900 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3901 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3902
3903 size_t cb = strlen(pImage->pszFilename);
3904 if (cb <= cbFilename)
3905 {
3906 strcpy(pszFilename, pImage->pszFilename);
3907 rc = VINF_SUCCESS;
3908 }
3909 else
3910 {
3911 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
3912 pszFilename[cbFilename - 1] = '\0';
3913 rc = VERR_BUFFER_OVERFLOW;
3914 }
3915 } while (0);
3916
3917 if (RT_UNLIKELY(fLockRead))
3918 {
3919 rc2 = vdThreadFinishRead(pDisk);
3920 AssertRC(rc2);
3921 }
3922
3923 LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
3924 return rc;
3925}
3926
3927/**
3928 * Get the comment line of image in HDD container.
3929 *
3930 * @returns VBox status code.
3931 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3932 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
3933 * @param pDisk Pointer to HDD container.
3934 * @param nImage Image number, counts from 0. 0 is always base image of container.
3935 * @param pszComment Where to store the comment string of image. NULL is ok.
3936 * @param cbComment The size of pszComment buffer. 0 is ok.
3937 */
3938VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
3939 char *pszComment, unsigned cbComment)
3940{
3941 int rc;
3942 int rc2;
3943 bool fLockRead = false;
3944
3945 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
3946 pDisk, nImage, pszComment, cbComment));
3947 do
3948 {
3949 /* sanity check */
3950 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
3951 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3952
3953 /* Check arguments. */
3954 AssertMsgBreakStmt(VALID_PTR(pszComment),
3955 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
3956 rc = VERR_INVALID_PARAMETER);
3957 AssertMsgBreakStmt(cbComment,
3958 ("cbComment=%u\n", cbComment),
3959 rc = VERR_INVALID_PARAMETER);
3960
3961 rc2 = vdThreadStartRead(pDisk);
3962 AssertRC(rc2);
3963 fLockRead = true;
3964
3965 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
3966 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
3967
3968 rc = pImage->Backend->pfnGetComment(pImage->pvBackendData, pszComment,
3969 cbComment);
3970 } while (0);
3971
3972 if (RT_UNLIKELY(fLockRead))
3973 {
3974 rc2 = vdThreadFinishRead(pDisk);
3975 AssertRC(rc2);
3976 }
3977
3978 LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
3979 return rc;
3980}
3981
3982/**
3983 * Changes the comment line of image in HDD container.
3984 *
3985 * @returns VBox status code.
3986 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
3987 * @param pDisk Pointer to HDD container.
3988 * @param nImage Image number, counts from 0. 0 is always base image of container.
3989 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
3990 */
3991VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
3992 const char *pszComment)
3993{
3994 int rc;
3995 int rc2;
3996 bool fLockWrite = false;
3997
3998 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
3999 pDisk, nImage, pszComment, pszComment));
4000 do
4001 {
4002 /* sanity check */
4003 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4004 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4005
4006 /* Check arguments. */
4007 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
4008 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
4009 rc = VERR_INVALID_PARAMETER);
4010
4011 rc2 = vdThreadStartWrite(pDisk);
4012 AssertRC(rc2);
4013 fLockWrite = true;
4014
4015 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
4016 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
4017
4018 rc = pImage->Backend->pfnSetComment(pImage->pvBackendData, pszComment);
4019 } while (0);
4020
4021 if (RT_UNLIKELY(fLockWrite))
4022 {
4023 rc2 = vdThreadFinishWrite(pDisk);
4024 AssertRC(rc2);
4025 }
4026
4027 LogFlowFunc(("returns %Rrc\n", rc));
4028 return rc;
4029}
4030
4031
4032/**
4033 * Get UUID of image in HDD container.
4034 *
4035 * @returns VBox status code.
4036 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
4037 * @param pDisk Pointer to HDD container.
4038 * @param nImage Image number, counts from 0. 0 is always base image of container.
4039 * @param pUuid Where to store the image creation UUID.
4040 */
4041VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
4042{
4043 int rc;
4044 int rc2;
4045 bool fLockRead = false;
4046
4047 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
4048 do
4049 {
4050 /* sanity check */
4051 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4052 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4053
4054 /* Check arguments. */
4055 AssertMsgBreakStmt(VALID_PTR(pUuid),
4056 ("pUuid=%#p\n", pUuid),
4057 rc = VERR_INVALID_PARAMETER);
4058
4059 rc2 = vdThreadStartRead(pDisk);
4060 AssertRC(rc2);
4061 fLockRead = true;
4062
4063 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
4064 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
4065
4066 rc = pImage->Backend->pfnGetUuid(pImage->pvBackendData, pUuid);
4067 } while (0);
4068
4069 if (RT_UNLIKELY(fLockRead))
4070 {
4071 rc2 = vdThreadFinishRead(pDisk);
4072 AssertRC(rc2);
4073 }
4074
4075 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
4076 return rc;
4077}
4078
4079/**
4080 * Set the image's UUID. Should not be used by normal applications.
4081 *
4082 * @returns VBox status code.
4083 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
4084 * @param pDisk Pointer to HDD container.
4085 * @param nImage Image number, counts from 0. 0 is always base image of container.
4086 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
4087 */
4088VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
4089{
4090 int rc;
4091 int rc2;
4092 bool fLockWrite = false;
4093
4094 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
4095 pDisk, nImage, pUuid, pUuid));
4096 do
4097 {
4098 /* sanity check */
4099 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4100 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4101
4102 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
4103 ("pUuid=%#p\n", pUuid),
4104 rc = VERR_INVALID_PARAMETER);
4105
4106 rc2 = vdThreadStartWrite(pDisk);
4107 AssertRC(rc2);
4108 fLockWrite = true;
4109
4110 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
4111 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
4112
4113 RTUUID Uuid;
4114 if (!pUuid)
4115 {
4116 RTUuidCreate(&Uuid);
4117 pUuid = &Uuid;
4118 }
4119 rc = pImage->Backend->pfnSetUuid(pImage->pvBackendData, pUuid);
4120 } while (0);
4121
4122 if (RT_UNLIKELY(fLockWrite))
4123 {
4124 rc2 = vdThreadFinishWrite(pDisk);
4125 AssertRC(rc2);
4126 }
4127
4128 LogFlowFunc(("returns %Rrc\n", rc));
4129 return rc;
4130}
4131
4132/**
4133 * Get last modification UUID of image in HDD container.
4134 *
4135 * @returns VBox status code.
4136 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
4137 * @param pDisk Pointer to HDD container.
4138 * @param nImage Image number, counts from 0. 0 is always base image of container.
4139 * @param pUuid Where to store the image modification UUID.
4140 */
4141VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
4142{
4143 int rc = VINF_SUCCESS;
4144 int rc2;
4145 bool fLockRead = false;
4146
4147 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
4148 do
4149 {
4150 /* sanity check */
4151 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4152 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4153
4154 /* Check arguments. */
4155 AssertMsgBreakStmt(VALID_PTR(pUuid),
4156 ("pUuid=%#p\n", pUuid),
4157 rc = VERR_INVALID_PARAMETER);
4158
4159 rc2 = vdThreadStartRead(pDisk);
4160 AssertRC(rc2);
4161 fLockRead = true;
4162
4163 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
4164 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
4165
4166 rc = pImage->Backend->pfnGetModificationUuid(pImage->pvBackendData,
4167 pUuid);
4168 } while (0);
4169
4170 if (RT_UNLIKELY(fLockRead))
4171 {
4172 rc2 = vdThreadFinishRead(pDisk);
4173 AssertRC(rc2);
4174 }
4175
4176 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
4177 return rc;
4178}
4179
4180/**
4181 * Set the image's last modification UUID. Should not be used by normal applications.
4182 *
4183 * @returns VBox status code.
4184 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
4185 * @param pDisk Pointer to HDD container.
4186 * @param nImage Image number, counts from 0. 0 is always base image of container.
4187 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
4188 */
4189VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
4190{
4191 int rc;
4192 int rc2;
4193 bool fLockWrite = false;
4194
4195 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
4196 pDisk, nImage, pUuid, pUuid));
4197 do
4198 {
4199 /* sanity check */
4200 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4201 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4202
4203 /* Check arguments. */
4204 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
4205 ("pUuid=%#p\n", pUuid),
4206 rc = VERR_INVALID_PARAMETER);
4207
4208 rc2 = vdThreadStartWrite(pDisk);
4209 AssertRC(rc2);
4210 fLockWrite = true;
4211
4212 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
4213 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
4214
4215 RTUUID Uuid;
4216 if (!pUuid)
4217 {
4218 RTUuidCreate(&Uuid);
4219 pUuid = &Uuid;
4220 }
4221 rc = pImage->Backend->pfnSetModificationUuid(pImage->pvBackendData,
4222 pUuid);
4223 } while (0);
4224
4225 if (RT_UNLIKELY(fLockWrite))
4226 {
4227 rc2 = vdThreadFinishWrite(pDisk);
4228 AssertRC(rc2);
4229 }
4230
4231 LogFlowFunc(("returns %Rrc\n", rc));
4232 return rc;
4233}
4234
4235/**
4236 * Get parent UUID of image in HDD container.
4237 *
4238 * @returns VBox status code.
4239 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
4240 * @param pDisk Pointer to HDD container.
4241 * @param nImage Image number, counts from 0. 0 is always base image of container.
4242 * @param pUuid Where to store the parent image UUID.
4243 */
4244VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
4245 PRTUUID pUuid)
4246{
4247 int rc = VINF_SUCCESS;
4248 int rc2;
4249 bool fLockRead = false;
4250
4251 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
4252 do
4253 {
4254 /* sanity check */
4255 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4256 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4257
4258 /* Check arguments. */
4259 AssertMsgBreakStmt(VALID_PTR(pUuid),
4260 ("pUuid=%#p\n", pUuid),
4261 rc = VERR_INVALID_PARAMETER);
4262
4263 rc2 = vdThreadStartRead(pDisk);
4264 AssertRC(rc2);
4265 fLockRead = true;
4266
4267 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
4268 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
4269
4270 rc = pImage->Backend->pfnGetParentUuid(pImage->pvBackendData, pUuid);
4271 } while (0);
4272
4273 if (RT_UNLIKELY(fLockRead))
4274 {
4275 rc2 = vdThreadFinishRead(pDisk);
4276 AssertRC(rc2);
4277 }
4278
4279 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
4280 return rc;
4281}
4282
4283/**
4284 * Set the image's parent UUID. Should not be used by normal applications.
4285 *
4286 * @returns VBox status code.
4287 * @param pDisk Pointer to HDD container.
4288 * @param nImage Image number, counts from 0. 0 is always base image of container.
4289 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
4290 */
4291VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
4292 PCRTUUID pUuid)
4293{
4294 int rc;
4295 int rc2;
4296 bool fLockWrite = false;
4297
4298 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
4299 pDisk, nImage, pUuid, pUuid));
4300 do
4301 {
4302 /* sanity check */
4303 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4304 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4305
4306 /* Check arguments. */
4307 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
4308 ("pUuid=%#p\n", pUuid),
4309 rc = VERR_INVALID_PARAMETER);
4310
4311 rc2 = vdThreadStartWrite(pDisk);
4312 AssertRC(rc2);
4313 fLockWrite = true;
4314
4315 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
4316 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
4317
4318 RTUUID Uuid;
4319 if (!pUuid)
4320 {
4321 RTUuidCreate(&Uuid);
4322 pUuid = &Uuid;
4323 }
4324 rc = pImage->Backend->pfnSetParentUuid(pImage->pvBackendData, pUuid);
4325 } while (0);
4326
4327 if (RT_UNLIKELY(fLockWrite))
4328 {
4329 rc2 = vdThreadFinishWrite(pDisk);
4330 AssertRC(rc2);
4331 }
4332
4333 LogFlowFunc(("returns %Rrc\n", rc));
4334 return rc;
4335}
4336
4337
4338/**
4339 * Debug helper - dumps all opened images in HDD container into the log file.
4340 *
4341 * @param pDisk Pointer to HDD container.
4342 */
4343VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
4344{
4345 int rc2;
4346 bool fLockRead = false;
4347
4348 do
4349 {
4350 /* sanity check */
4351 AssertPtrBreak(pDisk);
4352 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4353
4354 int (*pfnMessage)(void *, const char *, ...) = NULL;
4355 void *pvUser = pDisk->pInterfaceError->pvUser;
4356
4357 if (pDisk->pInterfaceErrorCallbacks && VALID_PTR(pDisk->pInterfaceErrorCallbacks->pfnMessage))
4358 pfnMessage = pDisk->pInterfaceErrorCallbacks->pfnMessage;
4359 else
4360 {
4361 pDisk->pInterfaceErrorCallbacks->pfnMessage = vdLogMessage;
4362 pfnMessage = vdLogMessage;
4363 }
4364
4365 rc2 = vdThreadStartRead(pDisk);
4366 AssertRC(rc2);
4367 fLockRead = true;
4368
4369 pfnMessage(pvUser, "--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
4370 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
4371 {
4372 pfnMessage(pvUser, "Dumping VD image \"%s\" (Backend=%s)\n",
4373 pImage->pszFilename, pImage->Backend->pszBackendName);
4374 pImage->Backend->pfnDump(pImage->pvBackendData);
4375 }
4376 } while (0);
4377
4378 if (RT_UNLIKELY(fLockRead))
4379 {
4380 rc2 = vdThreadFinishRead(pDisk);
4381 AssertRC(rc2);
4382 }
4383}
4384
4385/**
4386 * Query if asynchronous operations are supported for this disk.
4387 *
4388 * @returns VBox status code.
4389 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
4390 * @param pDisk Pointer to the HDD container.
4391 * @param nImage Image number, counts from 0. 0 is always base image of container.
4392 * @param pfAIOSupported Where to store if async IO is supported.
4393 */
4394VBOXDDU_DECL(int) VDImageIsAsyncIOSupported(PVBOXHDD pDisk, unsigned nImage, bool *pfAIOSupported)
4395{
4396 int rc = VINF_SUCCESS;
4397 int rc2;
4398 bool fLockRead = false;
4399
4400 LogFlowFunc(("pDisk=%#p nImage=%u pfAIOSupported=%#p\n", pDisk, nImage, pfAIOSupported));
4401 do
4402 {
4403 /* sanity check */
4404 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4405 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4406
4407 /* Check arguments. */
4408 AssertMsgBreakStmt(VALID_PTR(pfAIOSupported),
4409 ("pfAIOSupported=%#p\n", pfAIOSupported),
4410 rc = VERR_INVALID_PARAMETER);
4411
4412 rc2 = vdThreadStartRead(pDisk);
4413 AssertRC(rc2);
4414 fLockRead = true;
4415
4416 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
4417 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
4418
4419 if (pImage->Backend->uBackendCaps & VD_CAP_ASYNC)
4420 *pfAIOSupported = pImage->Backend->pfnIsAsyncIOSupported(pImage->pvBackendData);
4421 else
4422 *pfAIOSupported = false;
4423 } while (0);
4424
4425 if (RT_UNLIKELY(fLockRead))
4426 {
4427 rc2 = vdThreadFinishRead(pDisk);
4428 AssertRC(rc2);
4429 }
4430
4431 LogFlowFunc(("returns %Rrc, fAIOSupported=%u\n", rc, *pfAIOSupported));
4432 return rc;
4433}
4434
4435/**
4436 * Start a asynchronous read request.
4437 *
4438 * @returns VBox status code.
4439 * @param pDisk Pointer to the HDD container.
4440 * @param uOffset The offset of the virtual disk to read from.
4441 * @param cbRead How many bytes to read.
4442 * @param paSeg Pointer to an array of segments.
4443 * @param cSeg Number of segments in the array.
4444 * @param pvUser User data which is passed on completion
4445 */
4446VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
4447 PPDMDATASEG paSeg, unsigned cSeg,
4448 void *pvUser)
4449{
4450 int rc = VERR_VD_BLOCK_FREE;
4451 int rc2;
4452 bool fLockWrite = false;
4453
4454 LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbRead=%zu\n",
4455 pDisk, uOffset, paSeg, cSeg, cbRead));
4456 do
4457 {
4458 /* sanity check */
4459 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4460 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4461
4462 /* Check arguments. */
4463 AssertMsgBreakStmt(cbRead,
4464 ("cbRead=%zu\n", cbRead),
4465 rc = VERR_INVALID_PARAMETER);
4466 AssertMsgBreakStmt(VALID_PTR(paSeg),
4467 ("paSeg=%#p\n", paSeg),
4468 rc = VERR_INVALID_PARAMETER);
4469 AssertMsgBreakStmt(cSeg,
4470 ("cSeg=%zu\n", cSeg),
4471 rc = VERR_INVALID_PARAMETER);
4472
4473 /** @todo handle this just like a write, as in the completion code in
4474 * DrvVD.cpp there is no way to figure out if it is a read or write. */
4475 rc2 = vdThreadStartWrite(pDisk);
4476 AssertRC(rc2);
4477 fLockWrite = true;
4478
4479 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
4480 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
4481 uOffset, cbRead, pDisk->cbSize),
4482 rc = VERR_INVALID_PARAMETER);
4483
4484 PVDIMAGE pImage = pDisk->pLast;
4485 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
4486
4487 /* @todo: This does not work for images which do not have all meta data in memory. */
4488 for (PVDIMAGE pCurrImage = pImage;
4489 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
4490 pCurrImage = pCurrImage->pPrev)
4491 {
4492 rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pvBackendData,
4493 uOffset, cbRead, paSeg, cSeg,
4494 pvUser);
4495 }
4496
4497 /* No image in the chain contains the data for the block. */
4498 if (rc == VERR_VD_BLOCK_FREE)
4499 {
4500 for (unsigned i = 0; i < cSeg && (cbRead > 0); i++)
4501 {
4502 memset(paSeg[i].pvSeg, '\0', paSeg[i].cbSeg);
4503 cbRead -= paSeg[i].cbSeg;
4504 }
4505 /* Request finished without the need to enqueue a async I/O request. Tell caller. */
4506 rc = VINF_VD_ASYNC_IO_FINISHED;
4507
4508 rc2 = vdThreadFinishWrite(pDisk);
4509 AssertRC(rc2);
4510 fLockWrite = false;
4511 }
4512
4513 } while (0);
4514
4515 if (RT_UNLIKELY(fLockWrite) && RT_FAILURE(rc))
4516 {
4517 rc2 = vdThreadFinishWrite(pDisk);
4518 AssertRC(rc2);
4519 }
4520
4521 LogFlowFunc(("returns %Rrc\n", rc));
4522 return rc;
4523}
4524
4525
4526/**
4527 * Start a asynchronous write request.
4528 *
4529 * @returns VBox status code.
4530 * @param pDisk Pointer to the HDD container.
4531 * @param uOffset The offset of the virtual disk to write to.
4532 * @param cbWrtie How many bytes to write.
4533 * @param paSeg Pointer to an array of segments.
4534 * @param cSeg Number of segments in the array.
4535 * @param pvUser User data which is passed on completion.
4536 */
4537VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
4538 PPDMDATASEG paSeg, unsigned cSeg,
4539 void *pvUser)
4540{
4541 int rc;
4542 int rc2;
4543 bool fLockWrite = false;
4544
4545 LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbWrite=%zu\n",
4546 pDisk, uOffset, paSeg, cSeg, cbWrite));
4547 do
4548 {
4549 /* sanity check */
4550 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4551 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4552
4553 /* Check arguments. */
4554 AssertMsgBreakStmt(cbWrite,
4555 ("cbWrite=%zu\n", cbWrite),
4556 rc = VERR_INVALID_PARAMETER);
4557 AssertMsgBreakStmt(VALID_PTR(paSeg),
4558 ("paSeg=%#p\n", paSeg),
4559 rc = VERR_INVALID_PARAMETER);
4560 AssertMsgBreakStmt(cSeg,
4561 ("cSeg=%zu\n", cSeg),
4562 rc = VERR_INVALID_PARAMETER);
4563
4564 rc2 = vdThreadStartWrite(pDisk);
4565 AssertRC(rc2);
4566 fLockWrite = true;
4567
4568 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
4569 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
4570 uOffset, cbWrite, pDisk->cbSize),
4571 rc = VERR_INVALID_PARAMETER);
4572
4573 PVDIMAGE pImage = pDisk->pLast;
4574 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
4575
4576 vdSetModifiedFlag(pDisk);
4577 rc = pImage->Backend->pfnAsyncWrite(pImage->pvBackendData,
4578 uOffset, cbWrite,
4579 paSeg, cSeg, pvUser);
4580 } while (0);
4581
4582 if (RT_UNLIKELY(fLockWrite) && RT_FAILURE(rc))
4583 {
4584 rc2 = vdThreadFinishWrite(pDisk);
4585 AssertRC(rc2);
4586 }
4587 LogFlowFunc(("returns %Rrc\n", rc));
4588 return rc;
4589
4590}
4591
4592#if 0
4593/** @copydoc VBOXHDDBACKEND::pfnComposeLocation */
4594int genericFileComposeLocation(PVDINTERFACE pConfig, char **pszLocation)
4595{
4596 return NULL;
4597}
4598
4599
4600/** @copydoc VBOXHDDBACKEND::pfnComposeName */
4601int genericFileComposeName(PVDINTERFACE pConfig, char **pszName)
4602{
4603 return NULL;
4604}
4605#endif
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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