VirtualBox

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

Last change on this file since 38521 was 38469, checked in by vboxsync, 13 years ago

VD: Interface cleanup. Merge the two involved structures (generic interface descriptor and callback table) into one, remove the duplicated interface wrappers in the backends and move the interface definitions into separate headers separating public and private interfaces.

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