VirtualBox

source: vbox/trunk/src/VBox/Storage/VD.cpp@ 44252

Last change on this file since 44252 was 44252, checked in by vboxsync, 12 years ago

Storage/Backends: async/sync I/O unification, remove separate entries for sync and async I/O callbacks, remove unused code

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 355.5 KB
Line 
1/* $Id: VD.cpp 44252 2013-01-08 13:23:54Z vboxsync $ */
2/** @file
3 * VBoxHDD - VBox HDD Container implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_VD
22#include <VBox/vd.h>
23#include <VBox/err.h>
24#include <VBox/sup.h>
25#include <VBox/log.h>
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>
33#include <iprt/ldr.h>
34#include <iprt/dir.h>
35#include <iprt/path.h>
36#include <iprt/param.h>
37#include <iprt/memcache.h>
38#include <iprt/sg.h>
39#include <iprt/critsect.h>
40#include <iprt/list.h>
41#include <iprt/avl.h>
42
43#include <VBox/vd-plugin.h>
44#include <VBox/vd-cache-plugin.h>
45
46/** Disable dynamic backends on non x86 architectures. This feature
47 * requires the SUPR3 library which is not available there.
48 */
49#if !defined(VBOX_HDD_NO_DYNAMIC_BACKENDS) && !defined(RT_ARCH_X86) && !defined(RT_ARCH_AMD64)
50# define VBOX_HDD_NO_DYNAMIC_BACKENDS
51#endif
52
53#define VBOXHDDDISK_SIGNATURE 0x6f0e2a7d
54
55/** Buffer size used for merging images. */
56#define VD_MERGE_BUFFER_SIZE (16 * _1M)
57
58/** Maximum number of segments in one I/O task. */
59#define VD_IO_TASK_SEGMENTS_MAX 64
60
61/** Threshold after not recently used blocks are removed from the list. */
62#define VD_DISCARD_REMOVE_THRESHOLD (10 * _1M) /** @todo: experiment */
63
64/**
65 * VD async I/O interface storage descriptor.
66 */
67typedef struct VDIIOFALLBACKSTORAGE
68{
69 /** File handle. */
70 RTFILE File;
71 /** Completion callback. */
72 PFNVDCOMPLETED pfnCompleted;
73 /** Thread for async access. */
74 RTTHREAD ThreadAsync;
75} VDIIOFALLBACKSTORAGE, *PVDIIOFALLBACKSTORAGE;
76
77/**
78 * Structure containing everything I/O related
79 * for the image and cache descriptors.
80 */
81typedef struct VDIO
82{
83 /** I/O interface to the upper layer. */
84 PVDINTERFACEIO pInterfaceIo;
85
86 /** Per image internal I/O interface. */
87 VDINTERFACEIOINT VDIfIoInt;
88
89 /** Fallback I/O interface, only used if the caller doesn't provide it. */
90 VDINTERFACEIO VDIfIo;
91
92 /** Opaque backend data. */
93 void *pBackendData;
94 /** Disk this image is part of */
95 PVBOXHDD pDisk;
96 /** Flag whether to ignore flush requests. */
97 bool fIgnoreFlush;
98} VDIO, *PVDIO;
99
100/**
101 * VBox HDD Container image descriptor.
102 */
103typedef struct VDIMAGE
104{
105 /** Link to parent image descriptor, if any. */
106 struct VDIMAGE *pPrev;
107 /** Link to child image descriptor, if any. */
108 struct VDIMAGE *pNext;
109 /** Container base filename. (UTF-8) */
110 char *pszFilename;
111 /** Data managed by the backend which keeps the actual info. */
112 void *pBackendData;
113 /** Cached sanitized image flags. */
114 unsigned uImageFlags;
115 /** Image open flags (only those handled generically in this code and which
116 * the backends will never ever see). */
117 unsigned uOpenFlags;
118
119 /** Function pointers for the various backend methods. */
120 PCVBOXHDDBACKEND Backend;
121 /** Pointer to list of VD interfaces, per-image. */
122 PVDINTERFACE pVDIfsImage;
123 /** I/O related things. */
124 VDIO VDIo;
125} VDIMAGE, *PVDIMAGE;
126
127/**
128 * uModified bit flags.
129 */
130#define VD_IMAGE_MODIFIED_FLAG RT_BIT(0)
131#define VD_IMAGE_MODIFIED_FIRST RT_BIT(1)
132#define VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE RT_BIT(2)
133
134
135/**
136 * VBox HDD Cache image descriptor.
137 */
138typedef struct VDCACHE
139{
140 /** Cache base filename. (UTF-8) */
141 char *pszFilename;
142 /** Data managed by the backend which keeps the actual info. */
143 void *pBackendData;
144 /** Cached sanitized image flags. */
145 unsigned uImageFlags;
146 /** Image open flags (only those handled generically in this code and which
147 * the backends will never ever see). */
148 unsigned uOpenFlags;
149
150 /** Function pointers for the various backend methods. */
151 PCVDCACHEBACKEND Backend;
152
153 /** Pointer to list of VD interfaces, per-cache. */
154 PVDINTERFACE pVDIfsCache;
155 /** I/O related things. */
156 VDIO VDIo;
157} VDCACHE, *PVDCACHE;
158
159/**
160 * A block waiting for a discard.
161 */
162typedef struct VDDISCARDBLOCK
163{
164 /** AVL core. */
165 AVLRU64NODECORE Core;
166 /** LRU list node. */
167 RTLISTNODE NodeLru;
168 /** Number of bytes to discard. */
169 size_t cbDiscard;
170 /** Bitmap of allocated sectors. */
171 void *pbmAllocated;
172} VDDISCARDBLOCK, *PVDDISCARDBLOCK;
173
174/**
175 * VD discard state.
176 */
177typedef struct VDDISCARDSTATE
178{
179 /** Number of bytes waiting for a discard. */
180 size_t cbDiscarding;
181 /** AVL tree with blocks waiting for a discard.
182 * The uOffset + cbDiscard range is the search key. */
183 PAVLRU64TREE pTreeBlocks;
184 /** LRU list of the least frequently discarded blocks.
185 * If there are to many blocks waiting the least frequently used
186 * will be removed and the range will be set to 0.
187 */
188 RTLISTNODE ListLru;
189} VDDISCARDSTATE, *PVDDISCARDSTATE;
190
191/**
192 * VBox HDD Container main structure, private part.
193 */
194struct VBOXHDD
195{
196 /** Structure signature (VBOXHDDDISK_SIGNATURE). */
197 uint32_t u32Signature;
198
199 /** Image type. */
200 VDTYPE enmType;
201
202 /** Number of opened images. */
203 unsigned cImages;
204
205 /** Base image. */
206 PVDIMAGE pBase;
207
208 /** Last opened image in the chain.
209 * The same as pBase if only one image is used. */
210 PVDIMAGE pLast;
211
212 /** If a merge to one of the parents is running this may be non-NULL
213 * to indicate to what image the writes should be additionally relayed. */
214 PVDIMAGE pImageRelay;
215
216 /** Flags representing the modification state. */
217 unsigned uModified;
218
219 /** Cached size of this disk. */
220 uint64_t cbSize;
221 /** Cached PCHS geometry for this disk. */
222 VDGEOMETRY PCHSGeometry;
223 /** Cached LCHS geometry for this disk. */
224 VDGEOMETRY LCHSGeometry;
225
226 /** Pointer to list of VD interfaces, per-disk. */
227 PVDINTERFACE pVDIfsDisk;
228 /** Pointer to the common interface structure for error reporting. */
229 PVDINTERFACEERROR pInterfaceError;
230 /** Pointer to the optional thread synchronization callbacks. */
231 PVDINTERFACETHREADSYNC pInterfaceThreadSync;
232
233 /** Memory cache for I/O contexts */
234 RTMEMCACHE hMemCacheIoCtx;
235 /** Memory cache for I/O tasks. */
236 RTMEMCACHE hMemCacheIoTask;
237 /** Critical section protecting the disk against concurrent access. */
238 RTCRITSECT CritSect;
239 /** Head of queued I/O contexts - LIFO order. */
240 volatile PVDIOCTX pIoCtxHead;
241 /** Flag whether the disk is currently locked by growing write or a flush
242 * request. Other flush or growing write requests need to wait until
243 * the current one completes.
244 */
245 volatile bool fLocked;
246 /** List of waiting requests. - Protected by the critical section. */
247 RTLISTNODE ListWriteLocked;
248 /** I/O context which locked the disk. */
249 PVDIOCTX pIoCtxLockOwner;
250
251 /** Pointer to the L2 disk cache if any. */
252 PVDCACHE pCache;
253 /** Pointer to the discard state if any. */
254 PVDDISCARDSTATE pDiscard;
255};
256
257# define VD_THREAD_IS_CRITSECT_OWNER(Disk) \
258 do \
259 { \
260 AssertMsg(RTCritSectIsOwner(&Disk->CritSect), \
261 ("Thread does not own critical section\n"));\
262 } while(0)
263
264/**
265 * VBox parent read descriptor, used internally for compaction.
266 */
267typedef struct VDPARENTSTATEDESC
268{
269 /** Pointer to disk descriptor. */
270 PVBOXHDD pDisk;
271 /** Pointer to image descriptor. */
272 PVDIMAGE pImage;
273} VDPARENTSTATEDESC, *PVDPARENTSTATEDESC;
274
275/**
276 * Transfer direction.
277 */
278typedef enum VDIOCTXTXDIR
279{
280 /** Read */
281 VDIOCTXTXDIR_READ = 0,
282 /** Write */
283 VDIOCTXTXDIR_WRITE,
284 /** Flush */
285 VDIOCTXTXDIR_FLUSH,
286 /** Discard */
287 VDIOCTXTXDIR_DISCARD,
288 /** 32bit hack */
289 VDIOCTXTXDIR_32BIT_HACK = 0x7fffffff
290} VDIOCTXTXDIR, *PVDIOCTXTXDIR;
291
292/** Transfer function */
293typedef DECLCALLBACK(int) FNVDIOCTXTRANSFER (PVDIOCTX pIoCtx);
294/** Pointer to a transfer function. */
295typedef FNVDIOCTXTRANSFER *PFNVDIOCTXTRANSFER;
296
297/**
298 * I/O context
299 */
300typedef struct VDIOCTX
301{
302 /** Pointer to the next I/O context. */
303 struct VDIOCTX * volatile pIoCtxNext;
304 /** Disk this is request is for. */
305 PVBOXHDD pDisk;
306 /** Return code. */
307 int rcReq;
308 /** Various flags for the I/O context. */
309 uint32_t fFlags;
310 /** Number of data transfers currently pending. */
311 volatile uint32_t cDataTransfersPending;
312 /** How many meta data transfers are pending. */
313 volatile uint32_t cMetaTransfersPending;
314 /** Flag whether the request finished */
315 volatile bool fComplete;
316 /** Temporary allocated memory which is freed
317 * when the context completes. */
318 void *pvAllocation;
319 /** Transfer function. */
320 PFNVDIOCTXTRANSFER pfnIoCtxTransfer;
321 /** Next transfer part after the current one completed. */
322 PFNVDIOCTXTRANSFER pfnIoCtxTransferNext;
323 /** Transfer direction */
324 VDIOCTXTXDIR enmTxDir;
325 /** Request type dependent data. */
326 union
327 {
328 /** I/O request (read/write). */
329 struct
330 {
331 /** Number of bytes left until this context completes. */
332 volatile uint32_t cbTransferLeft;
333 /** Current offset */
334 volatile uint64_t uOffset;
335 /** Number of bytes to transfer */
336 volatile size_t cbTransfer;
337 /** Current image in the chain. */
338 PVDIMAGE pImageCur;
339 /** Start image to read from. pImageCur is reset to this
340 * value after it reached the first image in the chain. */
341 PVDIMAGE pImageStart;
342 /** S/G buffer */
343 RTSGBUF SgBuf;
344 } Io;
345 /** Discard requests. */
346 struct
347 {
348 /** Pointer to the range descriptor array. */
349 PCRTRANGE paRanges;
350 /** Number of ranges in the array. */
351 unsigned cRanges;
352 /** Range descriptor index which is processed. */
353 unsigned idxRange;
354 /** Start offset to discard currently. */
355 uint64_t offCur;
356 /** How many bytes left to discard in the current range. */
357 size_t cbDiscardLeft;
358 /** How many bytes to discard in the current block (<= cbDiscardLeft). */
359 size_t cbThisDiscard;
360 /** Discard block handled currently. */
361 PVDDISCARDBLOCK pBlock;
362 } Discard;
363 } Req;
364 /** Parent I/O context if any. Sets the type of the context (root/child) */
365 PVDIOCTX pIoCtxParent;
366 /** Type dependent data (root/child) */
367 union
368 {
369 /** Root data */
370 struct
371 {
372 /** Completion callback */
373 PFNVDASYNCTRANSFERCOMPLETE pfnComplete;
374 /** User argument 1 passed on completion. */
375 void *pvUser1;
376 /** User argument 2 passed on completion. */
377 void *pvUser2;
378 } Root;
379 /** Child data */
380 struct
381 {
382 /** Saved start offset */
383 uint64_t uOffsetSaved;
384 /** Saved transfer size */
385 size_t cbTransferLeftSaved;
386 /** Number of bytes transferred from the parent if this context completes. */
387 size_t cbTransferParent;
388 /** Number of bytes to pre read */
389 size_t cbPreRead;
390 /** Number of bytes to post read. */
391 size_t cbPostRead;
392 /** Number of bytes to write left in the parent. */
393 size_t cbWriteParent;
394 /** Write type dependent data. */
395 union
396 {
397 /** Optimized */
398 struct
399 {
400 /** Bytes to fill to satisfy the block size. Not part of the virtual disk. */
401 size_t cbFill;
402 /** Bytes to copy instead of reading from the parent */
403 size_t cbWriteCopy;
404 /** Bytes to read from the image. */
405 size_t cbReadImage;
406 } Optimized;
407 } Write;
408 } Child;
409 } Type;
410} VDIOCTX;
411
412/** Flag whether the context is blocked. */
413#define VDIOCTX_FLAGS_BLOCKED RT_BIT_32(0)
414/** Flag whether the I/O context is using synchronous I/O. */
415#define VDIOCTX_FLAGS_SYNC RT_BIT_32(1)
416
417/**
418 * List node for deferred I/O contexts.
419 */
420typedef struct VDIOCTXDEFERRED
421{
422 /** Node in the list of deferred requests.
423 * A request can be deferred if the image is growing
424 * and the request accesses the same range or if
425 * the backend needs to read or write metadata from the disk
426 * before it can continue. */
427 RTLISTNODE NodeDeferred;
428 /** I/O context this entry points to. */
429 PVDIOCTX pIoCtx;
430} VDIOCTXDEFERRED, *PVDIOCTXDEFERRED;
431
432/**
433 * I/O task.
434 */
435typedef struct VDIOTASK
436{
437 /** Storage this task belongs to. */
438 PVDIOSTORAGE pIoStorage;
439 /** Optional completion callback. */
440 PFNVDXFERCOMPLETED pfnComplete;
441 /** Opaque user data. */
442 void *pvUser;
443 /** Flag whether this is a meta data transfer. */
444 bool fMeta;
445 /** Type dependent data. */
446 union
447 {
448 /** User data transfer. */
449 struct
450 {
451 /** Number of bytes this task transferred. */
452 uint32_t cbTransfer;
453 /** Pointer to the I/O context the task belongs. */
454 PVDIOCTX pIoCtx;
455 } User;
456 /** Meta data transfer. */
457 struct
458 {
459 /** Meta transfer this task is for. */
460 PVDMETAXFER pMetaXfer;
461 } Meta;
462 } Type;
463} VDIOTASK, *PVDIOTASK;
464
465/**
466 * Storage handle.
467 */
468typedef struct VDIOSTORAGE
469{
470 /** Image I/O state this storage handle belongs to. */
471 PVDIO pVDIo;
472 /** AVL tree for pending async metadata transfers. */
473 PAVLRFOFFTREE pTreeMetaXfers;
474 /** Storage handle */
475 void *pStorage;
476} VDIOSTORAGE;
477
478/**
479 * Metadata transfer.
480 *
481 * @note This entry can't be freed if either the list is not empty or
482 * the reference counter is not 0.
483 * The assumption is that the backends don't need to read huge amounts of
484 * metadata to complete a transfer so the additional memory overhead should
485 * be relatively small.
486 */
487typedef struct VDMETAXFER
488{
489 /** AVL core for fast search (the file offset is the key) */
490 AVLRFOFFNODECORE Core;
491 /** I/O storage for this transfer. */
492 PVDIOSTORAGE pIoStorage;
493 /** Flags. */
494 uint32_t fFlags;
495 /** List of I/O contexts waiting for this metadata transfer to complete. */
496 RTLISTNODE ListIoCtxWaiting;
497 /** Number of references to this entry. */
498 unsigned cRefs;
499 /** Size of the data stored with this entry. */
500 size_t cbMeta;
501 /** Data stored - variable size. */
502 uint8_t abData[1];
503} VDMETAXFER;
504
505/**
506 * The transfer direction for the metadata.
507 */
508#define VDMETAXFER_TXDIR_MASK 0x3
509#define VDMETAXFER_TXDIR_NONE 0x0
510#define VDMETAXFER_TXDIR_WRITE 0x1
511#define VDMETAXFER_TXDIR_READ 0x2
512#define VDMETAXFER_TXDIR_FLUSH 0x3
513#define VDMETAXFER_TXDIR_GET(flags) ((flags) & VDMETAXFER_TXDIR_MASK)
514#define VDMETAXFER_TXDIR_SET(flags, dir) ((flags) = (flags & ~VDMETAXFER_TXDIR_MASK) | (dir))
515
516extern VBOXHDDBACKEND g_RawBackend;
517extern VBOXHDDBACKEND g_VmdkBackend;
518extern VBOXHDDBACKEND g_VDIBackend;
519extern VBOXHDDBACKEND g_VhdBackend;
520extern VBOXHDDBACKEND g_ParallelsBackend;
521extern VBOXHDDBACKEND g_DmgBackend;
522extern VBOXHDDBACKEND g_ISCSIBackend;
523extern VBOXHDDBACKEND g_QedBackend;
524extern VBOXHDDBACKEND g_QCowBackend;
525extern VBOXHDDBACKEND g_VhdxBackend;
526
527static unsigned g_cBackends = 0;
528static PVBOXHDDBACKEND *g_apBackends = NULL;
529static PVBOXHDDBACKEND aStaticBackends[] =
530{
531 &g_VmdkBackend,
532 &g_VDIBackend,
533 &g_VhdBackend,
534 &g_ParallelsBackend,
535 &g_DmgBackend,
536 &g_QedBackend,
537 &g_QCowBackend,
538 &g_VhdxBackend,
539 &g_RawBackend,
540 &g_ISCSIBackend
541};
542
543/**
544 * Supported backends for the disk cache.
545 */
546extern VDCACHEBACKEND g_VciCacheBackend;
547
548static unsigned g_cCacheBackends = 0;
549static PVDCACHEBACKEND *g_apCacheBackends = NULL;
550static PVDCACHEBACKEND aStaticCacheBackends[] =
551{
552 &g_VciCacheBackend
553};
554
555/** Forward declaration of the async discard helper. */
556static int vdDiscardHelperAsync(PVDIOCTX pIoCtx);
557
558/**
559 * internal: add several backends.
560 */
561static int vdAddBackends(PVBOXHDDBACKEND *ppBackends, unsigned cBackends)
562{
563 PVBOXHDDBACKEND *pTmp = (PVBOXHDDBACKEND*)RTMemRealloc(g_apBackends,
564 (g_cBackends + cBackends) * sizeof(PVBOXHDDBACKEND));
565 if (RT_UNLIKELY(!pTmp))
566 return VERR_NO_MEMORY;
567 g_apBackends = pTmp;
568 memcpy(&g_apBackends[g_cBackends], ppBackends, cBackends * sizeof(PVBOXHDDBACKEND));
569 g_cBackends += cBackends;
570 return VINF_SUCCESS;
571}
572
573/**
574 * internal: add single backend.
575 */
576DECLINLINE(int) vdAddBackend(PVBOXHDDBACKEND pBackend)
577{
578 return vdAddBackends(&pBackend, 1);
579}
580
581/**
582 * internal: add several cache backends.
583 */
584static int vdAddCacheBackends(PVDCACHEBACKEND *ppBackends, unsigned cBackends)
585{
586 PVDCACHEBACKEND *pTmp = (PVDCACHEBACKEND*)RTMemRealloc(g_apCacheBackends,
587 (g_cCacheBackends + cBackends) * sizeof(PVDCACHEBACKEND));
588 if (RT_UNLIKELY(!pTmp))
589 return VERR_NO_MEMORY;
590 g_apCacheBackends = pTmp;
591 memcpy(&g_apCacheBackends[g_cCacheBackends], ppBackends, cBackends * sizeof(PVDCACHEBACKEND));
592 g_cCacheBackends += cBackends;
593 return VINF_SUCCESS;
594}
595
596/**
597 * internal: add single cache backend.
598 */
599DECLINLINE(int) vdAddCacheBackend(PVDCACHEBACKEND pBackend)
600{
601 return vdAddCacheBackends(&pBackend, 1);
602}
603
604/**
605 * internal: issue error message.
606 */
607static int vdError(PVBOXHDD pDisk, int rc, RT_SRC_POS_DECL,
608 const char *pszFormat, ...)
609{
610 va_list va;
611 va_start(va, pszFormat);
612 if (pDisk->pInterfaceError)
613 pDisk->pInterfaceError->pfnError(pDisk->pInterfaceError->Core.pvUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
614 va_end(va);
615 return rc;
616}
617
618/**
619 * internal: thread synchronization, start read.
620 */
621DECLINLINE(int) vdThreadStartRead(PVBOXHDD pDisk)
622{
623 int rc = VINF_SUCCESS;
624 if (RT_UNLIKELY(pDisk->pInterfaceThreadSync))
625 rc = pDisk->pInterfaceThreadSync->pfnStartRead(pDisk->pInterfaceThreadSync->Core.pvUser);
626 return rc;
627}
628
629/**
630 * internal: thread synchronization, finish read.
631 */
632DECLINLINE(int) vdThreadFinishRead(PVBOXHDD pDisk)
633{
634 int rc = VINF_SUCCESS;
635 if (RT_UNLIKELY(pDisk->pInterfaceThreadSync))
636 rc = pDisk->pInterfaceThreadSync->pfnFinishRead(pDisk->pInterfaceThreadSync->Core.pvUser);
637 return rc;
638}
639
640/**
641 * internal: thread synchronization, start write.
642 */
643DECLINLINE(int) vdThreadStartWrite(PVBOXHDD pDisk)
644{
645 int rc = VINF_SUCCESS;
646 if (RT_UNLIKELY(pDisk->pInterfaceThreadSync))
647 rc = pDisk->pInterfaceThreadSync->pfnStartWrite(pDisk->pInterfaceThreadSync->Core.pvUser);
648 return rc;
649}
650
651/**
652 * internal: thread synchronization, finish write.
653 */
654DECLINLINE(int) vdThreadFinishWrite(PVBOXHDD pDisk)
655{
656 int rc = VINF_SUCCESS;
657 if (RT_UNLIKELY(pDisk->pInterfaceThreadSync))
658 rc = pDisk->pInterfaceThreadSync->pfnFinishWrite(pDisk->pInterfaceThreadSync->Core.pvUser);
659 return rc;
660}
661
662/**
663 * internal: find image format backend.
664 */
665static int vdFindBackend(const char *pszBackend, PCVBOXHDDBACKEND *ppBackend)
666{
667 int rc = VINF_SUCCESS;
668 PCVBOXHDDBACKEND pBackend = NULL;
669
670 if (!g_apBackends)
671 VDInit();
672
673 for (unsigned i = 0; i < g_cBackends; i++)
674 {
675 if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
676 {
677 pBackend = g_apBackends[i];
678 break;
679 }
680 }
681 *ppBackend = pBackend;
682 return rc;
683}
684
685/**
686 * internal: find cache format backend.
687 */
688static int vdFindCacheBackend(const char *pszBackend, PCVDCACHEBACKEND *ppBackend)
689{
690 int rc = VINF_SUCCESS;
691 PCVDCACHEBACKEND pBackend = NULL;
692
693 if (!g_apCacheBackends)
694 VDInit();
695
696 for (unsigned i = 0; i < g_cCacheBackends; i++)
697 {
698 if (!RTStrICmp(pszBackend, g_apCacheBackends[i]->pszBackendName))
699 {
700 pBackend = g_apCacheBackends[i];
701 break;
702 }
703 }
704 *ppBackend = pBackend;
705 return rc;
706}
707
708/**
709 * internal: add image structure to the end of images list.
710 */
711static void vdAddImageToList(PVBOXHDD pDisk, PVDIMAGE pImage)
712{
713 pImage->pPrev = NULL;
714 pImage->pNext = NULL;
715
716 if (pDisk->pBase)
717 {
718 Assert(pDisk->cImages > 0);
719 pImage->pPrev = pDisk->pLast;
720 pDisk->pLast->pNext = pImage;
721 pDisk->pLast = pImage;
722 }
723 else
724 {
725 Assert(pDisk->cImages == 0);
726 pDisk->pBase = pImage;
727 pDisk->pLast = pImage;
728 }
729
730 pDisk->cImages++;
731}
732
733/**
734 * internal: remove image structure from the images list.
735 */
736static void vdRemoveImageFromList(PVBOXHDD pDisk, PVDIMAGE pImage)
737{
738 Assert(pDisk->cImages > 0);
739
740 if (pImage->pPrev)
741 pImage->pPrev->pNext = pImage->pNext;
742 else
743 pDisk->pBase = pImage->pNext;
744
745 if (pImage->pNext)
746 pImage->pNext->pPrev = pImage->pPrev;
747 else
748 pDisk->pLast = pImage->pPrev;
749
750 pImage->pPrev = NULL;
751 pImage->pNext = NULL;
752
753 pDisk->cImages--;
754}
755
756/**
757 * internal: find image by index into the images list.
758 */
759static PVDIMAGE vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
760{
761 PVDIMAGE pImage = pDisk->pBase;
762 if (nImage == VD_LAST_IMAGE)
763 return pDisk->pLast;
764 while (pImage && nImage)
765 {
766 pImage = pImage->pNext;
767 nImage--;
768 }
769 return pImage;
770}
771
772/**
773 * Initialize the structure members of a given I/O context.
774 */
775DECLINLINE(void) vdIoCtxInit(PVDIOCTX pIoCtx, PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
776 uint64_t uOffset, size_t cbTransfer, PVDIMAGE pImageStart,
777 PCRTSGBUF pcSgBuf, void *pvAllocation,
778 PFNVDIOCTXTRANSFER pfnIoCtxTransfer, uint32_t fFlags)
779{
780 pIoCtx->pDisk = pDisk;
781 pIoCtx->enmTxDir = enmTxDir;
782 pIoCtx->Req.Io.cbTransferLeft = cbTransfer;
783 pIoCtx->Req.Io.uOffset = uOffset;
784 pIoCtx->Req.Io.cbTransfer = cbTransfer;
785 pIoCtx->Req.Io.pImageStart = pImageStart;
786 pIoCtx->Req.Io.pImageCur = pImageStart;
787 pIoCtx->cDataTransfersPending = 0;
788 pIoCtx->cMetaTransfersPending = 0;
789 pIoCtx->fComplete = false;
790 pIoCtx->fFlags = fFlags;
791 pIoCtx->pvAllocation = pvAllocation;
792 pIoCtx->pfnIoCtxTransfer = pfnIoCtxTransfer;
793 pIoCtx->pfnIoCtxTransferNext = NULL;
794 pIoCtx->rcReq = VINF_SUCCESS;
795
796 /* There is no S/G list for a flush request. */
797 if ( enmTxDir != VDIOCTXTXDIR_FLUSH
798 && enmTxDir != VDIOCTXTXDIR_DISCARD)
799 RTSgBufClone(&pIoCtx->Req.Io.SgBuf, pcSgBuf);
800 else
801 memset(&pIoCtx->Req.Io.SgBuf, 0, sizeof(RTSGBUF));
802}
803
804/**
805 * Internal: Tries to read the desired range from the given cache.
806 *
807 * @returns VBox status code.
808 * @retval VERR_VD_BLOCK_FREE if the block is not in the cache.
809 * pcbRead will be set to the number of bytes not in the cache.
810 * Everything thereafter might be in the cache.
811 * @param pCache The cache to read from.
812 * @param uOffset Offset of the virtual disk to read.
813 * @param pvBuf Where to store the read data.
814 * @param cbRead How much to read.
815 * @param pcbRead Where to store the number of bytes actually read.
816 * On success this indicates the number of bytes read from the cache.
817 * If VERR_VD_BLOCK_FREE is returned this gives the number of bytes
818 * which are not in the cache.
819 * In both cases everything beyond this value
820 * might or might not be in the cache.
821 */
822static int vdCacheReadHelper(PVDCACHE pCache, uint64_t uOffset,
823 void *pvBuf, size_t cbRead, size_t *pcbRead)
824{
825 int rc = VINF_SUCCESS;
826
827 LogFlowFunc(("pCache=%#p uOffset=%llu pvBuf=%#p cbRead=%zu pcbRead=%#p\n",
828 pCache, uOffset, pvBuf, cbRead, pcbRead));
829
830 AssertPtr(pCache);
831 AssertPtr(pcbRead);
832
833#if 0 /** @todo: Cache implementation update */
834 rc = pCache->Backend->pfnRead(pCache->pBackendData, uOffset, pvBuf,
835 cbRead, pcbRead);
836#endif
837
838 LogFlowFunc(("returns rc=%Rrc pcbRead=%zu\n", rc, *pcbRead));
839 return rc;
840}
841
842/**
843 * Internal: Writes data for the given block into the cache.
844 *
845 * @returns VBox status code.
846 * @param pCache The cache to write to.
847 * @param uOffset Offset of the virtual disk to write to the cache.
848 * @param pcvBuf The data to write.
849 * @param cbWrite How much to write.
850 * @param pcbWritten How much data could be written, optional.
851 */
852static int vdCacheWriteHelper(PVDCACHE pCache, uint64_t uOffset, const void *pcvBuf,
853 size_t cbWrite, size_t *pcbWritten)
854{
855 int rc = VINF_SUCCESS;
856
857 LogFlowFunc(("pCache=%#p uOffset=%llu pvBuf=%#p cbWrite=%zu pcbWritten=%#p\n",
858 pCache, uOffset, pcvBuf, cbWrite, pcbWritten));
859
860 AssertPtr(pCache);
861 AssertPtr(pcvBuf);
862 Assert(cbWrite > 0);
863
864#if 0 /** @todo: Change cache implementation */
865 if (pcbWritten)
866 rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, pcvBuf,
867 cbWrite, pcbWritten);
868 else
869 {
870 size_t cbWritten = 0;
871
872 do
873 {
874 rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, pcvBuf,
875 cbWrite, &cbWritten);
876 uOffset += cbWritten;
877 pcvBuf = (char *)pcvBuf + cbWritten;
878 cbWrite -= cbWritten;
879 } while ( cbWrite
880 && RT_SUCCESS(rc));
881 }
882#endif
883
884 LogFlowFunc(("returns rc=%Rrc pcbWritten=%zu\n",
885 rc, pcbWritten ? *pcbWritten : cbWrite));
886 return rc;
887}
888
889/**
890 * Internal: Reads a given amount of data from the image chain of the disk.
891 **/
892static int vdDiskReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
893 uint64_t uOffset, void *pvBuf, size_t cbRead, size_t *pcbThisRead)
894{
895 int rc = VINF_SUCCESS;
896 size_t cbThisRead = cbRead;
897 RTSGSEG SegmentBuf;
898 RTSGBUF SgBuf;
899 VDIOCTX IoCtx;
900
901 AssertPtr(pcbThisRead);
902
903 *pcbThisRead = 0;
904
905 SegmentBuf.pvSeg = pvBuf;
906 SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
907 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
908 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
909 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
910
911 /*
912 * Try to read from the given image.
913 * If the block is not allocated read from override chain if present.
914 */
915 rc = pImage->Backend->pfnRead(pImage->pBackendData,
916 uOffset, cbThisRead, &IoCtx,
917 &cbThisRead);
918
919 if (rc == VERR_VD_BLOCK_FREE)
920 {
921 for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
922 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
923 pCurrImage = pCurrImage->pPrev)
924 {
925 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
926 uOffset, cbThisRead, &IoCtx,
927 &cbThisRead);
928 }
929 }
930
931 if (RT_SUCCESS(rc) || rc == VERR_VD_BLOCK_FREE)
932 *pcbThisRead = cbThisRead;
933
934 return rc;
935}
936
937/**
938 * Extended version of vdReadHelper(), implementing certain optimizations
939 * for image cloning.
940 *
941 * @returns VBox status code.
942 * @param pDisk The disk to read from.
943 * @param pImage The image to start reading from.
944 * @param pImageParentOverride The parent image to read from
945 * if the starting image returns a free block.
946 * If NULL is passed the real parent of the image
947 * in the chain is used.
948 * @param uOffset Offset in the disk to start reading from.
949 * @param pvBuf Where to store the read data.
950 * @param cbRead How much to read.
951 * @param fZeroFreeBlocks Flag whether free blocks should be zeroed.
952 * If false and no image has data for sepcified
953 * range VERR_VD_BLOCK_FREE is returned.
954 * Note that unallocated blocks are still zeroed
955 * if at least one image has valid data for a part
956 * of the range.
957 * @param fUpdateCache Flag whether to update the attached cache if
958 * available.
959 * @param cImagesRead Number of images in the chain to read until
960 * the read is cut off. A value of 0 disables the cut off.
961 */
962static int vdReadHelperEx(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
963 uint64_t uOffset, void *pvBuf, size_t cbRead,
964 bool fZeroFreeBlocks, bool fUpdateCache, unsigned cImagesRead)
965{
966 int rc = VINF_SUCCESS;
967 size_t cbThisRead;
968 bool fAllFree = true;
969 size_t cbBufClear = 0;
970
971 /* Loop until all read. */
972 do
973 {
974 /* Search for image with allocated block. Do not attempt to read more
975 * than the previous reads marked as valid. Otherwise this would return
976 * stale data when different block sizes are used for the images. */
977 cbThisRead = cbRead;
978
979 if ( pDisk->pCache
980 && !pImageParentOverride)
981 {
982 rc = vdCacheReadHelper(pDisk->pCache, uOffset, pvBuf,
983 cbThisRead, &cbThisRead);
984
985 if (rc == VERR_VD_BLOCK_FREE)
986 {
987 rc = vdDiskReadHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbThisRead,
988 &cbThisRead);
989
990 /* If the read was successful, write the data back into the cache. */
991 if ( RT_SUCCESS(rc)
992 && fUpdateCache)
993 {
994 rc = vdCacheWriteHelper(pDisk->pCache, uOffset, pvBuf,
995 cbThisRead, NULL);
996 }
997 }
998 }
999 else
1000 {
1001 RTSGSEG SegmentBuf;
1002 RTSGBUF SgBuf;
1003 VDIOCTX IoCtx;
1004
1005 SegmentBuf.pvSeg = pvBuf;
1006 SegmentBuf.cbSeg = cbThisRead;
1007 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
1008 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
1009 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
1010
1011 /*
1012 * Try to read from the given image.
1013 * If the block is not allocated read from override chain if present.
1014 */
1015 rc = pImage->Backend->pfnRead(pImage->pBackendData,
1016 uOffset, cbThisRead, &IoCtx,
1017 &cbThisRead);
1018
1019 if ( rc == VERR_VD_BLOCK_FREE
1020 && cImagesRead != 1)
1021 {
1022 unsigned cImagesToProcess = cImagesRead;
1023
1024 for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
1025 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
1026 pCurrImage = pCurrImage->pPrev)
1027 {
1028 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
1029 uOffset, cbThisRead,
1030 &IoCtx, &cbThisRead);
1031 if (cImagesToProcess == 1)
1032 break;
1033 else if (cImagesToProcess > 0)
1034 cImagesToProcess--;
1035 }
1036 }
1037 }
1038
1039 /* No image in the chain contains the data for the block. */
1040 if (rc == VERR_VD_BLOCK_FREE)
1041 {
1042 /* Fill the free space with 0 if we are told to do so
1043 * or a previous read returned valid data. */
1044 if (fZeroFreeBlocks || !fAllFree)
1045 memset(pvBuf, '\0', cbThisRead);
1046 else
1047 cbBufClear += cbThisRead;
1048
1049 if (pImage->uOpenFlags & VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS)
1050 rc = VINF_VD_NEW_ZEROED_BLOCK;
1051 else
1052 rc = VINF_SUCCESS;
1053 }
1054 else if (RT_SUCCESS(rc))
1055 {
1056 /* First not free block, fill the space before with 0. */
1057 if (!fZeroFreeBlocks)
1058 {
1059 memset((char *)pvBuf - cbBufClear, '\0', cbBufClear);
1060 cbBufClear = 0;
1061 fAllFree = false;
1062 }
1063 }
1064
1065 cbRead -= cbThisRead;
1066 uOffset += cbThisRead;
1067 pvBuf = (char *)pvBuf + cbThisRead;
1068 } while (cbRead != 0 && RT_SUCCESS(rc));
1069
1070 return (!fZeroFreeBlocks && fAllFree) ? VERR_VD_BLOCK_FREE : rc;
1071}
1072
1073/**
1074 * internal: read the specified amount of data in whatever blocks the backend
1075 * will give us.
1076 */
1077static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
1078 void *pvBuf, size_t cbRead, bool fUpdateCache)
1079{
1080 return vdReadHelperEx(pDisk, pImage, NULL, uOffset, pvBuf, cbRead,
1081 true /* fZeroFreeBlocks */, fUpdateCache, 0);
1082}
1083
1084/**
1085 * Creates a new empty discard state.
1086 *
1087 * @returns Pointer to the new discard state or NULL if out of memory.
1088 */
1089static PVDDISCARDSTATE vdDiscardStateCreate(void)
1090{
1091 PVDDISCARDSTATE pDiscard = (PVDDISCARDSTATE)RTMemAllocZ(sizeof(VDDISCARDSTATE));
1092
1093 if (pDiscard)
1094 {
1095 RTListInit(&pDiscard->ListLru);
1096 pDiscard->pTreeBlocks = (PAVLRU64TREE)RTMemAllocZ(sizeof(AVLRU64TREE));
1097 if (!pDiscard->pTreeBlocks)
1098 {
1099 RTMemFree(pDiscard);
1100 pDiscard = NULL;
1101 }
1102 }
1103
1104 return pDiscard;
1105}
1106
1107/**
1108 * Removes the least recently used blocks from the waiting list until
1109 * the new value is reached.
1110 *
1111 * @returns VBox status code.
1112 * @param pDisk VD disk container.
1113 * @param pDiscard The discard state.
1114 * @param cbDiscardingNew How many bytes should be waiting on success.
1115 * The number of bytes waiting can be less.
1116 */
1117static int vdDiscardRemoveBlocks(PVBOXHDD pDisk, PVDDISCARDSTATE pDiscard, size_t cbDiscardingNew)
1118{
1119 int rc = VINF_SUCCESS;
1120
1121 LogFlowFunc(("pDisk=%#p pDiscard=%#p cbDiscardingNew=%zu\n",
1122 pDisk, pDiscard, cbDiscardingNew));
1123
1124 while (pDiscard->cbDiscarding > cbDiscardingNew)
1125 {
1126 PVDDISCARDBLOCK pBlock = RTListGetLast(&pDiscard->ListLru, VDDISCARDBLOCK, NodeLru);
1127
1128 Assert(!RTListIsEmpty(&pDiscard->ListLru));
1129
1130 /* Go over the allocation bitmap and mark all discarded sectors as unused. */
1131 uint64_t offStart = pBlock->Core.Key;
1132 uint32_t idxStart = 0;
1133 size_t cbLeft = pBlock->cbDiscard;
1134 bool fAllocated = ASMBitTest(pBlock->pbmAllocated, idxStart);
1135 uint32_t cSectors = pBlock->cbDiscard / 512;
1136
1137 while (cbLeft > 0)
1138 {
1139 int32_t idxEnd;
1140 size_t cbThis = cbLeft;
1141
1142 if (fAllocated)
1143 {
1144 /* Check for the first unallocated bit. */
1145 idxEnd = ASMBitNextClear(pBlock->pbmAllocated, cSectors, idxStart);
1146 if (idxEnd != -1)
1147 {
1148 cbThis = (idxEnd - idxStart) * 512;
1149 fAllocated = false;
1150 }
1151 }
1152 else
1153 {
1154 /* Mark as unused and check for the first set bit. */
1155 idxEnd = ASMBitNextSet(pBlock->pbmAllocated, cSectors, idxStart);
1156 if (idxEnd != -1)
1157 cbThis = (idxEnd - idxStart) * 512;
1158
1159
1160 VDIOCTX IoCtx;
1161 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_DISCARD, 0, 0, NULL,
1162 NULL, NULL, NULL, VDIOCTX_FLAGS_SYNC);
1163 rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData,
1164 &IoCtx, offStart, cbThis, NULL,
1165 NULL, &cbThis, NULL,
1166 VD_DISCARD_MARK_UNUSED);
1167 if (RT_FAILURE(rc))
1168 break;
1169
1170 fAllocated = true;
1171 }
1172
1173 idxStart = idxEnd;
1174 offStart += cbThis;
1175 cbLeft -= cbThis;
1176 }
1177
1178 if (RT_FAILURE(rc))
1179 break;
1180
1181 PVDDISCARDBLOCK pBlockRemove = (PVDDISCARDBLOCK)RTAvlrU64RangeRemove(pDiscard->pTreeBlocks, pBlock->Core.Key);
1182 Assert(pBlockRemove == pBlock);
1183 RTListNodeRemove(&pBlock->NodeLru);
1184
1185 pDiscard->cbDiscarding -= pBlock->cbDiscard;
1186 RTMemFree(pBlock->pbmAllocated);
1187 RTMemFree(pBlock);
1188 }
1189
1190 Assert(RT_FAILURE(rc) || pDiscard->cbDiscarding <= cbDiscardingNew);
1191
1192 LogFlowFunc(("returns rc=%Rrc\n", rc));
1193 return rc;
1194}
1195
1196/**
1197 * Destroys the current discard state, writing any waiting blocks to the image.
1198 *
1199 * @returns VBox status code.
1200 * @param pDisk VD disk container.
1201 */
1202static int vdDiscardStateDestroy(PVBOXHDD pDisk)
1203{
1204 int rc = VINF_SUCCESS;
1205
1206 if (pDisk->pDiscard)
1207 {
1208 rc = vdDiscardRemoveBlocks(pDisk, pDisk->pDiscard, 0 /* Remove all blocks. */);
1209 AssertRC(rc);
1210 RTMemFree(pDisk->pDiscard->pTreeBlocks);
1211 RTMemFree(pDisk->pDiscard);
1212 pDisk->pDiscard = NULL;
1213 }
1214
1215 return rc;
1216}
1217
1218/**
1219 * Discards the given range from the underlying block.
1220 *
1221 * @returns VBox status code.
1222 * @param pDisk VD container data.
1223 * @param offStart Where to start discarding.
1224 * @param cbDiscard How many bytes to discard.
1225 */
1226static int vdDiscardRange(PVBOXHDD pDisk, PVDDISCARDSTATE pDiscard, uint64_t offStart, size_t cbDiscard)
1227{
1228 int rc = VINF_SUCCESS;
1229
1230 LogFlowFunc(("pDisk=%#p pDiscard=%#p offStart=%llu cbDiscard=%zu\n",
1231 pDisk, pDiscard, offStart, cbDiscard));
1232
1233 do
1234 {
1235 size_t cbThisDiscard;
1236
1237 /* Look for a matching block in the AVL tree first. */
1238 PVDDISCARDBLOCK pBlock = (PVDDISCARDBLOCK)RTAvlrU64GetBestFit(pDiscard->pTreeBlocks, offStart, false);
1239 if (!pBlock || pBlock->Core.KeyLast < offStart)
1240 {
1241 void *pbmAllocated = NULL;
1242 size_t cbPreAllocated, cbPostAllocated;
1243 PVDDISCARDBLOCK pBlockAbove = (PVDDISCARDBLOCK)RTAvlrU64GetBestFit(pDiscard->pTreeBlocks, offStart, true);
1244
1245 /* Clip range to remain in the current block. */
1246 if (pBlockAbove)
1247 cbThisDiscard = RT_MIN(cbDiscard, pBlockAbove->Core.KeyLast - offStart + 1);
1248 else
1249 cbThisDiscard = cbDiscard;
1250
1251 Assert(!(cbThisDiscard % 512));
1252
1253 /* No block found, try to discard using the backend first. */
1254 VDIOCTX IoCtx;
1255
1256 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_DISCARD, 0, 0, NULL,
1257 NULL, NULL, NULL, VDIOCTX_FLAGS_SYNC);
1258 rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData,
1259 &IoCtx, offStart,
1260 cbThisDiscard, &cbPreAllocated,
1261 &cbPostAllocated, &cbThisDiscard,
1262 &pbmAllocated, 0);
1263 if (rc == VERR_VD_DISCARD_ALIGNMENT_NOT_MET)
1264 {
1265 /* Create new discard block. */
1266 pBlock = (PVDDISCARDBLOCK)RTMemAllocZ(sizeof(VDDISCARDBLOCK));
1267 if (pBlock)
1268 {
1269 pBlock->Core.Key = offStart - cbPreAllocated;
1270 pBlock->Core.KeyLast = offStart + cbThisDiscard + cbPostAllocated - 1;
1271 pBlock->cbDiscard = cbPreAllocated + cbThisDiscard + cbPostAllocated;
1272 pBlock->pbmAllocated = pbmAllocated;
1273 bool fInserted = RTAvlrU64Insert(pDiscard->pTreeBlocks, &pBlock->Core);
1274 Assert(fInserted);
1275
1276 RTListPrepend(&pDiscard->ListLru, &pBlock->NodeLru);
1277 pDiscard->cbDiscarding += pBlock->cbDiscard;
1278 if (pDiscard->cbDiscarding > VD_DISCARD_REMOVE_THRESHOLD)
1279 rc = vdDiscardRemoveBlocks(pDisk, pDiscard, VD_DISCARD_REMOVE_THRESHOLD);
1280 else
1281 rc = VINF_SUCCESS;
1282 }
1283 else
1284 {
1285 RTMemFree(pbmAllocated);
1286 rc = VERR_NO_MEMORY;
1287 }
1288 }
1289 }
1290 else
1291 {
1292 /* Range lies partly in the block, update allocation bitmap. */
1293 int32_t idxStart, idxEnd;
1294
1295 cbThisDiscard = RT_MIN(cbDiscard, pBlock->Core.KeyLast - offStart + 1);
1296
1297 AssertPtr(pBlock);
1298
1299 Assert(!(cbThisDiscard % 512));
1300 Assert(!((offStart - pBlock->Core.Key) % 512));
1301
1302 idxStart = (offStart - pBlock->Core.Key) / 512;
1303 idxEnd = idxStart + (cbThisDiscard / 512);
1304
1305 ASMBitClearRange(pBlock->pbmAllocated, idxStart, idxEnd);
1306
1307 /* Call the backend to discard the block if it is completely unallocated now. */
1308 if (ASMBitFirstSet((volatile void *)pBlock->pbmAllocated, pBlock->cbDiscard / 512) == -1)
1309 {
1310 VDIOCTX IoCtx;
1311 size_t cbPreAllocated, cbPostAllocated, cbActuallyDiscarded;
1312
1313 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_DISCARD, 0, 0, NULL,
1314 NULL, NULL, NULL, VDIOCTX_FLAGS_SYNC);
1315 rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData,
1316 &IoCtx, pBlock->Core.Key,
1317 pBlock->cbDiscard, &cbPreAllocated,
1318 &cbPostAllocated, &cbActuallyDiscarded,
1319 NULL, 0);
1320 Assert(rc != VERR_VD_DISCARD_ALIGNMENT_NOT_MET);
1321 Assert(!cbPreAllocated);
1322 Assert(!cbPostAllocated);
1323 Assert(cbActuallyDiscarded == pBlock->cbDiscard || RT_FAILURE(rc));
1324
1325 /* Remove the block on success. */
1326 if (RT_SUCCESS(rc))
1327 {
1328 PVDDISCARDBLOCK pBlockRemove = (PVDDISCARDBLOCK)RTAvlrU64RangeRemove(pDiscard->pTreeBlocks, pBlock->Core.Key);
1329 Assert(pBlockRemove == pBlock);
1330
1331 pDiscard->cbDiscarding -= pBlock->cbDiscard;
1332 RTListNodeRemove(&pBlock->NodeLru);
1333 RTMemFree(pBlock->pbmAllocated);
1334 RTMemFree(pBlock);
1335 }
1336 }
1337 else
1338 {
1339 RTListNodeRemove(&pBlock->NodeLru);
1340 RTListPrepend(&pDiscard->ListLru, &pBlock->NodeLru);
1341 rc = VINF_SUCCESS;
1342 }
1343 }
1344
1345 Assert(cbDiscard >= cbThisDiscard);
1346
1347 cbDiscard -= cbThisDiscard;
1348 offStart += cbThisDiscard;
1349 } while (cbDiscard != 0 && RT_SUCCESS(rc));
1350
1351 LogFlowFunc(("returns rc=%Rrc\n", rc));
1352 return rc;
1353}
1354
1355/**
1356 * Discard helper.
1357 *
1358 * @returns VBox status code.
1359 * @param pDisk VD container data.
1360 * @param paRanges The array of ranges to discard.
1361 * @param cRanges The number of ranges in the array.
1362 */
1363static int vdDiscardHelper(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges)
1364{
1365 int rc = VINF_SUCCESS;
1366 PVDDISCARDSTATE pDiscard = pDisk->pDiscard;
1367
1368 if (RT_UNLIKELY(!pDiscard))
1369 {
1370 pDiscard = vdDiscardStateCreate();
1371 if (!pDiscard)
1372 return VERR_NO_MEMORY;
1373
1374 pDisk->pDiscard = pDiscard;
1375 }
1376
1377 /* Go over the range array and discard individual blocks. */
1378 for (unsigned i = 0; i < cRanges; i++)
1379 {
1380 rc = vdDiscardRange(pDisk, pDiscard, paRanges[i].offStart, paRanges[i].cbRange);
1381 if (RT_FAILURE(rc))
1382 break;
1383 }
1384
1385 return rc;
1386}
1387
1388/**
1389 * Marks the given range as allocated in the image.
1390 * Required if there are discards in progress and a write to a block which can get discarded
1391 * is written to.
1392 *
1393 * @returns VBox status code.
1394 * @param pDisk VD container data.
1395 * @param uOffset First byte to mark as allocated.
1396 * @param cbRange Number of bytes to mark as allocated.
1397 */
1398static int vdDiscardSetRangeAllocated(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRange)
1399{
1400 PVDDISCARDSTATE pDiscard = pDisk->pDiscard;
1401 int rc = VINF_SUCCESS;
1402
1403 if (pDiscard)
1404 {
1405 do
1406 {
1407 size_t cbThisRange = cbRange;
1408 PVDDISCARDBLOCK pBlock = (PVDDISCARDBLOCK)RTAvlrU64RangeGet(pDiscard->pTreeBlocks, uOffset);
1409
1410 if (pBlock)
1411 {
1412 int32_t idxStart, idxEnd;
1413
1414 Assert(!(cbThisRange % 512));
1415 Assert(!((uOffset - pBlock->Core.Key) % 512));
1416
1417 cbThisRange = RT_MIN(cbThisRange, pBlock->Core.KeyLast - uOffset + 1);
1418
1419 idxStart = (uOffset - pBlock->Core.Key) / 512;
1420 idxEnd = idxStart + (cbThisRange / 512);
1421 ASMBitSetRange(pBlock->pbmAllocated, idxStart, idxEnd);
1422 }
1423 else
1424 {
1425 pBlock = (PVDDISCARDBLOCK)RTAvlrU64GetBestFit(pDiscard->pTreeBlocks, uOffset, true);
1426 if (pBlock)
1427 cbThisRange = RT_MIN(cbThisRange, pBlock->Core.Key - uOffset);
1428 }
1429
1430 Assert(cbRange >= cbThisRange);
1431
1432 uOffset += cbThisRange;
1433 cbRange -= cbThisRange;
1434 } while (cbRange != 0);
1435 }
1436
1437 return rc;
1438}
1439
1440DECLINLINE(PVDIOCTX) vdIoCtxAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
1441 uint64_t uOffset, size_t cbTransfer,
1442 PVDIMAGE pImageStart,PCRTSGBUF pcSgBuf,
1443 void *pvAllocation, PFNVDIOCTXTRANSFER pfnIoCtxTransfer,
1444 uint32_t fFlags)
1445{
1446 PVDIOCTX pIoCtx = NULL;
1447
1448 pIoCtx = (PVDIOCTX)RTMemCacheAlloc(pDisk->hMemCacheIoCtx);
1449 if (RT_LIKELY(pIoCtx))
1450 {
1451 vdIoCtxInit(pIoCtx, pDisk, enmTxDir, uOffset, cbTransfer, pImageStart,
1452 pcSgBuf, pvAllocation, pfnIoCtxTransfer, fFlags);
1453 }
1454
1455 return pIoCtx;
1456}
1457
1458DECLINLINE(PVDIOCTX) vdIoCtxRootAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
1459 uint64_t uOffset, size_t cbTransfer,
1460 PVDIMAGE pImageStart, PCRTSGBUF pcSgBuf,
1461 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
1462 void *pvUser1, void *pvUser2,
1463 void *pvAllocation,
1464 PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
1465{
1466 PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer, pImageStart,
1467 pcSgBuf, pvAllocation, pfnIoCtxTransfer, 0);
1468
1469 if (RT_LIKELY(pIoCtx))
1470 {
1471 pIoCtx->pIoCtxParent = NULL;
1472 pIoCtx->Type.Root.pfnComplete = pfnComplete;
1473 pIoCtx->Type.Root.pvUser1 = pvUser1;
1474 pIoCtx->Type.Root.pvUser2 = pvUser2;
1475 }
1476
1477 LogFlow(("Allocated root I/O context %#p\n", pIoCtx));
1478 return pIoCtx;
1479}
1480
1481DECLINLINE(PVDIOCTX) vdIoCtxDiscardAlloc(PVBOXHDD pDisk, PCRTRANGE paRanges,
1482 unsigned cRanges,
1483 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
1484 void *pvUser1, void *pvUser2,
1485 void *pvAllocation,
1486 PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
1487{
1488 PVDIOCTX pIoCtx = NULL;
1489
1490 pIoCtx = (PVDIOCTX)RTMemCacheAlloc(pDisk->hMemCacheIoCtx);
1491 if (RT_LIKELY(pIoCtx))
1492 {
1493 pIoCtx->pIoCtxNext = NULL;
1494 pIoCtx->pDisk = pDisk;
1495 pIoCtx->enmTxDir = VDIOCTXTXDIR_DISCARD;
1496 pIoCtx->cDataTransfersPending = 0;
1497 pIoCtx->cMetaTransfersPending = 0;
1498 pIoCtx->fComplete = false;
1499 pIoCtx->fFlags = 0;
1500 pIoCtx->pvAllocation = pvAllocation;
1501 pIoCtx->pfnIoCtxTransfer = pfnIoCtxTransfer;
1502 pIoCtx->pfnIoCtxTransferNext = NULL;
1503 pIoCtx->rcReq = VINF_SUCCESS;
1504 pIoCtx->Req.Discard.paRanges = paRanges;
1505 pIoCtx->Req.Discard.cRanges = cRanges;
1506 pIoCtx->Req.Discard.idxRange = 0;
1507 pIoCtx->Req.Discard.cbDiscardLeft = 0;
1508 pIoCtx->Req.Discard.offCur = 0;
1509 pIoCtx->Req.Discard.cbThisDiscard = 0;
1510
1511 pIoCtx->pIoCtxParent = NULL;
1512 pIoCtx->Type.Root.pfnComplete = pfnComplete;
1513 pIoCtx->Type.Root.pvUser1 = pvUser1;
1514 pIoCtx->Type.Root.pvUser2 = pvUser2;
1515 }
1516
1517 LogFlow(("Allocated discard I/O context %#p\n", pIoCtx));
1518 return pIoCtx;
1519}
1520
1521DECLINLINE(PVDIOCTX) vdIoCtxChildAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
1522 uint64_t uOffset, size_t cbTransfer,
1523 PVDIMAGE pImageStart, PCRTSGBUF pcSgBuf,
1524 PVDIOCTX pIoCtxParent, size_t cbTransferParent,
1525 size_t cbWriteParent, void *pvAllocation,
1526 PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
1527{
1528 PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer, pImageStart,
1529 pcSgBuf, pvAllocation, pfnIoCtxTransfer, 0);
1530
1531 AssertPtr(pIoCtxParent);
1532 Assert(!pIoCtxParent->pIoCtxParent);
1533
1534 if (RT_LIKELY(pIoCtx))
1535 {
1536 pIoCtx->pIoCtxParent = pIoCtxParent;
1537 pIoCtx->Type.Child.uOffsetSaved = uOffset;
1538 pIoCtx->Type.Child.cbTransferLeftSaved = cbTransfer;
1539 pIoCtx->Type.Child.cbTransferParent = cbTransferParent;
1540 pIoCtx->Type.Child.cbWriteParent = cbWriteParent;
1541 }
1542
1543 LogFlow(("Allocated child I/O context %#p\n", pIoCtx));
1544 return pIoCtx;
1545}
1546
1547DECLINLINE(PVDIOTASK) vdIoTaskUserAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDIOCTX pIoCtx, uint32_t cbTransfer)
1548{
1549 PVDIOTASK pIoTask = NULL;
1550
1551 pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pVDIo->pDisk->hMemCacheIoTask);
1552 if (pIoTask)
1553 {
1554 pIoTask->pIoStorage = pIoStorage;
1555 pIoTask->pfnComplete = pfnComplete;
1556 pIoTask->pvUser = pvUser;
1557 pIoTask->fMeta = false;
1558 pIoTask->Type.User.cbTransfer = cbTransfer;
1559 pIoTask->Type.User.pIoCtx = pIoCtx;
1560 }
1561
1562 return pIoTask;
1563}
1564
1565DECLINLINE(PVDIOTASK) vdIoTaskMetaAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDMETAXFER pMetaXfer)
1566{
1567 PVDIOTASK pIoTask = NULL;
1568
1569 pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pVDIo->pDisk->hMemCacheIoTask);
1570 if (pIoTask)
1571 {
1572 pIoTask->pIoStorage = pIoStorage;
1573 pIoTask->pfnComplete = pfnComplete;
1574 pIoTask->pvUser = pvUser;
1575 pIoTask->fMeta = true;
1576 pIoTask->Type.Meta.pMetaXfer = pMetaXfer;
1577 }
1578
1579 return pIoTask;
1580}
1581
1582DECLINLINE(void) vdIoCtxFree(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1583{
1584 LogFlow(("Freeing I/O context %#p\n", pIoCtx));
1585 if (pIoCtx->pvAllocation)
1586 RTMemFree(pIoCtx->pvAllocation);
1587#ifdef DEBUG
1588 memset(pIoCtx, 0xff, sizeof(VDIOCTX));
1589#endif
1590 RTMemCacheFree(pDisk->hMemCacheIoCtx, pIoCtx);
1591}
1592
1593DECLINLINE(void) vdIoTaskFree(PVBOXHDD pDisk, PVDIOTASK pIoTask)
1594{
1595 RTMemCacheFree(pDisk->hMemCacheIoTask, pIoTask);
1596}
1597
1598DECLINLINE(void) vdIoCtxChildReset(PVDIOCTX pIoCtx)
1599{
1600 AssertPtr(pIoCtx->pIoCtxParent);
1601
1602 RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
1603 pIoCtx->Req.Io.uOffset = pIoCtx->Type.Child.uOffsetSaved;
1604 pIoCtx->Req.Io.cbTransferLeft = pIoCtx->Type.Child.cbTransferLeftSaved;
1605}
1606
1607DECLINLINE(PVDMETAXFER) vdMetaXferAlloc(PVDIOSTORAGE pIoStorage, uint64_t uOffset, size_t cb)
1608{
1609 PVDMETAXFER pMetaXfer = (PVDMETAXFER)RTMemAlloc(RT_OFFSETOF(VDMETAXFER, abData[cb]));
1610
1611 if (RT_LIKELY(pMetaXfer))
1612 {
1613 pMetaXfer->Core.Key = uOffset;
1614 pMetaXfer->Core.KeyLast = uOffset + cb - 1;
1615 pMetaXfer->fFlags = VDMETAXFER_TXDIR_NONE;
1616 pMetaXfer->cbMeta = cb;
1617 pMetaXfer->pIoStorage = pIoStorage;
1618 pMetaXfer->cRefs = 0;
1619 RTListInit(&pMetaXfer->ListIoCtxWaiting);
1620 }
1621 return pMetaXfer;
1622}
1623
1624DECLINLINE(int) vdIoCtxDefer(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1625{
1626 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
1627
1628 if (!pDeferred)
1629 return VERR_NO_MEMORY;
1630
1631 LogFlowFunc(("Deferring write pIoCtx=%#p\n", pIoCtx));
1632
1633 Assert(!pIoCtx->pIoCtxParent && !(pIoCtx->fFlags & VDIOCTX_FLAGS_BLOCKED));
1634
1635 RTListInit(&pDeferred->NodeDeferred);
1636 pDeferred->pIoCtx = pIoCtx;
1637 RTListAppend(&pDisk->ListWriteLocked, &pDeferred->NodeDeferred);
1638 pIoCtx->fFlags |= VDIOCTX_FLAGS_BLOCKED;
1639 return VINF_SUCCESS;
1640}
1641
1642static size_t vdIoCtxCopy(PVDIOCTX pIoCtxDst, PVDIOCTX pIoCtxSrc, size_t cbData)
1643{
1644 return RTSgBufCopy(&pIoCtxDst->Req.Io.SgBuf, &pIoCtxSrc->Req.Io.SgBuf, cbData);
1645}
1646
1647static int vdIoCtxCmp(PVDIOCTX pIoCtx1, PVDIOCTX pIoCtx2, size_t cbData)
1648{
1649 return RTSgBufCmp(&pIoCtx1->Req.Io.SgBuf, &pIoCtx2->Req.Io.SgBuf, cbData);
1650}
1651
1652static size_t vdIoCtxCopyTo(PVDIOCTX pIoCtx, const uint8_t *pbData, size_t cbData)
1653{
1654 return RTSgBufCopyFromBuf(&pIoCtx->Req.Io.SgBuf, pbData, cbData);
1655}
1656
1657static size_t vdIoCtxCopyFrom(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
1658{
1659 return RTSgBufCopyToBuf(&pIoCtx->Req.Io.SgBuf, pbData, cbData);
1660}
1661
1662static size_t vdIoCtxSet(PVDIOCTX pIoCtx, uint8_t ch, size_t cbData)
1663{
1664 return RTSgBufSet(&pIoCtx->Req.Io.SgBuf, ch, cbData);
1665}
1666
1667/**
1668 * Process the I/O context, core method which assumes that the critsect is acquired
1669 * by the calling thread.
1670 *
1671 * @returns VBox status code.
1672 * @param pIoCtx I/O context to process.
1673 */
1674static int vdIoCtxProcessLocked(PVDIOCTX pIoCtx)
1675{
1676 int rc = VINF_SUCCESS;
1677
1678 VD_THREAD_IS_CRITSECT_OWNER(pIoCtx->pDisk);
1679
1680 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1681
1682 if ( !pIoCtx->cMetaTransfersPending
1683 && !pIoCtx->cDataTransfersPending
1684 && !pIoCtx->pfnIoCtxTransfer)
1685 {
1686 rc = VINF_VD_ASYNC_IO_FINISHED;
1687 goto out;
1688 }
1689
1690 /*
1691 * We complete the I/O context in case of an error
1692 * if there is no I/O task pending.
1693 */
1694 if ( RT_FAILURE(pIoCtx->rcReq)
1695 && !pIoCtx->cMetaTransfersPending
1696 && !pIoCtx->cDataTransfersPending)
1697 {
1698 rc = VINF_VD_ASYNC_IO_FINISHED;
1699 goto out;
1700 }
1701
1702 /* Don't change anything if there is a metadata transfer pending or we are blocked. */
1703 if ( pIoCtx->cMetaTransfersPending
1704 || (pIoCtx->fFlags & VDIOCTX_FLAGS_BLOCKED))
1705 {
1706 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1707 goto out;
1708 }
1709
1710 if (pIoCtx->pfnIoCtxTransfer)
1711 {
1712 /* Call the transfer function advancing to the next while there is no error. */
1713 while ( pIoCtx->pfnIoCtxTransfer
1714 && !pIoCtx->cMetaTransfersPending
1715 && RT_SUCCESS(rc))
1716 {
1717 LogFlowFunc(("calling transfer function %#p\n", pIoCtx->pfnIoCtxTransfer));
1718 rc = pIoCtx->pfnIoCtxTransfer(pIoCtx);
1719
1720 /* Advance to the next part of the transfer if the current one succeeded. */
1721 if (RT_SUCCESS(rc))
1722 {
1723 pIoCtx->pfnIoCtxTransfer = pIoCtx->pfnIoCtxTransferNext;
1724 pIoCtx->pfnIoCtxTransferNext = NULL;
1725 }
1726 }
1727 }
1728
1729 if ( RT_SUCCESS(rc)
1730 && !pIoCtx->cMetaTransfersPending
1731 && !pIoCtx->cDataTransfersPending)
1732 rc = VINF_VD_ASYNC_IO_FINISHED;
1733 else if ( RT_SUCCESS(rc)
1734 || rc == VERR_VD_NOT_ENOUGH_METADATA
1735 || rc == VERR_VD_IOCTX_HALT)
1736 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1737 else if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
1738 {
1739 ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rc, VINF_SUCCESS);
1740 /*
1741 * The I/O context completed if we have an error and there is no data
1742 * or meta data transfer pending.
1743 */
1744 if ( !pIoCtx->cMetaTransfersPending
1745 && !pIoCtx->cDataTransfersPending)
1746 rc = VINF_VD_ASYNC_IO_FINISHED;
1747 else
1748 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1749 }
1750
1751out:
1752 LogFlowFunc(("pIoCtx=%#p rc=%Rrc cDataTransfersPending=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
1753 pIoCtx, rc, pIoCtx->cDataTransfersPending, pIoCtx->cMetaTransfersPending,
1754 pIoCtx->fComplete));
1755
1756 return rc;
1757}
1758
1759/**
1760 * Processes the list of waiting I/O contexts.
1761 *
1762 * @returns VBox status code.
1763 * @param pDisk The disk structure.
1764 * @param pIoCtxRc An I/O context handle which waits on the list. When processed
1765 * The status code is returned. NULL if there is no I/O context
1766 * to return the status code for.
1767 */
1768static int vdDiskProcessWaitingIoCtx(PVBOXHDD pDisk, PVDIOCTX pIoCtxRc)
1769{
1770 int rc = VINF_SUCCESS;
1771
1772 LogFlowFunc(("pDisk=%#p pIoCtxRc=%#p\n", pDisk, pIoCtxRc));
1773
1774 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
1775
1776 /* Get the waiting list and process it in FIFO order. */
1777 PVDIOCTX pIoCtxHead = ASMAtomicXchgPtrT(&pDisk->pIoCtxHead, NULL, PVDIOCTX);
1778
1779 /* Reverse it. */
1780 PVDIOCTX pCur = pIoCtxHead;
1781 pIoCtxHead = NULL;
1782 while (pCur)
1783 {
1784 PVDIOCTX pInsert = pCur;
1785 pCur = pCur->pIoCtxNext;
1786 pInsert->pIoCtxNext = pIoCtxHead;
1787 pIoCtxHead = pInsert;
1788 }
1789
1790 /* Process now. */
1791 pCur = pIoCtxHead;
1792 while (pCur)
1793 {
1794 int rcTmp;
1795 PVDIOCTX pTmp = pCur;
1796
1797 pCur = pCur->pIoCtxNext;
1798 pTmp->pIoCtxNext = NULL;
1799
1800 rcTmp = vdIoCtxProcessLocked(pTmp);
1801 if (pTmp == pIoCtxRc)
1802 {
1803 /* The given I/O context was processed, pass the return code to the caller. */
1804 rc = rcTmp;
1805 }
1806 else if ( rcTmp == VINF_VD_ASYNC_IO_FINISHED
1807 && ASMAtomicCmpXchgBool(&pTmp->fComplete, true, false))
1808 {
1809 LogFlowFunc(("Waiting I/O context completed pTmp=%#p\n", pTmp));
1810 vdThreadFinishWrite(pDisk);
1811 pTmp->Type.Root.pfnComplete(pTmp->Type.Root.pvUser1,
1812 pTmp->Type.Root.pvUser2,
1813 pTmp->rcReq);
1814 vdIoCtxFree(pDisk, pTmp);
1815 }
1816 }
1817
1818 LogFlowFunc(("returns rc=%Rrc\n", rc));
1819 return rc;
1820}
1821
1822/**
1823 * Leaves the critical section of the disk processing waiting I/O contexts.
1824 *
1825 * @returns VBox status code.
1826 * @param pDisk The disk to unlock.
1827 * @param pIoCtxRc An I/O context handle which waits on the list. When processed
1828 * The status code is returned. NULL if there is no I/O context
1829 * to return the status code for.
1830 */
1831static int vdDiskCritSectLeave(PVBOXHDD pDisk, PVDIOCTX pIoCtxRc)
1832{
1833 int rc = VINF_SUCCESS;
1834
1835 LogFlowFunc(("pDisk=%#p pIoCtxRc=%#p\n", pDisk, pIoCtxRc));
1836
1837 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
1838
1839 rc = vdDiskProcessWaitingIoCtx(pDisk, pIoCtxRc);
1840 RTCritSectLeave(&pDisk->CritSect);
1841
1842 /*
1843 * We have to check for new waiting contexts here. It is possible that
1844 * another thread has queued another one while process waiting contexts
1845 * and because we still held the lock it was appended to the waiting list.
1846 *
1847 * @note Don't overwrite rc here because this might result in loosing
1848 * the status code of the given I/O context.
1849 */
1850 while (ASMAtomicReadPtrT(&pDisk->pIoCtxHead, PVDIOCTX) != NULL)
1851 {
1852 int rc2 = RTCritSectTryEnter(&pDisk->CritSect);
1853
1854 if (RT_SUCCESS(rc2))
1855 {
1856 /*
1857 * Don't pass status codes for any I/O context here. The context must hae been
1858 * in the first run.
1859 */
1860 vdDiskProcessWaitingIoCtx(pDisk, NULL);
1861 RTCritSectLeave(&pDisk->CritSect);
1862 }
1863 else
1864 {
1865 /*
1866 * Another thread is holding the lock already and will process the list
1867 * whewn leaving the lock, nothing left to do for us.
1868 */
1869 Assert(rc2 == VERR_SEM_BUSY);
1870 break;
1871 }
1872 }
1873
1874 LogFlowFunc(("returns rc=%Rrc\n", rc));
1875 return rc;
1876}
1877
1878/**
1879 * Processes the I/O context trying to lock the criticial section.
1880 * The context is deferred if the critical section is busy.
1881 *
1882 * @returns VBox status code.
1883 * @param pIoCtx The I/O context to process.
1884 */
1885static int vdIoCtxProcessTryLockDefer(PVDIOCTX pIoCtx)
1886{
1887 int rc = VINF_SUCCESS;
1888 PVBOXHDD pDisk = pIoCtx->pDisk;
1889
1890 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1891
1892 /* Put it on the waiting list first. */
1893 PVDIOCTX pNext = ASMAtomicUoReadPtrT(&pDisk->pIoCtxHead, PVDIOCTX);
1894 PVDIOCTX pHeadOld;
1895 pIoCtx->pIoCtxNext = pNext;
1896 while (!ASMAtomicCmpXchgExPtr(&pDisk->pIoCtxHead, pIoCtx, pNext, &pHeadOld))
1897 {
1898 pNext = pHeadOld;
1899 Assert(pNext != pIoCtx);
1900 pIoCtx->pIoCtxNext = pNext;
1901 ASMNopPause();
1902 }
1903
1904 rc = RTCritSectTryEnter(&pDisk->CritSect);
1905 if (RT_SUCCESS(rc))
1906 {
1907 /* Leave it again, the context will be processed just before leaving the lock. */
1908 LogFlowFunc(("Successfully acquired the critical section\n"));
1909 rc = vdDiskCritSectLeave(pDisk, pIoCtx);
1910 }
1911 else
1912 {
1913 AssertMsg(rc == VERR_SEM_BUSY, ("Invalid return code %Rrc\n", rc));
1914 LogFlowFunc(("Critical section is busy\n"));
1915 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1916 }
1917
1918 return rc;
1919}
1920
1921/**
1922 * Wrapper for vdIoCtxProcessLocked() which acquires the lock before.
1923 *
1924 * @returns VBox status code.
1925 * @param pIoCtx I/O context to process.
1926 */
1927static int vdIoCtxProcess(PVDIOCTX pIoCtx)
1928{
1929 int rc = VINF_SUCCESS;
1930 PVBOXHDD pDisk = pIoCtx->pDisk;
1931
1932 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
1933
1934 RTCritSectEnter(&pDisk->CritSect);
1935 rc = vdIoCtxProcessLocked(pIoCtx);
1936 vdDiskCritSectLeave(pDisk, NULL);
1937
1938 return rc;
1939}
1940
1941DECLINLINE(bool) vdIoCtxIsDiskLockOwner(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1942{
1943 return pDisk->fLocked
1944 && pDisk->pIoCtxLockOwner == pIoCtx;
1945}
1946
1947static int vdIoCtxLockDisk(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
1948{
1949 int rc = VINF_SUCCESS;
1950
1951 LogFlowFunc(("pDisk=%#p pIoCtx=%#p\n", pDisk, pIoCtx));
1952
1953 if (!ASMAtomicCmpXchgBool(&pDisk->fLocked, true, false))
1954 {
1955 Assert(pDisk->pIoCtxLockOwner != pIoCtx); /* No nesting allowed. */
1956
1957 rc = vdIoCtxDefer(pDisk, pIoCtx);
1958 if (RT_SUCCESS(rc))
1959 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1960 }
1961 else
1962 {
1963 Assert(!pDisk->pIoCtxLockOwner);
1964 pDisk->pIoCtxLockOwner = pIoCtx;
1965 }
1966
1967 LogFlowFunc(("returns -> %Rrc\n", rc));
1968 return rc;
1969}
1970
1971static void vdIoCtxUnlockDisk(PVBOXHDD pDisk, PVDIOCTX pIoCtx, bool fProcessDeferredReqs)
1972{
1973 LogFlowFunc(("pDisk=%#p pIoCtx=%#p fProcessDeferredReqs=%RTbool\n",
1974 pDisk, pIoCtx, fProcessDeferredReqs));
1975
1976 LogFlow(("Unlocking disk lock owner is %#p\n", pDisk->pIoCtxLockOwner));
1977 Assert(pDisk->fLocked);
1978 Assert(pDisk->pIoCtxLockOwner == pIoCtx);
1979 pDisk->pIoCtxLockOwner = NULL;
1980 ASMAtomicXchgBool(&pDisk->fLocked, false);
1981
1982 if (fProcessDeferredReqs)
1983 {
1984 /* Process any pending writes if the current request didn't caused another growing. */
1985 RTCritSectEnter(&pDisk->CritSect);
1986
1987 if (!RTListIsEmpty(&pDisk->ListWriteLocked))
1988 {
1989 RTLISTNODE ListTmp;
1990
1991 RTListMove(&ListTmp, &pDisk->ListWriteLocked);
1992 vdDiskCritSectLeave(pDisk, NULL);
1993
1994 /* Process the list. */
1995 do
1996 {
1997 int rc;
1998 PVDIOCTXDEFERRED pDeferred = RTListGetFirst(&ListTmp, VDIOCTXDEFERRED, NodeDeferred);
1999 PVDIOCTX pIoCtxWait = pDeferred->pIoCtx;
2000
2001 AssertPtr(pIoCtxWait);
2002
2003 RTListNodeRemove(&pDeferred->NodeDeferred);
2004 RTMemFree(pDeferred);
2005
2006 Assert(!pIoCtxWait->pIoCtxParent);
2007
2008 pIoCtxWait->fFlags &= ~VDIOCTX_FLAGS_BLOCKED;
2009 LogFlowFunc(("Processing waiting I/O context pIoCtxWait=%#p\n", pIoCtxWait));
2010
2011 rc = vdIoCtxProcess(pIoCtxWait);
2012 if ( rc == VINF_VD_ASYNC_IO_FINISHED
2013 && ASMAtomicCmpXchgBool(&pIoCtxWait->fComplete, true, false))
2014 {
2015 LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
2016 vdThreadFinishWrite(pDisk);
2017 pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
2018 pIoCtxWait->Type.Root.pvUser2,
2019 pIoCtxWait->rcReq);
2020 vdIoCtxFree(pDisk, pIoCtxWait);
2021 }
2022 } while (!RTListIsEmpty(&ListTmp));
2023 }
2024 else
2025 vdDiskCritSectLeave(pDisk, NULL);
2026 }
2027
2028 LogFlowFunc(("returns\n"));
2029}
2030
2031/**
2032 * internal: read the specified amount of data in whatever blocks the backend
2033 * will give us - async version.
2034 */
2035static int vdReadHelperAsync(PVDIOCTX pIoCtx)
2036{
2037 int rc;
2038 size_t cbToRead = pIoCtx->Req.Io.cbTransfer;
2039 uint64_t uOffset = pIoCtx->Req.Io.uOffset;
2040 PVDIMAGE pCurrImage = pIoCtx->Req.Io.pImageCur;;
2041 size_t cbThisRead;
2042
2043 /* Loop until all reads started or we have a backend which needs to read metadata. */
2044 do
2045 {
2046 /* Search for image with allocated block. Do not attempt to read more
2047 * than the previous reads marked as valid. Otherwise this would return
2048 * stale data when different block sizes are used for the images. */
2049 cbThisRead = cbToRead;
2050
2051 /*
2052 * Try to read from the given image.
2053 * If the block is not allocated read from override chain if present.
2054 */
2055 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
2056 uOffset, cbThisRead,
2057 pIoCtx, &cbThisRead);
2058
2059 if (rc == VERR_VD_BLOCK_FREE)
2060 {
2061 while ( pCurrImage->pPrev != NULL
2062 && rc == VERR_VD_BLOCK_FREE)
2063 {
2064 pCurrImage = pCurrImage->pPrev;
2065 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
2066 uOffset, cbThisRead,
2067 pIoCtx, &cbThisRead);
2068 }
2069 }
2070
2071 /* The task state will be updated on success already, don't do it here!. */
2072 if (rc == VERR_VD_BLOCK_FREE)
2073 {
2074 /* No image in the chain contains the data for the block. */
2075 vdIoCtxSet(pIoCtx, '\0', cbThisRead);
2076 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbThisRead);
2077 rc = VINF_SUCCESS;
2078 }
2079 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2080 rc = VINF_SUCCESS;
2081 else if (rc == VERR_VD_IOCTX_HALT)
2082 {
2083 uOffset += cbThisRead;
2084 cbToRead -= cbThisRead;
2085 pIoCtx->fFlags |= VDIOCTX_FLAGS_BLOCKED;
2086 }
2087
2088 if (RT_FAILURE(rc))
2089 break;
2090
2091 cbToRead -= cbThisRead;
2092 uOffset += cbThisRead;
2093 pCurrImage = pIoCtx->Req.Io.pImageStart; /* Start with the highest image in the chain. */
2094 } while (cbToRead != 0 && RT_SUCCESS(rc));
2095
2096 if ( rc == VERR_VD_NOT_ENOUGH_METADATA
2097 || rc == VERR_VD_IOCTX_HALT)
2098 {
2099 /* Save the current state. */
2100 pIoCtx->Req.Io.uOffset = uOffset;
2101 pIoCtx->Req.Io.cbTransfer = cbToRead;
2102 pIoCtx->Req.Io.pImageCur = pCurrImage ? pCurrImage : pIoCtx->Req.Io.pImageStart;
2103 }
2104
2105 return rc;
2106}
2107
2108/**
2109 * internal: parent image read wrapper for compacting.
2110 */
2111static int vdParentRead(void *pvUser, uint64_t uOffset, void *pvBuf,
2112 size_t cbRead)
2113{
2114 PVDPARENTSTATEDESC pParentState = (PVDPARENTSTATEDESC)pvUser;
2115 return vdReadHelper(pParentState->pDisk, pParentState->pImage, uOffset,
2116 pvBuf, cbRead, false /* fUpdateCache */);
2117}
2118
2119/**
2120 * internal: mark the disk as not modified.
2121 */
2122static void vdResetModifiedFlag(PVBOXHDD pDisk)
2123{
2124 if (pDisk->uModified & VD_IMAGE_MODIFIED_FLAG)
2125 {
2126 /* generate new last-modified uuid */
2127 if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
2128 {
2129 RTUUID Uuid;
2130
2131 RTUuidCreate(&Uuid);
2132 pDisk->pLast->Backend->pfnSetModificationUuid(pDisk->pLast->pBackendData,
2133 &Uuid);
2134
2135 if (pDisk->pCache)
2136 pDisk->pCache->Backend->pfnSetModificationUuid(pDisk->pCache->pBackendData,
2137 &Uuid);
2138 }
2139
2140 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FLAG;
2141 }
2142}
2143
2144/**
2145 * internal: mark the disk as modified.
2146 */
2147static void vdSetModifiedFlag(PVBOXHDD pDisk)
2148{
2149 pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
2150 if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
2151 {
2152 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
2153
2154 /* First modify, so create a UUID and ensure it's written to disk. */
2155 vdResetModifiedFlag(pDisk);
2156
2157 if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
2158 {
2159 VDIOCTX IoCtx;
2160 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_FLUSH, 0, 0, NULL,
2161 NULL, NULL, NULL, VDIOCTX_FLAGS_SYNC);
2162 pDisk->pLast->Backend->pfnFlush(pDisk->pLast->pBackendData, &IoCtx);
2163 }
2164 }
2165}
2166
2167/**
2168 * internal: write a complete block (only used for diff images), taking the
2169 * remaining data from parent images. This implementation does not optimize
2170 * anything (except that it tries to read only that portions from parent
2171 * images that are really needed).
2172 */
2173static int vdWriteHelperStandard(PVBOXHDD pDisk, PVDIMAGE pImage,
2174 PVDIMAGE pImageParentOverride,
2175 uint64_t uOffset, size_t cbWrite,
2176 size_t cbThisWrite, size_t cbPreRead,
2177 size_t cbPostRead, const void *pvBuf,
2178 void *pvTmp)
2179{
2180 int rc = VINF_SUCCESS;
2181
2182 /* Read the data that goes before the write to fill the block. */
2183 if (cbPreRead)
2184 {
2185 /*
2186 * Updating the cache doesn't make sense here because
2187 * this will be done after the complete block was written.
2188 */
2189 rc = vdReadHelperEx(pDisk, pImage, pImageParentOverride,
2190 uOffset - cbPreRead, pvTmp, cbPreRead,
2191 true /* fZeroFreeBlocks*/,
2192 false /* fUpdateCache */, 0);
2193 if (RT_FAILURE(rc))
2194 return rc;
2195 }
2196
2197 /* Copy the data to the right place in the buffer. */
2198 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
2199
2200 /* Read the data that goes after the write to fill the block. */
2201 if (cbPostRead)
2202 {
2203 /* If we have data to be written, use that instead of reading
2204 * data from the image. */
2205 size_t cbWriteCopy;
2206 if (cbWrite > cbThisWrite)
2207 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
2208 else
2209 cbWriteCopy = 0;
2210 /* Figure out how much we cannot read from the image, because
2211 * the last block to write might exceed the nominal size of the
2212 * image for technical reasons. */
2213 size_t cbFill;
2214 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
2215 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
2216 else
2217 cbFill = 0;
2218 /* The rest must be read from the image. */
2219 size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
2220
2221 /* Now assemble the remaining data. */
2222 if (cbWriteCopy)
2223 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
2224 (char *)pvBuf + cbThisWrite, cbWriteCopy);
2225 if (cbReadImage)
2226 rc = vdReadHelperEx(pDisk, pImage, pImageParentOverride,
2227 uOffset + cbThisWrite + cbWriteCopy,
2228 (char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy,
2229 cbReadImage, true /* fZeroFreeBlocks */,
2230 false /* fUpdateCache */, 0);
2231 if (RT_FAILURE(rc))
2232 return rc;
2233 /* Zero out the remainder of this block. Will never be visible, as this
2234 * is beyond the limit of the image. */
2235 if (cbFill)
2236 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
2237 '\0', cbFill);
2238 }
2239
2240 /* Write the full block to the virtual disk. */
2241 RTSGSEG SegmentBuf;
2242 RTSGBUF SgBuf;
2243 VDIOCTX IoCtx;
2244
2245 SegmentBuf.pvSeg = pvTmp;
2246 SegmentBuf.cbSeg = cbPreRead + cbThisWrite + cbPostRead;
2247 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
2248 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_WRITE, 0, 0, NULL,
2249 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
2250 rc = pImage->Backend->pfnWrite(pImage->pBackendData, uOffset - cbPreRead,
2251 cbPreRead + cbThisWrite + cbPostRead,
2252 &IoCtx, NULL, &cbPreRead, &cbPostRead, 0);
2253 Assert(rc != VERR_VD_BLOCK_FREE);
2254 Assert(cbPreRead == 0);
2255 Assert(cbPostRead == 0);
2256
2257 return rc;
2258}
2259
2260/**
2261 * internal: write a complete block (only used for diff images), taking the
2262 * remaining data from parent images. This implementation optimizes out writes
2263 * that do not change the data relative to the state as of the parent images.
2264 * All backends which support differential/growing images support this.
2265 */
2266static int vdWriteHelperOptimized(PVBOXHDD pDisk, PVDIMAGE pImage,
2267 PVDIMAGE pImageParentOverride,
2268 uint64_t uOffset, size_t cbWrite,
2269 size_t cbThisWrite, size_t cbPreRead,
2270 size_t cbPostRead, const void *pvBuf,
2271 void *pvTmp, unsigned cImagesRead)
2272{
2273 size_t cbFill = 0;
2274 size_t cbWriteCopy = 0;
2275 size_t cbReadImage = 0;
2276 int rc;
2277
2278 if (cbPostRead)
2279 {
2280 /* Figure out how much we cannot read from the image, because
2281 * the last block to write might exceed the nominal size of the
2282 * image for technical reasons. */
2283 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
2284 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
2285
2286 /* If we have data to be written, use that instead of reading
2287 * data from the image. */
2288 if (cbWrite > cbThisWrite)
2289 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
2290
2291 /* The rest must be read from the image. */
2292 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
2293 }
2294
2295 /* Read the entire data of the block so that we can compare whether it will
2296 * be modified by the write or not. */
2297 rc = vdReadHelperEx(pDisk, pImage, pImageParentOverride, uOffset - cbPreRead, pvTmp,
2298 cbPreRead + cbThisWrite + cbPostRead - cbFill,
2299 true /* fZeroFreeBlocks */, false /* fUpdateCache */,
2300 cImagesRead);
2301 if (RT_FAILURE(rc))
2302 return rc;
2303
2304 /* Check if the write would modify anything in this block. */
2305 if ( !memcmp((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite)
2306 && (!cbWriteCopy || !memcmp((char *)pvTmp + cbPreRead + cbThisWrite,
2307 (char *)pvBuf + cbThisWrite, cbWriteCopy)))
2308 {
2309 /* Block is completely unchanged, so no need to write anything. */
2310 return VINF_SUCCESS;
2311 }
2312
2313 /* Copy the data to the right place in the buffer. */
2314 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
2315
2316 /* Handle the data that goes after the write to fill the block. */
2317 if (cbPostRead)
2318 {
2319 /* Now assemble the remaining data. */
2320 if (cbWriteCopy)
2321 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
2322 (char *)pvBuf + cbThisWrite, cbWriteCopy);
2323 /* Zero out the remainder of this block. Will never be visible, as this
2324 * is beyond the limit of the image. */
2325 if (cbFill)
2326 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
2327 '\0', cbFill);
2328 }
2329
2330 /* Write the full block to the virtual disk. */
2331 RTSGSEG SegmentBuf;
2332 RTSGBUF SgBuf;
2333 VDIOCTX IoCtx;
2334
2335 SegmentBuf.pvSeg = pvTmp;
2336 SegmentBuf.cbSeg = cbPreRead + cbThisWrite + cbPostRead;
2337 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
2338 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_WRITE, 0, 0, NULL,
2339 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
2340 rc = pImage->Backend->pfnWrite(pImage->pBackendData, uOffset - cbPreRead,
2341 cbPreRead + cbThisWrite + cbPostRead,
2342 &IoCtx, NULL, &cbPreRead, &cbPostRead, 0);
2343 Assert(rc != VERR_VD_BLOCK_FREE);
2344 Assert(cbPreRead == 0);
2345 Assert(cbPostRead == 0);
2346
2347 return rc;
2348}
2349
2350/**
2351 * internal: write buffer to the image, taking care of block boundaries and
2352 * write optimizations.
2353 */
2354static int vdWriteHelperEx(PVBOXHDD pDisk, PVDIMAGE pImage,
2355 PVDIMAGE pImageParentOverride, uint64_t uOffset,
2356 const void *pvBuf, size_t cbWrite,
2357 bool fUpdateCache, unsigned cImagesRead)
2358{
2359 int rc;
2360 unsigned fWrite;
2361 size_t cbThisWrite;
2362 size_t cbPreRead, cbPostRead;
2363 uint64_t uOffsetCur = uOffset;
2364 size_t cbWriteCur = cbWrite;
2365 const void *pcvBufCur = pvBuf;
2366 RTSGSEG SegmentBuf;
2367 RTSGBUF SgBuf;
2368 VDIOCTX IoCtx;
2369
2370 /* Loop until all written. */
2371 do
2372 {
2373 /* Try to write the possibly partial block to the last opened image.
2374 * This works when the block is already allocated in this image or
2375 * if it is a full-block write (and allocation isn't suppressed below).
2376 * For image formats which don't support zero blocks, it's beneficial
2377 * to avoid unnecessarily allocating unchanged blocks. This prevents
2378 * unwanted expanding of images. VMDK is an example. */
2379 cbThisWrite = cbWriteCur;
2380 fWrite = (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
2381 ? 0 : VD_WRITE_NO_ALLOC;
2382
2383 SegmentBuf.pvSeg = (void *)pcvBufCur;
2384 SegmentBuf.cbSeg = cbWrite;
2385 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
2386 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_WRITE, 0, 0, NULL,
2387 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
2388 rc = pImage->Backend->pfnWrite(pImage->pBackendData, uOffsetCur, cbThisWrite,
2389 &IoCtx, &cbThisWrite, &cbPreRead,
2390 &cbPostRead, fWrite);
2391 if (rc == VERR_VD_BLOCK_FREE)
2392 {
2393 void *pvTmp = RTMemTmpAlloc(cbPreRead + cbThisWrite + cbPostRead);
2394 AssertBreakStmt(VALID_PTR(pvTmp), rc = VERR_NO_MEMORY);
2395
2396 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME))
2397 {
2398 /* Optimized write, suppress writing to a so far unallocated
2399 * block if the data is in fact not changed. */
2400 rc = vdWriteHelperOptimized(pDisk, pImage, pImageParentOverride,
2401 uOffsetCur, cbWriteCur,
2402 cbThisWrite, cbPreRead, cbPostRead,
2403 pcvBufCur, pvTmp, cImagesRead);
2404 }
2405 else
2406 {
2407 /* Normal write, not optimized in any way. The block will
2408 * be written no matter what. This will usually (unless the
2409 * backend has some further optimization enabled) cause the
2410 * block to be allocated. */
2411 rc = vdWriteHelperStandard(pDisk, pImage, pImageParentOverride,
2412 uOffsetCur, cbWriteCur,
2413 cbThisWrite, cbPreRead, cbPostRead,
2414 pcvBufCur, pvTmp);
2415 }
2416 RTMemTmpFree(pvTmp);
2417 if (RT_FAILURE(rc))
2418 break;
2419 }
2420
2421 cbWriteCur -= cbThisWrite;
2422 uOffsetCur += cbThisWrite;
2423 pcvBufCur = (char *)pcvBufCur + cbThisWrite;
2424 } while (cbWriteCur != 0 && RT_SUCCESS(rc));
2425
2426 /* Update the cache on success */
2427 if ( RT_SUCCESS(rc)
2428 && pDisk->pCache
2429 && fUpdateCache)
2430 rc = vdCacheWriteHelper(pDisk->pCache, uOffset, pvBuf, cbWrite, NULL);
2431
2432 if (RT_SUCCESS(rc))
2433 rc = vdDiscardSetRangeAllocated(pDisk, uOffset, cbWrite);
2434
2435 return rc;
2436}
2437
2438/**
2439 * internal: write buffer to the image, taking care of block boundaries and
2440 * write optimizations.
2441 */
2442static int vdWriteHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
2443 const void *pvBuf, size_t cbWrite, bool fUpdateCache)
2444{
2445 return vdWriteHelperEx(pDisk, pImage, NULL, uOffset, pvBuf, cbWrite,
2446 fUpdateCache, 0);
2447}
2448
2449/**
2450 * Internal: Copies the content of one disk to another one applying optimizations
2451 * to speed up the copy process if possible.
2452 */
2453static int vdCopyHelper(PVBOXHDD pDiskFrom, PVDIMAGE pImageFrom, PVBOXHDD pDiskTo,
2454 uint64_t cbSize, unsigned cImagesFromRead, unsigned cImagesToRead,
2455 bool fSuppressRedundantIo, PVDINTERFACEPROGRESS pIfProgress,
2456 PVDINTERFACEPROGRESS pDstIfProgress)
2457{
2458 int rc = VINF_SUCCESS;
2459 int rc2;
2460 uint64_t uOffset = 0;
2461 uint64_t cbRemaining = cbSize;
2462 void *pvBuf = NULL;
2463 bool fLockReadFrom = false;
2464 bool fLockWriteTo = false;
2465 bool fBlockwiseCopy = fSuppressRedundantIo || (cImagesFromRead > 0);
2466 unsigned uProgressOld = 0;
2467
2468 LogFlowFunc(("pDiskFrom=%#p pImageFrom=%#p pDiskTo=%#p cbSize=%llu cImagesFromRead=%u cImagesToRead=%u fSuppressRedundantIo=%RTbool pIfProgress=%#p pDstIfProgress=%#p\n",
2469 pDiskFrom, pImageFrom, pDiskTo, cbSize, cImagesFromRead, cImagesToRead, fSuppressRedundantIo, pDstIfProgress, pDstIfProgress));
2470
2471 /* Allocate tmp buffer. */
2472 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
2473 if (!pvBuf)
2474 return rc;
2475
2476 do
2477 {
2478 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
2479
2480 /* Note that we don't attempt to synchronize cross-disk accesses.
2481 * It wouldn't be very difficult to do, just the lock order would
2482 * need to be defined somehow to prevent deadlocks. Postpone such
2483 * magic as there is no use case for this. */
2484
2485 rc2 = vdThreadStartRead(pDiskFrom);
2486 AssertRC(rc2);
2487 fLockReadFrom = true;
2488
2489 if (fBlockwiseCopy)
2490 {
2491 RTSGSEG SegmentBuf;
2492 RTSGBUF SgBuf;
2493 VDIOCTX IoCtx;
2494
2495 SegmentBuf.pvSeg = pvBuf;
2496 SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
2497 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
2498 vdIoCtxInit(&IoCtx, pDiskFrom, VDIOCTXTXDIR_READ, 0, 0, NULL,
2499 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
2500
2501 /* Read the source data. */
2502 rc = pImageFrom->Backend->pfnRead(pImageFrom->pBackendData,
2503 uOffset, cbThisRead, &IoCtx,
2504 &cbThisRead);
2505
2506 if ( rc == VERR_VD_BLOCK_FREE
2507 && cImagesFromRead != 1)
2508 {
2509 unsigned cImagesToProcess = cImagesFromRead;
2510
2511 for (PVDIMAGE pCurrImage = pImageFrom->pPrev;
2512 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
2513 pCurrImage = pCurrImage->pPrev)
2514 {
2515 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
2516 uOffset, cbThisRead,
2517 &IoCtx, &cbThisRead);
2518 if (cImagesToProcess == 1)
2519 break;
2520 else if (cImagesToProcess > 0)
2521 cImagesToProcess--;
2522 }
2523 }
2524 }
2525 else
2526 rc = vdReadHelper(pDiskFrom, pImageFrom, uOffset, pvBuf, cbThisRead,
2527 false /* fUpdateCache */);
2528
2529 if (RT_FAILURE(rc) && rc != VERR_VD_BLOCK_FREE)
2530 break;
2531
2532 rc2 = vdThreadFinishRead(pDiskFrom);
2533 AssertRC(rc2);
2534 fLockReadFrom = false;
2535
2536 if (rc != VERR_VD_BLOCK_FREE)
2537 {
2538 rc2 = vdThreadStartWrite(pDiskTo);
2539 AssertRC(rc2);
2540 fLockWriteTo = true;
2541
2542 /* Only do collapsed I/O if we are copying the data blockwise. */
2543 rc = vdWriteHelperEx(pDiskTo, pDiskTo->pLast, NULL, uOffset, pvBuf,
2544 cbThisRead, false /* fUpdateCache */,
2545 fBlockwiseCopy ? cImagesToRead : 0);
2546 if (RT_FAILURE(rc))
2547 break;
2548
2549 rc2 = vdThreadFinishWrite(pDiskTo);
2550 AssertRC(rc2);
2551 fLockWriteTo = false;
2552 }
2553 else /* Don't propagate the error to the outside */
2554 rc = VINF_SUCCESS;
2555
2556 uOffset += cbThisRead;
2557 cbRemaining -= cbThisRead;
2558
2559 unsigned uProgressNew = uOffset * 99 / cbSize;
2560 if (uProgressNew != uProgressOld)
2561 {
2562 uProgressOld = uProgressNew;
2563
2564 if (pIfProgress && pIfProgress->pfnProgress)
2565 {
2566 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2567 uProgressOld);
2568 if (RT_FAILURE(rc))
2569 break;
2570 }
2571 if (pDstIfProgress && pDstIfProgress->pfnProgress)
2572 {
2573 rc = pDstIfProgress->pfnProgress(pDstIfProgress->Core.pvUser,
2574 uProgressOld);
2575 if (RT_FAILURE(rc))
2576 break;
2577 }
2578 }
2579 } while (uOffset < cbSize);
2580
2581 RTMemFree(pvBuf);
2582
2583 if (fLockReadFrom)
2584 {
2585 rc2 = vdThreadFinishRead(pDiskFrom);
2586 AssertRC(rc2);
2587 }
2588
2589 if (fLockWriteTo)
2590 {
2591 rc2 = vdThreadFinishWrite(pDiskTo);
2592 AssertRC(rc2);
2593 }
2594
2595 LogFlowFunc(("returns rc=%Rrc\n", rc));
2596 return rc;
2597}
2598
2599/**
2600 * Flush helper async version.
2601 */
2602static int vdSetModifiedHelperAsync(PVDIOCTX pIoCtx)
2603{
2604 int rc = VINF_SUCCESS;
2605 PVBOXHDD pDisk = pIoCtx->pDisk;
2606 PVDIMAGE pImage = pIoCtx->Req.Io.pImageCur;
2607
2608 rc = pImage->Backend->pfnFlush(pImage->pBackendData, pIoCtx);
2609 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2610 rc = VINF_SUCCESS;
2611
2612 return rc;
2613}
2614
2615/**
2616 * internal: mark the disk as modified - async version.
2617 */
2618static int vdSetModifiedFlagAsync(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
2619{
2620 int rc = VINF_SUCCESS;
2621
2622 pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
2623 if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
2624 {
2625 rc = vdIoCtxLockDisk(pDisk, pIoCtx);
2626 if (RT_SUCCESS(rc))
2627 {
2628 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
2629
2630 /* First modify, so create a UUID and ensure it's written to disk. */
2631 vdResetModifiedFlag(pDisk);
2632
2633 if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
2634 {
2635 PVDIOCTX pIoCtxFlush = vdIoCtxChildAlloc(pDisk, VDIOCTXTXDIR_FLUSH,
2636 0, 0, pDisk->pLast,
2637 NULL, pIoCtx, 0, 0, NULL,
2638 vdSetModifiedHelperAsync);
2639
2640 if (pIoCtxFlush)
2641 {
2642 rc = vdIoCtxProcess(pIoCtxFlush);
2643 if (rc == VINF_VD_ASYNC_IO_FINISHED)
2644 {
2645 vdIoCtxUnlockDisk(pDisk, pIoCtx, false /* fProcessDeferredReqs */);
2646 vdIoCtxFree(pDisk, pIoCtxFlush);
2647 }
2648 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2649 {
2650 ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
2651 pIoCtx->fFlags |= VDIOCTX_FLAGS_BLOCKED;
2652 }
2653 else /* Another error */
2654 vdIoCtxFree(pDisk, pIoCtxFlush);
2655 }
2656 else
2657 rc = VERR_NO_MEMORY;
2658 }
2659 }
2660 }
2661
2662 return rc;
2663}
2664
2665/**
2666 * internal: write a complete block (only used for diff images), taking the
2667 * remaining data from parent images. This implementation does not optimize
2668 * anything (except that it tries to read only that portions from parent
2669 * images that are really needed) - async version.
2670 */
2671static int vdWriteHelperStandardAsync(PVDIOCTX pIoCtx)
2672{
2673 int rc = VINF_SUCCESS;
2674
2675#if 0
2676
2677 /* Read the data that goes before the write to fill the block. */
2678 if (cbPreRead)
2679 {
2680 rc = vdReadHelperAsync(pIoCtxDst);
2681 if (RT_FAILURE(rc))
2682 return rc;
2683 }
2684
2685 /* Copy the data to the right place in the buffer. */
2686 vdIoCtxCopy(pIoCtxDst, pIoCtxSrc, cbThisWrite);
2687
2688 /* Read the data that goes after the write to fill the block. */
2689 if (cbPostRead)
2690 {
2691 /* If we have data to be written, use that instead of reading
2692 * data from the image. */
2693 size_t cbWriteCopy;
2694 if (cbWrite > cbThisWrite)
2695 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
2696 else
2697 cbWriteCopy = 0;
2698 /* Figure out how much we cannot read from the image, because
2699 * the last block to write might exceed the nominal size of the
2700 * image for technical reasons. */
2701 size_t cbFill;
2702 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
2703 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
2704 else
2705 cbFill = 0;
2706 /* The rest must be read from the image. */
2707 size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
2708
2709 /* Now assemble the remaining data. */
2710 if (cbWriteCopy)
2711 {
2712 vdIoCtxCopy(pIoCtxDst, pIoCtxSrc, cbWriteCopy);
2713 ASMAtomicSubU32(&pIoCtxDst->cbTransferLeft, cbWriteCopy);
2714 }
2715
2716 if (cbReadImage)
2717 rc = vdReadHelperAsync(pDisk, pImage, pImageParentOverride, pIoCtxDst,
2718 uOffset + cbThisWrite + cbWriteCopy,
2719 cbReadImage);
2720 if (RT_FAILURE(rc))
2721 return rc;
2722 /* Zero out the remainder of this block. Will never be visible, as this
2723 * is beyond the limit of the image. */
2724 if (cbFill)
2725 {
2726 vdIoCtxSet(pIoCtxDst, '\0', cbFill);
2727 ASMAtomicSubU32(&pIoCtxDst->cbTransferLeft, cbFill);
2728 }
2729 }
2730
2731 if ( !pIoCtxDst->cbTransferLeft
2732 && !pIoCtxDst->cMetaTransfersPending
2733 && ASMAtomicCmpXchgBool(&pIoCtxDst->fComplete, true, false))
2734 {
2735 /* Write the full block to the virtual disk. */
2736 vdIoCtxChildReset(pIoCtxDst);
2737 rc = pImage->Backend->pfnWrite(pImage->pBackendData,
2738 uOffset - cbPreRead,
2739 cbPreRead + cbThisWrite + cbPostRead,
2740 pIoCtxDst,
2741 NULL, &cbPreRead, &cbPostRead, 0);
2742 Assert(rc != VERR_VD_BLOCK_FREE);
2743 Assert(cbPreRead == 0);
2744 Assert(cbPostRead == 0);
2745 }
2746 else
2747 {
2748 LogFlow(("cbTransferLeft=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
2749 pIoCtxDst->cbTransferLeft, pIoCtxDst->cMetaTransfersPending,
2750 pIoCtxDst->fComplete));
2751 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2752 }
2753
2754 return rc;
2755#endif
2756 return VERR_NOT_IMPLEMENTED;
2757}
2758
2759static int vdWriteHelperOptimizedCommitAsync(PVDIOCTX pIoCtx)
2760{
2761 int rc = VINF_SUCCESS;
2762 PVDIMAGE pImage = pIoCtx->Req.Io.pImageStart;
2763 size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
2764 size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
2765 size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
2766
2767 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
2768 rc = pImage->Backend->pfnWrite(pImage->pBackendData,
2769 pIoCtx->Req.Io.uOffset - cbPreRead,
2770 cbPreRead + cbThisWrite + cbPostRead,
2771 pIoCtx, NULL, &cbPreRead, &cbPostRead, 0);
2772 Assert(rc != VERR_VD_BLOCK_FREE);
2773 Assert(rc == VERR_VD_NOT_ENOUGH_METADATA || cbPreRead == 0);
2774 Assert(rc == VERR_VD_NOT_ENOUGH_METADATA || cbPostRead == 0);
2775 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2776 rc = VINF_SUCCESS;
2777 else if (rc == VERR_VD_IOCTX_HALT)
2778 {
2779 pIoCtx->fFlags |= VDIOCTX_FLAGS_BLOCKED;
2780 rc = VINF_SUCCESS;
2781 }
2782
2783 LogFlowFunc(("returns rc=%Rrc\n", rc));
2784 return rc;
2785}
2786
2787static int vdWriteHelperOptimizedCmpAndWriteAsync(PVDIOCTX pIoCtx)
2788{
2789 int rc = VINF_SUCCESS;
2790 PVDIMAGE pImage = pIoCtx->Req.Io.pImageCur;
2791 size_t cbThisWrite = 0;
2792 size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
2793 size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
2794 size_t cbWriteCopy = pIoCtx->Type.Child.Write.Optimized.cbWriteCopy;
2795 size_t cbFill = pIoCtx->Type.Child.Write.Optimized.cbFill;
2796 size_t cbReadImage = pIoCtx->Type.Child.Write.Optimized.cbReadImage;
2797 PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
2798
2799 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
2800
2801 AssertPtr(pIoCtxParent);
2802 Assert(!pIoCtxParent->pIoCtxParent);
2803 Assert(!pIoCtx->Req.Io.cbTransferLeft && !pIoCtx->cMetaTransfersPending);
2804
2805 vdIoCtxChildReset(pIoCtx);
2806 cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
2807 RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbPreRead);
2808
2809 /* Check if the write would modify anything in this block. */
2810 if (!RTSgBufCmp(&pIoCtx->Req.Io.SgBuf, &pIoCtxParent->Req.Io.SgBuf, cbThisWrite))
2811 {
2812 RTSGBUF SgBufSrcTmp;
2813
2814 RTSgBufClone(&SgBufSrcTmp, &pIoCtxParent->Req.Io.SgBuf);
2815 RTSgBufAdvance(&SgBufSrcTmp, cbThisWrite);
2816 RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbThisWrite);
2817
2818 if (!cbWriteCopy || !RTSgBufCmp(&pIoCtx->Req.Io.SgBuf, &SgBufSrcTmp, cbWriteCopy))
2819 {
2820 /* Block is completely unchanged, so no need to write anything. */
2821 LogFlowFunc(("Block didn't changed\n"));
2822 ASMAtomicWriteU32(&pIoCtx->Req.Io.cbTransferLeft, 0);
2823 RTSgBufAdvance(&pIoCtxParent->Req.Io.SgBuf, cbThisWrite);
2824 return VINF_VD_ASYNC_IO_FINISHED;
2825 }
2826 }
2827
2828 /* Copy the data to the right place in the buffer. */
2829 RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
2830 RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbPreRead);
2831 vdIoCtxCopy(pIoCtx, pIoCtxParent, cbThisWrite);
2832
2833 /* Handle the data that goes after the write to fill the block. */
2834 if (cbPostRead)
2835 {
2836 /* Now assemble the remaining data. */
2837 if (cbWriteCopy)
2838 {
2839 /*
2840 * The S/G buffer of the parent needs to be cloned because
2841 * it is not allowed to modify the state.
2842 */
2843 RTSGBUF SgBufParentTmp;
2844
2845 RTSgBufClone(&SgBufParentTmp, &pIoCtxParent->Req.Io.SgBuf);
2846 RTSgBufCopy(&pIoCtx->Req.Io.SgBuf, &SgBufParentTmp, cbWriteCopy);
2847 }
2848
2849 /* Zero out the remainder of this block. Will never be visible, as this
2850 * is beyond the limit of the image. */
2851 if (cbFill)
2852 {
2853 RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbReadImage);
2854 vdIoCtxSet(pIoCtx, '\0', cbFill);
2855 }
2856 }
2857
2858 /* Write the full block to the virtual disk. */
2859 RTSgBufReset(&pIoCtx->Req.Io.SgBuf);
2860 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedCommitAsync;
2861
2862 return rc;
2863}
2864
2865static int vdWriteHelperOptimizedPreReadAsync(PVDIOCTX pIoCtx)
2866{
2867 int rc = VINF_SUCCESS;
2868
2869 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
2870
2871 if (pIoCtx->Req.Io.cbTransferLeft)
2872 rc = vdReadHelperAsync(pIoCtx);
2873
2874 if ( RT_SUCCESS(rc)
2875 && ( pIoCtx->Req.Io.cbTransferLeft
2876 || pIoCtx->cMetaTransfersPending))
2877 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2878 else
2879 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedCmpAndWriteAsync;
2880
2881 return rc;
2882}
2883
2884/**
2885 * internal: write a complete block (only used for diff images), taking the
2886 * remaining data from parent images. This implementation optimizes out writes
2887 * that do not change the data relative to the state as of the parent images.
2888 * All backends which support differential/growing images support this - async version.
2889 */
2890static int vdWriteHelperOptimizedAsync(PVDIOCTX pIoCtx)
2891{
2892 PVBOXHDD pDisk = pIoCtx->pDisk;
2893 uint64_t uOffset = pIoCtx->Type.Child.uOffsetSaved;
2894 size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
2895 size_t cbPreRead = pIoCtx->Type.Child.cbPreRead;
2896 size_t cbPostRead = pIoCtx->Type.Child.cbPostRead;
2897 size_t cbWrite = pIoCtx->Type.Child.cbWriteParent;
2898 size_t cbFill = 0;
2899 size_t cbWriteCopy = 0;
2900 size_t cbReadImage = 0;
2901
2902 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
2903
2904 AssertPtr(pIoCtx->pIoCtxParent);
2905 Assert(!pIoCtx->pIoCtxParent->pIoCtxParent);
2906
2907 if (cbPostRead)
2908 {
2909 /* Figure out how much we cannot read from the image, because
2910 * the last block to write might exceed the nominal size of the
2911 * image for technical reasons. */
2912 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
2913 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
2914
2915 /* If we have data to be written, use that instead of reading
2916 * data from the image. */
2917 if (cbWrite > cbThisWrite)
2918 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
2919
2920 /* The rest must be read from the image. */
2921 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
2922 }
2923
2924 pIoCtx->Type.Child.Write.Optimized.cbFill = cbFill;
2925 pIoCtx->Type.Child.Write.Optimized.cbWriteCopy = cbWriteCopy;
2926 pIoCtx->Type.Child.Write.Optimized.cbReadImage = cbReadImage;
2927
2928 /* Read the entire data of the block so that we can compare whether it will
2929 * be modified by the write or not. */
2930 pIoCtx->Req.Io.cbTransferLeft = cbPreRead + cbThisWrite + cbPostRead - cbFill;
2931 pIoCtx->Req.Io.cbTransfer = pIoCtx->Req.Io.cbTransferLeft;
2932 pIoCtx->Req.Io.uOffset -= cbPreRead;
2933
2934 /* Next step */
2935 pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedPreReadAsync;
2936 return VINF_SUCCESS;
2937}
2938
2939/**
2940 * internal: write buffer to the image, taking care of block boundaries and
2941 * write optimizations - async version.
2942 */
2943static int vdWriteHelperAsync(PVDIOCTX pIoCtx)
2944{
2945 int rc;
2946 size_t cbWrite = pIoCtx->Req.Io.cbTransfer;
2947 uint64_t uOffset = pIoCtx->Req.Io.uOffset;
2948 PVDIMAGE pImage = pIoCtx->Req.Io.pImageCur;
2949 PVBOXHDD pDisk = pIoCtx->pDisk;
2950 unsigned fWrite;
2951 size_t cbThisWrite;
2952 size_t cbPreRead, cbPostRead;
2953
2954 rc = vdSetModifiedFlagAsync(pDisk, pIoCtx);
2955 if (RT_FAILURE(rc)) /* Includes I/O in progress. */
2956 return rc;
2957
2958 rc = vdDiscardSetRangeAllocated(pDisk, uOffset, cbWrite);
2959 if (RT_FAILURE(rc))
2960 return rc;
2961
2962 /* Loop until all written. */
2963 do
2964 {
2965 /* Try to write the possibly partial block to the last opened image.
2966 * This works when the block is already allocated in this image or
2967 * if it is a full-block write (and allocation isn't suppressed below).
2968 * For image formats which don't support zero blocks, it's beneficial
2969 * to avoid unnecessarily allocating unchanged blocks. This prevents
2970 * unwanted expanding of images. VMDK is an example. */
2971 cbThisWrite = cbWrite;
2972 fWrite = (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
2973 ? 0 : VD_WRITE_NO_ALLOC;
2974 rc = pImage->Backend->pfnWrite(pImage->pBackendData, uOffset,
2975 cbThisWrite, pIoCtx,
2976 &cbThisWrite, &cbPreRead,
2977 &cbPostRead, fWrite);
2978 if (rc == VERR_VD_BLOCK_FREE)
2979 {
2980 /* Lock the disk .*/
2981 rc = vdIoCtxLockDisk(pDisk, pIoCtx);
2982 if (RT_SUCCESS(rc))
2983 {
2984 /*
2985 * Allocate segment and buffer in one go.
2986 * A bit hackish but avoids the need to allocate memory twice.
2987 */
2988 PRTSGBUF pTmp = (PRTSGBUF)RTMemAlloc(cbPreRead + cbThisWrite + cbPostRead + sizeof(RTSGSEG) + sizeof(RTSGBUF));
2989 AssertBreakStmt(VALID_PTR(pTmp), rc = VERR_NO_MEMORY);
2990 PRTSGSEG pSeg = (PRTSGSEG)(pTmp + 1);
2991
2992 pSeg->pvSeg = pSeg + 1;
2993 pSeg->cbSeg = cbPreRead + cbThisWrite + cbPostRead;
2994 RTSgBufInit(pTmp, pSeg, 1);
2995
2996 PVDIOCTX pIoCtxWrite = vdIoCtxChildAlloc(pDisk, VDIOCTXTXDIR_WRITE,
2997 uOffset, pSeg->cbSeg, pImage,
2998 pTmp,
2999 pIoCtx, cbThisWrite,
3000 cbWrite,
3001 pTmp,
3002 (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
3003 ? vdWriteHelperStandardAsync
3004 : vdWriteHelperOptimizedAsync);
3005 if (!VALID_PTR(pIoCtxWrite))
3006 {
3007 RTMemTmpFree(pTmp);
3008 rc = VERR_NO_MEMORY;
3009 break;
3010 }
3011
3012 LogFlowFunc(("Disk is growing because of pIoCtx=%#p pIoCtxWrite=%#p\n",
3013 pIoCtx, pIoCtxWrite));
3014
3015 pIoCtxWrite->Type.Child.cbPreRead = cbPreRead;
3016 pIoCtxWrite->Type.Child.cbPostRead = cbPostRead;
3017
3018 /* Process the write request */
3019 rc = vdIoCtxProcess(pIoCtxWrite);
3020
3021 if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
3022 {
3023 vdIoCtxFree(pDisk, pIoCtxWrite);
3024 break;
3025 }
3026 else if ( rc == VINF_VD_ASYNC_IO_FINISHED
3027 && ASMAtomicCmpXchgBool(&pIoCtxWrite->fComplete, true, false))
3028 {
3029 LogFlow(("Child write request completed\n"));
3030 Assert(pIoCtx->Req.Io.cbTransferLeft >= cbThisWrite);
3031 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbThisWrite);
3032 vdIoCtxUnlockDisk(pDisk, pIoCtx, false /* fProcessDeferredReqs*/ );
3033 vdIoCtxFree(pDisk, pIoCtxWrite);
3034
3035 rc = VINF_SUCCESS;
3036 }
3037 else
3038 {
3039 LogFlow(("Child write pending\n"));
3040 ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
3041 pIoCtx->fFlags |= VDIOCTX_FLAGS_BLOCKED;
3042 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
3043 cbWrite -= cbThisWrite;
3044 uOffset += cbThisWrite;
3045 break;
3046 }
3047 }
3048 else
3049 {
3050 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
3051 break;
3052 }
3053 }
3054
3055 if (rc == VERR_VD_IOCTX_HALT)
3056 {
3057 cbWrite -= cbThisWrite;
3058 uOffset += cbThisWrite;
3059 pIoCtx->fFlags |= VDIOCTX_FLAGS_BLOCKED;
3060 break;
3061 }
3062 else if (rc == VERR_VD_NOT_ENOUGH_METADATA)
3063 break;
3064
3065 cbWrite -= cbThisWrite;
3066 uOffset += cbThisWrite;
3067 } while (cbWrite != 0 && (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS));
3068
3069 if ( rc == VERR_VD_ASYNC_IO_IN_PROGRESS
3070 || rc == VERR_VD_NOT_ENOUGH_METADATA
3071 || rc == VERR_VD_IOCTX_HALT)
3072 {
3073 /*
3074 * Tell the caller that we don't need to go back here because all
3075 * writes are initiated.
3076 */
3077 if ( !cbWrite
3078 && rc != VERR_VD_IOCTX_HALT)
3079 rc = VINF_SUCCESS;
3080
3081 pIoCtx->Req.Io.uOffset = uOffset;
3082 pIoCtx->Req.Io.cbTransfer = cbWrite;
3083 }
3084
3085 return rc;
3086}
3087
3088/**
3089 * Flush helper async version.
3090 */
3091static int vdFlushHelperAsync(PVDIOCTX pIoCtx)
3092{
3093 int rc = VINF_SUCCESS;
3094 PVBOXHDD pDisk = pIoCtx->pDisk;
3095 PVDIMAGE pImage = pIoCtx->Req.Io.pImageCur;
3096
3097 rc = vdIoCtxLockDisk(pDisk, pIoCtx);
3098 if (RT_SUCCESS(rc))
3099 {
3100 vdResetModifiedFlag(pDisk);
3101 rc = pImage->Backend->pfnFlush(pImage->pBackendData, pIoCtx);
3102 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3103 rc = VINF_SUCCESS;
3104 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
3105 vdIoCtxUnlockDisk(pDisk, pIoCtx, true /* fProcessDeferredReqs */);
3106 }
3107
3108 return rc;
3109}
3110
3111/**
3112 * Async discard helper - discards a whole block which is recorded in the block
3113 * tree.
3114 *
3115 * @returns VBox status code.
3116 * @param pIoCtx The I/O context to operate on.
3117 */
3118static int vdDiscardWholeBlockAsync(PVDIOCTX pIoCtx)
3119{
3120 int rc = VINF_SUCCESS;
3121 PVBOXHDD pDisk = pIoCtx->pDisk;
3122 PVDDISCARDSTATE pDiscard = pDisk->pDiscard;
3123 PVDDISCARDBLOCK pBlock = pIoCtx->Req.Discard.pBlock;
3124 size_t cbPreAllocated, cbPostAllocated, cbActuallyDiscarded;
3125
3126 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
3127
3128 AssertPtr(pBlock);
3129
3130 rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData, pIoCtx,
3131 pBlock->Core.Key, pBlock->cbDiscard,
3132 &cbPreAllocated, &cbPostAllocated,
3133 &cbActuallyDiscarded, NULL, 0);
3134 Assert(rc != VERR_VD_DISCARD_ALIGNMENT_NOT_MET);
3135 Assert(!cbPreAllocated);
3136 Assert(!cbPostAllocated);
3137 Assert(cbActuallyDiscarded == pBlock->cbDiscard || RT_FAILURE(rc));
3138
3139 /* Remove the block on success. */
3140 if ( RT_SUCCESS(rc)
3141 || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3142 {
3143 PVDDISCARDBLOCK pBlockRemove = (PVDDISCARDBLOCK)RTAvlrU64RangeRemove(pDiscard->pTreeBlocks, pBlock->Core.Key);
3144 Assert(pBlockRemove == pBlock);
3145
3146 pDiscard->cbDiscarding -= pBlock->cbDiscard;
3147 RTListNodeRemove(&pBlock->NodeLru);
3148 RTMemFree(pBlock->pbmAllocated);
3149 RTMemFree(pBlock);
3150 pIoCtx->Req.Discard.pBlock = NULL;/* Safety precaution. */
3151 pIoCtx->pfnIoCtxTransferNext = vdDiscardHelperAsync; /* Next part. */
3152 rc = VINF_SUCCESS;
3153 }
3154
3155 LogFlowFunc(("returns rc=%Rrc\n", rc));
3156 return rc;
3157}
3158
3159/**
3160 * Removes the least recently used blocks from the waiting list until
3161 * the new value is reached - version for async I/O.
3162 *
3163 * @returns VBox status code.
3164 * @param pDisk VD disk container.
3165 * @param pDiscard The discard state.
3166 * @param cbDiscardingNew How many bytes should be waiting on success.
3167 * The number of bytes waiting can be less.
3168 */
3169static int vdDiscardRemoveBlocksAsync(PVBOXHDD pDisk, PVDIOCTX pIoCtx, size_t cbDiscardingNew)
3170{
3171 int rc = VINF_SUCCESS;
3172 PVDDISCARDSTATE pDiscard = pDisk->pDiscard;
3173
3174 LogFlowFunc(("pDisk=%#p pDiscard=%#p cbDiscardingNew=%zu\n",
3175 pDisk, pDiscard, cbDiscardingNew));
3176
3177 while (pDiscard->cbDiscarding > cbDiscardingNew)
3178 {
3179 PVDDISCARDBLOCK pBlock = RTListGetLast(&pDiscard->ListLru, VDDISCARDBLOCK, NodeLru);
3180
3181 Assert(!RTListIsEmpty(&pDiscard->ListLru));
3182
3183 /* Go over the allocation bitmap and mark all discarded sectors as unused. */
3184 uint64_t offStart = pBlock->Core.Key;
3185 uint32_t idxStart = 0;
3186 size_t cbLeft = pBlock->cbDiscard;
3187 bool fAllocated = ASMBitTest(pBlock->pbmAllocated, idxStart);
3188 uint32_t cSectors = pBlock->cbDiscard / 512;
3189
3190 while (cbLeft > 0)
3191 {
3192 int32_t idxEnd;
3193 size_t cbThis = cbLeft;
3194
3195 if (fAllocated)
3196 {
3197 /* Check for the first unallocated bit. */
3198 idxEnd = ASMBitNextClear(pBlock->pbmAllocated, cSectors, idxStart);
3199 if (idxEnd != -1)
3200 {
3201 cbThis = (idxEnd - idxStart) * 512;
3202 fAllocated = false;
3203 }
3204 }
3205 else
3206 {
3207 /* Mark as unused and check for the first set bit. */
3208 idxEnd = ASMBitNextSet(pBlock->pbmAllocated, cSectors, idxStart);
3209 if (idxEnd != -1)
3210 cbThis = (idxEnd - idxStart) * 512;
3211
3212 rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData, pIoCtx,
3213 offStart, cbThis, NULL, NULL, &cbThis,
3214 NULL, VD_DISCARD_MARK_UNUSED);
3215 if ( RT_FAILURE(rc)
3216 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
3217 break;
3218
3219 fAllocated = true;
3220 }
3221
3222 idxStart = idxEnd;
3223 offStart += cbThis;
3224 cbLeft -= cbThis;
3225 }
3226
3227 if ( RT_FAILURE(rc)
3228 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
3229 break;
3230
3231 PVDDISCARDBLOCK pBlockRemove = (PVDDISCARDBLOCK)RTAvlrU64RangeRemove(pDiscard->pTreeBlocks, pBlock->Core.Key);
3232 Assert(pBlockRemove == pBlock);
3233 RTListNodeRemove(&pBlock->NodeLru);
3234
3235 pDiscard->cbDiscarding -= pBlock->cbDiscard;
3236 RTMemFree(pBlock->pbmAllocated);
3237 RTMemFree(pBlock);
3238 }
3239
3240 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3241 rc = VINF_SUCCESS;
3242
3243 Assert(RT_FAILURE(rc) || pDiscard->cbDiscarding <= cbDiscardingNew);
3244
3245 LogFlowFunc(("returns rc=%Rrc\n", rc));
3246 return rc;
3247}
3248
3249/**
3250 * Async discard helper - discards the current range if there is no matching
3251 * block in the tree.
3252 *
3253 * @returns VBox status code.
3254 * @param pIoCtx The I/O context to operate on.
3255 */
3256static int vdDiscardCurrentRangeAsync(PVDIOCTX pIoCtx)
3257{
3258 PVBOXHDD pDisk = pIoCtx->pDisk;
3259 PVDDISCARDSTATE pDiscard = pDisk->pDiscard;
3260 uint64_t offStart = pIoCtx->Req.Discard.offCur;
3261 size_t cbThisDiscard = pIoCtx->Req.Discard.cbThisDiscard;
3262 void *pbmAllocated = NULL;
3263 size_t cbPreAllocated, cbPostAllocated;
3264 int rc = VINF_SUCCESS;
3265
3266 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
3267
3268 /* No block found, try to discard using the backend first. */
3269 rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData, pIoCtx,
3270 offStart, cbThisDiscard, &cbPreAllocated,
3271 &cbPostAllocated, &cbThisDiscard,
3272 &pbmAllocated, 0);
3273 if (rc == VERR_VD_DISCARD_ALIGNMENT_NOT_MET)
3274 {
3275 /* Create new discard block. */
3276 PVDDISCARDBLOCK pBlock = (PVDDISCARDBLOCK)RTMemAllocZ(sizeof(VDDISCARDBLOCK));
3277 if (pBlock)
3278 {
3279 pBlock->Core.Key = offStart - cbPreAllocated;
3280 pBlock->Core.KeyLast = offStart + cbThisDiscard + cbPostAllocated - 1;
3281 pBlock->cbDiscard = cbPreAllocated + cbThisDiscard + cbPostAllocated;
3282 pBlock->pbmAllocated = pbmAllocated;
3283 bool fInserted = RTAvlrU64Insert(pDiscard->pTreeBlocks, &pBlock->Core);
3284 Assert(fInserted);
3285
3286 RTListPrepend(&pDiscard->ListLru, &pBlock->NodeLru);
3287 pDiscard->cbDiscarding += pBlock->cbDiscard;
3288
3289 Assert(pIoCtx->Req.Discard.cbDiscardLeft >= cbThisDiscard);
3290 pIoCtx->Req.Discard.cbDiscardLeft -= cbThisDiscard;
3291 pIoCtx->Req.Discard.offCur += cbThisDiscard;
3292 pIoCtx->Req.Discard.cbThisDiscard = cbThisDiscard;
3293
3294 if (pDiscard->cbDiscarding > VD_DISCARD_REMOVE_THRESHOLD)
3295 rc = vdDiscardRemoveBlocksAsync(pDisk, pIoCtx, VD_DISCARD_REMOVE_THRESHOLD);
3296 else
3297 rc = VINF_SUCCESS;
3298
3299 if (RT_SUCCESS(rc))
3300 pIoCtx->pfnIoCtxTransferNext = vdDiscardHelperAsync; /* Next part. */
3301 }
3302 else
3303 {
3304 RTMemFree(pbmAllocated);
3305 rc = VERR_NO_MEMORY;
3306 }
3307 }
3308 else if ( RT_SUCCESS(rc)
3309 || rc == VERR_VD_ASYNC_IO_IN_PROGRESS) /* Save state and andvance to next range. */
3310 {
3311 Assert(pIoCtx->Req.Discard.cbDiscardLeft >= cbThisDiscard);
3312 pIoCtx->Req.Discard.cbDiscardLeft -= cbThisDiscard;
3313 pIoCtx->Req.Discard.offCur += cbThisDiscard;
3314 pIoCtx->Req.Discard.cbThisDiscard = cbThisDiscard;
3315 pIoCtx->pfnIoCtxTransferNext = vdDiscardHelperAsync;
3316 rc = VINF_SUCCESS;
3317 }
3318
3319 LogFlowFunc(("returns rc=%Rrc\n", rc));
3320 return rc;
3321}
3322
3323/**
3324 * Async discard helper - entry point.
3325 *
3326 * @returns VBox status code.
3327 * @param pIoCtx The I/O context to operate on.
3328 */
3329static int vdDiscardHelperAsync(PVDIOCTX pIoCtx)
3330{
3331 int rc = VINF_SUCCESS;
3332 PVBOXHDD pDisk = pIoCtx->pDisk;
3333 PCRTRANGE paRanges = pIoCtx->Req.Discard.paRanges;
3334 unsigned cRanges = pIoCtx->Req.Discard.cRanges;
3335 PVDDISCARDSTATE pDiscard = pDisk->pDiscard;
3336
3337 LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
3338
3339 /* Check if the I/O context processed all ranges. */
3340 if ( pIoCtx->Req.Discard.idxRange == cRanges
3341 && !pIoCtx->Req.Discard.cbDiscardLeft)
3342 {
3343 LogFlowFunc(("All ranges discarded, completing\n"));
3344 vdIoCtxUnlockDisk(pDisk, pIoCtx, true /* fProcessDeferredReqs*/);
3345 return VINF_SUCCESS;
3346 }
3347
3348 if (pDisk->pIoCtxLockOwner != pIoCtx)
3349 rc = vdIoCtxLockDisk(pDisk, pIoCtx);
3350
3351 if (RT_SUCCESS(rc))
3352 {
3353 uint64_t offStart = pIoCtx->Req.Discard.offCur;
3354 size_t cbDiscardLeft = pIoCtx->Req.Discard.cbDiscardLeft;
3355 size_t cbThisDiscard;
3356
3357 if (RT_UNLIKELY(!pDiscard))
3358 {
3359 pDiscard = vdDiscardStateCreate();
3360 if (!pDiscard)
3361 return VERR_NO_MEMORY;
3362
3363 pDisk->pDiscard = pDiscard;
3364 }
3365
3366 if (!pIoCtx->Req.Discard.cbDiscardLeft)
3367 {
3368 offStart = paRanges[pIoCtx->Req.Discard.idxRange].offStart;
3369 cbDiscardLeft = paRanges[pIoCtx->Req.Discard.idxRange].cbRange;
3370 LogFlowFunc(("New range descriptor loaded (%u) offStart=%llu cbDiscard=%zu\n",
3371 pIoCtx->Req.Discard.idxRange, offStart, cbDiscardLeft));
3372 pIoCtx->Req.Discard.idxRange++;
3373 }
3374
3375 /* Look for a matching block in the AVL tree first. */
3376 PVDDISCARDBLOCK pBlock = (PVDDISCARDBLOCK)RTAvlrU64GetBestFit(pDiscard->pTreeBlocks, offStart, false);
3377 if (!pBlock || pBlock->Core.KeyLast < offStart)
3378 {
3379 PVDDISCARDBLOCK pBlockAbove = (PVDDISCARDBLOCK)RTAvlrU64GetBestFit(pDiscard->pTreeBlocks, offStart, true);
3380
3381 /* Clip range to remain in the current block. */
3382 if (pBlockAbove)
3383 cbThisDiscard = RT_MIN(cbDiscardLeft, pBlockAbove->Core.KeyLast - offStart + 1);
3384 else
3385 cbThisDiscard = cbDiscardLeft;
3386
3387 Assert(!(cbThisDiscard % 512));
3388 pIoCtx->Req.Discard.pBlock = NULL;
3389 pIoCtx->pfnIoCtxTransferNext = vdDiscardCurrentRangeAsync;
3390 }
3391 else
3392 {
3393 /* Range lies partly in the block, update allocation bitmap. */
3394 int32_t idxStart, idxEnd;
3395
3396 cbThisDiscard = RT_MIN(cbDiscardLeft, pBlock->Core.KeyLast - offStart + 1);
3397
3398 AssertPtr(pBlock);
3399
3400 Assert(!(cbThisDiscard % 512));
3401 Assert(!((offStart - pBlock->Core.Key) % 512));
3402
3403 idxStart = (offStart - pBlock->Core.Key) / 512;
3404 idxEnd = idxStart + (cbThisDiscard / 512);
3405
3406 ASMBitClearRange(pBlock->pbmAllocated, idxStart, idxEnd);
3407
3408 cbDiscardLeft -= cbThisDiscard;
3409 offStart += cbThisDiscard;
3410
3411 /* Call the backend to discard the block if it is completely unallocated now. */
3412 if (ASMBitFirstSet((volatile void *)pBlock->pbmAllocated, pBlock->cbDiscard / 512) == -1)
3413 {
3414 pIoCtx->Req.Discard.pBlock = pBlock;
3415 pIoCtx->pfnIoCtxTransferNext = vdDiscardWholeBlockAsync;
3416 rc = VINF_SUCCESS;
3417 }
3418 else
3419 {
3420 RTListNodeRemove(&pBlock->NodeLru);
3421 RTListPrepend(&pDiscard->ListLru, &pBlock->NodeLru);
3422
3423 /* Start with next range. */
3424 pIoCtx->pfnIoCtxTransferNext = vdDiscardHelperAsync;
3425 rc = VINF_SUCCESS;
3426 }
3427 }
3428
3429 /* Save state in the context. */
3430 pIoCtx->Req.Discard.offCur = offStart;
3431 pIoCtx->Req.Discard.cbDiscardLeft = cbDiscardLeft;
3432 pIoCtx->Req.Discard.cbThisDiscard = cbThisDiscard;
3433 }
3434
3435 LogFlowFunc(("returns rc=%Rrc\n", rc));
3436 return rc;
3437}
3438
3439/**
3440 * internal: scans plugin directory and loads the backends have been found.
3441 */
3442static int vdLoadDynamicBackends()
3443{
3444#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
3445 int rc = VINF_SUCCESS;
3446 PRTDIR pPluginDir = NULL;
3447
3448 /* Enumerate plugin backends. */
3449 char szPath[RTPATH_MAX];
3450 rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
3451 if (RT_FAILURE(rc))
3452 return rc;
3453
3454 /* To get all entries with VBoxHDD as prefix. */
3455 char *pszPluginFilter = RTPathJoinA(szPath, VBOX_HDDFORMAT_PLUGIN_PREFIX "*");
3456 if (!pszPluginFilter)
3457 return VERR_NO_STR_MEMORY;
3458
3459 PRTDIRENTRYEX pPluginDirEntry = NULL;
3460 size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
3461 /* The plugins are in the same directory as the other shared libs. */
3462 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT, 0);
3463 if (RT_FAILURE(rc))
3464 {
3465 /* On Windows the above immediately signals that there are no
3466 * files matching, while on other platforms enumerating the
3467 * files below fails. Either way: no plugins. */
3468 goto out;
3469 }
3470
3471 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
3472 if (!pPluginDirEntry)
3473 {
3474 rc = VERR_NO_MEMORY;
3475 goto out;
3476 }
3477
3478 while ((rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) != VERR_NO_MORE_FILES)
3479 {
3480 RTLDRMOD hPlugin = NIL_RTLDRMOD;
3481 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad = NULL;
3482 PVBOXHDDBACKEND pBackend = NULL;
3483 char *pszPluginPath = NULL;
3484
3485 if (rc == VERR_BUFFER_OVERFLOW)
3486 {
3487 /* allocate new buffer. */
3488 RTMemFree(pPluginDirEntry);
3489 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
3490 if (!pPluginDirEntry)
3491 {
3492 rc = VERR_NO_MEMORY;
3493 break;
3494 }
3495 /* Retry. */
3496 rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
3497 if (RT_FAILURE(rc))
3498 break;
3499 }
3500 else if (RT_FAILURE(rc))
3501 break;
3502
3503 /* We got the new entry. */
3504 if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
3505 continue;
3506
3507 /* Prepend the path to the libraries. */
3508 pszPluginPath = RTPathJoinA(szPath, pPluginDirEntry->szName);
3509 if (!pszPluginPath)
3510 {
3511 rc = VERR_NO_STR_MEMORY;
3512 break;
3513 }
3514
3515 rc = SUPR3HardenedLdrLoadPlugIn(pszPluginPath, &hPlugin, NULL);
3516 if (RT_SUCCESS(rc))
3517 {
3518 rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad);
3519 if (RT_FAILURE(rc) || !pfnHDDFormatLoad)
3520 {
3521 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnHDDFormat=%#p\n", VBOX_HDDFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnHDDFormatLoad));
3522 if (RT_SUCCESS(rc))
3523 rc = VERR_SYMBOL_NOT_FOUND;
3524 }
3525
3526 if (RT_SUCCESS(rc))
3527 {
3528 /* Get the function table. */
3529 rc = pfnHDDFormatLoad(&pBackend);
3530 if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VBOXHDDBACKEND))
3531 {
3532 pBackend->hPlugin = hPlugin;
3533 vdAddBackend(pBackend);
3534 }
3535 else
3536 LogFunc(("ignored plugin '%s': pBackend->cbSize=%d rc=%Rrc\n", pszPluginPath, pBackend->cbSize, rc));
3537 }
3538 else
3539 LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszPluginPath, rc));
3540
3541 if (RT_FAILURE(rc))
3542 RTLdrClose(hPlugin);
3543 }
3544 RTStrFree(pszPluginPath);
3545 }
3546out:
3547 if (rc == VERR_NO_MORE_FILES)
3548 rc = VINF_SUCCESS;
3549 RTStrFree(pszPluginFilter);
3550 if (pPluginDirEntry)
3551 RTMemFree(pPluginDirEntry);
3552 if (pPluginDir)
3553 RTDirClose(pPluginDir);
3554 return rc;
3555#else
3556 return VINF_SUCCESS;
3557#endif
3558}
3559
3560/**
3561 * internal: scans plugin directory and loads the cache backends have been found.
3562 */
3563static int vdLoadDynamicCacheBackends()
3564{
3565#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
3566 int rc = VINF_SUCCESS;
3567 PRTDIR pPluginDir = NULL;
3568
3569 /* Enumerate plugin backends. */
3570 char szPath[RTPATH_MAX];
3571 rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
3572 if (RT_FAILURE(rc))
3573 return rc;
3574
3575 /* To get all entries with VBoxHDD as prefix. */
3576 char *pszPluginFilter = RTPathJoinA(szPath, VD_CACHEFORMAT_PLUGIN_PREFIX "*");
3577 if (!pszPluginFilter)
3578 {
3579 rc = VERR_NO_STR_MEMORY;
3580 return rc;
3581 }
3582
3583 PRTDIRENTRYEX pPluginDirEntry = NULL;
3584 size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
3585 /* The plugins are in the same directory as the other shared libs. */
3586 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT, 0);
3587 if (RT_FAILURE(rc))
3588 {
3589 /* On Windows the above immediately signals that there are no
3590 * files matching, while on other platforms enumerating the
3591 * files below fails. Either way: no plugins. */
3592 goto out;
3593 }
3594
3595 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
3596 if (!pPluginDirEntry)
3597 {
3598 rc = VERR_NO_MEMORY;
3599 goto out;
3600 }
3601
3602 while ((rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) != VERR_NO_MORE_FILES)
3603 {
3604 RTLDRMOD hPlugin = NIL_RTLDRMOD;
3605 PFNVDCACHEFORMATLOAD pfnVDCacheLoad = NULL;
3606 PVDCACHEBACKEND pBackend = NULL;
3607 char *pszPluginPath = NULL;
3608
3609 if (rc == VERR_BUFFER_OVERFLOW)
3610 {
3611 /* allocate new buffer. */
3612 RTMemFree(pPluginDirEntry);
3613 pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
3614 if (!pPluginDirEntry)
3615 {
3616 rc = VERR_NO_MEMORY;
3617 break;
3618 }
3619 /* Retry. */
3620 rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
3621 if (RT_FAILURE(rc))
3622 break;
3623 }
3624 else if (RT_FAILURE(rc))
3625 break;
3626
3627 /* We got the new entry. */
3628 if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
3629 continue;
3630
3631 /* Prepend the path to the libraries. */
3632 pszPluginPath = RTPathJoinA(szPath, pPluginDirEntry->szName);
3633 if (!pszPluginPath)
3634 {
3635 rc = VERR_NO_STR_MEMORY;
3636 break;
3637 }
3638
3639 rc = SUPR3HardenedLdrLoadPlugIn(pszPluginPath, &hPlugin, NULL);
3640 if (RT_SUCCESS(rc))
3641 {
3642 rc = RTLdrGetSymbol(hPlugin, VD_CACHEFORMAT_LOAD_NAME, (void**)&pfnVDCacheLoad);
3643 if (RT_FAILURE(rc) || !pfnVDCacheLoad)
3644 {
3645 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnVDCacheLoad=%#p\n",
3646 VD_CACHEFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnVDCacheLoad));
3647 if (RT_SUCCESS(rc))
3648 rc = VERR_SYMBOL_NOT_FOUND;
3649 }
3650
3651 if (RT_SUCCESS(rc))
3652 {
3653 /* Get the function table. */
3654 rc = pfnVDCacheLoad(&pBackend);
3655 if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VDCACHEBACKEND))
3656 {
3657 pBackend->hPlugin = hPlugin;
3658 vdAddCacheBackend(pBackend);
3659 }
3660 else
3661 LogFunc(("ignored plugin '%s': pBackend->cbSize=%d rc=%Rrc\n", pszPluginPath, pBackend->cbSize, rc));
3662 }
3663 else
3664 LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszPluginPath, rc));
3665
3666 if (RT_FAILURE(rc))
3667 RTLdrClose(hPlugin);
3668 }
3669 RTStrFree(pszPluginPath);
3670 }
3671out:
3672 if (rc == VERR_NO_MORE_FILES)
3673 rc = VINF_SUCCESS;
3674 RTStrFree(pszPluginFilter);
3675 if (pPluginDirEntry)
3676 RTMemFree(pPluginDirEntry);
3677 if (pPluginDir)
3678 RTDirClose(pPluginDir);
3679 return rc;
3680#else
3681 return VINF_SUCCESS;
3682#endif
3683}
3684
3685/**
3686 * VD async I/O interface open callback.
3687 */
3688static int vdIOOpenFallback(void *pvUser, const char *pszLocation,
3689 uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
3690 void **ppStorage)
3691{
3692 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)RTMemAllocZ(sizeof(VDIIOFALLBACKSTORAGE));
3693
3694 if (!pStorage)
3695 return VERR_NO_MEMORY;
3696
3697 pStorage->pfnCompleted = pfnCompleted;
3698
3699 /* Open the file. */
3700 int rc = RTFileOpen(&pStorage->File, pszLocation, fOpen);
3701 if (RT_SUCCESS(rc))
3702 {
3703 *ppStorage = pStorage;
3704 return VINF_SUCCESS;
3705 }
3706
3707 RTMemFree(pStorage);
3708 return rc;
3709}
3710
3711/**
3712 * VD async I/O interface close callback.
3713 */
3714static int vdIOCloseFallback(void *pvUser, void *pvStorage)
3715{
3716 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
3717
3718 RTFileClose(pStorage->File);
3719 RTMemFree(pStorage);
3720 return VINF_SUCCESS;
3721}
3722
3723static int vdIODeleteFallback(void *pvUser, const char *pcszFilename)
3724{
3725 return RTFileDelete(pcszFilename);
3726}
3727
3728static int vdIOMoveFallback(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
3729{
3730 return RTFileMove(pcszSrc, pcszDst, fMove);
3731}
3732
3733static int vdIOGetFreeSpaceFallback(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
3734{
3735 return RTFsQuerySizes(pcszFilename, NULL, pcbFreeSpace, NULL, NULL);
3736}
3737
3738static int vdIOGetModificationTimeFallback(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
3739{
3740 RTFSOBJINFO info;
3741 int rc = RTPathQueryInfo(pcszFilename, &info, RTFSOBJATTRADD_NOTHING);
3742 if (RT_SUCCESS(rc))
3743 *pModificationTime = info.ModificationTime;
3744 return rc;
3745}
3746
3747/**
3748 * VD async I/O interface callback for retrieving the file size.
3749 */
3750static int vdIOGetSizeFallback(void *pvUser, void *pvStorage, uint64_t *pcbSize)
3751{
3752 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
3753
3754 return RTFileGetSize(pStorage->File, pcbSize);
3755}
3756
3757/**
3758 * VD async I/O interface callback for setting the file size.
3759 */
3760static int vdIOSetSizeFallback(void *pvUser, void *pvStorage, uint64_t cbSize)
3761{
3762 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
3763
3764 return RTFileSetSize(pStorage->File, cbSize);
3765}
3766
3767/**
3768 * VD async I/O interface callback for a synchronous write to the file.
3769 */
3770static int vdIOWriteSyncFallback(void *pvUser, void *pvStorage, uint64_t uOffset,
3771 const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
3772{
3773 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
3774
3775 return RTFileWriteAt(pStorage->File, uOffset, pvBuf, cbWrite, pcbWritten);
3776}
3777
3778/**
3779 * VD async I/O interface callback for a synchronous read from the file.
3780 */
3781static int vdIOReadSyncFallback(void *pvUser, void *pvStorage, uint64_t uOffset,
3782 void *pvBuf, size_t cbRead, size_t *pcbRead)
3783{
3784 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
3785
3786 return RTFileReadAt(pStorage->File, uOffset, pvBuf, cbRead, pcbRead);
3787}
3788
3789/**
3790 * VD async I/O interface callback for a synchronous flush of the file data.
3791 */
3792static int vdIOFlushSyncFallback(void *pvUser, void *pvStorage)
3793{
3794 PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
3795
3796 return RTFileFlush(pStorage->File);
3797}
3798
3799/**
3800 * VD async I/O interface callback for a asynchronous read from the file.
3801 */
3802static int vdIOReadAsyncFallback(void *pvUser, void *pStorage, uint64_t uOffset,
3803 PCRTSGSEG paSegments, size_t cSegments,
3804 size_t cbRead, void *pvCompletion,
3805 void **ppTask)
3806{
3807 return VERR_NOT_IMPLEMENTED;
3808}
3809
3810/**
3811 * VD async I/O interface callback for a asynchronous write to the file.
3812 */
3813static int vdIOWriteAsyncFallback(void *pvUser, void *pStorage, uint64_t uOffset,
3814 PCRTSGSEG paSegments, size_t cSegments,
3815 size_t cbWrite, void *pvCompletion,
3816 void **ppTask)
3817{
3818 return VERR_NOT_IMPLEMENTED;
3819}
3820
3821/**
3822 * VD async I/O interface callback for a asynchronous flush of the file data.
3823 */
3824static int vdIOFlushAsyncFallback(void *pvUser, void *pStorage,
3825 void *pvCompletion, void **ppTask)
3826{
3827 return VERR_NOT_IMPLEMENTED;
3828}
3829
3830/**
3831 * Internal - Continues an I/O context after
3832 * it was halted because of an active transfer.
3833 */
3834static int vdIoCtxContinue(PVDIOCTX pIoCtx, int rcReq)
3835{
3836 PVBOXHDD pDisk = pIoCtx->pDisk;
3837 int rc = VINF_SUCCESS;
3838
3839 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
3840
3841 if (RT_FAILURE(rcReq))
3842 ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rcReq, VINF_SUCCESS);
3843
3844 if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_BLOCKED))
3845 {
3846 /* Continue the transfer */
3847 rc = vdIoCtxProcess(pIoCtx);
3848
3849 if ( rc == VINF_VD_ASYNC_IO_FINISHED
3850 && ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
3851 {
3852 LogFlowFunc(("I/O context completed pIoCtx=%#p\n", pIoCtx));
3853 if (pIoCtx->pIoCtxParent)
3854 {
3855 PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
3856
3857 Assert(!pIoCtxParent->pIoCtxParent);
3858 if (RT_FAILURE(pIoCtx->rcReq))
3859 ASMAtomicCmpXchgS32(&pIoCtxParent->rcReq, pIoCtx->rcReq, VINF_SUCCESS);
3860
3861 ASMAtomicDecU32(&pIoCtxParent->cDataTransfersPending);
3862
3863 if (pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE)
3864 {
3865 LogFlowFunc(("I/O context transferred %u bytes for the parent pIoCtxParent=%p\n",
3866 pIoCtx->Type.Child.cbTransferParent, pIoCtxParent));
3867
3868 /* Update the parent state. */
3869 Assert(pIoCtxParent->Req.Io.cbTransferLeft >= pIoCtx->Type.Child.cbTransferParent);
3870 ASMAtomicSubU32(&pIoCtxParent->Req.Io.cbTransferLeft, pIoCtx->Type.Child.cbTransferParent);
3871 }
3872 else
3873 Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_FLUSH);
3874
3875 /*
3876 * A completed child write means that we finished growing the image.
3877 * We have to process any pending writes now.
3878 */
3879 vdIoCtxUnlockDisk(pDisk, pIoCtxParent, false /* fProcessDeferredReqs */);
3880
3881 /* Unblock the parent */
3882 pIoCtxParent->fFlags &= ~VDIOCTX_FLAGS_BLOCKED;
3883
3884 rc = vdIoCtxProcess(pIoCtxParent);
3885
3886 if ( rc == VINF_VD_ASYNC_IO_FINISHED
3887 && ASMAtomicCmpXchgBool(&pIoCtxParent->fComplete, true, false))
3888 {
3889 RTCritSectLeave(&pDisk->CritSect);
3890 LogFlowFunc(("Parent I/O context completed pIoCtxParent=%#p rcReq=%Rrc\n", pIoCtxParent, pIoCtxParent->rcReq));
3891 pIoCtxParent->Type.Root.pfnComplete(pIoCtxParent->Type.Root.pvUser1,
3892 pIoCtxParent->Type.Root.pvUser2,
3893 pIoCtxParent->rcReq);
3894 vdThreadFinishWrite(pDisk);
3895 vdIoCtxFree(pDisk, pIoCtxParent);
3896 RTCritSectEnter(&pDisk->CritSect);
3897 }
3898
3899 /* Process any pending writes if the current request didn't caused another growing. */
3900 if ( !RTListIsEmpty(&pDisk->ListWriteLocked)
3901 && !vdIoCtxIsDiskLockOwner(pDisk, pIoCtx))
3902 {
3903 RTLISTNODE ListTmp;
3904
3905 LogFlowFunc(("Before: pNext=%#p pPrev=%#p\n", pDisk->ListWriteLocked.pNext,
3906 pDisk->ListWriteLocked.pPrev));
3907
3908 RTListMove(&ListTmp, &pDisk->ListWriteLocked);
3909
3910 LogFlowFunc(("After: pNext=%#p pPrev=%#p\n", pDisk->ListWriteLocked.pNext,
3911 pDisk->ListWriteLocked.pPrev));
3912
3913 RTCritSectLeave(&pDisk->CritSect);
3914
3915 /* Process the list. */
3916 do
3917 {
3918 PVDIOCTXDEFERRED pDeferred = RTListGetFirst(&ListTmp, VDIOCTXDEFERRED, NodeDeferred);
3919 PVDIOCTX pIoCtxWait = pDeferred->pIoCtx;
3920
3921 AssertPtr(pIoCtxWait);
3922
3923 RTListNodeRemove(&pDeferred->NodeDeferred);
3924 RTMemFree(pDeferred);
3925
3926 Assert(!pIoCtxWait->pIoCtxParent);
3927
3928 pIoCtxWait->fFlags &= ~VDIOCTX_FLAGS_BLOCKED;
3929 LogFlowFunc(("Processing waiting I/O context pIoCtxWait=%#p\n", pIoCtxWait));
3930
3931 rc = vdIoCtxProcess(pIoCtxWait);
3932 if ( rc == VINF_VD_ASYNC_IO_FINISHED
3933 && ASMAtomicCmpXchgBool(&pIoCtxWait->fComplete, true, false))
3934 {
3935 LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
3936 vdThreadFinishWrite(pDisk);
3937 pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
3938 pIoCtxWait->Type.Root.pvUser2,
3939 pIoCtxWait->rcReq);
3940 vdIoCtxFree(pDisk, pIoCtxWait);
3941 }
3942 } while (!RTListIsEmpty(&ListTmp));
3943
3944 RTCritSectEnter(&pDisk->CritSect);
3945 }
3946 }
3947 else
3948 {
3949 RTCritSectLeave(&pDisk->CritSect);
3950
3951 if (pIoCtx->enmTxDir == VDIOCTXTXDIR_FLUSH)
3952 {
3953 vdIoCtxUnlockDisk(pDisk, pIoCtx, true /* fProcessDerredReqs */);
3954 vdThreadFinishWrite(pDisk);
3955 }
3956 else if ( pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE
3957 || pIoCtx->enmTxDir == VDIOCTXTXDIR_DISCARD)
3958 vdThreadFinishWrite(pDisk);
3959 else
3960 {
3961 Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_READ);
3962 vdThreadFinishRead(pDisk);
3963 }
3964
3965 LogFlowFunc(("I/O context completed pIoCtx=%#p rcReq=%Rrc\n", pIoCtx, pIoCtx->rcReq));
3966 pIoCtx->Type.Root.pfnComplete(pIoCtx->Type.Root.pvUser1,
3967 pIoCtx->Type.Root.pvUser2,
3968 pIoCtx->rcReq);
3969 RTCritSectEnter(&pDisk->CritSect);
3970 }
3971
3972 vdIoCtxFree(pDisk, pIoCtx);
3973 }
3974 }
3975
3976 return VINF_SUCCESS;
3977}
3978
3979/**
3980 * Internal - Called when user transfer completed.
3981 */
3982static int vdUserXferCompleted(PVDIOSTORAGE pIoStorage, PVDIOCTX pIoCtx,
3983 PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
3984 size_t cbTransfer, int rcReq)
3985{
3986 int rc = VINF_SUCCESS;
3987 bool fIoCtxContinue = true;
3988 PVBOXHDD pDisk = pIoCtx->pDisk;
3989
3990 LogFlowFunc(("pIoStorage=%#p pIoCtx=%#p pfnComplete=%#p pvUser=%#p cbTransfer=%zu rcReq=%Rrc\n",
3991 pIoStorage, pIoCtx, pfnComplete, pvUser, cbTransfer, rcReq));
3992
3993 RTCritSectEnter(&pDisk->CritSect);
3994 Assert(pIoCtx->Req.Io.cbTransferLeft >= cbTransfer);
3995 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbTransfer);
3996 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
3997
3998 if (pfnComplete)
3999 rc = pfnComplete(pIoStorage->pVDIo->pBackendData, pIoCtx, pvUser, rcReq);
4000
4001 if (RT_SUCCESS(rc))
4002 rc = vdIoCtxContinue(pIoCtx, rcReq);
4003 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4004 rc = VINF_SUCCESS;
4005
4006 vdDiskCritSectLeave(pDisk, NULL);
4007
4008 return rc;
4009}
4010
4011/**
4012 * Internal - Called when a meta transfer completed.
4013 */
4014static int vdMetaXferCompleted(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
4015 PVDMETAXFER pMetaXfer, int rcReq)
4016{
4017 PVBOXHDD pDisk = pIoStorage->pVDIo->pDisk;
4018 RTLISTNODE ListIoCtxWaiting;
4019 bool fFlush;
4020
4021 LogFlowFunc(("pIoStorage=%#p pfnComplete=%#p pvUser=%#p pMetaXfer=%#p rcReq=%Rrc\n",
4022 pIoStorage, pfnComplete, pvUser, pMetaXfer, rcReq));
4023
4024 RTCritSectEnter(&pDisk->CritSect);
4025 fFlush = VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_FLUSH;
4026 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
4027
4028 if (!fFlush)
4029 {
4030 RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
4031
4032 if (RT_FAILURE(rcReq))
4033 {
4034 /* Remove from the AVL tree. */
4035 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
4036 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
4037 Assert(fRemoved);
4038 RTMemFree(pMetaXfer);
4039 }
4040 else
4041 {
4042 /* Increase the reference counter to make sure it doesn't go away before the last context is processed. */
4043 pMetaXfer->cRefs++;
4044 }
4045 }
4046 else
4047 RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
4048
4049 /* Go through the waiting list and continue the I/O contexts. */
4050 while (!RTListIsEmpty(&ListIoCtxWaiting))
4051 {
4052 int rc = VINF_SUCCESS;
4053 bool fContinue = true;
4054 PVDIOCTXDEFERRED pDeferred = RTListGetFirst(&ListIoCtxWaiting, VDIOCTXDEFERRED, NodeDeferred);
4055 PVDIOCTX pIoCtx = pDeferred->pIoCtx;
4056 RTListNodeRemove(&pDeferred->NodeDeferred);
4057
4058 RTMemFree(pDeferred);
4059 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
4060
4061 if (pfnComplete)
4062 rc = pfnComplete(pIoStorage->pVDIo->pBackendData, pIoCtx, pvUser, rcReq);
4063
4064 LogFlow(("Completion callback for I/O context %#p returned %Rrc\n", pIoCtx, rc));
4065
4066 if (RT_SUCCESS(rc))
4067 {
4068 rc = vdIoCtxContinue(pIoCtx, rcReq);
4069 AssertRC(rc);
4070 }
4071 else
4072 Assert(rc == VERR_VD_ASYNC_IO_IN_PROGRESS);
4073 }
4074
4075 /* Remove if not used anymore. */
4076 if (RT_SUCCESS(rcReq) && !fFlush)
4077 {
4078 pMetaXfer->cRefs--;
4079 if (!pMetaXfer->cRefs && RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting))
4080 {
4081 /* Remove from the AVL tree. */
4082 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
4083 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
4084 Assert(fRemoved);
4085 RTMemFree(pMetaXfer);
4086 }
4087 }
4088 else if (fFlush)
4089 RTMemFree(pMetaXfer);
4090
4091 vdDiskCritSectLeave(pDisk, NULL);
4092
4093 return VINF_SUCCESS;
4094}
4095
4096static int vdIOIntReqCompleted(void *pvUser, int rcReq)
4097{
4098 int rc = VINF_SUCCESS;
4099 PVDIOTASK pIoTask = (PVDIOTASK)pvUser;
4100 PVDIOSTORAGE pIoStorage = pIoTask->pIoStorage;
4101
4102 LogFlowFunc(("Task completed pIoTask=%#p\n", pIoTask));
4103
4104 if (!pIoTask->fMeta)
4105 rc = vdUserXferCompleted(pIoStorage, pIoTask->Type.User.pIoCtx,
4106 pIoTask->pfnComplete, pIoTask->pvUser,
4107 pIoTask->Type.User.cbTransfer, rcReq);
4108 else
4109 rc = vdMetaXferCompleted(pIoStorage, pIoTask->pfnComplete, pIoTask->pvUser,
4110 pIoTask->Type.Meta.pMetaXfer, rcReq);
4111
4112 vdIoTaskFree(pIoStorage->pVDIo->pDisk, pIoTask);
4113
4114 return rc;
4115}
4116
4117/**
4118 * VD I/O interface callback for opening a file.
4119 */
4120static int vdIOIntOpen(void *pvUser, const char *pszLocation,
4121 unsigned uOpenFlags, PPVDIOSTORAGE ppIoStorage)
4122{
4123 int rc = VINF_SUCCESS;
4124 PVDIO pVDIo = (PVDIO)pvUser;
4125 PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
4126
4127 if (!pIoStorage)
4128 return VERR_NO_MEMORY;
4129
4130 /* Create the AVl tree. */
4131 pIoStorage->pTreeMetaXfers = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
4132 if (pIoStorage->pTreeMetaXfers)
4133 {
4134 rc = pVDIo->pInterfaceIo->pfnOpen(pVDIo->pInterfaceIo->Core.pvUser,
4135 pszLocation, uOpenFlags,
4136 vdIOIntReqCompleted,
4137 &pIoStorage->pStorage);
4138 if (RT_SUCCESS(rc))
4139 {
4140 pIoStorage->pVDIo = pVDIo;
4141 *ppIoStorage = pIoStorage;
4142 return VINF_SUCCESS;
4143 }
4144
4145 RTMemFree(pIoStorage->pTreeMetaXfers);
4146 }
4147 else
4148 rc = VERR_NO_MEMORY;
4149
4150 RTMemFree(pIoStorage);
4151 return rc;
4152}
4153
4154static int vdIOIntTreeMetaXferDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
4155{
4156 AssertMsgFailed(("Tree should be empty at this point!\n"));
4157 return VINF_SUCCESS;
4158}
4159
4160static int vdIOIntClose(void *pvUser, PVDIOSTORAGE pIoStorage)
4161{
4162 PVDIO pVDIo = (PVDIO)pvUser;
4163
4164 int rc = pVDIo->pInterfaceIo->pfnClose(pVDIo->pInterfaceIo->Core.pvUser,
4165 pIoStorage->pStorage);
4166 AssertRC(rc);
4167
4168 RTAvlrFileOffsetDestroy(pIoStorage->pTreeMetaXfers, vdIOIntTreeMetaXferDestroy, NULL);
4169 RTMemFree(pIoStorage->pTreeMetaXfers);
4170 RTMemFree(pIoStorage);
4171 return VINF_SUCCESS;
4172}
4173
4174static int vdIOIntDelete(void *pvUser, const char *pcszFilename)
4175{
4176 PVDIO pVDIo = (PVDIO)pvUser;
4177 return pVDIo->pInterfaceIo->pfnDelete(pVDIo->pInterfaceIo->Core.pvUser,
4178 pcszFilename);
4179}
4180
4181static int vdIOIntMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
4182 unsigned fMove)
4183{
4184 PVDIO pVDIo = (PVDIO)pvUser;
4185 return pVDIo->pInterfaceIo->pfnMove(pVDIo->pInterfaceIo->Core.pvUser,
4186 pcszSrc, pcszDst, fMove);
4187}
4188
4189static int vdIOIntGetFreeSpace(void *pvUser, const char *pcszFilename,
4190 int64_t *pcbFreeSpace)
4191{
4192 PVDIO pVDIo = (PVDIO)pvUser;
4193 return pVDIo->pInterfaceIo->pfnGetFreeSpace(pVDIo->pInterfaceIo->Core.pvUser,
4194 pcszFilename, pcbFreeSpace);
4195}
4196
4197static int vdIOIntGetModificationTime(void *pvUser, const char *pcszFilename,
4198 PRTTIMESPEC pModificationTime)
4199{
4200 PVDIO pVDIo = (PVDIO)pvUser;
4201 return pVDIo->pInterfaceIo->pfnGetModificationTime(pVDIo->pInterfaceIo->Core.pvUser,
4202 pcszFilename, pModificationTime);
4203}
4204
4205static int vdIOIntGetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
4206 uint64_t *pcbSize)
4207{
4208 PVDIO pVDIo = (PVDIO)pvUser;
4209 return pVDIo->pInterfaceIo->pfnGetSize(pVDIo->pInterfaceIo->Core.pvUser,
4210 pIoStorage->pStorage, pcbSize);
4211}
4212
4213static int vdIOIntSetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
4214 uint64_t cbSize)
4215{
4216 PVDIO pVDIo = (PVDIO)pvUser;
4217 return pVDIo->pInterfaceIo->pfnSetSize(pVDIo->pInterfaceIo->Core.pvUser,
4218 pIoStorage->pStorage, cbSize);
4219}
4220
4221static int vdIOIntReadUser(void *pvUser, PVDIOSTORAGE pIoStorage, uint64_t uOffset,
4222 PVDIOCTX pIoCtx, size_t cbRead)
4223{
4224 int rc = VINF_SUCCESS;
4225 PVDIO pVDIo = (PVDIO)pvUser;
4226 PVBOXHDD pDisk = pVDIo->pDisk;
4227
4228 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbRead=%u\n",
4229 pvUser, pIoStorage, uOffset, pIoCtx, cbRead));
4230
4231 /** @todo: Enable check for sync I/O later. */
4232 if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
4233 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
4234
4235 Assert(cbRead > 0);
4236
4237 if (pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC)
4238 {
4239 RTSGSEG Seg;
4240 unsigned cSegments = 1;
4241 size_t cbTaskRead = 0;
4242
4243 /* Synchronous I/O contexts only have one buffer segment. */
4244 AssertMsgReturn(pIoCtx->Req.Io.SgBuf.cSegs == 1,
4245 ("Invalid number of buffer segments for synchronous I/O context"),
4246 VERR_INVALID_PARAMETER);
4247
4248 cbTaskRead = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, &Seg, &cSegments, cbRead);
4249 Assert(cbRead == cbTaskRead);
4250 Assert(cSegments == 1);
4251 rc = pVDIo->pInterfaceIo->pfnReadSync(pVDIo->pInterfaceIo->Core.pvUser,
4252 pIoStorage->pStorage, uOffset,
4253 Seg.pvSeg, cbRead, NULL);
4254 }
4255 else
4256 {
4257 /* Build the S/G array and spawn a new I/O task */
4258 while (cbRead)
4259 {
4260 RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
4261 unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
4262 size_t cbTaskRead = 0;
4263
4264 cbTaskRead = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, aSeg, &cSegments, cbRead);
4265
4266 Assert(cSegments > 0);
4267 Assert(cbTaskRead > 0);
4268 AssertMsg(cbTaskRead <= cbRead, ("Invalid number of bytes to read\n"));
4269
4270 LogFlow(("Reading %u bytes into %u segments\n", cbTaskRead, cSegments));
4271
4272#ifdef RT_STRICT
4273 for (unsigned i = 0; i < cSegments; i++)
4274 AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
4275 ("Segment %u is invalid\n", i));
4276#endif
4277
4278 PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, NULL, NULL, pIoCtx, cbTaskRead);
4279
4280 if (!pIoTask)
4281 return VERR_NO_MEMORY;
4282
4283 ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
4284
4285 void *pvTask;
4286 rc = pVDIo->pInterfaceIo->pfnReadAsync(pVDIo->pInterfaceIo->Core.pvUser,
4287 pIoStorage->pStorage, uOffset,
4288 aSeg, cSegments, cbTaskRead, pIoTask,
4289 &pvTask);
4290 if (RT_SUCCESS(rc))
4291 {
4292 AssertMsg(cbTaskRead <= pIoCtx->Req.Io.cbTransferLeft, ("Impossible!\n"));
4293 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbTaskRead);
4294 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
4295 vdIoTaskFree(pDisk, pIoTask);
4296 }
4297 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
4298 {
4299 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
4300 vdIoTaskFree(pDisk, pIoTask);
4301 break;
4302 }
4303
4304 uOffset += cbTaskRead;
4305 cbRead -= cbTaskRead;
4306 }
4307 }
4308
4309 LogFlowFunc(("returns rc=%Rrc\n", rc));
4310 return rc;
4311}
4312
4313static int vdIOIntWriteUser(void *pvUser, PVDIOSTORAGE pIoStorage, uint64_t uOffset,
4314 PVDIOCTX pIoCtx, size_t cbWrite, PFNVDXFERCOMPLETED pfnComplete,
4315 void *pvCompleteUser)
4316{
4317 int rc = VINF_SUCCESS;
4318 PVDIO pVDIo = (PVDIO)pvUser;
4319 PVBOXHDD pDisk = pVDIo->pDisk;
4320
4321 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbWrite=%u\n",
4322 pvUser, pIoStorage, uOffset, pIoCtx, cbWrite));
4323
4324 /** @todo: Enable check for sync I/O later. */
4325 if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
4326 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
4327
4328 Assert(cbWrite > 0);
4329
4330 if (pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC)
4331 {
4332 RTSGSEG Seg;
4333 unsigned cSegments = 1;
4334 size_t cbTaskWrite = 0;
4335
4336 /* Synchronous I/O contexts only have one buffer segment. */
4337 AssertMsgReturn(pIoCtx->Req.Io.SgBuf.cSegs == 1,
4338 ("Invalid number of buffer segments for synchronous I/O context"),
4339 VERR_INVALID_PARAMETER);
4340
4341 cbTaskWrite = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, &Seg, &cSegments, cbWrite);
4342 Assert(cbWrite == cbTaskWrite);
4343 Assert(cSegments == 1);
4344 rc = pVDIo->pInterfaceIo->pfnWriteSync(pVDIo->pInterfaceIo->Core.pvUser,
4345 pIoStorage->pStorage, uOffset,
4346 Seg.pvSeg, cbWrite, NULL);
4347 }
4348 else
4349 {
4350 /* Build the S/G array and spawn a new I/O task */
4351 while (cbWrite)
4352 {
4353 RTSGSEG aSeg[VD_IO_TASK_SEGMENTS_MAX];
4354 unsigned cSegments = VD_IO_TASK_SEGMENTS_MAX;
4355 size_t cbTaskWrite = 0;
4356
4357 cbTaskWrite = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, aSeg, &cSegments, cbWrite);
4358
4359 Assert(cSegments > 0);
4360 Assert(cbTaskWrite > 0);
4361 AssertMsg(cbTaskWrite <= cbWrite, ("Invalid number of bytes to write\n"));
4362
4363 LogFlow(("Writing %u bytes from %u segments\n", cbTaskWrite, cSegments));
4364
4365#ifdef DEBUG
4366 for (unsigned i = 0; i < cSegments; i++)
4367 AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
4368 ("Segment %u is invalid\n", i));
4369#endif
4370
4371 PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, pfnComplete, pvCompleteUser, pIoCtx, cbTaskWrite);
4372
4373 if (!pIoTask)
4374 return VERR_NO_MEMORY;
4375
4376 ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
4377
4378 void *pvTask;
4379 rc = pVDIo->pInterfaceIo->pfnWriteAsync(pVDIo->pInterfaceIo->Core.pvUser,
4380 pIoStorage->pStorage,
4381 uOffset, aSeg, cSegments,
4382 cbTaskWrite, pIoTask, &pvTask);
4383 if (RT_SUCCESS(rc))
4384 {
4385 AssertMsg(cbTaskWrite <= pIoCtx->Req.Io.cbTransferLeft, ("Impossible!\n"));
4386 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbTaskWrite);
4387 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
4388 vdIoTaskFree(pDisk, pIoTask);
4389 }
4390 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
4391 {
4392 ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
4393 vdIoTaskFree(pDisk, pIoTask);
4394 break;
4395 }
4396
4397 uOffset += cbTaskWrite;
4398 cbWrite -= cbTaskWrite;
4399 }
4400 }
4401
4402 LogFlowFunc(("returns rc=%Rrc\n", rc));
4403 return rc;
4404}
4405
4406static int vdIOIntReadMeta(void *pvUser, PVDIOSTORAGE pIoStorage, uint64_t uOffset,
4407 void *pvBuf, size_t cbRead, PVDIOCTX pIoCtx,
4408 PPVDMETAXFER ppMetaXfer, PFNVDXFERCOMPLETED pfnComplete,
4409 void *pvCompleteUser)
4410{
4411 PVDIO pVDIo = (PVDIO)pvUser;
4412 PVBOXHDD pDisk = pVDIo->pDisk;
4413 int rc = VINF_SUCCESS;
4414 RTSGSEG Seg;
4415 PVDIOTASK pIoTask;
4416 PVDMETAXFER pMetaXfer = NULL;
4417 void *pvTask = NULL;
4418
4419 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbRead=%u\n",
4420 pvUser, pIoStorage, uOffset, pvBuf, cbRead));
4421
4422 AssertMsgReturn( pIoCtx
4423 || (!ppMetaXfer && !pfnComplete && !pvCompleteUser),
4424 ("A synchronous metadata read is requested but the parameters are wrong\n"),
4425 VERR_INVALID_POINTER);
4426
4427 /** @todo: Enable check for sync I/O later. */
4428 if ( pIoCtx
4429 && !(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
4430 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
4431
4432 if ( !pIoCtx
4433 || pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC)
4434 {
4435 /* Handle synchronous metadata I/O. */
4436 /** @todo: Integrate with metadata transfers below. */
4437 rc = pVDIo->pInterfaceIo->pfnReadSync(pVDIo->pInterfaceIo->Core.pvUser,
4438 pIoStorage->pStorage, uOffset,
4439 pvBuf, cbRead, NULL);
4440 if (ppMetaXfer)
4441 *ppMetaXfer = NULL;
4442 }
4443 else
4444 {
4445 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
4446 if (!pMetaXfer)
4447 {
4448#ifdef RT_STRICT
4449 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGetBestFit(pIoStorage->pTreeMetaXfers, uOffset, false /* fAbove */);
4450 AssertMsg(!pMetaXfer || (pMetaXfer->Core.Key + (RTFOFF)pMetaXfer->cbMeta <= (RTFOFF)uOffset),
4451 ("Overlapping meta transfers!\n"));
4452#endif
4453
4454 /* Allocate a new meta transfer. */
4455 pMetaXfer = vdMetaXferAlloc(pIoStorage, uOffset, cbRead);
4456 if (!pMetaXfer)
4457 return VERR_NO_MEMORY;
4458
4459 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
4460 if (!pIoTask)
4461 {
4462 RTMemFree(pMetaXfer);
4463 return VERR_NO_MEMORY;
4464 }
4465
4466 Seg.cbSeg = cbRead;
4467 Seg.pvSeg = pMetaXfer->abData;
4468
4469 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_READ);
4470 rc = pVDIo->pInterfaceIo->pfnReadAsync(pVDIo->pInterfaceIo->Core.pvUser,
4471 pIoStorage->pStorage,
4472 uOffset, &Seg, 1,
4473 cbRead, pIoTask, &pvTask);
4474
4475 if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4476 {
4477 bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
4478 Assert(fInserted);
4479 }
4480 else
4481 RTMemFree(pMetaXfer);
4482
4483 if (RT_SUCCESS(rc))
4484 {
4485 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
4486 vdIoTaskFree(pDisk, pIoTask);
4487 }
4488 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS && !pfnComplete)
4489 rc = VERR_VD_NOT_ENOUGH_METADATA;
4490 }
4491
4492 Assert(VALID_PTR(pMetaXfer) || RT_FAILURE(rc));
4493
4494 if (RT_SUCCESS(rc) || rc == VERR_VD_NOT_ENOUGH_METADATA || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4495 {
4496 /* If it is pending add the request to the list. */
4497 if (VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_READ)
4498 {
4499 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
4500 AssertPtr(pDeferred);
4501
4502 RTListInit(&pDeferred->NodeDeferred);
4503 pDeferred->pIoCtx = pIoCtx;
4504
4505 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
4506 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
4507 rc = VERR_VD_NOT_ENOUGH_METADATA;
4508 }
4509 else
4510 {
4511 /* Transfer the data. */
4512 pMetaXfer->cRefs++;
4513 Assert(pMetaXfer->cbMeta >= cbRead);
4514 Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
4515 memcpy(pvBuf, pMetaXfer->abData, cbRead);
4516 *ppMetaXfer = pMetaXfer;
4517 }
4518 }
4519 }
4520
4521 LogFlowFunc(("returns rc=%Rrc\n", rc));
4522 return rc;
4523}
4524
4525static int vdIOIntWriteMeta(void *pvUser, PVDIOSTORAGE pIoStorage, uint64_t uOffset,
4526 const void *pvBuf, size_t cbWrite, PVDIOCTX pIoCtx,
4527 PFNVDXFERCOMPLETED pfnComplete, void *pvCompleteUser)
4528{
4529 PVDIO pVDIo = (PVDIO)pvUser;
4530 PVBOXHDD pDisk = pVDIo->pDisk;
4531 int rc = VINF_SUCCESS;
4532 RTSGSEG Seg;
4533 PVDIOTASK pIoTask;
4534 PVDMETAXFER pMetaXfer = NULL;
4535 bool fInTree = false;
4536 void *pvTask = NULL;
4537
4538 LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbWrite=%u\n",
4539 pvUser, pIoStorage, uOffset, pvBuf, cbWrite));
4540
4541 AssertMsgReturn( pIoCtx
4542 || (!pfnComplete && !pvCompleteUser),
4543 ("A synchronous metadata write is requested but the parameters are wrong\n"),
4544 VERR_INVALID_POINTER);
4545
4546 /** @todo: Enable check for sync I/O later. */
4547 if ( pIoCtx
4548 && !(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
4549 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
4550
4551 if ( !pIoCtx
4552 || pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC)
4553 {
4554 /* Handle synchronous metadata I/O. */
4555 /** @todo: Integrate with metadata transfers below. */
4556 rc = pVDIo->pInterfaceIo->pfnWriteSync(pVDIo->pInterfaceIo->Core.pvUser,
4557 pIoStorage->pStorage, uOffset,
4558 pvBuf, cbWrite, NULL);
4559 }
4560 else
4561 {
4562 pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
4563 if (!pMetaXfer)
4564 {
4565 /* Allocate a new meta transfer. */
4566 pMetaXfer = vdMetaXferAlloc(pIoStorage, uOffset, cbWrite);
4567 if (!pMetaXfer)
4568 return VERR_NO_MEMORY;
4569 }
4570 else
4571 {
4572 Assert(pMetaXfer->cbMeta >= cbWrite);
4573 Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
4574 fInTree = true;
4575 }
4576
4577 Assert(VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE);
4578
4579 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
4580 if (!pIoTask)
4581 {
4582 RTMemFree(pMetaXfer);
4583 return VERR_NO_MEMORY;
4584 }
4585
4586 memcpy(pMetaXfer->abData, pvBuf, cbWrite);
4587 Seg.cbSeg = cbWrite;
4588 Seg.pvSeg = pMetaXfer->abData;
4589
4590 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
4591
4592 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_WRITE);
4593 rc = pVDIo->pInterfaceIo->pfnWriteAsync(pVDIo->pInterfaceIo->Core.pvUser,
4594 pIoStorage->pStorage,
4595 uOffset, &Seg, 1, cbWrite, pIoTask,
4596 &pvTask);
4597 if (RT_SUCCESS(rc))
4598 {
4599 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
4600 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
4601 vdIoTaskFree(pDisk, pIoTask);
4602 if (fInTree && !pMetaXfer->cRefs)
4603 {
4604 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
4605 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
4606 AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
4607 RTMemFree(pMetaXfer);
4608 pMetaXfer = NULL;
4609 }
4610 }
4611 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4612 {
4613 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
4614 AssertPtr(pDeferred);
4615
4616 RTListInit(&pDeferred->NodeDeferred);
4617 pDeferred->pIoCtx = pIoCtx;
4618
4619 if (!fInTree)
4620 {
4621 bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
4622 Assert(fInserted);
4623 }
4624
4625 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
4626 }
4627 else
4628 {
4629 RTMemFree(pMetaXfer);
4630 pMetaXfer = NULL;
4631 }
4632 }
4633
4634 LogFlowFunc(("returns rc=%Rrc\n", rc));
4635 return rc;
4636}
4637
4638static void vdIOIntMetaXferRelease(void *pvUser, PVDMETAXFER pMetaXfer)
4639{
4640 PVDIO pVDIo = (PVDIO)pvUser;
4641 PVBOXHDD pDisk = pVDIo->pDisk;
4642 PVDIOSTORAGE pIoStorage;
4643
4644 /*
4645 * It is possible that we get called with a NULL metadata xfer handle
4646 * for synchronous I/O. Just exit.
4647 */
4648 if (!pMetaXfer)
4649 return;
4650
4651 pIoStorage = pMetaXfer->pIoStorage;
4652
4653 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
4654
4655 Assert( VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE
4656 || VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_WRITE);
4657 Assert(pMetaXfer->cRefs > 0);
4658
4659 pMetaXfer->cRefs--;
4660 if ( !pMetaXfer->cRefs
4661 && RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting)
4662 && VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE)
4663 {
4664 /* Free the meta data entry. */
4665 LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
4666 bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
4667 AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
4668
4669 RTMemFree(pMetaXfer);
4670 }
4671}
4672
4673static int vdIOIntFlush(void *pvUser, PVDIOSTORAGE pIoStorage, PVDIOCTX pIoCtx,
4674 PFNVDXFERCOMPLETED pfnComplete, void *pvCompleteUser)
4675{
4676 PVDIO pVDIo = (PVDIO)pvUser;
4677 PVBOXHDD pDisk = pVDIo->pDisk;
4678 int rc = VINF_SUCCESS;
4679 PVDIOTASK pIoTask;
4680 PVDMETAXFER pMetaXfer = NULL;
4681 void *pvTask = NULL;
4682
4683 LogFlowFunc(("pvUser=%#p pIoStorage=%#p pIoCtx=%#p\n",
4684 pvUser, pIoStorage, pIoCtx));
4685
4686 AssertMsgReturn( pIoCtx
4687 || (!pfnComplete && !pvCompleteUser),
4688 ("A synchronous metadata write is requested but the parameters are wrong\n"),
4689 VERR_INVALID_POINTER);
4690
4691 /** @todo: Enable check for sync I/O later. */
4692 if ( pIoCtx
4693 && !(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
4694 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
4695
4696 if (pVDIo->fIgnoreFlush)
4697 return VINF_SUCCESS;
4698
4699 if ( !pIoCtx
4700 || pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC)
4701 {
4702 /* Handle synchronous flushes. */
4703 /** @todo: Integrate with metadata transfers below. */
4704 rc = pVDIo->pInterfaceIo->pfnFlushSync(pVDIo->pInterfaceIo->Core.pvUser,
4705 pIoStorage->pStorage);
4706 }
4707 else
4708 {
4709 /* Allocate a new meta transfer. */
4710 pMetaXfer = vdMetaXferAlloc(pIoStorage, 0, 0);
4711 if (!pMetaXfer)
4712 return VERR_NO_MEMORY;
4713
4714 pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvUser, pMetaXfer);
4715 if (!pIoTask)
4716 {
4717 RTMemFree(pMetaXfer);
4718 return VERR_NO_MEMORY;
4719 }
4720
4721 ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
4722
4723 PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
4724 AssertPtr(pDeferred);
4725
4726 RTListInit(&pDeferred->NodeDeferred);
4727 pDeferred->pIoCtx = pIoCtx;
4728
4729 RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
4730 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_FLUSH);
4731 rc = pVDIo->pInterfaceIo->pfnFlushAsync(pVDIo->pInterfaceIo->Core.pvUser,
4732 pIoStorage->pStorage,
4733 pIoTask, &pvTask);
4734 if (RT_SUCCESS(rc))
4735 {
4736 VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
4737 ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
4738 vdIoTaskFree(pDisk, pIoTask);
4739 RTMemFree(pDeferred);
4740 RTMemFree(pMetaXfer);
4741 }
4742 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
4743 RTMemFree(pMetaXfer);
4744 }
4745
4746 LogFlowFunc(("returns rc=%Rrc\n", rc));
4747 return rc;
4748}
4749
4750static size_t vdIOIntIoCtxCopyTo(void *pvUser, PVDIOCTX pIoCtx,
4751 const void *pvBuf, size_t cbBuf)
4752{
4753 PVDIO pVDIo = (PVDIO)pvUser;
4754 PVBOXHDD pDisk = pVDIo->pDisk;
4755 size_t cbCopied = 0;
4756
4757 /** @todo: Enable check for sync I/O later. */
4758 if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
4759 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
4760
4761 cbCopied = vdIoCtxCopyTo(pIoCtx, (uint8_t *)pvBuf, cbBuf);
4762 Assert(cbCopied == cbBuf);
4763
4764 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbCopied);
4765
4766 return cbCopied;
4767}
4768
4769static size_t vdIOIntIoCtxCopyFrom(void *pvUser, PVDIOCTX pIoCtx,
4770 void *pvBuf, size_t cbBuf)
4771{
4772 PVDIO pVDIo = (PVDIO)pvUser;
4773 PVBOXHDD pDisk = pVDIo->pDisk;
4774 size_t cbCopied = 0;
4775
4776 /** @todo: Enable check for sync I/O later. */
4777 if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
4778 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
4779
4780 cbCopied = vdIoCtxCopyFrom(pIoCtx, (uint8_t *)pvBuf, cbBuf);
4781 Assert(cbCopied == cbBuf);
4782
4783 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbCopied);
4784
4785 return cbCopied;
4786}
4787
4788static size_t vdIOIntIoCtxSet(void *pvUser, PVDIOCTX pIoCtx, int ch, size_t cb)
4789{
4790 PVDIO pVDIo = (PVDIO)pvUser;
4791 PVBOXHDD pDisk = pVDIo->pDisk;
4792 size_t cbSet = 0;
4793
4794 /** @todo: Enable check for sync I/O later. */
4795 if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
4796 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
4797
4798 cbSet = vdIoCtxSet(pIoCtx, ch, cb);
4799 Assert(cbSet == cb);
4800
4801 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbSet);
4802
4803 return cbSet;
4804}
4805
4806static size_t vdIOIntIoCtxSegArrayCreate(void *pvUser, PVDIOCTX pIoCtx,
4807 PRTSGSEG paSeg, unsigned *pcSeg,
4808 size_t cbData)
4809{
4810 PVDIO pVDIo = (PVDIO)pvUser;
4811 PVBOXHDD pDisk = pVDIo->pDisk;
4812 size_t cbCreated = 0;
4813
4814 /** @todo: Enable check for sync I/O later. */
4815 if (!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC))
4816 VD_THREAD_IS_CRITSECT_OWNER(pDisk);
4817
4818 cbCreated = RTSgBufSegArrayCreate(&pIoCtx->Req.Io.SgBuf, paSeg, pcSeg, cbData);
4819 Assert(!paSeg || cbData == cbCreated);
4820
4821 return cbCreated;
4822}
4823
4824static void vdIOIntIoCtxCompleted(void *pvUser, PVDIOCTX pIoCtx, int rcReq,
4825 size_t cbCompleted)
4826{
4827 PVDIO pVDIo = (PVDIO)pvUser;
4828 PVBOXHDD pDisk = pVDIo->pDisk;
4829
4830 /*
4831 * Grab the disk critical section to avoid races with other threads which
4832 * might still modify the I/O context.
4833 * Example is that iSCSI is doing an asynchronous write but calls us already
4834 * while the other thread is still hanging in vdWriteHelperAsync and couldn't update
4835 * the blocked state yet.
4836 * It can overwrite the state to true before we call vdIoCtxContinue and the
4837 * the request would hang indefinite.
4838 */
4839 int rc = RTCritSectEnter(&pDisk->CritSect);
4840 AssertRC(rc);
4841
4842 /* Continue */
4843 pIoCtx->fFlags &= ~VDIOCTX_FLAGS_BLOCKED;
4844 ASMAtomicSubU32(&pIoCtx->Req.Io.cbTransferLeft, cbCompleted);
4845
4846 /* Clear the pointer to next transfer function in case we have nothing to transfer anymore.
4847 * @todo: Find a better way to prevent vdIoCtxContinue from calling the read/write helper again. */
4848 if (!pIoCtx->Req.Io.cbTransferLeft)
4849 pIoCtx->pfnIoCtxTransfer = NULL;
4850
4851 vdIoCtxContinue(pIoCtx, rcReq);
4852
4853 vdDiskCritSectLeave(pDisk, NULL);
4854}
4855
4856static DECLCALLBACK(bool) vdIOIntIoCtxIsSynchronous(void *pvUser, PVDIOCTX pIoCtx)
4857{
4858 NOREF(pvUser);
4859 return !!(pIoCtx->fFlags & VDIOCTX_FLAGS_SYNC);
4860}
4861
4862static DECLCALLBACK(bool) vdIOIntIoCtxIsZero(void *pvUser, PVDIOCTX pIoCtx, size_t cbCheck,
4863 bool fAdvance)
4864{
4865 NOREF(pvUser);
4866
4867 bool fIsZero = RTSgBufIsZero(&pIoCtx->Req.Io.SgBuf, cbCheck);
4868 if (fIsZero && fAdvance)
4869 RTSgBufAdvance(&pIoCtx->Req.Io.SgBuf, cbCheck);
4870
4871 return fIsZero;
4872}
4873
4874/**
4875 * VD I/O interface callback for opening a file (limited version for VDGetFormat).
4876 */
4877static int vdIOIntOpenLimited(void *pvUser, const char *pszLocation,
4878 uint32_t fOpen, PPVDIOSTORAGE ppIoStorage)
4879{
4880 int rc = VINF_SUCCESS;
4881 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4882 PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
4883
4884 if (!pIoStorage)
4885 return VERR_NO_MEMORY;
4886
4887 rc = pInterfaceIo->pfnOpen(NULL, pszLocation, fOpen, NULL, &pIoStorage->pStorage);
4888 if (RT_SUCCESS(rc))
4889 *ppIoStorage = pIoStorage;
4890 else
4891 RTMemFree(pIoStorage);
4892
4893 return rc;
4894}
4895
4896static int vdIOIntCloseLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
4897{
4898 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4899 int rc = pInterfaceIo->pfnClose(NULL, pIoStorage->pStorage);
4900 AssertRC(rc);
4901
4902 RTMemFree(pIoStorage);
4903 return VINF_SUCCESS;
4904}
4905
4906static int vdIOIntDeleteLimited(void *pvUser, const char *pcszFilename)
4907{
4908 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4909 return pInterfaceIo->pfnDelete(NULL, pcszFilename);
4910}
4911
4912static int vdIOIntMoveLimited(void *pvUser, const char *pcszSrc,
4913 const char *pcszDst, unsigned fMove)
4914{
4915 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4916 return pInterfaceIo->pfnMove(NULL, pcszSrc, pcszDst, fMove);
4917}
4918
4919static int vdIOIntGetFreeSpaceLimited(void *pvUser, const char *pcszFilename,
4920 int64_t *pcbFreeSpace)
4921{
4922 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4923 return pInterfaceIo->pfnGetFreeSpace(NULL, pcszFilename, pcbFreeSpace);
4924}
4925
4926static int vdIOIntGetModificationTimeLimited(void *pvUser,
4927 const char *pcszFilename,
4928 PRTTIMESPEC pModificationTime)
4929{
4930 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4931 return pInterfaceIo->pfnGetModificationTime(NULL, pcszFilename, pModificationTime);
4932}
4933
4934static int vdIOIntGetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
4935 uint64_t *pcbSize)
4936{
4937 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4938 return pInterfaceIo->pfnGetSize(NULL, pIoStorage->pStorage, pcbSize);
4939}
4940
4941static int vdIOIntSetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
4942 uint64_t cbSize)
4943{
4944 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4945 return pInterfaceIo->pfnSetSize(NULL, pIoStorage->pStorage, cbSize);
4946}
4947
4948static int vdIOIntWriteUserLimited(void *pvUser, PVDIOSTORAGE pStorage,
4949 uint64_t uOffset, PVDIOCTX pIoCtx,
4950 size_t cbWrite,
4951 PFNVDXFERCOMPLETED pfnComplete,
4952 void *pvCompleteUser)
4953{
4954 NOREF(pvUser);
4955 NOREF(pStorage);
4956 NOREF(uOffset);
4957 NOREF(pIoCtx);
4958 NOREF(cbWrite);
4959 NOREF(pfnComplete);
4960 NOREF(pvCompleteUser);
4961 AssertMsgFailedReturn(("This needs to be implemented when called\n"), VERR_NOT_IMPLEMENTED);
4962}
4963
4964static int vdIOIntReadUserLimited(void *pvUser, PVDIOSTORAGE pStorage,
4965 uint64_t uOffset, PVDIOCTX pIoCtx,
4966 size_t cbRead)
4967{
4968 NOREF(pvUser);
4969 NOREF(pStorage);
4970 NOREF(uOffset);
4971 NOREF(pIoCtx);
4972 NOREF(cbRead);
4973 AssertMsgFailedReturn(("This needs to be implemented when called\n"), VERR_NOT_IMPLEMENTED);
4974}
4975
4976static int vdIOIntWriteMetaLimited(void *pvUser, PVDIOSTORAGE pStorage,
4977 uint64_t uOffset, const void *pvBuffer,
4978 size_t cbBuffer, PVDIOCTX pIoCtx,
4979 PFNVDXFERCOMPLETED pfnComplete,
4980 void *pvCompleteUser)
4981{
4982 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4983
4984 AssertMsgReturn(!pIoCtx && !pfnComplete && !pvCompleteUser,
4985 ("Async I/O not implemented for the limited interface"),
4986 VERR_NOT_SUPPORTED);
4987
4988 return pInterfaceIo->pfnWriteSync(NULL, pStorage->pStorage, uOffset, pvBuffer, cbBuffer, NULL);
4989}
4990
4991static int vdIOIntReadMetaLimited(void *pvUser, PVDIOSTORAGE pStorage,
4992 uint64_t uOffset, void *pvBuffer,
4993 size_t cbBuffer, PVDIOCTX pIoCtx,
4994 PPVDMETAXFER ppMetaXfer,
4995 PFNVDXFERCOMPLETED pfnComplete,
4996 void *pvCompleteUser)
4997{
4998 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
4999
5000 AssertMsgReturn(!pIoCtx && !ppMetaXfer && !pfnComplete && !pvCompleteUser,
5001 ("Async I/O not implemented for the limited interface"),
5002 VERR_NOT_SUPPORTED);
5003
5004 return pInterfaceIo->pfnReadSync(NULL, pStorage->pStorage, uOffset, pvBuffer, cbBuffer, NULL);
5005}
5006
5007static int vdIOIntMetaXferReleaseLimited(void *pvUser, PVDMETAXFER pMetaXfer)
5008{
5009 /* This is a NOP in this case. */
5010 NOREF(pvUser);
5011 NOREF(pMetaXfer);
5012 return VINF_SUCCESS;
5013}
5014
5015static int vdIOIntFlushLimited(void *pvUser, PVDIOSTORAGE pStorage,
5016 PVDIOCTX pIoCtx,
5017 PFNVDXFERCOMPLETED pfnComplete,
5018 void *pvCompleteUser)
5019{
5020 PVDINTERFACEIO pInterfaceIo = (PVDINTERFACEIO)pvUser;
5021
5022 AssertMsgReturn(!pIoCtx && !pfnComplete && !pvCompleteUser,
5023 ("Async I/O not implemented for the limited interface"),
5024 VERR_NOT_SUPPORTED);
5025
5026 return pInterfaceIo->pfnFlushSync(NULL, pStorage->pStorage);
5027}
5028
5029/**
5030 * internal: send output to the log (unconditionally).
5031 */
5032int vdLogMessage(void *pvUser, const char *pszFormat, va_list args)
5033{
5034 NOREF(pvUser);
5035 RTLogPrintfV(pszFormat, args);
5036 return VINF_SUCCESS;
5037}
5038
5039DECLINLINE(int) vdMessageWrapper(PVBOXHDD pDisk, const char *pszFormat, ...)
5040{
5041 va_list va;
5042 va_start(va, pszFormat);
5043 int rc = pDisk->pInterfaceError->pfnMessage(pDisk->pInterfaceError->Core.pvUser,
5044 pszFormat, va);
5045 va_end(va);
5046 return rc;
5047}
5048
5049
5050/**
5051 * internal: adjust PCHS geometry
5052 */
5053static void vdFixupPCHSGeometry(PVDGEOMETRY pPCHS, uint64_t cbSize)
5054{
5055 /* Fix broken PCHS geometry. Can happen for two reasons: either the backend
5056 * mixes up PCHS and LCHS, or the application used to create the source
5057 * image has put garbage in it. Additionally, if the PCHS geometry covers
5058 * more than the image size, set it back to the default. */
5059 if ( pPCHS->cHeads > 16
5060 || pPCHS->cSectors > 63
5061 || pPCHS->cCylinders == 0
5062 || (uint64_t)pPCHS->cHeads * pPCHS->cSectors * pPCHS->cCylinders * 512 > cbSize)
5063 {
5064 Assert(!(RT_MIN(cbSize / 512 / 16 / 63, 16383) - (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383)));
5065 pPCHS->cCylinders = (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383);
5066 pPCHS->cHeads = 16;
5067 pPCHS->cSectors = 63;
5068 }
5069}
5070
5071/**
5072 * internal: adjust PCHS geometry
5073 */
5074static void vdFixupLCHSGeometry(PVDGEOMETRY pLCHS, uint64_t cbSize)
5075{
5076 /* Fix broken LCHS geometry. Can happen for two reasons: either the backend
5077 * mixes up PCHS and LCHS, or the application used to create the source
5078 * image has put garbage in it. The fix in this case is to clear the LCHS
5079 * geometry to trigger autodetection when it is used next. If the geometry
5080 * already says "please autodetect" (cylinders=0) keep it. */
5081 if ( ( pLCHS->cHeads > 255
5082 || pLCHS->cHeads == 0
5083 || pLCHS->cSectors > 63
5084 || pLCHS->cSectors == 0)
5085 && pLCHS->cCylinders != 0)
5086 {
5087 pLCHS->cCylinders = 0;
5088 pLCHS->cHeads = 0;
5089 pLCHS->cSectors = 0;
5090 }
5091 /* Always recompute the number of cylinders stored in the LCHS
5092 * geometry if it isn't set to "autotedetect" at the moment.
5093 * This is very useful if the destination image size is
5094 * larger or smaller than the source image size. Do not modify
5095 * the number of heads and sectors. Windows guests hate it. */
5096 if ( pLCHS->cCylinders != 0
5097 && pLCHS->cHeads != 0 /* paranoia */
5098 && pLCHS->cSectors != 0 /* paranoia */)
5099 {
5100 Assert(!(RT_MIN(cbSize / 512 / pLCHS->cHeads / pLCHS->cSectors, 1024) - (uint32_t)RT_MIN(cbSize / 512 / pLCHS->cHeads / pLCHS->cSectors, 1024)));
5101 pLCHS->cCylinders = (uint32_t)RT_MIN(cbSize / 512 / pLCHS->cHeads / pLCHS->cSectors, 1024);
5102 }
5103}
5104
5105/**
5106 * Sets the I/O callbacks of the given interface to the fallback methods
5107 *
5108 * @returns nothing.
5109 * @param pIfIo The I/O interface to setup.
5110 */
5111static void vdIfIoFallbackCallbacksSetup(PVDINTERFACEIO pIfIo)
5112{
5113 pIfIo->pfnOpen = vdIOOpenFallback;
5114 pIfIo->pfnClose = vdIOCloseFallback;
5115 pIfIo->pfnDelete = vdIODeleteFallback;
5116 pIfIo->pfnMove = vdIOMoveFallback;
5117 pIfIo->pfnGetFreeSpace = vdIOGetFreeSpaceFallback;
5118 pIfIo->pfnGetModificationTime = vdIOGetModificationTimeFallback;
5119 pIfIo->pfnGetSize = vdIOGetSizeFallback;
5120 pIfIo->pfnSetSize = vdIOSetSizeFallback;
5121 pIfIo->pfnReadSync = vdIOReadSyncFallback;
5122 pIfIo->pfnWriteSync = vdIOWriteSyncFallback;
5123 pIfIo->pfnFlushSync = vdIOFlushSyncFallback;
5124 pIfIo->pfnReadAsync = vdIOReadAsyncFallback;
5125 pIfIo->pfnWriteAsync = vdIOWriteAsyncFallback;
5126 pIfIo->pfnFlushAsync = vdIOFlushAsyncFallback;
5127}
5128
5129/**
5130 * Sets the internal I/O callbacks of the given interface.
5131 *
5132 * @returns nothing.
5133 * @param pIfIoInt The internal I/O interface to setup.
5134 */
5135static void vdIfIoIntCallbacksSetup(PVDINTERFACEIOINT pIfIoInt)
5136{
5137 pIfIoInt->pfnOpen = vdIOIntOpen;
5138 pIfIoInt->pfnClose = vdIOIntClose;
5139 pIfIoInt->pfnDelete = vdIOIntDelete;
5140 pIfIoInt->pfnMove = vdIOIntMove;
5141 pIfIoInt->pfnGetFreeSpace = vdIOIntGetFreeSpace;
5142 pIfIoInt->pfnGetModificationTime = vdIOIntGetModificationTime;
5143 pIfIoInt->pfnGetSize = vdIOIntGetSize;
5144 pIfIoInt->pfnSetSize = vdIOIntSetSize;
5145 pIfIoInt->pfnReadUser = vdIOIntReadUser;
5146 pIfIoInt->pfnWriteUser = vdIOIntWriteUser;
5147 pIfIoInt->pfnReadMeta = vdIOIntReadMeta;
5148 pIfIoInt->pfnWriteMeta = vdIOIntWriteMeta;
5149 pIfIoInt->pfnMetaXferRelease = vdIOIntMetaXferRelease;
5150 pIfIoInt->pfnFlush = vdIOIntFlush;
5151 pIfIoInt->pfnIoCtxCopyFrom = vdIOIntIoCtxCopyFrom;
5152 pIfIoInt->pfnIoCtxCopyTo = vdIOIntIoCtxCopyTo;
5153 pIfIoInt->pfnIoCtxSet = vdIOIntIoCtxSet;
5154 pIfIoInt->pfnIoCtxSegArrayCreate = vdIOIntIoCtxSegArrayCreate;
5155 pIfIoInt->pfnIoCtxCompleted = vdIOIntIoCtxCompleted;
5156 pIfIoInt->pfnIoCtxIsSynchronous = vdIOIntIoCtxIsSynchronous;
5157 pIfIoInt->pfnIoCtxIsZero = vdIOIntIoCtxIsZero;
5158}
5159
5160/**
5161 * Initializes HDD backends.
5162 *
5163 * @returns VBox status code.
5164 */
5165VBOXDDU_DECL(int) VDInit(void)
5166{
5167 int rc = vdAddBackends(aStaticBackends, RT_ELEMENTS(aStaticBackends));
5168 if (RT_SUCCESS(rc))
5169 {
5170 rc = vdAddCacheBackends(aStaticCacheBackends, RT_ELEMENTS(aStaticCacheBackends));
5171 if (RT_SUCCESS(rc))
5172 {
5173 rc = vdLoadDynamicBackends();
5174 if (RT_SUCCESS(rc))
5175 rc = vdLoadDynamicCacheBackends();
5176 }
5177 }
5178 LogRel(("VDInit finished\n"));
5179 return rc;
5180}
5181
5182/**
5183 * Destroys loaded HDD backends.
5184 *
5185 * @returns VBox status code.
5186 */
5187VBOXDDU_DECL(int) VDShutdown(void)
5188{
5189 PVBOXHDDBACKEND *pBackends = g_apBackends;
5190 PVDCACHEBACKEND *pCacheBackends = g_apCacheBackends;
5191 unsigned cBackends = g_cBackends;
5192
5193 if (!pBackends)
5194 return VERR_INTERNAL_ERROR;
5195
5196 g_cBackends = 0;
5197 g_apBackends = NULL;
5198
5199#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
5200 for (unsigned i = 0; i < cBackends; i++)
5201 if (pBackends[i]->hPlugin != NIL_RTLDRMOD)
5202 RTLdrClose(pBackends[i]->hPlugin);
5203#endif
5204
5205 /* Clear the supported cache backends. */
5206 cBackends = g_cCacheBackends;
5207 g_cCacheBackends = 0;
5208 g_apCacheBackends = NULL;
5209
5210#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
5211 for (unsigned i = 0; i < cBackends; i++)
5212 if (pCacheBackends[i]->hPlugin != NIL_RTLDRMOD)
5213 RTLdrClose(pCacheBackends[i]->hPlugin);
5214#endif
5215
5216 if (pCacheBackends)
5217 RTMemFree(pCacheBackends);
5218 RTMemFree(pBackends);
5219 return VINF_SUCCESS;
5220}
5221
5222
5223/**
5224 * Lists all HDD backends and their capabilities in a caller-provided buffer.
5225 *
5226 * @returns VBox status code.
5227 * VERR_BUFFER_OVERFLOW if not enough space is passed.
5228 * @param cEntriesAlloc Number of list entries available.
5229 * @param pEntries Pointer to array for the entries.
5230 * @param pcEntriesUsed Number of entries returned.
5231 */
5232VBOXDDU_DECL(int) VDBackendInfo(unsigned cEntriesAlloc, PVDBACKENDINFO pEntries,
5233 unsigned *pcEntriesUsed)
5234{
5235 int rc = VINF_SUCCESS;
5236 PRTDIR pPluginDir = NULL;
5237 unsigned cEntries = 0;
5238
5239 LogFlowFunc(("cEntriesAlloc=%u pEntries=%#p pcEntriesUsed=%#p\n", cEntriesAlloc, pEntries, pcEntriesUsed));
5240 /* Check arguments. */
5241 AssertMsgReturn(cEntriesAlloc,
5242 ("cEntriesAlloc=%u\n", cEntriesAlloc),
5243 VERR_INVALID_PARAMETER);
5244 AssertMsgReturn(VALID_PTR(pEntries),
5245 ("pEntries=%#p\n", pEntries),
5246 VERR_INVALID_PARAMETER);
5247 AssertMsgReturn(VALID_PTR(pcEntriesUsed),
5248 ("pcEntriesUsed=%#p\n", pcEntriesUsed),
5249 VERR_INVALID_PARAMETER);
5250 if (!g_apBackends)
5251 VDInit();
5252
5253 if (cEntriesAlloc < g_cBackends)
5254 {
5255 *pcEntriesUsed = g_cBackends;
5256 return VERR_BUFFER_OVERFLOW;
5257 }
5258
5259 for (unsigned i = 0; i < g_cBackends; i++)
5260 {
5261 pEntries[i].pszBackend = g_apBackends[i]->pszBackendName;
5262 pEntries[i].uBackendCaps = g_apBackends[i]->uBackendCaps;
5263 pEntries[i].paFileExtensions = g_apBackends[i]->paFileExtensions;
5264 pEntries[i].paConfigInfo = g_apBackends[i]->paConfigInfo;
5265 pEntries[i].pfnComposeLocation = g_apBackends[i]->pfnComposeLocation;
5266 pEntries[i].pfnComposeName = g_apBackends[i]->pfnComposeName;
5267 }
5268
5269 LogFlowFunc(("returns %Rrc *pcEntriesUsed=%u\n", rc, cEntries));
5270 *pcEntriesUsed = g_cBackends;
5271 return rc;
5272}
5273
5274/**
5275 * Lists the capabilities of a backend identified by its name.
5276 *
5277 * @returns VBox status code.
5278 * @param pszBackend The backend name.
5279 * @param pEntries Pointer to an entry.
5280 */
5281VBOXDDU_DECL(int) VDBackendInfoOne(const char *pszBackend, PVDBACKENDINFO pEntry)
5282{
5283 LogFlowFunc(("pszBackend=%#p pEntry=%#p\n", pszBackend, pEntry));
5284 /* Check arguments. */
5285 AssertMsgReturn(VALID_PTR(pszBackend),
5286 ("pszBackend=%#p\n", pszBackend),
5287 VERR_INVALID_PARAMETER);
5288 AssertMsgReturn(VALID_PTR(pEntry),
5289 ("pEntry=%#p\n", pEntry),
5290 VERR_INVALID_PARAMETER);
5291 if (!g_apBackends)
5292 VDInit();
5293
5294 /* Go through loaded backends. */
5295 for (unsigned i = 0; i < g_cBackends; i++)
5296 {
5297 if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
5298 {
5299 pEntry->pszBackend = g_apBackends[i]->pszBackendName;
5300 pEntry->uBackendCaps = g_apBackends[i]->uBackendCaps;
5301 pEntry->paFileExtensions = g_apBackends[i]->paFileExtensions;
5302 pEntry->paConfigInfo = g_apBackends[i]->paConfigInfo;
5303 return VINF_SUCCESS;
5304 }
5305 }
5306
5307 return VERR_NOT_FOUND;
5308}
5309
5310/**
5311 * Allocates and initializes an empty HDD container.
5312 * No image files are opened.
5313 *
5314 * @returns VBox status code.
5315 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
5316 * @param enmType Type of the image container.
5317 * @param ppDisk Where to store the reference to HDD container.
5318 */
5319VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, VDTYPE enmType, PVBOXHDD *ppDisk)
5320{
5321 int rc = VINF_SUCCESS;
5322 PVBOXHDD pDisk = NULL;
5323
5324 LogFlowFunc(("pVDIfsDisk=%#p\n", pVDIfsDisk));
5325 do
5326 {
5327 /* Check arguments. */
5328 AssertMsgBreakStmt(VALID_PTR(ppDisk),
5329 ("ppDisk=%#p\n", ppDisk),
5330 rc = VERR_INVALID_PARAMETER);
5331
5332 pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
5333 if (pDisk)
5334 {
5335 pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
5336 pDisk->enmType = enmType;
5337 pDisk->cImages = 0;
5338 pDisk->pBase = NULL;
5339 pDisk->pLast = NULL;
5340 pDisk->cbSize = 0;
5341 pDisk->PCHSGeometry.cCylinders = 0;
5342 pDisk->PCHSGeometry.cHeads = 0;
5343 pDisk->PCHSGeometry.cSectors = 0;
5344 pDisk->LCHSGeometry.cCylinders = 0;
5345 pDisk->LCHSGeometry.cHeads = 0;
5346 pDisk->LCHSGeometry.cSectors = 0;
5347 pDisk->pVDIfsDisk = pVDIfsDisk;
5348 pDisk->pInterfaceError = NULL;
5349 pDisk->pInterfaceThreadSync = NULL;
5350 pDisk->fLocked = false;
5351 pDisk->pIoCtxLockOwner = NULL;
5352 pDisk->pIoCtxHead = NULL;
5353 RTListInit(&pDisk->ListWriteLocked);
5354
5355 /* Create the I/O ctx cache */
5356 rc = RTMemCacheCreate(&pDisk->hMemCacheIoCtx, sizeof(VDIOCTX), 0, UINT32_MAX,
5357 NULL, NULL, NULL, 0);
5358 if (RT_FAILURE(rc))
5359 {
5360 RTMemFree(pDisk);
5361 break;
5362 }
5363
5364 /* Create the I/O task cache */
5365 rc = RTMemCacheCreate(&pDisk->hMemCacheIoTask, sizeof(VDIOTASK), 0, UINT32_MAX,
5366 NULL, NULL, NULL, 0);
5367 if (RT_FAILURE(rc))
5368 {
5369 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
5370 RTMemFree(pDisk);
5371 break;
5372 }
5373
5374 /* Create critical section. */
5375 rc = RTCritSectInit(&pDisk->CritSect);
5376 if (RT_FAILURE(rc))
5377 {
5378 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
5379 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
5380 RTMemFree(pDisk);
5381 break;
5382 }
5383
5384 pDisk->pInterfaceError = VDIfErrorGet(pVDIfsDisk);
5385 pDisk->pInterfaceThreadSync = VDIfThreadSyncGet(pVDIfsDisk);
5386
5387 *ppDisk = pDisk;
5388 }
5389 else
5390 {
5391 rc = VERR_NO_MEMORY;
5392 break;
5393 }
5394 } while (0);
5395
5396 LogFlowFunc(("returns %Rrc (pDisk=%#p)\n", rc, pDisk));
5397 return rc;
5398}
5399
5400/**
5401 * Destroys HDD container.
5402 * If container has opened image files they will be closed.
5403 *
5404 * @returns VBox status code.
5405 * @param pDisk Pointer to HDD container.
5406 */
5407VBOXDDU_DECL(int) VDDestroy(PVBOXHDD pDisk)
5408{
5409 int rc = VINF_SUCCESS;
5410 LogFlowFunc(("pDisk=%#p\n", pDisk));
5411 do
5412 {
5413 /* sanity check */
5414 AssertPtrBreak(pDisk);
5415 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5416 rc = VDCloseAll(pDisk);
5417 RTCritSectDelete(&pDisk->CritSect);
5418 RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
5419 RTMemCacheDestroy(pDisk->hMemCacheIoTask);
5420 RTMemFree(pDisk);
5421 } while (0);
5422 LogFlowFunc(("returns %Rrc\n", rc));
5423 return rc;
5424}
5425
5426/**
5427 * Try to get the backend name which can use this image.
5428 *
5429 * @returns VBox status code.
5430 * VINF_SUCCESS if a plugin was found.
5431 * ppszFormat contains the string which can be used as backend name.
5432 * VERR_NOT_SUPPORTED if no backend was found.
5433 * @param pVDIfsDisk Pointer to the per-disk VD interface list.
5434 * @param pVDIfsImage Pointer to the per-image VD interface list.
5435 * @param pszFilename Name of the image file for which the backend is queried.
5436 * @param ppszFormat Receives pointer of the UTF-8 string which contains the format name.
5437 * The returned pointer must be freed using RTStrFree().
5438 */
5439VBOXDDU_DECL(int) VDGetFormat(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
5440 const char *pszFilename, char **ppszFormat, VDTYPE *penmType)
5441{
5442 int rc = VERR_NOT_SUPPORTED;
5443 VDINTERFACEIOINT VDIfIoInt;
5444 VDINTERFACEIO VDIfIoFallback;
5445 PVDINTERFACEIO pInterfaceIo;
5446
5447 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
5448 /* Check arguments. */
5449 AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
5450 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
5451 VERR_INVALID_PARAMETER);
5452 AssertMsgReturn(VALID_PTR(ppszFormat),
5453 ("ppszFormat=%#p\n", ppszFormat),
5454 VERR_INVALID_PARAMETER);
5455 AssertMsgReturn(VALID_PTR(penmType),
5456 ("penmType=%#p\n", penmType),
5457 VERR_INVALID_PARAMETER);
5458
5459 if (!g_apBackends)
5460 VDInit();
5461
5462 pInterfaceIo = VDIfIoGet(pVDIfsImage);
5463 if (!pInterfaceIo)
5464 {
5465 /*
5466 * Caller doesn't provide an I/O interface, create our own using the
5467 * native file API.
5468 */
5469 vdIfIoFallbackCallbacksSetup(&VDIfIoFallback);
5470 pInterfaceIo = &VDIfIoFallback;
5471 }
5472
5473 /* Set up the internal I/O interface. */
5474 AssertReturn(!VDIfIoIntGet(pVDIfsImage), VERR_INVALID_PARAMETER);
5475 VDIfIoInt.pfnOpen = vdIOIntOpenLimited;
5476 VDIfIoInt.pfnClose = vdIOIntCloseLimited;
5477 VDIfIoInt.pfnDelete = vdIOIntDeleteLimited;
5478 VDIfIoInt.pfnMove = vdIOIntMoveLimited;
5479 VDIfIoInt.pfnGetFreeSpace = vdIOIntGetFreeSpaceLimited;
5480 VDIfIoInt.pfnGetModificationTime = vdIOIntGetModificationTimeLimited;
5481 VDIfIoInt.pfnGetSize = vdIOIntGetSizeLimited;
5482 VDIfIoInt.pfnSetSize = vdIOIntSetSizeLimited;
5483 VDIfIoInt.pfnReadUser = vdIOIntReadUserLimited;
5484 VDIfIoInt.pfnWriteUser = vdIOIntWriteUserLimited;
5485 VDIfIoInt.pfnReadMeta = vdIOIntReadMetaLimited;
5486 VDIfIoInt.pfnWriteMeta = vdIOIntWriteMetaLimited;
5487 VDIfIoInt.pfnFlush = vdIOIntFlushLimited;
5488 rc = VDInterfaceAdd(&VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
5489 pInterfaceIo, sizeof(VDINTERFACEIOINT), &pVDIfsImage);
5490 AssertRC(rc);
5491
5492 /* Find the backend supporting this file format. */
5493 for (unsigned i = 0; i < g_cBackends; i++)
5494 {
5495 if (g_apBackends[i]->pfnCheckIfValid)
5496 {
5497 rc = g_apBackends[i]->pfnCheckIfValid(pszFilename, pVDIfsDisk,
5498 pVDIfsImage, penmType);
5499 if ( RT_SUCCESS(rc)
5500 /* The correct backend has been found, but there is a small
5501 * incompatibility so that the file cannot be used. Stop here
5502 * and signal success - the actual open will of course fail,
5503 * but that will create a really sensible error message. */
5504 || ( rc != VERR_VD_GEN_INVALID_HEADER
5505 && rc != VERR_VD_VDI_INVALID_HEADER
5506 && rc != VERR_VD_VMDK_INVALID_HEADER
5507 && rc != VERR_VD_ISCSI_INVALID_HEADER
5508 && rc != VERR_VD_VHD_INVALID_HEADER
5509 && rc != VERR_VD_RAW_INVALID_HEADER
5510 && rc != VERR_VD_PARALLELS_INVALID_HEADER
5511 && rc != VERR_VD_DMG_INVALID_HEADER))
5512 {
5513 /* Copy the name into the new string. */
5514 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
5515 if (!pszFormat)
5516 {
5517 rc = VERR_NO_MEMORY;
5518 break;
5519 }
5520 *ppszFormat = pszFormat;
5521 /* Do not consider the typical file access errors as success,
5522 * which allows the caller to deal with such issues. */
5523 if ( rc != VERR_ACCESS_DENIED
5524 && rc != VERR_PATH_NOT_FOUND
5525 && rc != VERR_FILE_NOT_FOUND)
5526 rc = VINF_SUCCESS;
5527 break;
5528 }
5529 rc = VERR_NOT_SUPPORTED;
5530 }
5531 }
5532
5533 /* Try the cache backends. */
5534 if (rc == VERR_NOT_SUPPORTED)
5535 {
5536 for (unsigned i = 0; i < g_cCacheBackends; i++)
5537 {
5538 if (g_apCacheBackends[i]->pfnProbe)
5539 {
5540 rc = g_apCacheBackends[i]->pfnProbe(pszFilename, pVDIfsDisk,
5541 pVDIfsImage);
5542 if ( RT_SUCCESS(rc)
5543 || (rc != VERR_VD_GEN_INVALID_HEADER))
5544 {
5545 /* Copy the name into the new string. */
5546 char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
5547 if (!pszFormat)
5548 {
5549 rc = VERR_NO_MEMORY;
5550 break;
5551 }
5552 *ppszFormat = pszFormat;
5553 rc = VINF_SUCCESS;
5554 break;
5555 }
5556 rc = VERR_NOT_SUPPORTED;
5557 }
5558 }
5559 }
5560
5561 LogFlowFunc(("returns %Rrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
5562 return rc;
5563}
5564
5565/**
5566 * Opens an image file.
5567 *
5568 * The first opened image file in HDD container must have a base image type,
5569 * others (next opened images) must be a differencing or undo images.
5570 * Linkage is checked for differencing image to be in consistence with the previously opened image.
5571 * When another differencing image is opened and the last image was opened in read/write access
5572 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
5573 * other processes to use images in read-only mode too.
5574 *
5575 * Note that the image is opened in read-only mode if a read/write open is not possible.
5576 * Use VDIsReadOnly to check open mode.
5577 *
5578 * @returns VBox status code.
5579 * @param pDisk Pointer to HDD container.
5580 * @param pszBackend Name of the image file backend to use.
5581 * @param pszFilename Name of the image file to open.
5582 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
5583 * @param pVDIfsImage Pointer to the per-image VD interface list.
5584 */
5585VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
5586 const char *pszFilename, unsigned uOpenFlags,
5587 PVDINTERFACE pVDIfsImage)
5588{
5589 int rc = VINF_SUCCESS;
5590 int rc2;
5591 bool fLockWrite = false;
5592 PVDIMAGE pImage = NULL;
5593
5594 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsImage=%#p\n",
5595 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsImage));
5596
5597 do
5598 {
5599 /* sanity check */
5600 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5601 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5602
5603 /* Check arguments. */
5604 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
5605 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
5606 rc = VERR_INVALID_PARAMETER);
5607 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
5608 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
5609 rc = VERR_INVALID_PARAMETER);
5610 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
5611 ("uOpenFlags=%#x\n", uOpenFlags),
5612 rc = VERR_INVALID_PARAMETER);
5613 AssertMsgBreakStmt( !(uOpenFlags & VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)
5614 || (uOpenFlags & VD_OPEN_FLAGS_READONLY),
5615 ("uOpenFlags=%#x\n", uOpenFlags),
5616 rc = VERR_INVALID_PARAMETER);
5617
5618 /*
5619 * Destroy the current discard state first which might still have pending blocks
5620 * for the currently opened image which will be switched to readonly mode.
5621 */
5622 /* Lock disk for writing, as we modify pDisk information below. */
5623 rc2 = vdThreadStartWrite(pDisk);
5624 AssertRC(rc2);
5625 fLockWrite = true;
5626 rc = vdDiscardStateDestroy(pDisk);
5627 if (RT_FAILURE(rc))
5628 break;
5629 rc2 = vdThreadFinishWrite(pDisk);
5630 AssertRC(rc2);
5631 fLockWrite = false;
5632
5633 /* Set up image descriptor. */
5634 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
5635 if (!pImage)
5636 {
5637 rc = VERR_NO_MEMORY;
5638 break;
5639 }
5640 pImage->pszFilename = RTStrDup(pszFilename);
5641 if (!pImage->pszFilename)
5642 {
5643 rc = VERR_NO_MEMORY;
5644 break;
5645 }
5646
5647 pImage->VDIo.pDisk = pDisk;
5648 pImage->pVDIfsImage = pVDIfsImage;
5649
5650 rc = vdFindBackend(pszBackend, &pImage->Backend);
5651 if (RT_FAILURE(rc))
5652 break;
5653 if (!pImage->Backend)
5654 {
5655 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
5656 N_("VD: unknown backend name '%s'"), pszBackend);
5657 break;
5658 }
5659
5660 /*
5661 * Fail if the backend can't do async I/O but the
5662 * flag is set.
5663 */
5664 if ( !(pImage->Backend->uBackendCaps & VD_CAP_ASYNC)
5665 && (uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO))
5666 {
5667 rc = vdError(pDisk, VERR_NOT_SUPPORTED, RT_SRC_POS,
5668 N_("VD: Backend '%s' does not support async I/O"), pszBackend);
5669 break;
5670 }
5671
5672 /*
5673 * Fail if the backend doesn't support the discard operation but the
5674 * flag is set.
5675 */
5676 if ( !(pImage->Backend->uBackendCaps & VD_CAP_DISCARD)
5677 && (uOpenFlags & VD_OPEN_FLAGS_DISCARD))
5678 {
5679 rc = vdError(pDisk, VERR_VD_DISCARD_NOT_SUPPORTED, RT_SRC_POS,
5680 N_("VD: Backend '%s' does not support discard"), pszBackend);
5681 break;
5682 }
5683
5684 /* Set up the I/O interface. */
5685 pImage->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsImage);
5686 if (!pImage->VDIo.pInterfaceIo)
5687 {
5688 vdIfIoFallbackCallbacksSetup(&pImage->VDIo.VDIfIo);
5689 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
5690 pDisk, sizeof(VDINTERFACEIO), &pVDIfsImage);
5691 pImage->VDIo.pInterfaceIo = &pImage->VDIo.VDIfIo;
5692 }
5693
5694 /* Set up the internal I/O interface. */
5695 AssertBreakStmt(!VDIfIoIntGet(pVDIfsImage), rc = VERR_INVALID_PARAMETER);
5696 vdIfIoIntCallbacksSetup(&pImage->VDIo.VDIfIoInt);
5697 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
5698 &pImage->VDIo, sizeof(VDINTERFACEIOINT), &pImage->pVDIfsImage);
5699 AssertRC(rc);
5700
5701 pImage->uOpenFlags = uOpenFlags & (VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_DISCARD | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS);
5702 pImage->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
5703 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
5704 uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS),
5705 pDisk->pVDIfsDisk,
5706 pImage->pVDIfsImage,
5707 pDisk->enmType,
5708 &pImage->pBackendData);
5709 /*
5710 * If the image is corrupted and there is a repair method try to repair it
5711 * first if it was openend in read-write mode and open again afterwards.
5712 */
5713 if ( RT_UNLIKELY(rc == VERR_VD_IMAGE_CORRUPTED)
5714 && pImage->Backend->pfnRepair)
5715 {
5716 rc = pImage->Backend->pfnRepair(pszFilename, pDisk->pVDIfsDisk, pImage->pVDIfsImage, 0 /* fFlags */);
5717 if (RT_SUCCESS(rc))
5718 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
5719 uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS),
5720 pDisk->pVDIfsDisk,
5721 pImage->pVDIfsImage,
5722 pDisk->enmType,
5723 &pImage->pBackendData);
5724 else
5725 {
5726 rc = vdError(pDisk, rc, RT_SRC_POS,
5727 N_("VD: error %Rrc repairing corrupted image file '%s'"), rc, pszFilename);
5728 break;
5729 }
5730 }
5731 else if (RT_UNLIKELY(rc == VERR_VD_IMAGE_CORRUPTED))
5732 {
5733 rc = vdError(pDisk, rc, RT_SRC_POS,
5734 N_("VD: Image file '%s' is corrupted and can't be opened"), pszFilename);
5735 break;
5736 }
5737
5738 /* If the open in read-write mode failed, retry in read-only mode. */
5739 if (RT_FAILURE(rc))
5740 {
5741 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
5742 && ( rc == VERR_ACCESS_DENIED
5743 || rc == VERR_PERMISSION_DENIED
5744 || rc == VERR_WRITE_PROTECT
5745 || rc == VERR_SHARING_VIOLATION
5746 || rc == VERR_FILE_LOCK_FAILED))
5747 rc = pImage->Backend->pfnOpen(pImage->pszFilename,
5748 (uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS))
5749 | VD_OPEN_FLAGS_READONLY,
5750 pDisk->pVDIfsDisk,
5751 pImage->pVDIfsImage,
5752 pDisk->enmType,
5753 &pImage->pBackendData);
5754 if (RT_FAILURE(rc))
5755 {
5756 rc = vdError(pDisk, rc, RT_SRC_POS,
5757 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
5758 break;
5759 }
5760 }
5761
5762 /* Lock disk for writing, as we modify pDisk information below. */
5763 rc2 = vdThreadStartWrite(pDisk);
5764 AssertRC(rc2);
5765 fLockWrite = true;
5766
5767 pImage->VDIo.pBackendData = pImage->pBackendData;
5768
5769 /* Check image type. As the image itself has only partial knowledge
5770 * whether it's a base image or not, this info is derived here. The
5771 * base image can be fixed or normal, all others must be normal or
5772 * diff images. Some image formats don't distinguish between normal
5773 * and diff images, so this must be corrected here. */
5774 unsigned uImageFlags;
5775 uImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pBackendData);
5776 if (RT_FAILURE(rc))
5777 uImageFlags = VD_IMAGE_FLAGS_NONE;
5778 if ( RT_SUCCESS(rc)
5779 && !(uOpenFlags & VD_OPEN_FLAGS_INFO))
5780 {
5781 if ( pDisk->cImages == 0
5782 && (uImageFlags & VD_IMAGE_FLAGS_DIFF))
5783 {
5784 rc = VERR_VD_INVALID_TYPE;
5785 break;
5786 }
5787 else if (pDisk->cImages != 0)
5788 {
5789 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
5790 {
5791 rc = VERR_VD_INVALID_TYPE;
5792 break;
5793 }
5794 else
5795 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
5796 }
5797 }
5798
5799 /* Ensure we always get correct diff information, even if the backend
5800 * doesn't actually have a stored flag for this. It must not return
5801 * bogus information for the parent UUID if it is not a diff image. */
5802 RTUUID parentUuid;
5803 RTUuidClear(&parentUuid);
5804 rc2 = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, &parentUuid);
5805 if (RT_SUCCESS(rc2) && !RTUuidIsNull(&parentUuid))
5806 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
5807
5808 pImage->uImageFlags = uImageFlags;
5809
5810 /* Force sane optimization settings. It's not worth avoiding writes
5811 * to fixed size images. The overhead would have almost no payback. */
5812 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
5813 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
5814
5815 /** @todo optionally check UUIDs */
5816
5817 /* Cache disk information. */
5818 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
5819
5820 /* Cache PCHS geometry. */
5821 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
5822 &pDisk->PCHSGeometry);
5823 if (RT_FAILURE(rc2))
5824 {
5825 pDisk->PCHSGeometry.cCylinders = 0;
5826 pDisk->PCHSGeometry.cHeads = 0;
5827 pDisk->PCHSGeometry.cSectors = 0;
5828 }
5829 else
5830 {
5831 /* Make sure the PCHS geometry is properly clipped. */
5832 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
5833 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
5834 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
5835 }
5836
5837 /* Cache LCHS geometry. */
5838 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
5839 &pDisk->LCHSGeometry);
5840 if (RT_FAILURE(rc2))
5841 {
5842 pDisk->LCHSGeometry.cCylinders = 0;
5843 pDisk->LCHSGeometry.cHeads = 0;
5844 pDisk->LCHSGeometry.cSectors = 0;
5845 }
5846 else
5847 {
5848 /* Make sure the LCHS geometry is properly clipped. */
5849 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
5850 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
5851 }
5852
5853 if (pDisk->cImages != 0)
5854 {
5855 /* Switch previous image to read-only mode. */
5856 unsigned uOpenFlagsPrevImg;
5857 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
5858 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
5859 {
5860 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
5861 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
5862 }
5863 }
5864
5865 if (RT_SUCCESS(rc))
5866 {
5867 /* Image successfully opened, make it the last image. */
5868 vdAddImageToList(pDisk, pImage);
5869 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
5870 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
5871 }
5872 else
5873 {
5874 /* Error detected, but image opened. Close image. */
5875 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
5876 AssertRC(rc2);
5877 pImage->pBackendData = NULL;
5878 }
5879 } while (0);
5880
5881 if (RT_UNLIKELY(fLockWrite))
5882 {
5883 rc2 = vdThreadFinishWrite(pDisk);
5884 AssertRC(rc2);
5885 }
5886
5887 if (RT_FAILURE(rc))
5888 {
5889 if (pImage)
5890 {
5891 if (pImage->pszFilename)
5892 RTStrFree(pImage->pszFilename);
5893 RTMemFree(pImage);
5894 }
5895 }
5896
5897 LogFlowFunc(("returns %Rrc\n", rc));
5898 return rc;
5899}
5900
5901/**
5902 * Opens a cache image.
5903 *
5904 * @return VBox status code.
5905 * @param pDisk Pointer to the HDD container which should use the cache image.
5906 * @param pszBackend Name of the cache file backend to use (case insensitive).
5907 * @param pszFilename Name of the cache image to open.
5908 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
5909 * @param pVDIfsCache Pointer to the per-cache VD interface list.
5910 */
5911VBOXDDU_DECL(int) VDCacheOpen(PVBOXHDD pDisk, const char *pszBackend,
5912 const char *pszFilename, unsigned uOpenFlags,
5913 PVDINTERFACE pVDIfsCache)
5914{
5915 int rc = VINF_SUCCESS;
5916 int rc2;
5917 bool fLockWrite = false;
5918 PVDCACHE pCache = NULL;
5919
5920 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsCache=%#p\n",
5921 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsCache));
5922
5923 do
5924 {
5925 /* sanity check */
5926 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
5927 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
5928
5929 /* Check arguments. */
5930 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
5931 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
5932 rc = VERR_INVALID_PARAMETER);
5933 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
5934 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
5935 rc = VERR_INVALID_PARAMETER);
5936 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
5937 ("uOpenFlags=%#x\n", uOpenFlags),
5938 rc = VERR_INVALID_PARAMETER);
5939
5940 /* Set up image descriptor. */
5941 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
5942 if (!pCache)
5943 {
5944 rc = VERR_NO_MEMORY;
5945 break;
5946 }
5947 pCache->pszFilename = RTStrDup(pszFilename);
5948 if (!pCache->pszFilename)
5949 {
5950 rc = VERR_NO_MEMORY;
5951 break;
5952 }
5953
5954 pCache->VDIo.pDisk = pDisk;
5955 pCache->pVDIfsCache = pVDIfsCache;
5956
5957 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
5958 if (RT_FAILURE(rc))
5959 break;
5960 if (!pCache->Backend)
5961 {
5962 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
5963 N_("VD: unknown backend name '%s'"), pszBackend);
5964 break;
5965 }
5966
5967 /* Set up the I/O interface. */
5968 pCache->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsCache);
5969 if (!pCache->VDIo.pInterfaceIo)
5970 {
5971 vdIfIoFallbackCallbacksSetup(&pCache->VDIo.VDIfIo);
5972 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
5973 pDisk, sizeof(VDINTERFACEIO), &pVDIfsCache);
5974 pCache->VDIo.pInterfaceIo = &pCache->VDIo.VDIfIo;
5975 }
5976
5977 /* Set up the internal I/O interface. */
5978 AssertBreakStmt(!VDIfIoIntGet(pVDIfsCache), rc = VERR_INVALID_PARAMETER);
5979 vdIfIoIntCallbacksSetup(&pCache->VDIo.VDIfIoInt);
5980 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
5981 &pCache->VDIo, sizeof(VDINTERFACEIOINT), &pCache->pVDIfsCache);
5982 AssertRC(rc);
5983
5984 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
5985 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
5986 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
5987 pDisk->pVDIfsDisk,
5988 pCache->pVDIfsCache,
5989 &pCache->pBackendData);
5990 /* If the open in read-write mode failed, retry in read-only mode. */
5991 if (RT_FAILURE(rc))
5992 {
5993 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
5994 && ( rc == VERR_ACCESS_DENIED
5995 || rc == VERR_PERMISSION_DENIED
5996 || rc == VERR_WRITE_PROTECT
5997 || rc == VERR_SHARING_VIOLATION
5998 || rc == VERR_FILE_LOCK_FAILED))
5999 rc = pCache->Backend->pfnOpen(pCache->pszFilename,
6000 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
6001 | VD_OPEN_FLAGS_READONLY,
6002 pDisk->pVDIfsDisk,
6003 pCache->pVDIfsCache,
6004 &pCache->pBackendData);
6005 if (RT_FAILURE(rc))
6006 {
6007 rc = vdError(pDisk, rc, RT_SRC_POS,
6008 N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
6009 break;
6010 }
6011 }
6012
6013 /* Lock disk for writing, as we modify pDisk information below. */
6014 rc2 = vdThreadStartWrite(pDisk);
6015 AssertRC(rc2);
6016 fLockWrite = true;
6017
6018 /*
6019 * Check that the modification UUID of the cache and last image
6020 * match. If not the image was modified in-between without the cache.
6021 * The cache might contain stale data.
6022 */
6023 RTUUID UuidImage, UuidCache;
6024
6025 rc = pCache->Backend->pfnGetModificationUuid(pCache->pBackendData,
6026 &UuidCache);
6027 if (RT_SUCCESS(rc))
6028 {
6029 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
6030 &UuidImage);
6031 if (RT_SUCCESS(rc))
6032 {
6033 if (RTUuidCompare(&UuidImage, &UuidCache))
6034 rc = VERR_VD_CACHE_NOT_UP_TO_DATE;
6035 }
6036 }
6037
6038 /*
6039 * We assume that the user knows what he is doing if one of the images
6040 * doesn't support the modification uuid.
6041 */
6042 if (rc == VERR_NOT_SUPPORTED)
6043 rc = VINF_SUCCESS;
6044
6045 if (RT_SUCCESS(rc))
6046 {
6047 /* Cache successfully opened, make it the current one. */
6048 if (!pDisk->pCache)
6049 pDisk->pCache = pCache;
6050 else
6051 rc = VERR_VD_CACHE_ALREADY_EXISTS;
6052 }
6053
6054 if (RT_FAILURE(rc))
6055 {
6056 /* Error detected, but image opened. Close image. */
6057 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
6058 AssertRC(rc2);
6059 pCache->pBackendData = NULL;
6060 }
6061 } while (0);
6062
6063 if (RT_UNLIKELY(fLockWrite))
6064 {
6065 rc2 = vdThreadFinishWrite(pDisk);
6066 AssertRC(rc2);
6067 }
6068
6069 if (RT_FAILURE(rc))
6070 {
6071 if (pCache)
6072 {
6073 if (pCache->pszFilename)
6074 RTStrFree(pCache->pszFilename);
6075 RTMemFree(pCache);
6076 }
6077 }
6078
6079 LogFlowFunc(("returns %Rrc\n", rc));
6080 return rc;
6081}
6082
6083/**
6084 * Creates and opens a new base image file.
6085 *
6086 * @returns VBox status code.
6087 * @param pDisk Pointer to HDD container.
6088 * @param pszBackend Name of the image file backend to use.
6089 * @param pszFilename Name of the image file to create.
6090 * @param cbSize Image size in bytes.
6091 * @param uImageFlags Flags specifying special image features.
6092 * @param pszComment Pointer to image comment. NULL is ok.
6093 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
6094 * @param pLCHSGeometry Pointer to logical disk geometry <= (x,255,63). Not NULL.
6095 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
6096 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
6097 * @param pVDIfsImage Pointer to the per-image VD interface list.
6098 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6099 */
6100VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
6101 const char *pszFilename, uint64_t cbSize,
6102 unsigned uImageFlags, const char *pszComment,
6103 PCVDGEOMETRY pPCHSGeometry,
6104 PCVDGEOMETRY pLCHSGeometry,
6105 PCRTUUID pUuid, unsigned uOpenFlags,
6106 PVDINTERFACE pVDIfsImage,
6107 PVDINTERFACE pVDIfsOperation)
6108{
6109 int rc = VINF_SUCCESS;
6110 int rc2;
6111 bool fLockWrite = false, fLockRead = false;
6112 PVDIMAGE pImage = NULL;
6113 RTUUID uuid;
6114
6115 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",
6116 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment,
6117 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
6118 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
6119 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
6120 uOpenFlags, pVDIfsImage, pVDIfsOperation));
6121
6122 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
6123
6124 do
6125 {
6126 /* sanity check */
6127 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6128 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6129
6130 /* Check arguments. */
6131 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
6132 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
6133 rc = VERR_INVALID_PARAMETER);
6134 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
6135 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
6136 rc = VERR_INVALID_PARAMETER);
6137 AssertMsgBreakStmt(cbSize,
6138 ("cbSize=%llu\n", cbSize),
6139 rc = VERR_INVALID_PARAMETER);
6140 AssertMsgBreakStmt( ((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0)
6141 || ((uImageFlags & (VD_IMAGE_FLAGS_FIXED | VD_IMAGE_FLAGS_DIFF)) != VD_IMAGE_FLAGS_FIXED),
6142 ("uImageFlags=%#x\n", uImageFlags),
6143 rc = VERR_INVALID_PARAMETER);
6144 /* The PCHS geometry fields may be 0 to leave it for later. */
6145 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
6146 && pPCHSGeometry->cHeads <= 16
6147 && pPCHSGeometry->cSectors <= 63,
6148 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
6149 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
6150 pPCHSGeometry->cSectors),
6151 rc = VERR_INVALID_PARAMETER);
6152 /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
6153 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
6154 && pLCHSGeometry->cHeads <= 255
6155 && pLCHSGeometry->cSectors <= 63,
6156 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
6157 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
6158 pLCHSGeometry->cSectors),
6159 rc = VERR_INVALID_PARAMETER);
6160 /* The UUID may be NULL. */
6161 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
6162 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
6163 rc = VERR_INVALID_PARAMETER);
6164 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
6165 ("uOpenFlags=%#x\n", uOpenFlags),
6166 rc = VERR_INVALID_PARAMETER);
6167
6168 /* Check state. Needs a temporary read lock. Holding the write lock
6169 * all the time would be blocking other activities for too long. */
6170 rc2 = vdThreadStartRead(pDisk);
6171 AssertRC(rc2);
6172 fLockRead = true;
6173 AssertMsgBreakStmt(pDisk->cImages == 0,
6174 ("Create base image cannot be done with other images open\n"),
6175 rc = VERR_VD_INVALID_STATE);
6176 rc2 = vdThreadFinishRead(pDisk);
6177 AssertRC(rc2);
6178 fLockRead = false;
6179
6180 /* Set up image descriptor. */
6181 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
6182 if (!pImage)
6183 {
6184 rc = VERR_NO_MEMORY;
6185 break;
6186 }
6187 pImage->pszFilename = RTStrDup(pszFilename);
6188 if (!pImage->pszFilename)
6189 {
6190 rc = VERR_NO_MEMORY;
6191 break;
6192 }
6193 pImage->VDIo.pDisk = pDisk;
6194 pImage->pVDIfsImage = pVDIfsImage;
6195
6196 /* Set up the I/O interface. */
6197 pImage->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsImage);
6198 if (!pImage->VDIo.pInterfaceIo)
6199 {
6200 vdIfIoFallbackCallbacksSetup(&pImage->VDIo.VDIfIo);
6201 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
6202 pDisk, sizeof(VDINTERFACEIO), &pVDIfsImage);
6203 pImage->VDIo.pInterfaceIo = &pImage->VDIo.VDIfIo;
6204 }
6205
6206 /* Set up the internal I/O interface. */
6207 AssertBreakStmt(!VDIfIoIntGet(pVDIfsImage), rc = VERR_INVALID_PARAMETER);
6208 vdIfIoIntCallbacksSetup(&pImage->VDIo.VDIfIoInt);
6209 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
6210 &pImage->VDIo, sizeof(VDINTERFACEIOINT), &pImage->pVDIfsImage);
6211 AssertRC(rc);
6212
6213 rc = vdFindBackend(pszBackend, &pImage->Backend);
6214 if (RT_FAILURE(rc))
6215 break;
6216 if (!pImage->Backend)
6217 {
6218 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6219 N_("VD: unknown backend name '%s'"), pszBackend);
6220 break;
6221 }
6222 if (!(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
6223 | VD_CAP_CREATE_DYNAMIC)))
6224 {
6225 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6226 N_("VD: backend '%s' cannot create base images"), pszBackend);
6227 break;
6228 }
6229
6230 /* Create UUID if the caller didn't specify one. */
6231 if (!pUuid)
6232 {
6233 rc = RTUuidCreate(&uuid);
6234 if (RT_FAILURE(rc))
6235 {
6236 rc = vdError(pDisk, rc, RT_SRC_POS,
6237 N_("VD: cannot generate UUID for image '%s'"),
6238 pszFilename);
6239 break;
6240 }
6241 pUuid = &uuid;
6242 }
6243
6244 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
6245 uImageFlags &= ~VD_IMAGE_FLAGS_DIFF;
6246 pImage->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
6247 rc = pImage->Backend->pfnCreate(pImage->pszFilename, cbSize,
6248 uImageFlags, pszComment, pPCHSGeometry,
6249 pLCHSGeometry, pUuid,
6250 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
6251 0, 99,
6252 pDisk->pVDIfsDisk,
6253 pImage->pVDIfsImage,
6254 pVDIfsOperation,
6255 &pImage->pBackendData);
6256
6257 if (RT_SUCCESS(rc))
6258 {
6259 pImage->VDIo.pBackendData = pImage->pBackendData;
6260 pImage->uImageFlags = uImageFlags;
6261
6262 /* Force sane optimization settings. It's not worth avoiding writes
6263 * to fixed size images. The overhead would have almost no payback. */
6264 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
6265 pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
6266
6267 /* Lock disk for writing, as we modify pDisk information below. */
6268 rc2 = vdThreadStartWrite(pDisk);
6269 AssertRC(rc2);
6270 fLockWrite = true;
6271
6272 /** @todo optionally check UUIDs */
6273
6274 /* Re-check state, as the lock wasn't held and another image
6275 * creation call could have been done by another thread. */
6276 AssertMsgStmt(pDisk->cImages == 0,
6277 ("Create base image cannot be done with other images open\n"),
6278 rc = VERR_VD_INVALID_STATE);
6279 }
6280
6281 if (RT_SUCCESS(rc))
6282 {
6283 /* Cache disk information. */
6284 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
6285
6286 /* Cache PCHS geometry. */
6287 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
6288 &pDisk->PCHSGeometry);
6289 if (RT_FAILURE(rc2))
6290 {
6291 pDisk->PCHSGeometry.cCylinders = 0;
6292 pDisk->PCHSGeometry.cHeads = 0;
6293 pDisk->PCHSGeometry.cSectors = 0;
6294 }
6295 else
6296 {
6297 /* Make sure the CHS geometry is properly clipped. */
6298 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
6299 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
6300 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
6301 }
6302
6303 /* Cache LCHS geometry. */
6304 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
6305 &pDisk->LCHSGeometry);
6306 if (RT_FAILURE(rc2))
6307 {
6308 pDisk->LCHSGeometry.cCylinders = 0;
6309 pDisk->LCHSGeometry.cHeads = 0;
6310 pDisk->LCHSGeometry.cSectors = 0;
6311 }
6312 else
6313 {
6314 /* Make sure the CHS geometry is properly clipped. */
6315 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
6316 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
6317 }
6318
6319 /* Image successfully opened, make it the last image. */
6320 vdAddImageToList(pDisk, pImage);
6321 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
6322 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
6323 }
6324 else
6325 {
6326 /* Error detected, image may or may not be opened. Close and delete
6327 * image if it was opened. */
6328 if (pImage->pBackendData)
6329 {
6330 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
6331 AssertRC(rc2);
6332 pImage->pBackendData = NULL;
6333 }
6334 }
6335 } while (0);
6336
6337 if (RT_UNLIKELY(fLockWrite))
6338 {
6339 rc2 = vdThreadFinishWrite(pDisk);
6340 AssertRC(rc2);
6341 }
6342 else if (RT_UNLIKELY(fLockRead))
6343 {
6344 rc2 = vdThreadFinishRead(pDisk);
6345 AssertRC(rc2);
6346 }
6347
6348 if (RT_FAILURE(rc))
6349 {
6350 if (pImage)
6351 {
6352 if (pImage->pszFilename)
6353 RTStrFree(pImage->pszFilename);
6354 RTMemFree(pImage);
6355 }
6356 }
6357
6358 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
6359 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
6360
6361 LogFlowFunc(("returns %Rrc\n", rc));
6362 return rc;
6363}
6364
6365/**
6366 * Creates and opens a new differencing image file in HDD container.
6367 * See comments for VDOpen function about differencing images.
6368 *
6369 * @returns VBox status code.
6370 * @param pDisk Pointer to HDD container.
6371 * @param pszBackend Name of the image file backend to use.
6372 * @param pszFilename Name of the differencing image file to create.
6373 * @param uImageFlags Flags specifying special image features.
6374 * @param pszComment Pointer to image comment. NULL is ok.
6375 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
6376 * @param pParentUuid New parent UUID of the image. If NULL, the UUID is queried automatically.
6377 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
6378 * @param pVDIfsImage Pointer to the per-image VD interface list.
6379 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6380 */
6381VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
6382 const char *pszFilename, unsigned uImageFlags,
6383 const char *pszComment, PCRTUUID pUuid,
6384 PCRTUUID pParentUuid, unsigned uOpenFlags,
6385 PVDINTERFACE pVDIfsImage,
6386 PVDINTERFACE pVDIfsOperation)
6387{
6388 int rc = VINF_SUCCESS;
6389 int rc2;
6390 bool fLockWrite = false, fLockRead = false;
6391 PVDIMAGE pImage = NULL;
6392 RTUUID uuid;
6393
6394 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
6395 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsImage, pVDIfsOperation));
6396
6397 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
6398
6399 do
6400 {
6401 /* sanity check */
6402 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6403 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6404
6405 /* Check arguments. */
6406 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
6407 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
6408 rc = VERR_INVALID_PARAMETER);
6409 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
6410 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
6411 rc = VERR_INVALID_PARAMETER);
6412 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
6413 ("uImageFlags=%#x\n", uImageFlags),
6414 rc = VERR_INVALID_PARAMETER);
6415 /* The UUID may be NULL. */
6416 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
6417 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
6418 rc = VERR_INVALID_PARAMETER);
6419 /* The parent UUID may be NULL. */
6420 AssertMsgBreakStmt(pParentUuid == NULL || VALID_PTR(pParentUuid),
6421 ("pParentUuid=%#p ParentUUID=%RTuuid\n", pParentUuid, pParentUuid),
6422 rc = VERR_INVALID_PARAMETER);
6423 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
6424 ("uOpenFlags=%#x\n", uOpenFlags),
6425 rc = VERR_INVALID_PARAMETER);
6426
6427 /* Check state. Needs a temporary read lock. Holding the write lock
6428 * all the time would be blocking other activities for too long. */
6429 rc2 = vdThreadStartRead(pDisk);
6430 AssertRC(rc2);
6431 fLockRead = true;
6432 AssertMsgBreakStmt(pDisk->cImages != 0,
6433 ("Create diff image cannot be done without other images open\n"),
6434 rc = VERR_VD_INVALID_STATE);
6435 rc2 = vdThreadFinishRead(pDisk);
6436 AssertRC(rc2);
6437 fLockRead = false;
6438
6439 /*
6440 * Destroy the current discard state first which might still have pending blocks
6441 * for the currently opened image which will be switched to readonly mode.
6442 */
6443 /* Lock disk for writing, as we modify pDisk information below. */
6444 rc2 = vdThreadStartWrite(pDisk);
6445 AssertRC(rc2);
6446 fLockWrite = true;
6447 rc = vdDiscardStateDestroy(pDisk);
6448 if (RT_FAILURE(rc))
6449 break;
6450 rc2 = vdThreadFinishWrite(pDisk);
6451 AssertRC(rc2);
6452 fLockWrite = false;
6453
6454 /* Set up image descriptor. */
6455 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
6456 if (!pImage)
6457 {
6458 rc = VERR_NO_MEMORY;
6459 break;
6460 }
6461 pImage->pszFilename = RTStrDup(pszFilename);
6462 if (!pImage->pszFilename)
6463 {
6464 rc = VERR_NO_MEMORY;
6465 break;
6466 }
6467
6468 rc = vdFindBackend(pszBackend, &pImage->Backend);
6469 if (RT_FAILURE(rc))
6470 break;
6471 if (!pImage->Backend)
6472 {
6473 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6474 N_("VD: unknown backend name '%s'"), pszBackend);
6475 break;
6476 }
6477 if ( !(pImage->Backend->uBackendCaps & VD_CAP_DIFF)
6478 || !(pImage->Backend->uBackendCaps & ( VD_CAP_CREATE_FIXED
6479 | VD_CAP_CREATE_DYNAMIC)))
6480 {
6481 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6482 N_("VD: backend '%s' cannot create diff images"), pszBackend);
6483 break;
6484 }
6485
6486 pImage->VDIo.pDisk = pDisk;
6487 pImage->pVDIfsImage = pVDIfsImage;
6488
6489 /* Set up the I/O interface. */
6490 pImage->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsImage);
6491 if (!pImage->VDIo.pInterfaceIo)
6492 {
6493 vdIfIoFallbackCallbacksSetup(&pImage->VDIo.VDIfIo);
6494 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
6495 pDisk, sizeof(VDINTERFACEIO), &pVDIfsImage);
6496 pImage->VDIo.pInterfaceIo = &pImage->VDIo.VDIfIo;
6497 }
6498
6499 /* Set up the internal I/O interface. */
6500 AssertBreakStmt(!VDIfIoIntGet(pVDIfsImage), rc = VERR_INVALID_PARAMETER);
6501 vdIfIoIntCallbacksSetup(&pImage->VDIo.VDIfIoInt);
6502 rc = VDInterfaceAdd(&pImage->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
6503 &pImage->VDIo, sizeof(VDINTERFACEIOINT), &pImage->pVDIfsImage);
6504 AssertRC(rc);
6505
6506 /* Create UUID if the caller didn't specify one. */
6507 if (!pUuid)
6508 {
6509 rc = RTUuidCreate(&uuid);
6510 if (RT_FAILURE(rc))
6511 {
6512 rc = vdError(pDisk, rc, RT_SRC_POS,
6513 N_("VD: cannot generate UUID for image '%s'"),
6514 pszFilename);
6515 break;
6516 }
6517 pUuid = &uuid;
6518 }
6519
6520 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
6521 pImage->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
6522 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
6523 rc = pImage->Backend->pfnCreate(pImage->pszFilename, pDisk->cbSize,
6524 uImageFlags | VD_IMAGE_FLAGS_DIFF,
6525 pszComment, &pDisk->PCHSGeometry,
6526 &pDisk->LCHSGeometry, pUuid,
6527 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
6528 0, 99,
6529 pDisk->pVDIfsDisk,
6530 pImage->pVDIfsImage,
6531 pVDIfsOperation,
6532 &pImage->pBackendData);
6533
6534 if (RT_SUCCESS(rc))
6535 {
6536 pImage->VDIo.pBackendData = pImage->pBackendData;
6537 pImage->uImageFlags = uImageFlags;
6538
6539 /* Lock disk for writing, as we modify pDisk information below. */
6540 rc2 = vdThreadStartWrite(pDisk);
6541 AssertRC(rc2);
6542 fLockWrite = true;
6543
6544 /* Switch previous image to read-only mode. */
6545 unsigned uOpenFlagsPrevImg;
6546 uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
6547 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
6548 {
6549 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
6550 rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
6551 }
6552
6553 /** @todo optionally check UUIDs */
6554
6555 /* Re-check state, as the lock wasn't held and another image
6556 * creation call could have been done by another thread. */
6557 AssertMsgStmt(pDisk->cImages != 0,
6558 ("Create diff image cannot be done without other images open\n"),
6559 rc = VERR_VD_INVALID_STATE);
6560 }
6561
6562 if (RT_SUCCESS(rc))
6563 {
6564 RTUUID Uuid;
6565 RTTIMESPEC ts;
6566
6567 if (pParentUuid && !RTUuidIsNull(pParentUuid))
6568 {
6569 Uuid = *pParentUuid;
6570 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
6571 }
6572 else
6573 {
6574 rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pBackendData,
6575 &Uuid);
6576 if (RT_SUCCESS(rc2))
6577 pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
6578 }
6579 rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
6580 &Uuid);
6581 if (RT_SUCCESS(rc2))
6582 pImage->Backend->pfnSetParentModificationUuid(pImage->pBackendData,
6583 &Uuid);
6584 if (pDisk->pLast->Backend->pfnGetTimeStamp)
6585 rc2 = pDisk->pLast->Backend->pfnGetTimeStamp(pDisk->pLast->pBackendData,
6586 &ts);
6587 else
6588 rc2 = VERR_NOT_IMPLEMENTED;
6589 if (RT_SUCCESS(rc2) && pImage->Backend->pfnSetParentTimeStamp)
6590 pImage->Backend->pfnSetParentTimeStamp(pImage->pBackendData, &ts);
6591
6592 if (pImage->Backend->pfnSetParentFilename)
6593 rc2 = pImage->Backend->pfnSetParentFilename(pImage->pBackendData, pDisk->pLast->pszFilename);
6594 }
6595
6596 if (RT_SUCCESS(rc))
6597 {
6598 /* Image successfully opened, make it the last image. */
6599 vdAddImageToList(pDisk, pImage);
6600 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
6601 pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
6602 }
6603 else
6604 {
6605 /* Error detected, but image opened. Close and delete image. */
6606 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
6607 AssertRC(rc2);
6608 pImage->pBackendData = NULL;
6609 }
6610 } while (0);
6611
6612 if (RT_UNLIKELY(fLockWrite))
6613 {
6614 rc2 = vdThreadFinishWrite(pDisk);
6615 AssertRC(rc2);
6616 }
6617 else if (RT_UNLIKELY(fLockRead))
6618 {
6619 rc2 = vdThreadFinishRead(pDisk);
6620 AssertRC(rc2);
6621 }
6622
6623 if (RT_FAILURE(rc))
6624 {
6625 if (pImage)
6626 {
6627 if (pImage->pszFilename)
6628 RTStrFree(pImage->pszFilename);
6629 RTMemFree(pImage);
6630 }
6631 }
6632
6633 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
6634 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
6635
6636 LogFlowFunc(("returns %Rrc\n", rc));
6637 return rc;
6638}
6639
6640
6641/**
6642 * Creates and opens new cache image file in HDD container.
6643 *
6644 * @return VBox status code.
6645 * @param pDisk Name of the cache file backend to use (case insensitive).
6646 * @param pszFilename Name of the differencing cache file to create.
6647 * @param cbSize Maximum size of the cache.
6648 * @param uImageFlags Flags specifying special cache features.
6649 * @param pszComment Pointer to image comment. NULL is ok.
6650 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
6651 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
6652 * @param pVDIfsCache Pointer to the per-cache VD interface list.
6653 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6654 */
6655VBOXDDU_DECL(int) VDCreateCache(PVBOXHDD pDisk, const char *pszBackend,
6656 const char *pszFilename, uint64_t cbSize,
6657 unsigned uImageFlags, const char *pszComment,
6658 PCRTUUID pUuid, unsigned uOpenFlags,
6659 PVDINTERFACE pVDIfsCache, PVDINTERFACE pVDIfsOperation)
6660{
6661 int rc = VINF_SUCCESS;
6662 int rc2;
6663 bool fLockWrite = false, fLockRead = false;
6664 PVDCACHE pCache = NULL;
6665 RTUUID uuid;
6666
6667 LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
6668 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsCache, pVDIfsOperation));
6669
6670 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
6671
6672 do
6673 {
6674 /* sanity check */
6675 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6676 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6677
6678 /* Check arguments. */
6679 AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
6680 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
6681 rc = VERR_INVALID_PARAMETER);
6682 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
6683 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
6684 rc = VERR_INVALID_PARAMETER);
6685 AssertMsgBreakStmt(cbSize,
6686 ("cbSize=%llu\n", cbSize),
6687 rc = VERR_INVALID_PARAMETER);
6688 AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
6689 ("uImageFlags=%#x\n", uImageFlags),
6690 rc = VERR_INVALID_PARAMETER);
6691 /* The UUID may be NULL. */
6692 AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
6693 ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
6694 rc = VERR_INVALID_PARAMETER);
6695 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
6696 ("uOpenFlags=%#x\n", uOpenFlags),
6697 rc = VERR_INVALID_PARAMETER);
6698
6699 /* Check state. Needs a temporary read lock. Holding the write lock
6700 * all the time would be blocking other activities for too long. */
6701 rc2 = vdThreadStartRead(pDisk);
6702 AssertRC(rc2);
6703 fLockRead = true;
6704 AssertMsgBreakStmt(!pDisk->pCache,
6705 ("Create cache image cannot be done with a cache already attached\n"),
6706 rc = VERR_VD_CACHE_ALREADY_EXISTS);
6707 rc2 = vdThreadFinishRead(pDisk);
6708 AssertRC(rc2);
6709 fLockRead = false;
6710
6711 /* Set up image descriptor. */
6712 pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
6713 if (!pCache)
6714 {
6715 rc = VERR_NO_MEMORY;
6716 break;
6717 }
6718 pCache->pszFilename = RTStrDup(pszFilename);
6719 if (!pCache->pszFilename)
6720 {
6721 rc = VERR_NO_MEMORY;
6722 break;
6723 }
6724
6725 rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
6726 if (RT_FAILURE(rc))
6727 break;
6728 if (!pCache->Backend)
6729 {
6730 rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
6731 N_("VD: unknown backend name '%s'"), pszBackend);
6732 break;
6733 }
6734
6735 pCache->VDIo.pDisk = pDisk;
6736 pCache->pVDIfsCache = pVDIfsCache;
6737
6738 /* Set up the I/O interface. */
6739 pCache->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsCache);
6740 if (!pCache->VDIo.pInterfaceIo)
6741 {
6742 vdIfIoFallbackCallbacksSetup(&pCache->VDIo.VDIfIo);
6743 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIo.Core, "VD_IO", VDINTERFACETYPE_IO,
6744 pDisk, sizeof(VDINTERFACEIO), &pVDIfsCache);
6745 pCache->VDIo.pInterfaceIo = &pCache->VDIo.VDIfIo;
6746 }
6747
6748 /* Set up the internal I/O interface. */
6749 AssertBreakStmt(!VDIfIoIntGet(pVDIfsCache), rc = VERR_INVALID_PARAMETER);
6750 vdIfIoIntCallbacksSetup(&pCache->VDIo.VDIfIoInt);
6751 rc = VDInterfaceAdd(&pCache->VDIo.VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
6752 &pCache->VDIo, sizeof(VDINTERFACEIOINT), &pCache->pVDIfsCache);
6753 AssertRC(rc);
6754
6755 /* Create UUID if the caller didn't specify one. */
6756 if (!pUuid)
6757 {
6758 rc = RTUuidCreate(&uuid);
6759 if (RT_FAILURE(rc))
6760 {
6761 rc = vdError(pDisk, rc, RT_SRC_POS,
6762 N_("VD: cannot generate UUID for image '%s'"),
6763 pszFilename);
6764 break;
6765 }
6766 pUuid = &uuid;
6767 }
6768
6769 pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
6770 pCache->VDIo.fIgnoreFlush = (uOpenFlags & VD_OPEN_FLAGS_IGNORE_FLUSH) != 0;
6771 rc = pCache->Backend->pfnCreate(pCache->pszFilename, cbSize,
6772 uImageFlags,
6773 pszComment, pUuid,
6774 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
6775 0, 99,
6776 pDisk->pVDIfsDisk,
6777 pCache->pVDIfsCache,
6778 pVDIfsOperation,
6779 &pCache->pBackendData);
6780
6781 if (RT_SUCCESS(rc))
6782 {
6783 /* Lock disk for writing, as we modify pDisk information below. */
6784 rc2 = vdThreadStartWrite(pDisk);
6785 AssertRC(rc2);
6786 fLockWrite = true;
6787
6788 pCache->VDIo.pBackendData = pCache->pBackendData;
6789
6790 /* Re-check state, as the lock wasn't held and another image
6791 * creation call could have been done by another thread. */
6792 AssertMsgStmt(!pDisk->pCache,
6793 ("Create cache image cannot be done with another cache open\n"),
6794 rc = VERR_VD_CACHE_ALREADY_EXISTS);
6795 }
6796
6797 if ( RT_SUCCESS(rc)
6798 && pDisk->pLast)
6799 {
6800 RTUUID UuidModification;
6801
6802 /* Set same modification Uuid as the last image. */
6803 rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
6804 &UuidModification);
6805 if (RT_SUCCESS(rc))
6806 {
6807 rc = pCache->Backend->pfnSetModificationUuid(pCache->pBackendData,
6808 &UuidModification);
6809 }
6810
6811 if (rc == VERR_NOT_SUPPORTED)
6812 rc = VINF_SUCCESS;
6813 }
6814
6815 if (RT_SUCCESS(rc))
6816 {
6817 /* Cache successfully created. */
6818 pDisk->pCache = pCache;
6819 }
6820 else
6821 {
6822 /* Error detected, but image opened. Close and delete image. */
6823 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, true);
6824 AssertRC(rc2);
6825 pCache->pBackendData = NULL;
6826 }
6827 } while (0);
6828
6829 if (RT_UNLIKELY(fLockWrite))
6830 {
6831 rc2 = vdThreadFinishWrite(pDisk);
6832 AssertRC(rc2);
6833 }
6834 else if (RT_UNLIKELY(fLockRead))
6835 {
6836 rc2 = vdThreadFinishRead(pDisk);
6837 AssertRC(rc2);
6838 }
6839
6840 if (RT_FAILURE(rc))
6841 {
6842 if (pCache)
6843 {
6844 if (pCache->pszFilename)
6845 RTStrFree(pCache->pszFilename);
6846 RTMemFree(pCache);
6847 }
6848 }
6849
6850 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
6851 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
6852
6853 LogFlowFunc(("returns %Rrc\n", rc));
6854 return rc;
6855}
6856
6857/**
6858 * Merges two images (not necessarily with direct parent/child relationship).
6859 * As a side effect the source image and potentially the other images which
6860 * are also merged to the destination are deleted from both the disk and the
6861 * images in the HDD container.
6862 *
6863 * @returns VBox status code.
6864 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
6865 * @param pDisk Pointer to HDD container.
6866 * @param nImageFrom Name of the image file to merge from.
6867 * @param nImageTo Name of the image file to merge to.
6868 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
6869 */
6870VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
6871 unsigned nImageTo, PVDINTERFACE pVDIfsOperation)
6872{
6873 int rc = VINF_SUCCESS;
6874 int rc2;
6875 bool fLockWrite = false, fLockRead = false;
6876 void *pvBuf = NULL;
6877
6878 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pVDIfsOperation=%#p\n",
6879 pDisk, nImageFrom, nImageTo, pVDIfsOperation));
6880
6881 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
6882
6883 do
6884 {
6885 /* sanity check */
6886 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
6887 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
6888
6889 /* For simplicity reasons lock for writing as the image reopen below
6890 * might need it. After all the reopen is usually needed. */
6891 rc2 = vdThreadStartWrite(pDisk);
6892 AssertRC(rc2);
6893 fLockWrite = true;
6894 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
6895 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
6896 if (!pImageFrom || !pImageTo)
6897 {
6898 rc = VERR_VD_IMAGE_NOT_FOUND;
6899 break;
6900 }
6901 AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
6902
6903 /* Make sure destination image is writable. */
6904 unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
6905 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
6906 {
6907 /*
6908 * Clear skip consistency checks because the image is made writable now and
6909 * skipping consistency checks is only possible for readonly images.
6910 */
6911 uOpenFlags &= ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS);
6912 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
6913 uOpenFlags);
6914 if (RT_FAILURE(rc))
6915 break;
6916 }
6917
6918 /* Get size of destination image. */
6919 uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
6920 rc2 = vdThreadFinishWrite(pDisk);
6921 AssertRC(rc2);
6922 fLockWrite = false;
6923
6924 /* Allocate tmp buffer. */
6925 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
6926 if (!pvBuf)
6927 {
6928 rc = VERR_NO_MEMORY;
6929 break;
6930 }
6931
6932 /* Merging is done directly on the images itself. This potentially
6933 * causes trouble if the disk is full in the middle of operation. */
6934 if (nImageFrom < nImageTo)
6935 {
6936 /* Merge parent state into child. This means writing all not
6937 * allocated blocks in the destination image which are allocated in
6938 * the images to be merged. */
6939 uint64_t uOffset = 0;
6940 uint64_t cbRemaining = cbSize;
6941 do
6942 {
6943 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
6944 RTSGSEG SegmentBuf;
6945 RTSGBUF SgBuf;
6946 VDIOCTX IoCtx;
6947
6948 SegmentBuf.pvSeg = pvBuf;
6949 SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
6950 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
6951 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
6952 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
6953
6954 /* Need to hold the write lock during a read-write operation. */
6955 rc2 = vdThreadStartWrite(pDisk);
6956 AssertRC(rc2);
6957 fLockWrite = true;
6958
6959 rc = pImageTo->Backend->pfnRead(pImageTo->pBackendData,
6960 uOffset, cbThisRead,
6961 &IoCtx, &cbThisRead);
6962 if (rc == VERR_VD_BLOCK_FREE)
6963 {
6964 /* Search for image with allocated block. Do not attempt to
6965 * read more than the previous reads marked as valid.
6966 * Otherwise this would return stale data when different
6967 * block sizes are used for the images. */
6968 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
6969 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VD_BLOCK_FREE;
6970 pCurrImage = pCurrImage->pPrev)
6971 {
6972 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
6973 uOffset, cbThisRead,
6974 &IoCtx, &cbThisRead);
6975 }
6976
6977 if (rc != VERR_VD_BLOCK_FREE)
6978 {
6979 if (RT_FAILURE(rc))
6980 break;
6981 /* Updating the cache is required because this might be a live merge. */
6982 rc = vdWriteHelperEx(pDisk, pImageTo, pImageFrom->pPrev,
6983 uOffset, pvBuf, cbThisRead,
6984 true /* fUpdateCache */, 0);
6985 if (RT_FAILURE(rc))
6986 break;
6987 }
6988 else
6989 rc = VINF_SUCCESS;
6990 }
6991 else if (RT_FAILURE(rc))
6992 break;
6993
6994 rc2 = vdThreadFinishWrite(pDisk);
6995 AssertRC(rc2);
6996 fLockWrite = false;
6997
6998 uOffset += cbThisRead;
6999 cbRemaining -= cbThisRead;
7000
7001 if (pIfProgress && pIfProgress->pfnProgress)
7002 {
7003 /** @todo r=klaus: this can update the progress to the same
7004 * percentage over and over again if the image format makes
7005 * relatively small increments. */
7006 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
7007 uOffset * 99 / cbSize);
7008 if (RT_FAILURE(rc))
7009 break;
7010 }
7011 } while (uOffset < cbSize);
7012 }
7013 else
7014 {
7015 /*
7016 * We may need to update the parent uuid of the child coming after
7017 * the last image to be merged. We have to reopen it read/write.
7018 *
7019 * This is done before we do the actual merge to prevent an
7020 * inconsistent chain if the mode change fails for some reason.
7021 */
7022 if (pImageFrom->pNext)
7023 {
7024 PVDIMAGE pImageChild = pImageFrom->pNext;
7025
7026 /* Take the write lock. */
7027 rc2 = vdThreadStartWrite(pDisk);
7028 AssertRC(rc2);
7029 fLockWrite = true;
7030
7031 /* We need to open the image in read/write mode. */
7032 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
7033
7034 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
7035 {
7036 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
7037 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
7038 uOpenFlags);
7039 if (RT_FAILURE(rc))
7040 break;
7041 }
7042
7043 rc2 = vdThreadFinishWrite(pDisk);
7044 AssertRC(rc2);
7045 fLockWrite = false;
7046 }
7047
7048 /* If the merge is from the last image we have to relay all writes
7049 * to the merge destination as well, so that concurrent writes
7050 * (in case of a live merge) are handled correctly. */
7051 if (!pImageFrom->pNext)
7052 {
7053 /* Take the write lock. */
7054 rc2 = vdThreadStartWrite(pDisk);
7055 AssertRC(rc2);
7056 fLockWrite = true;
7057
7058 pDisk->pImageRelay = pImageTo;
7059
7060 rc2 = vdThreadFinishWrite(pDisk);
7061 AssertRC(rc2);
7062 fLockWrite = false;
7063 }
7064
7065 /* Merge child state into parent. This means writing all blocks
7066 * which are allocated in the image up to the source image to the
7067 * destination image. */
7068 uint64_t uOffset = 0;
7069 uint64_t cbRemaining = cbSize;
7070 do
7071 {
7072 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
7073 RTSGSEG SegmentBuf;
7074 RTSGBUF SgBuf;
7075 VDIOCTX IoCtx;
7076
7077 rc = VERR_VD_BLOCK_FREE;
7078
7079 SegmentBuf.pvSeg = pvBuf;
7080 SegmentBuf.cbSeg = VD_MERGE_BUFFER_SIZE;
7081 RTSgBufInit(&SgBuf, &SegmentBuf, 1);
7082 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_READ, 0, 0, NULL,
7083 &SgBuf, NULL, NULL, VDIOCTX_FLAGS_SYNC);
7084
7085 /* Need to hold the write lock during a read-write operation. */
7086 rc2 = vdThreadStartWrite(pDisk);
7087 AssertRC(rc2);
7088 fLockWrite = true;
7089
7090 /* Search for image with allocated block. Do not attempt to
7091 * read more than the previous reads marked as valid. Otherwise
7092 * this would return stale data when different block sizes are
7093 * used for the images. */
7094 for (PVDIMAGE pCurrImage = pImageFrom;
7095 pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VD_BLOCK_FREE;
7096 pCurrImage = pCurrImage->pPrev)
7097 {
7098 rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
7099 uOffset, cbThisRead,
7100 &IoCtx, &cbThisRead);
7101 }
7102
7103 if (rc != VERR_VD_BLOCK_FREE)
7104 {
7105 if (RT_FAILURE(rc))
7106 break;
7107 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
7108 cbThisRead, true /* fUpdateCache */);
7109 if (RT_FAILURE(rc))
7110 break;
7111 }
7112 else
7113 rc = VINF_SUCCESS;
7114
7115 rc2 = vdThreadFinishWrite(pDisk);
7116 AssertRC(rc2);
7117 fLockWrite = false;
7118
7119 uOffset += cbThisRead;
7120 cbRemaining -= cbThisRead;
7121
7122 if (pIfProgress && pIfProgress->pfnProgress)
7123 {
7124 /** @todo r=klaus: this can update the progress to the same
7125 * percentage over and over again if the image format makes
7126 * relatively small increments. */
7127 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
7128 uOffset * 99 / cbSize);
7129 if (RT_FAILURE(rc))
7130 break;
7131 }
7132 } while (uOffset < cbSize);
7133
7134 /* In case we set up a "write proxy" image above we must clear
7135 * this again now to prevent stray writes. Failure or not. */
7136 if (!pImageFrom->pNext)
7137 {
7138 /* Take the write lock. */
7139 rc2 = vdThreadStartWrite(pDisk);
7140 AssertRC(rc2);
7141 fLockWrite = true;
7142
7143 pDisk->pImageRelay = NULL;
7144
7145 rc2 = vdThreadFinishWrite(pDisk);
7146 AssertRC(rc2);
7147 fLockWrite = false;
7148 }
7149 }
7150
7151 /*
7152 * Leave in case of an error to avoid corrupted data in the image chain
7153 * (includes cancelling the operation by the user).
7154 */
7155 if (RT_FAILURE(rc))
7156 break;
7157
7158 /* Need to hold the write lock while finishing the merge. */
7159 rc2 = vdThreadStartWrite(pDisk);
7160 AssertRC(rc2);
7161 fLockWrite = true;
7162
7163 /* Update parent UUID so that image chain is consistent.
7164 * The two attempts work around the problem that some backends
7165 * (e.g. iSCSI) do not support UUIDs, so we exploit the fact that
7166 * so far there can only be one such image in the chain. */
7167 /** @todo needs a better long-term solution, passing the UUID
7168 * knowledge from the caller or some such */
7169 RTUUID Uuid;
7170 PVDIMAGE pImageChild = NULL;
7171 if (nImageFrom < nImageTo)
7172 {
7173 if (pImageFrom->pPrev)
7174 {
7175 /* plan A: ask the parent itself for its UUID */
7176 rc = pImageFrom->pPrev->Backend->pfnGetUuid(pImageFrom->pPrev->pBackendData,
7177 &Uuid);
7178 if (RT_FAILURE(rc))
7179 {
7180 /* plan B: ask the child of the parent for parent UUID */
7181 rc = pImageFrom->Backend->pfnGetParentUuid(pImageFrom->pBackendData,
7182 &Uuid);
7183 }
7184 AssertRC(rc);
7185 }
7186 else
7187 RTUuidClear(&Uuid);
7188 rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pBackendData,
7189 &Uuid);
7190 AssertRC(rc);
7191 }
7192 else
7193 {
7194 /* Update the parent uuid of the child of the last merged image. */
7195 if (pImageFrom->pNext)
7196 {
7197 /* plan A: ask the parent itself for its UUID */
7198 rc = pImageTo->Backend->pfnGetUuid(pImageTo->pBackendData,
7199 &Uuid);
7200 if (RT_FAILURE(rc))
7201 {
7202 /* plan B: ask the child of the parent for parent UUID */
7203 rc = pImageTo->pNext->Backend->pfnGetParentUuid(pImageTo->pNext->pBackendData,
7204 &Uuid);
7205 }
7206 AssertRC(rc);
7207
7208 rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext->pBackendData,
7209 &Uuid);
7210 AssertRC(rc);
7211
7212 pImageChild = pImageFrom->pNext;
7213 }
7214 }
7215
7216 /* Delete the no longer needed images. */
7217 PVDIMAGE pImg = pImageFrom, pTmp;
7218 while (pImg != pImageTo)
7219 {
7220 if (nImageFrom < nImageTo)
7221 pTmp = pImg->pNext;
7222 else
7223 pTmp = pImg->pPrev;
7224 vdRemoveImageFromList(pDisk, pImg);
7225 pImg->Backend->pfnClose(pImg->pBackendData, true);
7226 RTMemFree(pImg->pszFilename);
7227 RTMemFree(pImg);
7228 pImg = pTmp;
7229 }
7230
7231 /* Make sure destination image is back to read only if necessary. */
7232 if (pImageTo != pDisk->pLast)
7233 {
7234 uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
7235 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
7236 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
7237 uOpenFlags);
7238 if (RT_FAILURE(rc))
7239 break;
7240 }
7241
7242 /*
7243 * Make sure the child is readonly
7244 * for the child -> parent merge direction
7245 * if necessary.
7246 */
7247 if ( nImageFrom > nImageTo
7248 && pImageChild
7249 && pImageChild != pDisk->pLast)
7250 {
7251 uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
7252 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
7253 rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
7254 uOpenFlags);
7255 if (RT_FAILURE(rc))
7256 break;
7257 }
7258 } while (0);
7259
7260 if (RT_UNLIKELY(fLockWrite))
7261 {
7262 rc2 = vdThreadFinishWrite(pDisk);
7263 AssertRC(rc2);
7264 }
7265 else if (RT_UNLIKELY(fLockRead))
7266 {
7267 rc2 = vdThreadFinishRead(pDisk);
7268 AssertRC(rc2);
7269 }
7270
7271 if (pvBuf)
7272 RTMemTmpFree(pvBuf);
7273
7274 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
7275 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7276
7277 LogFlowFunc(("returns %Rrc\n", rc));
7278 return rc;
7279}
7280
7281/**
7282 * Copies an image from one HDD container to another - extended version.
7283 * The copy is opened in the target HDD container.
7284 * It is possible to convert between different image formats, because the
7285 * backend for the destination may be different from the source.
7286 * If both the source and destination reference the same HDD container,
7287 * then the image is moved (by copying/deleting or renaming) to the new location.
7288 * The source container is unchanged if the move operation fails, otherwise
7289 * the image at the new location is opened in the same way as the old one was.
7290 *
7291 * @note The read/write accesses across disks are not synchronized, just the
7292 * accesses to each disk. Once there is a use case which requires a defined
7293 * read/write behavior in this situation this needs to be extended.
7294 *
7295 * @return VBox status code.
7296 * @return VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7297 * @param pDiskFrom Pointer to source HDD container.
7298 * @param nImage Image number, counts from 0. 0 is always base image of container.
7299 * @param pDiskTo Pointer to destination HDD container.
7300 * @param pszBackend Name of the image file backend to use (may be NULL to use the same as the source, case insensitive).
7301 * @param pszFilename New name of the image (may be NULL to specify that the
7302 * copy destination is the destination container, or
7303 * if pDiskFrom == pDiskTo, i.e. when moving).
7304 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
7305 * @param cbSize New image size (0 means leave unchanged).
7306 * @param nImageSameFrom todo
7307 * @param nImageSameTo todo
7308 * @param uImageFlags Flags specifying special destination image features.
7309 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
7310 * This parameter is used if and only if a true copy is created.
7311 * In all rename/move cases or copy to existing image cases the modification UUIDs are copied over.
7312 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
7313 * Only used if the destination image is created.
7314 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
7315 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
7316 * destination image.
7317 * @param pDstVDIfsOperation Pointer to the per-operation VD interface list,
7318 * for the destination operation.
7319 */
7320VBOXDDU_DECL(int) VDCopyEx(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
7321 const char *pszBackend, const char *pszFilename,
7322 bool fMoveByRename, uint64_t cbSize,
7323 unsigned nImageFromSame, unsigned nImageToSame,
7324 unsigned uImageFlags, PCRTUUID pDstUuid,
7325 unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
7326 PVDINTERFACE pDstVDIfsImage,
7327 PVDINTERFACE pDstVDIfsOperation)
7328{
7329 int rc = VINF_SUCCESS;
7330 int rc2;
7331 bool fLockReadFrom = false, fLockWriteFrom = false, fLockWriteTo = false;
7332 PVDIMAGE pImageTo = NULL;
7333
7334 LogFlowFunc(("pDiskFrom=%#p nImage=%u pDiskTo=%#p pszBackend=\"%s\" pszFilename=\"%s\" fMoveByRename=%d cbSize=%llu nImageFromSame=%u nImageToSame=%u uImageFlags=%#x pDstUuid=%#p uOpenFlags=%#x pVDIfsOperation=%#p pDstVDIfsImage=%#p pDstVDIfsOperation=%#p\n",
7335 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, nImageFromSame, nImageToSame, uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
7336
7337 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7338 PVDINTERFACEPROGRESS pDstIfProgress = VDIfProgressGet(pDstVDIfsOperation);
7339
7340 do {
7341 /* Check arguments. */
7342 AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
7343 rc = VERR_INVALID_PARAMETER);
7344 AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
7345 ("u32Signature=%08x\n", pDiskFrom->u32Signature));
7346
7347 rc2 = vdThreadStartRead(pDiskFrom);
7348 AssertRC(rc2);
7349 fLockReadFrom = true;
7350 PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
7351 AssertPtrBreakStmt(pImageFrom, rc = VERR_VD_IMAGE_NOT_FOUND);
7352 AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
7353 rc = VERR_INVALID_PARAMETER);
7354 AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
7355 ("u32Signature=%08x\n", pDiskTo->u32Signature));
7356 AssertMsgBreakStmt( (nImageFromSame < nImage || nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN)
7357 && (nImageToSame < pDiskTo->cImages || nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
7358 && ( (nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN && nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
7359 || (nImageFromSame != VD_IMAGE_CONTENT_UNKNOWN && nImageToSame != VD_IMAGE_CONTENT_UNKNOWN)),
7360 ("nImageFromSame=%u nImageToSame=%u\n", nImageFromSame, nImageToSame),
7361 rc = VERR_INVALID_PARAMETER);
7362
7363 /* Move the image. */
7364 if (pDiskFrom == pDiskTo)
7365 {
7366 /* Rename only works when backends are the same, are file based
7367 * and the rename method is implemented. */
7368 if ( fMoveByRename
7369 && !RTStrICmp(pszBackend, pImageFrom->Backend->pszBackendName)
7370 && pImageFrom->Backend->uBackendCaps & VD_CAP_FILE
7371 && pImageFrom->Backend->pfnRename)
7372 {
7373 rc2 = vdThreadFinishRead(pDiskFrom);
7374 AssertRC(rc2);
7375 fLockReadFrom = false;
7376
7377 rc2 = vdThreadStartWrite(pDiskFrom);
7378 AssertRC(rc2);
7379 fLockWriteFrom = true;
7380 rc = pImageFrom->Backend->pfnRename(pImageFrom->pBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
7381 break;
7382 }
7383
7384 /** @todo Moving (including shrinking/growing) of the image is
7385 * requested, but the rename attempt failed or it wasn't possible.
7386 * Must now copy image to temp location. */
7387 AssertReleaseMsgFailed(("VDCopy: moving by copy/delete not implemented\n"));
7388 }
7389
7390 /* pszFilename is allowed to be NULL, as this indicates copy to the existing image. */
7391 AssertMsgBreakStmt(pszFilename == NULL || (VALID_PTR(pszFilename) && *pszFilename),
7392 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
7393 rc = VERR_INVALID_PARAMETER);
7394
7395 uint64_t cbSizeFrom;
7396 cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pBackendData);
7397 if (cbSizeFrom == 0)
7398 {
7399 rc = VERR_VD_VALUE_NOT_FOUND;
7400 break;
7401 }
7402
7403 VDGEOMETRY PCHSGeometryFrom = {0, 0, 0};
7404 VDGEOMETRY LCHSGeometryFrom = {0, 0, 0};
7405 pImageFrom->Backend->pfnGetPCHSGeometry(pImageFrom->pBackendData, &PCHSGeometryFrom);
7406 pImageFrom->Backend->pfnGetLCHSGeometry(pImageFrom->pBackendData, &LCHSGeometryFrom);
7407
7408 RTUUID ImageUuid, ImageModificationUuid;
7409 if (pDiskFrom != pDiskTo)
7410 {
7411 if (pDstUuid)
7412 ImageUuid = *pDstUuid;
7413 else
7414 RTUuidCreate(&ImageUuid);
7415 }
7416 else
7417 {
7418 rc = pImageFrom->Backend->pfnGetUuid(pImageFrom->pBackendData, &ImageUuid);
7419 if (RT_FAILURE(rc))
7420 RTUuidCreate(&ImageUuid);
7421 }
7422 rc = pImageFrom->Backend->pfnGetModificationUuid(pImageFrom->pBackendData, &ImageModificationUuid);
7423 if (RT_FAILURE(rc))
7424 RTUuidClear(&ImageModificationUuid);
7425
7426 char szComment[1024];
7427 rc = pImageFrom->Backend->pfnGetComment(pImageFrom->pBackendData, szComment, sizeof(szComment));
7428 if (RT_FAILURE(rc))
7429 szComment[0] = '\0';
7430 else
7431 szComment[sizeof(szComment) - 1] = '\0';
7432
7433 rc2 = vdThreadFinishRead(pDiskFrom);
7434 AssertRC(rc2);
7435 fLockReadFrom = false;
7436
7437 rc2 = vdThreadStartRead(pDiskTo);
7438 AssertRC(rc2);
7439 unsigned cImagesTo = pDiskTo->cImages;
7440 rc2 = vdThreadFinishRead(pDiskTo);
7441 AssertRC(rc2);
7442
7443 if (pszFilename)
7444 {
7445 if (cbSize == 0)
7446 cbSize = cbSizeFrom;
7447
7448 /* Create destination image with the properties of source image. */
7449 /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
7450 * calls to the backend. Unifies the code and reduces the API
7451 * dependencies. Would also make the synchronization explicit. */
7452 if (cImagesTo > 0)
7453 {
7454 rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename,
7455 uImageFlags, szComment, &ImageUuid,
7456 NULL /* pParentUuid */,
7457 uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
7458 pDstVDIfsImage, NULL);
7459
7460 rc2 = vdThreadStartWrite(pDiskTo);
7461 AssertRC(rc2);
7462 fLockWriteTo = true;
7463 } else {
7464 /** @todo hack to force creation of a fixed image for
7465 * the RAW backend, which can't handle anything else. */
7466 if (!RTStrICmp(pszBackend, "RAW"))
7467 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
7468
7469 vdFixupPCHSGeometry(&PCHSGeometryFrom, cbSize);
7470 vdFixupLCHSGeometry(&LCHSGeometryFrom, cbSize);
7471
7472 rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, cbSize,
7473 uImageFlags, szComment,
7474 &PCHSGeometryFrom, &LCHSGeometryFrom,
7475 NULL, uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
7476 pDstVDIfsImage, NULL);
7477
7478 rc2 = vdThreadStartWrite(pDiskTo);
7479 AssertRC(rc2);
7480 fLockWriteTo = true;
7481
7482 if (RT_SUCCESS(rc) && !RTUuidIsNull(&ImageUuid))
7483 pDiskTo->pLast->Backend->pfnSetUuid(pDiskTo->pLast->pBackendData, &ImageUuid);
7484 }
7485 if (RT_FAILURE(rc))
7486 break;
7487
7488 pImageTo = pDiskTo->pLast;
7489 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
7490
7491 cbSize = RT_MIN(cbSize, cbSizeFrom);
7492 }
7493 else
7494 {
7495 pImageTo = pDiskTo->pLast;
7496 AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
7497
7498 uint64_t cbSizeTo;
7499 cbSizeTo = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
7500 if (cbSizeTo == 0)
7501 {
7502 rc = VERR_VD_VALUE_NOT_FOUND;
7503 break;
7504 }
7505
7506 if (cbSize == 0)
7507 cbSize = RT_MIN(cbSizeFrom, cbSizeTo);
7508
7509 vdFixupPCHSGeometry(&PCHSGeometryFrom, cbSize);
7510 vdFixupLCHSGeometry(&LCHSGeometryFrom, cbSize);
7511
7512 /* Update the geometry in the destination image. */
7513 pImageTo->Backend->pfnSetPCHSGeometry(pImageTo->pBackendData, &PCHSGeometryFrom);
7514 pImageTo->Backend->pfnSetLCHSGeometry(pImageTo->pBackendData, &LCHSGeometryFrom);
7515 }
7516
7517 rc2 = vdThreadFinishWrite(pDiskTo);
7518 AssertRC(rc2);
7519 fLockWriteTo = false;
7520
7521 /* Whether we can take the optimized copy path (false) or not.
7522 * Don't optimize if the image existed or if it is a child image. */
7523 bool fSuppressRedundantIo = ( !(pszFilename == NULL || cImagesTo > 0)
7524 || (nImageToSame != VD_IMAGE_CONTENT_UNKNOWN));
7525 unsigned cImagesFromReadBack, cImagesToReadBack;
7526
7527 if (nImageFromSame == VD_IMAGE_CONTENT_UNKNOWN)
7528 cImagesFromReadBack = 0;
7529 else
7530 {
7531 if (nImage == VD_LAST_IMAGE)
7532 cImagesFromReadBack = pDiskFrom->cImages - nImageFromSame - 1;
7533 else
7534 cImagesFromReadBack = nImage - nImageFromSame;
7535 }
7536
7537 if (nImageToSame == VD_IMAGE_CONTENT_UNKNOWN)
7538 cImagesToReadBack = 0;
7539 else
7540 cImagesToReadBack = pDiskTo->cImages - nImageToSame - 1;
7541
7542 /* Copy the data. */
7543 rc = vdCopyHelper(pDiskFrom, pImageFrom, pDiskTo, cbSize,
7544 cImagesFromReadBack, cImagesToReadBack,
7545 fSuppressRedundantIo, pIfProgress, pDstIfProgress);
7546
7547 if (RT_SUCCESS(rc))
7548 {
7549 rc2 = vdThreadStartWrite(pDiskTo);
7550 AssertRC(rc2);
7551 fLockWriteTo = true;
7552
7553 /* Only set modification UUID if it is non-null, since the source
7554 * backend might not provide a valid modification UUID. */
7555 if (!RTUuidIsNull(&ImageModificationUuid))
7556 pImageTo->Backend->pfnSetModificationUuid(pImageTo->pBackendData, &ImageModificationUuid);
7557
7558 /* Set the requested open flags if they differ from the value
7559 * required for creating the image and copying the contents. */
7560 if ( pImageTo && pszFilename
7561 && uOpenFlags != (uOpenFlags & ~VD_OPEN_FLAGS_READONLY))
7562 rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
7563 uOpenFlags);
7564 }
7565 } while (0);
7566
7567 if (RT_FAILURE(rc) && pImageTo && pszFilename)
7568 {
7569 /* Take the write lock only if it is not taken. Not worth making the
7570 * above code even more complicated. */
7571 if (RT_UNLIKELY(!fLockWriteTo))
7572 {
7573 rc2 = vdThreadStartWrite(pDiskTo);
7574 AssertRC(rc2);
7575 fLockWriteTo = true;
7576 }
7577 /* Error detected, but new image created. Remove image from list. */
7578 vdRemoveImageFromList(pDiskTo, pImageTo);
7579
7580 /* Close and delete image. */
7581 rc2 = pImageTo->Backend->pfnClose(pImageTo->pBackendData, true);
7582 AssertRC(rc2);
7583 pImageTo->pBackendData = NULL;
7584
7585 /* Free remaining resources. */
7586 if (pImageTo->pszFilename)
7587 RTStrFree(pImageTo->pszFilename);
7588
7589 RTMemFree(pImageTo);
7590 }
7591
7592 if (RT_UNLIKELY(fLockWriteTo))
7593 {
7594 rc2 = vdThreadFinishWrite(pDiskTo);
7595 AssertRC(rc2);
7596 }
7597 if (RT_UNLIKELY(fLockWriteFrom))
7598 {
7599 rc2 = vdThreadFinishWrite(pDiskFrom);
7600 AssertRC(rc2);
7601 }
7602 else if (RT_UNLIKELY(fLockReadFrom))
7603 {
7604 rc2 = vdThreadFinishRead(pDiskFrom);
7605 AssertRC(rc2);
7606 }
7607
7608 if (RT_SUCCESS(rc))
7609 {
7610 if (pIfProgress && pIfProgress->pfnProgress)
7611 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7612 if (pDstIfProgress && pDstIfProgress->pfnProgress)
7613 pDstIfProgress->pfnProgress(pDstIfProgress->Core.pvUser, 100);
7614 }
7615
7616 LogFlowFunc(("returns %Rrc\n", rc));
7617 return rc;
7618}
7619
7620/**
7621 * Copies an image from one HDD container to another.
7622 * The copy is opened in the target HDD container.
7623 * It is possible to convert between different image formats, because the
7624 * backend for the destination may be different from the source.
7625 * If both the source and destination reference the same HDD container,
7626 * then the image is moved (by copying/deleting or renaming) to the new location.
7627 * The source container is unchanged if the move operation fails, otherwise
7628 * the image at the new location is opened in the same way as the old one was.
7629 *
7630 * @returns VBox status code.
7631 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7632 * @param pDiskFrom Pointer to source HDD container.
7633 * @param nImage Image number, counts from 0. 0 is always base image of container.
7634 * @param pDiskTo Pointer to destination HDD container.
7635 * @param pszBackend Name of the image file backend to use.
7636 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
7637 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
7638 * @param cbSize New image size (0 means leave unchanged).
7639 * @param uImageFlags Flags specifying special destination image features.
7640 * @param pDstUuid New UUID of the destination image. If NULL, a new UUID is created.
7641 * This parameter is used if and only if a true copy is created.
7642 * In all rename/move cases the UUIDs are copied over.
7643 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
7644 * Only used if the destination image is created.
7645 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
7646 * @param pDstVDIfsImage Pointer to the per-image VD interface list, for the
7647 * destination image.
7648 * @param pDstVDIfsOperation Pointer to the per-image VD interface list,
7649 * for the destination image.
7650 */
7651VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
7652 const char *pszBackend, const char *pszFilename,
7653 bool fMoveByRename, uint64_t cbSize,
7654 unsigned uImageFlags, PCRTUUID pDstUuid,
7655 unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
7656 PVDINTERFACE pDstVDIfsImage,
7657 PVDINTERFACE pDstVDIfsOperation)
7658{
7659 return VDCopyEx(pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename,
7660 cbSize, VD_IMAGE_CONTENT_UNKNOWN, VD_IMAGE_CONTENT_UNKNOWN,
7661 uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation,
7662 pDstVDIfsImage, pDstVDIfsOperation);
7663}
7664
7665/**
7666 * Optimizes the storage consumption of an image. Typically the unused blocks
7667 * have to be wiped with zeroes to achieve a substantial reduced storage use.
7668 * Another optimization done is reordering the image blocks, which can provide
7669 * a significant performance boost, as reads and writes tend to use less random
7670 * file offsets.
7671 *
7672 * @return VBox status code.
7673 * @return VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
7674 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
7675 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
7676 * the code for this isn't implemented yet.
7677 * @param pDisk Pointer to HDD container.
7678 * @param nImage Image number, counts from 0. 0 is always base image of container.
7679 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
7680 */
7681VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
7682 PVDINTERFACE pVDIfsOperation)
7683{
7684 int rc = VINF_SUCCESS;
7685 int rc2;
7686 bool fLockRead = false, fLockWrite = false;
7687 void *pvBuf = NULL;
7688 void *pvTmp = NULL;
7689
7690 LogFlowFunc(("pDisk=%#p nImage=%u pVDIfsOperation=%#p\n",
7691 pDisk, nImage, pVDIfsOperation));
7692
7693 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7694
7695 do {
7696 /* Check arguments. */
7697 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
7698 rc = VERR_INVALID_PARAMETER);
7699 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
7700 ("u32Signature=%08x\n", pDisk->u32Signature));
7701
7702 rc2 = vdThreadStartRead(pDisk);
7703 AssertRC(rc2);
7704 fLockRead = true;
7705
7706 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
7707 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
7708
7709 /* If there is no compact callback for not file based backends then
7710 * the backend doesn't need compaction. No need to make much fuss about
7711 * this. For file based ones signal this as not yet supported. */
7712 if (!pImage->Backend->pfnCompact)
7713 {
7714 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
7715 rc = VERR_NOT_SUPPORTED;
7716 else
7717 rc = VINF_SUCCESS;
7718 break;
7719 }
7720
7721 /* Insert interface for reading parent state into per-operation list,
7722 * if there is a parent image. */
7723 VDINTERFACEPARENTSTATE VDIfParent;
7724 VDPARENTSTATEDESC ParentUser;
7725 if (pImage->pPrev)
7726 {
7727 VDIfParent.pfnParentRead = vdParentRead;
7728 ParentUser.pDisk = pDisk;
7729 ParentUser.pImage = pImage->pPrev;
7730 rc = VDInterfaceAdd(&VDIfParent.Core, "VDCompact_ParentState", VDINTERFACETYPE_PARENTSTATE,
7731 &ParentUser, sizeof(VDINTERFACEPARENTSTATE), &pVDIfsOperation);
7732 AssertRC(rc);
7733 }
7734
7735 rc2 = vdThreadFinishRead(pDisk);
7736 AssertRC(rc2);
7737 fLockRead = false;
7738
7739 rc2 = vdThreadStartWrite(pDisk);
7740 AssertRC(rc2);
7741 fLockWrite = true;
7742
7743 rc = pImage->Backend->pfnCompact(pImage->pBackendData,
7744 0, 99,
7745 pDisk->pVDIfsDisk,
7746 pImage->pVDIfsImage,
7747 pVDIfsOperation);
7748 } while (0);
7749
7750 if (RT_UNLIKELY(fLockWrite))
7751 {
7752 rc2 = vdThreadFinishWrite(pDisk);
7753 AssertRC(rc2);
7754 }
7755 else if (RT_UNLIKELY(fLockRead))
7756 {
7757 rc2 = vdThreadFinishRead(pDisk);
7758 AssertRC(rc2);
7759 }
7760
7761 if (pvBuf)
7762 RTMemTmpFree(pvBuf);
7763 if (pvTmp)
7764 RTMemTmpFree(pvTmp);
7765
7766 if (RT_SUCCESS(rc))
7767 {
7768 if (pIfProgress && pIfProgress->pfnProgress)
7769 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7770 }
7771
7772 LogFlowFunc(("returns %Rrc\n", rc));
7773 return rc;
7774}
7775
7776/**
7777 * Resizes the given disk image to the given size.
7778 *
7779 * @return VBox status
7780 * @return VERR_VD_IMAGE_READ_ONLY if image is not writable.
7781 * @return VERR_NOT_SUPPORTED if this kind of image can be compacted, but
7782 *
7783 * @param pDisk Pointer to the HDD container.
7784 * @param cbSize New size of the image.
7785 * @param pPCHSGeometry Pointer to the new physical disk geometry <= (16383,16,63). Not NULL.
7786 * @param pLCHSGeometry Pointer to the new logical disk geometry <= (x,255,63). Not NULL.
7787 * @param pVDIfsOperation Pointer to the per-operation VD interface list.
7788 */
7789VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, uint64_t cbSize,
7790 PCVDGEOMETRY pPCHSGeometry,
7791 PCVDGEOMETRY pLCHSGeometry,
7792 PVDINTERFACE pVDIfsOperation)
7793{
7794 /** @todo r=klaus resizing was designed to be part of VDCopy, so having a separate function is not desirable. */
7795 int rc = VINF_SUCCESS;
7796 int rc2;
7797 bool fLockRead = false, fLockWrite = false;
7798
7799 LogFlowFunc(("pDisk=%#p cbSize=%llu pVDIfsOperation=%#p\n",
7800 pDisk, cbSize, pVDIfsOperation));
7801
7802 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7803
7804 do {
7805 /* Check arguments. */
7806 AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
7807 rc = VERR_INVALID_PARAMETER);
7808 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
7809 ("u32Signature=%08x\n", pDisk->u32Signature));
7810
7811 rc2 = vdThreadStartRead(pDisk);
7812 AssertRC(rc2);
7813 fLockRead = true;
7814
7815 /* Not supported if the disk has child images attached. */
7816 AssertMsgBreakStmt(pDisk->cImages == 1, ("cImages=%u\n", pDisk->cImages),
7817 rc = VERR_NOT_SUPPORTED);
7818
7819 PVDIMAGE pImage = pDisk->pBase;
7820
7821 /* If there is no compact callback for not file based backends then
7822 * the backend doesn't need compaction. No need to make much fuss about
7823 * this. For file based ones signal this as not yet supported. */
7824 if (!pImage->Backend->pfnResize)
7825 {
7826 if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
7827 rc = VERR_NOT_SUPPORTED;
7828 else
7829 rc = VINF_SUCCESS;
7830 break;
7831 }
7832
7833 rc2 = vdThreadFinishRead(pDisk);
7834 AssertRC(rc2);
7835 fLockRead = false;
7836
7837 rc2 = vdThreadStartWrite(pDisk);
7838 AssertRC(rc2);
7839 fLockWrite = true;
7840
7841 VDGEOMETRY PCHSGeometryOld;
7842 VDGEOMETRY LCHSGeometryOld;
7843 PCVDGEOMETRY pPCHSGeometryNew;
7844 PCVDGEOMETRY pLCHSGeometryNew;
7845
7846 if (pPCHSGeometry->cCylinders == 0)
7847 {
7848 /* Auto-detect marker, calculate new value ourself. */
7849 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData, &PCHSGeometryOld);
7850 if (RT_SUCCESS(rc) && (PCHSGeometryOld.cCylinders != 0))
7851 PCHSGeometryOld.cCylinders = RT_MIN(cbSize / 512 / PCHSGeometryOld.cHeads / PCHSGeometryOld.cSectors, 16383);
7852 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
7853 rc = VINF_SUCCESS;
7854
7855 pPCHSGeometryNew = &PCHSGeometryOld;
7856 }
7857 else
7858 pPCHSGeometryNew = pPCHSGeometry;
7859
7860 if (pLCHSGeometry->cCylinders == 0)
7861 {
7862 /* Auto-detect marker, calculate new value ourself. */
7863 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData, &LCHSGeometryOld);
7864 if (RT_SUCCESS(rc) && (LCHSGeometryOld.cCylinders != 0))
7865 LCHSGeometryOld.cCylinders = cbSize / 512 / LCHSGeometryOld.cHeads / LCHSGeometryOld.cSectors;
7866 else if (rc == VERR_VD_GEOMETRY_NOT_SET)
7867 rc = VINF_SUCCESS;
7868
7869 pLCHSGeometryNew = &LCHSGeometryOld;
7870 }
7871 else
7872 pLCHSGeometryNew = pLCHSGeometry;
7873
7874 if (RT_SUCCESS(rc))
7875 rc = pImage->Backend->pfnResize(pImage->pBackendData,
7876 cbSize,
7877 pPCHSGeometryNew,
7878 pLCHSGeometryNew,
7879 0, 99,
7880 pDisk->pVDIfsDisk,
7881 pImage->pVDIfsImage,
7882 pVDIfsOperation);
7883 } while (0);
7884
7885 if (RT_UNLIKELY(fLockWrite))
7886 {
7887 rc2 = vdThreadFinishWrite(pDisk);
7888 AssertRC(rc2);
7889 }
7890 else if (RT_UNLIKELY(fLockRead))
7891 {
7892 rc2 = vdThreadFinishRead(pDisk);
7893 AssertRC(rc2);
7894 }
7895
7896 if (RT_SUCCESS(rc))
7897 {
7898 if (pIfProgress && pIfProgress->pfnProgress)
7899 pIfProgress->pfnProgress(pIfProgress->Core.pvUser, 100);
7900
7901 pDisk->cbSize = cbSize;
7902 }
7903
7904 LogFlowFunc(("returns %Rrc\n", rc));
7905 return rc;
7906}
7907
7908/**
7909 * Closes the last opened image file in HDD container.
7910 * If previous image file was opened in read-only mode (the normal case) and
7911 * the last opened image is in read-write mode then the previous image will be
7912 * reopened in read/write mode.
7913 *
7914 * @returns VBox status code.
7915 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
7916 * @param pDisk Pointer to HDD container.
7917 * @param fDelete If true, delete the image from the host disk.
7918 */
7919VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
7920{
7921 int rc = VINF_SUCCESS;
7922 int rc2;
7923 bool fLockWrite = false;
7924
7925 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
7926 do
7927 {
7928 /* sanity check */
7929 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
7930 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
7931
7932 /* Not worth splitting this up into a read lock phase and write
7933 * lock phase, as closing an image is a relatively fast operation
7934 * dominated by the part which needs the write lock. */
7935 rc2 = vdThreadStartWrite(pDisk);
7936 AssertRC(rc2);
7937 fLockWrite = true;
7938
7939 PVDIMAGE pImage = pDisk->pLast;
7940 if (!pImage)
7941 {
7942 rc = VERR_VD_NOT_OPENED;
7943 break;
7944 }
7945
7946 /* Destroy the current discard state first which might still have pending blocks. */
7947 rc = vdDiscardStateDestroy(pDisk);
7948 if (RT_FAILURE(rc))
7949 break;
7950
7951 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
7952 /* Remove image from list of opened images. */
7953 vdRemoveImageFromList(pDisk, pImage);
7954 /* Close (and optionally delete) image. */
7955 rc = pImage->Backend->pfnClose(pImage->pBackendData, fDelete);
7956 /* Free remaining resources related to the image. */
7957 RTStrFree(pImage->pszFilename);
7958 RTMemFree(pImage);
7959
7960 pImage = pDisk->pLast;
7961 if (!pImage)
7962 break;
7963
7964 /* If disk was previously in read/write mode, make sure it will stay
7965 * like this (if possible) after closing this image. Set the open flags
7966 * accordingly. */
7967 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
7968 {
7969 uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
7970 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
7971 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
7972 }
7973
7974 /* Cache disk information. */
7975 pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
7976
7977 /* Cache PCHS geometry. */
7978 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
7979 &pDisk->PCHSGeometry);
7980 if (RT_FAILURE(rc2))
7981 {
7982 pDisk->PCHSGeometry.cCylinders = 0;
7983 pDisk->PCHSGeometry.cHeads = 0;
7984 pDisk->PCHSGeometry.cSectors = 0;
7985 }
7986 else
7987 {
7988 /* Make sure the PCHS geometry is properly clipped. */
7989 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
7990 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
7991 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
7992 }
7993
7994 /* Cache LCHS geometry. */
7995 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
7996 &pDisk->LCHSGeometry);
7997 if (RT_FAILURE(rc2))
7998 {
7999 pDisk->LCHSGeometry.cCylinders = 0;
8000 pDisk->LCHSGeometry.cHeads = 0;
8001 pDisk->LCHSGeometry.cSectors = 0;
8002 }
8003 else
8004 {
8005 /* Make sure the LCHS geometry is properly clipped. */
8006 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
8007 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
8008 }
8009 } while (0);
8010
8011 if (RT_UNLIKELY(fLockWrite))
8012 {
8013 rc2 = vdThreadFinishWrite(pDisk);
8014 AssertRC(rc2);
8015 }
8016
8017 LogFlowFunc(("returns %Rrc\n", rc));
8018 return rc;
8019}
8020
8021/**
8022 * Closes the currently opened cache image file in HDD container.
8023 *
8024 * @return VBox status code.
8025 * @return VERR_VD_NOT_OPENED if no cache is opened in HDD container.
8026 * @param pDisk Pointer to HDD container.
8027 * @param fDelete If true, delete the image from the host disk.
8028 */
8029VBOXDDU_DECL(int) VDCacheClose(PVBOXHDD pDisk, bool fDelete)
8030{
8031 int rc = VINF_SUCCESS;
8032 int rc2;
8033 bool fLockWrite = false;
8034 PVDCACHE pCache = NULL;
8035
8036 LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
8037
8038 do
8039 {
8040 /* sanity check */
8041 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8042 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8043
8044 rc2 = vdThreadStartWrite(pDisk);
8045 AssertRC(rc2);
8046 fLockWrite = true;
8047
8048 AssertPtrBreakStmt(pDisk->pCache, rc = VERR_VD_CACHE_NOT_FOUND);
8049
8050 pCache = pDisk->pCache;
8051 pDisk->pCache = NULL;
8052
8053 pCache->Backend->pfnClose(pCache->pBackendData, fDelete);
8054 if (pCache->pszFilename)
8055 RTStrFree(pCache->pszFilename);
8056 RTMemFree(pCache);
8057 } while (0);
8058
8059 if (RT_LIKELY(fLockWrite))
8060 {
8061 rc2 = vdThreadFinishWrite(pDisk);
8062 AssertRC(rc2);
8063 }
8064
8065 LogFlowFunc(("returns %Rrc\n", rc));
8066 return rc;
8067}
8068
8069/**
8070 * Closes all opened image files in HDD container.
8071 *
8072 * @returns VBox status code.
8073 * @param pDisk Pointer to HDD container.
8074 */
8075VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
8076{
8077 int rc = VINF_SUCCESS;
8078 int rc2;
8079 bool fLockWrite = false;
8080
8081 LogFlowFunc(("pDisk=%#p\n", pDisk));
8082 do
8083 {
8084 /* sanity check */
8085 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8086 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8087
8088 /* Lock the entire operation. */
8089 rc2 = vdThreadStartWrite(pDisk);
8090 AssertRC(rc2);
8091 fLockWrite = true;
8092
8093 PVDCACHE pCache = pDisk->pCache;
8094 if (pCache)
8095 {
8096 rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
8097 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
8098 rc = rc2;
8099
8100 if (pCache->pszFilename)
8101 RTStrFree(pCache->pszFilename);
8102 RTMemFree(pCache);
8103 }
8104
8105 PVDIMAGE pImage = pDisk->pLast;
8106 while (VALID_PTR(pImage))
8107 {
8108 PVDIMAGE pPrev = pImage->pPrev;
8109 /* Remove image from list of opened images. */
8110 vdRemoveImageFromList(pDisk, pImage);
8111 /* Close image. */
8112 rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
8113 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
8114 rc = rc2;
8115 /* Free remaining resources related to the image. */
8116 RTStrFree(pImage->pszFilename);
8117 RTMemFree(pImage);
8118 pImage = pPrev;
8119 }
8120 Assert(!VALID_PTR(pDisk->pLast));
8121 } while (0);
8122
8123 if (RT_UNLIKELY(fLockWrite))
8124 {
8125 rc2 = vdThreadFinishWrite(pDisk);
8126 AssertRC(rc2);
8127 }
8128
8129 LogFlowFunc(("returns %Rrc\n", rc));
8130 return rc;
8131}
8132
8133/**
8134 * Read data from virtual HDD.
8135 *
8136 * @returns VBox status code.
8137 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
8138 * @param pDisk Pointer to HDD container.
8139 * @param uOffset Offset of first reading byte from start of disk.
8140 * @param pvBuf Pointer to buffer for reading data.
8141 * @param cbRead Number of bytes to read.
8142 */
8143VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
8144 size_t cbRead)
8145{
8146 int rc = VINF_SUCCESS;
8147 int rc2;
8148 bool fLockRead = false;
8149
8150 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
8151 pDisk, uOffset, pvBuf, cbRead));
8152 do
8153 {
8154 /* sanity check */
8155 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8156 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8157
8158 /* Check arguments. */
8159 AssertMsgBreakStmt(VALID_PTR(pvBuf),
8160 ("pvBuf=%#p\n", pvBuf),
8161 rc = VERR_INVALID_PARAMETER);
8162 AssertMsgBreakStmt(cbRead,
8163 ("cbRead=%zu\n", cbRead),
8164 rc = VERR_INVALID_PARAMETER);
8165
8166 rc2 = vdThreadStartRead(pDisk);
8167 AssertRC(rc2);
8168 fLockRead = true;
8169
8170 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
8171 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
8172 uOffset, cbRead, pDisk->cbSize),
8173 rc = VERR_INVALID_PARAMETER);
8174
8175 PVDIMAGE pImage = pDisk->pLast;
8176 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
8177
8178 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead,
8179 true /* fUpdateCache */);
8180 } while (0);
8181
8182 if (RT_UNLIKELY(fLockRead))
8183 {
8184 rc2 = vdThreadFinishRead(pDisk);
8185 AssertRC(rc2);
8186 }
8187
8188 LogFlowFunc(("returns %Rrc\n", rc));
8189 return rc;
8190}
8191
8192/**
8193 * Write data to virtual HDD.
8194 *
8195 * @returns VBox status code.
8196 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
8197 * @param pDisk Pointer to HDD container.
8198 * @param uOffset Offset of the first byte being
8199 * written from start of disk.
8200 * @param pvBuf Pointer to buffer for writing data.
8201 * @param cbWrite Number of bytes to write.
8202 */
8203VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
8204 size_t cbWrite)
8205{
8206 int rc = VINF_SUCCESS;
8207 int rc2;
8208 bool fLockWrite = false;
8209
8210 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
8211 pDisk, uOffset, pvBuf, cbWrite));
8212 do
8213 {
8214 /* sanity check */
8215 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8216 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8217
8218 /* Check arguments. */
8219 AssertMsgBreakStmt(VALID_PTR(pvBuf),
8220 ("pvBuf=%#p\n", pvBuf),
8221 rc = VERR_INVALID_PARAMETER);
8222 AssertMsgBreakStmt(cbWrite,
8223 ("cbWrite=%zu\n", cbWrite),
8224 rc = VERR_INVALID_PARAMETER);
8225
8226 rc2 = vdThreadStartWrite(pDisk);
8227 AssertRC(rc2);
8228 fLockWrite = true;
8229
8230 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
8231 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
8232 uOffset, cbWrite, pDisk->cbSize),
8233 rc = VERR_INVALID_PARAMETER);
8234
8235 PVDIMAGE pImage = pDisk->pLast;
8236 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
8237
8238 vdSetModifiedFlag(pDisk);
8239 rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite,
8240 true /* fUpdateCache */);
8241 if (RT_FAILURE(rc))
8242 break;
8243
8244 /* If there is a merge (in the direction towards a parent) running
8245 * concurrently then we have to also "relay" the write to this parent,
8246 * as the merge position might be already past the position where
8247 * this write is going. The "context" of the write can come from the
8248 * natural chain, since merging either already did or will take care
8249 * of the "other" content which is might be needed to fill the block
8250 * to a full allocation size. The cache doesn't need to be touched
8251 * as this write is covered by the previous one. */
8252 if (RT_UNLIKELY(pDisk->pImageRelay))
8253 rc = vdWriteHelper(pDisk, pDisk->pImageRelay, uOffset,
8254 pvBuf, cbWrite, false /* fUpdateCache */);
8255 } while (0);
8256
8257 if (RT_UNLIKELY(fLockWrite))
8258 {
8259 rc2 = vdThreadFinishWrite(pDisk);
8260 AssertRC(rc2);
8261 }
8262
8263 LogFlowFunc(("returns %Rrc\n", rc));
8264 return rc;
8265}
8266
8267/**
8268 * Make sure the on disk representation of a virtual HDD is up to date.
8269 *
8270 * @returns VBox status code.
8271 * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
8272 * @param pDisk Pointer to HDD container.
8273 */
8274VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
8275{
8276 int rc = VINF_SUCCESS;
8277 int rc2;
8278 bool fLockWrite = false;
8279
8280 LogFlowFunc(("pDisk=%#p\n", pDisk));
8281 do
8282 {
8283 /* sanity check */
8284 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8285 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8286
8287 rc2 = vdThreadStartWrite(pDisk);
8288 AssertRC(rc2);
8289 fLockWrite = true;
8290
8291 PVDIMAGE pImage = pDisk->pLast;
8292 AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
8293
8294 vdResetModifiedFlag(pDisk);
8295
8296 VDIOCTX IoCtx;
8297 vdIoCtxInit(&IoCtx, pDisk, VDIOCTXTXDIR_FLUSH, 0, 0, NULL,
8298 NULL, NULL, NULL, VDIOCTX_FLAGS_SYNC);
8299 rc = pImage->Backend->pfnFlush(pImage->pBackendData, &IoCtx);
8300
8301#if 0 /** @todo: Change cache implementation. */
8302 if ( RT_SUCCESS(rc)
8303 && pDisk->pCache)
8304 rc = pDisk->pCache->Backend->pfnFlush(pDisk->pCache->pBackendData);
8305#endif
8306 } while (0);
8307
8308 if (RT_UNLIKELY(fLockWrite))
8309 {
8310 rc2 = vdThreadFinishWrite(pDisk);
8311 AssertRC(rc2);
8312 }
8313
8314 LogFlowFunc(("returns %Rrc\n", rc));
8315 return rc;
8316}
8317
8318/**
8319 * Get number of opened images in HDD container.
8320 *
8321 * @returns Number of opened images for HDD container. 0 if no images have been opened.
8322 * @param pDisk Pointer to HDD container.
8323 */
8324VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
8325{
8326 unsigned cImages;
8327 int rc2;
8328 bool fLockRead = false;
8329
8330 LogFlowFunc(("pDisk=%#p\n", pDisk));
8331 do
8332 {
8333 /* sanity check */
8334 AssertPtrBreakStmt(pDisk, cImages = 0);
8335 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8336
8337 rc2 = vdThreadStartRead(pDisk);
8338 AssertRC(rc2);
8339 fLockRead = true;
8340
8341 cImages = pDisk->cImages;
8342 } while (0);
8343
8344 if (RT_UNLIKELY(fLockRead))
8345 {
8346 rc2 = vdThreadFinishRead(pDisk);
8347 AssertRC(rc2);
8348 }
8349
8350 LogFlowFunc(("returns %u\n", cImages));
8351 return cImages;
8352}
8353
8354/**
8355 * Get read/write mode of HDD container.
8356 *
8357 * @returns Virtual disk ReadOnly status.
8358 * @returns true if no image is opened in HDD container.
8359 * @param pDisk Pointer to HDD container.
8360 */
8361VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
8362{
8363 bool fReadOnly;
8364 int rc2;
8365 bool fLockRead = false;
8366
8367 LogFlowFunc(("pDisk=%#p\n", pDisk));
8368 do
8369 {
8370 /* sanity check */
8371 AssertPtrBreakStmt(pDisk, fReadOnly = false);
8372 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8373
8374 rc2 = vdThreadStartRead(pDisk);
8375 AssertRC(rc2);
8376 fLockRead = true;
8377
8378 PVDIMAGE pImage = pDisk->pLast;
8379 AssertPtrBreakStmt(pImage, fReadOnly = true);
8380
8381 unsigned uOpenFlags;
8382 uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
8383 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
8384 } while (0);
8385
8386 if (RT_UNLIKELY(fLockRead))
8387 {
8388 rc2 = vdThreadFinishRead(pDisk);
8389 AssertRC(rc2);
8390 }
8391
8392 LogFlowFunc(("returns %d\n", fReadOnly));
8393 return fReadOnly;
8394}
8395
8396/**
8397 * Get total capacity of an image in HDD container.
8398 *
8399 * @returns Virtual disk size in bytes.
8400 * @returns 0 if no image with specified number was not opened.
8401 * @param pDisk Pointer to HDD container.
8402 * @param nImage Image number, counts from 0. 0 is always base image of container.
8403 */
8404VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
8405{
8406 uint64_t cbSize;
8407 int rc2;
8408 bool fLockRead = false;
8409
8410 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
8411 do
8412 {
8413 /* sanity check */
8414 AssertPtrBreakStmt(pDisk, cbSize = 0);
8415 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8416
8417 rc2 = vdThreadStartRead(pDisk);
8418 AssertRC(rc2);
8419 fLockRead = true;
8420
8421 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8422 AssertPtrBreakStmt(pImage, cbSize = 0);
8423 cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
8424 } while (0);
8425
8426 if (RT_UNLIKELY(fLockRead))
8427 {
8428 rc2 = vdThreadFinishRead(pDisk);
8429 AssertRC(rc2);
8430 }
8431
8432 LogFlowFunc(("returns %llu\n", cbSize));
8433 return cbSize;
8434}
8435
8436/**
8437 * Get total file size of an image in HDD container.
8438 *
8439 * @returns Virtual disk size in bytes.
8440 * @returns 0 if no image is opened in HDD container.
8441 * @param pDisk Pointer to HDD container.
8442 * @param nImage Image number, counts from 0. 0 is always base image of container.
8443 */
8444VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
8445{
8446 uint64_t cbSize;
8447 int rc2;
8448 bool fLockRead = false;
8449
8450 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
8451 do
8452 {
8453 /* sanity check */
8454 AssertPtrBreakStmt(pDisk, cbSize = 0);
8455 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8456
8457 rc2 = vdThreadStartRead(pDisk);
8458 AssertRC(rc2);
8459 fLockRead = true;
8460
8461 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8462 AssertPtrBreakStmt(pImage, cbSize = 0);
8463 cbSize = pImage->Backend->pfnGetFileSize(pImage->pBackendData);
8464 } while (0);
8465
8466 if (RT_UNLIKELY(fLockRead))
8467 {
8468 rc2 = vdThreadFinishRead(pDisk);
8469 AssertRC(rc2);
8470 }
8471
8472 LogFlowFunc(("returns %llu\n", cbSize));
8473 return cbSize;
8474}
8475
8476/**
8477 * Get virtual disk PCHS geometry stored in HDD container.
8478 *
8479 * @returns VBox status code.
8480 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8481 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
8482 * @param pDisk Pointer to HDD container.
8483 * @param nImage Image number, counts from 0. 0 is always base image of container.
8484 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
8485 */
8486VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
8487 PVDGEOMETRY pPCHSGeometry)
8488{
8489 int rc = VINF_SUCCESS;
8490 int rc2;
8491 bool fLockRead = false;
8492
8493 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
8494 pDisk, nImage, pPCHSGeometry));
8495 do
8496 {
8497 /* sanity check */
8498 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8499 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8500
8501 /* Check arguments. */
8502 AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
8503 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
8504 rc = VERR_INVALID_PARAMETER);
8505
8506 rc2 = vdThreadStartRead(pDisk);
8507 AssertRC(rc2);
8508 fLockRead = true;
8509
8510 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8511 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8512
8513 if (pImage == pDisk->pLast)
8514 {
8515 /* Use cached information if possible. */
8516 if (pDisk->PCHSGeometry.cCylinders != 0)
8517 *pPCHSGeometry = pDisk->PCHSGeometry;
8518 else
8519 rc = VERR_VD_GEOMETRY_NOT_SET;
8520 }
8521 else
8522 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
8523 pPCHSGeometry);
8524 } while (0);
8525
8526 if (RT_UNLIKELY(fLockRead))
8527 {
8528 rc2 = vdThreadFinishRead(pDisk);
8529 AssertRC(rc2);
8530 }
8531
8532 LogFlowFunc(("%Rrc (PCHS=%u/%u/%u)\n", rc,
8533 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
8534 pDisk->PCHSGeometry.cSectors));
8535 return rc;
8536}
8537
8538/**
8539 * Store virtual disk PCHS geometry in HDD container.
8540 *
8541 * Note that in case of unrecoverable error all images in HDD container will be closed.
8542 *
8543 * @returns VBox status code.
8544 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8545 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
8546 * @param pDisk Pointer to HDD container.
8547 * @param nImage Image number, counts from 0. 0 is always base image of container.
8548 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
8549 */
8550VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
8551 PCVDGEOMETRY pPCHSGeometry)
8552{
8553 int rc = VINF_SUCCESS;
8554 int rc2;
8555 bool fLockWrite = false;
8556
8557 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
8558 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
8559 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
8560 do
8561 {
8562 /* sanity check */
8563 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8564 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8565
8566 /* Check arguments. */
8567 AssertMsgBreakStmt( VALID_PTR(pPCHSGeometry)
8568 && pPCHSGeometry->cHeads <= 16
8569 && pPCHSGeometry->cSectors <= 63,
8570 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
8571 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
8572 pPCHSGeometry->cSectors),
8573 rc = VERR_INVALID_PARAMETER);
8574
8575 rc2 = vdThreadStartWrite(pDisk);
8576 AssertRC(rc2);
8577 fLockWrite = true;
8578
8579 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8580 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8581
8582 if (pImage == pDisk->pLast)
8583 {
8584 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
8585 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
8586 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
8587 {
8588 /* Only update geometry if it is changed. Avoids similar checks
8589 * in every backend. Most of the time the new geometry is set
8590 * to the previous values, so no need to go through the hassle
8591 * of updating an image which could be opened in read-only mode
8592 * right now. */
8593 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
8594 pPCHSGeometry);
8595
8596 /* Cache new geometry values in any case. */
8597 rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
8598 &pDisk->PCHSGeometry);
8599 if (RT_FAILURE(rc2))
8600 {
8601 pDisk->PCHSGeometry.cCylinders = 0;
8602 pDisk->PCHSGeometry.cHeads = 0;
8603 pDisk->PCHSGeometry.cSectors = 0;
8604 }
8605 else
8606 {
8607 /* Make sure the CHS geometry is properly clipped. */
8608 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
8609 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
8610 }
8611 }
8612 }
8613 else
8614 {
8615 VDGEOMETRY PCHS;
8616 rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
8617 &PCHS);
8618 if ( RT_FAILURE(rc)
8619 || pPCHSGeometry->cCylinders != PCHS.cCylinders
8620 || pPCHSGeometry->cHeads != PCHS.cHeads
8621 || pPCHSGeometry->cSectors != PCHS.cSectors)
8622 {
8623 /* Only update geometry if it is changed. Avoids similar checks
8624 * in every backend. Most of the time the new geometry is set
8625 * to the previous values, so no need to go through the hassle
8626 * of updating an image which could be opened in read-only mode
8627 * right now. */
8628 rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
8629 pPCHSGeometry);
8630 }
8631 }
8632 } while (0);
8633
8634 if (RT_UNLIKELY(fLockWrite))
8635 {
8636 rc2 = vdThreadFinishWrite(pDisk);
8637 AssertRC(rc2);
8638 }
8639
8640 LogFlowFunc(("returns %Rrc\n", rc));
8641 return rc;
8642}
8643
8644/**
8645 * Get virtual disk LCHS geometry stored in HDD container.
8646 *
8647 * @returns VBox status code.
8648 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8649 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
8650 * @param pDisk Pointer to HDD container.
8651 * @param nImage Image number, counts from 0. 0 is always base image of container.
8652 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
8653 */
8654VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
8655 PVDGEOMETRY pLCHSGeometry)
8656{
8657 int rc = VINF_SUCCESS;
8658 int rc2;
8659 bool fLockRead = false;
8660
8661 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
8662 pDisk, nImage, pLCHSGeometry));
8663 do
8664 {
8665 /* sanity check */
8666 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8667 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8668
8669 /* Check arguments. */
8670 AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
8671 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
8672 rc = VERR_INVALID_PARAMETER);
8673
8674 rc2 = vdThreadStartRead(pDisk);
8675 AssertRC(rc2);
8676 fLockRead = true;
8677
8678 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8679 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8680
8681 if (pImage == pDisk->pLast)
8682 {
8683 /* Use cached information if possible. */
8684 if (pDisk->LCHSGeometry.cCylinders != 0)
8685 *pLCHSGeometry = pDisk->LCHSGeometry;
8686 else
8687 rc = VERR_VD_GEOMETRY_NOT_SET;
8688 }
8689 else
8690 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
8691 pLCHSGeometry);
8692 } while (0);
8693
8694 if (RT_UNLIKELY(fLockRead))
8695 {
8696 rc2 = vdThreadFinishRead(pDisk);
8697 AssertRC(rc2);
8698 }
8699
8700 LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
8701 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
8702 pDisk->LCHSGeometry.cSectors));
8703 return rc;
8704}
8705
8706/**
8707 * Store virtual disk LCHS geometry in HDD container.
8708 *
8709 * Note that in case of unrecoverable error all images in HDD container will be closed.
8710 *
8711 * @returns VBox status code.
8712 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8713 * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
8714 * @param pDisk Pointer to HDD container.
8715 * @param nImage Image number, counts from 0. 0 is always base image of container.
8716 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
8717 */
8718VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
8719 PCVDGEOMETRY pLCHSGeometry)
8720{
8721 int rc = VINF_SUCCESS;
8722 int rc2;
8723 bool fLockWrite = false;
8724
8725 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
8726 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
8727 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
8728 do
8729 {
8730 /* sanity check */
8731 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8732 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8733
8734 /* Check arguments. */
8735 AssertMsgBreakStmt( VALID_PTR(pLCHSGeometry)
8736 && pLCHSGeometry->cHeads <= 255
8737 && pLCHSGeometry->cSectors <= 63,
8738 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
8739 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
8740 pLCHSGeometry->cSectors),
8741 rc = VERR_INVALID_PARAMETER);
8742
8743 rc2 = vdThreadStartWrite(pDisk);
8744 AssertRC(rc2);
8745 fLockWrite = true;
8746
8747 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8748 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8749
8750 if (pImage == pDisk->pLast)
8751 {
8752 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
8753 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
8754 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
8755 {
8756 /* Only update geometry if it is changed. Avoids similar checks
8757 * in every backend. Most of the time the new geometry is set
8758 * to the previous values, so no need to go through the hassle
8759 * of updating an image which could be opened in read-only mode
8760 * right now. */
8761 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
8762 pLCHSGeometry);
8763
8764 /* Cache new geometry values in any case. */
8765 rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
8766 &pDisk->LCHSGeometry);
8767 if (RT_FAILURE(rc2))
8768 {
8769 pDisk->LCHSGeometry.cCylinders = 0;
8770 pDisk->LCHSGeometry.cHeads = 0;
8771 pDisk->LCHSGeometry.cSectors = 0;
8772 }
8773 else
8774 {
8775 /* Make sure the CHS geometry is properly clipped. */
8776 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
8777 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
8778 }
8779 }
8780 }
8781 else
8782 {
8783 VDGEOMETRY LCHS;
8784 rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
8785 &LCHS);
8786 if ( RT_FAILURE(rc)
8787 || pLCHSGeometry->cCylinders != LCHS.cCylinders
8788 || pLCHSGeometry->cHeads != LCHS.cHeads
8789 || pLCHSGeometry->cSectors != LCHS.cSectors)
8790 {
8791 /* Only update geometry if it is changed. Avoids similar checks
8792 * in every backend. Most of the time the new geometry is set
8793 * to the previous values, so no need to go through the hassle
8794 * of updating an image which could be opened in read-only mode
8795 * right now. */
8796 rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
8797 pLCHSGeometry);
8798 }
8799 }
8800 } while (0);
8801
8802 if (RT_UNLIKELY(fLockWrite))
8803 {
8804 rc2 = vdThreadFinishWrite(pDisk);
8805 AssertRC(rc2);
8806 }
8807
8808 LogFlowFunc(("returns %Rrc\n", rc));
8809 return rc;
8810}
8811
8812/**
8813 * Get version of image in HDD container.
8814 *
8815 * @returns VBox status code.
8816 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8817 * @param pDisk Pointer to HDD container.
8818 * @param nImage Image number, counts from 0. 0 is always base image of container.
8819 * @param puVersion Where to store the image version.
8820 */
8821VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
8822 unsigned *puVersion)
8823{
8824 int rc = VINF_SUCCESS;
8825 int rc2;
8826 bool fLockRead = false;
8827
8828 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
8829 pDisk, nImage, puVersion));
8830 do
8831 {
8832 /* sanity check */
8833 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8834 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8835
8836 /* Check arguments. */
8837 AssertMsgBreakStmt(VALID_PTR(puVersion),
8838 ("puVersion=%#p\n", puVersion),
8839 rc = VERR_INVALID_PARAMETER);
8840
8841 rc2 = vdThreadStartRead(pDisk);
8842 AssertRC(rc2);
8843 fLockRead = true;
8844
8845 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8846 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8847
8848 *puVersion = pImage->Backend->pfnGetVersion(pImage->pBackendData);
8849 } while (0);
8850
8851 if (RT_UNLIKELY(fLockRead))
8852 {
8853 rc2 = vdThreadFinishRead(pDisk);
8854 AssertRC(rc2);
8855 }
8856
8857 LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
8858 return rc;
8859}
8860
8861/**
8862 * List the capabilities of image backend in HDD container.
8863 *
8864 * @returns VBox status code.
8865 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8866 * @param pDisk Pointer to the HDD container.
8867 * @param nImage Image number, counts from 0. 0 is always base image of container.
8868 * @param pbackendInfo Where to store the backend information.
8869 */
8870VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
8871 PVDBACKENDINFO pBackendInfo)
8872{
8873 int rc = VINF_SUCCESS;
8874 int rc2;
8875 bool fLockRead = false;
8876
8877 LogFlowFunc(("pDisk=%#p nImage=%u pBackendInfo=%#p\n",
8878 pDisk, nImage, pBackendInfo));
8879 do
8880 {
8881 /* sanity check */
8882 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8883 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8884
8885 /* Check arguments. */
8886 AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
8887 ("pBackendInfo=%#p\n", pBackendInfo),
8888 rc = VERR_INVALID_PARAMETER);
8889
8890 rc2 = vdThreadStartRead(pDisk);
8891 AssertRC(rc2);
8892 fLockRead = true;
8893
8894 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8895 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8896
8897 pBackendInfo->pszBackend = pImage->Backend->pszBackendName;
8898 pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
8899 pBackendInfo->paFileExtensions = pImage->Backend->paFileExtensions;
8900 pBackendInfo->paConfigInfo = pImage->Backend->paConfigInfo;
8901 } while (0);
8902
8903 if (RT_UNLIKELY(fLockRead))
8904 {
8905 rc2 = vdThreadFinishRead(pDisk);
8906 AssertRC(rc2);
8907 }
8908
8909 LogFlowFunc(("returns %Rrc\n", rc));
8910 return rc;
8911}
8912
8913/**
8914 * Get flags of image in HDD container.
8915 *
8916 * @returns VBox status code.
8917 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8918 * @param pDisk Pointer to HDD container.
8919 * @param nImage Image number, counts from 0. 0 is always base image of container.
8920 * @param puImageFlags Where to store the image flags.
8921 */
8922VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
8923 unsigned *puImageFlags)
8924{
8925 int rc = VINF_SUCCESS;
8926 int rc2;
8927 bool fLockRead = false;
8928
8929 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
8930 pDisk, nImage, puImageFlags));
8931 do
8932 {
8933 /* sanity check */
8934 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8935 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8936
8937 /* Check arguments. */
8938 AssertMsgBreakStmt(VALID_PTR(puImageFlags),
8939 ("puImageFlags=%#p\n", puImageFlags),
8940 rc = VERR_INVALID_PARAMETER);
8941
8942 rc2 = vdThreadStartRead(pDisk);
8943 AssertRC(rc2);
8944 fLockRead = true;
8945
8946 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8947 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8948
8949 *puImageFlags = pImage->uImageFlags;
8950 } while (0);
8951
8952 if (RT_UNLIKELY(fLockRead))
8953 {
8954 rc2 = vdThreadFinishRead(pDisk);
8955 AssertRC(rc2);
8956 }
8957
8958 LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
8959 return rc;
8960}
8961
8962/**
8963 * Get open flags of image in HDD container.
8964 *
8965 * @returns VBox status code.
8966 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
8967 * @param pDisk Pointer to HDD container.
8968 * @param nImage Image number, counts from 0. 0 is always base image of container.
8969 * @param puOpenFlags Where to store the image open flags.
8970 */
8971VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
8972 unsigned *puOpenFlags)
8973{
8974 int rc = VINF_SUCCESS;
8975 int rc2;
8976 bool fLockRead = false;
8977
8978 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
8979 pDisk, nImage, puOpenFlags));
8980 do
8981 {
8982 /* sanity check */
8983 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
8984 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
8985
8986 /* Check arguments. */
8987 AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
8988 ("puOpenFlags=%#p\n", puOpenFlags),
8989 rc = VERR_INVALID_PARAMETER);
8990
8991 rc2 = vdThreadStartRead(pDisk);
8992 AssertRC(rc2);
8993 fLockRead = true;
8994
8995 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
8996 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
8997
8998 *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
8999 } while (0);
9000
9001 if (RT_UNLIKELY(fLockRead))
9002 {
9003 rc2 = vdThreadFinishRead(pDisk);
9004 AssertRC(rc2);
9005 }
9006
9007 LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
9008 return rc;
9009}
9010
9011/**
9012 * Set open flags of image in HDD container.
9013 * This operation may cause file locking changes and/or files being reopened.
9014 * Note that in case of unrecoverable error all images in HDD container will be closed.
9015 *
9016 * @returns VBox status code.
9017 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9018 * @param pDisk Pointer to HDD container.
9019 * @param nImage Image number, counts from 0. 0 is always base image of container.
9020 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
9021 */
9022VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
9023 unsigned uOpenFlags)
9024{
9025 int rc;
9026 int rc2;
9027 bool fLockWrite = false;
9028
9029 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
9030 do
9031 {
9032 /* sanity check */
9033 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9034 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9035
9036 /* Check arguments. */
9037 AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
9038 ("uOpenFlags=%#x\n", uOpenFlags),
9039 rc = VERR_INVALID_PARAMETER);
9040
9041 rc2 = vdThreadStartWrite(pDisk);
9042 AssertRC(rc2);
9043 fLockWrite = true;
9044
9045 /* Destroy any discard state because the image might be changed to readonly mode. */
9046 rc = vdDiscardStateDestroy(pDisk);
9047 if (RT_FAILURE(rc))
9048 break;
9049
9050 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9051 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9052
9053 rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData,
9054 uOpenFlags & ~(VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS));
9055 if (RT_SUCCESS(rc))
9056 pImage->uOpenFlags = uOpenFlags & (VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_DISCARD | VD_OPEN_FLAGS_IGNORE_FLUSH | VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS);
9057 } while (0);
9058
9059 if (RT_UNLIKELY(fLockWrite))
9060 {
9061 rc2 = vdThreadFinishWrite(pDisk);
9062 AssertRC(rc2);
9063 }
9064
9065 LogFlowFunc(("returns %Rrc\n", rc));
9066 return rc;
9067}
9068
9069/**
9070 * Get base filename of image in HDD container. Some image formats use
9071 * other filenames as well, so don't use this for anything but informational
9072 * purposes.
9073 *
9074 * @returns VBox status code.
9075 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9076 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
9077 * @param pDisk Pointer to HDD container.
9078 * @param nImage Image number, counts from 0. 0 is always base image of container.
9079 * @param pszFilename Where to store the image file name.
9080 * @param cbFilename Size of buffer pszFilename points to.
9081 */
9082VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
9083 char *pszFilename, unsigned cbFilename)
9084{
9085 int rc;
9086 int rc2;
9087 bool fLockRead = false;
9088
9089 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
9090 pDisk, nImage, pszFilename, cbFilename));
9091 do
9092 {
9093 /* sanity check */
9094 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9095 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9096
9097 /* Check arguments. */
9098 AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
9099 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
9100 rc = VERR_INVALID_PARAMETER);
9101 AssertMsgBreakStmt(cbFilename,
9102 ("cbFilename=%u\n", cbFilename),
9103 rc = VERR_INVALID_PARAMETER);
9104
9105 rc2 = vdThreadStartRead(pDisk);
9106 AssertRC(rc2);
9107 fLockRead = true;
9108
9109 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9110 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9111
9112 size_t cb = strlen(pImage->pszFilename);
9113 if (cb <= cbFilename)
9114 {
9115 strcpy(pszFilename, pImage->pszFilename);
9116 rc = VINF_SUCCESS;
9117 }
9118 else
9119 {
9120 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
9121 pszFilename[cbFilename - 1] = '\0';
9122 rc = VERR_BUFFER_OVERFLOW;
9123 }
9124 } while (0);
9125
9126 if (RT_UNLIKELY(fLockRead))
9127 {
9128 rc2 = vdThreadFinishRead(pDisk);
9129 AssertRC(rc2);
9130 }
9131
9132 LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
9133 return rc;
9134}
9135
9136/**
9137 * Get the comment line of image in HDD container.
9138 *
9139 * @returns VBox status code.
9140 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9141 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
9142 * @param pDisk Pointer to HDD container.
9143 * @param nImage Image number, counts from 0. 0 is always base image of container.
9144 * @param pszComment Where to store the comment string of image. NULL is ok.
9145 * @param cbComment The size of pszComment buffer. 0 is ok.
9146 */
9147VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
9148 char *pszComment, unsigned cbComment)
9149{
9150 int rc;
9151 int rc2;
9152 bool fLockRead = false;
9153
9154 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
9155 pDisk, nImage, pszComment, cbComment));
9156 do
9157 {
9158 /* sanity check */
9159 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9160 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9161
9162 /* Check arguments. */
9163 AssertMsgBreakStmt(VALID_PTR(pszComment),
9164 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
9165 rc = VERR_INVALID_PARAMETER);
9166 AssertMsgBreakStmt(cbComment,
9167 ("cbComment=%u\n", cbComment),
9168 rc = VERR_INVALID_PARAMETER);
9169
9170 rc2 = vdThreadStartRead(pDisk);
9171 AssertRC(rc2);
9172 fLockRead = true;
9173
9174 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9175 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9176
9177 rc = pImage->Backend->pfnGetComment(pImage->pBackendData, pszComment,
9178 cbComment);
9179 } while (0);
9180
9181 if (RT_UNLIKELY(fLockRead))
9182 {
9183 rc2 = vdThreadFinishRead(pDisk);
9184 AssertRC(rc2);
9185 }
9186
9187 LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
9188 return rc;
9189}
9190
9191/**
9192 * Changes the comment line of image in HDD container.
9193 *
9194 * @returns VBox status code.
9195 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9196 * @param pDisk Pointer to HDD container.
9197 * @param nImage Image number, counts from 0. 0 is always base image of container.
9198 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
9199 */
9200VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
9201 const char *pszComment)
9202{
9203 int rc;
9204 int rc2;
9205 bool fLockWrite = false;
9206
9207 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
9208 pDisk, nImage, pszComment, pszComment));
9209 do
9210 {
9211 /* sanity check */
9212 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9213 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9214
9215 /* Check arguments. */
9216 AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
9217 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
9218 rc = VERR_INVALID_PARAMETER);
9219
9220 rc2 = vdThreadStartWrite(pDisk);
9221 AssertRC(rc2);
9222 fLockWrite = true;
9223
9224 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9225 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9226
9227 rc = pImage->Backend->pfnSetComment(pImage->pBackendData, pszComment);
9228 } while (0);
9229
9230 if (RT_UNLIKELY(fLockWrite))
9231 {
9232 rc2 = vdThreadFinishWrite(pDisk);
9233 AssertRC(rc2);
9234 }
9235
9236 LogFlowFunc(("returns %Rrc\n", rc));
9237 return rc;
9238}
9239
9240
9241/**
9242 * Get UUID of image in HDD container.
9243 *
9244 * @returns VBox status code.
9245 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9246 * @param pDisk Pointer to HDD container.
9247 * @param nImage Image number, counts from 0. 0 is always base image of container.
9248 * @param pUuid Where to store the image creation UUID.
9249 */
9250VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
9251{
9252 int rc;
9253 int rc2;
9254 bool fLockRead = false;
9255
9256 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
9257 do
9258 {
9259 /* sanity check */
9260 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9261 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9262
9263 /* Check arguments. */
9264 AssertMsgBreakStmt(VALID_PTR(pUuid),
9265 ("pUuid=%#p\n", pUuid),
9266 rc = VERR_INVALID_PARAMETER);
9267
9268 rc2 = vdThreadStartRead(pDisk);
9269 AssertRC(rc2);
9270 fLockRead = true;
9271
9272 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9273 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9274
9275 rc = pImage->Backend->pfnGetUuid(pImage->pBackendData, pUuid);
9276 } while (0);
9277
9278 if (RT_UNLIKELY(fLockRead))
9279 {
9280 rc2 = vdThreadFinishRead(pDisk);
9281 AssertRC(rc2);
9282 }
9283
9284 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
9285 return rc;
9286}
9287
9288/**
9289 * Set the image's UUID. Should not be used by normal applications.
9290 *
9291 * @returns VBox status code.
9292 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9293 * @param pDisk Pointer to HDD container.
9294 * @param nImage Image number, counts from 0. 0 is always base image of container.
9295 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
9296 */
9297VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
9298{
9299 int rc;
9300 int rc2;
9301 bool fLockWrite = false;
9302
9303 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
9304 pDisk, nImage, pUuid, pUuid));
9305 do
9306 {
9307 /* sanity check */
9308 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9309 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9310
9311 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
9312 ("pUuid=%#p\n", pUuid),
9313 rc = VERR_INVALID_PARAMETER);
9314
9315 rc2 = vdThreadStartWrite(pDisk);
9316 AssertRC(rc2);
9317 fLockWrite = true;
9318
9319 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9320 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9321
9322 RTUUID Uuid;
9323 if (!pUuid)
9324 {
9325 RTUuidCreate(&Uuid);
9326 pUuid = &Uuid;
9327 }
9328 rc = pImage->Backend->pfnSetUuid(pImage->pBackendData, pUuid);
9329 } while (0);
9330
9331 if (RT_UNLIKELY(fLockWrite))
9332 {
9333 rc2 = vdThreadFinishWrite(pDisk);
9334 AssertRC(rc2);
9335 }
9336
9337 LogFlowFunc(("returns %Rrc\n", rc));
9338 return rc;
9339}
9340
9341/**
9342 * Get last modification UUID of image in HDD container.
9343 *
9344 * @returns VBox status code.
9345 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9346 * @param pDisk Pointer to HDD container.
9347 * @param nImage Image number, counts from 0. 0 is always base image of container.
9348 * @param pUuid Where to store the image modification UUID.
9349 */
9350VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
9351{
9352 int rc = VINF_SUCCESS;
9353 int rc2;
9354 bool fLockRead = false;
9355
9356 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
9357 do
9358 {
9359 /* sanity check */
9360 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9361 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9362
9363 /* Check arguments. */
9364 AssertMsgBreakStmt(VALID_PTR(pUuid),
9365 ("pUuid=%#p\n", pUuid),
9366 rc = VERR_INVALID_PARAMETER);
9367
9368 rc2 = vdThreadStartRead(pDisk);
9369 AssertRC(rc2);
9370 fLockRead = true;
9371
9372 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9373 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9374
9375 rc = pImage->Backend->pfnGetModificationUuid(pImage->pBackendData,
9376 pUuid);
9377 } while (0);
9378
9379 if (RT_UNLIKELY(fLockRead))
9380 {
9381 rc2 = vdThreadFinishRead(pDisk);
9382 AssertRC(rc2);
9383 }
9384
9385 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
9386 return rc;
9387}
9388
9389/**
9390 * Set the image's last modification UUID. Should not be used by normal applications.
9391 *
9392 * @returns VBox status code.
9393 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9394 * @param pDisk Pointer to HDD container.
9395 * @param nImage Image number, counts from 0. 0 is always base image of container.
9396 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
9397 */
9398VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
9399{
9400 int rc;
9401 int rc2;
9402 bool fLockWrite = false;
9403
9404 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
9405 pDisk, nImage, pUuid, pUuid));
9406 do
9407 {
9408 /* sanity check */
9409 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9410 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9411
9412 /* Check arguments. */
9413 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
9414 ("pUuid=%#p\n", pUuid),
9415 rc = VERR_INVALID_PARAMETER);
9416
9417 rc2 = vdThreadStartWrite(pDisk);
9418 AssertRC(rc2);
9419 fLockWrite = true;
9420
9421 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9422 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9423
9424 RTUUID Uuid;
9425 if (!pUuid)
9426 {
9427 RTUuidCreate(&Uuid);
9428 pUuid = &Uuid;
9429 }
9430 rc = pImage->Backend->pfnSetModificationUuid(pImage->pBackendData,
9431 pUuid);
9432 } while (0);
9433
9434 if (RT_UNLIKELY(fLockWrite))
9435 {
9436 rc2 = vdThreadFinishWrite(pDisk);
9437 AssertRC(rc2);
9438 }
9439
9440 LogFlowFunc(("returns %Rrc\n", rc));
9441 return rc;
9442}
9443
9444/**
9445 * Get parent UUID of image in HDD container.
9446 *
9447 * @returns VBox status code.
9448 * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
9449 * @param pDisk Pointer to HDD container.
9450 * @param nImage Image number, counts from 0. 0 is always base image of container.
9451 * @param pUuid Where to store the parent image UUID.
9452 */
9453VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
9454 PRTUUID pUuid)
9455{
9456 int rc = VINF_SUCCESS;
9457 int rc2;
9458 bool fLockRead = false;
9459
9460 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
9461 do
9462 {
9463 /* sanity check */
9464 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9465 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9466
9467 /* Check arguments. */
9468 AssertMsgBreakStmt(VALID_PTR(pUuid),
9469 ("pUuid=%#p\n", pUuid),
9470 rc = VERR_INVALID_PARAMETER);
9471
9472 rc2 = vdThreadStartRead(pDisk);
9473 AssertRC(rc2);
9474 fLockRead = true;
9475
9476 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9477 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9478
9479 rc = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, pUuid);
9480 } while (0);
9481
9482 if (RT_UNLIKELY(fLockRead))
9483 {
9484 rc2 = vdThreadFinishRead(pDisk);
9485 AssertRC(rc2);
9486 }
9487
9488 LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
9489 return rc;
9490}
9491
9492/**
9493 * Set the image's parent UUID. Should not be used by normal applications.
9494 *
9495 * @returns VBox status code.
9496 * @param pDisk Pointer to HDD container.
9497 * @param nImage Image number, counts from 0. 0 is always base image of container.
9498 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
9499 */
9500VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
9501 PCRTUUID pUuid)
9502{
9503 int rc;
9504 int rc2;
9505 bool fLockWrite = false;
9506
9507 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
9508 pDisk, nImage, pUuid, pUuid));
9509 do
9510 {
9511 /* sanity check */
9512 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9513 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9514
9515 /* Check arguments. */
9516 AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
9517 ("pUuid=%#p\n", pUuid),
9518 rc = VERR_INVALID_PARAMETER);
9519
9520 rc2 = vdThreadStartWrite(pDisk);
9521 AssertRC(rc2);
9522 fLockWrite = true;
9523
9524 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
9525 AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
9526
9527 RTUUID Uuid;
9528 if (!pUuid)
9529 {
9530 RTUuidCreate(&Uuid);
9531 pUuid = &Uuid;
9532 }
9533 rc = pImage->Backend->pfnSetParentUuid(pImage->pBackendData, pUuid);
9534 } while (0);
9535
9536 if (RT_UNLIKELY(fLockWrite))
9537 {
9538 rc2 = vdThreadFinishWrite(pDisk);
9539 AssertRC(rc2);
9540 }
9541
9542 LogFlowFunc(("returns %Rrc\n", rc));
9543 return rc;
9544}
9545
9546
9547/**
9548 * Debug helper - dumps all opened images in HDD container into the log file.
9549 *
9550 * @param pDisk Pointer to HDD container.
9551 */
9552VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
9553{
9554 int rc2;
9555 bool fLockRead = false;
9556
9557 do
9558 {
9559 /* sanity check */
9560 AssertPtrBreak(pDisk);
9561 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9562
9563 if (!pDisk->pInterfaceError || !VALID_PTR(pDisk->pInterfaceError->pfnMessage))
9564 pDisk->pInterfaceError->pfnMessage = vdLogMessage;
9565
9566 rc2 = vdThreadStartRead(pDisk);
9567 AssertRC(rc2);
9568 fLockRead = true;
9569
9570 vdMessageWrapper(pDisk, "--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
9571 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
9572 {
9573 vdMessageWrapper(pDisk, "Dumping VD image \"%s\" (Backend=%s)\n",
9574 pImage->pszFilename, pImage->Backend->pszBackendName);
9575 pImage->Backend->pfnDump(pImage->pBackendData);
9576 }
9577 } while (0);
9578
9579 if (RT_UNLIKELY(fLockRead))
9580 {
9581 rc2 = vdThreadFinishRead(pDisk);
9582 AssertRC(rc2);
9583 }
9584}
9585
9586
9587VBOXDDU_DECL(int) VDDiscardRanges(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges)
9588{
9589 int rc;
9590 int rc2;
9591 bool fLockWrite = false;
9592
9593 LogFlowFunc(("pDisk=%#p paRanges=%#p cRanges=%u\n",
9594 pDisk, paRanges, cRanges));
9595 do
9596 {
9597 /* sanity check */
9598 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9599 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9600
9601 /* Check arguments. */
9602 AssertMsgBreakStmt(cRanges,
9603 ("cRanges=%u\n", cRanges),
9604 rc = VERR_INVALID_PARAMETER);
9605 AssertMsgBreakStmt(VALID_PTR(paRanges),
9606 ("paRanges=%#p\n", paRanges),
9607 rc = VERR_INVALID_PARAMETER);
9608
9609 rc2 = vdThreadStartWrite(pDisk);
9610 AssertRC(rc2);
9611 fLockWrite = true;
9612
9613 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
9614
9615 AssertMsgBreakStmt(pDisk->pLast->uOpenFlags & VD_OPEN_FLAGS_DISCARD,
9616 ("Discarding not supported\n"),
9617 rc = VERR_NOT_SUPPORTED);
9618
9619 vdSetModifiedFlag(pDisk);
9620 rc = vdDiscardHelper(pDisk, paRanges, cRanges);
9621 } while (0);
9622
9623 if (RT_UNLIKELY(fLockWrite))
9624 {
9625 rc2 = vdThreadFinishWrite(pDisk);
9626 AssertRC(rc2);
9627 }
9628
9629 LogFlowFunc(("returns %Rrc\n", rc));
9630 return rc;
9631}
9632
9633
9634VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
9635 PCRTSGBUF pcSgBuf,
9636 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
9637 void *pvUser1, void *pvUser2)
9638{
9639 int rc = VERR_VD_BLOCK_FREE;
9640 int rc2;
9641 bool fLockRead = false;
9642 PVDIOCTX pIoCtx = NULL;
9643
9644 LogFlowFunc(("pDisk=%#p uOffset=%llu pcSgBuf=%#p cbRead=%zu pvUser1=%#p pvUser2=%#p\n",
9645 pDisk, uOffset, pcSgBuf, cbRead, pvUser1, pvUser2));
9646
9647 do
9648 {
9649 /* sanity check */
9650 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9651 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9652
9653 /* Check arguments. */
9654 AssertMsgBreakStmt(cbRead,
9655 ("cbRead=%zu\n", cbRead),
9656 rc = VERR_INVALID_PARAMETER);
9657 AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
9658 ("pcSgBuf=%#p\n", pcSgBuf),
9659 rc = VERR_INVALID_PARAMETER);
9660
9661 rc2 = vdThreadStartRead(pDisk);
9662 AssertRC(rc2);
9663 fLockRead = true;
9664
9665 AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
9666 ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
9667 uOffset, cbRead, pDisk->cbSize),
9668 rc = VERR_INVALID_PARAMETER);
9669 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
9670
9671 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_READ, uOffset,
9672 cbRead, pDisk->pLast, pcSgBuf,
9673 pfnComplete, pvUser1, pvUser2,
9674 NULL, vdReadHelperAsync);
9675 if (!pIoCtx)
9676 {
9677 rc = VERR_NO_MEMORY;
9678 break;
9679 }
9680
9681#if 0
9682 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
9683#else
9684 rc = vdIoCtxProcess(pIoCtx);
9685#endif
9686 if (rc == VINF_VD_ASYNC_IO_FINISHED)
9687 {
9688 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
9689 vdIoCtxFree(pDisk, pIoCtx);
9690 else
9691 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
9692 }
9693 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
9694 vdIoCtxFree(pDisk, pIoCtx);
9695
9696 } while (0);
9697
9698 if (RT_UNLIKELY(fLockRead) && ( rc == VINF_VD_ASYNC_IO_FINISHED
9699 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
9700 {
9701 rc2 = vdThreadFinishRead(pDisk);
9702 AssertRC(rc2);
9703 }
9704
9705 LogFlowFunc(("returns %Rrc\n", rc));
9706 return rc;
9707}
9708
9709
9710VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
9711 PCRTSGBUF pcSgBuf,
9712 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
9713 void *pvUser1, void *pvUser2)
9714{
9715 int rc;
9716 int rc2;
9717 bool fLockWrite = false;
9718 PVDIOCTX pIoCtx = NULL;
9719
9720 LogFlowFunc(("pDisk=%#p uOffset=%llu cSgBuf=%#p cbWrite=%zu pvUser1=%#p pvUser2=%#p\n",
9721 pDisk, uOffset, pcSgBuf, cbWrite, pvUser1, pvUser2));
9722 do
9723 {
9724 /* sanity check */
9725 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9726 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9727
9728 /* Check arguments. */
9729 AssertMsgBreakStmt(cbWrite,
9730 ("cbWrite=%zu\n", cbWrite),
9731 rc = VERR_INVALID_PARAMETER);
9732 AssertMsgBreakStmt(VALID_PTR(pcSgBuf),
9733 ("pcSgBuf=%#p\n", pcSgBuf),
9734 rc = VERR_INVALID_PARAMETER);
9735
9736 rc2 = vdThreadStartWrite(pDisk);
9737 AssertRC(rc2);
9738 fLockWrite = true;
9739
9740 AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
9741 ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
9742 uOffset, cbWrite, pDisk->cbSize),
9743 rc = VERR_INVALID_PARAMETER);
9744 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
9745
9746 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_WRITE, uOffset,
9747 cbWrite, pDisk->pLast, pcSgBuf,
9748 pfnComplete, pvUser1, pvUser2,
9749 NULL, vdWriteHelperAsync);
9750 if (!pIoCtx)
9751 {
9752 rc = VERR_NO_MEMORY;
9753 break;
9754 }
9755
9756#if 0
9757 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
9758#else
9759 rc = vdIoCtxProcess(pIoCtx);
9760#endif
9761 if (rc == VINF_VD_ASYNC_IO_FINISHED)
9762 {
9763 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
9764 vdIoCtxFree(pDisk, pIoCtx);
9765 else
9766 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
9767 }
9768 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
9769 vdIoCtxFree(pDisk, pIoCtx);
9770 } while (0);
9771
9772 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
9773 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
9774 {
9775 rc2 = vdThreadFinishWrite(pDisk);
9776 AssertRC(rc2);
9777 }
9778
9779 LogFlowFunc(("returns %Rrc\n", rc));
9780 return rc;
9781}
9782
9783
9784VBOXDDU_DECL(int) VDAsyncFlush(PVBOXHDD pDisk, PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
9785 void *pvUser1, void *pvUser2)
9786{
9787 int rc;
9788 int rc2;
9789 bool fLockWrite = false;
9790 PVDIOCTX pIoCtx = NULL;
9791
9792 LogFlowFunc(("pDisk=%#p\n", pDisk));
9793
9794 do
9795 {
9796 /* sanity check */
9797 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9798 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9799
9800 rc2 = vdThreadStartWrite(pDisk);
9801 AssertRC(rc2);
9802 fLockWrite = true;
9803
9804 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
9805
9806 pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_FLUSH, 0,
9807 0, pDisk->pLast, NULL,
9808 pfnComplete, pvUser1, pvUser2,
9809 NULL, vdFlushHelperAsync);
9810 if (!pIoCtx)
9811 {
9812 rc = VERR_NO_MEMORY;
9813 break;
9814 }
9815
9816#if 0
9817 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
9818#else
9819 rc = vdIoCtxProcess(pIoCtx);
9820#endif
9821 if (rc == VINF_VD_ASYNC_IO_FINISHED)
9822 {
9823 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
9824 vdIoCtxFree(pDisk, pIoCtx);
9825 else
9826 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
9827 }
9828 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
9829 vdIoCtxFree(pDisk, pIoCtx);
9830 } while (0);
9831
9832 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
9833 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
9834 {
9835 rc2 = vdThreadFinishWrite(pDisk);
9836 AssertRC(rc2);
9837 }
9838
9839 LogFlowFunc(("returns %Rrc\n", rc));
9840 return rc;
9841}
9842
9843VBOXDDU_DECL(int) VDAsyncDiscardRanges(PVBOXHDD pDisk, PCRTRANGE paRanges, unsigned cRanges,
9844 PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
9845 void *pvUser1, void *pvUser2)
9846{
9847 int rc;
9848 int rc2;
9849 bool fLockWrite = false;
9850 PVDIOCTX pIoCtx = NULL;
9851
9852 LogFlowFunc(("pDisk=%#p\n", pDisk));
9853
9854 do
9855 {
9856 /* sanity check */
9857 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
9858 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
9859
9860 rc2 = vdThreadStartWrite(pDisk);
9861 AssertRC(rc2);
9862 fLockWrite = true;
9863
9864 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED);
9865
9866 pIoCtx = vdIoCtxDiscardAlloc(pDisk, paRanges, cRanges,
9867 pfnComplete, pvUser1, pvUser2, NULL,
9868 vdDiscardHelperAsync);
9869 if (!pIoCtx)
9870 {
9871 rc = VERR_NO_MEMORY;
9872 break;
9873 }
9874
9875#if 0
9876 rc = vdIoCtxProcessTryLockDefer(pIoCtx);
9877#else
9878 rc = vdIoCtxProcess(pIoCtx);
9879#endif
9880 if (rc == VINF_VD_ASYNC_IO_FINISHED)
9881 {
9882 if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
9883 vdIoCtxFree(pDisk, pIoCtx);
9884 else
9885 rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
9886 }
9887 else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
9888 vdIoCtxFree(pDisk, pIoCtx);
9889 } while (0);
9890
9891 if (RT_UNLIKELY(fLockWrite) && ( rc == VINF_VD_ASYNC_IO_FINISHED
9892 || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
9893 {
9894 rc2 = vdThreadFinishWrite(pDisk);
9895 AssertRC(rc2);
9896 }
9897
9898 LogFlowFunc(("returns %Rrc\n", rc));
9899 return rc;
9900}
9901
9902VBOXDDU_DECL(int) VDRepair(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
9903 const char *pszFilename, const char *pszBackend,
9904 uint32_t fFlags)
9905{
9906 int rc = VERR_NOT_SUPPORTED;
9907 PCVBOXHDDBACKEND pBackend = NULL;
9908 VDINTERFACEIOINT VDIfIoInt;
9909 VDINTERFACEIO VDIfIoFallback;
9910 PVDINTERFACEIO pInterfaceIo;
9911
9912 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
9913 /* Check arguments. */
9914 AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
9915 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
9916 VERR_INVALID_PARAMETER);
9917 AssertMsgReturn(VALID_PTR(pszBackend),
9918 ("pszBackend=%#p\n", pszBackend),
9919 VERR_INVALID_PARAMETER);
9920 AssertMsgReturn((fFlags & ~VD_REPAIR_FLAGS_MASK) == 0,
9921 ("fFlags=%#x\n", fFlags),
9922 VERR_INVALID_PARAMETER);
9923
9924 pInterfaceIo = VDIfIoGet(pVDIfsImage);
9925 if (!pInterfaceIo)
9926 {
9927 /*
9928 * Caller doesn't provide an I/O interface, create our own using the
9929 * native file API.
9930 */
9931 vdIfIoFallbackCallbacksSetup(&VDIfIoFallback);
9932 pInterfaceIo = &VDIfIoFallback;
9933 }
9934
9935 /* Set up the internal I/O interface. */
9936 AssertReturn(!VDIfIoIntGet(pVDIfsImage), VERR_INVALID_PARAMETER);
9937 VDIfIoInt.pfnOpen = vdIOIntOpenLimited;
9938 VDIfIoInt.pfnClose = vdIOIntCloseLimited;
9939 VDIfIoInt.pfnDelete = vdIOIntDeleteLimited;
9940 VDIfIoInt.pfnMove = vdIOIntMoveLimited;
9941 VDIfIoInt.pfnGetFreeSpace = vdIOIntGetFreeSpaceLimited;
9942 VDIfIoInt.pfnGetModificationTime = vdIOIntGetModificationTimeLimited;
9943 VDIfIoInt.pfnGetSize = vdIOIntGetSizeLimited;
9944 VDIfIoInt.pfnSetSize = vdIOIntSetSizeLimited;
9945 VDIfIoInt.pfnReadUser = vdIOIntReadUserLimited;
9946 VDIfIoInt.pfnWriteUser = vdIOIntWriteUserLimited;
9947 VDIfIoInt.pfnReadMeta = vdIOIntReadMetaLimited;
9948 VDIfIoInt.pfnWriteMeta = vdIOIntWriteMetaLimited;
9949 VDIfIoInt.pfnFlush = vdIOIntFlushLimited;
9950 rc = VDInterfaceAdd(&VDIfIoInt.Core, "VD_IOINT", VDINTERFACETYPE_IOINT,
9951 pInterfaceIo, sizeof(VDINTERFACEIOINT), &pVDIfsImage);
9952 AssertRC(rc);
9953
9954 rc = vdFindBackend(pszBackend, &pBackend);
9955 if (RT_SUCCESS(rc))
9956 {
9957 if (pBackend->pfnRepair)
9958 rc = pBackend->pfnRepair(pszFilename, pVDIfsDisk, pVDIfsImage, fFlags);
9959 else
9960 rc = VERR_VD_IMAGE_REPAIR_NOT_SUPPORTED;
9961 }
9962
9963 LogFlowFunc(("returns %Rrc\n", rc));
9964 return rc;
9965}
9966
Note: See TracBrowser for help on using the repository browser.

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