VirtualBox

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

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

Storage/VD: stop sabotaging the resize of diff images

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