VirtualBox

source: vbox/trunk/src/VBox/Storage/QED.cpp@ 79967

Last change on this file since 79967 was 79965, checked in by vboxsync, 5 years ago

Storage: Added a desired format parameter to VDGetFormat() so Main can pass along the device type. Bumped the RAW backend down after the CUE and VISO to prevent (impossible) mixups of tiny files. Extended rawProbe() to look for a valid ISO-9660/UDF descriptor sequence on the image if the caller doesn't say it should be a floppy or hdd, only if that fail go by the extension. Note! the pfnProbe callback should probably get a score return parameter to indicate how confient the backend is about the probe result, as this would prevent the RAW backend from accidentally grabbing images which belongs to a plug-in. bugref:9151

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 86.7 KB
Line 
1/* $Id: QED.cpp 79965 2019-07-24 20:32:32Z vboxsync $ */
2/** @file
3 * QED - QED Disk image.
4 */
5
6/*
7 * Copyright (C) 2011-2019 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/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_VD_QED
23#include <VBox/vd-plugin.h>
24#include <VBox/err.h>
25
26#include <VBox/log.h>
27#include <iprt/asm.h>
28#include <iprt/assert.h>
29#include <iprt/string.h>
30#include <iprt/alloc.h>
31#include <iprt/path.h>
32#include <iprt/list.h>
33
34#include "VDBackends.h"
35#include "VDBackendsInline.h"
36
37/**
38 * The QED backend implements support for the qemu enhanced disk format (short QED)
39 * The specification for the format is available under http://wiki.qemu.org/Features/QED/Specification
40 *
41 * Missing things to implement:
42 * - compaction
43 * - resizing which requires block relocation (very rare case)
44 */
45
46
47/*********************************************************************************************************************************
48* Structures in a QED image, little endian *
49*********************************************************************************************************************************/
50
51#pragma pack(1)
52typedef struct QedHeader
53{
54 /** Magic value. */
55 uint32_t u32Magic;
56 /** Cluster size in bytes. */
57 uint32_t u32ClusterSize;
58 /** Size of L1 and L2 tables in clusters. */
59 uint32_t u32TableSize;
60 /** size of this header structure in clusters. */
61 uint32_t u32HeaderSize;
62 /** Features used for the image. */
63 uint64_t u64FeatureFlags;
64 /** Compatibility features used for the image. */
65 uint64_t u64CompatFeatureFlags;
66 /** Self resetting feature bits. */
67 uint64_t u64AutoresetFeatureFlags;
68 /** Offset of the L1 table in bytes. */
69 uint64_t u64OffL1Table;
70 /** Logical image size as seen by the guest. */
71 uint64_t u64Size;
72 /** Offset of the backing filename in bytes. */
73 uint32_t u32OffBackingFilename;
74 /** Size of the backing filename. */
75 uint32_t u32BackingFilenameSize;
76} QedHeader;
77#pragma pack()
78/** Pointer to a on disk QED header. */
79typedef QedHeader *PQedHeader;
80
81/** QED magic value. */
82#define QED_MAGIC UINT32_C(0x00444551) /* QED\0 */
83/** Cluster size minimum. */
84#define QED_CLUSTER_SIZE_MIN RT_BIT(12)
85/** Cluster size maximum. */
86#define QED_CLUSTER_SIZE_MAX RT_BIT(26)
87/** L1 and L2 Table size minimum. */
88#define QED_TABLE_SIZE_MIN 1
89/** L1 and L2 Table size maximum. */
90#define QED_TABLE_SIZE_MAX 16
91
92/** QED default cluster size when creating an image. */
93#define QED_CLUSTER_SIZE_DEFAULT (64 * _1K)
94/** The default table size in clusters. */
95#define QED_TABLE_SIZE_DEFAULT 4
96
97/** Feature flags.
98 * @{
99 */
100/** Image uses a backing file to provide data for unallocated clusters. */
101#define QED_FEATURE_BACKING_FILE RT_BIT_64(0)
102/** Image needs checking before use. */
103#define QED_FEATURE_NEED_CHECK RT_BIT_64(1)
104/** Don't probe for format of the backing file, treat as raw image. */
105#define QED_FEATURE_BACKING_FILE_NO_PROBE RT_BIT_64(2)
106/** Mask of valid features. */
107#define QED_FEATURE_MASK (QED_FEATURE_BACKING_FILE | QED_FEATURE_NEED_CHECK | QED_FEATURE_BACKING_FILE_NO_PROBE)
108/** @} */
109
110/** Compatibility feature flags.
111 * @{
112 */
113/** Mask of valid compatibility features. */
114#define QED_COMPAT_FEATURE_MASK (0)
115/** @} */
116
117/** Autoreset feature flags.
118 * @{
119 */
120/** Mask of valid autoreset features. */
121#define QED_AUTORESET_FEATURE_MASK (0)
122/** @} */
123
124
125/*********************************************************************************************************************************
126* Constants And Macros, Structures and Typedefs *
127*********************************************************************************************************************************/
128
129/**
130 * QED L2 cache entry.
131 */
132typedef struct QEDL2CACHEENTRY
133{
134 /** List node for the search list. */
135 RTLISTNODE NodeSearch;
136 /** List node for the LRU list. */
137 RTLISTNODE NodeLru;
138 /** Reference counter. */
139 uint32_t cRefs;
140 /** The offset of the L2 table, used as search key. */
141 uint64_t offL2Tbl;
142 /** Pointer to the cached L2 table. */
143 uint64_t *paL2Tbl;
144} QEDL2CACHEENTRY, *PQEDL2CACHEENTRY;
145
146/** Maximum amount of memory the cache is allowed to use. */
147#define QED_L2_CACHE_MEMORY_MAX (2*_1M)
148
149/**
150 * QED image data structure.
151 */
152typedef struct QEDIMAGE
153{
154 /** Image name. */
155 const char *pszFilename;
156 /** Storage handle. */
157 PVDIOSTORAGE pStorage;
158
159 /** Pointer to the per-disk VD interface list. */
160 PVDINTERFACE pVDIfsDisk;
161 /** Pointer to the per-image VD interface list. */
162 PVDINTERFACE pVDIfsImage;
163 /** Error interface. */
164 PVDINTERFACEERROR pIfError;
165 /** I/O interface. */
166 PVDINTERFACEIOINT pIfIo;
167
168 /** Open flags passed by VBoxHD layer. */
169 unsigned uOpenFlags;
170 /** Image flags defined during creation or determined during open. */
171 unsigned uImageFlags;
172 /** Total size of the image. */
173 uint64_t cbSize;
174 /** Physical geometry of this image. */
175 VDGEOMETRY PCHSGeometry;
176 /** Logical geometry of this image. */
177 VDGEOMETRY LCHSGeometry;
178
179 /** Filename of the backing file if any. */
180 char *pszBackingFilename;
181 /** Offset of the filename in the image. */
182 uint32_t offBackingFilename;
183 /** Size of the backing filename excluding \0. */
184 uint32_t cbBackingFilename;
185
186 /** Size of the image, multiple of clusters. */
187 uint64_t cbImage;
188 /** Cluster size in bytes. */
189 uint32_t cbCluster;
190 /** Number of entries in the L1 and L2 table. */
191 uint32_t cTableEntries;
192 /** Size of an L1 or L2 table rounded to the next cluster size. */
193 uint32_t cbTable;
194 /** Pointer to the L1 table. */
195 uint64_t *paL1Table;
196 /** Offset of the L1 table. */
197 uint64_t offL1Table;
198
199 /** Offset mask for a cluster. */
200 uint64_t fOffsetMask;
201 /** L1 table mask to get the L1 index. */
202 uint64_t fL1Mask;
203 /** Number of bits to shift to get the L1 index. */
204 uint32_t cL1Shift;
205 /** L2 table mask to get the L2 index. */
206 uint64_t fL2Mask;
207 /** Number of bits to shift to get the L2 index. */
208 uint32_t cL2Shift;
209
210 /** Pointer to the L2 table we are currently allocating
211 * (can be only one at a time). */
212 PQEDL2CACHEENTRY pL2TblAlloc;
213
214 /** Memory occupied by the L2 table cache. */
215 size_t cbL2Cache;
216 /** The sorted L2 entry list used for searching. */
217 RTLISTNODE ListSearch;
218 /** The LRU L2 entry list used for eviction. */
219 RTLISTNODE ListLru;
220 /** The static region list. */
221 VDREGIONLIST RegionList;
222} QEDIMAGE, *PQEDIMAGE;
223
224/**
225 * State of the async cluster allocation.
226 */
227typedef enum QEDCLUSTERASYNCALLOCSTATE
228{
229 /** Invalid. */
230 QEDCLUSTERASYNCALLOCSTATE_INVALID = 0,
231 /** L2 table allocation. */
232 QEDCLUSTERASYNCALLOCSTATE_L2_ALLOC,
233 /** Link L2 table into L1. */
234 QEDCLUSTERASYNCALLOCSTATE_L2_LINK,
235 /** Allocate user data cluster. */
236 QEDCLUSTERASYNCALLOCSTATE_USER_ALLOC,
237 /** Link user data cluster. */
238 QEDCLUSTERASYNCALLOCSTATE_USER_LINK,
239 /** 32bit blowup. */
240 QEDCLUSTERASYNCALLOCSTATE_32BIT_HACK = 0x7fffffff
241} QEDCLUSTERASYNCALLOCSTATE, *PQEDCLUSTERASYNCALLOCSTATE;
242
243/**
244 * Data needed to track async cluster allocation.
245 */
246typedef struct QEDCLUSTERASYNCALLOC
247{
248 /** The state of the cluster allocation. */
249 QEDCLUSTERASYNCALLOCSTATE enmAllocState;
250 /** Old image size to rollback in case of an error. */
251 uint64_t cbImageOld;
252 /** L1 index to link if any. */
253 uint32_t idxL1;
254 /** L2 index to link, required in any case. */
255 uint32_t idxL2;
256 /** Start offset of the allocated cluster. */
257 uint64_t offClusterNew;
258 /** L2 cache entry if a L2 table is allocated. */
259 PQEDL2CACHEENTRY pL2Entry;
260 /** Number of bytes to write. */
261 size_t cbToWrite;
262} QEDCLUSTERASYNCALLOC, *PQEDCLUSTERASYNCALLOC;
263
264
265/*********************************************************************************************************************************
266* Static Variables *
267*********************************************************************************************************************************/
268
269/** NULL-terminated array of supported file extensions. */
270static const VDFILEEXTENSION s_aQedFileExtensions[] =
271{
272 {"qed", VDTYPE_HDD},
273 {NULL, VDTYPE_INVALID}
274};
275
276
277/*********************************************************************************************************************************
278* Internal Functions *
279*********************************************************************************************************************************/
280
281/**
282 * Converts the image header to the host endianess and performs basic checks.
283 *
284 * @returns Whether the given header is valid or not.
285 * @param pHeader Pointer to the header to convert.
286 */
287static bool qedHdrConvertToHostEndianess(PQedHeader pHeader)
288{
289 pHeader->u32Magic = RT_LE2H_U32(pHeader->u32Magic);
290 pHeader->u32ClusterSize = RT_LE2H_U32(pHeader->u32ClusterSize);
291 pHeader->u32TableSize = RT_LE2H_U32(pHeader->u32TableSize);
292 pHeader->u32HeaderSize = RT_LE2H_U32(pHeader->u32HeaderSize);
293 pHeader->u64FeatureFlags = RT_LE2H_U64(pHeader->u64FeatureFlags);
294 pHeader->u64CompatFeatureFlags = RT_LE2H_U64(pHeader->u64CompatFeatureFlags);
295 pHeader->u64AutoresetFeatureFlags = RT_LE2H_U64(pHeader->u64AutoresetFeatureFlags);
296 pHeader->u64OffL1Table = RT_LE2H_U64(pHeader->u64OffL1Table);
297 pHeader->u64Size = RT_LE2H_U64(pHeader->u64Size);
298 pHeader->u32OffBackingFilename = RT_LE2H_U32(pHeader->u32OffBackingFilename);
299 pHeader->u32BackingFilenameSize = RT_LE2H_U32(pHeader->u32BackingFilenameSize);
300
301 if (RT_UNLIKELY(pHeader->u32Magic != QED_MAGIC))
302 return false;
303 if (RT_UNLIKELY( pHeader->u32ClusterSize < QED_CLUSTER_SIZE_MIN
304 || pHeader->u32ClusterSize > QED_CLUSTER_SIZE_MAX))
305 return false;
306 if (RT_UNLIKELY( pHeader->u32TableSize < QED_TABLE_SIZE_MIN
307 || pHeader->u32TableSize > QED_TABLE_SIZE_MAX))
308 return false;
309 if (RT_UNLIKELY(pHeader->u64Size % 512 != 0))
310 return false;
311 if (RT_UNLIKELY( pHeader->u64FeatureFlags & QED_FEATURE_BACKING_FILE
312 && ( pHeader->u32BackingFilenameSize == 0
313 || pHeader->u32BackingFilenameSize == UINT32_MAX)))
314 return false;
315
316 return true;
317}
318
319/**
320 * Creates a QED header from the given image state.
321 *
322 * @returns nothing.
323 * @param pImage Image instance data.
324 * @param pHeader Pointer to the header to convert.
325 */
326static void qedHdrConvertFromHostEndianess(PQEDIMAGE pImage, PQedHeader pHeader)
327{
328 pHeader->u32Magic = RT_H2LE_U32(QED_MAGIC);
329 pHeader->u32ClusterSize = RT_H2LE_U32(pImage->cbCluster);
330 pHeader->u32TableSize = RT_H2LE_U32(pImage->cbTable / pImage->cbCluster);
331 pHeader->u32HeaderSize = RT_H2LE_U32(1);
332 pHeader->u64FeatureFlags = RT_H2LE_U64(pImage->pszBackingFilename ? QED_FEATURE_BACKING_FILE : UINT64_C(0));
333 pHeader->u64CompatFeatureFlags = RT_H2LE_U64(UINT64_C(0));
334 pHeader->u64AutoresetFeatureFlags = RT_H2LE_U64(UINT64_C(0));
335 pHeader->u64OffL1Table = RT_H2LE_U64(pImage->offL1Table);
336 pHeader->u64Size = RT_H2LE_U64(pImage->cbSize);
337 pHeader->u32OffBackingFilename = RT_H2LE_U32(pImage->offBackingFilename);
338 pHeader->u32BackingFilenameSize = RT_H2LE_U32(pImage->cbBackingFilename);
339}
340
341/**
342 * Convert table entries from little endian to host endianess.
343 *
344 * @returns nothing.
345 * @param paTbl Pointer to the table.
346 * @param cEntries Number of entries in the table.
347 */
348static void qedTableConvertToHostEndianess(uint64_t *paTbl, uint32_t cEntries)
349{
350 while(cEntries-- > 0)
351 {
352 *paTbl = RT_LE2H_U64(*paTbl);
353 paTbl++;
354 }
355}
356
357#if defined(RT_BIG_ENDIAN)
358/**
359 * Convert table entries from host to little endian format.
360 *
361 * @returns nothing.
362 * @param paTblImg Pointer to the table which will store the little endian table.
363 * @param paTbl The source table to convert.
364 * @param cEntries Number of entries in the table.
365 */
366static void qedTableConvertFromHostEndianess(uint64_t *paTblImg, uint64_t *paTbl,
367 uint32_t cEntries)
368{
369 while(cEntries-- > 0)
370 {
371 *paTblImg = RT_H2LE_U64(*paTbl);
372 paTbl++;
373 paTblImg++;
374 }
375}
376#endif
377
378/**
379 * Creates the L2 table cache.
380 *
381 * @returns VBox status code.
382 * @param pImage The image instance data.
383 */
384static int qedL2TblCacheCreate(PQEDIMAGE pImage)
385{
386 pImage->cbL2Cache = 0;
387 RTListInit(&pImage->ListSearch);
388 RTListInit(&pImage->ListLru);
389
390 return VINF_SUCCESS;
391}
392
393/**
394 * Destroys the L2 table cache.
395 *
396 * @returns nothing.
397 * @param pImage The image instance data.
398 */
399static void qedL2TblCacheDestroy(PQEDIMAGE pImage)
400{
401 PQEDL2CACHEENTRY pL2Entry;
402 PQEDL2CACHEENTRY pL2Next;
403 RTListForEachSafe(&pImage->ListSearch, pL2Entry, pL2Next, QEDL2CACHEENTRY, NodeSearch)
404 {
405 Assert(!pL2Entry->cRefs);
406
407 RTListNodeRemove(&pL2Entry->NodeSearch);
408 RTMemPageFree(pL2Entry->paL2Tbl, pImage->cbTable);
409 RTMemFree(pL2Entry);
410 }
411
412 pImage->cbL2Cache = 0;
413 RTListInit(&pImage->ListSearch);
414 RTListInit(&pImage->ListLru);
415}
416
417/**
418 * Returns the L2 table matching the given offset or NULL if none could be found.
419 *
420 * @returns Pointer to the L2 table cache entry or NULL.
421 * @param pImage The image instance data.
422 * @param offL2Tbl Offset of the L2 table to search for.
423 */
424static PQEDL2CACHEENTRY qedL2TblCacheRetain(PQEDIMAGE pImage, uint64_t offL2Tbl)
425{
426 if ( pImage->pL2TblAlloc
427 && pImage->pL2TblAlloc->offL2Tbl == offL2Tbl)
428 {
429 pImage->pL2TblAlloc->cRefs++;
430 return pImage->pL2TblAlloc;
431 }
432
433 PQEDL2CACHEENTRY pL2Entry;
434 RTListForEach(&pImage->ListSearch, pL2Entry, QEDL2CACHEENTRY, NodeSearch)
435 {
436 if (pL2Entry->offL2Tbl == offL2Tbl)
437 break;
438 }
439
440 if (!RTListNodeIsDummy(&pImage->ListSearch, pL2Entry, QEDL2CACHEENTRY, NodeSearch))
441 {
442 /* Update LRU list. */
443 RTListNodeRemove(&pL2Entry->NodeLru);
444 RTListPrepend(&pImage->ListLru, &pL2Entry->NodeLru);
445 pL2Entry->cRefs++;
446 return pL2Entry;
447 }
448 else
449 return NULL;
450}
451
452/**
453 * Releases a L2 table cache entry.
454 *
455 * @returns nothing.
456 * @param pL2Entry The L2 cache entry.
457 */
458static void qedL2TblCacheEntryRelease(PQEDL2CACHEENTRY pL2Entry)
459{
460 Assert(pL2Entry->cRefs > 0);
461 pL2Entry->cRefs--;
462}
463
464/**
465 * Allocates a new L2 table from the cache evicting old entries if required.
466 *
467 * @returns Pointer to the L2 cache entry or NULL.
468 * @param pImage The image instance data.
469 */
470static PQEDL2CACHEENTRY qedL2TblCacheEntryAlloc(PQEDIMAGE pImage)
471{
472 PQEDL2CACHEENTRY pL2Entry = NULL;
473
474 if (pImage->cbL2Cache + pImage->cbTable <= QED_L2_CACHE_MEMORY_MAX)
475 {
476 /* Add a new entry. */
477 pL2Entry = (PQEDL2CACHEENTRY)RTMemAllocZ(sizeof(QEDL2CACHEENTRY));
478 if (pL2Entry)
479 {
480 pL2Entry->paL2Tbl = (uint64_t *)RTMemPageAllocZ(pImage->cbTable);
481 if (RT_UNLIKELY(!pL2Entry->paL2Tbl))
482 {
483 RTMemFree(pL2Entry);
484 pL2Entry = NULL;
485 }
486 else
487 {
488 pL2Entry->cRefs = 1;
489 pImage->cbL2Cache += pImage->cbTable;
490 }
491 }
492 }
493 else
494 {
495 /* Evict the last not in use entry and use it */
496 Assert(!RTListIsEmpty(&pImage->ListLru));
497
498 RTListForEachReverse(&pImage->ListLru, pL2Entry, QEDL2CACHEENTRY, NodeLru)
499 {
500 if (!pL2Entry->cRefs)
501 break;
502 }
503
504 if (!RTListNodeIsDummy(&pImage->ListSearch, pL2Entry, QEDL2CACHEENTRY, NodeSearch))
505 {
506 RTListNodeRemove(&pL2Entry->NodeSearch);
507 RTListNodeRemove(&pL2Entry->NodeLru);
508 pL2Entry->offL2Tbl = 0;
509 pL2Entry->cRefs = 1;
510 }
511 else
512 pL2Entry = NULL;
513 }
514
515 return pL2Entry;
516}
517
518/**
519 * Frees a L2 table cache entry.
520 *
521 * @returns nothing.
522 * @param pImage The image instance data.
523 * @param pL2Entry The L2 cache entry to free.
524 */
525static void qedL2TblCacheEntryFree(PQEDIMAGE pImage, PQEDL2CACHEENTRY pL2Entry)
526{
527 Assert(!pL2Entry->cRefs);
528 RTMemPageFree(pL2Entry->paL2Tbl, pImage->cbTable);
529 RTMemFree(pL2Entry);
530
531 pImage->cbL2Cache -= pImage->cbTable;
532}
533
534/**
535 * Inserts an entry in the L2 table cache.
536 *
537 * @returns nothing.
538 * @param pImage The image instance data.
539 * @param pL2Entry The L2 cache entry to insert.
540 */
541static void qedL2TblCacheEntryInsert(PQEDIMAGE pImage, PQEDL2CACHEENTRY pL2Entry)
542{
543 Assert(pL2Entry->offL2Tbl > 0);
544
545 /* Insert at the top of the LRU list. */
546 RTListPrepend(&pImage->ListLru, &pL2Entry->NodeLru);
547
548 if (RTListIsEmpty(&pImage->ListSearch))
549 {
550 RTListAppend(&pImage->ListSearch, &pL2Entry->NodeSearch);
551 }
552 else
553 {
554 /* Insert into search list. */
555 PQEDL2CACHEENTRY pIt;
556 pIt = RTListGetFirst(&pImage->ListSearch, QEDL2CACHEENTRY, NodeSearch);
557 if (pIt->offL2Tbl > pL2Entry->offL2Tbl)
558 RTListPrepend(&pImage->ListSearch, &pL2Entry->NodeSearch);
559 else
560 {
561 bool fInserted = false;
562
563 RTListForEach(&pImage->ListSearch, pIt, QEDL2CACHEENTRY, NodeSearch)
564 {
565 Assert(pIt->offL2Tbl != pL2Entry->offL2Tbl);
566 if (pIt->offL2Tbl < pL2Entry->offL2Tbl)
567 {
568 RTListNodeInsertAfter(&pIt->NodeSearch, &pL2Entry->NodeSearch);
569 fInserted = true;
570 break;
571 }
572 }
573 Assert(fInserted);
574 }
575 }
576}
577
578/**
579 * Fetches the L2 from the given offset trying the LRU cache first and
580 * reading it from the image after a cache miss - version for async I/O.
581 *
582 * @returns VBox status code.
583 * @param pImage Image instance data.
584 * @param pIoCtx The I/O context.
585 * @param offL2Tbl The offset of the L2 table in the image.
586 * @param ppL2Entry Where to store the L2 table on success.
587 */
588static int qedL2TblCacheFetchAsync(PQEDIMAGE pImage, PVDIOCTX pIoCtx,
589 uint64_t offL2Tbl, PQEDL2CACHEENTRY *ppL2Entry)
590{
591 int rc = VINF_SUCCESS;
592
593 /* Try to fetch the L2 table from the cache first. */
594 PQEDL2CACHEENTRY pL2Entry = qedL2TblCacheRetain(pImage, offL2Tbl);
595 if (!pL2Entry)
596 {
597 pL2Entry = qedL2TblCacheEntryAlloc(pImage);
598
599 if (pL2Entry)
600 {
601 /* Read from the image. */
602 PVDMETAXFER pMetaXfer;
603
604 pL2Entry->offL2Tbl = offL2Tbl;
605 rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pImage->pStorage,
606 offL2Tbl, pL2Entry->paL2Tbl,
607 pImage->cbTable, pIoCtx,
608 &pMetaXfer, NULL, NULL);
609 if (RT_SUCCESS(rc))
610 {
611 vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
612#if defined(RT_BIG_ENDIAN)
613 qedTableConvertToHostEndianess(pL2Entry->paL2Tbl, pImage->cTableEntries);
614#endif
615 qedL2TblCacheEntryInsert(pImage, pL2Entry);
616 }
617 else
618 {
619 qedL2TblCacheEntryRelease(pL2Entry);
620 qedL2TblCacheEntryFree(pImage, pL2Entry);
621 }
622 }
623 else
624 rc = VERR_NO_MEMORY;
625 }
626
627 if (RT_SUCCESS(rc))
628 *ppL2Entry = pL2Entry;
629
630 return rc;
631}
632
633/**
634 * Return power of 2 or 0 if num error.
635 *
636 * @returns The power of 2 or 0 if the given number is not a power of 2.
637 * @param u32 The number.
638 */
639static uint32_t qedGetPowerOfTwo(uint32_t u32)
640{
641 if (u32 == 0)
642 return 0;
643 uint32_t uPower2 = 0;
644 while ((u32 & 1) == 0)
645 {
646 u32 >>= 1;
647 uPower2++;
648 }
649 return u32 == 1 ? uPower2 : 0;
650}
651
652/**
653 * Sets the L1, L2 and offset bitmasks and L1 and L2 bit shift members.
654 *
655 * @returns nothing.
656 * @param pImage The image instance data.
657 */
658static void qedTableMasksInit(PQEDIMAGE pImage)
659{
660 uint32_t cClusterBits, cTableBits;
661
662 cClusterBits = qedGetPowerOfTwo(pImage->cbCluster);
663 cTableBits = qedGetPowerOfTwo(pImage->cTableEntries);
664
665 Assert(cClusterBits + 2 * cTableBits <= 64);
666
667 pImage->fOffsetMask = ((uint64_t)pImage->cbCluster - 1);
668 pImage->fL2Mask = ((uint64_t)pImage->cTableEntries - 1) << cClusterBits;
669 pImage->cL2Shift = cClusterBits;
670 pImage->fL1Mask = ((uint64_t)pImage->cTableEntries - 1) << (cClusterBits + cTableBits);
671 pImage->cL1Shift = cClusterBits + cTableBits;
672}
673
674/**
675 * Converts a given logical offset into the
676 *
677 * @returns nothing.
678 * @param pImage The image instance data.
679 * @param off The logical offset to convert.
680 * @param pidxL1 Where to store the index in the L1 table on success.
681 * @param pidxL2 Where to store the index in the L2 table on success.
682 * @param poffCluster Where to store the offset in the cluster on success.
683 */
684DECLINLINE(void) qedConvertLogicalOffset(PQEDIMAGE pImage, uint64_t off, uint32_t *pidxL1,
685 uint32_t *pidxL2, uint32_t *poffCluster)
686{
687 AssertPtr(pidxL1);
688 AssertPtr(pidxL2);
689 AssertPtr(poffCluster);
690
691 *poffCluster = off & pImage->fOffsetMask;
692 *pidxL1 = (off & pImage->fL1Mask) >> pImage->cL1Shift;
693 *pidxL2 = (off & pImage->fL2Mask) >> pImage->cL2Shift;
694}
695
696/**
697 * Converts Cluster size to a byte size.
698 *
699 * @returns Number of bytes derived from the given number of clusters.
700 * @param pImage The image instance data.
701 * @param cClusters The clusters to convert.
702 */
703DECLINLINE(uint64_t) qedCluster2Byte(PQEDIMAGE pImage, uint64_t cClusters)
704{
705 return cClusters * pImage->cbCluster;
706}
707
708/**
709 * Converts number of bytes to cluster size rounding to the next cluster.
710 *
711 * @returns Number of bytes derived from the given number of clusters.
712 * @param pImage The image instance data.
713 * @param cb Number of bytes to convert.
714 */
715DECLINLINE(uint64_t) qedByte2Cluster(PQEDIMAGE pImage, uint64_t cb)
716{
717 return cb / pImage->cbCluster + (cb % pImage->cbCluster ? 1 : 0);
718}
719
720/**
721 * Allocates a new cluster in the image.
722 *
723 * @returns The start offset of the new cluster in the image.
724 * @param pImage The image instance data.
725 * @param cClusters Number of clusters to allocate.
726 */
727DECLINLINE(uint64_t) qedClusterAllocate(PQEDIMAGE pImage, uint32_t cClusters)
728{
729 uint64_t offCluster;
730
731 offCluster = pImage->cbImage;
732 pImage->cbImage += cClusters*pImage->cbCluster;
733
734 return offCluster;
735}
736
737/**
738 * Returns the real image offset for a given cluster or an error if the cluster is not
739 * yet allocated.
740 *
741 * @returns VBox status code.
742 * VERR_VD_BLOCK_FREE if the cluster is not yet allocated.
743 * @param pImage The image instance data.
744 * @param pIoCtx The I/O context.
745 * @param idxL1 The L1 index.
746 * @param idxL2 The L2 index.
747 * @param offCluster Offset inside the cluster.
748 * @param poffImage Where to store the image offset on success;
749 */
750static int qedConvertToImageOffset(PQEDIMAGE pImage, PVDIOCTX pIoCtx,
751 uint32_t idxL1, uint32_t idxL2,
752 uint32_t offCluster, uint64_t *poffImage)
753{
754 int rc = VERR_VD_BLOCK_FREE;
755
756 AssertReturn(idxL1 < pImage->cTableEntries, VERR_INVALID_PARAMETER);
757 AssertReturn(idxL2 < pImage->cTableEntries, VERR_INVALID_PARAMETER);
758
759 if (pImage->paL1Table[idxL1])
760 {
761 PQEDL2CACHEENTRY pL2Entry;
762
763 rc = qedL2TblCacheFetchAsync(pImage, pIoCtx, pImage->paL1Table[idxL1],
764 &pL2Entry);
765 if (RT_SUCCESS(rc))
766 {
767 /* Get real file offset. */
768 if (pL2Entry->paL2Tbl[idxL2])
769 *poffImage = pL2Entry->paL2Tbl[idxL2] + offCluster;
770 else
771 rc = VERR_VD_BLOCK_FREE;
772
773 qedL2TblCacheEntryRelease(pL2Entry);
774 }
775 }
776
777 return rc;
778}
779
780/**
781 * Write the given table to image converting to the image endianess if required.
782 *
783 * @returns VBox status code.
784 * @param pImage The image instance data.
785 * @param pIoCtx The I/O context.
786 * @param offTbl The offset the table should be written to.
787 * @param paTbl The table to write.
788 * @param pfnComplete Callback called when the write completes.
789 * @param pvUser Opaque user data to pass in the completion callback.
790 */
791static int qedTblWrite(PQEDIMAGE pImage, PVDIOCTX pIoCtx, uint64_t offTbl, uint64_t *paTbl,
792 PFNVDXFERCOMPLETED pfnComplete, void *pvUser)
793{
794 int rc = VINF_SUCCESS;
795
796#if defined(RT_BIG_ENDIAN)
797 uint64_t *paTblImg = (uint64_t *)RTMemAllocZ(pImage->cbTable);
798 if (paTblImg)
799 {
800 qedTableConvertFromHostEndianess(paTblImg, paTbl,
801 pImage->cTableEntries);
802 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
803 offTbl, paTblImg, pImage->cbTable,
804 pIoCtx, pfnComplete, pvUser);
805 RTMemFree(paTblImg);
806 }
807 else
808 rc = VERR_NO_MEMORY;
809#else
810 /* Write table directly. */
811 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
812 offTbl, paTbl, pImage->cbTable, pIoCtx,
813 pfnComplete, pvUser);
814#endif
815
816 return rc;
817}
818
819/**
820 * Internal. Flush image data to disk.
821 */
822static int qedFlushImage(PQEDIMAGE pImage)
823{
824 int rc = VINF_SUCCESS;
825
826 if ( pImage->pStorage
827 && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
828 {
829 QedHeader Header;
830
831 Assert(!(pImage->cbTable % pImage->cbCluster));
832#if defined(RT_BIG_ENDIAN)
833 uint64_t *paL1TblImg = (uint64_t *)RTMemAllocZ(pImage->cbTable);
834 if (paL1TblImg)
835 {
836 qedTableConvertFromHostEndianess(paL1TblImg, pImage->paL1Table,
837 pImage->cTableEntries);
838 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
839 pImage->offL1Table, paL1TblImg,
840 pImage->cbTable);
841 RTMemFree(paL1TblImg);
842 }
843 else
844 rc = VERR_NO_MEMORY;
845#else
846 /* Write L1 table directly. */
847 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->offL1Table,
848 pImage->paL1Table, pImage->cbTable);
849#endif
850 if (RT_SUCCESS(rc))
851 {
852 /* Write header. */
853 qedHdrConvertFromHostEndianess(pImage, &Header);
854 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, 0, &Header,
855 sizeof(Header));
856 if (RT_SUCCESS(rc))
857 rc = vdIfIoIntFileFlushSync(pImage->pIfIo, pImage->pStorage);
858 }
859 }
860
861 return rc;
862}
863
864/**
865 * Checks whether the given cluster offset is valid.
866 *
867 * @returns Whether the given cluster offset is valid.
868 * @param offCluster The table offset to check.
869 * @param cbFile The real file size of the image.
870 * @param cbCluster The cluster size in bytes.
871 */
872DECLINLINE(bool) qedIsClusterOffsetValid(uint64_t offCluster, uint64_t cbFile, size_t cbCluster)
873{
874 return (offCluster <= cbFile - cbCluster)
875 && !(offCluster & (cbCluster - 1));
876}
877
878/**
879 * Checks whether the given table offset is valid.
880 *
881 * @returns Whether the given table offset is valid.
882 * @param offTbl The table offset to check.
883 * @param cbFile The real file size of the image.
884 * @param cbTable The table size in bytes.
885 * @param cbCluster The cluster size in bytes.
886 */
887DECLINLINE(bool) qedIsTblOffsetValid(uint64_t offTbl, uint64_t cbFile, size_t cbTable, size_t cbCluster)
888{
889 return (offTbl <= cbFile - cbTable)
890 && !(offTbl & (cbCluster - 1));
891}
892
893/**
894 * Sets the specified range in the cluster bitmap checking whether any of the clusters is already
895 * used before.
896 *
897 * @returns Whether the range was clear and is set now.
898 * @param pvClusterBitmap The cluster bitmap to use.
899 * @param offClusterStart The first cluster to check and set.
900 * @param offClusterEnd The first cluster to not check and set anymore.
901 */
902static bool qedClusterBitmapCheckAndSet(void *pvClusterBitmap, uint32_t offClusterStart, uint32_t offClusterEnd)
903{
904 for (uint32_t offCluster = offClusterStart; offCluster < offClusterEnd; offCluster++)
905 if (ASMBitTest(pvClusterBitmap, offCluster))
906 return false;
907
908 ASMBitSetRange(pvClusterBitmap, offClusterStart, offClusterEnd);
909 return true;
910}
911
912/**
913 * Checks the given image for consistency, usually called when the
914 * QED_FEATURE_NEED_CHECK bit is set.
915 *
916 * @returns VBox status code.
917 * @retval VINF_SUCCESS when the image can be accessed.
918 * @param pImage The image instance data.
919 * @param pHeader The header to use for checking.
920 *
921 * @note It is not required that the image state is fully initialized Only
922 * The I/O interface and storage handle need to be valid.
923 * @note The header must be converted to the host CPU endian format already
924 * and should be validated already.
925 */
926static int qedCheckImage(PQEDIMAGE pImage, PQedHeader pHeader)
927{
928 uint64_t cbFile;
929 uint32_t cbTable;
930 uint32_t cTableEntries;
931 uint64_t *paL1Tbl = NULL;
932 uint64_t *paL2Tbl = NULL;
933 void *pvClusterBitmap = NULL;
934 uint32_t offClusterStart;
935 int rc = VINF_SUCCESS;
936
937 pImage->cbCluster = pHeader->u32ClusterSize;
938 cbTable = pHeader->u32TableSize * pHeader->u32ClusterSize;
939 cTableEntries = cbTable / sizeof(uint64_t);
940
941 do
942 {
943 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbFile);
944 if (RT_FAILURE(rc))
945 {
946 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
947 N_("Qed: Querying the file size of image '%s' failed"),
948 pImage->pszFilename);
949 break;
950 }
951
952 /* Allocate L1 table. */
953 paL1Tbl = (uint64_t *)RTMemAllocZ(cbTable);
954 if (!paL1Tbl)
955 {
956 rc = vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS,
957 N_("Qed: Allocating memory for the L1 table for image '%s' failed"),
958 pImage->pszFilename);
959 break;
960 }
961
962 paL2Tbl = (uint64_t *)RTMemAllocZ(cbTable);
963 if (!paL2Tbl)
964 {
965 rc = vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS,
966 N_("Qed: Allocating memory for the L2 table for image '%s' failed"),
967 pImage->pszFilename);
968 break;
969 }
970
971 pvClusterBitmap = RTMemAllocZ(cbFile / pHeader->u32ClusterSize / 8);
972 if (!pvClusterBitmap)
973 {
974 rc = vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS,
975 N_("Qed: Allocating memory for the cluster bitmap for image '%s' failed"),
976 pImage->pszFilename);
977 break;
978 }
979
980 /* Validate L1 table offset. */
981 if (!qedIsTblOffsetValid(pHeader->u64OffL1Table, cbFile, cbTable, pHeader->u32ClusterSize))
982 {
983 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
984 N_("Qed: L1 table offset of image '%s' is corrupt (%llu)"),
985 pImage->pszFilename, pHeader->u64OffL1Table);
986 break;
987 }
988
989 /* Read L1 table. */
990 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
991 pHeader->u64OffL1Table, paL1Tbl, cbTable);
992 if (RT_FAILURE(rc))
993 {
994 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
995 N_("Qed: Reading the L1 table from image '%s' failed"),
996 pImage->pszFilename);
997 break;
998 }
999
1000 /* Mark the L1 table in cluster bitmap. */
1001 ASMBitSet(pvClusterBitmap, 0); /* Header is always in cluster 0. */
1002 offClusterStart = qedByte2Cluster(pImage, pHeader->u64OffL1Table);
1003 bool fSet = qedClusterBitmapCheckAndSet(pvClusterBitmap, offClusterStart, offClusterStart + pHeader->u32TableSize);
1004 Assert(fSet);
1005
1006 /* Scan the L1 and L2 tables for invalid entries. */
1007 qedTableConvertToHostEndianess(paL1Tbl, cTableEntries);
1008
1009 for (unsigned iL1 = 0; iL1 < cTableEntries; iL1++)
1010 {
1011 if (!paL1Tbl[iL1])
1012 continue; /* Skip unallocated clusters. */
1013
1014 if (!qedIsTblOffsetValid(paL1Tbl[iL1], cbFile, cbTable, pHeader->u32ClusterSize))
1015 {
1016 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1017 N_("Qed: Entry %d of the L1 table from image '%s' is invalid (%llu)"),
1018 iL1, pImage->pszFilename, paL1Tbl[iL1]);
1019 break;
1020 }
1021
1022 /* Now check that the clusters are not allocated already. */
1023 offClusterStart = qedByte2Cluster(pImage, paL1Tbl[iL1]);
1024 fSet = qedClusterBitmapCheckAndSet(pvClusterBitmap, offClusterStart, offClusterStart + pHeader->u32TableSize);
1025 if (!fSet)
1026 {
1027 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1028 N_("Qed: Entry %d of the L1 table from image '%s' points to a already used cluster (%llu)"),
1029 iL1, pImage->pszFilename, paL1Tbl[iL1]);
1030 break;
1031 }
1032
1033 /* Read the linked L2 table and check it. */
1034 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
1035 paL1Tbl[iL1], paL2Tbl, cbTable);
1036 if (RT_FAILURE(rc))
1037 {
1038 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1039 N_("Qed: Reading the L2 table from image '%s' failed"),
1040 pImage->pszFilename);
1041 break;
1042 }
1043
1044 /* Check all L2 entries. */
1045 for (unsigned iL2 = 0; iL2 < cTableEntries; iL2++)
1046 {
1047 if (paL2Tbl[iL2])
1048 continue; /* Skip unallocated clusters. */
1049
1050 if (!qedIsClusterOffsetValid(paL2Tbl[iL2], cbFile, pHeader->u32ClusterSize))
1051 {
1052 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1053 N_("Qed: Entry %d of the L2 table from image '%s' is invalid (%llu)"),
1054 iL2, pImage->pszFilename, paL2Tbl[iL2]);
1055 break;
1056 }
1057
1058 /* Now check that the clusters are not allocated already. */
1059 offClusterStart = qedByte2Cluster(pImage, paL2Tbl[iL2]);
1060 fSet = qedClusterBitmapCheckAndSet(pvClusterBitmap, offClusterStart, offClusterStart + 1);
1061 if (!fSet)
1062 {
1063 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1064 N_("Qed: Entry %d of the L2 table from image '%s' points to a already used cluster (%llu)"),
1065 iL2, pImage->pszFilename, paL2Tbl[iL2]);
1066 break;
1067 }
1068 }
1069 }
1070 } while(0);
1071
1072 if (paL1Tbl)
1073 RTMemFree(paL1Tbl);
1074 if (paL2Tbl)
1075 RTMemFree(paL2Tbl);
1076 if (pvClusterBitmap)
1077 RTMemFree(pvClusterBitmap);
1078
1079 return rc;
1080}
1081
1082/**
1083 * Internal. Free all allocated space for representing an image except pImage,
1084 * and optionally delete the image from disk.
1085 */
1086static int qedFreeImage(PQEDIMAGE pImage, bool fDelete)
1087{
1088 int rc = VINF_SUCCESS;
1089
1090 /* Freeing a never allocated image (e.g. because the open failed) is
1091 * not signalled as an error. After all nothing bad happens. */
1092 if (pImage)
1093 {
1094 if (pImage->pStorage)
1095 {
1096 /* No point updating the file that is deleted anyway. */
1097 if (!fDelete)
1098 qedFlushImage(pImage);
1099
1100 rc = vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
1101 pImage->pStorage = NULL;
1102 }
1103
1104 if (pImage->paL1Table)
1105 RTMemFree(pImage->paL1Table);
1106
1107 if (pImage->pszBackingFilename)
1108 {
1109 RTStrFree(pImage->pszBackingFilename);
1110 pImage->pszBackingFilename = NULL;
1111 }
1112
1113 qedL2TblCacheDestroy(pImage);
1114
1115 if (fDelete && pImage->pszFilename)
1116 vdIfIoIntFileDelete(pImage->pIfIo, pImage->pszFilename);
1117 }
1118
1119 LogFlowFunc(("returns %Rrc\n", rc));
1120 return rc;
1121}
1122
1123/**
1124 * Internal: Open an image, constructing all necessary data structures.
1125 */
1126static int qedOpenImage(PQEDIMAGE pImage, unsigned uOpenFlags)
1127{
1128 pImage->uOpenFlags = uOpenFlags;
1129
1130 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
1131 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
1132 AssertPtrReturn(pImage->pIfIo, VERR_INVALID_PARAMETER);
1133
1134 /*
1135 * Create the L2 cache before opening the image so we can call qedFreeImage()
1136 * even if opening the image file fails.
1137 */
1138 int rc = qedL2TblCacheCreate(pImage);
1139 if (RT_SUCCESS(rc))
1140 {
1141 /* Open the image. */
1142 rc = vdIfIoIntFileOpen(pImage->pIfIo, pImage->pszFilename,
1143 VDOpenFlagsToFileOpenFlags(uOpenFlags,
1144 false /* fCreate */),
1145 &pImage->pStorage);
1146 if (RT_SUCCESS(rc))
1147 {
1148 uint64_t cbFile;
1149 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbFile);
1150 if ( RT_SUCCESS(rc)
1151 && cbFile > sizeof(QedHeader))
1152 {
1153 QedHeader Header;
1154
1155 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, 0, &Header, sizeof(Header));
1156 if ( RT_SUCCESS(rc)
1157 && qedHdrConvertToHostEndianess(&Header))
1158 {
1159 if ( !(Header.u64FeatureFlags & ~QED_FEATURE_MASK)
1160 && !(Header.u64FeatureFlags & QED_FEATURE_BACKING_FILE_NO_PROBE))
1161 {
1162 if (Header.u64FeatureFlags & QED_FEATURE_NEED_CHECK)
1163 {
1164 /* Image needs checking. */
1165 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1166 rc = qedCheckImage(pImage, &Header);
1167 else
1168 rc = vdIfError(pImage->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1169 N_("Qed: Image '%s' needs checking but is opened readonly"),
1170 pImage->pszFilename);
1171 }
1172
1173 if ( RT_SUCCESS(rc)
1174 && (Header.u64FeatureFlags & QED_FEATURE_BACKING_FILE))
1175 {
1176 /* Load backing filename from image. */
1177 pImage->pszBackingFilename = RTStrAlloc(Header.u32BackingFilenameSize + 1); /* +1 for \0 terminator. */
1178 if (pImage->pszBackingFilename)
1179 {
1180 RT_BZERO(pImage->pszBackingFilename, Header.u32BackingFilenameSize + 1);
1181 pImage->cbBackingFilename = Header.u32BackingFilenameSize;
1182 pImage->offBackingFilename = Header.u32OffBackingFilename;
1183 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
1184 Header.u32OffBackingFilename, pImage->pszBackingFilename,
1185 Header.u32BackingFilenameSize);
1186 if (RT_SUCCESS(rc))
1187 rc = RTStrValidateEncoding(pImage->pszBackingFilename);
1188 }
1189 else
1190 rc = VERR_NO_STR_MEMORY;
1191 }
1192
1193 if (RT_SUCCESS(rc))
1194 {
1195 pImage->cbImage = cbFile;
1196 pImage->cbCluster = Header.u32ClusterSize;
1197 pImage->cbTable = Header.u32TableSize * pImage->cbCluster;
1198 pImage->cTableEntries = pImage->cbTable / sizeof(uint64_t);
1199 pImage->offL1Table = Header.u64OffL1Table;
1200 pImage->cbSize = Header.u64Size;
1201 qedTableMasksInit(pImage);
1202
1203 /* Allocate L1 table. */
1204 pImage->paL1Table = (uint64_t *)RTMemAllocZ(pImage->cbTable);
1205 if (pImage->paL1Table)
1206 {
1207 /* Read from the image. */
1208 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
1209 pImage->offL1Table, pImage->paL1Table,
1210 pImage->cbTable);
1211 if (RT_SUCCESS(rc))
1212 {
1213 qedTableConvertToHostEndianess(pImage->paL1Table, pImage->cTableEntries);
1214
1215 /* If the consistency check succeeded, clear the flag by flushing the image. */
1216 if (Header.u64FeatureFlags & QED_FEATURE_NEED_CHECK)
1217 rc = qedFlushImage(pImage);
1218 }
1219 else
1220 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1221 N_("Qed: Reading the L1 table for image '%s' failed"),
1222 pImage->pszFilename);
1223 }
1224 else
1225 rc = vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS,
1226 N_("Qed: Out of memory allocating L1 table for image '%s'"),
1227 pImage->pszFilename);
1228 }
1229 }
1230 else
1231 rc = vdIfError(pImage->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1232 N_("Qed: The image '%s' makes use of unsupported features"),
1233 pImage->pszFilename);
1234 }
1235 else if (RT_SUCCESS(rc))
1236 rc = VERR_VD_GEN_INVALID_HEADER;
1237 }
1238 else if (RT_SUCCESS(rc))
1239 rc = VERR_VD_GEN_INVALID_HEADER;
1240 }
1241 /* else: Do NOT signal an appropriate error here, as the VD layer has the
1242 * choice of retrying the open if it failed. */
1243 }
1244 else
1245 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1246 N_("Qed: Creating the L2 table cache for image '%s' failed"),
1247 pImage->pszFilename);
1248
1249 if (RT_SUCCESS(rc))
1250 {
1251 PVDREGIONDESC pRegion = &pImage->RegionList.aRegions[0];
1252 pImage->RegionList.fFlags = 0;
1253 pImage->RegionList.cRegions = 1;
1254
1255 pRegion->offRegion = 0; /* Disk start. */
1256 pRegion->cbBlock = 512;
1257 pRegion->enmDataForm = VDREGIONDATAFORM_RAW;
1258 pRegion->enmMetadataForm = VDREGIONMETADATAFORM_NONE;
1259 pRegion->cbData = 512;
1260 pRegion->cbMetadata = 0;
1261 pRegion->cRegionBlocksOrBytes = pImage->cbSize;
1262 }
1263 else
1264 qedFreeImage(pImage, false);
1265 return rc;
1266}
1267
1268/**
1269 * Internal: Create a qed image.
1270 */
1271static int qedCreateImage(PQEDIMAGE pImage, uint64_t cbSize,
1272 unsigned uImageFlags, const char *pszComment,
1273 PCVDGEOMETRY pPCHSGeometry,
1274 PCVDGEOMETRY pLCHSGeometry, unsigned uOpenFlags,
1275 PVDINTERFACEPROGRESS pIfProgress,
1276 unsigned uPercentStart, unsigned uPercentSpan)
1277{
1278 RT_NOREF1(pszComment);
1279 int rc;
1280
1281 if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
1282 {
1283 rc = qedL2TblCacheCreate(pImage);
1284 if (RT_SUCCESS(rc))
1285 {
1286 pImage->uOpenFlags = uOpenFlags & ~VD_OPEN_FLAGS_READONLY;
1287 pImage->uImageFlags = uImageFlags;
1288 pImage->PCHSGeometry = *pPCHSGeometry;
1289 pImage->LCHSGeometry = *pLCHSGeometry;
1290
1291 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
1292 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
1293 AssertPtrReturn(pImage->pIfIo, VERR_INVALID_PARAMETER);
1294
1295 /* Create image file. */
1296 uint32_t fOpen = VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags, true /* fCreate */);
1297 rc = vdIfIoIntFileOpen(pImage->pIfIo, pImage->pszFilename, fOpen, &pImage->pStorage);
1298 if (RT_SUCCESS(rc))
1299 {
1300 /* Init image state. */
1301 pImage->cbSize = cbSize;
1302 pImage->cbCluster = QED_CLUSTER_SIZE_DEFAULT;
1303 pImage->cbTable = qedCluster2Byte(pImage, QED_TABLE_SIZE_DEFAULT);
1304 pImage->cTableEntries = pImage->cbTable / sizeof(uint64_t);
1305 pImage->offL1Table = qedCluster2Byte(pImage, 1); /* Cluster 0 is the header. */
1306 pImage->cbImage = (1 * pImage->cbCluster) + pImage->cbTable; /* Header + L1 table size. */
1307 pImage->cbBackingFilename = 0;
1308 pImage->offBackingFilename = 0;
1309 qedTableMasksInit(pImage);
1310
1311 /* Init L1 table. */
1312 pImage->paL1Table = (uint64_t *)RTMemAllocZ(pImage->cbTable);
1313 if (RT_LIKELY(pImage->paL1Table))
1314 {
1315 vdIfProgress(pIfProgress, uPercentStart + uPercentSpan * 98 / 100);
1316 rc = qedFlushImage(pImage);
1317 }
1318 else
1319 rc = vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS, N_("Qed: cannot allocate memory for L1 table of image '%s'"),
1320 pImage->pszFilename);
1321 }
1322 else
1323 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("Qed: cannot create image '%s'"), pImage->pszFilename);
1324 }
1325 else
1326 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("Qed: Failed to create L2 cache for image '%s'"),
1327 pImage->pszFilename);
1328 }
1329 else
1330 rc = vdIfError(pImage->pIfError, VERR_VD_INVALID_TYPE, RT_SRC_POS, N_("Qed: cannot create fixed image '%s'"), pImage->pszFilename);
1331
1332 if (RT_SUCCESS(rc))
1333 {
1334 PVDREGIONDESC pRegion = &pImage->RegionList.aRegions[0];
1335 pImage->RegionList.fFlags = 0;
1336 pImage->RegionList.cRegions = 1;
1337
1338 pRegion->offRegion = 0; /* Disk start. */
1339 pRegion->cbBlock = 512;
1340 pRegion->enmDataForm = VDREGIONDATAFORM_RAW;
1341 pRegion->enmMetadataForm = VDREGIONMETADATAFORM_NONE;
1342 pRegion->cbData = 512;
1343 pRegion->cbMetadata = 0;
1344 pRegion->cRegionBlocksOrBytes = pImage->cbSize;
1345
1346 vdIfProgress(pIfProgress, uPercentStart + uPercentSpan);
1347 }
1348 else
1349 qedFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
1350
1351 return rc;
1352}
1353
1354/**
1355 * Rollback anything done during async cluster allocation.
1356 *
1357 * @returns VBox status code.
1358 * @param pImage The image instance data.
1359 * @param pIoCtx The I/O context.
1360 * @param pClusterAlloc The cluster allocation to rollback.
1361 */
1362static int qedAsyncClusterAllocRollback(PQEDIMAGE pImage, PVDIOCTX pIoCtx, PQEDCLUSTERASYNCALLOC pClusterAlloc)
1363{
1364 RT_NOREF1(pIoCtx);
1365 int rc = VINF_SUCCESS;
1366
1367 switch (pClusterAlloc->enmAllocState)
1368 {
1369 case QEDCLUSTERASYNCALLOCSTATE_L2_ALLOC:
1370 case QEDCLUSTERASYNCALLOCSTATE_L2_LINK:
1371 {
1372 /* Revert the L1 table entry */
1373 pImage->paL1Table[pClusterAlloc->idxL1] = 0;
1374 pImage->pL2TblAlloc = NULL;
1375
1376 /* Assumption right now is that the L1 table is not modified on storage if the link fails. */
1377 rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, pClusterAlloc->cbImageOld);
1378 qedL2TblCacheEntryRelease(pClusterAlloc->pL2Entry); /* Release L2 cache entry. */
1379 Assert(!pClusterAlloc->pL2Entry->cRefs);
1380 qedL2TblCacheEntryFree(pImage, pClusterAlloc->pL2Entry); /* Free it, it is not in the cache yet. */
1381 break;
1382 }
1383 case QEDCLUSTERASYNCALLOCSTATE_USER_ALLOC:
1384 case QEDCLUSTERASYNCALLOCSTATE_USER_LINK:
1385 {
1386 /* Assumption right now is that the L2 table is not modified if the link fails. */
1387 pClusterAlloc->pL2Entry->paL2Tbl[pClusterAlloc->idxL2] = 0;
1388 rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, pClusterAlloc->cbImageOld);
1389 qedL2TblCacheEntryRelease(pClusterAlloc->pL2Entry); /* Release L2 cache entry. */
1390 break;
1391 }
1392 default:
1393 AssertMsgFailed(("Invalid cluster allocation state %d\n", pClusterAlloc->enmAllocState));
1394 rc = VERR_INVALID_STATE;
1395 }
1396
1397 RTMemFree(pClusterAlloc);
1398 return rc;
1399}
1400
1401/**
1402 * Updates the state of the async cluster allocation.
1403 *
1404 * @returns VBox status code.
1405 * @param pBackendData The opaque backend data.
1406 * @param pIoCtx I/O context associated with this request.
1407 * @param pvUser Opaque user data passed during a read/write request.
1408 * @param rcReq Status code for the completed request.
1409 */
1410static DECLCALLBACK(int) qedAsyncClusterAllocUpdate(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
1411{
1412 int rc = VINF_SUCCESS;
1413 PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
1414 PQEDCLUSTERASYNCALLOC pClusterAlloc = (PQEDCLUSTERASYNCALLOC)pvUser;
1415
1416 if (RT_FAILURE(rcReq))
1417 return qedAsyncClusterAllocRollback(pImage, pIoCtx, pClusterAlloc);
1418
1419 AssertPtr(pClusterAlloc->pL2Entry);
1420
1421 switch (pClusterAlloc->enmAllocState)
1422 {
1423 case QEDCLUSTERASYNCALLOCSTATE_L2_ALLOC:
1424 {
1425 /* Update the link in the in memory L1 table now. */
1426 pImage->paL1Table[pClusterAlloc->idxL1] = pClusterAlloc->pL2Entry->offL2Tbl;
1427
1428 /* Update the link in the on disk L1 table now. */
1429 pClusterAlloc->enmAllocState = QEDCLUSTERASYNCALLOCSTATE_L2_LINK;
1430 rc = qedTblWrite(pImage, pIoCtx, pImage->offL1Table, pImage->paL1Table,
1431 qedAsyncClusterAllocUpdate, pClusterAlloc);
1432 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1433 break;
1434 else if (RT_FAILURE(rc))
1435 {
1436 /* Rollback. */
1437 qedAsyncClusterAllocRollback(pImage, pIoCtx, pClusterAlloc);
1438 break;
1439 }
1440 }
1441 RT_FALL_THRU();
1442 case QEDCLUSTERASYNCALLOCSTATE_L2_LINK:
1443 {
1444 /* L2 link updated in L1 , save L2 entry in cache and allocate new user data cluster. */
1445 uint64_t offData = qedClusterAllocate(pImage, 1);
1446
1447 pImage->pL2TblAlloc = NULL;
1448 qedL2TblCacheEntryInsert(pImage, pClusterAlloc->pL2Entry);
1449
1450 pClusterAlloc->enmAllocState = QEDCLUSTERASYNCALLOCSTATE_USER_ALLOC;
1451 pClusterAlloc->cbImageOld = offData;
1452 pClusterAlloc->offClusterNew = offData;
1453
1454 /* Write data. */
1455 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
1456 offData, pIoCtx, pClusterAlloc->cbToWrite,
1457 qedAsyncClusterAllocUpdate, pClusterAlloc);
1458 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1459 break;
1460 else if (RT_FAILURE(rc))
1461 {
1462 qedAsyncClusterAllocRollback(pImage, pIoCtx, pClusterAlloc);
1463 RTMemFree(pClusterAlloc);
1464 break;
1465 }
1466 }
1467 RT_FALL_THRU();
1468 case QEDCLUSTERASYNCALLOCSTATE_USER_ALLOC:
1469 {
1470 pClusterAlloc->enmAllocState = QEDCLUSTERASYNCALLOCSTATE_USER_LINK;
1471 pClusterAlloc->pL2Entry->paL2Tbl[pClusterAlloc->idxL2] = pClusterAlloc->offClusterNew;
1472
1473 /* Link L2 table and update it. */
1474 rc = qedTblWrite(pImage, pIoCtx, pImage->paL1Table[pClusterAlloc->idxL1],
1475 pClusterAlloc->pL2Entry->paL2Tbl,
1476 qedAsyncClusterAllocUpdate, pClusterAlloc);
1477 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1478 break;
1479 else if (RT_FAILURE(rc))
1480 {
1481 qedAsyncClusterAllocRollback(pImage, pIoCtx, pClusterAlloc);
1482 RTMemFree(pClusterAlloc);
1483 break;
1484 }
1485 }
1486 RT_FALL_THRU();
1487 case QEDCLUSTERASYNCALLOCSTATE_USER_LINK:
1488 {
1489 /* Everything done without errors, signal completion. */
1490 qedL2TblCacheEntryRelease(pClusterAlloc->pL2Entry);
1491 RTMemFree(pClusterAlloc);
1492 rc = VINF_SUCCESS;
1493 break;
1494 }
1495 default:
1496 AssertMsgFailed(("Invalid async cluster allocation state %d\n",
1497 pClusterAlloc->enmAllocState));
1498 }
1499
1500 return rc;
1501}
1502
1503/** @copydoc VDIMAGEBACKEND::pfnProbe */
1504static DECLCALLBACK(int) qedProbe(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
1505 PVDINTERFACE pVDIfsImage, VDTYPE enmDesiredType, VDTYPE *penmType)
1506{
1507 RT_NOREF(pVDIfsDisk, enmDesiredType);
1508 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
1509 PVDIOSTORAGE pStorage = NULL;
1510 int rc = VINF_SUCCESS;
1511
1512 /* Get I/O interface. */
1513 PVDINTERFACEIOINT pIfIo = VDIfIoIntGet(pVDIfsImage);
1514 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
1515 AssertReturn((VALID_PTR(pszFilename) && *pszFilename), VERR_INVALID_PARAMETER);
1516
1517 /*
1518 * Open the file and read the footer.
1519 */
1520 rc = vdIfIoIntFileOpen(pIfIo, pszFilename,
1521 VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
1522 false /* fCreate */),
1523 &pStorage);
1524 if (RT_SUCCESS(rc))
1525 {
1526 uint64_t cbFile;
1527
1528 rc = vdIfIoIntFileGetSize(pIfIo, pStorage, &cbFile);
1529 if ( RT_SUCCESS(rc)
1530 && cbFile > sizeof(QedHeader))
1531 {
1532 QedHeader Header;
1533
1534 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &Header, sizeof(Header));
1535 if ( RT_SUCCESS(rc)
1536 && qedHdrConvertToHostEndianess(&Header))
1537 *penmType = VDTYPE_HDD;
1538 else
1539 rc = VERR_VD_GEN_INVALID_HEADER;
1540 }
1541 else
1542 rc = VERR_VD_GEN_INVALID_HEADER;
1543 }
1544
1545 if (pStorage)
1546 vdIfIoIntFileClose(pIfIo, pStorage);
1547
1548 LogFlowFunc(("returns %Rrc\n", rc));
1549 return rc;
1550}
1551
1552/** @copydoc VDIMAGEBACKEND::pfnOpen */
1553static DECLCALLBACK(int) qedOpen(const char *pszFilename, unsigned uOpenFlags,
1554 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1555 VDTYPE enmType, void **ppBackendData)
1556{
1557 RT_NOREF1(enmType); /**< @todo r=klaus make use of the type info. */
1558
1559 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p enmType=%u ppBackendData=%#p\n",
1560 pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, enmType, ppBackendData));
1561 int rc;
1562
1563 /* Check open flags. All valid flags are supported. */
1564 AssertReturn(!(uOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_PARAMETER);
1565 AssertReturn((VALID_PTR(pszFilename) && *pszFilename), VERR_INVALID_PARAMETER);
1566
1567 PQEDIMAGE pImage = (PQEDIMAGE)RTMemAllocZ(RT_UOFFSETOF(QEDIMAGE, RegionList.aRegions[1]));
1568 if (RT_LIKELY(pImage))
1569 {
1570 pImage->pszFilename = pszFilename;
1571 pImage->pStorage = NULL;
1572 pImage->pVDIfsDisk = pVDIfsDisk;
1573 pImage->pVDIfsImage = pVDIfsImage;
1574
1575 rc = qedOpenImage(pImage, uOpenFlags);
1576 if (RT_SUCCESS(rc))
1577 *ppBackendData = pImage;
1578 else
1579 RTMemFree(pImage);
1580 }
1581 else
1582 rc = VERR_NO_MEMORY;
1583
1584 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
1585 return rc;
1586}
1587
1588/** @copydoc VDIMAGEBACKEND::pfnCreate */
1589static DECLCALLBACK(int) qedCreate(const char *pszFilename, uint64_t cbSize,
1590 unsigned uImageFlags, const char *pszComment,
1591 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
1592 PCRTUUID pUuid, unsigned uOpenFlags,
1593 unsigned uPercentStart, unsigned uPercentSpan,
1594 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1595 PVDINTERFACE pVDIfsOperation, VDTYPE enmType,
1596 void **ppBackendData)
1597{
1598 RT_NOREF1(pUuid);
1599 LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p enmType=%d ppBackendData=%#p",
1600 pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, enmType, ppBackendData));
1601 int rc;
1602
1603 /* Check the VD container type. */
1604 if (enmType != VDTYPE_HDD)
1605 return VERR_VD_INVALID_TYPE;
1606
1607 /* Check open flags. All valid flags are supported. */
1608 AssertReturn(!(uOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_PARAMETER);
1609 AssertReturn( VALID_PTR(pszFilename)
1610 && *pszFilename
1611 && VALID_PTR(pPCHSGeometry)
1612 && VALID_PTR(pLCHSGeometry), VERR_INVALID_PARAMETER);
1613
1614 PQEDIMAGE pImage = (PQEDIMAGE)RTMemAllocZ(RT_UOFFSETOF(QEDIMAGE, RegionList.aRegions[1]));
1615 if (RT_LIKELY(pImage))
1616 {
1617 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
1618
1619 pImage->pszFilename = pszFilename;
1620 pImage->pStorage = NULL;
1621 pImage->pVDIfsDisk = pVDIfsDisk;
1622 pImage->pVDIfsImage = pVDIfsImage;
1623
1624 rc = qedCreateImage(pImage, cbSize, uImageFlags, pszComment,
1625 pPCHSGeometry, pLCHSGeometry, uOpenFlags,
1626 pIfProgress, uPercentStart, uPercentSpan);
1627 if (RT_SUCCESS(rc))
1628 {
1629 /* So far the image is opened in read/write mode. Make sure the
1630 * image is opened in read-only mode if the caller requested that. */
1631 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
1632 {
1633 qedFreeImage(pImage, false);
1634 rc = qedOpenImage(pImage, uOpenFlags);
1635 }
1636
1637 if (RT_SUCCESS(rc))
1638 *ppBackendData = pImage;
1639 }
1640
1641 if (RT_FAILURE(rc))
1642 RTMemFree(pImage);
1643 }
1644 else
1645 rc = VERR_NO_MEMORY;
1646
1647 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
1648 return rc;
1649}
1650
1651/** @copydoc VDIMAGEBACKEND::pfnRename */
1652static DECLCALLBACK(int) qedRename(void *pBackendData, const char *pszFilename)
1653{
1654 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
1655 int rc = VINF_SUCCESS;
1656 PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
1657
1658 /* Check arguments. */
1659 AssertReturn((pImage && pszFilename && *pszFilename), VERR_INVALID_PARAMETER);
1660
1661 /* Close the image. */
1662 rc = qedFreeImage(pImage, false);
1663 if (RT_SUCCESS(rc))
1664 {
1665 /* Rename the file. */
1666 rc = vdIfIoIntFileMove(pImage->pIfIo, pImage->pszFilename, pszFilename, 0);
1667 if (RT_SUCCESS(rc))
1668 {
1669 /* Update pImage with the new information. */
1670 pImage->pszFilename = pszFilename;
1671
1672 /* Open the old image with new name. */
1673 rc = qedOpenImage(pImage, pImage->uOpenFlags);
1674 }
1675 else
1676 {
1677 /* The move failed, try to reopen the original image. */
1678 int rc2 = qedOpenImage(pImage, pImage->uOpenFlags);
1679 if (RT_FAILURE(rc2))
1680 rc = rc2;
1681 }
1682 }
1683
1684 LogFlowFunc(("returns %Rrc\n", rc));
1685 return rc;
1686}
1687
1688/** @copydoc VDIMAGEBACKEND::pfnClose */
1689static DECLCALLBACK(int) qedClose(void *pBackendData, bool fDelete)
1690{
1691 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
1692 PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
1693
1694 int rc = qedFreeImage(pImage, fDelete);
1695 RTMemFree(pImage);
1696
1697 LogFlowFunc(("returns %Rrc\n", rc));
1698 return rc;
1699}
1700
1701/** @copydoc VDIMAGEBACKEND::pfnRead */
1702static DECLCALLBACK(int) qedRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
1703 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
1704{
1705 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
1706 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
1707 PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
1708 uint32_t offCluster = 0;
1709 uint32_t idxL1 = 0;
1710 uint32_t idxL2 = 0;
1711 uint64_t offFile = 0;
1712
1713 AssertPtr(pImage);
1714 Assert(uOffset % 512 == 0);
1715 Assert(cbToRead % 512 == 0);
1716 AssertReturn((VALID_PTR(pIoCtx) && cbToRead), VERR_INVALID_PARAMETER);
1717 AssertReturn(uOffset + cbToRead <= pImage->cbSize, VERR_INVALID_PARAMETER);
1718
1719 qedConvertLogicalOffset(pImage, uOffset, &idxL1, &idxL2, &offCluster);
1720
1721 /* Clip read size to remain in the cluster. */
1722 cbToRead = RT_MIN(cbToRead, pImage->cbCluster - offCluster);
1723
1724 /* Get offset in image. */
1725 int rc = qedConvertToImageOffset(pImage, pIoCtx, idxL1, idxL2, offCluster, &offFile);
1726 if (RT_SUCCESS(rc))
1727 rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage, offFile,
1728 pIoCtx, cbToRead);
1729
1730 if ( ( RT_SUCCESS(rc)
1731 || rc == VERR_VD_BLOCK_FREE
1732 || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1733 && pcbActuallyRead)
1734 *pcbActuallyRead = cbToRead;
1735
1736 LogFlowFunc(("returns %Rrc\n", rc));
1737 return rc;
1738}
1739
1740/** @copydoc VDIMAGEBACKEND::pfnWrite */
1741static DECLCALLBACK(int) qedWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
1742 PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
1743 size_t *pcbPostRead, unsigned fWrite)
1744{
1745 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
1746 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
1747 PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
1748 uint32_t offCluster = 0;
1749 uint32_t idxL1 = 0;
1750 uint32_t idxL2 = 0;
1751 uint64_t offImage = 0;
1752 int rc = VINF_SUCCESS;
1753
1754 AssertPtr(pImage);
1755 Assert(!(uOffset % 512));
1756 Assert(!(cbToWrite % 512));
1757 AssertReturn((VALID_PTR(pIoCtx) && cbToWrite), VERR_INVALID_PARAMETER);
1758 AssertReturn(uOffset + cbToWrite <= pImage->cbSize, VERR_INVALID_PARAMETER);
1759
1760 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
1761 {
1762 /* Convert offset to L1, L2 index and cluster offset. */
1763 qedConvertLogicalOffset(pImage, uOffset, &idxL1, &idxL2, &offCluster);
1764
1765 /* Clip write size to remain in the cluster. */
1766 cbToWrite = RT_MIN(cbToWrite, pImage->cbCluster - offCluster);
1767 Assert(!(cbToWrite % 512));
1768
1769 /* Get offset in image. */
1770 rc = qedConvertToImageOffset(pImage, pIoCtx, idxL1, idxL2, offCluster, &offImage);
1771 if (RT_SUCCESS(rc))
1772 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
1773 offImage, pIoCtx, cbToWrite, NULL, NULL);
1774 else if (rc == VERR_VD_BLOCK_FREE)
1775 {
1776 if ( cbToWrite == pImage->cbCluster
1777 && !(fWrite & VD_WRITE_NO_ALLOC))
1778 {
1779 PQEDL2CACHEENTRY pL2Entry = NULL;
1780
1781 /* Full cluster write to previously unallocated cluster.
1782 * Allocate cluster and write data. */
1783 Assert(!offCluster);
1784
1785 do
1786 {
1787 /* Check if we have to allocate a new cluster for L2 tables. */
1788 if (!pImage->paL1Table[idxL1])
1789 {
1790 uint64_t offL2Tbl;
1791 PQEDCLUSTERASYNCALLOC pL2ClusterAlloc = NULL;
1792
1793 /* Allocate new async cluster allocation state. */
1794 pL2ClusterAlloc = (PQEDCLUSTERASYNCALLOC)RTMemAllocZ(sizeof(QEDCLUSTERASYNCALLOC));
1795 if (RT_UNLIKELY(!pL2ClusterAlloc))
1796 {
1797 rc = VERR_NO_MEMORY;
1798 break;
1799 }
1800
1801 pL2Entry = qedL2TblCacheEntryAlloc(pImage);
1802 if (!pL2Entry)
1803 {
1804 rc = VERR_NO_MEMORY;
1805 RTMemFree(pL2ClusterAlloc);
1806 break;
1807 }
1808
1809 offL2Tbl = qedClusterAllocate(pImage, qedByte2Cluster(pImage, pImage->cbTable));
1810 pL2Entry->offL2Tbl = offL2Tbl;
1811 memset(pL2Entry->paL2Tbl, 0, pImage->cbTable);
1812
1813 pL2ClusterAlloc->enmAllocState = QEDCLUSTERASYNCALLOCSTATE_L2_ALLOC;
1814 pL2ClusterAlloc->cbImageOld = offL2Tbl;
1815 pL2ClusterAlloc->offClusterNew = offL2Tbl;
1816 pL2ClusterAlloc->idxL1 = idxL1;
1817 pL2ClusterAlloc->idxL2 = idxL2;
1818 pL2ClusterAlloc->cbToWrite = cbToWrite;
1819 pL2ClusterAlloc->pL2Entry = pL2Entry;
1820
1821 pImage->pL2TblAlloc = pL2Entry;
1822
1823 LogFlowFunc(("Allocating new L2 table at cluster offset %llu\n", offL2Tbl));
1824
1825 /*
1826 * Write the L2 table first and link to the L1 table afterwards.
1827 * If something unexpected happens the worst case which can happen
1828 * is a leak of some clusters.
1829 */
1830 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
1831 offL2Tbl, pL2Entry->paL2Tbl, pImage->cbTable, pIoCtx,
1832 qedAsyncClusterAllocUpdate, pL2ClusterAlloc);
1833 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1834 break;
1835 else if (RT_FAILURE(rc))
1836 {
1837 RTMemFree(pL2ClusterAlloc);
1838 qedL2TblCacheEntryFree(pImage, pL2Entry);
1839 break;
1840 }
1841
1842 rc = qedAsyncClusterAllocUpdate(pImage, pIoCtx, pL2ClusterAlloc, rc);
1843 }
1844 else
1845 {
1846 LogFlowFunc(("Fetching L2 table at cluster offset %llu\n", pImage->paL1Table[idxL1]));
1847
1848 rc = qedL2TblCacheFetchAsync(pImage, pIoCtx, pImage->paL1Table[idxL1],
1849 &pL2Entry);
1850
1851 if (RT_SUCCESS(rc))
1852 {
1853 PQEDCLUSTERASYNCALLOC pDataClusterAlloc = NULL;
1854
1855 /* Allocate new async cluster allocation state. */
1856 pDataClusterAlloc = (PQEDCLUSTERASYNCALLOC)RTMemAllocZ(sizeof(QEDCLUSTERASYNCALLOC));
1857 if (RT_UNLIKELY(!pDataClusterAlloc))
1858 {
1859 rc = VERR_NO_MEMORY;
1860 break;
1861 }
1862
1863 /* Allocate new cluster for the data. */
1864 uint64_t offData = qedClusterAllocate(pImage, 1);
1865
1866 pDataClusterAlloc->enmAllocState = QEDCLUSTERASYNCALLOCSTATE_USER_ALLOC;
1867 pDataClusterAlloc->cbImageOld = offData;
1868 pDataClusterAlloc->offClusterNew = offData;
1869 pDataClusterAlloc->idxL1 = idxL1;
1870 pDataClusterAlloc->idxL2 = idxL2;
1871 pDataClusterAlloc->cbToWrite = cbToWrite;
1872 pDataClusterAlloc->pL2Entry = pL2Entry;
1873
1874 /* Write data. */
1875 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
1876 offData, pIoCtx, cbToWrite,
1877 qedAsyncClusterAllocUpdate, pDataClusterAlloc);
1878 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1879 break;
1880 else if (RT_FAILURE(rc))
1881 {
1882 RTMemFree(pDataClusterAlloc);
1883 break;
1884 }
1885
1886 rc = qedAsyncClusterAllocUpdate(pImage, pIoCtx, pDataClusterAlloc, rc);
1887 }
1888 }
1889
1890 } while (0);
1891
1892 *pcbPreRead = 0;
1893 *pcbPostRead = 0;
1894 }
1895 else
1896 {
1897 /* Trying to do a partial write to an unallocated cluster. Don't do
1898 * anything except letting the upper layer know what to do. */
1899 *pcbPreRead = offCluster;
1900 *pcbPostRead = pImage->cbCluster - cbToWrite - *pcbPreRead;
1901 }
1902 }
1903
1904 if (pcbWriteProcess)
1905 *pcbWriteProcess = cbToWrite;
1906 }
1907 else
1908 rc = VERR_VD_IMAGE_READ_ONLY;
1909
1910 LogFlowFunc(("returns %Rrc\n", rc));
1911 return rc;
1912}
1913
1914/** @copydoc VDIMAGEBACKEND::pfnFlush */
1915static DECLCALLBACK(int) qedFlush(void *pBackendData, PVDIOCTX pIoCtx)
1916{
1917 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1918 PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
1919 int rc = VINF_SUCCESS;
1920
1921 AssertPtr(pImage);
1922 AssertPtrReturn(pIoCtx, VERR_INVALID_PARAMETER);
1923
1924 if ( pImage->pStorage
1925 && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
1926 {
1927 QedHeader Header;
1928
1929 Assert(!(pImage->cbTable % pImage->cbCluster));
1930 rc = qedTblWrite(pImage, pIoCtx, pImage->offL1Table, pImage->paL1Table,
1931 NULL, NULL);
1932 if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1933 {
1934 /* Write header. */
1935 qedHdrConvertFromHostEndianess(pImage, &Header);
1936 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
1937 0, &Header, sizeof(Header),
1938 pIoCtx, NULL, NULL);
1939 if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1940 rc = vdIfIoIntFileFlush(pImage->pIfIo, pImage->pStorage,
1941 pIoCtx, NULL, NULL);
1942 }
1943 }
1944
1945 LogFlowFunc(("returns %Rrc\n", rc));
1946 return rc;
1947}
1948
1949/** @copydoc VDIMAGEBACKEND::pfnGetVersion */
1950static DECLCALLBACK(unsigned) qedGetVersion(void *pBackendData)
1951{
1952 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1953 PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
1954
1955 AssertPtrReturn(pImage, 0);
1956
1957 return 1;
1958}
1959
1960/** @copydoc VDIMAGEBACKEND::pfnGetFileSize */
1961static DECLCALLBACK(uint64_t) qedGetFileSize(void *pBackendData)
1962{
1963 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1964 PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
1965 uint64_t cb = 0;
1966
1967 AssertPtrReturn(pImage, 0);
1968
1969 uint64_t cbFile;
1970 if (pImage->pStorage)
1971 {
1972 int rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbFile);
1973 if (RT_SUCCESS(rc))
1974 cb += cbFile;
1975 }
1976
1977 LogFlowFunc(("returns %lld\n", cb));
1978 return cb;
1979}
1980
1981/** @copydoc VDIMAGEBACKEND::pfnGetPCHSGeometry */
1982static DECLCALLBACK(int) qedGetPCHSGeometry(void *pBackendData,
1983 PVDGEOMETRY pPCHSGeometry)
1984{
1985 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
1986 PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
1987 int rc = VINF_SUCCESS;
1988
1989 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
1990
1991 if (pImage->PCHSGeometry.cCylinders)
1992 *pPCHSGeometry = pImage->PCHSGeometry;
1993 else
1994 rc = VERR_VD_GEOMETRY_NOT_SET;
1995
1996 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1997 return rc;
1998}
1999
2000/** @copydoc VDIMAGEBACKEND::pfnSetPCHSGeometry */
2001static DECLCALLBACK(int) qedSetPCHSGeometry(void *pBackendData,
2002 PCVDGEOMETRY pPCHSGeometry)
2003{
2004 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
2005 pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
2006 PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
2007 int rc = VINF_SUCCESS;
2008
2009 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
2010
2011 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2012 rc = VERR_VD_IMAGE_READ_ONLY;
2013 else
2014 pImage->PCHSGeometry = *pPCHSGeometry;
2015
2016 LogFlowFunc(("returns %Rrc\n", rc));
2017 return rc;
2018}
2019
2020/** @copydoc VDIMAGEBACKEND::pfnGetLCHSGeometry */
2021static DECLCALLBACK(int) qedGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
2022{
2023 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
2024 PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
2025 int rc = VINF_SUCCESS;
2026
2027 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
2028
2029 if (pImage->LCHSGeometry.cCylinders)
2030 *pLCHSGeometry = pImage->LCHSGeometry;
2031 else
2032 rc = VERR_VD_GEOMETRY_NOT_SET;
2033
2034 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders,
2035 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2036 return rc;
2037}
2038
2039/** @copydoc VDIMAGEBACKEND::pfnSetLCHSGeometry */
2040static DECLCALLBACK(int) qedSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
2041{
2042 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData,
2043 pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2044 PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
2045 int rc = VINF_SUCCESS;
2046
2047 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
2048
2049 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2050 rc = VERR_VD_IMAGE_READ_ONLY;
2051 else
2052 pImage->LCHSGeometry = *pLCHSGeometry;
2053
2054 LogFlowFunc(("returns %Rrc\n", rc));
2055 return rc;
2056}
2057
2058/** @copydoc VDIMAGEBACKEND::pfnQueryRegions */
2059static DECLCALLBACK(int) qedQueryRegions(void *pBackendData, PCVDREGIONLIST *ppRegionList)
2060{
2061 LogFlowFunc(("pBackendData=%#p ppRegionList=%#p\n", pBackendData, ppRegionList));
2062 PQEDIMAGE pThis = (PQEDIMAGE)pBackendData;
2063
2064 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
2065
2066 *ppRegionList = &pThis->RegionList;
2067 LogFlowFunc(("returns %Rrc\n", VINF_SUCCESS));
2068 return VINF_SUCCESS;
2069}
2070
2071/** @copydoc VDIMAGEBACKEND::pfnRegionListRelease */
2072static DECLCALLBACK(void) qedRegionListRelease(void *pBackendData, PCVDREGIONLIST pRegionList)
2073{
2074 RT_NOREF1(pRegionList);
2075 LogFlowFunc(("pBackendData=%#p pRegionList=%#p\n", pBackendData, pRegionList));
2076 PQEDIMAGE pThis = (PQEDIMAGE)pBackendData;
2077 AssertPtr(pThis); RT_NOREF(pThis);
2078
2079 /* Nothing to do here. */
2080}
2081
2082/** @copydoc VDIMAGEBACKEND::pfnGetImageFlags */
2083static DECLCALLBACK(unsigned) qedGetImageFlags(void *pBackendData)
2084{
2085 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2086 PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
2087
2088 AssertPtrReturn(pImage, 0);
2089
2090 LogFlowFunc(("returns %#x\n", pImage->uImageFlags));
2091 return pImage->uImageFlags;
2092}
2093
2094/** @copydoc VDIMAGEBACKEND::pfnGetOpenFlags */
2095static DECLCALLBACK(unsigned) qedGetOpenFlags(void *pBackendData)
2096{
2097 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2098 PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
2099
2100 AssertPtrReturn(pImage, 0);
2101
2102 LogFlowFunc(("returns %#x\n", pImage->uOpenFlags));
2103 return pImage->uOpenFlags;
2104}
2105
2106/** @copydoc VDIMAGEBACKEND::pfnSetOpenFlags */
2107static DECLCALLBACK(int) qedSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
2108{
2109 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
2110 PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
2111 int rc = VINF_SUCCESS;
2112
2113 /* Image must be opened and the new flags must be valid. */
2114 if (!pImage || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
2115 | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
2116 | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
2117 rc = VERR_INVALID_PARAMETER;
2118 else
2119 {
2120 /* Implement this operation via reopening the image. */
2121 rc = qedFreeImage(pImage, false);
2122 if (RT_SUCCESS(rc))
2123 rc = qedOpenImage(pImage, uOpenFlags);
2124 }
2125
2126 LogFlowFunc(("returns %Rrc\n", rc));
2127 return rc;
2128}
2129
2130/** @copydoc VDIMAGEBACKEND::pfnGetComment */
2131VD_BACKEND_CALLBACK_GET_COMMENT_DEF_NOT_SUPPORTED(qedGetComment);
2132
2133/** @copydoc VDIMAGEBACKEND::pfnSetComment */
2134VD_BACKEND_CALLBACK_SET_COMMENT_DEF_NOT_SUPPORTED(qedSetComment, PQEDIMAGE);
2135
2136/** @copydoc VDIMAGEBACKEND::pfnGetUuid */
2137VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(qedGetUuid);
2138
2139/** @copydoc VDIMAGEBACKEND::pfnSetUuid */
2140VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(qedSetUuid, PQEDIMAGE);
2141
2142/** @copydoc VDIMAGEBACKEND::pfnGetModificationUuid */
2143VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(qedGetModificationUuid);
2144
2145/** @copydoc VDIMAGEBACKEND::pfnSetModificationUuid */
2146VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(qedSetModificationUuid, PQEDIMAGE);
2147
2148/** @copydoc VDIMAGEBACKEND::pfnGetParentUuid */
2149VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(qedGetParentUuid);
2150
2151/** @copydoc VDIMAGEBACKEND::pfnSetParentUuid */
2152VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(qedSetParentUuid, PQEDIMAGE);
2153
2154/** @copydoc VDIMAGEBACKEND::pfnGetParentModificationUuid */
2155VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(qedGetParentModificationUuid);
2156
2157/** @copydoc VDIMAGEBACKEND::pfnSetParentModificationUuid */
2158VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(qedSetParentModificationUuid, PQEDIMAGE);
2159
2160/** @copydoc VDIMAGEBACKEND::pfnDump */
2161static DECLCALLBACK(void) qedDump(void *pBackendData)
2162{
2163 PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
2164
2165 AssertPtrReturnVoid(pImage);
2166 vdIfErrorMessage(pImage->pIfError, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%llu\n",
2167 pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
2168 pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
2169 pImage->cbSize / 512);
2170}
2171
2172/** @copydoc VDIMAGEBACKEND::pfnGetParentFilename */
2173static DECLCALLBACK(int) qedGetParentFilename(void *pBackendData, char **ppszParentFilename)
2174{
2175 int rc = VINF_SUCCESS;
2176 PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
2177
2178 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
2179
2180 if (pImage->pszBackingFilename)
2181 *ppszParentFilename = RTStrDup(pImage->pszBackingFilename);
2182 else
2183 rc = VERR_NOT_SUPPORTED;
2184
2185 LogFlowFunc(("returns %Rrc\n", rc));
2186 return rc;
2187}
2188
2189/** @copydoc VDIMAGEBACKEND::pfnSetParentFilename */
2190static DECLCALLBACK(int) qedSetParentFilename(void *pBackendData, const char *pszParentFilename)
2191{
2192 int rc = VINF_SUCCESS;
2193 PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
2194
2195 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
2196
2197 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2198 rc = VERR_VD_IMAGE_READ_ONLY;
2199 else if ( pImage->pszBackingFilename
2200 && (strlen(pszParentFilename) > pImage->cbBackingFilename))
2201 rc = VERR_NOT_SUPPORTED; /* The new filename is longer than the old one. */
2202 else
2203 {
2204 if (pImage->pszBackingFilename)
2205 RTStrFree(pImage->pszBackingFilename);
2206 pImage->pszBackingFilename = RTStrDup(pszParentFilename);
2207 if (!pImage->pszBackingFilename)
2208 rc = VERR_NO_STR_MEMORY;
2209 else
2210 {
2211 if (!pImage->offBackingFilename)
2212 {
2213 /* Allocate new cluster. */
2214 uint64_t offData = qedClusterAllocate(pImage, 1);
2215
2216 Assert((offData & UINT32_MAX) == offData);
2217 pImage->offBackingFilename = (uint32_t)offData;
2218 pImage->cbBackingFilename = (uint32_t)strlen(pszParentFilename);
2219 rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage,
2220 offData + pImage->cbCluster);
2221 }
2222
2223 if (RT_SUCCESS(rc))
2224 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2225 pImage->offBackingFilename,
2226 pImage->pszBackingFilename,
2227 strlen(pImage->pszBackingFilename));
2228 }
2229 }
2230
2231 LogFlowFunc(("returns %Rrc\n", rc));
2232 return rc;
2233}
2234
2235/** @copydoc VDIMAGEBACKEND::pfnResize */
2236static DECLCALLBACK(int) qedResize(void *pBackendData, uint64_t cbSize,
2237 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
2238 unsigned uPercentStart, unsigned uPercentSpan,
2239 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
2240 PVDINTERFACE pVDIfsOperation)
2241{
2242 RT_NOREF7(pPCHSGeometry, pLCHSGeometry, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation);
2243 PQEDIMAGE pImage = (PQEDIMAGE)pBackendData;
2244 int rc = VINF_SUCCESS;
2245
2246 /* Making the image smaller is not supported at the moment. */
2247 if (cbSize < pImage->cbSize)
2248 rc = VERR_NOT_SUPPORTED;
2249 else if (cbSize > pImage->cbSize)
2250 {
2251 /*
2252 * It is enough to just update the size field in the header to complete
2253 * growing. With the default cluster and table sizes the image can be expanded
2254 * to 64TB without overflowing the L1 and L2 tables making block relocation
2255 * superfluous.
2256 * @todo: The rare case where block relocation is still required (non default
2257 * table and/or cluster size or images with more than 64TB) is not
2258 * implemented yet and resizing such an image will fail with an error.
2259 */
2260 if (qedByte2Cluster(pImage, pImage->cbTable)*pImage->cTableEntries*pImage->cTableEntries*pImage->cbCluster < cbSize)
2261 rc = vdIfError(pImage->pIfError, VERR_BUFFER_OVERFLOW, RT_SRC_POS,
2262 N_("Qed: Resizing the image '%s' is not supported because it would overflow the L1 and L2 table\n"),
2263 pImage->pszFilename);
2264 else
2265 {
2266 uint64_t cbSizeOld = pImage->cbSize;
2267
2268 pImage->cbSize = cbSize;
2269 rc = qedFlushImage(pImage);
2270 if (RT_FAILURE(rc))
2271 {
2272 pImage->cbSize = cbSizeOld; /* Restore */
2273
2274 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("Qed: Resizing the image '%s' failed\n"),
2275 pImage->pszFilename);
2276 }
2277 }
2278 }
2279 /* Same size doesn't change the image at all. */
2280
2281 LogFlowFunc(("returns %Rrc\n", rc));
2282 return rc;
2283}
2284
2285
2286const VDIMAGEBACKEND g_QedBackend =
2287{
2288 /* u32Version */
2289 VD_IMGBACKEND_VERSION,
2290 /* pszBackendName */
2291 "QED",
2292 /* uBackendCaps */
2293 VD_CAP_FILE | VD_CAP_VFS | VD_CAP_CREATE_DYNAMIC | VD_CAP_DIFF | VD_CAP_ASYNC,
2294 /* paFileExtensions */
2295 s_aQedFileExtensions,
2296 /* paConfigInfo */
2297 NULL,
2298 /* pfnProbe */
2299 qedProbe,
2300 /* pfnOpen */
2301 qedOpen,
2302 /* pfnCreate */
2303 qedCreate,
2304 /* pfnRename */
2305 qedRename,
2306 /* pfnClose */
2307 qedClose,
2308 /* pfnRead */
2309 qedRead,
2310 /* pfnWrite */
2311 qedWrite,
2312 /* pfnFlush */
2313 qedFlush,
2314 /* pfnDiscard */
2315 NULL,
2316 /* pfnGetVersion */
2317 qedGetVersion,
2318 /* pfnGetFileSize */
2319 qedGetFileSize,
2320 /* pfnGetPCHSGeometry */
2321 qedGetPCHSGeometry,
2322 /* pfnSetPCHSGeometry */
2323 qedSetPCHSGeometry,
2324 /* pfnGetLCHSGeometry */
2325 qedGetLCHSGeometry,
2326 /* pfnSetLCHSGeometry */
2327 qedSetLCHSGeometry,
2328 /* pfnQueryRegions */
2329 qedQueryRegions,
2330 /* pfnRegionListRelease */
2331 qedRegionListRelease,
2332 /* pfnGetImageFlags */
2333 qedGetImageFlags,
2334 /* pfnGetOpenFlags */
2335 qedGetOpenFlags,
2336 /* pfnSetOpenFlags */
2337 qedSetOpenFlags,
2338 /* pfnGetComment */
2339 qedGetComment,
2340 /* pfnSetComment */
2341 qedSetComment,
2342 /* pfnGetUuid */
2343 qedGetUuid,
2344 /* pfnSetUuid */
2345 qedSetUuid,
2346 /* pfnGetModificationUuid */
2347 qedGetModificationUuid,
2348 /* pfnSetModificationUuid */
2349 qedSetModificationUuid,
2350 /* pfnGetParentUuid */
2351 qedGetParentUuid,
2352 /* pfnSetParentUuid */
2353 qedSetParentUuid,
2354 /* pfnGetParentModificationUuid */
2355 qedGetParentModificationUuid,
2356 /* pfnSetParentModificationUuid */
2357 qedSetParentModificationUuid,
2358 /* pfnDump */
2359 qedDump,
2360 /* pfnGetTimestamp */
2361 NULL,
2362 /* pfnGetParentTimestamp */
2363 NULL,
2364 /* pfnSetParentTimestamp */
2365 NULL,
2366 /* pfnGetParentFilename */
2367 qedGetParentFilename,
2368 /* pfnSetParentFilename */
2369 qedSetParentFilename,
2370 /* pfnComposeLocation */
2371 genericFileComposeLocation,
2372 /* pfnComposeName */
2373 genericFileComposeName,
2374 /* pfnCompact */
2375 NULL,
2376 /* pfnResize */
2377 qedResize,
2378 /* pfnRepair */
2379 NULL,
2380 /* pfnTraverseMetadata */
2381 NULL,
2382 /* u32Version */
2383 VD_IMGBACKEND_VERSION
2384};
Note: See TracBrowser for help on using the repository browser.

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