VirtualBox

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

Last change on this file since 33000 was 32858, checked in by vboxsync, 14 years ago

Devices: pass down the destination interface pointer

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 283.5 KB
RevLine 
[12639]1/* $Id: VBoxHDD.cpp 32858 2010-10-01 08:49:05Z vboxsync $ */
[2379]2/** @file
[12639]3 * VBoxHDD - VBox HDD Container implementation.
[2379]4 */
5
6/*
[28800]7 * Copyright (C) 2006-2010 Oracle Corporation
[2379]8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
[5999]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.
[2379]16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_VD
[16873]22#include <VBox/VBoxHDD.h>
[2379]23#include <VBox/err.h>
[12423]24#include <VBox/sup.h>
25#include <VBox/log.h>
[2379]26
27#include <iprt/alloc.h>
28#include <iprt/assert.h>
29#include <iprt/uuid.h>
30#include <iprt/file.h>
31#include <iprt/string.h>
32#include <iprt/asm.h>
[4522]33#include <iprt/ldr.h>
[5101]34#include <iprt/dir.h>
35#include <iprt/path.h>
[6291]36#include <iprt/param.h>
[27808]37#include <iprt/memcache.h>
[28065]38#include <iprt/sg.h>
[28620]39#include <iprt/critsect.h>
40#include <iprt/list.h>
[30555]41#include <iprt/avl.h>
[2379]42
[23223]43#include <VBox/VBoxHDD-Plugin.h>
[32370]44#include <VBox/VBoxHDD-CachePlugin.h>
[2379]45
[26987]46#define VBOXHDDDISK_SIGNATURE 0x6f0e2a7d
47
48/** Buffer size used for merging images. */
49#define VD_MERGE_BUFFER_SIZE (16 * _1M)
50
[27977]51/** Maximum number of segments in one I/O task. */
52#define VD_IO_TASK_SEGMENTS_MAX 64
53
[26987]54/**
55 * VD async I/O interface storage descriptor.
56 */
[32553]57typedef struct VDIIOFALLBACKSTORAGE
[26987]58{
59 /** File handle. */
60 RTFILE File;
61 /** Completion callback. */
62 PFNVDCOMPLETED pfnCompleted;
63 /** Thread for async access. */
64 RTTHREAD ThreadAsync;
[32553]65} VDIIOFALLBACKSTORAGE, *PVDIIOFALLBACKSTORAGE;
[26987]66
67/**
68 * VBox HDD Container image descriptor.
69 */
70typedef struct VDIMAGE
71{
72 /** Link to parent image descriptor, if any. */
73 struct VDIMAGE *pPrev;
74 /** Link to child image descriptor, if any. */
75 struct VDIMAGE *pNext;
76 /** Container base filename. (UTF-8) */
77 char *pszFilename;
78 /** Data managed by the backend which keeps the actual info. */
[32536]79 void *pBackendData;
[26987]80 /** Cached sanitized image flags. */
81 unsigned uImageFlags;
82 /** Image open flags (only those handled generically in this code and which
83 * the backends will never ever see). */
84 unsigned uOpenFlags;
85
86 /** Function pointers for the various backend methods. */
87 PCVBOXHDDBACKEND Backend;
[32691]88
89 /** Pointer to list of VD interfaces, per-image. */
90 PVDINTERFACE pVDIfsImage;
91
92 /** I/O interface to the upper layer. */
93 PVDINTERFACE pInterfaceIO;
94 /** I/O interface callback table. */
95 PVDINTERFACEIO pInterfaceIOCallbacks;
96
[32553]97 /** Per image internal I/O interface. */
[32691]98 VDINTERFACE VDIIOInt;
99
100 /** Fallback I/O interface, only used if the caller doesn't provide it. */
[28620]101 VDINTERFACE VDIIO;
[32691]102
[28620]103 /** Disk this image is part of */
104 PVBOXHDD pDisk;
[26987]105} VDIMAGE, *PVDIMAGE;
106
107/**
108 * uModified bit flags.
109 */
110#define VD_IMAGE_MODIFIED_FLAG RT_BIT(0)
111#define VD_IMAGE_MODIFIED_FIRST RT_BIT(1)
112#define VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE RT_BIT(2)
113
114
115/**
[32370]116 * VBox HDD Cache image descriptor.
117 */
118typedef struct VDCACHE
119{
120 /** Cache base filename. (UTF-8) */
121 char *pszFilename;
122 /** Data managed by the backend which keeps the actual info. */
[32536]123 void *pBackendData;
[32370]124 /** Cached sanitized image flags. */
125 unsigned uImageFlags;
126 /** Image open flags (only those handled generically in this code and which
127 * the backends will never ever see). */
128 unsigned uOpenFlags;
129
130 /** Function pointers for the various backend methods. */
131 PCVDCACHEBACKEND Backend;
[32691]132
133 /** Pointer to list of VD interfaces, per-cache. */
134 PVDINTERFACE pVDIfsCache;
135
136 /** I/O interface to the upper layer. */
137 PVDINTERFACE pInterfaceIO;
138 /** I/O interface callback table. */
139 PVDINTERFACEIO pInterfaceIOCallbacks;
140
[32553]141 /** Per image internal I/O interface. */
[32691]142 VDINTERFACE VDIIOInt;
143
144 /** Fallback I/O interface, only used if the caller doesn't provide it. */
[32370]145 VDINTERFACE VDIIO;
[32691]146
[32370]147 /** Disk this image is part of */
148 PVBOXHDD pDisk;
149} VDCACHE, *PVDCACHE;
150
151/**
[26987]152 * VBox HDD Container main structure, private part.
153 */
154struct VBOXHDD
155{
156 /** Structure signature (VBOXHDDDISK_SIGNATURE). */
157 uint32_t u32Signature;
158
159 /** Number of opened images. */
160 unsigned cImages;
161
162 /** Base image. */
163 PVDIMAGE pBase;
164
165 /** Last opened image in the chain.
166 * The same as pBase if only one image is used. */
167 PVDIMAGE pLast;
168
169 /** Flags representing the modification state. */
170 unsigned uModified;
171
172 /** Cached size of this disk. */
173 uint64_t cbSize;
174 /** Cached PCHS geometry for this disk. */
[32536]175 VDGEOMETRY PCHSGeometry;
[26987]176 /** Cached LCHS geometry for this disk. */
[32536]177 VDGEOMETRY LCHSGeometry;
[26987]178
179 /** Pointer to list of VD interfaces, per-disk. */
180 PVDINTERFACE pVDIfsDisk;
181 /** Pointer to the common interface structure for error reporting. */
182 PVDINTERFACE pInterfaceError;
[27232]183 /** Pointer to the error interface callbacks we use if available. */
[26987]184 PVDINTERFACEERROR pInterfaceErrorCallbacks;
185
[27232]186 /** Pointer to the optional thread synchronization interface. */
187 PVDINTERFACE pInterfaceThreadSync;
188 /** Pointer to the optional thread synchronization callbacks. */
189 PVDINTERFACETHREADSYNC pInterfaceThreadSyncCallbacks;
190
[32553]191 /** Internal I/O interface callback table for the images. */
192 VDINTERFACEIOINT VDIIOIntCallbacks;
[27808]193
[32553]194 /** Callback table for the fallback I/O interface. */
195 VDINTERFACEIO VDIIOCallbacks;
[27808]196
197 /** Memory cache for I/O contexts */
198 RTMEMCACHE hMemCacheIoCtx;
[27977]199 /** Memory cache for I/O tasks. */
200 RTMEMCACHE hMemCacheIoTask;
[28620]201 /** Critical section protecting the disk against concurrent access. */
202 RTCRITSECT CritSect;
203 /** Flag whether the last image is currently written to and needs to grow.
204 * Other write requests which will grow the image too need to be deferred to
205 * prevent data corruption. - Protected by the critical section.
206 */
[28683]207 volatile bool fGrowing;
[28620]208 /** List of waiting requests. - Protected by the critical section. */
209 RTLISTNODE ListWriteGrowing;
[32370]210
211 /** Pointer to the L2 disk cache if any. */
212 PVDCACHE pCache;
[26987]213};
214
[30555]215# define VD_THREAD_IS_CRITSECT_OWNER(Disk) \
216 do \
217 { \
218 AssertMsg(RTCritSectIsOwner(&Disk->CritSect), \
219 ("Thread does not own critical section\n"));\
220 } while(0)
[26987]221
222/**
223 * VBox parent read descriptor, used internally for compaction.
224 */
225typedef struct VDPARENTSTATEDESC
226{
227 /** Pointer to disk descriptor. */
228 PVBOXHDD pDisk;
229 /** Pointer to image descriptor. */
230 PVDIMAGE pImage;
231} VDPARENTSTATEDESC, *PVDPARENTSTATEDESC;
232
[27808]233/**
234 * Transfer direction.
235 */
236typedef enum VDIOCTXTXDIR
237{
238 /** Read */
239 VDIOCTXTXDIR_READ = 0,
240 /** Write */
241 VDIOCTXTXDIR_WRITE,
242 /** Flush */
243 VDIOCTXTXDIR_FLUSH,
244 /** 32bit hack */
245 VDIOCTXTXDIR_32BIT_HACK = 0x7fffffff
246} VDIOCTXTXDIR, *PVDIOCTXTXDIR;
[26987]247
[28620]248/** Transfer function */
249typedef DECLCALLBACK(int) FNVDIOCTXTRANSFER (PVDIOCTX pIoCtx);
250/** Pointer to a transfer function. */
251typedef FNVDIOCTXTRANSFER *PFNVDIOCTXTRANSFER;
252
[27808]253/**
254 * I/O context
255 */
256typedef struct VDIOCTX
257{
[27977]258 /** Disk this is request is for. */
259 PVBOXHDD pDisk;
260 /** Return code. */
261 int rcReq;
[27808]262 /** Transfer direction */
263 VDIOCTXTXDIR enmTxDir;
264 /** Number of bytes left until this context completes. */
265 volatile uint32_t cbTransferLeft;
266 /** Current offset */
267 volatile uint64_t uOffset;
[28620]268 /** Number of bytes to transfer */
269 volatile size_t cbTransfer;
270 /** Current image in the chain. */
271 PVDIMAGE pImage;
[28065]272 /** S/G buffer */
273 RTSGBUF SgBuf;
[28683]274 /** Flag whether the I/O context is blocked because it is in the growing list. */
275 bool fBlocked;
[29240]276 /** Number of data transfers currently pending. */
277 volatile uint32_t cDataTransfersPending;
[27977]278 /** How many meta data transfers are pending. */
279 volatile uint32_t cMetaTransfersPending;
[28065]280 /** Flag whether the request finished */
281 volatile bool fComplete;
[28154]282 /** Temporary allocated memory which is freed
283 * when the context completes. */
284 void *pvAllocation;
[28620]285 /** Transfer function. */
286 PFNVDIOCTXTRANSFER pfnIoCtxTransfer;
287 /** Next transfer part after the current one completed. */
288 PFNVDIOCTXTRANSFER pfnIoCtxTransferNext;
[28065]289 /** Parent I/O context if any. Sets the type of the context (root/child) */
290 PVDIOCTX pIoCtxParent;
291 /** Type dependent data (root/child) */
292 union
293 {
294 /** Root data */
295 struct
296 {
297 /** Completion callback */
298 PFNVDASYNCTRANSFERCOMPLETE pfnComplete;
299 /** User argument 1 passed on completion. */
300 void *pvUser1;
301 /** User argument 1 passed on completion. */
302 void *pvUser2;
303 } Root;
304 /** Child data */
305 struct
306 {
307 /** Saved start offset */
308 uint64_t uOffsetSaved;
309 /** Saved transfer size */
310 size_t cbTransferLeftSaved;
311 /** Number of bytes transfered from the parent if this context completes. */
312 size_t cbTransferParent;
[28620]313 /** Number of bytes to pre read */
314 size_t cbPreRead;
315 /** Number of bytes to post read. */
316 size_t cbPostRead;
[30044]317 /** Number of bytes to write left in the parent. */
318 size_t cbWriteParent;
[28620]319 /** Write type dependent data. */
320 union
321 {
322 /** Optimized */
323 struct
324 {
325 /** Bytes to fill to satisfy the block size. Not part of the virtual disk. */
326 size_t cbFill;
327 /** Bytes to copy instead of reading from the parent */
328 size_t cbWriteCopy;
329 /** Bytes to read from the image. */
330 size_t cbReadImage;
331 } Optimized;
332 } Write;
[28065]333 } Child;
334 } Type;
[27808]335} VDIOCTX;
336
[30555]337typedef struct VDIOCTXDEFERRED
338{
339 /** Node in the list of deferred requests.
340 * A request can be deferred if the image is growing
341 * and the request accesses the same range or if
342 * the backend needs to read or write metadata from the disk
343 * before it can continue. */
344 RTLISTNODE NodeDeferred;
345 /** I/O context this entry points to. */
346 PVDIOCTX pIoCtx;
347} VDIOCTXDEFERRED, *PVDIOCTXDEFERRED;
348
[27808]349/**
[27977]350 * I/O task.
351 */
352typedef struct VDIOTASK
353{
[30555]354 /** Storage this task belongs to. */
355 PVDIOSTORAGE pIoStorage;
356 /** Optional completion callback. */
357 PFNVDXFERCOMPLETED pfnComplete;
358 /** Opaque user data. */
359 void *pvUser;
[27977]360 /** Flag whether this is a meta data transfer. */
361 bool fMeta;
362 /** Type dependent data. */
363 union
364 {
365 /** User data transfer. */
366 struct
367 {
368 /** Number of bytes this task transfered. */
369 uint32_t cbTransfer;
[30555]370 /** Pointer to the I/O context the task belongs. */
371 PVDIOCTX pIoCtx;
[27977]372 } User;
373 /** Meta data transfer. */
374 struct
375 {
[30555]376 /** Meta transfer this task is for. */
377 PVDMETAXFER pMetaXfer;
[27977]378 } Meta;
379 } Type;
380} VDIOTASK, *PVDIOTASK;
381
382/**
[27808]383 * Storage handle.
384 */
385typedef struct VDIOSTORAGE
386{
[28620]387 /** Image this storage handle belongs to. */
388 PVDIMAGE pImage;
[30555]389 /** AVL tree for pending async metadata transfers. */
390 PAVLRFOFFTREE pTreeMetaXfers;
[32553]391 /** Storage handle */
392 void *pStorage;
[27808]393} VDIOSTORAGE;
394
[30555]395/**
396 * Metadata transfer.
397 *
398 * @note This entry can't be freed if either the list is not empty or
399 * the reference counter is not 0.
400 * The assumption is that the backends don't need to read huge amounts of
401 * metadata to complete a transfer so the additional memory overhead should
402 * be relatively small.
403 */
404typedef struct VDMETAXFER
405{
406 /** AVL core for fast search (the file offset is the key) */
407 AVLRFOFFNODECORE Core;
408 /** I/O storage for this transfer. */
409 PVDIOSTORAGE pIoStorage;
410 /** Flags. */
411 uint32_t fFlags;
412 /** List of I/O contexts waiting for this metadata transfer to complete. */
413 RTLISTNODE ListIoCtxWaiting;
414 /** Number of references to this entry. */
415 unsigned cRefs;
416 /** Size of the data stored with this entry. */
417 size_t cbMeta;
418 /** Data stored - variable size. */
419 uint8_t abData[1];
420} VDMETAXFER;
421
422/**
423 * The transfer direction for the metadata.
424 */
425#define VDMETAXFER_TXDIR_MASK 0x3
426#define VDMETAXFER_TXDIR_NONE 0x0
427#define VDMETAXFER_TXDIR_WRITE 0x1
428#define VDMETAXFER_TXDIR_READ 0x2
429#define VDMETAXFER_TXDIR_FLUSH 0x3
430#define VDMETAXFER_TXDIR_GET(flags) ((flags) & VDMETAXFER_TXDIR_MASK)
431#define VDMETAXFER_TXDIR_SET(flags, dir) ((flags) = (flags & ~VDMETAXFER_TXDIR_MASK) | (dir))
432
[7780]433extern VBOXHDDBACKEND g_RawBackend;
[2379]434extern VBOXHDDBACKEND g_VmdkBackend;
[32573]435extern VBOXHDDBACKEND g_VmdkStreamBackend;
[7160]436extern VBOXHDDBACKEND g_VDIBackend;
[6173]437extern VBOXHDDBACKEND g_VhdBackend;
[21371]438extern VBOXHDDBACKEND g_ParallelsBackend;
[32432]439#ifdef VBOX_WITH_DMG
440extern VBOXHDDBACKEND g_DmgBackend;
441#endif
[10540]442#ifdef VBOX_WITH_ISCSI
443extern VBOXHDDBACKEND g_ISCSIBackend;
444#endif
[2379]445
[14852]446static unsigned g_cBackends = 0;
447static PVBOXHDDBACKEND *g_apBackends = NULL;
448static PVBOXHDDBACKEND aStaticBackends[] =
[2379]449{
[7780]450 &g_RawBackend,
[6292]451 &g_VmdkBackend,
[32573]452 &g_VmdkStreamBackend,
[7161]453 &g_VDIBackend,
[21371]454 &g_VhdBackend,
455 &g_ParallelsBackend
[32432]456#ifdef VBOX_WITH_DMG
457 ,&g_DmgBackend
458#endif
[10540]459#ifdef VBOX_WITH_ISCSI
[14852]460 ,&g_ISCSIBackend
[10540]461#endif
[2379]462};
463
[14852]464/**
[32370]465 * Supported backends for the disk cache.
466 */
467extern VDCACHEBACKEND g_VciCacheBackend;
468
469static unsigned g_cCacheBackends = 0;
470static PVDCACHEBACKEND *g_apCacheBackends = NULL;
471static PVDCACHEBACKEND aStaticCacheBackends[] =
472{
473 &g_VciCacheBackend
474};
475
476/**
[14852]477 * internal: add several backends.
478 */
479static int vdAddBackends(PVBOXHDDBACKEND *ppBackends, unsigned cBackends)
480{
481 PVBOXHDDBACKEND *pTmp = (PVBOXHDDBACKEND*)RTMemRealloc(g_apBackends,
482 (g_cBackends + cBackends) * sizeof(PVBOXHDDBACKEND));
483 if (RT_UNLIKELY(!pTmp))
484 return VERR_NO_MEMORY;
485 g_apBackends = pTmp;
486 memcpy(&g_apBackends[g_cBackends], ppBackends, cBackends * sizeof(PVBOXHDDBACKEND));
487 g_cBackends += cBackends;
488 return VINF_SUCCESS;
489}
[2379]490
491/**
[14852]492 * internal: add single backend.
493 */
494DECLINLINE(int) vdAddBackend(PVBOXHDDBACKEND pBackend)
495{
496 return vdAddBackends(&pBackend, 1);
497}
498
499/**
[32370]500 * internal: add several cache backends.
501 */
502static int vdAddCacheBackends(PVDCACHEBACKEND *ppBackends, unsigned cBackends)
503{
504 PVDCACHEBACKEND *pTmp = (PVDCACHEBACKEND*)RTMemRealloc(g_apCacheBackends,
505 (g_cCacheBackends + cBackends) * sizeof(PVDCACHEBACKEND));
506 if (RT_UNLIKELY(!pTmp))
507 return VERR_NO_MEMORY;
508 g_apCacheBackends = pTmp;
509 memcpy(&g_apCacheBackends[g_cCacheBackends], ppBackends, cBackends * sizeof(PVDCACHEBACKEND));
510 g_cCacheBackends += cBackends;
511 return VINF_SUCCESS;
512}
513
514/**
515 * internal: add single cache backend.
516 */
517DECLINLINE(int) vdAddCacheBackend(PVDCACHEBACKEND pBackend)
518{
519 return vdAddCacheBackends(&pBackend, 1);
520}
521
522/**
[7277]523 * internal: issue error message.
[2379]524 */
[7277]525static int vdError(PVBOXHDD pDisk, int rc, RT_SRC_POS_DECL,
526 const char *pszFormat, ...)
[2379]527{
528 va_list va;
529 va_start(va, pszFormat);
[10715]530 if (pDisk->pInterfaceErrorCallbacks)
531 pDisk->pInterfaceErrorCallbacks->pfnError(pDisk->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
[2379]532 va_end(va);
533 return rc;
534}
535
536/**
[27232]537 * internal: thread synchronization, start read.
538 */
539DECLINLINE(int) vdThreadStartRead(PVBOXHDD pDisk)
540{
541 int rc = VINF_SUCCESS;
542 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
543 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnStartRead(pDisk->pInterfaceThreadSync->pvUser);
544 return rc;
545}
546
547/**
548 * internal: thread synchronization, finish read.
549 */
550DECLINLINE(int) vdThreadFinishRead(PVBOXHDD pDisk)
551{
552 int rc = VINF_SUCCESS;
553 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
554 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnFinishRead(pDisk->pInterfaceThreadSync->pvUser);
555 return rc;
556}
557
558/**
559 * internal: thread synchronization, start write.
560 */
561DECLINLINE(int) vdThreadStartWrite(PVBOXHDD pDisk)
562{
563 int rc = VINF_SUCCESS;
564 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
565 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnStartWrite(pDisk->pInterfaceThreadSync->pvUser);
566 return rc;
567}
568
569/**
570 * internal: thread synchronization, finish write.
571 */
572DECLINLINE(int) vdThreadFinishWrite(PVBOXHDD pDisk)
573{
574 int rc = VINF_SUCCESS;
575 if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
576 rc = pDisk->pInterfaceThreadSyncCallbacks->pfnFinishWrite(pDisk->pInterfaceThreadSync->pvUser);
577 return rc;
578}
579
580/**
[7277]581 * internal: find image format backend.
[2379]582 */
[14852]583static int vdFindBackend(const char *pszBackend, PCVBOXHDDBACKEND *ppBackend)
[2379]584{
[7277]585 int rc = VINF_SUCCESS;
586 PCVBOXHDDBACKEND pBackend = NULL;
587
[14852]588 if (!g_apBackends)
589 VDInit();
590
591 for (unsigned i = 0; i < g_cBackends; i++)
[7277]592 {
[15366]593 if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
[7277]594 {
[14852]595 pBackend = g_apBackends[i];
[7277]596 break;
597 }
598 }
599 *ppBackend = pBackend;
[2379]600 return rc;
601}
602
603/**
[32370]604 * internal: find cache format backend.
605 */
606static int vdFindCacheBackend(const char *pszBackend, PCVDCACHEBACKEND *ppBackend)
607{
608 int rc = VINF_SUCCESS;
609 PCVDCACHEBACKEND pBackend = NULL;
610
611 if (!g_apCacheBackends)
612 VDInit();
613
614 for (unsigned i = 0; i < g_cCacheBackends; i++)
615 {
616 if (!RTStrICmp(pszBackend, g_apCacheBackends[i]->pszBackendName))
617 {
618 pBackend = g_apCacheBackends[i];
619 break;
620 }
621 }
622 *ppBackend = pBackend;
623 return rc;
624}
625
626/**
[2379]627 * internal: add image structure to the end of images list.
628 */
[2590]629static void vdAddImageToList(PVBOXHDD pDisk, PVDIMAGE pImage)
[2379]630{
631 pImage->pPrev = NULL;
632 pImage->pNext = NULL;
633
634 if (pDisk->pBase)
635 {
636 Assert(pDisk->cImages > 0);
637 pImage->pPrev = pDisk->pLast;
638 pDisk->pLast->pNext = pImage;
639 pDisk->pLast = pImage;
640 }
641 else
642 {
643 Assert(pDisk->cImages == 0);
644 pDisk->pBase = pImage;
645 pDisk->pLast = pImage;
646 }
647
648 pDisk->cImages++;
649}
650
651/**
652 * internal: remove image structure from the images list.
653 */
[2590]654static void vdRemoveImageFromList(PVBOXHDD pDisk, PVDIMAGE pImage)
[2379]655{
656 Assert(pDisk->cImages > 0);
657
658 if (pImage->pPrev)
659 pImage->pPrev->pNext = pImage->pNext;
660 else
661 pDisk->pBase = pImage->pNext;
662
663 if (pImage->pNext)
664 pImage->pNext->pPrev = pImage->pPrev;
665 else
666 pDisk->pLast = pImage->pPrev;
667
668 pImage->pPrev = NULL;
669 pImage->pNext = NULL;
670
671 pDisk->cImages--;
672}
673
674/**
675 * internal: find image by index into the images list.
676 */
[26987]677static PVDIMAGE vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
[2379]678{
[2590]679 PVDIMAGE pImage = pDisk->pBase;
[6291]680 if (nImage == VD_LAST_IMAGE)
681 return pDisk->pLast;
[2379]682 while (pImage && nImage)
683 {
684 pImage = pImage->pNext;
685 nImage--;
686 }
687 return pImage;
688}
689
690/**
[32370]691 * Internal: Tries to read the desired range from the given cache.
692 *
693 * @returns VBox status code.
694 * @retval VERR_VD_BLOCK_FREE if the block is not in the cache.
695 * pcbRead will be set to the number of bytes not in the cache.
696 * Everything thereafter might be in the cache.
697 * @param pCache The cache to read from.
698 * @param uOffset Offset of the virtual disk to read.
699 * @param pvBuf Where to store the read data.
700 * @param cbRead How much to read.
701 * @param pcbRead Where to store the number of bytes actually read.
702 * On success this indicates the number of bytes read from the cache.
703 * If VERR_VD_BLOCK_FREE is returned this gives the number of bytes
704 * whih are not in the cache.
705 * In both cases everything beyond this value
706 * might or might not be in the cache.
707 */
708static int vdCacheReadHelper(PVDCACHE pCache, uint64_t uOffset,
709 void *pvBuf, size_t cbRead, size_t *pcbRead)
710{
711 int rc = VINF_SUCCESS;
712
713 LogFlowFunc(("pCache=%#p uOffset=%llu pvBuf=%#p cbRead=%zu pcbRead=%#p\n",
714 pCache, uOffset, pvBuf, cbRead, pcbRead));
715
716 AssertPtr(pCache);
717 AssertPtr(pcbRead);
718
[32536]719 rc = pCache->Backend->pfnRead(pCache->pBackendData, uOffset, pvBuf,
[32370]720 cbRead, pcbRead);
721
722 LogFlowFunc(("returns rc=%Rrc pcbRead=%zu\n", rc, *pcbRead));
723 return rc;
724}
725
726/**
727 * Internal: Writes data for the given block into the cache.
728 *
729 * @returns VBox status code.
730 * @param pCache The cache to write to.
731 * @param uOffset Offset of the virtual disk to write to teh cache.
732 * @param pcvBuf The data to write.
733 * @param cbWrite How much to write.
734 * @param pcbWritten How much data could be written, optional.
735 */
736static int vdCacheWriteHelper(PVDCACHE pCache, uint64_t uOffset, const void *pcvBuf,
737 size_t cbWrite, size_t *pcbWritten)
738{
739 int rc = VINF_SUCCESS;
740
741 LogFlowFunc(("pCache=%#p uOffset=%llu pvBuf=%#p cbWrite=%zu pcbWritten=%#p\n",
742 pCache, uOffset, pcvBuf, cbWrite, pcbWritten));
743
744 AssertPtr(pCache);
745 AssertPtr(pcvBuf);
746 Assert(cbWrite > 0);
747
748 if (pcbWritten)
[32536]749 rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, pcvBuf,
[32370]750 cbWrite, pcbWritten);
751 else
752 {
753 size_t cbWritten = 0;
754
755 do
756 {
[32536]757 rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, pcvBuf,
[32370]758 cbWrite, &cbWritten);
759 uOffset += cbWritten;
760 pcvBuf = (char *)pcvBuf + cbWritten;
761 cbWrite -= cbWritten;
762 } while ( cbWrite
763 && RT_SUCCESS(rc));
764 }
765
766 LogFlowFunc(("returns rc=%Rrc pcbWritten=%zu\n",
767 rc, pcbWritten ? *pcbWritten : cbWrite));
768 return rc;
769}
770
771/**
772 * Internal: Reads a given amount of data from the image chain of the disk.
773 **/
774static int vdDiskReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
775 uint64_t uOffset, void *pvBuf, size_t cbRead, size_t *pcbThisRead)
776{
777 int rc = VINF_SUCCESS;
778 size_t cbThisRead = cbRead;
779
780 AssertPtr(pcbThisRead);
781
782 *pcbThisRead = 0;
783
784 /*
785 * Try to read from the given image.
786 * If the block is not allocated read from override chain if present.
787 */
[32536]788 rc = pImage->Backend->pfnRead(pImage->pBackendData,
[32370]789 uOffset, pvBuf, cbThisRead,
790 &cbThisRead);
791
792 if (rc == VERR_VD_BLOCK_FREE)
793 {
794 for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
795 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
796 pCurrImage = pCurrImage->pPrev)
797 {
[32536]798 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
[32370]799 uOffset, pvBuf, cbThisRead,
800 &cbThisRead);
801 }
802 }
803
804 if (RT_SUCCESS(rc))
805 *pcbThisRead = cbThisRead;
806
807 return rc;
808}
809
810/**
[2379]811 * internal: read the specified amount of data in whatever blocks the backend
812 * will give us.
813 */
[27211]814static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
[32370]815 uint64_t uOffset, void *pvBuf, size_t cbRead,
816 bool fZeroFreeBlocks, bool fUpdateCache)
[2379]817{
[32370]818 int rc = VINF_SUCCESS;
[2379]819 size_t cbThisRead;
[30313]820 bool fAllFree = true;
821 size_t cbBufClear = 0;
[2379]822
823 /* Loop until all read. */
824 do
825 {
826 /* Search for image with allocated block. Do not attempt to read more
827 * than the previous reads marked as valid. Otherwise this would return
828 * stale data when different block sizes are used for the images. */
829 cbThisRead = cbRead;
[27211]830
[32370]831 if ( pDisk->pCache
832 && !pImageParentOverride)
833 {
834 rc = vdCacheReadHelper(pDisk->pCache, uOffset, pvBuf,
835 cbThisRead, &cbThisRead);
[27211]836
[32370]837 if (rc == VERR_VD_BLOCK_FREE)
838 {
839 rc = vdDiskReadHelper(pDisk, pImage, pImageParentOverride,
840 uOffset, pvBuf, cbThisRead, &cbThisRead);
841
842 /* If the read was successful, write the data back into the cache. */
843 if ( RT_SUCCESS(rc)
844 && fUpdateCache)
845 {
846 rc = vdCacheWriteHelper(pDisk->pCache, uOffset, pvBuf,
847 cbThisRead, NULL);
848 }
849 }
850 }
851 else
[2379]852 {
[32370]853 /** @todo can be be replaced by vdDiskReadHelper if it proves to be reliable,
854 * don't want to be responsible for data corruption...
855 */
856 /*
857 * Try to read from the given image.
858 * If the block is not allocated read from override chain if present.
859 */
[32536]860 rc = pImage->Backend->pfnRead(pImage->pBackendData,
[32370]861 uOffset, pvBuf, cbThisRead,
862 &cbThisRead);
863
864 if (rc == VERR_VD_BLOCK_FREE)
[27211]865 {
[32370]866 for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
867 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
868 pCurrImage = pCurrImage->pPrev)
869 {
[32536]870 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
[32370]871 uOffset, pvBuf, cbThisRead,
872 &cbThisRead);
873 }
[27211]874 }
[2379]875 }
876
877 /* No image in the chain contains the data for the block. */
[15366]878 if (rc == VERR_VD_BLOCK_FREE)
[2379]879 {
[30481]880 /* Fill the free space with 0 if we are told to do so
881 * or a previous read returned valid data. */
[32370]882 if (fZeroFreeBlocks || !fAllFree)
[30313]883 memset(pvBuf, '\0', cbThisRead);
884 else
885 cbBufClear += cbThisRead;
886
[2379]887 rc = VINF_SUCCESS;
888 }
[30313]889 else if (RT_SUCCESS(rc))
890 {
891 /* First not free block, fill the space before with 0. */
[32370]892 if (!fZeroFreeBlocks)
[30313]893 {
894 memset((char *)pvBuf - cbBufClear, '\0', cbBufClear);
895 cbBufClear = 0;
896 fAllFree = false;
897 }
898 }
[2379]899
900 cbRead -= cbThisRead;
901 uOffset += cbThisRead;
902 pvBuf = (char *)pvBuf + cbThisRead;
[11266]903 } while (cbRead != 0 && RT_SUCCESS(rc));
[2379]904
[32370]905 return (!fZeroFreeBlocks && fAllFree) ? VERR_VD_BLOCK_FREE : rc;
[2379]906}
907
[28065]908DECLINLINE(PVDIOCTX) vdIoCtxAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
909 uint64_t uOffset, size_t cbTransfer,
[28154]910 PCRTSGSEG pcaSeg, unsigned cSeg,
[28620]911 void *pvAllocation,
912 PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
[27808]913{
914 PVDIOCTX pIoCtx = NULL;
915
916 pIoCtx = (PVDIOCTX)RTMemCacheAlloc(pDisk->hMemCacheIoCtx);
[28065]917 if (RT_LIKELY(pIoCtx))
[27808]918 {
[28065]919 pIoCtx->pDisk = pDisk;
920 pIoCtx->enmTxDir = enmTxDir;
921 pIoCtx->cbTransferLeft = cbTransfer;
922 pIoCtx->uOffset = uOffset;
[28620]923 pIoCtx->cbTransfer = cbTransfer;
[29240]924 pIoCtx->cDataTransfersPending = 0;
[27977]925 pIoCtx->cMetaTransfersPending = 0;
[28065]926 pIoCtx->fComplete = false;
[28683]927 pIoCtx->fBlocked = false;
[28154]928 pIoCtx->pvAllocation = pvAllocation;
[28620]929 pIoCtx->pfnIoCtxTransfer = pfnIoCtxTransfer;
930 pIoCtx->pfnIoCtxTransferNext = NULL;
[28785]931 pIoCtx->rcReq = VINF_SUCCESS;
[28065]932
[29048]933 /* There is no S/G list for a flush request. */
934 if (enmTxDir != VDIOCTXTXDIR_FLUSH)
935 RTSgBufInit(&pIoCtx->SgBuf, pcaSeg, cSeg);
936 else
937 memset(&pIoCtx->SgBuf, 0, sizeof(RTSGBUF));
[27808]938 }
939
940 return pIoCtx;
941}
942
[28620]943DECLINLINE(PVDIOCTX) vdIoCtxRootAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
944 uint64_t uOffset, size_t cbTransfer,
945 PCRTSGSEG paSeg, unsigned cSeg,
946 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
947 void *pvUser1, void *pvUser2,
948 void *pvAllocation,
949 PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
[28065]950{
951 PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer,
[28620]952 paSeg, cSeg, pvAllocation, pfnIoCtxTransfer);
[28065]953
954 if (RT_LIKELY(pIoCtx))
955 {
956 pIoCtx->pIoCtxParent = NULL;
957 pIoCtx->Type.Root.pfnComplete = pfnComplete;
958 pIoCtx->Type.Root.pvUser1 = pvUser1;
959 pIoCtx->Type.Root.pvUser2 = pvUser2;
960 }
961
[30863]962 LogFlow(("Allocated root I/O context %#p\n", pIoCtx));
[28065]963 return pIoCtx;
964}
965
[28620]966DECLINLINE(PVDIOCTX) vdIoCtxChildAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
967 uint64_t uOffset, size_t cbTransfer,
968 PCRTSGSEG paSeg, unsigned cSeg,
969 PVDIOCTX pIoCtxParent, size_t cbTransferParent,
[30044]970 size_t cbWriteParent, void *pvAllocation,
[28620]971 PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
[28065]972{
973 PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer,
[28620]974 paSeg, cSeg, pvAllocation, pfnIoCtxTransfer);
[28065]975
[30555]976 AssertPtr(pIoCtxParent);
977 Assert(!pIoCtxParent->pIoCtxParent);
978
[28065]979 if (RT_LIKELY(pIoCtx))
980 {
981 pIoCtx->pIoCtxParent = pIoCtxParent;
982 pIoCtx->Type.Child.uOffsetSaved = uOffset;
983 pIoCtx->Type.Child.cbTransferLeftSaved = cbTransfer;
984 pIoCtx->Type.Child.cbTransferParent = cbTransferParent;
[30044]985 pIoCtx->Type.Child.cbWriteParent = cbWriteParent;
[28065]986 }
987
[30863]988 LogFlow(("Allocated child I/O context %#p\n", pIoCtx));
[28065]989 return pIoCtx;
990}
991
[30555]992DECLINLINE(PVDIOTASK) vdIoTaskUserAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDIOCTX pIoCtx, uint32_t cbTransfer)
[27977]993{
994 PVDIOTASK pIoTask = NULL;
995
[30555]996 pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pImage->pDisk->hMemCacheIoTask);
[27977]997 if (pIoTask)
998 {
[30555]999 pIoTask->pIoStorage = pIoStorage;
1000 pIoTask->pfnComplete = pfnComplete;
1001 pIoTask->pvUser = pvUser;
[27977]1002 pIoTask->fMeta = false;
1003 pIoTask->Type.User.cbTransfer = cbTransfer;
[30555]1004 pIoTask->Type.User.pIoCtx = pIoCtx;
[27977]1005 }
1006
1007 return pIoTask;
1008}
1009
[30555]1010DECLINLINE(PVDIOTASK) vdIoTaskMetaAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDMETAXFER pMetaXfer)
[27977]1011{
1012 PVDIOTASK pIoTask = NULL;
1013
[30555]1014 pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pImage->pDisk->hMemCacheIoTask);
[27977]1015 if (pIoTask)
1016 {
[30555]1017 pIoTask->pIoStorage = pIoStorage;
1018 pIoTask->pfnComplete = pfnComplete;
1019 pIoTask->pvUser = pvUser;
1020 pIoTask->fMeta = true;
1021 pIoTask->Type.Meta.pMetaXfer = pMetaXfer;
[27977]1022 }
1023
1024 return pIoTask;
1025}
1026
[28620]1027DECLINLINE(void) vdIoCtxFree(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
[27808]1028{
[30863]1029 LogFlow(("Freeing I/O context %#p\n", pIoCtx));
[28154]1030 if (pIoCtx->pvAllocation)
1031 RTMemFree(pIoCtx->pvAllocation);
[30863]1032#ifdef DEBUG
1033 memset(pIoCtx, 0xff, sizeof(VDIOCTX));
1034#endif
[27808]1035 RTMemCacheFree(pDisk->hMemCacheIoCtx, pIoCtx);
1036}
1037
[28620]1038DECLINLINE(void) vdIoTaskFree(PVBOXHDD pDisk, PVDIOTASK pIoTask)
[27977]1039{
1040 RTMemCacheFree(pDisk->hMemCacheIoTask, pIoTask);
1041}
1042
[28620]1043DECLINLINE(void) vdIoCtxChildReset(PVDIOCTX pIoCtx)
[27808]1044{
[28065]1045 AssertPtr(pIoCtx->pIoCtxParent);
[27808]1046
[28065]1047 RTSgBufReset(&pIoCtx->SgBuf);
1048 pIoCtx->uOffset = pIoCtx->Type.Child.uOffsetSaved;
1049 pIoCtx->cbTransferLeft = pIoCtx->Type.Child.cbTransferLeftSaved;
1050}
[27808]1051
[30555]1052DECLINLINE(PVDMETAXFER) vdMetaXferAlloc(PVDIMAGE pImage, PVDIOSTORAGE pIoStorage, uint64_t uOffset, size_t cb)
1053{
1054 PVDMETAXFER pMetaXfer = (PVDMETAXFER)RTMemAlloc(RT_OFFSETOF(VDMETAXFER, abData[cb]));
1055
1056 if (RT_LIKELY(pMetaXfer))
1057 {
1058 pMetaXfer->Core.Key = uOffset;
1059 pMetaXfer->Core.KeyLast = uOffset + cb - 1;
1060 pMetaXfer->fFlags = VDMETAXFER_TXDIR_NONE;
1061 pMetaXfer->cbMeta = cb;
1062 pMetaXfer->pIoStorage = pIoStorage;
1063 pMetaXfer->cRefs = 0;
1064 RTListInit(&pMetaXfer->ListIoCtxWaiting);
1065 }
1066 return pMetaXfer;
1067}
1068
[28065]1069static size_t vdIoCtxCopy(PVDIOCTX pIoCtxDst, PVDIOCTX pIoCtxSrc, size_t cbData)
1070{
1071 return RTSgBufCopy(&pIoCtxDst->SgBuf, &pIoCtxSrc->SgBuf, cbData);
1072}
[27808]1073
[28065]1074static int vdIoCtxCmp(PVDIOCTX pIoCtx1, PVDIOCTX pIoCtx2, size_t cbData)
1075{
1076 return RTSgBufCmp(&pIoCtx1->SgBuf, &pIoCtx2->SgBuf, cbData);
[27808]1077}
1078
1079static size_t vdIoCtxCopyTo(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
1080{
[28065]1081 return RTSgBufCopyToBuf(&pIoCtx->SgBuf, pbData, cbData);
[27808]1082}
1083
1084
1085static size_t vdIoCtxCopyFrom(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
1086{
[28065]1087 return RTSgBufCopyFromBuf(&pIoCtx->SgBuf, pbData, cbData);
[27808]1088}
1089
[28065]1090static size_t vdIoCtxSet(PVDIOCTX pIoCtx, uint8_t ch, size_t cbData)
[27808]1091{
[28065]1092 return RTSgBufSet(&pIoCtx->SgBuf, ch, cbData);
[27808]1093}
1094
[28620]1095static int vdIoCtxProcess(PVDIOCTX pIoCtx)
1096{
1097 int rc = VINF_SUCCESS;
1098 PVBOXHDD pDisk = pIoCtx->pDisk;
1099
[28704]1100 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
[28693]1101
[30863]1102 RTCritSectEnter(&pDisk->CritSect);
1103
[28620]1104 if ( !pIoCtx->cbTransferLeft
[28693]1105 && !pIoCtx->cMetaTransfersPending
[29240]1106 && !pIoCtx->cDataTransfersPending
[28693]1107 && !pIoCtx->pfnIoCtxTransfer)
[30863]1108 {
1109 rc = VINF_VD_ASYNC_IO_FINISHED;
1110 goto out;
1111 }
[28620]1112
[29240]1113 /*
1114 * We complete the I/O context in case of an error
1115 * if there is no I/O task pending.
1116 */
1117 if ( RT_FAILURE(pIoCtx->rcReq)
1118 && !pIoCtx->cMetaTransfersPending
1119 && !pIoCtx->cDataTransfersPending)
[30863]1120 {
1121 rc = VINF_VD_ASYNC_IO_FINISHED;
1122 goto out;
1123 }
[29240]1124
[30863]1125 /* Don't change anything if there is a metadata transfer pending or we are blocked. */
1126 if ( pIoCtx->cMetaTransfersPending
1127 || pIoCtx->fBlocked)
1128 {
1129 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1130 goto out;
1131 }
[30555]1132
[28620]1133 if (pIoCtx->pfnIoCtxTransfer)
1134 {
1135 /* Call the transfer function advancing to the next while there is no error. */
1136 while ( pIoCtx->pfnIoCtxTransfer
1137 && RT_SUCCESS(rc))
1138 {
[28693]1139 LogFlowFunc(("calling transfer function %#p\n", pIoCtx->pfnIoCtxTransfer));
[28620]1140 rc = pIoCtx->pfnIoCtxTransfer(pIoCtx);
1141
1142 /* Advance to the next part of the transfer if the current one succeeded. */
1143 if (RT_SUCCESS(rc))
1144 {
1145 pIoCtx->pfnIoCtxTransfer = pIoCtx->pfnIoCtxTransferNext;
1146 pIoCtx->pfnIoCtxTransferNext = NULL;
1147 }
1148 }
1149 }
1150
1151 if ( RT_SUCCESS(rc)
1152 && !pIoCtx->cbTransferLeft
[29240]1153 && !pIoCtx->cMetaTransfersPending
1154 && !pIoCtx->cDataTransfersPending)
[28620]1155 rc = VINF_VD_ASYNC_IO_FINISHED;
[31586]1156 else if ( RT_SUCCESS(rc)
1157 || rc == VERR_VD_NOT_ENOUGH_METADATA
1158 || rc == VERR_VD_IOCTX_HALT)
[28620]1159 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
[29244]1160 else if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
[29240]1161 {
1162 ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rc, VINF_SUCCESS);
1163 /*
1164 * The I/O context completed if we have an error and there is no data
1165 * or meta data transfer pending.
1166 */
1167 if ( !pIoCtx->cMetaTransfersPending
1168 && !pIoCtx->cDataTransfersPending)
1169 rc = VINF_VD_ASYNC_IO_FINISHED;
[29244]1170 else
1171 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
[29240]1172 }
[28620]1173
[30863]1174out:
1175 RTCritSectLeave(&pDisk->CritSect);
1176
[28620]1177 LogFlowFunc(("pIoCtx=%#p rc=%Rrc cbTransferLeft=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
1178 pIoCtx, rc, pIoCtx->cbTransferLeft, pIoCtx->cMetaTransfersPending,
1179 pIoCtx->fComplete));
1180
1181 return rc;
1182}
1183
[2379]1184/**
[27808]1185 * internal: read the specified amount of data in whatever blocks the backend
1186 * will give us - async version.
1187 */
[28620]1188static int vdReadHelperAsync(PVDIOCTX pIoCtx)
[27808]1189{
1190 int rc;
[28620]1191 size_t cbToRead = pIoCtx->cbTransfer;
1192 uint64_t uOffset = pIoCtx->uOffset;
1193 PVDIMAGE pCurrImage = NULL;
[27808]1194 size_t cbThisRead;
1195
1196 /* Loop until all reads started or we have a backend which needs to read metadata. */
1197 do
1198 {
[28620]1199 pCurrImage = pIoCtx->pImage;
1200
[27808]1201 /* Search for image with allocated block. Do not attempt to read more
1202 * than the previous reads marked as valid. Otherwise this would return
1203 * stale data when different block sizes are used for the images. */
[28620]1204 cbThisRead = cbToRead;
[27808]1205
1206 /*
1207 * Try to read from the given image.
1208 * If the block is not allocated read from override chain if present.
1209 */
[32536]1210 rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pBackendData,
[28620]1211 uOffset, cbThisRead,
1212 pIoCtx, &cbThisRead);
[27808]1213
1214 if (rc == VERR_VD_BLOCK_FREE)
1215 {
[28620]1216 for (pCurrImage = pCurrImage->pPrev;
[27808]1217 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
1218 pCurrImage = pCurrImage->pPrev)
1219 {
[32536]1220 rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pBackendData,
[27808]1221 uOffset, cbThisRead,
1222 pIoCtx, &cbThisRead);
1223 }
1224 }
1225
[31573]1226 /* The task state will be updated on success already, don't do it here!. */
1227 if (rc == VERR_VD_BLOCK_FREE)
[27808]1228 {
1229 /* No image in the chain contains the data for the block. */
1230 vdIoCtxSet(pIoCtx, '\0', cbThisRead);
[28065]1231 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbThisRead);
[27808]1232 rc = VINF_SUCCESS;
1233 }
[30555]1234 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1235 rc = VINF_SUCCESS;
[31586]1236 else if (rc == VERR_VD_IOCTX_HALT)
1237 {
1238 uOffset += cbThisRead;
1239 cbToRead -= cbThisRead;
1240 pIoCtx->fBlocked = true;
1241 }
[27808]1242
[28065]1243 if (RT_FAILURE(rc))
1244 break;
[27808]1245
[28620]1246 cbToRead -= cbThisRead;
1247 uOffset += cbThisRead;
1248 } while (cbToRead != 0 && RT_SUCCESS(rc));
[27808]1249
[31586]1250 if ( rc == VERR_VD_NOT_ENOUGH_METADATA
1251 || rc == VERR_VD_IOCTX_HALT)
[27808]1252 {
[28620]1253 /* Save the current state. */
1254 pIoCtx->uOffset = uOffset;
1255 pIoCtx->cbTransfer = cbToRead;
1256 pIoCtx->pImage = pCurrImage;
[27808]1257 }
1258
1259 return rc;
1260}
1261
1262/**
[19176]1263 * internal: parent image read wrapper for compacting.
1264 */
1265static int vdParentRead(void *pvUser, uint64_t uOffset, void *pvBuf,
1266 size_t cbRead)
1267{
1268 PVDPARENTSTATEDESC pParentState = (PVDPARENTSTATEDESC)pvUser;
[27211]1269 return vdReadHelper(pParentState->pDisk, pParentState->pImage, NULL, uOffset,
[32370]1270 pvBuf, cbRead, true /* fZeroFreeBlocks */,
1271 false /* fUpdateCache */);
[19176]1272}
1273
1274/**
[2379]1275 * internal: mark the disk as not modified.
1276 */
1277static void vdResetModifiedFlag(PVBOXHDD pDisk)
1278{
1279 if (pDisk->uModified & VD_IMAGE_MODIFIED_FLAG)
1280 {
1281 /* generate new last-modified uuid */
[7900]1282 if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
[2379]1283 {
1284 RTUUID Uuid;
1285
1286 RTUuidCreate(&Uuid);
[32536]1287 pDisk->pLast->Backend->pfnSetModificationUuid(pDisk->pLast->pBackendData,
[7277]1288 &Uuid);
[32370]1289
1290 if (pDisk->pCache)
[32536]1291 pDisk->pCache->Backend->pfnSetModificationUuid(pDisk->pCache->pBackendData,
[32370]1292 &Uuid);
[2379]1293 }
1294
1295 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FLAG;
1296 }
1297}
1298
1299/**
1300 * internal: mark the disk as modified.
1301 */
[26987]1302static void vdSetModifiedFlag(PVBOXHDD pDisk)
[2379]1303{
1304 pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
1305 if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
1306 {
1307 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
1308
1309 /* First modify, so create a UUID and ensure it's written to disk. */
1310 vdResetModifiedFlag(pDisk);
1311
[32370]1312 if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
[32536]1313 pDisk->pLast->Backend->pfnFlush(pDisk->pLast->pBackendData);
[2379]1314 }
1315}
1316
1317/**
[2590]1318 * internal: write a complete block (only used for diff images), taking the
1319 * remaining data from parent images. This implementation does not optimize
1320 * anything (except that it tries to read only that portions from parent
1321 * images that are really needed).
1322 */
1323static int vdWriteHelperStandard(PVBOXHDD pDisk, PVDIMAGE pImage,
[27211]1324 PVDIMAGE pImageParentOverride,
[2590]1325 uint64_t uOffset, size_t cbWrite,
1326 size_t cbThisWrite, size_t cbPreRead,
1327 size_t cbPostRead, const void *pvBuf,
1328 void *pvTmp)
1329{
[2742]1330 int rc = VINF_SUCCESS;
[2590]1331
1332 /* Read the data that goes before the write to fill the block. */
1333 if (cbPreRead)
1334 {
[32370]1335 /*
1336 * Updating the cache doesn't make sense here because
1337 * this will be done after the complete block was written.
1338 */
[27211]1339 rc = vdReadHelper(pDisk, pImage, pImageParentOverride,
[32370]1340 uOffset - cbPreRead, pvTmp, cbPreRead,
1341 true /* fZeroFreeBlocks*/,
1342 false /* fUpdateCache */);
[11266]1343 if (RT_FAILURE(rc))
[2590]1344 return rc;
1345 }
1346
1347 /* Copy the data to the right place in the buffer. */
1348 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
1349
1350 /* Read the data that goes after the write to fill the block. */
1351 if (cbPostRead)
1352 {
1353 /* If we have data to be written, use that instead of reading
1354 * data from the image. */
1355 size_t cbWriteCopy;
1356 if (cbWrite > cbThisWrite)
1357 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1358 else
1359 cbWriteCopy = 0;
1360 /* Figure out how much we cannnot read from the image, because
1361 * the last block to write might exceed the nominal size of the
1362 * image for technical reasons. */
1363 size_t cbFill;
1364 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1365 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1366 else
1367 cbFill = 0;
1368 /* The rest must be read from the image. */
1369 size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1370
1371 /* Now assemble the remaining data. */
1372 if (cbWriteCopy)
1373 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
1374 (char *)pvBuf + cbThisWrite, cbWriteCopy);
1375 if (cbReadImage)
[27211]1376 rc = vdReadHelper(pDisk, pImage, pImageParentOverride,
[2590]1377 uOffset + cbThisWrite + cbWriteCopy,
1378 (char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy,
[32370]1379 cbReadImage, true /* fZeroFreeBlocks */,
1380 false /* fUpdateCache */);
[11266]1381 if (RT_FAILURE(rc))
[2590]1382 return rc;
1383 /* Zero out the remainder of this block. Will never be visible, as this
1384 * is beyond the limit of the image. */
1385 if (cbFill)
1386 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
1387 '\0', cbFill);
1388 }
1389
1390 /* Write the full block to the virtual disk. */
[32536]1391 rc = pImage->Backend->pfnWrite(pImage->pBackendData,
[7277]1392 uOffset - cbPreRead, pvTmp,
1393 cbPreRead + cbThisWrite + cbPostRead,
[7780]1394 NULL, &cbPreRead, &cbPostRead, 0);
[15366]1395 Assert(rc != VERR_VD_BLOCK_FREE);
[2590]1396 Assert(cbPreRead == 0);
1397 Assert(cbPostRead == 0);
1398
1399 return rc;
1400}
1401
1402/**
1403 * internal: write a complete block (only used for diff images), taking the
[7900]1404 * remaining data from parent images. This implementation optimizes out writes
[2590]1405 * that do not change the data relative to the state as of the parent images.
1406 * All backends which support differential/growing images support this.
1407 */
1408static int vdWriteHelperOptimized(PVBOXHDD pDisk, PVDIMAGE pImage,
[27211]1409 PVDIMAGE pImageParentOverride,
[2590]1410 uint64_t uOffset, size_t cbWrite,
1411 size_t cbThisWrite, size_t cbPreRead,
1412 size_t cbPostRead, const void *pvBuf,
1413 void *pvTmp)
1414{
1415 size_t cbFill = 0;
1416 size_t cbWriteCopy = 0;
1417 size_t cbReadImage = 0;
1418 int rc;
1419
1420 if (cbPostRead)
1421 {
1422 /* Figure out how much we cannnot read from the image, because
1423 * the last block to write might exceed the nominal size of the
1424 * image for technical reasons. */
1425 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1426 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1427
1428 /* If we have data to be written, use that instead of reading
1429 * data from the image. */
1430 if (cbWrite > cbThisWrite)
1431 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1432
1433 /* The rest must be read from the image. */
1434 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1435 }
1436
1437 /* Read the entire data of the block so that we can compare whether it will
1438 * be modified by the write or not. */
[27211]1439 rc = vdReadHelper(pDisk, pImage, pImageParentOverride, uOffset - cbPreRead, pvTmp,
[32370]1440 cbPreRead + cbThisWrite + cbPostRead - cbFill,
1441 true /* fZeroFreeBlocks */,
1442 false /* fUpdateCache */);
[11266]1443 if (RT_FAILURE(rc))
[2590]1444 return rc;
1445
1446 /* Check if the write would modify anything in this block. */
1447 if ( !memcmp((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite)
1448 && (!cbWriteCopy || !memcmp((char *)pvTmp + cbPreRead + cbThisWrite,
1449 (char *)pvBuf + cbThisWrite, cbWriteCopy)))
1450 {
1451 /* Block is completely unchanged, so no need to write anything. */
1452 return VINF_SUCCESS;
1453 }
1454
1455 /* Copy the data to the right place in the buffer. */
1456 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
1457
1458 /* Handle the data that goes after the write to fill the block. */
1459 if (cbPostRead)
1460 {
1461 /* Now assemble the remaining data. */
1462 if (cbWriteCopy)
1463 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
1464 (char *)pvBuf + cbThisWrite, cbWriteCopy);
1465 /* Zero out the remainder of this block. Will never be visible, as this
1466 * is beyond the limit of the image. */
1467 if (cbFill)
1468 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
1469 '\0', cbFill);
1470 }
1471
1472 /* Write the full block to the virtual disk. */
[32536]1473 rc = pImage->Backend->pfnWrite(pImage->pBackendData,
[7277]1474 uOffset - cbPreRead, pvTmp,
1475 cbPreRead + cbThisWrite + cbPostRead,
[7780]1476 NULL, &cbPreRead, &cbPostRead, 0);
[15366]1477 Assert(rc != VERR_VD_BLOCK_FREE);
[2590]1478 Assert(cbPreRead == 0);
1479 Assert(cbPostRead == 0);
1480
1481 return rc;
1482}
1483
1484/**
[6291]1485 * internal: write buffer to the image, taking care of block boundaries and
1486 * write optimizations.
1487 */
[27211]1488static int vdWriteHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
[32370]1489 uint64_t uOffset, const void *pvBuf, size_t cbWrite,
1490 bool fUpdateCache)
[6291]1491{
1492 int rc;
[7900]1493 unsigned fWrite;
[6291]1494 size_t cbThisWrite;
1495 size_t cbPreRead, cbPostRead;
[32370]1496 uint64_t uOffsetCur = uOffset;
1497 size_t cbWriteCur = cbWrite;
1498 const void *pcvBufCur = pvBuf;
[6291]1499
1500 /* Loop until all written. */
1501 do
1502 {
1503 /* Try to write the possibly partial block to the last opened image.
1504 * This works when the block is already allocated in this image or
[7900]1505 * if it is a full-block write (and allocation isn't suppressed below).
1506 * For image formats which don't support zero blocks, it's beneficial
1507 * to avoid unnecessarily allocating unchanged blocks. This prevents
1508 * unwanted expanding of images. VMDK is an example. */
[32370]1509 cbThisWrite = cbWriteCur;
[7900]1510 fWrite = (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
1511 ? 0 : VD_WRITE_NO_ALLOC;
[32536]1512 rc = pImage->Backend->pfnWrite(pImage->pBackendData, uOffsetCur, pcvBufCur,
[7277]1513 cbThisWrite, &cbThisWrite, &cbPreRead,
[7900]1514 &cbPostRead, fWrite);
[15366]1515 if (rc == VERR_VD_BLOCK_FREE)
[6291]1516 {
1517 void *pvTmp = RTMemTmpAlloc(cbPreRead + cbThisWrite + cbPostRead);
[8569]1518 AssertBreakStmt(VALID_PTR(pvTmp), rc = VERR_NO_MEMORY);
[6291]1519
1520 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME))
1521 {
1522 /* Optimized write, suppress writing to a so far unallocated
1523 * block if the data is in fact not changed. */
[27211]1524 rc = vdWriteHelperOptimized(pDisk, pImage, pImageParentOverride,
[32370]1525 uOffsetCur, cbWriteCur,
[6291]1526 cbThisWrite, cbPreRead, cbPostRead,
[32370]1527 pcvBufCur, pvTmp);
[6291]1528 }
1529 else
1530 {
1531 /* Normal write, not optimized in any way. The block will
1532 * be written no matter what. This will usually (unless the
1533 * backend has some further optimization enabled) cause the
1534 * block to be allocated. */
[27211]1535 rc = vdWriteHelperStandard(pDisk, pImage, pImageParentOverride,
[32370]1536 uOffsetCur, cbWriteCur,
[6291]1537 cbThisWrite, cbPreRead, cbPostRead,
[32370]1538 pcvBufCur, pvTmp);
[6291]1539 }
1540 RTMemTmpFree(pvTmp);
[11266]1541 if (RT_FAILURE(rc))
[6291]1542 break;
1543 }
1544
[32370]1545 cbWriteCur -= cbThisWrite;
1546 uOffsetCur += cbThisWrite;
1547 pcvBufCur = (char *)pcvBufCur + cbThisWrite;
1548 } while (cbWriteCur != 0 && RT_SUCCESS(rc));
[6291]1549
[32370]1550 /* Update the cache on success */
1551 if ( RT_SUCCESS(rc)
1552 && pDisk->pCache
1553 && fUpdateCache)
1554 rc = vdCacheWriteHelper(pDisk->pCache, uOffset, pvBuf, cbWrite, NULL);
1555
[6291]1556 return rc;
1557}
1558
[27977]1559/**
[28065]1560 * internal: write a complete block (only used for diff images), taking the
1561 * remaining data from parent images. This implementation does not optimize
1562 * anything (except that it tries to read only that portions from parent
1563 * images that are really needed) - async version.
1564 */
[28620]1565static int vdWriteHelperStandardAsync(PVDIOCTX pIoCtx)
[28065]1566{
1567 int rc = VINF_SUCCESS;
1568
[28620]1569#if 0
1570
[28065]1571 /* Read the data that goes before the write to fill the block. */
1572 if (cbPreRead)
1573 {
[28620]1574 rc = vdReadHelperAsync(pIoCtxDst);
[28065]1575 if (RT_FAILURE(rc))
1576 return rc;
1577 }
1578
1579 /* Copy the data to the right place in the buffer. */
1580 vdIoCtxCopy(pIoCtxDst, pIoCtxSrc, cbThisWrite);
1581
1582 /* Read the data that goes after the write to fill the block. */
1583 if (cbPostRead)
1584 {
1585 /* If we have data to be written, use that instead of reading
1586 * data from the image. */
1587 size_t cbWriteCopy;
1588 if (cbWrite > cbThisWrite)
1589 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1590 else
1591 cbWriteCopy = 0;
1592 /* Figure out how much we cannnot read from the image, because
1593 * the last block to write might exceed the nominal size of the
1594 * image for technical reasons. */
1595 size_t cbFill;
1596 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1597 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1598 else
1599 cbFill = 0;
1600 /* The rest must be read from the image. */
1601 size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1602
1603 /* Now assemble the remaining data. */
1604 if (cbWriteCopy)
1605 {
1606 vdIoCtxCopy(pIoCtxDst, pIoCtxSrc, cbWriteCopy);
1607 ASMAtomicSubU32(&pIoCtxDst->cbTransferLeft, cbWriteCopy);
1608 }
1609
1610 if (cbReadImage)
1611 rc = vdReadHelperAsync(pDisk, pImage, pImageParentOverride, pIoCtxDst,
1612 uOffset + cbThisWrite + cbWriteCopy,
1613 cbReadImage);
1614 if (RT_FAILURE(rc))
1615 return rc;
1616 /* Zero out the remainder of this block. Will never be visible, as this
1617 * is beyond the limit of the image. */
1618 if (cbFill)
1619 {
1620 vdIoCtxSet(pIoCtxDst, '\0', cbFill);
1621 ASMAtomicSubU32(&pIoCtxDst->cbTransferLeft, cbFill);
1622 }
1623 }
1624
1625 if ( !pIoCtxDst->cbTransferLeft
1626 && !pIoCtxDst->cMetaTransfersPending
1627 && ASMAtomicCmpXchgBool(&pIoCtxDst->fComplete, true, false))
1628 {
1629 /* Write the full block to the virtual disk. */
1630 vdIoCtxChildReset(pIoCtxDst);
[32536]1631 rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData,
[28065]1632 uOffset - cbPreRead,
1633 cbPreRead + cbThisWrite + cbPostRead,
1634 pIoCtxDst,
1635 NULL, &cbPreRead, &cbPostRead, 0);
1636 Assert(rc != VERR_VD_BLOCK_FREE);
1637 Assert(cbPreRead == 0);
1638 Assert(cbPostRead == 0);
1639 }
1640 else
1641 {
1642 LogFlow(("cbTransferLeft=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
1643 pIoCtxDst->cbTransferLeft, pIoCtxDst->cMetaTransfersPending,
1644 pIoCtxDst->fComplete));
1645 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1646 }
1647
1648 return rc;
[28620]1649#endif
1650 return VERR_NOT_IMPLEMENTED;
[28065]1651}
1652
[28620]1653static int vdWriteHelperOptimizedCmpAndWriteAsync(PVDIOCTX pIoCtx)
[28065]1654{
[28620]1655 int rc = VINF_SUCCESS;
1656 PVDIMAGE pImage = pIoCtx->pImage;
1657 size_t cbThisWrite = 0;
1658 size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
1659 size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
1660 size_t cbWriteCopy = pIoCtx->Type.Child.Write.Optimized.cbWriteCopy;
1661 size_t cbFill = pIoCtx->Type.Child.Write.Optimized.cbFill;
1662 size_t cbReadImage = pIoCtx->Type.Child.Write.Optimized.cbReadImage;
1663 PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
[28065]1664
[28693]1665 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1666
[28620]1667 AssertPtr(pIoCtxParent);
[30555]1668 Assert(!pIoCtxParent->pIoCtxParent);
[28620]1669 Assert(!pIoCtx->cbTransferLeft && !pIoCtx->cMetaTransfersPending);
[28065]1670
[28620]1671 vdIoCtxChildReset(pIoCtx);
1672 cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
1673 RTSgBufAdvance(&pIoCtx->SgBuf, cbPreRead);
[28065]1674
1675 /* Check if the write would modify anything in this block. */
[28620]1676 if (!RTSgBufCmp(&pIoCtx->SgBuf, &pIoCtxParent->SgBuf, cbThisWrite))
[28065]1677 {
[28154]1678 RTSGBUF SgBufSrcTmp;
1679
[28620]1680 RTSgBufClone(&SgBufSrcTmp, &pIoCtxParent->SgBuf);
[28154]1681 RTSgBufAdvance(&SgBufSrcTmp, cbThisWrite);
[28620]1682 RTSgBufAdvance(&pIoCtx->SgBuf, cbThisWrite);
[28154]1683
[28620]1684 if (!cbWriteCopy || !RTSgBufCmp(&pIoCtx->SgBuf, &SgBufSrcTmp, cbWriteCopy))
[28154]1685 {
1686 /* Block is completely unchanged, so no need to write anything. */
1687 LogFlowFunc(("Block didn't changed\n"));
[28620]1688 ASMAtomicWriteU32(&pIoCtx->cbTransferLeft, 0);
[29413]1689 RTSgBufAdvance(&pIoCtxParent->SgBuf, cbThisWrite);
[28620]1690 return VINF_VD_ASYNC_IO_FINISHED;
[28154]1691 }
[28065]1692 }
1693
1694 /* Copy the data to the right place in the buffer. */
[28620]1695 RTSgBufReset(&pIoCtx->SgBuf);
1696 RTSgBufAdvance(&pIoCtx->SgBuf, cbPreRead);
1697 vdIoCtxCopy(pIoCtx, pIoCtxParent, cbThisWrite);
[28065]1698
1699 /* Handle the data that goes after the write to fill the block. */
1700 if (cbPostRead)
1701 {
1702 /* Now assemble the remaining data. */
1703 if (cbWriteCopy)
[30044]1704 {
1705 /*
1706 * The S/G buffer of the parent needs to be cloned because
1707 * it is not allowed to modify the state.
1708 */
1709 RTSGBUF SgBufParentTmp;
1710
1711 RTSgBufClone(&SgBufParentTmp, &pIoCtxParent->SgBuf);
1712 RTSgBufCopy(&pIoCtx->SgBuf, &SgBufParentTmp, cbWriteCopy);
1713 }
1714
[28065]1715 /* Zero out the remainder of this block. Will never be visible, as this
1716 * is beyond the limit of the image. */
1717 if (cbFill)
[28154]1718 {
[28620]1719 RTSgBufAdvance(&pIoCtx->SgBuf, cbReadImage);
1720 vdIoCtxSet(pIoCtx, '\0', cbFill);
[28154]1721 }
[28065]1722 }
1723
1724 /* Write the full block to the virtual disk. */
[28620]1725 RTSgBufReset(&pIoCtx->SgBuf);
[32536]1726 rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData,
[28620]1727 pIoCtx->uOffset - cbPreRead,
[30044]1728 cbPreRead + cbThisWrite + cbPostRead,
[28620]1729 pIoCtx, NULL, &cbPreRead, &cbPostRead, 0);
[28065]1730 Assert(rc != VERR_VD_BLOCK_FREE);
1731 Assert(cbPreRead == 0);
1732 Assert(cbPostRead == 0);
[30555]1733 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1734 rc = VINF_SUCCESS;
[31586]1735 else if (rc == VERR_VD_IOCTX_HALT)
1736 {
1737 pIoCtx->fBlocked = true;
1738 rc = VINF_SUCCESS;
1739 }
[28154]1740
[28065]1741 return rc;
1742}
1743
[28620]1744static int vdWriteHelperOptimizedPreReadAsync(PVDIOCTX pIoCtx)
1745{
[28693]1746 int rc = VINF_SUCCESS;
1747
1748 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1749
1750 if (pIoCtx->cbTransferLeft)
1751 rc = vdReadHelperAsync(pIoCtx);
1752
1753 if ( RT_SUCCESS(rc)
1754 && ( pIoCtx->cbTransferLeft
1755 || pIoCtx->cMetaTransfersPending))
1756 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1757 else
1758 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedCmpAndWriteAsync;
1759
1760 return rc;
[28620]1761}
1762
[28065]1763/**
[28620]1764 * internal: write a complete block (only used for diff images), taking the
1765 * remaining data from parent images. This implementation optimizes out writes
1766 * that do not change the data relative to the state as of the parent images.
1767 * All backends which support differential/growing images support this - async version.
1768 */
1769static int vdWriteHelperOptimizedAsync(PVDIOCTX pIoCtx)
1770{
1771 PVBOXHDD pDisk = pIoCtx->pDisk;
1772 uint64_t uOffset = pIoCtx->Type.Child.uOffsetSaved;
1773 size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
1774 size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
1775 size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
[30044]1776 size_t cbWrite = pIoCtx->Type.Child.cbWriteParent;
[28620]1777 size_t cbFill = 0;
1778 size_t cbWriteCopy = 0;
1779 size_t cbReadImage = 0;
1780
[28693]1781 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1782
[28620]1783 AssertPtr(pIoCtx->pIoCtxParent);
[30555]1784 Assert(!pIoCtx->pIoCtxParent->pIoCtxParent);
[28620]1785
1786 if (cbPostRead)
1787 {
1788 /* Figure out how much we cannnot read from the image, because
1789 * the last block to write might exceed the nominal size of the
1790 * image for technical reasons. */
1791 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
1792 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
1793
1794 /* If we have data to be written, use that instead of reading
1795 * data from the image. */
1796 if (cbWrite > cbThisWrite)
1797 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
1798
1799 /* The rest must be read from the image. */
1800 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
1801 }
1802
1803 pIoCtx->Type.Child.Write.Optimized.cbFill = cbFill;
1804 pIoCtx->Type.Child.Write.Optimized.cbWriteCopy = cbWriteCopy;
1805 pIoCtx->Type.Child.Write.Optimized.cbReadImage = cbReadImage;
1806
1807 /* Read the entire data of the block so that we can compare whether it will
1808 * be modified by the write or not. */
1809 pIoCtx->cbTransferLeft = cbPreRead + cbThisWrite + cbPostRead - cbFill;
1810 pIoCtx->cbTransfer = pIoCtx->cbTransferLeft;
[30044]1811 pIoCtx->uOffset -= cbPreRead;
[28620]1812
1813 /* Next step */
1814 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedPreReadAsync;
1815 return VINF_SUCCESS;
1816}
1817
1818/**
[27977]1819 * internal: write buffer to the image, taking care of block boundaries and
1820 * write optimizations - async version.
1821 */
[28620]1822static int vdWriteHelperAsync(PVDIOCTX pIoCtx)
[27977]1823{
1824 int rc;
[28620]1825 size_t cbWrite = pIoCtx->cbTransfer;
1826 uint64_t uOffset = pIoCtx->uOffset;
1827 PVDIMAGE pImage = pIoCtx->pImage;
1828 PVBOXHDD pDisk = pIoCtx->pDisk;
[27977]1829 unsigned fWrite;
1830 size_t cbThisWrite;
1831 size_t cbPreRead, cbPostRead;
[6291]1832
[27977]1833 /* Loop until all written. */
1834 do
1835 {
1836 /* Try to write the possibly partial block to the last opened image.
1837 * This works when the block is already allocated in this image or
1838 * if it is a full-block write (and allocation isn't suppressed below).
1839 * For image formats which don't support zero blocks, it's beneficial
1840 * to avoid unnecessarily allocating unchanged blocks. This prevents
1841 * unwanted expanding of images. VMDK is an example. */
1842 cbThisWrite = cbWrite;
1843 fWrite = (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
1844 ? 0 : VD_WRITE_NO_ALLOC;
[32536]1845 rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData, uOffset,
[27977]1846 cbThisWrite, pIoCtx,
1847 &cbThisWrite, &cbPreRead,
1848 &cbPostRead, fWrite);
1849 if (rc == VERR_VD_BLOCK_FREE)
1850 {
[28065]1851 /*
[28620]1852 * If there is a growing request already put this one onto the waiting list.
1853 * It will be restarted if the current request completes.
[28065]1854 */
[28683]1855 if (ASMAtomicReadBool(&pDisk->fGrowing))
[28065]1856 {
[30555]1857 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
1858 AssertPtr(pDeferred);
1859
[28620]1860 LogFlowFunc(("Deferring write pIoCtx=%#p\n", pIoCtx));
[30555]1861
[30863]1862 Assert(!pIoCtx->pIoCtxParent && !pIoCtx->fBlocked);
1863
[30555]1864 RTListInit(&pDeferred->NodeDeferred);
1865 pDeferred->pIoCtx = pIoCtx;
1866 RTListAppend(&pDisk->ListWriteGrowing, &pDeferred->NodeDeferred);
[28683]1867 pIoCtx->fBlocked = true;
[28620]1868 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
[28065]1869 break;
1870 }
[27977]1871 else
1872 {
[28620]1873 /*
1874 * Allocate segment and buffer in one go.
1875 * A bit hackish but avoids the need to allocate memory twice.
1876 */
1877 PRTSGSEG pTmp = (PRTSGSEG)RTMemAlloc(cbPreRead + cbThisWrite + cbPostRead + sizeof(RTSGSEG));
1878 AssertBreakStmt(VALID_PTR(pTmp), rc = VERR_NO_MEMORY);
[28065]1879
[28620]1880 pTmp->pvSeg = pTmp + 1;
1881 pTmp->cbSeg = cbPreRead + cbThisWrite + cbPostRead;
[27977]1882
[28620]1883 PVDIOCTX pIoCtxWrite = vdIoCtxChildAlloc(pDisk, VDIOCTXTXDIR_WRITE,
1884 uOffset, pTmp->cbSeg,
1885 pTmp, 1,
1886 pIoCtx, cbThisWrite,
[30044]1887 cbWrite,
[29497]1888 pTmp,
[28620]1889 (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
1890 ? vdWriteHelperStandardAsync
1891 : vdWriteHelperOptimizedAsync);
1892 if (!VALID_PTR(pIoCtxWrite))
1893 {
1894 RTMemTmpFree(pTmp);
1895 rc = VERR_NO_MEMORY;
1896 break;
1897 }
1898
1899 /* Set the state to growing. */
1900 LogFlowFunc(("Disk is growing because of pIoCtx=%#p pIoCtxWrite=%#p\n",
[31185]1901
[28620]1902 pIoCtx, pIoCtxWrite));
[28683]1903 ASMAtomicWriteBool(&pDisk->fGrowing, true);
[28620]1904
1905 pIoCtxWrite->pImage = pImage;
1906 pIoCtxWrite->Type.Child.cbPreRead = cbPreRead;
1907 pIoCtxWrite->Type.Child.cbPostRead = cbPostRead;
1908
1909 /* Process the write request */
1910 rc = vdIoCtxProcess(pIoCtxWrite);
1911
1912 if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
1913 {
1914 vdIoCtxFree(pDisk, pIoCtxWrite);
1915 break;
1916 }
1917 else if ( rc == VINF_VD_ASYNC_IO_FINISHED
1918 && ASMAtomicCmpXchgBool(&pIoCtxWrite->fComplete, true, false))
1919 {
1920 LogFlow(("Child write request completed\n"));
1921 Assert(pIoCtx->cbTransferLeft >= cbThisWrite);
1922 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbThisWrite);
[28683]1923 ASMAtomicWriteBool(&pDisk->fGrowing, false);
[28620]1924 vdIoCtxFree(pDisk, pIoCtxWrite);
1925
1926 rc = VINF_SUCCESS;
1927 }
1928 else
[30863]1929 {
[28620]1930 LogFlow(("Child write pending\n"));
[30863]1931 pIoCtx->fBlocked = true;
1932 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1933 cbWrite -= cbThisWrite;
1934 uOffset += cbThisWrite;
1935 break;
1936 }
[28065]1937 }
[27977]1938 }
1939
[31586]1940 if (rc == VERR_VD_IOCTX_HALT)
1941 {
1942 cbWrite -= cbThisWrite;
1943 uOffset += cbThisWrite;
1944 pIoCtx->fBlocked = true;
[30555]1945 break;
[31586]1946 }
1947 else if (rc == VERR_VD_NOT_ENOUGH_METADATA)
1948 break;
[30555]1949
[27977]1950 cbWrite -= cbThisWrite;
1951 uOffset += cbThisWrite;
[30555]1952 } while (cbWrite != 0 && (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS));
[27977]1953
[31586]1954 if ( rc == VERR_VD_ASYNC_IO_IN_PROGRESS
1955 || rc == VERR_VD_NOT_ENOUGH_METADATA
1956 || rc == VERR_VD_IOCTX_HALT)
[28620]1957 {
1958 /*
1959 * Tell the caller that we don't need to go back here because all
1960 * writes are initiated.
1961 */
1962 if (!cbWrite)
1963 rc = VINF_SUCCESS;
1964
1965 pIoCtx->uOffset = uOffset;
1966 pIoCtx->cbTransfer = cbWrite;
1967 }
1968
[27977]1969 return rc;
1970}
1971
[6291]1972/**
[28620]1973 * Flush helper async version.
1974 */
1975static int vdFlushHelperAsync(PVDIOCTX pIoCtx)
1976{
1977 int rc = VINF_SUCCESS;
1978 PVBOXHDD pDisk = pIoCtx->pDisk;
1979 PVDIMAGE pImage = pIoCtx->pImage;
1980
1981 vdResetModifiedFlag(pDisk);
[32536]1982 rc = pImage->Backend->pfnAsyncFlush(pImage->pBackendData, pIoCtx);
[30555]1983 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1984 rc = VINF_SUCCESS;
[28620]1985
1986 return rc;
1987}
1988
1989/**
[14852]1990 * internal: scans plugin directory and loads the backends have been found.
[7780]1991 */
[14852]1992static int vdLoadDynamicBackends()
[7780]1993{
[31504]1994#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
[7780]1995 int rc = VINF_SUCCESS;
1996 PRTDIR pPluginDir = NULL;
1997
[14852]1998 /* Enumerate plugin backends. */
1999 char szPath[RTPATH_MAX];
2000 rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
2001 if (RT_FAILURE(rc))
2002 return rc;
2003
2004 /* To get all entries with VBoxHDD as prefix. */
2005 char *pszPluginFilter;
[32370]2006 rc = RTStrAPrintf(&pszPluginFilter, "%s/%s*", szPath, VBOX_HDDFORMAT_PLUGIN_PREFIX);
[14852]2007 if (RT_FAILURE(rc))
[7780]2008 {
[14852]2009 rc = VERR_NO_MEMORY;
2010 return rc;
2011 }
[7780]2012
[14852]2013 PRTDIRENTRYEX pPluginDirEntry = NULL;
2014 size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
2015 /* The plugins are in the same directory as the other shared libs. */
2016 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
2017 if (RT_FAILURE(rc))
2018 {
2019 /* On Windows the above immediately signals that there are no
2020 * files matching, while on other platforms enumerating the
2021 * files below fails. Either way: no plugins. */
2022 goto out;
2023 }
2024
2025 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
2026 if (!pPluginDirEntry)
2027 {
2028 rc = VERR_NO_MEMORY;
2029 goto out;
2030 }
2031
[25292]2032 while ((rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) != VERR_NO_MORE_FILES)
[14852]2033 {
2034 RTLDRMOD hPlugin = NIL_RTLDRMOD;
2035 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad = NULL;
2036 PVBOXHDDBACKEND pBackend = NULL;
2037 char *pszPluginPath = NULL;
2038
2039 if (rc == VERR_BUFFER_OVERFLOW)
[7780]2040 {
[14852]2041 /* allocate new buffer. */
2042 RTMemFree(pPluginDirEntry);
2043 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
2044 /* Retry. */
[25292]2045 rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
[14852]2046 if (RT_FAILURE(rc))
[7780]2047 break;
2048 }
[14852]2049 else if (RT_FAILURE(rc))
[7780]2050 break;
2051
[14852]2052 /* We got the new entry. */
2053 if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
2054 continue;
[7780]2055
[14852]2056 /* Prepend the path to the libraries. */
2057 rc = RTStrAPrintf(&pszPluginPath, "%s/%s", szPath, pPluginDirEntry->szName);
[11266]2058 if (RT_FAILURE(rc))
[7780]2059 {
2060 rc = VERR_NO_MEMORY;
2061 break;
2062 }
2063
[14852]2064 rc = SUPR3HardenedLdrLoad(pszPluginPath, &hPlugin);
2065 if (RT_SUCCESS(rc))
[14344]2066 {
[14852]2067 rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad);
2068 if (RT_FAILURE(rc) || !pfnHDDFormatLoad)
[7780]2069 {
[14852]2070 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnHDDFormat=%#p\n", VBOX_HDDFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnHDDFormatLoad));
2071 if (RT_SUCCESS(rc))
2072 rc = VERR_SYMBOL_NOT_FOUND;
[7780]2073 }
2074
[11266]2075 if (RT_SUCCESS(rc))
[7780]2076 {
[14852]2077 /* Get the function table. */
2078 rc = pfnHDDFormatLoad(&pBackend);
2079 if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VBOXHDDBACKEND))
[7780]2080 {
[14852]2081 pBackend->hPlugin = hPlugin;
2082 vdAddBackend(pBackend);
[7780]2083 }
2084 else
[14852]2085 LogFunc(("ignored plugin '%s': pBackend->cbSize=%d rc=%Rrc\n", pszPluginPath, pBackend->cbSize, rc));
2086 }
2087 else
2088 LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszPluginPath, rc));
[7780]2089
[14852]2090 if (RT_FAILURE(rc))
[7780]2091 RTLdrClose(hPlugin);
2092 }
[14852]2093 RTStrFree(pszPluginPath);
2094 }
2095out:
2096 if (rc == VERR_NO_MORE_FILES)
[14344]2097 rc = VINF_SUCCESS;
[14852]2098 RTStrFree(pszPluginFilter);
2099 if (pPluginDirEntry)
2100 RTMemFree(pPluginDirEntry);
2101 if (pPluginDir)
2102 RTDirClose(pPluginDir);
2103 return rc;
[31504]2104#else
2105 return VINF_SUCCESS;
2106#endif
[14852]2107}
[7780]2108
[14852]2109/**
[32370]2110 * internal: scans plugin directory and loads the cache backends have been found.
2111 */
2112static int vdLoadDynamicCacheBackends()
2113{
2114#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
2115 int rc = VINF_SUCCESS;
2116 PRTDIR pPluginDir = NULL;
2117
2118 /* Enumerate plugin backends. */
2119 char szPath[RTPATH_MAX];
2120 rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
2121 if (RT_FAILURE(rc))
2122 return rc;
2123
2124 /* To get all entries with VBoxHDD as prefix. */
2125 char *pszPluginFilter;
2126 rc = RTStrAPrintf(&pszPluginFilter, "%s/%s*", szPath, VD_CACHEFORMAT_PLUGIN_PREFIX);
2127 if (RT_FAILURE(rc))
2128 {
2129 rc = VERR_NO_MEMORY;
2130 return rc;
2131 }
2132
2133 PRTDIRENTRYEX pPluginDirEntry = NULL;
2134 size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
2135 /* The plugins are in the same directory as the other shared libs. */
2136 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
2137 if (RT_FAILURE(rc))
2138 {
2139 /* On Windows the above immediately signals that there are no
2140 * files matching, while on other platforms enumerating the
2141 * files below fails. Either way: no plugins. */
2142 goto out;
2143 }
2144
2145 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
2146 if (!pPluginDirEntry)
2147 {
2148 rc = VERR_NO_MEMORY;
2149 goto out;
2150 }
2151
2152 while ((rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) != VERR_NO_MORE_FILES)
2153 {
2154 RTLDRMOD hPlugin = NIL_RTLDRMOD;
2155 PFNVDCACHEFORMATLOAD pfnVDCacheLoad = NULL;
2156 PVDCACHEBACKEND pBackend = NULL;
2157 char *pszPluginPath = NULL;
2158
2159 if (rc == VERR_BUFFER_OVERFLOW)
2160 {
2161 /* allocate new buffer. */
2162 RTMemFree(pPluginDirEntry);
2163 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
2164 /* Retry. */
2165 rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
2166 if (RT_FAILURE(rc))
2167 break;
2168 }
2169 else if (RT_FAILURE(rc))
2170 break;
2171
2172 /* We got the new entry. */
2173 if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
2174 continue;
2175
2176 /* Prepend the path to the libraries. */
2177 rc = RTStrAPrintf(&pszPluginPath, "%s/%s", szPath, pPluginDirEntry->szName);
2178 if (RT_FAILURE(rc))
2179 {
2180 rc = VERR_NO_MEMORY;
2181 break;
2182 }
2183
2184 rc = SUPR3HardenedLdrLoad(pszPluginPath, &hPlugin);
2185 if (RT_SUCCESS(rc))
2186 {
2187 rc = RTLdrGetSymbol(hPlugin, VD_CACHEFORMAT_LOAD_NAME, (void**)&pfnVDCacheLoad);
2188 if (RT_FAILURE(rc) || !pfnVDCacheLoad)
2189 {
2190 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnVDCacheLoad=%#p\n",
2191 VD_CACHEFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnVDCacheLoad));
2192 if (RT_SUCCESS(rc))
2193 rc = VERR_SYMBOL_NOT_FOUND;
2194 }
2195
2196 if (RT_SUCCESS(rc))
2197 {
2198 /* Get the function table. */
2199 rc = pfnVDCacheLoad(&pBackend);
2200 if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VDCACHEBACKEND))
2201 {
2202 pBackend->hPlugin = hPlugin;
2203 vdAddCacheBackend(pBackend);
2204 }
2205 else
2206 LogFunc(("ignored plugin '%s': pBackend->cbSize=%d rc=%Rrc\n", pszPluginPath, pBackend->cbSize, rc));
2207 }
2208 else
2209 LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszPluginPath, rc));
2210
2211 if (RT_FAILURE(rc))
2212 RTLdrClose(hPlugin);
2213 }
2214 RTStrFree(pszPluginPath);
2215 }
2216out:
2217 if (rc == VERR_NO_MORE_FILES)
2218 rc = VINF_SUCCESS;
2219 RTStrFree(pszPluginFilter);
2220 if (pPluginDirEntry)
2221 RTMemFree(pPluginDirEntry);
2222 if (pPluginDir)
2223 RTDirClose(pPluginDir);
2224 return rc;
2225#else
2226 return VINF_SUCCESS;
2227#endif
2228}
2229
2230/**
[22966]2231 * VD async I/O interface open callback.
2232 */
[32691]2233static int vdIOOpenFallback(void *pvUser, const char *pszLocation,
2234 uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
[32553]2235 void **ppStorage)
[22966]2236{
[32553]2237 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)RTMemAllocZ(sizeof(VDIIOFALLBACKSTORAGE));
[22966]2238
2239 if (!pStorage)
2240 return VERR_NO_MEMORY;
2241
2242 pStorage->pfnCompleted = pfnCompleted;
2243
2244 /* Open the file. */
[23973]2245 int rc = RTFileOpen(&pStorage->File, pszLocation, fOpen);
[22966]2246 if (RT_SUCCESS(rc))
2247 {
2248 *ppStorage = pStorage;
2249 return VINF_SUCCESS;
2250 }
2251
2252 RTMemFree(pStorage);
2253 return rc;
2254}
2255
2256/**
2257 * VD async I/O interface close callback.
2258 */
[32553]2259static int vdIOCloseFallback(void *pvUser, void *pvStorage)
[22966]2260{
[32553]2261 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
[22966]2262
2263 RTFileClose(pStorage->File);
2264 RTMemFree(pStorage);
2265 return VINF_SUCCESS;
2266}
2267
[32553]2268static int vdIODeleteFallback(void *pvUser, const char *pcszFilename)
2269{
2270 return RTFileDelete(pcszFilename);
2271}
2272
2273static int vdIOMoveFallback(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
2274{
2275 return RTFileMove(pcszSrc, pcszDst, fMove);
2276}
2277
2278static int vdIOGetFreeSpaceFallback(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
2279{
2280 return RTFsQuerySizes(pcszFilename, NULL, pcbFreeSpace, NULL, NULL);
2281}
2282
2283static int vdIOGetModificationTimeFallback(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
2284{
2285 RTFSOBJINFO info;
2286 int rc = RTPathQueryInfo(pcszFilename, &info, RTFSOBJATTRADD_NOTHING);
2287 if (RT_SUCCESS(rc))
2288 *pModificationTime = info.ModificationTime;
2289 return rc;
2290}
2291
[22966]2292/**
2293 * VD async I/O interface callback for retrieving the file size.
2294 */
[32553]2295static int vdIOGetSizeFallback(void *pvUser, void *pvStorage, uint64_t *pcbSize)
[22966]2296{
[32553]2297 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
[22966]2298
2299 return RTFileGetSize(pStorage->File, pcbSize);
2300}
2301
2302/**
2303 * VD async I/O interface callback for setting the file size.
2304 */
[32553]2305static int vdIOSetSizeFallback(void *pvUser, void *pvStorage, uint64_t cbSize)
[22966]2306{
[32553]2307 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
[22966]2308
2309 return RTFileSetSize(pStorage->File, cbSize);
2310}
2311
2312/**
2313 * VD async I/O interface callback for a synchronous write to the file.
2314 */
[32553]2315static int vdIOWriteSyncFallback(void *pvUser, void *pvStorage, uint64_t uOffset,
[32536]2316 const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
[22966]2317{
[32553]2318 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
[22966]2319
2320 return RTFileWriteAt(pStorage->File, uOffset, pvBuf, cbWrite, pcbWritten);
2321}
2322
2323/**
2324 * VD async I/O interface callback for a synchronous read from the file.
2325 */
[32553]2326static int vdIOReadSyncFallback(void *pvUser, void *pvStorage, uint64_t uOffset,
[32536]2327 void *pvBuf, size_t cbRead, size_t *pcbRead)
[22966]2328{
[32553]2329 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
[22966]2330
2331 return RTFileReadAt(pStorage->File, uOffset, pvBuf, cbRead, pcbRead);
2332}
2333
2334/**
2335 * VD async I/O interface callback for a synchronous flush of the file data.
2336 */
[32553]2337static int vdIOFlushSyncFallback(void *pvUser, void *pvStorage)
[22966]2338{
[32553]2339 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
[22966]2340
2341 return RTFileFlush(pStorage->File);
2342}
2343
2344/**
2345 * VD async I/O interface callback for a asynchronous read from the file.
2346 */
[32553]2347static int vdIOReadAsyncFallback(void *pvUser, void *pStorage, uint64_t uOffset,
2348 PCRTSGSEG paSegments, size_t cSegments,
2349 size_t cbRead, void *pvCompletion,
2350 void **ppTask)
[22966]2351{
2352 return VERR_NOT_IMPLEMENTED;
2353}
2354
2355/**
2356 * VD async I/O interface callback for a asynchronous write to the file.
2357 */
[32553]2358static int vdIOWriteAsyncFallback(void *pvUser, void *pStorage, uint64_t uOffset,
2359 PCRTSGSEG paSegments, size_t cSegments,
2360 size_t cbWrite, void *pvCompletion,
2361 void **ppTask)
[22966]2362{
2363 return VERR_NOT_IMPLEMENTED;
2364}
2365
2366/**
2367 * VD async I/O interface callback for a asynchronous flush of the file data.
2368 */
[32553]2369static int vdIOFlushAsyncFallback(void *pvUser, void *pStorage,
2370 void *pvCompletion, void **ppTask)
[22966]2371{
2372 return VERR_NOT_IMPLEMENTED;
2373}
2374
[30555]2375/**
2376 * Internal - Continues an I/O context after
2377 * it was halted because of an active transfer.
2378 */
2379static int vdIoCtxContinue(PVDIOCTX pIoCtx, int rcReq)
[27808]2380{
[30555]2381 PVBOXHDD pDisk = pIoCtx->pDisk;
[28620]2382 int rc = VINF_SUCCESS;
[27977]2383
[29240]2384 if (RT_FAILURE(rcReq))
2385 ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rcReq, VINF_SUCCESS);
2386
[28683]2387 if (!pIoCtx->fBlocked)
2388 {
2389 /* Continue the transfer */
2390 rc = vdIoCtxProcess(pIoCtx);
[28620]2391
[28683]2392 if ( rc == VINF_VD_ASYNC_IO_FINISHED
2393 && ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
[28154]2394 {
[28683]2395 LogFlowFunc(("I/O context completed pIoCtx=%#p\n", pIoCtx));
2396 if (pIoCtx->pIoCtxParent)
2397 {
2398 PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
[28154]2399
[28683]2400 LogFlowFunc(("I/O context transfered %u bytes for the parent pIoCtxParent=%p\n",
2401 pIoCtx->Type.Child.cbTransferParent, pIoCtxParent));
[28620]2402
[28683]2403 /* Update the parent state. */
2404 Assert(!pIoCtxParent->pIoCtxParent);
2405 Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE);
[30863]2406 Assert(pIoCtxParent->cbTransferLeft >= pIoCtx->Type.Child.cbTransferParent);
[28683]2407 ASMAtomicSubU32(&pIoCtxParent->cbTransferLeft, pIoCtx->Type.Child.cbTransferParent);
[28154]2408
[29240]2409 if (RT_FAILURE(pIoCtx->rcReq))
2410 ASMAtomicCmpXchgS32(&pIoCtxParent->rcReq, pIoCtx->rcReq, VINF_SUCCESS);
[29497]2411
[28620]2412 /*
[30863]2413 * A completed child write means that we finished growing the image.
[28620]2414 * We have to process any pending writes now.
2415 */
2416 Assert(pDisk->fGrowing);
[28683]2417 ASMAtomicWriteBool(&pDisk->fGrowing, false);
[28620]2418
[30863]2419 /* Unblock the parent */
2420 pIoCtxParent->fBlocked = false;
2421
[28683]2422 rc = vdIoCtxProcess(pIoCtxParent);
2423
2424 if ( rc == VINF_VD_ASYNC_IO_FINISHED
2425 && ASMAtomicCmpXchgBool(&pIoCtxParent->fComplete, true, false))
2426 {
[30555]2427 LogFlowFunc(("Parent I/O context completed pIoCtxParent=%#p rcReq=%Rrc\n", pIoCtxParent, pIoCtxParent->rcReq));
[28784]2428 pIoCtxParent->Type.Root.pfnComplete(pIoCtxParent->Type.Root.pvUser1,
2429 pIoCtxParent->Type.Root.pvUser2,
2430 pIoCtxParent->rcReq);
[29006]2431 vdThreadFinishWrite(pDisk);
[28683]2432 vdIoCtxFree(pDisk, pIoCtxParent);
2433 }
2434
[30863]2435 /* Process any pending writes if the current request didn't caused another growing. */
[28620]2436 RTCritSectEnter(&pDisk->CritSect);
2437
[30863]2438 if (!RTListIsEmpty(&pDisk->ListWriteGrowing) && !pDisk->fGrowing)
[28620]2439 {
2440 RTLISTNODE ListTmp;
2441
[28683]2442 LogFlowFunc(("Before: pNext=%#p pPrev=%#p\n", pDisk->ListWriteGrowing.pNext,
2443 pDisk->ListWriteGrowing.pPrev));
2444
[28620]2445 RTListMove(&ListTmp, &pDisk->ListWriteGrowing);
2446
[28683]2447 LogFlowFunc(("After: pNext=%#p pPrev=%#p\n", pDisk->ListWriteGrowing.pNext,
2448 pDisk->ListWriteGrowing.pPrev));
2449
[28620]2450 RTCritSectLeave(&pDisk->CritSect);
2451
2452 /* Process the list. */
[28683]2453 do
[28620]2454 {
[30555]2455 PVDIOCTXDEFERRED pDeferred = RTListNodeGetFirst(&ListTmp, VDIOCTXDEFERRED, NodeDeferred);
2456 PVDIOCTX pIoCtxWait = pDeferred->pIoCtx;
2457
[28683]2458 AssertPtr(pIoCtxWait);
2459
[30555]2460 RTListNodeRemove(&pDeferred->NodeDeferred);
2461 RTMemFree(pDeferred);
[28683]2462
[28620]2463 Assert(!pIoCtxWait->pIoCtxParent);
2464
[30863]2465 pIoCtxWait->fBlocked = false;
[28620]2466 LogFlowFunc(("Processing waiting I/O context pIoCtxWait=%#p\n", pIoCtxWait));
2467
2468 rc = vdIoCtxProcess(pIoCtxWait);
2469 if ( rc == VINF_VD_ASYNC_IO_FINISHED
2470 && ASMAtomicCmpXchgBool(&pIoCtxWait->fComplete, true, false))
2471 {
2472 LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
[29006]2473 vdThreadFinishWrite(pDisk);
[28620]2474 pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
[28784]2475 pIoCtxWait->Type.Root.pvUser2,
2476 pIoCtxWait->rcReq);
[28620]2477 vdIoCtxFree(pDisk, pIoCtxWait);
2478 }
[28683]2479 } while (!RTListIsEmpty(&ListTmp));
[28620]2480 }
2481 else
2482 RTCritSectLeave(&pDisk->CritSect);
[28154]2483 }
[28683]2484 else
[29006]2485 {
[32520]2486 if (pIoCtx->enmTxDir != VDIOCTXTXDIR_READ)
2487 {
2488 AssertMsg( pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE
2489 || pIoCtx->enmTxDir == VDIOCTXTXDIR_FLUSH,
2490 ("Invalid transfer direction\n"));
[29006]2491 vdThreadFinishWrite(pDisk);
[32520]2492 }
[29006]2493 else
2494 vdThreadFinishRead(pDisk);
2495
[30555]2496 LogFlowFunc(("I/O context completed pIoCtx=%#p rcReq=%Rrc\n", pIoCtx, pIoCtx->rcReq));
[28784]2497 pIoCtx->Type.Root.pfnComplete(pIoCtx->Type.Root.pvUser1,
2498 pIoCtx->Type.Root.pvUser2,
2499 pIoCtx->rcReq);
[29006]2500 }
[28683]2501
2502 vdIoCtxFree(pDisk, pIoCtx);
[28154]2503 }
[27977]2504 }
2505
[27808]2506 return VINF_SUCCESS;
2507}
2508
[22966]2509/**
[30555]2510 * Internal - Called when user transfer completed.
2511 */
2512static int vdUserXferCompleted(PVDIOSTORAGE pIoStorage, PVDIOCTX pIoCtx,
2513 PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
2514 size_t cbTransfer, int rcReq)
2515{
2516 int rc = VINF_SUCCESS;
2517 bool fIoCtxContinue = true;
2518 PVBOXHDD pDisk = pIoCtx->pDisk;
2519
2520 LogFlowFunc(("pIoStorage=%#p pIoCtx=%#p pfnComplete=%#p pvUser=%#p cbTransfer=%zu rcReq=%Rrc\n",
2521 pIoStorage, pIoCtx, pfnComplete, pvUser, cbTransfer, rcReq));
2522
[30863]2523 Assert(pIoCtx->cbTransferLeft >= cbTransfer);
[30555]2524 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTransfer);
2525 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
2526
2527 if (pfnComplete)
[30863]2528 {
2529 RTCritSectEnter(&pDisk->CritSect);
[32536]2530 rc = pfnComplete(pIoStorage->pImage->pBackendData, pIoCtx, pvUser, rcReq);
[30863]2531 RTCritSectLeave(&pDisk->CritSect);
2532 }
[30555]2533
2534 if (RT_SUCCESS(rc))
2535 rc = vdIoCtxContinue(pIoCtx, rcReq);
2536 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2537 rc = VINF_SUCCESS;
2538
2539 return rc;
2540}
2541
2542/**
2543 * Internal - Called when a meta transfer completed.
2544 */
2545static int vdMetaXferCompleted(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
2546 PVDMETAXFER pMetaXfer, int rcReq)
2547{
2548 PVBOXHDD pDisk = pIoStorage->pImage->pDisk;
2549 RTLISTNODE ListIoCtxWaiting;
[30863]2550 bool fFlush;
[30555]2551
2552 LogFlowFunc(("pIoStorage=%#p pfnComplete=%#p pvUser=%#p pMetaXfer=%#p rcReq=%Rrc\n",
2553 pIoStorage, pfnComplete, pvUser, pMetaXfer, rcReq));
2554
[30863]2555 RTCritSectEnter(&pDisk->CritSect);
2556 fFlush = VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_FLUSH;
[30555]2557 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
2558
2559 if (!fFlush)
2560 {
2561 RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
2562
2563 if (RT_FAILURE(rcReq))
2564 {
2565 /* Remove from the AVL tree. */
[30863]2566 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
[32386]2567 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
[30555]2568 Assert(fRemoved);
2569 RTMemFree(pMetaXfer);
2570 }
2571 else
2572 {
2573 /* Increase the reference counter to make sure it doesn't go away before the last context is processed. */
2574 pMetaXfer->cRefs++;
2575 }
2576 }
2577 else
2578 RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
[30863]2579 RTCritSectLeave(&pDisk->CritSect);
[30555]2580
2581 /* Go through the waiting list and continue the I/O contexts. */
2582 while (!RTListIsEmpty(&ListIoCtxWaiting))
2583 {
2584 int rc = VINF_SUCCESS;
2585 bool fContinue = true;
2586 PVDIOCTXDEFERRED pDeferred = RTListNodeGetFirst(&ListIoCtxWaiting, VDIOCTXDEFERRED, NodeDeferred);
2587 PVDIOCTX pIoCtx = pDeferred->pIoCtx;
2588 RTListNodeRemove(&pDeferred->NodeDeferred);
2589
2590 RTMemFree(pDeferred);
2591 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
2592
2593 if (pfnComplete)
[30863]2594 {
2595 RTCritSectEnter(&pDisk->CritSect);
[32536]2596 rc = pfnComplete(pIoStorage->pImage->pBackendData, pIoCtx, pvUser, rcReq);
[30863]2597 RTCritSectLeave(&pDisk->CritSect);
2598 }
[30555]2599
2600 LogFlow(("Completion callback for I/O context %#p returned %Rrc\n", pIoCtx, rc));
2601
2602 if (RT_SUCCESS(rc))
2603 {
2604 rc = vdIoCtxContinue(pIoCtx, rcReq);
2605 AssertRC(rc);
2606 }
2607 else
2608 Assert(rc == VERR_VD_ASYNC_IO_IN_PROGRESS);
2609 }
2610
2611 /* Remove if not used anymore. */
2612 if (RT_SUCCESS(rcReq) && !fFlush)
2613 {
2614 RTCritSectEnter(&pDisk->CritSect);
2615 pMetaXfer->cRefs--;
[30863]2616 if (!pMetaXfer->cRefs && RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting))
[30555]2617 {
2618 /* Remove from the AVL tree. */
[30863]2619 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
[32386]2620 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
[30555]2621 Assert(fRemoved);
2622 RTMemFree(pMetaXfer);
2623 }
2624 RTCritSectLeave(&pDisk->CritSect);
2625 }
2626 else if (fFlush)
2627 RTMemFree(pMetaXfer);
2628
2629 return VINF_SUCCESS;
2630}
2631
[32553]2632static int vdIOIntReqCompleted(void *pvUser, int rcReq)
[30555]2633{
2634 int rc = VINF_SUCCESS;
2635 PVDIOTASK pIoTask = (PVDIOTASK)pvUser;
2636 PVDIOSTORAGE pIoStorage = pIoTask->pIoStorage;
2637
2638 LogFlowFunc(("Task completed pIoTask=%#p\n", pIoTask));
2639
2640 if (!pIoTask->fMeta)
2641 rc = vdUserXferCompleted(pIoStorage, pIoTask->Type.User.pIoCtx,
2642 pIoTask->pfnComplete, pIoTask->pvUser,
2643 pIoTask->Type.User.cbTransfer, rcReq);
2644 else
2645 rc = vdMetaXferCompleted(pIoStorage, pIoTask->pfnComplete, pIoTask->pvUser,
2646 pIoTask->Type.Meta.pMetaXfer, rcReq);
2647
2648 vdIoTaskFree(pIoStorage->pImage->pDisk, pIoTask);
2649
2650 return rc;
2651}
2652
2653/**
[27808]2654 * VD I/O interface callback for opening a file.
2655 */
[32553]2656static int vdIOIntOpen(void *pvUser, const char *pszLocation,
[32691]2657 unsigned uOpenFlags, PPVDIOSTORAGE ppIoStorage)
[27808]2658{
2659 int rc = VINF_SUCCESS;
[28620]2660 PVDIMAGE pImage = (PVDIMAGE)pvUser;
[27808]2661 PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
2662
2663 if (!pIoStorage)
2664 return VERR_NO_MEMORY;
2665
[30555]2666 pIoStorage->pImage = pImage;
2667
2668 /* Create the AVl tree. */
2669 pIoStorage->pTreeMetaXfers = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
2670 if (pIoStorage->pTreeMetaXfers)
2671 {
[32691]2672 rc = pImage->pInterfaceIOCallbacks->pfnOpen(pImage->pInterfaceIO->pvUser,
2673 pszLocation, uOpenFlags,
2674 vdIOIntReqCompleted,
2675 &pIoStorage->pStorage);
[30555]2676 if (RT_SUCCESS(rc))
2677 {
2678 *ppIoStorage = pIoStorage;
2679 return VINF_SUCCESS;
2680 }
2681
2682 RTMemFree(pIoStorage->pTreeMetaXfers);
2683 }
[27808]2684 else
[30555]2685 rc = VERR_NO_MEMORY;
[27808]2686
[30555]2687 RTMemFree(pIoStorage);
[27808]2688 return rc;
2689}
2690
[32553]2691static int vdIOIntTreeMetaXferDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
[30555]2692{
2693 AssertMsgFailed(("Tree should be empty at this point!\n"));
2694 return VINF_SUCCESS;
2695}
2696
[32553]2697static int vdIOIntClose(void *pvUser, PVDIOSTORAGE pIoStorage)
[27808]2698{
[28620]2699 PVDIMAGE pImage = (PVDIMAGE)pvUser;
[27808]2700
[32691]2701 int rc = pImage->pInterfaceIOCallbacks->pfnClose(pImage->pInterfaceIO->pvUser,
2702 pIoStorage->pStorage);
[27808]2703 AssertRC(rc);
2704
[32553]2705 RTAvlrFileOffsetDestroy(pIoStorage->pTreeMetaXfers, vdIOIntTreeMetaXferDestroy, NULL);
[30555]2706 RTMemFree(pIoStorage->pTreeMetaXfers);
[27808]2707 RTMemFree(pIoStorage);
2708 return VINF_SUCCESS;
2709}
2710
[32553]2711static int vdIOIntDelete(void *pvUser, const char *pcszFilename)
[32536]2712{
[32691]2713 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2714 return pImage->pInterfaceIOCallbacks->pfnDelete(pImage->pInterfaceIO->pvUser,
2715 pcszFilename);
[32536]2716}
2717
[32691]2718static int vdIOIntMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
2719 unsigned fMove)
[32536]2720{
[32691]2721 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2722 return pImage->pInterfaceIOCallbacks->pfnMove(pImage->pInterfaceIO->pvUser,
2723 pcszSrc, pcszDst, fMove);
[32536]2724}
2725
[32691]2726static int vdIOIntGetFreeSpace(void *pvUser, const char *pcszFilename,
2727 int64_t *pcbFreeSpace)
[32536]2728{
[32691]2729 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2730 return pImage->pInterfaceIOCallbacks->pfnGetFreeSpace(pImage->pInterfaceIO->pvUser,
2731 pcszFilename,
2732 pcbFreeSpace);
[32536]2733}
2734
[32691]2735static int vdIOIntGetModificationTime(void *pvUser, const char *pcszFilename,
2736 PRTTIMESPEC pModificationTime)
[32536]2737{
[32691]2738 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2739 return pImage->pInterfaceIOCallbacks->pfnGetModificationTime(pImage->pInterfaceIO->pvUser,
2740 pcszFilename,
2741 pModificationTime);
[32536]2742}
2743
[32553]2744static int vdIOIntGetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
[32691]2745 uint64_t *pcbSize)
[27808]2746{
[28620]2747 PVDIMAGE pImage = (PVDIMAGE)pvUser;
[27808]2748
[32691]2749 return pImage->pInterfaceIOCallbacks->pfnGetSize(pImage->pInterfaceIO->pvUser,
[32553]2750 pIoStorage->pStorage,
2751 pcbSize);
[27808]2752}
2753
[32553]2754static int vdIOIntSetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
[32691]2755 uint64_t cbSize)
[27808]2756{
[28620]2757 PVDIMAGE pImage = (PVDIMAGE)pvUser;
[27808]2758
[32691]2759 return pImage->pInterfaceIOCallbacks->pfnSetSize(pImage->pInterfaceIO->pvUser,
2760 pIoStorage->pStorage,
2761 cbSize);
[27808]2762}
2763
[32691]2764static int vdIOIntWriteSync(void *pvUser, PVDIOSTORAGE pIoStorage,
2765 uint64_t uOffset, const void *pvBuf,
2766 size_t cbWrite, size_t *pcbWritten)
[27808]2767{
[28620]2768 PVDIMAGE pImage = (PVDIMAGE)pvUser;
[27808]2769
[32691]2770 return pImage->pInterfaceIOCallbacks->pfnWriteSync(pImage->pInterfaceIO->pvUser,
2771 pIoStorage->pStorage,
2772 uOffset, pvBuf, cbWrite,
2773 pcbWritten);
[27808]2774}
2775
[32691]2776static int vdIOIntReadSync(void *pvUser, PVDIOSTORAGE pIoStorage,
2777 uint64_t uOffset, void *pvBuf, size_t cbRead,
2778 size_t *pcbRead)
[27808]2779{
[28620]2780 PVDIMAGE pImage = (PVDIMAGE)pvUser;
[27808]2781
[32691]2782 return pImage->pInterfaceIOCallbacks->pfnReadSync(pImage->pInterfaceIO->pvUser,
2783 pIoStorage->pStorage,
2784 uOffset, pvBuf, cbRead,
2785 pcbRead);
[27808]2786}
2787
[32553]2788static int vdIOIntFlushSync(void *pvUser, PVDIOSTORAGE pIoStorage)
[27808]2789{
[28620]2790 PVDIMAGE pImage = (PVDIMAGE)pvUser;
[27808]2791
[32691]2792 return pImage->pInterfaceIOCallbacks->pfnFlushSync(pImage->pInterfaceIO->pvUser,
2793 pIoStorage->pStorage);
[27808]2794}
2795
[32553]2796static int vdIOIntReadUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
[32691]2797 uint64_t uOffset, PVDIOCTX pIoCtx,
2798 size_t cbRead)
[27808]2799{
[27977]2800 int rc = VINF_SUCCESS;
[28620]2801 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2802 PVBOXHDD pDisk = pImage->pDisk;
[27977]2803
[28620]2804 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbRead=%u\n",
2805 pvUser, pIoStorage, uOffset, pIoCtx, cbRead));
2806
[30555]2807 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
2808
[27977]2809 /* Build the S/G array and spawn a new I/O task */
2810 while (cbRead)
2811 {
[28065]2812 RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
2813 unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
2814 size_t cbTaskRead = 0;
[27977]2815
[28065]2816 cbTaskRead = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbRead);
[27977]2817
[28620]2818 AssertMsg(cbTaskRead <= cbRead, ("Invalid number of bytes to read\n"));
2819
2820 LogFlow(("Reading %u bytes into %u segments\n", cbTaskRead, cSegments));
2821
[30555]2822#ifdef RT_STRICT
[28620]2823 for (unsigned i = 0; i < cSegments; i++)
2824 AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
2825 ("Segment %u is invalid\n", i));
2826#endif
2827
[30555]2828 PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, NULL, NULL, pIoCtx, cbTaskRead);
[27977]2829
2830 if (!pIoTask)
2831 return VERR_NO_MEMORY;
2832
[29240]2833 ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
2834
[27977]2835 void *pvTask;
[32691]2836 rc = pImage->pInterfaceIOCallbacks->pfnReadAsync(pImage->pInterfaceIO->pvUser,
2837 pIoStorage->pStorage,
2838 uOffset, aSeg, cSegments,
2839 cbTaskRead, pIoTask,
2840 &pvTask);
[30555]2841 if (RT_SUCCESS(rc))
[28065]2842 {
2843 AssertMsg(cbTaskRead <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
2844 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskRead);
[29240]2845 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
[27977]2846 vdIoTaskFree(pDisk, pIoTask);
[28065]2847 }
[30555]2848 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
[28065]2849 {
[30555]2850 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
[28065]2851 break;
2852 }
[27977]2853
2854 uOffset += cbTaskRead;
[28108]2855 cbRead -= cbTaskRead;
[27977]2856 }
2857
[30555]2858 LogFlowFunc(("returns rc=%Rrc\n", rc));
[27977]2859 return rc;
[27808]2860}
2861
[32553]2862static int vdIOIntWriteUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
[32691]2863 uint64_t uOffset, PVDIOCTX pIoCtx,
2864 size_t cbWrite,
2865 PFNVDXFERCOMPLETED pfnComplete,
2866 void *pvCompleteUser)
[27808]2867{
[27977]2868 int rc = VINF_SUCCESS;
[28620]2869 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2870 PVBOXHDD pDisk = pImage->pDisk;
[27977]2871
[28620]2872 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbWrite=%u\n",
2873 pvUser, pIoStorage, uOffset, pIoCtx, cbWrite));
2874
[30555]2875 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
2876
[27977]2877 /* Build the S/G array and spawn a new I/O task */
2878 while (cbWrite)
2879 {
[28065]2880 RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
2881 unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
2882 size_t cbTaskWrite = 0;
[27977]2883
[28065]2884 cbTaskWrite = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbWrite);
[27977]2885
[28620]2886 AssertMsg(cbTaskWrite <= cbWrite, ("Invalid number of bytes to write\n"));
2887
2888 LogFlow(("Writing %u bytes from %u segments\n", cbTaskWrite, cSegments));
2889
2890#ifdef DEBUG
2891 for (unsigned i = 0; i < cSegments; i++)
2892 AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
2893 ("Segment %u is invalid\n", i));
2894#endif
2895
[30555]2896 PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, pfnComplete, pvCompleteUser, pIoCtx, cbTaskWrite);
[27977]2897
2898 if (!pIoTask)
2899 return VERR_NO_MEMORY;
2900
[29240]2901 ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
2902
[27977]2903 void *pvTask;
[32691]2904 rc = pImage->pInterfaceIOCallbacks->pfnWriteAsync(pImage->pInterfaceIO->pvUser,
2905 pIoStorage->pStorage,
2906 uOffset, aSeg, cSegments,
2907 cbTaskWrite, pIoTask,
2908 &pvTask);
[30555]2909 if (RT_SUCCESS(rc))
[28065]2910 {
2911 AssertMsg(cbTaskWrite <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
2912 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskWrite);
[29240]2913 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
[27977]2914 vdIoTaskFree(pDisk, pIoTask);
[28065]2915 }
[30555]2916 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
[28065]2917 {
[30555]2918 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
[28065]2919 break;
2920 }
[27977]2921
2922 uOffset += cbTaskWrite;
[28108]2923 cbWrite -= cbTaskWrite;
[27977]2924 }
2925
2926 return rc;
[27808]2927}
2928
[32553]2929static int vdIOIntReadMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
[32691]2930 uint64_t uOffset, void *pvBuf,
2931 size_t cbRead, PVDIOCTX pIoCtx,
2932 PPVDMETAXFER ppMetaXfer,
2933 PFNVDXFERCOMPLETED pfnComplete,
2934 void *pvCompleteUser)
[27808]2935{
[28620]2936 PVDIMAGE pImage = (PVDIMAGE)pvUser;
2937 PVBOXHDD pDisk = pImage->pDisk;
[28065]2938 int rc = VINF_SUCCESS;
2939 RTSGSEG Seg;
2940 PVDIOTASK pIoTask;
[30555]2941 PVDMETAXFER pMetaXfer = NULL;
[28065]2942 void *pvTask = NULL;
2943
[30555]2944 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbRead=%u\n",
2945 pvUser, pIoStorage, uOffset, pvBuf, cbRead));
[28065]2946
[30555]2947 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
[28065]2948
[30555]2949 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
2950 if (!pMetaXfer)
2951 {
[30863]2952#ifdef RT_STRICT
2953 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGetBestFit(pIoStorage->pTreeMetaXfers, uOffset, false /* fAbove */);
[32370]2954 AssertMsg(!pMetaXfer || (pMetaXfer->Core.Key + (RTFOFF)pMetaXfer->cbMeta <= (RTFOFF)uOffset),
[30863]2955 ("Overlapping meta transfers!\n"));
2956#endif
2957
[30555]2958 /* Allocate a new meta transfer. */
2959 pMetaXfer = vdMetaXferAlloc(pImage, pIoStorage, uOffset, cbRead);
2960 if (!pMetaXfer)
2961 return VERR_NO_MEMORY;
[28065]2962
[30863]2963 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
[30555]2964 if (!pIoTask)
2965 {
2966 RTMemFree(pMetaXfer);
2967 return VERR_NO_MEMORY;
2968 }
2969
2970 Seg.cbSeg = cbRead;
2971 Seg.pvSeg = pMetaXfer->abData;
2972
2973 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_READ);
[32691]2974 rc = pImage->pInterfaceIOCallbacks->pfnReadAsync(pImage->pInterfaceIO->pvUser,
2975 pIoStorage->pStorage,
2976 uOffset, &Seg, 1,
2977 cbRead, pIoTask,
2978 &pvTask);
[30555]2979
[30863]2980 if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
[30555]2981 {
2982 bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
2983 Assert(fInserted);
2984 }
2985 else
2986 RTMemFree(pMetaXfer);
[30863]2987
2988 if (RT_SUCCESS(rc))
2989 {
2990 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
2991 vdIoTaskFree(pDisk, pIoTask);
2992 }
2993 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS && !pfnComplete)
2994 rc = VERR_VD_NOT_ENOUGH_METADATA;
[30555]2995 }
2996
2997 Assert(VALID_PTR(pMetaXfer) || RT_FAILURE(rc));
2998
[30863]2999 if (RT_SUCCESS(rc) || rc == VERR_VD_NOT_ENOUGH_METADATA || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
[28065]3000 {
[30555]3001 /* If it is pending add the request to the list. */
3002 if (VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_READ)
3003 {
3004 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
3005 AssertPtr(pDeferred);
3006
3007 RTListInit(&pDeferred->NodeDeferred);
3008 pDeferred->pIoCtx = pIoCtx;
3009
3010 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
3011 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
3012 rc = VERR_VD_NOT_ENOUGH_METADATA;
3013 }
3014 else
3015 {
3016 /* Transfer the data. */
3017 pMetaXfer->cRefs++;
3018 Assert(pMetaXfer->cbMeta >= cbRead);
[30863]3019 Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
[30555]3020 memcpy(pvBuf, pMetaXfer->abData, cbRead);
3021 *ppMetaXfer = pMetaXfer;
3022 }
[28065]3023 }
3024
3025 return rc;
[27808]3026}
3027
[32553]3028static int vdIOIntWriteMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
[32691]3029 uint64_t uOffset, void *pvBuf,
3030 size_t cbWrite, PVDIOCTX pIoCtx,
3031 PFNVDXFERCOMPLETED pfnComplete,
3032 void *pvCompleteUser)
[27808]3033{
[28620]3034 PVDIMAGE pImage = (PVDIMAGE)pvUser;
3035 PVBOXHDD pDisk = pImage->pDisk;
[28065]3036 int rc = VINF_SUCCESS;
3037 RTSGSEG Seg;
3038 PVDIOTASK pIoTask;
[30555]3039 PVDMETAXFER pMetaXfer = NULL;
[30863]3040 bool fInTree = false;
[28065]3041 void *pvTask = NULL;
3042
[30555]3043 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbWrite=%u\n",
3044 pvUser, pIoStorage, uOffset, pvBuf, cbWrite));
[28065]3045
[30555]3046 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
[28065]3047
[30555]3048 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
3049 if (!pMetaXfer)
3050 {
3051 /* Allocate a new meta transfer. */
3052 pMetaXfer = vdMetaXferAlloc(pImage, pIoStorage, uOffset, cbWrite);
3053 if (!pMetaXfer)
3054 return VERR_NO_MEMORY;
[30863]3055 }
3056 else
3057 {
3058 Assert(pMetaXfer->cbMeta >= cbWrite);
3059 Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
3060 fInTree = true;
3061 }
[28065]3062
[30863]3063 Assert(VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE);
[30555]3064
[30863]3065 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
3066 if (!pIoTask)
3067 {
3068 RTMemFree(pMetaXfer);
3069 return VERR_NO_MEMORY;
3070 }
[30555]3071
[30863]3072 memcpy(pMetaXfer->abData, pvBuf, cbWrite);
3073 Seg.cbSeg = cbWrite;
3074 Seg.pvSeg = pMetaXfer->abData;
[30555]3075
[30863]3076 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
3077
3078 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_WRITE);
[32691]3079 rc = pImage->pInterfaceIOCallbacks->pfnWriteAsync(pImage->pInterfaceIO->pvUser,
3080 pIoStorage->pStorage,
3081 uOffset, &Seg, 1,
3082 cbWrite, pIoTask,
3083 &pvTask);
[30863]3084 if (RT_SUCCESS(rc))
3085 {
3086 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
3087 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
3088 vdIoTaskFree(pDisk, pIoTask);
3089 if (fInTree && !pMetaXfer->cRefs)
[30555]3090 {
[30863]3091 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
[32386]3092 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
[30863]3093 AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
[30555]3094 RTMemFree(pMetaXfer);
3095 pMetaXfer = NULL;
3096 }
[30863]3097 }
3098 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3099 {
3100 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
3101 AssertPtr(pDeferred);
[30555]3102
[30863]3103 RTListInit(&pDeferred->NodeDeferred);
3104 pDeferred->pIoCtx = pIoCtx;
[30555]3105
[30863]3106 if (!fInTree)
3107 {
[30555]3108 bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
3109 Assert(fInserted);
[30863]3110 }
[30555]3111
[30863]3112 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
[28065]3113 }
[30863]3114 else
3115 {
3116 RTMemFree(pMetaXfer);
3117 pMetaXfer = NULL;
3118 }
[28065]3119
3120 return rc;
[27808]3121}
3122
[32553]3123static void vdIOIntMetaXferRelease(void *pvUser, PVDMETAXFER pMetaXfer)
[30555]3124{
3125 PVDIMAGE pImage = (PVDIMAGE)pvUser;
[32691]3126 PVBOXHDD pDisk = pImage->pDisk;
[30555]3127 PVDIOSTORAGE pIoStorage = pMetaXfer->pIoStorage;
3128
[32691]3129 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
[30555]3130
3131 Assert( VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE
3132 || VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_WRITE);
3133 Assert(pMetaXfer->cRefs > 0);
3134
3135 pMetaXfer->cRefs--;
3136 if ( !pMetaXfer->cRefs
3137 && RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting)
3138 && VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE)
3139 {
3140 /* Free the meta data entry. */
[30863]3141 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
[32386]3142 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
[30555]3143 AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
3144
3145 RTMemFree(pMetaXfer);
3146 }
3147}
3148
[32553]3149static int vdIOIntFlushAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
[32691]3150 PVDIOCTX pIoCtx, PFNVDXFERCOMPLETED pfnComplete,
3151 void *pvCompleteUser)
[27808]3152{
[28620]3153 PVDIMAGE pImage = (PVDIMAGE)pvUser;
3154 PVBOXHDD pDisk = pImage->pDisk;
[28383]3155 int rc = VINF_SUCCESS;
3156 PVDIOTASK pIoTask;
[30555]3157 PVDMETAXFER pMetaXfer = NULL;
[28383]3158 void *pvTask = NULL;
3159
[30555]3160 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3161
3162 LogFlowFunc(("pvUser=%#p pIoStorage=%#p pIoCtx=%#p\n",
3163 pvUser, pIoStorage, pIoCtx));
3164
3165 /* Allocate a new meta transfer. */
3166 pMetaXfer = vdMetaXferAlloc(pImage, pIoStorage, 0, 0);
3167 if (!pMetaXfer)
3168 return VERR_NO_MEMORY;
3169
3170 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvUser, pMetaXfer);
[28383]3171 if (!pIoTask)
[30555]3172 {
3173 RTMemFree(pMetaXfer);
[28383]3174 return VERR_NO_MEMORY;
[30555]3175 }
[28383]3176
3177 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
3178
[30555]3179 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
3180 AssertPtr(pDeferred);
3181
3182 RTListInit(&pDeferred->NodeDeferred);
3183 pDeferred->pIoCtx = pIoCtx;
3184
3185 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
3186 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_FLUSH);
[32691]3187 rc = pImage->pInterfaceIOCallbacks->pfnFlushAsync(pImage->pInterfaceIO->pvUser,
3188 pIoStorage->pStorage,
3189 pIoTask, &pvTask);
[30555]3190 if (RT_SUCCESS(rc))
[28383]3191 {
[30555]3192 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
[28383]3193 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
3194 vdIoTaskFree(pDisk, pIoTask);
[30555]3195 RTMemFree(pDeferred);
3196 RTMemFree(pMetaXfer);
[28383]3197 }
[30555]3198 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
3199 RTMemFree(pMetaXfer);
[28383]3200
3201 return rc;
[27808]3202}
3203
[32553]3204static size_t vdIOIntIoCtxCopyTo(void *pvUser, PVDIOCTX pIoCtx,
[32691]3205 void *pvBuf, size_t cbBuf)
[27808]3206{
[30555]3207 PVDIMAGE pImage = (PVDIMAGE)pvUser;
3208 PVBOXHDD pDisk = pImage->pDisk;
[31573]3209 size_t cbCopied = 0;
[30555]3210
3211 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3212
[31573]3213 cbCopied = vdIoCtxCopyTo(pIoCtx, (uint8_t *)pvBuf, cbBuf);
3214 Assert(cbCopied == cbBuf);
3215
3216 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCopied);
3217
3218 return cbCopied;
[27808]3219}
3220
[32553]3221static size_t vdIOIntIoCtxCopyFrom(void *pvUser, PVDIOCTX pIoCtx,
[32691]3222 void *pvBuf, size_t cbBuf)
[27808]3223{
[30555]3224 PVDIMAGE pImage = (PVDIMAGE)pvUser;
3225 PVBOXHDD pDisk = pImage->pDisk;
[31573]3226 size_t cbCopied = 0;
[30555]3227
3228 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3229
[31573]3230 cbCopied = vdIoCtxCopyFrom(pIoCtx, (uint8_t *)pvBuf, cbBuf);
3231 Assert(cbCopied == cbBuf);
3232
3233 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCopied);
3234
3235 return cbCopied;
[27808]3236}
3237
[32691]3238static size_t vdIOIntIoCtxSet(void *pvUser, PVDIOCTX pIoCtx, int ch, size_t cb)
[27808]3239{
[30555]3240 PVDIMAGE pImage = (PVDIMAGE)pvUser;
3241 PVBOXHDD pDisk = pImage->pDisk;
[31573]3242 size_t cbSet = 0;
[30555]3243
3244 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3245
[31573]3246 cbSet = vdIoCtxSet(pIoCtx, ch, cb);
3247 Assert(cbSet == cb);
3248
3249 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbSet);
3250
3251 return cbSet;
[27808]3252}
3253
[32553]3254static size_t vdIOIntIoCtxSegArrayCreate(void *pvUser, PVDIOCTX pIoCtx,
[32691]3255 PRTSGSEG paSeg, unsigned *pcSeg,
3256 size_t cbData)
[31586]3257{
3258 PVDIMAGE pImage = (PVDIMAGE)pvUser;
3259 PVBOXHDD pDisk = pImage->pDisk;
3260 size_t cbCreated = 0;
3261
3262 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3263
3264 cbCreated = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, paSeg, pcSeg, cbData);
3265 Assert(!paSeg || cbData == cbCreated);
3266
3267 return cbCreated;
3268}
3269
[32553]3270static void vdIOIntIoCtxCompleted(void *pvUser, PVDIOCTX pIoCtx, int rcReq,
[32691]3271 size_t cbCompleted)
[31586]3272{
3273 /* Continue */
3274 pIoCtx->fBlocked = false;
3275 ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCompleted);
3276
3277 /* Clear the pointer to next transfer function in case we have nothing to transfer anymore.
3278 * @todo: Find a better way to prevent vdIoCtxContinue from calling the read/write helper again. */
3279 if (!pIoCtx->cbTransferLeft)
3280 pIoCtx->pfnIoCtxTransfer = NULL;
3281
3282 vdIoCtxContinue(pIoCtx, rcReq);
3283}
3284
[27808]3285/**
[27815]3286 * VD I/O interface callback for opening a file (limited version for VDGetFormat).
3287 */
[32553]3288static int vdIOIntOpenLimited(void *pvUser, const char *pszLocation,
3289 uint32_t fOpen, PPVDIOSTORAGE ppIoStorage)
[27815]3290{
3291 int rc = VINF_SUCCESS;
[32553]3292 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
[27815]3293 PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
3294
3295 if (!pIoStorage)
3296 return VERR_NO_MEMORY;
3297
[32553]3298 rc = pInterfaceIOCallbacks->pfnOpen(NULL, pszLocation, fOpen,
[32691]3299 NULL, &pIoStorage->pStorage);
[27815]3300 if (RT_SUCCESS(rc))
3301 *ppIoStorage = pIoStorage;
3302 else
3303 RTMemFree(pIoStorage);
3304
3305 return rc;
3306}
3307
[32553]3308static int vdIOIntCloseLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
[27815]3309{
[32553]3310 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3311 int rc = pInterfaceIOCallbacks->pfnClose(NULL, pIoStorage->pStorage);
[27815]3312 AssertRC(rc);
3313
3314 RTMemFree(pIoStorage);
3315 return VINF_SUCCESS;
3316}
3317
[32553]3318static int vdIOIntDeleteLimited(void *pvUser, const char *pcszFilename)
[32536]3319{
[32553]3320 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3321 return pInterfaceIOCallbacks->pfnDelete(NULL, pcszFilename);
[32536]3322}
3323
[32691]3324static int vdIOIntMoveLimited(void *pvUser, const char *pcszSrc,
3325 const char *pcszDst, unsigned fMove)
[32536]3326{
[32553]3327 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3328 return pInterfaceIOCallbacks->pfnMove(NULL, pcszSrc, pcszDst, fMove);
[32536]3329}
3330
[32691]3331static int vdIOIntGetFreeSpaceLimited(void *pvUser, const char *pcszFilename,
3332 int64_t *pcbFreeSpace)
[32536]3333{
[32553]3334 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3335 return pInterfaceIOCallbacks->pfnGetFreeSpace(NULL, pcszFilename, pcbFreeSpace);
[32536]3336}
3337
[32691]3338static int vdIOIntGetModificationTimeLimited(void *pvUser,
3339 const char *pcszFilename,
3340 PRTTIMESPEC pModificationTime)
[32536]3341{
[32553]3342 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3343 return pInterfaceIOCallbacks->pfnGetModificationTime(NULL, pcszFilename, pModificationTime);
[32536]3344}
3345
[32553]3346static int vdIOIntGetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
[32691]3347 uint64_t *pcbSize)
[27815]3348{
[32553]3349 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3350 return pInterfaceIOCallbacks->pfnGetSize(NULL, pIoStorage->pStorage, pcbSize);
[27815]3351}
3352
[32553]3353static int vdIOIntSetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
[32691]3354 uint64_t cbSize)
[27815]3355{
[32553]3356 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3357 return pInterfaceIOCallbacks->pfnSetSize(NULL, pIoStorage->pStorage, cbSize);
[27815]3358}
3359
[32691]3360static int vdIOIntWriteSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
3361 uint64_t uOffset, const void *pvBuf,
3362 size_t cbWrite, size_t *pcbWritten)
[27815]3363{
[32553]3364 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3365 return pInterfaceIOCallbacks->pfnWriteSync(NULL, pIoStorage->pStorage, uOffset, pvBuf, cbWrite, pcbWritten);
[27815]3366}
3367
[32691]3368static int vdIOIntReadSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
3369 uint64_t uOffset, void *pvBuf, size_t cbRead,
3370 size_t *pcbRead)
[27815]3371{
[32553]3372 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3373 return pInterfaceIOCallbacks->pfnReadSync(NULL, pIoStorage->pStorage, uOffset, pvBuf, cbRead, pcbRead);
[27815]3374}
3375
[32553]3376static int vdIOIntFlushSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
[27815]3377{
[32553]3378 PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
3379 return pInterfaceIOCallbacks->pfnFlushSync(NULL, pIoStorage->pStorage);
[27815]3380}
3381
3382/**
[21806]3383 * internal: send output to the log (unconditionally).
3384 */
[32536]3385int vdLogMessage(void *pvUser, const char *pszFormat, va_list args)
[21806]3386{
3387 NOREF(pvUser);
[32536]3388 RTLogPrintfV(pszFormat, args);
[21806]3389 return VINF_SUCCESS;
3390}
3391
[32536]3392DECLINLINE(int) vdMessageWrapper(PVBOXHDD pDisk, const char *pszFormat, ...)
3393{
3394 va_list va;
3395 va_start(va, pszFormat);
3396 int rc = pDisk->pInterfaceErrorCallbacks->pfnMessage(pDisk->pInterfaceError->pvUser,
3397 pszFormat, va);
3398 va_end(va);
3399 return rc;
3400}
[21806]3401
[32536]3402
[21806]3403/**
[14852]3404 * Initializes HDD backends.
3405 *
3406 * @returns VBox status code.
3407 */
[16962]3408VBOXDDU_DECL(int) VDInit(void)
[14852]3409{
3410 int rc = vdAddBackends(aStaticBackends, RT_ELEMENTS(aStaticBackends));
3411 if (RT_SUCCESS(rc))
[32370]3412 {
3413 rc = vdAddCacheBackends(aStaticCacheBackends, RT_ELEMENTS(aStaticCacheBackends));
3414 if (RT_SUCCESS(rc))
3415 {
3416 rc = vdLoadDynamicBackends();
3417 if (RT_SUCCESS(rc))
3418 rc = vdLoadDynamicCacheBackends();
3419 }
3420 }
[14852]3421 LogRel(("VDInit finished\n"));
3422 return rc;
3423}
3424
3425/**
3426 * Destroys loaded HDD backends.
3427 *
3428 * @returns VBox status code.
3429 */
[16962]3430VBOXDDU_DECL(int) VDShutdown(void)
[14852]3431{
3432 PVBOXHDDBACKEND *pBackends = g_apBackends;
[32370]3433 PVDCACHEBACKEND *pCacheBackends = g_apCacheBackends;
[14852]3434 unsigned cBackends = g_cBackends;
3435
3436 if (!pBackends)
3437 return VERR_INTERNAL_ERROR;
3438
3439 g_cBackends = 0;
3440 g_apBackends = NULL;
3441
[31504]3442#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
[14852]3443 for (unsigned i = 0; i < cBackends; i++)
3444 if (pBackends[i]->hPlugin != NIL_RTLDRMOD)
3445 RTLdrClose(pBackends[i]->hPlugin);
[31504]3446#endif
[14855]3447
[32370]3448 /* Clear the supported cache backends. */
3449 cBackends = g_cCacheBackends;
3450 g_cCacheBackends = 0;
3451 g_apCacheBackends = NULL;
3452
3453#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
3454 for (unsigned i = 0; i < cBackends; i++)
3455 if (pCacheBackends[i]->hPlugin != NIL_RTLDRMOD)
3456 RTLdrClose(pCacheBackends[i]->hPlugin);
3457#endif
3458
3459 if (pCacheBackends)
3460 RTMemFree(pCacheBackends);
[14855]3461 RTMemFree(pBackends);
[14852]3462 return VINF_SUCCESS;
3463}
3464
3465
3466/**
3467 * Lists all HDD backends and their capabilities in a caller-provided buffer.
3468 *
3469 * @returns VBox status code.
3470 * VERR_BUFFER_OVERFLOW if not enough space is passed.
3471 * @param cEntriesAlloc Number of list entries available.
3472 * @param pEntries Pointer to array for the entries.
3473 * @param pcEntriesUsed Number of entries returned.
3474 */
3475VBOXDDU_DECL(int) VDBackendInfo(unsigned cEntriesAlloc, PVDBACKENDINFO pEntries,
3476 unsigned *pcEntriesUsed)
3477{
3478 int rc = VINF_SUCCESS;
3479 PRTDIR pPluginDir = NULL;
3480 unsigned cEntries = 0;
3481
3482 LogFlowFunc(("cEntriesAlloc=%u pEntries=%#p pcEntriesUsed=%#p\n", cEntriesAlloc, pEntries, pcEntriesUsed));
3483 /* Check arguments. */
3484 AssertMsgReturn(cEntriesAlloc,
3485 ("cEntriesAlloc=%u\n", cEntriesAlloc),
3486 VERR_INVALID_PARAMETER);
3487 AssertMsgReturn(VALID_PTR(pEntries),
3488 ("pEntries=%#p\n", pEntries),
3489 VERR_INVALID_PARAMETER);
3490 AssertMsgReturn(VALID_PTR(pcEntriesUsed),
3491 ("pcEntriesUsed=%#p\n", pcEntriesUsed),
3492 VERR_INVALID_PARAMETER);
3493 if (!g_apBackends)
3494 VDInit();
3495
3496 if (cEntriesAlloc < g_cBackends)
3497 {
3498 *pcEntriesUsed = g_cBackends;
3499 return VERR_BUFFER_OVERFLOW;
3500 }
3501
[14967]3502 for (unsigned i = 0; i < g_cBackends; i++)
3503 {
3504 pEntries[i].pszBackend = g_apBackends[i]->pszBackendName;
3505 pEntries[i].uBackendCaps = g_apBackends[i]->uBackendCaps;
3506 pEntries[i].papszFileExtensions = g_apBackends[i]->papszFileExtensions;
3507 pEntries[i].paConfigInfo = g_apBackends[i]->paConfigInfo;
3508 pEntries[i].pfnComposeLocation = g_apBackends[i]->pfnComposeLocation;
3509 pEntries[i].pfnComposeName = g_apBackends[i]->pfnComposeName;
3510 }
[14852]3511
[11284]3512 LogFlowFunc(("returns %Rrc *pcEntriesUsed=%u\n", rc, cEntries));
[14852]3513 *pcEntriesUsed = g_cBackends;
[7780]3514 return rc;
3515}
3516
[10715]3517/**
3518 * Lists the capablities of a backend indentified by its name.
3519 *
3520 * @returns VBox status code.
3521 * @param pszBackend The backend name.
3522 * @param pEntries Pointer to an entry.
3523 */
3524VBOXDDU_DECL(int) VDBackendInfoOne(const char *pszBackend, PVDBACKENDINFO pEntry)
3525{
[14852]3526 LogFlowFunc(("pszBackend=%#p pEntry=%#p\n", pszBackend, pEntry));
3527 /* Check arguments. */
3528 AssertMsgReturn(VALID_PTR(pszBackend),
3529 ("pszBackend=%#p\n", pszBackend),
3530 VERR_INVALID_PARAMETER);
3531 AssertMsgReturn(VALID_PTR(pEntry),
3532 ("pEntry=%#p\n", pEntry),
3533 VERR_INVALID_PARAMETER);
3534 if (!g_apBackends)
3535 VDInit();
3536
3537 /* Go through loaded backends. */
3538 for (unsigned i = 0; i < g_cBackends; i++)
3539 {
[15366]3540 if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
[14852]3541 {
3542 pEntry->pszBackend = g_apBackends[i]->pszBackendName;
3543 pEntry->uBackendCaps = g_apBackends[i]->uBackendCaps;
3544 pEntry->papszFileExtensions = g_apBackends[i]->papszFileExtensions;
3545 pEntry->paConfigInfo = g_apBackends[i]->paConfigInfo;
3546 return VINF_SUCCESS;
3547 }
3548 }
3549
3550 return VERR_NOT_FOUND;
[10715]3551}
[7780]3552
3553/**
[6291]3554 * Allocates and initializes an empty HDD container.
[2379]3555 * No image files are opened.
3556 *
3557 * @returns VBox status code.
[11444]3558 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
[6291]3559 * @param ppDisk Where to store the reference to HDD container.
[2379]3560 */
[11444]3561VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, PVBOXHDD *ppDisk)
[2379]3562{
3563 int rc = VINF_SUCCESS;
3564 PVBOXHDD pDisk = NULL;
3565
[11444]3566 LogFlowFunc(("pVDIfsDisk=%#p\n", pVDIfsDisk));
[6291]3567 do
3568 {
3569 /* Check arguments. */
[8565]3570 AssertMsgBreakStmt(VALID_PTR(ppDisk),
3571 ("ppDisk=%#p\n", ppDisk),
3572 rc = VERR_INVALID_PARAMETER);
[2728]3573
[7277]3574 pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
3575 if (pDisk)
[2379]3576 {
[7277]3577 pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
3578 pDisk->cImages = 0;
3579 pDisk->pBase = NULL;
3580 pDisk->pLast = NULL;
3581 pDisk->cbSize = 0;
3582 pDisk->PCHSGeometry.cCylinders = 0;
3583 pDisk->PCHSGeometry.cHeads = 0;
3584 pDisk->PCHSGeometry.cSectors = 0;
3585 pDisk->LCHSGeometry.cCylinders = 0;
3586 pDisk->LCHSGeometry.cHeads = 0;
3587 pDisk->LCHSGeometry.cSectors = 0;
[11444]3588 pDisk->pVDIfsDisk = pVDIfsDisk;
[10715]3589 pDisk->pInterfaceError = NULL;
3590 pDisk->pInterfaceErrorCallbacks = NULL;
[27232]3591 pDisk->pInterfaceThreadSync = NULL;
3592 pDisk->pInterfaceThreadSyncCallbacks = NULL;
[28620]3593 pDisk->fGrowing = false;
3594 RTListInit(&pDisk->ListWriteGrowing);
[10715]3595
[27977]3596 /* Create the I/O ctx cache */
[27808]3597 rc = RTMemCacheCreate(&pDisk->hMemCacheIoCtx, sizeof(VDIOCTX), 0, UINT32_MAX,
3598 NULL, NULL, NULL, 0);
3599 if (RT_FAILURE(rc))
3600 {
3601 RTMemFree(pDisk);
3602 break;
3603 }
3604
[27977]3605 /* Create the I/O task cache */
3606 rc = RTMemCacheCreate(&pDisk->hMemCacheIoTask, sizeof(VDIOTASK), 0, UINT32_MAX,
3607 NULL, NULL, NULL, 0);
3608 if (RT_FAILURE(rc))
3609 {
3610 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
3611 RTMemFree(pDisk);
3612 break;
3613 }
3614
[28620]3615 /* Create critical section. */
3616 rc = RTCritSectInit(&pDisk->CritSect);
3617 if (RT_FAILURE(rc))
3618 {
3619 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
3620 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
3621 RTMemFree(pDisk);
3622 break;
3623 }
[27977]3624
[11444]3625 pDisk->pInterfaceError = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_ERROR);
[10715]3626 if (pDisk->pInterfaceError)
[11176]3627 pDisk->pInterfaceErrorCallbacks = VDGetInterfaceError(pDisk->pInterfaceError);
[22966]3628
[27232]3629 pDisk->pInterfaceThreadSync = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_THREADSYNC);
3630 if (pDisk->pInterfaceThreadSync)
3631 pDisk->pInterfaceThreadSyncCallbacks = VDGetInterfaceThreadSync(pDisk->pInterfaceThreadSync);
[27808]3632
[32691]3633 /* Create fallback I/O callback table */
3634 pDisk->VDIIOCallbacks.cbSize = sizeof(VDINTERFACEIO);
3635 pDisk->VDIIOCallbacks.enmInterface = VDINTERFACETYPE_IO;
3636 pDisk->VDIIOCallbacks.pfnOpen = vdIOOpenFallback;
3637 pDisk->VDIIOCallbacks.pfnClose = vdIOCloseFallback;
3638 pDisk->VDIIOCallbacks.pfnDelete = vdIODeleteFallback;
3639 pDisk->VDIIOCallbacks.pfnMove = vdIOMoveFallback;
3640 pDisk->VDIIOCallbacks.pfnGetFreeSpace = vdIOGetFreeSpaceFallback;
3641 pDisk->VDIIOCallbacks.pfnGetModificationTime = vdIOGetModificationTimeFallback;
3642 pDisk->VDIIOCallbacks.pfnGetSize = vdIOGetSizeFallback;
3643 pDisk->VDIIOCallbacks.pfnSetSize = vdIOSetSizeFallback;
3644 pDisk->VDIIOCallbacks.pfnReadSync = vdIOReadSyncFallback;
3645 pDisk->VDIIOCallbacks.pfnWriteSync = vdIOWriteSyncFallback;
3646 pDisk->VDIIOCallbacks.pfnFlushSync = vdIOFlushSyncFallback;
3647 pDisk->VDIIOCallbacks.pfnReadAsync = vdIOReadAsyncFallback;
3648 pDisk->VDIIOCallbacks.pfnWriteAsync = vdIOWriteAsyncFallback;
3649 pDisk->VDIIOCallbacks.pfnFlushAsync = vdIOFlushAsyncFallback;
[22966]3650
[32553]3651 /*
3652 * Create the internal I/O callback table.
3653 * The interface is per-image but no need to duplicate the
3654 * callback table every time.
3655 */
3656 pDisk->VDIIOIntCallbacks.cbSize = sizeof(VDINTERFACEIOINT);
3657 pDisk->VDIIOIntCallbacks.enmInterface = VDINTERFACETYPE_IOINT;
3658 pDisk->VDIIOIntCallbacks.pfnOpen = vdIOIntOpen;
3659 pDisk->VDIIOIntCallbacks.pfnClose = vdIOIntClose;
3660 pDisk->VDIIOIntCallbacks.pfnDelete = vdIOIntDelete;
3661 pDisk->VDIIOIntCallbacks.pfnMove = vdIOIntMove;
3662 pDisk->VDIIOIntCallbacks.pfnGetFreeSpace = vdIOIntGetFreeSpace;
3663 pDisk->VDIIOIntCallbacks.pfnGetModificationTime = vdIOIntGetModificationTime;
3664 pDisk->VDIIOIntCallbacks.pfnGetSize = vdIOIntGetSize;
3665 pDisk->VDIIOIntCallbacks.pfnSetSize = vdIOIntSetSize;
3666 pDisk->VDIIOIntCallbacks.pfnReadSync = vdIOIntReadSync;
3667 pDisk->VDIIOIntCallbacks.pfnWriteSync = vdIOIntWriteSync;
3668 pDisk->VDIIOIntCallbacks.pfnFlushSync = vdIOIntFlushSync;
3669 pDisk->VDIIOIntCallbacks.pfnReadUserAsync = vdIOIntReadUserAsync;
3670 pDisk->VDIIOIntCallbacks.pfnWriteUserAsync = vdIOIntWriteUserAsync;
3671 pDisk->VDIIOIntCallbacks.pfnReadMetaAsync = vdIOIntReadMetaAsync;
3672 pDisk->VDIIOIntCallbacks.pfnWriteMetaAsync = vdIOIntWriteMetaAsync;
3673 pDisk->VDIIOIntCallbacks.pfnMetaXferRelease = vdIOIntMetaXferRelease;
3674 pDisk->VDIIOIntCallbacks.pfnFlushAsync = vdIOIntFlushAsync;
3675 pDisk->VDIIOIntCallbacks.pfnIoCtxCopyFrom = vdIOIntIoCtxCopyFrom;
3676 pDisk->VDIIOIntCallbacks.pfnIoCtxCopyTo = vdIOIntIoCtxCopyTo;
3677 pDisk->VDIIOIntCallbacks.pfnIoCtxSet = vdIOIntIoCtxSet;
3678 pDisk->VDIIOIntCallbacks.pfnIoCtxSegArrayCreate = vdIOIntIoCtxSegArrayCreate;
3679 pDisk->VDIIOIntCallbacks.pfnIoCtxCompleted = vdIOIntIoCtxCompleted;
[27808]3680
[7277]3681 *ppDisk = pDisk;
[2379]3682 }
[7277]3683 else
[6291]3684 {
[7277]3685 rc = VERR_NO_MEMORY;
3686 break;
[4522]3687 }
[6291]3688 } while (0);
[2379]3689
[11284]3690 LogFlowFunc(("returns %Rrc (pDisk=%#p)\n", rc, pDisk));
[2379]3691 return rc;
3692}
3693
3694/**
[6291]3695 * Destroys HDD container.
3696 * If container has opened image files they will be closed.
[5101]3697 *
[6291]3698 * @param pDisk Pointer to HDD container.
3699 */
3700VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk)
3701{
3702 LogFlowFunc(("pDisk=%#p\n", pDisk));
3703 do
3704 {
3705 /* sanity check */
[8594]3706 AssertPtrBreak(pDisk);
[6291]3707 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
[8569]3708 VDCloseAll(pDisk);
[27808]3709 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
[27977]3710 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
[8569]3711 RTMemFree(pDisk);
[6291]3712 } while (0);
3713 LogFlowFunc(("returns\n"));
3714}
3715
3716/**
3717 * Try to get the backend name which can use this image.
3718 *
[5101]3719 * @returns VBox status code.
[7967]3720 * VINF_SUCCESS if a plugin was found.
[6195]3721 * ppszFormat contains the string which can be used as backend name.
[7900]3722 * VERR_NOT_SUPPORTED if no backend was found.
[22982]3723 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
[32536]3724 * @param pVDIfsImage Pointer to the per-image VD interface list.
[5101]3725 * @param pszFilename Name of the image file for which the backend is queried.
[6291]3726 * @param ppszFormat Receives pointer of the UTF-8 string which contains the format name.
3727 * The returned pointer must be freed using RTStrFree().
[5101]3728 */
[32536]3729VBOXDDU_DECL(int) VDGetFormat(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
3730 const char *pszFilename, char **ppszFormat)
[5101]3731{
3732 int rc = VERR_NOT_SUPPORTED;
[32553]3733 VDINTERFACEIOINT VDIIOIntCallbacks;
3734 VDINTERFACE VDIIOInt;
3735 VDINTERFACEIO VDIIOCallbacksFallback;
3736 PVDINTERFACE pInterfaceIO;
3737 PVDINTERFACEIO pInterfaceIOCallbacks;
[5101]3738
[6291]3739 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
[14852]3740 /* Check arguments. */
3741 AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
3742 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
3743 VERR_INVALID_PARAMETER);
3744 AssertMsgReturn(VALID_PTR(ppszFormat),
3745 ("ppszFormat=%#p\n", ppszFormat),
3746 VERR_INVALID_PARAMETER);
3747
3748 if (!g_apBackends)
3749 VDInit();
3750
[32553]3751 pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
3752 if (!pInterfaceIO)
[32536]3753 {
[32553]3754 /*
3755 * Caller doesn't provide an I/O interface, create our own using the
3756 * native file API.
3757 */
3758 VDIIOCallbacksFallback.cbSize = sizeof(VDINTERFACEIO);
3759 VDIIOCallbacksFallback.enmInterface = VDINTERFACETYPE_IO;
3760 VDIIOCallbacksFallback.pfnOpen = vdIOOpenFallback;
3761 VDIIOCallbacksFallback.pfnClose = vdIOCloseFallback;
3762 VDIIOCallbacksFallback.pfnDelete = vdIODeleteFallback;
3763 VDIIOCallbacksFallback.pfnMove = vdIOMoveFallback;
3764 VDIIOCallbacksFallback.pfnGetFreeSpace = vdIOGetFreeSpaceFallback;
3765 VDIIOCallbacksFallback.pfnGetModificationTime = vdIOGetModificationTimeFallback;
3766 VDIIOCallbacksFallback.pfnGetSize = vdIOGetSizeFallback;
3767 VDIIOCallbacksFallback.pfnSetSize = vdIOSetSizeFallback;
3768 VDIIOCallbacksFallback.pfnReadSync = vdIOReadSyncFallback;
3769 VDIIOCallbacksFallback.pfnWriteSync = vdIOWriteSyncFallback;
3770 VDIIOCallbacksFallback.pfnFlushSync = vdIOFlushSyncFallback;
3771 pInterfaceIOCallbacks = &VDIIOCallbacksFallback;
[32536]3772 }
[32553]3773 else
3774 pInterfaceIOCallbacks = VDGetInterfaceIO(pInterfaceIO);
[22982]3775
[32691]3776 /* Set up the internal I/O interface. */
3777 AssertReturn(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
3778 VERR_INVALID_PARAMETER);
[32553]3779 VDIIOIntCallbacks.cbSize = sizeof(VDINTERFACEIOINT);
3780 VDIIOIntCallbacks.enmInterface = VDINTERFACETYPE_IOINT;
3781 VDIIOIntCallbacks.pfnOpen = vdIOIntOpenLimited;
3782 VDIIOIntCallbacks.pfnClose = vdIOIntCloseLimited;
3783 VDIIOIntCallbacks.pfnDelete = vdIOIntDeleteLimited;
3784 VDIIOIntCallbacks.pfnMove = vdIOIntMoveLimited;
3785 VDIIOIntCallbacks.pfnGetFreeSpace = vdIOIntGetFreeSpaceLimited;
3786 VDIIOIntCallbacks.pfnGetModificationTime = vdIOIntGetModificationTimeLimited;
3787 VDIIOIntCallbacks.pfnGetSize = vdIOIntGetSizeLimited;
3788 VDIIOIntCallbacks.pfnSetSize = vdIOIntSetSizeLimited;
3789 VDIIOIntCallbacks.pfnReadSync = vdIOIntReadSyncLimited;
3790 VDIIOIntCallbacks.pfnWriteSync = vdIOIntWriteSyncLimited;
3791 VDIIOIntCallbacks.pfnFlushSync = vdIOIntFlushSyncLimited;
3792 VDIIOIntCallbacks.pfnReadUserAsync = NULL;
3793 VDIIOIntCallbacks.pfnWriteUserAsync = NULL;
3794 VDIIOIntCallbacks.pfnReadMetaAsync = NULL;
3795 VDIIOIntCallbacks.pfnWriteMetaAsync = NULL;
3796 VDIIOIntCallbacks.pfnFlushAsync = NULL;
3797 rc = VDInterfaceAdd(&VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
3798 &VDIIOIntCallbacks, pInterfaceIOCallbacks, &pVDIfsImage);
3799 AssertRC(rc);
3800
[14852]3801 /* Find the backend supporting this file format. */
3802 for (unsigned i = 0; i < g_cBackends; i++)
[6291]3803 {
[14852]3804 if (g_apBackends[i]->pfnCheckIfValid)
[6291]3805 {
[32536]3806 rc = g_apBackends[i]->pfnCheckIfValid(pszFilename, pVDIfsDisk,
3807 pVDIfsImage);
[15366]3808 if ( RT_SUCCESS(rc)
3809 /* The correct backend has been found, but there is a small
3810 * incompatibility so that the file cannot be used. Stop here
3811 * and signal success - the actual open will of course fail,
3812 * but that will create a really sensible error message. */
3813 || ( rc != VERR_VD_GEN_INVALID_HEADER
3814 && rc != VERR_VD_VDI_INVALID_HEADER
3815 && rc != VERR_VD_VMDK_INVALID_HEADER
3816 && rc != VERR_VD_ISCSI_INVALID_HEADER
3817 && rc != VERR_VD_VHD_INVALID_HEADER
[32536]3818 && rc != VERR_VD_RAW_INVALID_HEADER
[32604]3819 && rc != VERR_VD_PARALLELS_INVALID_HEADER
[32536]3820 && rc != VERR_VD_DMG_INVALID_HEADER))
[6291]3821 {
[14852]3822 /* Copy the name into the new string. */
3823 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
3824 if (!pszFormat)
[6291]3825 {
[14852]3826 rc = VERR_NO_MEMORY;
[6291]3827 break;
3828 }
[14852]3829 *ppszFormat = pszFormat;
3830 rc = VINF_SUCCESS;
[6291]3831 break;
[11781]3832 }
[15366]3833 rc = VERR_NOT_SUPPORTED;
[5101]3834 }
[14852]3835 }
[7900]3836
[32370]3837 /* Try the cache backends. */
3838 if (rc == VERR_NOT_SUPPORTED)
3839 {
3840 for (unsigned i = 0; i < g_cCacheBackends; i++)
3841 {
3842 if (g_apCacheBackends[i]->pfnProbe)
3843 {
[32536]3844 rc = g_apCacheBackends[i]->pfnProbe(pszFilename, pVDIfsDisk,
3845 pVDIfsImage);
[32370]3846 if ( RT_SUCCESS(rc)
3847 || (rc != VERR_VD_GEN_INVALID_HEADER))
3848 {
3849 /* Copy the name into the new string. */
3850 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
3851 if (!pszFormat)
3852 {
3853 rc = VERR_NO_MEMORY;
3854 break;
3855 }
3856 *ppszFormat = pszFormat;
3857 rc = VINF_SUCCESS;
3858 break;
3859 }
3860 rc = VERR_NOT_SUPPORTED;
3861 }
3862 }
3863 }
3864
[11284]3865 LogFlowFunc(("returns %Rrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
[5101]3866 return rc;
3867}
3868
3869/**
[2379]3870 * Opens an image file.
3871 *
[6291]3872 * The first opened image file in HDD container must have a base image type,
[2379]3873 * others (next opened images) must be a differencing or undo images.
3874 * Linkage is checked for differencing image to be in consistence with the previously opened image.
3875 * When another differencing image is opened and the last image was opened in read/write access
3876 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
3877 * other processes to use images in read-only mode too.
3878 *
[6291]3879 * Note that the image is opened in read-only mode if a read/write open is not possible.
[2379]3880 * Use VDIsReadOnly to check open mode.
3881 *
3882 * @returns VBox status code.
[6291]3883 * @param pDisk Pointer to HDD container.
[7277]3884 * @param pszBackend Name of the image file backend to use.
[2379]3885 * @param pszFilename Name of the image file to open.
3886 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
[11444]3887 * @param pVDIfsImage Pointer to the per-image VD interface list.
[2379]3888 */
[7277]3889VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
[11444]3890 const char *pszFilename, unsigned uOpenFlags,
3891 PVDINTERFACE pVDIfsImage)
[2379]3892{
3893 int rc = VINF_SUCCESS;
[27232]3894 int rc2;
3895 bool fLockWrite = false;
[6291]3896 PVDIMAGE pImage = NULL;
[2379]3897
[14928]3898 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsImage=%#p\n",
[11444]3899 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsImage));
[27232]3900
[6291]3901 do
[2379]3902 {
[6291]3903 /* sanity check */
[8569]3904 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]3905 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
[2379]3906
[6291]3907 /* Check arguments. */
[8565]3908 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
3909 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
3910 rc = VERR_INVALID_PARAMETER);
3911 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
3912 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
3913 rc = VERR_INVALID_PARAMETER);
3914 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
3915 ("uOpenFlags=%#x\n", uOpenFlags),
3916 rc = VERR_INVALID_PARAMETER);
[2833]3917
[6291]3918 /* Set up image descriptor. */
3919 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
3920 if (!pImage)
3921 {
3922 rc = VERR_NO_MEMORY;
3923 break;
3924 }
3925 pImage->pszFilename = RTStrDup(pszFilename);
3926 if (!pImage->pszFilename)
3927 {
3928 rc = VERR_NO_MEMORY;
3929 break;
3930 }
[27808]3931
[28620]3932 pImage->pDisk = pDisk;
[11444]3933 pImage->pVDIfsImage = pVDIfsImage;
[6291]3934
[14852]3935 rc = vdFindBackend(pszBackend, &pImage->Backend);
[11266]3936 if (RT_FAILURE(rc))
[7277]3937 break;
3938 if (!pImage->Backend)
3939 {
3940 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
3941 N_("VD: unknown backend name '%s'"), pszBackend);
3942 break;
3943 }
3944
[32691]3945 /* Set up the I/O interface. */
3946 pImage->pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
3947 if (pImage->pInterfaceIO)
3948 pImage->pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->pInterfaceIO);
3949 else
[32536]3950 {
[32691]3951 rc = VDInterfaceAdd(&pImage->VDIIO, "VD_IO", VDINTERFACETYPE_IO,
3952 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
3953 pImage->pInterfaceIO = &pImage->VDIIO;
3954 pImage->pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
[32536]3955 }
[28620]3956
[32691]3957 /* Set up the internal I/O interface. */
3958 AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
3959 rc = VERR_INVALID_PARAMETER);
3960 rc = VDInterfaceAdd(&pImage->VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
3961 &pDisk->VDIIOIntCallbacks, pImage, &pImage->pVDIfsImage);
3962 AssertRC(rc);
3963
[2590]3964 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
[7277]3965 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
3966 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
[11444]3967 pDisk->pVDIfsDisk,
3968 pImage->pVDIfsImage,
[32536]3969 &pImage->pBackendData);
[6291]3970 /* If the open in read-write mode failed, retry in read-only mode. */
[11266]3971 if (RT_FAILURE(rc))
[6291]3972 {
3973 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
[24887]3974 && ( rc == VERR_ACCESS_DENIED
[6291]3975 || rc == VERR_PERMISSION_DENIED
3976 || rc == VERR_WRITE_PROTECT
3977 || rc == VERR_SHARING_VIOLATION
3978 || rc == VERR_FILE_LOCK_FAILED))
[7277]3979 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
3980 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
3981 | VD_OPEN_FLAGS_READONLY,
[11444]3982 pDisk->pVDIfsDisk,
3983 pImage->pVDIfsImage,
[32536]3984 &pImage->pBackendData);
[11266]3985 if (RT_FAILURE(rc))
[6291]3986 {
3987 rc = vdError(pDisk, rc, RT_SRC_POS,
[24887]3988 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
[6291]3989 break;
3990 }
3991 }
[2379]3992
[27232]3993 /* Lock disk for writing, as we modify pDisk information below. */
3994 rc2 = vdThreadStartWrite(pDisk);
3995 AssertRC(rc2);
3996 fLockWrite = true;
3997
[9560]3998 /* Check image type. As the image itself has only partial knowledge
3999 * whether it's a base image or not, this info is derived here. The
4000 * base image can be fixed or normal, all others must be normal or
4001 * diff images. Some image formats don't distinguish between normal
4002 * and diff images, so this must be corrected here. */
[27232]4003 unsigned uImageFlags;
[32536]4004 uImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pBackendData);
[11266]4005 if (RT_FAILURE(rc))
[17970]4006 uImageFlags = VD_IMAGE_FLAGS_NONE;
[11266]4007 if ( RT_SUCCESS(rc)
[9560]4008 && !(uOpenFlags & VD_OPEN_FLAGS_INFO))
[6291]4009 {
[9565]4010 if ( pDisk->cImages == 0
[17970]4011 && (uImageFlags & VD_IMAGE_FLAGS_DIFF))
[9560]4012 {
[15366]4013 rc = VERR_VD_INVALID_TYPE;
[9560]4014 break;
4015 }
[9565]4016 else if (pDisk->cImages != 0)
4017 {
[17970]4018 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
[9565]4019 {
[15366]4020 rc = VERR_VD_INVALID_TYPE;
[9565]4021 break;
4022 }
4023 else
[17970]4024 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
[9565]4025 }
[9560]4026 }
[31258]4027
4028 /* Ensure we always get correct diff information, even if the backend
4029 * doesn't actually have a stored flag for this. It must not return
4030 * bogus information for the parent UUID if it is not a diff image. */
4031 RTUUID parentUuid;
4032 RTUuidClear(&parentUuid);
[32536]4033 rc2 = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, &parentUuid);
[31258]4034 if (RT_SUCCESS(rc2) && !RTUuidIsNull(&parentUuid))
4035 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
4036
[17970]4037 pImage->uImageFlags = uImageFlags;
[2379]4038
[7900]4039 /* Force sane optimization settings. It's not worth avoiding writes
4040 * to fixed size images. The overhead would have almost no payback. */
[17970]4041 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
[7900]4042 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
4043
[2564]4044 /** @todo optionally check UUIDs */
4045
[6291]4046 /* Cache disk information. */
[32536]4047 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
[2379]4048
[6291]4049 /* Cache PCHS geometry. */
[32536]4050 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
[7277]4051 &pDisk->PCHSGeometry);
[11266]4052 if (RT_FAILURE(rc2))
[6291]4053 {
4054 pDisk->PCHSGeometry.cCylinders = 0;
4055 pDisk->PCHSGeometry.cHeads = 0;
4056 pDisk->PCHSGeometry.cSectors = 0;
[2379]4057 }
[6291]4058 else
4059 {
4060 /* Make sure the PCHS geometry is properly clipped. */
4061 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
4062 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
4063 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
4064 }
[2379]4065
[6291]4066 /* Cache LCHS geometry. */
[32536]4067 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
[7277]4068 &pDisk->LCHSGeometry);
[11266]4069 if (RT_FAILURE(rc2))
[2379]4070 {
[6291]4071 pDisk->LCHSGeometry.cCylinders = 0;
4072 pDisk->LCHSGeometry.cHeads = 0;
4073 pDisk->LCHSGeometry.cSectors = 0;
4074 }
4075 else
4076 {
4077 /* Make sure the LCHS geometry is properly clipped. */
4078 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
4079 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
4080 }
4081
4082 if (pDisk->cImages != 0)
4083 {
[2379]4084 /* Switch previous image to read-only mode. */
4085 unsigned uOpenFlagsPrevImg;
[32536]4086 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
[2379]4087 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
4088 {
4089 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
[32536]4090 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
[2379]4091 }
4092 }
4093
[11266]4094 if (RT_SUCCESS(rc))
[2379]4095 {
4096 /* Image successfully opened, make it the last image. */
4097 vdAddImageToList(pDisk, pImage);
[7900]4098 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
4099 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
[2379]4100 }
[2650]4101 else
[2379]4102 {
4103 /* Error detected, but image opened. Close image. */
[32536]4104 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
[2379]4105 AssertRC(rc2);
[32536]4106 pImage->pBackendData = NULL;
[2379]4107 }
[6291]4108 } while (0);
[2379]4109
[27232]4110 if (RT_UNLIKELY(fLockWrite))
4111 {
4112 rc2 = vdThreadFinishWrite(pDisk);
4113 AssertRC(rc2);
4114 }
4115
[11266]4116 if (RT_FAILURE(rc))
[2379]4117 {
4118 if (pImage)
4119 {
4120 if (pImage->pszFilename)
4121 RTStrFree(pImage->pszFilename);
4122 RTMemFree(pImage);
4123 }
4124 }
4125
[11284]4126 LogFlowFunc(("returns %Rrc\n", rc));
[2379]4127 return rc;
4128}
4129
4130/**
[32370]4131 * Opens a cache image.
4132 *
4133 * @return VBox status code.
4134 * @param pDisk Pointer to the HDD container which should use the cache image.
4135 * @param pszBackend Name of the cache file backend to use (case insensitive).
4136 * @param pszFilename Name of the cache image to open.
4137 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4138 * @param pVDIfsCache Pointer to the per-cache VD interface list.
4139 */
4140VBOXDDU_DECL(int) VDCacheOpen(PVBOXHDD pDisk, const char *pszBackend,
4141 const char *pszFilename, unsigned uOpenFlags,
4142 PVDINTERFACE pVDIfsCache)
4143{
4144 int rc = VINF_SUCCESS;
4145 int rc2;
4146 bool fLockWrite = false;
4147 PVDCACHE pCache = NULL;
4148
4149 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsCache=%#p\n",
4150 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsCache));
4151
4152 do
4153 {
4154 /* sanity check */
4155 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4156 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4157
4158 /* Check arguments. */
4159 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4160 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4161 rc = VERR_INVALID_PARAMETER);
4162 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4163 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4164 rc = VERR_INVALID_PARAMETER);
4165 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4166 ("uOpenFlags=%#x\n", uOpenFlags),
4167 rc = VERR_INVALID_PARAMETER);
4168
4169 /* Set up image descriptor. */
4170 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
4171 if (!pCache)
4172 {
4173 rc = VERR_NO_MEMORY;
4174 break;
4175 }
4176 pCache->pszFilename = RTStrDup(pszFilename);
4177 if (!pCache->pszFilename)
4178 {
4179 rc = VERR_NO_MEMORY;
4180 break;
4181 }
4182
4183 pCache->pDisk = pDisk;
4184 pCache->pVDIfsCache = pVDIfsCache;
4185
4186 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
4187 if (RT_FAILURE(rc))
4188 break;
4189 if (!pCache->Backend)
4190 {
4191 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4192 N_("VD: unknown backend name '%s'"), pszBackend);
4193 break;
4194 }
4195
4196 /* Set up the I/O interface. */
[32691]4197 pCache->pInterfaceIO = VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IO);
4198 if (pCache->pInterfaceIO)
4199 pCache->pInterfaceIOCallbacks = VDGetInterfaceIO(pCache->pInterfaceIO);
4200 else
4201 {
4202 rc = VDInterfaceAdd(&pCache->VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4203 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsCache);
4204 pCache->pInterfaceIO = &pCache->VDIIO;
4205 pCache->pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4206 }
4207
4208 /* Set up the internal I/O interface. */
4209 AssertBreakStmt(!VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IOINT),
4210 rc = VERR_INVALID_PARAMETER);
4211 rc = VDInterfaceAdd(&pCache->VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
[32553]4212 &pDisk->VDIIOIntCallbacks, pCache, &pCache->pVDIfsCache);
[32370]4213 AssertRC(rc);
4214
4215 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4216 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
4217 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4218 pDisk->pVDIfsDisk,
4219 pCache->pVDIfsCache,
[32536]4220 &pCache->pBackendData);
[32370]4221 /* If the open in read-write mode failed, retry in read-only mode. */
4222 if (RT_FAILURE(rc))
4223 {
4224 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
4225 && ( rc == VERR_ACCESS_DENIED
4226 || rc == VERR_PERMISSION_DENIED
4227 || rc == VERR_WRITE_PROTECT
4228 || rc == VERR_SHARING_VIOLATION
4229 || rc == VERR_FILE_LOCK_FAILED))
4230 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
4231 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
4232 | VD_OPEN_FLAGS_READONLY,
4233 pDisk->pVDIfsDisk,
4234 pCache->pVDIfsCache,
[32536]4235 &pCache->pBackendData);
[32370]4236 if (RT_FAILURE(rc))
4237 {
4238 rc = vdError(pDisk, rc, RT_SRC_POS,
4239 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
4240 break;
4241 }
4242 }
4243
4244 /* Lock disk for writing, as we modify pDisk information below. */
4245 rc2 = vdThreadStartWrite(pDisk);
4246 AssertRC(rc2);
4247 fLockWrite = true;
4248
4249 /*
4250 * Check that the modification UUID of the cache and last image
4251 * match. If not the image was modified inbetween without the cache.
4252 * The cache might contain stale data.
4253 */
4254 RTUUID UuidImage, UuidCache;
4255
[32536]4256 rc = pCache->Backend->pfnGetModificationUuid(pCache->pBackendData,
[32370]4257 &UuidCache);
4258 if (RT_SUCCESS(rc))
4259 {
[32536]4260 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
[32370]4261 &UuidImage);
4262 if (RT_SUCCESS(rc))
4263 {
4264 if (RTUuidCompare(&UuidImage, &UuidCache))
4265 rc = VERR_VD_CACHE_NOT_UP_TO_DATE;
4266 }
4267 }
4268
4269 /*
4270 * We assume that the user knows what he is doing if one of the images
4271 * doesn't support the modification uuid.
4272 */
4273 if (rc == VERR_NOT_SUPPORTED)
4274 rc = VINF_SUCCESS;
4275
4276 if (RT_SUCCESS(rc))
4277 {
4278 /* Cache successfully opened, make it the current one. */
4279 if (!pDisk->pCache)
4280 pDisk->pCache = pCache;
4281 else
4282 rc = VERR_VD_CACHE_ALREADY_EXISTS;
4283 }
4284
4285 if (RT_FAILURE(rc))
4286 {
4287 /* Error detected, but image opened. Close image. */
[32536]4288 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
[32370]4289 AssertRC(rc2);
[32536]4290 pCache->pBackendData = NULL;
[32370]4291 }
4292 } while (0);
4293
4294 if (RT_UNLIKELY(fLockWrite))
4295 {
4296 rc2 = vdThreadFinishWrite(pDisk);
4297 AssertRC(rc2);
4298 }
4299
4300 if (RT_FAILURE(rc))
4301 {
4302 if (pCache)
4303 {
4304 if (pCache->pszFilename)
4305 RTStrFree(pCache->pszFilename);
4306 RTMemFree(pCache);
4307 }
4308 }
4309
4310 LogFlowFunc(("returns %Rrc\n", rc));
4311 return rc;
4312}
4313
4314/**
[2379]4315 * Creates and opens a new base image file.
4316 *
4317 * @returns VBox status code.
[6291]4318 * @param pDisk Pointer to HDD container.
[7277]4319 * @param pszBackend Name of the image file backend to use.
[2379]4320 * @param pszFilename Name of the image file to create.
4321 * @param cbSize Image size in bytes.
4322 * @param uImageFlags Flags specifying special image features.
4323 * @param pszComment Pointer to image comment. NULL is ok.
[6291]4324 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
[18557]4325 * @param pLCHSGeometry Pointer to logical disk geometry <= (x,255,63). Not NULL.
[11353]4326 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
[2379]4327 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
[11444]4328 * @param pVDIfsImage Pointer to the per-image VD interface list.
4329 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
[2379]4330 */
[7277]4331VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
[17970]4332 const char *pszFilename, uint64_t cbSize,
4333 unsigned uImageFlags, const char *pszComment,
[32536]4334 PCVDGEOMETRY pPCHSGeometry,
4335 PCVDGEOMETRY pLCHSGeometry,
[11353]4336 PCRTUUID pUuid, unsigned uOpenFlags,
[11444]4337 PVDINTERFACE pVDIfsImage,
4338 PVDINTERFACE pVDIfsOperation)
[2379]4339{
[2564]4340 int rc = VINF_SUCCESS;
[27232]4341 int rc2;
4342 bool fLockWrite = false, fLockRead = false;
[6291]4343 PVDIMAGE pImage = NULL;
[11353]4344 RTUUID uuid;
[2564]4345
[17970]4346 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",
4347 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment,
[6291]4348 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
4349 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
[11353]4350 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
[11444]4351 uOpenFlags, pVDIfsImage, pVDIfsOperation));
4352
4353 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
4354 VDINTERFACETYPE_PROGRESS);
4355 PVDINTERFACEPROGRESS pCbProgress = NULL;
4356 if (pIfProgress)
4357 pCbProgress = VDGetInterfaceProgress(pIfProgress);
4358
[6291]4359 do
[2564]4360 {
[6291]4361 /* sanity check */
[8569]4362 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]4363 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
[2564]4364
[6291]4365 /* Check arguments. */
[8565]4366 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4367 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4368 rc = VERR_INVALID_PARAMETER);
4369 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4370 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4371 rc = VERR_INVALID_PARAMETER);
4372 AssertMsgBreakStmt(cbSize,
4373 ("cbSize=%llu\n", cbSize),
4374 rc = VERR_INVALID_PARAMETER);
[17970]4375 AssertMsgBreakStmt( ((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0)
4376 || ((uImageFlags & (VD_IMAGE_FLAGS_FIXED | VD_IMAGE_FLAGS_DIFF)) != VD_IMAGE_FLAGS_FIXED),
[8565]4377 ("uImageFlags=%#x\n", uImageFlags),
4378 rc = VERR_INVALID_PARAMETER);
[7281]4379 /* The PCHS geometry fields may be 0 to leave it for later. */
[8565]4380 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
4381 && pPCHSGeometry->cHeads <= 16
4382 && pPCHSGeometry->cSectors <= 63,
4383 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
4384 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
4385 pPCHSGeometry->cSectors),
4386 rc = VERR_INVALID_PARAMETER);
[6960]4387 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
[8565]4388 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
4389 && pLCHSGeometry->cHeads <= 255
4390 && pLCHSGeometry->cSectors <= 63,
4391 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
4392 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
4393 pLCHSGeometry->cSectors),
4394 rc = VERR_INVALID_PARAMETER);
[11353]4395 /* The UUID may be NULL. */
4396 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
4397 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
4398 rc = VERR_INVALID_PARAMETER);
[8565]4399 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4400 ("uOpenFlags=%#x\n", uOpenFlags),
4401 rc = VERR_INVALID_PARAMETER);
[2564]4402
[27232]4403 /* Check state. Needs a temporary read lock. Holding the write lock
4404 * all the time would be blocking other activities for too long. */
4405 rc2 = vdThreadStartRead(pDisk);
4406 AssertRC(rc2);
4407 fLockRead = true;
[8565]4408 AssertMsgBreakStmt(pDisk->cImages == 0,
4409 ("Create base image cannot be done with other images open\n"),
[15366]4410 rc = VERR_VD_INVALID_STATE);
[27232]4411 rc2 = vdThreadFinishRead(pDisk);
4412 AssertRC(rc2);
4413 fLockRead = false;
[2564]4414
[6291]4415 /* Set up image descriptor. */
4416 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
4417 if (!pImage)
4418 {
4419 rc = VERR_NO_MEMORY;
4420 break;
4421 }
4422 pImage->pszFilename = RTStrDup(pszFilename);
4423 if (!pImage->pszFilename)
4424 {
4425 rc = VERR_NO_MEMORY;
4426 break;
4427 }
[28620]4428 pImage->pDisk = pDisk;
[11444]4429 pImage->pVDIfsImage = pVDIfsImage;
[6291]4430
[32691]4431 /* Set up the I/O interface. */
4432 pImage->pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
4433 if (pImage->pInterfaceIO)
4434 pImage->pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->pInterfaceIO);
4435 else
4436 {
4437 rc = VDInterfaceAdd(&pImage->VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4438 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
4439 pImage->pInterfaceIO = &pImage->VDIIO;
4440 pImage->pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4441 }
4442
4443 /* Set up the internal I/O interface. */
4444 AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
4445 rc = VERR_INVALID_PARAMETER);
4446 rc = VDInterfaceAdd(&pImage->VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4447 &pDisk->VDIIOIntCallbacks, pImage, &pImage->pVDIfsImage);
[32553]4448 AssertRC(rc);
[28620]4449
[14852]4450 rc = vdFindBackend(pszBackend, &pImage->Backend);
[11266]4451 if (RT_FAILURE(rc))
[7277]4452 break;
4453 if (!pImage->Backend)
4454 {
4455 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4456 N_("VD: unknown backend name '%s'"), pszBackend);
4457 break;
4458 }
[2564]4459
[11353]4460 /* Create UUID if the caller didn't specify one. */
4461 if (!pUuid)
4462 {
4463 rc = RTUuidCreate(&uuid);
4464 if (RT_FAILURE(rc))
4465 {
4466 rc = vdError(pDisk, rc, RT_SRC_POS,
4467 N_("VD: cannot generate UUID for image '%s'"),
4468 pszFilename);
4469 break;
4470 }
4471 pUuid = &uuid;
4472 }
4473
[7900]4474 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
[17970]4475 uImageFlags &= ~VD_IMAGE_FLAGS_DIFF;
4476 rc = pImage->Backend->pfnCreate(pImage->pszFilename, cbSize,
[7277]4477 uImageFlags, pszComment, pPCHSGeometry,
[11353]4478 pLCHSGeometry, pUuid,
[7900]4479 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
[11444]4480 0, 99,
4481 pDisk->pVDIfsDisk,
4482 pImage->pVDIfsImage,
4483 pVDIfsOperation,
[32536]4484 &pImage->pBackendData);
[7277]4485
[11266]4486 if (RT_SUCCESS(rc))
[2650]4487 {
[17970]4488 pImage->uImageFlags = uImageFlags;
[9560]4489
[7900]4490 /* Force sane optimization settings. It's not worth avoiding writes
4491 * to fixed size images. The overhead would have almost no payback. */
[17970]4492 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
[7900]4493 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
4494
[27232]4495 /* Lock disk for writing, as we modify pDisk information below. */
4496 rc2 = vdThreadStartWrite(pDisk);
4497 AssertRC(rc2);
4498 fLockWrite = true;
4499
[6291]4500 /** @todo optionally check UUIDs */
[2650]4501
[27232]4502 /* Re-check state, as the lock wasn't held and another image
4503 * creation call could have been done by another thread. */
4504 AssertMsgStmt(pDisk->cImages == 0,
4505 ("Create base image cannot be done with other images open\n"),
4506 rc = VERR_VD_INVALID_STATE);
4507 }
[2650]4508
[27232]4509 if (RT_SUCCESS(rc))
4510 {
[6291]4511 /* Cache disk information. */
[32536]4512 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
[6291]4513
4514 /* Cache PCHS geometry. */
[32536]4515 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
[7277]4516 &pDisk->PCHSGeometry);
[11266]4517 if (RT_FAILURE(rc2))
[6291]4518 {
4519 pDisk->PCHSGeometry.cCylinders = 0;
4520 pDisk->PCHSGeometry.cHeads = 0;
4521 pDisk->PCHSGeometry.cSectors = 0;
[2650]4522 }
4523 else
4524 {
[6291]4525 /* Make sure the CHS geometry is properly clipped. */
4526 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
4527 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
4528 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
[2650]4529 }
[6291]4530
4531 /* Cache LCHS geometry. */
[32536]4532 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
[7277]4533 &pDisk->LCHSGeometry);
[11266]4534 if (RT_FAILURE(rc2))
[6291]4535 {
4536 pDisk->LCHSGeometry.cCylinders = 0;
4537 pDisk->LCHSGeometry.cHeads = 0;
4538 pDisk->LCHSGeometry.cSectors = 0;
4539 }
4540 else
4541 {
4542 /* Make sure the CHS geometry is properly clipped. */
4543 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
4544 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
4545 }
[2650]4546
4547 /* Image successfully opened, make it the last image. */
4548 vdAddImageToList(pDisk, pImage);
[7900]4549 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
4550 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
[2650]4551 }
4552 else
4553 {
[32641]4554 /* Error detected, image may or may not be opened. Close and delete
4555 * image if it was opened. */
4556 if (pImage->pBackendData)
4557 {
4558 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
4559 AssertRC(rc2);
4560 pImage->pBackendData = NULL;
4561 }
[2650]4562 }
[6291]4563 } while (0);
[2650]4564
[27232]4565 if (RT_UNLIKELY(fLockWrite))
4566 {
4567 rc2 = vdThreadFinishWrite(pDisk);
4568 AssertRC(rc2);
4569 }
4570 else if (RT_UNLIKELY(fLockRead))
4571 {
4572 rc2 = vdThreadFinishRead(pDisk);
4573 AssertRC(rc2);
4574 }
4575
[11266]4576 if (RT_FAILURE(rc))
[2564]4577 {
[2650]4578 if (pImage)
4579 {
4580 if (pImage->pszFilename)
4581 RTStrFree(pImage->pszFilename);
4582 RTMemFree(pImage);
4583 }
[2564]4584 }
4585
[12143]4586 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
[27245]4587 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
[6291]4588
[11284]4589 LogFlowFunc(("returns %Rrc\n", rc));
[2564]4590 return rc;
[2379]4591}
4592
4593/**
4594 * Creates and opens a new differencing image file in HDD container.
4595 * See comments for VDOpen function about differencing images.
4596 *
4597 * @returns VBox status code.
[6291]4598 * @param pDisk Pointer to HDD container.
[7277]4599 * @param pszBackend Name of the image file backend to use.
[2379]4600 * @param pszFilename Name of the differencing image file to create.
4601 * @param uImageFlags Flags specifying special image features.
4602 * @param pszComment Pointer to image comment. NULL is ok.
[11353]4603 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
[15591]4604 * @param pParentUuid New parent UUID of the image. If NULL, the UUID is queried automatically.
[2379]4605 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
[11444]4606 * @param pVDIfsImage Pointer to the per-image VD interface list.
4607 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
[2379]4608 */
[7277]4609VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
4610 const char *pszFilename, unsigned uImageFlags,
[11353]4611 const char *pszComment, PCRTUUID pUuid,
[15591]4612 PCRTUUID pParentUuid, unsigned uOpenFlags,
4613 PVDINTERFACE pVDIfsImage,
[11444]4614 PVDINTERFACE pVDIfsOperation)
[2379]4615{
[6291]4616 int rc = VINF_SUCCESS;
[27232]4617 int rc2;
4618 bool fLockWrite = false, fLockRead = false;
[6291]4619 PVDIMAGE pImage = NULL;
[11353]4620 RTUUID uuid;
[6291]4621
[32370]4622 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
4623 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsImage, pVDIfsOperation));
[11444]4624
4625 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
4626 VDINTERFACETYPE_PROGRESS);
4627 PVDINTERFACEPROGRESS pCbProgress = NULL;
4628 if (pIfProgress)
4629 pCbProgress = VDGetInterfaceProgress(pIfProgress);
4630
[6291]4631 do
4632 {
4633 /* sanity check */
[8569]4634 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]4635 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4636
4637 /* Check arguments. */
[8565]4638 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4639 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4640 rc = VERR_INVALID_PARAMETER);
4641 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4642 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4643 rc = VERR_INVALID_PARAMETER);
4644 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
4645 ("uImageFlags=%#x\n", uImageFlags),
4646 rc = VERR_INVALID_PARAMETER);
[11353]4647 /* The UUID may be NULL. */
4648 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
4649 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
4650 rc = VERR_INVALID_PARAMETER);
[15591]4651 /* The parent UUID may be NULL. */
4652 AssertMsgBreakStmt(pParentUuid == NULL || VALID_PTR(pParentUuid),
4653 ("pParentUuid=%#p ParentUUID=%RTuuid\n", pParentUuid, pParentUuid),
4654 rc = VERR_INVALID_PARAMETER);
[8565]4655 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4656 ("uOpenFlags=%#x\n", uOpenFlags),
4657 rc = VERR_INVALID_PARAMETER);
[6291]4658
[27232]4659 /* Check state. Needs a temporary read lock. Holding the write lock
4660 * all the time would be blocking other activities for too long. */
4661 rc2 = vdThreadStartRead(pDisk);
4662 AssertRC(rc2);
4663 fLockRead = true;
[8565]4664 AssertMsgBreakStmt(pDisk->cImages != 0,
4665 ("Create diff image cannot be done without other images open\n"),
[15366]4666 rc = VERR_VD_INVALID_STATE);
[27232]4667 rc2 = vdThreadFinishRead(pDisk);
4668 AssertRC(rc2);
4669 fLockRead = false;
[6291]4670
4671 /* Set up image descriptor. */
4672 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
4673 if (!pImage)
4674 {
4675 rc = VERR_NO_MEMORY;
4676 break;
4677 }
4678 pImage->pszFilename = RTStrDup(pszFilename);
4679 if (!pImage->pszFilename)
4680 {
4681 rc = VERR_NO_MEMORY;
4682 break;
4683 }
4684
[14852]4685 rc = vdFindBackend(pszBackend, &pImage->Backend);
[11266]4686 if (RT_FAILURE(rc))
[7277]4687 break;
4688 if (!pImage->Backend)
4689 {
4690 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4691 N_("VD: unknown backend name '%s'"), pszBackend);
4692 break;
4693 }
[6291]4694
[28620]4695 pImage->pDisk = pDisk;
4696 pImage->pVDIfsImage = pVDIfsImage;
4697
[32691]4698 /* Set up the I/O interface. */
4699 pImage->pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
4700 if (pImage->pInterfaceIO)
4701 pImage->pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->pInterfaceIO);
4702 else
4703 {
4704 rc = VDInterfaceAdd(&pImage->VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4705 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
4706 pImage->pInterfaceIO = &pImage->VDIIO;
4707 pImage->pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4708 }
4709
4710 /* Set up the internal I/O interface. */
4711 AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
4712 rc = VERR_INVALID_PARAMETER);
4713 rc = VDInterfaceAdd(&pImage->VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
4714 &pDisk->VDIIOIntCallbacks, pImage, &pImage->pVDIfsImage);
[32553]4715 AssertRC(rc);
[28620]4716
[11353]4717 /* Create UUID if the caller didn't specify one. */
4718 if (!pUuid)
4719 {
4720 rc = RTUuidCreate(&uuid);
4721 if (RT_FAILURE(rc))
4722 {
4723 rc = vdError(pDisk, rc, RT_SRC_POS,
4724 N_("VD: cannot generate UUID for image '%s'"),
4725 pszFilename);
4726 break;
4727 }
4728 pUuid = &uuid;
4729 }
4730
[7900]4731 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
[21806]4732 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
[17970]4733 rc = pImage->Backend->pfnCreate(pImage->pszFilename, pDisk->cbSize,
[21806]4734 uImageFlags | VD_IMAGE_FLAGS_DIFF,
4735 pszComment, &pDisk->PCHSGeometry,
[11353]4736 &pDisk->LCHSGeometry, pUuid,
[7900]4737 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
[11444]4738 0, 99,
4739 pDisk->pVDIfsDisk,
4740 pImage->pVDIfsImage,
4741 pVDIfsOperation,
[32536]4742 &pImage->pBackendData);
[7277]4743
[27232]4744 if (RT_SUCCESS(rc))
[7900]4745 {
[21806]4746 pImage->uImageFlags = uImageFlags;
[9560]4747
[27232]4748 /* Lock disk for writing, as we modify pDisk information below. */
4749 rc2 = vdThreadStartWrite(pDisk);
4750 AssertRC(rc2);
4751 fLockWrite = true;
4752
[7900]4753 /* Switch previous image to read-only mode. */
4754 unsigned uOpenFlagsPrevImg;
[32536]4755 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
[7900]4756 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
4757 {
4758 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
[32536]4759 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
[7900]4760 }
[27232]4761
4762 /** @todo optionally check UUIDs */
4763
4764 /* Re-check state, as the lock wasn't held and another image
4765 * creation call could have been done by another thread. */
4766 AssertMsgStmt(pDisk->cImages != 0,
4767 ("Create diff image cannot be done without other images open\n"),
4768 rc = VERR_VD_INVALID_STATE);
[7900]4769 }
4770
[11266]4771 if (RT_SUCCESS(rc))
[6291]4772 {
[7900]4773 RTUUID Uuid;
[9734]4774 RTTIMESPEC ts;
[7900]4775
[15591]4776 if (pParentUuid && !RTUuidIsNull(pParentUuid))
4777 {
4778 Uuid = *pParentUuid;
[32536]4779 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
[15591]4780 }
4781 else
4782 {
[32536]4783 rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pBackendData,
[15591]4784 &Uuid);
4785 if (RT_SUCCESS(rc2))
[32536]4786 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
[15591]4787 }
[32536]4788 rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
[7900]4789 &Uuid);
[11266]4790 if (RT_SUCCESS(rc2))
[32536]4791 pImage->Backend->pfnSetParentModificationUuid(pImage->pBackendData,
[7900]4792 &Uuid);
[32536]4793 if (pDisk->pLast->Backend->pfnGetTimeStamp)
4794 rc2 = pDisk->pLast->Backend->pfnGetTimeStamp(pDisk->pLast->pBackendData,
4795 &ts);
4796 else
[32600]4797 rc2 = VERR_NOT_IMPLEMENTED;
[32536]4798 if (RT_SUCCESS(rc2) && pImage->Backend->pfnSetParentTimeStamp)
4799 pImage->Backend->pfnSetParentTimeStamp(pImage->pBackendData, &ts);
[9734]4800
[32536]4801 if (pImage->Backend->pfnSetParentFilename)
4802 rc2 = pImage->Backend->pfnSetParentFilename(pImage->pBackendData, pDisk->pLast->pszFilename);
[7900]4803 }
4804
[11266]4805 if (RT_SUCCESS(rc))
[7900]4806 {
[6291]4807 /* Image successfully opened, make it the last image. */
4808 vdAddImageToList(pDisk, pImage);
[7900]4809 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
4810 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
[6291]4811 }
4812 else
4813 {
4814 /* Error detected, but image opened. Close and delete image. */
[32536]4815 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
[6291]4816 AssertRC(rc2);
[32536]4817 pImage->pBackendData = NULL;
[6291]4818 }
4819 } while (0);
4820
[27232]4821 if (RT_UNLIKELY(fLockWrite))
4822 {
4823 rc2 = vdThreadFinishWrite(pDisk);
4824 AssertRC(rc2);
4825 }
4826 else if (RT_UNLIKELY(fLockRead))
4827 {
4828 rc2 = vdThreadFinishRead(pDisk);
4829 AssertRC(rc2);
4830 }
4831
[11266]4832 if (RT_FAILURE(rc))
[6291]4833 {
4834 if (pImage)
4835 {
4836 if (pImage->pszFilename)
4837 RTStrFree(pImage->pszFilename);
4838 RTMemFree(pImage);
4839 }
4840 }
4841
[12143]4842 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
[27245]4843 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
[6291]4844
[11284]4845 LogFlowFunc(("returns %Rrc\n", rc));
[6291]4846 return rc;
[2379]4847}
4848
[27211]4849
[2379]4850/**
[32370]4851 * Creates and opens new cache image file in HDD container.
4852 *
4853 * @return VBox status code.
4854 * @param pDisk Name of the cache file backend to use (case insensitive).
4855 * @param pszFilename Name of the differencing cache file to create.
4856 * @param cbSize Maximum size of the cache.
4857 * @param uImageFlags Flags specifying special cache features.
4858 * @param pszComment Pointer to image comment. NULL is ok.
4859 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
4860 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
4861 * @param pVDIfsCache Pointer to the per-cache VD interface list.
4862 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
4863 */
4864VBOXDDU_DECL(int) VDCreateCache(PVBOXHDD pDisk, const char *pszBackend,
4865 const char *pszFilename, uint64_t cbSize,
4866 unsigned uImageFlags, const char *pszComment,
4867 PCRTUUID pUuid, unsigned uOpenFlags,
4868 PVDINTERFACE pVDIfsCache, PVDINTERFACE pVDIfsOperation)
4869{
4870 int rc = VINF_SUCCESS;
4871 int rc2;
4872 bool fLockWrite = false, fLockRead = false;
4873 PVDCACHE pCache = NULL;
4874 RTUUID uuid;
4875
4876 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" cbSIze=%llu uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
4877 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsCache, pVDIfsOperation));
4878
4879 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
4880 VDINTERFACETYPE_PROGRESS);
4881 PVDINTERFACEPROGRESS pCbProgress = NULL;
4882 if (pIfProgress)
4883 pCbProgress = VDGetInterfaceProgress(pIfProgress);
4884
4885 do
4886 {
4887 /* sanity check */
4888 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
4889 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4890
4891 /* Check arguments. */
4892 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
4893 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
4894 rc = VERR_INVALID_PARAMETER);
4895 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
4896 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
4897 rc = VERR_INVALID_PARAMETER);
4898 AssertMsgBreakStmt(cbSize,
4899 ("cbSize=%llu\n", cbSize),
4900 rc = VERR_INVALID_PARAMETER);
4901 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
4902 ("uImageFlags=%#x\n", uImageFlags),
4903 rc = VERR_INVALID_PARAMETER);
4904 /* The UUID may be NULL. */
4905 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
4906 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
4907 rc = VERR_INVALID_PARAMETER);
4908 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
4909 ("uOpenFlags=%#x\n", uOpenFlags),
4910 rc = VERR_INVALID_PARAMETER);
4911
4912 /* Check state. Needs a temporary read lock. Holding the write lock
4913 * all the time would be blocking other activities for too long. */
4914 rc2 = vdThreadStartRead(pDisk);
4915 AssertRC(rc2);
4916 fLockRead = true;
4917 AssertMsgBreakStmt(pDisk->cImages != 0,
4918 ("Create diff image cannot be done without other images open\n"),
4919 rc = VERR_VD_INVALID_STATE);
4920 AssertMsgBreakStmt(!pDisk->pCache,
4921 ("Create cache image cannot be done with a cache already attached\n"),
4922 rc = VERR_VD_CACHE_ALREADY_EXISTS);
4923 rc2 = vdThreadFinishRead(pDisk);
4924 AssertRC(rc2);
4925 fLockRead = false;
4926
4927 /* Set up image descriptor. */
4928 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
4929 if (!pCache)
4930 {
4931 rc = VERR_NO_MEMORY;
4932 break;
4933 }
4934 pCache->pszFilename = RTStrDup(pszFilename);
4935 if (!pCache->pszFilename)
4936 {
4937 rc = VERR_NO_MEMORY;
4938 break;
4939 }
4940
4941 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
4942 if (RT_FAILURE(rc))
4943 break;
4944 if (!pCache->Backend)
4945 {
4946 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
4947 N_("VD: unknown backend name '%s'"), pszBackend);
4948 break;
4949 }
4950
4951 pCache->pDisk = pDisk;
4952 pCache->pVDIfsCache = pVDIfsCache;
4953
4954 /* Set up the I/O interface. */
[32691]4955 pCache->pInterfaceIO = VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IO);
4956 if (pCache->pInterfaceIO)
4957 pCache->pInterfaceIOCallbacks = VDGetInterfaceIO(pCache->pInterfaceIO);
4958 else
4959 {
4960 rc = VDInterfaceAdd(&pCache->VDIIO, "VD_IO", VDINTERFACETYPE_IO,
4961 &pDisk->VDIIOCallbacks, pDisk, &pVDIfsCache);
4962 pCache->pInterfaceIO = &pCache->VDIIO;
4963 pCache->pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
4964 }
4965
4966 /* Set up the internal I/O interface. */
4967 AssertBreakStmt(!VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IOINT),
4968 rc = VERR_INVALID_PARAMETER);
4969 rc = VDInterfaceAdd(&pCache->VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
[32553]4970 &pDisk->VDIIOIntCallbacks, pCache, &pCache->pVDIfsCache);
[32370]4971 AssertRC(rc);
4972
4973 /* Create UUID if the caller didn't specify one. */
4974 if (!pUuid)
4975 {
4976 rc = RTUuidCreate(&uuid);
4977 if (RT_FAILURE(rc))
4978 {
4979 rc = vdError(pDisk, rc, RT_SRC_POS,
4980 N_("VD: cannot generate UUID for image '%s'"),
4981 pszFilename);
4982 break;
4983 }
4984 pUuid = &uuid;
4985 }
4986
4987 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
4988 rc = pCache->Backend->pfnCreate(pCache->pszFilename, cbSize,
4989 uImageFlags,
4990 pszComment, pUuid,
4991 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
4992 0, 99,
4993 pDisk->pVDIfsDisk,
4994 pCache->pVDIfsCache,
4995 pVDIfsOperation,
[32536]4996 &pCache->pBackendData);
[32370]4997
4998 if (RT_SUCCESS(rc))
4999 {
5000 /* Lock disk for writing, as we modify pDisk information below. */
5001 rc2 = vdThreadStartWrite(pDisk);
5002 AssertRC(rc2);
5003 fLockWrite = true;
5004
5005 /* Re-check state, as the lock wasn't held and another image
5006 * creation call could have been done by another thread. */
5007 AssertMsgStmt(!pDisk->pCache,
5008 ("Create cache image cannot be done with another cache open\n"),
5009 rc = VERR_VD_CACHE_ALREADY_EXISTS);
5010 }
5011
5012 if (RT_SUCCESS(rc))
5013 {
5014 RTUUID UuidModification;
5015
5016 /* Set same modification Uuid as the last image. */
[32536]5017 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
[32370]5018 &UuidModification);
5019 if (RT_SUCCESS(rc))
5020 {
[32536]5021 rc = pCache->Backend->pfnSetModificationUuid(pCache->pBackendData,
[32370]5022 &UuidModification);
5023 }
5024
5025 if (rc == VERR_NOT_SUPPORTED)
5026 rc = VINF_SUCCESS;
5027 }
5028
5029 if (RT_SUCCESS(rc))
5030 {
5031 /* Cache successfully created. */
5032 pDisk->pCache = pCache;
5033 }
5034 else
5035 {
5036 /* Error detected, but image opened. Close and delete image. */
[32536]5037 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, true);
[32370]5038 AssertRC(rc2);
[32536]5039 pCache->pBackendData = NULL;
[32370]5040 }
5041 } while (0);
5042
5043 if (RT_UNLIKELY(fLockWrite))
5044 {
5045 rc2 = vdThreadFinishWrite(pDisk);
5046 AssertRC(rc2);
5047 }
5048 else if (RT_UNLIKELY(fLockRead))
5049 {
5050 rc2 = vdThreadFinishRead(pDisk);
5051 AssertRC(rc2);
5052 }
5053
5054 if (RT_FAILURE(rc))
5055 {
5056 if (pCache)
5057 {
5058 if (pCache->pszFilename)
5059 RTStrFree(pCache->pszFilename);
5060 RTMemFree(pCache);
5061 }
5062 }
5063
5064 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
5065 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
5066
5067 LogFlowFunc(("returns %Rrc\n", rc));
5068 return rc;
5069}
5070
5071/**
[6291]5072 * Merges two images (not necessarily with direct parent/child relationship).
5073 * As a side effect the source image and potentially the other images which
5074 * are also merged to the destination are deleted from both the disk and the
5075 * images in the HDD container.
[2379]5076 *
5077 * @returns VBox status code.
[15366]5078 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
[6291]5079 * @param pDisk Pointer to HDD container.
[2379]5080 * @param nImageFrom Name of the image file to merge from.
5081 * @param nImageTo Name of the image file to merge to.
[11444]5082 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
[2379]5083 */
[2590]5084VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
[11444]5085 unsigned nImageTo, PVDINTERFACE pVDIfsOperation)
[2379]5086{
[6291]5087 int rc = VINF_SUCCESS;
[27232]5088 int rc2;
5089 bool fLockWrite = false, fLockRead = false;
[6291]5090 void *pvBuf = NULL;
5091
[11444]5092 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pVDIfsOperation=%#p\n",
5093 pDisk, nImageFrom, nImageTo, pVDIfsOperation));
5094
5095 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5096 VDINTERFACETYPE_PROGRESS);
5097 PVDINTERFACEPROGRESS pCbProgress = NULL;
5098 if (pIfProgress)
5099 pCbProgress = VDGetInterfaceProgress(pIfProgress);
5100
[6291]5101 do
5102 {
5103 /* sanity check */
[8569]5104 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]5105 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5106
[27806]5107 /* For simplicity reasons lock for writing as the image reopen below
5108 * might need it. After all the reopen is usually needed. */
5109 rc2 = vdThreadStartWrite(pDisk);
[27232]5110 AssertRC(rc2);
[32518]5111 fLockWrite = true;
[6291]5112 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
5113 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
5114 if (!pImageFrom || !pImageTo)
5115 {
[15366]5116 rc = VERR_VD_IMAGE_NOT_FOUND;
[6291]5117 break;
5118 }
[8569]5119 AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
[6291]5120
[7900]5121 /* Make sure destination image is writable. */
[32536]5122 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
[6291]5123 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
5124 {
[7900]5125 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
[32536]5126 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
[7900]5127 uOpenFlags);
[11266]5128 if (RT_FAILURE(rc))
[7900]5129 break;
[6291]5130 }
5131
5132 /* Get size of destination image. */
[32536]5133 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
[27806]5134 rc2 = vdThreadFinishWrite(pDisk);
[27232]5135 AssertRC(rc2);
[32518]5136 fLockWrite = false;
[6291]5137
5138 /* Allocate tmp buffer. */
5139 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
5140 if (!pvBuf)
5141 {
5142 rc = VERR_NO_MEMORY;
5143 break;
5144 }
5145
5146 /* Merging is done directly on the images itself. This potentially
5147 * causes trouble if the disk is full in the middle of operation. */
5148 if (nImageFrom < nImageTo)
5149 {
5150 /* Merge parent state into child. This means writing all not
5151 * allocated blocks in the destination image which are allocated in
5152 * the images to be merged. */
5153 uint64_t uOffset = 0;
5154 uint64_t cbRemaining = cbSize;
5155 do
5156 {
5157 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
[27232]5158
5159 /* Need to hold the write lock during a read-write operation. */
5160 rc2 = vdThreadStartWrite(pDisk);
5161 AssertRC(rc2);
5162 fLockWrite = true;
5163
[32536]5164 rc = pImageTo->Backend->pfnRead(pImageTo->pBackendData,
[7277]5165 uOffset, pvBuf, cbThisRead,
5166 &cbThisRead);
[15366]5167 if (rc == VERR_VD_BLOCK_FREE)
[6291]5168 {
5169 /* Search for image with allocated block. Do not attempt to
5170 * read more than the previous reads marked as valid.
5171 * Otherwise this would return stale data when different
5172 * block sizes are used for the images. */
5173 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
[15366]5174 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VD_BLOCK_FREE;
[6291]5175 pCurrImage = pCurrImage->pPrev)
5176 {
[32536]5177 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
[7277]5178 uOffset, pvBuf,
5179 cbThisRead,
5180 &cbThisRead);
[6291]5181 }
5182
[15366]5183 if (rc != VERR_VD_BLOCK_FREE)
[6291]5184 {
[11266]5185 if (RT_FAILURE(rc))
[7900]5186 break;
[32370]5187 /* Updating the cache is required because this might be a live merge. */
[27211]5188 rc = vdWriteHelper(pDisk, pImageTo, pImageFrom->pPrev,
[32370]5189 uOffset, pvBuf, cbThisRead,
5190 true /* fUpdateCache */);
[11266]5191 if (RT_FAILURE(rc))
[6291]5192 break;
5193 }
[7900]5194 else
5195 rc = VINF_SUCCESS;
[6291]5196 }
[11266]5197 else if (RT_FAILURE(rc))
[7900]5198 break;
[6291]5199
[27232]5200 rc2 = vdThreadFinishWrite(pDisk);
5201 AssertRC(rc2);
5202 fLockWrite = false;
5203
[6291]5204 uOffset += cbThisRead;
5205 cbRemaining -= cbThisRead;
[11444]5206
[12143]5207 if (pCbProgress && pCbProgress->pfnProgress)
[11444]5208 {
[27806]5209 /** @todo r=klaus: this can update the progress to the same
5210 * percentage over and over again if the image format makes
5211 * relatively small increments. */
[27232]5212 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
5213 uOffset * 99 / cbSize);
[11444]5214 if (RT_FAILURE(rc))
5215 break;
5216 }
[6291]5217 } while (uOffset < cbSize);
5218 }
5219 else
5220 {
[27211]5221 /*
5222 * We may need to update the parent uuid of the child coming after the
5223 * last image to be merged. We have to reopen it read/write.
5224 *
5225 * This is done before we do the actual merge to prevent an incosistent
5226 * chain if the mode change fails for some reason.
5227 */
5228 if (pImageFrom->pNext)
5229 {
5230 PVDIMAGE pImageChild = pImageFrom->pNext;
5231
[32518]5232 /* Take the write lock. */
5233 rc2 = vdThreadStartWrite(pDisk);
5234 AssertRC(rc2);
5235 fLockWrite = true;
5236
[27211]5237 /* We need to open the image in read/write mode. */
[32536]5238 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
[27211]5239
5240 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
5241 {
5242 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
[32536]5243 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
[27211]5244 uOpenFlags);
5245 if (RT_FAILURE(rc))
5246 break;
5247 }
[32518]5248
5249 rc2 = vdThreadFinishWrite(pDisk);
5250 AssertRC(rc2);
5251 fLockWrite = false;
[27211]5252 }
5253
[6291]5254 /* Merge child state into parent. This means writing all blocks
5255 * which are allocated in the image up to the source image to the
5256 * destination image. */
5257 uint64_t uOffset = 0;
5258 uint64_t cbRemaining = cbSize;
5259 do
5260 {
5261 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
[15366]5262 rc = VERR_VD_BLOCK_FREE;
[27232]5263
5264 /* Need to hold the write lock during a read-write operation. */
5265 rc2 = vdThreadStartWrite(pDisk);
5266 AssertRC(rc2);
5267 fLockWrite = true;
5268
[6291]5269 /* Search for image with allocated block. Do not attempt to
5270 * read more than the previous reads marked as valid. Otherwise
5271 * this would return stale data when different block sizes are
5272 * used for the images. */
5273 for (PVDIMAGE pCurrImage = pImageFrom;
[15366]5274 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VD_BLOCK_FREE;
[6291]5275 pCurrImage = pCurrImage->pPrev)
5276 {
[32536]5277 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
[7277]5278 uOffset, pvBuf,
5279 cbThisRead, &cbThisRead);
[6291]5280 }
5281
[15366]5282 if (rc != VERR_VD_BLOCK_FREE)
[6291]5283 {
[11266]5284 if (RT_FAILURE(rc))
[7900]5285 break;
[27211]5286 rc = vdWriteHelper(pDisk, pImageTo, NULL, uOffset, pvBuf,
[32370]5287 cbThisRead, true /* fUpdateCache */);
[11266]5288 if (RT_FAILURE(rc))
[6291]5289 break;
5290 }
[7900]5291 else
5292 rc = VINF_SUCCESS;
[6291]5293
[27232]5294 rc2 = vdThreadFinishWrite(pDisk);
5295 AssertRC(rc2);
[32518]5296 fLockWrite = false;
[27232]5297
[6291]5298 uOffset += cbThisRead;
5299 cbRemaining -= cbThisRead;
[11444]5300
[12143]5301 if (pCbProgress && pCbProgress->pfnProgress)
[11444]5302 {
[27806]5303 /** @todo r=klaus: this can update the progress to the same
5304 * percentage over and over again if the image format makes
5305 * relatively small increments. */
[27232]5306 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
5307 uOffset * 99 / cbSize);
[11444]5308 if (RT_FAILURE(rc))
5309 break;
5310 }
[6291]5311 } while (uOffset < cbSize);
5312 }
5313
[32518]5314 /*
5315 * Leave in case of an error to avoid corrupted data in the image chain
5316 * (includes cancelling the operation by the user).
5317 */
[32519]5318 if (RT_FAILURE(rc))
[32518]5319 break;
5320
[27232]5321 /* Need to hold the write lock while finishing the merge. */
5322 rc2 = vdThreadStartWrite(pDisk);
5323 AssertRC(rc2);
5324 fLockWrite = true;
5325
[6291]5326 /* Update parent UUID so that image chain is consistent. */
5327 RTUUID Uuid;
[27213]5328 PVDIMAGE pImageChild = NULL;
[6291]5329 if (nImageFrom < nImageTo)
5330 {
[27359]5331 if (pImageFrom->pPrev)
[6291]5332 {
[32536]5333 rc = pImageFrom->pPrev->Backend->pfnGetUuid(pImageFrom->pPrev->pBackendData,
[27359]5334 &Uuid);
[6291]5335 AssertRC(rc);
5336 }
5337 else
5338 RTUuidClear(&Uuid);
[32536]5339 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pBackendData,
[7277]5340 &Uuid);
[6291]5341 AssertRC(rc);
5342 }
5343 else
5344 {
[27211]5345 /* Update the parent uuid of the child of the last merged image. */
[6291]5346 if (pImageFrom->pNext)
5347 {
[32536]5348 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pBackendData,
[7277]5349 &Uuid);
[6291]5350 AssertRC(rc);
[27211]5351
[32536]5352 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext->pBackendData,
[7277]5353 &Uuid);
[6291]5354 AssertRC(rc);
[27211]5355
[27213]5356 pImageChild = pImageFrom->pNext;
[6291]5357 }
5358 }
5359
5360 /* Delete the no longer needed images. */
5361 PVDIMAGE pImg = pImageFrom, pTmp;
5362 while (pImg != pImageTo)
5363 {
5364 if (nImageFrom < nImageTo)
5365 pTmp = pImg->pNext;
5366 else
5367 pTmp = pImg->pPrev;
5368 vdRemoveImageFromList(pDisk, pImg);
[32536]5369 pImg->Backend->pfnClose(pImg->pBackendData, true);
[13295]5370 RTMemFree(pImg->pszFilename);
5371 RTMemFree(pImg);
[6291]5372 pImg = pTmp;
5373 }
[27213]5374
5375 /* Make sure destination image is back to read only if necessary. */
5376 if (pImageTo != pDisk->pLast)
5377 {
[32536]5378 uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
[27213]5379 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
[32536]5380 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
[27213]5381 uOpenFlags);
5382 if (RT_FAILURE(rc))
5383 break;
5384 }
5385
5386 /*
5387 * Make sure the child is readonly
5388 * for the child -> parent merge direction
5389 * if neccessary.
5390 */
5391 if ( nImageFrom > nImageTo
5392 && pImageChild
5393 && pImageChild != pDisk->pLast)
5394 {
[32536]5395 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
[27213]5396 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
[32536]5397 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
[27213]5398 uOpenFlags);
5399 if (RT_FAILURE(rc))
5400 break;
5401 }
[6291]5402 } while (0);
5403
[27232]5404 if (RT_UNLIKELY(fLockWrite))
5405 {
5406 rc2 = vdThreadFinishWrite(pDisk);
5407 AssertRC(rc2);
5408 }
5409 else if (RT_UNLIKELY(fLockRead))
5410 {
5411 rc2 = vdThreadFinishRead(pDisk);
5412 AssertRC(rc2);
5413 }
5414
[6291]5415 if (pvBuf)
5416 RTMemTmpFree(pvBuf);
5417
[12143]5418 if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
[27232]5419 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
[6291]5420
[11284]5421 LogFlowFunc(("returns %Rrc\n", rc));
[6291]5422 return rc;
[2379]5423}
5424
5425/**
[6291]5426 * Copies an image from one HDD container to another.
5427 * The copy is opened in the target HDD container.
[2379]5428 * It is possible to convert between different image formats, because the
[7277]5429 * backend for the destination may be different from the source.
[6291]5430 * If both the source and destination reference the same HDD container,
5431 * then the image is moved (by copying/deleting or renaming) to the new location.
[2379]5432 * The source container is unchanged if the move operation fails, otherwise
5433 * the image at the new location is opened in the same way as the old one was.
5434 *
5435 * @returns VBox status code.
[15366]5436 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
[6291]5437 * @param pDiskFrom Pointer to source HDD container.
[2379]5438 * @param nImage Image number, counts from 0. 0 is always base image of container.
[6291]5439 * @param pDiskTo Pointer to destination HDD container.
[7277]5440 * @param pszBackend Name of the image file backend to use.
[6291]5441 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
5442 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
5443 * @param cbSize New image size (0 means leave unchanged).
[17836]5444 * @param uImageFlags Flags specifying special destination image features.
[15529]5445 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
5446 * This parameter is used if and only if a true copy is created.
5447 * In all rename/move cases the UUIDs are copied over.
[11444]5448 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
5449 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
5450 * destination image.
5451 * @param pDstVDIfsOperation Pointer to the per-image VD interface list,
5452 * for the destination image.
[2379]5453 */
5454VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
[7277]5455 const char *pszBackend, const char *pszFilename,
[17836]5456 bool fMoveByRename, uint64_t cbSize,
5457 unsigned uImageFlags, PCRTUUID pDstUuid,
[11444]5458 PVDINTERFACE pVDIfsOperation,
5459 PVDINTERFACE pDstVDIfsImage,
5460 PVDINTERFACE pDstVDIfsOperation)
[2379]5461{
[27245]5462 int rc = VINF_SUCCESS;
[27232]5463 int rc2;
5464 bool fLockReadFrom = false, fLockWriteFrom = false, fLockWriteTo = false;
[7690]5465 void *pvBuf = NULL;
5466 PVDIMAGE pImageTo = NULL;
5467
[11444]5468 LogFlowFunc(("pDiskFrom=%#p nImage=%u pDiskTo=%#p pszBackend=\"%s\" pszFilename=\"%s\" fMoveByRename=%d cbSize=%llu pVDIfsOperation=%#p pDstVDIfsImage=%#p pDstVDIfsOperation=%#p\n",
5469 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
[7690]5470
[11444]5471 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5472 VDINTERFACETYPE_PROGRESS);
5473 PVDINTERFACEPROGRESS pCbProgress = NULL;
5474 if (pIfProgress)
5475 pCbProgress = VDGetInterfaceProgress(pIfProgress);
5476
5477 PVDINTERFACE pDstIfProgress = VDInterfaceGet(pDstVDIfsOperation,
5478 VDINTERFACETYPE_PROGRESS);
5479 PVDINTERFACEPROGRESS pDstCbProgress = NULL;
5480 if (pDstIfProgress)
5481 pDstCbProgress = VDGetInterfaceProgress(pDstIfProgress);
5482
[7690]5483 do {
5484 /* Check arguments. */
[8565]5485 AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
5486 rc = VERR_INVALID_PARAMETER);
[7690]5487 AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
[7967]5488 ("u32Signature=%08x\n", pDiskFrom->u32Signature));
[7690]5489
[27232]5490 rc2 = vdThreadStartRead(pDiskFrom);
5491 AssertRC(rc2);
5492 fLockReadFrom = true;
[7690]5493 PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
[15366]5494 AssertPtrBreakStmt(pImageFrom, rc = VERR_VD_IMAGE_NOT_FOUND);
[8565]5495 AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
5496 rc = VERR_INVALID_PARAMETER);
[7690]5497 AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
[7967]5498 ("u32Signature=%08x\n", pDiskTo->u32Signature));
[7690]5499
[15529]5500 /* Move the image. */
5501 if (pDiskFrom == pDiskTo)
[7690]5502 {
[32536]5503 /* Rename only works when backends are the same, are file based
5504 * and the rename method is implemented. */
[15529]5505 if ( fMoveByRename
[32536]5506 && !RTStrICmp(pszBackend, pImageFrom->Backend->pszBackendName)
5507 && pImageFrom->Backend->uBackendCaps & VD_CAP_FILE
5508 && pImageFrom->Backend->pfnRename)
[15529]5509 {
[27232]5510 rc2 = vdThreadFinishRead(pDiskFrom);
5511 AssertRC(rc2);
5512 fLockReadFrom = false;
5513
5514 rc2 = vdThreadStartWrite(pDiskFrom);
5515 AssertRC(rc2);
5516 fLockWriteFrom = true;
[32536]5517 rc = pImageFrom->Backend->pfnRename(pImageFrom->pBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
[7690]5518 break;
[15529]5519 }
[7690]5520
[15529]5521 /** @todo Moving (including shrinking/growing) of the image is
5522 * requested, but the rename attempt failed or it wasn't possible.
5523 * Must now copy image to temp location. */
5524 AssertReleaseMsgFailed(("VDCopy: moving by copy/delete not implemented\n"));
[7690]5525 }
5526
[21025]5527 /* pszFilename is allowed to be NULL, as this indicates copy to the existing image. */
5528 AssertMsgBreakStmt(pszFilename == NULL || (VALID_PTR(pszFilename) && *pszFilename),
[8565]5529 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
5530 rc = VERR_INVALID_PARAMETER);
[7690]5531
[9560]5532 uint64_t cbSizeFrom;
[32536]5533 cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pBackendData);
[7690]5534 if (cbSizeFrom == 0)
5535 {
[15366]5536 rc = VERR_VD_VALUE_NOT_FOUND;
[7690]5537 break;
5538 }
[7967]5539
[32536]5540 VDGEOMETRY PCHSGeometryFrom = {0, 0, 0};
5541 VDGEOMETRY LCHSGeometryFrom = {0, 0, 0};
5542 pImageFrom->Backend->pfnGetPCHSGeometry(pImageFrom->pBackendData, &PCHSGeometryFrom);
5543 pImageFrom->Backend->pfnGetLCHSGeometry(pImageFrom->pBackendData, &LCHSGeometryFrom);
[7967]5544
[15529]5545 RTUUID ImageUuid, ImageModificationUuid;
5546 if (pDiskFrom != pDiskTo)
5547 {
5548 if (pDstUuid)
5549 ImageUuid = *pDstUuid;
5550 else
5551 RTUuidCreate(&ImageUuid);
5552 }
5553 else
5554 {
[32536]5555 rc = pImageFrom->Backend->pfnGetUuid(pImageFrom->pBackendData, &ImageUuid);
[15529]5556 if (RT_FAILURE(rc))
[15566]5557 RTUuidCreate(&ImageUuid);
[15529]5558 }
[32536]5559 rc = pImageFrom->Backend->pfnGetModificationUuid(pImageFrom->pBackendData, &ImageModificationUuid);
[15529]5560 if (RT_FAILURE(rc))
5561 RTUuidClear(&ImageModificationUuid);
5562
5563 char szComment[1024];
[32536]5564 rc = pImageFrom->Backend->pfnGetComment(pImageFrom->pBackendData, szComment, sizeof(szComment));
[15529]5565 if (RT_FAILURE(rc))
5566 szComment[0] = '\0';
5567 else
5568 szComment[sizeof(szComment) - 1] = '\0';
5569
[7690]5570 unsigned uOpenFlagsFrom;
[32536]5571 uOpenFlagsFrom = pImageFrom->Backend->pfnGetOpenFlags(pImageFrom->pBackendData);
[7690]5572
[27232]5573 rc2 = vdThreadFinishRead(pDiskFrom);
5574 AssertRC(rc2);
5575 fLockReadFrom = false;
5576
[29497]5577 rc2 = vdThreadStartRead(pDiskTo);
5578 AssertRC(rc2);
5579 unsigned cImagesTo = pDiskTo->cImages;
5580 rc2 = vdThreadFinishRead(pDiskTo);
5581 AssertRC(rc2);
5582
[21025]5583 if (pszFilename)
[7690]5584 {
[21025]5585 if (cbSize == 0)
5586 cbSize = cbSizeFrom;
[16963]5587
[29497]5588 /* Create destination image with the properties of source image. */
[21025]5589 /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
5590 * calls to the backend. Unifies the code and reduces the API
[27232]5591 * dependencies. Would also make the synchronization explicit. */
[29497]5592 if (cImagesTo > 0)
[21025]5593 {
[29497]5594 rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename,
5595 uImageFlags, szComment, &ImageUuid,
5596 NULL /* pParentUuid */,
5597 uOpenFlagsFrom & ~VD_OPEN_FLAGS_READONLY,
[32858]5598 pDstVDIfsImage, NULL);
[27232]5599
5600 rc2 = vdThreadStartWrite(pDiskTo);
5601 AssertRC(rc2);
5602 fLockWriteTo = true;
[21025]5603 } else {
[27232]5604 /** @todo hack to force creation of a fixed image for
5605 * the RAW backend, which can't handle anything else. */
[21025]5606 if (!RTStrICmp(pszBackend, "RAW"))
5607 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
5608
[24439]5609 /* Fix broken PCHS geometry. Can happen for two reasons: either
5610 * the backend mixes up PCHS and LCHS, or the application used
5611 * to create the source image has put garbage in it. */
5612 /** @todo double-check if the VHD backend correctly handles
5613 * PCHS and LCHS geometry. also reconsider our current paranoia
5614 * level when it comes to geometry settings here and in the
5615 * backends. */
5616 if (PCHSGeometryFrom.cHeads > 16 || PCHSGeometryFrom.cSectors > 63)
5617 {
5618 Assert(RT_MIN(cbSize / 512 / 16 / 63, 16383) - (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383));
5619 PCHSGeometryFrom.cCylinders = (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383);
5620 PCHSGeometryFrom.cHeads = 16;
5621 PCHSGeometryFrom.cSectors = 63;
5622 }
5623
[21025]5624 rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, cbSize,
5625 uImageFlags, szComment,
5626 &PCHSGeometryFrom, &LCHSGeometryFrom,
[32858]5627 NULL, uOpenFlagsFrom & ~VD_OPEN_FLAGS_READONLY, pDstVDIfsImage, NULL);
[27245]5628
[27232]5629 rc2 = vdThreadStartWrite(pDiskTo);
5630 AssertRC(rc2);
5631 fLockWriteTo = true;
5632
[21025]5633 if (RT_SUCCESS(rc) && !RTUuidIsNull(&ImageUuid))
[32536]5634 pDiskTo->pLast->Backend->pfnSetUuid(pDiskTo->pLast->pBackendData, &ImageUuid);
[21025]5635 }
5636 if (RT_FAILURE(rc))
5637 break;
5638
5639 pImageTo = pDiskTo->pLast;
5640 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
[21030]5641
5642 cbSize = RT_MIN(cbSize, cbSizeFrom);
[7690]5643 }
[21025]5644 else
5645 {
5646 pImageTo = pDiskTo->pLast;
5647 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
[7690]5648
[21025]5649 uint64_t cbSizeTo;
[32536]5650 cbSizeTo = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
[21025]5651 if (cbSizeTo == 0)
5652 {
5653 rc = VERR_VD_VALUE_NOT_FOUND;
5654 break;
5655 }
[7690]5656
[21025]5657 if (cbSize == 0)
5658 cbSize = RT_MIN(cbSizeFrom, cbSizeTo);
5659 }
5660
[27232]5661 rc2 = vdThreadFinishWrite(pDiskTo);
5662 AssertRC(rc2);
5663 fLockWriteTo = false;
5664
[7690]5665 /* Allocate tmp buffer. */
5666 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
5667 if (!pvBuf)
5668 {
5669 rc = VERR_NO_MEMORY;
5670 break;
5671 }
5672
[30313]5673 /* Whether we can take the optimized copy path (false) or not.
5674 * Don't optimize if the image existed or if it is a child image. */
5675 bool fRegularRead = (pszFilename == NULL) || (cImagesTo > 0);
5676
[7690]5677 /* Copy the data. */
5678 uint64_t uOffset = 0;
5679 uint64_t cbRemaining = cbSize;
[7967]5680
[7690]5681 do
5682 {
5683 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
5684
[27232]5685 /* Note that we don't attempt to synchronize cross-disk accesses.
5686 * It wouldn't be very difficult to do, just the lock order would
5687 * need to be defined somehow to prevent deadlocks. Postpone such
5688 * magic as there is no use case for this. */
5689
5690 rc2 = vdThreadStartRead(pDiskFrom);
5691 AssertRC(rc2);
5692 fLockReadFrom = true;
5693
[32370]5694 /*
5695 * Updating the cache doesn't make any sense
5696 * as we are looping once through the image.
5697 */
[27211]5698 rc = vdReadHelper(pDiskFrom, pImageFrom, NULL, uOffset, pvBuf,
[32370]5699 cbThisRead, fRegularRead,
5700 false /* fUpdateCache */);
[30313]5701 if (RT_FAILURE(rc) && rc != VERR_VD_BLOCK_FREE)
[7690]5702 break;
5703
[27232]5704 rc2 = vdThreadFinishRead(pDiskFrom);
5705 AssertRC(rc2);
5706 fLockReadFrom = false;
5707
[30313]5708 if (rc != VERR_VD_BLOCK_FREE)
5709 {
5710 rc2 = vdThreadStartWrite(pDiskTo);
5711 AssertRC(rc2);
5712 fLockWriteTo = true;
[27232]5713
[30313]5714 rc = vdWriteHelper(pDiskTo, pImageTo, NULL, uOffset, pvBuf,
[32370]5715 cbThisRead, false /* fUpdateCache */);
[30313]5716 if (RT_FAILURE(rc))
5717 break;
[7690]5718
[30313]5719 rc2 = vdThreadFinishWrite(pDiskTo);
5720 AssertRC(rc2);
5721 fLockWriteTo = false;
5722 }
[32381]5723 else /* Don't propagate the error to the outside */
5724 rc = VINF_SUCCESS;
[27232]5725
[7690]5726 uOffset += cbThisRead;
5727 cbRemaining -= cbThisRead;
[11444]5728
[12143]5729 if (pCbProgress && pCbProgress->pfnProgress)
[7690]5730 {
[27806]5731 /** @todo r=klaus: this can update the progress to the same
5732 * percentage over and over again if the image format makes
5733 * relatively small increments. */
[27232]5734 rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
5735 uOffset * 99 / cbSize);
[11266]5736 if (RT_FAILURE(rc))
[7690]5737 break;
5738 }
[12774]5739 if (pDstCbProgress && pDstCbProgress->pfnProgress)
[11444]5740 {
[27806]5741 /** @todo r=klaus: this can update the progress to the same
5742 * percentage over and over again if the image format makes
5743 * relatively small increments. */
[27232]5744 rc = pDstCbProgress->pfnProgress(pDstIfProgress->pvUser,
5745 uOffset * 99 / cbSize);
[11444]5746 if (RT_FAILURE(rc))
5747 break;
5748 }
[7690]5749 } while (uOffset < cbSize);
5750
[15529]5751 if (RT_SUCCESS(rc))
[7690]5752 {
[27232]5753 rc2 = vdThreadStartWrite(pDiskTo);
5754 AssertRC(rc2);
5755 fLockWriteTo = true;
5756
[24439]5757 /* Only set modification UUID if it is non-null, since the source
5758 * backend might not provide a valid modification UUID. */
5759 if (!RTUuidIsNull(&ImageModificationUuid))
[32536]5760 pImageTo->Backend->pfnSetModificationUuid(pImageTo->pBackendData, &ImageModificationUuid);
[7690]5761 }
5762 } while (0);
5763
[21025]5764 if (RT_FAILURE(rc) && pImageTo && pszFilename)
[7690]5765 {
[27232]5766 /* Take the write lock only if it is not taken. Not worth making the
5767 * above code even more complicated. */
5768 if (RT_UNLIKELY(!fLockWriteTo))
5769 {
5770 rc2 = vdThreadStartWrite(pDiskTo);
5771 AssertRC(rc2);
5772 fLockWriteTo = true;
5773 }
[7690]5774 /* Error detected, but new image created. Remove image from list. */
5775 vdRemoveImageFromList(pDiskTo, pImageTo);
5776
5777 /* Close and delete image. */
[32536]5778 rc2 = pImageTo->Backend->pfnClose(pImageTo->pBackendData, true);
[7690]5779 AssertRC(rc2);
[32536]5780 pImageTo->pBackendData = NULL;
[7690]5781
5782 /* Free remaining resources. */
5783 if (pImageTo->pszFilename)
5784 RTStrFree(pImageTo->pszFilename);
[7967]5785
[7690]5786 RTMemFree(pImageTo);
5787 }
5788
[27232]5789 if (RT_UNLIKELY(fLockWriteTo))
5790 {
5791 rc2 = vdThreadFinishWrite(pDiskTo);
5792 AssertRC(rc2);
5793 }
5794 if (RT_UNLIKELY(fLockWriteFrom))
5795 {
5796 rc2 = vdThreadFinishWrite(pDiskFrom);
5797 AssertRC(rc2);
5798 }
5799 else if (RT_UNLIKELY(fLockReadFrom))
5800 {
5801 rc2 = vdThreadFinishRead(pDiskFrom);
5802 AssertRC(rc2);
5803 }
5804
[7690]5805 if (pvBuf)
5806 RTMemTmpFree(pvBuf);
5807
[11444]5808 if (RT_SUCCESS(rc))
5809 {
[12143]5810 if (pCbProgress && pCbProgress->pfnProgress)
[27232]5811 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
[12774]5812 if (pDstCbProgress && pDstCbProgress->pfnProgress)
[27232]5813 pDstCbProgress->pfnProgress(pDstIfProgress->pvUser, 100);
[11444]5814 }
[7690]5815
[11284]5816 LogFlowFunc(("returns %Rrc\n", rc));
[7690]5817 return rc;
[2379]5818}
5819
5820/**
[19034]5821 * Optimizes the storage consumption of an image. Typically the unused blocks
5822 * have to be wiped with zeroes to achieve a substantial reduced storage use.
5823 * Another optimization done is reordering the image blocks, which can provide
5824 * a significant performance boost, as reads and writes tend to use less random
5825 * file offsets.
5826 *
5827 * @return VBox status code.
5828 * @return VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
5829 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
[19176]5830 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
5831 * the code for this isn't implemented yet.
[19034]5832 * @param pDisk Pointer to HDD container.
5833 * @param nImage Image number, counts from 0. 0 is always base image of container.
5834 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
5835 */
5836VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
5837 PVDINTERFACE pVDIfsOperation)
5838{
[27232]5839 int rc = VINF_SUCCESS;
5840 int rc2;
5841 bool fLockRead = false, fLockWrite = false;
[19176]5842 void *pvBuf = NULL;
5843 void *pvTmp = NULL;
5844
5845 LogFlowFunc(("pDisk=%#p nImage=%u pVDIfsOperation=%#p\n",
5846 pDisk, nImage, pVDIfsOperation));
5847
5848 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5849 VDINTERFACETYPE_PROGRESS);
5850 PVDINTERFACEPROGRESS pCbProgress = NULL;
5851 if (pIfProgress)
5852 pCbProgress = VDGetInterfaceProgress(pIfProgress);
5853
5854 do {
5855 /* Check arguments. */
5856 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
5857 rc = VERR_INVALID_PARAMETER);
5858 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
5859 ("u32Signature=%08x\n", pDisk->u32Signature));
5860
[27232]5861 rc2 = vdThreadStartRead(pDisk);
5862 AssertRC(rc2);
5863 fLockRead = true;
5864
[19176]5865 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
5866 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
5867
5868 /* If there is no compact callback for not file based backends then
5869 * the backend doesn't need compaction. No need to make much fuss about
5870 * this. For file based ones signal this as not yet supported. */
5871 if (!pImage->Backend->pfnCompact)
5872 {
5873 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
5874 rc = VERR_NOT_SUPPORTED;
5875 else
5876 rc = VINF_SUCCESS;
5877 break;
5878 }
5879
5880 /* Insert interface for reading parent state into per-operation list,
5881 * if there is a parent image. */
5882 VDINTERFACE IfOpParent;
5883 VDINTERFACEPARENTSTATE ParentCb;
5884 VDPARENTSTATEDESC ParentUser;
5885 if (pImage->pPrev)
5886 {
5887 ParentCb.cbSize = sizeof(ParentCb);
5888 ParentCb.enmInterface = VDINTERFACETYPE_PARENTSTATE;
5889 ParentCb.pfnParentRead = vdParentRead;
5890 ParentUser.pDisk = pDisk;
5891 ParentUser.pImage = pImage->pPrev;
5892 rc = VDInterfaceAdd(&IfOpParent, "VDCompact_ParentState", VDINTERFACETYPE_PARENTSTATE,
5893 &ParentCb, &ParentUser, &pVDIfsOperation);
5894 AssertRC(rc);
5895 }
5896
[27232]5897 rc2 = vdThreadFinishRead(pDisk);
5898 AssertRC(rc2);
5899 fLockRead = false;
5900
5901 rc2 = vdThreadStartWrite(pDisk);
5902 AssertRC(rc2);
5903 fLockWrite = true;
5904
[32536]5905 rc = pImage->Backend->pfnCompact(pImage->pBackendData,
[19176]5906 0, 99,
[27232]5907 pDisk->pVDIfsDisk,
5908 pImage->pVDIfsImage,
[19176]5909 pVDIfsOperation);
5910 } while (0);
5911
[27232]5912 if (RT_UNLIKELY(fLockWrite))
5913 {
5914 rc2 = vdThreadFinishWrite(pDisk);
5915 AssertRC(rc2);
5916 }
5917 else if (RT_UNLIKELY(fLockRead))
5918 {
5919 rc2 = vdThreadFinishRead(pDisk);
5920 AssertRC(rc2);
5921 }
5922
[19176]5923 if (pvBuf)
5924 RTMemTmpFree(pvBuf);
5925 if (pvTmp)
5926 RTMemTmpFree(pvTmp);
5927
5928 if (RT_SUCCESS(rc))
5929 {
5930 if (pCbProgress && pCbProgress->pfnProgress)
[27232]5931 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
[19176]5932 }
5933
5934 LogFlowFunc(("returns %Rrc\n", rc));
5935 return rc;
[19034]5936}
5937
5938/**
[31776]5939 * Resizes the the given disk image to the given size.
5940 *
[32431]5941 * @return VBox status
[31776]5942 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
5943 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
5944 *
5945 * @param pDisk Pointer to the HDD container.
5946 * @param cbSize New size of the image.
5947 * @param pPCHSGeometry Pointer to the new physical disk geometry <= (16383,16,63). Not NULL.
5948 * @param pLCHSGeometry Pointer to the new logical disk geometry <= (x,255,63). Not NULL.
5949 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
5950 */
5951VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, uint64_t cbSize,
[32536]5952 PCVDGEOMETRY pPCHSGeometry,
5953 PCVDGEOMETRY pLCHSGeometry,
[31776]5954 PVDINTERFACE pVDIfsOperation)
5955{
[32536]5956 /** @todo r=klaus resizing was designed to be part of VDCopy, so having a separate function is not desirable. */
[31776]5957 int rc = VINF_SUCCESS;
5958 int rc2;
5959 bool fLockRead = false, fLockWrite = false;
5960
5961 LogFlowFunc(("pDisk=%#p cbSize=%llu pVDIfsOperation=%#p\n",
5962 pDisk, cbSize, pVDIfsOperation));
5963
5964 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
5965 VDINTERFACETYPE_PROGRESS);
5966 PVDINTERFACEPROGRESS pCbProgress = NULL;
5967 if (pIfProgress)
5968 pCbProgress = VDGetInterfaceProgress(pIfProgress);
5969
5970 do {
5971 /* Check arguments. */
5972 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
5973 rc = VERR_INVALID_PARAMETER);
5974 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
5975 ("u32Signature=%08x\n", pDisk->u32Signature));
5976
5977 rc2 = vdThreadStartRead(pDisk);
5978 AssertRC(rc2);
5979 fLockRead = true;
5980
5981 /* Not supported if the disk has child images attached. */
5982 AssertMsgBreakStmt(pDisk->cImages == 1, ("cImages=%u\n", pDisk->cImages),
5983 rc = VERR_NOT_SUPPORTED);
5984
5985 PVDIMAGE pImage = pDisk->pBase;
5986
5987 /* If there is no compact callback for not file based backends then
5988 * the backend doesn't need compaction. No need to make much fuss about
5989 * this. For file based ones signal this as not yet supported. */
5990 if (!pImage->Backend->pfnResize)
5991 {
5992 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
5993 rc = VERR_NOT_SUPPORTED;
5994 else
5995 rc = VINF_SUCCESS;
5996 break;
5997 }
5998
5999 rc2 = vdThreadFinishRead(pDisk);
6000 AssertRC(rc2);
6001 fLockRead = false;
6002
6003 rc2 = vdThreadStartWrite(pDisk);
6004 AssertRC(rc2);
6005 fLockWrite = true;
6006
[32536]6007 VDGEOMETRY PCHSGeometryOld;
6008 VDGEOMETRY LCHSGeometryOld;
6009 PCVDGEOMETRY pPCHSGeometryNew;
6010 PCVDGEOMETRY pLCHSGeometryNew;
[31919]6011
6012 if (pPCHSGeometry->cCylinders == 0)
6013 {
6014 /* Auto-detect marker, calculate new value ourself. */
[32536]6015 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData, &PCHSGeometryOld);
[31919]6016 if (RT_SUCCESS(rc) && (PCHSGeometryOld.cCylinders != 0))
6017 PCHSGeometryOld.cCylinders = RT_MIN(cbSize / 512 / PCHSGeometryOld.cHeads / PCHSGeometryOld.cSectors, 16383);
6018 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
6019 rc = VINF_SUCCESS;
6020
6021 pPCHSGeometryNew = &PCHSGeometryOld;
6022 }
6023 else
6024 pPCHSGeometryNew = pPCHSGeometry;
6025
6026 if (pLCHSGeometry->cCylinders == 0)
6027 {
6028 /* Auto-detect marker, calculate new value ourself. */
[32536]6029 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData, &LCHSGeometryOld);
[31919]6030 if (RT_SUCCESS(rc) && (LCHSGeometryOld.cCylinders != 0))
6031 LCHSGeometryOld.cCylinders = cbSize / 512 / LCHSGeometryOld.cHeads / LCHSGeometryOld.cSectors;
6032 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
6033 rc = VINF_SUCCESS;
6034
6035 pLCHSGeometryNew = &LCHSGeometryOld;
6036 }
6037 else
6038 pLCHSGeometryNew = pLCHSGeometry;
6039
6040 if (RT_SUCCESS(rc))
[32536]6041 rc = pImage->Backend->pfnResize(pImage->pBackendData,
6042 cbSize,
[31919]6043 pPCHSGeometryNew,
6044 pLCHSGeometryNew,
6045 0, 99,
6046 pDisk->pVDIfsDisk,
6047 pImage->pVDIfsImage,
6048 pVDIfsOperation);
[31776]6049 } while (0);
6050
6051 if (RT_UNLIKELY(fLockWrite))
6052 {
6053 rc2 = vdThreadFinishWrite(pDisk);
6054 AssertRC(rc2);
6055 }
6056 else if (RT_UNLIKELY(fLockRead))
6057 {
6058 rc2 = vdThreadFinishRead(pDisk);
6059 AssertRC(rc2);
6060 }
6061
6062 if (RT_SUCCESS(rc))
6063 {
6064 if (pCbProgress && pCbProgress->pfnProgress)
6065 pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
6066 }
6067
6068 LogFlowFunc(("returns %Rrc\n", rc));
6069 return rc;
6070}
6071
6072/**
[6291]6073 * Closes the last opened image file in HDD container.
[27232]6074 * If previous image file was opened in read-only mode (the normal case) and
6075 * the last opened image is in read-write mode then the previous image will be
6076 * reopened in read/write mode.
[2379]6077 *
[6291]6078 * @returns VBox status code.
[15366]6079 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
[6291]6080 * @param pDisk Pointer to HDD container.
[2379]6081 * @param fDelete If true, delete the image from the host disk.
6082 */
6083VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
6084{
[18150]6085 int rc = VINF_SUCCESS;
[27232]6086 int rc2;
6087 bool fLockWrite = false;
[2650]6088
[7277]6089 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
[6291]6090 do
[2650]6091 {
[6291]6092 /* sanity check */
[8569]6093 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]6094 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
[2650]6095
[27232]6096 /* Not worth splitting this up into a read lock phase and write
6097 * lock phase, as closing an image is a relatively fast operation
6098 * dominated by the part which needs the write lock. */
6099 rc2 = vdThreadStartWrite(pDisk);
6100 AssertRC(rc2);
6101 fLockWrite = true;
6102
[6291]6103 PVDIMAGE pImage = pDisk->pLast;
[18150]6104 if (!pImage)
6105 {
6106 rc = VERR_VD_NOT_OPENED;
6107 break;
6108 }
[32536]6109 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
[6291]6110 /* Remove image from list of opened images. */
6111 vdRemoveImageFromList(pDisk, pImage);
6112 /* Close (and optionally delete) image. */
[32536]6113 rc = pImage->Backend->pfnClose(pImage->pBackendData, fDelete);
[6291]6114 /* Free remaining resources related to the image. */
6115 RTStrFree(pImage->pszFilename);
6116 RTMemFree(pImage);
6117
6118 pImage = pDisk->pLast;
6119 if (!pImage)
6120 break;
6121
6122 /* If disk was previously in read/write mode, make sure it will stay
6123 * like this (if possible) after closing this image. Set the open flags
6124 * accordingly. */
6125 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
6126 {
[32536]6127 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
[6291]6128 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
[32536]6129 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
[6291]6130 }
6131
6132 /* Cache disk information. */
[32536]6133 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
[6291]6134
6135 /* Cache PCHS geometry. */
[32536]6136 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
[6291]6137 &pDisk->PCHSGeometry);
[11266]6138 if (RT_FAILURE(rc2))
[6291]6139 {
6140 pDisk->PCHSGeometry.cCylinders = 0;
6141 pDisk->PCHSGeometry.cHeads = 0;
6142 pDisk->PCHSGeometry.cSectors = 0;
6143 }
6144 else
6145 {
6146 /* Make sure the PCHS geometry is properly clipped. */
6147 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
6148 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
6149 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
6150 }
6151
6152 /* Cache LCHS geometry. */
[32536]6153 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
[7277]6154 &pDisk->LCHSGeometry);
[11266]6155 if (RT_FAILURE(rc2))
[6291]6156 {
6157 pDisk->LCHSGeometry.cCylinders = 0;
6158 pDisk->LCHSGeometry.cHeads = 0;
6159 pDisk->LCHSGeometry.cSectors = 0;
6160 }
6161 else
6162 {
6163 /* Make sure the LCHS geometry is properly clipped. */
6164 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
6165 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
6166 }
6167 } while (0);
6168
[27232]6169 if (RT_UNLIKELY(fLockWrite))
6170 {
6171 rc2 = vdThreadFinishWrite(pDisk);
6172 AssertRC(rc2);
6173 }
6174
[11284]6175 LogFlowFunc(("returns %Rrc\n", rc));
[2650]6176 return rc;
[2379]6177}
6178
6179/**
[32370]6180 * Closes the currently opened cache image file in HDD container.
6181 *
6182 * @return VBox status code.
6183 * @return VERR_VD_NOT_OPENED if no cache is opened in HDD container.
6184 * @param pDisk Pointer to HDD container.
6185 * @param fDelete If true, delete the image from the host disk.
6186 */
6187VBOXDDU_DECL(int) VDCacheClose(PVBOXHDD pDisk, bool fDelete)
6188{
6189 int rc = VINF_SUCCESS;
6190 int rc2;
6191 bool fLockWrite = false;
6192 PVDCACHE pCache = NULL;
6193
6194 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
6195
6196 do
6197 {
6198 /* sanity check */
6199 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6200 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6201
6202 rc2 = vdThreadStartWrite(pDisk);
6203 AssertRC(rc2);
6204 fLockWrite = true;
6205
6206 AssertPtrBreakStmt(pDisk->pCache, rc = VERR_VD_CACHE_NOT_FOUND);
6207
6208 pCache = pDisk->pCache;
6209 pDisk->pCache = NULL;
6210
[32536]6211 pCache->Backend->pfnClose(pCache->pBackendData, fDelete);
[32370]6212 if (pCache->pszFilename)
6213 RTStrFree(pCache->pszFilename);
6214 RTMemFree(pCache);
6215 } while (0);
6216
6217 if (RT_LIKELY(fLockWrite))
6218 {
6219 rc2 = vdThreadFinishWrite(pDisk);
6220 AssertRC(rc2);
6221 }
6222
6223 LogFlowFunc(("returns %Rrc\n", rc));
6224 return rc;
6225}
6226
6227/**
[2379]6228 * Closes all opened image files in HDD container.
6229 *
[6291]6230 * @returns VBox status code.
6231 * @param pDisk Pointer to HDD container.
[2379]6232 */
6233VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
6234{
[6291]6235 int rc = VINF_SUCCESS;
[27232]6236 int rc2;
6237 bool fLockWrite = false;
[2379]6238
[6291]6239 LogFlowFunc(("pDisk=%#p\n", pDisk));
6240 do
[2379]6241 {
[6291]6242 /* sanity check */
[8569]6243 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]6244 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
[2379]6245
[27232]6246 /* Lock the entire operation. */
6247 rc2 = vdThreadStartWrite(pDisk);
6248 AssertRC(rc2);
6249 fLockWrite = true;
6250
[32370]6251 PVDCACHE pCache = pDisk->pCache;
6252 if (pCache)
6253 {
[32536]6254 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
[32370]6255 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
6256 rc = rc2;
6257
6258 if (pCache->pszFilename)
6259 RTStrFree(pCache->pszFilename);
6260 RTMemFree(pCache);
6261 }
6262
[6291]6263 PVDIMAGE pImage = pDisk->pLast;
[7199]6264 while (VALID_PTR(pImage))
[6291]6265 {
6266 PVDIMAGE pPrev = pImage->pPrev;
6267 /* Remove image from list of opened images. */
6268 vdRemoveImageFromList(pDisk, pImage);
6269 /* Close image. */
[32536]6270 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
[11266]6271 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
[6291]6272 rc = rc2;
6273 /* Free remaining resources related to the image. */
6274 RTStrFree(pImage->pszFilename);
6275 RTMemFree(pImage);
6276 pImage = pPrev;
6277 }
[7199]6278 Assert(!VALID_PTR(pDisk->pLast));
[6291]6279 } while (0);
6280
[27232]6281 if (RT_UNLIKELY(fLockWrite))
6282 {
6283 rc2 = vdThreadFinishWrite(pDisk);
6284 AssertRC(rc2);
6285 }
6286
[11284]6287 LogFlowFunc(("returns %Rrc\n", rc));
[2379]6288 return rc;
6289}
6290
6291/**
6292 * Read data from virtual HDD.
6293 *
6294 * @returns VBox status code.
[15366]6295 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
[6291]6296 * @param pDisk Pointer to HDD container.
[2379]6297 * @param uOffset Offset of first reading byte from start of disk.
6298 * @param pvBuf Pointer to buffer for reading data.
6299 * @param cbRead Number of bytes to read.
6300 */
[6291]6301VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
6302 size_t cbRead)
[2379]6303{
[27232]6304 int rc = VINF_SUCCESS;
6305 int rc2;
6306 bool fLockRead = false;
[2379]6307
[6326]6308 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
6309 pDisk, uOffset, pvBuf, cbRead));
[6291]6310 do
[2379]6311 {
[6291]6312 /* sanity check */
[8569]6313 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]6314 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
[2379]6315
[6291]6316 /* Check arguments. */
[8565]6317 AssertMsgBreakStmt(VALID_PTR(pvBuf),
6318 ("pvBuf=%#p\n", pvBuf),
6319 rc = VERR_INVALID_PARAMETER);
6320 AssertMsgBreakStmt(cbRead,
6321 ("cbRead=%zu\n", cbRead),
6322 rc = VERR_INVALID_PARAMETER);
[27232]6323
6324 rc2 = vdThreadStartRead(pDisk);
6325 AssertRC(rc2);
6326 fLockRead = true;
6327
[8565]6328 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
6329 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
6330 uOffset, cbRead, pDisk->cbSize),
6331 rc = VERR_INVALID_PARAMETER);
[2379]6332
[6291]6333 PVDIMAGE pImage = pDisk->pLast;
[15366]6334 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
[6291]6335
[32370]6336 rc = vdReadHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbRead,
6337 true /* fZeroFreeBlocks */,
6338 true /* fUpdateCache */);
[6291]6339 } while (0);
6340
[27232]6341 if (RT_UNLIKELY(fLockRead))
6342 {
6343 rc2 = vdThreadFinishRead(pDisk);
6344 AssertRC(rc2);
6345 }
6346
[11284]6347 LogFlowFunc(("returns %Rrc\n", rc));
[2379]6348 return rc;
6349}
6350
6351/**
6352 * Write data to virtual HDD.
6353 *
6354 * @returns VBox status code.
[15366]6355 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
[6291]6356 * @param pDisk Pointer to HDD container.
[11266]6357 * @param uOffset Offset of the first byte being
[9262]6358 * written from start of disk.
[2379]6359 * @param pvBuf Pointer to buffer for writing data.
6360 * @param cbWrite Number of bytes to write.
6361 */
[6325]6362VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
6363 size_t cbWrite)
[2379]6364{
6365 int rc = VINF_SUCCESS;
[27232]6366 int rc2;
6367 bool fLockWrite = false;
[2379]6368
[6326]6369 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
6370 pDisk, uOffset, pvBuf, cbWrite));
[6291]6371 do
[2379]6372 {
[6291]6373 /* sanity check */
[8569]6374 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]6375 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
[2379]6376
[6291]6377 /* Check arguments. */
[8565]6378 AssertMsgBreakStmt(VALID_PTR(pvBuf),
6379 ("pvBuf=%#p\n", pvBuf),
6380 rc = VERR_INVALID_PARAMETER);
6381 AssertMsgBreakStmt(cbWrite,
6382 ("cbWrite=%zu\n", cbWrite),
6383 rc = VERR_INVALID_PARAMETER);
[27232]6384
6385 rc2 = vdThreadStartWrite(pDisk);
6386 AssertRC(rc2);
6387 fLockWrite = true;
6388
[8565]6389 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
6390 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
6391 uOffset, cbWrite, pDisk->cbSize),
6392 rc = VERR_INVALID_PARAMETER);
[2379]6393
[6291]6394 PVDIMAGE pImage = pDisk->pLast;
[15366]6395 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
[2379]6396
[6291]6397 vdSetModifiedFlag(pDisk);
[32370]6398 rc = vdWriteHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbWrite,
6399 true /* fUpdateCache */);
[6291]6400 } while (0);
[2379]6401
[27232]6402 if (RT_UNLIKELY(fLockWrite))
6403 {
6404 rc2 = vdThreadFinishWrite(pDisk);
6405 AssertRC(rc2);
6406 }
6407
[11284]6408 LogFlowFunc(("returns %Rrc\n", rc));
[2379]6409 return rc;
6410}
6411
6412/**
6413 * Make sure the on disk representation of a virtual HDD is up to date.
6414 *
6415 * @returns VBox status code.
[15366]6416 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
[6291]6417 * @param pDisk Pointer to HDD container.
[2379]6418 */
6419VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
6420{
[6291]6421 int rc = VINF_SUCCESS;
[27232]6422 int rc2;
6423 bool fLockWrite = false;
[2379]6424
[6291]6425 LogFlowFunc(("pDisk=%#p\n", pDisk));
6426 do
[2379]6427 {
[6291]6428 /* sanity check */
[8569]6429 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]6430 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6431
[27232]6432 rc2 = vdThreadStartWrite(pDisk);
6433 AssertRC(rc2);
6434 fLockWrite = true;
6435
[6291]6436 PVDIMAGE pImage = pDisk->pLast;
[15366]6437 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
[6291]6438
[2379]6439 vdResetModifiedFlag(pDisk);
[32536]6440 rc = pImage->Backend->pfnFlush(pImage->pBackendData);
[32370]6441
6442 if ( RT_SUCCESS(rc)
6443 && pDisk->pCache)
[32536]6444 rc = pDisk->pCache->Backend->pfnFlush(pDisk->pCache->pBackendData);
[6291]6445 } while (0);
[2379]6446
[27232]6447 if (RT_UNLIKELY(fLockWrite))
6448 {
6449 rc2 = vdThreadFinishWrite(pDisk);
6450 AssertRC(rc2);
6451 }
6452
[11284]6453 LogFlowFunc(("returns %Rrc\n", rc));
[2379]6454 return rc;
6455}
6456
6457/**
6458 * Get number of opened images in HDD container.
6459 *
6460 * @returns Number of opened images for HDD container. 0 if no images have been opened.
[6291]6461 * @param pDisk Pointer to HDD container.
[2379]6462 */
6463VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
6464{
[6291]6465 unsigned cImages;
[27232]6466 int rc2;
6467 bool fLockRead = false;
[2379]6468
[6291]6469 LogFlowFunc(("pDisk=%#p\n", pDisk));
6470 do
6471 {
6472 /* sanity check */
[8569]6473 AssertPtrBreakStmt(pDisk, cImages = 0);
[6291]6474 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6475
[27232]6476 rc2 = vdThreadStartRead(pDisk);
6477 AssertRC(rc2);
6478 fLockRead = true;
6479
[6291]6480 cImages = pDisk->cImages;
6481 } while (0);
6482
[27232]6483 if (RT_UNLIKELY(fLockRead))
6484 {
6485 rc2 = vdThreadFinishRead(pDisk);
6486 AssertRC(rc2);
6487 }
6488
[6291]6489 LogFlowFunc(("returns %u\n", cImages));
6490 return cImages;
[2379]6491}
6492
6493/**
[6291]6494 * Get read/write mode of HDD container.
[2379]6495 *
6496 * @returns Virtual disk ReadOnly status.
6497 * @returns true if no image is opened in HDD container.
[6291]6498 * @param pDisk Pointer to HDD container.
[2379]6499 */
6500VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
6501{
[6291]6502 bool fReadOnly;
[27232]6503 int rc2;
6504 bool fLockRead = false;
[2379]6505
[6291]6506 LogFlowFunc(("pDisk=%#p\n", pDisk));
6507 do
[2379]6508 {
[6291]6509 /* sanity check */
[8569]6510 AssertPtrBreakStmt(pDisk, fReadOnly = false);
[6291]6511 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6512
[27232]6513 rc2 = vdThreadStartRead(pDisk);
6514 AssertRC(rc2);
6515 fLockRead = true;
6516
[6291]6517 PVDIMAGE pImage = pDisk->pLast;
[8569]6518 AssertPtrBreakStmt(pImage, fReadOnly = true);
[6291]6519
[2379]6520 unsigned uOpenFlags;
[32536]6521 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
[6291]6522 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
6523 } while (0);
6524
[27232]6525 if (RT_UNLIKELY(fLockRead))
6526 {
6527 rc2 = vdThreadFinishRead(pDisk);
6528 AssertRC(rc2);
6529 }
6530
[6291]6531 LogFlowFunc(("returns %d\n", fReadOnly));
6532 return fReadOnly;
6533}
6534
6535/**
6536 * Get total capacity of an image in HDD container.
6537 *
6538 * @returns Virtual disk size in bytes.
6539 * @returns 0 if no image with specified number was not opened.
6540 * @param pDisk Pointer to HDD container.
6541 * @param nImage Image number, counds from 0. 0 is always base image of container.
6542 */
6543VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
6544{
6545 uint64_t cbSize;
[27232]6546 int rc2;
6547 bool fLockRead = false;
[6291]6548
6549 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
6550 do
[2379]6551 {
[6291]6552 /* sanity check */
[8569]6553 AssertPtrBreakStmt(pDisk, cbSize = 0);
[6291]6554 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
[2379]6555
[27232]6556 rc2 = vdThreadStartRead(pDisk);
6557 AssertRC(rc2);
6558 fLockRead = true;
6559
[6291]6560 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[8569]6561 AssertPtrBreakStmt(pImage, cbSize = 0);
[32536]6562 cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
[6291]6563 } while (0);
6564
[27232]6565 if (RT_UNLIKELY(fLockRead))
6566 {
6567 rc2 = vdThreadFinishRead(pDisk);
6568 AssertRC(rc2);
6569 }
6570
[6291]6571 LogFlowFunc(("returns %llu\n", cbSize));
6572 return cbSize;
[2379]6573}
6574
6575/**
[6291]6576 * Get total file size of an image in HDD container.
[2379]6577 *
6578 * @returns Virtual disk size in bytes.
6579 * @returns 0 if no image is opened in HDD container.
[6291]6580 * @param pDisk Pointer to HDD container.
6581 * @param nImage Image number, counts from 0. 0 is always base image of container.
[2379]6582 */
[6291]6583VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
[2379]6584{
[6291]6585 uint64_t cbSize;
[27232]6586 int rc2;
6587 bool fLockRead = false;
[2379]6588
[6291]6589 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
6590 do
6591 {
6592 /* sanity check */
[8569]6593 AssertPtrBreakStmt(pDisk, cbSize = 0);
[6291]6594 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6595
[27232]6596 rc2 = vdThreadStartRead(pDisk);
6597 AssertRC(rc2);
6598 fLockRead = true;
6599
[6291]6600 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[8569]6601 AssertPtrBreakStmt(pImage, cbSize = 0);
[32536]6602 cbSize = pImage->Backend->pfnGetFileSize(pImage->pBackendData);
[6291]6603 } while (0);
6604
[27232]6605 if (RT_UNLIKELY(fLockRead))
6606 {
6607 rc2 = vdThreadFinishRead(pDisk);
6608 AssertRC(rc2);
6609 }
6610
[6291]6611 LogFlowFunc(("returns %llu\n", cbSize));
6612 return cbSize;
[2379]6613}
6614
6615/**
[6291]6616 * Get virtual disk PCHS geometry stored in HDD container.
[2379]6617 *
6618 * @returns VBox status code.
[15366]6619 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6620 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
[6291]6621 * @param pDisk Pointer to HDD container.
6622 * @param nImage Image number, counts from 0. 0 is always base image of container.
6623 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
[2379]6624 */
[6291]6625VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
[32536]6626 PVDGEOMETRY pPCHSGeometry)
[2379]6627{
[6291]6628 int rc = VINF_SUCCESS;
[27232]6629 int rc2;
6630 bool fLockRead = false;
[2379]6631
[6291]6632 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
6633 pDisk, nImage, pPCHSGeometry));
6634 do
[2379]6635 {
[6291]6636 /* sanity check */
[8569]6637 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]6638 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6639
6640 /* Check arguments. */
[8565]6641 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
6642 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
6643 rc = VERR_INVALID_PARAMETER);
[6291]6644
[27232]6645 rc2 = vdThreadStartRead(pDisk);
6646 AssertRC(rc2);
6647 fLockRead = true;
6648
[6291]6649 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[15366]6650 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
[6291]6651
6652 if (pImage == pDisk->pLast)
[2379]6653 {
[6291]6654 /* Use cached information if possible. */
6655 if (pDisk->PCHSGeometry.cCylinders != 0)
6656 *pPCHSGeometry = pDisk->PCHSGeometry;
6657 else
[15366]6658 rc = VERR_VD_GEOMETRY_NOT_SET;
[2379]6659 }
6660 else
[32536]6661 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
[7277]6662 pPCHSGeometry);
[6291]6663 } while (0);
6664
[27232]6665 if (RT_UNLIKELY(fLockRead))
6666 {
6667 rc2 = vdThreadFinishRead(pDisk);
6668 AssertRC(rc2);
6669 }
6670
[32536]6671 LogFlowFunc(("%Rrc (PCHS=%u/%u/%u)\n", rc,
[6291]6672 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
6673 pDisk->PCHSGeometry.cSectors));
[2379]6674 return rc;
6675}
6676
6677/**
[6291]6678 * Store virtual disk PCHS geometry in HDD container.
[2379]6679 *
6680 * Note that in case of unrecoverable error all images in HDD container will be closed.
6681 *
6682 * @returns VBox status code.
[15366]6683 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6684 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
[6291]6685 * @param pDisk Pointer to HDD container.
6686 * @param nImage Image number, counts from 0. 0 is always base image of container.
6687 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
[2379]6688 */
[6291]6689VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
[32536]6690 PCVDGEOMETRY pPCHSGeometry)
[2379]6691{
[6291]6692 int rc = VINF_SUCCESS;
[27232]6693 int rc2;
6694 bool fLockWrite = false;
[2379]6695
[6291]6696 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
6697 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
6698 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
6699 do
[2379]6700 {
[6291]6701 /* sanity check */
[8569]6702 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]6703 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6704
6705 /* Check arguments. */
[8565]6706 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
6707 && pPCHSGeometry->cHeads <= 16
6708 && pPCHSGeometry->cSectors <= 63,
6709 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
6710 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
6711 pPCHSGeometry->cSectors),
6712 rc = VERR_INVALID_PARAMETER);
[6291]6713
[27232]6714 rc2 = vdThreadStartWrite(pDisk);
6715 AssertRC(rc2);
6716 fLockWrite = true;
6717
[6291]6718 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[15366]6719 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
[6291]6720
6721 if (pImage == pDisk->pLast)
[2379]6722 {
[6291]6723 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
6724 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
6725 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
6726 {
6727 /* Only update geometry if it is changed. Avoids similar checks
6728 * in every backend. Most of the time the new geometry is set
6729 * to the previous values, so no need to go through the hassle
6730 * of updating an image which could be opened in read-only mode
6731 * right now. */
[32536]6732 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
[7277]6733 pPCHSGeometry);
[2379]6734
[6291]6735 /* Cache new geometry values in any case. */
[32536]6736 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
[27367]6737 &pDisk->PCHSGeometry);
[11266]6738 if (RT_FAILURE(rc2))
[6291]6739 {
6740 pDisk->PCHSGeometry.cCylinders = 0;
6741 pDisk->PCHSGeometry.cHeads = 0;
6742 pDisk->PCHSGeometry.cSectors = 0;
6743 }
6744 else
6745 {
6746 /* Make sure the CHS geometry is properly clipped. */
6747 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
6748 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
6749 }
[2379]6750 }
[6291]6751 }
6752 else
6753 {
[32536]6754 VDGEOMETRY PCHS;
6755 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
[7277]6756 &PCHS);
[11266]6757 if ( RT_FAILURE(rc)
[6291]6758 || pPCHSGeometry->cCylinders != PCHS.cCylinders
6759 || pPCHSGeometry->cHeads != PCHS.cHeads
6760 || pPCHSGeometry->cSectors != PCHS.cSectors)
[2379]6761 {
[6291]6762 /* Only update geometry if it is changed. Avoids similar checks
6763 * in every backend. Most of the time the new geometry is set
6764 * to the previous values, so no need to go through the hassle
6765 * of updating an image which could be opened in read-only mode
6766 * right now. */
[32536]6767 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
[7277]6768 pPCHSGeometry);
[2379]6769 }
6770 }
[6291]6771 } while (0);
[2379]6772
[27232]6773 if (RT_UNLIKELY(fLockWrite))
6774 {
6775 rc2 = vdThreadFinishWrite(pDisk);
6776 AssertRC(rc2);
6777 }
6778
[11284]6779 LogFlowFunc(("returns %Rrc\n", rc));
[2379]6780 return rc;
6781}
6782
6783/**
[6291]6784 * Get virtual disk LCHS geometry stored in HDD container.
[2379]6785 *
6786 * @returns VBox status code.
[15366]6787 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6788 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
[6291]6789 * @param pDisk Pointer to HDD container.
6790 * @param nImage Image number, counts from 0. 0 is always base image of container.
6791 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
[2379]6792 */
[6291]6793VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
[32536]6794 PVDGEOMETRY pLCHSGeometry)
[2379]6795{
[6291]6796 int rc = VINF_SUCCESS;
[27232]6797 int rc2;
6798 bool fLockRead = false;
[2379]6799
[6291]6800 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
6801 pDisk, nImage, pLCHSGeometry));
6802 do
[2379]6803 {
[6291]6804 /* sanity check */
[8569]6805 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]6806 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6807
6808 /* Check arguments. */
[8565]6809 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
6810 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
6811 rc = VERR_INVALID_PARAMETER);
[6291]6812
[27232]6813 rc2 = vdThreadStartRead(pDisk);
6814 AssertRC(rc2);
6815 fLockRead = true;
6816
[6291]6817 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[15366]6818 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
[6291]6819
6820 if (pImage == pDisk->pLast)
6821 {
6822 /* Use cached information if possible. */
6823 if (pDisk->LCHSGeometry.cCylinders != 0)
6824 *pLCHSGeometry = pDisk->LCHSGeometry;
6825 else
[15366]6826 rc = VERR_VD_GEOMETRY_NOT_SET;
[6291]6827 }
[2379]6828 else
[32536]6829 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
[7277]6830 pLCHSGeometry);
[6291]6831 } while (0);
6832
[27232]6833 if (RT_UNLIKELY(fLockRead))
6834 {
6835 rc2 = vdThreadFinishRead(pDisk);
6836 AssertRC(rc2);
6837 }
6838
[11284]6839 LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
[6291]6840 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
6841 pDisk->LCHSGeometry.cSectors));
[2379]6842 return rc;
6843}
6844
6845/**
[6291]6846 * Store virtual disk LCHS geometry in HDD container.
[2379]6847 *
6848 * Note that in case of unrecoverable error all images in HDD container will be closed.
6849 *
6850 * @returns VBox status code.
[15366]6851 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6852 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
[6291]6853 * @param pDisk Pointer to HDD container.
6854 * @param nImage Image number, counts from 0. 0 is always base image of container.
6855 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
[2379]6856 */
[6291]6857VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
[32536]6858 PCVDGEOMETRY pLCHSGeometry)
[2379]6859{
[6291]6860 int rc = VINF_SUCCESS;
[27232]6861 int rc2;
6862 bool fLockWrite = false;
[2379]6863
[6291]6864 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
6865 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
6866 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
6867 do
[2379]6868 {
[6291]6869 /* sanity check */
[8569]6870 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]6871 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6872
6873 /* Check arguments. */
[8565]6874 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
6875 && pLCHSGeometry->cHeads <= 255
6876 && pLCHSGeometry->cSectors <= 63,
6877 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
6878 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
6879 pLCHSGeometry->cSectors),
6880 rc = VERR_INVALID_PARAMETER);
[6291]6881
[27232]6882 rc2 = vdThreadStartWrite(pDisk);
6883 AssertRC(rc2);
6884 fLockWrite = true;
6885
[6291]6886 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[15366]6887 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
[6291]6888
6889 if (pImage == pDisk->pLast)
[2379]6890 {
[6291]6891 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
6892 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
6893 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
6894 {
6895 /* Only update geometry if it is changed. Avoids similar checks
6896 * in every backend. Most of the time the new geometry is set
6897 * to the previous values, so no need to go through the hassle
6898 * of updating an image which could be opened in read-only mode
6899 * right now. */
[32536]6900 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
[7277]6901 pLCHSGeometry);
[2379]6902
[6291]6903 /* Cache new geometry values in any case. */
[32536]6904 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
[27367]6905 &pDisk->LCHSGeometry);
[11266]6906 if (RT_FAILURE(rc2))
[6291]6907 {
6908 pDisk->LCHSGeometry.cCylinders = 0;
6909 pDisk->LCHSGeometry.cHeads = 0;
6910 pDisk->LCHSGeometry.cSectors = 0;
6911 }
6912 else
6913 {
6914 /* Make sure the CHS geometry is properly clipped. */
6915 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
6916 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
6917 }
6918 }
[2379]6919 }
[6291]6920 else
6921 {
[32536]6922 VDGEOMETRY LCHS;
6923 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
[7277]6924 &LCHS);
[11266]6925 if ( RT_FAILURE(rc)
[6291]6926 || pLCHSGeometry->cCylinders != LCHS.cCylinders
6927 || pLCHSGeometry->cHeads != LCHS.cHeads
6928 || pLCHSGeometry->cSectors != LCHS.cSectors)
6929 {
6930 /* Only update geometry if it is changed. Avoids similar checks
6931 * in every backend. Most of the time the new geometry is set
6932 * to the previous values, so no need to go through the hassle
6933 * of updating an image which could be opened in read-only mode
6934 * right now. */
[32536]6935 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
[7277]6936 pLCHSGeometry);
[6291]6937 }
6938 }
6939 } while (0);
[2379]6940
[27232]6941 if (RT_UNLIKELY(fLockWrite))
6942 {
6943 rc2 = vdThreadFinishWrite(pDisk);
6944 AssertRC(rc2);
6945 }
6946
[11284]6947 LogFlowFunc(("returns %Rrc\n", rc));
[2379]6948 return rc;
6949}
6950
6951/**
6952 * Get version of image in HDD container.
6953 *
6954 * @returns VBox status code.
[15366]6955 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
[6291]6956 * @param pDisk Pointer to HDD container.
[2379]6957 * @param nImage Image number, counts from 0. 0 is always base image of container.
6958 * @param puVersion Where to store the image version.
6959 */
[2590]6960VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
6961 unsigned *puVersion)
[2379]6962{
[6291]6963 int rc = VINF_SUCCESS;
[27232]6964 int rc2;
6965 bool fLockRead = false;
[6291]6966
6967 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
6968 pDisk, nImage, puVersion));
6969 do
6970 {
6971 /* sanity check */
[8569]6972 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]6973 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6974
6975 /* Check arguments. */
[8565]6976 AssertMsgBreakStmt(VALID_PTR(puVersion),
6977 ("puVersion=%#p\n", puVersion),
6978 rc = VERR_INVALID_PARAMETER);
[6291]6979
[27232]6980 rc2 = vdThreadStartRead(pDisk);
6981 AssertRC(rc2);
6982 fLockRead = true;
6983
[6291]6984 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[15366]6985 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
[6291]6986
[32536]6987 *puVersion = pImage->Backend->pfnGetVersion(pImage->pBackendData);
[6291]6988 } while (0);
6989
[27232]6990 if (RT_UNLIKELY(fLockRead))
6991 {
6992 rc2 = vdThreadFinishRead(pDisk);
6993 AssertRC(rc2);
6994 }
6995
[11284]6996 LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
[6291]6997 return rc;
[2379]6998}
6999
7000/**
[10715]7001 * List the capabilities of image backend in HDD container.
7002 *
7003 * @returns VBox status code.
[15366]7004 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
[10715]7005 * @param pDisk Pointer to the HDD container.
7006 * @param nImage Image number, counts from 0. 0 is always base image of container.
7007 * @param pbackendInfo Where to store the backend information.
7008 */
7009VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
7010 PVDBACKENDINFO pBackendInfo)
7011{
7012 int rc = VINF_SUCCESS;
[27232]7013 int rc2;
7014 bool fLockRead = false;
[10715]7015
[17970]7016 LogFlowFunc(("pDisk=%#p nImage=%u pBackendInfo=%#p\n",
[10715]7017 pDisk, nImage, pBackendInfo));
7018 do
7019 {
7020 /* sanity check */
7021 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7022 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7023
7024 /* Check arguments. */
7025 AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
7026 ("pBackendInfo=%#p\n", pBackendInfo),
7027 rc = VERR_INVALID_PARAMETER);
7028
[27232]7029 rc2 = vdThreadStartRead(pDisk);
7030 AssertRC(rc2);
7031 fLockRead = true;
7032
[10715]7033 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[15366]7034 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
[10715]7035
[27232]7036 pBackendInfo->pszBackend = pImage->Backend->pszBackendName;
[17970]7037 pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
7038 pBackendInfo->papszFileExtensions = pImage->Backend->papszFileExtensions;
7039 pBackendInfo->paConfigInfo = pImage->Backend->paConfigInfo;
[10715]7040 } while (0);
7041
[27232]7042 if (RT_UNLIKELY(fLockRead))
7043 {
7044 rc2 = vdThreadFinishRead(pDisk);
7045 AssertRC(rc2);
7046 }
7047
[11284]7048 LogFlowFunc(("returns %Rrc\n", rc));
[10715]7049 return rc;
7050}
7051
7052/**
[2379]7053 * Get flags of image in HDD container.
7054 *
7055 * @returns VBox status code.
[15366]7056 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
[6291]7057 * @param pDisk Pointer to HDD container.
[2379]7058 * @param nImage Image number, counts from 0. 0 is always base image of container.
7059 * @param puImageFlags Where to store the image flags.
7060 */
[2590]7061VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
7062 unsigned *puImageFlags)
[2379]7063{
[6291]7064 int rc = VINF_SUCCESS;
[27232]7065 int rc2;
7066 bool fLockRead = false;
[6291]7067
7068 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
7069 pDisk, nImage, puImageFlags));
7070 do
7071 {
7072 /* sanity check */
[8569]7073 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]7074 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7075
7076 /* Check arguments. */
[8565]7077 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
7078 ("puImageFlags=%#p\n", puImageFlags),
7079 rc = VERR_INVALID_PARAMETER);
[6291]7080
[27232]7081 rc2 = vdThreadStartRead(pDisk);
7082 AssertRC(rc2);
7083 fLockRead = true;
7084
[6291]7085 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[15366]7086 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
[6291]7087
[31258]7088 *puImageFlags = pImage->uImageFlags;
[6291]7089 } while (0);
7090
[27232]7091 if (RT_UNLIKELY(fLockRead))
7092 {
7093 rc2 = vdThreadFinishRead(pDisk);
7094 AssertRC(rc2);
7095 }
7096
[11284]7097 LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
[6291]7098 return rc;
[2379]7099}
7100
7101/**
[6291]7102 * Get open flags of image in HDD container.
[2379]7103 *
7104 * @returns VBox status code.
[15366]7105 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
[6291]7106 * @param pDisk Pointer to HDD container.
7107 * @param nImage Image number, counts from 0. 0 is always base image of container.
[2379]7108 * @param puOpenFlags Where to store the image open flags.
7109 */
[6291]7110VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
7111 unsigned *puOpenFlags)
[2379]7112{
[6291]7113 int rc = VINF_SUCCESS;
[27232]7114 int rc2;
7115 bool fLockRead = false;
[2379]7116
[6291]7117 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
7118 pDisk, nImage, puOpenFlags));
7119 do
[2379]7120 {
[6291]7121 /* sanity check */
[8569]7122 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]7123 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7124
7125 /* Check arguments. */
[8565]7126 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
7127 ("puOpenFlags=%#p\n", puOpenFlags),
7128 rc = VERR_INVALID_PARAMETER);
[6291]7129
[27232]7130 rc2 = vdThreadStartRead(pDisk);
7131 AssertRC(rc2);
7132 fLockRead = true;
7133
[6291]7134 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[15366]7135 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
[6291]7136
[32536]7137 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
[6291]7138 } while (0);
7139
[27232]7140 if (RT_UNLIKELY(fLockRead))
7141 {
7142 rc2 = vdThreadFinishRead(pDisk);
7143 AssertRC(rc2);
7144 }
7145
[11284]7146 LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
[6291]7147 return rc;
[2379]7148}
7149
7150/**
[6291]7151 * Set open flags of image in HDD container.
[2379]7152 * This operation may cause file locking changes and/or files being reopened.
7153 * Note that in case of unrecoverable error all images in HDD container will be closed.
7154 *
[6291]7155 * @returns VBox status code.
[15366]7156 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
[6291]7157 * @param pDisk Pointer to HDD container.
7158 * @param nImage Image number, counts from 0. 0 is always base image of container.
[2379]7159 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
7160 */
[6291]7161VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
7162 unsigned uOpenFlags)
[2379]7163{
[6291]7164 int rc;
[27232]7165 int rc2;
7166 bool fLockWrite = false;
[2379]7167
[6291]7168 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
7169 do
[2379]7170 {
[6291]7171 /* sanity check */
[8569]7172 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]7173 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7174
7175 /* Check arguments. */
[8565]7176 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
7177 ("uOpenFlags=%#x\n", uOpenFlags),
7178 rc = VERR_INVALID_PARAMETER);
[6291]7179
[27232]7180 rc2 = vdThreadStartWrite(pDisk);
7181 AssertRC(rc2);
7182 fLockWrite = true;
7183
[6291]7184 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[15366]7185 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
[6291]7186
[32536]7187 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData,
[7277]7188 uOpenFlags);
[6291]7189 } while (0);
7190
[27232]7191 if (RT_UNLIKELY(fLockWrite))
7192 {
7193 rc2 = vdThreadFinishWrite(pDisk);
7194 AssertRC(rc2);
7195 }
7196
[11284]7197 LogFlowFunc(("returns %Rrc\n", rc));
[2379]7198 return rc;
7199}
7200
7201/**
7202 * Get base filename of image in HDD container. Some image formats use
[6291]7203 * other filenames as well, so don't use this for anything but informational
[2379]7204 * purposes.
7205 *
7206 * @returns VBox status code.
[15366]7207 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
[2379]7208 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
[6291]7209 * @param pDisk Pointer to HDD container.
[2379]7210 * @param nImage Image number, counts from 0. 0 is always base image of container.
7211 * @param pszFilename Where to store the image file name.
7212 * @param cbFilename Size of buffer pszFilename points to.
7213 */
[2590]7214VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
7215 char *pszFilename, unsigned cbFilename)
[2379]7216{
[6291]7217 int rc;
[27232]7218 int rc2;
7219 bool fLockRead = false;
[6291]7220
7221 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
7222 pDisk, nImage, pszFilename, cbFilename));
7223 do
7224 {
7225 /* sanity check */
[8569]7226 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]7227 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7228
7229 /* Check arguments. */
[8565]7230 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
7231 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
7232 rc = VERR_INVALID_PARAMETER);
7233 AssertMsgBreakStmt(cbFilename,
7234 ("cbFilename=%u\n", cbFilename),
7235 rc = VERR_INVALID_PARAMETER);
[6291]7236
[27232]7237 rc2 = vdThreadStartRead(pDisk);
7238 AssertRC(rc2);
7239 fLockRead = true;
7240
[6291]7241 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[15366]7242 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
[6291]7243
7244 size_t cb = strlen(pImage->pszFilename);
7245 if (cb <= cbFilename)
7246 {
7247 strcpy(pszFilename, pImage->pszFilename);
7248 rc = VINF_SUCCESS;
7249 }
7250 else
7251 {
7252 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
7253 pszFilename[cbFilename - 1] = '\0';
7254 rc = VERR_BUFFER_OVERFLOW;
7255 }
7256 } while (0);
7257
[27232]7258 if (RT_UNLIKELY(fLockRead))
7259 {
7260 rc2 = vdThreadFinishRead(pDisk);
7261 AssertRC(rc2);
7262 }
7263
[11284]7264 LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
[6291]7265 return rc;
[2379]7266}
7267
7268/**
7269 * Get the comment line of image in HDD container.
7270 *
7271 * @returns VBox status code.
[15366]7272 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
[2379]7273 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
[6291]7274 * @param pDisk Pointer to HDD container.
[2379]7275 * @param nImage Image number, counts from 0. 0 is always base image of container.
7276 * @param pszComment Where to store the comment string of image. NULL is ok.
7277 * @param cbComment The size of pszComment buffer. 0 is ok.
7278 */
[2590]7279VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
7280 char *pszComment, unsigned cbComment)
[2379]7281{
[6291]7282 int rc;
[27232]7283 int rc2;
7284 bool fLockRead = false;
[2742]7285
[6291]7286 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
7287 pDisk, nImage, pszComment, cbComment));
7288 do
7289 {
7290 /* sanity check */
[8569]7291 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]7292 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7293
7294 /* Check arguments. */
[8565]7295 AssertMsgBreakStmt(VALID_PTR(pszComment),
7296 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
7297 rc = VERR_INVALID_PARAMETER);
7298 AssertMsgBreakStmt(cbComment,
7299 ("cbComment=%u\n", cbComment),
7300 rc = VERR_INVALID_PARAMETER);
[6291]7301
[27232]7302 rc2 = vdThreadStartRead(pDisk);
7303 AssertRC(rc2);
7304 fLockRead = true;
7305
[6291]7306 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[15366]7307 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
[6291]7308
[32536]7309 rc = pImage->Backend->pfnGetComment(pImage->pBackendData, pszComment,
[7277]7310 cbComment);
[6291]7311 } while (0);
[2742]7312
[27232]7313 if (RT_UNLIKELY(fLockRead))
7314 {
7315 rc2 = vdThreadFinishRead(pDisk);
7316 AssertRC(rc2);
7317 }
7318
[11284]7319 LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
[2742]7320 return rc;
[2379]7321}
7322
7323/**
[2590]7324 * Changes the comment line of image in HDD container.
7325 *
7326 * @returns VBox status code.
[15366]7327 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
[6291]7328 * @param pDisk Pointer to HDD container.
[2590]7329 * @param nImage Image number, counts from 0. 0 is always base image of container.
7330 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
7331 */
7332VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
7333 const char *pszComment)
[2379]7334{
[6291]7335 int rc;
[27232]7336 int rc2;
7337 bool fLockWrite = false;
[2742]7338
[6291]7339 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
7340 pDisk, nImage, pszComment, pszComment));
7341 do
7342 {
7343 /* sanity check */
[8569]7344 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]7345 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7346
7347 /* Check arguments. */
[8565]7348 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
7349 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
7350 rc = VERR_INVALID_PARAMETER);
[6291]7351
[27232]7352 rc2 = vdThreadStartWrite(pDisk);
7353 AssertRC(rc2);
7354 fLockWrite = true;
7355
[6291]7356 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[15366]7357 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
[6291]7358
[32536]7359 rc = pImage->Backend->pfnSetComment(pImage->pBackendData, pszComment);
[6291]7360 } while (0);
[2742]7361
[27232]7362 if (RT_UNLIKELY(fLockWrite))
7363 {
7364 rc2 = vdThreadFinishWrite(pDisk);
7365 AssertRC(rc2);
7366 }
7367
[11284]7368 LogFlowFunc(("returns %Rrc\n", rc));
[2742]7369 return rc;
[2379]7370}
7371
7372
7373/**
7374 * Get UUID of image in HDD container.
7375 *
7376 * @returns VBox status code.
[15366]7377 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
[6291]7378 * @param pDisk Pointer to HDD container.
[2379]7379 * @param nImage Image number, counts from 0. 0 is always base image of container.
7380 * @param pUuid Where to store the image creation UUID.
7381 */
7382VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
7383{
[6291]7384 int rc;
[27232]7385 int rc2;
7386 bool fLockRead = false;
[2379]7387
[6291]7388 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
7389 do
7390 {
7391 /* sanity check */
[8569]7392 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]7393 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7394
7395 /* Check arguments. */
[8565]7396 AssertMsgBreakStmt(VALID_PTR(pUuid),
7397 ("pUuid=%#p\n", pUuid),
7398 rc = VERR_INVALID_PARAMETER);
[6291]7399
[27232]7400 rc2 = vdThreadStartRead(pDisk);
7401 AssertRC(rc2);
7402 fLockRead = true;
7403
[6291]7404 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[15366]7405 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
[6291]7406
[32536]7407 rc = pImage->Backend->pfnGetUuid(pImage->pBackendData, pUuid);
[6291]7408 } while (0);
[2379]7409
[27232]7410 if (RT_UNLIKELY(fLockRead))
7411 {
7412 rc2 = vdThreadFinishRead(pDisk);
7413 AssertRC(rc2);
7414 }
7415
[11287]7416 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
[2379]7417 return rc;
7418}
7419
7420/**
7421 * Set the image's UUID. Should not be used by normal applications.
7422 *
7423 * @returns VBox status code.
[15366]7424 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
[6291]7425 * @param pDisk Pointer to HDD container.
[2379]7426 * @param nImage Image number, counts from 0. 0 is always base image of container.
[6291]7427 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
[2379]7428 */
7429VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
7430{
[6291]7431 int rc;
[27232]7432 int rc2;
7433 bool fLockWrite = false;
[2379]7434
[11287]7435 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
[6291]7436 pDisk, nImage, pUuid, pUuid));
7437 do
7438 {
7439 /* sanity check */
[8569]7440 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]7441 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7442
[8565]7443 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
7444 ("pUuid=%#p\n", pUuid),
7445 rc = VERR_INVALID_PARAMETER);
[6291]7446
[27232]7447 rc2 = vdThreadStartWrite(pDisk);
7448 AssertRC(rc2);
7449 fLockWrite = true;
7450
[6291]7451 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[15366]7452 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
[6291]7453
7454 RTUUID Uuid;
7455 if (!pUuid)
7456 {
7457 RTUuidCreate(&Uuid);
7458 pUuid = &Uuid;
7459 }
[32536]7460 rc = pImage->Backend->pfnSetUuid(pImage->pBackendData, pUuid);
[6291]7461 } while (0);
[2379]7462
[27232]7463 if (RT_UNLIKELY(fLockWrite))
7464 {
7465 rc2 = vdThreadFinishWrite(pDisk);
7466 AssertRC(rc2);
7467 }
7468
[11284]7469 LogFlowFunc(("returns %Rrc\n", rc));
[2379]7470 return rc;
7471}
7472
7473/**
7474 * Get last modification UUID of image in HDD container.
7475 *
7476 * @returns VBox status code.
[15366]7477 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
[6291]7478 * @param pDisk Pointer to HDD container.
[2379]7479 * @param nImage Image number, counts from 0. 0 is always base image of container.
7480 * @param pUuid Where to store the image modification UUID.
7481 */
7482VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
7483{
[6291]7484 int rc = VINF_SUCCESS;
[27232]7485 int rc2;
7486 bool fLockRead = false;
[2379]7487
[6291]7488 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
7489 do
7490 {
7491 /* sanity check */
[8569]7492 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]7493 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
[2379]7494
[6291]7495 /* Check arguments. */
[8565]7496 AssertMsgBreakStmt(VALID_PTR(pUuid),
7497 ("pUuid=%#p\n", pUuid),
7498 rc = VERR_INVALID_PARAMETER);
[6291]7499
[27232]7500 rc2 = vdThreadStartRead(pDisk);
7501 AssertRC(rc2);
7502 fLockRead = true;
7503
[6291]7504 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[15366]7505 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
[6291]7506
[32536]7507 rc = pImage->Backend->pfnGetModificationUuid(pImage->pBackendData,
[7277]7508 pUuid);
[6291]7509 } while (0);
7510
[27232]7511 if (RT_UNLIKELY(fLockRead))
7512 {
7513 rc2 = vdThreadFinishRead(pDisk);
7514 AssertRC(rc2);
7515 }
7516
[11287]7517 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
[2379]7518 return rc;
7519}
7520
7521/**
7522 * Set the image's last modification UUID. Should not be used by normal applications.
7523 *
7524 * @returns VBox status code.
[15366]7525 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
[6291]7526 * @param pDisk Pointer to HDD container.
[2379]7527 * @param nImage Image number, counts from 0. 0 is always base image of container.
[6291]7528 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
[2379]7529 */
7530VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
7531{
7532 int rc;
[27232]7533 int rc2;
7534 bool fLockWrite = false;
[2379]7535
[11287]7536 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
[6291]7537 pDisk, nImage, pUuid, pUuid));
7538 do
7539 {
7540 /* sanity check */
[8569]7541 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]7542 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7543
7544 /* Check arguments. */
[8565]7545 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
7546 ("pUuid=%#p\n", pUuid),
7547 rc = VERR_INVALID_PARAMETER);
[6291]7548
[27232]7549 rc2 = vdThreadStartWrite(pDisk);
7550 AssertRC(rc2);
7551 fLockWrite = true;
7552
[6291]7553 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[15366]7554 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
[6291]7555
7556 RTUUID Uuid;
7557 if (!pUuid)
7558 {
7559 RTUuidCreate(&Uuid);
7560 pUuid = &Uuid;
7561 }
[32536]7562 rc = pImage->Backend->pfnSetModificationUuid(pImage->pBackendData,
[7277]7563 pUuid);
[6291]7564 } while (0);
7565
[27232]7566 if (RT_UNLIKELY(fLockWrite))
7567 {
7568 rc2 = vdThreadFinishWrite(pDisk);
7569 AssertRC(rc2);
7570 }
7571
[11284]7572 LogFlowFunc(("returns %Rrc\n", rc));
[2379]7573 return rc;
7574}
7575
7576/**
7577 * Get parent UUID of image in HDD container.
7578 *
7579 * @returns VBox status code.
[15366]7580 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
[6291]7581 * @param pDisk Pointer to HDD container.
[2379]7582 * @param nImage Image number, counts from 0. 0 is always base image of container.
7583 * @param pUuid Where to store the parent image UUID.
7584 */
[2590]7585VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
7586 PRTUUID pUuid)
[2379]7587{
[6291]7588 int rc = VINF_SUCCESS;
[27232]7589 int rc2;
7590 bool fLockRead = false;
[2379]7591
[6291]7592 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
7593 do
7594 {
7595 /* sanity check */
[8569]7596 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]7597 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7598
7599 /* Check arguments. */
[8565]7600 AssertMsgBreakStmt(VALID_PTR(pUuid),
7601 ("pUuid=%#p\n", pUuid),
7602 rc = VERR_INVALID_PARAMETER);
[6291]7603
[27232]7604 rc2 = vdThreadStartRead(pDisk);
7605 AssertRC(rc2);
7606 fLockRead = true;
7607
[6291]7608 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[15366]7609 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
[6291]7610
[32536]7611 rc = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, pUuid);
[6291]7612 } while (0);
[2379]7613
[27232]7614 if (RT_UNLIKELY(fLockRead))
7615 {
7616 rc2 = vdThreadFinishRead(pDisk);
7617 AssertRC(rc2);
7618 }
7619
[11287]7620 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
[2379]7621 return rc;
7622}
7623
7624/**
7625 * Set the image's parent UUID. Should not be used by normal applications.
7626 *
7627 * @returns VBox status code.
[6291]7628 * @param pDisk Pointer to HDD container.
[2379]7629 * @param nImage Image number, counts from 0. 0 is always base image of container.
[6291]7630 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
[2379]7631 */
[2590]7632VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
7633 PCRTUUID pUuid)
[2379]7634{
[6291]7635 int rc;
[27232]7636 int rc2;
7637 bool fLockWrite = false;
[2379]7638
[11287]7639 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
[6291]7640 pDisk, nImage, pUuid, pUuid));
7641 do
7642 {
7643 /* sanity check */
[8569]7644 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
[6291]7645 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7646
7647 /* Check arguments. */
[8565]7648 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
7649 ("pUuid=%#p\n", pUuid),
7650 rc = VERR_INVALID_PARAMETER);
[6291]7651
[27232]7652 rc2 = vdThreadStartWrite(pDisk);
7653 AssertRC(rc2);
7654 fLockWrite = true;
7655
[6291]7656 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
[15366]7657 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
[6291]7658
7659 RTUUID Uuid;
7660 if (!pUuid)
7661 {
7662 RTUuidCreate(&Uuid);
7663 pUuid = &Uuid;
7664 }
[32536]7665 rc = pImage->Backend->pfnSetParentUuid(pImage->pBackendData, pUuid);
[6291]7666 } while (0);
[2379]7667
[27232]7668 if (RT_UNLIKELY(fLockWrite))
7669 {
7670 rc2 = vdThreadFinishWrite(pDisk);
7671 AssertRC(rc2);
7672 }
7673
[11284]7674 LogFlowFunc(("returns %Rrc\n", rc));
[2379]7675 return rc;
7676}
7677
7678
7679/**
7680 * Debug helper - dumps all opened images in HDD container into the log file.
7681 *
[6291]7682 * @param pDisk Pointer to HDD container.
[2379]7683 */
7684VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
7685{
[27232]7686 int rc2;
7687 bool fLockRead = false;
7688
[6291]7689 do
7690 {
7691 /* sanity check */
[8594]7692 AssertPtrBreak(pDisk);
[6291]7693 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
[2379]7694
[32536]7695 if (!pDisk->pInterfaceErrorCallbacks || !VALID_PTR(pDisk->pInterfaceErrorCallbacks->pfnMessage))
[21806]7696 pDisk->pInterfaceErrorCallbacks->pfnMessage = vdLogMessage;
7697
[27232]7698 rc2 = vdThreadStartRead(pDisk);
7699 AssertRC(rc2);
7700 fLockRead = true;
7701
[32536]7702 vdMessageWrapper(pDisk, "--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
[6291]7703 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
7704 {
[32536]7705 vdMessageWrapper(pDisk, "Dumping VD image \"%s\" (Backend=%s)\n",
7706 pImage->pszFilename, pImage->Backend->pszBackendName);
7707 pImage->Backend->pfnDump(pImage->pBackendData);
[6291]7708 }
7709 } while (0);
[27232]7710
7711 if (RT_UNLIKELY(fLockRead))
7712 {
7713 rc2 = vdThreadFinishRead(pDisk);
7714 AssertRC(rc2);
7715 }
[2379]7716}
7717
[26987]7718/**
7719 * Query if asynchronous operations are supported for this disk.
7720 *
7721 * @returns VBox status code.
7722 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7723 * @param pDisk Pointer to the HDD container.
7724 * @param nImage Image number, counts from 0. 0 is always base image of container.
7725 * @param pfAIOSupported Where to store if async IO is supported.
7726 */
7727VBOXDDU_DECL(int) VDImageIsAsyncIOSupported(PVBOXHDD pDisk, unsigned nImage, bool *pfAIOSupported)
7728{
7729 int rc = VINF_SUCCESS;
[27232]7730 int rc2;
7731 bool fLockRead = false;
[26987]7732
7733 LogFlowFunc(("pDisk=%#p nImage=%u pfAIOSupported=%#p\n", pDisk, nImage, pfAIOSupported));
7734 do
7735 {
7736 /* sanity check */
7737 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7738 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7739
7740 /* Check arguments. */
7741 AssertMsgBreakStmt(VALID_PTR(pfAIOSupported),
7742 ("pfAIOSupported=%#p\n", pfAIOSupported),
7743 rc = VERR_INVALID_PARAMETER);
7744
[27232]7745 rc2 = vdThreadStartRead(pDisk);
7746 AssertRC(rc2);
7747 fLockRead = true;
7748
[26987]7749 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7750 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7751
7752 if (pImage->Backend->uBackendCaps & VD_CAP_ASYNC)
[32536]7753 *pfAIOSupported = pImage->Backend->pfnIsAsyncIOSupported(pImage->pBackendData);
[26987]7754 else
7755 *pfAIOSupported = false;
7756 } while (0);
7757
[27232]7758 if (RT_UNLIKELY(fLockRead))
7759 {
7760 rc2 = vdThreadFinishRead(pDisk);
7761 AssertRC(rc2);
7762 }
7763
[26987]7764 LogFlowFunc(("returns %Rrc, fAIOSupported=%u\n", rc, *pfAIOSupported));
7765 return rc;
7766}
7767
[27808]7768
[26987]7769VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
[28065]7770 PCRTSGSEG paSeg, unsigned cSeg,
[27808]7771 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
7772 void *pvUser1, void *pvUser2)
[26987]7773{
7774 int rc = VERR_VD_BLOCK_FREE;
[27232]7775 int rc2;
[27808]7776 bool fLockRead = false;
[28066]7777 PVDIOCTX pIoCtx = NULL;
[26987]7778
[28704]7779 LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbRead=%zu pvUser1=%#p pvUser2=%#p\n",
7780 pDisk, uOffset, paSeg, cSeg, cbRead, pvUser1, pvUser2));
7781
[26987]7782 do
7783 {
7784 /* sanity check */
7785 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7786 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7787
7788 /* Check arguments. */
7789 AssertMsgBreakStmt(cbRead,
7790 ("cbRead=%zu\n", cbRead),
7791 rc = VERR_INVALID_PARAMETER);
7792 AssertMsgBreakStmt(VALID_PTR(paSeg),
7793 ("paSeg=%#p\n", paSeg),
7794 rc = VERR_INVALID_PARAMETER);
7795 AssertMsgBreakStmt(cSeg,
7796 ("cSeg=%zu\n", cSeg),
7797 rc = VERR_INVALID_PARAMETER);
7798
[27808]7799 rc2 = vdThreadStartRead(pDisk);
[27232]7800 AssertRC(rc2);
[27808]7801 fLockRead = true;
[26987]7802
[27232]7803 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
7804 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
7805 uOffset, cbRead, pDisk->cbSize),
7806 rc = VERR_INVALID_PARAMETER);
7807
[28065]7808 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_READ, uOffset,
7809 cbRead, paSeg, cSeg,
[28154]7810 pfnComplete, pvUser1, pvUser2,
[28620]7811 NULL, vdReadHelperAsync);
[27808]7812 if (!pIoCtx)
[26987]7813 {
[27808]7814 rc = VERR_NO_MEMORY;
7815 break;
[26987]7816 }
7817
[28620]7818 pIoCtx->pImage = pDisk->pLast;
7819 AssertPtrBreakStmt(pIoCtx->pImage, rc = VERR_VD_NOT_OPENED);
[27232]7820
[28620]7821 rc = vdIoCtxProcess(pIoCtx);
7822 if (rc == VINF_VD_ASYNC_IO_FINISHED)
7823 {
7824 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
7825 vdIoCtxFree(pDisk, pIoCtx);
7826 else
7827 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
7828 }
7829 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
7830 vdIoCtxFree(pDisk, pIoCtx);
7831
[26987]7832 } while (0);
7833
[29006]7834 if (RT_UNLIKELY(fLockRead) && ( rc == VINF_VD_ASYNC_IO_FINISHED
7835 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
[27232]7836 {
[27808]7837 rc2 = vdThreadFinishRead(pDisk);
[27232]7838 AssertRC(rc2);
7839 }
7840
[26987]7841 LogFlowFunc(("returns %Rrc\n", rc));
7842 return rc;
7843}
7844
7845
7846VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
[28065]7847 PCRTSGSEG paSeg, unsigned cSeg,
[27808]7848 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
7849 void *pvUser1, void *pvUser2)
[26987]7850{
7851 int rc;
[27232]7852 int rc2;
7853 bool fLockWrite = false;
[28066]7854 PVDIOCTX pIoCtx = NULL;
[26987]7855
[28704]7856 LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbWrite=%zu pvUser1=%#p pvUser2=%#p\n",
7857 pDisk, uOffset, paSeg, cSeg, cbWrite, pvUser1, pvUser2));
[26987]7858 do
7859 {
7860 /* sanity check */
7861 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7862 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7863
7864 /* Check arguments. */
7865 AssertMsgBreakStmt(cbWrite,
7866 ("cbWrite=%zu\n", cbWrite),
7867 rc = VERR_INVALID_PARAMETER);
7868 AssertMsgBreakStmt(VALID_PTR(paSeg),
7869 ("paSeg=%#p\n", paSeg),
7870 rc = VERR_INVALID_PARAMETER);
7871 AssertMsgBreakStmt(cSeg,
7872 ("cSeg=%zu\n", cSeg),
7873 rc = VERR_INVALID_PARAMETER);
[27977]7874
[27232]7875 rc2 = vdThreadStartWrite(pDisk);
7876 AssertRC(rc2);
7877 fLockWrite = true;
[26987]7878
[27232]7879 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
7880 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
7881 uOffset, cbWrite, pDisk->cbSize),
7882 rc = VERR_INVALID_PARAMETER);
7883
[28154]7884 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_WRITE, uOffset,
[28065]7885 cbWrite, paSeg, cSeg,
[28154]7886 pfnComplete, pvUser1, pvUser2,
[28620]7887 NULL, vdWriteHelperAsync);
[27977]7888 if (!pIoCtx)
7889 {
7890 rc = VERR_NO_MEMORY;
7891 break;
7892 }
7893
[26987]7894 PVDIMAGE pImage = pDisk->pLast;
7895 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
[28620]7896 pIoCtx->pImage = pImage;
[26987]7897
[28620]7898 rc = vdIoCtxProcess(pIoCtx);
7899 if (rc == VINF_VD_ASYNC_IO_FINISHED)
7900 {
7901 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
7902 vdIoCtxFree(pDisk, pIoCtx);
7903 else
7904 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
7905 }
7906 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
7907 vdIoCtxFree(pDisk, pIoCtx);
[26987]7908 } while (0);
7909
[29006]7910 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
7911 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
[27232]7912 {
7913 rc2 = vdThreadFinishWrite(pDisk);
7914 AssertRC(rc2);
7915 }
[27736]7916
[26987]7917 LogFlowFunc(("returns %Rrc\n", rc));
7918 return rc;
[28383]7919}
[26987]7920
[28383]7921
7922VBOXDDU_DECL(int) VDAsyncFlush(PVBOXHDD pDisk, PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
7923 void *pvUser1, void *pvUser2)
7924{
7925 int rc;
7926 int rc2;
7927 bool fLockWrite = false;
7928 PVDIOCTX pIoCtx = NULL;
7929
7930 LogFlowFunc(("pDisk=%#p\n", pDisk));
7931
7932 do
7933 {
7934 /* sanity check */
7935 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7936 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7937
7938 rc2 = vdThreadStartWrite(pDisk);
7939 AssertRC(rc2);
7940 fLockWrite = true;
7941
7942 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_FLUSH, 0,
7943 0, NULL, 0,
7944 pfnComplete, pvUser1, pvUser2,
[28620]7945 NULL, vdFlushHelperAsync);
[28383]7946 if (!pIoCtx)
7947 {
7948 rc = VERR_NO_MEMORY;
7949 break;
7950 }
7951
7952 PVDIMAGE pImage = pDisk->pLast;
7953 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
[28620]7954 pIoCtx->pImage = pImage;
[28383]7955
[28620]7956 rc = vdIoCtxProcess(pIoCtx);
[29006]7957 if (rc == VINF_VD_ASYNC_IO_FINISHED)
7958 {
7959 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
7960 vdIoCtxFree(pDisk, pIoCtx);
7961 else
7962 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
7963 }
7964 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
7965 vdIoCtxFree(pDisk, pIoCtx);
[28383]7966 } while (0);
7967
[29006]7968 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
7969 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
[28383]7970 {
7971 rc2 = vdThreadFinishWrite(pDisk);
7972 AssertRC(rc2);
7973 }
7974
7975 LogFlowFunc(("returns %Rrc\n", rc));
7976 return rc;
[26987]7977}
7978
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use