~ubuntu-branches/ubuntu/raring/virtualbox-ose/raring

« back to all changes in this revision

Viewing changes to src/VBox/Storage/VD.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Felix Geyer
  • Date: 2011-01-30 23:27:25 UTC
  • mfrom: (0.3.12 upstream)
  • Revision ID: james.westby@ubuntu.com-20110130232725-2ouajjd2ggdet0zd
Tags: 4.0.2-dfsg-1ubuntu1
* Merge from Debian unstable, remaining changes:
  - Add Apport hook.
    - debian/virtualbox-ose.files/source_virtualbox-ose.py
    - debian/virtualbox-ose.install
  - Drop *-source packages.
* Drop ubuntu-01-fix-build-gcc45.patch, fixed upstream.
* Drop ubuntu-02-as-needed.patch, added to the Debian package.

Show diffs side-by-side

added added

removed removed

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