VirtualBox

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

Last change on this file since 51073 was 51073, checked in by vboxsync, 11 years ago

VD: Fixes for filter support

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